Skip to content

Commit

Permalink
Fix StrippableSystem blunders (#26166)
Browse files Browse the repository at this point in the history
* Fixes target hand check when no hands were needed.

* Adds missing CanStripX checks.

* Whitespace.

* Fixed bad math causing instant strips.
  • Loading branch information
Krunklehorn authored and DangerRevolution committed Mar 21, 2024
1 parent bd0df53 commit 830927d
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 10 deletions.
241 changes: 241 additions & 0 deletions Content.Server/Strip/StrippableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,88 @@ private void OnActivateInWorld(EntityUid uid, StrippableComponent component, Act
StartOpeningStripper(args.User, (uid, component));
}

public override void StartOpeningStripper(EntityUid user, Entity<StrippableComponent> strippable, bool openInCombat = false)
{
base.StartOpeningStripper(user, strippable, openInCombat);

if (TryComp<CombatModeComponent>(user, out var mode) && mode.IsInCombatMode && !openInCombat)
return;

if (TryComp<ActorComponent>(user, out var actor))
{
if (_userInterfaceSystem.SessionHasOpenUi(strippable, StrippingUiKey.Key, actor.PlayerSession))
return;
_userInterfaceSystem.TryOpen(strippable, StrippingUiKey.Key, actor.PlayerSession);
}
}

private void OnStripButtonPressed(Entity<StrippableComponent> strippable, ref StrippingSlotButtonPressed args)
{
if (args.Session.AttachedEntity is not { Valid: true } user ||
!TryComp<HandsComponent>(user, out var userHands))
return;

if (args.IsHand)
{
StripHand((user, userHands), (strippable.Owner, null), args.Slot, strippable);
return;
}

if (!TryComp<InventoryComponent>(strippable, out var inventory))
return;

var hasEnt = _inventorySystem.TryGetSlotEntity(strippable, args.Slot, out var held, inventory);

if (userHands.ActiveHandEntity != null && !hasEnt)
StartStripInsertInventory((user, userHands), strippable.Owner, userHands.ActiveHandEntity.Value, args.Slot);
else if (userHands.ActiveHandEntity == null && hasEnt)
StartStripRemoveInventory(user, strippable.Owner, held!.Value, args.Slot);
}

private void StripHand(
Entity<HandsComponent?> user,
Entity<HandsComponent?> target,
string handId,
StrippableComponent? targetStrippable)
{
if (!Resolve(user, ref user.Comp) ||
!Resolve(target, ref target.Comp) ||
!Resolve(target, ref targetStrippable))
return;

if (!_handsSystem.TryGetHand(target.Owner, handId, out var handSlot))
return;

// Is the target a handcuff?
if (TryComp<VirtualItemComponent>(handSlot.HeldEntity, out var virtualItem) &&
TryComp<CuffableComponent>(target.Owner, out var cuffable) &&
_cuffableSystem.GetAllCuffs(cuffable).Contains(virtualItem.BlockingEntity))
{
_cuffableSystem.TryUncuff(target.Owner, user, virtualItem.BlockingEntity, cuffable);
return;
}

if (user.Comp.ActiveHandEntity != null && handSlot.HeldEntity == null)
StartStripInsertHand(user, target, user.Comp.ActiveHandEntity.Value, handId, targetStrippable);
else if (user.Comp.ActiveHandEntity == null && handSlot.HeldEntity != null)
StartStripRemoveHand(user, target, handSlot.HeldEntity.Value, handId, targetStrippable);
}

private void OnStripEnsnareMessage(EntityUid uid, EnsnareableComponent component, StrippingEnsnareButtonPressed args)
{
if (args.Session.AttachedEntity is not { Valid: true } user)
return;

foreach (var entity in component.Container.ContainedEntities)
{
if (!TryComp<EnsnaringComponent>(entity, out var ensnaring))
continue;

_ensnaringSystem.TryFree(uid, user, entity, ensnaring);
return;
}
}

/// <summary>
/// Places item in user's active hand to an inventory slot.
/// </summary>
Expand Down Expand Up @@ -464,11 +546,170 @@ bool Check()
if (result != DoAfterStatus.Finished)
return;

<<<<<<< HEAD
_handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: hands);
_handsSystem.PickupOrDrop(user, item, animateUser: !ev.Stealth, animate: !ev.Stealth, handsComp: userHands);
// hand update will trigger strippable update
_adminLogger.Add(LogType.Stripping, LogImpact.Medium,
$"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
=======
if (!CanStripInsertHand(user, target, held, handName))
return;

_handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: user.Comp);
_handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: stealth, animate: stealth, handsComp: target.Comp);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");

// Hand update will trigger strippable update.
}

/// <summary>
/// Checks whether the item is in the target's hand and whether it can be dropped.
/// </summary>
private bool CanStripRemoveHand(
EntityUid user,
Entity<HandsComponent?> target,
EntityUid item,
string handName)
{
if (!Resolve(target, ref target.Comp))
return false;

if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp))
{
_popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", target)), user);
return false;
}

