Cleans up StatusIconSystem and fixing some bugs (#28270)

This commit is contained in:
AJCM-git
2024-06-03 12:12:21 -04:00
committed by GitHub
parent 87ffbab461
commit 8c10581010
49 changed files with 224 additions and 259 deletions

View File

@@ -1,5 +1,7 @@
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.StatusIcon;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client.Access.UI namespace Content.Client.Access.UI
{ {
@@ -40,7 +42,7 @@ namespace Content.Client.Access.UI
SendMessage(new AgentIDCardJobChangedMessage(newJob)); SendMessage(new AgentIDCardJobChangedMessage(newJob));
} }
public void OnJobIconChanged(string newJobIconId) public void OnJobIconChanged(ProtoId<StatusIconPrototype> newJobIconId)
{ {
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId)); SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
} }

View File

@@ -38,7 +38,7 @@ namespace Content.Client.Access.UI
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text); JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
} }
public void SetAllowedIcons(HashSet<string> icons, string currentJobIconId) public void SetAllowedIcons(HashSet<ProtoId<StatusIconPrototype>> icons, string currentJobIconId)
{ {
IconGrid.DisposeAllChildren(); IconGrid.DisposeAllChildren();
@@ -46,10 +46,8 @@ namespace Content.Client.Access.UI
var i = 0; var i = 0;
foreach (var jobIconId in icons) foreach (var jobIconId in icons)
{ {
if (!_prototypeManager.TryIndex<StatusIconPrototype>(jobIconId, out var jobIcon)) if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
{
continue; continue;
}
String styleBase = StyleBase.ButtonOpenBoth; String styleBase = StyleBase.ButtonOpenBoth;
var modulo = i % JobIconColumnCount; var modulo = i % JobIconColumnCount;
@@ -77,7 +75,7 @@ namespace Content.Client.Access.UI
}; };
jobIconButton.AddChild(jobIconTexture); jobIconButton.AddChild(jobIconTexture);
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID); jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
IconGrid.AddChild(jobIconButton); IconGrid.AddChild(jobIconButton);
if (jobIconId.Equals(currentJobIconId)) if (jobIconId.Equals(currentJobIconId))

View File

@@ -1,55 +0,0 @@
using Content.Shared.Antag;
using Content.Shared.Revolutionary.Components;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Content.Shared.Zombies;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Antag;
/// <summary>
/// Used for assigning specified icons for antags.
/// </summary>
public sealed class AntagStatusIconSystem : SharedStatusIconSystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPlayerManager _player = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetIcon);
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetIcon);
SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetIcon);
}
/// <summary>
/// Adds a Status Icon on an entity if the player is supposed to see it.
/// </summary>
private void GetIcon<T>(EntityUid uid, T comp, ref GetStatusIconsEvent ev) where T: IAntagStatusIconComponent
{
var ent = _player.LocalSession?.AttachedEntity;
var canEv = new CanDisplayStatusIconsEvent(ent);
RaiseLocalEvent(uid, ref canEv);
if (!canEv.Cancelled)
ev.StatusIcons.Add(_prototype.Index(comp.StatusIcon));
}
/// <summary>
/// Adds the Rev Icon on an entity if the player is supposed to see it. This additional function is needed to deal
/// with a special case where if someone is a head rev we only want to display the headrev icon.
/// </summary>
private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent ev)
{
if (HasComp<HeadRevolutionaryComponent>(uid))
return;
GetIcon(uid, comp, ref ev);
}
}

View File

@@ -35,6 +35,7 @@ public sealed class ShowHealthBarsCommand : LocalizedCommands
var showHealthBarsComponent = new ShowHealthBarsComponent var showHealthBarsComponent = new ShowHealthBarsComponent
{ {
DamageContainers = args.ToList(), DamageContainers = args.ToList(),
HealthStatusIcon = "",
NetSyncEnabled = false NetSyncEnabled = false
}; };

View File

@@ -244,7 +244,7 @@ namespace Content.Client.LateJoin
VerticalAlignment = VAlignment.Center VerticalAlignment = VAlignment.Center
}; };
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(prototype.Icon); var jobIcon = _prototypeManager.Index(prototype.Icon);
icon.Texture = _sprites.Frame0(jobIcon.Icon); icon.Texture = _sprites.Frame0(jobIcon.Icon);
jobSelector.AddChild(icon); jobSelector.AddChild(icon);

View File

@@ -806,7 +806,7 @@ namespace Content.Client.Lobby.UI
TextureScale = new Vector2(2, 2), TextureScale = new Vector2(2, 2),
VerticalAlignment = VAlignment.Center VerticalAlignment = VAlignment.Center
}; };
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(job.Icon); var jobIcon = _prototypeManager.Index(job.Icon);
icon.Texture = jobIcon.Icon.Frame0(); icon.Texture = jobIcon.Icon.Frame0();
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon); selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon);

View File

