Spite up project, added BeltData, some asoited compoents, remvoed unused usings, and removed uneeded comment code.

This commit is contained in:
2026-05-10 12:33:20 -04:00
parent d620189e4e
commit 4db51be374
53 changed files with 1892 additions and 608 deletions

View File

@@ -37,33 +37,36 @@
<ItemGroup>
<!-- Production dependencies go here! -->
<PackageReference Include="Arch" Version="2.1.0" />
<PackageReference Include="Arch-Events" Version="2.1.0" />
<PackageReference Include="Arch.LowLevel" Version="1.1.5" />
<PackageReference Include="Arch.System" Version="1.1.0" />
<PackageReference Include="Chickensoft.GameTools" Version="3.1.18" />
<PackageReference Include="NCalc.LambdaCompilation" Version="5.12.0" />
<PackageReference Include="NCalcSync" Version="5.12.0" />
<PackageReference Include="SjkScripts" Version="1.0.17" />
<PackageReference Include="System.IO.Abstractions" Version="22.1.1" />
<PackageReference Include="System.IO.Abstractions" Version="22.1.0" />
<PackageReference Include="EnvironmentAbstractions" Version="5.0.0" />
<PackageReference Include="GodotSharp.SourceGenerators" Version="2.6.0" PrivateAssets="all" OutputItemType="analyzer" />
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="1.3.66" />
<PackageReference Include="Chickensoft.AutoInject" Version="2.13.0" PrivateAssets="all" />
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="1.3.54" />
<PackageReference Include="Chickensoft.AutoInject" Version="2.9.18" PrivateAssets="all" />
<PackageReference Include="Chickensoft.Collections" Version="3.1.4" />
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="3.0.12" />
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="2.4.57" />
<PackageReference Include="Chickensoft.Introspection" Version="3.0.2" />
<PackageReference Include="Chickensoft.Introspection.Generator" Version="3.0.2" PrivateAssets="all" OutputItemType="analyzer" />
<PackageReference Include="Chickensoft.Serialization" Version="3.1.0" />
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.8.64" />
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.8.46" />
<PackageReference Include="Chickensoft.LogicBlocks" Version="5.20.0" />
<PackageReference Include="Chickensoft.LogicBlocks.DiagramGenerator" Version="5.20.0" PrivateAssets="all" OutputItemType="analyzer" />
<PackageReference Include="Chickensoft.UMLGenerator" Version="1.3.1" />
<PackageReference Include="Chickensoft.Sync" Version="2.3.0" />
<PackageReference Include="Chickensoft.UMLGenerator" Version="1.1.0" />
<PackageReference Include="Chickensoft.Sync" Version="2.2.0" />
</ItemGroup>
<ItemGroup Condition="'$(RunTests)' == 'true'">
<!-- Test dependencies go here! -->
<!-- Dependencies added here will not be included in release builds. -->
<PackageReference Include="Chickensoft.GoDotTest" Version="2.0.33" />
<PackageReference Include="Chickensoft.GoDotTest" Version="2.0.27" />
<!-- Used to drive test scenes when testing visual code -->
<PackageReference Include="Chickensoft.GodotTestDriver" Version="3.1.68" />
<PackageReference Include="Chickensoft.GodotTestDriver" Version="3.1.56" />
<!-- Bring your own assertion library for tests! -->
<!-- We're using Shouldly for this example, but you can use anything. -->
<PackageReference Include="Shouldly" Version="4.3.0" />

2
assets

Submodule assets updated: b5ad429c59...37af2de434

View File

@@ -1,8 +1,8 @@
using System.Diagnostics;
using ChickenGameTest;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
using System;
[Meta(typeof(IAutoNode))]
public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
{
@@ -16,19 +16,21 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
get => GridTransform3D.FromGodot(GlobalTransform);
set => GlobalTransform = value.ToGodot();
}
[Node("ConveyorLogic")]
public TestItemConveyor ConveyorLogic { get; set; } = default!;
public override void _Ready()
{
var node = GetNode("Node") as TestItemConveyor;
base._Ready();
Debug.Assert(ConveyorLogic is not null, $"{nameof(ConveyorLogic)} is not set on {nameof(ConveyorBeltStraight)}, Path {SceneFilePath}");
// GD.PrintS(VoxelTransform.Origin,VoxelTransform.Forward);
node.StartPort = node.CreatePort(
ConveyorLogic.StartPort = ConveyorLogic.CreatePort(
new(VoxelTransform.TranslatedLocal(new(1 - Width, 0, 0)).RotateY(2),
// Direction.Back,
Width,
PortAccess.BiDirectional),
new BeltTEnd(ConveyorEnd.Start),
new LaneSpan(0, 1));
node.EndPort = node.CreatePort(
ConveyorLogic.EndPort = ConveyorLogic.CreatePort(
new(VoxelTransform, //new(GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)),
// Direction.Front,
Width,
@@ -36,22 +38,23 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
new BeltTEnd(ConveyorEnd.End),
new LaneSpan(0, 1));
node.OtherPorts = [
node.CreatePort(
ConveyorLogic.OtherPorts = [
ConveyorLogic.CreatePort(
new(VoxelTransform.RotateY90CW(),
// Direction.Right,
1,
PortAccess.In),
new BeltTOffset(node.Length/2),
new BeltTOffset(ConveyorLogic.Length/2),
LaneSpan.One),
node.CreatePort(
ConveyorLogic.CreatePort(
new(VoxelTransform.RotateY90CCW(),
// Direction.Left,
1,
PortAccess.In),
new BeltTOffset(node.Length/2),
new BeltTOffset(ConveyorLogic.Length/2),
LaneSpan.One)
];
}
public void OnResolved()
{

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://c4h7mwnfrdesg"]
[gd_scene format=3 uid="uid://c4h7mwnfrdesg"]
[ext_resource type="Script" uid="uid://dgyuh6un1qoj4" path="res://src/Conveyors/ConveyorBeltStraight/ConveyorBeltStraight.cs" id="1_mn8ro"]
[ext_resource type="Script" uid="uid://j24fuotdwwx4" path="res://src/VoxelGrid/TestItemConveyor.cs" id="2_1vr1d"]
@@ -13,22 +13,23 @@ _data = {
}
point_count = 2
[node name="ConveyorBeltStraight" type="Node3D"]
[node name="ConveyorBeltStraight" type="Node3D" unique_id=1032197109]
script = ExtResource("1_mn8ro")
Width = 1
[node name="Node" type="Node" parent="."]
[node name="ConveyorLogic" type="Node" parent="." unique_id=1693852977]
script = ExtResource("2_1vr1d")
[node name="conveyor-sides" parent="." instance=ExtResource("2_mn8ro")]
[node name="conveyor-sides" parent="." unique_id=516239399 instance=ExtResource("2_mn8ro")]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0, 0)
[node name="arrow-basic2" parent="." instance=ExtResource("3_1vr1d")]
[node name="arrow-basic2" parent="." unique_id=1668302786 instance=ExtResource("3_1vr1d")]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0.21879572, 0)
[node name="Path3D" type="Path3D" parent="."]
[node name="Path3D" type="Path3D" parent="." unique_id=878303794]
curve = SubResource("Curve3D_x5qdr")
[node name="Node2" type="Node" parent="." node_paths=PackedStringArray("Path3D", "ItemConveyor")]
[node name="Node2" type="Node" parent="." unique_id=1875202216 node_paths=PackedStringArray("Path3D", "ItemConveyor")]
script = ExtResource("4_g7n1f")
Path3D = NodePath("../Path3D")
ItemConveyor = NodePath("../Node")
ItemConveyor = NodePath("../ConveyorLogic")

View File

@@ -0,0 +1,30 @@
namespace FoodFactory.Items;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
public readonly struct Tag : IEquatable<Tag>, IEqualityOperators<Tag, Tag, bool>, IComparisonOperators<Tag, Tag, bool>,
IComparable<Tag>
{
public readonly string Name => TagRegistry.GetName(this);
internal readonly int _id;
internal Tag(int id)
{
Debug.Assert(id >= 0);
_id = id;
}
public override bool Equals([NotNullWhen(true)] object? obj) => obj is Tag tag && Equals(tag);
public bool Equals(Tag other) => _id == other._id;
public override int GetHashCode() => _id;
public override string ToString() => Name;
public int CompareTo(Tag other) => _id.CompareTo(other._id);
public static bool operator >(Tag left, Tag right) => left._id > right._id;
public static bool operator >=(Tag left, Tag right) => left._id >= right._id;
public static bool operator <(Tag left, Tag right) => left._id < right._id;
public static bool operator <=(Tag left, Tag right) => left._id <= right._id;
public static bool operator ==(Tag left, Tag right) => left._id == right._id;
public static bool operator !=(Tag left, Tag right) => left._id != right._id;
}

View File

@@ -0,0 +1 @@
uid://1o2e2wou367m

View File

@@ -2,13 +2,9 @@ namespace FoodFactory.Items;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Threading;
public readonly struct Tags : IEquatable<Tags>, IReadOnlyCollection<Tag>
{
@@ -178,30 +174,6 @@ public readonly struct Tags : IEquatable<Tags>, IReadOnlyCollection<Tag>
}
}
public readonly struct Tag : IEquatable<Tag>, IEqualityOperators<Tag, Tag, bool>, IComparisonOperators<Tag, Tag, bool>,
IComparable<Tag>
{
public readonly string Name => TagRegistry.GetName(this);
internal readonly int _id;
internal Tag(int id)
{
Debug.Assert(id >= 0);
_id = id;
}
public override bool Equals([NotNullWhen(true)] object? obj) => obj is Tag tag && Equals(tag);
public bool Equals(Tag other) => _id == other._id;
public override int GetHashCode() => _id;
public override string ToString() => Name;
public int CompareTo(Tag other) => _id.CompareTo(other._id);
public static bool operator >(Tag left, Tag right) => left._id > right._id;
public static bool operator >=(Tag left, Tag right) => left._id >= right._id;
public static bool operator <(Tag left, Tag right) => left._id < right._id;
public static bool operator <=(Tag left, Tag right) => left._id <= right._id;
public static bool operator ==(Tag left, Tag right) => left._id == right._id;
public static bool operator !=(Tag left, Tag right) => left._id != right._id;
}
public static class TagRegistry
{
private static Registry<string> _registry = new();

View File

@@ -0,0 +1 @@
uid://chk03ru27wxw1

View File

@@ -2,8 +2,12 @@ namespace ChickenGameTest;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Linq;
using Arch.Core;
using Arch.Core.Extensions;
using FoodFactory.Items;
using Godot;
using SJK.Functional;
/// <summary>
/// Items that can be on a belt
@@ -16,15 +20,46 @@ public interface IBeltItem : IDisposable
delegate void ItemRemoved(IBeltItem item);
event ItemRemoved? Disposed;
}
public class TestItem() : IBeltItem
public interface IBeltItemData<T>
{
T GetItem();
}
public class TestItem() : IBeltItem, IBeltItemData<Entity>
{
public float Temp { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public static PackedScene PotatoScene = ResourceLoader.Load<PackedScene>("res://assets/KayKit_Restaurant_Bits_1.0_FREE/Assets/gltf/food_ingredient_potato.gltf");
public static PackedScene OnionScene = ResourceLoader.Load<PackedScene>("res://assets/KayKit_Restaurant_Bits_1.0_FREE/Assets/gltf/food_ingredient_onion.gltf");
public static PackedScene ChoppedOnionScene = ResourceLoader.Load<PackedScene>("res://assets/KayKit_Restaurant_Bits_1.0_FREE/Assets/gltf/food_ingredient_onion_chopped.gltf");
public Node3D CreateItemVisual()
{
Node3D node;
if (Item.Get<Tags>().Contains(TagRegistry.GetTag("potato")))
{
node = PotatoScene.Instantiate<Node3D>();
}
else if (Item.Get<Tags>().ContainsAny(new Tags("sliced")))
{
node = ChoppedOnionScene.Instantiate<Node3D>();
}
else if (Item.Get<Tags>().Contains(TagRegistry.GetTag("onion")))
{
node = OnionScene.Instantiate<Node3D>();
}
else
{
node = new MeshInstance3D() { Mesh = new BoxMesh() };
}
node.GetChildren().OfType<Node3D>().ToList().ForEach(i => i.Scale *= new Vector3(.2f, .2f, .2f));
if (Item.TryGet<Color>(out var color))
{
node.GetChildren().OfType<MeshInstance3D>().FirstOrNone().IfSome(some => some.SetSurfaceOverrideMaterial(0, new StandardMaterial3D() { AlbedoColor = Item.TryGet<Color>(out var color) ? color : Colors.White }));
}
return node;
// return new MeshInstance3D() { Mesh = new BoxMesh() { Size = new(.1f, .1f, .1f) }, MaterialOverride = new StandardMaterial3D() { AlbedoColor = Item.TryGet<Color>(out var color) ? color : Colors.White } };
}
public Node3D CreateItemVisual() => new MeshInstance3D() { Mesh = new BoxMesh() { Size = new(.1f, .1f, .1f) } };
private bool _disposed;
public event IBeltItem.ItemRemoved? Disposed;
@@ -40,7 +75,10 @@ public class TestItem() : IBeltItem
_disposed = true;
Disposed?.Invoke(this);
}
public Dictionary<string, object> PlaceHolderData = [];
public Entity GetItem() => Item;
public Entity Item;
}
public interface IFluidItem

View File

@@ -2,10 +2,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Arch.Core;
using Arch.Core.Extensions;
using Arch.LowLevel.Jagged;
using Arch.LowLevel;
using FoodFactory;
using FoodFactory.Items;
using FoodFactory.Recipes;
@@ -21,8 +20,8 @@ public class Test
var bucket = new BucketStorage<List<Entity>>();
var pizza = world.Create(new Name("Pizza"), new Temperature(5f), new MarcoNutrients(5, 5, 10), new MicroNutrients([new Vitamin("D", 5)]));
var turkey = world.Create(new Name("Turkey"), new Temperature(5f), new MarcoNutrients(5, 5, 10));
var pizza = world.Create(new Name("Pizza"), new Temperature(5f), new MarcoNutrients(5, 5, new()), new MicroNutrients([new Vitamin("D", 5)]));
var turkey = world.Create(new Name("Turkey"), new Temperature(5f), new MarcoNutrients(5, 5, new()));
// var componentType = new ComponentType(ComponentRegistry.Size-1, 8);
// ComponentRegistry.Add(typeof(LayeredStack2), componentType);
@@ -37,7 +36,7 @@ public class Test
pizza.Get<Temperature>().Kelvin += .1f;
}
pizza.Add(new IngredientOf(turkey));
var a = new List<Entity>(){pizza};
var a = new List<Entity>() { pizza };
turkey.Add(new LayeredStack(bucket.Add(a)));
turkey.Add(new LayeredStack2(a));
@@ -70,36 +69,6 @@ public class Test
}
}
);
var recipes = new Recipes();
var item = recipes.TESTBlueprint.Factory(new BlueprintContext(){World = world, BluePrintId = new BlueprintId(recipes.TESTBlueprint)});//world.Create(new Name("Potato_Raw"), new Temperature(71f), new Tags("raw", "potato", "vegetable"));
Span<Entity> items = [item];
var context = new RecipeContext(world, items);
var builder = new RecipeResultBuilder(stackalloc bool[10], new ItemBuilder[10]);
// var recipe = new PotatoCookRecipe();
var recipe = recipes.GetRecipes(new RecipeAction("cook"), 1, [item.Get<Tags>()], [])[0].Recipe;
if (recipe.CanProcess(context))
{
var result = recipe.Process(context, ref builder);
var list3 = new List<Entity>();
for (int i = 0; i < result.CreateLength; i++)
{
list3.Add(result.Create[i](in context));
}
if (result.RemoveLength > 0)
{
for (int i = 0; i < result.RemoveLength; i++)
{
GD.Print(result.Remove[i]);
if (result.Remove[i])
{
world.Destroy(items[i]);
}
}
}
GD.Print(string.Join(',',list3[0].GetAllComponents()));
GD.Print(items[0].IsAlive());
}
}
}
@@ -109,15 +78,64 @@ internal interface IEntityContainer
}
public record struct Name(string Value);
public record struct MarcoNutrients(float Fat, float Protein, float Carbohydrate);
public record struct BurnableTemp(Temperature Temperature);
public record struct BurnedItem(Temperature BurnedAt, string Description);//Info about how burnted item is
public record struct MarcoNutrients(Weight Fat, Weight Protein, Carbohydrates Carbohydrate);
public readonly record struct Carbohydrates(
Weight Fiber,
Weight Starch,
SugarProfile Sugars
)
{
public Weight TotalWeight =>
Fiber + Starch + Sugars.TotalWeight;
public double Calories =>
(Fiber.Grams * 2.0) +
(Starch.Grams * 4.0) +
Sugars.Calories;
public double Sweetness =>
Sugars.Sweetness;
}
public readonly record struct SugarProfile(
Weight Glucose,
Weight Fructose,
Weight Sucrose,
Weight Lactose
)
{
public Weight TotalWeight =>
Glucose + Fructose + Sucrose + Lactose;
public double Calories =>
TotalWeight.Grams * 4.0;
public double Sweetness =>
(Glucose.Grams * 0.7) +
(Fructose.Grams * 1.7) +
(Sucrose.Grams * 1.0) +
(Lactose.Grams * 0.2);
public static SugarProfile operator +(
SugarProfile a,
SugarProfile b) => new(
a.Glucose + b.Glucose,
a.Fructose + b.Fructose,
a.Sucrose + b.Sucrose,
a.Lactose + b.Lactose);
}
public record struct Mass(Weight Value);// Repsenets the apomix weight of an item, should have things like nutrients allready accounted.
public record struct MicroNutrients(List<Vitamin> Vitamins);
public record struct Vitamin(string Id, float Amount);
public record struct Vitamin(string Id, Weight Amount);
public record struct Scale(float Value);
public record struct IngredientOf(Entity Source);
public record struct LayeredStack(Handle Handle);
public record struct LayeredStack2(List<Entity> Children);
public record struct Bacteria(Handle<List<BacteriaData>> Data);
public record struct BacteriaData(string Name, double Count);
public record struct Handle(int Index, int Version);
@@ -157,7 +175,7 @@ public class BucketStorage<T>
Version = 1,
Occupied = true
};
Array.Resize(ref _slots, _slots.Length + 1);
System.Array.Resize(ref _slots, _slots.Length + 1);
_slots[^1] = slot;
return new Handle

