diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index d49aa2f81e..48d30e0eea 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -116,6 +116,7 @@ namespace Content.Client.Entry "PowerSupplier", "PowerConsumer", "Battery", + "Lung", "BatteryDischarger", "Apc", "PowerProvider", diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index 3e3acd1a43..b9c1536e75 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -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(); + var lungSys = EntitySystem.Get(); + Assert.That(human.TryGetComponent(out SharedBodyComponent body)); - Assert.That(body.TryGetMechanismBehaviors(out List lungs)); + + var lungs = bodySys.GetComponentsOnMechanisms(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()); + Assert.True(human.HasComponent()); Assert.True(human.TryGetComponent(out respirator)); Assert.False(respirator.Suffocating); }); diff --git a/Content.Server/Body/Behavior/LungBehavior.cs b/Content.Server/Body/Behavior/LungBehavior.cs deleted file mode 100644 index 0e027f1152..0000000000 --- a/Content.Server/Body/Behavior/LungBehavior.cs +++ /dev/null @@ -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().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().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().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().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().PumpToxins(Body.OwnerUid, Air, bloodstream); - - var lungRemoved = Air.RemoveRatio(0.5f); - EntitySystem.Get().Merge(to, lungRemoved); - } - } - - public enum LungStatus - { - None = 0, - Inhaling, - Exhaling - } -} diff --git a/Content.Server/Body/Components/LungComponent.cs b/Content.Server/Body/Components/LungComponent.cs new file mode 100644 index 0000000000..ee5301ac8d --- /dev/null +++ b/Content.Server/Body/Components/LungComponent.cs @@ -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 +} diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index b93005fb9b..11b79789dc 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -40,8 +40,15 @@ namespace Content.Server.Body.Systems } } - public IEnumerable GetComponentsOnMechanisms(EntityUid uid, - SharedBodyComponent? body) where T : Component + /// + /// Returns a list of ValueTuples of and SharedMechanismComponent on each mechanism + /// in the given body. + /// + /// The entity to check for the component on. + /// The body to check for mechanisms on. + /// The component to check for. + public IEnumerable<(T Comp, SharedMechanismComponent Mech)> GetComponentsOnMechanisms(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(mechanism.OwnerUid, out var comp)) - yield return comp; + yield return (comp, mechanism); } } + /// + /// Tries to get a list of ValueTuples of and SharedMechanismComponent on each mechanism + /// in the given body. + /// + /// The entity to check for the component on. + /// The list of components. + /// The body to check for mechanisms on. + /// The component to check for. + /// Whether any were found. public bool TryGetComponentsOnMechanisms(EntityUid uid, - [NotNullWhen(true)] out IEnumerable? 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)) { diff --git a/Content.Server/Body/Systems/LungSystem.cs b/Content.Server/Body/Systems/LungSystem.cs new file mode 100644 index 0000000000..9e77149c06 --- /dev/null +++ b/Content.Server/Body/Systems/LungSystem.cs @@ -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(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; + } + + /// + /// Tries to find an air mixture to inhale from, then inhales from it. + /// + 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(uid).Coordinates, true) is not { } tileAir) + { + return; + } + + TakeGasFrom(uid, frameTime, tileAir, lung); + } + + /// + /// Inhales directly from a given mixture. + /// + 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(); + } + + /// + /// Tries to find a gas mixture to exhale to, then pushes gas to it. + /// + 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(uid).Coordinates, true) is not { } tileAir) + { + return; + } + + PushGasTo(uid, tileAir, lung, mech); + } + + /// + /// Pushes gas from the lungs to a gas mixture. + /// + 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); + } +} diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 698cdcd144..6f3cc8023f 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -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().ToArray(); + var lungs = _bodySystem.GetComponentsOnMechanisms(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(uid).IsCritical()) { // Panic inhale - foreach (var lung in lungs) + foreach (var (lung, mech) in lungs) { - lung.Gasp(); + _lungSystem.Gasp(lung.OwnerUid, lung, mech); } } diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 5a02820804..8bd2252b25 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -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) diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 5a5080f88a..f82c744399 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -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 { @@ -100,7 +101,7 @@ namespace Content.Server.Nutrition.EntitySystems args.Handled = true; return; } - + args.Handled = TryForceFeed(uid, args.UserUid, args.TargetUid.Value); } @@ -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)); @@ -221,7 +223,7 @@ namespace Content.Server.Nutrition.EntitySystems { TryUseFood(uid, ev.UserUid, component); }; - + verb.Text = Loc.GetString("food-system-verb-eat"); verb.Priority = -1; ev.Verbs.Add(verb); @@ -275,7 +277,7 @@ namespace Content.Server.Nutrition.EntitySystems var target = EntityManager.GetEntity(targetUid); var edible = EntityManager.GetEntity(uid); _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{user} is forcing {target} to eat {edible}"); - + food.InUse = true; return true; } @@ -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)) diff --git a/Resources/Prototypes/Body/Mechanisms/human.yml b/Resources/Prototypes/Body/Mechanisms/human.yml index 15df4aa3e6..d43ced7573 100644 --- a/Resources/Prototypes/Body/Mechanisms/human.yml +++ b/Resources/Prototypes/Body/Mechanisms/human.yml @@ -99,8 +99,7 @@ - type: Mechanism size: 1 compatibility: Biological - behaviors: - - !type:LungBehavior {} + - type: Lung - type: entity id: OrganHumanHeart diff --git a/Resources/Prototypes/Body/Parts/animal.yml b/Resources/Prototypes/Body/Parts/animal.yml index 3cc1566c4b..e7cb951a9d 100644 --- a/Resources/Prototypes/Body/Parts/animal.yml +++ b/Resources/Prototypes/Body/Parts/animal.yml @@ -83,8 +83,7 @@ - type: Mechanism size: 1 compatibility: Biological - behaviors: - - !type:LungBehavior {} + - type: Lung - type: entity id: OrganAnimalStomach