From 6b8842c44d17b6a0d870880fa9d82a33ec7a266d Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sat, 21 Jun 2025 19:59:37 +0200 Subject: [PATCH] Generic Numeric Alerts (#38370) --- .../Alerts/GenericCounterAlertSystem.cs | 90 ++++++++++++++++++ .../Alerts/UpdateAlertSpriteEvent.cs | 5 +- Content.Client/Revenant/RevenantSystem.cs | 16 ++-- .../Systems/Alerts/AlertsUIController.cs | 3 +- .../Systems/Alerts/Controls/AlertControl.cs | 7 +- .../GenericCounterAlertComponent.cs | 63 ++++++++++++ Content.Shared/Revenant/SharedRevenant.cs | 8 -- Resources/Prototypes/Alerts/revenant.yml | 11 ++- .../Alerts/generic_counter.rsi/0.png | Bin 0 -> 131 bytes .../Alerts/generic_counter.rsi/1.png | Bin 0 -> 120 bytes .../Alerts/generic_counter.rsi/2.png | Bin 0 -> 142 bytes .../Alerts/generic_counter.rsi/3.png | Bin 0 -> 139 bytes .../Alerts/generic_counter.rsi/4.png | Bin 0 -> 135 bytes .../Alerts/generic_counter.rsi/5.png | Bin 0 -> 141 bytes .../Alerts/generic_counter.rsi/6.png | Bin 0 -> 142 bytes .../Alerts/generic_counter.rsi/7.png | Bin 0 -> 137 bytes .../Alerts/generic_counter.rsi/8.png | Bin 0 -> 134 bytes .../Alerts/generic_counter.rsi/9.png | Bin 0 -> 144 bytes .../Alerts/generic_counter.rsi/base.png | Bin 0 -> 265 bytes .../Alerts/generic_counter.rsi/meta.json | 44 +++++++++ 20 files changed, 225 insertions(+), 22 deletions(-) create mode 100644 Content.Client/Alerts/GenericCounterAlertSystem.cs create mode 100644 Content.Shared/Alert/Components/GenericCounterAlertComponent.cs create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/0.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/1.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/2.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/3.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/4.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/5.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/6.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/7.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/8.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/9.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/base.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/meta.json diff --git a/Content.Client/Alerts/GenericCounterAlertSystem.cs b/Content.Client/Alerts/GenericCounterAlertSystem.cs new file mode 100644 index 0000000000..de9d97d063 --- /dev/null +++ b/Content.Client/Alerts/GenericCounterAlertSystem.cs @@ -0,0 +1,90 @@ +using System.Numerics; +using Content.Shared.Alert.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; + +namespace Content.Client.Alerts; + +/// +/// This handles +/// +public sealed class GenericCounterAlertSystem : EntitySystem +{ + [Dependency] private readonly SpriteSystem _sprite = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnUpdateAlertSprite); + } + + private void OnUpdateAlertSprite(Entity ent, ref UpdateAlertSpriteEvent args) + { + var sprite = args.SpriteViewEnt.Comp; + + var ev = new GetGenericAlertCounterAmountEvent(args.Alert); + RaiseLocalEvent(args.ViewerEnt, ref ev); + + if (!ev.Handled) + return; + + // It cannot be null if its handled, but good to check to avoid ugly null ignores. + if (ev.Amount == null) + return; + + // How many digits can we display + var maxDigitCount = GetMaxDigitCount((ent, ent, sprite)); + + // Clamp it to a positive number that we can actually display in full (no rollover to 0) + var amount = (int) Math.Clamp(ev.Amount.Value, 0, Math.Pow(10, maxDigitCount) - 1); + + // This is super wack but ig it works? + var digitCount = ent.Comp.HideLeadingZeroes + ? amount.ToString().Length + : maxDigitCount; + + if (ent.Comp.HideLeadingZeroes) + { + for (var i = 0; i < ent.Comp.DigitKeys.Count; i++) + { + if (!_sprite.LayerMapTryGet(ent.Owner, ent.Comp.DigitKeys[i], out var layer, false)) + continue; + + _sprite.LayerSetVisible(ent.Owner, layer, i <= digitCount - 1); + } + } + + // ReSharper disable once PossibleLossOfFraction + var baseOffset = (ent.Comp.AlertSize.X - digitCount * ent.Comp.GlyphWidth) / 2 * (1f / EyeManager.PixelsPerMeter); + + for (var i = 0; i < ent.Comp.DigitKeys.Count; i++) + { + if (!_sprite.LayerMapTryGet(ent.Owner, ent.Comp.DigitKeys[i], out var layer, false)) + continue; + + var result = amount / (int) Math.Pow(10, i) % 10; + _sprite.LayerSetRsiState(ent.Owner, layer, result.ToString()); + + if (ent.Comp.CenterGlyph) + { + var offset = baseOffset + (digitCount - 1 - i) * ent.Comp.GlyphWidth * (1f / EyeManager.PixelsPerMeter); + _sprite.LayerSetOffset(ent.Owner, layer, new Vector2(offset, 0)); + } + } + } + + /// + /// Gets the number of digits that we can display. + /// + /// The number of digits. + private int GetMaxDigitCount(Entity ent) + { + for (var i = ent.Comp1.DigitKeys.Count - 1; i >= 0; i--) + { + if (_sprite.LayerExists((ent.Owner, ent.Comp2), ent.Comp1.DigitKeys[i])) + return i + 1; + } + + return 0; + } +} diff --git a/Content.Client/Alerts/UpdateAlertSpriteEvent.cs b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs index 4f182c458c..d8222c2340 100644 --- a/Content.Client/Alerts/UpdateAlertSpriteEvent.cs +++ b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs @@ -11,11 +11,14 @@ public record struct UpdateAlertSpriteEvent { public Entity SpriteViewEnt; + public EntityUid ViewerEnt; + public AlertPrototype Alert; - public UpdateAlertSpriteEvent(Entity spriteViewEnt, AlertPrototype alert) + public UpdateAlertSpriteEvent(Entity spriteViewEnt, EntityUid viewerEnt, AlertPrototype alert) { SpriteViewEnt = spriteViewEnt; + ViewerEnt = viewerEnt; Alert = alert; } } diff --git a/Content.Client/Revenant/RevenantSystem.cs b/Content.Client/Revenant/RevenantSystem.cs index 0534522b40..21d2d7888d 100644 --- a/Content.Client/Revenant/RevenantSystem.cs +++ b/Content.Client/Revenant/RevenantSystem.cs @@ -1,4 +1,6 @@ using Content.Client.Alerts; +using Content.Shared.Alert; +using Content.Shared.Alert.Components; using Content.Shared.Revenant; using Content.Shared.Revenant.Components; using Robust.Client.GameObjects; @@ -15,7 +17,7 @@ public sealed class RevenantSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnAppearanceChange); - SubscribeLocalEvent(OnUpdateAlert); + SubscribeLocalEvent(OnGetCounterAmount); } private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args) @@ -40,14 +42,14 @@ public sealed class RevenantSystem : EntitySystem } } - private void OnUpdateAlert(Entity ent, ref UpdateAlertSpriteEvent args) + private void OnGetCounterAmount(Entity ent, ref GetGenericAlertCounterAmountEvent args) { - if (args.Alert.ID != ent.Comp.EssenceAlert) + if (args.Handled) return; - var essence = Math.Clamp(ent.Comp.Essence.Int(), 0, 999); - _sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit1, $"{(essence / 100) % 10}"); - _sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit2, $"{(essence / 10) % 10}"); - _sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit3, $"{essence % 10}"); + if (ent.Comp.EssenceAlert != args.Alert) + return; + + args.Amount = ent.Comp.Essence.Int(); } } diff --git a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs index 5c19512038..3fe553be3b 100644 --- a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs +++ b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs @@ -98,7 +98,8 @@ public sealed class AlertsUIController : UIController, IOnStateEntered(spriteViewEnt, out var sprite)) return; - var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), alert); + var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), player, alert); EntityManager.EventBus.RaiseLocalEvent(player, ref ev); + EntityManager.EventBus.RaiseLocalEvent(spriteViewEnt, ref ev); } } diff --git a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs index 847b253586..fe22ebba40 100644 --- a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs +++ b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs @@ -57,10 +57,15 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls _sprite = _entityManager.System(); TooltipSupplier = SupplyTooltip; Alert = alert; + + HorizontalAlignment = HAlignment.Left; _severity = severity; _icon = new SpriteView { - Scale = new Vector2(2, 2) + Scale = new Vector2(2, 2), + MaxSize = new Vector2(64, 64), + Stretch = SpriteView.StretchMode.None, + HorizontalAlignment = HAlignment.Left }; SetupIcon(); diff --git a/Content.Shared/Alert/Components/GenericCounterAlertComponent.cs b/Content.Shared/Alert/Components/GenericCounterAlertComponent.cs new file mode 100644 index 0000000000..d0c7fc1ad1 --- /dev/null +++ b/Content.Shared/Alert/Components/GenericCounterAlertComponent.cs @@ -0,0 +1,63 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Alert.Components; + +/// +/// This is used for an alert which simply displays a generic number over a texture. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class GenericCounterAlertComponent : Component +{ + /// + /// The width, in pixels, of an individual glyph, accounting for the space between glyphs. + /// A 3 pixel wide glyph with one pixel of space between it and the next would be a width of 4. + /// + [DataField] + public int GlyphWidth = 6; + + /// + /// Whether the numbers should be centered on the glyph or just follow a static position. + /// + [DataField] + public bool CenterGlyph = true; + + /// + /// Whether leading zeros should be hidden. + /// If true, "005" would display as "5". + /// + [DataField] + public bool HideLeadingZeroes = true; + + /// + /// The size of the alert sprite. + /// Used to calculate offsets. + /// + [DataField] + public Vector2i AlertSize = new(32, 32); + + /// + /// Digits that can be displayed by the alert, represented by their sprite layer. + /// Order defined corresponds to the digit it affects. 1st defined will affect 1st digit, 2nd affect 2nd digit and so on. + /// In this case ones would be on layer "1", tens on layer "10" etc. + /// + [DataField] + public List DigitKeys = new() + { + "1", + "10", + "100", + "1000", + "10000" + }; +} + +/// +/// Event raised to gather the amount the alert will display. +/// +/// The alert which is currently requesting an update. +/// The number to display on the alert. +[ByRefEvent] +public record struct GetGenericAlertCounterAmountEvent(AlertPrototype Alert, int? Amount = null) +{ + public bool Handled => Amount.HasValue; +} diff --git a/Content.Shared/Revenant/SharedRevenant.cs b/Content.Shared/Revenant/SharedRevenant.cs index 485ad26dd2..c44e4408aa 100644 --- a/Content.Shared/Revenant/SharedRevenant.cs +++ b/Content.Shared/Revenant/SharedRevenant.cs @@ -70,11 +70,3 @@ public enum RevenantVisuals : byte Stunned, Harvesting, } - -[NetSerializable, Serializable] -public enum RevenantVisualLayers : byte -{ - Digit1, - Digit2, - Digit3 -} diff --git a/Resources/Prototypes/Alerts/revenant.yml b/Resources/Prototypes/Alerts/revenant.yml index 38933df4fe..ab2b13905d 100644 --- a/Resources/Prototypes/Alerts/revenant.yml +++ b/Resources/Prototypes/Alerts/revenant.yml @@ -18,12 +18,15 @@ id: AlertEssenceSpriteView categories: [ HideSpawnMenu ] components: + - type: GenericCounterAlert + centerGlyph: false + hideLeadingZeroes: false - type: Sprite sprite: /Textures/Interface/Alerts/essence_counter.rsi layers: - map: [ "enum.AlertVisualLayers.Base" ] - - map: [ "enum.RevenantVisualLayers.Digit1" ] - - map: [ "enum.RevenantVisualLayers.Digit2" ] - offset: 0.125, 0 - - map: [ "enum.RevenantVisualLayers.Digit3" ] + - map: [ "1" ] offset: 0.25, 0 + - map: [ "10" ] + offset: 0.125, 0 + - map: [ "100" ] diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/0.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/0.png new file mode 100644 index 0000000000000000000000000000000000000000..8eb85f36dc347a26e25526f1d7ea1cfc8a18cc29 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ww^AIArY-_ zuNd+HRk0kj{{R2^W@E8!O`X=VCONGv3_s?oYIhXa!t|JE}w bT*zfQ*(Ja}A?JM*&>#j+S3j3^P67$V-*sza4Q>Q+6z#N3l|BlP&M p`!^k4e=KeLEh6`ym7&2;IOLW5dxsYHEkH9FJYD@<);T3K0RT!wGxh)g literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/3.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/3.png new file mode 100644 index 0000000000000000000000000000000000000000..181f425acc5c5e9308e58c580b381f1ffd3488fe GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}uAVNAArY-_ zuPE{{7;rdWJoNYdotddKBh(x}%x89H<`iN`*fl}rwq0+BhHn9X{ne+dm{Xo^(9geX l`E0Jj?(6sdvokQ<6|)voe(NcCt`%q?gQu&X%Q~loCIHerE;#@I literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/4.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/4.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5680eb412dca6dadb2a6f4c29781fed24c950f GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}j-D=#ArY-_ zFCF9ss&c+~?Qi+9$((BQULO1}ropPhz%Xmos+VV`^6Y2puzI^#z2oItVd)RrSGFx@ hpZMi9KLf)hGj}HWcNaGQxdAkc!PC{xWt~$(699TLFDC#1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/5.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/5.png new file mode 100644 index 0000000000000000000000000000000000000000..2361ca7f1d84086b6acd1d66b428d6f89eed079a GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}?w&4=ArY-_ zuNd+jFyLT0xcATh^GnL+Na<8AU-GcTsX^ss8RLpoQEi@4!8OcxHp`qS{OWIhN$$bR om~YYvyFR_;-y*+ICG!^d`Q6GN7A;ymA7~_lr>mdKI;Vst03L)ia{vGU literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/6.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/6.png new file mode 100644 index 0000000000000000000000000000000000000000..a636e7890b890384fdc70f9087af998a8c69472f GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}9-c0aArY-_ zuPE{{7;vy0Z2td$=B~C0S$-wQ0}pcraWZ_^Jz>eJRlfxUSF_gs%#P!~G0pafkg<5a p`;rukJ(3Sn_&@w-W?< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/7.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/7.png new file mode 100644 index 0000000000000000000000000000000000000000..d29d6192ded80be417a454b2f24732264b8fdfc6 GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}&YmugArY-_ zFKiTKP~0WH#vh!I fSU=e_FnG*9nj`(k;6?gTe~DWM4fT8=J` literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/9.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/9.png new file mode 100644 index 0000000000000000000000000000000000000000..6325fbdabc40f4f58e287a92f4aa144c8520c401 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}UY;(FArY-_ zuNd+HRXJZg^!I(wWH#aOgGc_G#YD+WU|`s9?Ag^3!Jig(OQC)Kv)g-&-uz{{dVfFb q`48KKcKW2wE6=}E&(6Sbm(43i{q+aCpWlF{GI+ZBxvXxW4ZO5X@rY;Gi_uUMECfA}0_=^ADGJpN+-j%4;`yZZmYSWOKc4H62 z;qMJ$3pZ!1Jz#l8bLpo`zp^%Kul#DqVIwOjz=(tzn;lNe@0SdFxmQr)`1FU(zprvx zy*|j}C+Q>Ltk#t_>sa)0