Replicated the weapon flying tick setup using resources
This commit is contained in:
@@ -0,0 +1,380 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
|
||||
public partial class StatescriptGraphNode
|
||||
{
|
||||
private readonly Dictionary<PropertySlotKey, InputPropertyContext> _inputPropertyContexts = [];
|
||||
|
||||
private void AddInputPropertyRow(
|
||||
StatescriptNodeDiscovery.InputPropertyInfo propInfo,
|
||||
int index,
|
||||
Control sectionContainer)
|
||||
{
|
||||
if (NodeResource is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var container = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
sectionContainer.AddChild(container);
|
||||
|
||||
var headerRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
container.AddChild(headerRow);
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Text = propInfo.Label,
|
||||
CustomMinimumSize = new Vector2(60, 0),
|
||||
};
|
||||
|
||||
nameLabel.AddThemeColorOverride("font_color", _inputPropertyColor);
|
||||
headerRow.AddChild(nameLabel);
|
||||
|
||||
List<Func<NodeEditorProperty>> resolverFactories =
|
||||
StatescriptResolverRegistry.GetCompatibleFactories(propInfo.ExpectedType);
|
||||
|
||||
if (resolverFactories.Count == 0)
|
||||
{
|
||||
var errorLabel = new Label
|
||||
{
|
||||
Text = "No compatible resolvers.",
|
||||
};
|
||||
|
||||
errorLabel.AddThemeColorOverride("font_color", Colors.Red);
|
||||
headerRow.AddChild(errorLabel);
|
||||
return;
|
||||
}
|
||||
|
||||
var resolverDropdown = new OptionButton
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
CustomMinimumSize = new Vector2(80, 0),
|
||||
};
|
||||
|
||||
foreach (Func<NodeEditorProperty> factory in resolverFactories)
|
||||
{
|
||||
using NodeEditorProperty temp = factory();
|
||||
resolverDropdown.AddItem(temp.DisplayName);
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? binding = FindBinding(StatescriptPropertyDirection.Input, index);
|
||||
var selectedIndex = 0;
|
||||
|
||||
if (binding?.Resolver is not null)
|
||||
{
|
||||
for (var i = 0; i < resolverFactories.Count; i++)
|
||||
{
|
||||
using NodeEditorProperty temp = resolverFactories[i]();
|
||||
|
||||
if (temp.ResolverTypeId == GetResolverTypeId(binding.Resolver))
|
||||
{
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < resolverFactories.Count; i++)
|
||||
{
|
||||
using NodeEditorProperty temp = resolverFactories[i]();
|
||||
|
||||
if (temp.ResolverTypeId == "Variant")
|
||||
{
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolverDropdown.Selected = selectedIndex;
|
||||
headerRow.AddChild(resolverDropdown);
|
||||
|
||||
var editorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
container.AddChild(editorContainer);
|
||||
|
||||
var key = new PropertySlotKey(StatescriptPropertyDirection.Input, index);
|
||||
_inputPropertyContexts[key] = new InputPropertyContext(resolverFactories, propInfo, editorContainer);
|
||||
|
||||
ShowResolverEditorUI(
|
||||
resolverFactories[selectedIndex],
|
||||
binding,
|
||||
propInfo.ExpectedType,
|
||||
editorContainer,
|
||||
StatescriptPropertyDirection.Input,
|
||||
index,
|
||||
propInfo.IsArray);
|
||||
|
||||
var capturedIndex = index;
|
||||
resolverDropdown.ItemSelected += selectedItem => OnInputResolverDropdownItemSelected(selectedItem, capturedIndex);
|
||||
}
|
||||
|
||||
private void OnInputResolverDropdownItemSelected(long x, int index)
|
||||
{
|
||||
var key = new PropertySlotKey(StatescriptPropertyDirection.Input, index);
|
||||
|
||||
if (!_inputPropertyContexts.TryGetValue(key, out InputPropertyContext? ctx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldResolver = FindBinding(StatescriptPropertyDirection.Input, index)?.Resolver?.Duplicate()
|
||||
as StatescriptResolverResource;
|
||||
|
||||
if (_activeResolverEditors.TryGetValue(key, out NodeEditorProperty? old))
|
||||
{
|
||||
_activeResolverEditors.Remove(key);
|
||||
}
|
||||
|
||||
ClearContainer(ctx.EditorContainer);
|
||||
|
||||
if (NodeResource is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShowResolverEditorUI(
|
||||
ctx.ResolverFactories[(int)x],
|
||||
null,
|
||||
ctx.PropInfo.ExpectedType,
|
||||
ctx.EditorContainer,
|
||||
StatescriptPropertyDirection.Input,
|
||||
index,
|
||||
ctx.PropInfo.IsArray);
|
||||
|
||||
if (_activeResolverEditors.TryGetValue(key, out NodeEditorProperty? editor))
|
||||
{
|
||||
SaveResolverEditor(editor, StatescriptPropertyDirection.Input, index);
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? updated = FindBinding(StatescriptPropertyDirection.Input, index);
|
||||
var newResolver = updated?.Resolver?.Duplicate() as StatescriptResolverResource;
|
||||
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Change Resolver Type", customContext: _graph);
|
||||
_undoRedo.AddDoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Input,
|
||||
index,
|
||||
newResolver ?? new StatescriptResolverResource());
|
||||
_undoRedo.AddUndoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Input,
|
||||
index,
|
||||
oldResolver ?? new StatescriptResolverResource());
|
||||
_undoRedo.CommitAction(false);
|
||||
}
|
||||
|
||||
PropertyBindingChanged?.Invoke();
|
||||
ResetSize();
|
||||
}
|
||||
|
||||
private void AddOutputVariableRow(
|
||||
StatescriptNodeDiscovery.OutputVariableInfo varInfo,
|
||||
int index,
|
||||
FoldableContainer sectionContainer)
|
||||
{
|
||||
if (NodeResource is null || _graph is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
sectionContainer.AddChild(hBox);
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Text = varInfo.Label,
|
||||
CustomMinimumSize = new Vector2(60, 0),
|
||||
};
|
||||
|
||||
nameLabel.AddThemeColorOverride("font_color", _outputVariableColor);
|
||||
hBox.AddChild(nameLabel);
|
||||
|
||||
var variableDropdown = new OptionButton { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
variableDropdown.SetMeta("is_variable_dropdown", true);
|
||||
variableDropdown.SetMeta("output_index", index);
|
||||
|
||||
foreach (StatescriptGraphVariable v in _graph.Variables)
|
||||
{
|
||||
variableDropdown.AddItem(v.VariableName);
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? binding = FindBinding(StatescriptPropertyDirection.Output, index);
|
||||
var selectedIndex = 0;
|
||||
|
||||
if (binding?.Resolver is VariableResolverResource varRes
|
||||
&& !string.IsNullOrEmpty(varRes.VariableName))
|
||||
{
|
||||
for (var i = 0; i < _graph.Variables.Count; i++)
|
||||
{
|
||||
if (_graph.Variables[i].VariableName == varRes.VariableName)
|
||||
{
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_graph.Variables.Count > 0)
|
||||
{
|
||||
variableDropdown.Selected = selectedIndex;
|
||||
|
||||
if (binding is null)
|
||||
{
|
||||
var variableName = _graph.Variables[selectedIndex].VariableName;
|
||||
EnsureBinding(StatescriptPropertyDirection.Output, index).Resolver =
|
||||
new VariableResolverResource { VariableName = variableName };
|
||||
}
|
||||
}
|
||||
|
||||
var capturedIndex = index;
|
||||
variableDropdown.ItemSelected += selectedItem => OnOutputVariableDropdownItemSelected(selectedItem, capturedIndex);
|
||||
|
||||
hBox.AddChild(variableDropdown);
|
||||
}
|
||||
|
||||
private void OnOutputVariableDropdownItemSelected(long x, int index)
|
||||
{
|
||||
if (NodeResource is null || _graph is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldResolver = FindBinding(StatescriptPropertyDirection.Output, index)?.Resolver?.Duplicate()
|
||||
as StatescriptResolverResource;
|
||||
|
||||
var variableName = _graph.Variables[(int)x].VariableName;
|
||||
var newResolver = new VariableResolverResource { VariableName = variableName };
|
||||
EnsureBinding(StatescriptPropertyDirection.Output, index).Resolver = newResolver;
|
||||
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Change Output Variable", customContext: _graph);
|
||||
_undoRedo.AddDoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Output,
|
||||
index,
|
||||
(StatescriptResolverResource)newResolver.Duplicate());
|
||||
_undoRedo.AddUndoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Output,
|
||||
index,
|
||||
oldResolver ?? new StatescriptResolverResource());
|
||||
_undoRedo.CommitAction(false);
|
||||
}
|
||||
|
||||
PropertyBindingChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void ShowResolverEditorUI(
|
||||
Func<NodeEditorProperty> factory,
|
||||
StatescriptNodeProperty? existingBinding,
|
||||
Type expectedType,
|
||||
VBoxContainer container,
|
||||
StatescriptPropertyDirection direction,
|
||||
int propertyIndex,
|
||||
bool isArray = false)
|
||||
{
|
||||
if (_graph is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NodeEditorProperty resolverEditor = factory();
|
||||
|
||||
var key = new PropertySlotKey(direction, propertyIndex);
|
||||
|
||||
resolverEditor.Setup(
|
||||
_graph,
|
||||
existingBinding,
|
||||
expectedType,
|
||||
() => SaveResolverEditorWithUndo(resolverEditor, direction, propertyIndex),
|
||||
isArray);
|
||||
|
||||
resolverEditor.LayoutSizeChanged += ResetSize;
|
||||
|
||||
container.AddChild(resolverEditor);
|
||||
|
||||
_activeResolverEditors[key] = resolverEditor;
|
||||
}
|
||||
|
||||
private void SaveResolverEditorWithUndo(
|
||||
NodeEditorProperty resolverEditor,
|
||||
StatescriptPropertyDirection direction,
|
||||
int propertyIndex)
|
||||
{
|
||||
if (NodeResource is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? existing = FindBinding(direction, propertyIndex);
|
||||
var oldResolver = existing?.Resolver?.Duplicate() as StatescriptResolverResource;
|
||||
|
||||
SaveResolverEditor(resolverEditor, direction, propertyIndex);
|
||||
|
||||
StatescriptNodeProperty? updated = FindBinding(direction, propertyIndex);
|
||||
var newResolver = updated?.Resolver?.Duplicate() as StatescriptResolverResource;
|
||||
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Change Node Property", customContext: _graph);
|
||||
_undoRedo.AddDoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)direction,
|
||||
propertyIndex,
|
||||
newResolver ?? new StatescriptResolverResource());
|
||||
_undoRedo.AddUndoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)direction,
|
||||
propertyIndex,
|
||||
oldResolver ?? new StatescriptResolverResource());
|
||||
_undoRedo.CommitAction(false);
|
||||
}
|
||||
|
||||
PropertyBindingChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void SaveResolverEditor(
|
||||
NodeEditorProperty resolverEditor,
|
||||
StatescriptPropertyDirection direction,
|
||||
int propertyIndex)
|
||||
{
|
||||
if (NodeResource is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StatescriptNodeProperty binding = EnsureBinding(direction, propertyIndex);
|
||||
resolverEditor.SaveTo(binding);
|
||||
}
|
||||
|
||||
private sealed class InputPropertyContext(
|
||||
List<Func<NodeEditorProperty>> resolverFactories,
|
||||
StatescriptNodeDiscovery.InputPropertyInfo propInfo,
|
||||
VBoxContainer editorContainer)
|
||||
{
|
||||
public List<Func<NodeEditorProperty>> ResolverFactories { get; } = resolverFactories;
|
||||
|
||||
public StatescriptNodeDiscovery.InputPropertyInfo PropInfo { get; } = propInfo;
|
||||
|
||||
public VBoxContainer EditorContainer { get; } = editorContainer;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user