From daf4f66414cd861dbbf9a3bdbca14bbf3593f3ea Mon Sep 17 00:00:00 2001
From: RedFoxIV <38788538+redfoxiv@users.noreply.github.com>
Date: Fri, 7 Feb 2025 19:41:09 +0300
Subject: [PATCH] "Proper" "Softcrit" "Support" (#1545)
# Description
Implements the softcrit functionality.
Similiar to critical state but spessmen will be able to communicate and
crawl around, but not pick up items.
Also supports configuring what is and isn't allowed in different
MobStates (per mob prototype): you can enable picking up items while in
softcrit so people can pick up their lasgun and continue shooting after
taking a 40x46mm to their ass cheeks from the guest nukies while being
dragged to safety.

Technical details
New prototype type: "mobStateParams" (`MobStateParametersPrototype`)
Used to specify what can and can't be done when in a certain mobstate.
Of note that they are not actually bound to any `MobState` by
themselves. To assign a params prototype to a mobstate, use
`InitMobStateParams` in `MobStateComponent`.
It has to be a prototype because if I just did something akin to
`Dictionary>`, you'd have to check
the parent and copy every flag besides the one you wish to modify. That
is, if I understand how the prototype system works correctly, which I
frankly doubt.
MobStateComponent now has:
- `Dictionary InitMobStateParams`, for storing "mobstate
- parameter prototype" pairs. `` because it has to be
editable via mob prototypes. Named "mobStateParams" for mob prototypes.
- `public Dictionary
MobStateParams` for actually storing the params for each state
- `public Dictionary
MobStateParamsOverrides` for storing overrides.
`MobStateParametersOverride` is a struct which mirrors all
`MobStateParametersPrototype`'s fields, except they're all nullable.
This is meant for code which wants to temporarily override some setting,
like a spell which allows dead people to talk. This is not the best
solution, but it should do at first. A better option would be tracking
each change separately, instead of hoping different systems overriding
the same flag will play nicely with eachother.
- a shitton of getter methods
TraitModifyMobState now has:
- `public Dictionary Params` to specify a new prototype
to use.
- Important note: All values of `MobStateParametersPrototype` are
nullable, which is a hack to support `TraitModifyMobState`. This trait
takes one `MobStateParametersPrototype` per mobstate and applies all of
its non-null values. This way, a params prototype can be created which
will only have `pointing: true` and the trait can apply it (e.g. to
critstate, so we can spam pointing while dying like it's a game of turbo
dota)
- The above is why that wall of getters exists: They check the relevant
override struct, then the relevant prototype. If both are null, they
default to false (0f for floats.) The only exception is
OxyDamageOverlay, because it's used both for oxy damage overlay (if
null) and as a vision-limiting black void in crit..
MobStateSystem now has:
- a bunch of new "IsSomething"/"CanDoSomething" methods to check the
various flags, alongside rewritten old ones.
-

