Generic Numeric Alerts (#38370)

This commit is contained in:
ScarKy0
2025-06-21 19:59:37 +02:00
committed by GitHub
parent b8e8918af7
commit 6b8842c44d
20 changed files with 225 additions and 22 deletions

View File

@@ -0,0 +1,90 @@
using System.Numerics;
using Content.Shared.Alert.Components;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
namespace Content.Client.Alerts;
/// <summary>
/// This handles <see cref="GenericCounterAlertComponent"/>
/// </summary>
public sealed class GenericCounterAlertSystem : EntitySystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<GenericCounterAlertComponent, UpdateAlertSpriteEvent>(OnUpdateAlertSprite);
}
private void OnUpdateAlertSprite(Entity<GenericCounterAlertComponent> 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));
}
}
}
/// <summary>
/// Gets the number of digits that we can display.
/// </summary>
/// <returns>The number of digits.</returns>
private int GetMaxDigitCount(Entity<GenericCounterAlertComponent, SpriteComponent> 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;
}
}

View File

@@ -11,11 +11,14 @@ public record struct UpdateAlertSpriteEvent
{ {
public Entity<SpriteComponent> SpriteViewEnt; public Entity<SpriteComponent> SpriteViewEnt;
public EntityUid ViewerEnt;
public AlertPrototype Alert; public AlertPrototype Alert;
public UpdateAlertSpriteEvent(Entity<SpriteComponent> spriteViewEnt, AlertPrototype alert) public UpdateAlertSpriteEvent(Entity<SpriteComponent> spriteViewEnt, EntityUid viewerEnt, AlertPrototype alert)
{ {
SpriteViewEnt = spriteViewEnt; SpriteViewEnt = spriteViewEnt;
ViewerEnt = viewerEnt;
Alert = alert; Alert = alert;
} }
} }

View File

@@ -1,4 +1,6 @@
using Content.Client.Alerts; using Content.Client.Alerts;
using Content.Shared.Alert;
using Content.Shared.Alert.Components;
using Content.Shared.Revenant; using Content.Shared.Revenant;
using Content.Shared.Revenant.Components; using Content.Shared.Revenant.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -15,7 +17,7 @@ public sealed class RevenantSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<RevenantComponent, AppearanceChangeEvent>(OnAppearanceChange); SubscribeLocalEvent<RevenantComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<RevenantComponent, UpdateAlertSpriteEvent>(OnUpdateAlert); SubscribeLocalEvent<RevenantComponent, GetGenericAlertCounterAmountEvent>(OnGetCounterAmount);
} }
private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args) private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args)
@@ -40,14 +42,14 @@ public sealed class RevenantSystem : EntitySystem
} }
} }
private void OnUpdateAlert(Entity<RevenantComponent> ent, ref UpdateAlertSpriteEvent args) private void OnGetCounterAmount(Entity<RevenantComponent> ent, ref GetGenericAlertCounterAmountEvent args)
{ {
if (args.Alert.ID != ent.Comp.EssenceAlert) if (args.Handled)
return; return;
var essence = Math.Clamp(ent.Comp.Essence.Int(), 0, 999); if (ent.Comp.EssenceAlert != args.Alert)
_sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit1, $"{(essence / 100) % 10}"); return;
_sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit2, $"{(essence / 10) % 10}");
_sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit3, $"{essence % 10}"); args.Amount = ent.Comp.Essence.Int();
} }
} }

View File

@@ -98,7 +98,8 @@ public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayS
if (!EntityManager.TryGetComponent<SpriteComponent>(spriteViewEnt, out var sprite)) if (!EntityManager.TryGetComponent<SpriteComponent>(spriteViewEnt, out var sprite))
return; 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(player, ref ev);
EntityManager.EventBus.RaiseLocalEvent(spriteViewEnt, ref ev);
} }
} }

View File

@@ -57,10 +57,15 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls
_sprite = _entityManager.System<SpriteSystem>(); _sprite = _entityManager.System<SpriteSystem>();
TooltipSupplier = SupplyTooltip; TooltipSupplier = SupplyTooltip;
Alert = alert; Alert = alert;
HorizontalAlignment = HAlignment.Left;
_severity = severity; _severity = severity;
_icon = new SpriteView _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(); SetupIcon();

View File

@@ -0,0 +1,63 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Alert.Components;
/// <summary>
/// This is used for an alert which simply displays a generic number over a texture.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class GenericCounterAlertComponent : Component
{
/// <summary>
/// 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.
/// </summary>
[DataField]
public int GlyphWidth = 6;
/// <summary>
/// Whether the numbers should be centered on the glyph or just follow a static position.
/// </summary>
[DataField]
public bool CenterGlyph = true;
/// <summary>
/// Whether leading zeros should be hidden.
/// If true, "005" would display as "5".
/// </summary>
[DataField]
public bool HideLeadingZeroes = true;
/// <summary>
/// The size of the alert sprite.
/// Used to calculate offsets.
/// </summary>
[DataField]
public Vector2i AlertSize = new(32, 32);
/// <summary>
/// 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.
/// </summary>
[DataField]
public List<string> DigitKeys = new()
{
"1",
"10",
"100",
"1000",
"10000"
};
}
/// <summary>
/// Event raised to gather the amount the alert will display.
/// </summary>
/// <param name="Alert">The alert which is currently requesting an update.</param>
/// <param name="Amount">The number to display on the alert.</param>
[ByRefEvent]
public record struct GetGenericAlertCounterAmountEvent(AlertPrototype Alert, int? Amount = null)
{
public bool Handled => Amount.HasValue;
}

View File

@@ -70,11 +70,3 @@ public enum RevenantVisuals : byte
Stunned, Stunned,
Harvesting, Harvesting,
} }
[NetSerializable, Serializable]
public enum RevenantVisualLayers : byte
{
Digit1,
Digit2,
Digit3
}

View File

@@ -18,12 +18,15 @@
id: AlertEssenceSpriteView id: AlertEssenceSpriteView
categories: [ HideSpawnMenu ] categories: [ HideSpawnMenu ]
components: components:
- type: GenericCounterAlert
centerGlyph: false
hideLeadingZeroes: false
- type: Sprite - type: Sprite
sprite: /Textures/Interface/Alerts/essence_counter.rsi sprite: /Textures/Interface/Alerts/essence_counter.rsi
layers: layers:
- map: [ "enum.AlertVisualLayers.Base" ] - map: [ "enum.AlertVisualLayers.Base" ]
- map: [ "enum.RevenantVisualLayers.Digit1" ] - map: [ "1" ]
- map: [ "enum.RevenantVisualLayers.Digit2" ]
offset: 0.125, 0
- map: [ "enum.RevenantVisualLayers.Digit3" ]
offset: 0.25, 0 offset: 0.25, 0
- map: [ "10" ]
offset: 0.125, 0
- map: [ "100" ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View File

@@ -0,0 +1,44 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Created by EmoGarbage404",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "base"
},
{
"name": "0"
},
{
"name": "1"
},
{
"name": "2"
},
{
"name": "3"
},
{
"name": "4"
},
{
"name": "5"
},
{
"name": "6"
},
{
"name": "7"
},
{
"name": "8"
},
{
"name": "9"
}
]
}