Files
MovementTests/addons/forge/editor/statescript/resolvers/SharedVariableResolverEditor.cs
Minimata 1d856fd937
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 26s
Create tag and build when new code gets to main / Export (push) Successful in 5m42s
Replicated the weapon flying tick setup using resources
2026-04-07 16:32:26 +02:00

320 lines
7.7 KiB
C#

// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Godot.Resources;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
using Gamesmiths.Forge.Statescript;
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
/// <summary>
/// Resolver editor that binds a node input property to a shared variable on the owning entity. Uses a two-step
/// selection: first select the <see cref="ForgeSharedVariableSet"/> resource, then select a compatible variable from
/// that set. At runtime the value is read from the entity's <see cref="GraphContext.SharedVariables"/> bag.
/// </summary>
[Tool]
internal sealed partial class SharedVariableResolverEditor : NodeEditorProperty
{
private readonly List<string> _setPaths = [];
private readonly List<string> _setDisplayNames = [];
private readonly List<string> _variableNames = [];
private OptionButton? _setDropdown;
private OptionButton? _variableDropdown;
private Action? _onChanged;
private Type _expectedType = typeof(Variant128);
private string _selectedSetPath = string.Empty;
private string _selectedVariableName = string.Empty;
private StatescriptVariableType _selectedVariableType = StatescriptVariableType.Int;
/// <inheritdoc/>
public override string DisplayName => "Shared Variable";
/// <inheritdoc/>
public override string ResolverTypeId => "SharedVariable";
/// <inheritdoc/>
public override bool IsCompatibleWith(Type expectedType)
{
return true;
}
/// <inheritdoc/>
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
_onChanged = onChanged;
_expectedType = expectedType;
SizeFlagsHorizontal = SizeFlags.ExpandFill;
var vBox = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(vBox);
if (property?.Resolver is SharedVariableResolverResource sharedRes)
{
_selectedSetPath = sharedRes.SharedVariableSetPath;
_selectedVariableName = sharedRes.VariableName;
_selectedVariableType = sharedRes.VariableType;
}
var setRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
vBox.AddChild(setRow);
setRow.AddChild(new Label
{
Text = "Set:",
CustomMinimumSize = new Vector2(45, 0),
HorizontalAlignment = HorizontalAlignment.Right,
});
_setDropdown = new OptionButton { SizeFlagsHorizontal = SizeFlags.ExpandFill };
PopulateSetDropdown();
setRow.AddChild(_setDropdown);
var varRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
vBox.AddChild(varRow);
varRow.AddChild(new Label
{
Text = "Var:",
CustomMinimumSize = new Vector2(45, 0),
HorizontalAlignment = HorizontalAlignment.Right,
});
_variableDropdown = new OptionButton { SizeFlagsHorizontal = SizeFlags.ExpandFill };
PopulateVariableDropdown();
varRow.AddChild(_variableDropdown);
_setDropdown.ItemSelected += OnSetDropdownItemSelected;
_variableDropdown.ItemSelected += OnVariableDropdownItemSelected;
}
/// <inheritdoc/>
public override void SaveTo(StatescriptNodeProperty property)
{
property.Resolver = new SharedVariableResolverResource
{
SharedVariableSetPath = _selectedSetPath,
VariableName = _selectedVariableName,
VariableType = _selectedVariableType,
};
}
/// <inheritdoc/>
public override void ClearCallbacks()
{
base.ClearCallbacks();
_onChanged = null;
}
private static List<string> FindAllSharedVariableSetPaths()
{
var results = new List<string>();
EditorFileSystemDirectory root = EditorInterface.Singleton.GetResourceFilesystem().GetFilesystem();
ScanFilesystemDirectory(root, results);
return results;
}
private static void ScanFilesystemDirectory(EditorFileSystemDirectory dir, List<string> results)
{
for (var i = 0; i < dir.GetFileCount(); i++)
{
var path = dir.GetFilePath(i);
if (!path.EndsWith(".tres", StringComparison.InvariantCultureIgnoreCase)
&& !path.EndsWith(".res", StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
Resource resource = ResourceLoader.Load(path);
if (resource is ForgeSharedVariableSet)
{
results.Add(path);
}
}
for (var i = 0; i < dir.GetSubdirCount(); i++)
{
ScanFilesystemDirectory(dir.GetSubdir(i), results);
}
}
private void OnSetDropdownItemSelected(long index)
{
if (_setDropdown is null)
{
return;
}
var idx = _setDropdown.Selected;
_selectedSetPath = idx >= 0 && idx < _setPaths.Count ? _setPaths[idx] : string.Empty;
_selectedVariableName = string.Empty;
_selectedVariableType = StatescriptVariableType.Int;
PopulateVariableDropdown();
_onChanged?.Invoke();
}
private void OnVariableDropdownItemSelected(long index)
{
if (_variableDropdown is null)
{
return;
}
var idx = _variableDropdown.Selected;
if (idx >= 0 && idx < _variableNames.Count)
{
_selectedVariableName = _variableNames[idx];
ResolveVariableType();
}
else
{
_selectedVariableName = string.Empty;
_selectedVariableType = StatescriptVariableType.Int;
}
_onChanged?.Invoke();
}
private void PopulateSetDropdown()
{
if (_setDropdown is null)
{
return;
}
_setDropdown.Clear();
_setPaths.Clear();
_setDisplayNames.Clear();
_setDropdown.AddItem("(None)");
_setPaths.Add(string.Empty);
_setDisplayNames.Add("(None)");
foreach (var path in FindAllSharedVariableSetPaths())
{
var displayName = path[(path.LastIndexOf('/') + 1)..];
if (displayName.EndsWith(".tres", StringComparison.OrdinalIgnoreCase))
{
displayName = displayName[..^5];
}
_setDropdown.AddItem(displayName);
_setPaths.Add(path);
_setDisplayNames.Add(displayName);
}
// Restore selection.
for (var i = 0; i < _setPaths.Count; i++)
{
if (_setPaths[i] == _selectedSetPath)
{
_setDropdown.Selected = i;
return;
}
}
_setDropdown.Selected = 0;
_selectedSetPath = string.Empty;
}
private void PopulateVariableDropdown()
{
if (_variableDropdown is null)
{
return;
}
_variableDropdown.Clear();
_variableNames.Clear();
_variableDropdown.AddItem("(None)");
_variableNames.Add(string.Empty);
if (!string.IsNullOrEmpty(_selectedSetPath) && ResourceLoader.Exists(_selectedSetPath))
{
ForgeSharedVariableSet? set = ResourceLoader.Load<ForgeSharedVariableSet>(_selectedSetPath);
if (set is not null)
{
foreach (ForgeSharedVariableDefinition def in set.Variables)
{
if (string.IsNullOrEmpty(def.VariableName))
{
continue;
}
if (_expectedType != typeof(Variant128)
&& !StatescriptVariableTypeConverter.IsCompatible(_expectedType, def.VariableType))
{
continue;
}
var label = $"{def.VariableName}";
_variableDropdown.AddItem(label);
_variableNames.Add(def.VariableName);
}
}
}
// Restore selection.
for (var i = 0; i < _variableNames.Count; i++)
{
if (_variableNames[i] == _selectedVariableName)
{
_variableDropdown.Selected = i;
return;
}
}
_variableDropdown.Selected = 0;
_selectedVariableName = string.Empty;
}
private void ResolveVariableType()
{
if (string.IsNullOrEmpty(_selectedSetPath)
|| string.IsNullOrEmpty(_selectedVariableName)
|| !ResourceLoader.Exists(_selectedSetPath))
{
_selectedVariableType = StatescriptVariableType.Int;
return;
}
ForgeSharedVariableSet? set = ResourceLoader.Load<ForgeSharedVariableSet>(_selectedSetPath);
if (set is null)
{
_selectedVariableType = StatescriptVariableType.Int;
return;
}
foreach (ForgeSharedVariableDefinition def in set.Variables)
{
if (def.VariableName == _selectedVariableName)
{
_selectedVariableType = def.VariableType;
return;
}
}
_selectedVariableType = StatescriptVariableType.Int;
}
}
#endif