Decouple standing state and drop item behavior (#41566)

* removed DropHandsItemEvent from standing state system, added DropHandsItemEvent calls in slippery system and shared stun system

* added DropHandItemsEvent calls in mobstate system subscribers

* Added DropHandItemsEvent call in SharedBodySystem.Parts

* Add a standingState check in RemoveLeg so removing the legs of a downed person won't cause them to drop items

* new method for downing + dropping held items in mobstatesystem

* mild cleanup

* Bugfix

* update BuckleTest to reflect new item dropping behavior when removing legs

* light cleanup

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
This commit is contained in:
alexalexmax 2025-12-16 10:32:56 -08:00 committed by BarryNorfolk
parent b4903348df
commit 9991a5aefc
6 changed files with 58 additions and 29 deletions

View File

@ -317,10 +317,10 @@ namespace Content.IntegrationTests.Tests.Buckle
// he's not supposed to be buckled with the new falling down system // he's not supposed to be buckled with the new falling down system
// do i just did this :trollface: // do i just did this :trollface:
// Now with no item in any hand // Still with items in hand
foreach (var hand in hands.Hands.Keys) foreach (var hand in hands.Hands.Keys)
{ {
Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Null); Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Not.Null);
} }
buckleSystem.Unbuckle(human, human); buckleSystem.Unbuckle(human, human);

View File

@ -7,6 +7,7 @@ using Content.Shared.Body.Part;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Standing;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@ -382,13 +383,25 @@ public partial class SharedBodySystem
if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false)) if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
return; return;
if (legEnt.Comp.PartType == BodyPartType.Leg) if (legEnt.Comp.PartType != BodyPartType.Leg)
{ return;
bodyEnt.Comp.LegEntities.Remove(legEnt);
UpdateMovementSpeed(bodyEnt); bodyEnt.Comp.LegEntities.Remove(legEnt);
Dirty(bodyEnt, bodyEnt.Comp); UpdateMovementSpeed(bodyEnt);
Standing.Down(bodyEnt); // Shitmed Change Dirty(bodyEnt, bodyEnt.Comp);
}
// Begin DeltaV Additions - Always down on leg removal
if (!TryComp<StandingStateComponent>(bodyEnt, out var standingState)
|| !standingState.Standing
|| !Standing.Down(bodyEnt, standingState: standingState))
return;
if (bodyEnt.Comp.LegEntities.Count != 0)
return;
// End DeltaV Additions - Always down on leg removal
var ev = new DropHandItemsEvent();
RaiseLocalEvent(bodyEnt, ref ev);
} }
// Shitmed Change: made virtual, bleeding damage is done on server // Shitmed Change: made virtual, bleeding damage is done on server

View File

