Always raise MobStateChanged on client (#45103)

* lets try this

* sub attribute
This commit is contained in:
ScarKy0 2026-08-03 03:44:01 +02:00 • committed by GitHub
parent 9c0e6ba80f
commit 1951b00875
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 54 additions and 41 deletions

View file

@ -2,31 +2,37 @@ using Content.Shared.Damage.Components;
using Content.Shared.Mobs.Systems;
using Robust.Shared.GameStates;
namespace Content.Shared.Mobs.Components
namespace Content.Shared.Mobs.Components;
/// <summary>
/// When attached to an <see cref="DamageableComponent"/>,
/// this component will handle critical and death behaviors for mobs.
/// Additionally, it handles sending effects to clients
/// (such as blur effect for unconsciousness) and managing the health HUD.
/// </summary>
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState(true)]
[Access(typeof(MobStateSystem), typeof(MobThresholdSystem))]
public sealed partial class MobStateComponent : Component
{
/// <summary>
/// When attached to an <see cref="DamageableComponent"/>,
/// this component will handle critical and death behaviors for mobs.
/// Additionally, it handles sending effects to clients
/// (such as blur effect for unconsciousness) and managing the health HUD.
/// The current mob state the entity is in.
/// </summary>
[RegisterComponent]
[NetworkedComponent]
[AutoGenerateComponentState]
[Access(typeof(MobStateSystem), typeof(MobThresholdSystem))]
public sealed partial class MobStateComponent : Component
{
//default mobstate is always the lowest state level
[AutoNetworkedField, ViewVariables]
public MobState CurrentState { get; set; } = MobState.Alive;
[DataField, AutoNetworkedField]
public MobState CurrentState = MobState.Alive; //default mobstate is always the lowest state level
[DataField]
[AutoNetworkedField]
public HashSet<MobState> AllowedStates = new()
{
MobState.Alive,
MobState.Critical,
MobState.Dead
};
}
/// <summary>
/// The last state that was received by the client
/// </summary>
[ViewVariables]
public MobState LastReceivedState = MobState.Alive;
[DataField]
[AutoNetworkedField]
public HashSet<MobState> AllowedStates = new()
{
MobState.Alive,
MobState.Critical,
MobState.Dead
};
}

View file

@ -33,7 +33,7 @@ public partial class MobStateSystem
if (!_mobStateQuery.Resolve(entity, ref component))
return;
var ev = new UpdateMobStateEvent {Target = entity, Component = component, Origin = origin};
var ev = new UpdateMobStateEvent { Target = entity, Component = component, Origin = origin };
RaiseLocalEvent(entity, ref ev);
ChangeState(entity, component, ev.State, origin: origin);
}

View file

