Remove LungBehavior and replace with LungComponent/System (#5630)
This commit is contained in:
@@ -116,6 +116,7 @@ namespace Content.Client.Entry
|
||||
"PowerSupplier",
|
||||
"PowerConsumer",
|
||||
"Battery",
|
||||
"Lung",
|
||||
"BatteryDischarger",
|
||||
"Apc",
|
||||
"PowerProvider",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Body.Behavior;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Body.Components;
|
||||
using NUnit.Framework;
|
||||
@@ -17,7 +16,7 @@ using Robust.Shared.Maths;
|
||||
namespace Content.IntegrationTests.Tests.Body
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(LungBehavior))]
|
||||
[TestOf(typeof(LungSystem))]
|
||||
public class LungTest : ContentIntegrationTest
|
||||
{
|
||||
private const string Prototypes = @"
|
||||
@@ -64,8 +63,12 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
|
||||
var human = entityManager.SpawnEntity("HumanBodyAndBloodstreamDummy", new MapCoordinates(Vector2.Zero, mapId));
|
||||
|
||||
var bodySys = EntitySystem.Get<BodySystem>();
|
||||
var lungSys = EntitySystem.Get<LungSystem>();
|
||||
|
||||
Assert.That(human.TryGetComponent(out SharedBodyComponent body));
|
||||
Assert.That(body.TryGetMechanismBehaviors(out List<LungBehavior> lungs));
|
||||
|
||||
var lungs = bodySys.GetComponentsOnMechanisms<LungComponent>(human.Uid, body).ToArray();
|
||||
Assert.That(lungs.Count, Is.EqualTo(1));
|
||||
Assert.That(human.TryGetComponent(out BloodstreamComponent bloodstream));
|
||||
|
||||
@@ -78,8 +81,8 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
gas.AdjustMoles(Gas.Oxygen, originalOxygen);
|
||||
gas.AdjustMoles(Gas.Nitrogen, originalNitrogen);
|
||||
|
||||
var lung = lungs[0];
|
||||
lung.Inhale(1, gas);
|
||||
var (lung, _) = lungs[0];
|
||||
lungSys.TakeGasFrom(lung.OwnerUid, 1, gas, lung);
|
||||
|
||||
var lungOxygen = originalOxygen * breathedPercentage;
|
||||
var lungNitrogen = originalNitrogen * breathedPercentage;
|
||||
@@ -100,7 +103,7 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
Assert.Zero(lungOxygenBeforeExhale);
|
||||
Assert.Zero(lungNitrogenBeforeExhale);
|
||||
|
||||
lung.Exhale(1, gas);
|
||||
lungSys.PushGasTo(lung.OwnerUid, gas, lung);
|
||||
|
||||
var lungOxygenAfterExhale = lung.Air.GetMoles(Gas.Oxygen);
|
||||
var exhaledOxygen = Math.Abs(lungOxygenBeforeExhale - lungOxygenAfterExhale);
|
||||
@@ -168,8 +171,7 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
var coordinates = new EntityCoordinates(grid.GridEntityId, center);
|
||||
human = entityManager.SpawnEntity("HumanBodyAndBloodstreamDummy", coordinates);
|
||||
|
||||
Assert.True(human.TryGetComponent(out SharedBodyComponent body));
|
||||
Assert.True(body.HasMechanismBehavior<LungBehavior>());
|
||||
Assert.True(human.HasComponent<SharedBodyComponent>());
|
||||
Assert.True(human.TryGetComponent(out respirator));
|
||||
Assert.False(respirator.Suffocating);
|
||||
});
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
using System;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Body.Behavior
|
||||
{
|
||||
[DataDefinition]
|
||||
public class LungBehavior : MechanismBehavior, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private float _accumulatedFrameTime;
|
||||
|
||||
[ViewVariables] private TimeSpan _lastGaspPopupTime;
|
||||
|
||||
[DataField("air")]
|
||||
[ViewVariables]
|
||||
public GasMixture Air { get; set; } = new()
|
||||
{
|
||||
Volume = 6,
|
||||
Temperature = Atmospherics.NormalBodyTemperature
|
||||
};
|
||||
|
||||
[DataField("gaspPopupCooldown")]
|
||||
[ViewVariables]
|
||||
public TimeSpan GaspPopupCooldown { get; private set; } = TimeSpan.FromSeconds(8);
|
||||
|
||||
[ViewVariables] public LungStatus Status { get; set; }
|
||||
|
||||
[DataField("cycleDelay")]
|
||||
[ViewVariables]
|
||||
public float CycleDelay { get; set; } = 2;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void OnAddedToBody(SharedBodyComponent body)
|
||||
{
|
||||
base.OnAddedToBody(body);
|
||||
Inhale(CycleDelay);
|
||||
}
|
||||
|
||||
public void Gasp()
|
||||
{
|
||||
if (_gameTiming.CurTime >= _lastGaspPopupTime + GaspPopupCooldown)
|
||||
{
|
||||
_lastGaspPopupTime = _gameTiming.CurTime;
|
||||
Owner.PopupMessageEveryone(Loc.GetString("lung-behavior-gasp"));
|
||||
}
|
||||
|
||||
Inhale(CycleDelay);
|
||||
}
|
||||
|
||||
public void Transfer(GasMixture from, GasMixture to, float ratio)
|
||||
{
|
||||
EntitySystem.Get<AtmosphereSystem>().Merge(to, from.RemoveRatio(ratio));
|
||||
}
|
||||
|
||||
public void ToBloodstream(GasMixture mixture)
|
||||
{
|
||||
if (Body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var to = bloodstream.Air;
|
||||
|
||||
EntitySystem.Get<AtmosphereSystem>().Merge(to, mixture);
|
||||
mixture.Clear();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (Body != null && Body.Owner.TryGetComponent(out MobStateComponent? mobState) && mobState.IsCritical())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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 (EntitySystem.Get<AtmosphereSystem>().GetTileMixture(Owner.Transform.Coordinates, true) is not {} 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 (EntitySystem.Get<AtmosphereSystem>().GetTileMixture(Owner.Transform.Coordinates, true) is not {} 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;
|
||||
}
|
||||
|
||||
EntitySystem.Get<BloodstreamSystem>().PumpToxins(Body.OwnerUid, Air, bloodstream);
|
||||
|
||||
var lungRemoved = Air.RemoveRatio(0.5f);
|
||||
EntitySystem.Get<AtmosphereSystem>().Merge(to, lungRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
public enum LungStatus
|
||||
{
|
||||
None = 0,
|
||||
Inhaling,
|
||||
Exhaling
|
||||
}
|
||||
}
|
||||
44
Content.Server/Body/Components/LungComponent.cs
Normal file
44
Content.Server/Body/Components/LungComponent.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Body.Components;
|
||||
|
||||
[RegisterComponent, Friend(typeof(LungSystem))]
|
||||
public class LungComponent : Component
|
||||
{
|
||||
public override string Name => "Lung";
|
||||
|
||||
public float AccumulatedFrametime;
|
||||
|
||||
[ViewVariables]
|
||||
public TimeSpan LastGaspPopupTime;
|
||||
|
||||
[DataField("air")]
|
||||
public GasMixture Air { get; set; } = new()
|
||||
{
|
||||
Volume = 6,
|
||||
Temperature = Atmospherics.NormalBodyTemperature
|
||||
};
|
||||
|
||||
[DataField("gaspPopupCooldown")]
|
||||
public TimeSpan GaspPopupCooldown { get; private set; } = TimeSpan.FromSeconds(8);
|
||||
|
||||
[ViewVariables]
|
||||
public LungStatus Status { get; set; }
|
||||
|
||||
[DataField("cycleDelay")]
|
||||
public float CycleDelay { get; set; } = 2;
|
||||
}
|
||||
|
||||
public enum LungStatus
|
||||
{
|
||||
None = 0,
|
||||
Inhaling,
|
||||
Exhaling
|
||||
}
|
||||
@@ -40,8 +40,15 @@ namespace Content.Server.Body.Systems
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetComponentsOnMechanisms<T>(EntityUid uid,
|
||||
SharedBodyComponent? body) where T : Component
|
||||
/// <summary>
|
||||
/// Returns a list of ValueTuples of <see cref="T"/> and SharedMechanismComponent on each mechanism
|
||||
/// in the given body.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to check for the component on.</param>
|
||||
/// <param name="body">The body to check for mechanisms on.</param>
|
||||
/// <typeparam name="T">The component to check for.</typeparam>
|
||||
public IEnumerable<(T Comp, SharedMechanismComponent Mech)> GetComponentsOnMechanisms<T>(EntityUid uid,
|
||||
SharedBodyComponent? body=null) where T : Component
|
||||
{
|
||||
if (!Resolve(uid, ref body))
|
||||
yield break;
|
||||
@@ -50,13 +57,22 @@ namespace Content.Server.Body.Systems
|
||||
foreach (var mechanism in part.Mechanisms)
|
||||
{
|
||||
if (EntityManager.TryGetComponent<T>(mechanism.OwnerUid, out var comp))
|
||||
yield return comp;
|
||||
yield return (comp, mechanism);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a list of ValueTuples of <see cref="T"/> and SharedMechanismComponent on each mechanism
|
||||
/// in the given body.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to check for the component on.</param>
|
||||
/// <param name="comps">The list of components.</param>
|
||||
/// <param name="body">The body to check for mechanisms on.</param>
|
||||
/// <typeparam name="T">The component to check for.</typeparam>
|
||||
/// <returns>Whether any were found.</returns>
|
||||
public bool TryGetComponentsOnMechanisms<T>(EntityUid uid,
|
||||
[NotNullWhen(true)] out IEnumerable<T>? comps,
|
||||
SharedBodyComponent? body) where T: Component
|
||||
[NotNullWhen(true)] out IEnumerable<(T Comp, SharedMechanismComponent Mech)>? comps,
|
||||
SharedBodyComponent? body=null) where T: Component
|
||||
{
|
||||
if (!Resolve(uid, ref body))
|
||||
{
|
||||
|
||||
200
Content.Server/Body/Systems/LungSystem.cs
Normal file
200
Content.Server/Body/Systems/LungSystem.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
|
||||
public class LungSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosSys = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<LungComponent, AddedToBodyEvent>(OnAddedToBody);
|
||||
}
|
||||
|
||||
private void OnAddedToBody(EntityUid uid, LungComponent component, AddedToBodyEvent args)
|
||||
{
|
||||
Inhale(uid, component.CycleDelay);
|
||||
}
|
||||
|
||||
public void Gasp(EntityUid uid,
|
||||
LungComponent? lung=null,
|
||||
SharedMechanismComponent? mech=null)
|
||||
{
|
||||
if (!Resolve(uid, ref lung))
|
||||
return;
|
||||
|
||||
if (_gameTiming.CurTime >= lung.LastGaspPopupTime + lung.GaspPopupCooldown)
|
||||
{
|
||||
lung.LastGaspPopupTime = _gameTiming.CurTime;
|
||||
_popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid, Filter.Pvs(uid));
|
||||
}
|
||||
|
||||
Inhale(uid, lung.CycleDelay);
|
||||
}
|
||||
|
||||
public void UpdateLung(EntityUid uid, float frameTime,
|
||||
LungComponent? lung=null,
|
||||
SharedMechanismComponent? mech=null)
|
||||
{
|
||||
if (!Resolve(uid, ref lung, ref mech))
|
||||
return;
|
||||
|
||||
if (mech.Body != null && EntityManager.TryGetComponent(mech.Body.OwnerUid, out MobStateComponent? mobState) && mobState.IsCritical())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (lung.Status == LungStatus.None)
|
||||
{
|
||||
lung.Status = LungStatus.Inhaling;
|
||||
}
|
||||
|
||||
lung.AccumulatedFrametime += lung.Status switch
|
||||
{
|
||||
LungStatus.Inhaling => frameTime,
|
||||
LungStatus.Exhaling => -frameTime,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
var absoluteTime = Math.Abs(lung.AccumulatedFrametime);
|
||||
var delay = lung.CycleDelay;
|
||||
|
||||
if (absoluteTime < delay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (lung.Status)
|
||||
{
|
||||
case LungStatus.Inhaling:
|
||||
Inhale(uid, absoluteTime);
|
||||
lung.Status = LungStatus.Exhaling;
|
||||
break;
|
||||
case LungStatus.Exhaling:
|
||||
Exhale(uid, absoluteTime);
|
||||
lung.Status = LungStatus.Inhaling;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
lung.AccumulatedFrametime = absoluteTime - delay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find an air mixture to inhale from, then inhales from it.
|
||||
/// </summary>
|
||||
public void Inhale(EntityUid uid, float frameTime,
|
||||
LungComponent? lung=null,
|
||||
SharedMechanismComponent? mech=null)
|
||||
{
|
||||
if (!Resolve(uid, ref lung, ref mech))
|
||||
return;
|
||||
|
||||
// TODO Jesus Christ make this event based.
|
||||
if (mech.Body != null &&
|
||||
EntityManager.TryGetComponent(mech.Body.OwnerUid, out InternalsComponent? internals) &&
|
||||
internals.BreathToolEntity != null &&
|
||||
internals.GasTankEntity != null &&
|
||||
internals.BreathToolEntity.TryGetComponent(out BreathToolComponent? breathTool) &&
|
||||
breathTool.IsFunctional &&
|
||||
internals.GasTankEntity.TryGetComponent(out GasTankComponent? gasTank))
|
||||
{
|
||||
TakeGasFrom(uid, frameTime, gasTank.RemoveAirVolume(Atmospherics.BreathVolume), lung);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_atmosSys.GetTileMixture(EntityManager.GetComponent<TransformComponent>(uid).Coordinates, true) is not { } tileAir)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TakeGasFrom(uid, frameTime, tileAir, lung);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inhales directly from a given mixture.
|
||||
/// </summary>
|
||||
public void TakeGasFrom(EntityUid uid, float frameTime, GasMixture from,
|
||||
LungComponent? lung=null,
|
||||
SharedMechanismComponent? mech=null)
|
||||
{
|
||||
if (!Resolve(uid, ref lung, ref mech))
|
||||
return;
|
||||
|
||||
var ratio = (Atmospherics.BreathVolume / from.Volume) * frameTime;
|
||||
|
||||
_atmosSys.Merge(lung.Air, from.RemoveRatio(ratio));
|
||||
|
||||
// Push to bloodstream
|
||||
if (mech.Body == null)
|
||||
return;
|
||||
|
||||
if (!EntityManager.TryGetComponent(mech.Body.OwnerUid, out BloodstreamComponent? bloodstream))
|
||||
return;
|
||||
|
||||
var to = bloodstream.Air;
|
||||
|
||||
_atmosSys.Merge(to, lung.Air);
|
||||
lung.Air.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a gas mixture to exhale to, then pushes gas to it.
|
||||
/// </summary>
|
||||
public void Exhale(EntityUid uid, float frameTime,
|
||||
LungComponent? lung=null,
|
||||
SharedMechanismComponent? mech=null)
|
||||
{
|
||||
if (!Resolve(uid, ref lung, ref mech))
|
||||
return;
|
||||
|
||||
if (_atmosSys.GetTileMixture(EntityManager.GetComponent<TransformComponent>(uid).Coordinates, true) is not { } tileAir)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PushGasTo(uid, tileAir, lung, mech);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes gas from the lungs to a gas mixture.
|
||||
/// </summary>
|
||||
public void PushGasTo(EntityUid uid, GasMixture to,
|
||||
LungComponent? lung=null,
|
||||
SharedMechanismComponent? mech=null)
|
||||
{
|
||||
if (!Resolve(uid, ref lung, ref mech))
|
||||
return;
|
||||
|
||||
// TODO: Make the bloodstream separately pump toxins into the lungs, making the lungs' only job to empty.
|
||||
if (mech.Body == null)
|
||||
return;
|
||||
|
||||
if (!EntityManager.TryGetComponent(mech.Body.OwnerUid, out BloodstreamComponent? bloodstream))
|
||||
return;
|
||||
|
||||
_bloodstreamSystem.PumpToxins(mech.Body.OwnerUid, lung.Air, bloodstream);
|
||||
|
||||
var lungRemoved = lung.Air.RemoveRatio(0.5f);
|
||||
_atmosSys.Merge(to, lungRemoved);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Alert;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Body.Behavior;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
@@ -23,6 +22,8 @@ namespace Content.Server.Body.Systems
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damageableSys = default!;
|
||||
[Dependency] private readonly AdminLogSystem _logSys = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly LungSystem _lungSystem = default!;
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
@@ -136,10 +137,16 @@ namespace Content.Server.Body.Systems
|
||||
if (!Resolve(uid, ref bloodstream, ref body, false))
|
||||
return;
|
||||
|
||||
var lungs = body.GetMechanismBehaviors<LungBehavior>().ToArray();
|
||||
var lungs = _bodySystem.GetComponentsOnMechanisms<LungComponent>(uid, body).ToArray();
|
||||
|
||||
var needs = NeedsAndDeficit(respirator, frameTime);
|
||||
var used = 0f;
|
||||
|
||||
foreach (var (lung, mech) in lungs)
|
||||
{
|
||||
_lungSystem.UpdateLung(lung.OwnerUid, frameTime, lung, mech);
|
||||
}
|
||||
|
||||
foreach (var (gas, amountNeeded) in needs)
|
||||
{
|
||||
var bloodstreamAmount = bloodstream.Air.GetMoles(gas);
|
||||
@@ -150,9 +157,9 @@ namespace Content.Server.Body.Systems
|
||||
if (!EntityManager.GetComponent<MobStateComponent>(uid).IsCritical())
|
||||
{
|
||||
// Panic inhale
|
||||
foreach (var lung in lungs)
|
||||
foreach (var (lung, mech) in lungs)
|
||||
{
|
||||
lung.Gasp();
|
||||
_lungSystem.Gasp(lung.OwnerUid, lung, mech);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
@@ -263,8 +264,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
var transferAmount = FixedPoint2.Min(drink.TransferAmount, drinkSolution.DrainAvailable);
|
||||
var drain = _solutionContainerSystem.Drain(uid, drinkSolution, transferAmount);
|
||||
var firstStomach = stomachs.FirstOrDefault(
|
||||
stomach => _stomachSystem.CanTransferSolution(stomach.OwnerUid, drain));
|
||||
var firstStomach = stomachs.FirstOrNull(
|
||||
stomach => _stomachSystem.CanTransferSolution(stomach.Comp.OwnerUid, drain));
|
||||
|
||||
// All stomach are full or can't handle whatever solution we have.
|
||||
if (firstStomach == null)
|
||||
@@ -289,7 +290,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
Filter.Pvs(userUid));
|
||||
|
||||
drain.DoEntityReaction(userUid, ReactionMethod.Ingestion);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.OwnerUid, drain, firstStomach);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.OwnerUid, drain, firstStomach.Value.Comp);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -371,8 +372,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
var firstStomach = stomachs.FirstOrDefault(
|
||||
stomach => _stomachSystem.CanTransferSolution(stomach.OwnerUid, drained));
|
||||
var firstStomach = stomachs.FirstOrNull(
|
||||
stomach => _stomachSystem.CanTransferSolution(stomach.Comp.OwnerUid, drained));
|
||||
|
||||
// All stomach are full or can't handle whatever solution we have.
|
||||
if (firstStomach == null)
|
||||
@@ -399,7 +400,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
SoundSystem.Play(Filter.Pvs(uid), args.Drink.UseSound.GetSound(), uid, AudioParams.Default.WithVolume(-2f));
|
||||
|
||||
drained.DoEntityReaction(uid, ReactionMethod.Ingestion);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.OwnerUid, drained, firstStomach);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.OwnerUid, drained, firstStomach.Value.Comp);
|
||||
}
|
||||
|
||||
private void OnForceDrinkCancelled(ForceDrinkCancelledEvent args)
|
||||
|
||||
@@ -24,6 +24,7 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
@@ -144,7 +145,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
var transferAmount = component.TransferAmount != null ? FixedPoint2.Min((FixedPoint2) component.TransferAmount, solution.CurrentVolume) : solution.CurrentVolume;
|
||||
var split = _solutionContainerSystem.SplitSolution(uid, solution, transferAmount);
|
||||
var firstStomach = stomachs.FirstOrDefault(stomach => _stomachSystem.CanTransferSolution(stomach.OwnerUid, split));
|
||||
var firstStomach = stomachs.FirstOrNull(
|
||||
stomach => _stomachSystem.CanTransferSolution(stomach.Comp.OwnerUid, split));
|
||||
|
||||
if (firstStomach == null)
|
||||
{
|
||||
@@ -155,7 +157,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
// TODO: Account for partial transfer.
|
||||
split.DoEntityReaction(userUid, ReactionMethod.Ingestion);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.OwnerUid, split, firstStomach);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.OwnerUid, split, firstStomach.Value.Comp);
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(userUid), component.UseSound.GetSound(), userUid, AudioParams.Default.WithVolume(-1f));
|
||||
_popupSystem.PopupEntity(Loc.GetString(component.EatMessage, ("food", component.Owner)), userUid, Filter.Entities(userUid));
|
||||
@@ -292,7 +294,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
: args.FoodSolution.CurrentVolume;
|
||||
|
||||
var split = _solutionContainerSystem.SplitSolution(args.Food.OwnerUid, args.FoodSolution, transferAmount);
|
||||
var firstStomach = stomachs.FirstOrDefault(stomach => _stomachSystem.CanTransferSolution(stomach.OwnerUid, split));
|
||||
var firstStomach = stomachs.FirstOrNull(
|
||||
stomach => _stomachSystem.CanTransferSolution(stomach.Comp.OwnerUid, split));
|
||||
|
||||
if (firstStomach == null)
|
||||
{
|
||||
@@ -302,7 +305,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
}
|
||||
|
||||
split.DoEntityReaction(uid, ReactionMethod.Ingestion);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.OwnerUid, split, firstStomach);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.OwnerUid, split, firstStomach.Value.Comp);
|
||||
|
||||
EntityManager.TryGetComponent(uid, out MetaDataComponent? targetMeta);
|
||||
var targetName = targetMeta?.EntityName ?? string.Empty;
|
||||
@@ -350,7 +353,9 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
if (food.UsesRemaining <= 0)
|
||||
DeleteAndSpawnTrash(food);
|
||||
|
||||
var firstStomach = stomachs.FirstOrDefault(stomach => _stomachSystem.CanTransferSolution(stomach.OwnerUid, foodSolution));
|
||||
var firstStomach = stomachs.FirstOrNull(
|
||||
stomach => _stomachSystem.CanTransferSolution(stomach.Comp.OwnerUid, foodSolution));
|
||||
|
||||
if (firstStomach == null)
|
||||
return;
|
||||
|
||||
@@ -365,7 +370,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
_popupSystem.PopupEntity(Loc.GetString(food.EatMessage), target, Filter.Entities(target));
|
||||
|
||||
foodSolution.DoEntityReaction(uid, ReactionMethod.Ingestion);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.OwnerUid, foodSolution, firstStomach);
|
||||
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.OwnerUid, foodSolution, firstStomach.Value.Comp);
|
||||
SoundSystem.Play(Filter.Pvs(target), food.UseSound.GetSound(), target, AudioParams.Default.WithVolume(-1f));
|
||||
|
||||
if (string.IsNullOrEmpty(food.TrashPrototype))
|
||||
|
||||
@@ -99,8 +99,7 @@
|
||||
- type: Mechanism
|
||||
size: 1
|
||||
compatibility: Biological
|
||||
behaviors:
|
||||
- !type:LungBehavior {}
|
||||
- type: Lung
|
||||
|
||||
- type: entity
|
||||
id: OrganHumanHeart
|
||||
|
||||
@@ -83,8 +83,7 @@
|
||||
- type: Mechanism
|
||||
size: 1
|
||||
compatibility: Biological
|
||||
behaviors:
|
||||
- !type:LungBehavior {}
|
||||
- type: Lung
|
||||
|
||||
- type: entity
|
||||
id: OrganAnimalStomach
|
||||
|
||||
Reference in New Issue
Block a user