init
This commit is contained in:
268
src/Items/Item.cs
Normal file
268
src/Items/Item.cs
Normal 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
1
src/Items/Item.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dacksxt66lea4
|
||||
Reference in New Issue
Block a user