Major Changes including Options menu, logic blocks, modding system, and more.

This commit is contained in:
2026-08-06 01:28:52 -04:00
parent c9ac4641a0
commit b58acff195
90 changed files with 4065 additions and 485 deletions

View File

@@ -0,0 +1,406 @@
//https://github.com/FoolLin/ReorderableContainer
namespace FoodFactory;
using System;
using System.Collections.Generic;
using System.Linq;
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
public interface IReOrderableContainer : IContainer
{
}
[Meta(typeof(IAutoNode))]
[Tool]
public partial class ReOrderableContainer : Container, IReOrderableContainer
{
public override void _Notification(int what) => this.Notify(what);
[Signal] public delegate void ReOrderedEventHandler(int from, int to);
[Export] public double HoldDuration { get; set; } = .5;
[Export(PropertyHint.Range, "3,30,0.01,or_greater,or_less")] public double Speed { get; set; } = 10;
[Export] public double Separation
{
get;
set
{
if (value == Separation || value < 0)
{
return;
}
field = value;
OnSortChildren();
}
}
[Export]
public bool IsVertical
{
get;
set
{
if (value == IsVertical)
{
return;
}
field = value;
if (IsVertical)
{
CustomMinimumSize = CustomMinimumSize with { X = 0 };
}
else
{
CustomMinimumSize = CustomMinimumSize with { Y = 0 };
}
OnSortChildren();
}
}
[Export] public ScrollContainer ScrollContainer { get; set; } = default!;
[Export] public double AutoScrollSpeed { get; set; } = 10;
[Export(PropertyHint.Range, "0,.5")] public double AutoScrollRange { get; set; } = .3;
[Export] public double ScrollThreshold { get; set; } = 30;
[Export] public bool IsDebugging { get; set; } = false;
private double _scrollStartingPoint = 0;
// private bool _isSmoothScroll = false;
private readonly List<Rect2> _dropZones = [];
private int _dropZoneIndex = -1;
private readonly List<Rect2> _expectChildRect = [];
protected Control? _focusChild = null;
protected bool _isPress = false;
protected bool _isHold = false;
private double _currentDuration = 0.0;
private bool _isUsingProcess = false;
private const int DROP_ZONE_EXTEND = 2000;
public void OnReady()
{
if (ScrollContainer is null && GetParent() is ScrollContainer)
{
ScrollContainer = GetParent<ScrollContainer>();
}
// if scroll_container != null and scroll_container.has_method("handle_overdrag"):
// _is_smooth_scroll = true
ProcessMode = ProcessModeEnum.Pausable;
AdjustExpectedChildRect();
SortChildren += OnSortChildrenWrapper;
// GetTree().NodeAdded += OnNodeAdded;
}
public override void _GuiInput(InputEvent @event)
{
if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.ButtonIndex == MouseButton.Left)
{
GD.Print(string.Join(',',((IReOrderableContainer)this).GetChildren().Select(s => $"{s}, {s.Name}, {s.GetType()}, {(Control)s}")));
foreach (var child in ((IReOrderableContainer)this).GetChildren().OfType<Control>())
{
if (child.GetRect().HasPoint(GetLocalMousePosition()) && eventMouseButton.IsPressed())
{
_focusChild = child;
_isPress = true;
}
else if (!eventMouseButton.Pressed)
{
_isPress = false;
_isHold = false;
}
}
}
}
public override void _Process(double delta)
{
if (Engine.IsEditorHint())
{
return;
}
HandleInput(delta);
if ((_currentDuration >= HoldDuration) != _isHold)
{
_isHold = _currentDuration >= HoldDuration;
if (_isHold)
{
OnStartDragging();
}
}
if (_isHold)
{
HandleDraggingChildPos(delta);
if (ScrollContainer is not null)
{
HandleAutoScroll(delta);
}
}
else if (!_isHold && _dropZoneIndex != -1)
{
OnStopDragging();
}
if (_isUsingProcess)
{
OnSortChildren(delta);
}
QueueRedraw();
}
public override void _Draw()
{
var r = new Random(0);
foreach (var item in _dropZones)
{
// GD.Print(item);
DrawRect(item,new Color(r.NextSingle(),r.NextSingle(),r.NextSingle()));
}
base._Draw();
}
private void HandleInput(double delta)
{
if (ScrollContainer is not null && _isPress && !_isHold)
{
var scrollPoint = IsVertical ? ScrollContainer.ScrollVertical : ScrollContainer.ScrollHorizontal;
if (_currentDuration == 0)
{
_scrollStartingPoint = scrollPoint;
}
else
{
_isPress = Mathf.Abs(scrollPoint - _scrollStartingPoint) <= ScrollThreshold;
}
}
_currentDuration = _isPress ? _currentDuration + delta : 0;
}
private void OnStartDragging()
{
_isUsingProcess = true;
_focusChild?.ZIndex = 1;
// if _is_smooth_scroll:
// scroll_container.process_mode = Node.PROCESS_MODE_DISABLED
foreach (var child in GetVisibleChildren())
{
child.PropagateCall(Control.MethodName.SetMouseFilter, [Variant.From(MouseFilterEnum.Ignore)]);
}
}
private void OnStopDragging()
{
if (_focusChild is null)
{
return;
}
_focusChild.ZIndex = 0;
var focusChildIndex = _focusChild.GetIndex();
AsIReOrderableContainer().MoveChild(_focusChild, _dropZoneIndex);
EmitSignalReOrdered(focusChildIndex, _dropZoneIndex);
_focusChild = null;
GD.Print(_dropZoneIndex);
_dropZoneIndex = -1;
// if _is_smooth_scroll:
// scroll_container.pos = -Vector2(scroll_container.scroll_horizontal, scroll_container.scroll_vertical)
// scroll_container.process_mode = Node.PROCESS_MODE_INHERIT
foreach (var child in GetVisibleChildren())
{
child.PropagateCall(Control.MethodName.SetMouseFilter,[Variant.From(MouseFilterEnum.Pass)]);
}
}
public void HandleDraggingChildPos(double delta)
{
if (IsVertical)
{
var targetPos = GetLocalMousePosition().Y - (_focusChild.Size.Y/2);
_focusChild.Position = _focusChild.Position with {Y = Mathf.Lerp(_focusChild.Position.Y, targetPos, (float)(delta * Speed))};
}
else
{
var targetPos = GetLocalMousePosition().X - (_focusChild.Size.X/2);
_focusChild.Position = _focusChild.Position with {X = Mathf.Lerp(_focusChild.Position.X, targetPos, (float)(delta * Speed))};
}
var childCenterPos = _focusChild.GetRect().GetCenter();
for (int i = 0; i < _dropZones.Count; i++)
{
var dropZone = _dropZones[i];
GD.Print(Dump.With(i).And(_dropZones[i]).And(childCenterPos).And(dropZone.HasPoint(childCenterPos)).And(_dropZones.Count));
if (dropZone.HasPoint(childCenterPos))
{
_dropZoneIndex = i;
break;
}
else if (i == _dropZones.Count - 1)
{
_dropZoneIndex = -1;
}
// elif i == _drop_zones.size() - 1:
// _drop_zone_index = -1
}
}
public void HandleAutoScroll(double delta)
{
var mouseGPos = GetGlobalMousePosition();
var scrollGRect = ScrollContainer.GetGlobalRect();
var index = IsVertical ? 0 : 1;
var leftUpper = scrollGRect.Position[index] + (scrollGRect.Size[index] * AutoScrollRange);
var rightLower = scrollGRect.Position[index] + (scrollGRect.Size[index] * (1 - AutoScrollRange));
setScroll(
mouseGPos[index] switch
{
var i when leftUpper > i => scroll => useDelta((leftUpper - mouseGPos[index]) / (leftUpper - scrollGRect.Position[index])),
var i when rightLower < i => scroll => useDelta((mouseGPos[index] - rightLower) / (scrollGRect.Position[index] - rightLower)),
var i => scroll => (int)scroll
}
);
int useDelta(double d) => (int)(delta * AutoScrollSpeed * 150.0 * d);
void setScroll(Func<double, int> scroll)
{
if (IsVertical)
{
ScrollContainer.ScrollVertical = scroll(ScrollContainer.ScrollVertical);
}
else
{
ScrollContainer.ScrollHorizontal = scroll(ScrollContainer.ScrollHorizontal);
}
}
}
public void OnExitTree()
{
if (Engine.IsEditorHint())
{
return;
}
SortChildren -= OnSortChildrenWrapper;
// GetTree().NodeAdded -= OnNodeAdded;
}
private void OnNodeAdded(Node node)
{
if (node is Control control && !Engine.IsEditorHint())
{
control.MouseFilter = MouseFilterEnum.Pass;
}
}
private void OnSortChildrenWrapper() => OnSortChildren();
private void OnSortChildren(double delta = -1)
{
if (_isUsingProcess && delta == -1)
{
return;
}
AdjustExpectedChildRect();
AdjustChildRect(delta);
AdjustDropZoneRect();
}
private void AdjustExpectedChildRect()
{
_expectChildRect.Clear();
var endPoint = 0.0;
var i = 0;
var index = IsVertical ? 1 : 0;
foreach (var child in GetVisibleChildren())
{
var minSize = child.GetCombinedMinimumSize();
if (i == _dropZoneIndex)
{
endPoint += _focusChild.Size[index] + Separation;
}
_expectChildRect.Add(
new(
IsVertical
? new(0, (float)endPoint)
: new((float)endPoint, 0)
, IsVertical
? new(Size.X, minSize.Y)
: new(minSize.X, Size.Y)));
endPoint += minSize[index] + Separation;
i++;
}
}
private void AdjustChildRect(double delta = -1)
{
if (!GetVisibleChildren().Any())
{
return;
}
var isAnimating = false;
var endPoint = 0.0;
var children = GetVisibleChildren().ToArray();
for (int i = 0; i < children.Length; i++)
{
var child = children[i];
if (child.Position == _expectChildRect[i].Position && child.Size == _expectChildRect[i].Size)
{
continue;
}
if (_isUsingProcess)
{
isAnimating = true;
child.Position = child.Position.Lerp(_expectChildRect[i].Position, (float)(delta * Speed));
child.Size = _expectChildRect[i].Size;
if ((child.Position - _expectChildRect[i].Position).Length() <= 1)
{
child.Position = _expectChildRect[i].Position;
}
continue;
}
child.Position = _expectChildRect[i].Position;
child.Size = _expectChildRect[i].Size;
}
var lastChild = children[^1];
var index = IsVertical ? 0 : 1;
var newCustomSize =
_isUsingProcess && _dropZoneIndex == children.Length
? _expectChildRect[^1].End[index] + _focusChild.Size[index] + Separation
: !_isUsingProcess
?lastChild.GetRect().End[index]
:lastChild.CustomMinimumSize[index];
// lastChild.CustomMinimumSize = IsVertical?lastChild.CustomMinimumSize with { Y = (float)newCustomSize} :lastChild.CustomMinimumSize with { X = (float)newCustomSize};
if (!isAnimating && _focusChild is not null)
{
_isUsingProcess = false;
}
}
private void AdjustDropZoneRect()
{
_dropZones.Clear();
if (!GetVisibleChildren().Any())
{
return;
}
var children = GetVisibleChildren().ToArray();
var minSize = children.Max(child => child.GetMinimumSize()[IsVertical?0:1]);
if (_focusChild is not null)
{
minSize = Mathf.Max(minSize,_focusChild.GetMinimumSize()[IsVertical?0:1]);
}
for (int i = 0; i < children.Length; i++)
{
var child = children[i];
_dropZones.Add(i == 0 ?
new Rect2()
{
Position = child.Position - (IsVertical?new(0,DROP_ZONE_EXTEND):new(DROP_ZONE_EXTEND,0)),
End = IsVertical? new(minSize,child.GetRect().GetCenter().Y):new(child.GetRect().GetCenter().X,minSize)
}
:
new Rect2()
{
Position = IsVertical?new(children[i-1].Position.X,children[i-1].GetRect().GetCenter().Y) : new(children[i-1].GetRect().GetCenter().X,children[i-1].Position.Y),
End = IsVertical?new(minSize,child.GetRect().GetCenter().Y) : new(child.GetRect().GetCenter().X,minSize),
});
if (i == children.Length - 1)
{
_dropZones.Add(new Rect2()
{
Position = IsVertical?new(child.Position.X,child.GetRect().GetCenter().Y):new(child.GetRect().GetCenter().X,child.Position.Y),
End = IsVertical? new(minSize,child.GetRect().End.Y + DROP_ZONE_EXTEND):new(child.GetRect().End.X + DROP_ZONE_EXTEND, minSize)
});
}
}
}
private IReOrderableContainer AsIReOrderableContainer() => (IReOrderableContainer)this;
private IEnumerable<Control> GetVisibleChildren() => GetChildren().OfType<Control>().Where(child => child.Visible && !(child == _focusChild && _isHold));
}

