diff --git a/Assets/Fonts/RobotoMono-Bold.ttf b/Assets/Fonts/RobotoMono-Bold.ttf new file mode 100644 index 0000000..d884128 Binary files /dev/null and b/Assets/Fonts/RobotoMono-Bold.ttf differ diff --git a/Assets/Fonts/RobotoMono-Bold.ttf.import b/Assets/Fonts/RobotoMono-Bold.ttf.import new file mode 100644 index 0000000..94e6ce3 --- /dev/null +++ b/Assets/Fonts/RobotoMono-Bold.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://dpxde42hku7e4" +path="res://.godot/imported/RobotoMono-Bold.ttf-5ef56699eefec9ae3f60998f926ed5e6.fontdata" + +[deps] + +source_file="res://Assets/Fonts/RobotoMono-Bold.ttf" +dest_files=["res://.godot/imported/RobotoMono-Bold.ttf-5ef56699eefec9ae3f60998f926ed5e6.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Assets/Fonts/RobotoMono-BoldItalic.ttf b/Assets/Fonts/RobotoMono-BoldItalic.ttf new file mode 100644 index 0000000..e9c4802 Binary files /dev/null and b/Assets/Fonts/RobotoMono-BoldItalic.ttf differ diff --git a/Assets/Fonts/RobotoMono-BoldItalic.ttf.import b/Assets/Fonts/RobotoMono-BoldItalic.ttf.import new file mode 100644 index 0000000..53f537c --- /dev/null +++ b/Assets/Fonts/RobotoMono-BoldItalic.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://634gg67hl134" +path="res://.godot/imported/RobotoMono-BoldItalic.ttf-2187145e1604e9580f88c665eb740135.fontdata" + +[deps] + +source_file="res://Assets/Fonts/RobotoMono-BoldItalic.ttf" +dest_files=["res://.godot/imported/RobotoMono-BoldItalic.ttf-2187145e1604e9580f88c665eb740135.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Assets/Fonts/RobotoMono-Italic.ttf b/Assets/Fonts/RobotoMono-Italic.ttf new file mode 100644 index 0000000..61e5303 Binary files /dev/null and b/Assets/Fonts/RobotoMono-Italic.ttf differ diff --git a/Assets/Fonts/RobotoMono-Italic.ttf.import b/Assets/Fonts/RobotoMono-Italic.ttf.import new file mode 100644 index 0000000..1728dc7 --- /dev/null +++ b/Assets/Fonts/RobotoMono-Italic.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://dj0652yei7yiw" +path="res://.godot/imported/RobotoMono-Italic.ttf-989ab307e516d9cd034e27188c7b1633.fontdata" + +[deps] + +source_file="res://Assets/Fonts/RobotoMono-Italic.ttf" +dest_files=["res://.godot/imported/RobotoMono-Italic.ttf-989ab307e516d9cd034e27188c7b1633.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Assets/Fonts/RobotoMono-Regular.ttf b/Assets/Fonts/RobotoMono-Regular.ttf new file mode 100644 index 0000000..6df2b25 Binary files /dev/null and b/Assets/Fonts/RobotoMono-Regular.ttf differ diff --git a/Assets/Fonts/RobotoMono-Regular.ttf.import b/Assets/Fonts/RobotoMono-Regular.ttf.import new file mode 100644 index 0000000..b4258ca --- /dev/null +++ b/Assets/Fonts/RobotoMono-Regular.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://br1ivkgagsm6e" +path="res://.godot/imported/RobotoMono-Regular.ttf-d60df72f1dd7eaf3651e080d6852d0c6.fontdata" + +[deps] + +source_file="res://Assets/Fonts/RobotoMono-Regular.ttf" +dest_files=["res://.godot/imported/RobotoMono-Regular.ttf-d60df72f1dd7eaf3651e080d6852d0c6.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Assets/Fonts/iosevka-bold.ttf b/Assets/Fonts/iosevka-bold.ttf new file mode 100644 index 0000000..5e2cef4 Binary files /dev/null and b/Assets/Fonts/iosevka-bold.ttf differ diff --git a/Assets/Fonts/iosevka-bold.ttf.import b/Assets/Fonts/iosevka-bold.ttf.import new file mode 100644 index 0000000..66c1474 --- /dev/null +++ b/Assets/Fonts/iosevka-bold.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://bm6pfkq3ihl5o" +path="res://.godot/imported/iosevka-bold.ttf-97aa97b299fedb67e54814ad33f00008.fontdata" + +[deps] + +source_file="res://Assets/Fonts/iosevka-bold.ttf" +dest_files=["res://.godot/imported/iosevka-bold.ttf-97aa97b299fedb67e54814ad33f00008.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Assets/Fonts/iosevka-medium.ttf b/Assets/Fonts/iosevka-medium.ttf new file mode 100644 index 0000000..7208fc5 Binary files /dev/null and b/Assets/Fonts/iosevka-medium.ttf differ diff --git a/Assets/Fonts/iosevka-medium.ttf.import b/Assets/Fonts/iosevka-medium.ttf.import new file mode 100644 index 0000000..b4b3726 --- /dev/null +++ b/Assets/Fonts/iosevka-medium.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://bc2ex8d8klivt" +path="res://.godot/imported/iosevka-medium.ttf-4b57839c72f99191e193eecde1f3b96c.fontdata" + +[deps] + +source_file="res://Assets/Fonts/iosevka-medium.ttf" +dest_files=["res://.godot/imported/iosevka-medium.ttf-4b57839c72f99191e193eecde1f3b96c.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Assets/Fonts/iosevka.ttf b/Assets/Fonts/iosevka.ttf new file mode 100644 index 0000000..9eb9e2a Binary files /dev/null and b/Assets/Fonts/iosevka.ttf differ diff --git a/Assets/Fonts/iosevka.ttf.import b/Assets/Fonts/iosevka.ttf.import new file mode 100644 index 0000000..6c00f5e --- /dev/null +++ b/Assets/Fonts/iosevka.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://c88b18xteq6b8" +path="res://.godot/imported/iosevka.ttf-42231c60126a7f0f1b381cf93906a3ed.fontdata" + +[deps] + +source_file="res://Assets/Fonts/iosevka.ttf" +dest_files=["res://.godot/imported/iosevka.ttf-42231c60126a7f0f1b381cf93906a3ed.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Debug/CharIterator.cs b/Debug/CharIterator.cs index 889a9a9..e314bba 100644 --- a/Debug/CharIterator.cs +++ b/Debug/CharIterator.cs @@ -2,6 +2,10 @@ namespace SupaLidlGame.Debug; public class CharIterator : Iterator { + public int Line { get; protected set; } = 1; + + public int Column { get; protected set; } = 0; + public CharIterator(string str) : base(str.ToCharArray()) { @@ -18,7 +22,7 @@ public class CharIterator : Iterator if (c == '\n') { Line++; - Column = 0; + Column = 1; } else { diff --git a/Debug/DebugConsole.cs b/Debug/DebugConsole.cs index de02685..580f6b0 100644 --- a/Debug/DebugConsole.cs +++ b/Debug/DebugConsole.cs @@ -18,6 +18,7 @@ public sealed partial class DebugConsole : Control { _entry.PlaceholderText = "Enter Godot expression from " + _context.GetPath(); + _entry.SetContext(_context); GetParent().Title = "Supa Developer Console: " + _context.GetPath(); } @@ -27,6 +28,8 @@ public sealed partial class DebugConsole : Control private Entry _entry; + public Entry Entry => _entry; + private RichTextLabel _output; private Node ctx => Context; @@ -45,17 +48,21 @@ public sealed partial class DebugConsole : Control } catch (InterpreterException ex) { - _output.Text += ex.Message + '\n'; + _output.Text += ex.Message; + if (!ex.Message.StartsWith("Error occurred while")) + { + _output.Text += $" @{ex.Line}:{ex.Column}" + '\n'; + _output.Text += input + '\n'; + _output.Text += new string(' ', ex.Column - 1) + "^\n"; + } + else + { + _output.Text += '\n'; + } } }; } - enum PathType - { - Node, - Property - }; - public IEnumerable SplitPath(NodePath path) { CharIterator iterator = new(path); @@ -67,6 +74,40 @@ public sealed partial class DebugConsole : Control return Variant.From(path).AsNodePath(); } + public object GetNodeOrProperty(NodePath path) + { + var tokens = new List(SplitPath(path)); + Node variant = Context ?? this; + for (int i = 0; i < tokens.Count; i++) + { + var subpath = tokens[i]; + if (i == tokens.Count - 1) + { + if (subpath.Type == NodePathTokenType.Property) + { + return new PropertyPointer(variant, subpath.Path); + } + } + else + { + if (subpath.Type == NodePathTokenType.Node) + { + if (subpath.Path != "") + { + variant = variant.GetNode(subpath.Path); + } + } + else + { + variant = variant.GetIndexed(subpath.Path) + .AsGodotObject() as Node; + } + } + } + + return variant; + } + public Variant From(NodePath path) { Variant variant = Context ?? this; @@ -103,7 +144,6 @@ public sealed partial class DebugConsole : Control for (int i = 0; i < tokens.Count; i++) { var subpath = tokens[i]; - GD.Print(subpath); if (i == tokens.Count - 1) { if (subpath.Type == NodePathTokenType.Property) @@ -154,6 +194,11 @@ public sealed partial class DebugConsole : Control GD.Print(text); } + public void Inspect(NodePath node) + { + + } + public void Execute(string str) { //str = Sanitizer.Sanitize(str); diff --git a/Debug/Entry.cs b/Debug/Entry.cs index f2e43c8..f215a46 100644 --- a/Debug/Entry.cs +++ b/Debug/Entry.cs @@ -1,4 +1,6 @@ using Godot; +using GodotUtilities; +using System.Linq; namespace SupaLidlGame.Debug; @@ -10,6 +12,9 @@ public partial class Entry : CodeEdit public override void _Ready() { GuiInput += OnGuiInput; + + AddStringDelimiter("\'", "\"", true); + AddStringDelimiter("'", "'", true); } /* @@ -28,12 +33,12 @@ public partial class Entry : CodeEdit public void OnGuiInput(InputEvent @event) { - if (@event is InputEventKey key) + if (@event is InputEventKey key && key.Pressed) { if (key.KeyLabel == Key.Enter) { AcceptEvent(); - if (!key.Pressed) + if (key.Pressed) { EmitSignal(SignalName.ConsoleInput, Text); @@ -45,4 +50,17 @@ public partial class Entry : CodeEdit } } } + + public void SetContext(Node context) + { + var properties = context.GetPropertyList(); + var propNames = properties.Select((prop) => prop["name"].ToString()); + foreach (var prop in propNames) + { + AddCodeCompletionOption( + CodeCompletionKind.Member, + prop, prop); + } + UpdateCodeCompletionOptions(true); + } } diff --git a/Debug/Inspector.cs b/Debug/Inspector.cs new file mode 100644 index 0000000..2f1955c --- /dev/null +++ b/Debug/Inspector.cs @@ -0,0 +1,53 @@ +using Godot; +using Godot.Collections; +using System.Linq; + +namespace SupaLidlGame.Debug; + +public partial class Inspector : Node +{ + private Node _target; + + private Array _props; + + private Dictionary _labels; + + public Node Target + { + get => _target; + set + { + _target = value; + UpdateTarget(); + } + } + + public override void _Process(double delta) + { + foreach (var kv in _labels) + { + string propName = kv.Key; + var node = kv.Value; + node.Text = _target.Get(propName).ToString(); + } + } + + public void UpdateTarget() + { + foreach (var kv in _labels) + { + var node = kv.Value; + node.QueueFree(); + } + _labels.Clear(); + LoadAllProperties(); + } + + public void LoadAllProperties() + { + _props = _target.GetPropertyList(); + foreach (var kv in _props) + { + } + } +} diff --git a/Debug/Iterator.cs b/Debug/Iterator.cs index 0e258f3..b060303 100644 --- a/Debug/Iterator.cs +++ b/Debug/Iterator.cs @@ -4,10 +4,6 @@ namespace SupaLidlGame.Debug; public class Iterator where T : struct { - public int Line { get; protected set; } = 1; - - public int Column { get; protected set; } = 0; - public int Index { get; protected set; } = -1; protected List _elements; diff --git a/Debug/PropertyPointer.cs b/Debug/PropertyPointer.cs new file mode 100644 index 0000000..6fd0079 --- /dev/null +++ b/Debug/PropertyPointer.cs @@ -0,0 +1,20 @@ +using Godot; + +namespace SupaLidlGame.Debug; + +public struct PropertyPointer +{ + public Node Node { get; set; } + + public string Property { get; set; } + + public PropertyPointer(Node node, string property) + { + Node = node; + Property = property; + } + + public Variant Dereferenced => Node.Get(Property); + + public void Set(Variant value) => Node.Set(Property, value); +} diff --git a/Debug/Transpiler/LiteralExpression.cs b/Debug/Transpiler/LiteralExpression.cs index b9d9970..5b62d73 100644 --- a/Debug/Transpiler/LiteralExpression.cs +++ b/Debug/Transpiler/LiteralExpression.cs @@ -12,23 +12,31 @@ public class LiteralExpression : Expression Literal = literal; } + public string EscapedLiteral() + { + return Literal.Value.Replace("\"", "\\\"") + .Replace("'", "\\'") + .Replace("\n", "\\n") + .Replace("\t", "\\t"); + } + public override string Transpile() { + var val = EscapedLiteral(); if (Literal.Type == TokenType.NodePath) { - var val = Regex.Escape(Literal.Value); return $"from.call(\"{val}\")"; } else if (Literal.Type == TokenType.String) { - return $"\"{Literal.Value}\""; + return $"\"{val}\""; } return Literal.Value; } public string TranspileNodePath() { - var val = Regex.Escape(Literal.Value); + var val = EscapedLiteral(); return $"to_node_path.call(\"{val}\")"; } } diff --git a/UI/Debug/DebugUI.cs b/UI/Debug/DebugUI.cs new file mode 100644 index 0000000..3abee70 --- /dev/null +++ b/UI/Debug/DebugUI.cs @@ -0,0 +1,68 @@ +using SupaLidlGame.Debug; +using Godot; + +namespace SupaLidlGame.UI.Debug; + +public partial class DebugUI : CanvasLayer +{ + private bool _areWindowsVisible = false; + private Window _lastFocusedWindow = null; + + public bool AreWindowsVisible + { + get => _areWindowsVisible; + set + { + _areWindowsVisible = value; + foreach (var node in GetChildren()) + { + if (node is Window w) + { + w.Visible = value; + } + else if (node is Control c) + { + c.Visible = value; + } + } + } + } + + //public override void _Ready() + //{ + // AreWindowsVisible = false; + + // ChildEnteredTree += (Node child) => + // { + // if (child is Window w) + // { + // void setFocus() + // { + // _lastFocusedWindow = w; + // } + // w.FocusEntered += setFocus; + // } + // }; + + // _lastFocusedWindow = GetNode("%ConsoleWindow"); + //} + + public override void _UnhandledInput(InputEvent @event) + { + if (@event is InputEventKey key) + { + if (key.Keycode == Key.Quoteleft) + { + if (key.Pressed) + { + Toggle(); + } + } + } + } + + public void Toggle() + { + AreWindowsVisible = !AreWindowsVisible; + } +} diff --git a/UI/Debug/DebugUI.tscn b/UI/Debug/DebugUI.tscn index 81c52e9..c1b4019 100644 --- a/UI/Debug/DebugUI.tscn +++ b/UI/Debug/DebugUI.tscn @@ -1,34 +1,33 @@ -[gd_scene load_steps=3 format=3 uid="uid://be8bc4eivsg4s"] +[gd_scene load_steps=4 format=3 uid="uid://be8bc4eivsg4s"] [ext_resource type="Script" path="res://Debug/DebugConsole.cs" id="1_3fw5a"] +[ext_resource type="Script" path="res://UI/Debug/DebugUI.cs" id="1_b7eq2"] [ext_resource type="Script" path="res://Debug/Entry.cs" id="2_kdlsh"] -[node name="DebugUI" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 +[node name="DebugUI" type="CanvasLayer"] +process_mode = 3 +script = ExtResource("1_b7eq2") -[node name="Window" type="Window" parent="."] -disable_3d = true -gui_embed_subwindows = true +[node name="ConsoleWindow" type="Window" parent="."] +unique_name_in_owner = true +handle_input_locally = false title = "Supa Developer Console" position = Vector2i(32, 32) size = Vector2i(1280, 720) +visible = false always_on_top = true -[node name="DebugConsole" type="Control" parent="Window"] -layout_mode = 3 +[node name="DebugConsole" type="ColorRect" parent="ConsoleWindow"] +unique_name_in_owner = true anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +color = Color(0.117647, 0.117647, 0.180392, 1) script = ExtResource("1_3fw5a") -[node name="VBoxContainer" type="VBoxContainer" parent="Window/DebugConsole"] +[node name="VBoxContainer" type="VBoxContainer" parent="ConsoleWindow/DebugConsole"] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -36,23 +35,21 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="Output" type="RichTextLabel" parent="Window/DebugConsole/VBoxContainer"] +[node name="Output" type="RichTextLabel" parent="ConsoleWindow/DebugConsole/VBoxContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -theme_override_font_sizes/normal_font_size = 24 -theme_override_font_sizes/bold_font_size = 24 +theme_override_font_sizes/normal_font_size = 16 +theme_override_font_sizes/bold_font_size = 16 bbcode_enabled = true -text = "[b]/root/World:[/b] \\echo :CurrentPlayer:Health -100 -" scroll_following = true -[node name="Entry" type="CodeEdit" parent="Window/DebugConsole/VBoxContainer"] +[node name="Entry" type="CodeEdit" parent="ConsoleWindow/DebugConsole/VBoxContainer"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 48) +custom_minimum_size = Vector2(0, 36) layout_mode = 2 -theme_override_font_sizes/font_size = 24 +theme_override_colors/background_color = Color(0.0941176, 0.0941176, 0.145098, 1) +theme_override_font_sizes/font_size = 16 placeholder_text = "Enter a GDScript expression or \\command..." draw_control_chars = true code_completion_enabled = true diff --git a/UI/Themes/debug.tres b/UI/Themes/debug.tres new file mode 100644 index 0000000..89d9dcd --- /dev/null +++ b/UI/Themes/debug.tres @@ -0,0 +1,16 @@ +[gd_resource type="Theme" load_steps=6 format=3 uid="uid://f403wogmdo7s"] + +[ext_resource type="FontFile" uid="uid://dpxde42hku7e4" path="res://Assets/Fonts/RobotoMono-Bold.ttf" id="1_luw4j"] +[ext_resource type="FontFile" uid="uid://dj0652yei7yiw" path="res://Assets/Fonts/RobotoMono-Italic.ttf" id="2_eyrn1"] +[ext_resource type="FontFile" uid="uid://634gg67hl134" path="res://Assets/Fonts/RobotoMono-BoldItalic.ttf" id="3_cod6d"] +[ext_resource type="FontFile" uid="uid://br1ivkgagsm6e" path="res://Assets/Fonts/RobotoMono-Regular.ttf" id="4_5u55u"] + +[sub_resource type="FontVariation" id="FontVariation_x2wu6"] +base_font = ExtResource("4_5u55u") + +[resource] +default_font = SubResource("FontVariation_x2wu6") +/fonts/Bold = ExtResource("1_luw4j") +/fonts/Italic = ExtResource("2_eyrn1") +/fonts/Oblique = ExtResource("3_cod6d") +/fonts/Regular = ExtResource("4_5u55u") diff --git a/project.godot b/project.godot index 0fd601d..115bf3b 100644 --- a/project.godot +++ b/project.godot @@ -23,7 +23,6 @@ EventBus="*res://Events/EventBus.cs" BaseUI="*res://UI/Base.tscn" World="*res://Scenes/Level.tscn" AudioManager="*res://Audio/AudioManager.cs" -Panku="*res://addons/panku_console/console.tscn" DebugUi="*res://UI/Debug/DebugUI.tscn" [dialogue_manager]