Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions BodySim.Tests/BodyTests.cs
Original file line number Diff line number Diff line change
@@ -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 _));
}
}
31 changes: 31 additions & 0 deletions BodySim/Body.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Text.Json;

namespace BodySim;

public class Body
Expand Down Expand Up @@ -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(resource => resource.Key.ToString(), resource => resource.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(
nodeComponent => nodeComponent.ComponentType.ToString(),
nodeComponent => new
{
current = nodeComponent.Current,
max = nodeComponent.Max,
regenRate = nodeComponent.RegenRate,
}),
}))
};

return JsonSerializer.Serialize(payload, new JsonSerializerOptions
{
WriteIndented = indented
});
}
}
2 changes: 2 additions & 0 deletions BodySim/BodySystemBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public void SetNodeStatus(BodyPartType bodyPartType, SystemNodeStatus status)
}
return null;
}

public IReadOnlyDictionary<BodyPartType, BodyPartNodeBase> GetNodes() => Statuses;

public virtual void MetabolicUpdate()
{
Expand Down
60 changes: 60 additions & 0 deletions BodySim/RespiratorySystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace BodySim;

public class RespiratorySystem : BodySystemBase
{
private const float NormalAirFlow = 100f;

public RespiratorySystem(BodyResourcePool pool, EventHub eventHub)
: base(BodySystemType.Respiratory, pool, eventHub)
{
InitSystem();
eventHub.RegisterListener<SuffocateEvent>(this);
eventHub.RegisterListener<ClearAirwayEvent>(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<BodyPartType>())
{
Statuses[partType] = new RespiratoryNode(partType);
}
}

private void SetAirwayState(BodyPartType bodyPartType, bool blocked)
{
if (!Statuses.TryGetValue(bodyPartType, out var node)) return;
node.Status = blocked ? SystemNodeStatus.Disabled : SystemNodeStatus.Healthy;
var airflow = node.GetComponent(BodyComponentType.AirFlow);
if (airflow != null)
{
airflow.Current = blocked ? 0 : NormalAirFlow;
}
}
}

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<BodyComponentBase> CreateComponents() =>
[
new BodyComponentBase(DefaultComponentValue, DefaultComponentValue, HealthRegenRate, BodyComponentType.Health),
new BodyComponentBase(DefaultComponentValue, DefaultComponentValue, NoRegenRate, BodyComponentType.LungCapacity),
new BodyComponentBase(DefaultComponentValue, DefaultComponentValue, NoRegenRate, BodyComponentType.AirFlow),
];
}