Mobstate Refactor (#13389)
Refactors mobstate and moves mob health thresholds to their own component Co-authored-by: DrSmugleaf <drsmugleaf@gmail.com>
This commit is contained in:
@@ -54,7 +54,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
|
||||
{
|
||||
if (damageVisComp.Thresholds.Count < 1)
|
||||
{
|
||||
Logger.ErrorS(SawmillName, $"Thresholds were invalid for entity {entity}. Thresholds: {damageVisComp.Thresholds}");
|
||||
Logger.ErrorS(SawmillName, $"ThresholdsLookup were invalid for entity {entity}. ThresholdsLookup: {damageVisComp.Thresholds}");
|
||||
damageVisComp.Valid = false;
|
||||
return;
|
||||
}
|
||||
@@ -144,7 +144,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
|
||||
|
||||
if (damageVisComp.Thresholds[0] != 0)
|
||||
{
|
||||
Logger.ErrorS(SawmillName, $"Thresholds were invalid for entity {entity}. Thresholds: {damageVisComp.Thresholds}");
|
||||
Logger.ErrorS(SawmillName, $"ThresholdsLookup were invalid for entity {entity}. ThresholdsLookup: {damageVisComp.Thresholds}");
|
||||
damageVisComp.Valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.Mobs;
|
||||
using Robust.Client.GameObjects;
|
||||
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
|
||||
|
||||
namespace Content.Client.MobState;
|
||||
namespace Content.Client.DamageState;
|
||||
|
||||
public sealed class DamageStateVisualizerSystem : VisualizerSystem<DamageStateVisualsComponent>
|
||||
{
|
||||
@@ -10,7 +10,7 @@ public sealed class DamageStateVisualizerSystem : VisualizerSystem<DamageStateVi
|
||||
{
|
||||
var sprite = args.Sprite;
|
||||
|
||||
if (sprite == null || !args.Component.TryGetData(DamageStateVisuals.State, out DamageState data))
|
||||
if (sprite == null || !args.Component.TryGetData(MobStateVisuals.State, out MobState data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -24,8 +24,8 @@ public sealed class DamageStateVisualizerSystem : VisualizerSystem<DamageStateVi
|
||||
{
|
||||
sprite.NoRotation = data switch
|
||||
{
|
||||
DamageState.Critical => false,
|
||||
DamageState.Dead => false,
|
||||
MobState.Critical => false,
|
||||
MobState.Dead => false,
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public sealed class DamageStateVisualizerSystem : VisualizerSystem<DamageStateVi
|
||||
}
|
||||
|
||||
// So they don't draw over mobs anymore
|
||||
if (data == DamageState.Dead)
|
||||
if (data == MobState.Dead)
|
||||
{
|
||||
if (sprite.DrawDepth > (int) DrawDepth.FloorObjects)
|
||||
{
|
||||
@@ -1,13 +1,13 @@
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.Mobs;
|
||||
|
||||
namespace Content.Client.MobState;
|
||||
namespace Content.Client.DamageState;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class DamageStateVisualsComponent : Component
|
||||
{
|
||||
public int? OriginalDrawDepth;
|
||||
|
||||
[DataField("states")] public Dictionary<DamageState, Dictionary<DamageStateVisualLayers, string>> States = new();
|
||||
[DataField("states")] public Dictionary<MobState, Dictionary<DamageStateVisualLayers, string>> States = new();
|
||||
|
||||
/// <summary>
|
||||
/// Should noRot be turned off when crit / dead.
|
||||
@@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||
using Content.Client.HealthOverlay.UI;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using Content.Client.IoC;
|
||||
using Content.Client.MobState;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -84,11 +85,10 @@ namespace Content.Client.HealthOverlay.UI
|
||||
}
|
||||
|
||||
var mobStateSystem = _entities.EntitySysManager.GetEntitySystem<MobStateSystem>();
|
||||
FixedPoint2 threshold;
|
||||
|
||||
var mobThresholdSystem = _entities.EntitySysManager.GetEntitySystem<MobThresholdSystem>();
|
||||
if (mobStateSystem.IsAlive(mobState.Owner, mobState))
|
||||
{
|
||||
if (!mobStateSystem.TryGetEarliestCriticalState(mobState, damageable.TotalDamage, out _, out threshold))
|
||||
if (!mobThresholdSystem.TryGetThresholdForState(Entity,MobState.Critical, out var threshold))
|
||||
{
|
||||
CritBar.Visible = false;
|
||||
HealthBar.Visible = false;
|
||||
@@ -97,7 +97,7 @@ namespace Content.Client.HealthOverlay.UI
|
||||
|
||||
CritBar.Ratio = 1;
|
||||
CritBar.Visible = true;
|
||||
HealthBar.Ratio = 1 - (damageable.TotalDamage / threshold).Float();
|
||||
HealthBar.Ratio = 1 - ((FixedPoint2)(damageable.TotalDamage / threshold)).Float();
|
||||
HealthBar.Visible = true;
|
||||
}
|
||||
else if (mobStateSystem.IsCritical(mobState.Owner, mobState))
|
||||
@@ -105,8 +105,8 @@ namespace Content.Client.HealthOverlay.UI
|
||||
HealthBar.Ratio = 0;
|
||||
HealthBar.Visible = false;
|
||||
|
||||
if (!mobStateSystem.TryGetPreviousCriticalState(mobState, damageable.TotalDamage, out _, out var critThreshold) ||
|
||||
!mobStateSystem.TryGetEarliestDeadState(mobState, damageable.TotalDamage, out _, out var deadThreshold))
|
||||
if (!mobThresholdSystem.TryGetThresholdForState(Entity, MobState.Critical, out var critThreshold) ||
|
||||
!mobThresholdSystem.TryGetThresholdForState(Entity, MobState.Dead, out var deadThreshold))
|
||||
{
|
||||
CritBar.Visible = false;
|
||||
return;
|
||||
@@ -115,7 +115,7 @@ namespace Content.Client.HealthOverlay.UI
|
||||
CritBar.Visible = true;
|
||||
CritBar.Ratio = 1 -
|
||||
((damageable.TotalDamage - critThreshold) /
|
||||
(deadThreshold - critThreshold)).Float();
|
||||
(deadThreshold - critThreshold)).Value.Float();
|
||||
}
|
||||
else if (mobStateSystem.IsDead(mobState.Owner, mobState))
|
||||
{
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.MobState.EntitySystems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.MobState;
|
||||
|
||||
public sealed partial class MobStateSystem : SharedMobStateSystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
private Overlays.DamageOverlay _overlay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_overlay = new Overlays.DamageOverlay();
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
|
||||
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttach);
|
||||
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetach);
|
||||
SubscribeLocalEvent<MobStateComponent, ComponentHandleState>(OnMobHandleState);
|
||||
SubscribeLocalEvent<MobStateComponent, AttackAttemptEvent>(OnAttack);
|
||||
}
|
||||
|
||||
private void OnAttack(EntityUid uid, MobStateComponent component, AttackAttemptEvent args)
|
||||
{
|
||||
if (IsIncapacitated(uid, component))
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnMobHandleState(EntityUid uid, MobStateComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not MobStateComponentState state) return;
|
||||
|
||||
if (component.CurrentThreshold == state.CurrentThreshold)
|
||||
return;
|
||||
|
||||
if (state.CurrentThreshold == null)
|
||||
{
|
||||
RemoveState(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateState(component, state.CurrentThreshold.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerAttach(PlayerAttachedEvent ev)
|
||||
{
|
||||
if (TryComp<MobStateComponent>(ev.Entity, out var mobState) && TryComp<DamageableComponent>(ev.Entity, out var damageable))
|
||||
{
|
||||
_overlay.DeadLevel = 0f;
|
||||
SetLevel(mobState, damageable.TotalDamage);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerDetach(PlayerDetachedEvent ev)
|
||||
{
|
||||
ClearOverlay();
|
||||
}
|
||||
|
||||
private void ClearOverlay()
|
||||
{
|
||||
_overlay.State = DamageState.Alive;
|
||||
_overlay.BruteLevel = 0f;
|
||||
_overlay.OxygenLevel = 0f;
|
||||
_overlay.CritLevel = 0f;
|
||||
}
|
||||
|
||||
protected override void UpdateState(MobStateComponent component, DamageState? state, FixedPoint2 threshold)
|
||||
{
|
||||
base.UpdateState(component, state, threshold);
|
||||
SetLevel(component, threshold);
|
||||
}
|
||||
|
||||
private void SetLevel(MobStateComponent stateComponent, FixedPoint2 threshold)
|
||||
{
|
||||
var uid = stateComponent.Owner;
|
||||
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity != uid) return;
|
||||
|
||||
ClearOverlay();
|
||||
|
||||
if (!TryComp<DamageableComponent>(uid, out var damageable))
|
||||
return;
|
||||
|
||||
switch (stateComponent.CurrentState)
|
||||
{
|
||||
case DamageState.Dead:
|
||||
_overlay.State = DamageState.Dead;
|
||||
return;
|
||||
}
|
||||
|
||||
var bruteLevel = 0f;
|
||||
var oxyLevel = 0f;
|
||||
var critLevel = 0f;
|
||||
|
||||
if (TryGetEarliestIncapacitatedState(stateComponent, threshold, out _, out var earliestThreshold) && damageable.TotalDamage != 0)
|
||||
{
|
||||
if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
||||
{
|
||||
bruteLevel = MathF.Min(1f, (bruteDamage / earliestThreshold).Float());
|
||||
}
|
||||
|
||||
if (damageable.Damage.DamageDict.TryGetValue("Asphyxiation", out var oxyDamage))
|
||||
{
|
||||
oxyLevel = MathF.Min(1f, (oxyDamage / earliestThreshold).Float());
|
||||
}
|
||||
|
||||
if (threshold >= earliestThreshold && TryGetEarliestDeadState(stateComponent, threshold, out _, out var earliestDeadHold))
|
||||
{
|
||||
critLevel = (float) Math.Clamp((damageable.TotalDamage - earliestThreshold).Double() / (earliestDeadHold - earliestThreshold).Double(), 0.1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't show damage overlay if they're near enough to max.
|
||||
|
||||
if (bruteLevel < 0.05f)
|
||||
{
|
||||
bruteLevel = 0f;
|
||||
}
|
||||
|
||||
_overlay.State = critLevel > 0f ? DamageState.Critical : DamageState.Alive;
|
||||
_overlay.BruteLevel = bruteLevel;
|
||||
_overlay.OxygenLevel = oxyLevel;
|
||||
_overlay.CritLevel = critLevel;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Content.Client.Pointing.Components;
|
||||
using Content.Shared.MobState.EntitySystems;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Pointing;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Client.Animations;
|
||||
@@ -12,7 +12,7 @@ namespace Content.Client.Pointing;
|
||||
public sealed class PointingSystem : SharedPointingSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _player = default!;
|
||||
[Dependency] private readonly SharedMobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
|
||||
private const string AnimationKey = "pointingarrow";
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
using Content.Client.Alerts;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.DamageOverlays;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class DamageOverlayUiController : UIController, IOnStateChanged<GameplayState>
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
[UISystemDependency] private readonly ClientAlertsSystem _alertsSystem = default!;
|
||||
[UISystemDependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
||||
private Overlays.DamageOverlay _overlay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_overlay = new Overlays.DamageOverlay();
|
||||
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttach);
|
||||
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<MobThresholdChecked>(OnThresholdCheck);
|
||||
}
|
||||
|
||||
public void OnStateEntered(GameplayState state)
|
||||
{
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
}
|
||||
|
||||
public void OnStateExited(GameplayState state)
|
||||
{
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnPlayerAttach(PlayerAttachedEvent args)
|
||||
{
|
||||
ClearOverlay();
|
||||
if (!EntityManager.TryGetComponent<MobStateComponent>(args.Entity, out var mobState))
|
||||
return;
|
||||
if (mobState.CurrentState != MobState.Dead)
|
||||
UpdateOverlays(args.Entity, mobState);
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(PlayerDetachedEvent args)
|
||||
{
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
ClearOverlay();
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(MobStateChangedEvent args)
|
||||
{
|
||||
if (args.Target != _playerManager.LocalPlayer?.ControlledEntity)
|
||||
return;
|
||||
|
||||
UpdateOverlays(args.Target, args.Component);
|
||||
}
|
||||
|
||||
private void OnThresholdCheck(ref MobThresholdChecked args)
|
||||
{
|
||||
|
||||
if (args.Target != _playerManager.LocalPlayer?.ControlledEntity)
|
||||
return;
|
||||
UpdateOverlays(args.Target, args.MobState, args.Damageable, args.Threshold);
|
||||
}
|
||||
|
||||
private void ClearOverlay()
|
||||
{
|
||||
_overlay.DeadLevel = 0f;
|
||||
_overlay.CritLevel = 0f;
|
||||
_overlay.BruteLevel = 0f;
|
||||
_overlay.OxygenLevel = 0f;
|
||||
}
|
||||
|
||||
//TODO: Jezi: adjust oxygen and hp overlays to use appropriate systems once bodysim is implemented
|
||||
private void UpdateOverlays(EntityUid entity, MobStateComponent? mobState, DamageableComponent? damageable = null, MobThresholdsComponent? thresholds = null)
|
||||
{
|
||||
if (mobState == null && !EntityManager.TryGetComponent(entity, out mobState) ||
|
||||
thresholds == null && !EntityManager.TryGetComponent(entity, out thresholds) ||
|
||||
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
|
||||
return;
|
||||
|
||||
|
||||
if (!_mobThresholdSystem.TryGetIncapThreshold(entity, out var foundThreshold, thresholds))
|
||||
return; //this entity cannot die or crit!!
|
||||
var critThreshold = foundThreshold.Value;
|
||||
_overlay.State = mobState.CurrentState;
|
||||
|
||||
switch (mobState.CurrentState)
|
||||
{
|
||||
case MobState.Alive:
|
||||
{
|
||||
if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
||||
{
|
||||
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
|
||||
}
|
||||
|
||||
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
|
||||
{
|
||||
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
|
||||
}
|
||||
|
||||
if (_overlay.BruteLevel < 0.05f) // Don't show damage overlay if they're near enough to max.
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
}
|
||||
|
||||
_overlay.CritLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
case MobState.Critical:
|
||||
{
|
||||
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
|
||||
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
|
||||
return;
|
||||
_overlay.CritLevel = critLevel.Value.Float();
|
||||
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
case MobState.Dead:
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.CritLevel = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.Mobs;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.MobState.Overlays;
|
||||
namespace Content.Client.UserInterface.Systems.DamageOverlays.Overlays;
|
||||
|
||||
public sealed class DamageOverlay : Overlay
|
||||
{
|
||||
@@ -21,7 +21,7 @@ public sealed class DamageOverlay : Overlay
|
||||
private readonly ShaderInstance _oxygenShader;
|
||||
private readonly ShaderInstance _bruteShader;
|
||||
|
||||
public DamageState State = DamageState.Alive;
|
||||
public MobState State = MobState.Alive;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the red pulsing overlay
|
||||
@@ -79,7 +79,7 @@ public sealed class DamageOverlay : Overlay
|
||||
var lastFrameTime = (float) _timing.FrameTime.TotalSeconds;
|
||||
|
||||
// If they just died then lerp out the white overlay.
|
||||
if (State != DamageState.Dead)
|
||||
if (State != MobState.Dead)
|
||||
{
|
||||
DeadLevel = 1f;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public sealed class DamageOverlay : Overlay
|
||||
_oldBruteLevel = BruteLevel;
|
||||
}
|
||||
|
||||
level = State != DamageState.Critical ? _oldOxygenLevel : 1f;
|
||||
level = State != MobState.Critical ? _oldOxygenLevel : 1f;
|
||||
|
||||
if (level > 0f)
|
||||
{
|
||||
@@ -215,7 +215,7 @@ public sealed class DamageOverlay : Overlay
|
||||
handle.DrawRect(viewport, Color.White);
|
||||
}
|
||||
|
||||
level = State != DamageState.Dead ? _oldCritLevel : DeadLevel;
|
||||
level = State != MobState.Dead ? _oldCritLevel : DeadLevel;
|
||||
|
||||
if (level > 0f)
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Client.CombatMode;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Hands;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.StatusEffect;
|
||||
|
||||
Reference in New Issue
Block a user