View File

@@ -0,0 +1 @@
uid://c5ik7ke6rrlmn

109
src/Math/Ordered1DList.cs Normal file
View File

@@ -0,0 +1,109 @@
namespace SJK.Math;
using System;
using System.Collections.Generic;
using Arch.LowLevel;
public sealed class Ordered1DList<T> : IDisposable where T : unmanaged
{
private readonly UnsafeList<Entry> _values;
public Ordered1DList(int capacity)
{
_values = new(capacity);
}
public record struct Entry(float Position, T Value);
public void Insert(T item, float position)
{
if (_values.Count == 0 || position >= _values[^1].Position)
{
_values.Add(new(position, item));
return;
}
if (position <= _values[0].Position)
{
_values.Insert(0, new(position, item));
return;
}
var index = LowerBound(position);
_values.Insert(index, new(position, item));
SortFromIndex(index);
}
public void RemoveAt(int index) => _values.RemoveAt(index);
public void Clear() => _values.Clear();
public void MoveAllBy(float offset)
{
for (int i = 0; i < _values.Count; i++)
{
_values[i].Position = _values[i].Position + offset;
}
}
private void SortFromIndex(int index)
{
var entry = _values[index];
while (index > 0 &&
_values[index - 1].Position > entry.Position)
{
Swap(index, index - 1);
index--;
}
// bubble right
while (index < _values.Count - 1 &&
_values[index + 1].Position < entry.Position)
{
Swap(index, index + 1);
index++;
}
}
private void Swap(int a, int b) => (_values[b], _values[a]) = (_values[a], _values[b]);
private int LowerBound(float pos)
{
int lo = 0;
int hi = _values.Count;
while (lo < hi)
{
int mid = (lo + hi) >> 1;
if (_values[mid].Position < pos)
{
lo = mid + 1;
}
else
{
hi = mid;
}
}
return lo;
}
public Enumerator EnumerateTowardEnd(int startIndex) => new(this, towardsEnd: true, startIndex);
public Enumerator EnumerateTowardStart(int startIndex) => new(this, towardsEnd: false, startIndex);
public ref struct Enumerator
{
public Enumerator(Ordered1DList<T> list, bool towardsEnd, int index)
{
_list = list;
_towardsEnd = towardsEnd;
_index = index;
}
private readonly Ordered1DList<T> _list;
private readonly bool _towardsEnd;
private int _index;
public bool MoveNext()
{
_index += _towardsEnd ? 1 : -1;
return _index >= 0 && _index < _list._values.Count;
}
public void Remove()
{
_list._values.RemoveAt(_index);
_index -= _towardsEnd ? 1 : -1;
}
public readonly ref Entry Current => ref _list._values[_index];
}
public void Dispose() => _values.Dispose();
}

View File

@@ -0,0 +1 @@
uid://uuf56xygihoh

View File

@@ -0,0 +1 @@
uid://pcjdirermsr7

148
src/Math/Weight.cs Normal file
View File

@@ -0,0 +1,148 @@
using System;
using System.Globalization;
public readonly record struct Weight(double Grams)
: IComparable<Weight>,
IFormattable
{
// -----------------------------
// Metric Constants
// -----------------------------
public const double GRAMS_PER_KILOGRAM = 1000.0;
public const double GRAMS_PER_CENTIGRAM = 0.01;
public const double GRAMS_PER_MILLIGRAM = 0.001;
// -----------------------------
// Imperial Constants
// -----------------------------
public const double GRAMS_PER_OUNCE = 28.349523125;
public const double GRAMS_PER_POUND = 453.59237;
// -----------------------------
// Factory Methods
// -----------------------------
public static Weight FromGrams(double value)
=> new(value);
public static Weight FromKilograms(double value)
=> new(value * GRAMS_PER_KILOGRAM);
public static Weight FromCentigrams(double value)
=> new(value * GRAMS_PER_CENTIGRAM);
public static Weight FromMilligrams(double value)
=> new(value * GRAMS_PER_MILLIGRAM);
public static Weight FromOunces(double value)
=> new(value * GRAMS_PER_OUNCE);
public static Weight FromPounds(double value)
=> new(value * GRAMS_PER_POUND);
// -----------------------------
// Unit Properties
// -----------------------------
public double Kilograms => Grams / GRAMS_PER_KILOGRAM;
public double Centigrams => Grams / GRAMS_PER_CENTIGRAM;
public double Milligrams => Grams / GRAMS_PER_MILLIGRAM;
public double Ounces => Grams / GRAMS_PER_OUNCE;
public double Pounds => Grams / GRAMS_PER_POUND;
// -----------------------------
// Operators
// -----------------------------
public static Weight operator +(Weight a, Weight b)
=> new(a.Grams + b.Grams);
public static Weight operator -(Weight a, Weight b)
=> new(a.Grams - b.Grams);
public static Weight operator *(Weight a, double scalar)
=> new(a.Grams * scalar);
public static Weight operator *(double scalar, Weight a)
=> new(a.Grams * scalar);
public static Weight operator /(Weight a, double scalar)
=> new(a.Grams / scalar);
// Ratio
public static double operator /(Weight a, Weight b)
=> a.Grams / b.Grams;
// -----------------------------
// Comparisons
// -----------------------------
public int CompareTo(Weight other)
=> Grams.CompareTo(other.Grams);
// -----------------------------
// Implicit / Explicit Casts
// -----------------------------
// Weight -> double (grams)
public static implicit operator double(Weight weight)
=> weight.Grams;
// double -> Weight (grams)
public static implicit operator Weight(double grams)
=> new(grams);
// -----------------------------
// Formatting
// -----------------------------
public override string ToString()
=> ToString("auto", CultureInfo.InvariantCulture);
public string ToString(string? format, IFormatProvider? provider)
{
provider ??= CultureInfo.InvariantCulture;
format = format?.ToLowerInvariant();
return format switch
{
"g" => $"{Grams.ToString("0.##", provider)} g",
"kg" => $"{Kilograms.ToString("0.###", provider)} kg",
"cg" => $"{Centigrams.ToString("0.##", provider)} cg",
"mg" => $"{Milligrams.ToString("0.##", provider)} mg",
"lb" => $"{Pounds.ToString("0.##", provider)} lb",
"oz" => $"{Ounces.ToString("0.##", provider)} oz",
"auto" or null => AutoFormat(provider),
_ => throw new FormatException($"Unknown format '{format}'")
};
}
private string AutoFormat(IFormatProvider provider)
{
var abs = Math.Abs(Grams);
if (abs >= GRAMS_PER_KILOGRAM)
{
return $"{Kilograms.ToString("0.###", provider)} kg";
}
if (abs < 1.0)
{
return $"{Milligrams.ToString("0.##", provider)} mg";
}
return $"{Grams.ToString("0.##", provider)} g";
}
// -----------------------------
// Common Values
// -----------------------------
public static Weight Zero => new(0);
}