@@ -1,14 +1,17 @@
using System.Numerics; using System.Numerics;
using Content.Client.StatusIcon;
using Content.Client.UserInterface.Systems; 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;
using Content.Shared.StatusIcon.Components; 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 Robust.Shared.Prototypes;
using static Robust.Shared.Maths.Color; using static Robust.Shared.Maths.Color;
namespace Content.Client.Overlays; namespace Content.Client.Overlays;
@@ -19,19 +22,27 @@ namespace Content.Client.Overlays;
public sealed class EntityHealthBarOverlay : Overlay public sealed class EntityHealthBarOverlay : Overlay
{ {
private readonly IEntityManager _entManager; private readonly IEntityManager _entManager;
private readonly IPrototypeManager _prototype;
private readonly SharedTransformSystem _transform; private readonly SharedTransformSystem _transform;
private readonly MobStateSystem _mobStateSystem; private readonly MobStateSystem _mobStateSystem;
private readonly MobThresholdSystem _mobThresholdSystem; private readonly MobThresholdSystem _mobThresholdSystem;
private readonly StatusIconSystem _statusIconSystem;
private readonly ProgressColorSystem _progressColor; private readonly ProgressColorSystem _progressColor;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
public HashSet<string> DamageContainers = new(); public HashSet<string> DamageContainers = new();
public ProtoId<StatusIconPrototype> StatusIcon;
public EntityHealthBarOverlay(IEntityManager entManager) public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype)
{ {
_entManager = entManager; _entManager = entManager;
_prototype = prototype;
_transform = _entManager.System<SharedTransformSystem>(); _transform = _entManager.System<SharedTransformSystem>();
_mobStateSystem = _entManager.System<MobStateSystem>(); _mobStateSystem = _entManager.System<MobStateSystem>();
_mobThresholdSystem = _entManager.System<MobThresholdSystem>(); _mobThresholdSystem = _entManager.System<MobThresholdSystem>();
_statusIconSystem = _entManager.System<StatusIconSystem>();
_progressColor = _entManager.System<ProgressColorSystem>(); _progressColor = _entManager.System<ProgressColorSystem>();
} }
@@ -44,6 +55,7 @@ public sealed class EntityHealthBarOverlay : Overlay
const float scale = 1f; const float scale = 1f;
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale)); var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation); var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
_prototype.TryIndex(StatusIcon, out var statusIcon);
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>(); var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
while (query.MoveNext(out var uid, while (query.MoveNext(out var uid,
@@ -52,31 +64,23 @@ public sealed class EntityHealthBarOverlay : Overlay
out var damageableComponent, out var damageableComponent,
out var spriteComponent)) out var spriteComponent))
{ {
if (_entManager.TryGetComponent<MetaDataComponent>(uid, out var metaDataComponent) && if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent<MetaDataComponent>(uid)), statusIcon))
metaDataComponent.Flags.HasFlag(MetaDataFlags.InContainer))
{
continue; continue;
}
// We want the stealth user to still be able to see his health bar himself
if (!xformQuery.TryGetComponent(uid, out var xform) || if (!xformQuery.TryGetComponent(uid, out var xform) ||
xform.MapID != args.MapId) xform.MapID != args.MapId)
{
continue; continue;
}
if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID)) if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID))
{
continue; continue;
}
// we use the status icon component bounds if specified otherwise use sprite // we use the status icon component bounds if specified otherwise use sprite
var bounds = _entManager.GetComponentOrNull<StatusIconComponent>(uid)?.Bounds ?? spriteComponent.Bounds; var bounds = _entManager.GetComponentOrNull<StatusIconComponent>(uid)?.Bounds ?? spriteComponent.Bounds;
var worldPos = _transform.GetWorldPosition(xform, xformQuery); var worldPos = _transform.GetWorldPosition(xform, xformQuery);
if (!bounds.Translated(worldPos).Intersects(args.WorldAABB)) if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
{
continue; continue;
}
// we are all progressing towards death every day // we are all progressing towards death every day
if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress) if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress)

View File

@@ -19,10 +19,10 @@ public sealed class ShowCriminalRecordIconsSystem : EquipmentHudSystem<ShowCrimi
private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev) private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev)
{ {
if (!IsActive || ev.InContainer) if (!IsActive)
return; return;
if (_prototype.TryIndex<StatusIconPrototype>(component.StatusIcon.Id, out var iconPrototype)) if (_prototype.TryIndex(component.StatusIcon, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype); ev.StatusIcons.Add(iconPrototype);
} }
} }

View File

@@ -2,6 +2,8 @@ using Content.Shared.Inventory.Events;
using Content.Shared.Overlays; using Content.Shared.Overlays;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using System.Linq; using System.Linq;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Overlays; namespace Content.Client.Overlays;
@@ -11,6 +13,7 @@ namespace Content.Client.Overlays;
public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComponent> public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComponent>
{ {
[Dependency] private readonly IOverlayManager _overlayMan = default!; [Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private EntityHealthBarOverlay _overlay = default!; private EntityHealthBarOverlay _overlay = default!;
@@ -18,18 +21,23 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
{ {
base.Initialize(); base.Initialize();
_overlay = new(EntityManager); _overlay = new(EntityManager, _prototype);
} }
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component) protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
{ {
base.UpdateInternal(component); base.UpdateInternal(component);
foreach (var damageContainerId in component.Components.SelectMany(x => x.DamageContainers)) foreach (var comp in component.Components)
{
foreach (var damageContainerId in comp.DamageContainers)
{ {
_overlay.DamageContainers.Add(damageContainerId); _overlay.DamageContainers.Add(damageContainerId);
} }
_overlay.StatusIcon = comp.HealthStatusIcon;
}
if (!_overlayMan.HasOverlay<EntityHealthBarOverlay>()) if (!_overlayMan.HasOverlay<EntityHealthBarOverlay>())
{ {
_overlayMan.AddOverlay(_overlay); _overlayMan.AddOverlay(_overlay);

View File

@@ -24,7 +24,6 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
base.Initialize(); base.Initialize();
SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent); SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
} }
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component) protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component)
@@ -46,7 +45,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args) private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
{ {
if (!IsActive || args.InContainer) if (!IsActive)
return; return;
var healthIcons = DecideHealthIcons(entity); var healthIcons = DecideHealthIcons(entity);

View File

@@ -18,7 +18,7 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev) private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev)
{ {
if (!IsActive || ev.InContainer) if (!IsActive)
return; return;
if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype)) if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype))

