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

View File

@@ -38,7 +38,7 @@ namespace Content.Client.Access.UI
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();
@@ -46,10 +46,8 @@ namespace Content.Client.Access.UI
var i = 0;
foreach (var jobIconId in icons)
{
if (!_prototypeManager.TryIndex<StatusIconPrototype>(jobIconId, out var jobIcon))
{
if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
continue;
}
String styleBase = StyleBase.ButtonOpenBoth;
var modulo = i % JobIconColumnCount;
@@ -77,7 +75,7 @@ namespace Content.Client.Access.UI
};
jobIconButton.AddChild(jobIconTexture);
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
IconGrid.AddChild(jobIconButton);
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
{
DamageContainers = args.ToList(),
HealthStatusIcon = "",
NetSyncEnabled = false
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,13 +30,12 @@ public sealed class SSDIndicatorSystem : EntitySystem
{
if (component.IsSSD &&
_cfg.GetCVar(CCVars.ICShowSSDIndicator) &&
!args.InContainer &&
!_mobState.IsDead(uid) &&
!HasComp<ActiveNPCComponent>(uid) &&
TryComp<MindContainerComponent>(uid, out var mindContainer) &&
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>();
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;
var bounds = comp.Bounds ?? sprite.Bounds;
@@ -72,6 +72,8 @@ public sealed class StatusIconOverlay : Overlay
foreach (var proto in icons)
{
if (!_statusIcon.IsVisible((uid, meta), proto))
continue;
var curTime = _timing.RealTime;
var texture = _sprite.GetFrame(proto.Icon, curTime);

View File

@@ -1,7 +1,11 @@
using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Content.Shared.Stealth.Components;
using Content.Shared.Whitelist;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Configuration;
namespace Content.Client.StatusIcon;
@@ -13,6 +17,8 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
{
[Dependency] private readonly IConfigurationManager _configuration = default!;
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
private bool _globalEnabled;
private bool _localEnabled;
@@ -54,10 +60,34 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
return list;
var inContainer = (meta.Flags & MetaDataFlags.InContainer) != 0;
var ev = new GetStatusIconsEvent(list, inContainer);
var ev = new GetStatusIconsEvent(list);
RaiseLocalEvent(uid, ref ev);
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.StatusIcon;
using Content.Shared.Stealth;
using Content.Shared.Stealth.Components;
using Robust.Client.GameObjects;
@@ -18,6 +19,7 @@ public sealed class StealthSystem : SharedStealthSystem
base.Initialize();
_shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique();
SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<StealthComponent, BeforePostShaderRenderEvent>(OnShaderRender);
@@ -93,4 +95,3 @@ public sealed class StealthSystem : SharedStealthSystem
args.Sprite.Color = new Color(visibility, visibility, 1, 1);
}
}

View File

@@ -1,21 +1,40 @@
using System.Linq;
using Content.Shared.Ghost;
using Content.Shared.Humanoid;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Content.Shared.Zombies;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client.Zombies;
public sealed class ZombieSystem : EntitySystem
public sealed class ZombieSystem : SharedZombieSystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ZombieComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<ZombieComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
SubscribeLocalEvent<InitialInfectedComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetZombieIcon);
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)
@@ -31,29 +50,4 @@ public sealed class ZombieSystem : EntitySystem
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 Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
using Robust.Shared.Prototypes;
namespace Content.Server.Access.Components
{
@@ -9,7 +9,7 @@ namespace Content.Server.Access.Components
/// <summary>
/// Set of job icons that the agent ID card can show.
/// </summary>
[DataField("icons", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<StatusIconPrototype>))]
public HashSet<string> Icons = new();
[DataField]
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)
{
if (!TryComp<IdCardComponent>(uid, out var idCard))
{
return;
}
if (!_prototypeManager.TryIndex<StatusIconPrototype>(args.JobIconId, out var jobIcon))
{
if (!_prototypeManager.TryIndex(args.JobIconId, out var jobIcon))
return;
}
_cardSystem.TryChangeJobIcon(uid, jobIcon, idCard);
@@ -109,7 +105,7 @@ namespace Content.Server.Access.Systems
{
foreach (var jobPrototype in _prototypeManager.EnumeratePrototypes<JobPrototype>())
{
if(jobPrototype.Icon == jobIcon.ID)
if (jobPrototype.Icon == jobIcon.ID)
{
job = jobPrototype;
return true;

View File

@@ -129,7 +129,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
_idCard.TryChangeJobTitle(targetId, newJobTitle, player: player);
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.TryChangeJobDepartment(targetId, job);

View File

@@ -82,9 +82,7 @@ public sealed class PresetIdCardSystem : EntitySystem
_cardSystem.TryChangeJobTitle(uid, job.LocalizedName);
_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);
}
}
}

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.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);
}
var extendedAccess = false;
if (station != null)

View File