1
src/Math/Weight.cs.uid Normal file
View File

@@ -0,0 +1 @@
uid://7e8o324qvsox

58
src/Recipes/Blueprint.cs Normal file
View File

@@ -0,0 +1,58 @@
namespace FoodFactory.Recipes;
using System;
using System.Collections.Generic;
using Arch.Core;
using ChickenGameTest;
using FoodFactory.Items;
public interface IBlueprintManger
{
Blueprint GetBlueprint(string name);
}
public class BlueprintManger : IBlueprintManger
{
private readonly Dictionary<string, Blueprint> _bluePrints = [];
public Blueprint GetBlueprint(string name)
{
if (!_bluePrints.TryGetValue(name, out var blueprint))
{
//TODO this is where this should look through mods/base game
if (name == "Potato")
{
blueprint = new Blueprint("Potato", static ctx => ctx.World.Create(new Name("Potato_raw"),
new Tags("potato", "raw", "vegetable", "burnable"),
new Temperature(40, TemperatureUnit.Fahrenheit),
ctx.BluePrintId,
new Bacteria(BacterialSystem.Resources.Add([new BacteriaData("slamnala", 400)])),
new BurnableTemp(new Temperature(400, TemperatureUnit.Fahrenheit))));//TODO blueprintID should not be passed, but know when blueprint is created, meaning a rework of Blueprint id register
}
else if (name == "Onion")
{
blueprint = new Blueprint("Onion", static ctx => ctx.World.Create(new Name("Onion_raw"),
new Tags("onion", "raw", "vegetable", "burnable", "sliceable"),
new Temperature(71, TemperatureUnit.Fahrenheit),
ctx.BluePrintId,
new BurnableTemp(new Temperature(400, TemperatureUnit.Fahrenheit)),
new Mass(Weight.FromGrams(110)),
new Carbohydrates(Fiber: 2, Starch: 3, Sugars: new(Glucose: 0, Fructose: 1.2f, Sucrose: 1, Lactose: 0))
));//TODO blueprintID should not be passed, but know when blueprint is created, meaning a rework of Blueprint id register
}
else
{
throw new Exception();
}
}
return blueprint;
}
}
public record Blueprint(string Name, Func<BlueprintContext, Entity> Factory)
{
}
public struct BlueprintContext
{
public World World;
public BlueprintId BluePrintId;
}

View File

@@ -0,0 +1 @@
uid://cj088ogl8uqmt

View File

@@ -0,0 +1,14 @@
namespace FoodFactory.Recipes;
using FoodFactory.Items;
public readonly record struct BlueprintId
{
public readonly int Id;
public Blueprint Blueprint() => _registry.GetEntry(Id);
private static readonly Registry<Blueprint> _registry = new();
public BlueprintId(Blueprint blueprint)
{
Id = _registry.GetOrCreate(blueprint);
}
}

View File

@@ -0,0 +1 @@
uid://b7kmhd6uamhg

231
src/Recipes/Recipe.cs Normal file
View File

@@ -0,0 +1,231 @@
namespace FoodFactory.Recipes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Arch.Core;
using Arch.Core.Extensions;
using Arch.Core.Extensions.Dangerous;
using FoodFactory.Items;
using Godot;
public abstract record Recipe(string Name, BlueprintId[]? BlueprintIds, bool[] RequireAll, Tags[] Inputs, Tags[] Exclude, RecipeOutput RecipeOutput, RecipeAction Action = default, bool InputsOrdered = true)
{
public abstract bool CanProcess(in RecipeContext context);
public abstract RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder);
}
public readonly record struct RecipeOutput
{
public int? Min { get; }
public int? Max { get; }
[MemberNotNullWhen(true, nameof(Min), nameof(Max))]
public bool IsExact => Min.HasValue && Max.HasValue && Min == Max;
[MemberNotNullWhen(true, nameof(Min), nameof(Max))]
public bool IsRange => Min.HasValue && Max.HasValue && Min != Max;
public bool IsUnbounded => Min == null && Max == null;
public RecipeOutput(int exact)
{
Min = exact;
Max = exact;
}
public RecipeOutput(int min, int max)
{
Min = min;
Max = max;
}
private RecipeOutput(int? min, int? max)
{
Min = min;
Max = max;
}
public static RecipeOutput Any => new(null, null);
}
public struct CompiledRecipe
{
public Recipe RecipeRef;
// public BlueprintId[] InputBlueprints;
public ItemEntry[] ItemEntries;
public bool IsOrdered;
}
public struct ItemEntry
{
public bool RequireAll;
public Tags InputTags;
public Tags ExcludeTags;
public BlueprintId? Blueprint;
}
public static class RecipeCompiler
{
public static CompiledRecipe Compile(Recipe recipe) => new()
{
RecipeRef = recipe,
ItemEntries = BuildEntries(recipe),
// IsOrdered = recipe.InputsOrdered,
};
private static ItemEntry[] BuildEntries(Recipe recipe)
{
var entries = new ItemEntry[recipe.Inputs.Length];
for (var i = 0; i < entries.Length; i++)
{
entries[i] = new ItemEntry
{
RequireAll = recipe.RequireAll[i],
InputTags = recipe.Inputs[i],
ExcludeTags = recipe.Exclude[i],
// Blueprint = recipe.BlueprintIds?[i],
};
}
return entries;
}
}
public record BurnItemRecipe : Recipe
{
public BurnItemRecipe() : base("Burn_item",
BlueprintIds: null,
RequireAll: [false],
Inputs: [new Tags("burnable")],
Exclude: [new Tags("burned")],
RecipeOutput: RecipeOutput.Any,
Action: new RecipeAction("cook"),
InputsOrdered: true)
{
}
public override bool CanProcess(in RecipeContext context)
{
if (context.Items[0].TryGet<Temperature>(out var temp) && context.Items[0].TryGet<BurnableTemp>(out var burnableTemp) && temp > burnableTemp.Temperature)
{
return true;
}
return false;
}
public override RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder) => new(BurnItem);
private void BurnItem(RecipeContext context)
{
// context.Entity[0].Remove<BurnableTemp>();
context.Items[0].Add(new BurnedItem(context.Items[0].Get<Temperature>(), "Was Burned as some time thing"));
context.Items[0].Set(context.Items[0].Get<Tags>().WithOut("burnable").With("burned"));
context.Items[0].Set($"Burned_{context.Items[0].Get<Name>().Value}");
if (context.Items[0].Has<Color>())
{
context.Items[0].Set(context.Items[0].Get<Color>() * Colors.Black);
}
else
{
context.Items[0].Add(Colors.Black);
}
}
}
public record OnionSliceRecipe() : Recipe(Name: "Onion_Slice",
BlueprintIds: null,
RequireAll: [false],
Inputs: [new Tags("onion", "sliceable")],
Exclude: [new Tags()],
Action: "slice",
RecipeOutput: new RecipeOutput(2)
)
{
public override bool CanProcess(in RecipeContext context) => true;
public override RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder)
{
builder.AddRemove(true);
builder.AddCreate(Slice);
builder.AddCreate(Slice);
return builder.Build();
}
private Entity Slice(in RecipeContext context)
{
Span<Entity> span = stackalloc Entity[1];
var signature = context.World.GetSignature(context.Items[0]);
context.World.Create(span, signature, 1);
PotatoCookRecipe.CopyEntities(context.Items, span, context.World);
span[0].Set(new Name("Sliced " + context.Items[0].Get<Name>().Value));
span[0].Set(new Mass(context.Items[0].Get<Mass>().Value * .5));
span[0].Set(span[0].Get<Tags>().With("sliced"));
return span[0];
}
}
public record PotatoCookRecipe() : Recipe(Name: "Potato_Cook",
BlueprintIds: null,
RequireAll: [false],
Inputs: [new Tags("potato", "raw")],
Exclude: [new Tags()],
InputsOrdered: true,
Action: "cook",
RecipeOutput: new RecipeOutput(1))
{
public override bool CanProcess(in RecipeContext context)
{
if (!(context.Items[0].TryGet<Temperature>(out var temp) && temp.Fahrenheit > 350))
{
return false;
}
return true;
}
public override RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder)
{
builder.AddRemove(true);
builder.AddCreate(NewPotato);
return builder.Build();
}
private Entity NewPotato(in RecipeContext context)
{
Span<Entity> span = stackalloc Entity[1];
var signature = context.World.GetSignature(context.Items[0]);
context.World.Create(span, signature, 1);
CopyEntity(context.Items[0], span[0], context.World);
span[0].Set(new Name("Cooked_Potato"));
span[0].Set(context.Items[0].Get<Tags>().With("cooked").WithOut("raw"));
// span[0].Set(context.Entity[0].Get<Temperature>());
if (context.Items[0].Has<Color>())
{
context.Items[0].Set(context.Items[0].Get<Color>() * Colors.Brown);
}
else
{
context.Items[0].Add(Colors.Brown);
}
return span[0];
}
public static void CopyEntities(Span<Entity> from, Span<Entity> to, World world)
{
Debug.Assert(from.Length == to.Length);
for (int i = 0; i < from.Length; i++)
{
CopyEntity(from[i], to[i], world);
}
}
public static void CopyEntity(Entity src, Entity dst, World world)
{
var srcData = world.GetEntityData(src);
var dstData = world.GetEntityData(dst);
var components = srcData.Archetype
.GetChunk(srcData.Slot.ChunkIndex)
.Components;
var componentsDst = dstData.Archetype
.GetChunk(dstData.Slot.ChunkIndex)
.Components;
int srcIndex = srcData.Slot.Index;
int dstIndex = dstData.Slot.Index;
for (int i = 0; i < components.Length; i++)
{
componentsDst[i].SetValue(components[i].GetValue(srcIndex), dstIndex);
}
}
}

View File

@@ -0,0 +1 @@
uid://152ii3ysj30f

View File

@@ -0,0 +1,12 @@
namespace FoodFactory.Recipes;
public readonly record struct RecipeAction
{
public readonly string Value;
public RecipeAction(string value)
{
Value = value.ToLowerInvariant();
}
public static implicit operator string(RecipeAction name) => name.Value;
public static implicit operator RecipeAction(string name) => new(name);
}

View File

@@ -0,0 +1 @@
uid://dut1goeo1md46

View File

@@ -0,0 +1,37 @@
namespace FoodFactory.Recipes;
using System;
using System.Collections.Generic;
public sealed class RecipeBucket
{
public Dictionary<BlueprintId, CompiledRecipe[]> Single = [];
public Dictionary<BlueprintId, CompiledRecipe[]> Ordered = [];
public CompiledRecipe[] Unordered = [];
public ReadOnlySpan<CompiledRecipe> Resolve(Span<BlueprintId> items)
{
if (items.Length == 0)
{
return Unordered;
}
if (items.Length == 1)
{
if (Single.TryGetValue(items[0], out var r))
{
return r;
}
return Unordered;
}
var main = items[0];
if (Ordered.TryGetValue(main, out var ordered))
{
return ordered;
}
return Unordered;
}
}

View File

@@ -0,0 +1 @@
uid://cfhklflid8ed1

View File

@@ -0,0 +1,20 @@
namespace FoodFactory.Recipes;
using System;
using Arch.Core;
public readonly ref struct RecipeContext
{
public readonly World World;
public RecipeContext(World world, Span<Entity> entity) : this()
{
World = world;
Items = entity;
}
public readonly Span<Entity> Items;
// optional later:
// public readonly Machine Machine;
// public readonly float Delta;
}

View File

@@ -0,0 +1 @@
uid://cjamv4y4aikhc

20
src/Recipes/RecipeName.cs Normal file
View File