lookin ahh predicate factory
---
# TODO
done:
- [x] Make proper use of `MobStateSystem.IsIncapacitated()`.
done: some checks were changed, some left as they did what was (more or
less) intended.
Previous `IsIncapacitated()` implementation simply checked if
person was in crit or dead. Now there is a `IsIncapacitated` flag in the
parameters, but it's heavily underutilized. I may need some help on this
one, since I don't know where would be a good place to check for it and
I absolutely will not just scour the entire build in search for them.
- [x] Separate force-dropping items from being downed
done: dropItemsOnEntering bool field. If true, will drop items upon
entering linked mobstate.
- [x] Don't drop items if `ForceDown` is true but `PickingUp` is also
true.
done: dropItemsOnEntering bool field. If true, will drop items upon
entering linked mobstate.
- [x] Actually check what are "conscious attempts" are used for
done: whether or not mob is conscious. Renamed the bool field
accordingly.
- [x] Look into adding a way to make people choke "slowly" in softcrit
as opposed to choking at "regular speed" in crit. Make that into a param
option? Make that into a float so the speed can be finetuned?
done: `BreathingMultiplier` float field added.
1f is regular breathing, 0.25 is "quarter-breathing". Air taken is
multiplied by `BreathingMultiplier` and suffocation damage taken (that
is dealt by RespiratorSystem, not all oxy damage) is multiplied by
`1-BreathingMultiplier`.
- [x] make sure the serializer actually does its job
done: it doesn't. Removed.
- [x] Make an option to prohibit using radio headsets while in softcrit
done: Requires Incapacitated parameter to be false to be able to use
headset radio.
- [x] Make sure it at least compiles
not done:
- [ ] probably move some other stuff to Params if it makes sense. Same
thing as with `IsIncapacitated` though: I kinda don't want to, at least
for now.
---
No media
:p
---
# Changelog
:cl:
- add: Soft critical state. Crawl to safety, or to your doom - whatever
is closer.
---------
Signed-off-by: RedFoxIV <38788538+RedFoxIV@users.noreply.github.com>
Co-authored-by: VMSolidus
(cherry picked from commit 9a357c1774f1a783844a07b5414f504ca574d84c)
---
.../UI/HealthAnalyzerWindow.xaml.cs | 1 +
Content.Client/Interaction/DragDropSystem.cs | 2 +-
.../Overlays/EntityHealthBarOverlay.cs | 4 +-
.../ReplaySpectatorSystem.Blockers.cs | 2 +-
.../DamageOverlayUiController.cs | 6 +-
.../DamageOverlays/Overlays/DamageOverlay.cs | 5 +-
.../Tests/Commands/RejuvenateTest.cs | 7 +-
.../Systems/AdminVerbSystem.Smites.cs | 2 +-
.../Administration/Systems/SuperBonkSystem.cs | 2 +-
.../Body/Systems/RespiratorSystem.cs | 21 +-
Content.Server/Carrying/CarryingSystem.cs | 4 +-
Content.Server/Chat/Systems/ChatSystem.cs | 4 +-
.../Chat/TelepathicChatSystem.Psychognomy.cs | 3 +-
.../Chemistry/EntitySystems/InjectorSystem.cs | 3 +-
.../Cloning/CloningSystem.Utility.cs | 2 +-
.../Damage/ForceSay/DamageForceSaySystem.cs | 17 +-
.../Explosion/EntitySystems/TriggerSystem.cs | 4 +-
Content.Server/Flight/FlightSystem.cs | 4 +-
.../GameTicking/Rules/NukeopsRuleSystem.cs | 2 +-
.../GameTicking/Rules/RespawnRuleSystem.cs | 2 +-
.../Rules/RevolutionaryRuleSystem.cs | 2 +-
Content.Server/Ghost/Roles/GhostRoleSystem.cs | 1 +
Content.Server/Guardian/GuardianSystem.cs | 4 +-
Content.Server/Medical/HealingSystem.cs | 2 +-
.../Medical/SuitSensors/SuitSensorSystem.cs | 2 +-
Content.Server/Mobs/DeathgaspComponent.cs | 6 +
Content.Server/Mobs/DeathgaspSystem.cs | 5 +-
Content.Server/Mood/MoodComponent.cs | 12 +-
Content.Server/Mood/MoodSystem.cs | 52 ++--
.../Operators/Combat/Melee/MeleeOperator.cs | 2 +-
.../Considerations/TargetIsThreateningCon.cs | 9 +
Content.Server/NPC/Systems/NPCSystem.cs | 3 +-
.../NPC/Systems/NPCUtilitySystem.cs | 4 +
.../EntitySystems/AnimalHusbandrySystem.cs | 3 +
.../Systems/HijackShuttleConditionSystem.cs | 8 +-
.../PlayTimeTrackingSystem.cs | 2 +-
.../Polymorph/Systems/PolymorphSystem.cs | 2 +-
Content.Server/Psionics/PsionicsSystem.cs | 2 +-
.../Radio/EntitySystems/HeadsetSystem.cs | 5 +-
.../Revenant/EntitySystems/EssenceSystem.cs | 3 +
.../Shadowkin/ShowEtherealSystem.cs | 2 +-
.../DeadStartupButtonSystem.cs | 4 +-
.../EmitBuzzWhileDamagedSystem.cs | 4 +-
Content.Server/Silicons/Borgs/BorgSystem.cs | 2 +-
.../Traits/Assorted/SingerSystem.cs | 4 +-
.../Traits/TraitSystem.Functions.cs | 103 ++-----
.../Weapons/Melee/MeleeWeaponSystem.cs | 8 +-
.../Zombies/ZombieSystem.Transform.cs | 4 +-
Content.Server/Zombies/ZombieSystem.cs | 6 +-
.../_Shitmed/Targeting/TargetingSystem.cs | 2 +-
.../ActionBlocker/ActionBlockerSystem.cs | 6 +-
.../Administration/SharedAdminFrozenSystem.cs | 2 +-
Content.Shared/Bed/Sleep/SleepingSystem.cs | 2 +-
.../Buckle/Components/BuckleComponent.cs | 63 +++--
.../Buckle/SharedBuckleSystem.Buckle.cs | 8 +-
Content.Shared/Contests/ContestsSystem.cs | 6 +-
Content.Shared/Cuffs/SharedCuffableSystem.cs | 20 +-
.../Damage/Components/DamageableComponent.cs | 1 +
Content.Shared/Devour/SharedDevourSystem.cs | 1 +
Content.Shared/DoAfter/SharedDoAfterSystem.cs | 17 +-
Content.Shared/Examine/ExamineSystemShared.cs | 4 +-
Content.Shared/Foldable/FoldableSystem.cs | 4 +-
Content.Shared/Ghost/SharedGhostSystem.cs | 2 +-
.../Events/InteractionAttemptEvent.cs | 14 +-
.../SharedInteractionSystem.Blocking.cs | 2 +-
.../Medical/Cryogenics/SharedCryoPodSystem.cs | 2 +-
.../Mobs/Components/MobStateComponent.cs | 254 ++++++++++++------
.../Mobs/Components/MobThresholdsComponent.cs | 2 +-
Content.Shared/Mobs/MobState.cs | 112 +++++++-
.../Mobs/Systems/MobStateActionsSystem.cs | 8 +-
.../Systems/MobStateSystem.Subscribers.cs | 203 +++++++-------
Content.Shared/Mobs/Systems/MobStateSystem.cs | 188 +++++++++++--
.../Mobs/Systems/MobThresholdSystem.cs | 18 +-
.../Systems/SharedMoverController.Input.cs | 3 +-
.../Movement/Systems/SharedMoverController.cs | 2 +-
.../Interaction/NoNormalInteractionSystem.cs | 2 +-
.../Item/PseudoItem/SharedPseudoItemSystem.cs | 2 +-
.../Puppet/SharedVentriloquistPuppetSystem.cs | 2 +-
.../Shadowkin/SharedEtherealSystem.cs | 7 +-
.../StationAi/SharedStationAiSystem.Held.cs | 14 +-
.../Standing/SharedLayingDownSystem.cs | 8 +-
Content.Shared/Stunnable/SharedStunSystem.cs | 3 +-
.../SubFloor/SharedSubFloorHideSystem.cs | 2 +-
.../Components/CritModifierComponent.cs | 10 +-
.../Systems/TraitStatModifierSystem.cs | 8 +-
.../Weapons/Misc/SharedTetherGunSystem.cs | 2 +-
Resources/Prototypes/Entities/Mobs/base.yml | 8 +-
Resources/Prototypes/NPCs/utility_queries.yml | 6 +-
.../Prototypes/mobStateParams/default.yml | 131 +++++++++
89 files changed, 1027 insertions(+), 487 deletions(-)
create mode 100644 Content.Server/NPC/Queries/Considerations/TargetIsThreateningCon.cs
create mode 100644 Resources/Prototypes/mobStateParams/default.yml
diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
index e2b07a5d77..c014c3650e 100644
--- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
+++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
@@ -218,6 +218,7 @@ namespace Content.Client.HealthAnalyzer.UI
return mobState switch
{
MobState.Alive => Loc.GetString("health-analyzer-window-entity-alive-text"),
+ MobState.SoftCritical => Loc.GetString("health-analyzer-window-entity-critical-text"),
MobState.Critical => Loc.GetString("health-analyzer-window-entity-critical-text"),
MobState.Dead => Loc.GetString("health-analyzer-window-entity-dead-text"),
_ => Loc.GetString("health-analyzer-window-entity-unknown-text"),
diff --git a/Content.Client/Interaction/DragDropSystem.cs b/Content.Client/Interaction/DragDropSystem.cs
index d249766bbc..f647005d11 100644
--- a/Content.Client/Interaction/DragDropSystem.cs
+++ b/Content.Client/Interaction/DragDropSystem.cs
@@ -495,7 +495,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
// CanInteract() doesn't support checking a second "target" entity.
// Doing so manually:
var ev = new GettingInteractedWithAttemptEvent(user, dragged);
- RaiseLocalEvent(dragged, ref ev);
+ RaiseLocalEvent(dragged, ev);
if (ev.Cancelled)
return false;
diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs
index 193635bda7..b04ee5bf6f 100644
--- a/Content.Client/Overlays/EntityHealthBarOverlay.cs
+++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs
@@ -132,7 +132,7 @@ public sealed class EntityHealthBarOverlay : Overlay
if (dmg.HealthBarThreshold != null && dmg.TotalDamage < dmg.HealthBarThreshold)
return null;
- if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholds) &&
+ if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.SoftCritical, MobState.Critical, out var threshold, thresholds) &&
!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds))
return (1, false);
@@ -142,7 +142,7 @@ public sealed class EntityHealthBarOverlay : Overlay
if (_mobStateSystem.IsCritical(uid, component))
{
- if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var critThreshold, thresholds) ||
+ if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.SoftCritical, MobState.Critical, out var critThreshold, thresholds) ||
!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out var deadThreshold, thresholds))
{
return (1, true);
diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs
index 99d85350b5..5c454bcdbb 100644
--- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs
+++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs
@@ -29,7 +29,7 @@ public sealed partial class ReplaySpectatorSystem
private void OnInteractAttempt(Entity ent, ref InteractionAttemptEvent args)
{
- args.Cancelled = true;
+ args.Cancel();
}
private void OnAttempt(EntityUid uid, ReplaySpectatorComponent component, CancellableEntityEventArgs args)
diff --git a/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs b/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs
index c10c33a990..b9563d4233 100644
--- a/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs
+++ b/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs
@@ -35,7 +35,7 @@ public sealed class DamageOverlayUiController : UIController
ClearOverlay();
if (!EntityManager.TryGetComponent(args.Entity, out var mobState))
return;
- if (mobState.CurrentState != MobState.Dead)
+ if (mobState.CurrentState.IsCritOrAlive())
UpdateOverlays(args.Entity, mobState);
_overlayManager.AddOverlay(_overlay);
}
@@ -89,6 +89,7 @@ public sealed class DamageOverlayUiController : UIController
var critThreshold = foundThreshold.Value;
_overlay.State = mobState.CurrentState;
+ _overlay.OxygenLevelOverride = mobState.GetOxyDamageOverlay();
switch (mobState.CurrentState)
{
@@ -113,6 +114,7 @@ public sealed class DamageOverlayUiController : UIController
_overlay.DeadLevel = 0;
break;
}
+ case MobState.SoftCritical:
case MobState.Critical:
{
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
@@ -124,7 +126,7 @@ public sealed class DamageOverlayUiController : UIController
_overlay.DeadLevel = 0;
break;
}
- case MobState.Dead:
+ case MobState.Dead: // todo: move (some of?) the stuff above and below to mobstate parameters
{
_overlay.BruteLevel = 0;
_overlay.CritLevel = 0;
diff --git a/Content.Client/UserInterface/Systems/DamageOverlays/Overlays/DamageOverlay.cs b/Content.Client/UserInterface/Systems/DamageOverlays/Overlays/DamageOverlay.cs
index fd74815316..cf817f3125 100644
--- a/Content.Client/UserInterface/Systems/DamageOverlays/Overlays/DamageOverlay.cs
+++ b/Content.Client/UserInterface/Systems/DamageOverlays/Overlays/DamageOverlay.cs
@@ -1,4 +1,5 @@
using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
@@ -21,7 +22,6 @@ public sealed class DamageOverlay : Overlay
private readonly ShaderInstance _bruteShader;
public MobState State = MobState.Alive;
-
///
/// Handles the red pulsing overlay
///
@@ -33,6 +33,7 @@ public sealed class DamageOverlay : Overlay
/// Handles the darkening overlay.
///
public float OxygenLevel = 0f;
+ public float? OxygenLevelOverride; // for darkening in crit
private float _oldOxygenLevel = 0f;
@@ -168,7 +169,7 @@ public sealed class DamageOverlay : Overlay
_oldBruteLevel = BruteLevel;
}
- level = State != MobState.Critical ? _oldOxygenLevel : 1f;
+ level = OxygenLevelOverride ?? _oldOxygenLevel;
if (level > 0f)
{
diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
index cfc8007306..3ed1f7ae18 100644
--- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
+++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs
@@ -1,4 +1,4 @@
-using Content.Server.Administration.Commands;
+using Content.Server.Administration.Commands;
using Content.Server.Administration.Systems;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
@@ -72,6 +72,10 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.That(mobStateSystem.IsAlive(human, mobState), Is.False);
Assert.That(mobStateSystem.IsCritical(human, mobState), Is.False);
Assert.That(mobStateSystem.IsDead(human, mobState), Is.True);
+ // This now may not be true for all mobs since the introduction of MobState parameters.
+ // Even though being dead should be incapacitating makes perfect sense.
+ // If you're fucking around with parameters for MobState.Dead and this
+ // throws at you, feel free to comment it. Or reconsider what you're doing.
Assert.That(mobStateSystem.IsIncapacitated(human, mobState), Is.True);
});
@@ -84,6 +88,7 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.That(mobStateSystem.IsAlive(human, mobState), Is.True);
Assert.That(mobStateSystem.IsCritical(human, mobState), Is.False);
Assert.That(mobStateSystem.IsDead(human, mobState), Is.False);
+ // see above, same thing but for MobState.Alive
Assert.That(mobStateSystem.IsIncapacitated(human, mobState), Is.False);
Assert.That(damageable.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
index 2aaab767c6..b3733965cf 100644
--- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
+++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
@@ -203,7 +203,7 @@ public sealed partial class AdminVerbSystem
Act = () =>
{
int damageToDeal;
- if (!_mobThresholdSystem.TryGetThresholdForState(args.Target, MobState.Critical, out var criticalThreshold)) {
+ if (!_mobThresholdSystem.TryGetThresholdForState(args.Target, MobState.SoftCritical, MobState.Critical, out var criticalThreshold)) {
// We can't crit them so try killing them.
if (!_mobThresholdSystem.TryGetThresholdForState(args.Target, MobState.Dead,
out var deadThreshold))
diff --git a/Content.Server/Administration/Systems/SuperBonkSystem.cs b/Content.Server/Administration/Systems/SuperBonkSystem.cs
index 5cd62d8357..a243bce136 100644
--- a/Content.Server/Administration/Systems/SuperBonkSystem.cs
+++ b/Content.Server/Administration/Systems/SuperBonkSystem.cs
@@ -27,7 +27,7 @@ public sealed class SuperBonkSystem : EntitySystem
//The other check in the code to stop when the target dies does not work if the target is already dead.
if (stopWhenDead && TryComp(target, out var mState))
{
- if (mState.CurrentState == MobState.Dead)
+ if (mState.CurrentState.IsDead())
return;
}
diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs
index 5ad0fb35ac..b1b13dfb86 100644
--- a/Content.Server/Body/Systems/RespiratorSystem.cs
+++ b/Content.Server/Body/Systems/RespiratorSystem.cs
@@ -21,6 +21,7 @@ using Content.Shared.Mood;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
+using Content.Shared.Mobs.Components;
namespace Content.Server.Body.Systems;
@@ -83,12 +84,13 @@ public sealed class RespiratorSystem : EntitySystem
UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator);
- if (!_mobState.IsIncapacitated(uid) && !HasComp(uid)) // Shitmed: cannot breathe in crit or when no brain.
+ float mul = _mobState.BreatheMultiplier(uid);
+ if (mul > 0f && !HasComp(uid)) // Shitmed: cannot breathe in crit or when no brain.
{
switch (respirator.Status)
{
case RespiratorStatus.Inhaling:
- Inhale(uid, body);
+ Inhale(uid, body, mul);
respirator.Status = RespiratorStatus.Exhaling;
break;
case RespiratorStatus.Exhaling:
@@ -106,7 +108,7 @@ public sealed class RespiratorSystem : EntitySystem
_popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid);
}
- TakeSuffocationDamage((uid, respirator));
+ TakeSuffocationDamage((uid, respirator), 1 - mul);
respirator.SuffocationCycles += 1;
continue;
}
@@ -116,7 +118,7 @@ public sealed class RespiratorSystem : EntitySystem
}
}
- public void Inhale(EntityUid uid, BodyComponent? body = null)
+ public void Inhale(EntityUid uid, BodyComponent? body = null, float breathVolumeMultiplier = 1f)
{
if (!Resolve(uid, ref body, logMissing: false))
return;
@@ -134,7 +136,7 @@ public sealed class RespiratorSystem : EntitySystem
return;
}
- var actualGas = ev.Gas.RemoveVolume(Atmospherics.BreathVolume);
+ var actualGas = ev.Gas.RemoveVolume(Atmospherics.BreathVolume * breathVolumeMultiplier);
var lungRatio = 1.0f / organs.Count;
var gas = organs.Count == 1 ? actualGas : actualGas.RemoveRatio(lungRatio);
@@ -286,7 +288,7 @@ public sealed class RespiratorSystem : EntitySystem
return saturation;
}
- private void TakeSuffocationDamage(Entity ent)
+ private void TakeSuffocationDamage(Entity ent, float damageMultiplier = 1f)
{
if (ent.Comp.SuffocationCycles == 2)
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating");
@@ -301,8 +303,11 @@ public sealed class RespiratorSystem : EntitySystem
}
RaiseLocalEvent(ent, new MoodEffectEvent("Suffocating"));
}
-
- _damageableSys.TryChangeDamage(ent, HasComp(ent) ? ent.Comp.Damage * 4.5f : ent.Comp.Damage, interruptsDoAfters: false);
+ var damageToTake = ent.Comp.Damage;
+ if (HasComp(ent))
+ damageToTake *= 4.5f;
+ damageToTake *= damageMultiplier;
+ _damageableSys.TryChangeDamage(ent, damageToTake, interruptsDoAfters: false);
}
private void StopSuffocation(Entity ent)
diff --git a/Content.Server/Carrying/CarryingSystem.cs b/Content.Server/Carrying/CarryingSystem.cs
index 224e38bcbd..72cfeff91d 100644
--- a/Content.Server/Carrying/CarryingSystem.cs
+++ b/Content.Server/Carrying/CarryingSystem.cs
@@ -168,7 +168,7 @@ namespace Content.Server.Carrying
var targetParent = Transform(args.Target.Value).ParentUid;
if (args.Target.Value != component.Carrier && targetParent != component.Carrier && targetParent != uid)
- args.Cancelled = true;
+ args.Cancel();
}
///
@@ -202,7 +202,7 @@ namespace Content.Server.Carrying
private void OnInteractedWith(EntityUid uid, BeingCarriedComponent component, GettingInteractedWithAttemptEvent args)
{
if (args.Uid != component.Carrier)
- args.Cancelled = true;
+ args.Cancel();
}
private void OnPullAttempt(EntityUid uid, BeingCarriedComponent component, PullAttemptEvent args)
diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs
index af4ecfcbe1..36719f1a6a 100644
--- a/Content.Server/Chat/Systems/ChatSystem.cs
+++ b/Content.Server/Chat/Systems/ChatSystem.cs
@@ -314,7 +314,7 @@ public sealed partial class ChatSystem : SharedChatSystem
sendType = InGameOOCChatType.Dead;
// If crit player LOOC is disabled, don't send the message at all.
- if (!_critLoocEnabled && _mobStateSystem.IsCritical(source))
+ if (!_critLoocEnabled && _mobStateSystem.IsHardCritical(source))
return;
switch (sendType)
@@ -618,7 +618,7 @@ public sealed partial class ChatSystem : SharedChatSystem
else if (!_loocEnabled) return;
// If crit player LOOC is disabled, don't send the message at all.
- if (!_critLoocEnabled && _mobStateSystem.IsCritical(source))
+ if (!_critLoocEnabled && _mobStateSystem.IsHardCritical(source))
return;
var wrappedMessage = Loc.GetString("chat-manager-entity-looc-wrap-message",
diff --git a/Content.Server/Chat/TelepathicChatSystem.Psychognomy.cs b/Content.Server/Chat/TelepathicChatSystem.Psychognomy.cs
index 383096c03d..75488e2f73 100644
--- a/Content.Server/Chat/TelepathicChatSystem.Psychognomy.cs
+++ b/Content.Server/Chat/TelepathicChatSystem.Psychognomy.cs
@@ -17,6 +17,7 @@ using Robust.Shared.Enums;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Random;
+using Content.Shared.Mobs;
namespace Content.Server.Chat;
public sealed partial class TelepathicChatSystem
@@ -76,7 +77,7 @@ public sealed partial class TelepathicChatSystem
private void DescribeMobState(EntityUid uid, MobStateComponent component, GetPsychognomicDescriptorEvent ev)
{
- if (component.CurrentState != Shared.Mobs.MobState.Critical)
+ if (!component.CurrentState.IsCrit())
return;
ev.Descriptors.Add(Loc.GetString("p-descriptor-liminal"));
diff --git a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs
index 39ddca3412..11c7e2a700 100644
--- a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs
@@ -18,7 +18,6 @@ using Content.Shared.Mobs.Components;
using Content.Shared.Stacks;
using Robust.Server.Player;
-
namespace Content.Server.Chemistry.EntitySystems;
public sealed class InjectorSystem : SharedInjectorSystem
@@ -190,7 +189,7 @@ public sealed class InjectorSystem : SharedInjectorSystem
// Check if the target is incapacitated or in combat mode and modify time accordingly.
- if (MobState.IsIncapacitated(target))
+ if (MobState.IsIncapacitated(target) /*|| _statusEffects.TryGetTime(target, "Stun", out var _)*/)
{
actualDelay /= 2.5f;
}
diff --git a/Content.Server/Cloning/CloningSystem.Utility.cs b/Content.Server/Cloning/CloningSystem.Utility.cs
index 336ed50a6a..459f88bc52 100644
--- a/Content.Server/Cloning/CloningSystem.Utility.cs
+++ b/Content.Server/Cloning/CloningSystem.Utility.cs
@@ -354,7 +354,7 @@ public sealed partial class CloningSystem
{
if (!clonePodComp.DoGeneticDamage
|| !HasComp(mob)
- || !_thresholds.TryGetThresholdForState(mob, Shared.Mobs.MobState.Critical, out var threshold))
+ || !_thresholds.TryGetThresholdForState(mob, Shared.Mobs.MobState.Critical, Shared.Mobs.MobState.SoftCritical, out var threshold))
return;
DamageSpecifier damage = new();
damage.DamageDict.Add("Cellular", (int) threshold + 1 + geneticDamage);
diff --git a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs
index bc61c5d141..815c0ad5d1 100644
--- a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs
+++ b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs
@@ -3,6 +3,7 @@ using Content.Shared.Damage;
using Content.Shared.Damage.ForceSay;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Stunnable;
using Robust.Shared.Player;
@@ -114,12 +115,14 @@ public sealed class DamageForceSaySystem : EntitySystem
private void OnMobStateChanged(EntityUid uid, DamageForceSayComponent component, MobStateChangedEvent args)
{
- if (args is not { OldMobState: MobState.Alive, NewMobState: MobState.Critical or MobState.Dead })
- return;
-
- // no suffix for the drama
- // LING IN MAI-
- TryForceSay(uid, component, false);
- AllowNextSpeech(uid);
+ var mobState = Comp(uid);
+ if (mobState.CanTalk(args.OldMobState) && // could talk before and
+ !mobState.CanTalk()) // can't talk now
+ {
+ // no suffix for the drama
+ // LING IN MAI-
+ TryForceSay(uid, component, false);
+ AllowNextSpeech(uid);
+ }
}
}
diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
index a18f3ef725..1136583a1d 100644
--- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
@@ -197,9 +197,9 @@ namespace Content.Server.Explosion.EntitySystems
return;
// Sends a message to the radio channel specified by the implant
- if (mobstate.CurrentState == MobState.Critical)
+ if (mobstate.CurrentState.IsCrit())
_radioSystem.SendRadioMessage(uid, critMessage, _prototypeManager.Index(component.RadioChannel), uid);
- if (mobstate.CurrentState == MobState.Dead)
+ if (mobstate.CurrentState.IsDead())
_radioSystem.SendRadioMessage(uid, deathMessage, _prototypeManager.Index(component.RadioChannel), uid);
args.Handled = true;
diff --git a/Content.Server/Flight/FlightSystem.cs b/Content.Server/Flight/FlightSystem.cs
index 5e3ef354f2..0e767368df 100644
--- a/Content.Server/Flight/FlightSystem.cs
+++ b/Content.Server/Flight/FlightSystem.cs
@@ -6,6 +6,7 @@ using Content.Shared.DoAfter;
using Content.Shared.Flight;
using Content.Shared.Flight.Events;
using Content.Shared.Mobs;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Standing;
using Content.Shared.Stunnable;
@@ -19,6 +20,7 @@ public sealed class FlightSystem : SharedFlightSystem
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly StandingStateSystem _standing = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
public override void Initialize()
{
@@ -119,7 +121,7 @@ public sealed class FlightSystem : SharedFlightSystem
private void OnMobStateChangedEvent(EntityUid uid, FlightComponent component, MobStateChangedEvent args)
{
if (!component.On
- || args.NewMobState is MobState.Critical or MobState.Dead)
+ || _mobState.IsIncapacitated(uid))
return;
ToggleActive(args.Target, false, component);
diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs
index 4d02e2311d..af1daf5636 100644
--- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs
@@ -253,7 +253,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem
private void OnMobStateChanged(EntityUid uid, NukeOperativeComponent component, MobStateChangedEvent ev)
{
- if (ev.NewMobState == MobState.Dead)
+ if (ev.IsDead())
CheckRoundShouldEnd();
}
diff --git a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs
index 920668472a..0665b8c3ac 100644
--- a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs
@@ -50,7 +50,7 @@ public sealed class RespawnRuleSystem : GameRuleSystem
private void OnMobStateChanged(MobStateChangedEvent args)
{
- if (args.NewMobState == MobState.Alive)
+ if (args.IsAlive())
return;
if (!TryComp(args.Target, out var actor))
diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs
index c5f88ab6cf..7cacca6b22 100644
--- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs
@@ -256,7 +256,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem(entity, out var state))
{
- if (state.CurrentState == MobState.Dead || state.CurrentState == MobState.Invalid)
+ if (state.CurrentState.IsDead() || !state.CurrentState.IsValid())
{
gone++;
}
diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs
index 346cb64ea8..f64c62bbda 100644
--- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs
+++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs
@@ -97,6 +97,7 @@ namespace Content.Server.Ghost.Roles
RegisterGhostRole((component, ghostRole));
break;
}
+ //case MobState.SoftCritical: // allow ghosts to enter a softcritted vessel and beg for mercy
case MobState.Critical:
case MobState.Dead:
UnregisterGhostRole((component, ghostRole));
diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs
index 203882ed9e..eb4b8be51d 100644
--- a/Content.Server/Guardian/GuardianSystem.cs
+++ b/Content.Server/Guardian/GuardianSystem.cs
@@ -238,12 +238,12 @@ namespace Content.Server.Guardian
if (component.HostedGuardian == null)
return;
- if (args.NewMobState == MobState.Critical)
+ if (args.EnteredCrit())
{
_popupSystem.PopupEntity(Loc.GetString("guardian-host-critical-warn"), component.HostedGuardian.Value, component.HostedGuardian.Value);
_audio.PlayPvs("/Audio/Effects/guardian_warn.ogg", component.HostedGuardian.Value);
}
- else if (args.NewMobState == MobState.Dead)
+ else if (args.IsDead())
{
//TODO: Replace WithVariation with datafield
_audio.PlayPvs("/Audio/Voice/Human/malescream_guardian.ogg", uid, AudioParams.Default.WithVariation(0.20f));
diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs
index 73cce89990..b8f49bfc68 100644
--- a/Content.Server/Medical/HealingSystem.cs
+++ b/Content.Server/Medical/HealingSystem.cs
@@ -244,7 +244,7 @@ public sealed class HealingSystem : EntitySystem
if (!TryComp(uid, out var mobThreshold) ||
!TryComp(uid, out var damageable))
return output;
- if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var amount, mobThreshold))
+ if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.SoftCritical, out var amount, mobThreshold))
return 1;
var percentDamage = (float) (damageable.TotalDamage / amount);
diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
index aee62a58e5..ae3aa035a5 100644
--- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
+++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
@@ -346,7 +346,7 @@ public sealed class SuitSensorSystem : EntitySystem
// Get mob total damage crit threshold
int? totalDamageThreshold = null;
- if (_mobThresholdSystem.TryGetThresholdForState(sensor.User.Value, Shared.Mobs.MobState.Critical, out var critThreshold))
+ if (_mobThresholdSystem.TryGetThresholdForState(sensor.User.Value, Shared.Mobs.MobState.SoftCritical, out var critThreshold))
totalDamageThreshold = critThreshold.Value.Int();
// finally, form suit sensor status
diff --git a/Content.Server/Mobs/DeathgaspComponent.cs b/Content.Server/Mobs/DeathgaspComponent.cs
index 7afcaa1965..c25cbff180 100644
--- a/Content.Server/Mobs/DeathgaspComponent.cs
+++ b/Content.Server/Mobs/DeathgaspComponent.cs
@@ -21,4 +21,10 @@ public sealed partial class DeathgaspComponent : Component
///
[DataField]
public bool NeedsCritical = true;
+
+ ///
+ /// Allows deathgasp to happen in softcrit too. Does nothing if is false.
+ ///
+ [DataField]
+ public bool AllowSoftCritical = true;
}
diff --git a/Content.Server/Mobs/DeathgaspSystem.cs b/Content.Server/Mobs/DeathgaspSystem.cs
index 32be7bfe32..1dcbd41f60 100644
--- a/Content.Server/Mobs/DeathgaspSystem.cs
+++ b/Content.Server/Mobs/DeathgaspSystem.cs
@@ -21,8 +21,9 @@ public sealed class DeathgaspSystem: EntitySystem
private void OnMobStateChanged(EntityUid uid, DeathgaspComponent component, MobStateChangedEvent args)
{
// don't deathgasp if they arent going straight from crit to dead
- if (component.NeedsCritical && args.OldMobState != MobState.Critical
- || args.NewMobState != MobState.Dead)
+ if (component.NeedsCritical && (args.WasCrit(component.AllowSoftCritical))
+ ||
+ !args.IsDead() && args.IsValid())
return;
Deathgasp(uid, component);
diff --git a/Content.Server/Mood/MoodComponent.cs b/Content.Server/Mood/MoodComponent.cs
index caa221fe18..a7c6ff8c32 100644
--- a/Content.Server/Mood/MoodComponent.cs
+++ b/Content.Server/Mood/MoodComponent.cs
@@ -1,4 +1,4 @@
-using Content.Shared.Alert;
+using Content.Shared.Alert;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
@@ -48,8 +48,18 @@ public sealed partial class MoodComponent : Component
[DataField]
public float DecreaseCritThreshold = 0.9f;
+ // WD EDIT START
+ [DataField]
+ public float IncreaseSoftCritThreshold = 1.1f;
+
+ [DataField]
+ public float DecreaseSoftCritThreshold = 0.95f;
+ // WD EDIT END
+
[ViewVariables(VVAccess.ReadOnly)]
public FixedPoint2 CritThresholdBeforeModify;
+ [ViewVariables(VVAccess.ReadOnly)]
+ public FixedPoint2 SoftCritThresholdBeforeModify;
[DataField]
public ProtoId MoodCategory = "Mood";
diff --git a/Content.Server/Mood/MoodSystem.cs b/Content.Server/Mood/MoodSystem.cs
index 44a93217cf..ac6825b10e 100644
--- a/Content.Server/Mood/MoodSystem.cs
+++ b/Content.Server/Mood/MoodSystem.cs
@@ -1,4 +1,4 @@
-using Content.Server.Chat.Managers;
+using Content.Server.Chat.Managers;
using Content.Server.Popups;
using Content.Shared.Alert;
using Content.Shared.Chat;
@@ -202,12 +202,12 @@ public sealed class MoodSystem : EntitySystem
if (!_config.GetCVar(CCVars.MoodEnabled))
return;
- if (args.NewMobState == MobState.Dead && args.OldMobState != MobState.Dead)
+ if (args.IsDead() && !args.WasDead())
{
var ev = new MoodEffectEvent("Dead");
RaiseLocalEvent(uid, ev);
}
- else if (args.OldMobState == MobState.Dead && args.NewMobState != MobState.Dead)
+ else if (args.WasDead() && !args.IsDead())
{
var ev = new MoodRemoveEffectEvent("Dead");
RaiseLocalEvent(uid, ev);
@@ -242,10 +242,13 @@ public sealed class MoodSystem : EntitySystem
return;
if (_config.GetCVar(CCVars.MoodModifiesThresholds)
- && TryComp(uid, out var mobThresholdsComponent)
- && _mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var critThreshold, mobThresholdsComponent))
- component.CritThresholdBeforeModify = critThreshold.Value;
-
+ && TryComp(uid, out var mobThresholdsComponent))
+ {
+ if(_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var critThreshold, mobThresholdsComponent))
+ component.CritThresholdBeforeModify = critThreshold.Value;
+ if(_mobThreshold.TryGetThresholdForState(uid, MobState.SoftCritical, out var softCritThreshold, mobThresholdsComponent))
+ component.SoftCritThresholdBeforeModify = softCritThreshold.Value;
+ }
EnsureComp(uid);
RefreshMood(uid, component);
}
@@ -335,19 +338,34 @@ public sealed class MoodSystem : EntitySystem
private void SetCritThreshold(EntityUid uid, MoodComponent component, int modifier)
{
if (!_config.GetCVar(CCVars.MoodModifiesThresholds)
- || !TryComp(uid, out var mobThresholds)
- || !_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var key))
+ || !TryComp(uid, out var mobThresholds))
return;
- var newKey = modifier switch
+ if (_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var key))
{
- 1 => FixedPoint2.New(key.Value.Float() * component.IncreaseCritThreshold),
- -1 => FixedPoint2.New(key.Value.Float() * component.DecreaseCritThreshold),
- _ => component.CritThresholdBeforeModify,
- };
+ var newKey = modifier switch
+ {
+ 1 => FixedPoint2.New(key.Value.Float() * component.IncreaseCritThreshold),
+ -1 => FixedPoint2.New(key.Value.Float() * component.DecreaseCritThreshold),
+ _ => component.CritThresholdBeforeModify,
+ };
- component.CritThresholdBeforeModify = key.Value;
- _mobThreshold.SetMobStateThreshold(uid, newKey, MobState.Critical, mobThresholds);
+ component.CritThresholdBeforeModify = key.Value;
+ _mobThreshold.SetMobStateThreshold(uid, newKey, MobState.Critical, mobThresholds);
+ }
+
+ if (_mobThreshold.TryGetThresholdForState(uid, MobState.SoftCritical, out key))
+ {
+ var newKey = modifier switch
+ {
+ 1 => FixedPoint2.New(key.Value.Float() * component.IncreaseSoftCritThreshold),
+ -1 => FixedPoint2.New(key.Value.Float() * component.DecreaseSoftCritThreshold),
+ _ => component.SoftCritThresholdBeforeModify,
+ };
+
+ component.SoftCritThresholdBeforeModify = key.Value;
+ _mobThreshold.SetMobStateThreshold(uid, newKey, MobState.SoftCritical, mobThresholds);
+ }
}
private MoodThreshold GetMoodThreshold(MoodComponent component, float? moodLevel = null)
@@ -376,7 +394,7 @@ public sealed class MoodSystem : EntitySystem
private void OnDamageChange(EntityUid uid, MoodComponent component, DamageChangedEvent args)
{
- if (!_mobThreshold.TryGetPercentageForState(uid, MobState.Critical, args.Damageable.TotalDamage, out var damage))
+ if (!_mobThreshold.TryGetPercentageForState(uid, MobState.SoftCritical, args.Damageable.TotalDamage, out var damage))
return;
var protoId = "HealthNoDamage";
diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs
index 32be027ec4..a6ad5f3dae 100644
--- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs
+++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs
@@ -95,7 +95,7 @@ public sealed partial class MeleeOperator : HTNOperator, IHtnConditionalShutdown
// Success
if (_entManager.TryGetComponent(target, out var mobState) &&
- mobState.CurrentState > TargetState)
+ !mobState.IsThreatening())
{
status = HTNOperatorStatus.Finished;
}
diff --git a/Content.Server/NPC/Queries/Considerations/TargetIsThreateningCon.cs b/Content.Server/NPC/Queries/Considerations/TargetIsThreateningCon.cs
new file mode 100644
index 0000000000..2f1423a84d
--- /dev/null
+++ b/Content.Server/NPC/Queries/Considerations/TargetIsThreateningCon.cs
@@ -0,0 +1,9 @@
+namespace Content.Server.NPC.Queries.Considerations;
+
+///
+/// Returns 1f if the target is threatening or 0f if not.
+///
+public sealed partial class TargetIsThreateningCon : UtilityConsideration
+{
+
+}
diff --git a/Content.Server/NPC/Systems/NPCSystem.cs b/Content.Server/NPC/Systems/NPCSystem.cs
index 24c038a3e7..ff82ad84dd 100644
--- a/Content.Server/NPC/Systems/NPCSystem.cs
+++ b/Content.Server/NPC/Systems/NPCSystem.cs
@@ -47,7 +47,7 @@ namespace Content.Server.NPC.Systems
public void OnPlayerNPCDetach(EntityUid uid, HTNComponent component, PlayerDetachedEvent args)
{
- if (_mobState.IsIncapacitated(uid) || TerminatingOrDeleted(uid))
+ if (TerminatingOrDeleted(uid))
return;
// This NPC has an attached mind, so it should not wake up.
@@ -150,6 +150,7 @@ namespace Content.Server.NPC.Systems
case MobState.Alive:
WakeNPC(uid, component);
break;
+ case MobState.SoftCritical:
case MobState.Critical:
case MobState.Dead:
SleepNPC(uid, component);
diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs
index eb111baea8..87956b137c 100644
--- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs
+++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs
@@ -324,6 +324,10 @@ public sealed class NPCUtilitySystem : EntitySystem
{
return _mobState.IsAlive(targetUid) ? 1f : 0f;
}
+ case TargetIsThreateningCon:
+ {
+ return _mobState.IsThreatening(targetUid) ? 1f : 0f;
+ }
case TargetIsCritCon:
{
return _mobState.IsCritical(targetUid) ? 1f : 0f;
diff --git a/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs b/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs
index 68d8258c6b..4ef30b162f 100644
--- a/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs
+++ b/Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs
@@ -150,6 +150,9 @@ public sealed class AnimalHusbandrySystem : EntitySystem
if (_mobState.IsIncapacitated(uid))
return false;
+ if (!_mobState.IsConscious(uid))
+ return false;
+
if (TryComp(uid, out var hunger) && _hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay)
return false;
diff --git a/Content.Server/Objectives/Systems/HijackShuttleConditionSystem.cs b/Content.Server/Objectives/Systems/HijackShuttleConditionSystem.cs
index 9e0d2c3d5b..72f98db35e 100644
--- a/Content.Server/Objectives/Systems/HijackShuttleConditionSystem.cs
+++ b/Content.Server/Objectives/Systems/HijackShuttleConditionSystem.cs
@@ -1,4 +1,4 @@
-using Content.Server.Objectives.Components;
+using Content.Server.Objectives.Components;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems;
using Content.Shared.Cuffs.Components;
@@ -87,7 +87,11 @@ public sealed class HijackShuttleConditionSystem : EntitySystem
continue;
var isPersonIncapacitated = _mobState.IsIncapacitated(player.AttachedEntity.Value);
- if (isPersonIncapacitated) // Allow dead and crit
+ if (isPersonIncapacitated) // allow incapacitated people
+ continue;
+
+ var isPersonHardcritOrDead = _mobState.IsCriticalOrDead(player.AttachedEntity.Value, false);
+ if (isPersonIncapacitated) // allow dead and hardcritted people
continue;
var isPersonCuffed =
diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs
index ba7b9f3c37..e4caa8ffb7 100644
--- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs
+++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs
@@ -100,7 +100,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
if (!TryComp(attached, out var state))
return false;
- return state.CurrentState is MobState.Alive or MobState.Critical;
+ return state.CurrentState.IsCritOrAlive();
}
public IEnumerable GetTimedRoles(EntityUid mindId)
diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs
index c9a4ec2d0b..7a9acc0b79 100644
--- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs
+++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs
@@ -85,7 +85,7 @@ public sealed partial class PolymorphSystem : EntitySystem
continue;
if (comp.Configuration.RevertOnDeath && _mobState.IsDead(uid, mob) ||
- comp.Configuration.RevertOnCrit && _mobState.IsIncapacitated(uid, mob))
+ comp.Configuration.RevertOnCrit && _mobState.IsCritical(uid, mob))
{
Revert((uid, comp));
}
diff --git a/Content.Server/Psionics/PsionicsSystem.cs b/Content.Server/Psionics/PsionicsSystem.cs
index f8a37c6cd3..fac4f206ee 100644
--- a/Content.Server/Psionics/PsionicsSystem.cs
+++ b/Content.Server/Psionics/PsionicsSystem.cs
@@ -304,7 +304,7 @@ public sealed class PsionicsSystem : EntitySystem
private void OnMobstateChanged(EntityUid uid, PsionicComponent component, MobStateChangedEvent args)
{
if (component.Familiars.Count <= 0
- || args.NewMobState != MobState.Dead)
+ || !args.IsDead())
return;
foreach (var familiar in component.Familiars)
diff --git a/Content.Server/Radio/EntitySystems/HeadsetSystem.cs b/Content.Server/Radio/EntitySystems/HeadsetSystem.cs
index 2500138a23..7d8ae5c0d2 100644
--- a/Content.Server/Radio/EntitySystems/HeadsetSystem.cs
+++ b/Content.Server/Radio/EntitySystems/HeadsetSystem.cs
@@ -5,6 +5,7 @@ using Content.Server.Radio.Components;
using Content.Server.Speech;
using Content.Shared.Chat;
using Content.Shared.Inventory.Events;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Radio;
using Content.Shared.Radio.Components;
using Content.Shared.Radio.EntitySystems;
@@ -18,6 +19,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly RadioSystem _radio = default!;
[Dependency] private readonly LanguageSystem _language = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
public override void Initialize()
{
@@ -54,7 +56,8 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
{
if (args.Channel != null
&& TryComp(component.Headset, out EncryptionKeyHolderComponent? keys)
- && keys.Channels.Contains(args.Channel.ID))
+ && keys.Channels.Contains(args.Channel.ID)
+ && !_mobState.IsIncapacitated(uid))
{
_radio.SendRadioMessage(uid, args.Message, args.Channel, component.Headset);
args.Channel = null; // prevent duplicate messages from other listeners.
diff --git a/Content.Server/Revenant/EntitySystems/EssenceSystem.cs b/Content.Server/Revenant/EntitySystems/EssenceSystem.cs
index f6de79fee5..cebcd97851 100644
--- a/Content.Server/Revenant/EntitySystems/EssenceSystem.cs
+++ b/Content.Server/Revenant/EntitySystems/EssenceSystem.cs
@@ -72,6 +72,9 @@ public sealed class EssenceSystem : EntitySystem
else
component.EssenceAmount = _random.NextFloat(45f, 70f);
break;
+ case MobState.SoftCritical:
+ component.EssenceAmount = _random.NextFloat(40f, 60f); // i dunno?
+ break;
case MobState.Critical:
component.EssenceAmount = _random.NextFloat(35f, 50f);
break;
diff --git a/Content.Server/Shadowkin/ShowEtherealSystem.cs b/Content.Server/Shadowkin/ShowEtherealSystem.cs
index e908bd9ce7..f51a2e4f7d 100644
--- a/Content.Server/Shadowkin/ShowEtherealSystem.cs
+++ b/Content.Server/Shadowkin/ShowEtherealSystem.cs
@@ -70,7 +70,7 @@ public sealed class ShowEtherealSystem : EntitySystem
|| !HasComp(args.Target))
return;
- args.Cancelled = true;
+ args.Cancel();
if (_gameTiming.InPrediction)
return;
diff --git a/Content.Server/Silicon/DeadStartupButtonSystem/DeadStartupButtonSystem.cs b/Content.Server/Silicon/DeadStartupButtonSystem/DeadStartupButtonSystem.cs
index 6e516f3011..8be5c40b5d 100644
--- a/Content.Server/Silicon/DeadStartupButtonSystem/DeadStartupButtonSystem.cs
+++ b/Content.Server/Silicon/DeadStartupButtonSystem/DeadStartupButtonSystem.cs
@@ -42,7 +42,7 @@ public sealed class DeadStartupButtonSystem : SharedDeadStartupButtonSystem
|| !_mobState.IsDead(uid, mobStateComponent)
|| !TryComp(uid, out var mobThresholdsComponent)
|| !TryComp(uid, out var damageable)
- || !_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var criticalThreshold, mobThresholdsComponent))
+ || !_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, MobState.SoftCritical, out var criticalThreshold, mobThresholdsComponent))
return;
if (damageable.TotalDamage < criticalThreshold)
@@ -70,7 +70,7 @@ public sealed class DeadStartupButtonSystem : SharedDeadStartupButtonSystem
private void OnMobStateChanged(EntityUid uid, DeadStartupButtonComponent comp, MobStateChangedEvent args)
{
- if (args.NewMobState != MobState.Alive)
+ if (_mobState.IsIncapacitated(uid))
return;
_popup.PopupEntity(Loc.GetString("dead-startup-system-reboot-success", ("target", MetaData(uid).EntityName)), uid);
diff --git a/Content.Server/Silicon/EmitBuzzWhileDamaged/EmitBuzzWhileDamagedSystem.cs b/Content.Server/Silicon/EmitBuzzWhileDamaged/EmitBuzzWhileDamagedSystem.cs
index 161a912d06..e91d8afcae 100644
--- a/Content.Server/Silicon/EmitBuzzWhileDamaged/EmitBuzzWhileDamagedSystem.cs
+++ b/Content.Server/Silicon/EmitBuzzWhileDamaged/EmitBuzzWhileDamagedSystem.cs
@@ -32,8 +32,8 @@ public sealed class EmitBuzzWhileDamagedSystem : EntitySystem
while (query.MoveNext(out var uid, out var emitBuzzOnCritComponent, out var mobStateComponent, out var thresholdsComponent, out var damageableComponent))
{
if (_mobState.IsDead(uid, mobStateComponent)
- || !_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholdsComponent)
- || damageableComponent.TotalDamage < threshold / 2)
+ || !_mobThreshold.TryGetThresholdForState(uid, MobState.SoftCritical, MobState.Critical, out var threshold, thresholdsComponent) // who knows, maybe we'll want softcrittable borgs some day
+ || damageableComponent.TotalDamage < threshold.Value / 2)
continue;
// Check update time
diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs
index c4f840eed1..eeff295b76 100644
--- a/Content.Server/Silicons/Borgs/BorgSystem.cs
+++ b/Content.Server/Silicons/Borgs/BorgSystem.cs
@@ -172,7 +172,7 @@ public sealed partial class BorgSystem : SharedBorgSystem
private void OnMobStateChanged(EntityUid uid, BorgChassisComponent component, MobStateChangedEvent args)
{
- if (args.NewMobState == MobState.Alive)
+ if (args.IsAlive())
{
if (_mind.TryGetMind(uid, out _, out _))
_powerCell.SetDrawEnabled(uid, true);
diff --git a/Content.Server/Traits/Assorted/SingerSystem.cs b/Content.Server/Traits/Assorted/SingerSystem.cs
index 95087c0f1e..c1e148a58b 100644
--- a/Content.Server/Traits/Assorted/SingerSystem.cs
+++ b/Content.Server/Traits/Assorted/SingerSystem.cs
@@ -10,6 +10,7 @@ using Content.Shared.Instruments;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Mobs;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
@@ -29,6 +30,7 @@ public sealed class SingerSystem : SharedSingerSystem
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly InstrumentSystem _instrument = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
public override void Initialize()
{
@@ -71,7 +73,7 @@ public sealed class SingerSystem : SharedSingerSystem
private void OnMobStateChangedEvent(EntityUid uid, SharedInstrumentComponent component, MobStateChangedEvent args)
{
- if (args.NewMobState is MobState.Critical or MobState.Dead)
+ if (_mobState.IsIncapacitated(uid))
CloseMidiUi(args.Target);
}
diff --git a/Content.Server/Traits/TraitSystem.Functions.cs b/Content.Server/Traits/TraitSystem.Functions.cs
index deb3102d4b..f87e1f1677 100644
--- a/Content.Server/Traits/TraitSystem.Functions.cs
+++ b/Content.Server/Traits/TraitSystem.Functions.cs
@@ -19,10 +19,14 @@ using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Mobs;
using Content.Shared.Damage.Components;
+using Content.Server.Administration.Commands;
using Content.Shared.NPC.Systems;
+using Robust.Shared.Utility;
+using Robust.Shared.Reflection;
using Content.Shared.Weapons.Melee;
using Robust.Shared.Audio;
+
namespace Content.Server.Traits;
/// Used for traits that add a Component upon spawning in, overwriting the pre-existing component if it already exists.
@@ -415,19 +419,19 @@ public sealed partial class TraitModifyMobThresholds : TraitFunction
return;
var thresholdSystem = entityManager.System();
- if (CritThresholdModifier != 0)
- {
- var critThreshold = thresholdSystem.GetThresholdForState(uid, MobState.Critical, threshold);
- if (critThreshold != 0)
- thresholdSystem.SetMobStateThreshold(uid, critThreshold + CritThresholdModifier, MobState.Critical);
- }
-
if (SoftCritThresholdModifier != 0)
{
var softCritThreshold = thresholdSystem.GetThresholdForState(uid, MobState.SoftCritical, threshold);
if (softCritThreshold != 0)
thresholdSystem.SetMobStateThreshold(uid, softCritThreshold + SoftCritThresholdModifier, MobState.SoftCritical);
}
+
+ if (CritThresholdModifier != 0)
+ {
+ var critThreshold = thresholdSystem.GetThresholdForState(uid, MobState.Critical, threshold);
+ if (critThreshold != 0)
+ thresholdSystem.SetMobStateThreshold(uid, critThreshold + CritThresholdModifier, MobState.Critical);
+ }
if (DeadThresholdModifier != 0)
{
@@ -444,85 +448,30 @@ public sealed partial class TraitModifyMobState : TraitFunction
// Three-State Booleans my beloved.
// :faridabirb.png:
- [DataField, AlwaysPushInheritance]
- public bool? AllowMovementWhileCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowMovementWhileSoftCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowMovementWhileDead;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowTalkingWhileCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowTalkingWhileSoftCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowTalkingWhileDead;
-
- [DataField, AlwaysPushInheritance]
- public bool? DownWhenCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? DownWhenSoftCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? DownWhenDead;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowHandInteractWhileCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowHandInteractWhileSoftCrit;
-
- [DataField, AlwaysPushInheritance]
- public bool? AllowHandInteractWhileDead;
+ [DataField(required: true)]
+ public Dictionary> Params = default!;
public override void OnPlayerSpawn(EntityUid uid,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager)
{
- if (!entityManager.TryGetComponent(uid, out var mobStateComponent))
+ if (!entityManager.TryGetComponent(uid, out var comp))
return;
- if (AllowMovementWhileCrit is not null)
- mobStateComponent.AllowMovementWhileCrit = AllowMovementWhileCrit.Value;
+ var _reflection = IoCManager.Resolve();
+ var _proto = IoCManager.Resolve();
- if (AllowMovementWhileSoftCrit is not null)
- mobStateComponent.AllowHandInteractWhileSoftCrit = AllowMovementWhileSoftCrit.Value;
-
- if (AllowMovementWhileDead is not null)
- mobStateComponent.AllowMovementWhileDead = AllowMovementWhileDead.Value;
-
- if (AllowTalkingWhileCrit is not null)
- mobStateComponent.AllowTalkingWhileCrit = AllowTalkingWhileCrit.Value;
-
- if (AllowTalkingWhileSoftCrit is not null)
- mobStateComponent.AllowTalkingWhileSoftCrit = AllowTalkingWhileSoftCrit.Value;
-
- if (AllowTalkingWhileDead is not null)
- mobStateComponent.AllowTalkingWhileDead = AllowTalkingWhileDead.Value;
-
- if (DownWhenCrit is not null)
- mobStateComponent.DownWhenCrit = DownWhenCrit.Value;
-
- if (DownWhenSoftCrit is not null)
- mobStateComponent.DownWhenSoftCrit = DownWhenSoftCrit.Value;
-
- if (DownWhenDead is not null)
- mobStateComponent.DownWhenDead = DownWhenDead.Value;
-
- if (AllowHandInteractWhileCrit is not null)
- mobStateComponent.AllowHandInteractWhileCrit = AllowHandInteractWhileCrit.Value;
-
- if (AllowHandInteractWhileSoftCrit is not null)
- mobStateComponent.AllowHandInteractWhileSoftCrit = AllowHandInteractWhileSoftCrit.Value;
-
- if (AllowHandInteractWhileDead is not null)
- mobStateComponent.AllowHandInteractWhileDead = AllowHandInteractWhileDead.Value;
+ foreach (var pair in Params) {
+ var succ = _reflection.TryParseEnumReference($"enum.MobState.{pair.Key}", out var rawEnum);
+ DebugTools.Assert(succ, $"MobState.{pair.Key} does not exist.");
+ MobState state = (MobState) rawEnum!;
+ MobStateParametersPrototype current = comp.MobStateParams[state];
+ var mod = _proto.Index(pair.Value);
+ current.MergeWith(mod);
+ //current.FillDefaults();
+ }
+ comp.Dirty(); // why is this deprecated? it's much better than manually resolving entitymanager with ioc
}
}
diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
index 42cc357b8d..d6d4f6cfb7 100644
--- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
+++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
@@ -112,10 +112,10 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
var target = GetEntity(ev.Target!.Value);
- if (_mobState.IsIncapacitated(target))
- {
- return false;
- }
+ //if (_mobState.IsIncapacitated(target))
+ //{
+ // return false;
+ //}
if (!TryComp(target, out var targetHandsComponent))
{
diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs
index 2ddff91d40..db6aa7e5b6 100644
--- a/Content.Server/Zombies/ZombieSystem.Transform.cs
+++ b/Content.Server/Zombies/ZombieSystem.Transform.cs
@@ -70,7 +70,7 @@ namespace Content.Server.Zombies
///
private void OnDamageChanged(EntityUid uid, ZombifyOnDeathComponent component, MobStateChangedEvent args)
{
- if (args.NewMobState == MobState.Dead)
+ if (args.IsDead())
{
ZombifyEntity(uid, args.Component);
}
@@ -141,7 +141,7 @@ namespace Content.Server.Zombies
melee.Angle = 0.0f;
melee.SoundHit = zombiecomp.BiteSound;
- if (mobState.CurrentState == MobState.Alive)
+ if (mobState.CurrentState.IsAlive())
{
// Groaning when damaged
EnsureComp(target);
diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs
index ed1f2580f6..c3100a9151 100644
--- a/Content.Server/Zombies/ZombieSystem.cs
+++ b/Content.Server/Zombies/ZombieSystem.cs
@@ -167,7 +167,7 @@ namespace Content.Server.Zombies
private void OnMobState(EntityUid uid, ZombieComponent component, MobStateChangedEvent args)
{
- if (args.NewMobState == MobState.Alive)
+ if (args.IsAlive())
{
// Groaning when damaged
EnsureComp(uid);
@@ -248,12 +248,12 @@ namespace Content.Server.Zombies
}
}
- if (_mobState.IsIncapacitated(entity, mobState) && !HasComp(entity) && !HasComp(entity))
+ if (_mobState.IsCriticalOrDead(entity, false, mobState) && !HasComp(entity) && !HasComp(entity))
{
ZombifyEntity(entity);
args.BonusDamage = -args.BaseDamage;
}
- else if (mobState.CurrentState == MobState.Alive) //heals when zombies bite live entities
+ else if (_mobState.IsAlive(entity, mobState)) //heals when zombies bite live entities
{
_damageable.TryChangeDamage(uid, component.HealingOnBite, true, false);
}
diff --git a/Content.Server/_Shitmed/Targeting/TargetingSystem.cs b/Content.Server/_Shitmed/Targeting/TargetingSystem.cs
index 889d8e7b30..78019a2eb4 100644
--- a/Content.Server/_Shitmed/Targeting/TargetingSystem.cs
+++ b/Content.Server/_Shitmed/Targeting/TargetingSystem.cs
@@ -40,7 +40,7 @@ public sealed class TargetingSystem : SharedTargetingSystem
// I love groin shitcode.
component.BodyStatus[TargetBodyPart.Groin] = TargetIntegrity.Dead;
}
- else if (args.OldMobState == MobState.Dead && (args.NewMobState == MobState.Alive || args.NewMobState == MobState.Critical))
+ else if (args.WasDead() && args.IsCritOrAlive())
{
component.BodyStatus = _bodySystem.GetBodyPartStatus(uid);
changed = true;
diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs
index 457e3875d4..bd6ee8cae1 100644
--- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs
+++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs
@@ -89,7 +89,7 @@ namespace Content.Shared.ActionBlocker
return false;
var ev = new InteractionAttemptEvent(user, target);
- RaiseLocalEvent(user, ref ev);
+ RaiseLocalEvent(user, ev);
if (ev.Cancelled)
return false;
@@ -98,7 +98,7 @@ namespace Content.Shared.ActionBlocker
return true;
var targetEv = new GettingInteractedWithAttemptEvent(user, target);
- RaiseLocalEvent(target.Value, ref targetEv);
+ RaiseLocalEvent(target.Value, targetEv);
return !targetEv.Cancelled;
}
@@ -129,7 +129,7 @@ namespace Content.Shared.ActionBlocker
public bool CanConsciouslyPerformAction(EntityUid user)
{
var ev = new ConsciousAttemptEvent(user);
- RaiseLocalEvent(user, ref ev);
+ RaiseLocalEvent(user, ev);
return !ev.Cancelled;
}
diff --git a/Content.Shared/Administration/SharedAdminFrozenSystem.cs b/Content.Shared/Administration/SharedAdminFrozenSystem.cs
index 259df2bdf2..b1ae89fdd7 100644
--- a/Content.Shared/Administration/SharedAdminFrozenSystem.cs
+++ b/Content.Shared/Administration/SharedAdminFrozenSystem.cs
@@ -37,7 +37,7 @@ public abstract class SharedAdminFrozenSystem : EntitySystem
private void OnInteractAttempt(Entity ent, ref InteractionAttemptEvent args)
{
- args.Cancelled = true;
+ args.Cancel();
}
private void OnSpeakAttempt(EntityUid uid, AdminFrozenComponent component, SpeakAttemptEvent args)
diff --git a/Content.Shared/Bed/Sleep/SleepingSystem.cs b/Content.Shared/Bed/Sleep/SleepingSystem.cs
index 8e6f1193ac..a73e18c82a 100644
--- a/Content.Shared/Bed/Sleep/SleepingSystem.cs
+++ b/Content.Shared/Bed/Sleep/SleepingSystem.cs
@@ -155,7 +155,7 @@ public sealed partial class SleepingSystem : EntitySystem
private void OnConsciousAttempt(Entity ent, ref ConsciousAttemptEvent args)
{
- args.Cancelled = true;
+ args.Cancel();
}
private void OnExamined(Entity ent, ref ExaminedEvent args)
diff --git a/Content.Shared/Buckle/Components/BuckleComponent.cs b/Content.Shared/Buckle/Components/BuckleComponent.cs
index 55831515ed..bb30e4f2d5 100644
--- a/Content.Shared/Buckle/Components/BuckleComponent.cs
+++ b/Content.Shared/Buckle/Components/BuckleComponent.cs
@@ -75,52 +75,61 @@ public sealed partial class BuckleComponent : Component
/// Event raised directed at a strap entity before some entity gets buckled to it.
///
[ByRefEvent]
-public record struct StrapAttemptEvent(
- Entity Strap,
- Entity Buckle,
- EntityUid? User,
- bool Popup)
+public class StrapAttemptEvent(
+ Entity strap,
+ Entity buckle,
+ EntityUid? user,
+ bool popup) : CancellableEntityEventArgs
{
- public bool Cancelled;
+ public Entity Strap = strap;
+ public Entity Buckle = buckle;
+ public EntityUid? User = user;
+ public bool Popup = popup;
}
///
/// Event raised directed at a buckle entity before it gets buckled to some strap entity.
///
-[ByRefEvent]
-public record struct BuckleAttemptEvent(
- Entity Strap,
- Entity Buckle,
- EntityUid? User,
- bool Popup)
+public class BuckleAttemptEvent(
+ Entity strap,
+ Entity buckle,
+ EntityUid? user,
+ bool popup) : CancellableEntityEventArgs
{
- public bool Cancelled;
+ public Entity Strap = strap;
+ public Entity Buckle = buckle;
+ public EntityUid? User = user;
+ public bool Popup = popup;
}
///
/// Event raised directed at a strap entity before some entity gets unbuckled from it.
///
-[ByRefEvent]
-public record struct UnstrapAttemptEvent(
- Entity Strap,
- Entity Buckle,
- EntityUid? User,
- bool Popup)
+public class UnstrapAttemptEvent(
+ Entity strap,
+ Entity buckle,
+ EntityUid? user,
+ bool popup) : CancellableEntityEventArgs
{
- public bool Cancelled;
+ public Entity Strap = strap;
+ public Entity Buckle = buckle;
+ public EntityUid? User = user;
+ public bool Popup = popup;
}
///
/// Event raised directed at a buckle entity before it gets unbuckled.
///
-[ByRefEvent]
-public record struct UnbuckleAttemptEvent(
- Entity Strap,
- Entity Buckle,
- EntityUid? User,
- bool Popup)
+public class UnbuckleAttemptEvent(
+ Entity strap,
+ Entity buckle,
+ EntityUid? user,
+ bool popup) : CancellableEntityEventArgs
{
- public bool Cancelled;
+ public Entity Strap = strap;
+ public Entity Buckle = buckle;
+ public EntityUid? User = user;
+ public bool Popup = popup;
}
///
diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs
index c992f442e2..73adf6d57c 100644
--- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs
+++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs
@@ -303,7 +303,7 @@ public abstract partial class SharedBuckleSystem
}
var buckleAttempt = new BuckleAttemptEvent((strapUid, strapComp), (buckleUid, buckleComp), user, popup);
- RaiseLocalEvent(buckleUid, ref buckleAttempt);
+ RaiseLocalEvent(buckleUid, buckleAttempt);
if (buckleAttempt.Cancelled)
return false;
@@ -464,7 +464,7 @@ public abstract partial class SharedBuckleSystem
Appearance.SetData(strap, StrapVisuals.State, strap.Comp.BuckledEntities.Count != 0);
Appearance.SetData(buckle, BuckleVisuals.Buckled, false);
- if (HasComp(buckle) || _mobState.IsIncapacitated(buckle))
+ if (HasComp(buckle) || _mobState.IsDown(buckle))
_standing.Down(buckle, playSound: false);
else
_standing.Stand(buckle);
@@ -507,12 +507,12 @@ public abstract partial class SharedBuckleSystem
return false;
var unbuckleAttempt = new UnbuckleAttemptEvent(strap, buckle!, user, popup);
- RaiseLocalEvent(buckle, ref unbuckleAttempt);
+ RaiseLocalEvent(buckle, unbuckleAttempt);
if (unbuckleAttempt.Cancelled)
return false;
var unstrapAttempt = new UnstrapAttemptEvent(strap, buckle!, user, popup);
- RaiseLocalEvent(strap, ref unstrapAttempt);
+ RaiseLocalEvent(strap, unstrapAttempt);
return !unstrapAttempt.Cancelled;
}
diff --git a/Content.Shared/Contests/ContestsSystem.cs b/Content.Shared/Contests/ContestsSystem.cs
index 454d17a5d8..254c354e39 100644
--- a/Content.Shared/Contests/ContestsSystem.cs
+++ b/Content.Shared/Contests/ContestsSystem.cs
@@ -228,7 +228,7 @@ public sealed partial class ContestsSystem : EntitySystem
|| !_cfg.GetCVar(CCVars.DoHealthContests)
|| !TryComp(performer, out var damage)
|| !TryComp(performer, out var thresholdsComponent)
- || !_mobThreshold.TryGetThresholdForState(performer, Mobs.MobState.Critical, out var threshold, thresholdsComponent))
+ || !_mobThreshold.TryGetThresholdForState(performer, Mobs.MobState.Critical, Mobs.MobState.SoftCritical, out var threshold, thresholdsComponent))
return 1f;
return ContestClamp(ContestClampOverride(bypassClamp)
@@ -251,8 +251,8 @@ public sealed partial class ContestsSystem : EntitySystem
|| !TryComp(target, out var targetDamage)
|| !TryComp(performer, out var perfThresholdComp)
|| !TryComp(target, out var targetThresholdComp)
- || !_mobThreshold.TryGetThresholdForState(performer, Mobs.MobState.Critical, out var perfThreshold, perfThresholdComp)
- || !_mobThreshold.TryGetThresholdForState(target, Mobs.MobState.Critical, out var targetThreshold, targetThresholdComp))
+ || !_mobThreshold.TryGetThresholdForState(performer, Mobs.MobState.Critical, Mobs.MobState.SoftCritical, out var perfThreshold, perfThresholdComp)
+ || !_mobThreshold.TryGetThresholdForState(target, Mobs.MobState.Critical, Mobs.MobState.SoftCritical, out var targetThreshold, targetThresholdComp))
return 1f;
return ContestClamp(ContestClampOverride(bypassClamp)
diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs
index 9269f7d179..1f4c21aa12 100644
--- a/Content.Shared/Cuffs/SharedCuffableSystem.cs
+++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs
@@ -95,7 +95,7 @@ namespace Content.Shared.Cuffs
private void CheckInteract(Entity ent, ref InteractionAttemptEvent args)
{
if (!ent.Comp.CanStillInteract)
- args.Cancelled = true;
+ args.Cancel();
}
private void OnUncuffAttempt(ref UncuffAttemptEvent args)
@@ -207,15 +207,15 @@ namespace Content.Shared.Cuffs
args.Cancel();
}
- private void OnBuckleAttempt(Entity ent, EntityUid? user, ref bool cancelled, bool buckling, bool popup)
+ private void OnBuckleAttempt(EntityUid uid, CuffableComponent comp, EntityUid? user, CancellableEntityEventArgs args, bool buckling, bool popup)
{
- if (cancelled || user != ent.Owner)
+ if (args.Cancelled || user != uid)
return;
- if (!TryComp(ent, out var hands) || ent.Comp.CuffedHandCount < hands.Count)
+ if (!TryComp(uid, out var hands) || comp.CuffedHandCount < hands.Count)
return;
- cancelled = true;
+ args.Cancel();
if (!popup)
return;
@@ -223,17 +223,17 @@ namespace Content.Shared.Cuffs
? Loc.GetString("handcuff-component-cuff-interrupt-buckled-message")
: Loc.GetString("handcuff-component-cuff-interrupt-unbuckled-message");
- _popup.PopupClient(message, ent, user);
+ _popup.PopupClient(message, uid, user);
}
- private void OnBuckleAttemptEvent(Entity ent, ref BuckleAttemptEvent args)
+ private void OnBuckleAttemptEvent(EntityUid uid, CuffableComponent comp, BuckleAttemptEvent args)
{
- OnBuckleAttempt(ent, args.User, ref args.Cancelled, true, args.Popup);
+ OnBuckleAttempt(uid, comp, args.User, args, true, args.Popup);
}
- private void OnUnbuckleAttemptEvent(Entity ent, ref UnbuckleAttemptEvent args)
+ private void OnUnbuckleAttemptEvent(EntityUid uid, CuffableComponent comp, UnbuckleAttemptEvent args)
{
- OnBuckleAttempt(ent, args.User, ref args.Cancelled, false, args.Popup);
+ OnBuckleAttempt(uid, comp, args.User, args, false, args.Popup);
}
private void OnPull(EntityUid uid, CuffableComponent component, PullMessage args)
diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs
index ed01bd2647..69c4e556f0 100644
--- a/Content.Shared/Damage/Components/DamageableComponent.cs
+++ b/Content.Shared/Damage/Components/DamageableComponent.cs
@@ -77,6 +77,7 @@ namespace Content.Shared.Damage
public Dictionary> HealthIcons = new()
{
{ MobState.Alive, "HealthIconFine" },
+ { MobState.SoftCritical, "HealthIconCritical" },
{ MobState.Critical, "HealthIconCritical" },
{ MobState.Dead, "HealthIconDead" },
};
diff --git a/Content.Shared/Devour/SharedDevourSystem.cs b/Content.Shared/Devour/SharedDevourSystem.cs
index 14047fba7d..12ef7c9710 100644
--- a/Content.Shared/Devour/SharedDevourSystem.cs
+++ b/Content.Shared/Devour/SharedDevourSystem.cs
@@ -54,6 +54,7 @@ public abstract class SharedDevourSystem : EntitySystem
{
switch (targetState.CurrentState)
{
+ // case MobState.SoftCritical: // they squirm too much
case MobState.Critical:
case MobState.Dead:
diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs
index 48051e0a30..629efee967 100644
--- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs
+++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs
@@ -4,6 +4,7 @@ using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
using Content.Shared.Hands.Components;
using Content.Shared.Mobs;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Tag;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
@@ -18,6 +19,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly TagSystem _tag = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
///
/// We'll use an excess time so stuff like finishing effects can show.
@@ -48,14 +50,17 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
private void OnStateChanged(EntityUid uid, DoAfterComponent component, MobStateChangedEvent args)
{
- if (args.NewMobState != MobState.Dead || args.NewMobState != MobState.Critical)
- return;
-
- foreach (var doAfter in component.DoAfters.Values)
+ // Original code
+ /*if (args.NewMobState != MobState.Dead || args.NewMobState != MobState.Critical) // comment block of shame
+ return;*/
+ if (_mobState.IsIncapacitated(uid))
{
- InternalCancel(doAfter, component);
+ foreach (var doAfter in component.DoAfters.Values)
+ {
+ InternalCancel(doAfter, component);
+ }
+ Dirty(uid, component);
}
- Dirty(uid, component);
}
///
diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs
index a1c30a2bd0..f1f0aa8778 100644
--- a/Content.Shared/Examine/ExamineSystemShared.cs
+++ b/Content.Shared/Examine/ExamineSystemShared.cs
@@ -54,8 +54,8 @@ namespace Content.Shared.Examine
if (IsClientSide(entity))
return true;
- // check if the mob is in critical or dead
- if (MobStateSystem.IsIncapacitated(examiner))
+ // check if the mob is conscious to examine something
+ if (MobStateSystem.IsConscious(examiner))
return false;
if (!InRangeUnOccluded(examiner, entity, ExamineDetailsRange))
diff --git a/Content.Shared/Foldable/FoldableSystem.cs b/Content.Shared/Foldable/FoldableSystem.cs
index 2a846f4f23..597ddbc310 100644
--- a/Content.Shared/Foldable/FoldableSystem.cs
+++ b/Content.Shared/Foldable/FoldableSystem.cs
@@ -53,10 +53,10 @@ public sealed class FoldableSystem : EntitySystem
args.Cancelled = true;
}
- public void OnStrapAttempt(EntityUid uid, FoldableComponent comp, ref StrapAttemptEvent args)
+ public void OnStrapAttempt(EntityUid uid, FoldableComponent comp, StrapAttemptEvent args)
{
if (comp.IsFolded)
- args.Cancelled = true;
+ args.Cancel();
}
///
diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs
index f10ce162bf..63263fc684 100644
--- a/Content.Shared/Ghost/SharedGhostSystem.cs
+++ b/Content.Shared/Ghost/SharedGhostSystem.cs
@@ -30,7 +30,7 @@ namespace Content.Shared.Ghost
private void OnAttemptInteract(Entity ent, ref InteractionAttemptEvent args)
{
if (!ent.Comp.CanGhostInteract)
- args.Cancelled = true;
+ args.Cancel();
}
private void OnAttempt(EntityUid uid, GhostComponent component, CancellableEntityEventArgs args)
diff --git a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs
index a04c053635..fcc0c478cd 100644
--- a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs
+++ b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs
@@ -1,12 +1,10 @@
-namespace Content.Shared.Interaction.Events
+namespace Content.Shared.Interaction.Events
{
///
/// Event raised directed at a user to see if they can perform a generic interaction.
///
- [ByRefEvent]
- public struct InteractionAttemptEvent(EntityUid uid, EntityUid? target)
+ public class InteractionAttemptEvent(EntityUid uid, EntityUid? target) : CancellableEntityEventArgs
{
- public bool Cancelled;
public readonly EntityUid Uid = uid;
public readonly EntityUid? Target = target;
}
@@ -14,10 +12,8 @@
///
/// Raised to determine whether an entity is conscious to perform an action.
///
- [ByRefEvent]
- public struct ConsciousAttemptEvent(EntityUid uid)
+ public class ConsciousAttemptEvent(EntityUid uid) : CancellableEntityEventArgs
{
- public bool Cancelled;
public readonly EntityUid Uid = uid;
}
@@ -25,10 +21,8 @@
/// Event raised directed at the target entity of an interaction to see if the user is allowed to perform some
/// generic interaction.
///
- [ByRefEvent]
- public struct GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target)
+ public class GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target) : CancellableEntityEventArgs
{
- public bool Cancelled;
public readonly EntityUid Uid = uid;
public readonly EntityUid? Target = target;
}
diff --git a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs
index 52c40477c9..8a9e1ea077 100644
--- a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs
+++ b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs
@@ -29,7 +29,7 @@ public partial class SharedInteractionSystem
private void CancelInteractEvent(Entity ent, ref InteractionAttemptEvent args)
{
if (ent.Comp.BlockInteraction)
- args.Cancelled = true;
+ args.Cancel();
}
private void OnMoveAttempt(EntityUid uid, BlockMovementComponent component, UpdateCanMoveEvent args)
diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs
index f6ce235ff7..fdd9dd0e25 100644
--- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs
+++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs
@@ -122,7 +122,7 @@ public abstract partial class SharedCryoPodSystem: EntitySystem
// RemComp(contained);
// Restore the correct position of the patient. Checking the components manually feels hacky, but I did not find a better way for now.
- if (HasComp(contained) || _mobStateSystem.IsIncapacitated(contained))
+ if (HasComp(contained) || _mobStateSystem.IsDown(contained))
{
_standingStateSystem.Down(contained);
}
diff --git a/Content.Shared/Mobs/Components/MobStateComponent.cs b/Content.Shared/Mobs/Components/MobStateComponent.cs
index d767e77a36..74fefbc560 100644
--- a/Content.Shared/Mobs/Components/MobStateComponent.cs
+++ b/Content.Shared/Mobs/Components/MobStateComponent.cs
@@ -1,5 +1,18 @@
using Content.Shared.Damage;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Serialization.Markdown.Sequence;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.Markdown.Value;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Runtime.CompilerServices;
namespace Content.Shared.Mobs.Components;
@@ -14,92 +27,173 @@ namespace Content.Shared.Mobs.Components;
[AutoGenerateComponentState]
public sealed partial class MobStateComponent : Component
{
- ///
- /// Whether this mob will be allowed to issue movement commands when in the Critical MobState.
- ///
- [DataField]
- public bool AllowMovementWhileCrit;
+
+ [DataField("mobStateParams")]
+ public Dictionary InitMobStateParams = new()
+ {
+ {"Alive", "AliveDefault" },
+ {"SoftCritical", "SoftCriticalDefault" },
+ {"Critical", "CriticalDefault" },
+ {"Dead", "DeadDefault" }
+ };
+
+ [AutoNetworkedField, ViewVariables]
+ public Dictionary MobStateParams = new();
///
- /// Whether this mob will be allowed to issue movement commands when in the Soft-Crit MobState.
+ /// Use this to modify mobstate parameters without actually overwrithing them.
///
- [DataField]
- public bool AllowMovementWhileSoftCrit;
-
- ///
- /// Whether this mob will be allowed to issue movement commands when in the Dead MobState.
- /// This is provided for completeness sake, and *probably* shouldn't be used by default.
- ///
- [DataField]
- public bool AllowMovementWhileDead;
-
- ///
- /// Whether this mob will be allowed to talk while in the Critical MobState.
- ///
- [DataField]
- public bool AllowTalkingWhileCrit = true;
-
- ///
- /// Whether this mob will be allowed to talk while in the SoftCritical MobState.
- ///
- [DataField]
- public bool AllowTalkingWhileSoftCrit = true;
-
- ///
- /// Whether this mob will be allowed to talk while in the Dead MobState.
- /// This is provided for completeness sake, and *probably* shouldn't be used by default.
- ///
- [DataField]
- public bool AllowTalkingWhileDead;
-
- ///
- /// Whether this mob is forced to be downed when entering the Critical MobState.
- ///
- [DataField]
- public bool DownWhenCrit = true;
-
- ///
- /// Whether this mob is forced to be downed when entering the SoftCritical MobState.
- ///
- [DataField]
- public bool DownWhenSoftCrit = true;
-
- ///
- /// Whether this mob is forced to be downed when entering the Dead MobState.
- ///
- [DataField]
- public bool DownWhenDead = true;
-
- ///
- /// Whether this mob is allowed to perform hand interactions while in the Critical MobState.
- ///
- [DataField]
- public bool AllowHandInteractWhileCrit;
-
- ///
- /// Whether this mob is allowed to perform hand interactions while in the SoftCritical MobState.
- ///
- [DataField]
- public bool AllowHandInteractWhileSoftCrit;
-
- ///
- /// Whether this mob is allowed to perform hand interactions while in the Dead MobState.
- /// This is provided for completeness sake, and *probably* shouldn't be used by default.
- ///
- [DataField]
- public bool AllowHandInteractWhileDead;
+ [AutoNetworkedField, ViewVariables]
+ public Dictionary MobStateParamsOverrides = new()
+ {
+ { MobState.Alive, new() },
+ { MobState.SoftCritical, new() },
+ { MobState.Critical, new() },
+ { MobState.Dead, new() }
+ };
+ [ViewVariables]
+ public MobStateParametersPrototype CurrentStateParams => MobStateParams[CurrentState];
+ [ViewVariables]
+ public MobStateParametersOverride CurrentStateOverrides => MobStateParamsOverrides[CurrentState];
//default mobstate is always the lowest state level
[AutoNetworkedField, ViewVariables]
public MobState CurrentState { get; set; } = MobState.Alive;
- [DataField]
- [AutoNetworkedField]
- public HashSet AllowedStates = new()
- {
- MobState.Alive,
- MobState.Critical,
- MobState.SoftCritical,
- MobState.Dead
- };
+ [DataField]
+ [AutoNetworkedField]
+ public HashSet AllowedStates = new()
+ {
+ MobState.Alive,
+ MobState.SoftCritical,
+ MobState.Critical,
+ MobState.Dead
+ };
+
+
+ #region terraria wall of getters boss
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private MobStateParametersOverride GetOverride(MobState? State = null) { MobStateParamsOverrides.TryGetValue(State ?? CurrentState, out var value); return value; }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private MobStateParametersPrototype? GetParams(MobState? State = null) { MobStateParams.TryGetValue(State ?? CurrentState, out var value); return value; }
+
+
+ // the "?." and "?? false" at the end is because at round restard MobStateParams apparently can be wiped,
+ // but stuff that relies on it will still run, and I don't know why. Cool.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanMove(MobState? State = null) => GetOverride(State).Moving ?? GetParams(State)?.Moving ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanTalk(MobState? State = null) => GetOverride(State).Talking ?? GetParams(State)?.Talking ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanEmote(MobState? State = null) => GetOverride(State).Emoting ?? GetParams(State)?.Emoting ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanThrow(MobState? State = null) => GetOverride(State).Throwing ?? GetParams(State)?.Throwing ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanPickUp(MobState? State = null) => GetOverride(State).PickingUp ?? GetParams(State)?.PickingUp ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanPull(MobState? State = null) => GetOverride(State).Pulling ?? GetParams(State)?.Pulling ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanAttack(MobState? State = null) => GetOverride(State).Attacking ?? GetParams(State)?.Attacking ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanUse(MobState? State = null) => GetOverride(State).Using ?? GetParams(State)?.Using ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanPoint(MobState? State = null) =>GetOverride(State).Pointing ?? GetParams(State)?.Pointing ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsConscious(MobState? State = null) => GetOverride(State).IsConscious ?? GetParams(State)?.IsConscious ?? false;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsDowned(MobState? State = null) => GetOverride(State).ForceDown ?? GetParams(State)?.ForceDown ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool ShouldDropItems(MobState? State = null) => GetOverride(State).DropItemsOnEntering ?? GetParams(State)?.DropItemsOnEntering ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsIncapacitated(MobState? State = null) => GetOverride(State).Incapacitated ?? GetParams(State)?.Incapacitated ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsThreatening(MobState? State = null) => GetOverride(State).Threatening ?? GetParams(State)?.Threatening ?? true; // :)
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanEquipSelf(MobState? State = null) => GetOverride(State).CanEquipSelf ?? GetParams(State)?.CanEquipSelf ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanUnequipSelf(MobState? State = null) => GetOverride(State).CanUnequipSelf ?? GetParams(State)?.CanUnequipSelf ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanEquipOther(MobState? State = null) => GetOverride(State).CanEquipOther ?? GetParams(State)?.CanEquipOther ?? false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool CanUnequipOther(MobState? State = null) => GetOverride(State).CanUnequipOther ?? GetParams(State)?.CanUnequipOther ?? false;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float? GetOxyDamageOverlay(MobState? State = null) => GetOverride(State).OxyDamageOverlay ?? GetParams(State)?.OxyDamageOverlay;
+ ///
+ /// Not clamped, but you really should, in case someone decides to be funny with the prototypes.
+ /// [0,1].
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float GetBreathingMultiplier(MobState? State = null) => GetOverride(State).BreathingMultiplier ?? GetParams(State)?.BreathingMultiplier ?? 1f;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float GetStrippingTimeMultiplier(MobState? State = null) => GetOverride(State).StrippingTimeMultiplier ?? GetParams(State)?.StrippingTimeMultiplier ?? 1f;
+ #endregion
+}
+
+[NetSerializable, Serializable]
+public struct MobStateParametersOverride
+{
+ [DataField]
+ public bool? Moving, Talking, Emoting,
+ Throwing, PickingUp, Pulling, Attacking, Using, Pointing,
+ IsConscious,
+ CanEquipSelf, CanUnequipSelf, CanEquipOther, CanUnequipOther,
+ ForceDown, DropItemsOnEntering, Incapacitated, Threatening;
+
+ [DataField]
+ public float? OxyDamageOverlay, BreathingMultiplier, StrippingTimeMultiplier;
+}
+
+[Prototype("mobStateParams")]
+[Serializable] // do i need to put netserializable on it?
+public sealed partial class MobStateParametersPrototype : IPrototype, IInheritingPrototype
+{
+ [IdDataField]
+ public string ID { get; } = default!;
+
+ [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))]
+ public string[]? Parents { get; private set; }
+
+ [AbstractDataField]
+ [NeverPushInheritance]
+ public bool Abstract { get; }
+
+ [DataField]
+ public bool? Moving, Talking, Emoting,
+ Throwing, PickingUp, Pulling, Attacking, Using, Pointing,
+ IsConscious,
+ CanEquipSelf, CanUnequipSelf, CanEquipOther, CanUnequipOther,
+ ForceDown, DropItemsOnEntering, Incapacitated, Threatening;
+
+ [DataField]
+ public float? OxyDamageOverlay, BreathingMultiplier, StrippingTimeMultiplier;
+
+ public void MergeWith(MobStateParametersPrototype other)
+ {
+ this.Moving = other.Moving ?? this.Moving;
+ this.Talking = other.Talking ?? this.Talking;
+ this.Emoting = other.Emoting ?? this.Emoting;
+ this.Throwing = other.Throwing ?? this.Throwing;
+ this.PickingUp = other.PickingUp ?? this.PickingUp;
+ this.Pulling = other.Pulling ?? this.Pulling;
+ this.Attacking = other.Attacking ?? this.Attacking;
+ this.Using = other.Using ?? this.Using;
+ this.Pointing = other.Pointing ?? this.Pointing;
+ this.IsConscious = other.IsConscious ?? this.IsConscious;
+ this.CanEquipSelf = other.CanEquipSelf ?? this.CanEquipSelf;
+ this.CanEquipOther = other.CanEquipOther ?? this.CanEquipOther;
+ this.CanUnequipSelf = other.CanUnequipSelf ?? this.CanUnequipSelf;
+ this.CanUnequipOther = other.CanUnequipOther ?? this.CanUnequipOther;
+ this.ForceDown = other.ForceDown ?? this.ForceDown;
+ this.Incapacitated = other.Incapacitated ?? this.Incapacitated;
+ this.Threatening = other.Threatening ?? this.Threatening;
+ this.DropItemsOnEntering = other.DropItemsOnEntering ?? this.DropItemsOnEntering;
+ this.BreathingMultiplier = other.BreathingMultiplier ?? this.BreathingMultiplier;
+ this.OxyDamageOverlay = other.OxyDamageOverlay ?? this.OxyDamageOverlay;
+ this.StrippingTimeMultiplier = other.StrippingTimeMultiplier ?? this.StrippingTimeMultiplier;
+ }
+
}
diff --git a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs
index fe435db4d8..5d9ef74776 100644
--- a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs
+++ b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs
@@ -28,8 +28,8 @@ public sealed partial class MobThresholdsComponent : Component
public Dictionary> StateAlertDict = new()
{
{MobState.Alive, "HumanHealth"},
- {MobState.Critical, "HumanCrit"},
{MobState.SoftCritical, "HumanCrit"},
+ {MobState.Critical, "HumanCrit"},
{MobState.Dead, "HumanDead"},
};
diff --git a/Content.Shared/Mobs/MobState.cs b/Content.Shared/Mobs/MobState.cs
index 3476651e4f..c0d9f52a51 100644
--- a/Content.Shared/Mobs/MobState.cs
+++ b/Content.Shared/Mobs/MobState.cs
@@ -1,23 +1,22 @@
-using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs.Components;
using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+using System.Runtime.CompilerServices;
namespace Content.Shared.Mobs;
///
/// Defines what state an is in.
-///
/// Ordered from most alive to least alive.
-/// To enumerate them in this way see
-/// .
///
[Serializable, NetSerializable]
public enum MobState : byte
{
Invalid = 0,
Alive = 1,
- Critical = 2,
- SoftCritical = 3,
- Dead = 4,
+ SoftCritical = 2,
+ Critical = 3,
+ Dead = 4
}
///
@@ -31,9 +30,104 @@ public enum MobState : byte
public record struct MobStateChangedEvent(EntityUid Target, MobStateComponent Component, MobState OldMobState,
MobState NewMobState, EntityUid? Origin = null);
-public static class A
+
+
+///
+/// so i've decided to implement softcrit and now i have to go through
+/// all the mobstate comparisons and make sure stuff that uses MobState.Critical
+/// knows about MobState.SoftCritical.
+///
+/// A better approach to this would probably be look like "IsConscious", "IsDying" etc.
+/// which would be more generalised.
+///
+/// But for now i just want to wrap all MobState comparisons in these static methods so it's easier to deal with.
+///
+public static class MobStateHelpers
{
- //^.^
+ //^.^ // fuck off
+
+ ///
+ /// Returns true if newmobstate is crit/softcrit and the old one isn't.
+ /// if ignoreDead is true, will not return true if mobstate changed from dead to crit/softcrit.
+ ///
+ public static bool EnteredCrit(this MobStateChangedEvent ev, bool ignoreDead = true)
+ {
+ if (ignoreDead)
+ return ev.IsCrit() && ev.WasCritOrAlive();
+ return ev.IsCrit() && (ev.WasCrit() || ev.WasAlive());
+ }
+ ///
+ /// Returns true if newmobstate is crit/softcrit and the old one isn't.
+ /// if ignoreDead is true, will not return true if mobstate changed from crit/softcrit to dead.
+ ///
+ public static bool ExitedCrit(this MobStateChangedEvent ev, bool ignoreDead = true)
+ {
+ if (ignoreDead)
+ return ev.WasCrit() && ev.IsCritOrAlive();
+ return ev.WasCrit() && (ev.IsCrit() || ev.IsAlive());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsCritOrDead(this MobStateChangedEvent ev, bool CountSoftCrit = true) => ev.NewMobState.IsCritOrDead(CountSoftCrit);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsCritOrAlive(this MobStateChangedEvent ev, bool CountSoftCrit = true) => ev.NewMobState.IsCritOrAlive(CountSoftCrit);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsCrit(this MobStateChangedEvent ev, bool CountSoftCrit = true) => ev.NewMobState.IsCrit(CountSoftCrit);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsDead(this MobStateChangedEvent ev) => ev.NewMobState.IsDead();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsAlive(this MobStateChangedEvent ev) => ev.NewMobState.IsAlive();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsValid(this MobStateChangedEvent ev) => ev.NewMobState.IsValid();
+
+ public static bool WasCritOrDead(this MobStateChangedEvent ev, bool CountSoftCrit = true) => ev.OldMobState.IsCritOrDead(CountSoftCrit);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool WasCritOrAlive(this MobStateChangedEvent ev, bool CountSoftCrit = true) => ev.OldMobState.IsCritOrAlive(CountSoftCrit);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool WasCrit(this MobStateChangedEvent ev, bool CountSoftCrit = true) => ev.OldMobState.IsCrit(CountSoftCrit);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool WasDead(this MobStateChangedEvent ev) => ev.OldMobState.IsDead();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool WasAlive(this MobStateChangedEvent ev) => ev.OldMobState.IsAlive();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool WasValid(this MobStateChangedEvent ev) => ev.OldMobState.IsValid();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsCritOrDead(this MobState state, bool CountSoftCrit = true) => state.IsCrit(CountSoftCrit) || state.IsDead();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsCritOrAlive(this MobState state, bool CountSoftCrit = true) => state.IsCrit(CountSoftCrit) || state.IsDead();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsDead(this MobState state) => state == MobState.Dead;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsAlive(this MobState state) => state == MobState.Alive;
+ ///
+ /// i think i am finally losing it. I do not remember writhing this and
+ /// i cannot fathom what situation would this be warranted in.
+ ///
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsAliveOrDead(this MobState state, bool CountSoftCrit = true) => state.IsDead() || state.IsAlive();
+
+ public static bool IsCrit(this MobState state, bool CountSoftCrit = true)
+ {
+ switch (state)
+ {
+ case MobState.SoftCritical:
+ return CountSoftCrit;
+ case MobState.Critical:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsValid(this MobState state) // fix your shit. mobstate should never be invalid.
+ {
+ DebugTools.Assert(state != MobState.Invalid, "MobState is invalid.");
+ return state != MobState.Invalid;
+ }
}
//This is dumb and I hate it but I don't feel like refactoring this garbage
diff --git a/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs b/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs
index 9419daf348..20625b3464 100644
--- a/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs
+++ b/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs
@@ -14,7 +14,7 @@ public sealed class MobStateActionsSystem : EntitySystem
public override void Initialize()
{
SubscribeLocalEvent(OnMobStateChanged);
- SubscribeLocalEvent(OnMobStateComponentInit);
+ SubscribeLocalEvent(OnMobStateComponentInit);
}
private void OnMobStateChanged(EntityUid uid, MobStateActionsComponent component, MobStateChangedEvent args)
@@ -22,12 +22,12 @@ public sealed class MobStateActionsSystem : EntitySystem
ComposeActions(uid, component, args.NewMobState);
}
- private void OnMobStateComponentInit(EntityUid uid, MobStateComponent component, ComponentInit args)
+ private void OnMobStateComponentInit(EntityUid uid, MobStateActionsComponent component, ComponentStartup args)
{
- if (!TryComp(uid, out var mobStateActionsComp))
+ if (!TryComp(uid, out var mobState))
return;
- ComposeActions(uid, mobStateActionsComp, component.CurrentState);
+ ComposeActions(uid, component, mobState.CurrentState);
}
///
diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs
index f5acced4db..8e81d87757 100644
--- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs
+++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs
@@ -1,10 +1,11 @@
-using Content.Shared.Bed.Sleep;
+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.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
@@ -19,49 +20,71 @@ using Content.Shared.Strip.Components;
using Content.Shared.Throwing;
using Robust.Shared.Configuration;
using Robust.Shared.Physics.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Reflection;
+using Robust.Shared.Utility;
+using System.ComponentModel;
namespace Content.Shared.Mobs.Systems;
public partial class MobStateSystem
{
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
+ [Dependency] private readonly IReflectionManager _wehavereflectionathome = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
//General purpose event subscriptions. If you can avoid it register these events inside their own systems
private void SubscribeEvents()
{
+ SubscribeLocalEvent(OnComponentInit);
+
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(OnDirectionAttempt);
+ SubscribeLocalEvent(CheckActFactory(c => c.CanUse()));
+ SubscribeLocalEvent(CheckActFactory(c => c.CanAttack()));
+ SubscribeLocalEvent(CheckActFactory(c => c.IsConscious()));
+ SubscribeLocalEvent(CheckActFactory(c => !c.IsIncapacitated()));
+ SubscribeLocalEvent(CheckActFactory(c => c.CanThrow()));
+ SubscribeLocalEvent(CheckActFactory(c => c.CanEmote()));
+ SubscribeLocalEvent(CheckActFactory(c => c.CanPickUp()));
+ SubscribeLocalEvent(CheckActFactory(c => c.CanPickUp()));
+ SubscribeLocalEvent(CheckActFactory(c => c.CanPull()));
+ SubscribeLocalEvent(CheckActFactory(c => !c.IsDowned()));
+ SubscribeLocalEvent(CheckActFactory(c => c.CanPoint()));
SubscribeLocalEvent(OnCombatModeShouldHandInteract);
SubscribeLocalEvent(OnAttemptPacifiedAttack);
SubscribeLocalEvent(OnUnbuckleAttempt);
}
+ private void OnComponentInit(EntityUid uid, MobStateComponent comp, ComponentInit args)
+ {
+ foreach(var entry in comp.InitMobStateParams)
+ {
+ Enum? e = null;
+ DebugTools.Assert(_wehavereflectionathome.TryParseEnumReference($"enum.MobState.{entry.Key}", out e), $"MobState.{entry.Key} does not exist.");
+
+ MobState state = (MobState) e;
+
+ // if this fails, then either the prototype has two parameters specified for one mobstate,
+ // or we've already received the params from the server before we had the chance to set them
+ // ourselves. (see TraitModifyMobState)
+ if (comp.MobStateParams.TryAdd(state, _proto.Index(entry.Value)))
+ {
+ //comp.MobStateParams[state].FillDefaults();
+ }
+ }
+ }
+
+
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)
+ if (ent.Comp.CanMove())
return;
args.Cancel();
@@ -69,37 +92,19 @@ public partial class MobStateSystem
private void OnMoveAttempt(Entity ent, ref UpdateCanMoveEvent 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)
+ if (ent.Comp.CanMove())
return;
args.Cancel();
}
- private void OnUnbuckleAttempt(Entity ent, ref UnbuckleAttemptEvent args)
+ private void OnUnbuckleAttempt(EntityUid uid, MobStateComponent comp, 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;
- }
+ if (args.User.HasValue && IsIncapacitated(args.User.Value))
+ args.Cancel();
}
private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state)
@@ -109,24 +114,20 @@ public partial class MobStateSystem
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);
+ case MobState.Critical:
+ //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);
-
- if (!_standing.IsDown(target) && TryComp(target, out var physics))
- _physics.SetCanCollide(target, true, body: physics);
-
+ //if (component.CurrentState is MobState.Alive)
+ // _standing.Stand(target);
+ //
+ //if (!_standing.IsDown(target) && TryComp(target, out var physics))
+ // _physics.SetCanCollide(target, true, body: physics);
break;
case MobState.Invalid:
//unused
@@ -138,6 +139,14 @@ public partial class MobStateSystem
private void OnStateEnteredSubscribers(EntityUid target, MobStateComponent component, MobState state)
{
+ if (component.ShouldDropItems() && HasComp(target))
+ RaiseLocalEvent(target, new DropHandItemsEvent());
+
+ if (component.IsDowned())
+ _standing.Down(target, dropHeldItems: false);
+ else
+ _standing.Stand(target);
+
// 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)
@@ -147,26 +156,16 @@ public partial class MobStateSystem
switch (state)
{
case MobState.Alive:
- _standing.Stand(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Alive);
break;
- case MobState.Critical:
- if (component.DownWhenCrit)
- _standing.Down(target);
- _appearance.SetData(target, MobStateVisuals.State, MobState.Critical);
- break;
case MobState.SoftCritical:
- if (component.DownWhenSoftCrit)
- _standing.Down(target);
+ case MobState.Critical:
_appearance.SetData(target, MobStateVisuals.State, MobState.Critical);
break;
case MobState.Dead:
EnsureComp(target);
- if (component.DownWhenDead)
- _standing.Down(target);
-
- if (_standing.IsDown(target) && TryComp(target, out var physics))
- _physics.SetCanCollide(target, false, body: physics);
+ //if (_standing.IsDown(target) && TryComp(target, out var physics))
+ // _physics.SetCanCollide(target, false, body: physics);
_appearance.SetData(target, MobStateVisuals.State, MobState.Dead);
break;
@@ -190,11 +189,7 @@ public partial class MobStateSystem
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;
+ args.Multiplier *= component.GetStrippingTimeMultiplier();
}
private void OnSpeakAttempt(EntityUid uid, MobStateComponent component, SpeakAttemptEvent args)
@@ -205,57 +200,55 @@ public partial class MobStateSystem
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)
+ if (component.CanTalk())
return;
args.Cancel();
}
- private void CheckAct(EntityUid target, MobStateComponent component, CancellableEntityEventArgs args)
+ ///
+ /// anti-boilerplate.
+ /// Cancels the event if predicate returns false.
+ ///
+ private ComponentEventHandler CheckActFactory(Func predicate)
{
- switch (component.CurrentState)
+ return (EntityUid uid, MobStateComponent component, CancellableEntityEventArgs args) =>
{
- case MobState.Dead:
- case MobState.SoftCritical:
- case MobState.Critical:
+ if (!predicate(component))
args.Cancel();
- break;
- }
+ };
}
- private void OnEquipAttempt(EntityUid target, MobStateComponent component, IsEquippingAttemptEvent args)
+ //private void CheckAct(EntityUid target, MobStateComponent component, CancellableEntityEventArgs args)
+ //{
+ // if (!CanHandInteract(target, component))
+ // args.Cancel();
+ //}
+
+ private void OnEquipAttempt(EntityUid uid, MobStateComponent component, IsEquippingAttemptEvent args)
{
// is this a self-equip, or are they being stripped?
- if (args.Equipee == target)
- CheckAct(target, component, args);
+ if (uid == args.Equipee)
+ if (uid == args.EquipTarget && !component.CanEquipSelf() ||
+ uid != args.EquipTarget && !component.CanEquipOther())
+ args.Cancel();
}
- private void OnUnequipAttempt(EntityUid target, MobStateComponent component, IsUnequippingAttemptEvent args)
+ private void OnUnequipAttempt(EntityUid uid, MobStateComponent component, IsUnequippingAttemptEvent args)
{
// is this a self-equip, or are they being stripped?
- if (args.Unequipee == target)
- CheckAct(target, component, args);
+ if (uid == args.Unequipee)
+ if (uid == args.UnEquipTarget && !component.CanUnequipSelf() ||
+ uid != args.UnEquipTarget && !component.CanUnequipOther())
+ args.Cancel();
}
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;
+ // Disallow empty-hand-interacting in combat mode
+ // for non-dead mobs
+ if (!IsDead(uid, component))
+ args.Cancelled = true;
}
private void OnAttemptPacifiedAttack(Entity ent, ref AttemptPacifiedAttackEvent args)
diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.cs b/Content.Shared/Mobs/Systems/MobStateSystem.cs
index 9cdd055c15..61a9993ed9 100644
--- a/Content.Shared/Mobs/Systems/MobStateSystem.cs
+++ b/Content.Shared/Mobs/Systems/MobStateSystem.cs
@@ -49,12 +49,34 @@ public partial class MobStateSystem : EntitySystem
/// The MobState component owned by the target
/// If the entity is Critical
public bool IsCritical(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CurrentState.IsCrit();
+ }
+
+ public bool IsCriticalOrDead(EntityUid target, MobStateComponent? component = null) => IsCriticalOrDead(target, true, component);
+ public bool IsCriticalOrDead(EntityUid target, bool countSoftCrit = true, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CurrentState.IsCritOrDead(countSoftCrit);
+ }
+
+ public bool IsHardCritical(EntityUid target, MobStateComponent? component = null)
{
if (!Resolve(target, ref component, false))
return false;
return component.CurrentState == MobState.Critical;
}
+ public bool IsSoftCritical(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CurrentState == MobState.SoftCritical;
+ }
+
///
/// Check if a Mob is Dead
///
@@ -69,26 +91,8 @@ public partial class MobStateSystem : EntitySystem
return component.CurrentState == MobState.Dead;
}
- public bool IsSoftCritical(EntityUid target, MobStateComponent? component = null)
- {
- if (!Resolve(target, ref component, false))
- return false;
- return component.CurrentState == MobState.SoftCritical;
- }
- ///
- /// Check if a Mob is Critical or Dead or SoftCrit
- ///
- /// Target Entity
- /// The MobState component owned by the target
- /// If the entity is Critical or Dead
- public bool IsIncapacitated(EntityUid target, MobStateComponent? component = null)
- {
- if (!Resolve(target, ref component, false))
- return false;
- return component.CurrentState is MobState.Critical or MobState.Dead or MobState.SoftCritical;
- }
///
/// Check if a Mob is in an Invalid state
@@ -97,12 +101,158 @@ public partial class MobStateSystem : EntitySystem
/// The MobState component owned by the target
/// If the entity is in an Invalid State
public bool IsInvalidState(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, true)) // i think this one should log it
+ return false;
+ return component.CurrentState.IsValid();
+ }
+
+
+ public bool CanMove(EntityUid target, MobStateComponent? component = null)
{
if (!Resolve(target, ref component, false))
return false;
- return component.CurrentState is MobState.Invalid;
+ return component.CanMove();
}
+ public bool CanSpeak(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanTalk();
+ }
+
+ public bool CanEmote(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanEmote();
+ }
+
+ public bool CanThrow(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanThrow();
+ }
+
+ public bool CanPickUp(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanPickUp();
+ }
+
+ public bool CanPull(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanPull();
+ }
+
+ public bool CanAttack(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanAttack();
+ }
+
+ public bool CanUse(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanUse();
+ }
+
+ public bool CanPoint(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanPoint();
+ }
+
+ public bool ConsciousAttemptAllowed(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.IsConscious();
+ }
+
+ public bool IsDown(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.IsDowned();
+ }
+
+ public bool IsConscious(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.IsConscious();
+ }
+
+
+ ///
+ /// Check if a Mob is incapacitated in its current MobState.
+ ///
+ /// Target Entity
+ /// The MobState component owned by the target
+ /// If the entity is Critical or Dead
+ public bool IsIncapacitated(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.IsIncapacitated();
+ }
+
+ public bool CanEquipSelf(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanEquipSelf();
+ }
+
+ public bool CanUnequipSelf(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanUnequipSelf();
+ }
+
+ public bool CanEquipOther(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanEquipOther();
+ }
+
+ public bool CanUnequipOther(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.CanUnequipOther();
+ }
+
+
+
+ public bool IsThreatening(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return false;
+ return component.IsThreatening();
+ }
+ ///
+ /// Clamped to [0,1]. Use to get unclamped value.
+ ///
+ public float BreatheMultiplier(EntityUid target, MobStateComponent? component = null)
+ {
+ if (!Resolve(target, ref component, false))
+ return 0f;
+ return Math.Clamp(component.GetBreathingMultiplier(), 0, 1);
+ }
+
+
#endregion
#region Private Implementation
diff --git a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs
index a5307c4ba5..624dc71122 100644
--- a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs
+++ b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs
@@ -136,6 +136,20 @@ public sealed class MobThresholdSystem : EntitySystem
return false;
}
+ // WD EDIT START
+ public bool TryGetThresholdForState(EntityUid target, MobState mobState, MobState fallbackMobState,
+ [NotNullWhen(true)] out FixedPoint2? threshold,
+ MobThresholdsComponent? thresholdComponent = null)
+ {
+ threshold = null;
+ if (TryGetThresholdForState(target, mobState, out threshold, thresholdComponent))
+ return true;
+ if (TryGetThresholdForState(target, fallbackMobState, out threshold, thresholdComponent))
+ return true;
+ return false;
+ }
+ // WD EDIT END
+
///
/// Try to get the a percentage of the Damage Threshold for the appropriate state if it exists
///
@@ -436,11 +450,11 @@ public sealed class MobThresholdSystem : EntitySystem
private void OnUpdateMobState(EntityUid target, MobThresholdsComponent component, ref UpdateMobStateEvent args)
{
- if (!component.AllowRevives && component.CurrentThresholdState == MobState.Dead)
+ if (!component.AllowRevives && component.CurrentThresholdState.IsDead())
{
args.State = MobState.Dead;
}
- else if (component.CurrentThresholdState != MobState.Invalid)
+ else if (component.CurrentThresholdState.IsValid())
{
args.State = component.CurrentThresholdState;
}
diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs
index bb6951f2de..fb785c7564 100644
--- a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs
+++ b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs
@@ -311,8 +311,7 @@ namespace Content.Shared.Movement.Systems
if (MoverQuery.TryGetComponent(entity, out var mover))
SetMoveInput((entity, mover), MoveButtons.None);
- if (_mobState.IsDead(entity)
- || _mobState.IsCritical(entity) && !_configManager.GetCVar(CCVars.AllowMovementWhileCrit))
+ if (!_mobState.CanMove(entity))
return;
HandleDirChange(relayMover.RelayEntity, dir, subTick, state);
diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs
index 701f32f0c7..6aae9c26dd 100644
--- a/Content.Shared/Movement/Systems/SharedMoverController.cs
+++ b/Content.Shared/Movement/Systems/SharedMoverController.cs
@@ -128,7 +128,7 @@ namespace Content.Shared.Movement.Systems
if (_mobState.IsDead(relayTarget.Source)
|| TryComp(relayTarget.Source, out _)
|| !MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover)
- || _mobState.IsCritical(relayTarget.Source) && !_configManager.GetCVar(CCVars.AllowMovementWhileCrit))
+ || _mobState.CanMove(uid))
{
canMove = false;
}
diff --git a/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs b/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs
index 9b7a6954de..0f66dff309 100644
--- a/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs
+++ b/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs
@@ -12,7 +12,7 @@ namespace Content.Shared.Interaction
private void OnInteractionAttempt(EntityUid uid, NoNormalInteractionComponent component, InteractionAttemptEvent args)
{
- args.Cancelled = true;
+ args.Cancel();
}
}
}
diff --git a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs
index 448311471f..7dc8578117 100644
--- a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs
+++ b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs
@@ -143,7 +143,7 @@ public abstract partial class SharedPseudoItemSystem : EntitySystem
private void OnInteractAttempt(EntityUid uid, PseudoItemComponent component, InteractionAttemptEvent args)
{
if (args.Uid == args.Target && component.Active)
- args.Cancelled = true;
+ args.Cancel();
}
private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args)
diff --git a/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs b/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs
index e3fa21ed37..0ea15f944e 100644
--- a/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs
+++ b/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs
@@ -27,7 +27,7 @@ public abstract class SharedVentriloquistPuppetSystem : EntitySystem
private void CancelInteract(Entity ent, ref InteractionAttemptEvent args)
{
- args.Cancelled = true;
+ args.Cancel();
}
private void OnStartup(EntityUid uid, VentriloquistPuppetComponent component, ComponentStartup args)
diff --git a/Content.Shared/Shadowkin/SharedEtherealSystem.cs b/Content.Shared/Shadowkin/SharedEtherealSystem.cs
index 6d6dd3daa2..55191f9bbf 100644
--- a/Content.Shared/Shadowkin/SharedEtherealSystem.cs
+++ b/Content.Shared/Shadowkin/SharedEtherealSystem.cs
@@ -17,6 +17,7 @@ using Content.Shared.Tag;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Standing;
+using Content.Shared.Mobs.Systems;
namespace Content.Shared.Shadowkin;
@@ -30,6 +31,7 @@ public abstract class SharedEtherealSystem : EntitySystem
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly StandingStateSystem _standingState = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
public override void Initialize()
{
@@ -95,8 +97,7 @@ public abstract class SharedEtherealSystem : EntitySystem
private void OnMobStateChanged(EntityUid uid, EtherealComponent component, MobStateChangedEvent args)
{
- if (args.NewMobState == MobState.Critical
- || args.NewMobState == MobState.Dead)
+ if (_mobState.IsIncapacitated(uid))
{
SpawnAtPosition("ShadowkinShadow", Transform(uid).Coordinates);
SpawnAtPosition("EffectFlashShadowkinDarkSwapOff", Transform(uid).Coordinates);
@@ -137,7 +138,7 @@ public abstract class SharedEtherealSystem : EntitySystem
|| HasComp(args.Target))
return;
- args.Cancelled = true;
+ args.Cancel();
if (_gameTiming.InPrediction)
return;
diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs
index ca54b5a853..e8c65a67be 100644
--- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs
+++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs
@@ -140,11 +140,15 @@ public abstract partial class SharedStationAiSystem
private void OnHeldInteraction(Entity ent, ref InteractionAttemptEvent args)
{
// Cancel if it's not us or something with a whitelist, or whitelist is disabled.
- args.Cancelled = (!TryComp(args.Target, out StationAiWhitelistComponent? whitelistComponent)
- || !whitelistComponent.Enabled)
- && ent.Owner != args.Target
- && args.Target != null;
- if (whitelistComponent is { Enabled: false })
+ if ((!TryComp(args.Target, out var whitelistComponent)
+ || !whitelistComponent.Enabled)
+ && ent.Owner != args.Target
+ && args.Target != null)
+ {
+ args.Cancel();
+ }
+
+ if (whitelistComponent is {Enabled: false })
{
ShowDeviceNotRespondingPopup(ent.Owner);
}
diff --git a/Content.Shared/Standing/SharedLayingDownSystem.cs b/Content.Shared/Standing/SharedLayingDownSystem.cs
index ec5f2a6d66..4e5dde3776 100644
--- a/Content.Shared/Standing/SharedLayingDownSystem.cs
+++ b/Content.Shared/Standing/SharedLayingDownSystem.cs
@@ -102,8 +102,8 @@ public abstract class SharedLayingDownSystem : EntitySystem
RaiseNetworkEvent(new CheckAutoGetUpEvent(GetNetEntity(uid)));
- if (HasComp(uid)
- || !_mobState.IsAlive(uid))
+ if (HasComp(uid))
+ //|| _mobState.IsDown(uid))
return;
if (_standing.IsDown(uid, standing))
@@ -116,7 +116,7 @@ public abstract class SharedLayingDownSystem : EntitySystem
{
if (args.Handled || args.Cancelled
|| HasComp(uid)
- || _mobState.IsIncapacitated(uid)
+ || _mobState.IsDown(uid)
|| !_standing.Stand(uid))
component.CurrentState = StandingState.Lying;
@@ -153,7 +153,7 @@ public abstract class SharedLayingDownSystem : EntitySystem
if (!Resolve(uid, ref standingState, false)
|| !Resolve(uid, ref layingDown, false)
|| standingState.CurrentState is not StandingState.Lying
- || !_mobState.IsAlive(uid)
+ || _mobState.IsDown(uid)
|| TerminatingOrDeleted(uid)
|| !TryComp(uid, out var body)
|| body.LegEntities.Count < body.RequiredLegs
diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs
index f82a3cc745..217790c410 100644
--- a/Content.Shared/Stunnable/SharedStunSystem.cs
+++ b/Content.Shared/Stunnable/SharedStunSystem.cs
@@ -81,7 +81,7 @@ public abstract class SharedStunSystem : EntitySystem
private void OnAttemptInteract(Entity ent, ref InteractionAttemptEvent args)
{
- args.Cancelled = true;
+ args.Cancel();
}
private void OnMobStateChanged(EntityUid uid, MobStateComponent component, MobStateChangedEvent args)
@@ -93,6 +93,7 @@ public abstract class SharedStunSystem : EntitySystem
{
case MobState.Alive:
break;
+ // case MobState.SoftCritical:
case MobState.Critical:
{
_statusEffect.TryRemoveStatusEffect(uid, "Stun");
diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs
index cebc84ecb9..1cca016dc7 100644
--- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs
+++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs
@@ -49,7 +49,7 @@ namespace Content.Shared.SubFloor
{
// No interactions with entities hidden under floor tiles.
if (component.BlockInteractions && component.IsUnderCover)
- args.Cancelled = true;
+ args.Cancel();
}
private void OnSubFloorStarted(EntityUid uid, SubFloorHideComponent component, ComponentStartup _)
diff --git a/Content.Shared/Traits/Assorted/Components/CritModifierComponent.cs b/Content.Shared/Traits/Assorted/Components/CritModifierComponent.cs
index 6cf11e3f7f..4cd301e5c0 100644
--- a/Content.Shared/Traits/Assorted/Components/CritModifierComponent.cs
+++ b/Content.Shared/Traits/Assorted/Components/CritModifierComponent.cs
@@ -13,4 +13,12 @@ public sealed partial class CritModifierComponent : Component
///
[DataField]
public int CritThresholdModifier { get; private set; } = 0;
-}
\ No newline at end of file
+
+ // WD EDIT START
+ ///
+ /// The amount that an entity's softcrit threshold will be incremented by.
+ ///
+ [DataField]
+ public int SoftCritThresholdModifier { get; private set; } = 0;
+ // WD EDIT END
+}
diff --git a/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs b/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs
index 97b88f559e..e0a6133048 100644
--- a/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs
+++ b/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs
@@ -29,9 +29,11 @@ public sealed partial class TraitStatModifierSystem : EntitySystem
if (!TryComp(uid, out var threshold))
return;
- var critThreshold = _threshold.GetThresholdForState(uid, Mobs.MobState.Critical, threshold);
- if (critThreshold != 0)
- _threshold.SetMobStateThreshold(uid, critThreshold + component.CritThresholdModifier, Mobs.MobState.Critical);
+ if(_threshold.TryGetThresholdForState(uid, Mobs.MobState.Critical, out var critThreshold))
+ _threshold.SetMobStateThreshold(uid, critThreshold.Value + component.CritThresholdModifier, Mobs.MobState.Critical);
+
+ if(_threshold.TryGetThresholdForState(uid, Mobs.MobState.SoftCritical, out var softCritThreshold))
+ _threshold.SetMobStateThreshold(uid, softCritThreshold.Value + component.SoftCritThresholdModifier, Mobs.MobState.SoftCritical);
}
private void OnDeadStartup(EntityUid uid, DeadModifierComponent component, ComponentStartup args)
diff --git a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs
index 3d7f9df458..ece7731254 100644
--- a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs
+++ b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs
@@ -69,7 +69,7 @@ public abstract partial class SharedTetherGunSystem : EntitySystem
private void OnTetheredBuckleAttempt(EntityUid uid, TetheredComponent component, ref BuckleAttemptEvent args)
{
- args.Cancelled = true;
+ args.Cancel();
}
private void OnTetheredUpdateCanMove(EntityUid uid, TetheredComponent component, UpdateCanMoveEvent args)
diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml
index a5c3065c4d..cbfb52a0be 100644
--- a/Resources/Prototypes/Entities/Mobs/base.yml
+++ b/Resources/Prototypes/Entities/Mobs/base.yml
@@ -92,10 +92,16 @@
- type: RadiationReceiver
- type: Stamina
- type: MobState
+ mobStateParams:
+ Alive: AliveDefault
+ SoftCritical: SoftCriticalDefault
+ Critical: CriticalDefault
+ Dead: DeadDefault
- type: MobThresholds
thresholds:
0: Alive
- 100: Critical
+ 100: SoftCritical
+ 130: Critical
200: Dead
- type: MobStateActions
actions:
diff --git a/Resources/Prototypes/NPCs/utility_queries.yml b/Resources/Prototypes/NPCs/utility_queries.yml
index 3d5385ecbc..9aad83f096 100644
--- a/Resources/Prototypes/NPCs/utility_queries.yml
+++ b/Resources/Prototypes/NPCs/utility_queries.yml
@@ -86,7 +86,7 @@
query:
- !type:NearbyHostilesQuery
considerations:
- - !type:TargetIsAliveCon
+ - !type:TargetIsThreateningCon
curve: !type:BoolCurve
- !type:TargetDistanceCon
curve: !type:PresetCurve
@@ -104,7 +104,7 @@
query:
- !type:NearbyHostilesQuery
considerations:
- - !type:TargetIsAliveCon
+ - !type:TargetIsThreateningCon
curve: !type:BoolCurve
- !type:TargetDistanceCon
curve: !type:PresetCurve
@@ -164,7 +164,7 @@
query:
- !type:NearbyHostilesQuery
considerations:
- - !type:TargetIsAliveCon
+ - !type:TargetIsThreateningCon
curve: !type:BoolCurve
- !type:TargetDistanceCon
curve: !type:PresetCurve
diff --git a/Resources/Prototypes/mobStateParams/default.yml b/Resources/Prototypes/mobStateParams/default.yml
new file mode 100644
index 0000000000..fc0384dd4c
--- /dev/null
+++ b/Resources/Prototypes/mobStateParams/default.yml
@@ -0,0 +1,131 @@
+#moving
+#talking
+#emoting
+#throwing
+#pickingUp - Allows picking items up or dropping them.
+#pulling
+#attacking
+#using - Blocks UseAttemptEvent if false.
+#pointing
+#isConscious - Used to check if a mob in this state is to be considered "conscious". Effects may differ based on how entity systems interpret this value.
+#canEquipSelf
+#canUnequipSelf
+#canEquipOther
+#canUnequipOther
+#forceDown - Forces mob in this state to be down. (i.e. crawling)
+#dropItemsOnEntering - Forces mob to drop held items when entering this state.
+#incapacitated - Blocks InteractionAttemptEvents if true. Effects may differ based on how entity systems interpret this value.
+#threatening - Used in mob AI. If false, will not be considered as a target by hostile AI.
+#breathingMultiplier - Fraction of Atmospherics.BreathVolume this mob will take, with 1 being full, 0.5 being half, 0 being none at all.
+# Also modifies the amount of damage the mob will take when suffocating, with the inverse of this value. (1 / breathingMultiplier).
+# Clamped to [0,1].
+#oxyDamageOverlay - If set, controls the black vingette that is used for crit blackout. If not, overlay is drawn depending on current oxy damage..
+#strippingTimeMultiplier - How fast this mob can be stripped by other mobs. Less is faster.
+
+
+- type: mobStateParams
+ id: AliveDefault
+ moving: true
+ talking: true
+ emoting: true
+ throwing: true
+ pickingUp: true
+ pulling: true
+ attacking: true
+ using: true
+ pointing: true
+ isConscious: true
+ threatening: true
+ canEquipSelf: true
+ canUnequipSelf: true
+ canEquipOther: true
+ canUnequipOther: true
+ #forceDown: false # both default to false if not specified
+ #incapacitated: false #
+ #breathingMultiplier: 1 # defaults to 1 if not specified
+
+- type: mobStateParams
+ id: SoftCriticalDefault
+ moving: true
+ talking: true
+ emoting: true
+ throwing: false
+ pickingUp: false
+ pulling: false
+ attacking: false
+ using: true
+ pointing: true
+ isConscious: true
+ threatening: true
+ canEquipSelf: false
+ canUnequipSelf: false
+ canEquipOther: false
+ canUnequipOther: false
+ forceDown: true
+ dropItemsOnEntering: true
+ incapacitated: true
+ breathingMultiplier: 0.25 # This should probably be handled by lungs instead.
+ oxyDamageOverlay: 0.75
+ strippingTimeMultiplier: 0.5
+
+
+- type: mobStateParams
+ id: CriticalDefault
+ moving: false
+ talking: false
+ emoting: false
+ throwing: false
+ pickingUp: false
+ pulling: false
+ attacking: false
+ using: false
+ pointing: false
+ isConscious: false
+ threatening: false
+ canEquipSelf: false
+ canUnequipSelf: false
+ canEquipOther: false
+ canUnequipOther: false
+ forceDown: true
+ dropItemsOnEntering: true
+ incapacitated: true
+ breathingMultiplier: 0
+ oxyDamageOverlay: 1
+ strippingTimeMultiplier: 0.33
+
+- type: mobStateParams
+ id: DeadDefault
+ moving: false
+ talking: false
+ emoting: false
+ throwing: false
+ pickingUp: false
+ pulling: false
+ attacking: false
+ using: false
+ pointing: false
+ isConscious: false
+ threatening: false
+ canEquipSelf: false
+ canUnequipSelf: false
+ canEquipOther: false
+ canUnequipOther: false
+ forceDown: true
+ dropItemsOnEntering: true
+ incapacitated: true
+ breathingMultiplier: 0
+ oxyDamageOverlay: 0
+ strippingTimeMultiplier: 0.33
+
+- type: mobStateParams
+ id: DeadGripMod
+ dropItemsOnEntering: false
+
+- type: mobStateParams
+ id: AuxiliaryDiaphragmSoftCrit
+ breathingMultiplier: 0.75
+
+- type: mobStateParams
+ id: AuxiliaryDiaphragmHardCrit
+ breathingMultiplier: 0.25
+