@@ -1,3 +1,5 @@
using Content.Shared.StatusIcon;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Access.Systems
@@ -23,12 +25,12 @@ namespace Content.Shared.Access.Systems
[Serializable, NetSerializable]
public sealed class AgentIDCardBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly HashSet<string> Icons;
public readonly HashSet<ProtoId<StatusIconPrototype>> Icons;
public string CurrentName { get; }
public string CurrentJob { 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;
CurrentName = currentName;
@@ -62,9 +64,9 @@ namespace Content.Shared.Access.Systems
[Serializable, NetSerializable]
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;
}

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.StatusIcon;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Overlays;
@@ -15,4 +17,7 @@ public sealed partial class ShowHealthBarsComponent : Component
/// </summary>
[DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
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.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
public sealed partial class HeadRevolutionaryComponent : Component, IAntagStatusIconComponent
public sealed partial class HeadRevolutionaryComponent : Component
{
/// <summary>
/// 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 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.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
public sealed partial class RevolutionaryComponent : Component, IAntagStatusIconComponent
public sealed partial class RevolutionaryComponent : Component
{
/// <summary>
/// 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 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.Mindshield.Components;
using Content.Shared.Popups;
@@ -6,10 +5,11 @@ using Content.Shared.Revolutionary.Components;
using Content.Shared.Stunnable;
using Robust.Shared.GameStates;
using Robust.Shared.Player;
using Content.Shared.Antag;
namespace Content.Shared.Revolutionary;
public sealed class SharedRevolutionarySystem : EntitySystem
public abstract class SharedRevolutionarySystem : EntitySystem
{
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedStunSystem _sharedStun = default!;
@@ -23,7 +23,7 @@ public sealed class SharedRevolutionarySystem : EntitySystem
SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentGetStateAttemptEvent>(OnRevCompGetStateAttempt);
SubscribeLocalEvent<RevolutionaryComponent, ComponentStartup>(DirtyRevComps);
SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentStartup>(DirtyRevComps);
SubscribeLocalEvent<ShowRevIconsComponent, ComponentStartup>(DirtyRevComps);
SubscribeLocalEvent<ShowAntagIconsComponent, ComponentStartup>(DirtyRevComps);
}
/// <summary>
@@ -52,7 +52,7 @@ public sealed class SharedRevolutionarySystem : EntitySystem
/// </summary>
private void OnRevCompGetStateAttempt(EntityUid uid, HeadRevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args)
{
args.Cancelled = !CanGetState(args.Player, comp.IconVisibleToGhost);
args.Cancelled = !CanGetState(args.Player);
}
/// <summary>
@@ -60,30 +60,24 @@ public sealed class SharedRevolutionarySystem : EntitySystem
/// </summary>
private void OnRevCompGetStateAttempt(EntityUid uid, RevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args)
{
args.Cancelled = !CanGetState(args.Player, comp.IconVisibleToGhost);
args.Cancelled = !CanGetState(args.Player);
}
/// <summary>
/// The criteria that determine whether a Rev/HeadRev component should be sent to a client.
/// </summary>
/// <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>
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.
if (player is null)
if (player?.AttachedEntity is not {} uid)
return true;
var uid = player.AttachedEntity;
if (HasComp<RevolutionaryComponent>(uid) || HasComp<HeadRevolutionaryComponent>(uid))
return true;
if (visibleToGhosts && HasComp<GhostComponent>(uid))
return true;
return HasComp<ShowRevIconsComponent>(uid);
return HasComp<ShowAntagIconsComponent>(uid);
}
/// <summary>
/// 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>))]
public string? JobEntity = null;
[DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer<StatusIconPrototype>))]
public string Icon { get; private set; } = "JobIconUnknown";
[DataField]
public ProtoId<StatusIconPrototype> Icon { get; private set; } = "JobIconUnknown";
[DataField("special", serverOnly: true)]
public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>();

View File

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

View File

@@ -24,16 +24,4 @@ public sealed partial class StatusIconComponent : Component
/// </summary>
/// <param name="StatusIcons"></param>
[ByRefEvent]
public record struct GetStatusIconsEvent(List<StatusIconData> StatusIcons, bool InContainer);
/// <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;
}
public record struct GetStatusIconsEvent(List<StatusIconData> StatusIcons);

View File

@@ -1,3 +1,5 @@
using Content.Shared.Stealth.Components;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
@@ -15,26 +17,45 @@ public partial class StatusIconData : IComparable<StatusIconData>
/// <summary>
/// The icon that's displayed on the entity.
/// </summary>
[DataField("icon", required: true)]
[DataField(required: true)]
public SpriteSpecifier Icon = default!;
/// <summary>
/// A priority for the order in which the icons will be displayed.
/// </summary>
[DataField("priority")]
[DataField]
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>
/// A preference for where the icon will be displayed. None | Left | Right
/// </summary>
[DataField("locationPreference")]
[DataField]
public StatusIconLocationPreference LocationPreference = StatusIconLocationPreference.None;
public int CompareTo(StatusIconData? other)
{
return Priority.CompareTo(other?.Priority ?? int.MaxValue);
}
/// <summary>
/// The layer the icon is displayed on. Mod is drawn above Base. Base | Mod
/// </summary>
@@ -52,6 +73,11 @@ public partial class StatusIconData : IComparable<StatusIconData>
/// </summary>
[DataField]
public bool IsShaded = false;
public int CompareTo(StatusIconData? other)
{
return Priority.CompareTo(other?.Priority ?? int.MaxValue);
}
}
/// <summary>

View File

@@ -1,4 +1,3 @@
using Content.Shared.Antag;
using Content.Shared.StatusIcon;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -6,11 +5,8 @@ using Robust.Shared.Prototypes;
namespace Content.Shared.Zombies;
[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]
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;
[RegisterComponent, NetworkedComponent]
public sealed partial class ZombieComponent : Component, IAntagStatusIconComponent
public sealed partial class ZombieComponent : Component
{
/// <summary>
/// The baseline infection chance you have if you are completely nude
@@ -97,9 +97,6 @@ public sealed partial class ZombieComponent : Component, IAntagStatusIconCompone
[DataField("zombieStatusIcon")]
public ProtoId<StatusIconPrototype> StatusIcon { get; set; } = "ZombieFaction";
[DataField]
public bool IconVisibleToGhost { get; set; } = true;
/// <summary>
/// Healing each second
/// </summary>

View File

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

View File

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