@@ -0,0 +1,20 @@
namespace FoodFactory.Recipes;
// public record SingleItemRecipe(string Name, Tags Required, Tags Exclude) : Recipe(Name)
// {
// }
// public record MultipleItemRecipe(string Name, params ItemInput[] Inputs) : Recipe(Name)
// {
// }
public readonly record struct RecipeName
{
public readonly string Name;
public RecipeName(string name)
{
Name = name.ToLowerInvariant();
}
public static implicit operator string(RecipeName name) => name.Name;
public static implicit operator RecipeName(string name) => new(name);
}

View File

@@ -0,0 +1 @@
uid://b635p37fwu0xl

View File

@@ -0,0 +1,26 @@
namespace FoodFactory.Recipes;
using System;
public readonly ref struct RecipeResult
{
public readonly int RemoveLength => Remove.Length;
public readonly int CreateLength => Create.Length;
public readonly ReadOnlySpan<bool> Remove; // destroy original
public RecipeResult(ReadOnlySpan<bool> remove, ReadOnlySpan<ItemBuilder> create) : this()
{
Remove = remove;
Create = create;
Mutate = null;
}
public readonly ReadOnlySpan<ItemBuilder> Create; // multi-output
public readonly Action<RecipeContext>? Mutate; // component changes
public RecipeResult(Action<RecipeContext> mutate) : this()
{
Mutate = mutate;
}
}

View File

@@ -0,0 +1 @@
uid://ctyimxkx8bomh

View File

@@ -0,0 +1,34 @@
namespace FoodFactory.Recipes;
using System;
using Arch.Core;
public ref struct RecipeResultBuilder
{
private Span<bool> _remove;
private Span<ItemBuilder> _create;
private int _removeCount;
private int _createCount;
public RecipeResultBuilder(Span<bool> remove, Span<ItemBuilder> create)
{
_remove = remove;
_create = create;
_removeCount = 0;
_createCount = 0;
}
public void AddRemove(bool value) => _remove[_removeCount++] = value;
public void AddCreate(ItemBuilder item) => _create[_createCount++] = item;
public void Clear()
{
_removeCount = 0;
_createCount = 0;
}
public readonly RecipeResult Build()
=> new(
_remove[.._removeCount],
_create[.._createCount]);
}
public delegate Entity ItemBuilder(in RecipeContext ctx);

View File

@@ -0,0 +1 @@
uid://ba6ll7cygo6hg

View File

