#nullable enable
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Chemistry
{
///
/// The "mastermind" of a SolutionAreaEffect group. It gets updated by the SolutionAreaEffectSystem and tells the
/// group when to spread, react and remove itself. This makes the group act like a single unit.
///
/// It should only be manually added to an entity by the and not with a prototype.
[RegisterComponent]
public class SolutionAreaEffectInceptionComponent : Component
{
public override string Name => "AreaEffectInception";
private const float ReactionDelay = 0.5f;
private readonly HashSet _group = new();
[ViewVariables] private float _lifeTimer;
[ViewVariables] private float _spreadTimer;
[ViewVariables] private float _reactionTimer;
[ViewVariables] private int _amountCounterSpreading;
[ViewVariables] private int _amountCounterRemoving;
///
/// How much time to wait after fully spread before starting to remove itself.
///
[ViewVariables] private float _duration;
///
/// Time between each spread step. Decreasing this makes spreading faster.
///
[ViewVariables] private float _spreadDelay;
///
/// Time between each remove step. Decreasing this makes removing faster.
///
[ViewVariables] private float _removeDelay;
///
/// How many times will the effect react. As some entities from the group last a different amount of time than
/// others, they will react a different amount of times, so we calculate the average to make the group behave
/// a bit more uniformly.
///
[ViewVariables] private float _averageExposures;
public void Setup(int amount, float duration, float spreadDelay, float removeDelay)
{
_amountCounterSpreading = amount;
_duration = duration;
_spreadDelay = spreadDelay;
_removeDelay = removeDelay;
// So the first square reacts immediately after spawning
_reactionTimer = ReactionDelay;
/*
The group takes amount*spreadDelay seconds to fully spread, same with fully disappearing.
The outer squares will last duration seconds.
The first square will last duration + how many seconds the group takes to fully spread and fully disappear, so
it will last duration + amount*(spreadDelay+removeDelay).
Thus, the average lifetime of the smokes will be (outerSmokeLifetime + firstSmokeLifetime)/2 = duration + amount*(spreadDelay+removeDelay)/2
*/
_averageExposures = (duration + amount * (spreadDelay+removeDelay) / 2)/ReactionDelay;
}
public void InceptionUpdate(float frameTime)
{
_group.RemoveWhere(effect => effect.Deleted);
if (_group.Count == 0)
return;
// Make every outer square from the group spread
if (_amountCounterSpreading > 0)
{
_spreadTimer += frameTime;
if (_spreadTimer > _spreadDelay)
{
_spreadTimer -= _spreadDelay;
var outerEffects = new HashSet(_group.Where(effect => effect.Amount == _amountCounterSpreading));
foreach (var effect in outerEffects)
{
effect.Spread();
}
_amountCounterSpreading -= 1;
}
}
// Start counting for _duration after fully spreading
else
{
_lifeTimer += frameTime;
}
// Delete every outer square
if (_lifeTimer > _duration)
{
_spreadTimer += frameTime;
if (_spreadTimer > _removeDelay)
{
_spreadTimer -= _removeDelay;
var outerEffects = new HashSet(_group.Where(effect => effect.Amount == _amountCounterRemoving));
foreach (var effect in outerEffects)
{
effect.Kill();
}
_amountCounterRemoving += 1;
}
}
// Make every square from the group react with the tile and entities
_reactionTimer += frameTime;
if (_reactionTimer > ReactionDelay)
{
_reactionTimer -= ReactionDelay;
foreach (var effect in _group)
{
effect.React(_averageExposures);
}
}
}
public void Add(SolutionAreaEffectComponent effect)
{
_group.Add(effect);
effect.Inception = this;
}
public void Remove(SolutionAreaEffectComponent effect)
{
_group.Remove(effect);
effect.Inception = null;
}
}
}