update forge
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 27s
Create tag and build when new code gets to main / Export (push) Successful in 7m25s

This commit is contained in:
2026-05-17 00:06:44 +02:00
parent 8b54f00814
commit e09714cf83
473 changed files with 13577 additions and 767 deletions

View File

@@ -0,0 +1,289 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using Godot;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class AsymmetricBinaryNestedResolverEditorBase<TResource> : NodeEditorProperty
where TResource : BinaryNestedResolverResourceBase, new()
{
private StatescriptGraph? _graph;
private Action? _onChanged;
private Type _expectedType = typeof(ForgeVariant128);
private OptionButton? _leftResolverDropdown;
private OptionButton? _rightResolverDropdown;
private FoldableContainer? _leftFoldable;
private FoldableContainer? _rightFoldable;
private VBoxContainer? _leftEditorContainer;
private VBoxContainer? _rightEditorContainer;
private NodeEditorProperty? _leftEditor;
private NodeEditorProperty? _rightEditor;
private List<Func<NodeEditorProperty>> _leftFactories = [];
private List<Func<NodeEditorProperty>> _rightFactories = [];
protected abstract Type[] LeftFactoryExpectedTypes { get; }
protected abstract Type[] RightFactoryExpectedTypes { get; }
protected abstract Type LeftNestedExpectedType { get; }
protected abstract Type RightNestedExpectedType { get; }
protected virtual string LeftTitle => "Left:";
protected virtual string RightTitle => "Right:";
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
_graph = graph;
_onChanged = onChanged;
_expectedType = expectedType;
_leftFactories = ResolverEditorFactoryCatalog.GetCompatibleFactories(
GetConstrainedExpectedTypes(GetLeftFactoryExpectedTypes(expectedType), expectedType));
_rightFactories = ResolverEditorFactoryCatalog.GetCompatibleFactories(
GetConstrainedExpectedTypes(GetRightFactoryExpectedTypes(expectedType), expectedType));
SizeFlagsHorizontal = SizeFlags.ExpandFill;
var vBox = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(vBox);
if (_leftFactories.Count == 0 || _rightFactories.Count == 0)
{
var errorLabel = new Label { Text = "No compatible resolvers." };
errorLabel.AddThemeColorOverride("font_color", Colors.Red);
vBox.AddChild(errorLabel);
return;
}
var existingResource = property?.Resolver as TResource;
_leftFoldable = CreateFoldable(LeftTitle, existingResource?.LeftFolded ?? true);
vBox.AddChild(_leftFoldable);
var leftContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_leftFoldable.AddChild(leftContainer);
_leftEditorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_leftResolverDropdown = CreateResolverDropdownControl(_leftFactories, existingResource?.Left);
leftContainer.AddChild(_leftResolverDropdown);
leftContainer.AddChild(_leftEditorContainer);
ShowNestedEditor(
_leftFactories,
GetSelectedIndex(_leftFactories, existingResource?.Left),
existingResource?.Left,
GetConstrainedExpectedTypes(GetLeftFactoryExpectedTypes(expectedType), expectedType),
_leftEditorContainer,
x => _leftEditor = x);
_leftResolverDropdown.ItemSelected += OnLeftResolverDropdownItemSelected;
_rightFoldable = CreateFoldable(RightTitle, existingResource?.RightFolded ?? true);
vBox.AddChild(_rightFoldable);
var rightContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_rightFoldable.AddChild(rightContainer);
_rightEditorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_rightResolverDropdown = CreateResolverDropdownControl(_rightFactories, existingResource?.Right);
rightContainer.AddChild(_rightResolverDropdown);
rightContainer.AddChild(_rightEditorContainer);
ShowNestedEditor(
_rightFactories,
GetSelectedIndex(_rightFactories, existingResource?.Right),
existingResource?.Right,
GetConstrainedExpectedTypes(GetRightFactoryExpectedTypes(expectedType), expectedType),
_rightEditorContainer,
x => _rightEditor = x);
_rightResolverDropdown.ItemSelected += OnRightResolverDropdownItemSelected;
UpdateFoldableTitles();
}
public override void SaveTo(StatescriptNodeProperty property)
{
StatescriptResolverResource? left = null;
StatescriptResolverResource? right = null;
if (_leftEditor is not null)
{
var leftProperty = new StatescriptNodeProperty();
_leftEditor.SaveTo(leftProperty);
left = leftProperty.Resolver;
}
if (_rightEditor is not null)
{
var rightProperty = new StatescriptNodeProperty();
_rightEditor.SaveTo(rightProperty);
right = rightProperty.Resolver;
}
property.Resolver = new TResource
{
Left = left,
LeftFolded = _leftFoldable?.Folded ?? false,
Right = right,
RightFolded = _rightFoldable?.Folded ?? false,
};
}
public override void ClearCallbacks()
{
base.ClearCallbacks();
_onChanged = null;
_leftEditor?.ClearCallbacks();
_rightEditor?.ClearCallbacks();
}
protected virtual Type[] GetLeftFactoryExpectedTypes(Type expectedType)
{
return LeftFactoryExpectedTypes;
}
protected virtual Type[] GetRightFactoryExpectedTypes(Type expectedType)
{
return RightFactoryExpectedTypes;
}
private static int GetSelectedIndex(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver)
{
return NestedResolverEditorUtilities.GetSelectedIndex(factories, existingResolver);
}
private static OptionButton CreateResolverDropdownControl(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver)
{
return NestedResolverEditorUtilities.CreateResolverDropdownControl(factories, existingResolver);
}
private FoldableContainer CreateFoldable(string title, bool folded)
{
var foldable = new FoldableContainer { Title = title, Folded = folded };
foldable.FoldingChanged += OnFoldingChanged;
return foldable;
}
private void OnFoldingChanged(bool isFolded)
{
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void OnLeftResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged(
_leftFactories,
(int)index,
GetConstrainedExpectedTypes(GetLeftFactoryExpectedTypes(_expectedType), _expectedType),
_leftEditorContainer,
x => _leftEditor = x);
}
private void OnRightResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged(
_rightFactories,
(int)index,
GetConstrainedExpectedTypes(GetRightFactoryExpectedTypes(_expectedType), _expectedType),
_rightEditorContainer,
x => _rightEditor = x);
}
private void HandleResolverDropdownChanged(
List<Func<NodeEditorProperty>> factories,
int selectedIndex,
Type[] allowedExpectedTypes,
VBoxContainer? editorContainer,
Action<NodeEditorProperty?> setEditor)
{
if (editorContainer is null)
{
return;
}
NestedResolverEditorUtilities.ClearContainer(editorContainer);
setEditor(null);
ShowNestedEditor(
factories,
selectedIndex,
null,
allowedExpectedTypes,
editorContainer,
setEditor);
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void ShowNestedEditor(
List<Func<NodeEditorProperty>> factories,
int factoryIndex,
StatescriptResolverResource? existingResolver,
Type[] allowedExpectedTypes,
VBoxContainer? container,
Action<NodeEditorProperty?> setEditor)
{
if (_graph is null || container is null || factoryIndex < 0 || factoryIndex >= factories.Count)
{
return;
}
NodeEditorProperty? editor = NestedResolverEditorUtilities.CreateNestedEditor(
_graph,
factories,
factoryIndex,
existingResolver,
allowedExpectedTypes,
OnNestedEditorChanged,
RaiseLayoutSizeChanged);
if (editor is null)
{
return;
}
container.AddChild(editor);
setEditor(editor);
}
private void OnNestedEditorChanged()
{
UpdateFoldableTitles();
_onChanged?.Invoke();
}
private void UpdateFoldableTitles()
{
if (_leftFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(LeftTitle, _leftFoldable, _leftEditor);
}
if (_rightFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(RightTitle, _rightFoldable, _rightEditor);
}
}
private Type[] GetConstrainedExpectedTypes(Type[] candidateExpectedTypes, Type expectedType)
{
return NestedResolverEditorUtilities.ConstrainExpectedTypes(
GetAllowedExpectedTypes(expectedType),
candidateExpectedTypes);
}
}
#endif

