security HUD now shows a job icon on entities with a body (#18054)
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
<LineEdit Name="NameLineEdit" />
|
<LineEdit Name="NameLineEdit" />
|
||||||
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
|
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
|
||||||
<LineEdit Name="JobLineEdit" />
|
<LineEdit Name="JobLineEdit" />
|
||||||
<BoxContainer Orientation="Horizontal" Visible="False">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
||||||
<Control HorizontalExpand="True" MinSize="50 0"/>
|
<Control HorizontalExpand="True" MinSize="50 0"/>
|
||||||
<GridContainer Name="IconGrid" Columns="10">
|
<GridContainer Name="IconGrid" Columns="10">
|
||||||
|
|||||||
117
Content.Client/Overlays/EquipmentHudSystem.cs
Normal file
117
Content.Client/Overlays/EquipmentHudSystem.cs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Inventory.Events;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
|
||||||
|
namespace Content.Client.Overlays;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is a base system to make it easier to enable or disabling UI elements based on whether or not the player has
|
||||||
|
/// some component, either on their controlled entity on some worn piece of equipment.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
|
|
||||||
|
protected bool IsActive;
|
||||||
|
protected virtual SlotFlags TargetSlots => ~SlotFlags.POCKET;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<T, ComponentStartup>(OnStartup);
|
||||||
|
SubscribeLocalEvent<T, ComponentRemove>(OnRemove);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
|
||||||
|
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<T, GotEquippedEvent>(OnCompEquip);
|
||||||
|
SubscribeLocalEvent<T, GotUnequippedEvent>(OnCompUnequip);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<T, RefreshEquipmentHudEvent<T>>(OnRefreshComponentHud);
|
||||||
|
SubscribeLocalEvent<T, InventoryRelayedEvent<RefreshEquipmentHudEvent<T>>>(OnRefreshEquipmentHud);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update(RefreshEquipmentHudEvent<T> ev)
|
||||||
|
{
|
||||||
|
IsActive = true;
|
||||||
|
UpdateInternal(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deactivate()
|
||||||
|
{
|
||||||
|
if (!IsActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsActive = false;
|
||||||
|
DeactivateInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateInternal(RefreshEquipmentHudEvent<T> args) { }
|
||||||
|
|
||||||
|
protected virtual void DeactivateInternal() { }
|
||||||
|
|
||||||
|
private void OnStartup(EntityUid uid, T component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
RefreshOverlay(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemove(EntityUid uid, T component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
RefreshOverlay(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayerAttached(PlayerAttachedEvent args)
|
||||||
|
{
|
||||||
|
RefreshOverlay(args.Entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayerDetached(PlayerDetachedEvent args)
|
||||||
|
{
|
||||||
|
if (_player.LocalPlayer?.ControlledEntity == null)
|
||||||
|
Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCompEquip(EntityUid uid, T component, GotEquippedEvent args)
|
||||||
|
{
|
||||||
|
RefreshOverlay(args.Equipee);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCompUnequip(EntityUid uid, T component, GotUnequippedEvent args)
|
||||||
|
{
|
||||||
|
RefreshOverlay(args.Equipee);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundRestart(RoundRestartCleanupEvent args)
|
||||||
|
{
|
||||||
|
Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnRefreshEquipmentHud(EntityUid uid, T component, InventoryRelayedEvent<RefreshEquipmentHudEvent<T>> args)
|
||||||
|
{
|
||||||
|
args.Args.Active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnRefreshComponentHud(EntityUid uid, T component, RefreshEquipmentHudEvent<T> args)
|
||||||
|
{
|
||||||
|
args.Active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshOverlay(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (uid != _player.LocalPlayer?.ControlledEntity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ev = new RefreshEquipmentHudEvent<T>(TargetSlots);
|
||||||
|
RaiseLocalEvent(uid, ev);
|
||||||
|
|
||||||
|
if (ev.Active)
|
||||||
|
Update(ev);
|
||||||
|
else
|
||||||
|
Deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
73
Content.Client/Overlays/ShowSecurityIconsSystem.cs
Normal file
73
Content.Client/Overlays/ShowSecurityIconsSystem.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using Content.Shared.Access.Components;
|
||||||
|
using Content.Shared.Access.Systems;
|
||||||
|
using Content.Shared.Overlays;
|
||||||
|
using Content.Shared.PDA;
|
||||||
|
using Content.Shared.StatusIcon;
|
||||||
|
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!;
|
||||||
|
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||||
|
|
||||||
|
[ValidatePrototypeId<StatusIconPrototype>]
|
||||||
|
private const string JobIconForNoId = "JobIconNoId";
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StatusIconComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent @event)
|
||||||
|
{
|
||||||
|
if (!IsActive || @event.InContainer)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var healthIcons = DecideSecurityIcon(uid);
|
||||||
|
|
||||||
|
@event.StatusIcons.AddRange(healthIcons);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<StatusIconPrototype> DecideSecurityIcon(EntityUid uid)
|
||||||
|
{
|
||||||
|
var result = new List<StatusIconPrototype>();
|
||||||
|
|
||||||
|
var jobIconToGet = JobIconForNoId;
|
||||||
|
if (_accessReader.FindAccessItemsInventory(uid, out var items))
|
||||||
|
{
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
// ID Card
|
||||||
|
if (TryComp(item, out IdCardComponent? id))
|
||||||
|
{
|
||||||
|
jobIconToGet = id.JobIcon;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDA
|
||||||
|
if (TryComp(item, out PdaComponent? pda)
|
||||||
|
&& pda.ContainedId != null
|
||||||
|
&& TryComp(pda.ContainedId, out id))
|
||||||
|
{
|
||||||
|
jobIconToGet = id.JobIcon;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_prototypeMan.TryIndex<StatusIconPrototype>(jobIconToGet, out var jobIcon))
|
||||||
|
result.Add(jobIcon);
|
||||||
|
else
|
||||||
|
Log.Error($"Invalid job icon prototype: {jobIcon}");
|
||||||
|
|
||||||
|
// Add arrest icons here, WYCI.
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
using System.Numerics;
|
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 System.Numerics;
|
||||||
|
|
||||||
namespace Content.Client.StatusIcon;
|
namespace Content.Client.StatusIcon;
|
||||||
|
|
||||||
@@ -41,10 +42,6 @@ public sealed class StatusIconOverlay : Overlay
|
|||||||
if (xform.MapID != args.MapId)
|
if (xform.MapID != args.MapId)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var icons = _statusIcon.GetStatusIcons(uid, meta);
|
|
||||||
if (icons.Count == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var bounds = comp.Bounds ?? sprite.Bounds;
|
var bounds = comp.Bounds ?? sprite.Bounds;
|
||||||
|
|
||||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
||||||
@@ -52,12 +49,17 @@ public sealed class StatusIconOverlay : Overlay
|
|||||||
if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
|
if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
var icons = _statusIcon.GetStatusIcons(uid, meta);
|
||||||
|
if (icons.Count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
var worldMatrix = Matrix3.CreateTranslation(worldPos);
|
var worldMatrix = Matrix3.CreateTranslation(worldPos);
|
||||||
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
|
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
|
||||||
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
|
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
|
||||||
handle.SetTransform(matty);
|
handle.SetTransform(matty);
|
||||||
|
|
||||||
var count = 0;
|
var countL = 0;
|
||||||
|
var countR = 0;
|
||||||
var accOffsetL = 0;
|
var accOffsetL = 0;
|
||||||
var accOffsetR = 0;
|
var accOffsetR = 0;
|
||||||
icons.Sort();
|
icons.Sort();
|
||||||
@@ -71,13 +73,16 @@ public sealed class StatusIconOverlay : Overlay
|
|||||||
|
|
||||||
// the icons are ordered left to right, top to bottom.
|
// the icons are ordered left to right, top to bottom.
|
||||||
// extra icons that don't fit are just cut off.
|
// extra icons that don't fit are just cut off.
|
||||||
if (count % 2 == 0)
|
if (proto.LocationPreference == StatusIconLocationPreference.Left ||
|
||||||
|
proto.LocationPreference == StatusIconLocationPreference.None && countL <= countR)
|
||||||
{
|
{
|
||||||
if (accOffsetL + texture.Height > sprite.Bounds.Height * EyeManager.PixelsPerMeter)
|
if (accOffsetL + texture.Height > sprite.Bounds.Height * EyeManager.PixelsPerMeter)
|
||||||
break;
|
break;
|
||||||
accOffsetL += texture.Height;
|
accOffsetL += texture.Height;
|
||||||
yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetL / EyeManager.PixelsPerMeter;
|
yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetL / EyeManager.PixelsPerMeter;
|
||||||
xOffset = -(bounds.Width + sprite.Offset.X) / 2f;
|
xOffset = -(bounds.Width + sprite.Offset.X) / 2f;
|
||||||
|
|
||||||
|
countL++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -86,8 +91,9 @@ public sealed class StatusIconOverlay : Overlay
|
|||||||
accOffsetR += texture.Height;
|
accOffsetR += texture.Height;
|
||||||
yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetR / EyeManager.PixelsPerMeter;
|
yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) accOffsetR / EyeManager.PixelsPerMeter;
|
||||||
xOffset = (bounds.Width + sprite.Offset.X) / 2f - (float) texture.Width / EyeManager.PixelsPerMeter;
|
xOffset = (bounds.Width + sprite.Offset.X) / 2f - (float) texture.Width / EyeManager.PixelsPerMeter;
|
||||||
|
|
||||||
|
countR++;
|
||||||
}
|
}
|
||||||
count++;
|
|
||||||
|
|
||||||
var position = new Vector2(xOffset, yOffset);
|
var position = new Vector2(xOffset, yOffset);
|
||||||
handle.DrawTexture(texture, position);
|
handle.DrawTexture(texture, position);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.StatusIcon;
|
using Content.Shared.StatusIcon;
|
||||||
using Content.Shared.StatusIcon.Components;
|
using Content.Shared.StatusIcon.Components;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
|
|||||||
13
Content.Shared/Inventory/Events/RefreshEquipmentHudEvent.cs
Normal file
13
Content.Shared/Inventory/Events/RefreshEquipmentHudEvent.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Content.Shared.Inventory.Events;
|
||||||
|
|
||||||
|
public sealed class RefreshEquipmentHudEvent<T> : EntityEventArgs, IInventoryRelayEvent where T : IComponent
|
||||||
|
{
|
||||||
|
public SlotFlags TargetSlots { get; init; }
|
||||||
|
public bool Active = false;
|
||||||
|
public object? ExtraData;
|
||||||
|
|
||||||
|
public RefreshEquipmentHudEvent(SlotFlags targetSlots)
|
||||||
|
{
|
||||||
|
TargetSlots = targetSlots;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ using Content.Shared.Electrocution;
|
|||||||
using Content.Shared.Explosion;
|
using Content.Shared.Explosion;
|
||||||
using Content.Shared.Eye.Blinding.Systems;
|
using Content.Shared.Eye.Blinding.Systems;
|
||||||
using Content.Shared.IdentityManagement.Components;
|
using Content.Shared.IdentityManagement.Components;
|
||||||
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
|
using Content.Shared.Overlays;
|
||||||
using Content.Shared.Radio;
|
using Content.Shared.Radio;
|
||||||
using Content.Shared.Slippery;
|
using Content.Shared.Slippery;
|
||||||
using Content.Shared.Strip.Components;
|
using Content.Shared.Strip.Components;
|
||||||
@@ -34,6 +36,9 @@ public partial class InventorySystem
|
|||||||
SubscribeLocalEvent<InventoryComponent, GetBlurEvent>(RelayInventoryEvent);
|
SubscribeLocalEvent<InventoryComponent, GetBlurEvent>(RelayInventoryEvent);
|
||||||
SubscribeLocalEvent<InventoryComponent, SolutionScanEvent>(RelayInventoryEvent);
|
SubscribeLocalEvent<InventoryComponent, SolutionScanEvent>(RelayInventoryEvent);
|
||||||
|
|
||||||
|
// ComponentActivatedClientSystems
|
||||||
|
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSecurityIconsComponent>>(RelayInventoryEvent);
|
||||||
|
|
||||||
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetStrippingVerbs);
|
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetStrippingVerbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +52,7 @@ public partial class InventorySystem
|
|||||||
while (containerEnumerator.MoveNext(out var container))
|
while (containerEnumerator.MoveNext(out var container))
|
||||||
{
|
{
|
||||||
if (!container.ContainedEntity.HasValue) continue;
|
if (!container.ContainedEntity.HasValue) continue;
|
||||||
RaiseLocalEvent(container.ContainedEntity.Value, ev, false);
|
RaiseLocalEvent(container.ContainedEntity.Value, ev, broadcast: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
Content.Shared/Overlays/ShowSecurityIconsComponent.cs
Normal file
10
Content.Shared/Overlays/ShowSecurityIconsComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Overlays
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This component allows you to see job icons above mobs.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed class ShowSecurityIconsComponent : Component { }
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -24,6 +24,12 @@ public class StatusIconData : IComparable<StatusIconData>
|
|||||||
[DataField("priority")]
|
[DataField("priority")]
|
||||||
public int Priority = 10;
|
public int Priority = 10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A preference for where the icon will be displayed. None | Left | Right
|
||||||
|
/// </summary>
|
||||||
|
[DataField("locationPreference")]
|
||||||
|
public StatusIconLocationPreference LocationPreference = StatusIconLocationPreference.None;
|
||||||
|
|
||||||
public int CompareTo(StatusIconData? other)
|
public int CompareTo(StatusIconData? other)
|
||||||
{
|
{
|
||||||
return Priority.CompareTo(other?.Priority ?? int.MaxValue);
|
return Priority.CompareTo(other?.Priority ?? int.MaxValue);
|
||||||
@@ -49,3 +55,11 @@ public sealed class StatusIconPrototype : StatusIconData, IPrototype, IInheritin
|
|||||||
[IdDataField]
|
[IdDataField]
|
||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum StatusIconLocationPreference : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
sprite: Clothing/Eyes/Hud/sec.rsi
|
sprite: Clothing/Eyes/Hud/sec.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Eyes/Hud/sec.rsi
|
sprite: Clothing/Eyes/Hud/sec.rsi
|
||||||
|
- type: ShowSecurityIcons
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingEyesBase
|
parent: ClothingEyesBase
|
||||||
@@ -96,6 +97,7 @@
|
|||||||
sprite: Clothing/Eyes/Hud/medsec.rsi
|
sprite: Clothing/Eyes/Hud/medsec.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Eyes/Hud/medsec.rsi
|
sprite: Clothing/Eyes/Hud/medsec.rsi
|
||||||
|
- type: ShowSecurityIcons
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingEyesBase
|
parent: ClothingEyesBase
|
||||||
@@ -107,6 +109,7 @@
|
|||||||
sprite: Clothing/Eyes/Hud/medsecengi.rsi
|
sprite: Clothing/Eyes/Hud/medsecengi.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Eyes/Hud/medsecengi.rsi
|
sprite: Clothing/Eyes/Hud/medsecengi.rsi
|
||||||
|
- type: ShowSecurityIcons
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingEyesBase
|
parent: ClothingEyesBase
|
||||||
@@ -118,3 +121,4 @@
|
|||||||
sprite: Clothing/Eyes/Hud/omni.rsi
|
sprite: Clothing/Eyes/Hud/omni.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Eyes/Hud/omni.rsi
|
sprite: Clothing/Eyes/Hud/omni.rsi
|
||||||
|
- type: ShowSecurityIcons
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
id: JobIcon
|
id: JobIcon
|
||||||
abstract: true
|
abstract: true
|
||||||
priority: 1
|
priority: 1
|
||||||
|
locationPreference: Left
|
||||||
|
|
||||||
- type: statusIcon
|
- type: statusIcon
|
||||||
parent: JobIcon
|
parent: JobIcon
|
||||||
@@ -337,7 +338,7 @@
|
|||||||
id: JobIconSeniorPhysician
|
id: JobIconSeniorPhysician
|
||||||
icon:
|
icon:
|
||||||
sprite: Interface/Misc/job_icons.rsi
|
sprite: Interface/Misc/job_icons.rsi
|
||||||
state: SeniorPhysician
|
state: SeniorPhysician
|
||||||
|
|
||||||
- type: statusIcon
|
- type: statusIcon
|
||||||
parent: JobIcon
|
parent: JobIcon
|
||||||
|
|||||||
Reference in New Issue
Block a user