Files
FoodFactory/src/VoxelGrid/VoxelGridNode.cs

250 lines
6.5 KiB
C#

namespace FoodFactory.Voxel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Arch.Core;
using Arch.LowLevel;
using Arch.System;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder;
using Chickensoft.Serialization;
using Chickensoft.Serialization.Godot;
using FoodFactory;
using FoodFactory.Blueprints;
using FoodFactory.Conveyors;
using FoodFactory.Math;
using FoodFactory.Recipes;
using Godot;
using SharpYaml;
public class BacterialSystem : BaseSystem<World, float>
{
private QueryDescription _desc = new QueryDescription().WithAll<Bacteria, Temperature>();
public static Resources<List<BacteriaData>> Resources = new();
public BacterialSystem(World world) : base(world)
{
}
public override void Update(in float t)
{
var delta = t;
World.Query(in _desc, (Entity entity, ref Bacteria bacteria, ref Temperature temperature) =>
{
var handle = bacteria.Data;
var data = Resources.Get(in handle);
for (int i = 0; i < data.Count; i++)
{
data[i] = data[i] with { Count = BacteriaModel.UpdatePopulation(data[i].Count, temperature.Celsius, delta) };
// GD.Print(data[i].Count);
if (data[i].Count <= 0.5)
{
data.RemoveAt(i);
i--;
}
}
});
}
}
//Will Liklely be the game instead of a node like this
[Meta(typeof(IAutoNode))]// [Tool]
public partial class VoxelGridNode : Node3D,
IProvide<IVoxelGridRegistry>,
IProvide<IItemRenderer>,
IProvide<IRecipes>,
IProvide<IBlueprintManger>,
IProvide<World>,
IProvide<IFoodFactoryApi>//,
// IProvide<ISaveChunk<GameData>>
{
public override void _Notification(int what) => this.Notify(what);
private IFoodFactoryApi _api = default!;
IFoodFactoryApi IProvide<IFoodFactoryApi>.Value() => _api;
private World _world = default!;
World IProvide<World>.Value() => _world;
private IVoxelGridRegistry _voxelGridRegistry = default!;
IVoxelGridRegistry IProvide<IVoxelGridRegistry>.Value() => _voxelGridRegistry;
private IBlueprintManger _blueprintManger = default!;
IBlueprintManger IProvide<IBlueprintManger>.Value() => _blueprintManger;
private Recipes _recipes = default!;
IRecipes IProvide<IRecipes>.Value() => _recipes;
private IItemRenderer _itemRenderer = default!;
IItemRenderer IProvide<IItemRenderer>.Value() => _itemRenderer;
// public ISaveChunk<GameData> Value() => SaveFile.Root;
public static string SavePath => $"{OS.GetUserDataDir()}/SaveFile.json";
JsonSerializerOptions _options;
public void Setup()
{
GodotSerialization.Setup();
_options = new JsonSerializerOptions
{
WriteIndented = true,
TypeInfoResolver = new SerializableTypeResolver(),
Converters = { new SerializableTypeConverter() }
};
var test = "gg";
GD.Print(SavePath);
// SaveFile = new SaveFile<GameData>(
// new SaveChunk<GameData>(
// onSave: (chunk) =>
// {
// var gameData = new GameData()
// {
// World = _world,
// // Equipments = chunk.GetChunkSaveData<EquipmentsData>()
// };
// return gameData;
// },
// onLoad: (chunk, data) =>
// {
// World.Destroy(_world);
// _world = data.World;
// // chunk.LoadChunkSaveData(data.Equipments);
// }
// ),
// onSave: async data =>
// {
// var yaml = JsonSerializer.Serialize(data, _options);
// GD.Print(SavePath);
// await File.WriteAllTextAsync(SavePath, yaml);
// },
// onLoad: async () =>
// {
// if (!File.Exists(SavePath))
// {
// GD.PushWarning("Save does nto exist");
// return null;
// }
// var data = JsonSerializer.Deserialize<GameData>(await File.ReadAllTextAsync(SavePath), _options);
// return data;
// }
// );
}
// public ISaveFile<GameData> SaveFile { get; set; } = default!;
Group<float> _systems;
public override void _Ready()
{
// GD.Print();
base._Ready();
_voxelGridRegistry = new VoxelRegistry();
_itemRenderer = new ItemRenderBuffered();
_blueprintManger = new BlueprintManger();
_recipes = new Recipes();
_world = World.Create();
_systems = new Group<float>("Items", new BacterialSystem(_world));
var tickManger = new TickManger();
AddChild(_itemRenderer as Node);
var delta = .2f;
Timer timer = new Timer() { WaitTime = delta, Autostart = true };//TEST
AddChild(timer);//TEST
// timer.Timeout += _itemRenderer.Tick;//TEST
_api = new FoodFactoryApi()
{
BlueprintManger = _blueprintManger,
Recipes = _recipes,
ItemRenderer = _itemRenderer,
TickManger = tickManger,
GridRegistry = _voxelGridRegistry
};
int tick = 0;
timer.Timeout += () =>
{
_systems.BeforeUpdate(delta);
_systems.Update(delta);
_systems.AfterUpdate(delta);
tickManger.BroadCast(new(tick, delta));
tick++;
};
_systems.Initialize();
this.Provide();
}
public override void _Input(InputEvent @event)
{
if (@event.IsActionPressed("ui_up"))
{
foreach (var item in GetChildren())
{
if (item is ConveyorBeltStraight conveyor)
{
var node = conveyor.ConveyorLogic;
node.IsReversed = !node.IsReversed;
}
}
}
if (@event.IsActionPressed("ui_right"))
{
// SaveFile.Save();
}
if (@event.IsActionPressed("ui_left"))
{
// SaveFile.Load();
}
}
public override void _ExitTree()
{
base._ExitTree();
_systems.Dispose();
_world.Dispose();
}
}
public static class BacteriaModel
{
// population = current bacteria count
// temperature = degrees Celsius
// deltaTime = elapsed time (hours)
public static double UpdatePopulation(
double population,
double temperature,
double deltaTime)
{
double rate;
// Very cold -> slight die-off
if (temperature <= 5)
{
rate = -0.1;
}
// Growth zone
else if (temperature < 35)
{
rate = 0.06 * (temperature - 5);
}
// Hot but still survivable
else if (temperature < 60)
{
rate = 1.8 - 0.08 * (temperature - 35);
}
// Dangerous heat -> bacteria die
else
{
rate = -0.8;
}
// Population update
double newPopulation =
population + (deltaTime * population * rate);
// Prevent negative bacteria counts
return Math.Max(0, newPopulation);
}
}