View File

@@ -0,0 +1,288 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using Godot;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class BinaryNestedResolverEditorBase<TResource> : NodeEditorProperty
where TResource : BinaryNestedResolverResourceBase, new()
{
private StatescriptGraph? _graph;
private Action? _onChanged;
private Type _expectedType = typeof(ForgeVariant128);
private OptionButton? _leftResolverDropdown;
private OptionButton? _rightResolverDropdown;
private FoldableContainer? _leftFoldable;
private FoldableContainer? _rightFoldable;
private VBoxContainer? _leftEditorContainer;
private VBoxContainer? _rightEditorContainer;
private NodeEditorProperty? _leftEditor;
private NodeEditorProperty? _rightEditor;
private List<Func<NodeEditorProperty>> _factories = [];
protected abstract Type[] FactoryExpectedTypes { get; }
protected abstract Type NestedExpectedType { get; }
protected virtual string LeftTitle => "Left:";
protected virtual string RightTitle => "Right:";
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
_graph = graph;
_onChanged = onChanged;
_expectedType = expectedType;
_factories = ResolverEditorFactoryCatalog.GetCompatibleFactories(GetConstrainedExpectedTypes(expectedType));
SizeFlagsHorizontal = SizeFlags.ExpandFill;
var vBox = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(vBox);
if (_factories.Count == 0)
{
var errorLabel = new Label { Text = "No compatible resolvers." };
errorLabel.AddThemeColorOverride("font_color", Colors.Red);
vBox.AddChild(errorLabel);
return;
}
var existingResource = property?.Resolver as TResource;
_leftFoldable = CreateFoldable(LeftTitle, existingResource?.LeftFolded ?? true);
vBox.AddChild(_leftFoldable);
var leftContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_leftFoldable.AddChild(leftContainer);
_leftEditorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_leftResolverDropdown = CreateResolverDropdownControl(existingResource?.Left);
leftContainer.AddChild(_leftResolverDropdown);
leftContainer.AddChild(_leftEditorContainer);
ShowNestedEditor(
GetSelectedIndex(existingResource?.Left),
existingResource?.Left,
_leftEditorContainer,
x => _leftEditor = x);
_leftResolverDropdown.ItemSelected += OnLeftResolverDropdownItemSelected;
_rightFoldable = CreateFoldable(RightTitle, existingResource?.RightFolded ?? true);
vBox.AddChild(_rightFoldable);
var rightContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_rightFoldable.AddChild(rightContainer);
_rightEditorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_rightResolverDropdown = CreateResolverDropdownControl(existingResource?.Right);
rightContainer.AddChild(_rightResolverDropdown);
rightContainer.AddChild(_rightEditorContainer);
ShowNestedEditor(
GetSelectedIndex(existingResource?.Right),
existingResource?.Right,
_rightEditorContainer,
x => _rightEditor = x);
_rightResolverDropdown.ItemSelected += OnRightResolverDropdownItemSelected;
UpdateFoldableTitles();
}
public override void SaveTo(StatescriptNodeProperty property)
{
StatescriptResolverResource? left = null;
StatescriptResolverResource? right = null;
if (_leftEditor is not null)
{
var leftProperty = new StatescriptNodeProperty();
_leftEditor.SaveTo(leftProperty);
left = leftProperty.Resolver;
}
if (_rightEditor is not null)
{
var rightProperty = new StatescriptNodeProperty();
_rightEditor.SaveTo(rightProperty);
right = rightProperty.Resolver;
}
property.Resolver = new TResource
{
Left = left,
LeftFolded = _leftFoldable?.Folded ?? false,
Right = right,
RightFolded = _rightFoldable?.Folded ?? false,
};
}
public override void ClearCallbacks()
{
base.ClearCallbacks();
_onChanged = null;
_leftEditor?.ClearCallbacks();
_rightEditor?.ClearCallbacks();
}
public override bool TryGetHighlightedVariableName(out string variableName)
{
if (_leftEditor is not null && _leftEditor.TryGetHighlightedVariableName(out variableName))
{
return true;
}
if (_rightEditor is not null && _rightEditor.TryGetHighlightedVariableName(out variableName))
{
return true;
}
variableName = string.Empty;
return false;
}
public override bool TryGetHighlightedSharedVariable(out string sharedVariableSetPath, out string variableName)
{
if (_leftEditor is not null
&& _leftEditor.TryGetHighlightedSharedVariable(out sharedVariableSetPath, out variableName))
{
return true;
}
if (_rightEditor is not null
&& _rightEditor.TryGetHighlightedSharedVariable(out sharedVariableSetPath, out variableName))
{
return true;
}
sharedVariableSetPath = string.Empty;
variableName = string.Empty;
return false;
}
protected virtual Type[] GetFactoryExpectedTypes(Type expectedType)
{
return FactoryExpectedTypes;
}
protected virtual Type GetNestedExpectedType(Type expectedType)
{
return NestedExpectedType;
}
private FoldableContainer CreateFoldable(string title, bool folded)
{
var foldable = new FoldableContainer { Title = title, Folded = folded };
foldable.FoldingChanged += OnFoldingChanged;
return foldable;
}
private void OnFoldingChanged(bool isFolded)
{
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void OnLeftResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged((int)index, _leftEditorContainer, x => _leftEditor = x);
}
private void OnRightResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged((int)index, _rightEditorContainer, x => _rightEditor = x);
}
private void HandleResolverDropdownChanged(
int selectedIndex,
VBoxContainer? editorContainer,
Action<NodeEditorProperty?> setEditor)
{
if (editorContainer is null)
{
return;
}
NestedResolverEditorUtilities.ClearContainer(editorContainer);
setEditor(null);
ShowNestedEditor(selectedIndex, null, editorContainer, setEditor);
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private int GetSelectedIndex(StatescriptResolverResource? existingResolver)
{
return NestedResolverEditorUtilities.GetSelectedIndex(_factories, existingResolver);
}
private OptionButton CreateResolverDropdownControl(StatescriptResolverResource? existingResolver)
{
return NestedResolverEditorUtilities.CreateResolverDropdownControl(_factories, existingResolver);
}
private void ShowNestedEditor(
int factoryIndex,
StatescriptResolverResource? existingResolver,
VBoxContainer? container,
Action<NodeEditorProperty?> setEditor)
{
if (_graph is null || container is null || factoryIndex < 0 || factoryIndex >= _factories.Count)
{
return;
}
NodeEditorProperty? editor = NestedResolverEditorUtilities.CreateNestedEditor(
_graph,
_factories,
factoryIndex,
existingResolver,
GetConstrainedExpectedTypes(_expectedType),
OnNestedEditorChanged,
RaiseLayoutSizeChanged);
if (editor is null)
{
return;
}
container.AddChild(editor);
setEditor(editor);
}
private void OnNestedEditorChanged()
{
UpdateFoldableTitles();
_onChanged?.Invoke();
}
private void UpdateFoldableTitles()
{
if (_leftFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(LeftTitle, _leftFoldable, _leftEditor);
}
if (_rightFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(RightTitle, _rightFoldable, _rightEditor);
}
}
private Type[] GetConstrainedExpectedTypes(Type expectedType)
{
return NestedResolverEditorUtilities.ConstrainExpectedTypes(
GetAllowedExpectedTypes(expectedType),
GetFactoryExpectedTypes(expectedType));
}
}
#endif

