--- /dev/null
+using Content.Shared.Overlays;
+using Robust.Client.Player;
+using Robust.Shared.Console;
+using System.Linq;
+
+namespace Content.Client.Commands;
+
+public sealed class ShowHealthBarsCommand : LocalizedCommands
+{
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+
+ public override string Command => "showhealthbars";
+
+ public override string Help => LocalizationManager.GetString($"cmd-{Command}-help", ("command", Command));
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ var player = _playerManager.LocalSession;
+ if (player == null)
+ {
+ shell.WriteError(LocalizationManager.GetString($"cmd-{Command}-error-not-player"));
+ return;
+ }
+
+ var playerEntity = player?.AttachedEntity;
+ if (!playerEntity.HasValue)
+ {
+ shell.WriteError(LocalizationManager.GetString($"cmd-{Command}-error-no-entity"));
+ return;
+ }
+
+ if (!_entityManager.HasComponent<ShowHealthBarsComponent>(playerEntity))
+ {
+ var showHealthBarsComponent = new ShowHealthBarsComponent
+ {
+ DamageContainers = args.ToList(),
+ NetSyncEnabled = false
+ };
+
+ _entityManager.AddComponent(playerEntity.Value, showHealthBarsComponent, true);
+
+ shell.WriteLine(LocalizationManager.GetString($"cmd-{Command}-notify-enabled", ("args", string.Join(", ", args))));
+ return;
+ }
+ else
+ {
+ _entityManager.RemoveComponentDeferred<ShowHealthBarsComponent>(playerEntity.Value);
+ shell.WriteLine(LocalizationManager.GetString($"cmd-{Command}-notify-disabled"));
+ }
+
+ return;
+ }
+}
+++ /dev/null
-using Content.Client.HealthOverlay;
-using Robust.Shared.Console;
-
-namespace Content.Client.Commands;
-
-public sealed class ToggleHealthOverlayCommand : LocalizedCommands
-{
- [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
-
- public override string Command => "togglehealthoverlay";
-
- public override string Help => LocalizationManager.GetString($"cmd-{Command}-help", ("command", Command));
-
- public override void Execute(IConsoleShell shell, string argStr, string[] args)
- {
- var system = _entitySystemManager.GetEntitySystem<HealthOverlaySystem>();
- system.Enabled = !system.Enabled;
-
- shell.WriteLine(LocalizationManager.GetString($"cmd-{Command}-notify", ("state", system.Enabled ? "enabled" : "disabled")));
- }
-}
+++ /dev/null
-using Content.Client.HealthOverlay.UI;
-using Content.Shared.Damage;
-using Content.Shared.GameTicking;
-using Content.Shared.Mobs.Components;
-using JetBrains.Annotations;
-using Robust.Client.Graphics;
-using Robust.Client.Player;
-
-namespace Content.Client.HealthOverlay
-{
- [UsedImplicitly]
- public sealed class HealthOverlaySystem : EntitySystem
- {
- [Dependency] private readonly IEyeManager _eyeManager = default!;
- [Dependency] private readonly IEntityManager _entities = default!;
- [Dependency] private readonly IPlayerManager _player = default!;
-
- private readonly Dictionary<EntityUid, HealthOverlayGui> _guis = new();
- private bool _enabled;
-
- public bool Enabled
- {
- get => _enabled;
- set
- {
- if (_enabled == value)
- {
- return;
- }
-
- _enabled = value;
-
- foreach (var gui in _guis.Values)
- {
- gui.SetVisibility(value);
- }
- }
- }
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeNetworkEvent<RoundRestartCleanupEvent>(Reset);
- }
-
- public void Reset(RoundRestartCleanupEvent ev)
- {
- foreach (var gui in _guis.Values)
- {
- gui.Dispose();
- }
-
- _guis.Clear();
- }
-
- public override void FrameUpdate(float frameTime)
- {
- base.Update(frameTime);
-
- if (!_enabled)
- {
- return;
- }
-
- if (_player.LocalEntity is not {} ent || Deleted(ent))
- {
- return;
- }
-
- var viewBox = _eyeManager.GetWorldViewport().Enlarged(2.0f);
-
- var query = EntityQueryEnumerator<MobStateComponent, DamageableComponent>();
- while (query.MoveNext(out var entity, out var mobState, out _))
- {
- if (_entities.GetComponent<TransformComponent>(ent).MapID != _entities.GetComponent<TransformComponent>(entity).MapID ||
- !viewBox.Contains(_entities.GetComponent<TransformComponent>(entity).WorldPosition))
- {
- if (_guis.TryGetValue(entity, out var oldGui))
- {
- _guis.Remove(entity);
- oldGui.Dispose();
- }
-
- continue;
- }
-
- if (_guis.ContainsKey(entity))
- {
- continue;
- }
-
- var gui = new HealthOverlayGui(entity);
- _guis.Add(entity, gui);
- }
- }
- }
-}
+++ /dev/null
-using Robust.Client.Graphics;
-using Robust.Client.UserInterface;
-using Robust.Shared.IoC;
-using Robust.Shared.Maths;
-using Robust.Shared.Prototypes;
-
-namespace Content.Client.HealthOverlay.UI
-{
- public sealed class HealthOverlayBar : Control
- {
- public const byte HealthBarScale = 2;
-
- private const int XPixelDiff = 20 * HealthBarScale;
-
- public HealthOverlayBar()
- {
- IoCManager.InjectDependencies(this);
- Shader = IoCManager.Resolve<IPrototypeManager>().Index<ShaderPrototype>("unshaded").Instance();
- }
-
- private ShaderInstance Shader { get; }
-
- /// <summary>
- /// From -1 (dead) to 0 (crit) and 1 (alive)
- /// </summary>
- public float Ratio { get; set; }
-
- public Color Color { get; set; }
-
- protected override void Draw(DrawingHandleScreen handle)
- {
- base.Draw(handle);
-
- handle.UseShader(Shader);
-
- var leftOffset = 2 * HealthBarScale;
- var box = new UIBox2i(
- leftOffset,
- -2 + 2 * HealthBarScale,
- leftOffset + (int) (XPixelDiff * Ratio * UIScale),
- -2);
-
- handle.DrawRect(box, Color);
- }
- }
-}
+++ /dev/null
-using System.Numerics;
-using Content.Client.IoC;
-using Content.Client.Resources;
-using Content.Shared.Damage;
-using Content.Shared.FixedPoint;
-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;
-
-namespace Content.Client.HealthOverlay.UI
-{
- public sealed class HealthOverlayGui : BoxContainer
- {
- [Dependency] private readonly IEyeManager _eyeManager = default!;
- [Dependency] private readonly IEntityManager _entities = default!;
-
- public HealthOverlayGui(EntityUid entity)
- {
- IoCManager.InjectDependencies(this);
- UserInterfaceManager.WindowRoot.AddChild(this);
- SeparationOverride = 0;
- Orientation = LayoutOrientation.Vertical;
-
- CritBar = new HealthOverlayBar
- {
- Visible = false,
- VerticalAlignment = VAlignment.Center,
- Color = Color.Red
- };
-
- HealthBar = new HealthOverlayBar
- {
- Visible = false,
- VerticalAlignment = VAlignment.Center,
- Color = Color.LimeGreen
- };
-
- AddChild(Panel = new PanelContainer
- {
- Children =
- {
- new TextureRect
- {
- Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/Misc/health_bar.rsi/icon.png"),
- TextureScale = Vector2.One * HealthOverlayBar.HealthBarScale,
- VerticalAlignment = VAlignment.Center,
- },
- CritBar,
- HealthBar
- }
- });
-
- Entity = entity;
- }
-
- public PanelContainer Panel { get; }
-
- public HealthOverlayBar HealthBar { get; }
-
- public HealthOverlayBar CritBar { get; }
-
- public EntityUid Entity { get; }
-
- public void SetVisibility(bool val)
- {
- Visible = val;
- Panel.Visible = val;
- }
-
- private void MoreFrameUpdate()
- {
- if (_entities.Deleted(Entity))
- {
- return;
- }
-
- if (!_entities.TryGetComponent(Entity, out MobStateComponent? mobState) ||
- !_entities.TryGetComponent(Entity, out DamageableComponent? damageable))
- {
- CritBar.Visible = false;
- HealthBar.Visible = false;
- return;
- }
-
- var mobStateSystem = _entities.EntitySysManager.GetEntitySystem<MobStateSystem>();
- var mobThresholdSystem = _entities.EntitySysManager.GetEntitySystem<MobThresholdSystem>();
- if (mobStateSystem.IsAlive(Entity, mobState))
- {
- if (!mobThresholdSystem.TryGetThresholdForState(Entity,MobState.Critical, out var threshold))
- {
- CritBar.Visible = false;
- HealthBar.Visible = false;
- return;
- }
-
- CritBar.Ratio = 1;
- CritBar.Visible = true;
- HealthBar.Ratio = 1 - ((FixedPoint2)(damageable.TotalDamage / threshold)).Float();
- HealthBar.Visible = true;
- }
- else if (mobStateSystem.IsCritical(Entity, mobState))
- {
- HealthBar.Ratio = 0;
- HealthBar.Visible = false;
-
- if (!mobThresholdSystem.TryGetThresholdForState(Entity, MobState.Critical, out var critThreshold) ||
- !mobThresholdSystem.TryGetThresholdForState(Entity, MobState.Dead, out var deadThreshold))
- {
- CritBar.Visible = false;
- return;
- }
-
- CritBar.Visible = true;
- CritBar.Ratio = 1 -
- ((damageable.TotalDamage - critThreshold) /
- (deadThreshold - critThreshold)).Value.Float();
- }
- else if (mobStateSystem.IsDead(Entity, mobState))
- {
- CritBar.Ratio = 0;
- CritBar.Visible = false;
- HealthBar.Ratio = 0;
- HealthBar.Visible = true;
- }
- else
- {
- CritBar.Visible = false;
- HealthBar.Visible = false;
- }
- }
-
- protected override void FrameUpdate(FrameEventArgs args)
- {
- base.FrameUpdate(args);
-
- MoreFrameUpdate();
-
- if (_entities.Deleted(Entity) || _eyeManager.CurrentMap != _entities.GetComponent<TransformComponent>(Entity).MapID)
- {
- Visible = false;
- return;
- }
-
- Visible = true;
-
- var screenCoordinates = _eyeManager.CoordinatesToScreen(_entities.GetComponent<TransformComponent>(Entity).Coordinates);
- var playerPosition = UserInterfaceManager.ScreenToUIPosition(screenCoordinates);
- LayoutContainer.SetPosition(this, new Vector2(playerPosition.X - Width / 2, playerPosition.Y - Height - 30.0f));
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (!disposing)
- return;
-
- HealthBar.Dispose();
- }
- }
-}
--- /dev/null
+using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
+using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs.Systems;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+using System.Numerics;
+using static Robust.Shared.Maths.Color;
+
+namespace Content.Client.Overlays;
+
+/// <summary>
+/// Overlay that shows a health bar on mobs.
+/// </summary>
+public sealed class EntityHealthBarOverlay : Overlay
+{
+ private readonly IEntityManager _entManager;
+ private readonly SharedTransformSystem _transform;
+ private readonly MobStateSystem _mobStateSystem;
+ private readonly MobThresholdSystem _mobThresholdSystem;
+ public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
+ public HashSet<string> DamageContainers = new();
+
+ public EntityHealthBarOverlay(IEntityManager entManager)
+ {
+ _entManager = entManager;
+ _transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
+ _mobStateSystem = _entManager.EntitySysManager.GetEntitySystem<MobStateSystem>();
+ _mobThresholdSystem = _entManager.EntitySysManager.GetEntitySystem<MobThresholdSystem>();
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ var handle = args.WorldHandle;
+ var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero;
+ var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
+
+ const float scale = 1f;
+ var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
+ var rotationMatrix = Matrix3.CreateRotation(-rotation);
+
+ var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
+ while (query.MoveNext(out var uid,
+ out var mobThresholdsComponent,
+ out var mobStateComponent,
+ out var damageableComponent,
+ out var spriteComponent))
+ {
+ if (_entManager.TryGetComponent<MetaDataComponent>(uid, out var metaDataComponent) &&
+ metaDataComponent.Flags.HasFlag(MetaDataFlags.InContainer))
+ {
+ continue;
+ }
+
+ if (!xformQuery.TryGetComponent(uid, out var xform) ||
+ xform.MapID != args.MapId)
+ {
+ continue;
+ }
+
+ if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID))
+ {
+ continue;
+ }
+
+ var bounds = spriteComponent.Bounds;
+ var worldPos = _transform.GetWorldPosition(xform, xformQuery);
+
+ if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
+ {
+ continue;
+ }
+
+ var worldPosition = _transform.GetWorldPosition(xform);
+ var worldMatrix = Matrix3.CreateTranslation(worldPosition);
+
+ Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
+ Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
+
+ handle.SetTransform(matty);
+
+ var yOffset = spriteComponent.Bounds.Height * EyeManager.PixelsPerMeter / 2 - 3f;
+ var widthOfMob = spriteComponent.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.
+ const float startX = 8f;
+ var endX = widthOfMob - 8f;
+
+ var xProgress = (endX - startX) * deathProgress.ratio + startX;
+
+ var boxBackground = new Box2(new Vector2(startX, 0f) / EyeManager.PixelsPerMeter, new Vector2(endX, 3f) / EyeManager.PixelsPerMeter);
+ boxBackground = boxBackground.Translated(position);
+ handle.DrawRect(boxBackground, Black.WithAlpha(192));
+
+ var boxMain = new Box2(new Vector2(startX, 0f) / EyeManager.PixelsPerMeter, new Vector2(xProgress, 3f) / EyeManager.PixelsPerMeter);
+ boxMain = boxMain.Translated(position);
+ handle.DrawRect(boxMain, color);
+
+ var pixelDarken = new Box2(new Vector2(startX, 2f) / EyeManager.PixelsPerMeter, new Vector2(xProgress, 3f) / EyeManager.PixelsPerMeter);
+ pixelDarken = pixelDarken.Translated(position);
+ handle.DrawRect(pixelDarken, Black.WithAlpha(128));
+ }
+
+ handle.UseShader(null);
+ handle.SetTransform(Matrix3.Identity);
+ }
+
+ /// <summary>
+ /// Returns a ratio between 0 and 1, and whether the entity is in crit.
+ /// </summary>
+ private (float, bool) CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds)
+ {
+ if (_mobStateSystem.IsAlive(uid, component))
+ {
+ if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholds) &&
+ !_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds))
+ return (1, false);
+
+ var ratio = 1 - ((FixedPoint2) (dmg.TotalDamage / threshold)).Float();
+ return (ratio, false);
+ }
+
+ if (_mobStateSystem.IsCritical(uid, component))
+ {
+ if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var critThreshold, thresholds) ||
+ !_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out var deadThreshold, thresholds))
+ {
+ return (1, true);
+ }
+
+ var ratio = 1 - ((dmg.TotalDamage - critThreshold) / (deadThreshold - critThreshold)).Value.Float();
+
+ return (ratio, true);
+ }
+
+ return (0, true);
+ }
+
+ public static Color GetProgressColor(float progress, bool crit)
+ {
+ if (progress >= 1.0f)
+ {
+ return SeaBlue;
+ }
+
+ if (!crit)
+ {
+ switch (progress)
+ {
+ case > 0.90F:
+ return SeaBlue;
+ case > 0.50F:
+ return Violet;
+ case > 0.15F:
+ return Ruber;
+ }
+ }
+
+ return VividGamboge;
+ }
+}
private void OnPlayerDetached(LocalPlayerDetachedEvent args)
{
- if (_player.LocalPlayer?.ControlledEntity == null)
+ if (_player.LocalSession?.AttachedEntity == null)
Deactivate();
}
protected virtual void OnRefreshEquipmentHud(EntityUid uid, T component, InventoryRelayedEvent<RefreshEquipmentHudEvent<T>> args)
{
- args.Args.Active = true;
+ OnRefreshComponentHud(uid, component, args.Args);
}
protected virtual void OnRefreshComponentHud(EntityUid uid, T component, RefreshEquipmentHudEvent<T> args)
{
args.Active = true;
+ args.Components.Add(component);
}
private void RefreshOverlay(EntityUid uid)
{
- if (uid != _player.LocalPlayer?.ControlledEntity)
+ if (uid != _player.LocalSession?.AttachedEntity)
return;
var ev = new RefreshEquipmentHudEvent<T>(TargetSlots);
--- /dev/null
+using Content.Shared.Inventory.Events;
+using Content.Shared.Overlays;
+using Robust.Client.Graphics;
+using System.Linq;
+
+namespace Content.Client.Overlays;
+
+/// <summary>
+/// Adds a health bar overlay.
+/// </summary>
+public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComponent>
+{
+ [Dependency] private readonly IOverlayManager _overlayMan = default!;
+
+ private EntityHealthBarOverlay _overlay = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _overlay = new(EntityManager);
+ }
+
+ protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
+ {
+ base.UpdateInternal(component);
+
+ foreach (var damageContainerId in component.Components.SelectMany(x => x.DamageContainers))
+ {
+ _overlay.DamageContainers.Add(damageContainerId);
+ }
+
+ if (!_overlayMan.HasOverlay<EntityHealthBarOverlay>())
+ {
+ _overlayMan.AddOverlay(_overlay);
+ }
+ }
+
+ protected override void DeactivateInternal()
+ {
+ base.DeactivateInternal();
+
+ _overlay.DamageContainers.Clear();
+ _overlayMan.RemoveOverlay(_overlay);
+ }
+}
--- /dev/null
+using Content.Shared.Damage;
+using Content.Shared.Inventory.Events;
+using Content.Shared.Overlays;
+using Content.Shared.StatusIcon;
+using Content.Shared.StatusIcon.Components;
+using Robust.Shared.Prototypes;
+using System.Linq;
+
+namespace Content.Client.Overlays;
+
+/// <summary>
+/// Shows a healthy icon on mobs.
+/// </summary>
+public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsComponent>
+{
+ [Dependency] private readonly IPrototypeManager _prototypeMan = default!;
+
+ public HashSet<string> DamageContainers = new();
+
+ [ValidatePrototypeId<StatusIconPrototype>]
+ private const string HealthIconFine = "HealthIconFine";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
+
+ }
+
+ protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> 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 OnGetStatusIconsEvent(EntityUid uid, DamageableComponent damageableComponent, ref GetStatusIconsEvent args)
+ {
+ if (!IsActive || args.InContainer)
+ return;
+
+ var healthIcons = DecideHealthIcons(damageableComponent);
+
+ args.StatusIcons.AddRange(healthIcons);
+ }
+
+ private IReadOnlyList<StatusIconPrototype> DecideHealthIcons(DamageableComponent damageableComponent)
+ {
+ if (damageableComponent.DamageContainerID == null ||
+ !DamageContainers.Contains(damageableComponent.DamageContainerID))
+ {
+ return Array.Empty<StatusIconPrototype>();
+ }
+
+ var result = new List<StatusIconPrototype>();
+
+ // Here you could check health status, diseases, mind status, etc. and pick a good icon, or multiple depending on whatever.
+ if (damageableComponent?.DamageContainerID == "Biological" &&
+ _prototypeMan.TryIndex<StatusIconPrototype>(HealthIconFine, out var healthyIcon))
+ {
+ result.Add(healthyIcon);
+ }
+
+ return result;
+ }
+}
if (!IsActive || args.InContainer)
return;
- var healthIcons = DecideHungerIcon(uid, hungerComponent);
+ var hungerIcons = DecideHungerIcon(uid, hungerComponent);
- args.StatusIcons.AddRange(healthIcons);
+ args.StatusIcons.AddRange(hungerIcons);
}
private IReadOnlyList<StatusIconPrototype> DecideHungerIcon(EntityUid uid, HungerComponent hungerComponent)
using Robust.Shared.Prototypes;
namespace Content.Client.Overlays;
+
public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIconsComponent>
{
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
return;
}
- var healthIcons = DecideSecurityIcon(uid);
+ var securityIcons = DecideSecurityIcon(uid);
- @event.StatusIcons.AddRange(healthIcons);
+ @event.StatusIcons.AddRange(securityIcons);
}
private IReadOnlyList<StatusIconPrototype> DecideSecurityIcon(EntityUid uid)
return;
}
- var healthIcons = SyndicateIcon(uid, nukeOperativeComponent);
+ var syndicateIcons = SyndicateIcon(uid, nukeOperativeComponent);
- args.StatusIcons.AddRange(healthIcons);
+ args.StatusIcons.AddRange(syndicateIcons);
}
private IReadOnlyList<StatusIconPrototype> SyndicateIcon(EntityUid uid, NukeOperativeComponent nukeOperativeComponent)
if (!IsActive || args.InContainer)
return;
- var healthIcons = DecideThirstIcon(uid, thirstComponent);
+ var thirstIcons = DecideThirstIcon(uid, thirstComponent);
- args.StatusIcons.AddRange(healthIcons);
+ args.StatusIcons.AddRange(thirstIcons);
}
private IReadOnlyList<StatusIconPrototype> DecideThirstIcon(EntityUid uid, ThirstComponent thirstComponent)
private readonly SpriteSystem _sprite;
private readonly TransformSystem _transform;
private readonly StatusIconSystem _statusIcon;
-
private readonly ShaderInstance _shader;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
_sprite = _entity.System<SpriteSystem>();
_transform = _entity.System<TransformSystem>();
_statusIcon = _entity.System<StatusIconSystem>();
-
_shader = _prototype.Index<ShaderPrototype>("unshaded").Instance();
}
{
public SlotFlags TargetSlots { get; init; }
public bool Active = false;
- public object? ExtraData;
+ public List<T> Components = new();
public RefreshEquipmentHudEvent(SlotFlags targetSlots)
{
// ComponentActivatedClientSystems
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSecurityIconsComponent>>(RelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowHealthBarsComponent>>(RelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowHealthIconsComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowHungerIconsComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowThirstIconsComponent>>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RelayInventoryEvent);
--- /dev/null
+using Content.Shared.Damage.Prototypes;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+
+namespace Content.Shared.Overlays;
+
+/// <summary>
+/// This component allows you to see health bars above damageable mobs.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ShowHealthBarsComponent : Component
+{
+ /// <summary>
+ /// Displays health bars of the damage containers.
+ /// </summary>
+ [DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
+ public List<string> DamageContainers = new();
+}
--- /dev/null
+using Content.Shared.Damage.Prototypes;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+
+namespace Content.Shared.Overlays;
+
+/// <summary>
+/// This component allows you to see health status icons above damageable mobs.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ShowHealthIconsComponent : Component
+{
+ /// <summary>
+ /// Displays health status icons of the damage containers.
+ /// </summary>
+ [DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
+ public List<string> DamageContainers = new();
+}
--- /dev/null
+cmd-showhealthbars-desc = Toggles health bars above mobs.
+cmd-showhealthbars-help = Usage: {$command} [<DamageContainerId>]
+cmd-showhealthbars-error-not-player = You aren't a player.
+cmd-showhealthbars-error-no-entity = You do not have an attached entity.
+cmd-showhealthbars-notify-enabled = Enabled health overlay for DamageContainers: {$args}.
+cmd-showhealthbars-notify-disabled = Disabled health overlay.
\ No newline at end of file
+++ /dev/null
-cmd-togglehealthoverlay-desc = Toggles a health bar above mobs.
-cmd-togglehealthoverlay-help = Usage: {$command}
-cmd-togglehealthoverlay-notify = Health overlay system {$state}.
\ No newline at end of file
sprite: Clothing/Eyes/Hud/diag.rsi
- type: Clothing
sprite: Clothing/Eyes/Hud/diag.rsi
+ - type: ShowHealthBars
+ damageContainers:
+ - Inorganic
- type: entity
parent: ClothingEyesBase
sprite: Clothing/Eyes/Hud/med.rsi
- type: Clothing
sprite: Clothing/Eyes/Hud/med.rsi
+ - type: ShowHealthBars
+ damageContainers:
+ - Biological
+ - type: ShowHealthIcons
+ damageContainers:
+ - Biological
- type: entity
parent: ClothingEyesBase
sprite: Clothing/Eyes/Hud/medonion.rsi
- type: Clothing
sprite: Clothing/Eyes/Hud/medonion.rsi
+ - type: ShowHealthBars
+ damageContainers:
+ - Biological
+ - type: ShowHealthIcons
+ damageContainers:
+ - Biological
- type: ShowHungerIcons
- type: entity
sprite: Clothing/Eyes/Hud/medonionbeer.rsi
- type: Clothing
sprite: Clothing/Eyes/Hud/medonionbeer.rsi
+ - type: ShowHealthBars
+ damageContainers:
+ - Biological
+ - type: ShowHealthIcons
+ damageContainers:
+ - Biological
- type: ShowHungerIcons
- type: ShowThirstIcons
- type: Clothing
sprite: Clothing/Eyes/Hud/medsecengi.rsi
- type: ShowSecurityIcons
+ - type: ShowHealthBars
+ damageContainers:
+ - Biological
+ - Inorganic
+ - type: ShowHealthIcons
+ damageContainers:
+ - Biological
- type: ShowSyndicateIcons
- type: entity
- type: Clothing
sprite: Clothing/Eyes/Hud/omni.rsi
- type: ShowSecurityIcons
+ - type: ShowHealthBars
+ damageContainers:
+ - Biological
+ - Inorganic
+ - type: ShowHealthIcons
+ damageContainers:
+ - Biological
- type: ShowHungerIcons
- type: ShowThirstIcons
- type: ShowSyndicateIcons
interactFailureString: petting-failure-medibot
interactSuccessSound:
path: /Audio/Ambience/Objects/periodic_beep.ogg
-
+ - type: ShowHealthBars
+ damageContainers:
+ - Biological
+ - type: ShowHealthIcons
+ damageContainers:
+ - Biological
+
- type: entity
parent: MobSiliconBase
id: MobMimeBot
--- /dev/null
+- type: statusIcon
+ id: HealthIconFine
+ priority: 1
+ icon:
+ sprite: Interface/Misc/health_icons.rsi
+ state: Fine
+ locationPreference: Right
\ No newline at end of file
+++ /dev/null
-{
- "version": 1,
- "size": {
- "y": 7,
- "x": 24
- },
- "license": "CC-BY-SA-3.0",
- "copyright": "https://github.com/tgstation/tgstation/blob/886ca0f8dddf83ecaf10c92ff106172722352192/icons/effects/progessbar.dmi",
- "states": [
- {
- "name": "icon"
- }
- ]
-}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 8,
+ "y": 8
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "https://github.com/tgstation/tgstation/blob/master/icons/mob/huds/hud.dmi",
+ "states": [
+ {
+ "name": "Fine"
+ }
+ ]
+}
\ No newline at end of file