Add health bar overlays for eye equipment (#21980)

* PR 1

* fix an error with health bar overlay (#1292)

* Revert "Revert "Replace `ResourcePath` with `ResPath` (#15308)" (#155… (#15566)

* [1612] change ShowHealthBarsComponent's DamageContainer field to a list (#1662)

* fix build

* no crit entities from not updating

* cleanup

* namespace

* undu irrelevant changes

* undo icon change

* make health bar 1 px taller and icon 1 px shorter

* fix medibot

* fix comment

* don't show health bar ratio when in crit

* fix build

* put the crit bar back

* don't render healthbars for mobs that are in containers

* draw more boxes without the background sprite

* fine status icon for all bio mobs

* add wacky mandatory things

* attempt 2

* whoops wrong file

* cool, this works too

* move null check to top

* only 1 init

* security huds

* remove shader

* fix build after cleanup

* slight cleanup

* little more cleanup

* Remove clothing grant component system

* security HUD now shows a job icon on entities with a body

* remove sec stuff and do similar changes to split off PR + remove unused comp

* process comments

* don't return

* update to ComponentAddedOverlaySystemBase

* no cache

* colors and not rendering out of sight

* touch ups

* fix build & cleanup

* undo

* remove shader from icons

* process comments

* documentation

* fix licence

* validate prototype id

* just use args

* rename method and append in method

* type

* just fucken delete the command

* space

* undo

* remove

* don't use LocalPlayer

* re-add showhealthbars command, but working

* rename icon lists and conform health icon code to the others

* space

* undo

* update command

* oops

---------

Co-authored-by: Rane <60792108+Elijahrane@users.noreply.github.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
PrPleGoo
2024-01-04 17:48:57 +01:00
committed by GitHub
parent a9b797a091
commit eed663e8b4
27 changed files with 468 additions and 361 deletions

View File

@@ -0,0 +1,54 @@
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;
}
}

View File

@@ -1,21 +0,0 @@
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")));
}
}

View File

@@ -1,98 +0,0 @@
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);
}
}
}
}

View File

@@ -1,46 +0,0 @@
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);
}
}
}

View File

@@ -1,164 +0,0 @@
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();
}
}
}

View File

@@ -0,0 +1,170 @@
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;
}
}

View File

@@ -72,7 +72,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
private void OnPlayerDetached(LocalPlayerDetachedEvent args)
{
if (_player.LocalPlayer?.ControlledEntity == null)
if (_player.LocalSession?.AttachedEntity == null)
Deactivate();
}
@@ -93,17 +93,18 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
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);

View File

@@ -0,0 +1,46 @@
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);
}
}

View File

@@ -0,0 +1,77 @@
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;
}
}

View File

@@ -22,9 +22,9 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
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)

View File

@@ -8,6 +8,7 @@ using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
namespace Content.Client.Overlays;
public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIconsComponent>
{
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
@@ -30,9 +31,9 @@ public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIco
return;
}
var healthIcons = DecideSecurityIcon(uid);
var securityIcons = DecideSecurityIcon(uid);
@event.StatusIcons.AddRange(healthIcons);
@event.StatusIcons.AddRange(securityIcons);
}
private IReadOnlyList<StatusIconPrototype> DecideSecurityIcon(EntityUid uid)

View File

@@ -23,9 +23,9 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
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)

View File

@@ -22,9 +22,9 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
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)

View File

@@ -16,7 +16,6 @@ public sealed class StatusIconOverlay : Overlay
private readonly SpriteSystem _sprite;
private readonly TransformSystem _transform;
private readonly StatusIconSystem _statusIcon;
private readonly ShaderInstance _shader;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
@@ -28,7 +27,6 @@ public sealed class StatusIconOverlay : Overlay
_sprite = _entity.System<SpriteSystem>();
_transform = _entity.System<TransformSystem>();
_statusIcon = _entity.System<StatusIconSystem>();
_shader = _prototype.Index<ShaderPrototype>("unshaded").Instance();
}

View File

@@ -4,7 +4,7 @@ public sealed class RefreshEquipmentHudEvent<T> : EntityEventArgs, IInventoryRel
{
public SlotFlags TargetSlots { get; init; }
public bool Active = false;
public object? ExtraData;
public List<T> Components = new();
public RefreshEquipmentHudEvent(SlotFlags targetSlots)
{

View File

@@ -39,6 +39,8 @@ public partial class InventorySystem
// 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);

View File

@@ -0,0 +1,18 @@
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();
}

View File

@@ -0,0 +1,18 @@
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();
}

View File

@@ -0,0 +1,6 @@
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.

View File

@@ -1,3 +0,0 @@
cmd-togglehealthoverlay-desc = Toggles a health bar above mobs.
cmd-togglehealthoverlay-help = Usage: {$command}
cmd-togglehealthoverlay-notify = Health overlay system {$state}.

View File

@@ -8,6 +8,9 @@
sprite: Clothing/Eyes/Hud/diag.rsi
- type: Clothing
sprite: Clothing/Eyes/Hud/diag.rsi
- type: ShowHealthBars
damageContainers:
- Inorganic
- type: entity
parent: ClothingEyesBase
@@ -19,6 +22,12 @@
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
@@ -94,6 +103,12 @@
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
@@ -106,6 +121,12 @@
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
@@ -132,6 +153,13 @@
- type: Clothing
sprite: Clothing/Eyes/Hud/medsecengi.rsi
- type: ShowSecurityIcons
- type: ShowHealthBars
damageContainers:
- Biological
- Inorganic
- type: ShowHealthIcons
damageContainers:
- Biological
- type: ShowSyndicateIcons
- type: entity
@@ -145,6 +173,13 @@
- 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

View File

@@ -349,7 +349,13 @@
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

View File

@@ -0,0 +1,7 @@
- type: statusIcon
id: HealthIconFine
priority: 1
icon:
sprite: Interface/Misc/health_icons.rsi
state: Fine
locationPreference: Right

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,14 +0,0 @@
{
"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"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,14 @@
{
"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"
}
]
}