View File

@@ -25,7 +25,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev) private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev)
{ {
if (!IsActive || ev.InContainer) if (!IsActive)
return; return;
var iconId = JobIconForNoId; var iconId = JobIconForNoId;

View File

@@ -19,10 +19,10 @@ public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShiel
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev) private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
{ {
if (!IsActive || ev.InContainer) if (!IsActive)
return; return;
if (_prototype.TryIndex<StatusIconPrototype>(component.MindShieldStatusIcon.Id, out var iconPrototype)) if (_prototype.TryIndex(component.MindShieldStatusIcon, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype); ev.StatusIcons.Add(iconPrototype);
} }
} }

View File

@@ -19,11 +19,10 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev) private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev)
{ {
if (!IsActive || ev.InContainer) if (!IsActive)
return; return;
if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype)) if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype); ev.StatusIcons.Add(iconPrototype);
} }
} }

View File

@@ -18,7 +18,7 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev) private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev)
{ {
if (!IsActive || ev.InContainer) if (!IsActive)
return; return;
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype)) if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))

View File

@@ -1,44 +1,37 @@
using Content.Shared.Antag;
using Content.Shared.Revolutionary.Components; using Content.Shared.Revolutionary.Components;
using Content.Shared.Ghost; using Content.Shared.Revolutionary;
using Content.Shared.StatusIcon.Components; using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
namespace Content.Client.Revolutionary; namespace Content.Client.Revolutionary;
/// <summary> /// <summary>
/// Used for the client to get status icons from other revs. /// Used for the client to get status icons from other revs.
/// </summary> /// </summary>
public sealed class RevolutionarySystem : EntitySystem public sealed class RevolutionarySystem : SharedRevolutionarySystem
{ {
[Dependency] private readonly IPrototypeManager _prototype = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<RevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon); SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
SubscribeLocalEvent<HeadRevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon); SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetHeadRevIcon);
} }
/// <summary> private void GetRevIcon(Entity<RevolutionaryComponent> ent, ref GetStatusIconsEvent args)
/// Determine whether a client should display the rev icon.
/// </summary>
private void OnCanShowRevIcon<T>(EntityUid uid, T comp, ref CanDisplayStatusIconsEvent args) where T : IAntagStatusIconComponent
{ {
args.Cancelled = !CanDisplayIcon(args.User, comp.IconVisibleToGhost); if (HasComp<HeadRevolutionaryComponent>(ent))
return;
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
args.StatusIcons.Add(iconPrototype);
} }
/// <summary> private void GetHeadRevIcon(Entity<HeadRevolutionaryComponent> ent, ref GetStatusIconsEvent args)
/// The criteria that determine whether a client should see Rev/Head rev icons.
/// </summary>
private bool CanDisplayIcon(EntityUid? uid, bool visibleToGhost)
{ {
if (HasComp<HeadRevolutionaryComponent>(uid) || HasComp<RevolutionaryComponent>(uid)) if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
return true; args.StatusIcons.Add(iconPrototype);
if (visibleToGhost && HasComp<GhostComponent>(uid))
return true;
return HasComp<ShowRevIconsComponent>(uid);
} }
} }

View File

@@ -30,13 +30,12 @@ public sealed class SSDIndicatorSystem : EntitySystem
{ {
if (component.IsSSD && if (component.IsSSD &&
_cfg.GetCVar(CCVars.ICShowSSDIndicator) && _cfg.GetCVar(CCVars.ICShowSSDIndicator) &&
!args.InContainer &&
!_mobState.IsDead(uid) && !_mobState.IsDead(uid) &&
!HasComp<ActiveNPCComponent>(uid) && !HasComp<ActiveNPCComponent>(uid) &&
TryComp<MindContainerComponent>(uid, out var mindContainer) && TryComp<MindContainerComponent>(uid, out var mindContainer) &&
mindContainer.ShowExamineInfo) mindContainer.ShowExamineInfo)
{ {
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(component.Icon)); args.StatusIcons.Add(_prototype.Index(component.Icon));
} }
} }
} }

View File

