* HealthChangeMetabolism now scales with ticktime and metabolism rate Both Food and Drink metabolisms scale with ticktime, Now HealthChangeMetabolism also does so. Additionally, 'healthChange' now correctly scales with the metabolism rate, so it's description is now correct. * LiverBehaviour now uses correct frameTime Previously, the liver only metabolised reagants once every second, but incorrectly passes the current frameTime to the metabilism function, not 1 second. * Stomach now only transfers non-empty solutions. Makes debugging bloodstream bugs easier if the stomach is not constantly adding empty solution to it. * Fixed StomachBehaviour using wrong SolutionContainerComponent Stomach was using the first SolutionContainerComponent in the owner of the body, instead of the container in the owner of the mechanism (stomach). As a result, it used to use the BloodStreamComponent.Solution as a "Stomach". * Update StomachBehavior.cs Somach now checks if it still contains a reagant, before transferring it. * Added argument to IMetabolizable.Metabolize() Added availableReagent argument to IMetabolizable.Metabolize(), This ensures that this function does not over-metabolize a reagant, which can happen if tickTime*metabolismRate is larger than the available reagant * Revert "Stomach now only transfers non-empty solutions." This reverts commit 2a51e2d87e6e17ab76b48e5316ce501ec05ac061. * Renamed _updateInterval to _updateIntervalSeconds Also modified doc comment specifying units * Fix spelling of healthChangeAmount Changed from healthChangeAmmount to healthChangeAmount * Fixed comment comment used to mention _updateInterval, which has been renamed _updateIntervalSeconds * Fixed typo in comment * fixed typos: reagant -> reagent Most typos were just in comments. * Make metabolizable classes inherit from DefaultMetabolizable Also involved changing around IMetabolizable * Renamed variables metabolismAmount -> amountMetabolized * Updated Comments in DefaultMetabolizable Makes it clearer why DefaultMetabolizable works as it does, and that other classes depend on it.
190 lines
6.6 KiB
C#
190 lines
6.6 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Content.Server.Body.Circulatory;
|
|
using Content.Server.Chemistry.Components;
|
|
using Content.Shared.Body.Networks;
|
|
using Content.Shared.Chemistry.Reagent;
|
|
using Content.Shared.Chemistry.Solution;
|
|
using Content.Shared.Chemistry.Solution.Components;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Serialization.Manager.Attributes;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Content.Server.Body.Behavior
|
|
{
|
|
/// <summary>
|
|
/// Where reagents go when ingested. Tracks ingested reagents over time, and
|
|
/// eventually transfers them to <see cref="SharedBloodstreamComponent"/> once digested.
|
|
/// </summary>
|
|
public class StomachBehavior : MechanismBehavior
|
|
{
|
|
private float _accumulatedFrameTime;
|
|
|
|
/// <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="frameTime">
|
|
/// The time since the last update in seconds.
|
|
/// </param>
|
|
public override void Update(float frameTime)
|
|
{
|
|
|
|
// Do not metabolise if the organ does not have a body.
|
|
if (Body == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_accumulatedFrameTime += frameTime;
|
|
|
|
// Update at most once per second
|
|
if (_accumulatedFrameTime < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_accumulatedFrameTime -= 1;
|
|
|
|
// Note that "Owner" should be the organ that has this behaviour/mechanism, and it should have a dedicated
|
|
// solution container. "Body.Owner" is something else, and may have more than one solution container.
|
|
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) ||
|
|
!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Add reagents ready for transfer to bloodstream to transferSolution
|
|
var transferSolution = new Solution();
|
|
|
|
// Use ToList here to remove entries while iterating
|
|
foreach (var delta in _reagentDeltas.ToList())
|
|
{
|
|
//Increment lifetime of reagents
|
|
delta.Increment(1);
|
|
if (delta.Lifetime > _digestionDelay)
|
|
{
|
|
// This reagent has been in the somach long enough, TRY to transfer it.
|
|
// But first, check if the reagent still exists, and how much is left.
|
|
// Some poor spessman may have washed down a potassium snack with some water.
|
|
if (solution.Solution.ContainsReagent(delta.ReagentId, out ReagentUnit quantity)){
|
|
|
|
if (quantity > delta.Quantity) {
|
|
quantity = delta.Quantity;
|
|
}
|
|
|
|
solution.TryRemoveReagent(delta.ReagentId, quantity);
|
|
transferSolution.AddReagent(delta.ReagentId, quantity);
|
|
}
|
|
|
|
_reagentDeltas.Remove(delta);
|
|
}
|
|
}
|
|
|
|
// Transfer digested reagents to bloodstream
|
|
bloodstream.TryTransferSolution(transferSolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Max volume of internal solution storage
|
|
/// </summary>
|
|
public ReagentUnit MaxVolume
|
|
{
|
|
get => Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
|
|
set
|
|
{
|
|
if (Owner.TryGetComponent(out SharedSolutionContainerComponent? solution))
|
|
{
|
|
solution.MaxVolume = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initial internal solution storage volume
|
|
/// </summary>
|
|
[DataField("maxVolume")]
|
|
[ViewVariables]
|
|
protected ReagentUnit InitialMaxVolume { get; private set; } = ReagentUnit.New(100);
|
|
|
|
/// <summary>
|
|
/// Time in seconds between reagents being ingested and them being
|
|
/// transferred to <see cref="SharedBloodstreamComponent"/>
|
|
/// </summary>
|
|
[DataField("digestionDelay")] [ViewVariables]
|
|
private float _digestionDelay = 20;
|
|
|
|
/// <summary>
|
|
/// Used to track how long each reagent has been in the stomach
|
|
/// </summary>
|
|
[ViewVariables]
|
|
private readonly List<ReagentDelta> _reagentDeltas = new();
|
|
|
|
public override void Startup()
|
|
{
|
|
base.Startup();
|
|
|
|
Owner.EnsureComponentWarn(out SolutionContainerComponent solution);
|
|
|
|
solution.MaxVolume = InitialMaxVolume;
|
|
}
|
|
|
|
public bool CanTransferSolution(Solution solution)
|
|
{
|
|
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// TODO: For now no partial transfers. Potentially change by design
|
|
if (!solutionComponent.CanAddSolution(solution))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool TryTransferSolution(Solution solution)
|
|
{
|
|
if (Owner == null || !CanTransferSolution(solution))
|
|
return false;
|
|
|
|
if (!Owner.TryGetComponent(out SolutionContainerComponent? solutionComponent))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Add solution to _stomachContents
|
|
solutionComponent.TryAddSolution(solution);
|
|
// 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>
|
|
/// Used to track quantity changes when ingesting & digesting reagents
|
|
/// </summary>
|
|
protected class ReagentDelta
|
|
{
|
|
public readonly string ReagentId;
|
|
public readonly ReagentUnit Quantity;
|
|
public float Lifetime { get; private set; }
|
|
|
|
public ReagentDelta(string reagentId, ReagentUnit quantity)
|
|
{
|
|
ReagentId = reagentId;
|
|
Quantity = quantity;
|
|
Lifetime = 0.0f;
|
|
}
|
|
|
|
public void Increment(float delta) => Lifetime += delta;
|
|
}
|
|
}
|
|
}
|