View File

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

View File

@@ -0,0 +1,22 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class BooleanBinaryResolverEditorBase<TResource> : BinaryNestedResolverEditorBase<TResource>
where TResource : BinaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes => [typeof(bool)];
protected override Type NestedExpectedType => typeof(bool);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(bool) || expectedType == typeof(ForgeVariant128);
}
}
#endif

View File

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

View File

@@ -0,0 +1,22 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class BooleanUnaryResolverEditorBase<TResource> : UnaryNestedResolverEditorBase<TResource>
where TResource : UnaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes => [typeof(bool)];
protected override Type NestedExpectedType => typeof(bool);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(bool) || expectedType == typeof(ForgeVariant128);
}
}
#endif

View File

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

View File

@@ -0,0 +1,23 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class FloatUnaryResolverEditorBase<TResource> : UnaryNestedResolverEditorBase<TResource>
where TResource : UnaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes => ResolverEditorCompatibility.FloatOperandExpectedTypes;
protected override Type NestedExpectedType => typeof(float);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsFloatType(expectedType);
}
}
#endif

View File

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

View File

@@ -0,0 +1,165 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Godot;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal static class NestedResolverEditorUtilities
{
public static int GetSelectedIndex(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver)
{
return ResolverEditorFactoryCatalog.GetDefaultFactoryIndex(factories, existingResolver, "Variant");
}
public static OptionButton CreateResolverDropdownControl(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver)
{
var dropdown = new OptionButton { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill };
foreach (Func<NodeEditorProperty> factory in factories)
{
dropdown.AddItem(StatescriptResolverRegistry.GetDisplayName(factory));
}
dropdown.Selected = GetSelectedIndex(factories, existingResolver);
return dropdown;
}
public static void ClearContainer(VBoxContainer? container)
{
if (container is null)
{
return;
}
foreach (Node child in container.GetChildren())
{
container.RemoveChild(child);
child.Free();
}
}
public static Type[] ConstrainExpectedTypes(Type[] allowedExpectedTypes, Type[] candidateExpectedTypes)
{
if (allowedExpectedTypes.Length == 0)
{
return candidateExpectedTypes;
}
if (ContainsVariantType(allowedExpectedTypes))
{
return candidateExpectedTypes;
}
var constrainedExpectedTypes = new List<Type>();
for (int i = 0; i < candidateExpectedTypes.Length; i++)
{
Type candidateExpectedType = candidateExpectedTypes[i];
for (int j = 0; j < allowedExpectedTypes.Length; j++)
{
if (!AreExpectedTypesCompatible(candidateExpectedType, allowedExpectedTypes[j]))
{
continue;
}
if (!constrainedExpectedTypes.Contains(candidateExpectedType))
{
constrainedExpectedTypes.Add(candidateExpectedType);
}
break;
}
}
return constrainedExpectedTypes.Count > 0 ? [.. constrainedExpectedTypes] : candidateExpectedTypes;
}
public static NodeEditorProperty? CreateNestedEditor(
StatescriptGraph? graph,
List<Func<NodeEditorProperty>> factories,
int factoryIndex,
StatescriptResolverResource? existingResolver,
Type[] allowedExpectedTypes,
Action onChanged,
Action layoutSizeChanged)
{
if (graph is null || factoryIndex < 0 || factoryIndex >= factories.Count)
{
return null;
}
NodeEditorProperty editor = factories[factoryIndex]();
Type[] compatibleExpectedTypes = GetCompatibleExpectedTypes(editor, allowedExpectedTypes);
Type[] effectiveAllowedExpectedTypes = compatibleExpectedTypes.Length > 0
? compatibleExpectedTypes
: allowedExpectedTypes;
editor.ConfigureAllowedExpectedTypes(effectiveAllowedExpectedTypes);
StatescriptNodeProperty? tempProperty = existingResolver is null
? null
: new StatescriptNodeProperty { Resolver = existingResolver };
editor.Setup(
graph,
tempProperty,
GetEffectiveExpectedType(effectiveAllowedExpectedTypes),
onChanged,
false);
editor.LayoutSizeChanged += layoutSizeChanged;
return editor;
}
private static Type[] GetCompatibleExpectedTypes(NodeEditorProperty editor, Type[] allowedExpectedTypes)
{
var compatibleExpectedTypes = new List<Type>();
for (int i = 0; i < allowedExpectedTypes.Length; i++)
{
Type allowedExpectedType = allowedExpectedTypes[i];
if (editor.IsCompatibleWith(allowedExpectedType) && !compatibleExpectedTypes.Contains(allowedExpectedType))
{
compatibleExpectedTypes.Add(allowedExpectedType);
}
}
return [.. compatibleExpectedTypes];
}
private static Type GetEffectiveExpectedType(Type[] allowedExpectedTypes)
{
return allowedExpectedTypes.Length == 1
? allowedExpectedTypes[0]
: typeof(ForgeVariant128);
}
private static bool AreExpectedTypesCompatible(Type candidateExpectedType, Type allowedExpectedType)
{
return ResolverEditorCompatibility.AreExpectedTypesCompatible(candidateExpectedType, allowedExpectedType);
}
private static bool ContainsVariantType(Type[] expectedTypes)
{
for (int i = 0; i < expectedTypes.Length; i++)
{
if (expectedTypes[i] == typeof(ForgeVariant128))
{
return true;
}
}
return false;
}
}
#endif