@@ -45,7 +45,7 @@ public sealed class StatusIconOverlay : Overlay
var query = _entity.AllEntityQueryEnumerator<StatusIconComponent, SpriteComponent, TransformComponent, MetaDataComponent>(); var query = _entity.AllEntityQueryEnumerator<StatusIconComponent, SpriteComponent, TransformComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta)) while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta))
{ {
if (xform.MapID != args.MapId) if (xform.MapID != args.MapId || !sprite.Visible)
continue; continue;
var bounds = comp.Bounds ?? sprite.Bounds; var bounds = comp.Bounds ?? sprite.Bounds;
@@ -72,6 +72,8 @@ public sealed class StatusIconOverlay : Overlay
foreach (var proto in icons) foreach (var proto in icons)
{ {
if (!_statusIcon.IsVisible((uid, meta), proto))
continue;
var curTime = _timing.RealTime; var curTime = _timing.RealTime;
var texture = _sprite.GetFrame(proto.Icon, curTime); var texture = _sprite.GetFrame(proto.Icon, curTime);

View File

@@ -1,7 +1,11 @@
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Content.Shared.StatusIcon; using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components; using Content.Shared.StatusIcon.Components;
using Content.Shared.Stealth.Components;
using Content.Shared.Whitelist;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
namespace Content.Client.StatusIcon; namespace Content.Client.StatusIcon;
@@ -13,6 +17,8 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
{ {
[Dependency] private readonly IConfigurationManager _configuration = default!; [Dependency] private readonly IConfigurationManager _configuration = default!;
[Dependency] private readonly IOverlayManager _overlay = default!; [Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
private bool _globalEnabled; private bool _globalEnabled;
private bool _localEnabled; private bool _localEnabled;
@@ -54,10 +60,34 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
if (meta.EntityLifeStage >= EntityLifeStage.Terminating) if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
return list; return list;
var inContainer = (meta.Flags & MetaDataFlags.InContainer) != 0; var ev = new GetStatusIconsEvent(list);
var ev = new GetStatusIconsEvent(list, inContainer);
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);
return ev.StatusIcons; return ev.StatusIcons;
} }
}
/// <summary>
/// For overlay to check if an entity can be seen.
/// </summary>
public bool IsVisible(Entity<MetaDataComponent> ent, StatusIconData data)
{
var viewer = _playerManager.LocalSession?.AttachedEntity;
// Always show our icons to our entity
if (viewer == ent.Owner)
return true;
if (data.VisibleToGhosts && HasComp<GhostComponent>(viewer))
return true;
if (data.HideInContainer && (ent.Comp.Flags & MetaDataFlags.InContainer) != 0)
return false;
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
return false;
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
return false;
return true;
}
}

View File

@@ -1,4 +1,5 @@
using Content.Client.Interactable.Components; using Content.Client.Interactable.Components;
using Content.Client.StatusIcon;
using Content.Shared.Stealth; using Content.Shared.Stealth;
using Content.Shared.Stealth.Components; using Content.Shared.Stealth.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -18,6 +19,7 @@ public sealed class StealthSystem : SharedStealthSystem
base.Initialize(); base.Initialize();
_shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique(); _shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique();
SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown); SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<StealthComponent, BeforePostShaderRenderEvent>(OnShaderRender); SubscribeLocalEvent<StealthComponent, BeforePostShaderRenderEvent>(OnShaderRender);
@@ -93,4 +95,3 @@ public sealed class StealthSystem : SharedStealthSystem
args.Sprite.Color = new Color(visibility, visibility, 1, 1); args.Sprite.Color = new Color(visibility, visibility, 1, 1);
} }
} }

View File

@@ -1,21 +1,40 @@
using System.Linq; using System.Linq;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components; using Content.Shared.StatusIcon.Components;
using Content.Shared.Zombies; using Content.Shared.Zombies;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client.Zombies; namespace Content.Client.Zombies;
public sealed class ZombieSystem : EntitySystem public sealed class ZombieSystem : SharedZombieSystem
{ {
[Dependency] private readonly IPrototypeManager _prototype = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ZombieComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<ZombieComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<ZombieComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons); SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetZombieIcon);
SubscribeLocalEvent<InitialInfectedComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons); SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetInitialInfectedIcon);
}
private void GetZombieIcon(Entity<ZombieComponent> ent, ref GetStatusIconsEvent args)
{
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
args.StatusIcons.Add(iconPrototype);
}
private void GetInitialInfectedIcon(Entity<InitialInfectedComponent> ent, ref GetStatusIconsEvent args)
{
if (HasComp<ZombieComponent>(ent))
return;
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
args.StatusIcons.Add(iconPrototype);
} }
private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args) private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args)
@@ -31,29 +50,4 @@ public sealed class ZombieSystem : EntitySystem
sprite.LayerSetColor(i, component.SkinColor); sprite.LayerSetColor(i, component.SkinColor);
} }
} }
/// <summary>
/// Determines whether a player should be able to see the StatusIcon for zombies.
/// </summary>
private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args)
{
if (HasComp<ZombieComponent>(args.User) || HasComp<InitialInfectedComponent>(args.User) || HasComp<ShowZombieIconsComponent>(args.User))
return;
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
return;
args.Cancelled = true;
}
private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, ref CanDisplayStatusIconsEvent args)
{
if (HasComp<InitialInfectedComponent>(args.User) && !HasComp<ZombieComponent>(args.User))
return;
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
return;
args.Cancelled = true;
}
} }

View File

@@ -1,5 +1,5 @@
using Content.Shared.StatusIcon; using Content.Shared.StatusIcon;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; using Robust.Shared.Prototypes;
namespace Content.Server.Access.Components namespace Content.Server.Access.Components
{ {
@@ -9,7 +9,7 @@ namespace Content.Server.Access.Components
/// <summary> /// <summary>
/// Set of job icons that the agent ID card can show. /// Set of job icons that the agent ID card can show.
/// </summary> /// </summary>
[DataField("icons", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<StatusIconPrototype>))] [DataField]
public HashSet<string> Icons = new(); public HashSet<ProtoId<StatusIconPrototype>> Icons;
} }
} }