@@ -1,82 +1,81 @@
namespace FoodFactory.Recipes;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Arch.Core;
using Arch.Core.Extensions;
using FastExpressionCompiler.ImTools;
using ChickenGameTest;
using FoodFactory.Items;
using Godot;
using Parlot.Fluent;
using SJK.Functional;
public class Recipes
public record struct RecipeEntryKey(RecipeAction Action, int Inputs, BlueprintId? FirstBlueprintId, RecipeOutput OutputsCount);
public interface IRecipes
{
private readonly Dictionary<RecipeAction, Dictionary<int, RecipeBucket>> _actions = [];
Option<Recipe> GetRecipeByName(RecipeName name);
RecipeEnumerator GetRecipes(RecipeAction action, int inputs, Tags[] itemTags, BlueprintId[] blueprintIds, RecipeOutput outputCount, Span<int> mapping);
}
public readonly record struct RecipeInput(RecipeAction Action, int Inputs, Tags[] ItemTags, BlueprintId[] BlueprintIds, RecipeOutput OutputCount);
public class Recipes : IRecipes
{
private readonly Dictionary<RecipeEntryKey, RecipeBucket> _sortedRecipes = [];
private readonly Dictionary<RecipeName, Recipe> _recipes = [];
public Option<Recipe> GetRecipeByName(RecipeName name) => _recipes.GetValue(name).ToStructOption();
public Blueprint TESTBlueprint;
public Recipes()
{
//TODO Utilize the singtaure ssytem to create the item, the have a post proccess step that would be for setting propertied
var rawPotatoBlueprint = TESTBlueprint = new Blueprint("Potato", static ctx => ctx.World.Create(new Name("Potato_raw"), new Tags("potato", "raw", "vegetable"), new Temperature(71, TemperatureUnit.Fahrenheit), ctx.BluePrintId));
var recipe = new PotatoCookRecipe();
_recipes.Add(new RecipeName(recipe.Name), recipe);
var compiledRecipe = new CompiledRecipe() { IsOrdered = true, RecipeRef = recipe, InputBlueprints = [new BlueprintId(rawPotatoBlueprint)], ItemEntries = [new CompiledRecipe.ItemEntry() { InputTags = recipe.Inputs[0], ExcludeTags = recipe.Exclude[0], RequireAll = recipe.RequireAll[0] }] };
_actions.Add(new("cook"), new Dictionary<int, RecipeBucket>() { [1] = new RecipeBucket() {Single = new Dictionary<BlueprintId, CompiledRecipe[]>(){[new BlueprintId(rawPotatoBlueprint)] = [compiledRecipe] }, Unordered = [compiledRecipe] } });
var builder = new RecipesBuilder();
builder.Add(new PotatoCookRecipe());
builder.Add(new BurnItemRecipe());
builder.Add(new OnionSliceRecipe());
_sortedRecipes = builder.Build();
_recipes = builder.CompiledRecipes.Select(f => f.RecipeRef).ToDictionary(k => new RecipeName(k.Name));
}
public List<(Recipe Recipe, int[]? Mapping)> GetRecipes(RecipeAction action, int inputs, Tags[] itemTags, BlueprintId[] blueprintIds)
public unsafe RecipeEnumerator GetRecipes(RecipeAction action, int inputs, Tags[] itemTags, BlueprintId[] blueprintIds, RecipeOutput outputCount, Span<int> mapping)
{
var list = new List<(Recipe, int[])>();
Debug.Assert(inputs > 0);
Debug.Assert(inputs == itemTags.Length);
if (!_actions.TryGetValue(action, out var inputAmount))
var key = new RecipeEntryKey(action, inputs, blueprintIds.Length > 0 ? blueprintIds[0] : null, outputCount);
if (!_sortedRecipes.TryGetValue(key, out var bucket))
{
return null;
return new RecipeEnumerator();
}
if (!inputAmount.TryGetValue(inputs, out var blueprints))
{
return null;
}
var recipes = blueprints.Resolve(blueprintIds);
var mapping = new int[itemTags.Length];
var used = new bool[itemTags.Length];
for (int i = 0; i < recipes.Length; i++)
{
var item = recipes[i];
if (inputs == 1 && IsValid(itemTags[0], item.ItemEntries[0]))
{
list.Add((item.RecipeRef, null));
}
else if (item.IsOrdered)
{
for (int ii = 0; ii < itemTags.Length; ii++)
{
if (!IsValid(itemTags[ii], item.ItemEntries[ii]))
{
continue;
}
}
list.Add((item.RecipeRef, null));
}
else if (CanPossiblyMatch(itemTags, item))
{
if (Match(itemTags, item.ItemEntries, 0, used, mapping))
{
list.Add((item.RecipeRef, mapping));
}
}
}
return list;
var recipes = bucket.Resolve(blueprintIds);
return new RecipeEnumerator(recipes, new(action,inputs,itemTags,blueprintIds,outputCount), &Filter, mapping);
}
private static bool IsValid(Tags tags, CompiledRecipe.ItemEntry compiled)
private static bool Filter(RecipeInput input, CompiledRecipe compiledRecipe, Span<int> mapping, out RecipeMatch recipeMatch)
{
if (input.Inputs == 1 && IsValid(input.ItemTags[0], compiledRecipe.ItemEntries[0]))
{
recipeMatch = new RecipeMatch(compiledRecipe.RecipeRef, []);
return true;
}
else if (compiledRecipe.IsOrdered)
{
for (var ii = 0; ii < input.ItemTags.Length; ii++)
{
if (!IsValid(input.ItemTags[ii], compiledRecipe.ItemEntries[ii]))
{
continue;
}
}
recipeMatch = new RecipeMatch(compiledRecipe.RecipeRef, []);
return true;
}
else if (CanPossiblyMatch(input.ItemTags, compiledRecipe))
{
Span<bool> used = stackalloc bool[input.ItemTags.Length];
if (Match(input.ItemTags, compiledRecipe.ItemEntries, 0, used, mapping))
{
recipeMatch = new RecipeMatch(compiledRecipe.RecipeRef, mapping);
return true;
}
}
recipeMatch = default;
return false;
}
private static bool IsValid(Tags tags, ItemEntry compiled)
{
if (!(compiled.RequireAll ? tags.ContainsAll(compiled.InputTags) : tags.ContainsAny(compiled.InputTags)))
{
@@ -92,7 +91,7 @@ public class Recipes
{
foreach (var entry in recipe.ItemEntries)
{
bool found = false;
var found = false;
foreach (var item in items)
{
@@ -104,17 +103,19 @@ public class Recipes
}
if (!found)
{
return false;
}
}
return true;
}
private static bool Match(
Tags[] items,
CompiledRecipe.ItemEntry[] entries,
int depth,
Span<bool> used,
Span<int> mapping)
Tags[] items,
ItemEntry[] entries,
int depth,
Span<bool> used,
Span<int> mapping)
{
if (depth == entries.Length)
{
@@ -190,7 +191,7 @@ public class Recipes
{
return [.. mapping];
}
for (int i = 0; i < test.Length; i++)
for (var i = 0; i < test.Length; i++)
{
if (used[i])
{
@@ -215,199 +216,114 @@ public class Recipes
return backTrace(0);
}
}
public abstract record Recipe(string Name, BlueprintId[]? BlueprintIds, bool[] RequireAll, Tags[] Inputs, Tags[] Exclude)
public class RecipesBuilder
{
public abstract bool CanProcess(in RecipeContext context);
public abstract RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder);
}
public record PotatoCookRecipe() : Recipe(Name: "Potato_Cook", BlueprintIds: null, RequireAll: [false], Inputs: [new Tags("potato")], Exclude: [new Tags()])
{
public override bool CanProcess(in RecipeContext context)
private readonly Dictionary<RecipeEntryKey, BucketBuilder> _data = [];
private readonly List<CompiledRecipe> _compiledRecipes = [];
public IReadOnlyCollection<CompiledRecipe> CompiledRecipes => _compiledRecipes;
public void Add(Recipe recipe)
{
if (!context.Entity[0].Has<Temperature>())
var compiled = RecipeCompiler.Compile(recipe);
_compiledRecipes.Add(compiled);
var key = new RecipeEntryKey(recipe.Action, recipe.Inputs.Length, recipe.BlueprintIds?[0], recipe.RecipeOutput);
var key2 = new RecipeEntryKey(recipe.Action, recipe.Inputs.Length, recipe.BlueprintIds?[0], RecipeOutput.Any);
//TODO have items automatic added to any if not a zero output recipe
if (!_data.TryGetValue(key, out var bucket))
{
return false;
bucket = new BucketBuilder();
_data[key] = bucket;
}
return true;
}
public override RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder)
{
builder.AddRemove(true);
builder.AddCreate(NewPotato);
return builder.Build();
}
private Entity NewPotato(in RecipeContext context)
{
Span<Entity> span = stackalloc Entity[1];
var signature = context.World.GetSignature(context.Entity[0]);
context.World.Create(span, signature, 1);
span[0].Set(new Name("Cooked_Potato"));
span[0].Set(context.Entity[0].Get<Tags>().With("cooked").WithOut("raw"));
span[0].Set(context.Entity[0].Get<Temperature>());
return span[0];
}
}
public ref struct RecipeResultBuilder
{
private Span<bool> _remove;
private Span<ItemBuilder> _create;
private int _removeCount;
private int _createCount;
public RecipeResultBuilder(Span<bool> remove, Span<ItemBuilder> create)
{
_remove = remove;
_create = create;
_removeCount = 0;
_createCount = 0;
}
public void AddRemove(bool value) => _remove[_removeCount++] = value;
public void AddCreate(ItemBuilder item) => _create[_createCount++] = item;
public RecipeResult Build()
=> new RecipeResult(
_remove.Slice(0, _removeCount),
_create.Slice(0, _createCount));
}
public delegate Entity ItemBuilder(in RecipeContext ctx);
public readonly ref struct RecipeContext
{
public readonly World World;
public RecipeContext(World world, Span<Entity> entity) : this()
{
World = world;
Entity = entity;
}
public readonly Span<Entity> Entity;
// optional later:
// public readonly Machine Machine;
// public readonly float Delta;
}
public readonly ref struct RecipeResult
{
public readonly int RemoveLength => Remove.Length;
public readonly int CreateLength => Create.Length;
public readonly ReadOnlySpan<bool> Remove; // destroy original
public RecipeResult(ReadOnlySpan<bool> remove, ReadOnlySpan<ItemBuilder> create) : this()
{
Remove = remove;
Create = create;
Mutate = null;
}
public readonly ReadOnlySpan<ItemBuilder> Create; // multi-output
public readonly Action<RecipeContext>? Mutate; // component changes
public RecipeResult(Action<RecipeContext> mutate) : this()
{
Mutate = mutate;
}
}
public struct CompiledRecipe
{
public Recipe RecipeRef;
public BlueprintId[] InputBlueprints;
public ItemEntry[] ItemEntries;
public bool IsOrdered;
public struct ItemEntry
{
public bool RequireAll;
public Tags InputTags;
public Tags ExcludeTags;
}
}
// public record SingleItemRecipe(string Name, Tags Required, Tags Exclude) : Recipe(Name)
// {
// }
// public record MultipleItemRecipe(string Name, params ItemInput[] Inputs) : Recipe(Name)
// {
// }
public readonly record struct RecipeName
{
public readonly string Name;
public RecipeName(string name)
{
Name = name.ToLowerInvariant();
}
public static implicit operator string(RecipeName name) => name.Name;
}
public readonly record struct RecipeAction
{
public readonly string Value;
public RecipeAction(string value)
{
Value = value.ToLowerInvariant();
}
public static implicit operator string(RecipeAction name) => name.Value;
}
public readonly record struct BlueprintId
{
public readonly int Id;
public Blueprint Blueprint => _registry.GetEntry(Id);
private static Registry<Blueprint> _registry = new();
public BlueprintId(Blueprint blueprint)
{
Id = _registry.GetOrCreate(blueprint);
}
}
public record Blueprint(string Name, Func<BlueprintContext, Entity> Factory)
{
}
public struct BlueprintContext
{
public World World;
public BlueprintId BluePrintId;
}
public sealed class RecipeBucket
{
// 1 input → direct blueprint lookup
public Dictionary<BlueprintId, CompiledRecipe[]> Single = [];
// ordered multi-input → first item is anchor
public Dictionary<BlueprintId, CompiledRecipe[]> Ordered = [];
// fallback (unordered / no blueprint)
public CompiledRecipe[] Unordered = [];
/// <summary>
/// Resloves Blueprints, if span is length 0, tehn Unordred is returned imdetly
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public ReadOnlySpan<CompiledRecipe> Resolve(Span<BlueprintId> items)
{
if (items.Length == 0)
bucket.Add(compiled, recipe.BlueprintIds ?? []);
if (recipe.RecipeOutput == RecipeOutput.Any)
{
return Unordered;
return;
}
if (!_data.TryGetValue(key2, out bucket))
{
bucket = new BucketBuilder();
_data[key2] = bucket;
}
bucket.Add(compiled, recipe.BlueprintIds ?? []);
}
public Dictionary<RecipeEntryKey, RecipeBucket> Build()
{
var result = new Dictionary<RecipeEntryKey, RecipeBucket>();
foreach (var (key, bucket) in _data)
{
result[key] = bucket.Build();
}
if (items.Length == 1)
{
if (Single.TryGetValue(items[0], out var r))
return r;
return result;
}
private sealed class BucketBuilder
{
private readonly Dictionary<BlueprintId, List<CompiledRecipe>> _single = [];
private readonly Dictionary<BlueprintId, List<CompiledRecipe>> _ordered = [];
private readonly List<CompiledRecipe> _unordered = [];
return Unordered;
public void Add(CompiledRecipe recipe, ReadOnlySpan<BlueprintId> inputs)
{
if (inputs.Length == 0)
{
_unordered.Add(recipe);
return;
}
if (inputs.Length == 1)
{
var list = GetOrCreate(_single, inputs[0]);
list.Add(recipe);
return;
}
// multi-input → ordered (anchor = first)
if (recipe.IsOrdered)
{
var orderedList = GetOrCreate(_ordered, inputs[0]);
orderedList.Add(recipe);
}
else
{
_unordered.Add(recipe);
}
}
var main = items[0];
if (Ordered.TryGetValue(main, out var ordered))
public RecipeBucket Build()
{
return ordered;
return new RecipeBucket
{
Single = ToArrayDict(_single),
Ordered = ToArrayDict(_ordered),
Unordered = [.. _unordered]
};
}
return Unordered;
}
private static List<CompiledRecipe> GetOrCreate(
Dictionary<BlueprintId, List<CompiledRecipe>> dict,
BlueprintId key)
{
if (!dict.TryGetValue(key, out var list))
{
list = new List<CompiledRecipe>(4);
dict[key] = list;
}
return list;
}
private static Dictionary<BlueprintId, CompiledRecipe[]> ToArrayDict(
Dictionary<BlueprintId, List<CompiledRecipe>> source)
{
var result = new Dictionary<BlueprintId, CompiledRecipe[]>(source.Count);
foreach (var (key, list) in source)
{
result[key] = [.. list];
}
return result;
}
}
}

View File

@@ -0,0 +1 @@
uid://cp61k5vfvknv8

61
src/VoxelGrid/Balancer.cs Normal file
View File

@@ -0,0 +1,61 @@
namespace ChickenGameTest;
using Godot;
using System.Collections.Generic;
using System.Linq;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
[Meta(typeof(IAutoNode))]
public partial class Balancer : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxelGridRegistry>
{
public override void _Notification(int what) => this.Notify(what);
private BeltPortHost _insertLogic = default!;
public IBeltPortHost Value() => _insertLogic;
IVoxelGridRegistry IProvide<IVoxelGridRegistry>.Value() => GridRegistry;
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
private List<BeltPort> _outPuts = new();
public GridTransform3D VoxelTransform
{
get => GridTransform3D.FromGodot(GlobalTransform);
set => GlobalTransform = value.ToGodot();
}
public void OnResolved()
{
_insertLogic = new BeltPortHost();
_insertLogic.Bind(port => port is BeltPort beltPort && beltPort.Access.HasFlag(PortAccess.In), () => new DelegateInsertBeltItemLogic(CanAccept, CanInsert));
_insertLogic.Bind(port => port is BeltPort beltPort && beltPort.Access.HasFlag(PortAccess.Out), () => new DelegateInsertBeltItemLogic((_, _) => true, (_, _) => true));
this.Provide();
_outPuts = [.. OutputPorts];
}
private bool CanAccept(IBeltPort port, IBeltItem item)
{
foreach (var portOther in OutputPorts)
{
if (portOther.GetPortFacing(GridRegistry).Map(f => f.CanAccept(item, LaneSpan.One, 0)).Or(false))
{
return true;
}
}
return false;
}
private IEnumerable<BeltPort> OutputPorts => _insertLogic.GetPorts().OfType<BeltPort>().Where(i => i.Access.HasFlag(PortAccess.Out));
private bool CanInsert(IBeltPort port, IBeltItem item)
{
for (var i = 0; i < _outPuts.Count; i++)
{
var facing = _outPuts[i].GetPortFacing(GridRegistry);
if (facing.HasValue(out var face) && face.CanAccept(item, LaneSpan.One, 0) && face.TryInsert(item, LaneSpan.One, 0))
{
var c = _outPuts[i];
_outPuts.RemoveAt(i);
_outPuts.Add(c);
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1 @@
uid://opbkqoaa7x2n

View File

@@ -1,14 +1,9 @@
namespace ChickenGameTest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
using SJK.Functional;
[Tool]
[Meta(typeof(IAutoNode))]
@@ -21,11 +16,9 @@ public partial class BeltPort : Node3D, IBeltPort
[Export] public Direction Face { get; set; } = default!;
[Export] public int Width { get; set; } = default!;
[Export] public PortAccess Access { get; set; } = default!;
[Export] public Path3D Path { get; set; } = default!;
[Export] public string PortName { get; set; } = "";
[Dependency] public IItemRenderer ItemRenderer => this.DependOn<IItemRenderer>();
public BeltPortProfile Profile => new(GridTransform3D.FromGodot(GlobalTransform), Width, Access);
[Dependency] public IItemTransferAnimator ItemTransferAnimator => this.DependOn<IItemTransferAnimator>(() => new CurveItemTransfer() { Curve3D = Path.Curve, Tree = GetTree(), ItemRenderer = ItemRenderer, Transform3D = GlobalTransform });
private VoxelGuid _guid = new(Guid.Empty);
public void OnResolved()
{
@@ -37,26 +30,7 @@ public partial class BeltPort : Node3D, IBeltPort
_guid = Grid.Register<IBeltPort>(this, [.. (this as IBeltPort).Points()]);
GD.Print(_guid);
}
public override void _Process(double delta)
{
DebugDraw3D.DrawLine(GlobalPosition, GlobalTransform * Face.ToVector(), Colors.Red);
if (Engine.IsEditorHint())
{
return;
}
for (int i = 0; i < _itemsDummys.Count; i++)
{
_itemsDummys[i] = (_itemsDummys[i].i + (float)delta, _itemsDummys[i].beltItem);
// GD.Print(_itemsDummys[i].i);
ItemRenderer.UpdateTransform(_itemsDummys[i].beltItem, GlobalTransform * Path.Curve.SampleBakedWithRotation(_itemsDummys[i].i));
if (_itemsDummys[i].i > Path.Curve.GetBakedLength())
{
_itemsDummys[i].beltItem.Dispose();
_itemsDummys.RemoveAt(i);
i--;
}
}
}
public override void _Process(double delta) => DebugDraw3D.DrawLine(GlobalPosition, GlobalTransform * Face.ToVector(), Colors.Red);
public override void _ExitTree()
{
@@ -69,20 +43,7 @@ public partial class BeltPort : Node3D, IBeltPort
}
public bool CanAccept(IBeltItem item, LaneSpan laneSpan, float beltT) => Access.HasFlag(PortAccess.In) && InsertLogic.CanAccept(this, item);
List<(float i, IBeltItem beltItem)> _itemsDummys = [];
public bool TryInsert(IBeltItem item, LaneSpan laneSpan, float beltT) => CanAccept(item, laneSpan, beltT) && InsertLogic.CanInsert(this, item);
// {
// var tween = ItemTransferAnimator.StartTransfer(new(item, beltT) { LaneSpan = laneSpan }, () => GD.Print("Done"));
// // GD.Print("gg "+laneSpan);
// tween.TweenCallback(Callable.From(item.Dispose));
// // GD.Print(laneSpan);
// // GD.Print(item.ToString());
// // _itemsDummys.Add((0,item));
// // item.Dispose();
// return true;
// throw new System.NotImplementedException();
// }
}
public interface IBeltItemInsertLogic
@@ -90,106 +51,3 @@ public interface IBeltItemInsertLogic
bool CanInsert(IBeltPort beltPort, IBeltItem item);
bool CanAccept(IBeltPort beltPort, IBeltItem item);
}
public interface IBeltPortHost
{
IBeltItemInsertLogic ResolveInsertLogic(IBeltPort port);
}
public class BeltPortHost : IBeltPortHost
{
private readonly List<(Func<IBeltPort, bool> match, Func<IBeltItemInsertLogic> factory)> _bindings
= new();
public BeltPortHost Bind(
Func<IBeltPort, bool> match,
Func<IBeltItemInsertLogic> factory)
{
_bindings.Add((match, factory));
return this;
}
private HashSet<IBeltPort> _ports = [];
public IBeltItemInsertLogic? Default;
/// <summary>
/// Resolve the insert logic for a port. If no matching bind exists then the default is returned if supplied. Other wise throws an Exception.
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public IBeltItemInsertLogic ResolveInsertLogic(IBeltPort port)
{
_ports.Add(port);
foreach (var (match, factory) in _bindings)
{
if (match(port))
{
return factory();
}
}
if (Default is null)
{
throw new Exception($"{nameof(port)} does not have any binding attached that accepts it.");
}
return Default;
}
public IEnumerable<IBeltPort> GetPorts() => _ports;
}
public interface IItemTransferAnimator
{
Tween StartTransfer(
ConveyorSlice item,
Action onFinished = null);
}
public class InstanceItemTransfer : IItemTransferAnimator
{
public SceneTree Tree = default!;
public Func<ConveyorSlice, bool> AcceptFunc = default!;
public Tween StartTransfer(ConveyorSlice item, Action onFinished = null)
{
AcceptFunc(item);
onFinished?.Invoke();
return Tree.CreateTween();
throw new NotImplementedException();
}
}
public class CurveItemTransfer : IItemTransferAnimator
{
public Curve3D Curve3D = default!;
public IItemRenderer ItemRenderer = default!;
public Func<ConveyorSlice, bool> AcceptFunc = default!;
public SceneTree Tree = default!;
public Transform3D Transform3D;
public Tween AnimateAlongCurve(
ConveyorSlice item,
Curve3D curve,
float duration,
IItemRenderer renderer)
{
float length = curve.GetBakedLength();
var tween = Tree.CreateTween();
tween.TweenMethod(
Callable.From<float>(t =>
{
//Transform is not being set when using update transform in a tween, but can set position
// renderer.UpdateTransform(item, Transform3D * curve.SampleBakedWithRotation(t),1/duration);
(renderer as TestItemRendered)._items[item.Item].Transform = Transform3D * Curve3D.SampleBakedWithRotation(t).Translated(-Curve3D.SampleBakedWithRotation(t).Basis.X * item.LaneSpan.Start);
// GD.PrintS(Transform3D * curve.SampleBakedWithRotation(t).Origin,1/duration,(renderer as TestItemRendered)._items[item].GlobalPosition);
}),
0f,
length,
duration
);
return tween;
}
public Tween StartTransfer(ConveyorSlice item, Action onFinished = null)
{
var tween = AnimateAlongCurve(item, Curve3D, 1, ItemRenderer);
if (onFinished is not null)
{
tween.TweenCallback(Callable.From(onFinished));
}
return tween;
}
}

View File

@@ -0,0 +1,48 @@
namespace ChickenGameTest;
using System;
using System.Collections.Generic;
public interface IBeltPortHost
{
IBeltItemInsertLogic ResolveInsertLogic(IBeltPort port);
}
public class BeltPortHost : IBeltPortHost
{
private readonly List<(Func<IBeltPort, bool> match, Func<IBeltItemInsertLogic> factory)> _bindings
= new();
public BeltPortHost Bind(
Func<IBeltPort, bool> match,
Func<IBeltItemInsertLogic> factory)
{
_bindings.Add((match, factory));
return this;
}
private HashSet<IBeltPort> _ports = [];
public IBeltItemInsertLogic? Default;
/// <summary>
/// Resolve the insert logic for a port. If no matching bind exists then the default is returned if supplied. Other wise throws an Exception.
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public IBeltItemInsertLogic ResolveInsertLogic(IBeltPort port)
{
_ports.Add(port);
foreach (var (match, factory) in _bindings)
{
if (match(port))
{
return factory();
}
}
if (Default is null)
{
throw new Exception($"{nameof(port)} does not have any binding attached that accepts it.");
}
return Default;
}
public IEnumerable<IBeltPort> GetPorts() => _ports;
}

View File

@@ -0,0 +1 @@
uid://mimkyombngwg

View File

@@ -1,10 +1,7 @@
namespace ChickenGameTest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
@@ -55,6 +52,32 @@ public interface IItemRenderer
void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time);
void Remove(IBeltItem beltItem);
}
public partial class ItemRenderSimple : Node3D, IItemRenderer
{
public Dictionary<IBeltItem, Node3D> _items = [];
public void Remove(IBeltItem beltItem)
{
if (_items.TryGetValue(beltItem, out var node))
{
node.QueueFree();
}
_items.Remove(beltItem);
}
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
{
if (!_items.TryGetValue(beltItem, out var node))
{
_items.Add(beltItem, node = beltItem.CreateItemVisual());
AddChild(node);
beltItem.Disposed += _ => { node.QueueFree(); _items.Remove(beltItem); };
node.Transform = newTransform;
return;
}
node.Transform = newTransform;
}
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => UpdateTransform(beltItem, newTransform, .25f);
}
public partial class TestItemRendered : Node3D, IItemRenderer
{
public Dictionary<IBeltItem, Node3D> _items = [];

View File

@@ -1,14 +1,11 @@
namespace ChickenGameTest;
using Chickensoft.Introspection;
using Chickensoft.AutoInject;
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using Chickensoft.Sync.Primitives;
using SJK.Functional;
using System.Diagnostics.CodeAnalysis;
public interface IMovementConveyor
{

View File

@@ -0,0 +1,70 @@
using Godot;
using System;
namespace FoodFactory;
using Arch.Core;
using ChickenGameTest;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using FoodFactory.Recipes;
using Godot;
[Meta(typeof(IAutoNode))]
public partial class ItemSpawner : Node3D, IProvide<IBeltPortHost>
{
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
[Dependency] public IBlueprintManger ItemFactory => this.DependOn<IBlueprintManger>();
[Dependency] public World World => this.DependOn<World>();
[Dependency] public IRecipes Recipes => this.DependOn<IRecipes>();
private BeltPortHost _insertLogic = default!;
public IBeltPortHost Value() => _insertLogic;
private VoxelGuid _guid;
public GridTransform3D VoxelTransform
{
get => GridTransform3D.FromGodot(GlobalTransform);
set => GlobalTransform = value.ToGodot();
}
[Export] public string ItemName { get; set; } = default!;
public override void _Ready()
{
_insertLogic = new BeltPortHost
{
Default = new DelegateInsertBeltItemLogic((_, _) => false, (_, _) => false)
};
var timer = new Timer() { Autostart = true, WaitTime = 1f };
AddChild(timer);
timer.Timeout += Tick;
this.Provide();
}
public void OnResolved() => _guid = GridRegistry.Register(this, VoxelTransform.Origin);
public override void _ExitTree() => GridRegistry.UnRegister(_guid);
public void Tick()
{
var ports = _insertLogic.GetPorts();
foreach (var item in ports)
{
var port = item.GetPortFacing(GridRegistry);
if (!port.HasValue(out var beltPort))
{
continue;
}
var itemBlueprint = ItemFactory.GetBlueprint(ItemName);
var dummyItem = new TestItem();
if (beltPort.CanAccept(dummyItem, LaneSpan.One, 0))
{
var ctx = new BlueprintContext() { BluePrintId = new BlueprintId(itemBlueprint), World = World };
dummyItem.Item = itemBlueprint.Factory(ctx);
if (!beltPort.TryInsert(dummyItem, LaneSpan.One, 0))
{
World.Destroy(dummyItem.Item);//TODO this should not call, but not sure
GD.PushWarning("Item failed to insert into port and removed item, item was destroyed but make sure item to prevent overhead.");
}
}
}
}
}

View File

@@ -0,0 +1 @@
uid://btmbv7oaeps4p

View File

@@ -0,0 +1,17 @@
[gd_scene format=3 uid="uid://bktqs1lw6go4"]
[ext_resource type="Script" uid="uid://btmbv7oaeps4p" path="res://src/VoxelGrid/ItemSpawner.cs" id="1_3dxrm"]
[ext_resource type="PackedScene" uid="uid://bfwvlnurlcbwk" path="res://assets/kenney_conveyor-kit/Models/GLB format/cover-hopper.glb" id="2_552yl"]
[ext_resource type="Script" uid="uid://ee5aoxi8mjnw" path="res://src/VoxelGrid/BeltPort.cs" id="3_0b0h5"]
[node name="ItemSpawner" type="Node3D" unique_id=966349707]
script = ExtResource("1_3dxrm")
[node name="cover-hopper2" parent="." unique_id=2099493372 instance=ExtResource("2_552yl")]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0, 0)
[node name="Node3D" type="Node3D" parent="." unique_id=430904887]
script = ExtResource("3_0b0h5")
Face = 4
Width = 1
Access = 2

View File

@@ -2,38 +2,50 @@ namespace ChickenGameTest;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Arch.Core;
using Arch.Core.Extensions;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using FoodFactory;
using FoodFactory.Items;
using FoodFactory.Recipes;
using Godot;
using SJK.Functional;
[Meta(typeof(IAutoNode))]
public partial class OvenTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxelGridRegistry>
{
private BeltPortHost _insertLogic;
public IBeltPortHost Value() => _insertLogic;
public override void _Notification(int what) => this.Notify(what);
private BeltPortHost _insertLogic = default!;
public IBeltPortHost Value() => _insertLogic;
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
[Dependency] public IRecipes Recipes => this.DependOn<IRecipes>();
IVoxelGridRegistry IProvide<IVoxelGridRegistry>.Value() => GridRegistry;
public GridTransform3D VoxelTransform
{
get => GridTransform3D.FromGodot(GlobalTransform);
set => GlobalTransform = value.ToGodot();
}
private Dictionary<string, object>? _placeholderItem = null;
private VoxelGuid _guid = default;
private Entity _itemBeingHeld = Entity.Null;
private VoxelGuid _guid;
public void OnResolved()
{
_insertLogic = new BeltPortHost();
_insertLogic.Bind(port => port is BeltPort beltPort && beltPort.PortName == "Input", () => new DelegateInsertBeltItemLogic(canAccept, canInsert));
_insertLogic.Bind(port => port is BeltPort beltPort && beltPort.PortName == "OutPut", () => new DelegateInsertBeltItemLogic((_, _) => false, (_, _) => false));
bool canAccept(IBeltPort port, IBeltItem item) => _placeholderItem is null;
bool canAccept(IBeltPort port, IBeltItem item) => _itemBeingHeld.Id == -1 && !_itemBeingHeld.IsAlive();
bool canInsert(IBeltPort beltPort, IBeltItem item)
{
_placeholderItem = (item as TestItem).PlaceHolderData;
item.Dispose();
GD.Print("Added Item");
return true;
if (item is IBeltItemData<Entity> itemData)
{
_itemBeingHeld = itemData.GetItem();
item.Dispose();
GD.Print("Added Item");
return true;
}
throw new NotImplementedException();
}
_guid = GridRegistry.Register(this, VoxelTransform.Origin);
this.Provide();
@@ -43,22 +55,54 @@ public partial class OvenTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxel
time.Timeout += () =>
{
if (_placeholderItem is null)
if (_itemBeingHeld.Id == -1 && !_itemBeingHeld.IsAlive())//TODO have a better way of dertming if item is valid, possibly nullable
{
return;
}
_placeholderItem.GetValue("temp").IfSome(some => _placeholderItem["temp"] = (float)some + 1);
if (_placeholderItem.GetValue("temp").Map(f => ((float)f) < 5).Or(true))
if (!_itemBeingHeld.TryGet<Temperature>(out var temp))
{
return;
}
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
if (port.HasValue(out var v) && _placeholderItem is not null && v.TryInsert(new TestItem() { PlaceHolderData = _placeholderItem }, LaneSpan.One, 0))
temp.Kelvin += new TemperatureDelta(5f, TemperatureUnit.Fahrenheit).KelvinDelta;
_itemBeingHeld.Set(temp);
Span<Entity> items = [_itemBeingHeld];
var context = new RecipeContext(World.Worlds[0], items);
var builder = new RecipeResultBuilder(stackalloc bool[10], new ItemBuilder[10]);
Span<int> mapping = stackalloc int[1];
var recipes = Recipes.GetRecipes(new RecipeAction("cook"), 1, [_itemBeingHeld.Get<Tags>()], [], RecipeOutput.Any, mapping);
// Span<Entity> created = stackalloc Entity[10];
unsafe
{
_placeholderItem = null;
while (RecipeProcessor.TryProcess(
ref recipes,
ref context,
ref builder,
&DestroyEntity,
out var created
))
{
Debug.Assert(created.Length <= 1);
_itemBeingHeld = created[0];
break;
}
}
if (_itemBeingHeld.TryGet<Tags>(out var tags) && tags.Contains(TagRegistry.GetTag("cooked")))
{
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
if (port.HasValue(out var v) && _itemBeingHeld.Id != -1 && _itemBeingHeld.IsAlive() && v.TryInsert(new TestItem() { Item = _itemBeingHeld }, LaneSpan.One, 0))
{
_itemBeingHeld = Entity.Null;
}
}
};
}
private static void DestroyEntity(Entity entity) => World.Worlds[entity.WorldId].Destroy(entity);
public override void _ExitTree()
{
GridRegistry.UnRegister(_guid);
@@ -79,3 +123,123 @@ public class DelegateInsertBeltItemLogic : IBeltItemInsertLogic
public bool CanAccept(IBeltPort beltPort, IBeltItem item) => _canAccept(beltPort, item);
public bool CanInsert(IBeltPort beltPort, IBeltItem item) => _canInsert(beltPort, item);
}
public readonly ref struct RecipeMatch
{
public readonly Recipe Recipe;
public readonly ReadOnlySpan<int> Mapping;
public RecipeMatch(
Recipe recipe,
ReadOnlySpan<int> mapping)
{
Recipe = recipe;
Mapping = mapping;
}
}
public interface IRecipeEnumerator
{
RecipeMatch Current { get; }
bool MoveNext();
}
public unsafe ref struct RecipeEnumerator : IRecipeEnumerator
{
public readonly RecipeMatch Current => _match;
private RecipeMatch _match;
private int _index;
private readonly ReadOnlySpan<CompiledRecipe> _recipes;
private readonly RecipeInput _input;
private readonly Span<int> _mapping;
private readonly delegate*<RecipeInput, CompiledRecipe, Span<int>, out RecipeMatch, bool> _filter;
public RecipeEnumerator(ReadOnlySpan<CompiledRecipe> compiledRecipes, RecipeInput input, delegate*<RecipeInput, CompiledRecipe, Span<int>, out RecipeMatch, bool> filter, Span<int> mapping)
{
_recipes = compiledRecipes;
_input = input;
_index = -1;
_filter = filter;
_mapping = mapping;
}
public bool MoveNext()
{
_index++;
while (_index < _recipes.Length)
{
if (_filter(_input, _recipes[_index], _mapping, out _match))
{
break;
}
_index++;
}
return _index < _recipes.Length;
}
}
public static class RecipeProcessor
{
public static unsafe bool TryProcess<TRecipeEnumerator>(
ref TRecipeEnumerator recipes,
scoped ref RecipeContext context,
scoped ref RecipeResultBuilder builder,
delegate*<Entity, void> destroyEntity,
out Span<Entity> resultEntity)
where TRecipeEnumerator : IRecipeEnumerator, allows ref struct
{
resultEntity = default;
while (recipes.MoveNext())
{
var entry = recipes.Current;
var recipe = entry.Recipe;
if (!recipe.CanProcess(context))
{
continue;
}
var result = recipe.Process(context, ref builder);
ApplyResult(
in context,
result,
destroyEntity,
out resultEntity);
builder.Clear();
return true;
}
return false;
}
private static unsafe void ApplyResult(
scoped in RecipeContext context,
scoped RecipeResult result,
delegate*<Entity, void> destroyEntity,
out Span<Entity> created)
{
created = default;
result.Mutate?.Invoke(context);
created = new Entity[result.CreateLength];
for (int i = 0; i < result.CreateLength; i++)
{
created[i] = result.Create[i](in context);
}
if (result.RemoveLength > 0)
{
for (var i = 0; i < result.RemoveLength; i++)
{
if (result.Remove[i])
{
destroyEntity(context.Items[i]);
}
}
}
}
}

106
src/VoxelGrid/SlicerTest.cs Normal file
View File

@@ -0,0 +1,106 @@
namespace ChickenGameTest;
using System;
using System.Collections.Generic;
using System.Linq;
using Arch.Core;
using Arch.Core.Extensions;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using FoodFactory;
using FoodFactory.Items;
using FoodFactory.Recipes;
using Godot;
using SJK.Functional;
[Meta(typeof(IAutoNode))]
public partial class SlicerTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxelGridRegistry>
{
public override void _Notification(int what) => this.Notify(what);
private BeltPortHost _insertLogic = default!;
public IBeltPortHost Value() => _insertLogic;
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
[Dependency] public IRecipes Recipes => this.DependOn<IRecipes>();
IVoxelGridRegistry IProvide<IVoxelGridRegistry>.Value() => GridRegistry;
public GridTransform3D VoxelTransform
{
get => GridTransform3D.FromGodot(GlobalTransform);
set => GlobalTransform = value.ToGodot();
}
private Entity _itemBeingHeld = Entity.Null;
private List<Entity> sliced = [];
private VoxelGuid _guid;
public void OnResolved()
{
_insertLogic = new BeltPortHost();
_insertLogic.Bind(port => port is BeltPort beltPort && beltPort.PortName == "Input", () => new DelegateInsertBeltItemLogic(canAccept, canInsert));
_insertLogic.Bind(port => port is BeltPort beltPort && beltPort.PortName == "OutPut", () => new DelegateInsertBeltItemLogic((_, _) => false, (_, _) => false));
bool canAccept(IBeltPort port, IBeltItem item) => _itemBeingHeld.Id == -1 && !_itemBeingHeld.IsAlive() && item is IBeltItemData<Entity> beltItemData && beltItemData.GetItem().TryGet<Tags>(out var tags) && tags.Contains(TagRegistry.GetTag("sliceable"));
bool canInsert(IBeltPort beltPort, IBeltItem item)
{
if (item is IBeltItemData<Entity> itemData)
{
_itemBeingHeld = itemData.GetItem();
item.Dispose();
GD.Print("Added Item to slicer");
return true;
}
throw new NotImplementedException();
}
_guid = GridRegistry.Register(this, VoxelTransform.Origin);
this.Provide();
var time = new Timer() { Autostart = true, OneShot = false, WaitTime = .1 };
AddChild(time);
time.Timeout += () =>
{
if (sliced.Any())
{
var port = _insertLogic.GetPorts().FirstOrNone(port => port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry));
if (port.HasValue(out var v) && v.TryInsert(new TestItem() { Item = sliced[0] }, LaneSpan.One, 0))
{
GD.Print(sliced[0].Get<Carbohydrates>());
sliced.RemoveAt(0);
}
}
if (_itemBeingHeld.Id == -1)//TODO have a better way of dertming if item is valid, possibly nullable
{
return;
}
Span<Entity> items = [_itemBeingHeld];
var context = new RecipeContext(World.Worlds[0], items);
var builder = new RecipeResultBuilder(stackalloc bool[10], new ItemBuilder[10]);
Span<int> mapping = stackalloc int[1];
// var recipe = new PotatoCookRecipe();
var recipes = Recipes.GetRecipes("slice", 1, [_itemBeingHeld.Get<Tags>()], [], new RecipeOutput(2), mapping);
unsafe
{
while (RecipeProcessor.TryProcess(
ref recipes,
ref context,
ref builder,
&DestroyEntity,
out var created
))
{
_itemBeingHeld = Entity.Null;
sliced.AddRange(created);
break;
}
}
};
}
private static void DestroyEntity(Entity entity) => World.Worlds[entity.WorldId].Destroy(entity);
public override void _ExitTree()
{
GridRegistry.UnRegister(_guid);
base._ExitTree();
}
}

View File

@@ -0,0 +1 @@
uid://yec84plemjv1

View File

@@ -8,6 +8,8 @@ using System.Collections.Generic;
using System.Linq;
using Chickensoft.Sync.Primitives;
using SJK.Functional;
using FoodFactory.Recipes;
using Arch.Core;
public class Sorted1DList<T>
{
@@ -258,7 +260,6 @@ public partial class TestItemConveyor : Node, IMovementConveyor
var guid = GridRegistry.Register(item, [.. points]);
TreeExiting += () => GridRegistry.UnRegister(guid);
}
StartPort.TryInsert(new TestItem() { PlaceHolderData = new() { ["temp"] = 0f } }, LaneSpan.Shifted(LaneSpan.One, 0), 0);
}
//ChatGPT Assisted
public BeltObstacle GetDistanceToNextItem(
@@ -458,22 +459,22 @@ public partial class TestItemConveyor : Node, IMovementConveyor
public const float ITEMSIZE = .2f;
private const float ITEMHALFSIZE = ITEMSIZE / 2f;
public override void _Process(double delta)
{
if (StartPort is null || EndPort is null)
{
return;
}
foreach (var item in GetPorts())
{
for (int ii = 0; ii < item.Profile.Width; ii++)
{
DebugDraw3D.DrawLinePath(_square.Select(i => item.Profile.LocalOffset.ToGodot().TranslatedLocal(i).TranslatedLocal(new Vector3(ii, 0, 0)).Origin).ToArray(), (!Engine.IsEditorHint()) && GetPortFacing(item).HasValue() ? Colors.Green : Colors.Red);
// public override void _Process(double delta)
// {
// if (StartPort is null || EndPort is null)
// {
// return;
// }
// foreach (var item in GetPorts())
// {
// for (int ii = 0; ii < item.Profile.Width; ii++)
// {
// DebugDraw3D.DrawLinePath(_square.Select(i => item.Profile.LocalOffset.ToGodot().TranslatedLocal(i).TranslatedLocal(new Vector3(ii, 0, 0)).Origin).ToArray(), (!Engine.IsEditorHint()) && GetPortFacing(item).HasValue() ? Colors.Green : Colors.Red);
}
// DebugDraw3D.DrawArrow(item.Profile.LocalOffset.Origin, item.Profile.LocalOffset.LocalToWorld(item.Profile.Face.ToVector()),(!Engine.IsEditorHint())&&GetPortFacing(item).HasValue()?Colors.Green:Colors.Red);
}
}
// }
// // DebugDraw3D.DrawArrow(item.Profile.LocalOffset.Origin, item.Profile.LocalOffset.LocalToWorld(item.Profile.Face.ToVector()),(!Engine.IsEditorHint())&&GetPortFacing(item).HasValue()?Colors.Green:Colors.Red);
// }
// }
public ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT, LaneSpan laneSpan)
{

View File

@@ -2,36 +2,90 @@ namespace ChickenGameTest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Arch.Core;
using Arch.Core.Extensions;
using Arch.LowLevel;
using Arch.System;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using FoodFactory;
using FoodFactory.Recipes;
using Godot;
using Godot.Collections;
using SJK.Functional;
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.ParallelQuery(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>
public partial class VoxelGridNode : Node3D, IProvide<IVoxelGridRegistry>, IProvide<IItemRenderer>, IProvide<IRecipes>, IProvide<IBlueprintManger>, IProvide<World>
{
public override void _Notification(int what) => this.Notify(what);
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;
Group<float> _systems;
public override void _Ready()
{
// GD.Print();
base._Ready();
_voxelGridRegistry = new VoxelRegistry();
_itemRenderer = new TestItemRendered();
_itemRenderer = new ItemRenderSimple();
_blueprintManger = new BlueprintManger();
_recipes = new Recipes();
_world = World.Create();
_systems = new Group<float>("Items", new BacterialSystem(_world));
AddChild(_itemRenderer as Node);
Timer timer = new Timer() { WaitTime = .25f, Autostart = true };//TEST
AddChild(timer);//TEST
// timer.Timeout += _itemRenderer.Tick;//TEST
timer.Timeout += () =>
{
_systems.BeforeUpdate(.25f);
_systems.Update(.25f);
_systems.AfterUpdate(.25f);
};
_systems.Initialize();
this.Provide();
}
public override void _Input(InputEvent @event)
{
if (@event.IsActionPressed("ui_up"))
@@ -46,4 +100,51 @@ public partial class VoxelGridNode : Node3D, IProvide<IVoxelGridRegistry>, IProv
}
}
}
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);
}
}

