Files
tbd-station-14/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs
moneyl 1b7860aeda Add injectors + injected reagent metabolism via BloodstreamCompo… (#730)
* Add BloodstreamComponent and BloodstreamSystem

New component for metabolizing reagents that other organs like the stomach pass their input reagents to.

* Change StomachComponent to put ingested reagents in bloodstream after delay

Now StomachComponent does not metabolize any reagents. Instead, it tracks how long each reagent has been inside it, and once they pass "digestionDelay" they'll be put inside the bloodstream, where the bloodstream will handle metabolism of the reagent.

* Add reagent injectors

Injects reagents straight into the bloodstream when used on mobs with bloodstreams. Also allows draw/inject from beakers. Does not support drawing blood/reagents from the bloodstream yet.

* Address code review

Make use of `Loc` static class instead of using `ILocalizationManager`. Localize InjectorToggleMode enum properly.
2020-02-24 01:47:33 +01:00

158 lines
5.8 KiB
C#

using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.Metabolism;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Nutrition;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Nutrition
{
/// <summary>
/// Where reagents go when ingested. Tracks ingested reagents over time, and
/// eventually transfers them to <see cref="BloodstreamComponent"/> once digested.
/// </summary>
[RegisterComponent]
public class StomachComponent : SharedStomachComponent
{
#pragma warning disable 649
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649
/// <summary>
/// Max volume of internal solution storage
/// </summary>
public int MaxVolume
{
get => _stomachContents.MaxVolume;
set => _stomachContents.MaxVolume = value;
}
/// <summary>
/// Internal solution storage
/// </summary>
[ViewVariables]
private SolutionComponent _stomachContents;
/// <summary>
/// Initial internal solution storage volume
/// </summary>
[ViewVariables]
private int _initialMaxVolume;
/// <summary>
/// Time in seconds between reagents being ingested and them being transferred to <see cref="BloodstreamComponent"/>
/// </summary>
[ViewVariables]
private float _digestionDelay;
/// <summary>
/// Used to track how long each reagent has been in the stomach
/// </summary>
private readonly List<ReagentDelta> _reagentDeltas = new List<ReagentDelta>();
/// <summary>
/// Reference to bloodstream where digested reagents are transferred to
/// </summary>
private BloodstreamComponent _bloodstream;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _initialMaxVolume, "maxVolume", 100);
serializer.DataField(ref _digestionDelay, "digestionDelay", 20);
}
public override void Initialize()
{
base.Initialize();
//Doesn't use Owner.AddComponent<>() to avoid cross-contamination (e.g. with blood or whatever they holds other solutions)
_stomachContents = new SolutionComponent();
_stomachContents.InitializeFromPrototype();
_stomachContents.MaxVolume = _initialMaxVolume;
_stomachContents.Owner = Owner; //Manually set owner to avoid crash when VV'ing this
//Ensure bloodstream in present
if (!Owner.TryGetComponent<BloodstreamComponent>(out _bloodstream))
{
Logger.Warning(_localizationManager.GetString(
"StomachComponent entity does not have a BloodstreamComponent, which is required for it to function. Owner entity name: {0}",
Owner.Name));
}
}
public bool TryTransferSolution(Solution solution)
{
// TODO: For now no partial transfers. Potentially change by design
if (solution.TotalVolume + _stomachContents.CurrentVolume > _stomachContents.MaxVolume)
{
return false;
}
//Add solution to _stomachContents
_stomachContents.TryAddSolution(solution, false, true);
//Add each reagent to _reagentDeltas. Used to track how long each reagent has been in the stomach
foreach (var reagent in solution.Contents)
{
_reagentDeltas.Add(new ReagentDelta(reagent.ReagentId, reagent.Quantity));
}
return true;
}
/// <summary>
/// Updates digestion status of ingested reagents. Once reagents surpass _digestionDelay
/// they are moved to the bloodstream, where they are then metabolized.
/// </summary>
/// <param name="tickTime">The time since the last update in seconds.</param>
public void OnUpdate(float tickTime)
{
if (_bloodstream == null)
{
return;
}
//Add reagents ready for transfer to bloodstream to transferSolution
var transferSolution = new Solution();
foreach (var delta in _reagentDeltas.ToList()) //Use ToList here to remove entries while iterating
{
//Increment lifetime of reagents
delta.Increment(tickTime);
if (delta.Lifetime > _digestionDelay)
{
_stomachContents.TryRemoveReagent(delta.ReagentId, delta.Quantity);
transferSolution.AddReagent(delta.ReagentId, delta.Quantity);
_reagentDeltas.Remove(delta);
}
}
//Transfer digested reagents to bloodstream
_bloodstream.TryTransferSolution(transferSolution);
}
/// <summary>
/// Used to track quantity changes when ingesting & digesting reagents
/// </summary>
private class ReagentDelta
{
public readonly string ReagentId;
public readonly int Quantity;
public float Lifetime { get; private set; }
public ReagentDelta(string reagentId, int quantity)
{
ReagentId = reagentId;
Quantity = quantity;
Lifetime = 0.0f;
}
public void Increment(float delta) => Lifetime += delta;
}
}
}