Replicated the weapon flying tick setup using resources
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user