Added a RTS camera, and some assorted changes.

This commit is contained in:
2026-07-08 13:18:13 -04:00
parent 0fa7ba766b
commit c9ac4641a0
39 changed files with 982 additions and 211 deletions

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "assets"]
path = assets
url = http://192.168.1.4:3000/Ronnie/FoodFactoryAssets
url = https://gitea.superjrking.com/Ronnie/FoodFactoryAssets

View File

@@ -40,6 +40,8 @@
<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="GodotHelper" Version="0.0.2" />
<!-- <PackageReference Include="LanguageExt.Core" Version="5.0.0-beta-77" /> -->
<PackageReference Include="MessagePack" Version="3.1.4" />
<PackageReference Include="NCalc.LambdaCompilation" Version="5.12.0" />
<PackageReference Include="NCalcSync" Version="5.12.0" />

168
RTSCamera.cs Executable file
View File

@@ -0,0 +1,168 @@
using Godot;
using System;
using SJK.Math;
public partial class RTSCamera : Node3D
{
[Export]
public Camera3D camera;
Vector3 positionStartPos;
Vector3 positionOffsetPos;
Vector3 lastTransalation;
Vector3 cameraTargetOffset;
Action Update_CurrentFunc;
//Generic bookkeeping variables;
Vector2 LastMousePostition;
//Camera Dragging Bookkeeping variables
Vector3 LastMouseGroundPlanePositon;
//Camera Rotating Bookkeeping variables
float CameraRotateSpeed = .5f;
bool isDraging;
public override void _Ready()
{
Update_CurrentFunc = Update_DetectModeStart;
}
void Update_DetectModeStart()
{
if (Input.IsActionJustPressed("move_mouse"))
{
//Mouse Left went Down
//Does nothing basicly
}
else if (Input.IsActionPressed("move_mouse") && GetViewport().GetMousePosition() != LastMousePostition)
{
LastMouseGroundPlanePositon = camera.MouseToGroundPlane() ?? Vector3.Zero;
Update_CurrentFunc = Update_CameraDrag;
Update_CurrentFunc();
// GD.Print("1");
}
else if (Input.IsActionPressed("camera_rotate") && GetViewport().GetMousePosition() != LastMousePostition)
{
LastMouseGroundPlanePositon = camera.MouseToGroundPlane() ?? Vector3.Zero;
Update_CurrentFunc = Update_RotateCamera;
Update_CurrentFunc();
// GD.Print("2");
}
}
public override void _Process(double delta)
{
if (Input.IsActionJustPressed("ui_cancel"))
{
CancelUpdateFunc();
}
GD.PrintS(positionStartPos,positionOffsetPos,lastTransalation,LastMouseGroundPlanePositon,LastMousePostition,cameraTargetOffset,scrollAmount);
Update_CurrentFunc();
Update_CameraScroll((float)delta);
LastMousePostition = GetViewport().GetMousePosition();
camera.Fov = Mathf.Lerp(camera.Fov, Mathf.Lerp(90, 50, (camera.Position.Y - 5) / (80 - 5)), (float)delta * 10);
}
void CancelUpdateFunc()
{
Update_CurrentFunc = Update_DetectModeStart;
}
void Update_CameraDrag()
{
if (Input.IsActionJustReleased("move_mouse"))
{
CancelUpdateFunc();
}
var hitpos = camera.MouseToGroundPlane() ?? Vector3.Zero;
var diff = LastMouseGroundPlanePositon - hitpos;
GlobalTranslate(diff);
LastMouseGroundPlanePositon = hitpos = camera.MouseToGroundPlane() ?? Vector3.Zero;
}
float scrollAmount;
public override void _UnhandledInput(InputEvent @event)
{
scrollAmount += Input.IsActionJustReleased("mouse_scroll_up") ? 1 : 0;
scrollAmount += Input.IsActionJustReleased("mouse_scroll_down") ? -1 : 0;
if (@event is InputEventKey eventKey && eventKey.Keycode == Key.F)
{
GlobalPosition = Vector3.Zero;
camera.Position = Vector3.Zero;
CancelUpdateFunc();
}
}
void Update_CameraScroll(float delta)
{
// Zoom to scrollwheel
// float scrollAmount = Input.IsActionJustReleased("mouse_scroll_up")?1:0;
// scrollAmount += Input.IsActionJustReleased( "mouse_scroll_down")?-1:0;
// GD.Print(scrollAmount);
float minHeight = 2;
float maxHeight = 30;
// Move camera towards hitPos
// var hitPos = camera.MouseToGroundPlane();
// Vector3 dir = hitPos - camera.Translation;
var dir = -camera.GlobalTransform.Basis.Z * .5f;
var p = camera.Position;
// Stop zooming out at a certain distance.
// TODO: Maybe you should still slide around at 20 zoom?
if (scrollAmount > 0 || p.Y < (maxHeight - 0.1f))
{
cameraTargetOffset += dir * scrollAmount;
}
DebugDraw3D.DrawArrow(Vector3.Zero,cameraTargetOffset);
Vector3 lastCameraPosition = camera.Position;
camera.Position = camera.Position.Lerp(camera.Position + cameraTargetOffset, delta);
cameraTargetOffset -= camera.Position - lastCameraPosition;
p = camera.Position;
if (p.Y < minHeight)
{
p.Y = minHeight;
}
if (p.Y > maxHeight)
{
p.Y = maxHeight;
}
camera.Position = p;
// Change camera angle
camera.RotationDegrees = new Vector3(
Mathf.Lerp(-50, -75, camera.Position.Y / maxHeight),
camera.RotationDegrees.Y,
camera.RotationDegrees.Z
);
scrollAmount = 0;
}
void Update_RotateCamera()
{
if (!Input.IsActionPressed("move_mouse"))
{
CancelUpdateFunc();
}
Vector2 hitpos = GetViewport().GetMousePosition();
Vector2 diff = LastMousePostition - hitpos;
camera.RotationDegrees = new Vector3(
camera.RotationDegrees.X,
camera.RotationDegrees.Y + (diff.X * CameraRotateSpeed),
camera.RotationDegrees.Z
);
LastMousePostition = hitpos = GetViewport().GetMousePosition();
}
}
public static partial class SJKMath
{
public static Vector3? MouseToPlane(this Plane plane, Camera3D viewCamera, Vector2? mousePos = null)
{
var from = viewCamera.ProjectRayOrigin( mousePos ?? viewCamera.GetViewport().GetMousePosition());
var normal = viewCamera.ProjectRayNormal(mousePos ?? viewCamera.GetViewport().GetMousePosition());
return plane.IntersectsRay(from, normal);
}
public static Vector3? MouseToGroundPlane(this Camera3D viewCamera, Vector2? mousePos = null, float offset = 0) =>
(Plane.PlaneXZ with { D = offset }).MouseToPlane(viewCamera, mousePos);
}

