Ensnaring Component and Bola Update (#9968)
This commit is contained in:
13
Content.Client/Ensnaring/Components/EnsnareableComponent.cs
Normal file
13
Content.Client/Ensnaring/Components/EnsnareableComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Ensnaring.Components;
|
||||||
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(SharedEnsnareableComponent))]
|
||||||
|
public sealed class EnsnareableComponent : SharedEnsnareableComponent
|
||||||
|
{
|
||||||
|
[DataField("sprite")]
|
||||||
|
public string? Sprite;
|
||||||
|
|
||||||
|
[DataField("state")]
|
||||||
|
public string? State;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Ensnaring.Components;
|
||||||
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(SharedEnsnaringComponent))]
|
||||||
|
public sealed class EnsnaringComponent : SharedEnsnaringComponent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Ensnaring.Visualizers;
|
||||||
|
[RegisterComponent]
|
||||||
|
[Access(typeof(EnsnareableVisualizerSystem))]
|
||||||
|
public sealed class EnsnareableVisualizerComponent : Component
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using Content.Client.Ensnaring.Components;
|
||||||
|
using Content.Shared.Ensnaring;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Ensnaring.Visualizers;
|
||||||
|
|
||||||
|
public sealed class EnsnareableVisualizerSystem : VisualizerSystem<EnsnareableComponent>
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<EnsnareableVisualizerComponent, ComponentInit>(OnComponentInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnComponentInit(EntityUid uid, EnsnareableVisualizerComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
if(!TryComp<SpriteComponent>(uid, out var sprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sprite.LayerMapReserveBlank(EnsnaredVisualLayers.Ensnared);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAppearanceChange(EntityUid uid, EnsnareableComponent component, ref AppearanceChangeEvent args)
|
||||||
|
{
|
||||||
|
if (args.Component.TryGetData(EnsnareableVisuals.IsEnsnared, out bool isEnsnared))
|
||||||
|
{
|
||||||
|
if (args.Sprite != null && component.Sprite != null)
|
||||||
|
{
|
||||||
|
args.Sprite.LayerSetRSI(EnsnaredVisualLayers.Ensnared, component.Sprite);
|
||||||
|
args.Sprite.LayerSetState(EnsnaredVisualLayers.Ensnared, component.State);
|
||||||
|
args.Sprite.LayerSetVisible(EnsnaredVisualLayers.Ensnared, isEnsnared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EnsnaredVisualLayers : byte
|
||||||
|
{
|
||||||
|
Ensnared,
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ namespace Content.Client.Inventory
|
|||||||
public Dictionary<(string ID, string Name), string>? Inventory { get; private set; }
|
public Dictionary<(string ID, string Name), string>? Inventory { get; private set; }
|
||||||
public Dictionary<string, string>? Hands { get; private set; }
|
public Dictionary<string, string>? Hands { get; private set; }
|
||||||
public Dictionary<EntityUid, string>? Handcuffs { get; private set; }
|
public Dictionary<EntityUid, string>? Handcuffs { get; private set; }
|
||||||
|
public Dictionary<EntityUid, string>? Ensnare { get; private set; }
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private StrippingMenu? _strippingMenu;
|
private StrippingMenu? _strippingMenu;
|
||||||
@@ -79,6 +80,17 @@ namespace Content.Client.Inventory
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Ensnare != null)
|
||||||
|
{
|
||||||
|
foreach (var (id, name) in Ensnare)
|
||||||
|
{
|
||||||
|
_strippingMenu.AddButton(Loc.GetString("strippable-bound-user-interface-stripping-menu-ensnare-button"), name, (ev) =>
|
||||||
|
{
|
||||||
|
SendMessage(new StrippingEnsnareButtonPressed(id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateState(BoundUserInterfaceState state)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
@@ -90,6 +102,7 @@ namespace Content.Client.Inventory
|
|||||||
Inventory = stripState.Inventory;
|
Inventory = stripState.Inventory;
|
||||||
Hands = stripState.Hands;
|
Hands = stripState.Hands;
|
||||||
Handcuffs = stripState.Handcuffs;
|
Handcuffs = stripState.Handcuffs;
|
||||||
|
Ensnare = stripState.Ensnare;
|
||||||
|
|
||||||
UpdateMenu();
|
UpdateMenu();
|
||||||
}
|
}
|
||||||
|
|||||||
25
Content.Server/Alert/Click/RemoveEnsnare.cs
Normal file
25
Content.Server/Alert/Click/RemoveEnsnare.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Content.Server.Ensnaring;
|
||||||
|
using Content.Server.Ensnaring.Components;
|
||||||
|
using Content.Shared.Alert;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Content.Server.Alert.Click;
|
||||||
|
[UsedImplicitly]
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class RemoveEnsnare : IAlertClick
|
||||||
|
{
|
||||||
|
public void AlertClicked(EntityUid player)
|
||||||
|
{
|
||||||
|
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
if (entManager.TryGetComponent(player, out EnsnareableComponent? ensnareableComponent))
|
||||||
|
{
|
||||||
|
foreach (var ensnare in ensnareableComponent.Container.ContainedEntities)
|
||||||
|
{
|
||||||
|
if (!entManager.TryGetComponent(ensnare, out EnsnaringComponent? ensnaringComponent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
entManager.EntitySysManager.GetEntitySystem<EnsnareableSystem>().TryFree(player, ensnaringComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Content.Server/Ensnaring/Components/EnsnareableComponent.cs
Normal file
15
Content.Server/Ensnaring/Components/EnsnareableComponent.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
|
namespace Content.Server.Ensnaring.Components;
|
||||||
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(SharedEnsnareableComponent))]
|
||||||
|
public sealed class EnsnareableComponent : SharedEnsnareableComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The container where the <see cref="EnsnaringComponent"/> entity will be stored
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("container")]
|
||||||
|
public Container Container = default!;
|
||||||
|
}
|
||||||
43
Content.Server/Ensnaring/Components/EnsnaringComponent.cs
Normal file
43
Content.Server/Ensnaring/Components/EnsnaringComponent.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Ensnaring.Components;
|
||||||
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(SharedEnsnaringComponent))]
|
||||||
|
public sealed class EnsnaringComponent : SharedEnsnaringComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Should movement cancel breaking out?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("canMoveBreakout")]
|
||||||
|
public bool CanMoveBreakout;
|
||||||
|
|
||||||
|
public CancellationTokenSource? CancelToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for the do after event to free the entity that owns the <see cref="EnsnareableComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FreeEnsnareDoAfterComplete : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly EntityUid EnsnaringEntity;
|
||||||
|
|
||||||
|
public FreeEnsnareDoAfterComplete(EntityUid ensnaringEntity)
|
||||||
|
{
|
||||||
|
EnsnaringEntity = ensnaringEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for the do after event when it fails to free the entity that owns the <see cref="EnsnareableComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FreeEnsnareDoAfterCancel : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly EntityUid EnsnaringEntity;
|
||||||
|
|
||||||
|
public FreeEnsnareDoAfterCancel(EntityUid ensnaringEntity)
|
||||||
|
{
|
||||||
|
EnsnaringEntity = ensnaringEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
149
Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs
Normal file
149
Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using Content.Server.DoAfter;
|
||||||
|
using Content.Server.Ensnaring.Components;
|
||||||
|
using Content.Shared.Alert;
|
||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.StepTrigger.Systems;
|
||||||
|
using Content.Shared.Throwing;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
|
namespace Content.Server.Ensnaring;
|
||||||
|
|
||||||
|
public sealed partial class EnsnareableSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
|
[Dependency] private readonly AlertsSystem _alerts = 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(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, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnThrowHit(EntityUid uid, EnsnaringComponent component, ThrowDoHitEvent args)
|
||||||
|
{
|
||||||
|
if (!component.CanThrowTrigger)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryEnsnare(args.Target, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used where you want to try to ensnare an entity with the <see cref="EnsnareableComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ensnaringEntity">The entity that will be used to ensnare</param>
|
||||||
|
/// <param name="target">The entity that will be ensnared</param>
|
||||||
|
/// <param name="component">The ensnaring component</param>
|
||||||
|
public void TryEnsnare(EntityUid target, EnsnaringComponent component)
|
||||||
|
{
|
||||||
|
//Don't do anything if they don't have the ensnareable component.
|
||||||
|
if (!TryComp<EnsnareableComponent>(target, out var ensnareable))
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.Ensnared = target;
|
||||||
|
ensnareable.Container.Insert(component.Owner);
|
||||||
|
ensnareable.IsEnsnared = true;
|
||||||
|
|
||||||
|
UpdateAlert(ensnareable);
|
||||||
|
var ev = new EnsnareEvent(component.WalkSpeed, component.SprintSpeed);
|
||||||
|
RaiseLocalEvent(target, ev, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 free</param>
|
||||||
|
/// <param name="component">The ensnaring component</param>
|
||||||
|
public void TryFree(EntityUid target, EnsnaringComponent component, EntityUid? user = null)
|
||||||
|
{
|
||||||
|
//Don't do anything if they don't have the ensnareable component.
|
||||||
|
if (!TryComp<EnsnareableComponent>(target, out var ensnareable))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.CancelToken != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.CancelToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var isOwner = !(user != null && target != user);
|
||||||
|
var freeTime = isOwner ? component.BreakoutTime : component.FreeTime;
|
||||||
|
bool breakOnMove;
|
||||||
|
|
||||||
|
if (isOwner)
|
||||||
|
breakOnMove = !component.CanMoveBreakout;
|
||||||
|
else
|
||||||
|
breakOnMove = true;
|
||||||
|
|
||||||
|
var doAfterEventArgs = new DoAfterEventArgs(target, freeTime, component.CancelToken.Token, target)
|
||||||
|
{
|
||||||
|
BreakOnUserMove = breakOnMove,
|
||||||
|
BreakOnTargetMove = breakOnMove,
|
||||||
|
BreakOnDamage = false,
|
||||||
|
BreakOnStun = true,
|
||||||
|
NeedHand = true,
|
||||||
|
TargetFinishedEvent = new FreeEnsnareDoAfterComplete(component.Owner),
|
||||||
|
TargetCancelledEvent = new FreeEnsnareDoAfterCancel(component.Owner),
|
||||||
|
};
|
||||||
|
|
||||||
|
_doAfter.DoAfter(doAfterEventArgs);
|
||||||
|
|
||||||
|
if (isOwner)
|
||||||
|
_popup.PopupEntity(Loc.GetString("ensnare-component-try-free", ("ensnare", component.Owner)), target, Filter.Entities(target));
|
||||||
|
|
||||||
|
if (!isOwner && user != null)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("ensnare-component-try-free-other", ("ensnare", component.Owner), ("user", Identity.Entity(target, EntityManager))), user.Value, Filter.Entities(user.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to force free someone for things like if the <see cref="EnsnaringComponent"/> is removed
|
||||||
|
/// </summary>
|
||||||
|
public void ForceFree(EnsnaringComponent component)
|
||||||
|
{
|
||||||
|
if (!TryComp<EnsnareableComponent>(component.Ensnared, out var ensnareable))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ensnareable.Container.ForceRemove(component.Owner);
|
||||||
|
ensnareable.IsEnsnared = false;
|
||||||
|
component.Ensnared = null;
|
||||||
|
|
||||||
|
UpdateAlert(ensnareable);
|
||||||
|
var ev = new EnsnareRemoveEvent();
|
||||||
|
RaiseLocalEvent(component.Owner, ev, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateAlert(EnsnareableComponent component)
|
||||||
|
{
|
||||||
|
if (!component.IsEnsnared)
|
||||||
|
{
|
||||||
|
_alerts.ClearAlert(component.Owner, AlertType.Ensnared);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_alerts.ShowAlert(component.Owner, AlertType.Ensnared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Content.Server/Ensnaring/EnsnareableSystem.cs
Normal file
62
Content.Server/Ensnaring/EnsnareableSystem.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using Content.Server.Ensnaring.Components;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Shared.Ensnaring;
|
||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Robust.Server.Containers;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
|
namespace Content.Server.Ensnaring;
|
||||||
|
|
||||||
|
public sealed partial class EnsnareableSystem : SharedEnsnareableSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ContainerSystem _container = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
InitializeEnsnaring();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<EnsnareableComponent, ComponentInit>(OnEnsnareableInit);
|
||||||
|
SubscribeLocalEvent<EnsnareableComponent, FreeEnsnareDoAfterComplete>(OnFreeComplete);
|
||||||
|
SubscribeLocalEvent<EnsnareableComponent, FreeEnsnareDoAfterCancel>(OnFreeFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnsnareableInit(EntityUid uid, EnsnareableComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
component.Container = _container.EnsureContainer<Container>(component.Owner, "ensnare");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFreeComplete(EntityUid uid, EnsnareableComponent component, FreeEnsnareDoAfterComplete args)
|
||||||
|
{
|
||||||
|
if (!TryComp<EnsnaringComponent>(args.EnsnaringEntity, out var ensnaring))
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.Container.Remove(args.EnsnaringEntity);
|
||||||
|
component.IsEnsnared = false;
|
||||||
|
ensnaring.Ensnared = null;
|
||||||
|
|
||||||
|
_popup.PopupEntity(Loc.GetString("ensnare-component-try-free-complete", ("ensnare", args.EnsnaringEntity)),
|
||||||
|
uid, Filter.Entities(uid), PopupType.Large);
|
||||||
|
|
||||||
|
UpdateAlert(component);
|
||||||
|
var ev = new EnsnareRemoveEvent();
|
||||||
|
RaiseLocalEvent(uid, ev, false);
|
||||||
|
|
||||||
|
ensnaring.CancelToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFreeFail(EntityUid uid, EnsnareableComponent component, FreeEnsnareDoAfterCancel args)
|
||||||
|
{
|
||||||
|
if (!TryComp<EnsnaringComponent>(args.EnsnaringEntity, out var ensnaring))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ensnaring.CancelToken = null;
|
||||||
|
|
||||||
|
_popup.PopupEntity(Loc.GetString("ensnare-component-try-free-fail", ("ensnare", args.EnsnaringEntity)),
|
||||||
|
uid, Filter.Entities(uid), PopupType.Large);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ namespace Content.Server.Entry
|
|||||||
"CharacterInfo",
|
"CharacterInfo",
|
||||||
"HandheldGPS",
|
"HandheldGPS",
|
||||||
"CableVisualizer",
|
"CableVisualizer",
|
||||||
|
"EnsnareableVisualizer",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Content.Server.Cuffs.Components;
|
using Content.Server.Cuffs.Components;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
|
using Content.Server.Ensnaring;
|
||||||
|
using Content.Server.Ensnaring.Components;
|
||||||
using Content.Server.Hands.Components;
|
using Content.Server.Hands.Components;
|
||||||
using Content.Server.Inventory;
|
using Content.Server.Inventory;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
@@ -14,6 +18,7 @@ using Content.Shared.Popups;
|
|||||||
using Content.Shared.Strip.Components;
|
using Content.Shared.Strip.Components;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
namespace Content.Server.Strip
|
namespace Content.Server.Strip
|
||||||
@@ -24,6 +29,7 @@ namespace Content.Server.Strip
|
|||||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly EnsnareableSystem _ensnaring = default!;
|
||||||
|
|
||||||
// TODO: ECS popups. Not all of these have ECS equivalents yet.
|
// TODO: ECS popups. Not all of these have ECS equivalents yet.
|
||||||
|
|
||||||
@@ -36,11 +42,13 @@ namespace Content.Server.Strip
|
|||||||
SubscribeLocalEvent<StrippableComponent, DidUnequipEvent>(OnDidUnequip);
|
SubscribeLocalEvent<StrippableComponent, DidUnequipEvent>(OnDidUnequip);
|
||||||
SubscribeLocalEvent<StrippableComponent, ComponentInit>(OnCompInit);
|
SubscribeLocalEvent<StrippableComponent, ComponentInit>(OnCompInit);
|
||||||
SubscribeLocalEvent<StrippableComponent, CuffedStateChangeEvent>(OnCuffStateChange);
|
SubscribeLocalEvent<StrippableComponent, CuffedStateChangeEvent>(OnCuffStateChange);
|
||||||
|
SubscribeLocalEvent<StrippableComponent, EnsnaredChangedEvent>(OnEnsnareChange);
|
||||||
|
|
||||||
// BUI
|
// BUI
|
||||||
SubscribeLocalEvent<StrippableComponent, StrippingInventoryButtonPressed>(OnStripInvButtonMessage);
|
SubscribeLocalEvent<StrippableComponent, StrippingInventoryButtonPressed>(OnStripInvButtonMessage);
|
||||||
SubscribeLocalEvent<StrippableComponent, StrippingHandButtonPressed>(OnStripHandMessage);
|
SubscribeLocalEvent<StrippableComponent, StrippingHandButtonPressed>(OnStripHandMessage);
|
||||||
SubscribeLocalEvent<StrippableComponent, StrippingHandcuffButtonPressed>(OnStripHandcuffMessage);
|
SubscribeLocalEvent<StrippableComponent, StrippingHandcuffButtonPressed>(OnStripHandcuffMessage);
|
||||||
|
SubscribeLocalEvent<StrippableComponent, StrippingEnsnareButtonPressed>(OnStripEnsnareMessage);
|
||||||
|
|
||||||
SubscribeLocalEvent<StrippableComponent, OpenStrippingCompleteEvent>(OnOpenStripComplete);
|
SubscribeLocalEvent<StrippableComponent, OpenStrippingCompleteEvent>(OnOpenStripComplete);
|
||||||
SubscribeLocalEvent<StrippableComponent, OpenStrippingCancelledEvent>(OnOpenStripCancelled);
|
SubscribeLocalEvent<StrippableComponent, OpenStrippingCancelledEvent>(OnOpenStripCancelled);
|
||||||
@@ -76,6 +84,26 @@ namespace Content.Server.Strip
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnStripEnsnareMessage(EntityUid uid, StrippableComponent component, StrippingEnsnareButtonPressed args)
|
||||||
|
{
|
||||||
|
if (args.Session.AttachedEntity is not {Valid: true} user)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ensnareQuery = GetEntityQuery<EnsnareableComponent>();
|
||||||
|
|
||||||
|
foreach (var entity in ensnareQuery.GetComponent(uid).Container.ContainedEntities)
|
||||||
|
{
|
||||||
|
if (!TryComp<EnsnaringComponent>(entity, out var ensnaring))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (entity != args.Ensnare)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_ensnaring.TryFree(component.Owner, ensnaring, user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnStripHandMessage(EntityUid uid, StrippableComponent component, StrippingHandButtonPressed args)
|
private void OnStripHandMessage(EntityUid uid, StrippableComponent component, StrippingHandButtonPressed args)
|
||||||
{
|
{
|
||||||
if (args.Session.AttachedEntity is not {Valid: true} user ||
|
if (args.Session.AttachedEntity is not {Valid: true} user ||
|
||||||
@@ -154,6 +182,11 @@ namespace Content.Server.Strip
|
|||||||
UpdateState(uid, component);
|
UpdateState(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEnsnareChange(EntityUid uid, StrippableComponent component, EnsnaredChangedEvent args)
|
||||||
|
{
|
||||||
|
SendUpdate(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnDidUnequip(EntityUid uid, StrippableComponent component, DidUnequipEvent args)
|
private void OnDidUnequip(EntityUid uid, StrippableComponent component, DidUnequipEvent args)
|
||||||
{
|
{
|
||||||
SendUpdate(uid, component);
|
SendUpdate(uid, component);
|
||||||
@@ -174,6 +207,7 @@ namespace Content.Server.Strip
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cuffs = new Dictionary<EntityUid, string>();
|
var cuffs = new Dictionary<EntityUid, string>();
|
||||||
|
var ensnare = new Dictionary<EntityUid, string>();
|
||||||
var inventory = new Dictionary<(string ID, string Name), string>();
|
var inventory = new Dictionary<(string ID, string Name), string>();
|
||||||
var hands = new Dictionary<string, string>();
|
var hands = new Dictionary<string, string>();
|
||||||
|
|
||||||
@@ -186,6 +220,17 @@ namespace Content.Server.Strip
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ensnareQuery = GetEntityQuery<EnsnareableComponent>();
|
||||||
|
|
||||||
|
if (ensnareQuery.TryGetComponent(uid, out var _))
|
||||||
|
{
|
||||||
|
foreach (var entity in ensnareQuery.GetComponent(uid).Container.ContainedEntities)
|
||||||
|
{
|
||||||
|
var name = Name(entity);
|
||||||
|
ensnare.Add(entity, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_inventorySystem.TryGetSlots(uid, out var slots))
|
if (_inventorySystem.TryGetSlots(uid, out var slots))
|
||||||
{
|
{
|
||||||
foreach (var slot in slots)
|
foreach (var slot in slots)
|
||||||
@@ -223,7 +268,7 @@ namespace Content.Server.Strip
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bui.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs));
|
bui.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs, ensnare));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddStripVerb(EntityUid uid, StrippableComponent component, GetVerbsEvent<Verb> args)
|
private void AddStripVerb(EntityUid uid, StrippableComponent component, GetVerbsEvent<Verb> args)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace Content.Shared.Alert
|
|||||||
Weightless,
|
Weightless,
|
||||||
Stun,
|
Stun,
|
||||||
Handcuffed,
|
Handcuffed,
|
||||||
|
Ensnared,
|
||||||
Buckled,
|
Buckled,
|
||||||
HumanCrit,
|
HumanCrit,
|
||||||
HumanDead,
|
HumanDead,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace Content.Shared.CharacterAppearance
|
|||||||
LFoot,
|
LFoot,
|
||||||
Handcuffs,
|
Handcuffs,
|
||||||
StencilMask,
|
StencilMask,
|
||||||
|
Ensnare,
|
||||||
Fire,
|
Fire,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
namespace Content.Shared.Ensnaring.Components;
|
||||||
|
/// <summary>
|
||||||
|
/// Use this on an entity that you would like to be ensnared by anything that has the <see cref="SharedEnsnaringComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedEnsnareableComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How much should this slow down the entities walk?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("walkSpeed")]
|
||||||
|
public float WalkSpeed = 1.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much should this slow down the entities sprint?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("sprintSpeed")]
|
||||||
|
public float SprintSpeed = 1.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this entity currently ensnared?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("isEnsnared")]
|
||||||
|
public bool IsEnsnared;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class EnsnaredChangedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly bool IsEnsnared;
|
||||||
|
|
||||||
|
public EnsnaredChangedEvent(bool isEnsnared)
|
||||||
|
{
|
||||||
|
IsEnsnared = isEnsnared;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
namespace Content.Shared.Ensnaring.Components;
|
||||||
|
/// <summary>
|
||||||
|
/// Use this on something you want to use to ensnare an entity with
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedEnsnaringComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How long it should take to free someone else.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("freeTime")]
|
||||||
|
public float FreeTime = 3.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long it should take for an entity to free themselves.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("breakoutTime")]
|
||||||
|
public float BreakoutTime = 30.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much should this slow down the entities walk?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("walkSpeed")]
|
||||||
|
public float WalkSpeed = 0.9f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much should this slow down the entities sprint?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("sprintSpeed")]
|
||||||
|
public float SprintSpeed = 0.9f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should this ensnare someone when thrown?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("canThrowTrigger")]
|
||||||
|
public bool CanThrowTrigger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What is ensnared?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("ensnared")]
|
||||||
|
public EntityUid? Ensnared;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used whenever you want to do something when someone becomes ensnared by the <see cref="SharedEnsnaringComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EnsnareEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly float WalkSpeed;
|
||||||
|
public readonly float SprintSpeed;
|
||||||
|
|
||||||
|
public EnsnareEvent(float walkSpeed, float sprintSpeed)
|
||||||
|
{
|
||||||
|
WalkSpeed = walkSpeed;
|
||||||
|
SprintSpeed = sprintSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used whenever you want to do something when someone is freed by the <see cref="SharedEnsnaringComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EnsnareRemoveEvent : CancellableEntityEventArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
9
Content.Shared/Ensnaring/EnsnareableVisuals.cs
Normal file
9
Content.Shared/Ensnaring/EnsnareableVisuals.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Ensnaring;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum EnsnareableVisuals : byte
|
||||||
|
{
|
||||||
|
IsEnsnared
|
||||||
|
}
|
||||||
59
Content.Shared/Ensnaring/SharedEnsnareableSystem.cs
Normal file
59
Content.Shared/Ensnaring/SharedEnsnareableSystem.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using Content.Shared.Ensnaring.Components;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
|
|
||||||
|
namespace Content.Shared.Ensnaring;
|
||||||
|
|
||||||
|
public abstract class SharedEnsnareableSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly MovementSpeedModifierSystem _speedModifier = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SharedEnsnareableComponent, RefreshMovementSpeedModifiersEvent>(MovementSpeedModify);
|
||||||
|
SubscribeLocalEvent<SharedEnsnareableComponent, EnsnareEvent>(OnEnsnare);
|
||||||
|
SubscribeLocalEvent<SharedEnsnareableComponent, EnsnareRemoveEvent>(OnEnsnareRemove);
|
||||||
|
SubscribeLocalEvent<SharedEnsnareableComponent, EnsnaredChangedEvent>(OnEnsnareChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnsnare(EntityUid uid, SharedEnsnareableComponent component, EnsnareEvent args)
|
||||||
|
{
|
||||||
|
component.WalkSpeed = args.WalkSpeed;
|
||||||
|
component.SprintSpeed = args.SprintSpeed;
|
||||||
|
|
||||||
|
_speedModifier.RefreshMovementSpeedModifiers(uid);
|
||||||
|
|
||||||
|
var ev = new EnsnaredChangedEvent(component.IsEnsnared);
|
||||||
|
RaiseLocalEvent(uid, ev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnsnareRemove(EntityUid uid, SharedEnsnareableComponent component, EnsnareRemoveEvent args)
|
||||||
|
{
|
||||||
|
_speedModifier.RefreshMovementSpeedModifiers(uid);
|
||||||
|
|
||||||
|
var ev = new EnsnaredChangedEvent(component.IsEnsnared);
|
||||||
|
RaiseLocalEvent(uid, ev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnsnareChange(EntityUid uid, SharedEnsnareableComponent component, EnsnaredChangedEvent args)
|
||||||
|
{
|
||||||
|
UpdateAppearance(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAppearance(EntityUid uid, SharedEnsnareableComponent? component, AppearanceComponent? appearance = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component, ref appearance, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
appearance.SetData(EnsnareableVisuals.IsEnsnared, component.IsEnsnared);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MovementSpeedModify(EntityUid uid, SharedEnsnareableComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||||
|
{
|
||||||
|
if (!component.IsEnsnared)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.ModifySpeed(component.WalkSpeed, component.SprintSpeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,18 +64,32 @@ namespace Content.Shared.Strip.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public sealed class StrippingEnsnareButtonPressed : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public EntityUid Ensnare { get; }
|
||||||
|
|
||||||
|
public StrippingEnsnareButtonPressed(EntityUid ensnare)
|
||||||
|
{
|
||||||
|
Ensnare = ensnare;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[NetSerializable, Serializable]
|
[NetSerializable, Serializable]
|
||||||
public sealed class StrippingBoundUserInterfaceState : BoundUserInterfaceState
|
public sealed class StrippingBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
{
|
{
|
||||||
public Dictionary<(string ID, string Name), string> Inventory { get; }
|
public Dictionary<(string ID, string Name), string> Inventory { get; }
|
||||||
public Dictionary<string, string> Hands { get; }
|
public Dictionary<string, string> Hands { get; }
|
||||||
public Dictionary<EntityUid, string> Handcuffs { get; }
|
public Dictionary<EntityUid, string> Handcuffs { get; }
|
||||||
|
public Dictionary<EntityUid, string> Ensnare { get; }
|
||||||
|
|
||||||
public StrippingBoundUserInterfaceState(Dictionary<(string ID, string Name), string> inventory, Dictionary<string, string> hands, Dictionary<EntityUid, string> handcuffs)
|
public StrippingBoundUserInterfaceState(Dictionary<(string ID, string Name), string> inventory, Dictionary<string, string> hands, Dictionary<EntityUid, string> handcuffs,
|
||||||
|
Dictionary<EntityUid, string> ensnare)
|
||||||
{
|
{
|
||||||
Inventory = inventory;
|
Inventory = inventory;
|
||||||
Hands = hands;
|
Hands = hands;
|
||||||
Handcuffs = handcuffs;
|
Handcuffs = handcuffs;
|
||||||
|
Ensnare = ensnare;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace Content.Tests.Shared.Alert
|
|||||||
id: testAlertOrder
|
id: testAlertOrder
|
||||||
order:
|
order:
|
||||||
- alertType: Handcuffed
|
- alertType: Handcuffed
|
||||||
|
- alertType: Ensnared
|
||||||
- category: Pressure
|
- category: Pressure
|
||||||
- category: Hunger
|
- category: Hunger
|
||||||
- alertType: Hot
|
- alertType: Hot
|
||||||
@@ -47,6 +48,10 @@ namespace Content.Tests.Shared.Alert
|
|||||||
id: Handcuffed
|
id: Handcuffed
|
||||||
icons: []
|
icons: []
|
||||||
|
|
||||||
|
- type: alert
|
||||||
|
id: Ensnared
|
||||||
|
icons: []
|
||||||
|
|
||||||
- type: alert
|
- type: alert
|
||||||
id: Hot
|
id: Hot
|
||||||
icons: []
|
icons: []
|
||||||
@@ -82,6 +87,7 @@ namespace Content.Tests.Shared.Alert
|
|||||||
// ensure they sort according to our expected criteria
|
// ensure they sort according to our expected criteria
|
||||||
var expectedOrder = new List<AlertType>();
|
var expectedOrder = new List<AlertType>();
|
||||||
expectedOrder.Add(AlertType.Handcuffed);
|
expectedOrder.Add(AlertType.Handcuffed);
|
||||||
|
expectedOrder.Add(AlertType.Ensnared);
|
||||||
expectedOrder.Add(AlertType.HighPressure);
|
expectedOrder.Add(AlertType.HighPressure);
|
||||||
// stuff with only category + same category ordered by enum value
|
// stuff with only category + same category ordered by enum value
|
||||||
expectedOrder.Add(AlertType.Peckish);
|
expectedOrder.Add(AlertType.Peckish);
|
||||||
|
|||||||
5
Resources/Locale/en-US/ensnare/ensnare-component.ftl
Normal file
5
Resources/Locale/en-US/ensnare/ensnare-component.ftl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ensnare-component-try-free = You struggle to remove {$ensnare} that's ensnaring you!
|
||||||
|
ensnare-component-try-free-complete = You successfully free yourself from the {$ensnare}!
|
||||||
|
ensnare-component-try-free-fail = You fail to free yourself from the {$ensnare}!
|
||||||
|
|
||||||
|
ensnare-component-try-free-other = You start removing the {$ensnare} caught on {$user}!
|
||||||
@@ -17,4 +17,5 @@ strip-verb-get-data-text = Strip
|
|||||||
|
|
||||||
strippable-bound-user-interface-stripping-menu-title = {$ownerName}'s inventory
|
strippable-bound-user-interface-stripping-menu-title = {$ownerName}'s inventory
|
||||||
strippable-bound-user-interface-stripping-menu-handcuffs-button = Restraints
|
strippable-bound-user-interface-stripping-menu-handcuffs-button = Restraints
|
||||||
|
strippable-bound-user-interface-stripping-menu-ensnare-button = Leg Restraints
|
||||||
strippable-bound-user-interface-stripping-menu-obfuscate = Occupied
|
strippable-bound-user-interface-stripping-menu-obfuscate = Occupied
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
- category: Internals
|
- category: Internals
|
||||||
- alertType: Fire
|
- alertType: Fire
|
||||||
- alertType: Handcuffed
|
- alertType: Handcuffed
|
||||||
|
- alertType: Ensnared
|
||||||
- category: Buckled
|
- category: Buckled
|
||||||
- alertType: Pulling
|
- alertType: Pulling
|
||||||
- category: Piloting
|
- category: Piloting
|
||||||
@@ -120,6 +121,13 @@
|
|||||||
name: "[color=yellow]Handcuffed[/color]"
|
name: "[color=yellow]Handcuffed[/color]"
|
||||||
description: "You're [color=yellow]handcuffed[/color] and can't use your hands. If anyone drags you, you won't be able to resist."
|
description: "You're [color=yellow]handcuffed[/color] and can't use your hands. If anyone drags you, you won't be able to resist."
|
||||||
|
|
||||||
|
- type: alert
|
||||||
|
id: Ensnared
|
||||||
|
onClick: !type:RemoveEnsnare { }
|
||||||
|
icons: [ /Textures/Interface/Alerts/ensnared.rsi/ensnared.png ]
|
||||||
|
name: "[color=yellow]Ensnared[/color]"
|
||||||
|
description: "You're [color=yellow]ensnared[/color] and is impairing your ability to move."
|
||||||
|
|
||||||
- type: alert
|
- type: alert
|
||||||
id: Buckled
|
id: Buckled
|
||||||
category: Buckled
|
category: Buckled
|
||||||
|
|||||||
@@ -280,9 +280,13 @@
|
|||||||
normalState: Generic_mob_burning
|
normalState: Generic_mob_burning
|
||||||
alternateState: Standing
|
alternateState: Standing
|
||||||
fireStackAlternateState: 3
|
fireStackAlternateState: 3
|
||||||
|
- type: EnsnareableVisualizer
|
||||||
- type: CombatMode
|
- type: CombatMode
|
||||||
- type: Climbing
|
- type: Climbing
|
||||||
- type: Cuffable
|
- type: Cuffable
|
||||||
|
- type: Ensnareable
|
||||||
|
sprite: Objects/Misc/ensnare.rsi
|
||||||
|
state: icon
|
||||||
- type: CharacterInfo
|
- type: CharacterInfo
|
||||||
- type: AnimationPlayer
|
- type: AnimationPlayer
|
||||||
- type: Buckle
|
- type: Buckle
|
||||||
|
|||||||
@@ -34,3 +34,11 @@
|
|||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Blunt: 5
|
Blunt: 5
|
||||||
|
- type: Ensnaring
|
||||||
|
freeTime: 2.0
|
||||||
|
breakoutTime: 3.5 #all bola should generally be fast to remove
|
||||||
|
walkSpeed: 0.7 #makeshift bola shouldn't slow too much
|
||||||
|
sprintSpeed: 0.7
|
||||||
|
canThrowTrigger: true
|
||||||
|
canMoveBreakout: true
|
||||||
|
|
||||||
|
|||||||
BIN
Resources/Textures/Interface/Alerts/ensnared.rsi/ensnared.png
Normal file
BIN
Resources/Textures/Interface/Alerts/ensnared.rsi/ensnared.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
14
Resources/Textures/Interface/Alerts/ensnared.rsi/meta.json
Normal file
14
Resources/Textures/Interface/Alerts/ensnared.rsi/meta.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Ensnared alert created by Hyenh. Bear Trap sprite taken from Citadel Station at https://github.com/Citadel-Station-13/Citadel-Station-13/commit/3cfea7eb92246d311de8b531347795bc76d6dab6",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "ensnared"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
Resources/Textures/Objects/Misc/ensnare.rsi/icon.png
Normal file
BIN
Resources/Textures/Objects/Misc/ensnare.rsi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 437 B |
15
Resources/Textures/Objects/Misc/ensnare.rsi/meta.json
Normal file
15
Resources/Textures/Objects/Misc/ensnare.rsi/meta.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from https://github.com/Citadel-Station-13/Citadel-Station-13/commit/2bf6d888bd89e614ec4d83fefa2130231e7ddc3d",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "icon",
|
||||||
|
"directions": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user