View File

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

View File

@@ -0,0 +1,35 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class NumericOrVectorBinaryResolverEditorBase<TResource>
: BinaryNestedResolverEditorBase<TResource>
where TResource : BinaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes =>
[
typeof(int),
typeof(float),
typeof(double),
typeof(SysVector2),
typeof(SysVector3),
typeof(SysVector4),
];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsNumericOrVectorType(expectedType);
}
}
#endif

View File

@@ -0,0 +1,51 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class NumericOrVectorTernaryResolverEditorBase<TResource>
: TernaryNestedResolverEditorBase<TResource>
where TResource : TernaryNestedResolverResourceBase, new()
{
protected override Type[] FirstFactoryExpectedTypes =>
[typeof(int), typeof(float), typeof(double), typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)];
protected override Type[] SecondFactoryExpectedTypes =>
[typeof(int), typeof(float), typeof(double), typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)];
protected override Type[] ThirdFactoryExpectedTypes => [typeof(int), typeof(float), typeof(double)];
protected override Type FirstNestedExpectedType => typeof(ForgeVariant128);
protected override Type SecondNestedExpectedType => typeof(ForgeVariant128);
protected override Type ThirdNestedExpectedType => typeof(float);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsNumericOrVectorType(expectedType);
}
protected override Type[] GetFirstFactoryExpectedTypes(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? FirstFactoryExpectedTypes
: [expectedType];
}
protected override Type[] GetSecondFactoryExpectedTypes(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? SecondFactoryExpectedTypes
: [expectedType];
}
}
#endif

View File

@@ -0,0 +1,49 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class NumericOrVectorUnaryResolverEditorBase<TResource>
: UnaryNestedResolverEditorBase<TResource>
where TResource : UnaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes =>
[
typeof(int),
typeof(float),
typeof(double),
typeof(SysVector2),
typeof(SysVector3),
typeof(SysVector4),
];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsNumericOrVectorType(expectedType);
}
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? FactoryExpectedTypes
: [expectedType];
}
protected override Type GetNestedExpectedType(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? typeof(ForgeVariant128)
: expectedType;
}
}
#endif

View File

@@ -0,0 +1,22 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class NumericUnaryResolverEditorBase<TResource> : UnaryNestedResolverEditorBase<TResource>
where TResource : UnaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes => [typeof(int), typeof(float), typeof(double)];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(int) || expectedType == typeof(ForgeVariant128);
}
}
#endif

View File

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

View File

@@ -0,0 +1,37 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysQuaternion = System.Numerics.Quaternion;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class NumericVectorOrQuaternionBinaryResolverEditorBase<TResource>
: BinaryNestedResolverEditorBase<TResource>
where TResource : BinaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes =>
[
typeof(int),
typeof(float),
typeof(double),
typeof(SysVector2),
typeof(SysVector3),
typeof(SysVector4),
typeof(SysQuaternion),
];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsNumericVectorOrQuaternionType(expectedType);
}
}
#endif

View File

@@ -0,0 +1,31 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
[Tool]
internal abstract partial class RandomInfoResolverEditorBase : NodeEditorProperty
{
protected abstract string InfoText { get; }
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
SizeFlagsHorizontal = SizeFlags.ExpandFill;
AddChild(new Label
{
Text = InfoText,
HorizontalAlignment = HorizontalAlignment.Center,
SizeFlagsHorizontal = SizeFlags.ExpandFill,
});
}
}
#endif

View File

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

View File

