expanded FillLevelSpriteTest test and fixed found issues (#34165)

* fix clustersoap eaten sprite

* also assure that every entity with the SolutionContainerVisualsComponent has a AppearanceComponent

* use the new sprite system + fix the fill for cardboard arrows and the mosin

* fix merge issue
This commit is contained in:
Ignaz "Ian" Kraft 2026-01-03 02:20:41 +01:00 committed by BarryNorfolk
parent b9a6b778b4
commit bee1b9aa33
6 changed files with 74 additions and 29 deletions

View File

@ -1,5 +1,7 @@
using System.Linq;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Prototypes;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
@ -13,14 +15,17 @@ namespace Content.IntegrationTests.Tests;
public sealed class FillLevelSpriteTest
{
private static readonly string[] HandStateNames = ["left", "right"];
private static readonly string[] EquipStateNames = ["back", "suitstorage"];
[Test]
public async Task FillLevelSpritesExist()
{
await using var pair = await PoolManager.GetServerClient();
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true });
var client = pair.Client;
var protoMan = client.ResolveDependency<IPrototypeManager>();
var componentFactory = client.ResolveDependency<IComponentFactory>();
var entMan = client.ResolveDependency<IEntityManager>();
var spriteSystem = client.System<SpriteSystem>();
await client.WaitAssertion(() =>
{
@ -31,39 +36,70 @@ public sealed class FillLevelSpriteTest
.OrderBy(p => p.ID)
.ToList();
foreach (var proto in protos)
Assert.Multiple(() =>
{
Assert.That(proto.TryGetComponent<SolutionContainerVisualsComponent>(out var visuals, componentFactory));
Assert.That(proto.TryGetComponent<SpriteComponent>(out var sprite, componentFactory));
var rsi = sprite.BaseRSI;
// Test base sprite fills
if (!string.IsNullOrEmpty(visuals.FillBaseName))
foreach (var proto in protos)
{
for (var i = 1; i <= visuals.MaxFillLevels; i++)
Assert.That(proto.TryGetComponent<SolutionContainerVisualsComponent>(out var visuals, componentFactory));
Assert.That(proto.TryGetComponent<SpriteComponent>(out var sprite, componentFactory));
if (!proto.HasComponent<AppearanceComponent>(componentFactory))
{
var state = $"{visuals.FillBaseName}{i}";
Assert.That(rsi.TryGetState(state, out _), @$"{proto.ID} has SolutionContainerVisualsComponent with
MaxFillLevels = {visuals.MaxFillLevels}, but {rsi.Path} doesn't have state {state}!");
Assert.Fail(@$"{proto.ID} has SolutionContainerVisualsComponent but no AppearanceComponent.");
}
}
// Test inhand sprite fills
if (!string.IsNullOrEmpty(visuals.InHandsFillBaseName))
{
for (var i = 1; i <= visuals.InHandsMaxFillLevels; i++)
// Test base sprite fills
if (!string.IsNullOrEmpty(visuals.FillBaseName) && visuals.MaxFillLevels > 0)
{
foreach (var handname in HandStateNames)
var entity = entMan.Spawn(proto.ID);
if (!spriteSystem.LayerMapTryGet(entity, SolutionContainerLayers.Fill, out var fillLayerId, false))
{
var state = $"inhand-{handname}{visuals.InHandsFillBaseName}{i}";
Assert.That(rsi.TryGetState(state, out _), @$"{proto.ID} has SolutionContainerVisualsComponent with
InHandsMaxFillLevels = {visuals.InHandsMaxFillLevels}, but {rsi.Path} doesn't have state {state}!");
Assert.Fail(@$"{proto.ID} has SolutionContainerVisualsComponent but no fill layer map.");
}
if (!spriteSystem.TryGetLayer(entity, fillLayerId, out var fillLayer, false))
{
Assert.Fail(@$"{proto.ID} somehow lost a layer.");
}
var rsi = fillLayer.ActualRsi;
for (var i = 1; i <= visuals.MaxFillLevels; i++)
{
var state = $"{visuals.FillBaseName}{i}";
Assert.That(rsi.TryGetState(state, out _), @$"{proto.ID} has SolutionContainerVisualsComponent with
MaxFillLevels = {visuals.MaxFillLevels}, but {rsi.Path} doesn't have state {state}!");
}
}
// Test inhand sprite fills
if (!string.IsNullOrEmpty(visuals.InHandsFillBaseName) && visuals.InHandsMaxFillLevels > 0)
{
var rsi = sprite.BaseRSI;
for (var i = 1; i <= visuals.InHandsMaxFillLevels; i++)
{
foreach (var handname in HandStateNames)
{
var state = $"inhand-{handname}{visuals.InHandsFillBaseName}{i}";
Assert.That(rsi.TryGetState(state, out _), @$"{proto.ID} has SolutionContainerVisualsComponent with
InHandsMaxFillLevels = {visuals.InHandsMaxFillLevels}, but {rsi.Path} doesn't have state {state}!");
}
}
}
// Test equipped sprite fills
if (!string.IsNullOrEmpty(visuals.EquippedFillBaseName) && visuals.EquippedMaxFillLevels > 0)
{
var rsi = sprite.BaseRSI;
for (var i = 1; i <= visuals.EquippedMaxFillLevels; i++)
{
foreach (var equipName in EquipStateNames)
{
var state = $"equipped-{equipName}{visuals.EquippedFillBaseName}{i}";
Assert.That(rsi.TryGetState(state, out _), @$"{proto.ID} has SolutionContainerVisualsComponent with
EquippedMaxFillLevels = {visuals.EquippedMaxFillLevels}, but {rsi.Path} doesn't have state {state}!");
}
}
}
}
}
});
});
await pair.CleanReturnAsync();

View File

@ -161,16 +161,18 @@
description: A tiny piece of syndicate soap.
categories: [ HideSpawnMenu ]
components:
- type: Sprite
layers:
- state: syndie-soaplet
- type: SolutionContainerManager
solutions:
soap:
maxVol: 10
maxVol: 1.5 # 50 / 30 = 1.666
reagents:
- ReagentId: SoapReagent
Quantity: 10
Quantity: 1.5
- type: SolutionContainerVisuals
maxFillLevels: 0
- type: Sprite
layers:
- state: syndie-soaplet
- type: Slippery
- type: StepTrigger
intersectRatio: 0.04
@ -201,6 +203,9 @@
path: "/Audio/Effects/Fluids/splat.ogg"
params:
volume: -8
- type: Residue
residueAdjective: residue-slippery
residueColor: residue-red
- type: entity
parent: BaseSoap

View File

@ -201,6 +201,9 @@
color: brown
- state: tip
color: brown
- state: solution1
map: ["enum.SolutionContainerLayers.Fill"]
visible: false
- type: Projectile
damage:
types:

View File

@ -146,6 +146,7 @@
sprite: Objects/Specific/Janitorial/soap.rsi
layers:
- state: syndie-4
map: ["enum.SolutionContainerLayers.Fill"]
- type: ScatteringGrenade
fillPrototype: SoapletSyndie
capacity: 30

View File

@ -53,7 +53,7 @@
"directions": 4
},
{
"name": "equipped-backpack-fill-1",
"name": "equipped-back-fill-1",
"directions": 4
},
{