The alert for snares will appear again. Previously it was being updated on the snare itself and not the player. It is no longer possible to infinitely ensnare someone; the maximum number is dependent on the target's legs. Only one snare at a time will be removed now. Clarified the wording and logic around CanMoveBreakout. It was inconsistent. Made multiple snares impose cumulative speed penalties. It is no longer possible to remove bolas while moving.
154 lines
5.7 KiB
C#
154 lines
5.7 KiB
C#
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<EnsnaringComponent, ComponentRemove>(OnComponentRemove);
|
|
SubscribeLocalEvent<EnsnaringComponent, StepTriggerAttemptEvent>(AttemptStepTrigger);
|
|
SubscribeLocalEvent<EnsnaringComponent, StepTriggeredEvent>(OnStepTrigger);
|
|
SubscribeLocalEvent<EnsnaringComponent, ThrowDoHitEvent>(OnThrowHit);
|
|
}
|
|
|
|
private void OnComponentRemove(EntityUid uid, EnsnaringComponent component, ComponentRemove args)
|
|
{
|
|
if (!TryComp<EnsnareableComponent>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used where you want to try to ensnare an entity with the <see cref="EnsnareableComponent"/>
|
|
/// </summary>
|
|
/// <param name="target">The entity that will be ensnared</param>
|
|
/// <paramref name="ensnare"> The entity that is used to ensnare</param>
|
|
/// <param name="component">The ensnaring component</param>
|
|
public void TryEnsnare(EntityUid target, EntityUid ensnare, EnsnaringComponent component)
|
|
{
|
|
//Don't do anything if they don't have the ensnareable component.
|
|
if (!TryComp<EnsnareableComponent>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used where you want to try to free an entity with the <see cref="EnsnareableComponent"/>
|
|
/// </summary>
|
|
/// <param name="target">The entity that will be freed</param>
|
|
/// <param name="user">The entity that is freeing the target</param>
|
|
/// <param name="ensnare">The entity used to ensnare</param>
|
|
/// <param name="component">The ensnaring component</param>
|
|
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<EnsnareableComponent>(target))
|
|
return;
|
|
|
|
var freeTime = user == target ? component.BreakoutTime : component.FreeTime;
|
|
var breakOnMove = !component.CanMoveBreakout;
|
|
|
|
var doAfterEventArgs = new DoAfterArgs(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to force free someone for things like if the <see cref="EnsnaringComponent"/> is removed
|
|
/// </summary>
|
|
public void ForceFree(EntityUid ensnare, EnsnaringComponent component)
|
|
{
|
|
if (component.Ensnared == null)
|
|
return;
|
|
|
|
if (!TryComp<EnsnareableComponent>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the Ensnared alert for an entity.
|
|
/// </summary>
|
|
/// <param name="target">The entity that has been affected by a snare</param>
|
|
public void UpdateAlert(EntityUid target, EnsnareableComponent component)
|
|
{
|
|
if (!component.IsEnsnared)
|
|
_alerts.ClearAlert(target, AlertType.Ensnared);
|
|
else
|
|
_alerts.ShowAlert(target, AlertType.Ensnared);
|
|
}
|
|
}
|