From 65d77756651cfcb384670fa03766594dcf4bb49c Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 12 Sep 2020 22:52:50 +0200 Subject: [PATCH] Fix breathing once and for all (#1996) * Fix breathing * WIP changes because I don't trust git stash after 2 weeks * My imports * Add gasping, adjust breathing values and fix test * Make the gasp message appear to others * Add PopupMessageEveryone extension * Change used percentage to use a single number instead * Remove unnecessary logging * Fix air consistency test * Add test map to SkippedMaps array --- Content.IntegrationTests/Tests/LungTest.cs | 151 +++++++++++++ .../Body/Circulatory/BloodstreamComponent.cs | 22 +- .../Body/Respiratory/LungComponent.cs | 121 ++++++++--- .../Metabolism/MetabolismComponent.cs | 106 +++++++--- Content.Shared/Atmos/Atmospherics.cs | 11 + .../Maps/Test/Breathing/3by3-20oxy-80nit.yml | 198 ++++++++++++++++++ .../Entities/Mobs/Species/human.yml | 6 +- 7 files changed, 543 insertions(+), 72 deletions(-) create mode 100644 Content.IntegrationTests/Tests/LungTest.cs create mode 100644 Resources/Maps/Test/Breathing/3by3-20oxy-80nit.yml diff --git a/Content.IntegrationTests/Tests/LungTest.cs b/Content.IntegrationTests/Tests/LungTest.cs new file mode 100644 index 0000000000..58c9b78079 --- /dev/null +++ b/Content.IntegrationTests/Tests/LungTest.cs @@ -0,0 +1,151 @@ +using System.Linq; +using System.Threading.Tasks; +using Content.Server.Atmos; +using Content.Server.GameObjects.Components.Body.Circulatory; +using Content.Server.GameObjects.Components.Body.Respiratory; +using Content.Server.GameObjects.Components.Metabolism; +using Content.Shared.Atmos; +using NUnit.Framework; +using Robust.Server.Interfaces.Maps; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; + +namespace Content.IntegrationTests.Tests +{ + [TestFixture] + [TestOf(typeof(LungComponent))] + public class LungTest : ContentIntegrationTest + { + [Test] + public async Task AirConsistencyTest() + { + var server = StartServerDummyTicker(); + + server.Assert(() => + { + var mapManager = IoCManager.Resolve(); + + mapManager.CreateNewMapEntity(MapId.Nullspace); + + var entityManager = IoCManager.Resolve(); + + var human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace); + + Assert.True(human.TryGetComponent(out LungComponent lung)); + Assert.True(human.TryGetComponent(out BloodstreamComponent bloodstream)); + + var gas = new GasMixture(1); + + var originalOxygen = 2; + var originalNitrogen = 8; + var breathedPercentage = Atmospherics.BreathPercentage; + + gas.AdjustMoles(Gas.Oxygen, originalOxygen); + gas.AdjustMoles(Gas.Nitrogen, originalNitrogen); + + lung.Inhale(1, gas); + + var lungOxygen = originalOxygen * breathedPercentage; + var lungNitrogen = originalNitrogen * breathedPercentage; + + Assert.That(bloodstream.Air.GetMoles(Gas.Oxygen), Is.EqualTo(lungOxygen)); + Assert.That(bloodstream.Air.GetMoles(Gas.Nitrogen), Is.EqualTo(lungNitrogen)); + + var mixtureOxygen = originalOxygen - lungOxygen; + var mixtureNitrogen = originalNitrogen - lungNitrogen; + + Assert.That(gas.GetMoles(Gas.Oxygen), Is.EqualTo(mixtureOxygen)); + Assert.That(gas.GetMoles(Gas.Nitrogen), Is.EqualTo(mixtureNitrogen)); + + var lungOxygenBeforeExhale = lung.Air.GetMoles(Gas.Oxygen); + var lungNitrogenBeforeExhale = lung.Air.GetMoles(Gas.Nitrogen); + + // Empty after it transfer to the bloodstream + Assert.Zero(lungOxygenBeforeExhale); + Assert.Zero(lungNitrogenBeforeExhale); + + lung.Exhale(1, gas); + + var lungOxygenAfterExhale = lung.Air.GetMoles(Gas.Oxygen); + var exhaledOxygen = lungOxygenBeforeExhale - lungOxygenAfterExhale; + + // Not completely empty + Assert.Positive(lung.Air.Gases.Sum()); + + // Retains needed gas + Assert.Positive(bloodstream.Air.GetMoles(Gas.Oxygen)); + + // Expels toxins + Assert.Zero(bloodstream.Air.GetMoles(Gas.Nitrogen)); + + mixtureOxygen += exhaledOxygen; + + var finalTotalOxygen = gas.GetMoles(Gas.Oxygen) + + bloodstream.Air.GetMoles(Gas.Oxygen) + + lung.Air.GetMoles(Gas.Oxygen); + + // No ticks were run, metabolism doesn't run and so no oxygen is used up + Assert.That(finalTotalOxygen, Is.EqualTo(originalOxygen)); + Assert.That(gas.GetMoles(Gas.Oxygen), Is.EqualTo(mixtureOxygen).Within(0.000001f)); + + var finalTotalNitrogen = gas.GetMoles(Gas.Nitrogen) + + bloodstream.Air.GetMoles(Gas.Nitrogen) + + lung.Air.GetMoles(Gas.Nitrogen); + + // Nitrogen stays constant + Assert.That(finalTotalNitrogen, Is.EqualTo(originalNitrogen).Within(0.000001f)); + }); + + await server.WaitIdleAsync(); + } + + [Test] + public async Task NoSuffocationTest() + { + var server = StartServerDummyTicker(); + await server.WaitIdleAsync(); + + var mapLoader = server.ResolveDependency(); + var mapManager = server.ResolveDependency(); + var entityManager = server.ResolveDependency(); + + MapId mapId; + IMapGrid grid = null; + LungComponent lung = null; + MetabolismComponent metabolism = null; + IEntity human = null; + + var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml"; + + await server.WaitPost(() => + { + mapId = mapManager.CreateMap(); + grid = mapLoader.LoadBlueprint(mapId, testMapName); + }); + + Assert.NotNull(grid, $"Test blueprint {testMapName} not found."); + + await server.WaitAssertion(() => + { + var center = new Vector2(0.5f, -1.5f); + var coordinates = new EntityCoordinates(grid.GridEntityId, center); + human = entityManager.SpawnEntity("HumanMob_Content", coordinates); + + Assert.True(human.TryGetComponent(out lung)); + Assert.True(human.TryGetComponent(out metabolism)); + Assert.False(metabolism.Suffocating); + }); + + for (var tick = 0; tick < 600; tick++) + { + await server.WaitRunTicks(tick); + Assert.False(metabolism.Suffocating, $"Entity {human.Name} is suffocating on tick {tick}"); + } + + await server.WaitIdleAsync(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Body/Circulatory/BloodstreamComponent.cs b/Content.Server/GameObjects/Components/Body/Circulatory/BloodstreamComponent.cs index 27f2b0c21e..7f1ff43101 100644 --- a/Content.Server/GameObjects/Components/Body/Circulatory/BloodstreamComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Circulatory/BloodstreamComponent.cs @@ -1,7 +1,9 @@ +using System.Linq; using Content.Server.Atmos; using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Metabolism; using Content.Server.Interfaces; +using Content.Shared.Atmos; using Content.Shared.Chemistry; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; @@ -45,7 +47,7 @@ namespace Content.Server.GameObjects.Components.Body.Circulatory { base.ExposeData(serializer); - Air = new GasMixture(6); + Air = new GasMixture(6) {Temperature = Atmospherics.NormalBodyTemperature}; serializer.DataField(ref _initialMaxVolume, "maxVolume", ReagentUnit.New(250)); } @@ -68,17 +70,29 @@ namespace Content.Server.GameObjects.Components.Body.Circulatory return true; } - public void PumpToxins(GasMixture into, float pressure) + public void PumpToxins(GasMixture to) { if (!Owner.TryGetComponent(out MetabolismComponent metabolism)) { - Air.PumpGasTo(into, pressure); + to.Merge(Air); + Air.Clear(); return; } var toxins = metabolism.Clean(this); + var toOld = to.Gases.ToArray(); + + to.Merge(toxins); + + for (var i = 0; i < toOld.Length; i++) + { + var newAmount = to.GetMoles(i); + var oldAmount = toOld[i]; + var delta = newAmount - oldAmount; + + toxins.AdjustMoles(i, -delta); + } - toxins.PumpGasTo(into, pressure); Air.Merge(toxins); } } diff --git a/Content.Server/GameObjects/Components/Body/Respiratory/LungComponent.cs b/Content.Server/GameObjects/Components/Body/Respiratory/LungComponent.cs index e8392c7948..af1e520c07 100644 --- a/Content.Server/GameObjects/Components/Body/Respiratory/LungComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Respiratory/LungComponent.cs @@ -1,8 +1,11 @@ using System; +using System.Linq; using Content.Server.Atmos; using Content.Server.GameObjects.Components.Body.Circulatory; using Content.Server.Interfaces; +using Content.Server.Utility; using Content.Shared.Atmos; +using Content.Shared.Interfaces; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -16,27 +19,31 @@ namespace Content.Server.GameObjects.Components.Body.Respiratory private float _accumulatedFrameTime; - /// - /// The pressure that this lung exerts on the air around it - /// - [ViewVariables(VVAccess.ReadWrite)] private float Pressure { get; set; } - [ViewVariables] public GasMixture Air { get; 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(); + Air = new GasMixture {Temperature = Atmospherics.NormalBodyTemperature}; serializer.DataReadWriteFunction( "volume", 6, vol => Air.Volume = vol, () => Air.Volume); - serializer.DataField(this, l => l.Pressure, "pressure", 100); + + serializer.DataReadWriteFunction( + "temperature", + Atmospherics.NormalBodyTemperature, + temp => Air.Temperature = temp, + () => Air.Temperature); + + serializer.DataField(this, l => l.CycleDelay, "cycleDelay", 2); } public void Update(float frameTime) @@ -54,7 +61,9 @@ namespace Content.Server.GameObjects.Components.Body.Respiratory }; var absoluteTime = Math.Abs(_accumulatedFrameTime); - if (absoluteTime < 2) + var delay = CycleDelay; + + if (absoluteTime < delay) { return; } @@ -73,50 +82,100 @@ namespace Content.Server.GameObjects.Components.Body.Respiratory throw new ArgumentOutOfRangeException(); } - _accumulatedFrameTime = absoluteTime - 2; + _accumulatedFrameTime = absoluteTime - delay; + } + + public void Transfer(GasMixture from, GasMixture to, float ratio) + { + var removed = from.RemoveRatio(ratio); + var toOld = to.Gases.ToArray(); + + to.Merge(removed); + + for (var gas = 0; gas < Atmospherics.TotalNumberOfGases; gas++) + { + var newAmount = to.GetMoles(gas); + var oldAmount = toOld[gas]; + var delta = newAmount - oldAmount; + + removed.AdjustMoles(gas, -delta); + } + + from.Merge(removed); + } + + public void ToBloodstream(GasMixture mixture) + { + if (!Owner.TryGetComponent(out BloodstreamComponent bloodstream)) + { + return; + } + + var to = bloodstream.Air; + + to.Merge(mixture); + mixture.Clear(); } public void Inhale(float frameTime) { - if (!Owner.TryGetComponent(out BloodstreamComponent bloodstream)) - { - return; - } - if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir)) { return; } - var amount = Atmospherics.BreathPercentage * frameTime; - var volumeRatio = amount / tileAir.Volume; - var temp = tileAir.RemoveRatio(volumeRatio); + Inhale(frameTime, tileAir); + } - temp.PumpGasTo(Air, Pressure); - Air.PumpGasTo(bloodstream.Air, Pressure); - tileAir.Merge(temp); + public void Inhale(float frameTime, GasMixture from) + { + var ratio = Atmospherics.BreathPercentage * frameTime; + + Transfer(from, Air, ratio); + ToBloodstream(Air); } public void Exhale(float frameTime) { - if (!Owner.TryGetComponent(out BloodstreamComponent bloodstream)) - { - return; - } - if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir)) { return; } - bloodstream.PumpToxins(Air, Pressure); + Exhale(frameTime, tileAir); + } - var amount = Atmospherics.BreathPercentage * frameTime; - var volumeRatio = amount / tileAir.Volume; - var temp = tileAir.RemoveRatio(volumeRatio); + 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 (!Owner.TryGetComponent(out BloodstreamComponent bloodstream)) + { + return; + } - temp.PumpGasTo(tileAir, Pressure); - Air.Merge(temp); + bloodstream.PumpToxins(Air); + + var lungRemoved = Air.RemoveRatio(0.5f); + var toOld = to.Gases.ToArray(); + + to.Merge(lungRemoved); + + for (var gas = 0; gas < Atmospherics.TotalNumberOfGases; gas++) + { + var newAmount = to.GetMoles(gas); + var oldAmount = toOld[gas]; + var delta = newAmount - oldAmount; + + lungRemoved.AdjustMoles(gas, -delta); + } + + Air.Merge(lungRemoved); + } + + public void Gasp() + { + Owner.PopupMessageEveryone("Gasp"); + Inhale(CycleDelay); } } diff --git a/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs b/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs index 45fb5e2e11..655847376a 100644 --- a/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs +++ b/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs @@ -3,16 +3,16 @@ using System.Collections.Generic; using System.Linq; using Content.Server.Atmos; using Content.Server.GameObjects.Components.Body.Circulatory; +using Content.Server.GameObjects.Components.Body.Respiratory; using Content.Server.GameObjects.Components.Temperature; -using Content.Server.GameObjects.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Chemistry; +using Content.Shared.Damage; using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; using Content.Shared.Interfaces.Chemistry; using Robust.Shared.GameObjects; -using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; @@ -28,7 +28,6 @@ namespace Content.Server.GameObjects.Components.Metabolism [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; - public override string Name => "Metabolism"; private float _accumulatedFrameTime; @@ -85,7 +84,7 @@ namespace Content.Server.GameObjects.Components.Metabolism /// public float ThermalRegulationTemperatureThreshold { get; private set; } - [ViewVariables] public bool Suffocating => SuffocatingPercentage() > 0; + [ViewVariables] public bool Suffocating { get; private set; } public override void ExposeData(ObjectSerializer serializer) { @@ -156,12 +155,16 @@ namespace Content.Server.GameObjects.Components.Metabolism private float GasProducedMultiplier(Gas gas, float usedAverage) { - if (!NeedsGases.TryGetValue(gas, out var needs) || - !ProducesGases.TryGetValue(gas, out var produces)) + if (!ProducesGases.TryGetValue(gas, out var produces)) { return 0; } + if (!NeedsGases.TryGetValue(gas, out var needs)) + { + needs = 1; + } + return needs * produces * usedAverage; } @@ -177,31 +180,44 @@ namespace Content.Server.GameObjects.Components.Metabolism return; } - var usedPercentages = new float[Atmospherics.TotalNumberOfGases]; var needs = NeedsAndDeficit(frameTime); + var used = 0f; foreach (var (gas, amountNeeded) in needs) { var bloodstreamAmount = bloodstream.Air.GetMoles(gas); var deficit = 0f; - if (bloodstreamAmount >= amountNeeded) + if (bloodstreamAmount < amountNeeded) { - bloodstream.Air.AdjustMoles(gas, -amountNeeded); + // Panic inhale + if (Owner.TryGetComponent(out LungComponent lung)) + { + lung.Gasp(); + bloodstreamAmount = bloodstream.Air.GetMoles(gas); + } + + deficit = Math.Max(0, amountNeeded - bloodstreamAmount); + + if (deficit > 0) + { + bloodstream.Air.SetMoles(gas, 0); + } + else + { + bloodstream.Air.AdjustMoles(gas, -amountNeeded); + } } else { - deficit = amountNeeded - bloodstreamAmount; - bloodstream.Air.SetMoles(gas, 0); + bloodstream.Air.AdjustMoles(gas, -amountNeeded); } DeficitGases[gas] = deficit; - var used = amountNeeded - deficit; - usedPercentages[(int) gas] = used / amountNeeded; + used += (amountNeeded - deficit) / amountNeeded; } - var usedAverage = usedPercentages.Average(); - var produced = GasProduced(usedAverage); + var produced = GasProduced(used / needs.Count); foreach (var (gas, amountProduced) in produced) { @@ -280,7 +296,6 @@ namespace Content.Server.GameObjects.Components.Metabolism } } - /// /// Loops through each reagent in _internalSolution, /// and calls for each of them. @@ -338,42 +353,65 @@ namespace Content.Server.GameObjects.Components.Metabolism return; } + ProcessGases(_accumulatedFrameTime); + ProcessNutrients(_accumulatedFrameTime); + ProcessThermalRegulation(_accumulatedFrameTime); + _accumulatedFrameTime -= 1; - ProcessGases(frameTime); - ProcessNutrients(frameTime); - ProcessThermalRegulation(frameTime); - - if (Suffocating) + if (SuffocatingPercentage() > 0) { - // damageable.ChangeDamage(DamageClass.Airloss, _suffocationDamage, false); + TakeSuffocationDamage(); + return; } + + StopSuffocation(); } - public void Transfer(BloodstreamComponent @from, GasMixture to, Gas gas, float pressure) + private void TakeSuffocationDamage() { - var transfer = new GasMixture(); - var molesInBlood = @from.Air.GetMoles(gas); + Suffocating = true; - transfer.SetMoles(gas, molesInBlood); - transfer.ReleaseGasTo(to, pressure); + if (!Owner.TryGetComponent(out IDamageableComponent damageable)) + { + return; + } - @from.Air.Merge(transfer); + damageable.ChangeDamage(DamageClass.Airloss, _suffocationDamage, false); } - public GasMixture Clean(BloodstreamComponent bloodstream, float pressure = 100) + private void StopSuffocation() { - var gasMixture = new GasMixture(bloodstream.Air.Volume); + Suffocating = false; + } + + public GasMixture Clean(BloodstreamComponent bloodstream) + { + var gasMixture = new GasMixture(bloodstream.Air.Volume) + { + Temperature = bloodstream.Air.Temperature + }; for (Gas gas = 0; gas < (Gas) Atmospherics.TotalNumberOfGases; gas++) { - if (NeedsGases.TryGetValue(gas, out var needed) && - bloodstream.Air.GetMoles(gas) < needed * 1.5f) + float amount; + var molesInBlood = bloodstream.Air.GetMoles(gas); + + if (!NeedsGases.TryGetValue(gas, out var needed)) { - continue; + amount = molesInBlood; + } + else + { + var overflowThreshold = needed * 1.5f; + + amount = molesInBlood > overflowThreshold + ? molesInBlood - overflowThreshold + : 0; } - Transfer(bloodstream, gasMixture, gas, pressure); + gasMixture.AdjustMoles(gas, amount); + bloodstream.Air.AdjustMoles(gas, -amount); } return gasMixture; diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs index f9391a040c..4cf118dd2a 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -217,6 +217,17 @@ /// See on the server. /// public const int Directions = 4; + + /// + /// The normal body temperature in degrees Celsius. + /// + public const float NormalBodyTemperature = 37f; + + public const float HumanNeededOxygen = MolesCellStandard * BreathPercentage * 0.16f; + + public const float HumanProducedOxygen = HumanNeededOxygen * 0.75f; + + public const float HumanProducedCarbonDioxide = HumanNeededOxygen * 0.25f; } /// diff --git a/Resources/Maps/Test/Breathing/3by3-20oxy-80nit.yml b/Resources/Maps/Test/Breathing/3by3-20oxy-80nit.yml new file mode 100644 index 0000000000..84685e41e3 --- /dev/null +++ b/Resources/Maps/Test/Breathing/3by3-20oxy-80nit.yml @@ -0,0 +1,198 @@ +meta: + format: 2 + name: DemoStation + author: Space-Wizards + postmapinit: true +tilemap: + 0: space + 1: floor_asteroid_coarse_sand0 + 2: floor_asteroid_coarse_sand1 + 3: floor_asteroid_coarse_sand2 + 4: floor_asteroid_coarse_sand_dug + 5: floor_asteroid_sand + 6: floor_asteroid_tile + 7: floor_carpet + 8: floor_dark + 9: floor_elevator_shaft + 10: floor_freezer + 11: floor_gold + 12: floor_green_circuit + 13: floor_hull_center0 + 14: floor_hull_center1 + 15: floor_hull_center10 + 16: floor_hull_center11 + 17: floor_hull_center12 + 18: floor_hull_center13 + 19: floor_hull_center14 + 20: floor_hull_center15 + 21: floor_hull_center16 + 22: floor_hull_center17 + 23: floor_hull_center18 + 24: floor_hull_center19 + 25: floor_hull_center2 + 26: floor_hull_center20 + 27: floor_hull_center21 + 28: floor_hull_center22 + 29: floor_hull_center23 + 30: floor_hull_center24 + 31: floor_hull_center25 + 32: floor_hull_center26 + 33: floor_hull_center27 + 34: floor_hull_center28 + 35: floor_hull_center29 + 36: floor_hull_center3 + 37: floor_hull_center30 + 38: floor_hull_center31 + 39: floor_hull_center32 + 40: floor_hull_center33 + 41: floor_hull_center34 + 42: floor_hull_center35 + 43: floor_hull_center4 + 44: floor_hull_center5 + 45: floor_hull_center6 + 46: floor_hull_center7 + 47: floor_hull_center8 + 48: floor_hull_center9 + 49: floor_hydro + 50: floor_lino + 51: floor_mono + 52: floor_reinforced + 53: floor_rock_vault + 54: floor_showroom + 55: floor_snow + 56: floor_steel + 57: floor_steel_dirty + 58: floor_techmaint + 59: floor_white + 60: floor_wood + 61: plating + 62: underplating +grids: +- settings: + chunksize: 16 + tilesize: 1 + snapsize: 1 + chunks: + - ind: "-1,-1" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAA== + - ind: "0,-1" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + - ind: "-1,0" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + - ind: "0,0" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== +entities: +- uid: 0 + components: + - parent: null + type: Transform + - index: 0 + type: MapGrid + - shapes: + - !type:PhysShapeGrid + grid: 0 + type: Collidable + - uniqueMixes: + - volume: 2500 + temperatureArchived: 293.15 + moles: + - 20 + - 80 + - 0 + - 0 + - 0 + - 0 + molesArchived: + - 20 + - 80 + - 0 + - 0 + - 0 + - 0 + temperature: 293.15 + tiles: + ? X: 0 + Y: -2 + : 0 + type: GridAtmosphere +- uid: 1 + type: reinforced_wall + components: + - parent: 0 + pos: -0.5,-0.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +- uid: 2 + type: reinforced_wall + components: + - parent: 0 + pos: -0.5,-1.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +- uid: 3 + type: reinforced_wall + components: + - parent: 0 + pos: -0.5,-2.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +- uid: 4 + type: reinforced_wall + components: + - parent: 0 + pos: 0.5,-2.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +- uid: 5 + type: reinforced_wall + components: + - parent: 0 + pos: 1.5,-2.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +- uid: 6 + type: reinforced_wall + components: + - parent: 0 + pos: 1.5,-1.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +- uid: 7 + type: reinforced_wall + components: + - parent: 0 + pos: 1.5,-0.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +- uid: 8 + type: reinforced_wall + components: + - parent: 0 + pos: 0.5,-0.5 + rot: -1.5707963267948966 rad + type: Transform + - flags: + - None + type: Destructible +... diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 6e369a458a..a10b7404e9 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -149,10 +149,10 @@ normalBodyTemperature: 310.15 thermalRegulationTemperatureThreshold: 25 needsGases: - Oxygen: 0.006365740 + Oxygen: 0.00332569564 producesGases: - Oxygen: 0.004774305 - CarbonDioxide: 0.001591435 + Oxygen: 0.00249427173 + CarbonDioxide: 0.00083142391 - type: MobStateManager - type: HeatResistance - type: Appearance