@@ -0,0 +1,80 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysPlane = System.Numerics.Plane;
using SysQuaternion = System.Numerics.Quaternion;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal static class ResolverEditorCompatibility
{
public static readonly Type[] FloatOperandExpectedTypes = [typeof(int), typeof(float), typeof(double)];
public static bool IsNumericType(Type expectedType)
{
if (!StatescriptVariableTypeConverter.TryFromSystemType(expectedType, out StatescriptVariableType variableType))
{
return false;
}
return StatescriptEditorControls.IsIntegerType(variableType)
|| StatescriptEditorControls.IsFloatType(variableType);
}
public static bool IsFloatType(Type expectedType)
{
if (!StatescriptVariableTypeConverter.TryFromSystemType(expectedType, out StatescriptVariableType variableType))
{
return false;
}
return StatescriptEditorControls.IsFloatType(variableType);
}
public static bool IsVectorType(Type expectedType)
{
return expectedType == typeof(SysVector2)
|| expectedType == typeof(SysVector3)
|| expectedType == typeof(SysVector4);
}
public static bool IsQuaternionType(Type expectedType)
{
return expectedType == typeof(SysQuaternion);
}
public static bool IsPlaneType(Type expectedType)
{
return expectedType == typeof(SysPlane);
}
public static bool IsVectorPlaneOrQuaternionType(Type expectedType)
{
return IsVectorType(expectedType) || IsPlaneType(expectedType) || IsQuaternionType(expectedType);
}
public static bool IsNumericOrVectorType(Type expectedType)
{
return IsNumericType(expectedType) || IsVectorType(expectedType);
}
public static bool IsNumericVectorOrQuaternionType(Type expectedType)
{
return IsNumericOrVectorType(expectedType) || IsQuaternionType(expectedType);
}
public static bool AreExpectedTypesCompatible(Type candidateExpectedType, Type allowedExpectedType)
{
return candidateExpectedType == allowedExpectedType
|| candidateExpectedType == typeof(ForgeVariant128)
|| allowedExpectedType == typeof(ForgeVariant128)
|| StatescriptVariableTypeConverter.IsCompatible(allowedExpectedType, candidateExpectedType);
}
}
#endif

View File

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

View File

@@ -0,0 +1,54 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using System.Linq;
using Gamesmiths.Forge.Godot.Resources.Statescript;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal static class ResolverEditorFactoryCatalog
{
public static List<Func<NodeEditorProperty>> GetCompatibleFactories(params Type[] expectedTypes)
{
var result = new List<Func<NodeEditorProperty>>();
var seenTypeIds = new HashSet<string>(StringComparer.Ordinal);
foreach (Type expectedType in expectedTypes)
{
result.AddRange(StatescriptResolverRegistry.GetCompatibleFactories(expectedType)
.Where(factory => seenTypeIds.Add(StatescriptResolverRegistry.GetResolverTypeId(factory))));
}
return result;
}
public static int GetDefaultFactoryIndex(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver,
string preferredResolverTypeId)
{
if (existingResolver is not null)
{
for (int i = 0; i < factories.Count; i++)
{
if (StatescriptResolverRegistry.GetResolverTypeId(factories[i]) == existingResolver.ResolverTypeId)
{
return i;
}
}
}
for (int i = 0; i < factories.Count; i++)
{
if (StatescriptResolverRegistry.GetResolverTypeId(factories[i]) == preferredResolverTypeId)
{
return i;
}
}
return 0;
}
}
#endif

View File

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

View File

@@ -0,0 +1,24 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class ScalarBinaryResolverEditorBase<TResource> : BinaryNestedResolverEditorBase<TResource>
where TResource : BinaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes => ResolverEditorCompatibility.FloatOperandExpectedTypes;
protected override Type NestedExpectedType => typeof(float);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(float)
|| expectedType == typeof(double)
|| expectedType == typeof(ForgeVariant128);
}
}
#endif

View File

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

View File

@@ -0,0 +1,68 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using Godot;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class ScalarConstantResolverEditorBase<TResource> : NodeEditorProperty
where TResource : TypedConstantResolverResourceBase, new()
{
private StatescriptVariableType _valueType;
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(float)
|| expectedType == typeof(double)
|| expectedType == typeof(ForgeVariant128);
}
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
if (property?.Resolver is TResource)
{
_valueType = NormalizeValueType();
}
else
{
_valueType = StatescriptVariableType.Double;
}
SizeFlagsHorizontal = SizeFlags.ExpandFill;
var row = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(row);
row.AddChild(new Label
{
Text = "Type:",
CustomMinimumSize = new Vector2(45, 0),
HorizontalAlignment = HorizontalAlignment.Right,
});
row.AddChild(new Label
{
Text = "Float",
SizeFlagsHorizontal = SizeFlags.ExpandFill,
});
}
public override void SaveTo(StatescriptNodeProperty property)
{
property.Resolver = new TResource { ValueType = _valueType };
}
private static StatescriptVariableType NormalizeValueType()
{
return StatescriptVariableType.Double;
}
}
#endif

View File

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

View File

@@ -0,0 +1,24 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class ScalarUnaryResolverEditorBase<TResource> : UnaryNestedResolverEditorBase<TResource>
where TResource : UnaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes => ResolverEditorCompatibility.FloatOperandExpectedTypes;
protected override Type NestedExpectedType => typeof(float);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(float)
|| expectedType == typeof(double)
|| expectedType == typeof(ForgeVariant128);
}
}
#endif

View File

@@ -0,0 +1 @@
uid://20nlyaem5arx

View File

@@ -0,0 +1,41 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class ScaleResolverEditorBase<TResource> : AsymmetricBinaryNestedResolverEditorBase<TResource>
where TResource : BinaryNestedResolverResourceBase, new()
{
protected override Type[] LeftFactoryExpectedTypes => [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)];
protected override Type[] RightFactoryExpectedTypes => ResolverEditorCompatibility.FloatOperandExpectedTypes;
protected override Type LeftNestedExpectedType => typeof(ForgeVariant128);
protected override Type RightNestedExpectedType => typeof(float);
protected override string LeftTitle => "Vector:";
protected override string RightTitle => "Scalar:";
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsVectorType(expectedType);
}
protected override Type[] GetLeftFactoryExpectedTypes(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? LeftFactoryExpectedTypes
: [expectedType];
}
}
#endif

View File

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

View File

