Stripping ECS + window do_after (#8111)

* Stripping ECS + window do_after

* stuff

* workies

* Delays
This commit is contained in:
metalgearsloth
2022-05-13 14:59:57 +10:00
committed by GitHub
parent 4b8a323a74
commit eef4671f24
4 changed files with 393 additions and 372 deletions

View File

@@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.DoAfter; using Content.Server.DoAfter;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
@@ -8,21 +6,17 @@ using Content.Shared.Alert;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.ViewVariables;
namespace Content.Server.Cuffs.Components namespace Content.Server.Cuffs.Components
{ {
[ByRefEvent]
public readonly struct CuffedStateChangeEvent {}
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedCuffableComponent))] [ComponentReference(typeof(SharedCuffableComponent))]
public sealed class CuffableComponent : SharedCuffableComponent public sealed class CuffableComponent : SharedCuffableComponent
@@ -46,9 +40,6 @@ namespace Content.Server.Cuffs.Components
[ViewVariables(VVAccess.ReadOnly)] [ViewVariables(VVAccess.ReadOnly)]
public Container Container { get; set; } = default!; public Container Container { get; set; } = default!;
// TODO: Make a component message
public event Action? OnCuffedStateChanged;
private bool _uncuffing; private bool _uncuffing;
protected override void Initialize() protected override void Initialize()
@@ -115,17 +106,19 @@ namespace Content.Server.Cuffs.Components
CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? ownerHands) && ownerHands.Hands.Count() > CuffedHandCount; CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? ownerHands) && ownerHands.Hands.Count() > CuffedHandCount;
_sysMan.GetEntitySystem<ActionBlockerSystem>().UpdateCanMove(Owner); _sysMan.GetEntitySystem<ActionBlockerSystem>().UpdateCanMove(Owner);
OnCuffedStateChanged?.Invoke(); var ev = new CuffedStateChangeEvent();
_entMan.EventBus.RaiseLocalEvent(Owner, ref ev);
UpdateAlert(); UpdateAlert();
UpdateHeldItems(); UpdateHeldItems();
Dirty(); Dirty(_entMan);
return true; return true;
} }
public void CuffedStateChanged() public void CuffedStateChanged()
{ {
UpdateAlert(); UpdateAlert();
OnCuffedStateChanged?.Invoke(); var ev = new CuffedStateChangeEvent();
_entMan.EventBus.RaiseLocalEvent(Owner, ref ev);
} }
/// <summary> /// <summary>
@@ -270,9 +263,10 @@ namespace Content.Server.Cuffs.Components
CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? handsComponent) && handsComponent.SortedHands.Count() > CuffedHandCount; CanStillInteract = _entMan.TryGetComponent(Owner, out HandsComponent? handsComponent) && handsComponent.SortedHands.Count() > CuffedHandCount;
_sysMan.GetEntitySystem<ActionBlockerSystem>().UpdateCanMove(Owner); _sysMan.GetEntitySystem<ActionBlockerSystem>().UpdateCanMove(Owner);
OnCuffedStateChanged?.Invoke(); var ev = new CuffedStateChangeEvent();
_entMan.EventBus.RaiseLocalEvent(Owner, ref ev);
UpdateAlert(); UpdateAlert();
Dirty(); Dirty(_entMan);
if (CuffedHandCount == 0) if (CuffedHandCount == 0)
{ {

View File

@@ -1,18 +1,6 @@
using System.Threading; using System.Threading;
using Content.Server.Cuffs.Components;
using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Server.Inventory;
using Content.Server.UserInterface;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Popups;
using Content.Shared.Strip.Components; using Content.Shared.Strip.Components;
using Robust.Server.GameObjects;
using Robust.Server.Player;
namespace Content.Server.Strip namespace Content.Server.Strip
{ {
@@ -21,336 +9,20 @@ namespace Content.Server.Strip
[Friend(typeof(StrippableSystem))] [Friend(typeof(StrippableSystem))]
public sealed class StrippableComponent : SharedStrippableComponent public sealed class StrippableComponent : SharedStrippableComponent
{ {
[Dependency] private readonly IEntityManager _entities = default!; [ViewVariables]
[Dependency] private readonly IEntitySystemManager _sysMan = default!; [DataField("openDelay")]
private StrippableSystem _strippableSystem = default!; public float OpenDelay = 4f;
public const float StripDelay = 6f;
// TODO: This component needs localization.
[ViewVariables] [ViewVariables]
public BoundUserInterface? UserInterface => Owner.GetUIOrNull(StrippingUiKey.Key); [DataField("delay")]
public float StripDelay = 2f;
protected override void Initialize()
{
base.Initialize();
if (UserInterface != null)
{
UserInterface.OnReceiveMessage += HandleUserInterfaceMessage;
}
_strippableSystem = EntitySystem.Get<StrippableSystem>();
Owner.EnsureComponentWarn<ServerInventoryComponent>();
if(_entities.TryGetComponent<CuffableComponent>(Owner, out var cuffed))
cuffed.OnCuffedStateChanged += UpdateState;
}
protected override void Shutdown()
{
base.Shutdown();
if(_entities.TryGetComponent<CuffableComponent>(Owner, out var cuffed))
cuffed.OnCuffedStateChanged -= UpdateState;
}
private void UpdateState()
{
_strippableSystem.SendUpdate(Owner, this);
}
public override bool Drop(DragDropEvent args) public override bool Drop(DragDropEvent args)
{ {
if (!_entities.TryGetComponent(args.User, out ActorComponent? actor)) return false; IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<StrippableSystem>().StartOpeningStripper(args.User, this);
OpenUserInterface(actor.PlayerSession);
return true; return true;
} }
public void OpenUserInterface(IPlayerSession session) public Dictionary<EntityUid, CancellationTokenSource> CancelTokens = new();
{
UserInterface?.Open(session);
}
/// <summary>
/// Places item in user's active hand to an inventory slot.
/// </summary>
private async void PlaceActiveHandItemInInventory(EntityUid user, string slot)
{
var userHands = _entities.GetComponent<HandsComponent>(user);
var invSystem = _sysMan.GetEntitySystem<InventorySystem>();
var handSys = _sysMan.GetEntitySystem<SharedHandsSystem>();
bool Check()
{
if (userHands.ActiveHand?.HeldEntity is not EntityUid held)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
return false;
}
if (!handSys.CanDropHeld(user, userHands.ActiveHand))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
return false;
}
if (!invSystem.HasSlot(Owner, slot))
return false;
if (invSystem.TryGetSlotEntity(Owner, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied",("owner", Owner)));
return false;
}
if (!invSystem.CanEquip(user, Owner, held, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-equip-message",("owner", Owner)));
return false;
}
return true;
}
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
NeedHand = true,
};
var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (userHands.ActiveHand?.HeldEntity is EntityUid held
&& handSys.TryDrop(user, userHands.ActiveHand, handsComp: userHands))
{
invSystem.TryEquip(user, Owner, held, slot);
}
UpdateState();
}
/// <summary>
/// Places item in user's active hand in one of the entity's hands.
/// </summary>
private async void PlaceActiveHandItemInHands(EntityUid user, string handName)
{
var hands = _entities.GetComponent<HandsComponent>(Owner);
var userHands = _entities.GetComponent<HandsComponent>(user);
var sys = _sysMan.GetEntitySystem<SharedHandsSystem>();
bool Check()
{
if (userHands.ActiveHandEntity == null)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
return false;
}
if (!sys.CanDropHeld(user, userHands.ActiveHand!))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
return false;
}
if (!hands.Hands.TryGetValue(handName, out var hand)
|| !sys.CanPickupToHand(Owner, userHands.ActiveHandEntity.Value, hand, checkActionBlocker: false, hands))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message",("owner", Owner)));
return false;
}
return true;
}
var doAfterSystem = _sysMan.GetEntitySystem<DoAfterSystem>();
var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
NeedHand = true,
};
var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (userHands.ActiveHandEntity is not EntityUid held)
return;
sys.TryDrop(user, checkActionBlocker: false, handsComp: userHands);
sys.TryPickup(Owner, held, handName, checkActionBlocker: false, animateUser: true, handsComp: hands);
// hand update will trigger strippable update
}
/// <summary>
/// Takes an item from the inventory and places it in the user's active hand.
/// </summary>
private async void TakeItemFromInventory(EntityUid user, string slot)
{
var inventory = _entities.GetComponent<InventoryComponent>(Owner);
var userHands = _entities.GetComponent<HandsComponent>(user);
var invSystem = _sysMan.GetEntitySystem<InventorySystem>();
bool Check()
{
if (!invSystem.HasSlot(Owner, slot))
return false;
if (!invSystem.TryGetSlotEntity(Owner, slot, out var item))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message",("owner", Owner)));
return false;
}
if (!invSystem.CanUnequip(user, Owner, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-unequip-message",("owner", Owner)));
return false;
}
return true;
}
var doAfterSystem = _sysMan.GetEntitySystem<DoAfterSystem>();
var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
};
var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (invSystem.TryGetSlotEntity(Owner, slot, out var item) && invSystem.TryUnequip(user, Owner, slot))
{
// Raise a dropped event, so that things like gas tank internals properly deactivate when stripping
_entities.EventBus.RaiseLocalEvent(item.Value, new DroppedEvent(user));
_sysMan.GetEntitySystem<SharedHandsSystem>().PickupOrDrop(user, item.Value);
}
UpdateState();
}
/// <summary>
/// Takes an item from a hand and places it in the user's active hand.
/// </summary>
private async void TakeItemFromHands(EntityUid user, string handName)
{
var hands = _entities.GetComponent<HandsComponent>(Owner);
var userHands = _entities.GetComponent<HandsComponent>(user);
var handSys = _sysMan.GetEntitySystem<SharedHandsSystem>();
bool Check()
{
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity == null)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message",("owner", Owner)));
return false;
}
if (_entities.HasComponent<HandVirtualItemComponent>(hand.HeldEntity))
return false;
if (!handSys.CanDropHeld(Owner, hand, false))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message",("owner", Owner)));
return false;
}
return true;
}
var doAfterSystem = _sysMan.GetEntitySystem<DoAfterSystem>();
var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
};
var result = await doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity is not EntityUid held)
return;
handSys.TryDrop(Owner, hand, checkActionBlocker: false, handsComp: hands);
handSys.PickupOrDrop(user, held, handsComp: userHands);
// hand update will trigger strippable update
}
private void HandleUserInterfaceMessage(ServerBoundUserInterfaceMessage obj)
{
if (obj.Session.AttachedEntity is not {Valid: true} user ||
!_entities.TryGetComponent(user, out HandsComponent? userHands))
return;
var placingItem = userHands.ActiveHandEntity != null;
switch (obj.Message)
{
case StrippingInventoryButtonPressed inventoryMessage:
if (_entities.TryGetComponent<InventoryComponent?>(Owner, out var inventory))
{
if (_sysMan.GetEntitySystem<InventorySystem>().TryGetSlotEntity(Owner, inventoryMessage.Slot, out _, inventory))
placingItem = false;
if (placingItem)
PlaceActiveHandItemInInventory(user, inventoryMessage.Slot);
else
TakeItemFromInventory(user, inventoryMessage.Slot);
}
break;
case StrippingHandButtonPressed handMessage:
if (_entities.TryGetComponent<HandsComponent?>(Owner, out var hands))
{
if (hands.Hands.TryGetValue(handMessage.Hand, out var hand) && !hand.IsEmpty)
placingItem = false;
if (placingItem)
PlaceActiveHandItemInHands(user, handMessage.Hand);
else
TakeItemFromHands(user, handMessage.Hand);
}
break;
case StrippingHandcuffButtonPressed handcuffMessage:
if (_entities.TryGetComponent<CuffableComponent?>(Owner, out var cuffed))
{
foreach (var entity in cuffed.StoredEntities)
{
if (entity == handcuffMessage.Handcuff)
{
cuffed.TryUncuff(user, entity);
return;
}
}
}
break;
}
}
} }
} }

