Added Arch Persitance and Relationships, styackable items, hot fixed Tags.containsAll. AutoList in Ordered!DList

This commit is contained in:
2026-05-16 12:44:51 -04:00
parent bf4d856608
commit 75c93f0c43
40 changed files with 52609 additions and 197 deletions

View File

@@ -12,8 +12,8 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
{
public override void _Notification(int what) => this.Notify(what);
[Export] public int Width { get; set; } = default;
public IVoxelGridRegistry Value() => Grid;
[Dependency] protected IVoxelGridRegistry Grid => this.DependOn<IVoxelGridRegistry>();
public IVoxelGridRegistry Value() => FoodFactoryApi.GridRegistry;
[Dependency] protected IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
private VoxelGuid _guid = default;
public GridTransform3D VoxelTransform
{
@@ -62,13 +62,13 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
}
public void OnResolved()
{
_guid = Grid.Register(this, VoxelTransform.Origin);
_guid = FoodFactoryApi.GridRegistry.Register(this, VoxelTransform.Origin);
this.Provide();
}
public override void _ExitTree()
{
Grid.UnRegister(_guid);
FoodFactoryApi.GridRegistry.UnRegister(_guid);
base._ExitTree();
}
}

View File

@@ -1,10 +1,10 @@
[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"]
[ext_resource type="Script" uid="uid://j24fuotdwwx4" path="res://src/Conveyors/TestItemConveyor.cs" id="2_1vr1d"]
[ext_resource type="PackedScene" uid="uid://bapdy1c6rb4qr" path="res://assets/kenney_conveyor-kit/Models/GLB format/conveyor-sides.glb" id="2_mn8ro"]
[ext_resource type="PackedScene" uid="uid://ccuhhy2oa8xvm" path="res://assets/kenney_conveyor-kit/Models/GLB format/arrow-basic.glb" id="3_1vr1d"]
[ext_resource type="Script" uid="uid://bjuntmf2sjynp" path="res://src/VoxelGrid/ConveyorItemRender.cs" id="4_g7n1f"]
[ext_resource type="Script" uid="uid://bjuntmf2sjynp" path="res://src/Conveyors/ConveyorItemRender.cs" id="4_g7n1f"]
[sub_resource type="Curve3D" id="Curve3D_x5qdr"]
_data = {

View File

@@ -3,10 +3,14 @@ namespace FoodFactory.Conveyors;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Arch.Core;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Chickensoft.Sync.Primitives;
using CommunityToolkit.HighPerformance.Helpers;
using FoodFactory.Items;
using Godot;
using SJK.Math;
[Meta(typeof(IAutoNode))]
public partial class ConveyorItemRender : Node
@@ -15,48 +19,69 @@ public partial class ConveyorItemRender : Node
[Export] protected Path3D Path3D { get; set; } = default!;
[Export] protected TestItemConveyor ItemConveyor { get; set; } = default!;
[Chickensoft.AutoInject.Dependency] protected IItemRenderer Items => this.DependOn<IItemRenderer>();
[Chickensoft.AutoInject.Dependency] protected IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
// private Chickensoft.Sync.Primitives.AutoList<ConveyorSlice>.Binding _binding = default!;
public override async void _Ready()
private AutoList<Ordered1DList<IBeltItem>.Entry>.Binding _binding = default!;
public void OnResolved()
{
base._Ready();
if (!ItemConveyor.IsNodeReady())
{
await ToSignal(ItemConveyor, Node.SignalName.Ready);
}
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
ItemConveyor.GetChildren().OfType<Timer>().First().Timeout += () => offset = 0;
return;
// _binding = ItemConveyor.Items.Items.Bind();
// binding.OnRemove(callback =>
// {
// Items.Remove(callback.Item);
// // GD.PrintS(callback.Item, callback.BeltT);
// // if (itemsRenders.TryGetValue(callback.Item, out var node))
// // {
// // GD.PrintS(callback.Item, callback.BeltT,node);
// // itemsRenders.Remove(callback.Item);
// // node.QueueFree();
// // }
// });
// _binding.OnAdd((i, v) => Items.UpdateTransform(i.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(i.BeltT)));
// _binding.OnUpdate((a, b) => Items.UpdateTransform(a.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(b.BeltT).Translated(-Path3D.Curve.SampleBakedWithRotation(b.BeltT).Basis.X * b.LaneSpan.Start)));
}
float offset = 0;
public override void _Process(double delta)
{
offset += (float)delta;
base._Process(delta);
var e = ItemConveyor.EnumerateTowardStart();
while (e.MoveNext())
_binding = ItemConveyor.Items.Values.Bind().OnUpdate((old, updated) =>
{
Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position + (ItemConveyor.SignedSpeed * offset)));
}
if (old.Position == updated.Position)
{
return;
}
Items.UpdateTransform(updated.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(updated.Position), (float)delta);
});
// FoodFactoryApi.TickManger.GameTick += _ =>Process(_.Delta);// offset = 0;
FoodFactoryApi.TickManger.GameTick += _ => delta = _.Delta;// offset = 0;
}
// public override async void _Ready()
// {
// base._Ready();
// if (!ItemConveyor.IsNodeReady())
// {
// await ToSignal(ItemConveyor, Node.SignalName.Ready);
// }
// await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
// // ItemConveyor.GetChildren().OfType<Timer>().First().Timeout += () => offset = 0;
// return;
// // _binding = ItemConveyor.Items.Items.Bind();
// // binding.OnRemove(callback =>
// // {
// // Items.Remove(callback.Item);
// // // GD.PrintS(callback.Item, callback.BeltT);
// // // if (itemsRenders.TryGetValue(callback.Item, out var node))
// // // {
// // // GD.PrintS(callback.Item, callback.BeltT,node);
// // // itemsRenders.Remove(callback.Item);
// // // node.QueueFree();
// // // }
// // });
// // _binding.OnAdd((i, v) => Items.UpdateTransform(i.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(i.BeltT)));
// // _binding.OnUpdate((a, b) => Items.UpdateTransform(a.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(b.BeltT).Translated(-Path3D.Curve.SampleBakedWithRotation(b.BeltT).Basis.X * b.LaneSpan.Start)));
// }
float offset = 0;
double delta = 0;
// public void Process(double delta)
// {
// offset += (float)delta;
// base._Process(delta);
// var e = ItemConveyor.EnumerateTowardStart();
// while (e.MoveNext())
// {
// // Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position + (ItemConveyor.SignedSpeed * offset)));
// Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position), (float)delta);
// }
// }
protected override void Dispose(bool disposing)
{
// _binding.Dispose();
_binding.Dispose();
base.Dispose(disposing);
}
}
@@ -68,6 +93,58 @@ public interface IItemRenderer
void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time);
void Remove(IBeltItem beltItem);
}
public partial class ItemRenderBuffered : Node3D, IItemRenderer
{
private record struct ItemEntry(IBeltItem Item, Transform3D PreviousTransform, Transform3D NewTransform, float Elapsed, float Duration);
private List<ItemEntry> _current = [];
private List<ItemEntry> _next = [];
private Dictionary<IBeltItem, Node3D> _visuals = [];
public void Remove(IBeltItem beltItem) => throw new System.NotImplementedException();
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => throw new System.NotImplementedException();
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
{
var index = _current.FindIndex(f => f.Item == beltItem);
if (index >= 0)
{
_current[index] = _current[index] with { PreviousTransform = _current[index].NewTransform, NewTransform = newTransform, Elapsed = 0, Duration = time };
}
else
{
if (!_visuals.TryGetValue(beltItem, out var node3D))
{
_visuals[beltItem] = node3D = beltItem.CreateItemVisual();
node3D.Transform = newTransform;
AddChild(node3D);
beltItem.Disposed += _ => node3D.QueueFree();
}
// if (node3D.Transform.Origin.DistanceTo(newTransform.Origin) < .00001f)
// {
// return;
// }
_current.Add(new(beltItem, node3D.Transform, newTransform, 0, time));
}
}
public override void _Process(double delta)
{
_next.Clear();
GD.Print(_current.Count);
for (int i = 0; i < _current.Count; i++)
{
var entry = _current[i];
if (!IsInstanceValid(_visuals[entry.Item])){
_visuals.Remove(entry.Item);
continue;
}
entry.Elapsed = (float)(entry.Elapsed + delta);
_visuals[entry.Item].Transform = entry.PreviousTransform.InterpolateWith(entry.NewTransform, entry.Elapsed / entry.Duration);
if ((entry.Elapsed / entry.Duration) < 1f)
{
_next.Add(entry);
}
}
(_current, _next) = (_next, _current);
}
}
public partial class ItemRenderSimple : Node3D, IItemRenderer
{
private Dictionary<IBeltItem, Node3D> _items = [];

View File

@@ -25,6 +25,7 @@ public interface IMovementConveyor
float SignedSpeed { get; set; }
bool IsReversed { get; set; }
float Length { get; set; }
bool HasItems();
// float GetAvailableTravel(ItemConveyor.BeltDirection beltDirection, LaneSpanT itemSpan, float maxDistance);
// IBeltSlotProfile GetPortFacingStart();
// IBeltSlotProfile GetPortFacingEnd();
@@ -41,25 +42,6 @@ public interface IMovementConveyor
BeltObstacle GetDistanceToNextItem(BeltDirection beltDirection, float itemBeltT, float maxDistToCheck, LaneSpan laneSpan, HashSet<IMovementConveyor>? visted = null);
ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT, LaneSpan laneSpan);
}
public readonly struct ConveyorItemHandle
{
private readonly Action _remove;
private readonly Action<ConveyorSlice> _replace;
public int Index { get; }
public ConveyorSlice Slice { get; }
public IBeltItem Item => Slice.Item;
public LaneSpan Span => Slice.LaneSpan;
public float BeltT => Slice.BeltT;
public ConveyorItemHandle(int index, ConveyorSlice slice, Action remove, Action<ConveyorSlice> replace)
{
Index = index;
Slice = slice;
_remove = remove;
_replace = replace;
}
public void Remove() => _remove();
public void Replace(ConveyorSlice slice) => _replace(slice);
}
public interface IBeltMovement
{
void AdvanceBelt(IMovementConveyor conveyor, float delta);
@@ -112,7 +94,7 @@ public sealed class IndividualMovement : IBeltMovement
}
}
}
enumerator.SortAll();
// enumerator.SortAll();
}
}
public sealed class StrictMovement : IBeltMovement
@@ -208,9 +190,78 @@ public sealed class StrictMovement : IBeltMovement
// }
// }
public void AdvanceBelt(IMovementConveyor conveyor, float delta) => throw new NotImplementedException();
}
public void AdvanceBelt(IMovementConveyor conveyor, float delta)
{
// GD.Print(conveyor.Items);
if (conveyor.GetBeltDirection() == BeltDirection.NotMoving)
{
return;
}
if (!conveyor.HasItems())
{
return;
}
// GD.Print("hello");
var towardStart = conveyor.GetBeltDirection() == BeltDirection.TowardStart;
float maxMove = conveyor.SpeedMagnitude * delta;
var enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
BeltObstacle freeSpace = default!;
float lastPos = 0;
if (enumerator.MoveNext())
{
lastPos = enumerator.Current.Position;
freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),lastPos,maxMove,LaneSpan.One,[]);
}
var move = MathF.Min(maxMove, freeSpace.Distance);
if (lastPos > conveyor.Length || lastPos < 0)
{
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
while (enumerator.MoveNext() && (enumerator.Current.Position >= conveyor.Length || enumerator.Current.Position <= 0))
{
if (towardStart ? enumerator.Current.Position <= 0 : enumerator.Current.Position >= conveyor.Length)
{
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, LaneSpan.One, 0))
{
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
// GD.Print(itemRef.Item);
enumerator.Remove();
}
}
}
return;
}
// var freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),conveyor.GetLastItemPosition(),maxMove,LaneSpan.One,[]);
if (freeSpace.Distance <= 0)
{
return;
}
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
if (enumerator.MoveNext())
{
enumerator.MoveAllBy(MathF.Min(maxMove, freeSpace.Distance));
}
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
while (enumerator.MoveNext() && (enumerator.Current.Position >= conveyor.Length || enumerator.Current.Position <= 0))
{
if (towardStart ? enumerator.Current.Position <= 0 : enumerator.Current.Position >= conveyor.Length)
{
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, LaneSpan.One, 0))
{
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
// GD.Print(itemRef.Item);
enumerator.Remove();
}
}
}
}
}
public struct ConveyorSlice(IBeltItem item, float beltT = 0)
@@ -258,7 +309,12 @@ public record BeltTOffset(float T) : BeltT();
public static class ConveyorExtensions
{
public static BeltDirection Reverse(this BeltDirection beltDirection) => beltDirection switch
{
BeltDirection.TowardStart => BeltDirection.TowardEnd,
BeltDirection.TowardEnd => BeltDirection.TowardStart,
_ => throw new NotSupportedException(),
};
public static IOption<IBeltPort> GetPortFacing(this IBeltPort slot, IVoxelGridRegistry gridRegistry)
{
var toCheck = new List<Vector3I>();

View File

@@ -19,10 +19,12 @@ public partial class TestItemConveyor : Node, IMovementConveyor
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IBeltMovement MovementSystem => this.DependOn<IBeltMovement>(() => new IndividualMovement());
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
[Dependency] public IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
// private readonly AutoList<ConveyorSlice> _items = [];
// public IAutoList<ConveyorSlice> Items => _items;
// public readonly Sorted1DList<ConveyorSlice> Items = new(pos => pos.BeltT);
private readonly Ordered1DList<IBeltItem> _items = new();
public Ordered1DList<IBeltItem> Items => _items;
// private AutoList<ConveyorSlice>.Binding _itemsBinding;
public IBeltPort StartPort { get; set; }
// public IBeltSlotProfile StartPort => new ItemConveyor.ConveyorPort() {
@@ -52,7 +54,7 @@ public partial class TestItemConveyor : Node, IMovementConveyor
public IList<IBeltPort> OtherPorts = [];
// [Export] public Vector3I Position { get; set; } = default!;
//Speed per unit time
private readonly AutoValue<float> _speed = new(1);
private readonly AutoValue<float> _speed = new(.5f);
public IAutoValue<float> SpeedValue => _speed;
public float SignedSpeed
@@ -82,9 +84,9 @@ public partial class TestItemConveyor : Node, IMovementConveyor
{
return;
}
var timer = new Timer() { WaitTime = .05f, Autostart = true };
AddChild(timer);
timer.Timeout += () => MovementSystem.AdvanceBelt(this, (float)timer.WaitTime);
// var timer = new Timer() { WaitTime = .05f, Autostart = true };
// AddChild(timer);
FoodFactoryApi.TickManger.GameTick += args => MovementSystem.AdvanceBelt(this, (float)args.Delta);
if (StartPort is null || EndPort is null)
{
throw new Exception();
@@ -127,13 +129,15 @@ public partial class TestItemConveyor : Node, IMovementConveyor
}
// 2⃣ No local item → try crossing into adjacent conveyor
return GetDistanceAcrossPort(
var recursiveDistance = GetDistanceAcrossPort(
towardStart,
itemBeltT,
maxDistToCheck,
laneSpan,
visited
);
return recursiveDistance;// with { Distance = recursiveDistance.Distance + Length };
}
//ChatGPT Assisted
private IOption<ConveyorSlice> FindNextLocalItem(
@@ -412,6 +416,7 @@ public partial class TestItemConveyor : Node, IMovementConveyor
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardEnd() => _items.EnumerateTowardEnd(-1);
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardStart() => _items.EnumerateTowardStart(_items.Count);
public bool HasItems() => _items.Count > 0;
}
//TODO Should liklely account for max search distance where the conveyorm may be needed to know
public record class BeltObstacle(float Distance);