@@ -0,0 +1,450 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class TernaryNestedResolverEditorBase<TResource> : NodeEditorProperty
where TResource : TernaryNestedResolverResourceBase, new()
{
private enum ResolverSlot
{
First = 0,
Second = 1,
Third = 2,
}
private StatescriptGraph? _graph;
private Action? _onChanged;
private FoldableContainer? _firstFoldable;
private FoldableContainer? _secondFoldable;
private FoldableContainer? _thirdFoldable;
private NodeEditorProperty? _firstEditor;
private NodeEditorProperty? _secondEditor;
private NodeEditorProperty? _thirdEditor;
private List<Func<NodeEditorProperty>> _firstFactories = [];
private List<Func<NodeEditorProperty>> _secondFactories = [];
private List<Func<NodeEditorProperty>> _thirdFactories = [];
protected abstract Type[] FirstFactoryExpectedTypes { get; }
protected abstract Type[] SecondFactoryExpectedTypes { get; }
protected abstract Type[] ThirdFactoryExpectedTypes { get; }
protected abstract Type FirstNestedExpectedType { get; }
protected abstract Type SecondNestedExpectedType { get; }
protected abstract Type ThirdNestedExpectedType { get; }
protected virtual string FirstTitle => "First:";
protected virtual string SecondTitle => "Second:";
protected virtual string ThirdTitle => "Third:";
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
_graph = graph;
_onChanged = onChanged;
_firstFactories =
ResolverEditorFactoryCatalog.GetCompatibleFactories(
GetConstrainedExpectedTypes(GetFirstFactoryExpectedTypes(expectedType), expectedType));
_secondFactories =
ResolverEditorFactoryCatalog.GetCompatibleFactories(
GetConstrainedExpectedTypes(GetSecondFactoryExpectedTypes(expectedType), expectedType));
_thirdFactories =
ResolverEditorFactoryCatalog.GetCompatibleFactories(
GetConstrainedExpectedTypes(GetThirdFactoryExpectedTypes(expectedType), expectedType));
SizeFlagsHorizontal = SizeFlags.ExpandFill;
var vBox = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(vBox);
if (_firstFactories.Count == 0 || _secondFactories.Count == 0 || _thirdFactories.Count == 0)
{
var errorLabel = new Label { Text = "No compatible resolvers." };
errorLabel.AddThemeColorOverride("font_color", Colors.Red);
vBox.AddChild(errorLabel);
return;
}
var existingResource = property?.Resolver as TResource;
BuildSlot(
vBox,
ResolverSlot.First,
FirstTitle,
_firstFactories,
existingResource?.First,
GetConstrainedExpectedTypes(GetFirstFactoryExpectedTypes(expectedType), expectedType),
existingResource?.FirstFolded ?? true);
BuildSlot(
vBox,
ResolverSlot.Second,
SecondTitle,
_secondFactories,
existingResource?.Second,
GetConstrainedExpectedTypes(GetSecondFactoryExpectedTypes(expectedType), expectedType),
existingResource?.SecondFolded ?? true);
BuildSlot(
vBox,
ResolverSlot.Third,
ThirdTitle,
_thirdFactories,
existingResource?.Third,
GetConstrainedExpectedTypes(GetThirdFactoryExpectedTypes(expectedType), expectedType),
existingResource?.ThirdFolded ?? true);
UpdateFoldableTitles();
}
public override void SaveTo(StatescriptNodeProperty property)
{
property.Resolver = new TResource
{
First = SaveNestedEditor(_firstEditor),
FirstFolded = _firstFoldable?.Folded ?? false,
Second = SaveNestedEditor(_secondEditor),
SecondFolded = _secondFoldable?.Folded ?? false,
Third = SaveNestedEditor(_thirdEditor),
ThirdFolded = _thirdFoldable?.Folded ?? false,
};
}
public override void ClearCallbacks()
{
base.ClearCallbacks();
_onChanged = null;
_firstEditor?.ClearCallbacks();
_secondEditor?.ClearCallbacks();
_thirdEditor?.ClearCallbacks();
_firstFoldable = null;
_secondFoldable = null;
_thirdFoldable = null;
_firstEditor = null;
_secondEditor = null;
_thirdEditor = null;
}
public override bool TryGetHighlightedVariableName(out string variableName)
{
if (_firstEditor is not null && _firstEditor.TryGetHighlightedVariableName(out variableName))
{
return true;
}
if (_secondEditor is not null && _secondEditor.TryGetHighlightedVariableName(out variableName))
{
return true;
}
if (_thirdEditor is not null && _thirdEditor.TryGetHighlightedVariableName(out variableName))
{
return true;
}
variableName = string.Empty;
return false;
}
public override bool TryGetHighlightedSharedVariable(out string sharedVariableSetPath, out string variableName)
{
if (_firstEditor is not null
&& _firstEditor.TryGetHighlightedSharedVariable(out sharedVariableSetPath, out variableName))
{
return true;
}
if (_secondEditor is not null
&& _secondEditor.TryGetHighlightedSharedVariable(out sharedVariableSetPath, out variableName))
{
return true;
}
if (_thirdEditor is not null
&& _thirdEditor.TryGetHighlightedSharedVariable(out sharedVariableSetPath, out variableName))
{
return true;
}
sharedVariableSetPath = string.Empty;
variableName = string.Empty;
return false;
}
protected virtual Type[] GetFirstFactoryExpectedTypes(Type expectedType)
{
return FirstFactoryExpectedTypes;
}
protected virtual Type[] GetSecondFactoryExpectedTypes(Type expectedType)
{
return SecondFactoryExpectedTypes;
}
protected virtual Type[] GetThirdFactoryExpectedTypes(Type expectedType)
{
return ThirdFactoryExpectedTypes;
}
private static StatescriptResolverResource? SaveNestedEditor(NodeEditorProperty? editor)
{
if (editor is null)
{
return null;
}
var property = new StatescriptNodeProperty();
editor.SaveTo(property);
return property.Resolver;
}
private static int GetSelectedIndex(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver)
{
return NestedResolverEditorUtilities.GetSelectedIndex(factories, existingResolver);
}
private static VBoxContainer? GetNestedEditorContainer(FoldableContainer? foldable)
{
if (foldable is null || foldable.GetChildCount() <= 0)
{
return null;
}
if (foldable.GetChild(0) is not VBoxContainer slotContainer || slotContainer.GetChildCount() <= 1)
{
return null;
}
return slotContainer.GetChild(1) as VBoxContainer;
}
private void BuildSlot(
VBoxContainer root,
ResolverSlot slot,
string title,
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver,
Type[] allowedExpectedTypes,
bool folded)
{
FoldableContainer foldable = new() { Title = title, Folded = folded };
foldable.FoldingChanged += OnFoldableFoldingChanged;
SetFoldable(slot, foldable);
root.AddChild(foldable);
var container = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
foldable.AddChild(container);
OptionButton resolverDropdown =
NestedResolverEditorUtilities.CreateResolverDropdownControl(factories, existingResolver);
int selectedIndex = GetSelectedIndex(factories, existingResolver);
resolverDropdown.Selected = selectedIndex;
container.AddChild(resolverDropdown);
var editorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
container.AddChild(editorContainer);
ShowNestedEditor(
slot,
factories,
selectedIndex,
existingResolver,
allowedExpectedTypes,
editorContainer);
resolverDropdown.ItemSelected += slot switch
{
ResolverSlot.First => OnFirstResolverDropdownItemSelected,
ResolverSlot.Second => OnSecondResolverDropdownItemSelected,
_ => OnThirdResolverDropdownItemSelected,
};
}
private void ShowNestedEditor(
ResolverSlot slot,
List<Func<NodeEditorProperty>> factories,
int factoryIndex,
StatescriptResolverResource? existingResolver,
Type[] allowedExpectedTypes,
VBoxContainer container)
{
if (_graph is null || factoryIndex < 0 || factoryIndex >= factories.Count)
{
return;
}
NodeEditorProperty? editor = NestedResolverEditorUtilities.CreateNestedEditor(
_graph,
factories,
factoryIndex,
existingResolver,
allowedExpectedTypes,
OnNestedEditorChanged,
RaiseLayoutSizeChanged);
if (editor is null)
{
return;
}
container.AddChild(editor);
SetEditor(slot, editor);
}
private void OnFoldableFoldingChanged(bool folded)
{
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void OnFirstResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged(ResolverSlot.First, (int)index, _firstFactories, GetFirstAllowedExpectedTypes());
}
private void OnSecondResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged(ResolverSlot.Second, (int)index, _secondFactories, GetSecondAllowedExpectedTypes());
}
private void OnThirdResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged(ResolverSlot.Third, (int)index, _thirdFactories, GetThirdAllowedExpectedTypes());
}
private void HandleResolverDropdownChanged(
ResolverSlot slot,
int index,
List<Func<NodeEditorProperty>> factories,
Type[] allowedExpectedTypes)
{
VBoxContainer? editorContainer = GetEditorContainer(slot);
if (editorContainer is null)
{
return;
}
NestedResolverEditorUtilities.ClearContainer(editorContainer);
SetEditor(slot, null);
ShowNestedEditor(slot, factories, index, null, allowedExpectedTypes, editorContainer);
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private Type[] GetFirstAllowedExpectedTypes()
{
return GetConstrainedExpectedTypes(FirstFactoryExpectedTypes, FirstNestedExpectedType);
}
private Type[] GetSecondAllowedExpectedTypes()
{
return GetConstrainedExpectedTypes(SecondFactoryExpectedTypes, SecondNestedExpectedType);
}
private Type[] GetThirdAllowedExpectedTypes()
{
return GetConstrainedExpectedTypes(ThirdFactoryExpectedTypes, ThirdNestedExpectedType);
}
private void SetFoldable(ResolverSlot slot, FoldableContainer foldable)
{
switch (slot)
{
case ResolverSlot.First:
_firstFoldable = foldable;
break;
case ResolverSlot.Second:
_secondFoldable = foldable;
break;
default:
_thirdFoldable = foldable;
break;
}
}
private void SetEditor(ResolverSlot slot, NodeEditorProperty? editor)
{
switch (slot)
{
case ResolverSlot.First:
_firstEditor = editor;
break;
case ResolverSlot.Second:
_secondEditor = editor;
break;
default:
_thirdEditor = editor;
break;
}
}
private VBoxContainer? GetEditorContainer(ResolverSlot slot)
{
return slot switch
{
ResolverSlot.First => GetNestedEditorContainer(_firstFoldable),
ResolverSlot.Second => GetNestedEditorContainer(_secondFoldable),
_ => GetNestedEditorContainer(_thirdFoldable),
};
}
private void OnNestedEditorChanged()
{
UpdateFoldableTitles();
_onChanged?.Invoke();
}
private void UpdateFoldableTitles()
{
if (_firstFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(
FirstTitle,
_firstFoldable,
_firstEditor);
}
if (_secondFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(
SecondTitle,
_secondFoldable,
_secondEditor);
}
if (_thirdFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(
ThirdTitle,
_thirdFoldable,
_thirdEditor);
}
}
private Type[] GetConstrainedExpectedTypes(Type[] candidateExpectedTypes, Type expectedType)
{
return NestedResolverEditorUtilities.ConstrainExpectedTypes(
GetAllowedExpectedTypes(expectedType),
candidateExpectedTypes);
}
}
#endif