if (HasComp<VirtualItemComponent>(handSlot.HeldEntity))
return false;

if (handSlot.HeldEntity == null)
return false;

if (handSlot.HeldEntity != item)
return false;

if (!_handsSystem.CanDropHeld(target, handSlot, false))
{
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", target)), user);
return false;
}

return true;
}

/// <summary>
/// Begins a DoAfter to remove the item from the target's hand and insert it in the user's active hand.
/// </summary>
private void StartStripRemoveHand(
Entity<HandsComponent?> user,
Entity<HandsComponent?> target,
EntityUid item,
string handName,
StrippableComponent? targetStrippable = null)
{
if (!Resolve(user, ref user.Comp) ||
!Resolve(target, ref target.Comp) ||
!Resolve(target, ref targetStrippable))
return;

if (!CanStripRemoveHand(user, target, item, handName))
return;

var (time, stealth) = GetStripTimeModifiers(user, target, targetStrippable.HandStripDelay);

if (!stealth)
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", item)), target, target);

var prefix = stealth ? "stealthily " : "";
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");

var doAfterArgs = new DoAfterArgs(EntityManager, user, time, new StrippableDoAfterEvent(false, false, handName), user, target, item)
{
Hidden = stealth,
AttemptFrequency = AttemptFrequency.EveryTick,
BreakOnDamage = true,
BreakOnTargetMove = true,
BreakOnUserMove = true,
NeedHand = true,
BreakOnHandChange = false, // Allow simultaneously removing multiple items.
DuplicateCondition = DuplicateConditions.SameTool
};

_doAfterSystem.TryStartDoAfter(doAfterArgs);
}

/// <summary>
/// Takes the item from the target's hand and inserts it in the user's active hand.
/// </summary>
private void StripRemoveHand(
Entity<HandsComponent?> user,
Entity<HandsComponent?> target,
EntityUid item,
string handName,
bool stealth)
{
if (!Resolve(user, ref user.Comp) ||
!Resolve(target, ref target.Comp))
return;

if (!CanStripRemoveHand(user, target, item, handName))
return;

_handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: target.Comp);
_handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: stealth, handsComp: user.Comp);
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");

// Hand update will trigger strippable update.
}

private void OnStrippableDoAfterRunning(Entity<HandsComponent> entity, ref DoAfterAttemptEvent<StrippableDoAfterEvent> ev)
{
var args = ev.DoAfter.Args;

DebugTools.Assert(entity.Owner == args.User);
DebugTools.Assert(args.Target != null);
DebugTools.Assert(args.Used != null);
DebugTools.Assert(ev.Event.SlotOrHandName != null);

if (ev.Event.InventoryOrHand)
{
if ( ev.Event.InsertOrRemove && !CanStripInsertInventory((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
!ev.Event.InsertOrRemove && !CanStripRemoveInventory(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
ev.Cancel();
}
else
{
if ( ev.Event.InsertOrRemove && !CanStripInsertHand((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
!ev.Event.InsertOrRemove && !CanStripRemoveHand(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
ev.Cancel();
}
}

private void OnStrippableDoAfterFinished(Entity<HandsComponent> entity, ref StrippableDoAfterEvent ev)
{
if (ev.Cancelled)
return;

DebugTools.Assert(entity.Owner == ev.User);
DebugTools.Assert(ev.Target != null);
DebugTools.Assert(ev.Used != null);
DebugTools.Assert(ev.SlotOrHandName != null);

if (ev.InventoryOrHand)
{
if (ev.InsertOrRemove)
StripInsertInventory((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName);
else StripRemoveInventory(entity.Owner, ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden);
}
else
{
if (ev.InsertOrRemove)
StripInsertHand((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden);
else StripRemoveHand((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden);
}
>>>>>>> 8ecb78ee5a (Fix StrippableSystem blunders (#26166))
}
}
}
14 changes: 4 additions & 10 deletions Content.Shared/Strip/Components/StrippableComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,13 @@ public StrippingSlotButtonPressed(string slot, bool isHand)
[NetSerializable, Serializable]
public sealed class StrippingEnsnareButtonPressed : BoundUserInterfaceMessage
{
public StrippingEnsnareButtonPressed()
{
}
}

public abstract class BaseBeforeStripEvent : EntityEventArgs, IInventoryRelayEvent
{
public readonly float InitialTime;
public float Time => MathF.Max(InitialTime * Multiplier + Additive, 0f);
public float Additive = 0;
public readonly TimeSpan InitialTime = initialTime;
public float Multiplier = 1f;
public TimeSpan Additive = TimeSpan.Zero;
public bool Stealth;

public TimeSpan Time => TimeSpan.FromSeconds(MathF.Max(InitialTime.Seconds * Multiplier + Additive.Seconds, 0f));

public SlotFlags TargetSlots { get; } = SlotFlags.GLOVES;

public BaseBeforeStripEvent(float initialTime, bool stealth = false)
Expand Down

0 comments on commit 830927d

Please sign in to comment.