@ -59,6 +59,13 @@ public partial class MobStateSystem
args.Cancelled = true; args.Cancelled = true;
} }
private void Down(EntityUid target)
{
_standing.Down(target);
var ev = new DropHandItemsEvent();
RaiseLocalEvent(target, ref ev);
}
private void CheckConcious(Entity<MobStateComponent> ent, ref ConsciousAttemptEvent args) private void CheckConcious(Entity<MobStateComponent> ent, ref ConsciousAttemptEvent args)
{ {
switch (ent.Comp.CurrentState) switch (ent.Comp.CurrentState)
@ -103,25 +110,35 @@ public partial class MobStateSystem
switch (state) switch (state)
{ {
case MobState.Alive: case MobState.Alive:
{
if (!TryComp(target, out BuckleComponent? buckle) if (!TryComp(target, out BuckleComponent? buckle)
|| !buckle.Buckled) // DeltaV - Stop buckled mobs from standing up. || !buckle.Buckled) // DeltaV - Stop buckled mobs from standing up.
_standing.Stand(target); _standing.Stand(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Alive); _appearance.SetData(target, MobStateVisuals.State, MobState.Alive);
break; break;
}
case MobState.Critical: case MobState.Critical:
_standing.Down(target); {
Down(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Critical); _appearance.SetData(target, MobStateVisuals.State, MobState.Critical);
break; break;
}
case MobState.Dead: case MobState.Dead:
{
EnsureComp<CollisionWakeComponent>(target); EnsureComp<CollisionWakeComponent>(target);
_standing.Down(target); Down(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Dead); _appearance.SetData(target, MobStateVisuals.State, MobState.Dead);
break; break;
}
case MobState.Invalid: case MobState.Invalid:
{
//unused; //unused;
break; break;
}
default: default:
{
throw new NotImplementedException(); throw new NotImplementedException();
}
} }
} }

View File

@ -4,6 +4,7 @@ using Content.Shared.Database;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.Standing;
using Content.Shared.StatusEffectNew; using Content.Shared.StatusEffectNew;
using Content.Shared.StepTrigger.Systems; using Content.Shared.StepTrigger.Systems;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
@ -131,6 +132,9 @@ public sealed class SlipperySystem : EntitySystem
// Preventing from playing the slip sound and stunning when you are already knocked down. // Preventing from playing the slip sound and stunning when you are already knocked down.
if (!knockedDown) if (!knockedDown)
{ {
var evDropHands = new DropHandItemsEvent();
RaiseLocalEvent(uid, ref evDropHands);
// Status effects should handle a TimeSpan of 0 properly... // Status effects should handle a TimeSpan of 0 properly...
_stun.TryUpdateStunDuration(other, component.SlipData.StunTime); _stun.TryUpdateStunDuration(other, component.SlipData.StunTime);

View File

@ -82,7 +82,10 @@ public sealed class StandingStateSystem : EntitySystem
return !entity.Comp.Standing; return !entity.Comp.Standing;
} }
public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true, public bool Down(EntityUid uid,
bool playSound = true,
bool dropHeldItems = true,
bool force = false,
StandingStateComponent? standingState = null, StandingStateComponent? standingState = null,
AppearanceComponent? appearance = null, AppearanceComponent? appearance = null,
HandsComponent? hands = null) HandsComponent? hands = null)
@ -96,26 +99,15 @@ public sealed class StandingStateSystem : EntitySystem
if (!standingState.Standing) if (!standingState.Standing)
return true; return true;
// This is just to avoid most callers doing this manually saving boilerplate if (!force)
// 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to.
// We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway
// and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent.
if (dropHeldItems && hands != null)
{ {
var ev = new DropHandItemsEvent(); var msg = new DownAttemptEvent();
RaiseLocalEvent(uid, ref ev, false); RaiseLocalEvent(uid, msg, false);
if (msg.Cancelled)
return false;
} }
// DeltaV - Unsure if this is needed after ripping out the old laying down system. Left it in in case.
//if (TryComp(uid, out BuckleComponent? buckle) && buckle.Buckled && !_buckle.TryUnbuckle(uid, uid, buckleComp: buckle))
// return false;
var msg = new DownAttemptEvent();
RaiseLocalEvent(uid, msg, false);
if (msg.Cancelled)
return false;
standingState.Standing = false; standingState.Standing = false;
Dirty(uid, standingState); Dirty(uid, standingState);
RaiseLocalEvent(uid, new DownedEvent(), false); RaiseLocalEvent(uid, new DownedEvent(), false);

View File

@ -151,6 +151,9 @@ public abstract partial class SharedStunSystem : EntitySystem
var ev = new StunnedEvent(); // todo: rename event or change how it is raised - this event is raised each time duration of stun was externally changed var ev = new StunnedEvent(); // todo: rename event or change how it is raised - this event is raised each time duration of stun was externally changed
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);
var evDropHands = new DropHandItemsEvent();
RaiseLocalEvent(uid, ref evDropHands);
var timeForLogs = duration.HasValue var timeForLogs = duration.HasValue
? duration.Value.Seconds.ToString() ? duration.Value.Seconds.ToString()
: "Infinite"; : "Infinite";