View File

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

View File

@@ -0,0 +1,65 @@
namespace FoodFactory;
using System.Linq;
using Chickensoft.GodotNodeInterfaces;
using Godot;
public interface IReOrderableContainerFancy: IReOrderableContainer
{
}
public partial class ReOrderableContainerFancy : ReOrderableContainer, IReOrderableContainerFancy
{
public override void _GuiInput(InputEvent @event)
{
if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.ButtonIndex == MouseButton.Left)
{
foreach (var child in (this as IReOrderableContainerFancy).GetChildren().OfType<Control>())
{
if (child is OrderableItem orderableItem)
{
if (orderableItem.DragHandel.GetRect().HasPoint(child.GetLocalMousePosition()) && eventMouseButton.IsPressed())
{
_focusChild = orderableItem;
_isPress = true;
}
else if (!eventMouseButton.Pressed)
{
_isPress = false;
_isHold = false;
}
}
else
{
if (child.GetRect().HasPoint(child.GetLocalMousePosition()) && eventMouseButton.IsPressed())
{
_focusChild = child;
_isPress = true;
}
else if (!eventMouseButton.Pressed)
{
_isPress = false;
_isHold = false;
}
}
}
}
}
}
public interface IOrderableItem : IControl
{
IControl DragHandel { get; }
}
public partial class OrderableItem : Control, IOrderableItem
{
[Export] public Control DragHandel { get; set; } = default!;
IControl IOrderableItem.DragHandel => (IControl)DragHandel;
public override void _Ready()
{
if (DragHandel is null)
{
DragHandel = this;
}
}
}

View File

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