using Content.Shared.Atmos.Rotting; using Content.Shared.Inventory.Events; using Content.Shared.Mobs.Components; using Content.Shared.Overlays; using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; using Robust.Shared.Prototypes; using System.Linq; using Content.Shared.Damage.Components; using Content.Shared._Offbrand.Wounds; // Offbrand using Content.Shared.Mobs; // Offbrand namespace Content.Client.Overlays; /// /// Shows a healthy icon on mobs. /// public sealed class ShowHealthIconsSystem : EquipmentHudSystem { [Dependency] private readonly IPrototypeManager _prototypeMan = default!; [ViewVariables] public HashSet DamageContainers = new(); public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnGetStatusIconsEvent); SubscribeLocalEvent(OnHandleState); } protected override void UpdateInternal(RefreshEquipmentHudEvent component) { base.UpdateInternal(component); foreach (var damageContainerId in component.Components.SelectMany(x => x.DamageContainers)) { DamageContainers.Add(damageContainerId); } } protected override void DeactivateInternal() { base.DeactivateInternal(); DamageContainers.Clear(); } private void OnHandleState(Entity ent, ref AfterAutoHandleStateEvent args) { RefreshOverlay(); } private void OnGetStatusIconsEvent(Entity entity, ref GetStatusIconsEvent args) { if (!IsActive) return; var healthIcons = DecideHealthIcons(entity); args.StatusIcons.AddRange(healthIcons); } // Begin Offbrand private List DecideBrainHealthIcons(Entity ent) { if (ent.Comp2.CurrentState == MobState.Dead) { return new() { _prototypeMan.Index(ent.Comp2.DeadIcon) }; } var current = ent.Comp1.Damage; var max = ent.Comp1.MaxDamage; if (ent.Comp2.CurrentState == MobState.Critical || ent.Comp1.Oxygen == 0) { var amount = ent.Comp2.CriticalDamageIcons.Count; var idx = Math.Clamp((int)Math.Floor(amount - (amount / max.Double()) * current.Double()), 0, amount-1); return new() { _prototypeMan.Index(ent.Comp2.CriticalDamageIcons[idx]) }; } else { var amount = ent.Comp2.AliveDamageIcons.Count; var idx = Math.Clamp((int)Math.Floor(amount - (amount / max.Double()) * current.Double()), 0, amount-1); return new() { _prototypeMan.Index(ent.Comp2.AliveDamageIcons[idx]) }; } } // End Offbrand private IReadOnlyList DecideHealthIcons(Entity entity) { if (TryComp(entity, out var brain) && TryComp(entity, out var thresholds)) { return DecideBrainHealthIcons((entity.Owner, brain, thresholds)); } var damageableComponent = entity.Comp; if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID)) { return Array.Empty(); } var result = new List(); // Here you could check health status, diseases, mind status, etc. and pick a good icon, or multiple depending on whatever. if (damageableComponent?.DamageContainerID == "Biological") { if (TryComp(entity, out var state)) { // Since there is no MobState for a rotting mob, we have to deal with this case first. if (HasComp(entity) && _prototypeMan.Resolve(damageableComponent.RottingIcon, out var rottingIcon)) result.Add(rottingIcon); else if (damageableComponent.HealthIcons.TryGetValue(state.CurrentState, out var value) && _prototypeMan.Resolve(value, out var icon)) result.Add(icon); } } return result; } }