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
// 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)
{
Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Null);
Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Not.Null);
}
buckleSystem.Unbuckle(human, human);

View File

@ -7,6 +7,7 @@ using Content.Shared.Body.Part;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Movement.Components;
using Content.Shared.Standing;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@ -382,13 +383,25 @@ public partial class SharedBodySystem
if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
return;
if (legEnt.Comp.PartType == BodyPartType.Leg)
{
bodyEnt.Comp.LegEntities.Remove(legEnt);
UpdateMovementSpeed(bodyEnt);
Dirty(bodyEnt, bodyEnt.Comp);
Standing.Down(bodyEnt); // Shitmed Change
}
if (legEnt.Comp.PartType != BodyPartType.Leg)
return;
bodyEnt.Comp.LegEntities.Remove(legEnt);
UpdateMovementSpeed(bodyEnt);
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

View File

@ -59,6 +59,13 @@ public partial class MobStateSystem
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)
{
switch (ent.Comp.CurrentState)
@ -103,25 +110,35 @@ public partial class MobStateSystem
switch (state)
{
case MobState.Alive:
{
if (!TryComp(target, out BuckleComponent? buckle)
|| !buckle.Buckled) // DeltaV - Stop buckled mobs from standing up.
_standing.Stand(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Alive);
break;
}
case MobState.Critical:
_standing.Down(target);
{
Down(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Critical);
break;
}
case MobState.Dead:
{
EnsureComp<CollisionWakeComponent>(target);
_standing.Down(target);
Down(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Dead);
break;
}
case MobState.Invalid:
{
//unused;
break;
}
default:
{
throw new NotImplementedException();
}
}
}

View File

@ -4,6 +4,7 @@ using Content.Shared.Database;
using Content.Shared.Inventory;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Standing;
using Content.Shared.StatusEffectNew;
using Content.Shared.StepTrigger.Systems;
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.
if (!knockedDown)
{
var evDropHands = new DropHandItemsEvent();
RaiseLocalEvent(uid, ref evDropHands);
// Status effects should handle a TimeSpan of 0 properly...
_stun.TryUpdateStunDuration(other, component.SlipData.StunTime);

View File

@ -82,7 +82,10 @@ public sealed class StandingStateSystem : EntitySystem
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,
AppearanceComponent? appearance = null,
HandsComponent? hands = null)
@ -96,26 +99,15 @@ public sealed class StandingStateSystem : EntitySystem
if (!standingState.Standing)
return true;
// This is just to avoid most callers doing this manually saving boilerplate
// 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)
if (!force)
{
var ev = new DropHandItemsEvent();
RaiseLocalEvent(uid, ref ev, false);
var msg = new DownAttemptEvent();
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;
Dirty(uid, standingState);
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
RaiseLocalEvent(uid, ref ev);
var evDropHands = new DropHandItemsEvent();
RaiseLocalEvent(uid, ref evDropHands);
var timeForLogs = duration.HasValue
? duration.Value.Seconds.ToString()
: "Infinite";