using Content.Shared.Bed.Sleep; using Content.Shared.Buckle.Components; using Content.Shared.CCVar; using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage.ForceSay; using Content.Shared.Emoting; using Content.Shared.Hands; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Mobs.Components; using Content.Shared.Movement.Events; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Events; using Content.Shared.Pointing; using Content.Shared.Pulling.Events; using Content.Shared.Speech; using Content.Shared.Standing; using Content.Shared.Strip.Components; using Content.Shared.Throwing; using Robust.Shared.Configuration; using Robust.Shared.Physics.Components; namespace Content.Shared.Mobs.Systems; public partial class MobStateSystem { [Dependency] private readonly IConfigurationManager _configurationManager = default!; //General purpose event subscriptions. If you can avoid it register these events inside their own systems private void SubscribeEvents() { SubscribeLocalEvent(OnGettingStripped); SubscribeLocalEvent(OnDirectionAttempt); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckConcious); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(OnSpeakAttempt); SubscribeLocalEvent(OnEquipAttempt); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(OnUnequipAttempt); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(OnMoveAttempt); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(OnSleepAttempt); SubscribeLocalEvent(OnCombatModeShouldHandInteract); SubscribeLocalEvent(OnAttemptPacifiedAttack); SubscribeLocalEvent(OnUnbuckleAttempt); SubscribeLocalEvent(OnPull); // WD EDIT } private void OnDirectionAttempt(Entity ent, ref ChangeDirectionAttemptEvent args) { if (ent.Comp.CurrentState is MobState.Alive || ent.Comp.CurrentState is MobState.Critical && ent.Comp.AllowMovementWhileCrit && _configurationManager.GetCVar(CCVars.AllowMovementWhileCrit) || ent.Comp.CurrentState is MobState.SoftCritical && ent.Comp.AllowMovementWhileSoftCrit || ent.Comp.CurrentState is MobState.Dead && ent.Comp.AllowMovementWhileDead) return; args.Cancel(); } private void OnMoveAttempt(Entity ent, ref UpdateCanMoveEvent args) { // WWDP edit; no breaking pulling while in crit if (EntityManager.TryGetComponent(ent, out PullableComponent? pullable) && pullable.BeingPulled) { if (ent.Comp.CurrentState is MobState.Critical && !ent.Comp.AllowBreakingPullingWhileCrit && !_configurationManager.GetCVar(CCVars.AllowBreakingPullingWhileCrit) || ent.Comp.CurrentState is MobState.SoftCritical && !ent.Comp.AllowBreakingPullingWhileSoftCrit || ent.Comp.CurrentState is MobState.Dead && !ent.Comp.AllowBreakingPullingWhileDead) { args.Cancel(); return; } } // WWDP edit end if (ent.Comp.CurrentState is MobState.Alive || ent.Comp.CurrentState is MobState.Critical && ent.Comp.AllowMovementWhileCrit && _configurationManager.GetCVar(CCVars.AllowMovementWhileCrit) || ent.Comp.CurrentState is MobState.SoftCritical && ent.Comp.AllowMovementWhileSoftCrit || ent.Comp.CurrentState is MobState.Dead && ent.Comp.AllowMovementWhileDead) return; args.Cancel(); } // WWDP edit; no breaking pulling while in crit private void OnPull(EntityUid uid, MobStateComponent component, PullMessage args) { _blocker.UpdateCanMove(uid); } // WWDP edit end private void OnUnbuckleAttempt(Entity ent, ref UnbuckleAttemptEvent args) { // TODO is this necessary? // Shouldn't the interaction have already been blocked by a general interaction check? if (args.User == ent.Owner && IsIncapacitated(ent)) args.Cancelled = true; } private void CheckConcious(Entity ent, ref ConsciousAttemptEvent args) { switch (ent.Comp.CurrentState) { case MobState.Dead: case MobState.Critical: args.Cancelled = true; break; } } private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state) { switch (state) { case MobState.Alive: //unused break; case MobState.Critical: if (component.CurrentState is not MobState.Alive) break; _standing.Stand(target); break; case MobState.SoftCritical: if (component.CurrentState is not MobState.Alive) break; _standing.Stand(target); break; case MobState.Dead: RemComp(target); if (component.CurrentState is MobState.Alive) _standing.Stand(target); break; case MobState.Invalid: //unused break; default: throw new NotImplementedException(); } } private void OnStateEnteredSubscribers(EntityUid target, MobStateComponent component, MobState state) { // All of the state changes here should already be networked, so we do nothing if we are currently applying a // server state. if (_timing.ApplyingState) return; _blocker.UpdateCanMove(target); //update movement anytime a state changes switch (state) { case MobState.Alive: // _standing.Stand(target); // WWDP EDIT // _appearance.SetData(target, MobStateVisuals.State, MobState.Alive); // WWDP EDIT break; case MobState.Critical: if (component.DownWhenCrit) _layingDown.TryLieDown(target, behavior:DropHeldItemsBehavior.AlwaysDrop); // WD EDIT _appearance.SetData(target, MobStateVisuals.State, MobState.Critical); break; case MobState.SoftCritical: if (component.DownWhenSoftCrit) _layingDown.TryLieDown(target, behavior:DropHeldItemsBehavior.AlwaysDrop); // WD EDIT _appearance.SetData(target, MobStateVisuals.State, MobState.Critical); break; case MobState.Dead: EnsureComp(target); if (component.DownWhenDead) _layingDown.TryLieDown(target, behavior:DropHeldItemsBehavior.AlwaysDrop); // WD EDIT _appearance.SetData(target, MobStateVisuals.State, MobState.Dead); break; case MobState.Invalid: //unused; break; default: throw new NotImplementedException(); } } #region Event Subscribers private void OnSleepAttempt(EntityUid target, MobStateComponent component, ref TryingToSleepEvent args) { if (component.CurrentState is MobState.Alive) return; args.Cancelled = true; } 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. if (IsDead(target, component)) args.Multiplier /= 3; else if (IsCritical(target, component)) args.Multiplier /= 2; } private void OnSpeakAttempt(EntityUid uid, MobStateComponent component, SpeakAttemptEvent args) { if (HasComp(uid)) { RemCompDeferred(uid); return; } if (component.CurrentState is MobState.Alive || component.CurrentState is MobState.Critical && component.AllowTalkingWhileCrit && _configurationManager.GetCVar(CCVars.AllowTalkingWhileCrit) || component.CurrentState is MobState.SoftCritical && component.AllowTalkingWhileSoftCrit || component.CurrentState is MobState.Dead && component.AllowTalkingWhileDead) return; args.Cancel(); } private void CheckAct(EntityUid target, MobStateComponent component, CancellableEntityEventArgs args) { switch (component.CurrentState) { case MobState.Dead: case MobState.SoftCritical: case MobState.Critical: args.Cancel(); break; } } private void OnEquipAttempt(EntityUid target, MobStateComponent component, IsEquippingAttemptEvent args) { // is this a self-equip, or are they being stripped? if (args.Equipee == target) CheckAct(target, component, args); } private void OnUnequipAttempt(EntityUid target, MobStateComponent component, IsUnequippingAttemptEvent args) { // is this a self-equip, or are they being stripped? if (args.Unequipee == target) CheckAct(target, component, args); } private void OnCombatModeShouldHandInteract(EntityUid uid, MobStateComponent component, ref CombatModeShouldHandInteractEvent args) { if (component.CurrentState is MobState.Alive || component.CurrentState is MobState.Critical && component.AllowHandInteractWhileCrit || component.CurrentState is MobState.SoftCritical && component.AllowHandInteractWhileSoftCrit || component.CurrentState is MobState.Dead && component.AllowHandInteractWhileDead) return; args.Cancelled = true; } private void OnAttemptPacifiedAttack(Entity ent, ref AttemptPacifiedAttackEvent args) { args.Cancelled = true; } #endregion }