Files
tbd-station-14/Content.Server/GameObjects/Components/Body/Behavior/LungBehavior.cs
2020-12-01 10:39:12 +01:00

204 lines
6.0 KiB
C#

#nullable enable
using System;
using System.Linq;
using Content.Server.Atmos;
using Content.Server.GameObjects.Components.Atmos;
using Content.Server.GameObjects.Components.Body.Circulatory;
using Content.Server.GameObjects.Components.Body.Respiratory;
using Content.Server.Utility;
using Content.Shared.Atmos;
using Content.Shared.GameObjects.Components.Body.Behavior;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Body.Behavior
{
public class LungBehavior : MechanismBehavior
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
private float _accumulatedFrameTime;
[ViewVariables] private TimeSpan _lastGaspPopupTime;
[ViewVariables] public GasMixture Air { get; set; } = default!;
[ViewVariables] public float Temperature => Air.Temperature;
[ViewVariables] public float Volume => Air.Volume;
[ViewVariables] public TimeSpan GaspPopupCooldown { get; private set; }
[ViewVariables] public LungStatus Status { get; set; }
[ViewVariables] public float CycleDelay { get; set; }
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
Air = new GasMixture {Temperature = Atmospherics.NormalBodyTemperature};
serializer.DataField(this, l => l.CycleDelay, "cycleDelay", 2);
serializer.DataReadWriteFunction(
"volume",
6,
vol => Air.Volume = vol,
() => Air.Volume);
serializer.DataReadWriteFunction(
"temperature",
Atmospherics.NormalBodyTemperature,
temp => Air.Temperature = temp,
() => Air.Temperature);
serializer.DataReadWriteFunction(
"gaspPopupCooldown",
8f,
delay => GaspPopupCooldown = TimeSpan.FromSeconds(delay),
() => GaspPopupCooldown.TotalSeconds);
}
public void Gasp()
{
if (_gameTiming.CurTime >= _lastGaspPopupTime + GaspPopupCooldown)
{
_lastGaspPopupTime = _gameTiming.CurTime;
Owner.PopupMessageEveryone(Loc.GetString("Gasp"));
}
Inhale(CycleDelay);
}
public void Transfer(GasMixture from, GasMixture to, float ratio)
{
to.Merge(from.RemoveRatio(ratio));
}
public void ToBloodstream(GasMixture mixture)
{
if (Body == null)
{
return;
}
if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
{
return;
}
var to = bloodstream.Air;
to.Merge(mixture);
mixture.Clear();
}
public override void Update(float frameTime)
{
if (Status == LungStatus.None)
{
Status = LungStatus.Inhaling;
}
_accumulatedFrameTime += Status switch
{
LungStatus.Inhaling => frameTime,
LungStatus.Exhaling => -frameTime,
_ => throw new ArgumentOutOfRangeException()
};
var absoluteTime = Math.Abs(_accumulatedFrameTime);
var delay = CycleDelay;
if (absoluteTime < delay)
{
return;
}
switch (Status)
{
case LungStatus.Inhaling:
Inhale(absoluteTime);
Status = LungStatus.Exhaling;
break;
case LungStatus.Exhaling:
Exhale(absoluteTime);
Status = LungStatus.Inhaling;
break;
default:
throw new ArgumentOutOfRangeException();
}
_accumulatedFrameTime = absoluteTime - delay;
}
public void Inhale(float frameTime)
{
if (Body != null && Body.Owner.TryGetComponent(out InternalsComponent? internals)
&& internals.BreathToolEntity != null && internals.GasTankEntity != null
&& internals.BreathToolEntity.TryGetComponent(out BreathToolComponent? breathTool)
&& breathTool.IsFunctional && internals.GasTankEntity.TryGetComponent(out GasTankComponent? gasTank)
&& gasTank.Air != null)
{
Inhale(frameTime, gasTank.RemoveAirVolume(Atmospherics.BreathVolume));
return;
}
if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir))
{
return;
}
Inhale(frameTime, tileAir);
}
public void Inhale(float frameTime, GasMixture from)
{
var ratio = (Atmospherics.BreathVolume / from.Volume) * frameTime;
Transfer(from, Air, ratio);
ToBloodstream(Air);
}
public void Exhale(float frameTime)
{
if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir))
{
return;
}
Exhale(frameTime, tileAir);
}
public void Exhale(float frameTime, GasMixture to)
{
// TODO: Make the bloodstream separately pump toxins into the lungs, making the lungs' only job to empty.
if (Body == null)
{
return;
}
if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
{
return;
}
bloodstream.PumpToxins(Air);
var lungRemoved = Air.RemoveRatio(0.5f);
to.Merge(lungRemoved);
}
}
public enum LungStatus
{
None = 0,
Inhaling,
Exhaling
}
}