added forge addon
This commit is contained in:
34
addons/forge/editor/attributes/AttributeEditorPlugin.cs
Normal file
34
addons/forge/editor/attributes/AttributeEditorPlugin.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
|
||||
|
||||
[Tool]
|
||||
public partial class AttributeEditorPlugin : EditorInspectorPlugin
|
||||
{
|
||||
public override bool _CanHandle(GodotObject @object)
|
||||
{
|
||||
return @object is Resources.ForgeModifier || @object is Resources.ForgeCue;
|
||||
}
|
||||
|
||||
public override bool _ParseProperty(
|
||||
GodotObject @object,
|
||||
Variant.Type type,
|
||||
string name,
|
||||
PropertyHint hintType,
|
||||
string hintString,
|
||||
PropertyUsageFlags usageFlags,
|
||||
bool wide)
|
||||
{
|
||||
if (name == "Attribute" || name == "CapturedAttribute" || name == "MagnitudeAttribute")
|
||||
{
|
||||
AddPropertyEditor(name, new AttributeEditorProperty());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://bl2w0vp6b5p8k
|
||||
101
addons/forge/editor/attributes/AttributeEditorProperty.cs
Normal file
101
addons/forge/editor/attributes/AttributeEditorProperty.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
|
||||
|
||||
[Tool]
|
||||
public partial class AttributeEditorProperty : EditorProperty
|
||||
{
|
||||
private const int ButtonSize = 26;
|
||||
private const int PopupSize = 300;
|
||||
|
||||
private Label _label = null!;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Texture2D dropdownIcon = EditorInterface.Singleton
|
||||
.GetEditorTheme()
|
||||
.GetIcon("GuiDropdown", "EditorIcons");
|
||||
|
||||
var hbox = new HBoxContainer();
|
||||
_label = new Label { Text = "None", SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
var button = new Button { Icon = dropdownIcon, CustomMinimumSize = new Vector2(ButtonSize, 0) };
|
||||
|
||||
hbox.AddChild(_label);
|
||||
hbox.AddChild(button);
|
||||
AddChild(hbox);
|
||||
|
||||
var popup = new Popup { Size = new Vector2I(PopupSize, PopupSize) };
|
||||
var tree = new Tree
|
||||
{
|
||||
HideRoot = true,
|
||||
AnchorRight = 1,
|
||||
AnchorBottom = 1,
|
||||
};
|
||||
popup.AddChild(tree);
|
||||
|
||||
var bg = new StyleBoxFlat
|
||||
{
|
||||
BgColor = EditorInterface.Singleton
|
||||
.GetEditorTheme()
|
||||
.GetColor("dark_color_2", "Editor"),
|
||||
};
|
||||
tree.AddThemeStyleboxOverride("panel", bg);
|
||||
|
||||
AddChild(popup);
|
||||
|
||||
BuildAttributeTree(tree);
|
||||
|
||||
button.Pressed += () =>
|
||||
{
|
||||
Window win = GetWindow();
|
||||
popup.Position = (Vector2I)button.GlobalPosition
|
||||
+ win.Position
|
||||
- new Vector2I(PopupSize - ButtonSize, -30);
|
||||
popup.Popup();
|
||||
};
|
||||
|
||||
tree.ItemActivated += () =>
|
||||
{
|
||||
TreeItem item = tree.GetSelected();
|
||||
if (item?.HasMeta("attribute_path") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var fullPath = item.GetMeta("attribute_path").AsString();
|
||||
_label.Text = fullPath;
|
||||
EmitChanged(GetEditedProperty(), fullPath);
|
||||
popup.Hide();
|
||||
};
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
var value = GetEditedObject().Get(GetEditedProperty()).AsString();
|
||||
_label.Text = string.IsNullOrEmpty(value) ? "None" : value;
|
||||
}
|
||||
|
||||
private static void BuildAttributeTree(Tree tree)
|
||||
{
|
||||
TreeItem root = tree.CreateItem();
|
||||
|
||||
foreach (var attributeSet in EditorUtils.GetAttributeSetOptions())
|
||||
{
|
||||
TreeItem setItem = tree.CreateItem(root);
|
||||
setItem.SetText(0, attributeSet);
|
||||
setItem.Collapsed = true;
|
||||
|
||||
foreach (var attribute in EditorUtils.GetAttributeOptions(attributeSet))
|
||||
{
|
||||
TreeItem attributeItem = tree.CreateItem(setItem);
|
||||
var attributePath = $"{attributeSet}.{attribute}";
|
||||
attributeItem.SetText(0, attribute);
|
||||
attributeItem.SetMeta("attribute_path", attributePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://dvjqj637kfav
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Gamesmiths.Forge.Attributes;
|
||||
using Gamesmiths.Forge.Godot.Nodes;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
|
||||
|
||||
[Tool]
|
||||
public partial class AttributeSetClassEditorProperty : EditorProperty
|
||||
{
|
||||
private OptionButton _optionButton = null!;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_optionButton = new OptionButton();
|
||||
AddChild(_optionButton);
|
||||
|
||||
_optionButton.AddItem("Select AttributeSet Class");
|
||||
foreach (var option in EditorUtils.GetAttributeSetOptions())
|
||||
{
|
||||
_optionButton.AddItem(option);
|
||||
}
|
||||
|
||||
_optionButton.ItemSelected += x =>
|
||||
{
|
||||
var className = _optionButton.GetItemText((int)x);
|
||||
EmitChanged(GetEditedProperty(), className);
|
||||
|
||||
GodotObject obj = GetEditedObject();
|
||||
if (obj is not null)
|
||||
{
|
||||
var dict = new Dictionary<string, AttributeValues>();
|
||||
|
||||
var assembly = Assembly.GetAssembly(typeof(ForgeAttributeSet));
|
||||
Type? targetType = System.Array.Find(assembly?.GetTypes() ?? [], x => x.Name == className);
|
||||
if (targetType is not null)
|
||||
{
|
||||
System.Collections.Generic.IEnumerable<PropertyInfo> attrProps = targetType
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(x => x.PropertyType == typeof(EntityAttribute));
|
||||
|
||||
foreach (PropertyInfo? pi in attrProps)
|
||||
{
|
||||
dict[pi.Name] = new AttributeValues(0, 0, int.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
EmitChanged("InitialAttributeValues", dict);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
GodotObject obj = GetEditedObject();
|
||||
StringName property = GetEditedProperty();
|
||||
var val = obj.Get(property).AsString();
|
||||
for (var i = 0; i < _optionButton.GetItemCount(); i++)
|
||||
{
|
||||
if (_optionButton.GetItemText(i) == val)
|
||||
{
|
||||
_optionButton.Selected = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://bumuxlivyt66b
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Nodes;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
|
||||
|
||||
[Tool]
|
||||
public partial class AttributeSetInspectorPlugin : EditorInspectorPlugin
|
||||
{
|
||||
private PackedScene? _inspectorScene;
|
||||
|
||||
public override bool _CanHandle(GodotObject @object)
|
||||
{
|
||||
return @object is ForgeAttributeSet;
|
||||
}
|
||||
|
||||
public override bool _ParseProperty(
|
||||
GodotObject @object,
|
||||
Variant.Type type,
|
||||
string name,
|
||||
PropertyHint hintType,
|
||||
string hintString,
|
||||
PropertyUsageFlags usageFlags,
|
||||
bool wide)
|
||||
{
|
||||
if (name == "AttributeSetClass")
|
||||
{
|
||||
AddPropertyEditor(name, new AttributeSetClassEditorProperty());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "InitialAttributeValues")
|
||||
{
|
||||
AddPropertyEditor(name, new AttributeSetValuesEditorProperty());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://t3gpjlcyqor
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Gamesmiths.Forge.Attributes;
|
||||
using Gamesmiths.Forge.Godot.Nodes;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
|
||||
|
||||
[Tool]
|
||||
public partial class AttributeSetValuesEditorProperty : EditorProperty
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
var attributesRoot = new VBoxContainer { Name = "AttributesRoot" };
|
||||
AddChild(attributesRoot);
|
||||
SetBottomEditor(attributesRoot);
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
VBoxContainer attributesRoot = GetNodeOrNull<VBoxContainer>("AttributesRoot");
|
||||
|
||||
if (attributesRoot is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FreeAllChildren(attributesRoot);
|
||||
|
||||
if (GetEditedObject() is not ForgeAttributeSet obj
|
||||
|| string.IsNullOrEmpty(obj.AttributeSetClass)
|
||||
|| obj.InitialAttributeValues is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var className = obj.AttributeSetClass;
|
||||
var assembly = Assembly.GetAssembly(typeof(ForgeAttributeSet));
|
||||
Type? targetType = System.Array.Find(assembly?.GetTypes() ?? [], x => x.Name == className);
|
||||
|
||||
if (targetType is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
System.Collections.Generic.IEnumerable<PropertyInfo> attributeProperties = targetType
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(x => x.PropertyType == typeof(EntityAttribute));
|
||||
|
||||
foreach (var attributeName in attributeProperties.Select(x => x.Name))
|
||||
{
|
||||
var groupVBox = new VBoxContainer();
|
||||
|
||||
groupVBox.AddChild(AttributeHeader(attributeName));
|
||||
|
||||
AttributeValues value = obj.InitialAttributeValues.TryGetValue(attributeName, out AttributeValues? v)
|
||||
? v
|
||||
: new AttributeValues(0, 0, int.MaxValue);
|
||||
|
||||
SpinBox spinDefault = CreateSpinBox(value.Min, value.Max, value.Default);
|
||||
SpinBox spinMin = CreateSpinBox(int.MinValue, value.Max, value.Min);
|
||||
SpinBox spinMax = CreateSpinBox(value.Min, int.MaxValue, value.Max);
|
||||
|
||||
groupVBox.AddChild(AttributeFieldRow("Default", spinDefault));
|
||||
groupVBox.AddChild(AttributeFieldRow("Min", spinMin));
|
||||
groupVBox.AddChild(AttributeFieldRow("Max", spinMax));
|
||||
|
||||
spinDefault.ValueChanged += x =>
|
||||
{
|
||||
UpdateAndEmit(obj, attributeName, (int)x, (int)spinMin.Value, (int)spinMax.Value);
|
||||
};
|
||||
|
||||
spinMin.ValueChanged += x =>
|
||||
{
|
||||
spinDefault.MinValue = x;
|
||||
spinMax.MinValue = x;
|
||||
UpdateAndEmit(obj, attributeName, (int)spinDefault.Value, (int)x, (int)spinMax.Value);
|
||||
};
|
||||
|
||||
spinMax.ValueChanged += x =>
|
||||
{
|
||||
spinDefault.MaxValue = x;
|
||||
spinMin.MaxValue = x;
|
||||
UpdateAndEmit(obj, attributeName, (int)spinDefault.Value, (int)spinMin.Value, (int)x);
|
||||
};
|
||||
|
||||
attributesRoot.AddChild(groupVBox);
|
||||
}
|
||||
}
|
||||
|
||||
private static PanelContainer AttributeHeader(string text)
|
||||
{
|
||||
var headerPanel = new PanelContainer
|
||||
{
|
||||
CustomMinimumSize = new Vector2(0, 28),
|
||||
};
|
||||
|
||||
var style = new StyleBoxFlat
|
||||
{
|
||||
BgColor = new Color(0.16f, 0.17f, 0.20f),
|
||||
};
|
||||
|
||||
headerPanel.AddThemeStyleboxOverride("panel", style);
|
||||
|
||||
var label = new Label
|
||||
{
|
||||
Text = text,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
SizeFlagsHorizontal = (SizeFlags)(int)SizeFlags.ExpandFill,
|
||||
CustomMinimumSize = new Vector2(0, 22),
|
||||
AutowrapMode = TextServer.AutowrapMode.Off,
|
||||
};
|
||||
|
||||
headerPanel.AddChild(label);
|
||||
return headerPanel;
|
||||
}
|
||||
|
||||
private static HBoxContainer AttributeFieldRow(string label, SpinBox spinBox)
|
||||
{
|
||||
var hbox = new HBoxContainer();
|
||||
|
||||
hbox.AddChild(new Label
|
||||
{
|
||||
Text = label,
|
||||
CustomMinimumSize = new Vector2(80, 0),
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
});
|
||||
|
||||
hbox.AddChild(spinBox);
|
||||
return hbox;
|
||||
}
|
||||
|
||||
private static SpinBox CreateSpinBox(int min, int max, int value)
|
||||
{
|
||||
return new SpinBox
|
||||
{
|
||||
MinValue = min,
|
||||
MaxValue = max,
|
||||
Value = value,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
}
|
||||
|
||||
private static void FreeAllChildren(Node node)
|
||||
{
|
||||
for (var i = node.GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
node.GetChild(i).QueueFree();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAndEmit(ForgeAttributeSet obj, string name, int def, int min, int max)
|
||||
{
|
||||
Debug.Assert(obj.InitialAttributeValues is not null, "InitialAttributeValues should not be null here.");
|
||||
|
||||
var dict = new Dictionary<string, AttributeValues>(obj.InitialAttributeValues)
|
||||
{
|
||||
[name] = new AttributeValues(def, min, max),
|
||||
};
|
||||
|
||||
EmitChanged(nameof(ForgeAttributeSet.InitialAttributeValues), dict);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://cdj20gbpxkda1
|
||||
29
addons/forge/editor/attributes/AttributeValues.cs
Normal file
29
addons/forge/editor/attributes/AttributeValues.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
|
||||
|
||||
[Tool]
|
||||
public partial class AttributeValues : Resource
|
||||
{
|
||||
[Export]
|
||||
public int Default { get; set; }
|
||||
|
||||
[Export]
|
||||
public int Min { get; set; }
|
||||
|
||||
[Export]
|
||||
public int Max { get; set; }
|
||||
|
||||
public AttributeValues()
|
||||
{
|
||||
}
|
||||
|
||||
public AttributeValues(int @default, int min, int max)
|
||||
{
|
||||
Default = @default;
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
}
|
||||
1
addons/forge/editor/attributes/AttributeValues.cs.uid
Normal file
1
addons/forge/editor/attributes/AttributeValues.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ccovd5i0wr3kk
|
||||
Reference in New Issue
Block a user