Make it possible to hide full health bars below a total damage threshold (#28127)
* Make it possible to hide full health bars below a total damage threshold * Fix not setting state
This commit is contained in:
@@ -1,15 +1,14 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.UserInterface.Systems;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Mobs;
|
using Content.Shared.Mobs;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
|
using Content.Shared.StatusIcon.Components;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Shared.Enums;
|
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;
|
using static Robust.Shared.Maths.Color;
|
||||||
|
|
||||||
namespace Content.Client.Overlays;
|
namespace Content.Client.Overlays;
|
||||||
@@ -79,6 +78,10 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||||||
continue;
|
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 worldPosition = _transform.GetWorldPosition(xform);
|
||||||
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
|
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
|
||||||
|
|
||||||
@@ -91,10 +94,6 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||||||
var widthOfMob = bounds.Width * EyeManager.PixelsPerMeter;
|
var widthOfMob = bounds.Width * EyeManager.PixelsPerMeter;
|
||||||
|
|
||||||
var position = new Vector2(-widthOfMob / EyeManager.PixelsPerMeter / 2, yOffset / 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);
|
var color = GetProgressColor(deathProgress.ratio, deathProgress.inCrit);
|
||||||
|
|
||||||
// Hardcoded width of the progress bar because it doesn't match the texture.
|
// Hardcoded width of the progress bar because it doesn't match the texture.
|
||||||
@@ -122,10 +121,13 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a ratio between 0 and 1, and whether the entity is in crit.
|
/// Returns a ratio between 0 and 1, and whether the entity is in crit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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 (_mobStateSystem.IsAlive(uid, component))
|
||||||
{
|
{
|
||||||
|
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.Critical, out var threshold, thresholds) &&
|
||||||
!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds))
|
!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds))
|
||||||
return (1, false);
|
return (1, false);
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ using Content.Shared.StatusIcon;
|
|||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
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
|
namespace Content.Shared.Damage
|
||||||
{
|
{
|
||||||
@@ -18,7 +16,7 @@ namespace Content.Shared.Damage
|
|||||||
/// may also have resistances to certain damage types, defined via a <see cref="DamageModifierSetPrototype"/>.
|
/// may also have resistances to certain damage types, defined via a <see cref="DamageModifierSetPrototype"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[NetworkedComponent()]
|
[NetworkedComponent]
|
||||||
[Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)]
|
[Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)]
|
||||||
public sealed partial class DamageableComponent : Component
|
public sealed partial class DamageableComponent : Component
|
||||||
{
|
{
|
||||||
@@ -26,8 +24,8 @@ namespace Content.Shared.Damage
|
|||||||
/// This <see cref="DamageContainerPrototype"/> specifies what damage types are supported by this component.
|
/// This <see cref="DamageContainerPrototype"/> specifies what damage types are supported by this component.
|
||||||
/// If null, all damage types will be supported.
|
/// If null, all damage types will be supported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("damageContainer", customTypeSerializer: typeof(PrototypeIdSerializer<DamageContainerPrototype>))]
|
[DataField("damageContainer")]
|
||||||
public string? DamageContainerID;
|
public ProtoId<DamageContainerPrototype>? DamageContainerID;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This <see cref="DamageModifierSetPrototype"/> will be applied to any damage that is dealt to this container,
|
/// This <see cref="DamageModifierSetPrototype"/> 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
|
/// Though DamageModifierSets can be deserialized directly, we only want to use the prototype version here
|
||||||
/// to reduce duplication.
|
/// to reduce duplication.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataField("damageModifierSet", customTypeSerializer: typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
|
[DataField("damageModifierSet")]
|
||||||
public string? DamageModifierSetId;
|
public ProtoId<DamageModifierSetPrototype>? DamageModifierSetId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the damage information is stored in this <see cref="DamageSpecifier"/>.
|
/// All the damage information is stored in this <see cref="DamageSpecifier"/>.
|
||||||
@@ -46,7 +44,7 @@ namespace Content.Shared.Damage
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
|
/// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[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();
|
public DamageSpecifier Damage = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,8 +62,8 @@ namespace Content.Shared.Damage
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public FixedPoint2 TotalDamage;
|
public FixedPoint2 TotalDamage;
|
||||||
|
|
||||||
[DataField("radiationDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageTypePrototype>))]
|
[DataField("radiationDamageTypes")]
|
||||||
public List<string> RadiationDamageTypeIDs = new() { "Radiation" };
|
public List<ProtoId<DamageTypePrototype>> RadiationDamageTypeIDs = new() { "Radiation" };
|
||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
public Dictionary<MobState, ProtoId<StatusIconPrototype>> HealthIcons = new()
|
public Dictionary<MobState, ProtoId<StatusIconPrototype>> HealthIcons = new()
|
||||||
@@ -77,6 +75,9 @@ namespace Content.Shared.Damage
|
|||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
public ProtoId<StatusIconPrototype> RottingIcon = "HealthIconRotting";
|
public ProtoId<StatusIconPrototype> RottingIcon = "HealthIconRotting";
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public FixedPoint2? HealthBarThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
@@ -84,13 +85,16 @@ namespace Content.Shared.Damage
|
|||||||
{
|
{
|
||||||
public readonly Dictionary<string, FixedPoint2> DamageDict;
|
public readonly Dictionary<string, FixedPoint2> DamageDict;
|
||||||
public readonly string? ModifierSetId;
|
public readonly string? ModifierSetId;
|
||||||
|
public readonly FixedPoint2? HealthBarThreshold;
|
||||||
|
|
||||||
public DamageableComponentState(
|
public DamageableComponentState(
|
||||||
Dictionary<string, FixedPoint2> damageDict,
|
Dictionary<string, FixedPoint2> damageDict,
|
||||||
string? modifierSetId)
|
string? modifierSetId,
|
||||||
|
FixedPoint2? healthBarThreshold)
|
||||||
{
|
{
|
||||||
DamageDict = damageDict;
|
DamageDict = damageDict;
|
||||||
ModifierSetId = modifierSetId;
|
ModifierSetId = modifierSetId;
|
||||||
|
HealthBarThreshold = healthBarThreshold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Administration.Logs;
|
|
||||||
using Content.Shared.Damage.Prototypes;
|
using Content.Shared.Damage.Prototypes;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
@@ -229,12 +228,12 @@ namespace Content.Shared.Damage
|
|||||||
{
|
{
|
||||||
if (_netMan.IsServer)
|
if (_netMan.IsServer)
|
||||||
{
|
{
|
||||||
args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId);
|
args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId, component.HealthBarThreshold);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// avoid mispredicting damage on newly spawned entities.
|
// 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.DamageModifierSetId = state.ModifierSetId;
|
||||||
|
component.HealthBarThreshold = state.HealthBarThreshold;
|
||||||
|
|
||||||
// Has the damage actually changed?
|
// Has the damage actually changed?
|
||||||
DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) };
|
DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) };
|
||||||
|
|||||||
Reference in New Issue
Block a user