--- /dev/null
+using System.Collections.Generic;
+using Content.Client.Weapons.Ranged.Components;
+using Content.Shared.Prototypes;
+using Robust.Client.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Prototypes;
+
+namespace Content.IntegrationTests.Tests;
+
+/// <summary>
+/// Tests all entity prototypes with the MagazineVisualsComponent.
+/// </summary>
+[TestFixture]
+public sealed class MagazineVisualsSpriteTest
+{
+ [Test]
+ public async Task MagazineVisualsSpritesExist()
+ {
+ await using var pair = await PoolManager.GetServerClient();
+ var client = pair.Client;
+ var protoMan = client.ResolveDependency<IPrototypeManager>();
+ var componentFactory = client.ResolveDependency<IComponentFactory>();
+
+ await client.WaitAssertion(() =>
+ {
+ foreach (var proto in protoMan.EnumeratePrototypes<EntityPrototype>())
+ {
+ if (proto.Abstract || pair.IsTestPrototype(proto))
+ continue;
+
+ if (!proto.TryGetComponent<MagazineVisualsComponent>(out var visuals, componentFactory))
+ continue;
+
+ Assert.That(proto.TryGetComponent<SpriteComponent>(out var sprite, componentFactory),
+ @$"{proto.ID} has MagazineVisualsComponent but no SpriteComponent.");
+ Assert.That(proto.HasComponent<AppearanceComponent>(componentFactory),
+ @$"{proto.ID} has MagazineVisualsComponent but no AppearanceComponent.");
+
+ var toTest = new List<(int, string)>();
+ if (sprite.LayerMapTryGet(GunVisualLayers.Mag, out var magLayerId))
+ toTest.Add((magLayerId, ""));
+ if (sprite.LayerMapTryGet(GunVisualLayers.MagUnshaded, out var magUnshadedLayerId))
+ toTest.Add((magUnshadedLayerId, "-unshaded"));
+
+ Assert.That(toTest, Is.Not.Empty,
+ @$"{proto.ID} has MagazineVisualsComponent but no Mag or MagUnshaded layer map.");
+
+ var start = visuals.ZeroVisible ? 0 : 1;
+ foreach (var (id, midfix) in toTest)
+ {
+ Assert.That(sprite.TryGetLayer(id, out var layer));
+ var rsi = layer.ActualRsi;
+ for (var i = start; i < visuals.MagSteps; i++)
+ {
+ var state = $"{visuals.MagState}{midfix}-{i}";
+ Assert.That(rsi.TryGetState(state, out _),
+ @$"{proto.ID} has MagazineVisualsComponent with MagSteps = {visuals.MagSteps}, but {rsi.Path} doesn't have state {state}!");
+ }
+
+ // MagSteps includes the 0th step, so sometimes people are off by one.
+ var extraState = $"{visuals.MagState}{midfix}-{visuals.MagSteps}";
+ Assert.That(rsi.TryGetState(extraState, out _), Is.False,
+ @$"{proto.ID} has MagazineVisualsComponent with MagSteps = {visuals.MagSteps}, but more states exist!");
+ }
+ }
+ });
+
+ await pair.CleanReturnAsync();
+ }
+}
- type: Battery
maxCharge: 1000
startingCharge: 1000
- - type: MagazineVisuals
- magState: mag
- steps: 5
- zeroVisible: false
- - type: Appearance
- type: StaticPrice
price: 500
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: false
+ - type: Appearance
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/makeshift.rsi
- type: HitscanBatteryAmmoProvider
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: false
+ - type: Appearance
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/laser_gun.rsi
- type: Gun
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: false
+ - type: Appearance
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/pulse_pistol.rsi
- type: Gun
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: false
+ - type: Appearance
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/pulse_carbine.rsi
- type: Gun
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: false
+ - type: Appearance
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/pulse_rifle.rsi
- type: Gun
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: false
+ - type: Appearance
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/laser_cannon.rsi
- type: Gun
magState: mag
steps: 5
zeroVisible: true
+ - type: Appearance
- type: StaticPrice
price: 260
- type: BatterySelfRecharger
autoRecharge: true
autoRechargeRate: 40
- - type: MagazineVisuals
- magState: mag
- steps: 5
- zeroVisible: true
- - type: Appearance
- type: StaticPrice
price: 750
- state: mag-unshaded-4
map: ["enum.GunVisualLayers.MagUnshaded"]
shader: unshaded
+ - type: MagazineVisuals
+ magState: mag
+ steps: 5
+ zeroVisible: false
+ - type: Appearance
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/energy_shotgun.rsi
- type: Gun