using System.Linq; using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.EntitySystems; using Content.Shared.Chemistry; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Metabolism { /// /// Handles all metabolism for mobs. All delivery methods eventually bring reagents /// to the bloodstream. For example, injectors put reagents directly into the bloodstream, /// and the stomach does with some delay. /// [RegisterComponent] public class BloodstreamComponent : Component { #pragma warning disable 649 [Dependency] private readonly IPrototypeManager _prototypeManager; #pragma warning restore 649 public override string Name => "Bloodstream"; /// /// Internal solution for reagent storage /// [ViewVariables] private SolutionComponent _internalSolution; /// /// Max volume of internal solution storage /// [ViewVariables] private int _initialMaxVolume; /// /// Empty volume of internal solution /// public decimal EmptyVolume => _internalSolution.EmptyVolume; public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref _initialMaxVolume, "maxVolume", 250); } public override void Initialize() { base.Initialize(); //Create and setup internal solution storage _internalSolution = new SolutionComponent(); _internalSolution.InitializeFromPrototype(); _internalSolution.Init(); _internalSolution.MaxVolume = _initialMaxVolume; _internalSolution.Owner = Owner; //Manually set owner to avoid crash when VV'ing this } /// /// Attempt to transfer provided solution to internal solution. Only supports complete transfers /// /// Solution to be transferred /// Whether or not transfer was a success public bool TryTransferSolution(Solution solution) { //For now doesn't support partial transfers if (solution.TotalVolume + _internalSolution.CurrentVolume > _internalSolution.MaxVolume) { return false; } _internalSolution.TryAddSolution(solution, false, true); return true; } /// /// Loops through each reagent in _internalSolution, and calls the IMetabolizable for each of them./> /// /// The time since the last metabolism tick in seconds. private void Metabolize(float tickTime) { if (_internalSolution.CurrentVolume == 0) { return; } //Run metabolism for each reagent, remove metabolized reagents foreach (var reagent in _internalSolution.ReagentList.ToList()) //Using ToList here lets us edit reagents while iterating { if (!_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto)) { continue; } //Run metabolism code for each reagent foreach (var metabolizable in proto.Metabolism) { var reagentDelta = metabolizable.Metabolize(Owner, reagent.ReagentId, tickTime); _internalSolution.TryRemoveReagent(reagent.ReagentId, reagentDelta); } } } /// /// Triggers metabolism of the reagents inside _internalSolution. Called by /// /// The time since the last metabolism tick in seconds. public void OnUpdate(float tickTime) { Metabolize(tickTime); } } }