using System.Linq; using Content.Server.Body.Systems; using Content.Shared.Alert; using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.DoAfter; using Content.Shared.Ensnaring; using Content.Shared.Ensnaring.Components; using Content.Shared.IdentityManagement; using Content.Shared.StepTrigger.Systems; using Content.Shared.Throwing; namespace Content.Server.Ensnaring; public sealed partial class EnsnareableSystem { [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly BodySystem _body = default!; public void InitializeEnsnaring() { SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent(AttemptStepTrigger); SubscribeLocalEvent(OnStepTrigger); SubscribeLocalEvent(OnThrowHit); } private void OnComponentRemove(EntityUid uid, EnsnaringComponent component, ComponentRemove args) { if (!TryComp(component.Ensnared, out var ensnared)) return; if (ensnared.IsEnsnared) ForceFree(uid, component); } private void AttemptStepTrigger(EntityUid uid, EnsnaringComponent component, ref StepTriggerAttemptEvent args) { args.Continue = true; } private void OnStepTrigger(EntityUid uid, EnsnaringComponent component, ref StepTriggeredEvent args) { TryEnsnare(args.Tripper, uid, component); } private void OnThrowHit(EntityUid uid, EnsnaringComponent component, ThrowDoHitEvent args) { if (!component.CanThrowTrigger) return; TryEnsnare(args.Target, uid, component); } /// /// Used where you want to try to ensnare an entity with the /// /// The entity that will be ensnared /// The entity that is used to ensnare /// The ensnaring component public void TryEnsnare(EntityUid target, EntityUid ensnare, EnsnaringComponent component) { //Don't do anything if they don't have the ensnareable component. if (!TryComp(target, out var ensnareable)) return; var legs = _body.GetBodyChildrenOfType(target, BodyPartType.Leg).Count(); var ensnaredLegs = (2 * ensnareable.Container.ContainedEntities.Count); var freeLegs = legs - ensnaredLegs; if (freeLegs <= 0) return; component.Ensnared = target; ensnareable.Container.Insert(ensnare); ensnareable.IsEnsnared = true; Dirty(ensnareable); UpdateAlert(target, ensnareable); var ev = new EnsnareEvent(component.WalkSpeed, component.SprintSpeed); RaiseLocalEvent(target, ev); } /// /// Used where you want to try to free an entity with the /// /// The entity that will be freed /// The entity that is freeing the target /// The entity used to ensnare /// The ensnaring component public void TryFree(EntityUid target, EntityUid user, EntityUid ensnare, EnsnaringComponent component) { //Don't do anything if they don't have the ensnareable component. if (!HasComp(target)) return; var freeTime = user == target ? component.BreakoutTime : component.FreeTime; var breakOnMove = !component.CanMoveBreakout; var doAfterEventArgs = new DoAfterArgs(EntityManager, user, freeTime, new EnsnareableDoAfterEvent(), target, target: target, used: ensnare) { BreakOnUserMove = breakOnMove, BreakOnTargetMove = breakOnMove, BreakOnDamage = false, NeedHand = true, BlockDuplicate = true, }; if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) return; if (user == target) _popup.PopupEntity(Loc.GetString("ensnare-component-try-free", ("ensnare", ensnare)), target, target); else _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-other", ("ensnare", ensnare), ("user", Identity.Entity(target, EntityManager))), user, user); } /// /// Used to force free someone for things like if the is removed /// public void ForceFree(EntityUid ensnare, EnsnaringComponent component) { if (component.Ensnared == null) return; if (!TryComp(component.Ensnared, out var ensnareable)) return; var target = component.Ensnared.Value; ensnareable.Container.Remove(ensnare, force: true); ensnareable.IsEnsnared = ensnareable.Container.ContainedEntities.Count > 0; Dirty(ensnareable); component.Ensnared = null; UpdateAlert(target, ensnareable); var ev = new EnsnareRemoveEvent(component.WalkSpeed, component.SprintSpeed); RaiseLocalEvent(ensnare, ev); } /// /// Update the Ensnared alert for an entity. /// /// The entity that has been affected by a snare public void UpdateAlert(EntityUid target, EnsnareableComponent component) { if (!component.IsEnsnared) _alerts.ClearAlert(target, AlertType.Ensnared); else _alerts.ShowAlert(target, AlertType.Ensnared); } }