using Content.Client.Items;
using Content.Client.Storage.Systems;
using Content.Shared.Stacks;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Stack
{
///
[UsedImplicitly]
public sealed class StackSystem : SharedStackSystem
{
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly ItemCounterSystem _counterSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnAppearanceChange);
Subs.ItemStatus(ent => new StackStatusControl(ent));
}
#region Appearance
private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args)
{
var (uid, comp) = ent;
if (args.Sprite == null || comp.LayerStates.Count < 1)
return;
// Skip processing if no elements in the stack
if (!_appearanceSystem.TryGetData(uid, StackVisuals.Actual, out var actual, args.Component))
return;
if (!_appearanceSystem.TryGetData(uid, StackVisuals.MaxCount, out var maxCount, args.Component))
maxCount = comp.LayerStates.Count;
if (!_appearanceSystem.TryGetData(uid, StackVisuals.Hide, out var hidden, args.Component))
hidden = false;
if (comp.LayerFunction != StackLayerFunction.None)
ApplyLayerFunction((uid, comp), ref actual, ref maxCount);
if (comp.IsComposite)
{
_counterSystem.ProcessCompositeSprite(uid,
actual,
maxCount,
comp.LayerStates,
hidden,
sprite: args.Sprite);
}
else
{
_counterSystem.ProcessOpaqueSprite(uid,
comp.BaseLayer,
actual,
maxCount,
comp.LayerStates,
hidden,
sprite: args.Sprite);
}
}
///
/// Adjusts the actual and maxCount to change how stack amounts are displayed.
///
/// The entity considered.
/// The actual number of items in the stack. Altered depending on the function to run.
/// The maximum number of items in the stack. Altered depending on the function to run.
/// True if a function was applied.
private bool ApplyLayerFunction(Entity ent, ref int actual, ref int maxCount)
{
switch (ent.Comp.LayerFunction)
{
case StackLayerFunction.Threshold:
if (TryComp(ent, out var threshold))
{
ApplyThreshold(threshold, ref actual, ref maxCount);
return true;
}
break;
}
// No function applied.
return false;
}
///
/// Selects which layer a stack applies based on a list of thresholds.
/// Each threshold passed results in the next layer being selected.
///
/// The threshold parameters to apply.
/// The number of items in the stack. Will be set to the index of the layer to use.
/// The maximum possible number of items in the stack. Will be set to the number of selectable layers.
private static void ApplyThreshold(StackLayerThresholdComponent comp, ref int actual, ref int maxCount)
{
// We must stop before we run out of thresholds or layers, whichever's smaller.
maxCount = Math.Min(comp.Thresholds.Count + 1, maxCount);
var newActual = 0;
foreach (var threshold in comp.Thresholds)
{
//If our value exceeds threshold, the next layer should be displayed.
//Note: we must ensure actual <= MaxCount.
if (actual >= threshold && newActual < maxCount)
newActual++;
else
break;
}
actual = newActual;
}
#endregion
}
}