From a74849c75176385b281da0ddca5517613870e2f6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:36:41 +0000 Subject: [PATCH 1/3] Initial plan From f1ff53f372920bd8ec67ba0742b7905a6d009f67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:40:07 +0000 Subject: [PATCH 2/3] Add respiratory system and Godot JSON export Co-authored-by: Uggeli <55832075+Uggeli@users.noreply.github.com> --- BodySim.Tests/BodyTests.cs | 32 ++++++++++++++++++++++++ BodySim/Body.cs | 31 +++++++++++++++++++++++ BodySim/BodySystemBase.cs | 2 ++ BodySim/RespiratorySystem.cs | 48 ++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 BodySim.Tests/BodyTests.cs create mode 100644 BodySim/RespiratorySystem.cs diff --git a/BodySim.Tests/BodyTests.cs b/BodySim.Tests/BodyTests.cs new file mode 100644 index 0000000..27bea66 --- /dev/null +++ b/BodySim.Tests/BodyTests.cs @@ -0,0 +1,32 @@ +using System.Text.Json; + +namespace BodySim.Tests; + +public class BodyTests +{ + [Fact] + public void Constructor_InitializesRespiratorySystem() + { + var body = new Body(); + + Assert.NotNull(body.GetSystem(BodySystemType.Respiratory)); + } + + [Fact] + public void ExportForGodotJson_ContainsSystemsAndResources() + { + var body = new Body(); + + using var document = JsonDocument.Parse(body.ExportForGodotJson()); + var root = document.RootElement; + + Assert.True(root.GetProperty("resources").TryGetProperty(BodyResourceType.Blood.ToString(), out _)); + + var systems = root.GetProperty("systems"); + Assert.True(systems.TryGetProperty(BodySystemType.Skeletal.ToString(), out _)); + Assert.True(systems.TryGetProperty(BodySystemType.Circulatory.ToString(), out _)); + Assert.True(systems.TryGetProperty(BodySystemType.Respiratory.ToString(), out var respiratory)); + Assert.True(respiratory.TryGetProperty(BodyPartType.Chest.ToString(), out var chest)); + Assert.True(chest.GetProperty("components").TryGetProperty(BodyComponentType.AirFlow.ToString(), out _)); + } +} diff --git a/BodySim/Body.cs b/BodySim/Body.cs index ace44e4..1b8bba3 100644 --- a/BodySim/Body.cs +++ b/BodySim/Body.cs @@ -1,3 +1,5 @@ +using System.Text.Json; + namespace BodySim; public class Body @@ -61,4 +63,33 @@ public void Clot(BodyPartType bodyPart) { return Systems.TryGetValue(systemType, out BodySystemBase? system) ? system : null; } + + public string ExportForGodotJson(bool indented = false) + { + var payload = new + { + resources = ResourcePool.GetResources().ToDictionary(r => r.Key.ToString(), r => r.Value), + systems = Systems.ToDictionary( + system => system.Key.ToString(), + system => system.Value.GetNodes().ToDictionary( + node => node.Key.ToString(), + node => new + { + status = node.Value.Status.ToString(), + components = node.Value.Components.ToDictionary( + component => component.ComponentType.ToString(), + component => new + { + current = component.Current, + max = component.Max, + regenRate = component.RegenRate, + }), + })) + }; + + return JsonSerializer.Serialize(payload, new JsonSerializerOptions + { + WriteIndented = indented + }); + } } diff --git a/BodySim/BodySystemBase.cs b/BodySim/BodySystemBase.cs index 3ec5a93..78f8392 100644 --- a/BodySim/BodySystemBase.cs +++ b/BodySim/BodySystemBase.cs @@ -37,6 +37,8 @@ public void SetNodeStatus(BodyPartType bodyPartType, SystemNodeStatus status) } return null; } + + public IReadOnlyDictionary GetNodes() => Statuses; public virtual void MetabolicUpdate() { diff --git a/BodySim/RespiratorySystem.cs b/BodySim/RespiratorySystem.cs new file mode 100644 index 0000000..476e584 --- /dev/null +++ b/BodySim/RespiratorySystem.cs @@ -0,0 +1,48 @@ +namespace BodySim; + +public class RespiratorySystem : BodySystemBase +{ + public RespiratorySystem(BodyResourcePool pool, EventHub eventHub) + : base(BodySystemType.Respiratory, pool, eventHub) + { + InitSystem(); + eventHub.RegisterListener(this); + eventHub.RegisterListener(this); + } + + public override void HandleMessage(IEvent evt) + { + switch (evt) + { + case SuffocateEvent suffocateEvent: + SetAirwayState(suffocateEvent.BodyPartType, blocked: true); + break; + case ClearAirwayEvent clearAirwayEvent: + SetAirwayState(clearAirwayEvent.BodyPartType, blocked: false); + break; + } + } + + public override void InitSystem() + { + foreach (BodyPartType partType in Enum.GetValues()) + { + Statuses[partType] = new RespiratoryNode(partType); + } + } + + void SetAirwayState(BodyPartType bodyPartType, bool blocked) + { + if (!Statuses.TryGetValue(bodyPartType, out var node)) return; + node.Status = blocked ? SystemNodeStatus.Disabled : SystemNodeStatus.Healthy; + node.GetComponent(BodyComponentType.AirFlow)!.Current = blocked ? 0 : 100; + } +} + +public class RespiratoryNode(BodyPartType bodyPartType) : BodyPartNodeBase(bodyPartType, [ + new BodyComponentBase(100, 100, 0.1f, BodyComponentType.Health), + new BodyComponentBase(100, 100, 0f, BodyComponentType.LungCapacity), + new BodyComponentBase(100, 100, 0f, BodyComponentType.AirFlow), +]) +{ +} From 93f952fb1e400d7fc71a49ee21caee71c3f3cc92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:42:13 +0000 Subject: [PATCH 3/3] Polish Godot export and respiratory safety Co-authored-by: Uggeli <55832075+Uggeli@users.noreply.github.com> --- BodySim/Body.cs | 12 ++++++------ BodySim/RespiratorySystem.cs | 26 +++++++++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/BodySim/Body.cs b/BodySim/Body.cs index 1b8bba3..a58114d 100644 --- a/BodySim/Body.cs +++ b/BodySim/Body.cs @@ -68,7 +68,7 @@ public string ExportForGodotJson(bool indented = false) { var payload = new { - resources = ResourcePool.GetResources().ToDictionary(r => r.Key.ToString(), r => r.Value), + resources = ResourcePool.GetResources().ToDictionary(resource => resource.Key.ToString(), resource => resource.Value), systems = Systems.ToDictionary( system => system.Key.ToString(), system => system.Value.GetNodes().ToDictionary( @@ -77,12 +77,12 @@ public string ExportForGodotJson(bool indented = false) { status = node.Value.Status.ToString(), components = node.Value.Components.ToDictionary( - component => component.ComponentType.ToString(), - component => new + nodeComponent => nodeComponent.ComponentType.ToString(), + nodeComponent => new { - current = component.Current, - max = component.Max, - regenRate = component.RegenRate, + current = nodeComponent.Current, + max = nodeComponent.Max, + regenRate = nodeComponent.RegenRate, }), })) }; diff --git a/BodySim/RespiratorySystem.cs b/BodySim/RespiratorySystem.cs index 476e584..a85fd4a 100644 --- a/BodySim/RespiratorySystem.cs +++ b/BodySim/RespiratorySystem.cs @@ -2,6 +2,8 @@ namespace BodySim; public class RespiratorySystem : BodySystemBase { + private const float NormalAirFlow = 100f; + public RespiratorySystem(BodyResourcePool pool, EventHub eventHub) : base(BodySystemType.Respiratory, pool, eventHub) { @@ -31,18 +33,28 @@ public override void InitSystem() } } - void SetAirwayState(BodyPartType bodyPartType, bool blocked) + private void SetAirwayState(BodyPartType bodyPartType, bool blocked) { if (!Statuses.TryGetValue(bodyPartType, out var node)) return; node.Status = blocked ? SystemNodeStatus.Disabled : SystemNodeStatus.Healthy; - node.GetComponent(BodyComponentType.AirFlow)!.Current = blocked ? 0 : 100; + var airflow = node.GetComponent(BodyComponentType.AirFlow); + if (airflow != null) + { + airflow.Current = blocked ? 0 : NormalAirFlow; + } } } -public class RespiratoryNode(BodyPartType bodyPartType) : BodyPartNodeBase(bodyPartType, [ - new BodyComponentBase(100, 100, 0.1f, BodyComponentType.Health), - new BodyComponentBase(100, 100, 0f, BodyComponentType.LungCapacity), - new BodyComponentBase(100, 100, 0f, BodyComponentType.AirFlow), -]) +public class RespiratoryNode(BodyPartType bodyPartType) : BodyPartNodeBase(bodyPartType, CreateComponents()) { + private const float DefaultComponentValue = 100f; + private const float HealthRegenRate = 0.1f; + private const float NoRegenRate = 0f; + + private static List CreateComponents() => + [ + new BodyComponentBase(DefaultComponentValue, DefaultComponentValue, HealthRegenRate, BodyComponentType.Health), + new BodyComponentBase(DefaultComponentValue, DefaultComponentValue, NoRegenRate, BodyComponentType.LungCapacity), + new BodyComponentBase(DefaultComponentValue, DefaultComponentValue, NoRegenRate, BodyComponentType.AirFlow), + ]; }