View File

@@ -90,14 +90,10 @@ namespace Content.Server.Access.Systems
private void OnJobIconChanged(EntityUid uid, AgentIDCardComponent comp, AgentIDCardJobIconChangedMessage args) private void OnJobIconChanged(EntityUid uid, AgentIDCardComponent comp, AgentIDCardJobIconChangedMessage args)
{ {
if (!TryComp<IdCardComponent>(uid, out var idCard)) if (!TryComp<IdCardComponent>(uid, out var idCard))
{
return; return;
}
if (!_prototypeManager.TryIndex<StatusIconPrototype>(args.JobIconId, out var jobIcon)) if (!_prototypeManager.TryIndex(args.JobIconId, out var jobIcon))
{
return; return;
}
_cardSystem.TryChangeJobIcon(uid, jobIcon, idCard); _cardSystem.TryChangeJobIcon(uid, jobIcon, idCard);
@@ -109,7 +105,7 @@ namespace Content.Server.Access.Systems
{ {
foreach (var jobPrototype in _prototypeManager.EnumeratePrototypes<JobPrototype>()) foreach (var jobPrototype in _prototypeManager.EnumeratePrototypes<JobPrototype>())
{ {
if(jobPrototype.Icon == jobIcon.ID) if (jobPrototype.Icon == jobIcon.ID)
{ {
job = jobPrototype; job = jobPrototype;
return true; return true;

View File

@@ -129,7 +129,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
_idCard.TryChangeJobTitle(targetId, newJobTitle, player: player); _idCard.TryChangeJobTitle(targetId, newJobTitle, player: player);
if (_prototype.TryIndex<JobPrototype>(newJobProto, out var job) if (_prototype.TryIndex<JobPrototype>(newJobProto, out var job)
&& _prototype.TryIndex<StatusIconPrototype>(job.Icon, out var jobIcon)) && _prototype.TryIndex(job.Icon, out var jobIcon))
{ {
_idCard.TryChangeJobIcon(targetId, jobIcon, player: player); _idCard.TryChangeJobIcon(targetId, jobIcon, player: player);
_idCard.TryChangeJobDepartment(targetId, job); _idCard.TryChangeJobDepartment(targetId, job);

View File

@@ -82,9 +82,7 @@ public sealed class PresetIdCardSystem : EntitySystem
_cardSystem.TryChangeJobTitle(uid, job.LocalizedName); _cardSystem.TryChangeJobTitle(uid, job.LocalizedName);
_cardSystem.TryChangeJobDepartment(uid, job); _cardSystem.TryChangeJobDepartment(uid, job);
if (_prototypeManager.TryIndex<StatusIconPrototype>(job.Icon, out var jobIcon)) if (_prototypeManager.TryIndex(job.Icon, out var jobIcon))
{
_cardSystem.TryChangeJobIcon(uid, jobIcon); _cardSystem.TryChangeJobIcon(uid, jobIcon);
} }
}
} }

View File

@@ -0,0 +1,5 @@
using Content.Shared.Revolutionary;
namespace Content.Server.Revolutionary;
public sealed class RevolutionarySystem : SharedRevolutionarySystem;

View File

@@ -257,10 +257,8 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
_cardSystem.TryChangeFullName(cardId, characterName, card); _cardSystem.TryChangeFullName(cardId, characterName, card);
_cardSystem.TryChangeJobTitle(cardId, jobPrototype.LocalizedName, card); _cardSystem.TryChangeJobTitle(cardId, jobPrototype.LocalizedName, card);
if (_prototypeManager.TryIndex<StatusIconPrototype>(jobPrototype.Icon, out var jobIcon)) if (_prototypeManager.TryIndex(jobPrototype.Icon, out var jobIcon))
{
_cardSystem.TryChangeJobIcon(cardId, jobIcon, card); _cardSystem.TryChangeJobIcon(cardId, jobIcon, card);
}
var extendedAccess = false; var extendedAccess = false;
if (station != null) if (station != null)

View File

@@ -1,3 +1,5 @@
using Content.Shared.StatusIcon;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Access.Systems namespace Content.Shared.Access.Systems
@@ -23,12 +25,12 @@ namespace Content.Shared.Access.Systems
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class AgentIDCardBoundUserInterfaceState : BoundUserInterfaceState public sealed class AgentIDCardBoundUserInterfaceState : BoundUserInterfaceState
{ {
public readonly HashSet<string> Icons; public readonly HashSet<ProtoId<StatusIconPrototype>> Icons;
public string CurrentName { get; } public string CurrentName { get; }
public string CurrentJob { get; } public string CurrentJob { get; }
public string CurrentJobIconId { get; } public string CurrentJobIconId { get; }
public AgentIDCardBoundUserInterfaceState(string currentName, string currentJob, string currentJobIconId, HashSet<string> icons) public AgentIDCardBoundUserInterfaceState(string currentName, string currentJob, string currentJobIconId, HashSet<ProtoId<StatusIconPrototype>> icons)
{ {
Icons = icons; Icons = icons;
CurrentName = currentName; CurrentName = currentName;
@@ -62,9 +64,9 @@ namespace Content.Shared.Access.Systems
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class AgentIDCardJobIconChangedMessage : BoundUserInterfaceMessage public sealed class AgentIDCardJobIconChangedMessage : BoundUserInterfaceMessage
{ {
public string JobIconId { get; } public ProtoId<StatusIconPrototype> JobIconId { get; }
public AgentIDCardJobIconChangedMessage(string jobIconId) public AgentIDCardJobIconChangedMessage(ProtoId<StatusIconPrototype> jobIconId)
{ {
JobIconId = jobIconId; JobIconId = jobIconId;
} }

View File

@@ -1,12 +0,0 @@
using Content.Shared.StatusIcon;
using Robust.Shared.Prototypes;
namespace Content.Shared.Antag;
public interface IAntagStatusIconComponent
{
public ProtoId<StatusIconPrototype> StatusIcon { get; set; }
public bool IconVisibleToGhost { get; set; }
}

View File

@@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Antag;
/// <summary>
/// Determines whether Someone can see antags icons
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class ShowAntagIconsComponent: Component;

View File

@@ -1,5 +1,7 @@
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.StatusIcon;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Overlays; namespace Content.Shared.Overlays;
@@ -15,4 +17,7 @@ public sealed partial class ShowHealthBarsComponent : Component
/// </summary> /// </summary>
[DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))] [DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
public List<string> DamageContainers = new(); public List<string> DamageContainers = new();
[DataField]
public ProtoId<StatusIconPrototype> HealthStatusIcon = "HealthIconFine";
} }

View File

@@ -9,7 +9,7 @@ namespace Content.Shared.Revolutionary.Components;
/// Component used for marking a Head Rev for conversion and winning/losing. /// Component used for marking a Head Rev for conversion and winning/losing.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))] [RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
public sealed partial class HeadRevolutionaryComponent : Component, IAntagStatusIconComponent public sealed partial class HeadRevolutionaryComponent : Component
{ {
/// <summary> /// <summary>
/// The status icon corresponding to the head revolutionary. /// The status icon corresponding to the head revolutionary.
@@ -24,7 +24,4 @@ public sealed partial class HeadRevolutionaryComponent : Component, IAntagStatus
public TimeSpan StunTime = TimeSpan.FromSeconds(3); public TimeSpan StunTime = TimeSpan.FromSeconds(3);
public override bool SessionSpecific => true; public override bool SessionSpecific => true;
[DataField]
public bool IconVisibleToGhost { get; set; } = true;
} }

View File

@@ -10,7 +10,7 @@ namespace Content.Shared.Revolutionary.Components;
/// Used for marking regular revs as well as storing icon prototypes so you can see fellow revs. /// Used for marking regular revs as well as storing icon prototypes so you can see fellow revs.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))] [RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
public sealed partial class RevolutionaryComponent : Component, IAntagStatusIconComponent public sealed partial class RevolutionaryComponent : Component
{ {
/// <summary> /// <summary>
/// The status icon prototype displayed for revolutionaries /// The status icon prototype displayed for revolutionaries
@@ -25,7 +25,4 @@ public sealed partial class RevolutionaryComponent : Component, IAntagStatusIcon
public SoundSpecifier RevStartSound = new SoundPathSpecifier("/Audio/Ambience/Antag/headrev_start.ogg"); public SoundSpecifier RevStartSound = new SoundPathSpecifier("/Audio/Ambience/Antag/headrev_start.ogg");
public override bool SessionSpecific => true; public override bool SessionSpecific => true;
[DataField]
public bool IconVisibleToGhost { get; set; } = true;
} }

View File

@@ -1,11 +0,0 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Revolutionary.Components;
/// <summary>
/// Determines whether Someone can see rev/headrev icons on revs.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class ShowRevIconsComponent: Component
{
}

View File

@@ -1,4 +1,3 @@
using Content.Shared.Ghost;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Mindshield.Components; using Content.Shared.Mindshield.Components;
using Content.Shared.Popups; using Content.Shared.Popups;
@@ -6,10 +5,11 @@ using Content.Shared.Revolutionary.Components;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Player; using Robust.Shared.Player;
using Content.Shared.Antag;
namespace Content.Shared.Revolutionary; namespace Content.Shared.Revolutionary;
public sealed class SharedRevolutionarySystem : EntitySystem public abstract class SharedRevolutionarySystem : EntitySystem
{ {
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedStunSystem _sharedStun = default!; [Dependency] private readonly SharedStunSystem _sharedStun = default!;
@@ -23,7 +23,7 @@ public sealed class SharedRevolutionarySystem : EntitySystem
SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentGetStateAttemptEvent>(OnRevCompGetStateAttempt); SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentGetStateAttemptEvent>(OnRevCompGetStateAttempt);
SubscribeLocalEvent<RevolutionaryComponent, ComponentStartup>(DirtyRevComps); SubscribeLocalEvent<RevolutionaryComponent, ComponentStartup>(DirtyRevComps);
SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentStartup>(DirtyRevComps); SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentStartup>(DirtyRevComps);
SubscribeLocalEvent<ShowRevIconsComponent, ComponentStartup>(DirtyRevComps); SubscribeLocalEvent<ShowAntagIconsComponent, ComponentStartup>(DirtyRevComps);
} }
/// <summary> /// <summary>
@@ -52,7 +52,7 @@ public sealed class SharedRevolutionarySystem : EntitySystem
/// </summary> /// </summary>
private void OnRevCompGetStateAttempt(EntityUid uid, HeadRevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args) private void OnRevCompGetStateAttempt(EntityUid uid, HeadRevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args)
{ {
args.Cancelled = !CanGetState(args.Player, comp.IconVisibleToGhost); args.Cancelled = !CanGetState(args.Player);
} }
/// <summary> /// <summary>
@@ -60,30 +60,24 @@ public sealed class SharedRevolutionarySystem : EntitySystem
/// </summary> /// </summary>
private void OnRevCompGetStateAttempt(EntityUid uid, RevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args) private void OnRevCompGetStateAttempt(EntityUid uid, RevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args)
{ {
args.Cancelled = !CanGetState(args.Player, comp.IconVisibleToGhost); args.Cancelled = !CanGetState(args.Player);
} }
/// <summary> /// <summary>
/// The criteria that determine whether a Rev/HeadRev component should be sent to a client. /// The criteria that determine whether a Rev/HeadRev component should be sent to a client.
/// </summary> /// </summary>
/// <param name="player"> The Player the component will be sent to.</param> /// <param name="player"> The Player the component will be sent to.</param>
/// <param name="visibleToGhosts"> Whether the component permits the icon to be visible to observers. </param>
/// <returns></returns> /// <returns></returns>
private bool CanGetState(ICommonSession? player, bool visibleToGhosts) private bool CanGetState(ICommonSession? player)
{ {
//Apparently this can be null in replays so I am just returning true. //Apparently this can be null in replays so I am just returning true.
if (player is null) if (player?.AttachedEntity is not {} uid)
return true; return true;
var uid = player.AttachedEntity;
if (HasComp<RevolutionaryComponent>(uid) || HasComp<HeadRevolutionaryComponent>(uid)) if (HasComp<RevolutionaryComponent>(uid) || HasComp<HeadRevolutionaryComponent>(uid))
return true; return true;
if (visibleToGhosts && HasComp<GhostComponent>(uid)) return HasComp<ShowAntagIconsComponent>(uid);
return true;
return HasComp<ShowRevIconsComponent>(uid);
} }
/// <summary> /// <summary>
/// Dirties all the Rev components so they are sent to clients. /// Dirties all the Rev components so they are sent to clients.

View File

@@ -97,8 +97,8 @@ namespace Content.Shared.Roles
[DataField("jobEntity", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))] [DataField("jobEntity", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? JobEntity = null; public string? JobEntity = null;
[DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer<StatusIconPrototype>))] [DataField]
public string Icon { get; private set; } = "JobIconUnknown"; public ProtoId<StatusIconPrototype> Icon { get; private set; } = "JobIconUnknown";
[DataField("special", serverOnly: true)] [DataField("special", serverOnly: true)]
public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>(); public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>();

View File

@@ -1,5 +1,6 @@
using Content.Shared.StatusIcon; using Content.Shared.StatusIcon;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.SSDIndicator; namespace Content.Shared.SSDIndicator;
@@ -16,6 +17,6 @@ public sealed partial class SSDIndicatorComponent : Component
public bool IsSSD = true; public bool IsSSD = true;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer<StatusIconPrototype>))] [DataField]
public string Icon = "SSDIcon"; public ProtoId<StatusIconPrototype> Icon = "SSDIcon";
} }

View File

@@ -24,16 +24,4 @@ public sealed partial class StatusIconComponent : Component
/// </summary> /// </summary>
/// <param name="StatusIcons"></param> /// <param name="StatusIcons"></param>
[ByRefEvent] [ByRefEvent]
public record struct GetStatusIconsEvent(List<StatusIconData> StatusIcons, bool InContainer); public record struct GetStatusIconsEvent(List<StatusIconData> StatusIcons);
/// <summary>
/// Event raised on the Client-side to determine whether to display a status icon on an entity.
/// </summary>
/// <param name="User">The player that will see the icons</param>
[ByRefEvent]
public record struct CanDisplayStatusIconsEvent(EntityUid? User = null)
{
public EntityUid? User = User;
public bool Cancelled = false;
}

View File

@@ -1,3 +1,5 @@
using Content.Shared.Stealth.Components;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
@@ -15,26 +17,45 @@ public partial class StatusIconData : IComparable<StatusIconData>
/// <summary> /// <summary>
/// The icon that's displayed on the entity. /// The icon that's displayed on the entity.
/// </summary> /// </summary>
[DataField("icon", required: true)] [DataField(required: true)]
public SpriteSpecifier Icon = default!; public SpriteSpecifier Icon = default!;
/// <summary> /// <summary>
/// A priority for the order in which the icons will be displayed. /// A priority for the order in which the icons will be displayed.
/// </summary> /// </summary>
[DataField("priority")] [DataField]
public int Priority = 10; public int Priority = 10;
/// <summary>
/// Whether or not to hide the icon to ghosts
/// </summary>
[DataField]
public bool VisibleToGhosts = true;
/// <summary>
/// Whether or not to hide the icon when we are inside a container like a locker or a crate.
/// </summary>
[DataField]
public bool HideInContainer = true;
/// <summary>
/// Whether or not to hide the icon when the entity has an active <see cref="StealthComponent"/>
/// </summary>
[DataField]
public bool HideOnStealth = true;
/// <summary>
/// Specifies what entities and components/tags this icon can be shown to.
/// </summary>
[DataField]
public EntityWhitelist? ShowTo;
/// <summary> /// <summary>
/// A preference for where the icon will be displayed. None | Left | Right /// A preference for where the icon will be displayed. None | Left | Right
/// </summary> /// </summary>
[DataField("locationPreference")] [DataField]
public StatusIconLocationPreference LocationPreference = StatusIconLocationPreference.None; public StatusIconLocationPreference LocationPreference = StatusIconLocationPreference.None;
public int CompareTo(StatusIconData? other)
{
return Priority.CompareTo(other?.Priority ?? int.MaxValue);
}
/// <summary> /// <summary>
/// The layer the icon is displayed on. Mod is drawn above Base. Base | Mod /// The layer the icon is displayed on. Mod is drawn above Base. Base | Mod
/// </summary> /// </summary>
@@ -52,6 +73,11 @@ public partial class StatusIconData : IComparable<StatusIconData>
/// </summary> /// </summary>
[DataField] [DataField]
public bool IsShaded = false; public bool IsShaded = false;
public int CompareTo(StatusIconData? other)
{
return Priority.CompareTo(other?.Priority ?? int.MaxValue);
}
} }
/// <summary> /// <summary>

View File

@@ -1,4 +1,3 @@
using Content.Shared.Antag;
using Content.Shared.StatusIcon; using Content.Shared.StatusIcon;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -6,11 +5,8 @@ using Robust.Shared.Prototypes;
namespace Content.Shared.Zombies; namespace Content.Shared.Zombies;
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class InitialInfectedComponent : Component, IAntagStatusIconComponent public sealed partial class InitialInfectedComponent : Component
{ {
[DataField("initialInfectedStatusIcon")]
public ProtoId<StatusIconPrototype> StatusIcon { get; set; } = "InitialInfectedFaction";
[DataField] [DataField]
public bool IconVisibleToGhost { get; set; } = true; public ProtoId<StatusIconPrototype> StatusIcon = "InitialInfectedFaction";
} }

View File

@@ -1,12 +0,0 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Zombies;
/// <summary>
/// Makes it so an entity can view ZombieAntagIcons.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class ShowZombieIconsComponent: Component
{
}

View File

@@ -14,7 +14,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
namespace Content.Shared.Zombies; namespace Content.Shared.Zombies;
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class ZombieComponent : Component, IAntagStatusIconComponent public sealed partial class ZombieComponent : Component
{ {
/// <summary> /// <summary>
/// The baseline infection chance you have if you are completely nude /// The baseline infection chance you have if you are completely nude
@@ -97,9 +97,6 @@ public sealed partial class ZombieComponent : Component, IAntagStatusIconCompone
[DataField("zombieStatusIcon")] [DataField("zombieStatusIcon")]
public ProtoId<StatusIconPrototype> StatusIcon { get; set; } = "ZombieFaction"; public ProtoId<StatusIconPrototype> StatusIcon { get; set; } = "ZombieFaction";
[DataField]
public bool IconVisibleToGhost { get; set; } = true;
/// <summary> /// <summary>
/// Healing each second /// Healing each second
/// </summary> /// </summary>

View File

@@ -84,8 +84,7 @@
- type: Stripping - type: Stripping
- type: SolutionScanner - type: SolutionScanner
- type: IgnoreUIRange - type: IgnoreUIRange
- type: ShowRevIcons - type: ShowAntagIcons
- type: ShowZombieIcons
- type: Inventory - type: Inventory
templateId: aghost templateId: aghost
- type: InventorySlots - type: InventorySlots

View File

@@ -1,6 +1,11 @@
- type: statusIcon - type: statusIcon
id: ZombieFaction id: ZombieFaction
priority: 11 priority: 11
showTo:
components:
- ShowAntagIcons
- Zombie
- InitialInfected
icon: icon:
sprite: /Textures/Interface/Misc/job_icons.rsi sprite: /Textures/Interface/Misc/job_icons.rsi
state: Zombie state: Zombie
@@ -8,6 +13,10 @@
- type: statusIcon - type: statusIcon
id: InitialInfectedFaction id: InitialInfectedFaction
priority: 11 priority: 11
showTo:
components:
- ShowAntagIcons
- InitialInfected
icon: icon:
sprite: /Textures/Interface/Misc/job_icons.rsi sprite: /Textures/Interface/Misc/job_icons.rsi
state: InitialInfected state: InitialInfected
@@ -15,6 +24,10 @@
- type: statusIcon - type: statusIcon
id: RevolutionaryFaction id: RevolutionaryFaction
priority: 11 priority: 11
showTo:
components:
- ShowAntagIcons
- Revolutionary
icon: icon:
sprite: /Textures/Interface/Misc/job_icons.rsi sprite: /Textures/Interface/Misc/job_icons.rsi
state: Revolutionary state: Revolutionary
@@ -22,6 +35,10 @@
- type: statusIcon - type: statusIcon
id: HeadRevolutionaryFaction id: HeadRevolutionaryFaction
priority: 11 priority: 11
showTo:
components:
- ShowAntagIcons
- Revolutionary
icon: icon:
sprite: /Textures/Interface/Misc/job_icons.rsi sprite: /Textures/Interface/Misc/job_icons.rsi
state: HeadRevolutionary state: HeadRevolutionary