This commit is contained in:
2026-04-15 22:22:56 -04:00
commit 5906f248f4
90 changed files with 6345 additions and 0 deletions

268
src/Items/Item.cs Normal file
View File

@@ -0,0 +1,268 @@
namespace ChickenGameTest;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Godot;
/// <summary>
/// Items that can be on a belt
/// </summary>
public interface IBeltItem : IDisposable
{
int Width { get; }
int Height { get; }
Node3D CreateItemVisual();
delegate void ItemRemoved(IBeltItem item);
event ItemRemoved Disposed;
}
public class TestItem() : IBeltItem
{
public float Temp {get;set;}
public int Width {get;set;}
public int Height {get;set;}
public Node3D CreateItemVisual() => new MeshInstance3D(){Mesh = new BoxMesh(){Size = new(.1f,.1f,.1f)}};
bool _disposed;
public event IBeltItem.ItemRemoved Disposed;
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
Disposed?.Invoke(this);
}
}
public interface IFluidItem
{
float Volume { get; }
}
public interface IConveyorProfile
{
int LaneCount { get; }
bool LaneSwitching { get; }
}
public readonly record struct BeltPortProfile(
GridTransform3D LocalOffset,
// Direction Face,
int Width,
PortAccess Access
) : IBeltSlotProfile
{
public Vector3I Position => LocalOffset.Origin;
// Direction IBeltSlotProfile.Direction => Face;
}
public interface IBeltPort
{
BeltPortProfile Profile { get; }
bool CanAccept(
IBeltItem item,
LaneSpan laneSpan,
float beltT
);
bool TryInsert(
IBeltItem item,
LaneSpan laneSpan,
float beltT
);
IEnumerable<Vector3I> Points()
{
for (int i = 0; i < Profile.Width; i++)
{
yield return (Profile.LocalOffset.Right * i) + Profile.LocalOffset.Origin;
}
}
LaneSpan LaneSpan => new LaneSpan(0,(ushort)Profile.Width);
}
public sealed class ConveyorPort : IBeltPort
{
public BeltPortProfile Profile { get; }
public IMovementConveyor Conveyor { get; }
public ItemConveyor.BeltT BeltT { get; }
private readonly Func<IBeltItem, float, LaneSpan, bool>? _canAccept;
private readonly Func<IBeltItem, float, LaneSpan, bool>? _accept;
public ConveyorPort(
IMovementConveyor conveyor,
BeltPortProfile profile,
ItemConveyor.BeltT beltT,
Func<IBeltItem, float, LaneSpan, bool>? canAccept,
Func<IBeltItem, float, LaneSpan, bool>? accept)
{
Conveyor = conveyor;
Profile = profile;
BeltT = beltT;
_canAccept = canAccept;
_accept = accept;
}
public bool CanAccept(
IBeltItem item,
LaneSpan laneSpan,
float beltT)
{
if (!Profile.Access.HasFlag(PortAccess.In))
{
return false;
}
if (_canAccept is null || _accept is null)
{
return false;
}
// LaneSpan checks live here, not in delegates
if (!LaneSpan.Encapsulates(
new LaneSpan(0, (ushort)Profile.Width),
laneSpan))
{
return false;
}
return _canAccept(item, beltT, laneSpan);
}
public bool TryInsert(
IBeltItem item,
LaneSpan laneSpan,
float beltT)
{
if (!CanAccept(item, laneSpan, beltT))
{
return false;
}
return _accept!(item, beltT, laneSpan);
}
}
public interface IBeltSlotProfile
{
Vector3I Position { get; }
// Direction Direction { get; }
int Width { get; }
PortAccess Access { get; }
// bool CanAcceptItem(IBeltItem beltItem, LaneSpan laneSpan, float beltT = 0);
// /// <summary>
// ///Tries to insert an belt item offset by laneSpan,
// /// </summary>
// /// <param name="beltItem"></param>
// /// <param name="laneSpan"></param>
// /// <param name="beltT"></param>
// /// <returns></returns>
// bool TryInsertItem(IBeltItem beltItem, LaneSpan laneSpan, float beltT = 0);
}
public record LaneId(int Index);
[Flags]
public enum PortAccess : byte
{
// Disabled = 0,
In = 1,
Out = 2,
InOut = In | Out,
BiDirectional = InOut
}
[Flags]
public enum TransferMode : byte//Need Better Name
{
Passive = 0,
Push = 1,
Pull = 2,
PushPull = Push | Pull
}
public static class SlotExtesion
{
public static LaneSpan MapLaneSpanToFacingPort(this IBeltPort self, IBeltPort other) => MapSlotToFacingSlot(self.Profile.LocalOffset, self.Profile.Width, other.Profile.LocalOffset, other.Profile.Width);
public static LaneSpan MapSlotToFacingSlot(
GridTransform3D fromTx,
int fromWidth,
GridTransform3D toTx,
int toWidth)
{
ushort minLane = ushort.MaxValue;
ushort maxLane = ushort.MinValue;
// In port-local space:
// +Z = forward
// +X = width axis
// origin = port center / lane 0 start
// (adjust if your convention differs)
for (int i = 0; i < fromWidth; i++)
{
var fromLocalLane = new Vector3I(i, 0, 0);
var fromLocalFacing = fromLocalLane + Vector3I.Forward;
var worldFacing = fromTx.LocalToWorld(fromLocalFacing);
var toLocal = toTx.WorldToLocal(worldFacing);
int laneIndex = toLocal.X;
bool inWidth = laneIndex >= 0 && laneIndex < toWidth;
bool onFacePlane = toLocal.Z == 0; // directly entering target face
if (!inWidth || !onFacePlane)
continue;
minLane = (ushort)Math.Min(minLane, laneIndex);
maxLane = (ushort)Math.Max(maxLane, laneIndex);
}
if (minLane > maxLane)
return LaneSpan.Zero;
return new LaneSpan(minLane, (ushort)(maxLane + 1));
}
// Map a sub-span from 'from' port into the 'to' port space
public static LaneSpan MapLaneSpan(this IBeltPort from, IBeltPort to, LaneSpan incoming)
{
ushort minLane = ushort.MaxValue;
ushort maxLane = ushort.MinValue;
// Loop over each lane in the incoming span
for (int i = incoming.Start; i < incoming.End; i++)
{
// from-local lane
var fromLocalLane = new Vector3I(i, 0, 0);
// shift forward along +Z to "face" the target
var fromLocalFacing = fromLocalLane + Vector3I.Forward;
// world space
var worldFacing = from.Profile.LocalOffset.LocalToWorld(fromLocalFacing);
// target local space
var toLocal = to.Profile.LocalOffset.WorldToLocal(worldFacing);
int laneIndex = toLocal.X;
bool inWidth = laneIndex >= 0 && laneIndex < to.Profile.Width;
bool onFacePlane = toLocal.Z == 0; // entering target face
if (!inWidth || !onFacePlane)
continue;
minLane = (ushort)Math.Min(minLane, laneIndex);
maxLane = (ushort)Math.Max(maxLane, laneIndex);
}
if (minLane > maxLane)
return LaneSpan.Zero;
return new LaneSpan(minLane, (ushort)(maxLane + 1));
}
}

1
src/Items/Item.cs.uid Normal file
View File

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