1
RTSCamera.cs.uid Normal file
View File

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

View File

@@ -49,6 +49,74 @@ theme/default_font_multichannel_signed_distance_field=true
theme/default_font_generate_mipmaps=true
theme/default_theme_scale=2.0
[input]
move_forward={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
move_back={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
move_left={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
move_right={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
rotate_left={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
]
}
rotate_right={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
camera_up={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
camera_down={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
place={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(97, 9),"global_position":Vector2(106, 57),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
mouse_scroll_up={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":4,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}
mouse_scroll_down={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}
move_mouse={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(88, 14),"global_position":Vector2(97, 62),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
camera_rotate={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(197, 20),"global_position":Vector2(206, 68),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
[rendering]
renderer/rendering_method="mobile"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
uid://2srbqlc7hyo8

View File

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

View File

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

6
src/Core/Direction.cs Normal file
View File

@@ -0,0 +1,6 @@
namespace FoodFactory.Core;
public enum Direction
{
Up, Down, Left, Right, Front, Back
}

View File

@@ -0,0 +1 @@
uid://5eaay872p2wp

View File

@@ -0,0 +1,48 @@
namespace FoodFactory.Core;
using Godot;
using System;
public static class DirectionExtension
{
public static Vector3I ToVector(this Direction direction) => direction switch
{
Direction.Up => Vector3I.Up,
Direction.Down => Vector3I.Down,
Direction.Left => Vector3I.Left,
Direction.Right => Vector3I.Right,
Direction.Front => Vector3I.Forward,
Direction.Back => Vector3I.Back,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
public static Direction Reverse(this Direction direction) => direction switch
{
Direction.Up => Direction.Down,
Direction.Down => Direction.Up,
Direction.Left => Direction.Right,
Direction.Right => Direction.Left,
Direction.Front => Direction.Back,
Direction.Back => Direction.Front,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
public static Direction RotateClockWise(this Direction direction) => direction switch
{
Direction.Up => Direction.Up,
Direction.Down => Direction.Down,
Direction.Left => Direction.Back,
Direction.Right => Direction.Front,
Direction.Front => Direction.Right,
Direction.Back => Direction.Left,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
public static Direction RotateCounterClockWise(this Direction direction) => direction switch
{
Direction.Up => Direction.Up,
Direction.Down => Direction.Down,
Direction.Right => Direction.Front,
Direction.Left => Direction.Back,
Direction.Back => Direction.Right,
Direction.Front => Direction.Left,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
}

View File

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

1
src/Core/Registry.cs.uid Normal file
View File

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

View File

@@ -4,6 +4,7 @@ using System;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using FoodFactory.Conveyors;
using FoodFactory.Core;
using FoodFactory.Items;
using FoodFactory.Math;
using FoodFactory.Voxel;

View File

@@ -0,0 +1,15 @@
namespace FoodFactory.Equipment;
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
// [Tool]
[Meta, Id("equipment_entry")]
public partial class EquipmentEntry : Resource
{
[Export] public PackedScene VisualScene { get; set; }
[Save("scene_path")] public string ScenePath { get => VisualScene.ResourcePath; set => VisualScene = GD.Load<PackedScene>(value); }
[Export, Save("name")] public string Name { get; set; } = "";
[Export, Save("id")] public string ID { get; set; } = "";
[Export, Save("cost")] public int Cost { get; set; } = default;
}

View File

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

View File

@@ -0,0 +1,47 @@
namespace FoodFactory.Equipment;
using System;
using System.Collections.Generic;
using System.Linq;
using Godot;
using SJK.Functional;
public interface IEquipmentManager
{
IEnumerable<EquipmentMetaData> GetEquipments();
public Option<EquipmentId> Add(Node node, EquipmentEntry entry);
}
public class EquipmentManger : IEquipmentManager
{
private readonly Dictionary<string, EquipmentEntry> _equipmentEntrys = new();
private readonly Dictionary<EquipmentId, EquipmentMetaData> _equipment = new();
public Option<EquipmentId> Add(Node node, EquipmentEntry entry)
{
var metaData = new EquipmentMetaData(Guid.NewGuid(), node){ Entry = entry };
node.TreeExiting += () =>
{
_equipment.Remove(metaData.Id);
};
_equipment.Add(metaData.Id,metaData);
return Option<EquipmentId>.Some(metaData.Id);
}
public Option<EquipmentMetaData> GetEquipmentMetaData(EquipmentId id) => _equipment.GetValue(id).ToStructOption();
public void Free(EquipmentId id) => GetEquipmentMetaData(id).ToClassOption().IfSome(some => some.Node.QueueFree());
public IEnumerable<EquipmentMetaData> GetEquipments() =>_equipment.Values;
}
public class EquipmentMetaData
{
public EquipmentId Id { get; }
public Node Node { get; }
public required EquipmentEntry Entry { get; init;}
public EquipmentMetaData(EquipmentId id, Node node)
{
Id = id;
Node = node;
}
}
public readonly record struct EquipmentId(Guid Id)
{
public static implicit operator EquipmentId(Guid id) => new(id);
}

View File

@@ -0,0 +1 @@
uid://2h6dtjopa53w

View File

@@ -6,6 +6,7 @@ using Arch.Core;
using Arch.Core.Extensions;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder;
using FoodFactory.Conveyors;
using FoodFactory.Core.Components;
using FoodFactory.Items;
@@ -52,7 +53,6 @@ public partial class OvenTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxel
}
_guid = GridRegistry.Register(this, VoxelTransform.Origin);
this.Provide();
// var time = new Timer() { Autostart = true, OneShot = false, WaitTime = .1 };
// AddChild(time);
@@ -69,7 +69,6 @@ public partial class OvenTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxel
}
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]);
@@ -116,7 +115,27 @@ public partial class OvenTest : Node3D, IProvide<IBeltPortHost>, IProvide<IVoxel
GridRegistry.UnRegister(_guid);
base._ExitTree();
}
// [Meta, Id("equipment_oven_data")]
// public partial record OvenData(GridTransform3D GridTransform3D, Entity HeldItem) : EquipmentData
// {
// }
}
public class DelegateInsertBeltItemLogic : IBeltItemInsertLogic
{
private readonly Func<IBeltPort, IBeltItem, bool> _canAccept;

View File

@@ -2,6 +2,7 @@ namespace FoodFactory;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -12,7 +13,9 @@ using Arch.LowLevel;
using Arch.Persistence;
using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder;
using Chickensoft.Serialization;
using FoodFactory.Core.Components;
using FoodFactory.Equipment;
using FoodFactory.Items;
using FoodFactory.Math;
using FoodFactory.Recipes;
@@ -20,7 +23,6 @@ using Godot;
using SharpYaml;
using SharpYaml.Serialization;
using Utf8Json;
public class Test
{
@@ -29,8 +31,6 @@ public class Test
using var world = World.Create();
var bucket = new BucketStorage<List<Entity>>();
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()));
@@ -46,22 +46,22 @@ public class Test
{
pizza.Get<Temperature>().Kelvin += .1f;
}
pizza.Add(new IngredientOf(turkey));
// pizza.Add(new IngredientOf(turkey));
var a = new List<Entity>() { pizza };
turkey.Add(new LayeredStack(bucket.Add(a)));
turkey.Add(new LayeredStack2(a));
// turkey.Add(new LayeredStack(bucket.Add(a)));
// turkey.Add(new LayeredStack2(a));
var list = bucket.GetRef(turkey.Get<LayeredStack>().Handle);
GD.Print(string.Join(',', list));
// var list = bucket.GetRef(turkey.Get<LayeredStack>().Handle);
// GD.Print(string.Join(',', list));
var list2 = turkey.Get<LayeredStack2>().Children;
turkey.Get<LayeredStack2>().Children.Add(turkey);
GD.Print(string.Join(',', list2));
var r = pizza.Get(typeof(Name));
ComponentRegistry.TryGet<LayeredStack2>(out var type);
GD.Print(type);
// var list2 = turkey.Get<LayeredStack2>().Children;
// turkey.Get<LayeredStack2>().Children.Add(turkey);
// GD.Print(string.Join(',', list2));
// var r = pizza.Get(typeof(Name));
// ComponentRegistry.TryGet<LayeredStack2>(out var type);
// GD.Print(type);
var flour = world.Create(new Name("flour"), new Temperature(71, TemperatureUnit.Fahrenheit), new Tags("flour", "wheat"));
// var tag2 = new Tags();
@@ -80,6 +80,7 @@ public class Test
GD.Print(item.Namespace);
GD.Print(item.FullName);
}
string yaml = @"
Id: potato_raw
@@ -104,23 +105,23 @@ public class Test
var options = new YamlSerializerOptions()
{
// Converters = [new Recipes.DataNodeSerlicer()]
// PolymorphismOptions = new YamlPolymorphismOptions
// {
// DerivedTypeMappings =
// {
// [typeof(DataNode)] =
// [
// new YamlDerivedType(typeof(SequenceNode), "sequence"){Tag = "!sequence"},
// new YamlDerivedType(typeof(MappingNode), "mapping"){Tag = "!mapping"},
// new YamlDerivedType(typeof(ValueNode), "value"){Tag = "!value"}
// ],
// [typeof(object)] =
// [
// new YamlDerivedType(typeof(Temperature), "temperature") { Tag = "!temperature"}
// ]
// PolymorphismOptions = new YamlPolymorphismOptions
// {
// DerivedTypeMappings =
// {
// [typeof(DataNode)] =
// [
// new YamlDerivedType(typeof(SequenceNode), "sequence"){Tag = "!sequence"},
// new YamlDerivedType(typeof(MappingNode), "mapping"){Tag = "!mapping"},
// new YamlDerivedType(typeof(ValueNode), "value"){Tag = "!value"}
// ],
// [typeof(object)] =
// [
// new YamlDerivedType(typeof(Temperature), "temperature") { Tag = "!temperature"}
// ]
// },
// },
// },
// },
};
// var test = YamlSerializer.Serialize(new ItemData()
// {
@@ -290,114 +291,38 @@ public record struct MicroNutrients(List<Vitamin> Vitamins);
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 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);
public class BucketStorage<T>
{
private struct Slot
{
public T Value;
public int Version;
public bool Occupied;
}
private Slot[] _slots = new Slot[1];
private Stack<int> _freeIndices = new();
// Allocate a new slot
public Handle Add(T value)
{
if (_freeIndices.Count > 0)
{
int index = _freeIndices.Pop();
var slot = _slots[index];
slot.Value = value;
slot.Occupied = true;
slot.Version++; // bump version on reuse
_slots[index] = slot;
return new Handle { Index = index, Version = slot.Version };
}
else
{
var slot = new Slot
{
Value = value,
Version = 1,
Occupied = true
};
System.Array.Resize(ref _slots, _slots.Length + 1);
_slots[^1] = slot;
return new Handle
{
Index = _slots.Length - 1,
Version = slot.Version
};
}
}
// Safe access
public bool TryGet(Handle handle, out T value)
{
if (handle.Index < 0 || handle.Index >= _slots.Length)
{
value = default;
return false;
}
var slot = _slots[handle.Index];
if (!slot.Occupied || slot.Version != handle.Version)
{
value = default;
return false;
}
value = slot.Value;
return true;
}
// Direct (unsafe-ish) access if you trust the handle
public ref T GetRef(Handle handle)
{
return ref _slots[handle.Index].Value;
}
// Remove and recycle index
public bool Remove(Handle handle)
{
if (handle.Index < 0 || handle.Index >= _slots.Length)
return false;
var slot = _slots[handle.Index];
if (!slot.Occupied || slot.Version != handle.Version)
return false;
slot.Occupied = false;
slot.Version++; // invalidate old handles
_slots[handle.Index] = slot;
_freeIndices.Push(handle.Index);
return true;
}
}
[Meta, Id("game_data")]
public partial record GameData
{
[Save("world_data")]
public required World World { get; init; }
// [Save("equipments_data")]
// public required Dictionary<EquipmentId, EquipmentData> Equipments { get; init; }
// public required EquipmentsData Equipments { get; init; }//TempTest /\
}
// [Meta, Id("equipments_data")]
// public partial record EquipmentsData
// {
// [Save("world_data")]
// public required Dictionary<EquipmentId, EquipmentData> Equipments { get; init; }
// }
[Meta, Id("equipment_data")]
public abstract partial record EquipmentData
{
[Save("guid_data")]
public required EquipmentId Id { get; init; }
}

View File

@@ -0,0 +1,253 @@
namespace FoodFactory;
using System;
using System.Collections.Generic;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using FoodFactory.Equipment;
using Godot;
using GodotHelpers.Raycasts;
using SJK.Math;
[Meta(typeof(IAutoNode))]
public partial class ItemPlacerTest : Node
{
[Export] public string PlaceAction { get; set; } = "";
[Export] public float Length { get; set; } = 10f;
public override void _Notification(int what) => this.Notify(what);
[Export] public EquipmentEntry[] EquipmentEntrys { get; set; } = [];
private InputGestureManager _inputThing = default!;
public override void _Ready()
{
base._Ready();
_inputThing = new InputGestureManager.Builder()
.AddAction(PlaceAction)
.HoldTime(0.1f)
.OnQuickPress(TryPlaceItem)
.BuildAction()
.Build();
}
public void TryPlaceItem()
{
var camera = GetViewport().GetCamera3D();
var space = camera.GetWorld3D().DirectSpaceState;
var orgin = camera.ProjectRayOrigin(GetViewport().GetMousePosition());
var to = camera.ProjectRayNormal(GetViewport().GetMousePosition());
var result = space.RaycastEx(orgin, orgin + to * Length);
if (result is not null)
{
DebugDraw3D.DrawLine(orgin, result.Position, Colors.Red, 1);
var rounderPos = new Vector3I(Mathf.RoundToInt(result.Position.X), Mathf.RoundToInt(result.Position.Y), Mathf.RoundToInt(result.Position.Z));
DebugDraw3D.DrawBox(rounderPos, Quaternion.Identity, Vector3.One, Colors.Blue, true, 1);
var instance = EquipmentEntrys[0].VisualScene.Instantiate<Node3D>();
instance.Position = rounderPos;
GetParent().GetParent().AddChild(instance);
}
}
public override void _UnhandledInput(InputEvent @event) => _inputThing.HandleInput(@event);
public override void _Process(double delta) => _inputThing.Update((float)delta);
}
public sealed class InputGestureManager
{
private readonly Dictionary<string, GestureBinding> _bindings;
private InputGestureManager(Dictionary<string, GestureBinding> bindings)
{
_bindings = bindings;
}
public void HandleInput(InputEvent inputEvent)
{
foreach (var binding in _bindings.Values)
{
binding.HandleInput(inputEvent);
}
}
public void Update(float delta)
{
foreach (var binding in _bindings.Values)
{
binding.Update(delta);
}
}
#region Builder
public sealed class Builder
{
private readonly Dictionary<string, GestureBinding> _bindings = new();
public GestureBuilder AddAction(string actionName)
{
var binding = new GestureBinding(actionName);
return new GestureBuilder(
this,
binding,
completed => _bindings[actionName] = completed);
}
public InputGestureManager Build() => new(_bindings);
}
public sealed class GestureBuilder
{
private readonly Builder _parent;
private readonly GestureBinding _binding;
private readonly Action<GestureBinding> _onComplete;
internal GestureBuilder(
Builder parent,
GestureBinding binding,
Action<GestureBinding> onComplete)
{
_parent = parent;
_binding = binding;
_onComplete = onComplete;
}
public GestureBuilder HoldTime(float seconds)
{
_binding.HoldThreshold = seconds;
return this;
}
public GestureBuilder Predicate(Func<bool> predicate)
{
_binding.Predicate = predicate;
return this;
}
public GestureBuilder OnQuickPress(Action callback)
{
_binding.OnQuickPress = callback;
return this;
}
public GestureBuilder OnHoldStart(Action callback)
{
_binding.OnHoldStart = callback;
return this;
}
public GestureBuilder OnHolding(Action<float> callback)
{
_binding.OnHolding = callback;
return this;
}
public GestureBuilder OnDrag(Action<Vector2> callback)
{
_binding.OnDrag = callback;
return this;
}
public GestureBuilder OnRelease(Action callback)
{
_binding.OnRelease = callback;
return this;
}
public Builder BuildAction()
{
_onComplete(_binding);
return _parent;
}
}
#endregion
internal sealed class GestureBinding
{
public string ActionName { get; }
public float HoldThreshold = 0.25f;
public Func<bool>? Predicate;
public Action? OnQuickPress;
public Action? OnHoldStart;
public Action<float>? OnHolding;
public Action<Vector2>? OnDrag;
public Action? OnRelease;
private bool _pressed;
private bool _holding;
private float _heldTime;
public GestureBinding(string actionName)
{
ActionName = actionName;
}
public void HandleInput(InputEvent inputEvent)
{
if (Predicate != null && !Predicate())
{
return;
}
if (inputEvent.IsActionPressed(ActionName))
{
_pressed = true;
_holding = false;
_heldTime = 0f;
return;
}
if (inputEvent.IsActionReleased(ActionName))
{
if (_holding)
{
OnRelease?.Invoke();
}
else
{
OnQuickPress?.Invoke();
}
_pressed = false;
_holding = false;
_heldTime = 0f;
return;
}
if (_holding &&
inputEvent is InputEventMouseMotion motion)
{
OnDrag?.Invoke(motion.Relative);
}
}
public void Update(float delta)
{
if (!_pressed)
{
return;
}
_heldTime += delta;
if (!_holding &&
_heldTime >= HoldThreshold)
{
_holding = true;
OnHoldStart?.Invoke();
}
if (_holding)
{
OnHolding?.Invoke(delta);
}
}
}
}

View File

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

View File

@@ -0,0 +1,149 @@
namespace FoodFactory;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
[Meta(typeof(IAutoNode))]
public partial class RTSCameraController : Node3D
{
public override void _Notification(int what) => this.Notify(what);
[Export] public float MoveSpeed = 20f;
[Export] public float RotationSpeed = 90f;
[Export] public float ZoomSpeed = 5f;
[Export] public float MinZoom = 5f;
[Export] public float MaxZoom = 50f;
[Export] public float HeightSpeed = 10f;
[Node("YawPivot")]
private Node3D _yawPivot{get;set;} = default!;
[Node("YawPivot/PitchPivent")]
private Node3D _pitchPivot{get;set;} = default!;
[Node("YawPivot/PitchPivent/Camera3D")]
private Camera3D _camera{get;set;} = default!;
private float _zoomDistance = 20f;
private bool _freeLook = false;
public override void _Ready()
{
// _yawPivot = GetNode<Node3D>("YawPivot");
// _pitchPivot = _yawPivot.GetNode<Node3D>("PitchPivot");
// _camera = _pitchPivot.GetNode<Camera3D>("Camera3D");
UpdateZoom();
}
public override void _Process(double delta)
{
float dt = (float)delta;
HandleMovement(dt);
HandleRotation(dt);
HandleHeight(dt);
}
private void HandleMovement(float dt)
{
Vector3 move = Vector3.Zero;
if (Input.IsActionPressed("move_forward"))
move -= _yawPivot.GlobalBasis.Z;
if (Input.IsActionPressed("move_back"))
move += _yawPivot.GlobalBasis.Z;
if (Input.IsActionPressed("move_left"))
move -= _yawPivot.GlobalBasis.X;
if (Input.IsActionPressed("move_right"))
move += _yawPivot.GlobalBasis.X;
move.Y = 0;
move = move.Normalized();
GlobalPosition += move * MoveSpeed * dt;
}
private void HandleRotation(float dt)
{
if (Input.IsActionPressed("rotate_left"))
RotateY(Mathf.DegToRad(RotationSpeed * dt));
if (Input.IsActionPressed("rotate_right"))
RotateY(Mathf.DegToRad(-RotationSpeed * dt));
}
private void HandleHeight(float dt)
{
Vector3 pos = GlobalPosition;
if (Input.IsActionPressed("camera_up"))
pos.Y += HeightSpeed * dt;
if (Input.IsActionPressed("camera_down"))
pos.Y -= HeightSpeed * dt;
GlobalPosition = pos;
}
private void UpdateZoom()
{
_camera.Position = new Vector3(0, 0, _zoomDistance);
}
public override void _UnhandledInput(InputEvent @event)
{
if (@event is InputEventMouseButton mouseButton)
{
if (mouseButton.ButtonIndex == MouseButton.WheelUp)
{
_zoomDistance -= ZoomSpeed;
_zoomDistance = Mathf.Clamp(
_zoomDistance,
MinZoom,
MaxZoom
);
UpdateZoom();
}
if (mouseButton.ButtonIndex == MouseButton.WheelDown)
{
_zoomDistance += ZoomSpeed;
_zoomDistance = Mathf.Clamp(
_zoomDistance,
MinZoom,
MaxZoom
);
UpdateZoom();
}
if (mouseButton.ButtonIndex == MouseButton.Middle)
{
_freeLook = mouseButton.Pressed;
}
}
if (_freeLook && @event is InputEventMouseMotion motion)
{
_yawPivot.RotateY(
Mathf.DegToRad(-motion.Relative.X * 0.2f)
);
Vector3 rot = _pitchPivot.Rotation;
rot.X += Mathf.DegToRad(-motion.Relative.Y * 0.2f);
rot.X = Mathf.Clamp(
rot.X,
Mathf.DegToRad(-80),
Mathf.DegToRad(-15)
);
_pitchPivot.Rotation = rot;
}
}
}

View File

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

View File

@@ -0,0 +1,15 @@
[gd_scene format=3 uid="uid://dr5gfg25sjr04"]
[ext_resource type="Script" uid="uid://bdvwvdqrbks7c" path="res://RTSCamera.cs" id="1_0un4r"]
[node name="RTS" type="Node3D" unique_id=1383983755 node_paths=PackedStringArray("camera")]
script = ExtResource("1_0un4r")
camera = NodePath("Camera3D")
[node name="YawPivot" type="Node3D" parent="." unique_id=1634769952]
[node name="PitchPivent" type="Node3D" parent="YawPivot" unique_id=1035768574]
[node name="Camera3D" type="Camera3D" parent="." unique_id=938232118]
transform = Transform3D(1, 0, 0, 0, 0.8646082, 0.5024466, 0, -0.5024466, 0.8646082, 1.1920929e-07, 1.8606924, 2.2873116)
current = true

View File

@@ -2,17 +2,24 @@ namespace FoodFactory.Voxel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Arch.Core;
using Arch.LowLevel;
using Arch.System;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder;
using Chickensoft.Serialization;
using Chickensoft.Serialization.Godot;
using FoodFactory;
using FoodFactory.Blueprints;
using FoodFactory.Conveyors;
using FoodFactory.Math;
using FoodFactory.Recipes;
using Godot;
using SharpYaml;
public class BacterialSystem : BaseSystem<World, float>
{
private QueryDescription _desc = new QueryDescription().WithAll<Bacteria, Temperature>();
@@ -44,7 +51,14 @@ public class BacterialSystem : BaseSystem<World, float>
//Will Liklely be the game instead of a node like this
[Meta(typeof(IAutoNode))]// [Tool]
public partial class VoxelGridNode : Node3D, IProvide<IVoxelGridRegistry>, IProvide<IItemRenderer>, IProvide<IRecipes>, IProvide<IBlueprintManger>, IProvide<World>, IProvide<IFoodFactoryApi>
public partial class VoxelGridNode : Node3D,
IProvide<IVoxelGridRegistry>,
IProvide<IItemRenderer>,
IProvide<IRecipes>,
IProvide<IBlueprintManger>,
IProvide<World>,
IProvide<IFoodFactoryApi>,
IProvide<ISaveChunk<GameData>>
{
public override void _Notification(int what) => this.Notify(what);
@@ -60,6 +74,62 @@ public partial class VoxelGridNode : Node3D, IProvide<IVoxelGridRegistry>, IProv
IRecipes IProvide<IRecipes>.Value() => _recipes;
private IItemRenderer _itemRenderer = default!;
IItemRenderer IProvide<IItemRenderer>.Value() => _itemRenderer;
public ISaveChunk<GameData> Value() => SaveFile.Root;
public static string SavePath => $"{OS.GetUserDataDir()}/SaveFile.json";
JsonSerializerOptions _options;
public void Setup()
{
GodotSerialization.Setup();
_options = new JsonSerializerOptions
{
WriteIndented = true,
TypeInfoResolver = new SerializableTypeResolver(),
Converters = { new SerializableTypeConverter() }
};
var test = "gg";
GD.Print(SavePath);
SaveFile = new SaveFile<GameData>(
new SaveChunk<GameData>(
onSave: (chunk) =>
{
var gameData = new GameData()
{
World = _world,
// Equipments = chunk.GetChunkSaveData<EquipmentsData>()
};
return gameData;
},
onLoad: (chunk, data) =>
{
World.Destroy(_world);
_world = data.World;
// chunk.LoadChunkSaveData(data.Equipments);
}
),
onSave: async data =>
{
var yaml = JsonSerializer.Serialize(data, _options);
GD.Print(SavePath);
await File.WriteAllTextAsync(SavePath, yaml);
},
onLoad: async () =>
{
if (!File.Exists(SavePath))
{
GD.PushWarning("Save does nto exist");
return null;
}
var data = JsonSerializer.Deserialize<GameData>(await File.ReadAllTextAsync(SavePath), _options);
return data;
}
);
}
public ISaveFile<GameData> SaveFile { get; set; } = default!;
Group<float> _systems;
public override void _Ready()
{
@@ -118,6 +188,15 @@ public partial class VoxelGridNode : Node3D, IProvide<IVoxelGridRegistry>, IProv
}
}
}
if (@event.IsActionPressed("ui_right"))
{
SaveFile.Save();
}
if (@event.IsActionPressed("ui_left"))
{
SaveFile.Load();
}
}
public override void _ExitTree()
{
@@ -125,6 +204,7 @@ public partial class VoxelGridNode : Node3D, IProvide<IVoxelGridRegistry>, IProv
_systems.Dispose();
_world.Dispose();
}
}
public static class BacteriaModel
{

View File

@@ -10,6 +10,9 @@
[ext_resource type="Script" uid="uid://opbkqoaa7x2n" path="res://src/Equipment/Balancer.cs" id="7_2wkfx"]
[ext_resource type="Script" uid="uid://yec84plemjv1" path="res://src/Equipment/SlicerTest.cs" id="8_2lg7i"]
[ext_resource type="Script" uid="uid://culjdbwllmsyk" path="res://src/Equipment/StackerTest.cs" id="8_e2skk"]
[ext_resource type="PackedScene" uid="uid://dr5gfg25sjr04" path="res://src/VoxelGrid/RtsController.tscn" id="11_21ota"]
[ext_resource type="Script" uid="uid://0yvk53xc1dix" path="res://src/VoxelGrid/ItemPlacerTest.cs" id="12_6whqj"]
[ext_resource type="Resource" uid="uid://cus0vfrtgh0uh" path="res://src/VoxelGrid/test_equipment.tres" id="13_njjff"]
[sub_resource type="BoxMesh" id="BoxMesh_2wkfx"]
size = Vector3(1, 1, 2)
@@ -21,12 +24,15 @@ _data = {
}
point_count = 2
[sub_resource type="BoxMesh" id="BoxMesh_6whqj"]
size = Vector3(25, 1, 25)
[sub_resource type="BoxShape3D" id="BoxShape3D_njjff"]
size = Vector3(25, 1, 25)
[node name="VoxelGridNode" type="Node3D" unique_id=825696340]
script = ExtResource("1_tsdpe")
[node name="Camera3D" type="Camera3D" parent="." unique_id=75458542]
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, 1, 0, 0)
@@ -345,3 +351,19 @@ 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)
[node name="RTS" parent="." unique_id=1383983755 instance=ExtResource("11_21ota")]
[node name="Node" type="Node" parent="RTS" unique_id=1499015368]
script = ExtResource("12_6whqj")
PlaceAction = "place"
EquipmentEntrys = [ExtResource("13_njjff")]
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1598166493]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
mesh = SubResource("BoxMesh_6whqj")
[node name="StaticBody3D" type="StaticBody3D" parent="MeshInstance3D" unique_id=919680718]
[node name="CollisionShape3D" type="CollisionShape3D" parent="MeshInstance3D/StaticBody3D" unique_id=622210784]
shape = SubResource("BoxShape3D_njjff")

View File

@@ -77,79 +77,3 @@ public sealed class VoxelRegistry : IVoxelGridRegistry
});
}
public enum Direction
{
Up, Down, Left, Right, Front, Back
}
public static class DirectionExtession
{
public static Vector3I ToVector(this Direction direction) => direction switch
{
Direction.Up => Vector3I.Up,
Direction.Down => Vector3I.Down,
Direction.Left => Vector3I.Left,
Direction.Right => Vector3I.Right,
Direction.Front => Vector3I.Forward,
Direction.Back => Vector3I.Back,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
public static Direction Reverse(this Direction direction) => direction switch
{
Direction.Up => Direction.Down,
Direction.Down => Direction.Up,
Direction.Left => Direction.Right,
Direction.Right => Direction.Left,
Direction.Front => Direction.Back,
Direction.Back => Direction.Front,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
public static Direction RotateClockWise(this Direction direction) => direction switch
{
Direction.Up => Direction.Up,
Direction.Down => Direction.Down,
Direction.Left => Direction.Back,
Direction.Right => Direction.Front,
Direction.Front => Direction.Right,
Direction.Back => Direction.Left,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
public static Direction RotateCounterClockWise(this Direction direction) => direction switch
{
Direction.Up => Direction.Up,
Direction.Down => Direction.Down,
Direction.Right => Direction.Front,
Direction.Left => Direction.Back,
Direction.Back => Direction.Right,
Direction.Front => Direction.Left,
_ => throw new NotSupportedException($"{nameof(direction)} does not support value {direction}")
};
}
public interface IStorage<T> where T : class
{
StorageDefinition Definition { get; }
IEnumerable<T> GetAllItems();
void Remove(T item);
bool TryInsert(T item);
record StorageDefinition(string Name, ItemType Type);
}
public record ItemType(string Name);
public partial class ItemStoragePool : Node, IStorage<IBeltItem>
{
[Export] public int MaxAmount { get; set; } = 5;
public IStorage<IBeltItem>.StorageDefinition Definition { get; set; } = default!;
private readonly List<IBeltItem> _items = [];
public IEnumerable<IBeltItem> GetAllItems() => _items;
public bool TryInsert(IBeltItem item)
{
if (_items.Count >= MaxAmount)
{
return false;
}
_items.Add(item);
return true;
}
public void Remove(IBeltItem item) => _items.Remove(item);
}

View File

@@ -0,0 +1,8 @@
[gd_resource type="Resource" format=3 uid="uid://cus0vfrtgh0uh"]
[ext_resource type="PackedScene" uid="uid://c4h7mwnfrdesg" path="res://src/Conveyors/ConveyorBeltStraight/ConveyorBeltStraight.tscn" id="1_ipx2f"]
[ext_resource type="Script" uid="uid://x8adhd57sup8" path="res://src/Equipment/EquipmentEntry.cs" id="2_82d1t"]
[resource]
script = ExtResource("2_82d1t")
VisualScene = ExtResource("1_ipx2f")