using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Shared.Nutrition.EntitySystems;
///
/// This abstract system provides a convenient interface for implementing effects which react to changes in
/// thresholds.
///
///
/// uses this to maintain a damage descriptor which changes depending on the entity's
/// satiation values. When the satiation values change, the damage descriptor is automatically updated by this system.
///
/// The type of component this system interacts with
///
/// The type of value this system maintains. It must be contained within a
/// accessible via TComp.
///
/// Note that this is not related to
public abstract partial class BaseSatiationEffectSystem : EntitySystem where TComp : Component
{
[Dependency] private SatiationSystem _satiation = default!;
[Dependency] private EntityQuery _satiationQuery;
[Dependency] private IGameTiming _timing = default!;
///
/// How to access via a .
///
protected abstract Dictionary, SatiationThresholds> GetThresholds(TComp comp);
///
/// The default value to set our maintained value to in the case that our current satiation
/// is outside of .
///
protected abstract T DefaultValue();
///
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnSatiationUpdate);
}
///
// Updates maintained values when reaching the projected threshold time.
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator();
while (query.MoveNext(out var ent, out var comp, out var satiation))
{
foreach (var (type, thresholds) in GetThresholds(comp))
{
if (_timing.CurTime < thresholds.ProjectedThresholdChangeTime)
continue;
UpdateSatiation((ent, comp), satiation, type);
}
}
}
[MustCallBase]
protected virtual void OnMapInit(Entity entity, ref MapInitEvent args)
{
// Make sure we have a satiation component. Realistically, this just exists to cause test failures if an entity
// with `TComp` doesn't have a `SatiationComponent`.
var comp = EnsureComp(entity);
foreach (var type in GetThresholds(entity.Comp).Keys)
{
UpdateSatiation(entity, comp, type);
}
}
// If a satiation value is changed directly, we react and update our maintained value.
[MustCallBase]
protected void OnSatiationUpdate(Entity entity, ref SatiationUpdateEvent args)
{
if (!_satiationQuery.TryComp(entity, out var comp))
return;
UpdateSatiation(entity, comp, args.Type);
}
///
/// Updates the maintained based on the entity's satiation.
///
private void UpdateSatiation(Entity entity, SatiationComponent comp, ProtoId type)
{
// Get the current satiation value...
if (!GetThresholds(entity.Comp).TryGetValue(type, out var thresholds))
return;
// ... and then use it to get the appropriate threshold T value.
if (_satiation.TryGetValueByThreshold(
(entity, comp),
type,
thresholds.Thresholds,
out var result,
out var nextLowerThreshold))
{
thresholds.Current = result ?? DefaultValue();
// Predict when our satiation will decay to the next threshold down.
thresholds.ProjectedThresholdChangeTime = nextLowerThreshold is { } lower
? _satiation.GetTimeToDecay((entity, comp), type, lower)
: null;
}
else
{
thresholds.Current = DefaultValue();
thresholds.ProjectedThresholdChangeTime = null;
}
Dirty(entity);
AfterSatiationUpdate(entity);
}
///
/// This function is called after completes its work maintaining .
///
protected virtual void AfterSatiationUpdate(Entity entity) { }
}