@ -28,34 +28,20 @@ public partial class MobStateSystem
//General purpose event subscriptions. If you can avoid it register these events inside their own systems
private void SubscribeEvents()
{
SubscribeLocalEvent<MobStateComponent, BeforeGettingStrippedEvent>(OnGettingStripped);
SubscribeLocalEvent<MobStateComponent, ChangeDirectionAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, UseAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, AttackAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, ConsciousAttemptEvent>(CheckConcious);
SubscribeLocalEvent<MobStateComponent, ThrowAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, SpeakAttemptEvent>(OnSpeakAttempt);
SubscribeLocalEvent<MobStateComponent, IsEquippingAttemptEvent>(OnEquipAttempt);
SubscribeLocalEvent<MobStateComponent, EmoteAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, IsUnequippingAttemptEvent>(OnUnequipAttempt);
SubscribeLocalEvent<MobStateComponent, DropAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, PickupAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, StartPullAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, UpdateCanMoveEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, StandAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, PointAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, TryingToSleepEvent>(OnSleepAttempt);
SubscribeLocalEvent<MobStateComponent, CombatModeShouldHandInteractEvent>(OnCombatModeShouldHandInteract);
SubscribeLocalEvent<MobStateComponent, AttemptPacifiedAttackEvent>(OnAttemptPacifiedAttack);
SubscribeLocalEvent<MobStateComponent, DamageModifyEvent>(OnDamageModify);
SubscribeLocalEvent<MobStateComponent, AttemptToolRefineEvent>(OnAttemptToolRefine);
SubscribeLocalEvent<MobStateComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
// Actions
SubscribeLocalEvent<ActionRequireMobStateComponent, ActionAttemptEvent>(OnMobStateActionAttempt);
}
[SubscribeLocalEvent]
private void OnUnbuckleAttempt(Entity<MobStateComponent> ent, ref UnbuckleAttemptEvent args)
{
// TODO is this necessary?
@ -71,6 +57,7 @@ public partial class MobStateSystem
RaiseLocalEvent(target, ref ev);
}
[SubscribeLocalEvent]
private void CheckConcious(Entity<MobStateComponent> ent, ref ConsciousAttemptEvent args)
{
switch (ent.Comp.CurrentState)
@ -145,6 +132,7 @@ public partial class MobStateSystem
}
}
[SubscribeLocalEvent]
private void OnAttemptToolRefine(Entity<MobStateComponent> ent, ref AttemptToolRefineEvent args)
{
if (!IsDead(ent, ent))
@ -155,12 +143,26 @@ public partial class MobStateSystem
#region Event Subscribers
[SubscribeLocalEvent]
private void OnAfterAutoHandleState(Entity<MobStateComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (ent.Comp.LastReceivedState == ent.Comp.CurrentState)
return;
var ev = new MobStateChangedEvent(ent, ent.Comp, ent.Comp.LastReceivedState, ent.Comp.CurrentState);
OnStateChanged(ent, ent.Comp, ent.Comp.LastReceivedState, ent.Comp.CurrentState);
RaiseLocalEvent(ent, ev, true);
ent.Comp.LastReceivedState = ent.Comp.CurrentState;
}
[SubscribeLocalEvent]
private void OnSleepAttempt(EntityUid target, MobStateComponent component, ref TryingToSleepEvent args)
{
if (IsDead(target, component))
args.Cancelled = true;
}
[SubscribeLocalEvent]
private void OnGettingStripped(EntityUid target, MobStateComponent component, BeforeGettingStrippedEvent args)
{
// Incapacitated or dead targets get stripped two or three times as fast. Makes stripping corpses less tedious.
@ -170,6 +172,7 @@ public partial class MobStateSystem
args.Multiplier /= 2;
}
[SubscribeLocalEvent]
private void OnSpeakAttempt(EntityUid uid, MobStateComponent component, SpeakAttemptEvent args)
{
if (HasComp<AllowNextCritSpeechComponent>(uid))
@ -192,6 +195,7 @@ public partial class MobStateSystem
}
}
[SubscribeLocalEvent]
private void OnEquipAttempt(EntityUid target, MobStateComponent component, IsEquippingAttemptEvent args)
{
// is this a self-equip, or are they being stripped?
@ -199,6 +203,7 @@ public partial class MobStateSystem
CheckAct(target, component, args);
}
[SubscribeLocalEvent]
private void OnUnequipAttempt(EntityUid target, MobStateComponent component, IsUnequippingAttemptEvent args)
{
// is this a self-equip, or are they being stripped?
@ -206,6 +211,7 @@ public partial class MobStateSystem
CheckAct(target, component, args);
}
[SubscribeLocalEvent]
private void OnCombatModeShouldHandInteract(EntityUid uid, MobStateComponent component, ref CombatModeShouldHandInteractEvent args)
{
// Disallow empty-hand-interacting in combat mode
@ -214,16 +220,19 @@ public partial class MobStateSystem
args.Cancelled = true;
}
[SubscribeLocalEvent]
private void OnAttemptPacifiedAttack(Entity<MobStateComponent> ent, ref AttemptPacifiedAttackEvent args)
{
args.Cancelled = true;
}
[SubscribeLocalEvent]
private void OnDamageModify(Entity<MobStateComponent> ent, ref DamageModifyEvent args)
{
args.Damage *= _damageable.UniversalMobDamageModifier;
}
[SubscribeLocalEvent]
private void OnMobStateActionAttempt(Entity<ActionRequireMobStateComponent> ent, ref ActionAttemptEvent args)
{
if (_mobStateQuery.TryComp(args.User, out var mobState) &&

View file

@ -18,13 +18,11 @@ public partial class MobStateSystem : EntitySystem
[Dependency] private IGameTiming _timing = default!;
[Dependency] private DamageableSystem _damageable = default!;
[Dependency] private SharedPopupSystem _popup = default!;
private ISawmill _sawmill = default!;
[Dependency] private EntityQuery<MobStateComponent> _mobStateQuery = default!;
public override void Initialize()
{
_sawmill = LogManager.GetSawmill("MobState");
base.Initialize();
SubscribeEvents();
}