diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs index c1c0ae93ec..2b2ff14a22 100644 --- a/Content.Client/Overlays/EntityHealthBarOverlay.cs +++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs @@ -1,15 +1,14 @@ +using System.Numerics; +using Content.Client.UserInterface.Systems; using Content.Shared.Damage; using Content.Shared.FixedPoint; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.StatusIcon.Components; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; -using System.Numerics; -using Content.Shared.StatusIcon.Components; -using Content.Client.UserInterface.Systems; -using Robust.Shared.Prototypes; using static Robust.Shared.Maths.Color; namespace Content.Client.Overlays; @@ -79,6 +78,10 @@ public sealed class EntityHealthBarOverlay : Overlay continue; } + // we are all progressing towards death every day + if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress) + continue; + var worldPosition = _transform.GetWorldPosition(xform); var worldMatrix = Matrix3.CreateTranslation(worldPosition); @@ -91,10 +94,6 @@ public sealed class EntityHealthBarOverlay : Overlay var widthOfMob = bounds.Width * EyeManager.PixelsPerMeter; var position = new Vector2(-widthOfMob / EyeManager.PixelsPerMeter / 2, yOffset / EyeManager.PixelsPerMeter); - - // we are all progressing towards death every day - (float ratio, bool inCrit) deathProgress = CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent); - var color = GetProgressColor(deathProgress.ratio, deathProgress.inCrit); // Hardcoded width of the progress bar because it doesn't match the texture. @@ -122,10 +121,13 @@ public sealed class EntityHealthBarOverlay : Overlay /// /// Returns a ratio between 0 and 1, and whether the entity is in crit. /// - private (float, bool) CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds) + private (float ratio, bool inCrit)? CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds) { if (_mobStateSystem.IsAlive(uid, component)) { + if (dmg.HealthBarThreshold != null && dmg.TotalDamage < dmg.HealthBarThreshold) + return null; + if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholds) && !_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds)) return (1, false); diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs index be66d51e3b..f8205568f1 100644 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ b/Content.Shared/Damage/Components/DamageableComponent.cs @@ -5,8 +5,6 @@ using Content.Shared.StatusIcon; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Damage { @@ -18,7 +16,7 @@ namespace Content.Shared.Damage /// may also have resistances to certain damage types, defined via a . /// [RegisterComponent] - [NetworkedComponent()] + [NetworkedComponent] [Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)] public sealed partial class DamageableComponent : Component { @@ -26,8 +24,8 @@ namespace Content.Shared.Damage /// This specifies what damage types are supported by this component. /// If null, all damage types will be supported. /// - [DataField("damageContainer", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? DamageContainerID; + [DataField("damageContainer")] + public ProtoId? DamageContainerID; /// /// This will be applied to any damage that is dealt to this container, @@ -37,8 +35,8 @@ namespace Content.Shared.Damage /// Though DamageModifierSets can be deserialized directly, we only want to use the prototype version here /// to reduce duplication. /// - [DataField("damageModifierSet", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? DamageModifierSetId; + [DataField("damageModifierSet")] + public ProtoId? DamageModifierSetId; /// /// All the damage information is stored in this . @@ -46,7 +44,7 @@ namespace Content.Shared.Damage /// /// If this data-field is specified, this allows damageable components to be initialized with non-zero damage. /// - [DataField("damage", readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier + [DataField(readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier public DamageSpecifier Damage = new(); /// @@ -64,8 +62,8 @@ namespace Content.Shared.Damage [ViewVariables] public FixedPoint2 TotalDamage; - [DataField("radiationDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List RadiationDamageTypeIDs = new() { "Radiation" }; + [DataField("radiationDamageTypes")] + public List> RadiationDamageTypeIDs = new() { "Radiation" }; [DataField] public Dictionary> HealthIcons = new() @@ -77,6 +75,9 @@ namespace Content.Shared.Damage [DataField] public ProtoId RottingIcon = "HealthIconRotting"; + + [DataField] + public FixedPoint2? HealthBarThreshold; } [Serializable, NetSerializable] @@ -84,13 +85,16 @@ namespace Content.Shared.Damage { public readonly Dictionary DamageDict; public readonly string? ModifierSetId; + public readonly FixedPoint2? HealthBarThreshold; public DamageableComponentState( Dictionary damageDict, - string? modifierSetId) + string? modifierSetId, + FixedPoint2? healthBarThreshold) { DamageDict = damageDict; ModifierSetId = modifierSetId; + HealthBarThreshold = healthBarThreshold; } } } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 4aaf380c47..3c3e1b736d 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Shared.Administration.Logs; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Inventory; @@ -229,12 +228,12 @@ namespace Content.Shared.Damage { if (_netMan.IsServer) { - args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId); + args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId, component.HealthBarThreshold); } else { // avoid mispredicting damage on newly spawned entities. - args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId); + args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId, component.HealthBarThreshold); } } @@ -268,6 +267,7 @@ namespace Content.Shared.Damage } component.DamageModifierSetId = state.ModifierSetId; + component.HealthBarThreshold = state.HealthBarThreshold; // Has the damage actually changed? DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) };