View File

@@ -1,21 +1,29 @@
using System.Collections.Generic; using System.Threading;
using Content.Server.Cuffs.Components; using Content.Server.Cuffs.Components;
using Content.Server.DoAfter;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Inventory;
using Content.Server.UserInterface;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Inventory.Events; using Content.Shared.Inventory.Events;
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.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Server.Strip namespace Content.Server.Strip
{ {
public sealed class StrippableSystem : EntitySystem public sealed class StrippableSystem : EntitySystem
{ {
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
// TODO: ECS popups. Not all of these have ECS equivalents yet.
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -24,13 +32,125 @@ namespace Content.Server.Strip
SubscribeLocalEvent<StrippableComponent, DidEquipEvent>(OnDidEquip); SubscribeLocalEvent<StrippableComponent, DidEquipEvent>(OnDidEquip);
SubscribeLocalEvent<StrippableComponent, DidUnequipEvent>(OnDidUnequip); SubscribeLocalEvent<StrippableComponent, DidUnequipEvent>(OnDidUnequip);
SubscribeLocalEvent<StrippableComponent, ComponentInit>(OnCompInit); SubscribeLocalEvent<StrippableComponent, ComponentInit>(OnCompInit);
SubscribeLocalEvent<StrippableComponent, CuffedStateChangeEvent>(OnCuffStateChange);
// BUI
SubscribeLocalEvent<StrippableComponent, StrippingInventoryButtonPressed>(OnStripInvButtonMessage);
SubscribeLocalEvent<StrippableComponent, StrippingHandButtonPressed>(OnStripHandMessage);
SubscribeLocalEvent<StrippableComponent, StrippingHandcuffButtonPressed>(OnStripHandcuffMessage);
SubscribeLocalEvent<StrippableComponent, OpenStrippingCompleteEvent>(OnOpenStripComplete);
SubscribeLocalEvent<StrippableComponent, OpenStrippingCancelledEvent>(OnOpenStripCancelled);
}
private void OnOpenStripCancelled(EntityUid uid, StrippableComponent component, OpenStrippingCancelledEvent args)
{
component.CancelTokens.Remove(args.User);
}
private void OnOpenStripComplete(EntityUid uid, StrippableComponent component, OpenStrippingCompleteEvent args)
{
component.CancelTokens.Remove(args.User);
if (!TryComp<ActorComponent>(args.User, out var actor)) return;
uid.GetUIOrNull(StrippingUiKey.Key)?.Open(actor.PlayerSession);
}
private void OnStripHandcuffMessage(EntityUid uid, StrippableComponent component, StrippingHandcuffButtonPressed args)
{
if (args.Session.AttachedEntity is not {Valid: true} user)
return;
if (TryComp<CuffableComponent>(component.Owner, out var cuffed))
{
foreach (var entity in cuffed.StoredEntities)
{
if (entity != args.Handcuff) continue;
cuffed.TryUncuff(user, entity);
return;
}
}
}
private void OnStripHandMessage(EntityUid uid, StrippableComponent component, StrippingHandButtonPressed args)
{
if (args.Session.AttachedEntity is not {Valid: true} user ||
!TryComp<HandsComponent>(user, out var userHands))
return;
var placingItem = userHands.ActiveHandEntity != null;
if (TryComp<HandsComponent>(component.Owner, out var hands))
{
if (hands.Hands.TryGetValue(args.Hand, out var hand) && !hand.IsEmpty)
placingItem = false;
if (placingItem)
PlaceActiveHandItemInHands(user, args.Hand, component);
else
TakeItemFromHands(user, args.Hand, component);
}
}
private void OnStripInvButtonMessage(EntityUid uid, StrippableComponent component, StrippingInventoryButtonPressed args)
{
if (args.Session.AttachedEntity is not {Valid: true} user ||
!TryComp<HandsComponent>(user, out var userHands))
return;
var placingItem = userHands.ActiveHandEntity != null;
if (TryComp<InventoryComponent>(component.Owner, out var inventory))
{
if (_inventorySystem.TryGetSlotEntity(component.Owner, args.Slot, out _, inventory))
placingItem = false;
if (placingItem)
PlaceActiveHandItemInInventory(user, args.Slot, component);
else
TakeItemFromInventory(user, args.Slot, component);
}
}
public void StartOpeningStripper(EntityUid user, StrippableComponent component)
{
if (component.CancelTokens.ContainsKey(user)) return;
if (TryComp<ActorComponent>(user, out var actor))
{
if (component.Owner.GetUIOrNull(StrippingUiKey.Key)?.SessionHasOpen(actor.PlayerSession) == true)
return;
}
var token = new CancellationTokenSource();
var doAfterArgs = new DoAfterEventArgs(user, component.OpenDelay, token.Token, component.Owner)
{
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
NeedHand = true,
TargetCancelledEvent = new OpenStrippingCancelledEvent(user),
TargetFinishedEvent = new OpenStrippingCompleteEvent(user),
};
component.CancelTokens[user] = token;
_doAfterSystem.DoAfter(doAfterArgs);
} }
private void OnCompInit(EntityUid uid, StrippableComponent component, ComponentInit args) private void OnCompInit(EntityUid uid, StrippableComponent component, ComponentInit args)
{ {
EnsureComp<ServerInventoryComponent>(uid);
SendUpdate(uid, component); SendUpdate(uid, component);
} }
private void OnCuffStateChange(EntityUid uid, StrippableComponent component, ref CuffedStateChangeEvent args)
{
UpdateState(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);
@@ -43,7 +163,9 @@ namespace Content.Server.Strip
public void SendUpdate(EntityUid uid, StrippableComponent? strippableComponent = null) public void SendUpdate(EntityUid uid, StrippableComponent? strippableComponent = null)
{ {
if (!Resolve(uid, ref strippableComponent, false) || strippableComponent.UserInterface == null) var bui = uid.GetUIOrNull(StrippingUiKey.Key);
if (!Resolve(uid, ref strippableComponent, false) || bui == null)
{ {
return; return;
} }
@@ -88,7 +210,7 @@ namespace Content.Server.Strip
} }
} }
strippableComponent.UserInterface.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs)); bui.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs));
} }
private void AddStripVerb(EntityUid uid, StrippableComponent component, GetVerbsEvent<Verb> args) private void AddStripVerb(EntityUid uid, StrippableComponent component, GetVerbsEvent<Verb> args)
@@ -99,11 +221,248 @@ namespace Content.Server.Strip
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;
Verb verb = new(); Verb verb = new()
verb.Text = Loc.GetString("strip-verb-get-data-text"); {
verb.IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png"; Text = Loc.GetString("strip-verb-get-data-text"),
verb.Act = () => component.OpenUserInterface(actor.PlayerSession); IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png",
Act = () => StartOpeningStripper(args.User, component),
};
args.Verbs.Add(verb); args.Verbs.Add(verb);
} }
private void UpdateState(EntityUid uid, StrippableComponent component)
{
SendUpdate(uid, component);
}
/// <summary>
/// Places item in user's active hand to an inventory slot.
/// </summary>
private async void PlaceActiveHandItemInInventory(EntityUid user, string slot, StrippableComponent component)
{
var userHands = Comp<HandsComponent>(user);
bool Check()
{
if (userHands.ActiveHand?.HeldEntity is not { } held)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
return false;
}
if (!_handsSystem.CanDropHeld(user, userHands.ActiveHand))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
return false;
}
if (!_inventorySystem.HasSlot(component.Owner, slot))
return false;
if (_inventorySystem.TryGetSlotEntity(component.Owner, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied",("uid", component.Owner)));
return false;
}
if (!_inventorySystem.CanEquip(user, component.Owner, held, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-equip-message",("uid", component.Owner)));
return false;
}
return true;
}
var doAfterArgs = new DoAfterEventArgs(user, component.StripDelay, CancellationToken.None, component.Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
NeedHand = true,
};
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (userHands.ActiveHand?.HeldEntity is { } held
&& _handsSystem.TryDrop(user, userHands.ActiveHand, handsComp: userHands))
{
_inventorySystem.TryEquip(user, component.Owner, held, slot);
}
UpdateState(component.Owner, component);
}
/// <summary>
/// Places item in user's active hand in one of the entity's hands.
/// </summary>
private async void PlaceActiveHandItemInHands(EntityUid user, string handName, StrippableComponent component)
{
var hands = Comp<HandsComponent>(component.Owner);
var userHands = Comp<HandsComponent>(user);
bool Check()
{
if (userHands.ActiveHandEntity == null)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
return false;
}
if (!_handsSystem.CanDropHeld(user, userHands.ActiveHand!))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
return false;
}
if (!hands.Hands.TryGetValue(handName, out var hand)
|| !_handsSystem.CanPickupToHand(component.Owner, userHands.ActiveHandEntity.Value, hand, checkActionBlocker: false, hands))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message",("uid", component.Owner)));
return false;
}
return true;
}
var doAfterArgs = new DoAfterEventArgs(user, component.StripDelay, CancellationToken.None, component.Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
NeedHand = true,
};
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (userHands.ActiveHandEntity is not { } held)
return;
_handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: userHands);
_handsSystem.TryPickup(component.Owner, held, handName, checkActionBlocker: false, animateUser: true, handsComp: hands);
// hand update will trigger strippable update
}
/// <summary>
/// Takes an item from the inventory and places it in the user's active hand.
/// </summary>
private async void TakeItemFromInventory(EntityUid user, string slot, StrippableComponent component)
{
bool Check()
{
if (!_inventorySystem.HasSlot(component.Owner, slot))
return false;
if (!_inventorySystem.TryGetSlotEntity(component.Owner, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("uid", component.Owner)));
return false;
}
if (!_inventorySystem.CanUnequip(user, component.Owner, slot, out _))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-unequip-message", ("uid", component.Owner)));
return false;
}
return true;
}
var doAfterArgs = new DoAfterEventArgs(user, component.StripDelay, CancellationToken.None, component.Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
};
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (_inventorySystem.TryGetSlotEntity(component.Owner, slot, out var item) && _inventorySystem.TryUnequip(user, component.Owner, slot))
{
// Raise a dropped event, so that things like gas tank internals properly deactivate when stripping
RaiseLocalEvent(item.Value, new DroppedEvent(user));
_handsSystem.PickupOrDrop(user, item.Value);
}
UpdateState(component.Owner, component);
}
/// <summary>
/// Takes an item from a hand and places it in the user's active hand.
/// </summary>
private async void TakeItemFromHands(EntityUid user, string handName, StrippableComponent component)
{
var hands = Comp<HandsComponent>(component.Owner);
var userHands = Comp<HandsComponent>(user);
bool Check()
{
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity == null)
{
user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message",("uid", component.Owner)));
return false;
}
if (HasComp<HandVirtualItemComponent>(hand.HeldEntity))
return false;
if (!_handsSystem.CanDropHeld(component.Owner, hand, false))
{
user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message",("uid", component.Owner)));
return false;
}
return true;
}
var doAfterArgs = new DoAfterEventArgs(user, component.StripDelay, CancellationToken.None, component.Owner)
{
ExtraCheck = Check,
BreakOnStun = true,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
};
var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);
if (result != DoAfterStatus.Finished) return;
if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity is not { } held)
return;
_handsSystem.TryDrop(component.Owner, hand, checkActionBlocker: false, handsComp: hands);
_handsSystem.PickupOrDrop(user, held, handsComp: userHands);
// hand update will trigger strippable update
}
private sealed class OpenStrippingCompleteEvent
{
public readonly EntityUid User;
public OpenStrippingCompleteEvent(EntityUid user)
{
User = user;
}
}
private sealed class OpenStrippingCancelledEvent
{
public readonly EntityUid User;
public OpenStrippingCancelledEvent(EntityUid user)
{
User = user;
}
}
} }
} }

View File

@@ -1,10 +1,6 @@
using System; using Content.Shared.ActionBlocker;
using System.Collections.Generic;
using Content.Shared.ActionBlocker;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Strip.Components namespace Content.Shared.Strip.Components
@@ -26,13 +22,13 @@ namespace Content.Shared.Strip.Components
} }
public abstract bool Drop(DragDropEvent args); public abstract bool Drop(DragDropEvent args);
}
[NetSerializable, Serializable] [NetSerializable, Serializable]
public enum StrippingUiKey public enum StrippingUiKey : byte
{ {
Key, Key,
} }
}
[NetSerializable, Serializable] [NetSerializable, Serializable]
public sealed class StrippingInventoryButtonPressed : BoundUserInterfaceMessage public sealed class StrippingInventoryButtonPressed : BoundUserInterfaceMessage