View File

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

View File

@@ -0,0 +1,225 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using Godot;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class UnaryNestedResolverEditorBase<TResource> : NodeEditorProperty
where TResource : UnaryNestedResolverResourceBase, new()
{
private StatescriptGraph? _graph;
private Action? _onChanged;
private Type _expectedType = typeof(ForgeVariant128);
private FoldableContainer? _operandFoldable;
private OptionButton? _resolverDropdown;
private VBoxContainer? _editorContainer;
private NodeEditorProperty? _operandEditor;
private List<Func<NodeEditorProperty>> _factories = [];
protected abstract Type[] FactoryExpectedTypes { get; }
protected abstract Type NestedExpectedType { get; }
protected virtual string OperandTitle => "Operand:";
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
_graph = graph;
_onChanged = onChanged;
_expectedType = expectedType;
_factories = ResolverEditorFactoryCatalog.GetCompatibleFactories(GetConstrainedExpectedTypes(expectedType));
SizeFlagsHorizontal = SizeFlags.ExpandFill;
var vBox = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(vBox);
if (_factories.Count == 0)
{
var errorLabel = new Label { Text = "No compatible resolvers." };
errorLabel.AddThemeColorOverride("font_color", Colors.Red);
vBox.AddChild(errorLabel);
return;
}
var existingResource = property?.Resolver as TResource;
_operandFoldable = CreateFoldable(OperandTitle, existingResource?.OperandFolded ?? true);
vBox.AddChild(_operandFoldable);
var operandContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_operandFoldable.AddChild(operandContainer);
_editorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_resolverDropdown = CreateResolverDropdownControl(existingResource?.Operand);
operandContainer.AddChild(_resolverDropdown);
operandContainer.AddChild(_editorContainer);
ShowNestedEditor(GetSelectedIndex(existingResource?.Operand), existingResource?.Operand);
_resolverDropdown.ItemSelected += OnResolverDropdownItemSelected;
UpdateFoldableTitle();
}
public override void SaveTo(StatescriptNodeProperty property)
{
StatescriptResolverResource? operand = null;
if (_operandEditor is not null)
{
var operandProperty = new StatescriptNodeProperty();
_operandEditor.SaveTo(operandProperty);
operand = operandProperty.Resolver;
}
property.Resolver = new TResource
{
Operand = operand,
OperandFolded = _operandFoldable?.Folded ?? false,
};
}
public override void ClearCallbacks()
{
base.ClearCallbacks();
_onChanged = null;
_operandEditor?.ClearCallbacks();
}
public override bool TryGetHighlightedVariableName(out string variableName)
{
if (_operandEditor is not null && _operandEditor.TryGetHighlightedVariableName(out variableName))
{
return true;
}
variableName = string.Empty;
return false;
}
public override bool TryGetHighlightedSharedVariable(out string sharedVariableSetPath, out string variableName)
{
if (_operandEditor is not null
&& _operandEditor.TryGetHighlightedSharedVariable(out sharedVariableSetPath, out variableName))
{
return true;
}
sharedVariableSetPath = string.Empty;
variableName = string.Empty;
return false;
}
protected virtual Type[] GetFactoryExpectedTypes(Type expectedType)
{
return FactoryExpectedTypes;
}
protected virtual Type GetNestedExpectedType(Type expectedType)
{
return NestedExpectedType;
}
private FoldableContainer CreateFoldable(string title, bool folded)
{
var foldable = new FoldableContainer { Title = title, Folded = folded };
foldable.FoldingChanged += OnFoldingChanged;
return foldable;
}
private void OnFoldingChanged(bool isFolded)
{
UpdateFoldableTitle();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void OnResolverDropdownItemSelected(long index)
{
HandleResolverDropdownChanged((int)index);
}
private void HandleResolverDropdownChanged(int selectedIndex)
{
if (_editorContainer is null)
{
return;
}
NestedResolverEditorUtilities.ClearContainer(_editorContainer);
_operandEditor = null;
ShowNestedEditor(selectedIndex, null);
UpdateFoldableTitle();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private int GetSelectedIndex(StatescriptResolverResource? existingResolver)
{
return NestedResolverEditorUtilities.GetSelectedIndex(_factories, existingResolver);
}
private OptionButton CreateResolverDropdownControl(StatescriptResolverResource? existingResolver)
{
return NestedResolverEditorUtilities.CreateResolverDropdownControl(_factories, existingResolver);
}
private void ShowNestedEditor(int factoryIndex, StatescriptResolverResource? existingResolver)
{
if (_graph is null || _editorContainer is null || factoryIndex < 0 || factoryIndex >= _factories.Count)
{
return;
}
NodeEditorProperty? editor = NestedResolverEditorUtilities.CreateNestedEditor(
_graph,
_factories,
factoryIndex,
existingResolver,
GetConstrainedExpectedTypes(_expectedType),
OnNestedEditorChanged,
RaiseLayoutSizeChanged);
if (editor is null)
{
return;
}
_editorContainer.AddChild(editor);
_operandEditor = editor;
}
private void OnNestedEditorChanged()
{
UpdateFoldableTitle();
_onChanged?.Invoke();
}
private void UpdateFoldableTitle()
{
if (_operandFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle(
OperandTitle,
_operandFoldable,
_operandEditor);
}
}
private Type[] GetConstrainedExpectedTypes(Type expectedType)
{
return NestedResolverEditorUtilities.ConstrainExpectedTypes(
GetAllowedExpectedTypes(expectedType),
GetFactoryExpectedTypes(expectedType));
}
}
#endif

View File

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

View File

@@ -0,0 +1,40 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class VectorBinaryResolverEditorBase<TResource> : BinaryNestedResolverEditorBase<TResource>
where TResource : BinaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes => [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsVectorType(expectedType);
}
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)]
: [expectedType];
}
protected override Type GetNestedExpectedType(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? typeof(ForgeVariant128)
: expectedType;
}
}
#endif

