using System.Collections.Generic; using System.Linq; using Robust.Shared.GameObjects; using Robust.Shared.ViewVariables; namespace Content.Server.Chemistry.Components { /// /// 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 { private const float ReactionDelay = 1.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; } } }