Generic Numeric Alerts (#38370)
90
Content.Client/Alerts/GenericCounterAlertSystem.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,14 @@ public record struct UpdateAlertSpriteEvent
|
||||
{
|
||||
public Entity<SpriteComponent> SpriteViewEnt;
|
||||
|
||||
public EntityUid ViewerEnt;
|
||||
|
||||
public AlertPrototype Alert;
|
||||
|
||||
public UpdateAlertSpriteEvent(Entity<SpriteComponent> spriteViewEnt, AlertPrototype alert)
|
||||
public UpdateAlertSpriteEvent(Entity<SpriteComponent> spriteViewEnt, EntityUid viewerEnt, AlertPrototype alert)
|
||||
{
|
||||
SpriteViewEnt = spriteViewEnt;
|
||||
ViewerEnt = viewerEnt;
|
||||
Alert = alert;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RevenantComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<RevenantComponent, UpdateAlertSpriteEvent>(OnUpdateAlert);
|
||||
SubscribeLocalEvent<RevenantComponent, GetGenericAlertCounterAmountEvent>(OnGetCounterAmount);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,8 @@ public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayS
|
||||
if (!EntityManager.TryGetComponent<SpriteComponent>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,15 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls
|
||||
_sprite = _entityManager.System<SpriteSystem>();
|
||||
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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -70,11 +70,3 @@ public enum RevenantVisuals : byte
|
||||
Stunned,
|
||||
Harvesting,
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public enum RevenantVisualLayers : byte
|
||||
{
|
||||
Digit1,
|
||||
Digit2,
|
||||
Digit3
|
||||
}
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/0.png
Normal file
|
After Width: | Height: | Size: 131 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/1.png
Normal file
|
After Width: | Height: | Size: 120 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/2.png
Normal file
|
After Width: | Height: | Size: 142 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/3.png
Normal file
|
After Width: | Height: | Size: 139 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/4.png
Normal file
|
After Width: | Height: | Size: 135 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/5.png
Normal file
|
After Width: | Height: | Size: 141 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/6.png
Normal file
|
After Width: | Height: | Size: 142 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/7.png
Normal file
|
After Width: | Height: | Size: 137 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/8.png
Normal file
|
After Width: | Height: | Size: 134 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/9.png
Normal file
|
After Width: | Height: | Size: 144 B |
BIN
Resources/Textures/Interface/Alerts/generic_counter.rsi/base.png
Normal file
|
After Width: | Height: | Size: 265 B |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||