View File

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

View File

@@ -0,0 +1,38 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysQuaternion = System.Numerics.Quaternion;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class VectorOrQuaternionBinaryFloatResolverEditorBase<TResource>
: BinaryNestedResolverEditorBase<TResource>
where TResource : BinaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes =>
[typeof(SysVector2), typeof(SysVector3), typeof(SysVector4), typeof(SysQuaternion)];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128) || ResolverEditorCompatibility.IsFloatType(expectedType);
}
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
{
return FactoryExpectedTypes;
}
protected override Type GetNestedExpectedType(Type expectedType)
{
return expectedType == typeof(ForgeVariant128) ? typeof(ForgeVariant128) : expectedType;
}
}
#endif

View File

@@ -0,0 +1,28 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysQuaternion = System.Numerics.Quaternion;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class VectorOrQuaternionUnaryFloatResolverEditorBase<TResource>
: UnaryNestedResolverEditorBase<TResource>
where TResource : UnaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes =>
[typeof(SysVector2), typeof(SysVector3), typeof(SysVector4), typeof(SysQuaternion)];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128) || ResolverEditorCompatibility.IsFloatType(expectedType);
}
}
#endif

View File

@@ -0,0 +1,44 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers.Bases;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysPlane = System.Numerics.Plane;
using SysQuaternion = System.Numerics.Quaternion;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
internal abstract partial class VectorPlaneQuaternionUnaryResolverEditorBase<TResource>
: UnaryNestedResolverEditorBase<TResource>
where TResource : UnaryNestedResolverResourceBase, new()
{
protected override Type[] FactoryExpectedTypes =>
[typeof(SysVector2), typeof(SysVector3), typeof(SysVector4), typeof(SysPlane), typeof(SysQuaternion)];
protected override Type NestedExpectedType => typeof(ForgeVariant128);
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
|| ResolverEditorCompatibility.IsVectorPlaneOrQuaternionType(expectedType);
}
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? FactoryExpectedTypes
: [expectedType];
}
protected override Type GetNestedExpectedType(Type expectedType)
{
return expectedType == typeof(ForgeVariant128)
? typeof(ForgeVariant128)
: expectedType;
}
}
#endif