View File

@@ -2,9 +2,12 @@
[ext_resource type="Script" uid="uid://dcrb286hmpli" path="res://src/VoxelGrid/VoxelGridNode.cs" id="1_tsdpe"]
[ext_resource type="Script" uid="uid://cnkblltup5guy" path="res://src/VoxelGrid/OvenTest.cs" id="3_r7dgx"]
[ext_resource type="PackedScene" uid="uid://bktqs1lw6go4" path="res://src/VoxelGrid/ItemSpawner.tscn" id="5_mxaon"]
[ext_resource type="PackedScene" uid="uid://h00mq2srsbfa" path="res://assets/kenney_conveyor-kit/Models/GLB format/door.glb" id="5_wk2t5"]
[ext_resource type="Script" uid="uid://ee5aoxi8mjnw" path="res://src/VoxelGrid/BeltPort.cs" id="6_2wkfx"]
[ext_resource type="PackedScene" uid="uid://c4h7mwnfrdesg" path="res://src/Conveyors/ConveyorBeltStraight/ConveyorBeltStraight.tscn" id="6_mxaon"]
[ext_resource type="Script" uid="uid://opbkqoaa7x2n" path="res://src/VoxelGrid/Balancer.cs" id="7_2wkfx"]
[ext_resource type="Script" uid="uid://yec84plemjv1" path="res://src/VoxelGrid/SlicerTest.cs" id="8_2lg7i"]
[sub_resource type="Curve3D" id="Curve3D_mxaon"]
_data = {
@@ -13,6 +16,9 @@ _data = {
}
point_count = 2
[sub_resource type="BoxMesh" id="BoxMesh_2wkfx"]
size = Vector3(1, 1, 2)
[node name="VoxelGridNode" type="Node3D" unique_id=825696340]
script = ExtResource("1_tsdpe")
@@ -20,108 +26,123 @@ script = ExtResource("1_tsdpe")
transform = Transform3D(0.6279494, -0.32487354, 0.70720345, -8.896721e-09, 0.908705, 0.4174389, -0.77825415, -0.26213053, 0.5706208, 4.635174, 2.6038146, 2.739409)
[node name="ConveyorBeltStraight5" parent="." unique_id=975225936 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 3, 0, 0)
Width = 1
[node name="ConveyorBeltStraight22" parent="." unique_id=81137442 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 2, 0, 0)
Width = 1
[node name="ConveyorBeltStraight13" parent="." unique_id=607296315 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 0, 0, 0)
Width = 1
[node name="ConveyorBeltStraight26" parent="." unique_id=626657643 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 1, 0, 0)
Width = 1
[node name="ConveyorBeltStraight6" parent="." unique_id=264656404 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -1, 0, 0)
Width = 1
[node name="ConveyorBeltStraight23" parent="." unique_id=1438072389 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -2, 0, 0)
Width = 1
[node name="ConveyorBeltStraight14" parent="." unique_id=1045608025 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -4, 0, 0)
Width = 1
[node name="ConveyorBeltStraight27" parent="." unique_id=899289297 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -3, 0, 0)
Width = 1
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 0, 0, 0)
[node name="ConveyorBeltStraight7" parent="." unique_id=1602707514 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 3, 0, 1)
Width = 1
transform = Transform3D(-1, 0, -8.742277e-08, 0, 1, 0, 8.742277e-08, 0, -1, 1, 0, 1)
[node name="ConveyorBeltStraight8" parent="." unique_id=1032197109 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 1, 0, 2)
[node name="ConveyorBeltStraight24" parent="." unique_id=574723066 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 1, 0, 6)
[node name="ConveyorBeltStraight25" parent="." unique_id=1584944068 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 0, 0, 6)
[node name="ConveyorBeltStraight13" parent="." unique_id=490179430 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 3, 0, 2)
[node name="ConveyorBeltStraight20" parent="." unique_id=1684880263 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 4, 0, 2)
[node name="ConveyorBeltStraight15" parent="." unique_id=1971663524 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 3, 0, 3)
[node name="ConveyorBeltStraight23" parent="." unique_id=830278017 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 4, 0, 3)
[node name="ConveyorBeltStraight9" parent="." unique_id=1449572266 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 1)
Width = 1
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 3, 0, 1)
[node name="ConveyorBeltStraight26" parent="." unique_id=181268712 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 5)
[node name="ConveyorBeltStraight27" parent="." unique_id=1546024538 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 6)
[node name="ConveyorBeltStraight28" parent="." unique_id=1945842047 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 4)
[node name="ConveyorBeltStraight29" parent="." unique_id=116753972 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 8)
[node name="ConveyorBeltStraight30" parent="." unique_id=903050872 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 9)
[node name="ConveyorBeltStraight31" parent="." unique_id=848458617 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 7)
[node name="ConveyorBeltStraight32" parent="." unique_id=889036013 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 6, 0, 8)
[node name="ConveyorBeltStraight33" parent="." unique_id=992629708 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 6, 0, 9)
[node name="ConveyorBeltStraight34" parent="." unique_id=795974742 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 7, 0, 9)
[node name="ConveyorBeltStraight35" parent="." unique_id=601890089 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 6, 0, 5)
[node name="ConveyorBeltStraight36" parent="." unique_id=2134741630 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 7, 0, 4)
[node name="ConveyorBeltStraight37" parent="." unique_id=196786809 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 6, 0, 4)
[node name="ConveyorBeltStraight10" parent="." unique_id=68139731 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 6, 0, 1)
Width = 1
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 4, 0, 1)
[node name="ConveyorBeltStraight11" parent="." unique_id=358641682 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 7, 0, 1)
Width = 1
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 1)
[node name="ConveyorBeltStraight12" parent="." unique_id=310565328 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 8, 0, 1)
Width = 1
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 6, 0, 1)
[node name="ConveyorBeltStraight17" parent="." unique_id=111805683 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 9, 0, 1)
Width = 1
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 7, 0, 1)
[node name="ConveyorBeltStraight21" parent="." unique_id=764425478 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 7, 0, 2)
[node name="ConveyorBeltStraight22" parent="." unique_id=1089887693 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 7, 0, 3)
[node name="ConveyorBeltStraight18" parent="." unique_id=971476244 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 9, 0, 0)
Width = 1
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 7, 0, 0)
[node name="ConveyorBeltStraight19" parent="." unique_id=87035771 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 9, 0, -1)
Width = 1
[node name="ConveyorBeltStraight19" parent="." unique_id=1940918727 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 8, 0, -3)
[node name="ConveyorBeltStraight24" parent="." unique_id=904248251 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 2, 0, 1)
Width = 1
[node name="ConveyorBeltStraight40" parent="." unique_id=1578916497 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 8, 0, -2)
[node name="ConveyorBeltStraight15" parent="." unique_id=533235542 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 0, 0, 1)
Width = 1
[node name="ConveyorBeltStraight41" parent="." unique_id=1562444632 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 8, 0, -4)
[node name="ConveyorBeltStraight28" parent="." unique_id=2116943332 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 1, 0, 1)
Width = 1
[node name="ConveyorBeltStraight38" parent="." unique_id=1520289120 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 7, 0, -3)
[node name="ConveyorBeltStraight8" parent="." unique_id=1109978762 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -1, 0, 1)
Width = 1
[node name="ConveyorBeltStraight25" parent="." unique_id=1110001269 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -2, 0, 1)
Width = 1
[node name="ConveyorBeltStraight39" parent="." unique_id=44974977 instance=ExtResource("6_mxaon")]
transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 7, 0, -4)
[node name="ConveyorBeltStraight16" parent="." unique_id=1178816500 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -4, 0, 1)
Width = 1
[node name="ConveyorBeltStraight29" parent="." unique_id=1537356869 instance=ExtResource("6_mxaon")]
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, -3, 0, 1)
Width = 1
transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 0, 0, 1)
[node name="OvenTest" type="Node3D" parent="." unique_id=824176002]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4, 0, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, 0)
script = ExtResource("3_r7dgx")
[node name="Node3D2" type="Node3D" parent="OvenTest" unique_id=1815100025 node_paths=PackedStringArray("Path")]
[node name="Node3D2" type="Node3D" parent="OvenTest" unique_id=1815100025]
transform = Transform3D(1.3113416e-07, 0, 1, 0, 1, 0, -1, 0, 1.3113416e-07, 0, 0, 0)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 3
Path = NodePath("Path3D")
Access = 1
PortName = "Input"
[node name="Path3D" type="Path3D" parent="OvenTest/Node3D2" unique_id=1525648764]
@@ -130,13 +151,12 @@ curve = SubResource("Curve3D_mxaon")
[node name="door3" parent="OvenTest/Node3D2" unique_id=351624885 instance=ExtResource("5_wk2t5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.870017e-08, 0, -0.6001501)
[node name="Node3D3" type="Node3D" parent="OvenTest" unique_id=1033202746 node_paths=PackedStringArray("Path")]
[node name="Node3D3" type="Node3D" parent="OvenTest" unique_id=1033202746]
transform = Transform3D(-2.1855693e-07, 0, -1, 0, 1, 0, 1, 0, -2.1855693e-07, 0, 0, 1)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 3
Path = NodePath("Path3D2")
Access = 2
PortName = "OutPut"
[node name="Path3D2" type="Path3D" parent="OvenTest/Node3D3" unique_id=739562310]
@@ -144,3 +164,115 @@ curve = SubResource("Curve3D_mxaon")
[node name="door4" parent="OvenTest/Node3D3" unique_id=1602265662 instance=ExtResource("5_wk2t5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.9604645e-08, 0, -0.3998499)
[node name="door5" parent="OvenTest/Node3D3" unique_id=102838222 instance=ExtResource("5_wk2t5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.9604645e-08, 0, -0.3998499)
[node name="ItemSpawner" parent="." unique_id=966349707 instance=ExtResource("5_mxaon")]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, -1.0023941, 0, 0.0019463301)
ItemName = "Potato"
[node name="ItemSpawner2" parent="." unique_id=405829395 instance=ExtResource("5_mxaon")]
transform = Transform3D(3.059797e-07, 0, -1, 0, 1, 0, 1, 0, 3.059797e-07, -1, 0, 1)
ItemName = "Onion"
[node name="ItemSpawner3" parent="." unique_id=422927887 instance=ExtResource("5_mxaon")]
transform = Transform3D(3.059797e-07, 0, -1, 0, 1, 0, 1, 0, 3.059797e-07, -1, 0, 6)
ItemName = "Potato"
[node name="Balancer" type="Node3D" parent="." unique_id=619243985]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, 2)
script = ExtResource("7_2wkfx")
[node name="Node3D" type="Node3D" parent="Balancer" unique_id=793236513]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0, 0)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 1
[node name="Node3D3" type="Node3D" parent="Balancer" unique_id=1846433120]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 0, 0, 0)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 2
[node name="Node3D4" type="Node3D" parent="Balancer" unique_id=1579207766]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 0, 0, 1)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 1
[node name="Node3D2" type="Node3D" parent="Balancer" unique_id=717100096]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0, 1)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 2
[node name="MeshInstance3D" type="MeshInstance3D" parent="Balancer" unique_id=383290173]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5)
mesh = SubResource("BoxMesh_2wkfx")
[node name="Balancer8" type="Node3D" parent="." unique_id=1783081047]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 7, 0, -1)
script = ExtResource("7_2wkfx")
[node name="Node3D" type="Node3D" parent="Balancer8" unique_id=1818940385]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0, 0)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 1
[node name="Node3D3" type="Node3D" parent="Balancer8" unique_id=51764402]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 0, 0, 0)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 2
[node name="Node3D4" type="Node3D" parent="Balancer8" unique_id=233486636]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 0, 0, 1)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 1
[node name="Node3D2" type="Node3D" parent="Balancer8" unique_id=1446214840]
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0, 1)
script = ExtResource("6_2wkfx")
Face = 4
Width = 1
Access = 2
[node name="MeshInstance3D" type="MeshInstance3D" parent="Balancer8" unique_id=556689065]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5)
mesh = SubResource("BoxMesh_2wkfx")
[node name="Slicer" type="Node3D" parent="." unique_id=566544377]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7, 0, -2)
script = ExtResource("8_2lg7i")
[node name="Node3D4" type="Node3D" parent="Slicer" unique_id=1206144064]
transform = Transform3D(-1, 0, 8.742277e-08, 0, 1, 0, -8.742277e-08, 0, -1, 0, 0, 0)
[node name="Path3D" type="Path3D" parent="Slicer/Node3D4" unique_id=894348025]
curve = SubResource("Curve3D_mxaon")
[node name="door3" parent="Slicer/Node3D4" unique_id=1024066398 instance=ExtResource("5_wk2t5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.870017e-08, 0, -0.6001501)
[node name="Node3D5" type="Node3D" parent="Slicer" unique_id=1113082191]
transform = Transform3D(1, 0, -1.7484555e-07, 0, 1, 0, 1.7484555e-07, 0, 1, 0, 0, 0)
[node name="Path3D2" type="Path3D" parent="Slicer/Node3D5" unique_id=1352328131]
curve = SubResource("Curve3D_mxaon")
[node name="door4" parent="Slicer/Node3D5" unique_id=1411233670 instance=ExtResource("5_wk2t5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.9604645e-08, 0, -0.3998499)
[node name="door5" parent="Slicer/Node3D5" unique_id=770270315 instance=ExtResource("5_wk2t5")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.9604645e-08, 0, -0.3998499)

View File

@@ -1,13 +1,9 @@
namespace ChickenGameTest;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Chickensoft.AutoInject;
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using ChickenGameTest;
using SJK.Functional;
public interface IVoxelGridRegistry