diff --git a/Art/BlackScreen.png b/Art/BlackScreen.png new file mode 100644 index 0000000..d3bfd9e Binary files /dev/null and b/Art/BlackScreen.png differ diff --git a/icon.svg.import b/Art/BlackScreen.png.import similarity index 62% rename from icon.svg.import rename to Art/BlackScreen.png.import index ce44a6c..212d2f6 100644 --- a/icon.svg.import +++ b/Art/BlackScreen.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bf3x4m5567v75" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +uid="uid://c3je4gir1qlot" +path="res://.godot/imported/BlackScreen.png-451daa3be1d42cdae0c158b32196a468.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] +source_file="res://Art/BlackScreen.png" +dest_files=["res://.godot/imported/BlackScreen.png-451daa3be1d42cdae0c158b32196a468.ctex"] [params] @@ -32,6 +32,3 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/Art/Office.png b/Art/Office.png new file mode 100644 index 0000000..fa7202b Binary files /dev/null and b/Art/Office.png differ diff --git a/addons/dialogue_manager/assets/update.svg.import b/Art/Office.png.import similarity index 60% rename from addons/dialogue_manager/assets/update.svg.import rename to Art/Office.png.import index 2d8171a..3cf39c2 100644 --- a/addons/dialogue_manager/assets/update.svg.import +++ b/Art/Office.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3baj6rygkb3f" -path="res://.godot/imported/update.svg-f1628866ed4eb2e13e3b81f75443687e.ctex" +uid="uid://cppnad2sauulq" +path="res://.godot/imported/Office.png-e6c790823ef35154f82a3192d68815e5.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://addons/dialogue_manager/assets/update.svg" -dest_files=["res://.godot/imported/update.svg-f1628866ed4eb2e13e3b81f75443687e.ctex"] +source_file="res://Art/Office.png" +dest_files=["res://.godot/imported/Office.png-e6c790823ef35154f82a3192d68815e5.ctex"] [params] @@ -32,6 +32,3 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/Art/PatientRoom.jpg b/Art/PatientRoom.jpg new file mode 100644 index 0000000..2d0cb3a Binary files /dev/null and b/Art/PatientRoom.jpg differ diff --git a/addons/dialogue_manager/assets/icon.svg.import b/Art/PatientRoom.jpg.import similarity index 59% rename from addons/dialogue_manager/assets/icon.svg.import rename to Art/PatientRoom.jpg.import index 3b6fd5e..2b85adb 100644 --- a/addons/dialogue_manager/assets/icon.svg.import +++ b/Art/PatientRoom.jpg.import @@ -2,17 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3lr2uas6ax8v" -path="res://.godot/imported/icon.svg-17eb5d3e2a3cfbe59852220758c5b7bd.ctex" +uid="uid://c7vyp0s2ci7qf" +path="res://.godot/imported/PatientRoom.jpg-e349da84ef9248e0711fe44a9c6bbc5d.ctex" metadata={ -"has_editor_variant": true, "vram_texture": false } [deps] -source_file="res://addons/dialogue_manager/assets/icon.svg" -dest_files=["res://.godot/imported/icon.svg-17eb5d3e2a3cfbe59852220758c5b7bd.ctex"] +source_file="res://Art/PatientRoom.jpg" +dest_files=["res://.godot/imported/PatientRoom.jpg-e349da84ef9248e0711fe44a9c6bbc5d.ctex"] [params] @@ -33,6 +32,3 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=true -editor/convert_colors_with_editor_theme=true diff --git a/Art/PatientRoom2.jpg b/Art/PatientRoom2.jpg new file mode 100644 index 0000000..1feba1a Binary files /dev/null and b/Art/PatientRoom2.jpg differ diff --git a/addons/dialogue_manager/assets/responses_menu.svg.import b/Art/PatientRoom2.jpg.import similarity index 57% rename from addons/dialogue_manager/assets/responses_menu.svg.import rename to Art/PatientRoom2.jpg.import index 83355fc..e2e32fd 100644 --- a/addons/dialogue_manager/assets/responses_menu.svg.import +++ b/Art/PatientRoom2.jpg.import @@ -2,17 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://drjfciwitjm83" -path="res://.godot/imported/responses_menu.svg-87cf63ca685d53616205049572f4eb8f.ctex" +uid="uid://c1bq2kroefeoy" +path="res://.godot/imported/PatientRoom2.jpg-368b72d1e43ab22dbc19aedf5f78c1a6.ctex" metadata={ -"has_editor_variant": true, "vram_texture": false } [deps] -source_file="res://addons/dialogue_manager/assets/responses_menu.svg" -dest_files=["res://.godot/imported/responses_menu.svg-87cf63ca685d53616205049572f4eb8f.ctex"] +source_file="res://Art/PatientRoom2.jpg" +dest_files=["res://.godot/imported/PatientRoom2.jpg-368b72d1e43ab22dbc19aedf5f78c1a6.ctex"] [params] @@ -33,6 +32,3 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=true -editor/convert_colors_with_editor_theme=true diff --git a/Bedroom.tscn b/Bedroom.tscn new file mode 100644 index 0000000..59c83cf --- /dev/null +++ b/Bedroom.tscn @@ -0,0 +1,100 @@ +[gd_scene load_steps=4 format=3 uid="uid://dcjhx2g4d056v"] + +[ext_resource type="Script" path="res://office.gd" id="1_dtfv6"] +[ext_resource type="Texture2D" uid="uid://c3je4gir1qlot" path="res://Art/BlackScreen.png" id="2_trxfw"] +[ext_resource type="Texture2D" uid="uid://cppnad2sauulq" path="res://Art/Office.png" id="3_y3nfl"] + +[node name="Corridor" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_dtfv6") + +[node name="Background" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="UpperBorder" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.25 +texture = ExtResource("2_trxfw") +expand_mode = 1 + +[node name="Background" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 1.5 +texture = ExtResource("3_y3nfl") +expand_mode = 1 + +[node name="LowerBorder" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.25 +texture = ExtResource("2_trxfw") +expand_mode = 1 + +[node name="Choices" type="GridContainer" parent="."] +layout_mode = 0 +offset_left = 659.0 +offset_top = 751.0 +offset_right = 1288.0 +offset_bottom = 915.0 +columns = 3 + +[node name="PatientRoom1" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Patient Room 1" + +[node name="PatientRoom2" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Patient Room 2 +" + +[node name="PatientRoom3" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Patient Room 3 +" + +[node name="IsolationRoom" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Isolation Room +" + +[node name="MedicsOffice" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Medics Office +" + +[node name="PsychiatristDesk" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Psychiatrist Desk" + +[connection signal="pressed" from="Choices/PatientRoom1" to="." method="_on_patient_room_1_pressed"] +[connection signal="pressed" from="Choices/PatientRoom2" to="." method="_on_patient_room_2_pressed"] diff --git a/MainHub.tscn b/MainHub.tscn new file mode 100644 index 0000000..1727f7a --- /dev/null +++ b/MainHub.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=5 format=3 uid="uid://buoev4bra11fe"] + +[ext_resource type="Script" path="res://main_hub.gd" id="1_rvfpd"] +[ext_resource type="PackedScene" uid="uid://cahspuhi43bx7" path="res://TitleScreen.tscn" id="2_3wmv1"] +[ext_resource type="PackedScene" uid="uid://chytk2cctjk3x" path="res://Office.tscn" id="3_whctb"] +[ext_resource type="PackedScene" uid="uid://dcjhx2g4d056v" path="res://Bedroom.tscn" id="4_4fm57"] + +[node name="MainHub" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_rvfpd") +title_screen = ExtResource("2_3wmv1") +office = ExtResource("3_whctb") +bedroom = ExtResource("4_4fm57") diff --git a/Office.tscn b/Office.tscn new file mode 100644 index 0000000..e71593b --- /dev/null +++ b/Office.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=4 format=3 uid="uid://chytk2cctjk3x"] + +[ext_resource type="Script" path="res://office.gd" id="1_1s7k3"] +[ext_resource type="Texture2D" uid="uid://c3je4gir1qlot" path="res://Art/BlackScreen.png" id="1_r2ji1"] +[ext_resource type="Texture2D" uid="uid://cppnad2sauulq" path="res://Art/Office.png" id="3_vcj7d"] + +[node name="Corridor" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_1s7k3") + +[node name="Background" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="UpperBorder" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.1 +texture = ExtResource("1_r2ji1") +expand_mode = 1 + +[node name="Background" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 1.5 +texture = ExtResource("3_vcj7d") +expand_mode = 1 + +[node name="LowerBorder" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.1 +texture = ExtResource("1_r2ji1") +expand_mode = 1 + +[node name="Choices" type="GridContainer" parent="."] +layout_mode = 0 +offset_left = 13.0 +offset_top = 791.0 +offset_right = 638.0 +offset_bottom = 1004.0 +columns = 2 + +[node name="MedicsOffice" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Check Patient's Profile +" + +[node name="PsychiatristDesk" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Call Security" + +[node name="PsychiatristDesk2" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Send to Containment" + +[node name="PsychiatristDesk3" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Send to Psych Ward" diff --git a/TitleScreen.tscn b/TitleScreen.tscn new file mode 100644 index 0000000..6ba577b --- /dev/null +++ b/TitleScreen.tscn @@ -0,0 +1,72 @@ +[gd_scene load_steps=4 format=3 uid="uid://cahspuhi43bx7"] + +[ext_resource type="Texture2D" uid="uid://c3je4gir1qlot" path="res://Art/BlackScreen.png" id="1_3k8ub"] +[ext_resource type="Script" path="res://title_screen.gd" id="1_ejdst"] + +[sub_resource type="FontVariation" id="FontVariation_o5qo5"] + +[node name="TitleScreen" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ejdst") + +[node name="Background" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("1_3k8ub") + +[node name="MenuContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +offset_left = 156.0 +offset_top = 284.0 +offset_right = 432.0 +offset_bottom = 612.0 +metadata/_edit_group_ = true + +[node name="NewGame" type="Button" parent="MenuContainer"] +layout_mode = 2 +size_flags_vertical = 3 +text = "NEW GAME" + +[node name="Save" type="Button" parent="MenuContainer"] +layout_mode = 2 +size_flags_vertical = 3 +text = "SAVE" + +[node name="Load" type="Button" parent="MenuContainer"] +layout_mode = 2 +size_flags_vertical = 3 +text = "LOAD" + +[node name="Settings" type="Button" parent="MenuContainer"] +layout_mode = 2 +size_flags_vertical = 3 +text = "SETTINGS" + +[node name="QuitGame" type="Button" parent="MenuContainer"] +layout_mode = 2 +size_flags_vertical = 3 +text = "QUIT GAME" + +[node name="GameTitle" type="Label" parent="."] +layout_mode = 0 +offset_left = 68.0 +offset_top = 85.0 +offset_right = 501.0 +offset_bottom = 241.0 +theme_type_variation = &"HeaderLarge" +theme_override_fonts/font = SubResource("FontVariation_o5qo5") +theme_override_font_sizes/font_size = 71 +text = "I am not sick" +horizontal_alignment = 1 +vertical_alignment = 1 + +[connection signal="pressed" from="MenuContainer/NewGame" to="." method="_on_new_game_pressed"] diff --git a/addons/dialogue_manager/DialogueManager.cs b/addons/dialogue_manager/DialogueManager.cs deleted file mode 100644 index fb0e44c..0000000 --- a/addons/dialogue_manager/DialogueManager.cs +++ /dev/null @@ -1,440 +0,0 @@ -using Godot; -using Godot.Collections; -using System; -using System.Reflection; -using System.Threading.Tasks; - -#nullable enable - -namespace DialogueManagerRuntime -{ - public enum TranslationSource - { - None, - Guess, - CSV, - PO - } - - public partial class DialogueManager : RefCounted - { - public delegate void DialogueStartedEventHandler(Resource dialogueResource); - public delegate void PassedTitleEventHandler(string title); - public delegate void GotDialogueEventHandler(DialogueLine dialogueLine); - public delegate void MutatedEventHandler(Dictionary mutation); - public delegate void DialogueEndedEventHandler(Resource dialogueResource); - - public static DialogueStartedEventHandler? DialogueStarted; - public static PassedTitleEventHandler? PassedTitle; - public static GotDialogueEventHandler? GotDialogue; - public static MutatedEventHandler? Mutated; - public static DialogueEndedEventHandler? DialogueEnded; - - [Signal] public delegate void ResolvedEventHandler(Variant value); - - private static GodotObject? instance; - public static GodotObject Instance - { - get - { - if (instance == null) - { - instance = Engine.GetSingleton("DialogueManager"); - } - return instance; - } - } - - - public static Godot.Collections.Array GameStates - { - get => (Godot.Collections.Array)Instance.Get("game_states"); - set => Instance.Set("game_states", value); - } - - - public static bool IncludeSingletons - { - get => (bool)Instance.Get("include_singletons"); - set => Instance.Set("include_singletons", value); - } - - - public static bool IncludeClasses - { - get => (bool)Instance.Get("include_classes"); - set => Instance.Set("include_classes", value); - } - - - public static TranslationSource TranslationSource - { - get => (TranslationSource)(int)Instance.Get("translation_source"); - set => Instance.Set("translation_source", (int)value); - } - - - public static Func GetCurrentScene - { - set => Instance.Set("get_current_scene", Callable.From(value)); - } - - - public static void Prepare(GodotObject instance) - { - instance.Connect("dialogue_started", Callable.From((Resource dialogueResource) => DialogueStarted?.Invoke(dialogueResource))); - instance.Connect("passed_title", Callable.From((string title) => PassedTitle?.Invoke(title))); - instance.Connect("got_dialogue", Callable.From((RefCounted line) => GotDialogue?.Invoke(new DialogueLine(line)))); - instance.Connect("mutated", Callable.From((Dictionary mutation) => Mutated?.Invoke(mutation))); - instance.Connect("dialogue_ended", Callable.From((Resource dialogueResource) => DialogueEnded?.Invoke(dialogueResource))); - } - - public void Prepare() - { - Prepare(Instance); - } - - - public static async Task GetSingleton() - { - if (instance != null) return instance; - - var tree = Engine.GetMainLoop(); - int x = 0; - - // Try and find the singleton for a few seconds - while (!Engine.HasSingleton("DialogueManager") && x < 300) - { - await tree.ToSignal(tree, SceneTree.SignalName.ProcessFrame); - x++; - } - - // If it times out something is wrong - if (x >= 300) - { - throw new Exception("The DialogueManager singleton is missing."); - } - - instance = Engine.GetSingleton("DialogueManager"); - return instance; - } - - public static Resource CreateResourceFromText(string text) - { - return (Resource)Instance.Call("create_resource_from_text", text); - } - - public static async Task GetNextDialogueLine(Resource dialogueResource, string key = "", Array? extraGameStates = null) - { - var instance = (Node)Instance.Call("_bridge_get_new_instance"); - Prepare(instance); - instance.Call("_bridge_get_next_dialogue_line", dialogueResource, key, extraGameStates ?? new Array()); - var result = await instance.ToSignal(instance, "bridge_get_next_dialogue_line_completed"); - instance.QueueFree(); - - if ((RefCounted)result[0] == null) return null; - - return new DialogueLine((RefCounted)result[0]); - } - - - public static CanvasLayer ShowExampleDialogueBalloon(Resource dialogueResource, string key = "", Array? extraGameStates = null) - { - return (CanvasLayer)Instance.Call("show_example_dialogue_balloon", dialogueResource, key, extraGameStates ?? new Array()); - } - - - public static Node ShowDialogueBalloonScene(string balloonScene, Resource dialogueResource, string key = "", Array? extraGameStates = null) - { - return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array()); - } - - public static Node ShowDialogueBalloonScene(PackedScene balloonScene, Resource dialogueResource, string key = "", Array? extraGameStates = null) - { - return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array()); - } - - public static Node ShowDialogueBalloonScene(Node balloonScene, Resource dialogueResource, string key = "", Array? extraGameStates = null) - { - return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array()); - } - - - public static Node ShowDialogueBalloon(Resource dialogueResource, string key = "", Array? extraGameStates = null) - { - return (Node)Instance.Call("show_dialogue_balloon", dialogueResource, key, extraGameStates ?? new Array()); - } - - - public static async void Mutate(Dictionary mutation, Array? extraGameStates = null, bool isInlineMutation = false) - { - Instance.Call("_bridge_mutate", mutation, extraGameStates ?? new Array(), isInlineMutation); - await Instance.ToSignal(Instance, "bridge_mutated"); - } - - - public bool ThingHasMethod(GodotObject thing, string method) - { - MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); - return info != null; - } - - - public async void ResolveThingMethod(GodotObject thing, string method, Array args) - { - MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); - - if (info == null) return; - -#nullable disable - // Convert the method args to something reflection can handle - ParameterInfo[] argTypes = info.GetParameters(); - object[] _args = new object[argTypes.Length]; - for (int i = 0; i < argTypes.Length; i++) - { - // check if args is assignable from derived type - if (i < args.Count && args[i].Obj != null) - { - if (argTypes[i].ParameterType.IsAssignableFrom(args[i].Obj.GetType())) - { - _args[i] = args[i].Obj; - } - // fallback to assigning primitive types - else - { - _args[i] = Convert.ChangeType(args[i].Obj, argTypes[i].ParameterType); - } - } - else if (argTypes[i].DefaultValue != null) - { - _args[i] = argTypes[i].DefaultValue; - } - } - - // Add a single frame wait in case the method returns before signals can listen - await ToSignal(Engine.GetMainLoop(), SceneTree.SignalName.ProcessFrame); - - // invoke method and handle the result based on return type - object result = info.Invoke(thing, _args); - - if (result is Task taskResult) - { - await taskResult; - try - { - Variant value = (Variant)taskResult.GetType().GetProperty("Result").GetValue(taskResult); - EmitSignal(SignalName.Resolved, value); - } - catch (Exception err) - { - EmitSignal(SignalName.Resolved); - } - } - else - { - EmitSignal(SignalName.Resolved, (Variant)result); - } - } -#nullable enable - } - - - public partial class DialogueLine : RefCounted - { - private string id = ""; - public string Id - { - get => id; - set => id = value; - } - - private string type = "dialogue"; - public string Type - { - get => type; - set => type = value; - } - - private string next_id = ""; - public string NextId - { - get => next_id; - set => next_id = value; - } - - private string character = ""; - public string Character - { - get => character; - set => character = value; - } - - private string text = ""; - public string Text - { - get => text; - set => text = value; - } - - private string translation_key = ""; - public string TranslationKey - { - get => translation_key; - set => translation_key = value; - } - - private Array responses = new Array(); - public Array Responses - { - get => responses; - } - - private string? time = null; - public string? Time - { - get => time; - } - - private Dictionary pauses = new Dictionary(); - public Dictionary Pauses - { - get => pauses; - } - - private Dictionary speeds = new Dictionary(); - public Dictionary Speeds - { - get => speeds; - } - - private Array inline_mutations = new Array(); - public Array InlineMutations - { - get => inline_mutations; - } - - private Array extra_game_states = new Array(); - public Array ExtraGameStates - { - get => extra_game_states; - } - - private Array tags = new Array(); - public Array Tags - { - get => tags; - } - - public DialogueLine(RefCounted data) - { - type = (string)data.Get("type"); - next_id = (string)data.Get("next_id"); - character = (string)data.Get("character"); - text = (string)data.Get("text"); - translation_key = (string)data.Get("translation_key"); - pauses = (Dictionary)data.Get("pauses"); - speeds = (Dictionary)data.Get("speeds"); - inline_mutations = (Array)data.Get("inline_mutations"); - time = (string)data.Get("time"); - tags = (Array)data.Get("tags"); - - foreach (var response in (Array)data.Get("responses")) - { - responses.Add(new DialogueResponse(response)); - } - } - - - public string GetTagValue(string tagName) - { - string wrapped = $"{tagName}="; - foreach (var tag in tags) - { - if (tag.StartsWith(wrapped)) - { - return tag.Substring(wrapped.Length); - } - } - return ""; - } - - public override string ToString() - { - switch (type) - { - case "dialogue": - return $""; - case "mutation": - return ""; - default: - return ""; - } - } - } - - - public partial class DialogueResponse : RefCounted - { - private string next_id = ""; - public string NextId - { - get => next_id; - set => next_id = value; - } - - private bool is_allowed = true; - public bool IsAllowed - { - get => is_allowed; - set => is_allowed = value; - } - - private string text = ""; - public string Text - { - get => text; - set => text = value; - } - - private string translation_key = ""; - public string TranslationKey - { - get => translation_key; - set => translation_key = value; - } - - private Array tags = new Array(); - public Array Tags - { - get => tags; - } - - public DialogueResponse(RefCounted data) - { - next_id = (string)data.Get("next_id"); - is_allowed = (bool)data.Get("is_allowed"); - text = (string)data.Get("text"); - translation_key = (string)data.Get("translation_key"); - tags = (Array)data.Get("tags"); - } - - public string GetTagValue(string tagName) - { - string wrapped = $"{tagName}="; - foreach (var tag in tags) - { - if (tag.StartsWith(wrapped)) - { - return tag.Substring(wrapped.Length); - } - } - return ""; - } - - public override string ToString() - { - return $" - - - - - - - - - diff --git a/addons/dialogue_manager/assets/responses_menu.svg b/addons/dialogue_manager/assets/responses_menu.svg deleted file mode 100644 index 4e4089d..0000000 --- a/addons/dialogue_manager/assets/responses_menu.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - diff --git a/addons/dialogue_manager/assets/update.svg b/addons/dialogue_manager/assets/update.svg deleted file mode 100644 index a5b80ee..0000000 --- a/addons/dialogue_manager/assets/update.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - diff --git a/addons/dialogue_manager/compiler/compilation.gd b/addons/dialogue_manager/compiler/compilation.gd deleted file mode 100644 index 1022566..0000000 --- a/addons/dialogue_manager/compiler/compilation.gd +++ /dev/null @@ -1,1078 +0,0 @@ -## A single compilation instance of some dialogue. -class_name DMCompilation extends RefCounted - - -#region Compilation locals - - -## A list of file paths that were imported by this file. -var imported_paths: PackedStringArray = [] -## A list of state names from "using" clauses. -var using_states: PackedStringArray = [] -## A map of titles in this file. -var titles: Dictionary = {} -## The first encountered title in this file. -var first_title: String = "" -## A list of character names in this file. -var character_names: PackedStringArray = [] -## A list of any compilation errors. -var errors: Array[Dictionary] = [] -## A map of all compiled lines. -var lines: Dictionary = {} -## A flattened and simplified map of compiled lines for storage in a resource. -var data: Dictionary = {} - - -#endregion - -#region Internal variables - - -# A list of all [RegEx] references -var regex: DMCompilerRegEx = DMCompilerRegEx.new() -# For parsing condition/mutation expressions -var expression_parser: DMExpressionParser = DMExpressionParser.new() - -# A map of titles that came from imported files. -var _imported_titles: Dictionary = {} -# Used to keep track of circular imports. -var _imported_line_map: Dictionary = {} -# The number of imported lines. -var _imported_line_count: int = 0 -# A list of already encountered static line IDs. -var _known_translation_keys: Dictionary = {} -# A noop for retrieving the next line without conditions. -var _first: Callable = func(_s): return true - -# Title jumps are adjusted as they are parsed so any goto lines might need to be adjusted after they are first seen. -var _goto_lines: Dictionary = {} - - -#endregion - -#region Main - - -## Compile some text. -func compile(text: String, path: String = ".") -> Error: - titles = {} - character_names = [] - - parse_line_tree(build_line_tree(inject_imported_files(text + "\n=> END", path))) - - # Convert the compiles lines to a Dictionary so they can be stored. - for id in lines: - var line: DMCompiledLine = lines[id] - data[id] = line.to_data() - - if errors.size() > 0: - return ERR_PARSE_ERROR - - return OK - - -## Inject any imported files -func inject_imported_files(text: String, path: String) -> PackedStringArray: - # Work out imports - var known_imports: Dictionary = {} - - # Include the base file path so that we can get around circular dependencies - known_imports[path.hash()] = "." - - var raw_lines: PackedStringArray = text.split("\n") - - for id in range(0, raw_lines.size()): - var line = raw_lines[id] - if is_import_line(line): - var import_data: Dictionary = extract_import_path_and_name(line) - - if not import_data.has("path"): continue - - var import_hash: int = import_data.path.hash() - if import_data.size() > 0: - # Keep track of titles so we can add imported ones later - if str(import_hash) in _imported_titles.keys(): - add_error(id, 0, DMConstants.ERR_FILE_ALREADY_IMPORTED) - if import_data.prefix in _imported_titles.values(): - add_error(id, 0, DMConstants.ERR_DUPLICATE_IMPORT_NAME) - _imported_titles[str(import_hash)] = import_data.prefix - - # Import the file content - if not known_imports.has(import_hash): - var error: Error = import_content(import_data.path, import_data.prefix, _imported_line_map, known_imports) - if error != OK: - add_error(id, 0, error) - - # Make a map so we can refer compiled lines to where they were imported from - if not _imported_line_map.has(import_hash): - _imported_line_map[import_hash] = { - hash = import_hash, - imported_on_line_number = id, - from_line = 0, - to_line = 0 - } - - var imported_content: String = "" - var cummulative_line_number: int = 0 - for item in _imported_line_map.values(): - item["from_line"] = cummulative_line_number - if known_imports.has(item.hash): - cummulative_line_number += known_imports[item.hash].split("\n").size() - item["to_line"] = cummulative_line_number - if known_imports.has(item.hash): - imported_content += known_imports[item.hash] + "\n" - - if imported_content == "": - _imported_line_count = 0 - return text.split("\n") - else: - _imported_line_count = cummulative_line_number + 1 - # Combine imported lines with the original lines - return (imported_content + "\n" + text).split("\n") - - -## Import content from another dialogue file or return an ERR -func import_content(path: String, prefix: String, imported_line_map: Dictionary, known_imports: Dictionary) -> Error: - if FileAccess.file_exists(path): - var file = FileAccess.open(path, FileAccess.READ) - var content: PackedStringArray = file.get_as_text().strip_edges().split("\n") - - for index in range(0, content.size()): - var line = content[index] - if is_import_line(line): - var import = extract_import_path_and_name(line) - if import.size() > 0: - if not known_imports.has(import.path.hash()): - # Add an empty record into the keys just so we don't end up with cyclic dependencies - known_imports[import.path.hash()] = "" - if import_content(import.path, import.prefix, imported_line_map, known_imports) != OK: - return ERR_LINK_FAILED - - if not imported_line_map.has(import.path.hash()): - # Make a map so we can refer compiled lines to where they were imported from - imported_line_map[import.path.hash()] = { - hash = import.path.hash(), - imported_on_line_number = index, - from_line = 0, - to_line = 0 - } - - _imported_titles[import.prefix] = import.path.hash() - - var origin_hash: int = -1 - for hash_value in known_imports.keys(): - if known_imports[hash_value] == ".": - origin_hash = hash_value - - # Replace any titles or jump points with references to the files they point to (event if they point to their own file) - for i in range(0, content.size()): - var line = content[i] - if line.strip_edges().begins_with("~ "): - var title = line.strip_edges().substr(2) - if "/" in line: - var bits = title.split("/") - content[i] = "~ %s/%s" % [_imported_titles[bits[0]], bits[1]] - else: - content[i] = "~ %s/%s" % [str(path.hash()), title] - - elif "=>< " in line: - var jump: String = line.substr(line.find("=>< ") + "=>< ".length()).strip_edges() - if "/" in jump: - var bits: PackedStringArray = jump.split("/") - var title_hash: int = _imported_titles[bits[0]] - if title_hash == origin_hash: - content[i] = "%s=>< %s" % [line.split("=>< ")[0], bits[1]] - else: - content[i] = "%s=>< %s/%s" % [line.split("=>< ")[0], title_hash, bits[1]] - - elif not jump in ["END", "END!"]: - content[i] = "%s=>< %s/%s" % [line.split("=>< ")[0], str(path.hash()), jump] - - elif "=> " in line: - var jump: String = line.substr(line.find("=> ") + "=> ".length()).strip_edges() - if "/" in jump: - var bits: PackedStringArray = jump.split("/") - var title_hash: int = _imported_titles[bits[0]] - if title_hash == origin_hash: - content[i] = "%s=> %s" % [line.split("=> ")[0], bits[1]] - else: - content[i] = "%s=> %s/%s" % [line.split("=> ")[0], title_hash, bits[1]] - - elif not jump in ["END", "END!"]: - content[i] = "%s=> %s/%s" % [line.split("=> ")[0], str(path.hash()), jump] - - imported_paths.append(path) - known_imports[path.hash()] = "\n".join(content) + "\n=> END\n" - return OK - else: - return ERR_FILE_NOT_FOUND - - -## Build a tree of parent/child relationships -func build_line_tree(raw_lines: PackedStringArray) -> DMTreeLine: - var root: DMTreeLine = DMTreeLine.new("") - var parent_chain: Array[DMTreeLine] = [root] - var previous_line: DMTreeLine - var doc_comments: PackedStringArray = [] - - # Get list of known autoloads - var autoload_names: PackedStringArray = get_autoload_names() - - for i in range(0, raw_lines.size()): - var raw_line: String = raw_lines[i] - var tree_line: DMTreeLine = DMTreeLine.new(str(i - _imported_line_count)) - - tree_line.line_number = i + 1 - tree_line.type = get_line_type(raw_line) - tree_line.text = raw_line.strip_edges() - - # Handle any "using" directives. - if raw_line.begins_with("using "): - var using_match: RegExMatch = regex.USING_REGEX.search(raw_line) - if "state" in using_match.names: - var using_state: String = using_match.strings[using_match.names.state].strip_edges() - if not using_state in autoload_names: - add_error(i, 0, DMConstants.ERR_UNKNOWN_USING) - elif not using_state in using_states: - using_states.append(using_state) - continue - # Ignore import lines because they've already been processed. - elif is_import_line(raw_line): - continue - - tree_line.indent = get_indent(raw_line) - - # Attach doc comments - if raw_line.strip_edges().begins_with("##"): - doc_comments.append(raw_line.replace("##", "").strip_edges()) - elif tree_line.type == DMConstants.TYPE_DIALOGUE: - tree_line.notes = "\n".join(doc_comments) - doc_comments.clear() - - # Empty lines are only kept so that we can work out groupings of things (eg. responses and - # randomised lines). Therefore we only need to keep one empty line in a row even if there - # are multiple. The indent of an empty line is assumed to be the same as the non-empty line - # following it. That way, grouping calculations should work. - if tree_line.type in [DMConstants.TYPE_UNKNOWN, DMConstants.TYPE_COMMENT] and raw_lines.size() > i + 1: - var next_line = raw_lines[i + 1] - if previous_line and previous_line.type in [DMConstants.TYPE_UNKNOWN, DMConstants.TYPE_COMMENT] and tree_line.type in [DMConstants.TYPE_UNKNOWN, DMConstants.TYPE_COMMENT]: - continue - else: - tree_line.type = DMConstants.TYPE_UNKNOWN - tree_line.indent = get_indent(next_line) - - # Check for indentation changes - if tree_line.indent > parent_chain.size() - 1: - parent_chain.append(previous_line) - elif tree_line.indent < parent_chain.size() - 1: - parent_chain.resize(tree_line.indent + 1) - - # Add any titles to the list of known titles - if tree_line.type == DMConstants.TYPE_TITLE: - var title: String = tree_line.text.substr(2) - if title == "": - add_error(i, 2, DMConstants.ERR_EMPTY_TITLE) - elif titles.has(title): - add_error(i, 2, DMConstants.ERR_DUPLICATE_TITLE) - else: - titles[title] = tree_line.id - if "/" in title: - # Replace the hash title with something human readable. - var bits: PackedStringArray = title.split("/") - if _imported_titles.has(bits[0]): - title = _imported_titles[bits[0]] + "/" + bits[1] - titles[title] = tree_line.id - elif first_title == "" and i >= _imported_line_count: - first_title = tree_line.id - - # Append the current line to the current parent (note: the root is the most basic parent). - var parent: DMTreeLine = parent_chain[parent_chain.size() - 1] - tree_line.parent = parent - parent.children.append(tree_line) - - previous_line = tree_line - - return root - - -#endregion - -#region Parsing - - -func parse_line_tree(root: DMTreeLine, parent: DMCompiledLine = null) -> Array[DMCompiledLine]: - var compiled_lines: Array[DMCompiledLine] = [] - - for i in range(0, root.children.size()): - var tree_line: DMTreeLine = root.children[i] - var line: DMCompiledLine = DMCompiledLine.new(tree_line.id, tree_line.type) - - match line.type: - DMConstants.TYPE_UNKNOWN: - line.next_id = get_next_matching_sibling_id(root.children, i, parent, _first) - - DMConstants.TYPE_TITLE: - parse_title_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_CONDITION: - parse_condition_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_WHILE: - parse_while_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_MATCH: - parse_match_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_WHEN: - parse_when_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_MUTATION: - parse_mutation_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_GOTO: - # Extract any weighted random calls before parsing dialogue - if tree_line.text.begins_with("%"): - parse_random_line(tree_line, line, root.children, i, parent) - parse_goto_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_RESPONSE: - parse_response_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_RANDOM: - parse_random_line(tree_line, line, root.children, i, parent) - - DMConstants.TYPE_DIALOGUE: - # Extract any weighted random calls before parsing dialogue - if tree_line.text.begins_with("%"): - parse_random_line(tree_line, line, root.children, i, parent) - parse_dialogue_line(tree_line, line, root.children, i, parent) - - # Main line map is keyed by ID - lines[line.id] = line - - # Returned lines order is preserved so that it can be used for compiling children - compiled_lines.append(line) - - return compiled_lines - - -## Parse a title and apply it to the given line -func parse_title_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - var result: Error = OK - - line.text = tree_line.text.substr(tree_line.text.find("~ ") + 2).strip_edges() - - # Titles can't have numbers as the first letter (unless they are external titles which get replaced with hashes) - if tree_line.line_number >= _imported_line_count and regex.BEGINS_WITH_NUMBER_REGEX.search(line.text): - result = add_error(tree_line.line_number, 2, DMConstants.ERR_TITLE_BEGINS_WITH_NUMBER) - - # Only import titles are allowed to have "/" in them - var valid_title = regex.VALID_TITLE_REGEX.search(line.text.replace("/", "")) - if not valid_title: - result = add_error(tree_line.line_number, 2, DMConstants.ERR_TITLE_INVALID_CHARACTERS) - - line.next_id = get_next_matching_sibling_id(siblings, sibling_index, parent, _first) - - ## Update the titles reference to point to the actual first line - titles[line.text] = line.next_id - - ## Update any lines that point to this title - if _goto_lines.has(line.text): - for goto_line in _goto_lines[line.text]: - goto_line.next_id = line.next_id - - return result - - -## Parse a goto and apply it to the given line. -func parse_goto_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - # Work out where this line is jumping to. - var goto_data: DMResolvedGotoData = DMResolvedGotoData.new(tree_line.text, titles) - if goto_data.error: - return add_error(tree_line.line_number, tree_line.indent + 2, goto_data.error) - if goto_data.next_id or goto_data.expression: - line.next_id = goto_data.next_id - line.next_id_expression = goto_data.expression - add_reference_to_title(goto_data.title, line) - - if goto_data.is_snippet: - line.is_snippet = true - line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, _first) - - return OK - - -## Parse a condition line and apply to the given line -func parse_condition_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - # Work out the next IDs before parsing the condition line itself so that the last - # child can inherit from the chain. - - # Find the next conditional sibling that is part of this grouping (if there is one). - for next_sibling: DMTreeLine in siblings.slice(sibling_index + 1): - if not next_sibling.type in [DMConstants.TYPE_UNKNOWN, DMConstants.TYPE_CONDITION]: - break - elif next_sibling.type == DMConstants.TYPE_CONDITION: - if next_sibling.text.begins_with("el"): - line.next_sibling_id = next_sibling.id - break - else: - break - - line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, func(s: DMTreeLine): - # The next line that isn't a conditional or is a new "if" - return s.type != DMConstants.TYPE_CONDITION or s.text.begins_with("if ") - ) - # Any empty IDs should end the conversation. - if line.next_id_after == DMConstants.ID_NULL: - line.next_id_after = parent.next_id_after if parent != null and parent.next_id_after else DMConstants.ID_END - - # Having no nested body is an immediate failure. - if tree_line.children.size() == 0: - return add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_INVALID_CONDITION_INDENTATION) - - # Try to parse the conditional expression ("else" has no expression). - if "if " in tree_line.text: - var condition: Dictionary = extract_condition(tree_line.text, false, tree_line.indent) - if condition.has("error"): - return add_error(tree_line.line_number, condition.index, condition.error) - else: - line.expression = condition - - # Parse any nested body lines - parse_children(tree_line, line) - - return OK - - -## Parse a while loop and apply it to the given line. -func parse_while_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, _first) - - # Parse the while condition - var condition: Dictionary = extract_condition(tree_line.text, false, tree_line.indent) - if condition.has("error"): - return add_error(tree_line.line_number, condition.index, condition.error) - else: - line.expression = condition - - # Parse the nested body (it should take care of looping back to this line when it finishes) - parse_children(tree_line, line) - - return OK - - -func parse_match_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - var result: Error = OK - - # The next line after is the next sibling - line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, _first) - - # Extract the condition to match to - var condition: Dictionary = extract_condition(tree_line.text, false, tree_line.indent) - if condition.has("error"): - result = add_error(tree_line.line_number, condition.index, condition.error) - else: - line.expression = condition - - # Match statements should have children - if tree_line.children.size() == 0: - result = add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_INVALID_CONDITION_INDENTATION) - - # Check that all children are when or else. - for child in tree_line.children: - if child.type == DMConstants.TYPE_WHEN: continue - if child.type == DMConstants.TYPE_CONDITION and child.text.begins_with("else"): continue - - result = add_error(child.line_number, child.indent, DMConstants.ERR_EXPECTED_WHEN_OR_ELSE) - - # Each child should be a "when" or "else". We don't need those lines themselves, just their - # condition and the line they point to if the conditions passes. - var children: Array[DMCompiledLine] = parse_children(tree_line, line) - for child: DMCompiledLine in children: - # "when" cases - if child.type == DMConstants.TYPE_WHEN: - line.siblings.append({ - condition = child.expression, - next_id = child.next_id - }) - # "else" case - elif child.type == DMConstants.TYPE_CONDITION: - if line.siblings.any(func(s): return s.has("is_else")): - result = add_error(child.line_number, child.indent, DMConstants.ERR_ONLY_ONE_ELSE_ALLOWED) - else: - line.siblings.append({ - next_id = child.next_id, - is_else = true - }) - # Remove the line from the list of all lines because we don't need it any more. - lines.erase(child.id) - - return result - - -func parse_when_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - var result: Error = OK - - # This when line should be found inside a match line - if parent.type != DMConstants.TYPE_MATCH: - result = add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_WHEN_MUST_BELONG_TO_MATCH) - - # When lines should have children - if tree_line.children.size() == 0: - result = add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_INVALID_CONDITION_INDENTATION) - - # The next line after a when is the same as its parent match line - line.next_id_after = parent.next_id_after - - # Extract the condition to match to - var condition: Dictionary = extract_condition(tree_line.text, false, tree_line.indent) - if condition.has("error"): - result = add_error(tree_line.line_number, condition.index, condition.error) - else: - line.expression = condition - - parse_children(tree_line, line) - - return result - - -## Parse a mutation line and apply it to the given line -func parse_mutation_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - var mutation: Dictionary = extract_mutation(tree_line.text) - if mutation.has("error"): - return add_error(tree_line.line_number, mutation.index, mutation.error) - else: - line.expression = mutation - - line.next_id = get_next_matching_sibling_id(siblings, sibling_index, parent, _first) - - return OK - - -## Parse a response and apply it to the given line. -func parse_response_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - var result: Error = OK - - # Remove the "- " - tree_line.text = tree_line.text.substr(2) - - # Extract the static line ID - var static_line_id: String = extract_static_line_id(tree_line.text) - if static_line_id: - tree_line.text = tree_line.text.replace("[ID:%s]" % [static_line_id], "") - line.translation_key = static_line_id - - # Handle conditional responses and remove them from the prompt text. - if " [if " in tree_line.text: - var condition = extract_condition(tree_line.text, true, tree_line.indent) - if condition.has("error"): - result = add_error(tree_line.line_number, condition.index, condition.error) - else: - line.expression = condition - tree_line.text = regex.WRAPPED_CONDITION_REGEX.sub(tree_line.text, "").strip_edges() - - # Find the original response in this group of responses. - var original_response: DMTreeLine = tree_line - for i in range(sibling_index - 1, 0, -1): - if siblings[i].type == DMConstants.TYPE_RESPONSE: - original_response = siblings[i] - elif siblings[i].type != DMConstants.TYPE_UNKNOWN: - break - - # If it's the original response then set up an original line. - if original_response == tree_line: - line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, (func(s: DMTreeLine): - # The next line that isn't a response. - return not s.type in [DMConstants.TYPE_RESPONSE, DMConstants.TYPE_UNKNOWN] - ), true) - line.responses = [line.id] - # If this line has children then the next ID is the first child. - if tree_line.children.size() > 0: - parse_children(tree_line, line) - # Otherwise use the same ID for after the random group. - else: - line.next_id = line.next_id_after - # Otherwise let the original line know about it. - else: - var original_line: DMCompiledLine = lines[original_response.id] - line.next_id_after = original_line.next_id_after - line.siblings = original_line.siblings - original_line.responses.append(line.id) - # If this line has children then the next ID is the first child. - if tree_line.children.size() > 0: - parse_children(tree_line, line) - # Otherwise use the original line's next ID after. - else: - line.next_id = original_line.next_id_after - - parse_character_and_dialogue(tree_line, line, siblings, sibling_index, parent) - - return OK - - -## Parse a randomised line -func parse_random_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - # Find the weight - var weight: float = 1 - var found = regex.WEIGHTED_RANDOM_SIBLINGS_REGEX.search(tree_line.text + " ") - var condition: Dictionary = {} - if found: - if found.names.has("weight"): - weight = found.strings[found.names.weight].to_float() - if found.names.has("condition"): - condition = extract_condition(tree_line.text, true, tree_line.indent) - - # Find the original random sibling. It will be the jump off point. - var original_sibling: DMTreeLine = tree_line - for i in range(sibling_index - 1, -1, -1): - if siblings[i] and siblings[i].is_random: - original_sibling = siblings[i] - else: - break - - var weighted_sibling: Dictionary = { weight = weight, id = line.id, condition = condition } - - # If it's the original sibling then set up an original line. - if original_sibling == tree_line: - line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, (func(s: DMTreeLine): - # The next line that isn't a randomised line. - # NOTE: DMTreeLine.is_random won't be set at this point so we need to check for the "%" prefix. - return not s.text.begins_with("%") - ), true) - line.siblings = [weighted_sibling] - # If this line has children then the next ID is the first child. - if tree_line.children.size() > 0: - parse_children(tree_line, line) - # Otherwise use the same ID for after the random group. - else: - line.next_id = line.next_id_after - - # Otherwise let the original line know about it. - else: - var original_line: DMCompiledLine = lines[original_sibling.id] - line.next_id_after = original_line.next_id_after - line.siblings = original_line.siblings - original_line.siblings.append(weighted_sibling) - # If this line has children then the next ID is the first child. - if tree_line.children.size() > 0: - parse_children(tree_line, line) - # Otherwise use the original line's next ID after. - else: - line.next_id = original_line.next_id_after - - # Remove the randomise syntax from the line. - tree_line.text = regex.WEIGHTED_RANDOM_SIBLINGS_REGEX.sub(tree_line.text, "") - tree_line.is_random = true - - return OK - - -## Parse some dialogue and apply it to the given line. -func parse_dialogue_line(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - var result: Error = OK - - # Remove escape character - if tree_line.text.begins_with("\\using"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\if"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\elif"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\else"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\while"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\match"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\when"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\do"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\set"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\-"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\~"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\=>"): tree_line.text = tree_line.text.substr(1) - if tree_line.text.begins_with("\\%"): tree_line.text = tree_line.text.substr(1) - - # Append any further dialogue - for i in range(0, tree_line.children.size()): - var child: DMTreeLine = tree_line.children[i] - if child.type == DMConstants.TYPE_DIALOGUE: - tree_line.text += "\n" + child.text - else: - result = add_error(child.line_number, child.indent, DMConstants.ERR_INVALID_INDENTATION) - - # Extract the static line ID - var static_line_id: String = extract_static_line_id(tree_line.text) - if static_line_id: - tree_line.text = tree_line.text.replace("[ID:%s]" % [static_line_id], "") - line.translation_key = static_line_id - - # Check for simultaneous lines - if tree_line.text.begins_with("| "): - # Jumps are only allowed on the origin line. - if " =>" in tree_line.text: - result = add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_GOTO_NOT_ALLOWED_ON_CONCURRECT_LINES) - # Check for a valid previous line. - tree_line.text = tree_line.text.substr(2) - var previous_sibling: DMTreeLine = siblings[sibling_index - 1] - if previous_sibling.type != DMConstants.TYPE_DIALOGUE: - result = add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_CONCURRENT_LINE_WITHOUT_ORIGIN) - else: - # Because the previous line's concurrent_lines array is the same as - # any line before that this doesn't need to check any higher up. - var previous_line: DMCompiledLine = lines[previous_sibling.id] - previous_line.concurrent_lines.append(line.id) - line.concurrent_lines = previous_line.concurrent_lines - - parse_character_and_dialogue(tree_line, line, siblings, sibling_index, parent) - - # Check for any inline expression errors - var resolved_line_data: DMResolvedLineData = DMResolvedLineData.new("") - var bbcodes: Array[Dictionary] = resolved_line_data.find_bbcode_positions_in_string(tree_line.text, true, true) - for bbcode: Dictionary in bbcodes: - var tag: String = bbcode.code - var code: String = bbcode.raw_args - if tag.begins_with("do") or tag.begins_with("set") or tag.begins_with("if"): - var expression: Array = expression_parser.tokenise(code, DMConstants.TYPE_MUTATION, bbcode.start + bbcode.code.length()) - if expression.size() == 0: - add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_INVALID_EXPRESSION) - elif expression[0].type == DMConstants.TYPE_ERROR: - add_error(tree_line.line_number, tree_line.indent + expression[0].index, expression[0].value) - - # If the line isn't part of a weighted random group then make it point to the next - # available sibling. - if line.next_id == DMConstants.ID_NULL and line.siblings.size() == 0: - line.next_id = get_next_matching_sibling_id(siblings, sibling_index, parent, func(s: DMTreeLine): - # Ignore concurrent lines. - return not s.text.begins_with("| ") - ) - - return result - - -## Parse the character name and dialogue and apply it to a given line. -func parse_character_and_dialogue(tree_line: DMTreeLine, line: DMCompiledLine, siblings: Array[DMTreeLine], sibling_index: int, parent: DMCompiledLine) -> Error: - var result: Error = OK - - var text: String = tree_line.text - - # Attach any doc comments. - line.notes = tree_line.notes - - # Extract tags. - var tag_data: DMResolvedTagData = DMResolvedTagData.new(text) - line.tags = tag_data.tags - text = tag_data.text_without_tags - - # Handle inline gotos and remove them from the prompt text. - if " =><" in text: - # Because of when the return point needs to be known at runtime we need to split - # this line into two (otherwise the return point would be dependent on the balloon). - var goto_data: DMResolvedGotoData = DMResolvedGotoData.new(text, titles) - if goto_data.error: - result = add_error(tree_line.line_number, tree_line.indent + 3, goto_data.error) - if goto_data.next_id or goto_data.expression: - text = goto_data.text_without_goto - var goto_line: DMCompiledLine = DMCompiledLine.new(line.id + ".1", DMConstants.TYPE_GOTO) - goto_line.next_id = goto_data.next_id - line.next_id_expression = goto_data.expression - if line.type == DMConstants.TYPE_RESPONSE: - goto_line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, func(s: DMTreeLine): - # If this is coming from a response then we want the next non-response line. - return s.type != DMConstants.TYPE_RESPONSE - ) - else: - goto_line.next_id_after = get_next_matching_sibling_id(siblings, sibling_index, parent, _first) - goto_line.is_snippet = true - lines[goto_line.id] = goto_line - line.next_id = goto_line.id - add_reference_to_title(goto_data.title, goto_line) - elif " =>" in text: - var goto_data: DMResolvedGotoData = DMResolvedGotoData.new(text, titles) - if goto_data.error: - result = add_error(tree_line.line_number, tree_line.indent + 2, goto_data.error) - if goto_data.next_id or goto_data.expression: - text = goto_data.text_without_goto - line.next_id = goto_data.next_id - line.next_id_expression = goto_data.expression - add_reference_to_title(goto_data.title, line) - - # Handle the dialogue. - text = text.replace("\\:", "!ESCAPED_COLON!") - if ": " in text: - # If a character was given then split it out. - var bits = Array(text.strip_edges().split(": ")) - line.character = bits.pop_front().strip_edges() - if not line.character in character_names: - character_names.append(line["character"]) - # Character names can have expressions in them. - line.character_replacements = expression_parser.extract_replacements(line.character, tree_line.indent) - for replacement in line.character_replacements: - if replacement.has("error"): - result = add_error(tree_line.line_number, replacement.index, replacement.error) - text = ": ".join(bits).replace("!ESCAPED_COLON!", ":") - else: - line.character = "" - text = text.replace("!ESCAPED_COLON!", ":") - - # Extract any expressions in the dialogue. - line.text_replacements = expression_parser.extract_replacements(text, line.character.length() + 2 + tree_line.indent) - for replacement in line.text_replacements: - if replacement.has("error"): - result = add_error(tree_line.line_number, replacement.index, replacement.error) - - # Replace any newlines. - text = text.replace("\\n", "\n").strip_edges() - - # If there was no manual translation key then just use the text itself - if line.translation_key == "": - line.translation_key = text - - line.text = text - - # IDs can't be duplicated for text that doesn't match. - if line.translation_key != "": - if _known_translation_keys.has(line.translation_key) and _known_translation_keys.get(line.translation_key) != line.text: - result = add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_DUPLICATE_ID) - else: - _known_translation_keys[line.translation_key] = line.text - # Show an error if missing translations is enabled - elif DMSettings.get_setting(DMSettings.MISSING_TRANSLATIONS_ARE_ERRORS, false): - result = add_error(tree_line.line_number, tree_line.indent, DMConstants.ERR_MISSING_ID) - - return result - - -#endregion - -#region Errors - - -## Add a compilation error to the list. Returns the given error code. -func add_error(line_number: int, column_number: int, error: int) -> Error: - # See if the error was in an imported file - for item in _imported_line_map.values(): - if line_number < item.to_line: - errors.append({ - line_number = item.imported_on_line_number, - column_number = 0, - error = DMConstants.ERR_ERRORS_IN_IMPORTED_FILE, - external_error = error, - external_line_number = line_number - }) - return error - - # Otherwise, it's in this file - errors.append({ - line_number = line_number - _imported_line_count, - column_number = column_number, - error = error - }) - - return error - - -#endregion - -#region Helpers - - -## Get the names of any autoloads in the project. -func get_autoload_names() -> PackedStringArray: - var autoloads: PackedStringArray = [] - - var project = ConfigFile.new() - project.load("res://project.godot") - if project.has_section("autoload"): - return Array(project.get_section_keys("autoload")).filter(func(key): return key != "DialogueManager") - - return autoloads - - -## Check if a line is importing another file. -func is_import_line(text: String) -> bool: - return text.begins_with("import ") and " as " in text - - -## Extract the import information from an import line -func extract_import_path_and_name(line: String) -> Dictionary: - var found: RegExMatch = regex.IMPORT_REGEX.search(line) - if found: - return { - path = found.strings[found.names.path], - prefix = found.strings[found.names.prefix] - } - else: - return {} - - -## Get the indent of a raw line -func get_indent(raw_line: String) -> int: - var tabs: RegExMatch = regex.INDENT_REGEX.search(raw_line) - if tabs: - return tabs.get_string().length() - else: - return 0 - - -## Get the type of a raw line -func get_line_type(raw_line: String) -> String: - raw_line = raw_line.strip_edges() - var text: String = regex.WEIGHTED_RANDOM_SIBLINGS_REGEX.sub(raw_line + " ", "").strip_edges() - - if text.begins_with("import "): - return DMConstants.TYPE_IMPORT - - if text.begins_with("#"): - return DMConstants.TYPE_COMMENT - - if text.begins_with("~ "): - return DMConstants.TYPE_TITLE - - if text.begins_with("if ") or text.begins_with("elif") or text.begins_with("else"): - return DMConstants.TYPE_CONDITION - - if text.begins_with("while "): - return DMConstants.TYPE_WHILE - - if text.begins_with("match "): - return DMConstants.TYPE_MATCH - - if text.begins_with("when "): - return DMConstants.TYPE_WHEN - - if text.begins_with("do ") or text.begins_with("do! ") or text.begins_with("set "): - return DMConstants.TYPE_MUTATION - - if text.begins_with("=> ") or text.begins_with("=>< "): - return DMConstants.TYPE_GOTO - - if text.begins_with("- "): - return DMConstants.TYPE_RESPONSE - - if raw_line.begins_with("%") and text.is_empty(): - return DMConstants.TYPE_RANDOM - - if not text.is_empty(): - return DMConstants.TYPE_DIALOGUE - - return DMConstants.TYPE_UNKNOWN - - -## Get the next sibling that passes a [Callable] matcher. -func get_next_matching_sibling_id(siblings: Array[DMTreeLine], from_index: int, parent: DMCompiledLine, matcher: Callable, with_empty_lines: bool = false) -> String: - for i in range(from_index + 1, siblings.size()): - var next_sibling: DMTreeLine = siblings[i] - - if not with_empty_lines: - # Ignore empty lines - if not next_sibling or next_sibling.type == DMConstants.TYPE_UNKNOWN: - continue - - if matcher.call(next_sibling): - return next_sibling.id - - # If no next ID can be found then check the parent for where to go next. - if parent != null: - return parent.id if parent.type == DMConstants.TYPE_WHILE else parent.next_id_after - - return DMConstants.ID_NULL - - -## Extract a static line ID from some text. -func extract_static_line_id(text: String) -> String: - # Find a static translation key, eg. [ID:something] - var found: RegExMatch = regex.STATIC_LINE_ID_REGEX.search(text) - if found: - return found.strings[found.names.id] - else: - return "" - - -## Extract a condition (or inline condition) from some text. -func extract_condition(text: String, is_wrapped: bool, index: int) -> Dictionary: - var regex: RegEx = regex.WRAPPED_CONDITION_REGEX if is_wrapped else regex.CONDITION_REGEX - var found: RegExMatch = regex.search(text) - - if found == null: - return { - index = 0, - error = DMConstants.ERR_INCOMPLETE_EXPRESSION - } - - var raw_condition: String = found.strings[found.names.expression] - if raw_condition.ends_with(":"): - raw_condition = raw_condition.substr(0, raw_condition.length() - 1) - - var expression: Array = expression_parser.tokenise(raw_condition, DMConstants.TYPE_CONDITION, index + found.get_start("expression")) - - if expression.size() == 0: - return { - index = index + found.get_start("expression"), - error = DMConstants.ERR_INCOMPLETE_EXPRESSION - } - elif expression[0].type == DMConstants.TYPE_ERROR: - return { - index = expression[0].index, - error = expression[0].value - } - else: - return { - expression = expression - } - - -## Extract a mutation from some text. -func extract_mutation(text: String) -> Dictionary: - var found: RegExMatch = regex.MUTATION_REGEX.search(text) - - if not found: - return { - index = 0, - error = DMConstants.ERR_INCOMPLETE_EXPRESSION - } - - if found.names.has("expression"): - var expression: Array = expression_parser.tokenise(found.strings[found.names.expression], DMConstants.TYPE_MUTATION, found.get_start("expression")) - if expression.size() == 0: - return { - index = found.get_start("expression"), - error = DMConstants.ERR_INCOMPLETE_EXPRESSION - } - elif expression[0].type == DMConstants.TYPE_ERROR: - return { - index = expression[0].index, - error = expression[0].value - } - else: - return { - expression = expression, - is_blocking = not "!" in found.strings[found.names.keyword] - } - - else: - return { - index = found.get_start(), - error = DMConstants.ERR_INCOMPLETE_EXPRESSION - } - - -## Keep track of lines referencing titles because their own next_id might not have been resolved yet. -func add_reference_to_title(title: String, line: DMCompiledLine) -> void: - if title in [DMConstants.ID_END, DMConstants.ID_END_CONVERSATION, DMConstants.ID_NULL]: return - - if not _goto_lines.has(title): - _goto_lines[title] = [] - _goto_lines[title].append(line) - - -## Parse a nested block of child lines -func parse_children(tree_line: DMTreeLine, line: DMCompiledLine) -> Array[DMCompiledLine]: - var children = parse_line_tree(tree_line, line) - if children.size() > 0: - line.next_id = children.front().id - # The last child should jump to the next line after its parent condition group - var last_child: DMCompiledLine = children.back() - if last_child.next_id == DMConstants.ID_NULL: - last_child.next_id = line.next_id_after - if last_child.siblings.size() > 0: - for sibling in last_child.siblings: - lines.get(sibling.id).next_id = last_child.next_id - - return children - - -#endregion diff --git a/addons/dialogue_manager/compiler/compilation.gd.uid b/addons/dialogue_manager/compiler/compilation.gd.uid deleted file mode 100644 index 24a13ee..0000000 --- a/addons/dialogue_manager/compiler/compilation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dsgpnyqg6cprg diff --git a/addons/dialogue_manager/compiler/compiled_line.gd b/addons/dialogue_manager/compiler/compiled_line.gd deleted file mode 100644 index 972fd5c..0000000 --- a/addons/dialogue_manager/compiler/compiled_line.gd +++ /dev/null @@ -1,157 +0,0 @@ -## A compiled line of dialogue. -class_name DMCompiledLine extends RefCounted - - -## The ID of the line -var id: String -## The translation key (or static line ID). -var translation_key: String = "" -## The type of line. -var type: String = "" -## The character name. -var character: String = "" -## Any interpolation expressions for the character name. -var character_replacements: Array[Dictionary] = [] -## The text of the line. -var text: String = "" -## Any interpolation expressions for the text. -var text_replacements: Array[Dictionary] = [] -## Any response siblings associated with this line. -var responses: PackedStringArray = [] -## Any randomise or case siblings for this line. -var siblings: Array[Dictionary] = [] -## Any lines said simultaneously. -var concurrent_lines: PackedStringArray = [] -## Any tags on this line. -var tags: PackedStringArray = [] -## The condition or mutation expression for this line. -var expression: Dictionary = {} -## The next sequential line to go to after this line. -var next_id: String = "" -## The next line to go to after this line if it is unknown and compile time. -var next_id_expression: Array[Dictionary] = [] -## Whether this jump line should return after the jump target sequence has ended. -var is_snippet: bool = false -## The ID of the next sibling line. -var next_sibling_id: String = "" -## The ID after this line if it belongs to a block (eg. conditions). -var next_id_after: String = "" -## Any doc comments attached to this line. -var notes: String = "" - - -#region Hooks - - -func _init(initial_id: String, initial_type: String) -> void: - id = initial_id - type = initial_type - - -func _to_string() -> String: - var s: Array = [ - "[%s]" % [type], - "%s:" % [character] if character != "" else null, - text if text != "" else null, - expression if expression.size() > 0 else null, - "[%s]" % [",".join(tags)] if tags.size() > 0 else null, - str(siblings) if siblings.size() > 0 else null, - str(responses) if responses.size() > 0 else null, - "=> END" if "end" in next_id else "=> %s" % [next_id], - "(~> %s)" % [next_sibling_id] if next_sibling_id != "" else null, - "(==> %s)" % [next_id_after] if next_id_after != "" else null, - ].filter(func(item): return item != null) - - return " ".join(s) - - -#endregion - -#region Helpers - - -## Express this line as a [Dictionary] that can be stored in a resource. -func to_data() -> Dictionary: - var d: Dictionary = { - id = id, - type = type, - next_id = next_id - } - - if next_id_expression.size() > 0: - d.next_id_expression = next_id_expression - - match type: - DMConstants.TYPE_CONDITION: - d.condition = expression - if not next_sibling_id.is_empty(): - d.next_sibling_id = next_sibling_id - d.next_id_after = next_id_after - - DMConstants.TYPE_WHILE: - d.condition = expression - d.next_id_after = next_id_after - - DMConstants.TYPE_MATCH: - d.condition = expression - d.next_id_after = next_id_after - d.cases = siblings - - DMConstants.TYPE_MUTATION: - d.mutation = expression - - DMConstants.TYPE_GOTO: - d.is_snippet = is_snippet - d.next_id_after = next_id_after - if not siblings.is_empty(): - d.siblings = siblings - - DMConstants.TYPE_RANDOM: - d.siblings = siblings - - DMConstants.TYPE_RESPONSE: - d.text = text - - if not responses.is_empty(): - d.responses = responses - - if translation_key != text: - d.translation_key = translation_key - if not expression.is_empty(): - d.condition = expression - if not character.is_empty(): - d.character = character - if not character_replacements.is_empty(): - d.character_replacements = character_replacements - if not text_replacements.is_empty(): - d.text_replacements = text_replacements - if not tags.is_empty(): - d.tags = tags - if not notes.is_empty(): - d.notes = notes - - DMConstants.TYPE_DIALOGUE: - d.text = text - - if translation_key != text: - d.translation_key = translation_key - - if not character.is_empty(): - d.character = character - if not character_replacements.is_empty(): - d.character_replacements = character_replacements - if not text_replacements.is_empty(): - d.text_replacements = text_replacements - if not tags.is_empty(): - d.tags = tags - if not notes.is_empty(): - d.notes = notes - if not siblings.is_empty(): - d.siblings = siblings - if not concurrent_lines.is_empty(): - d.concurrent_lines = concurrent_lines - - return d - - -#endregion diff --git a/addons/dialogue_manager/compiler/compiled_line.gd.uid b/addons/dialogue_manager/compiler/compiled_line.gd.uid deleted file mode 100644 index 17ec55e..0000000 --- a/addons/dialogue_manager/compiler/compiled_line.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dg8j5hudp4210 diff --git a/addons/dialogue_manager/compiler/compiler.gd b/addons/dialogue_manager/compiler/compiler.gd deleted file mode 100644 index a370ef6..0000000 --- a/addons/dialogue_manager/compiler/compiler.gd +++ /dev/null @@ -1,51 +0,0 @@ -## A compiler of Dialogue Manager dialogue. -class_name DMCompiler extends RefCounted - - -## Compile a dialogue script. -static func compile_string(text: String, path: String) -> DMCompilerResult: - var compilation: DMCompilation = DMCompilation.new() - compilation.compile(text, path) - - var result: DMCompilerResult = DMCompilerResult.new() - result.imported_paths = compilation.imported_paths - result.using_states = compilation.using_states - result.character_names = compilation.character_names - result.titles = compilation.titles - result.first_title = compilation.first_title - result.errors = compilation.errors - result.lines = compilation.data - result.raw_text = text - - return result - - -## Get the line type of a string. The returned string will match one of the [code]TYPE_[/code] constants of [DMConstants]. -static func get_line_type(text: String) -> String: - var compilation: DMCompilation = DMCompilation.new() - return compilation.get_line_type(text) - - -## Get the static line ID (eg. [code][ID:SOMETHING][/code]) of some text. -static func get_static_line_id(text: String) -> String: - var compilation: DMCompilation = DMCompilation.new() - return compilation.extract_static_line_id(text) - - -## Get the translatable part of a line. -static func extract_translatable_string(text: String) -> String: - var compilation: DMCompilation = DMCompilation.new() - - var tree_line = DMTreeLine.new("") - tree_line.text = text - var line: DMCompiledLine = DMCompiledLine.new("", compilation.get_line_type(text)) - compilation.parse_character_and_dialogue(tree_line, line, [tree_line], 0, null) - - return line.text - - -## Get the known titles in a dialogue script. -static func get_titles_in_text(text: String, path: String) -> Dictionary: - var compilation: DMCompilation = DMCompilation.new() - compilation.build_line_tree(compilation.inject_imported_files(text, path)) - return compilation.titles diff --git a/addons/dialogue_manager/compiler/compiler.gd.uid b/addons/dialogue_manager/compiler/compiler.gd.uid deleted file mode 100644 index e041f10..0000000 --- a/addons/dialogue_manager/compiler/compiler.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://chtfdmr0cqtp4 diff --git a/addons/dialogue_manager/compiler/compiler_regex.gd b/addons/dialogue_manager/compiler/compiler_regex.gd deleted file mode 100644 index ead998b..0000000 --- a/addons/dialogue_manager/compiler/compiler_regex.gd +++ /dev/null @@ -1,49 +0,0 @@ -## A collection of [RegEx] for use by the [DMCompiler]. -class_name DMCompilerRegEx extends RefCounted - - -var IMPORT_REGEX: RegEx = RegEx.create_from_string("import \"(?[^\"]+)\" as (?[a-zA-Z_\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}][a-zA-Z_0-9\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}]+)") -var USING_REGEX: RegEx = RegEx.create_from_string("^using (?.*)$") -var INDENT_REGEX: RegEx = RegEx.create_from_string("^\\t+") -var VALID_TITLE_REGEX: RegEx = RegEx.create_from_string("^[a-zA-Z_0-9\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}][a-zA-Z_0-9\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}]*$") -var BEGINS_WITH_NUMBER_REGEX: RegEx = RegEx.create_from_string("^\\d") -var CONDITION_REGEX: RegEx = RegEx.create_from_string("(if|elif|while|else if|match|when) (?.*)\\:?") -var WRAPPED_CONDITION_REGEX: RegEx = RegEx.create_from_string("\\[if (?.*)\\]") -var MUTATION_REGEX: RegEx = RegEx.create_from_string("(?do|do!|set) (?.*)") -var STATIC_LINE_ID_REGEX: RegEx = RegEx.create_from_string("\\[ID:(?.*?)\\]") -var WEIGHTED_RANDOM_SIBLINGS_REGEX: RegEx = RegEx.create_from_string("^\\%(?[\\d.]+)?( \\[if (?.+?)\\])? ") -var GOTO_REGEX: RegEx = RegEx.create_from_string("=>.*)") - -var INLINE_RANDOM_REGEX: RegEx = RegEx.create_from_string("\\[\\[(?.*?)\\]\\]") -var INLINE_CONDITIONALS_REGEX: RegEx = RegEx.create_from_string("\\[if (?.+?)\\](?.*?)\\[\\/if\\]") - -var TAGS_REGEX: RegEx = RegEx.create_from_string("\\[#(?.*?)\\]") - -var REPLACEMENTS_REGEX: RegEx = RegEx.create_from_string("{{(.*?)}}") - -var ALPHA_NUMERIC: RegEx = RegEx.create_from_string("[^a-zA-Z0-9\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}]+") - -var TOKEN_DEFINITIONS: Dictionary = { - DMConstants.TOKEN_FUNCTION: RegEx.create_from_string("^[a-zA-Z_\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}][a-zA-Z_0-9\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}]*\\("), - DMConstants.TOKEN_DICTIONARY_REFERENCE: RegEx.create_from_string("^[a-zA-Z_\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}][a-zA-Z_0-9\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}]*\\["), - DMConstants.TOKEN_PARENS_OPEN: RegEx.create_from_string("^\\("), - DMConstants.TOKEN_PARENS_CLOSE: RegEx.create_from_string("^\\)"), - DMConstants.TOKEN_BRACKET_OPEN: RegEx.create_from_string("^\\["), - DMConstants.TOKEN_BRACKET_CLOSE: RegEx.create_from_string("^\\]"), - DMConstants.TOKEN_BRACE_OPEN: RegEx.create_from_string("^\\{"), - DMConstants.TOKEN_BRACE_CLOSE: RegEx.create_from_string("^\\}"), - DMConstants.TOKEN_COLON: RegEx.create_from_string("^:"), - DMConstants.TOKEN_COMPARISON: RegEx.create_from_string("^(==|<=|>=|<|>|!=|in )"), - DMConstants.TOKEN_ASSIGNMENT: RegEx.create_from_string("^(\\+=|\\-=|\\*=|/=|=)"), - DMConstants.TOKEN_NUMBER: RegEx.create_from_string("^\\-?\\d+(\\.\\d+)?"), - DMConstants.TOKEN_OPERATOR: RegEx.create_from_string("^(\\+|\\-|\\*|/|%)"), - DMConstants.TOKEN_COMMA: RegEx.create_from_string("^,"), - DMConstants.TOKEN_DOT: RegEx.create_from_string("^\\."), - DMConstants.TOKEN_STRING: RegEx.create_from_string("^&?(\".*?\"|\'.*?\')"), - DMConstants.TOKEN_NOT: RegEx.create_from_string("^(not( |$)|!)"), - DMConstants.TOKEN_AND_OR: RegEx.create_from_string("^(and|or|&&|\\|\\|)( |$)"), - DMConstants.TOKEN_VARIABLE: RegEx.create_from_string("^[a-zA-Z_\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}][a-zA-Z_0-9\\p{Emoji_Presentation}\\p{Han}\\p{Katakana}\\p{Hiragana}\\p{Cyrillic}]*"), - DMConstants.TOKEN_COMMENT: RegEx.create_from_string("^#.*"), - DMConstants.TOKEN_CONDITION: RegEx.create_from_string("^(if|elif|else)"), - DMConstants.TOKEN_BOOL: RegEx.create_from_string("^(true|false)") -} diff --git a/addons/dialogue_manager/compiler/compiler_regex.gd.uid b/addons/dialogue_manager/compiler/compiler_regex.gd.uid deleted file mode 100644 index bd969df..0000000 --- a/addons/dialogue_manager/compiler/compiler_regex.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d3tvcrnicjibp diff --git a/addons/dialogue_manager/compiler/compiler_result.gd b/addons/dialogue_manager/compiler/compiler_result.gd deleted file mode 100644 index acbf60f..0000000 --- a/addons/dialogue_manager/compiler/compiler_result.gd +++ /dev/null @@ -1,27 +0,0 @@ -## The result of using the [DMCompiler] to compile some dialogue. -class_name DMCompilerResult extends RefCounted - - -## Any paths that were imported into the compiled dialogue file. -var imported_paths: PackedStringArray = [] - -## Any "using" directives. -var using_states: PackedStringArray = [] - -## All titles in the file and the line they point to. -var titles: Dictionary = {} - -## The first title in the file. -var first_title: String = "" - -## All character names. -var character_names: PackedStringArray = [] - -## Any compilation errors. -var errors: Array[Dictionary] = [] - -## A map of all compiled lines. -var lines: Dictionary = {} - -## The raw dialogue text. -var raw_text: String = "" diff --git a/addons/dialogue_manager/compiler/compiler_result.gd.uid b/addons/dialogue_manager/compiler/compiler_result.gd.uid deleted file mode 100644 index f1f76fd..0000000 --- a/addons/dialogue_manager/compiler/compiler_result.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dmk74tknimqvg diff --git a/addons/dialogue_manager/compiler/expression_parser.gd b/addons/dialogue_manager/compiler/expression_parser.gd deleted file mode 100644 index 384340f..0000000 --- a/addons/dialogue_manager/compiler/expression_parser.gd +++ /dev/null @@ -1,497 +0,0 @@ -## A class for parsing a condition/mutation expression for use with the [DMCompiler]. -class_name DMExpressionParser extends RefCounted - - -# Reference to the common [RegEx] that the parser needs. -var regex: DMCompilerRegEx = DMCompilerRegEx.new() - - -## Break a string down into an expression. -func tokenise(text: String, line_type: String, index: int) -> Array: - var tokens: Array[Dictionary] = [] - var limit: int = 0 - while text.strip_edges() != "" and limit < 1000: - limit += 1 - var found = _find_match(text) - if found.size() > 0: - tokens.append({ - index = index, - type = found.type, - value = found.value - }) - index += found.value.length() - text = found.remaining_text - elif text.begins_with(" "): - index += 1 - text = text.substr(1) - else: - return _build_token_tree_error(DMConstants.ERR_INVALID_EXPRESSION, index) - - return _build_token_tree(tokens, line_type, "")[0] - - -## Extract any expressions from some text -func extract_replacements(text: String, index: int) -> Array[Dictionary]: - var founds: Array[RegExMatch] = regex.REPLACEMENTS_REGEX.search_all(text) - - if founds == null or founds.size() == 0: - return [] - - var replacements: Array[Dictionary] = [] - for found in founds: - var replacement: Dictionary = {} - var value_in_text: String = found.strings[0].substr(0, found.strings[0].length() - 2).substr(2) - - # If there are closing curlie hard-up against the end of a {{...}} block then check for further - # curlies just outside of the block. - var text_suffix: String = text.substr(found.get_end(0)) - var expression_suffix: String = "" - while text_suffix.begins_with("}"): - expression_suffix += "}" - text_suffix = text_suffix.substr(1) - value_in_text += expression_suffix - - var expression: Array = tokenise(value_in_text, DMConstants.TYPE_DIALOGUE, index + found.get_start(1)) - if expression.size() == 0: - replacement = { - index = index + found.get_start(1), - error = DMConstants.ERR_INCOMPLETE_EXPRESSION - } - elif expression[0].type == DMConstants.TYPE_ERROR: - replacement = { - index = expression[0].index, - error = expression[0].value - } - else: - replacement = { - value_in_text = "{{%s}}" % value_in_text, - expression = expression - } - replacements.append(replacement) - - return replacements - - -#region Helpers - - -# Create a token that represents an error. -func _build_token_tree_error(error: int, index: int) -> Array: - return [{ type = DMConstants.TOKEN_ERROR, value = error, index = index }] - - -# Convert a list of tokens into an abstract syntax tree. -func _build_token_tree(tokens: Array[Dictionary], line_type: String, expected_close_token: String) -> Array: - var tree: Array[Dictionary] = [] - var limit = 0 - while tokens.size() > 0 and limit < 1000: - limit += 1 - var token = tokens.pop_front() - - var error = _check_next_token(token, tokens, line_type, expected_close_token) - if error != OK: - var error_token: Dictionary = tokens[1] if tokens.size() > 1 else token - return [_build_token_tree_error(error, error_token.index), tokens] - - match token.type: - DMConstants.TOKEN_FUNCTION: - var sub_tree = _build_token_tree(tokens, line_type, DMConstants.TOKEN_PARENS_CLOSE) - - if sub_tree[0].size() > 0 and sub_tree[0][0].type == DMConstants.TOKEN_ERROR: - return [_build_token_tree_error(sub_tree[0][0].value, sub_tree[0][0].index), tokens] - - tree.append({ - type = DMConstants.TOKEN_FUNCTION, - # Consume the trailing "(" - function = token.value.substr(0, token.value.length() - 1), - value = _tokens_to_list(sub_tree[0]), - i = token.index - }) - tokens = sub_tree[1] - - DMConstants.TOKEN_DICTIONARY_REFERENCE: - var sub_tree = _build_token_tree(tokens, line_type, DMConstants.TOKEN_BRACKET_CLOSE) - - if sub_tree[0].size() > 0 and sub_tree[0][0].type == DMConstants.TOKEN_ERROR: - return [_build_token_tree_error(sub_tree[0][0].value, sub_tree[0][0].index), tokens] - - var args = _tokens_to_list(sub_tree[0]) - if args.size() != 1: - return [_build_token_tree_error(DMConstants.ERR_INVALID_INDEX, token.index), tokens] - - tree.append({ - type = DMConstants.TOKEN_DICTIONARY_REFERENCE, - # Consume the trailing "[" - variable = token.value.substr(0, token.value.length() - 1), - value = args[0], - i = token.index - }) - tokens = sub_tree[1] - - DMConstants.TOKEN_BRACE_OPEN: - var sub_tree = _build_token_tree(tokens, line_type, DMConstants.TOKEN_BRACE_CLOSE) - - if sub_tree[0].size() > 0 and sub_tree[0][0].type == DMConstants.TOKEN_ERROR: - return [_build_token_tree_error(sub_tree[0][0].value, sub_tree[0][0].index), tokens] - - var t = sub_tree[0] - for i in range(0, t.size() - 2): - # Convert Lua style dictionaries to string keys - if t[i].type == DMConstants.TOKEN_VARIABLE and t[i+1].type == DMConstants.TOKEN_ASSIGNMENT: - t[i].type = DMConstants.TOKEN_STRING - t[i+1].type = DMConstants.TOKEN_COLON - t[i+1].erase("value") - - tree.append({ - type = DMConstants.TOKEN_DICTIONARY, - value = _tokens_to_dictionary(sub_tree[0]), - i = token.index - }) - - tokens = sub_tree[1] - - DMConstants.TOKEN_BRACKET_OPEN: - var sub_tree = _build_token_tree(tokens, line_type, DMConstants.TOKEN_BRACKET_CLOSE) - - if sub_tree[0].size() > 0 and sub_tree[0][0].type == DMConstants.TOKEN_ERROR: - return [_build_token_tree_error(sub_tree[0][0].value, sub_tree[0][0].index), tokens] - - var type = DMConstants.TOKEN_ARRAY - var value = _tokens_to_list(sub_tree[0]) - - # See if this is referencing a nested dictionary value - if tree.size() > 0: - var previous_token = tree[tree.size() - 1] - if previous_token.type in [DMConstants.TOKEN_DICTIONARY_REFERENCE, DMConstants.TOKEN_DICTIONARY_NESTED_REFERENCE]: - type = DMConstants.TOKEN_DICTIONARY_NESTED_REFERENCE - value = value[0] - - tree.append({ - type = type, - value = value, - i = token.index - }) - tokens = sub_tree[1] - - DMConstants.TOKEN_PARENS_OPEN: - var sub_tree = _build_token_tree(tokens, line_type, DMConstants.TOKEN_PARENS_CLOSE) - - if sub_tree[0].size() > 0 and sub_tree[0][0].type == DMConstants.TOKEN_ERROR: - return [_build_token_tree_error(sub_tree[0][0].value, sub_tree[0][0].index), tokens] - - tree.append({ - type = DMConstants.TOKEN_GROUP, - value = sub_tree[0], - i = token.index - }) - tokens = sub_tree[1] - - DMConstants.TOKEN_PARENS_CLOSE, \ - DMConstants.TOKEN_BRACE_CLOSE, \ - DMConstants.TOKEN_BRACKET_CLOSE: - if token.type != expected_close_token: - return [_build_token_tree_error(DMConstants.ERR_UNEXPECTED_CLOSING_BRACKET, token.index), tokens] - - tree.append({ - type = token.type, - i = token.index - }) - - return [tree, tokens] - - DMConstants.TOKEN_NOT: - # Double nots negate each other - if tokens.size() > 0 and tokens.front().type == DMConstants.TOKEN_NOT: - tokens.pop_front() - else: - tree.append({ - type = token.type, - i = token.index - }) - - DMConstants.TOKEN_COMMA, \ - DMConstants.TOKEN_COLON, \ - DMConstants.TOKEN_DOT: - tree.append({ - type = token.type, - i = token.index - }) - - DMConstants.TOKEN_COMPARISON, \ - DMConstants.TOKEN_ASSIGNMENT, \ - DMConstants.TOKEN_OPERATOR, \ - DMConstants.TOKEN_AND_OR, \ - DMConstants.TOKEN_VARIABLE: - var value = token.value.strip_edges() - if value == "&&": - value = "and" - elif value == "||": - value = "or" - tree.append({ - type = token.type, - value = value, - i = token.index - }) - - DMConstants.TOKEN_STRING: - if token.value.begins_with("&"): - tree.append({ - type = token.type, - value = StringName(token.value.substr(2, token.value.length() - 3)), - i = token.index - }) - else: - tree.append({ - type = token.type, - value = token.value.substr(1, token.value.length() - 2), - i = token.index - }) - - DMConstants.TOKEN_CONDITION: - return [_build_token_tree_error(DMConstants.ERR_UNEXPECTED_CONDITION, token.index), token] - - DMConstants.TOKEN_BOOL: - tree.append({ - type = token.type, - value = token.value.to_lower() == "true", - i = token.index - }) - - DMConstants.TOKEN_NUMBER: - var value = token.value.to_float() if "." in token.value else token.value.to_int() - # If previous token is a number and this one is a negative number then - # inject a minus operator token in between them. - if tree.size() > 0 and token.value.begins_with("-") and tree[tree.size() - 1].type == DMConstants.TOKEN_NUMBER: - tree.append(({ - type = DMConstants.TOKEN_OPERATOR, - value = "-", - i = token.index - })) - tree.append({ - type = token.type, - value = -1 * value, - i = token.index - }) - else: - tree.append({ - type = token.type, - value = value, - i = token.index - }) - - if expected_close_token != "": - var index: int = tokens[0].index if tokens.size() > 0 else 0 - return [_build_token_tree_error(DMConstants.ERR_MISSING_CLOSING_BRACKET, index), tokens] - - return [tree, tokens] - - -# Check the next token to see if it is valid to follow this one. -func _check_next_token(token: Dictionary, next_tokens: Array[Dictionary], line_type: String, expected_close_token: String) -> Error: - var next_token: Dictionary = { type = null } - if next_tokens.size() > 0: - next_token = next_tokens.front() - - # Guard for assigning in a condition. If the assignment token isn't inside a Lua dictionary - # then it's an unexpected assignment in a condition line. - if token.type == DMConstants.TOKEN_ASSIGNMENT and line_type == DMConstants.TYPE_CONDITION and not next_tokens.any(func(t): return t.type == expected_close_token): - return DMConstants.ERR_UNEXPECTED_ASSIGNMENT - - # Special case for a negative number after this one - if token.type == DMConstants.TOKEN_NUMBER and next_token.type == DMConstants.TOKEN_NUMBER and next_token.value.begins_with("-"): - return OK - - var expected_token_types = [] - var unexpected_token_types = [] - match token.type: - DMConstants.TOKEN_FUNCTION, \ - DMConstants.TOKEN_PARENS_OPEN: - unexpected_token_types = [ - null, - DMConstants.TOKEN_COMMA, - DMConstants.TOKEN_COLON, - DMConstants.TOKEN_COMPARISON, - DMConstants.TOKEN_ASSIGNMENT, - DMConstants.TOKEN_OPERATOR, - DMConstants.TOKEN_AND_OR, - DMConstants.TOKEN_DOT - ] - - DMConstants.TOKEN_BRACKET_CLOSE: - unexpected_token_types = [ - DMConstants.TOKEN_NOT, - DMConstants.TOKEN_BOOL, - DMConstants.TOKEN_STRING, - DMConstants.TOKEN_NUMBER, - DMConstants.TOKEN_VARIABLE - ] - - DMConstants.TOKEN_BRACE_OPEN: - expected_token_types = [ - DMConstants.TOKEN_STRING, - DMConstants.TOKEN_VARIABLE, - DMConstants.TOKEN_NUMBER, - DMConstants.TOKEN_BRACE_CLOSE - ] - - DMConstants.TOKEN_PARENS_CLOSE, \ - DMConstants.TOKEN_BRACE_CLOSE: - unexpected_token_types = [ - DMConstants.TOKEN_NOT, - DMConstants.TOKEN_ASSIGNMENT, - DMConstants.TOKEN_BOOL, - DMConstants.TOKEN_STRING, - DMConstants.TOKEN_NUMBER, - DMConstants.TOKEN_VARIABLE - ] - - DMConstants.TOKEN_COMPARISON, \ - DMConstants.TOKEN_OPERATOR, \ - DMConstants.TOKEN_COMMA, \ - DMConstants.TOKEN_DOT, \ - DMConstants.TOKEN_NOT, \ - DMConstants.TOKEN_AND_OR, \ - DMConstants.TOKEN_DICTIONARY_REFERENCE: - unexpected_token_types = [ - null, - DMConstants.TOKEN_COMMA, - DMConstants.TOKEN_COLON, - DMConstants.TOKEN_COMPARISON, - DMConstants.TOKEN_ASSIGNMENT, - DMConstants.TOKEN_OPERATOR, - DMConstants.TOKEN_AND_OR, - DMConstants.TOKEN_PARENS_CLOSE, - DMConstants.TOKEN_BRACE_CLOSE, - DMConstants.TOKEN_BRACKET_CLOSE, - DMConstants.TOKEN_DOT - ] - - DMConstants.TOKEN_COLON: - unexpected_token_types = [ - DMConstants.TOKEN_COMMA, - DMConstants.TOKEN_COLON, - DMConstants.TOKEN_COMPARISON, - DMConstants.TOKEN_ASSIGNMENT, - DMConstants.TOKEN_OPERATOR, - DMConstants.TOKEN_AND_OR, - DMConstants.TOKEN_PARENS_CLOSE, - DMConstants.TOKEN_BRACE_CLOSE, - DMConstants.TOKEN_BRACKET_CLOSE, - DMConstants.TOKEN_DOT - ] - - DMConstants.TOKEN_BOOL, \ - DMConstants.TOKEN_STRING, \ - DMConstants.TOKEN_NUMBER: - unexpected_token_types = [ - DMConstants.TOKEN_NOT, - DMConstants.TOKEN_ASSIGNMENT, - DMConstants.TOKEN_BOOL, - DMConstants.TOKEN_STRING, - DMConstants.TOKEN_NUMBER, - DMConstants.TOKEN_VARIABLE, - DMConstants.TOKEN_FUNCTION, - DMConstants.TOKEN_PARENS_OPEN, - DMConstants.TOKEN_BRACE_OPEN, - DMConstants.TOKEN_BRACKET_OPEN - ] - - DMConstants.TOKEN_VARIABLE: - unexpected_token_types = [ - DMConstants.TOKEN_NOT, - DMConstants.TOKEN_BOOL, - DMConstants.TOKEN_STRING, - DMConstants.TOKEN_NUMBER, - DMConstants.TOKEN_VARIABLE, - DMConstants.TOKEN_FUNCTION, - DMConstants.TOKEN_PARENS_OPEN, - DMConstants.TOKEN_BRACE_OPEN, - DMConstants.TOKEN_BRACKET_OPEN - ] - - if (expected_token_types.size() > 0 and not next_token.type in expected_token_types or unexpected_token_types.size() > 0 and next_token.type in unexpected_token_types): - match next_token.type: - null: - return DMConstants.ERR_UNEXPECTED_END_OF_EXPRESSION - - DMConstants.TOKEN_FUNCTION: - return DMConstants.ERR_UNEXPECTED_FUNCTION - - DMConstants.TOKEN_PARENS_OPEN, \ - DMConstants.TOKEN_PARENS_CLOSE: - return DMConstants.ERR_UNEXPECTED_BRACKET - - DMConstants.TOKEN_COMPARISON, \ - DMConstants.TOKEN_ASSIGNMENT, \ - DMConstants.TOKEN_OPERATOR, \ - DMConstants.TOKEN_NOT, \ - DMConstants.TOKEN_AND_OR: - return DMConstants.ERR_UNEXPECTED_OPERATOR - - DMConstants.TOKEN_COMMA: - return DMConstants.ERR_UNEXPECTED_COMMA - DMConstants.TOKEN_COLON: - return DMConstants.ERR_UNEXPECTED_COLON - DMConstants.TOKEN_DOT: - return DMConstants.ERR_UNEXPECTED_DOT - - DMConstants.TOKEN_BOOL: - return DMConstants.ERR_UNEXPECTED_BOOLEAN - DMConstants.TOKEN_STRING: - return DMConstants.ERR_UNEXPECTED_STRING - DMConstants.TOKEN_NUMBER: - return DMConstants.ERR_UNEXPECTED_NUMBER - DMConstants.TOKEN_VARIABLE: - return DMConstants.ERR_UNEXPECTED_VARIABLE - - return DMConstants.ERR_INVALID_EXPRESSION - - return OK - - -# Convert a series of comma separated tokens to an [Array]. -func _tokens_to_list(tokens: Array[Dictionary]) -> Array[Array]: - var list: Array[Array] = [] - var current_item: Array[Dictionary] = [] - for token in tokens: - if token.type == DMConstants.TOKEN_COMMA: - list.append(current_item) - current_item = [] - else: - current_item.append(token) - - if current_item.size() > 0: - list.append(current_item) - - return list - - -# Convert a series of key/value tokens into a [Dictionary] -func _tokens_to_dictionary(tokens: Array[Dictionary]) -> Dictionary: - var dictionary = {} - for i in range(0, tokens.size()): - if tokens[i].type == DMConstants.TOKEN_COLON: - if tokens.size() == i + 2: - dictionary[tokens[i - 1]] = tokens[i + 1] - else: - dictionary[tokens[i - 1]] = { type = DMConstants.TOKEN_GROUP, value = tokens.slice(i + 1), i = tokens[0].i } - - return dictionary - - -# Work out what the next token is from a string. -func _find_match(input: String) -> Dictionary: - for key in regex.TOKEN_DEFINITIONS.keys(): - var regex = regex.TOKEN_DEFINITIONS.get(key) - var found = regex.search(input) - if found: - return { - type = key, - remaining_text = input.substr(found.strings[0].length()), - value = found.strings[0] - } - - return {} - - -#endregion diff --git a/addons/dialogue_manager/compiler/expression_parser.gd.uid b/addons/dialogue_manager/compiler/expression_parser.gd.uid deleted file mode 100644 index 0793701..0000000 --- a/addons/dialogue_manager/compiler/expression_parser.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dbi4hbar8ubwu diff --git a/addons/dialogue_manager/compiler/resolved_goto_data.gd b/addons/dialogue_manager/compiler/resolved_goto_data.gd deleted file mode 100644 index 16bca6f..0000000 --- a/addons/dialogue_manager/compiler/resolved_goto_data.gd +++ /dev/null @@ -1,68 +0,0 @@ -## Data associated with a dialogue jump/goto line. -class_name DMResolvedGotoData extends RefCounted - - -## The title that was specified -var title: String = "" -## The target line's ID -var next_id: String = "" -## An expression to determine the target line at runtime. -var expression: Array[Dictionary] = [] -## The given line text with the jump syntax removed. -var text_without_goto: String = "" -## Whether this is a jump-and-return style jump. -var is_snippet: bool = false -## A parse error if there was one. -var error: int -## The index in the string where -var index: int = 0 - -# An instance of the compiler [RegEx] list. -var regex: DMCompilerRegEx = DMCompilerRegEx.new() - - -func _init(text: String, titles: Dictionary) -> void: - if not "=> " in text and not "=>< " in text: return - - if "=> " in text: - text_without_goto = text.substr(0, text.find("=> ")).strip_edges() - elif "=>< " in text: - is_snippet = true - text_without_goto = text.substr(0, text.find("=>< ")).strip_edges() - - var found: RegExMatch = regex.GOTO_REGEX.search(text) - if found == null: - return - - title = found.strings[found.names.goto].strip_edges() - index = found.get_start(0) - - if title == "": - error = DMConstants.ERR_UNKNOWN_TITLE - return - - # "=> END!" means end the conversation, ignoring any "=><" chains. - if title == "END!": - next_id = DMConstants.ID_END_CONVERSATION - - # "=> END" means end the current title (and go back to the previous one if there is one - # in the stack) - elif title == "END": - next_id = DMConstants.ID_END - - elif titles.has(title): - next_id = titles.get(title) - elif title.begins_with("{{"): - var expression_parser: DMExpressionParser = DMExpressionParser.new() - var title_expression: Array[Dictionary] = expression_parser.extract_replacements(title, 0) - if title_expression[0].has("error"): - error = title_expression[0].error - else: - expression = title_expression[0].expression - else: - next_id = title - error = DMConstants.ERR_UNKNOWN_TITLE - - -func _to_string() -> String: - return "%s =>%s %s (%s)" % [text_without_goto, "<" if is_snippet else "", title, next_id] diff --git a/addons/dialogue_manager/compiler/resolved_goto_data.gd.uid b/addons/dialogue_manager/compiler/resolved_goto_data.gd.uid deleted file mode 100644 index cb05e08..0000000 --- a/addons/dialogue_manager/compiler/resolved_goto_data.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://llhl5pt47eoq diff --git a/addons/dialogue_manager/compiler/resolved_line_data.gd b/addons/dialogue_manager/compiler/resolved_line_data.gd deleted file mode 100644 index 1d1a716..0000000 --- a/addons/dialogue_manager/compiler/resolved_line_data.gd +++ /dev/null @@ -1,167 +0,0 @@ -## Any data associated with inline dialogue BBCodes. -class_name DMResolvedLineData extends RefCounted - -## The line's text -var text: String = "" -## A map of pauses against where they are found in the text. -var pauses: Dictionary = {} -## A map of speed changes against where they are found in the text. -var speeds: Dictionary = {} -## A list of any mutations to run and where they are found in the text. -var mutations: Array[Array] = [] -## A duration reference for the line. Represented as "auto" or a stringified number. -var time: String = "" - - -func _init(line: String) -> void: - text = line - pauses = {} - speeds = {} - mutations = [] - time = "" - - var bbcodes: Array = [] - - # Remove any escaped brackets (ie. "\[") - var escaped_open_brackets: PackedInt32Array = [] - var escaped_close_brackets: PackedInt32Array = [] - for i in range(0, text.length() - 1): - if text.substr(i, 2) == "\\[": - text = text.substr(0, i) + "!" + text.substr(i + 2) - escaped_open_brackets.append(i) - elif text.substr(i, 2) == "\\]": - text = text.substr(0, i) + "!" + text.substr(i + 2) - escaped_close_brackets.append(i) - - # Extract all of the BB codes so that we know the actual text (we could do this easier with - # a RichTextLabel but then we'd need to await idle_frame which is annoying) - var bbcode_positions = find_bbcode_positions_in_string(text) - var accumulaive_length_offset = 0 - for position in bbcode_positions: - # Ignore our own markers - if position.code in ["wait", "speed", "/speed", "do", "do!", "set", "next", "if", "else", "/if"]: - continue - - bbcodes.append({ - bbcode = position.bbcode, - start = position.start, - offset_start = position.start - accumulaive_length_offset - }) - accumulaive_length_offset += position.bbcode.length() - - for bb in bbcodes: - text = text.substr(0, bb.offset_start) + text.substr(bb.offset_start + bb.bbcode.length()) - - # Now find any dialogue markers - var next_bbcode_position = find_bbcode_positions_in_string(text, false) - var limit = 0 - while next_bbcode_position.size() > 0 and limit < 1000: - limit += 1 - - var bbcode = next_bbcode_position[0] - - var index = bbcode.start - var code = bbcode.code - var raw_args = bbcode.raw_args - var args = {} - if code in ["do", "do!", "set"]: - var compilation: DMCompilation = DMCompilation.new() - args["value"] = compilation.extract_mutation("%s %s" % [code, raw_args]) - else: - # Could be something like: - # "=1.0" - # " rate=20 level=10" - if raw_args and raw_args[0] == "=": - raw_args = "value" + raw_args - for pair in raw_args.strip_edges().split(" "): - if "=" in pair: - var bits = pair.split("=") - args[bits[0]] = bits[1] - - match code: - "wait": - if pauses.has(index): - pauses[index] += args.get("value").to_float() - else: - pauses[index] = args.get("value").to_float() - "speed": - speeds[index] = args.get("value").to_float() - "/speed": - speeds[index] = 1.0 - "do", "do!", "set": - mutations.append([index, args.get("value")]) - "next": - time = args.get("value") if args.has("value") else "0" - - # Find any BB codes that are after this index and remove the length from their start - var length = bbcode.bbcode.length() - for bb in bbcodes: - if bb.offset_start > bbcode.start: - bb.offset_start -= length - bb.start -= length - - # Find any escaped brackets after this that need moving - for i in range(0, escaped_open_brackets.size()): - if escaped_open_brackets[i] > bbcode.start: - escaped_open_brackets[i] -= length - for i in range(0, escaped_close_brackets.size()): - if escaped_close_brackets[i] > bbcode.start: - escaped_close_brackets[i] -= length - - text = text.substr(0, index) + text.substr(index + length) - next_bbcode_position = find_bbcode_positions_in_string(text, false) - - # Put the BB Codes back in - for bb in bbcodes: - text = text.insert(bb.start, bb.bbcode) - - # Put the escaped brackets back in - for index in escaped_open_brackets: - text = text.left(index) + "[" + text.right(text.length() - index - 1) - for index in escaped_close_brackets: - text = text.left(index) + "]" + text.right(text.length() - index - 1) - - -func find_bbcode_positions_in_string(string: String, find_all: bool = true, include_conditions: bool = false) -> Array[Dictionary]: - if not "[" in string: return [] - - var positions: Array[Dictionary] = [] - - var open_brace_count: int = 0 - var start: int = 0 - var bbcode: String = "" - var code: String = "" - var is_finished_code: bool = false - for i in range(0, string.length()): - if string[i] == "[": - if open_brace_count == 0: - start = i - bbcode = "" - code = "" - is_finished_code = false - open_brace_count += 1 - - else: - if not is_finished_code and (string[i].to_upper() != string[i] or string[i] == "/" or string[i] == "!"): - code += string[i] - else: - is_finished_code = true - - if open_brace_count > 0: - bbcode += string[i] - - if string[i] == "]": - open_brace_count -= 1 - if open_brace_count == 0 and (include_conditions or not code in ["if", "else", "/if"]): - positions.append({ - bbcode = bbcode, - code = code, - start = start, - end = i, - raw_args = bbcode.substr(code.length() + 1, bbcode.length() - code.length() - 2).strip_edges() - }) - - if not find_all: - return positions - - return positions diff --git a/addons/dialogue_manager/compiler/resolved_line_data.gd.uid b/addons/dialogue_manager/compiler/resolved_line_data.gd.uid deleted file mode 100644 index bbea7d2..0000000 --- a/addons/dialogue_manager/compiler/resolved_line_data.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://0k6q8kukq0qa diff --git a/addons/dialogue_manager/compiler/resolved_tag_data.gd b/addons/dialogue_manager/compiler/resolved_tag_data.gd deleted file mode 100644 index e926ada..0000000 --- a/addons/dialogue_manager/compiler/resolved_tag_data.gd +++ /dev/null @@ -1,26 +0,0 @@ -## Tag data associated with a line of dialogue. -class_name DMResolvedTagData extends RefCounted - - -## The list of tags. -var tags: PackedStringArray = [] -## The line with any tag syntax removed. -var text_without_tags: String = "" - -# An instance of the compiler [RegEx]. -var regex: DMCompilerRegEx = DMCompilerRegEx.new() - - -func _init(text: String) -> void: - var resolved_tags: PackedStringArray = [] - var tag_matches: Array[RegExMatch] = regex.TAGS_REGEX.search_all(text) - for tag_match in tag_matches: - text = text.replace(tag_match.get_string(), "") - var tags = tag_match.get_string().replace("[#", "").replace("]", "").replace(", ", ",").split(",") - for tag in tags: - tag = tag.replace("#", "") - if not tag in resolved_tags: - resolved_tags.append(tag) - - tags = resolved_tags - text_without_tags = text diff --git a/addons/dialogue_manager/compiler/resolved_tag_data.gd.uid b/addons/dialogue_manager/compiler/resolved_tag_data.gd.uid deleted file mode 100644 index 98c6f51..0000000 --- a/addons/dialogue_manager/compiler/resolved_tag_data.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cqai3ikuilqfq diff --git a/addons/dialogue_manager/compiler/tree_line.gd b/addons/dialogue_manager/compiler/tree_line.gd deleted file mode 100644 index 381e8f6..0000000 --- a/addons/dialogue_manager/compiler/tree_line.gd +++ /dev/null @@ -1,42 +0,0 @@ -## An intermediate representation of a dialogue line before it gets compiled. -class_name DMTreeLine extends RefCounted - - -## The line number where this dialogue was found (after imported files have had their content imported). -var line_number: int = 0 -## The parent [DMTreeLine] of this line -var parent: DMTreeLine -## The ID of this line. -var id: String -## The type of this line (as a [String] defined in [DMConstants]. -var type: String = "" -## Is this line part of a randomised group? -var is_random: bool = false -## The indent count for this line. -var indent: int = 0 -## The text of this line. -var text: String = "" -## The child [DMTreeLine]s of this line. -var children: Array[DMTreeLine] = [] -## Any doc comments attached to this line. -var notes: String = "" - - -func _init(initial_id: String) -> void: - id = initial_id - - -func _to_string() -> String: - var tabs = [] - tabs.resize(indent) - tabs.fill("\t") - tabs = "".join(tabs) - - return tabs.join([tabs + "{\n", - "\tid: %s\n" % [id], - "\ttype: %s\n" % [type], - "\tis_random: %s\n" % ["true" if is_random else "false"], - "\ttext: %s\n" % [text], - "\tnotes: %s\n" % [notes], - "\tchildren: []\n" if children.size() == 0 else "\tchildren: [\n" + ",\n".join(children.map(func(child): return str(child))) + "]\n", - "}"]) diff --git a/addons/dialogue_manager/compiler/tree_line.gd.uid b/addons/dialogue_manager/compiler/tree_line.gd.uid deleted file mode 100644 index fe1db3a..0000000 --- a/addons/dialogue_manager/compiler/tree_line.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dsu4i84dpif14 diff --git a/addons/dialogue_manager/components/code_edit.gd b/addons/dialogue_manager/components/code_edit.gd deleted file mode 100644 index e180af4..0000000 --- a/addons/dialogue_manager/components/code_edit.gd +++ /dev/null @@ -1,461 +0,0 @@ -@tool -class_name DMCodeEdit extends CodeEdit - - -signal active_title_change(title: String) -signal error_clicked(line_number: int) -signal external_file_requested(path: String, title: String) - - -# A link back to the owner `MainView` -var main_view - -# Theme overrides for syntax highlighting, etc -var theme_overrides: Dictionary: - set(value): - theme_overrides = value - - syntax_highlighter = DMSyntaxHighlighter.new() - - # General UI - add_theme_color_override("font_color", theme_overrides.text_color) - add_theme_color_override("background_color", theme_overrides.background_color) - add_theme_color_override("current_line_color", theme_overrides.current_line_color) - add_theme_font_override("font", get_theme_font("source", "EditorFonts")) - add_theme_font_size_override("font_size", theme_overrides.font_size * theme_overrides.scale) - font_size = round(theme_overrides.font_size) - get: - return theme_overrides - -# Any parse errors -var errors: Array: - set(next_errors): - errors = next_errors - for i in range(0, get_line_count()): - var is_error: bool = false - for error in errors: - if error.line_number == i: - is_error = true - mark_line_as_error(i, is_error) - _on_code_edit_caret_changed() - get: - return errors - -# The last selection (if there was one) so we can remember it for refocusing -var last_selected_text: String - -var font_size: int: - set(value): - font_size = value - add_theme_font_size_override("font_size", font_size * theme_overrides.scale) - get: - return font_size - -var WEIGHTED_RANDOM_PREFIX: RegEx = RegEx.create_from_string("^\\%[\\d.]+\\s") - - -func _ready() -> void: - # Add error gutter - add_gutter(0) - set_gutter_type(0, TextEdit.GUTTER_TYPE_ICON) - - # Add comment delimiter - if not has_comment_delimiter("#"): - add_comment_delimiter("#", "", true) - - syntax_highlighter = DMSyntaxHighlighter.new() - - -func _gui_input(event: InputEvent) -> void: - # Handle shortcuts that come from the editor - if event is InputEventKey and event.is_pressed(): - var shortcut: String = Engine.get_meta("DialogueManagerPlugin").get_editor_shortcut(event) - match shortcut: - "toggle_comment": - toggle_comment() - get_viewport().set_input_as_handled() - "delete_line": - delete_current_line() - get_viewport().set_input_as_handled() - "move_up": - move_line(-1) - get_viewport().set_input_as_handled() - "move_down": - move_line(1) - get_viewport().set_input_as_handled() - "text_size_increase": - self.font_size += 1 - get_viewport().set_input_as_handled() - "text_size_decrease": - self.font_size -= 1 - get_viewport().set_input_as_handled() - "text_size_reset": - self.font_size = theme_overrides.font_size - get_viewport().set_input_as_handled() - - elif event is InputEventMouse: - match event.as_text(): - "Ctrl+Mouse Wheel Up", "Command+Mouse Wheel Up": - self.font_size += 1 - get_viewport().set_input_as_handled() - "Ctrl+Mouse Wheel Down", "Command+Mouse Wheel Down": - self.font_size -= 1 - get_viewport().set_input_as_handled() - - -func _can_drop_data(at_position: Vector2, data) -> bool: - if typeof(data) != TYPE_DICTIONARY: return false - if data.type != "files": return false - - var files: PackedStringArray = Array(data.files) - return files.size() > 0 - - -func _drop_data(at_position: Vector2, data) -> void: - var replace_regex: RegEx = RegEx.create_from_string("[^a-zA-Z_0-9]+") - - var files: PackedStringArray = Array(data.files) - for file in files: - # Don't import the file into itself - if file == main_view.current_file_path: continue - - if file.get_extension() == "dialogue": - var path = file.replace("res://", "").replace(".dialogue", "") - # Find the first non-import line in the file to add our import - var lines = text.split("\n") - for i in range(0, lines.size()): - if not lines[i].begins_with("import "): - insert_line_at(i, "import \"%s\" as %s\n" % [file, replace_regex.sub(path, "_", true)]) - set_caret_line(i) - break - else: - var cursor: Vector2 = get_line_column_at_pos(at_position) - if cursor.x > -1 and cursor.y > -1: - set_cursor(cursor) - remove_secondary_carets() - if has_method("insert_text"): - call("insert_text", "\"%s\"" % file, cursor.y, cursor.x) - else: - call("insert_text_at_cursor", "\"%s\"" % file) - grab_focus() - - -func _request_code_completion(force: bool) -> void: - var cursor: Vector2 = get_cursor() - var current_line: String = get_line(cursor.y) - - if ("=> " in current_line or "=>< " in current_line) and (cursor.x > current_line.find("=>")): - var prompt: String = current_line.split("=>")[1] - if prompt.begins_with("< "): - prompt = prompt.substr(2) - else: - prompt = prompt.substr(1) - - if "=> " in current_line: - if matches_prompt(prompt, "end"): - add_code_completion_option(CodeEdit.KIND_CLASS, "END", "END".substr(prompt.length()), theme_overrides.text_color, get_theme_icon("Stop", "EditorIcons")) - if matches_prompt(prompt, "end!"): - add_code_completion_option(CodeEdit.KIND_CLASS, "END!", "END!".substr(prompt.length()), theme_overrides.text_color, get_theme_icon("Stop", "EditorIcons")) - - # Get all titles, including those in imports - for title: String in DMCompiler.get_titles_in_text(text, main_view.current_file_path): - # Ignore any imported titles that aren't resolved to human readable. - if title.to_int() > 0: - continue - - elif "/" in title: - var bits = title.split("/") - if matches_prompt(prompt, bits[0]) or matches_prompt(prompt, bits[1]): - add_code_completion_option(CodeEdit.KIND_CLASS, title, title.substr(prompt.length()), theme_overrides.text_color, get_theme_icon("CombineLines", "EditorIcons")) - elif matches_prompt(prompt, title): - add_code_completion_option(CodeEdit.KIND_CLASS, title, title.substr(prompt.length()), theme_overrides.text_color, get_theme_icon("ArrowRight", "EditorIcons")) - update_code_completion_options(true) - return - - var name_so_far: String = WEIGHTED_RANDOM_PREFIX.sub(current_line.strip_edges(), "") - if name_so_far != "" and name_so_far[0].to_upper() == name_so_far[0]: - # Only show names starting with that character - var names: PackedStringArray = get_character_names(name_so_far) - if names.size() > 0: - for name in names: - add_code_completion_option(CodeEdit.KIND_CLASS, name + ": ", name.substr(name_so_far.length()) + ": ", theme_overrides.text_color, get_theme_icon("Sprite2D", "EditorIcons")) - update_code_completion_options(true) - else: - cancel_code_completion() - - -func _filter_code_completion_candidates(candidates: Array) -> Array: - # Not sure why but if this method isn't overridden then all completions are wrapped in quotes. - return candidates - - -func _confirm_code_completion(replace: bool) -> void: - var completion = get_code_completion_option(get_code_completion_selected_index()) - begin_complex_operation() - # Delete any part of the text that we've already typed - for i in range(0, completion.display_text.length() - completion.insert_text.length()): - backspace() - # Insert the whole match - insert_text_at_caret(completion.display_text) - end_complex_operation() - - # Close the autocomplete menu on the next tick - call_deferred("cancel_code_completion") - - -### Helpers - - -# Get the current caret as a Vector2 -func get_cursor() -> Vector2: - return Vector2(get_caret_column(), get_caret_line()) - - -# Set the caret from a Vector2 -func set_cursor(from_cursor: Vector2) -> void: - set_caret_line(from_cursor.y, false) - set_caret_column(from_cursor.x, false) - - -# Check if a prompt is the start of a string without actually being that string -func matches_prompt(prompt: String, matcher: String) -> bool: - return prompt.length() < matcher.length() and matcher.to_lower().begins_with(prompt.to_lower()) - - -## Get a list of titles from the current text -func get_titles() -> PackedStringArray: - var titles = PackedStringArray([]) - var lines = text.split("\n") - for line in lines: - if line.strip_edges().begins_with("~ "): - titles.append(line.strip_edges().substr(2)) - - return titles - - -## Work out what the next title above the current line is -func check_active_title() -> void: - var line_number = get_caret_line() - var lines = text.split("\n") - # Look at each line above this one to find the next title line - for i in range(line_number, -1, -1): - if lines[i].begins_with("~ "): - active_title_change.emit(lines[i].replace("~ ", "")) - return - - active_title_change.emit("") - - -# Move the caret line to match a given title -func go_to_title(title: String) -> void: - var lines = text.split("\n") - for i in range(0, lines.size()): - if lines[i].strip_edges() == "~ " + title: - set_caret_line(i) - center_viewport_to_caret() - - -func get_character_names(beginning_with: String) -> PackedStringArray: - var names: PackedStringArray = [] - var lines = text.split("\n") - for line in lines: - if ": " in line: - var name: String = WEIGHTED_RANDOM_PREFIX.sub(line.split(": ")[0].strip_edges(), "") - if not name in names and matches_prompt(beginning_with, name): - names.append(name) - return names - - -# Mark a line as an error or not -func mark_line_as_error(line_number: int, is_error: bool) -> void: - # Lines display counting from 1 but are actually indexed from 0 - line_number -= 1 - - if line_number < 0: return - - if is_error: - set_line_background_color(line_number, theme_overrides.error_line_color) - set_line_gutter_icon(line_number, 0, get_theme_icon("StatusError", "EditorIcons")) - else: - set_line_background_color(line_number, theme_overrides.background_color) - set_line_gutter_icon(line_number, 0, null) - - -# Insert or wrap some bbcode at the caret/selection -func insert_bbcode(open_tag: String, close_tag: String = "") -> void: - if close_tag == "": - insert_text_at_caret(open_tag) - grab_focus() - else: - var selected_text = get_selected_text() - insert_text_at_caret("%s%s%s" % [open_tag, selected_text, close_tag]) - grab_focus() - set_caret_column(get_caret_column() - close_tag.length()) - -# Insert text at current caret position -# Move Caret down 1 line if not => END -func insert_text_at_cursor(text: String) -> void: - if text != "=> END": - insert_text_at_caret(text+"\n") - set_caret_line(get_caret_line()+1) - else: - insert_text_at_caret(text) - grab_focus() - - -# Toggle the selected lines as comments -func toggle_comment() -> void: - begin_complex_operation() - - var comment_delimiter: String = delimiter_comments[0] - var is_first_line: bool = true - var will_comment: bool = true - var selections: Array = [] - var line_offsets: Dictionary = {} - - for caret_index in range(0, get_caret_count()): - var from_line: int = get_caret_line(caret_index) - var from_column: int = get_caret_column(caret_index) - var to_line: int = get_caret_line(caret_index) - var to_column: int = get_caret_column(caret_index) - - if has_selection(caret_index): - from_line = get_selection_from_line(caret_index) - to_line = get_selection_to_line(caret_index) - from_column = get_selection_from_column(caret_index) - to_column = get_selection_to_column(caret_index) - - selections.append({ - from_line = from_line, - from_column = from_column, - to_line = to_line, - to_column = to_column - }) - - for line_number in range(from_line, to_line + 1): - if line_offsets.has(line_number): continue - - var line_text: String = get_line(line_number) - - # The first line determines if we are commenting or uncommentingg - if is_first_line: - is_first_line = false - will_comment = not line_text.strip_edges().begins_with(comment_delimiter) - - # Only comment/uncomment if the current line needs to - if will_comment: - set_line(line_number, comment_delimiter + line_text) - line_offsets[line_number] = 1 - elif line_text.begins_with(comment_delimiter): - set_line(line_number, line_text.substr(comment_delimiter.length())) - line_offsets[line_number] = -1 - else: - line_offsets[line_number] = 0 - - for caret_index in range(0, get_caret_count()): - var selection: Dictionary = selections[caret_index] - select( - selection.from_line, - selection.from_column + line_offsets[selection.from_line], - selection.to_line, - selection.to_column + line_offsets[selection.to_line], - caret_index - ) - set_caret_column(selection.from_column + line_offsets[selection.from_line], false, caret_index) - - end_complex_operation() - - text_set.emit() - text_changed.emit() - - -# Remove the current line -func delete_current_line() -> void: - var cursor = get_cursor() - if get_line_count() == 1: - select_all() - elif cursor.y == 0: - select(0, 0, 1, 0) - else: - select(cursor.y - 1, get_line_width(cursor.y - 1), cursor.y, get_line_width(cursor.y)) - delete_selection() - text_changed.emit() - - -# Move the selected lines up or down -func move_line(offset: int) -> void: - offset = clamp(offset, -1, 1) - - var starting_scroll := scroll_vertical - var cursor = get_cursor() - var reselect: bool = false - var from: int = cursor.y - var to: int = cursor.y - if has_selection(): - reselect = true - from = get_selection_from_line() - to = get_selection_to_line() - - var lines := text.split("\n") - - # Prevent the lines from being out of bounds - if from + offset < 0 or to + offset >= lines.size(): return - - var target_from_index = from - 1 if offset == -1 else to + 1 - var target_to_index = to if offset == -1 else from - var line_to_move = lines[target_from_index] - lines.remove_at(target_from_index) - lines.insert(target_to_index, line_to_move) - - text = "\n".join(lines) - - cursor.y += offset - set_cursor(cursor) - from += offset - to += offset - if reselect: - select(from, 0, to, get_line_width(to)) - - text_changed.emit() - scroll_vertical = starting_scroll + offset - - -### Signals - - -func _on_code_edit_symbol_validate(symbol: String) -> void: - if symbol.begins_with("res://") and symbol.ends_with(".dialogue"): - set_symbol_lookup_word_as_valid(true) - return - - for title in get_titles(): - if symbol == title: - set_symbol_lookup_word_as_valid(true) - return - set_symbol_lookup_word_as_valid(false) - - -func _on_code_edit_symbol_lookup(symbol: String, line: int, column: int) -> void: - if symbol.begins_with("res://") and symbol.ends_with(".dialogue"): - external_file_requested.emit(symbol, "") - else: - go_to_title(symbol) - - -func _on_code_edit_text_changed() -> void: - request_code_completion(true) - - -func _on_code_edit_text_set() -> void: - queue_redraw() - - -func _on_code_edit_caret_changed() -> void: - check_active_title() - last_selected_text = get_selected_text() - - -func _on_code_edit_gutter_clicked(line: int, gutter: int) -> void: - var line_errors = errors.filter(func(error): return error.line_number == line) - if line_errors.size() > 0: - error_clicked.emit(line) diff --git a/addons/dialogue_manager/components/code_edit.gd.uid b/addons/dialogue_manager/components/code_edit.gd.uid deleted file mode 100644 index ab2b9e5..0000000 --- a/addons/dialogue_manager/components/code_edit.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://djeybvlb332mp diff --git a/addons/dialogue_manager/components/code_edit.tscn b/addons/dialogue_manager/components/code_edit.tscn deleted file mode 100644 index 0c25707..0000000 --- a/addons/dialogue_manager/components/code_edit.tscn +++ /dev/null @@ -1,56 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://civ6shmka5e8u"] - -[ext_resource type="Script" uid="uid://klpiq4tk3t7a" path="res://addons/dialogue_manager/components/code_edit_syntax_highlighter.gd" id="1_58cfo"] -[ext_resource type="Script" uid="uid://djeybvlb332mp" path="res://addons/dialogue_manager/components/code_edit.gd" id="1_g324i"] - -[sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_cobxx"] -script = ExtResource("1_58cfo") - -[node name="CodeEdit" type="CodeEdit"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -text = "~ title_thing - -if this = \"that\" or 'this' -Nathan: Something -- Then [if test.thing() == 2.0] => somewhere -- Other => END! - -~ somewhere - -set has_something = true -=> END" -highlight_all_occurrences = true -highlight_current_line = true -draw_tabs = true -syntax_highlighter = SubResource("SyntaxHighlighter_cobxx") -scroll_past_end_of_file = true -minimap_draw = true -symbol_lookup_on_click = true -line_folding = true -gutters_draw_line_numbers = true -gutters_draw_fold_gutter = true -delimiter_strings = Array[String](["\" \""]) -delimiter_comments = Array[String](["#"]) -code_completion_enabled = true -code_completion_prefixes = Array[String]([">", "<"]) -indent_automatic = true -auto_brace_completion_enabled = true -auto_brace_completion_highlight_matching = true -auto_brace_completion_pairs = { -"\"": "\"", -"(": ")", -"[": "]", -"{": "}" -} -script = ExtResource("1_g324i") - -[connection signal="caret_changed" from="." to="." method="_on_code_edit_caret_changed"] -[connection signal="gutter_clicked" from="." to="." method="_on_code_edit_gutter_clicked"] -[connection signal="symbol_lookup" from="." to="." method="_on_code_edit_symbol_lookup"] -[connection signal="symbol_validate" from="." to="." method="_on_code_edit_symbol_validate"] -[connection signal="text_changed" from="." to="." method="_on_code_edit_text_changed"] -[connection signal="text_set" from="." to="." method="_on_code_edit_text_set"] diff --git a/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd b/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd deleted file mode 100644 index c6b201e..0000000 --- a/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd +++ /dev/null @@ -1,204 +0,0 @@ -@tool -class_name DMSyntaxHighlighter extends SyntaxHighlighter - - -var regex: DMCompilerRegEx = DMCompilerRegEx.new() -var compilation: DMCompilation = DMCompilation.new() -var expression_parser = DMExpressionParser.new() - -var cache: Dictionary = {} - - -func _clear_highlighting_cache() -> void: - cache.clear() - - -func _get_line_syntax_highlighting(line: int) -> Dictionary: - var colors: Dictionary = {} - var text_edit: TextEdit = get_text_edit() - var text: String = text_edit.get_line(line) - - # Prevent an error from popping up while developing - if not is_instance_valid(text_edit) or text_edit.theme_overrides.is_empty(): - return colors - - # Disable this, as well as the line at the bottom of this function to remove the cache. - if text in cache: - return cache[text] - - var theme: Dictionary = text_edit.theme_overrides - - var index: int = 0 - - match DMCompiler.get_line_type(text): - DMConstants.TYPE_COMMENT: - colors[index] = { color = theme.comments_color } - - DMConstants.TYPE_TITLE: - colors[index] = { color = theme.titles_color } - - DMConstants.TYPE_CONDITION, DMConstants.TYPE_WHILE, DMConstants.TYPE_MATCH, DMConstants.TYPE_WHEN: - colors[0] = { color = theme.conditions_color } - index = text.find(" ") - if index > -1: - var expression: Array = expression_parser.tokenise(text.substr(index), DMConstants.TYPE_CONDITION, 0) - if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR: - colors[index] = { color = theme.critical_color } - else: - _highlight_expression(expression, colors, index) - - DMConstants.TYPE_MUTATION: - colors[0] = { color = theme.mutations_color } - index = text.find(" ") - var expression: Array = expression_parser.tokenise(text.substr(index), DMConstants.TYPE_MUTATION, 0) - if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR: - colors[index] = { color = theme.critical_color } - else: - _highlight_expression(expression, colors, index) - - DMConstants.TYPE_GOTO: - if text.strip_edges().begins_with("%"): - colors[index] = { color = theme.symbols_color } - index = text.find(" ") - _highlight_goto(text, colors, index) - - DMConstants.TYPE_RANDOM: - colors[index] = { color = theme.symbols_color } - - DMConstants.TYPE_DIALOGUE, DMConstants.TYPE_RESPONSE: - if text.strip_edges().begins_with("%"): - colors[index] = { color = theme.symbols_color } - index = text.find(" ") - colors[index] = { color = theme.text_color } - - var dialogue_text: String = text.substr(index, text.find("=>")) - - # Interpolation - var replacements: Array[RegExMatch] = regex.REPLACEMENTS_REGEX.search_all(dialogue_text) - for replacement: RegExMatch in replacements: - var expression_text: String = replacement.get_string().substr(0, replacement.get_string().length() - 2).substr(2) - var expression: Array = expression_parser.tokenise(expression_text, DMConstants.TYPE_MUTATION, replacement.get_start()) - var expression_index: int = index + replacement.get_start() - colors[expression_index] = { color = theme.symbols_color } - if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR: - colors[expression_index] = { color = theme.critical_color } - else: - _highlight_expression(expression, colors, index + 2) - colors[expression_index + expression_text.length() + 2] = { color = theme.symbols_color } - colors[expression_index + expression_text.length() + 4] = { color = theme.text_color } - # Tags (and inline mutations) - var resolved_line_data: DMResolvedLineData = DMResolvedLineData.new("") - var bbcodes: Array[Dictionary] = resolved_line_data.find_bbcode_positions_in_string(dialogue_text, true, true) - for bbcode: Dictionary in bbcodes: - var tag: String = bbcode.code - var code: String = bbcode.raw_args - if code.begins_with("["): - colors[bbcode.start] = { color = theme.symbols_color } - colors[bbcode.start + 2] = { color = theme.text_color } - var pipe_cursor: int = code.find("|") - while pipe_cursor > -1: - colors[bbcode.start + pipe_cursor + 1] = { color = theme.symbols_color } - colors[bbcode.start + pipe_cursor + 2] = { color = theme.text_color } - pipe_cursor = code.find("|", pipe_cursor + 1) - colors[bbcode.end - 1] = { color = theme.symbols_color } - colors[bbcode.end + 1] = { color = theme.text_color } - else: - colors[bbcode.start] = { color = theme.symbols_color } - if tag.begins_with("do") or tag.begins_with("set") or tag.begins_with("if"): - if tag.begins_with("if"): - colors[bbcode.start + 1] = { color = theme.conditions_color } - else: - colors[bbcode.start + 1] = { color = theme.mutations_color } - var expression: Array = expression_parser.tokenise(code, DMConstants.TYPE_MUTATION, bbcode.start + bbcode.code.length()) - if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR: - colors[bbcode.start + tag.length() + 1] = { color = theme.critical_color } - else: - _highlight_expression(expression, colors, index + 2) - # else and closing if have no expression - elif tag.begins_with("else") or tag.begins_with("/if"): - colors[bbcode.start + 1] = { color = theme.conditions_color } - colors[bbcode.end] = { color = theme.symbols_color } - colors[bbcode.end + 1] = { color = theme.text_color } - # Jumps - if "=> " in text or "=>< " in text: - _highlight_goto(text, colors, index) - - # Order the dictionary keys to prevent CodeEdit from having issues - var ordered_colors: Dictionary = {} - var ordered_keys: Array = colors.keys() - ordered_keys.sort() - for key_index: int in ordered_keys: - ordered_colors[key_index] = colors[key_index] - - cache[text] = ordered_colors - return ordered_colors - - -func _highlight_expression(tokens: Array, colors: Dictionary, index: int) -> int: - var theme: Dictionary = get_text_edit().theme_overrides - var last_index: int = index - for token: Dictionary in tokens: - last_index = token.i - match token.type: - DMConstants.TOKEN_CONDITION, DMConstants.TOKEN_AND_OR: - colors[index + token.i] = { color = theme.conditions_color } - - DMConstants.TOKEN_VARIABLE: - if token.value in ["true", "false"]: - colors[index + token.i] = { color = theme.conditions_color } - else: - colors[index + token.i] = { color = theme.members_color } - - DMConstants.TOKEN_OPERATOR, DMConstants.TOKEN_COLON, DMConstants.TOKEN_COMMA, DMConstants.TOKEN_NUMBER, DMConstants.TOKEN_ASSIGNMENT: - colors[index + token.i] = { color = theme.symbols_color } - - DMConstants.TOKEN_STRING: - colors[index + token.i] = { color = theme.strings_color } - - DMConstants.TOKEN_FUNCTION: - colors[index + token.i] = { color = theme.mutations_color } - colors[index + token.i + token.function.length()] = { color = theme.symbols_color } - for parameter: Array in token.value: - last_index = _highlight_expression(parameter, colors, index) - DMConstants.TOKEN_PARENS_CLOSE: - colors[index + token.i] = { color = theme.symbols_color } - - DMConstants.TOKEN_DICTIONARY_REFERENCE: - colors[index + token.i] = { color = theme.members_color } - colors[index + token.i + token.variable.length()] = { color = theme.symbols_color } - last_index = _highlight_expression(token.value, colors, index) - DMConstants.TOKEN_ARRAY: - colors[index + token.i] = { color = theme.symbols_color } - for item: Array in token.value: - last_index = _highlight_expression(item, colors, index) - DMConstants.TOKEN_BRACKET_CLOSE: - colors[index + token.i] = { color = theme.symbols_color } - - DMConstants.TOKEN_DICTIONARY: - colors[index + token.i] = { color = theme.symbols_color } - last_index = _highlight_expression(token.value.keys() + token.value.values(), colors, index) - DMConstants.TOKEN_BRACE_CLOSE: - colors[index + token.i] = { color = theme.symbols_color } - last_index += 1 - - DMConstants.TOKEN_GROUP: - last_index = _highlight_expression(token.value, colors, index) - - return last_index - - -func _highlight_goto(text: String, colors: Dictionary, index: int) -> int: - var theme: Dictionary = get_text_edit().theme_overrides - var goto_data: DMResolvedGotoData = DMResolvedGotoData.new(text, {}) - colors[goto_data.index] = { color = theme.jumps_color } - if "{{" in text: - index = text.find("{{", goto_data.index) - var last_index: int = 0 - if goto_data.error: - colors[index + 2] = { color = theme.critical_color } - else: - last_index = _highlight_expression(goto_data.expression, colors, index) - index = text.find("}}", index + last_index) - colors[index] = { color = theme.jumps_color } - - return index diff --git a/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd.uid b/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd.uid deleted file mode 100644 index 9bad8cc..0000000 --- a/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://klpiq4tk3t7a diff --git a/addons/dialogue_manager/components/download_update_panel.gd b/addons/dialogue_manager/components/download_update_panel.gd deleted file mode 100644 index 617d308..0000000 --- a/addons/dialogue_manager/components/download_update_panel.gd +++ /dev/null @@ -1,84 +0,0 @@ -@tool -extends Control - - -signal failed() -signal updated(updated_to_version: String) - - -const DialogueConstants = preload("../constants.gd") - -const TEMP_FILE_NAME = "user://temp.zip" - - -@onready var logo: TextureRect = %Logo -@onready var label: Label = $VBox/Label -@onready var http_request: HTTPRequest = $HTTPRequest -@onready var download_button: Button = %DownloadButton - -var next_version_release: Dictionary: - set(value): - next_version_release = value - label.text = DialogueConstants.translate(&"update.is_available_for_download") % value.tag_name.substr(1) - get: - return next_version_release - - -func _ready() -> void: - $VBox/Center/DownloadButton.text = DialogueConstants.translate(&"update.download_update") - $VBox/Center2/NotesButton.text = DialogueConstants.translate(&"update.release_notes") - - -### Signals - - -func _on_download_button_pressed() -> void: - # Safeguard the actual dialogue manager repo from accidentally updating itself - if FileAccess.file_exists("res://examples/test_scenes/test_scene.gd"): - prints("You can't update the addon from within itself.") - failed.emit() - return - - http_request.request(next_version_release.zipball_url) - download_button.disabled = true - download_button.text = DialogueConstants.translate(&"update.downloading") - - -func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void: - if result != HTTPRequest.RESULT_SUCCESS: - failed.emit() - return - - # Save the downloaded zip - var zip_file: FileAccess = FileAccess.open(TEMP_FILE_NAME, FileAccess.WRITE) - zip_file.store_buffer(body) - zip_file.close() - - OS.move_to_trash(ProjectSettings.globalize_path("res://addons/dialogue_manager")) - - var zip_reader: ZIPReader = ZIPReader.new() - zip_reader.open(TEMP_FILE_NAME) - var files: PackedStringArray = zip_reader.get_files() - - var base_path = files[1] - # Remove archive folder - files.remove_at(0) - # Remove assets folder - files.remove_at(0) - - for path in files: - var new_file_path: String = path.replace(base_path, "") - if path.ends_with("/"): - DirAccess.make_dir_recursive_absolute("res://addons/%s" % new_file_path) - else: - var file: FileAccess = FileAccess.open("res://addons/%s" % new_file_path, FileAccess.WRITE) - file.store_buffer(zip_reader.read_file(path)) - - zip_reader.close() - DirAccess.remove_absolute(TEMP_FILE_NAME) - - updated.emit(next_version_release.tag_name.substr(1)) - - -func _on_notes_button_pressed() -> void: - OS.shell_open(next_version_release.html_url) diff --git a/addons/dialogue_manager/components/download_update_panel.gd.uid b/addons/dialogue_manager/components/download_update_panel.gd.uid deleted file mode 100644 index 7910ab4..0000000 --- a/addons/dialogue_manager/components/download_update_panel.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://kpwo418lb2t2 diff --git a/addons/dialogue_manager/components/download_update_panel.tscn b/addons/dialogue_manager/components/download_update_panel.tscn deleted file mode 100644 index 540abd3..0000000 --- a/addons/dialogue_manager/components/download_update_panel.tscn +++ /dev/null @@ -1,60 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://qdxrxv3c3hxk"] - -[ext_resource type="Script" uid="uid://kpwo418lb2t2" path="res://addons/dialogue_manager/components/download_update_panel.gd" id="1_4tm1k"] -[ext_resource type="Texture2D" uid="uid://d3baj6rygkb3f" path="res://addons/dialogue_manager/assets/update.svg" id="2_4o2m6"] - -[node name="DownloadUpdatePanel" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_4tm1k") - -[node name="HTTPRequest" type="HTTPRequest" parent="."] - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -1.0 -offset_top = 9.0 -offset_right = -1.0 -offset_bottom = 9.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/separation = 10 - -[node name="Logo" type="TextureRect" parent="VBox"] -unique_name_in_owner = true -clip_contents = true -custom_minimum_size = Vector2(300, 80) -layout_mode = 2 -texture = ExtResource("2_4o2m6") -stretch_mode = 5 - -[node name="Label" type="Label" parent="VBox"] -layout_mode = 2 -text = "v1.2.3 is available for download." -horizontal_alignment = 1 - -[node name="Center" type="CenterContainer" parent="VBox"] -layout_mode = 2 - -[node name="DownloadButton" type="Button" parent="VBox/Center"] -unique_name_in_owner = true -layout_mode = 2 -text = "Download update" - -[node name="Center2" type="CenterContainer" parent="VBox"] -layout_mode = 2 - -[node name="NotesButton" type="LinkButton" parent="VBox/Center2"] -layout_mode = 2 -text = "Read release notes" - -[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"] -[connection signal="pressed" from="VBox/Center/DownloadButton" to="." method="_on_download_button_pressed"] -[connection signal="pressed" from="VBox/Center2/NotesButton" to="." method="_on_notes_button_pressed"] diff --git a/addons/dialogue_manager/components/editor_property/editor_property.gd b/addons/dialogue_manager/components/editor_property/editor_property.gd deleted file mode 100644 index 5deef65..0000000 --- a/addons/dialogue_manager/components/editor_property/editor_property.gd +++ /dev/null @@ -1,48 +0,0 @@ -@tool -extends EditorProperty - - -const DialoguePropertyEditorControl = preload("./editor_property_control.tscn") - - -var editor_plugin: EditorPlugin - -var control = DialoguePropertyEditorControl.instantiate() -var current_value: Resource -var is_updating: bool = false - - -func _init() -> void: - add_child(control) - - control.resource = current_value - - control.pressed.connect(_on_button_pressed) - control.resource_changed.connect(_on_resource_changed) - - -func _update_property() -> void: - var next_value = get_edited_object()[get_edited_property()] - - # The resource might have been deleted elsewhere so check that it's not in a weird state - if is_instance_valid(next_value) and not next_value.resource_path.ends_with(".dialogue"): - emit_changed(get_edited_property(), null) - return - - if next_value == current_value: return - - is_updating = true - current_value = next_value - control.resource = current_value - is_updating = false - - -### Signals - - -func _on_button_pressed() -> void: - editor_plugin.edit(current_value) - - -func _on_resource_changed(next_resource: Resource) -> void: - emit_changed(get_edited_property(), next_resource) diff --git a/addons/dialogue_manager/components/editor_property/editor_property.gd.uid b/addons/dialogue_manager/components/editor_property/editor_property.gd.uid deleted file mode 100644 index 283cc43..0000000 --- a/addons/dialogue_manager/components/editor_property/editor_property.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://nyypeje1a036 diff --git a/addons/dialogue_manager/components/editor_property/editor_property_control.gd b/addons/dialogue_manager/components/editor_property/editor_property_control.gd deleted file mode 100644 index d063d0e..0000000 --- a/addons/dialogue_manager/components/editor_property/editor_property_control.gd +++ /dev/null @@ -1,147 +0,0 @@ -@tool -extends HBoxContainer - - -signal pressed() -signal resource_changed(next_resource: Resource) - - -const ITEM_NEW = 100 -const ITEM_QUICK_LOAD = 200 -const ITEM_LOAD = 201 -const ITEM_EDIT = 300 -const ITEM_CLEAR = 301 -const ITEM_FILESYSTEM = 400 - - -@onready var button: Button = $ResourceButton -@onready var menu_button: Button = $MenuButton -@onready var menu: PopupMenu = $Menu -@onready var quick_open_dialog: ConfirmationDialog = $QuickOpenDialog -@onready var files_list = $QuickOpenDialog/FilesList -@onready var new_dialog: FileDialog = $NewDialog -@onready var open_dialog: FileDialog = $OpenDialog - -var editor_plugin: EditorPlugin - -var resource: Resource: - set(next_resource): - resource = next_resource - if button: - button.resource = resource - get: - return resource - -var is_waiting_for_file: bool = false -var quick_selected_file: String = "" - - -func _ready() -> void: - menu_button.icon = get_theme_icon("GuiDropdown", "EditorIcons") - editor_plugin = Engine.get_meta("DialogueManagerPlugin") - - -func build_menu() -> void: - menu.clear() - - menu.add_icon_item(editor_plugin._get_plugin_icon(), "New Dialogue", ITEM_NEW) - menu.add_separator() - menu.add_icon_item(get_theme_icon("Load", "EditorIcons"), "Quick Load", ITEM_QUICK_LOAD) - menu.add_icon_item(get_theme_icon("Load", "EditorIcons"), "Load", ITEM_LOAD) - if resource: - menu.add_icon_item(get_theme_icon("Edit", "EditorIcons"), "Edit", ITEM_EDIT) - menu.add_icon_item(get_theme_icon("Clear", "EditorIcons"), "Clear", ITEM_CLEAR) - menu.add_separator() - menu.add_item("Show in FileSystem", ITEM_FILESYSTEM) - - menu.size = Vector2.ZERO - - -### Signals - - -func _on_new_dialog_file_selected(path: String) -> void: - editor_plugin.main_view.new_file(path) - is_waiting_for_file = false - if Engine.get_meta("DMCache").has_file(path): - resource_changed.emit(load(path)) - else: - var next_resource: Resource = await editor_plugin.import_plugin.compiled_resource - next_resource.resource_path = path - resource_changed.emit(next_resource) - - -func _on_open_dialog_file_selected(file: String) -> void: - resource_changed.emit(load(file)) - - -func _on_file_dialog_canceled() -> void: - is_waiting_for_file = false - - -func _on_resource_button_pressed() -> void: - if is_instance_valid(resource): - EditorInterface.call_deferred("edit_resource", resource) - else: - build_menu() - menu.position = get_viewport().position + Vector2i( - button.global_position.x + button.size.x - menu.size.x, - 2 + menu_button.global_position.y + button.size.y - ) - menu.popup() - - -func _on_resource_button_resource_dropped(next_resource: Resource) -> void: - resource_changed.emit(next_resource) - - -func _on_menu_button_pressed() -> void: - build_menu() - menu.position = get_viewport().position + Vector2i( - menu_button.global_position.x + menu_button.size.x - menu.size.x, - 2 + menu_button.global_position.y + menu_button.size.y - ) - menu.popup() - - -func _on_menu_id_pressed(id: int) -> void: - match id: - ITEM_NEW: - is_waiting_for_file = true - new_dialog.popup_centered() - - ITEM_QUICK_LOAD: - quick_selected_file = "" - files_list.files = Engine.get_meta("DMCache").get_files() - if resource: - files_list.select_file(resource.resource_path) - quick_open_dialog.popup_centered() - files_list.focus_filter() - - ITEM_LOAD: - is_waiting_for_file = true - open_dialog.popup_centered() - - ITEM_EDIT: - EditorInterface.call_deferred("edit_resource", resource) - - ITEM_CLEAR: - resource_changed.emit(null) - - ITEM_FILESYSTEM: - var file_system = EditorInterface.get_file_system_dock() - file_system.navigate_to_path(resource.resource_path) - - -func _on_files_list_file_double_clicked(file_path: String) -> void: - resource_changed.emit(load(file_path)) - quick_open_dialog.hide() - - -func _on_files_list_file_selected(file_path: String) -> void: - quick_selected_file = file_path - - -func _on_quick_open_dialog_confirmed() -> void: - if quick_selected_file != "": - resource_changed.emit(load(quick_selected_file)) diff --git a/addons/dialogue_manager/components/editor_property/editor_property_control.gd.uid b/addons/dialogue_manager/components/editor_property/editor_property_control.gd.uid deleted file mode 100644 index aab7d8d..0000000 --- a/addons/dialogue_manager/components/editor_property/editor_property_control.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dooe2pflnqtve diff --git a/addons/dialogue_manager/components/editor_property/editor_property_control.tscn b/addons/dialogue_manager/components/editor_property/editor_property_control.tscn deleted file mode 100644 index 7cb02e8..0000000 --- a/addons/dialogue_manager/components/editor_property/editor_property_control.tscn +++ /dev/null @@ -1,58 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://ycn6uaj7dsrh"] - -[ext_resource type="Script" uid="uid://dooe2pflnqtve" path="res://addons/dialogue_manager/components/editor_property/editor_property_control.gd" id="1_het12"] -[ext_resource type="PackedScene" uid="uid://b16uuqjuof3n5" path="res://addons/dialogue_manager/components/editor_property/resource_button.tscn" id="2_hh3d4"] -[ext_resource type="PackedScene" uid="uid://dnufpcdrreva3" path="res://addons/dialogue_manager/components/files_list.tscn" id="3_l8fp6"] - -[node name="PropertyEditorButton" type="HBoxContainer"] -offset_right = 40.0 -offset_bottom = 40.0 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 -script = ExtResource("1_het12") - -[node name="ResourceButton" parent="." instance=ExtResource("2_hh3d4")] -layout_mode = 2 -text = "" -text_overrun_behavior = 3 -clip_text = true - -[node name="MenuButton" type="Button" parent="."] -layout_mode = 2 - -[node name="Menu" type="PopupMenu" parent="."] - -[node name="QuickOpenDialog" type="ConfirmationDialog" parent="."] -title = "Find Dialogue Resource" -size = Vector2i(400, 600) -min_size = Vector2i(400, 600) -ok_button_text = "Open" - -[node name="FilesList" parent="QuickOpenDialog" instance=ExtResource("3_l8fp6")] - -[node name="NewDialog" type="FileDialog" parent="."] -size = Vector2i(900, 750) -min_size = Vector2i(900, 750) -dialog_hide_on_ok = true -filters = PackedStringArray("*.dialogue ; Dialogue") - -[node name="OpenDialog" type="FileDialog" parent="."] -title = "Open a File" -size = Vector2i(900, 750) -min_size = Vector2i(900, 750) -ok_button_text = "Open" -dialog_hide_on_ok = true -file_mode = 0 -filters = PackedStringArray("*.dialogue ; Dialogue") - -[connection signal="pressed" from="ResourceButton" to="." method="_on_resource_button_pressed"] -[connection signal="resource_dropped" from="ResourceButton" to="." method="_on_resource_button_resource_dropped"] -[connection signal="pressed" from="MenuButton" to="." method="_on_menu_button_pressed"] -[connection signal="id_pressed" from="Menu" to="." method="_on_menu_id_pressed"] -[connection signal="confirmed" from="QuickOpenDialog" to="." method="_on_quick_open_dialog_confirmed"] -[connection signal="file_double_clicked" from="QuickOpenDialog/FilesList" to="." method="_on_files_list_file_double_clicked"] -[connection signal="file_selected" from="QuickOpenDialog/FilesList" to="." method="_on_files_list_file_selected"] -[connection signal="canceled" from="NewDialog" to="." method="_on_file_dialog_canceled"] -[connection signal="file_selected" from="NewDialog" to="." method="_on_new_dialog_file_selected"] -[connection signal="canceled" from="OpenDialog" to="." method="_on_file_dialog_canceled"] -[connection signal="file_selected" from="OpenDialog" to="." method="_on_open_dialog_file_selected"] diff --git a/addons/dialogue_manager/components/editor_property/resource_button.gd b/addons/dialogue_manager/components/editor_property/resource_button.gd deleted file mode 100644 index 5ba33dc..0000000 --- a/addons/dialogue_manager/components/editor_property/resource_button.gd +++ /dev/null @@ -1,48 +0,0 @@ -@tool -extends Button - - -signal resource_dropped(next_resource: Resource) - - -var resource: Resource: - set(next_resource): - resource = next_resource - if resource: - icon = Engine.get_meta("DialogueManagerPlugin")._get_plugin_icon() - text = resource.resource_path.get_file().replace(".dialogue", "") - else: - icon = null - text = "" - get: - return resource - - -func _notification(what: int) -> void: - match what: - NOTIFICATION_DRAG_BEGIN: - var data = get_viewport().gui_get_drag_data() - if typeof(data) == TYPE_DICTIONARY and data.type == "files" and data.files.size() > 0 and data.files[0].ends_with(".dialogue"): - add_theme_stylebox_override("normal", get_theme_stylebox("focus", "LineEdit")) - add_theme_stylebox_override("hover", get_theme_stylebox("focus", "LineEdit")) - - NOTIFICATION_DRAG_END: - self.resource = resource - remove_theme_stylebox_override("normal") - remove_theme_stylebox_override("hover") - - -func _can_drop_data(at_position: Vector2, data) -> bool: - if typeof(data) != TYPE_DICTIONARY: return false - if data.type != "files": return false - - var files: PackedStringArray = Array(data.files).filter(func(f): return f.get_extension() == "dialogue") - return files.size() > 0 - - -func _drop_data(at_position: Vector2, data) -> void: - var files: PackedStringArray = Array(data.files).filter(func(f): return f.get_extension() == "dialogue") - - if files.size() == 0: return - - resource_dropped.emit(load(files[0])) diff --git a/addons/dialogue_manager/components/editor_property/resource_button.gd.uid b/addons/dialogue_manager/components/editor_property/resource_button.gd.uid deleted file mode 100644 index b1b9d26..0000000 --- a/addons/dialogue_manager/components/editor_property/resource_button.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://damhqta55t67c diff --git a/addons/dialogue_manager/components/editor_property/resource_button.tscn b/addons/dialogue_manager/components/editor_property/resource_button.tscn deleted file mode 100644 index 691e527..0000000 --- a/addons/dialogue_manager/components/editor_property/resource_button.tscn +++ /dev/null @@ -1,9 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://b16uuqjuof3n5"] - -[ext_resource type="Script" uid="uid://damhqta55t67c" path="res://addons/dialogue_manager/components/editor_property/resource_button.gd" id="1_7u2i7"] - -[node name="ResourceButton" type="Button"] -offset_right = 8.0 -offset_bottom = 8.0 -size_flags_horizontal = 3 -script = ExtResource("1_7u2i7") diff --git a/addons/dialogue_manager/components/errors_panel.gd b/addons/dialogue_manager/components/errors_panel.gd deleted file mode 100644 index 0b72d37..0000000 --- a/addons/dialogue_manager/components/errors_panel.gd +++ /dev/null @@ -1,85 +0,0 @@ -@tool -extends HBoxContainer - - -signal error_pressed(line_number) - - -const DialogueConstants = preload("../constants.gd") - - -@onready var error_button: Button = $ErrorButton -@onready var next_button: Button = $NextButton -@onready var count_label: Label = $CountLabel -@onready var previous_button: Button = $PreviousButton - -## The index of the current error being shown -var error_index: int = 0: - set(next_error_index): - error_index = wrap(next_error_index, 0, errors.size()) - show_error() - get: - return error_index - -## The list of all errors -var errors: Array = []: - set(next_errors): - errors = next_errors - self.error_index = 0 - get: - return errors - - -func _ready() -> void: - apply_theme() - hide() - - -## Set up colors and icons -func apply_theme() -> void: - error_button.add_theme_color_override("font_color", get_theme_color("error_color", "Editor")) - error_button.add_theme_color_override("font_hover_color", get_theme_color("error_color", "Editor")) - error_button.icon = get_theme_icon("StatusError", "EditorIcons") - previous_button.icon = get_theme_icon("ArrowLeft", "EditorIcons") - next_button.icon = get_theme_icon("ArrowRight", "EditorIcons") - - -## Move the error index to match a given line -func show_error_for_line_number(line_number: int) -> void: - for i in range(0, errors.size()): - if errors[i].line_number == line_number: - self.error_index = i - - -## Show the current error -func show_error() -> void: - if errors.size() == 0: - hide() - else: - show() - count_label.text = DialogueConstants.translate(&"n_of_n").format({ index = error_index + 1, total = errors.size() }) - var error = errors[error_index] - error_button.text = DialogueConstants.translate(&"errors.line_and_message").format({ line = error.line_number, column = error.column_number, message = DialogueConstants.get_error_message(error.error) }) - if error.has("external_error"): - error_button.text += " " + DialogueConstants.get_error_message(error.external_error) - - -### Signals - - -func _on_errors_panel_theme_changed() -> void: - apply_theme() - - -func _on_error_button_pressed() -> void: - error_pressed.emit(errors[error_index].line_number, errors[error_index].column_number) - - -func _on_previous_button_pressed() -> void: - self.error_index -= 1 - _on_error_button_pressed() - - -func _on_next_button_pressed() -> void: - self.error_index += 1 - _on_error_button_pressed() diff --git a/addons/dialogue_manager/components/errors_panel.gd.uid b/addons/dialogue_manager/components/errors_panel.gd.uid deleted file mode 100644 index c305a80..0000000 --- a/addons/dialogue_manager/components/errors_panel.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d2l8nlb6hhrfp diff --git a/addons/dialogue_manager/components/errors_panel.tscn b/addons/dialogue_manager/components/errors_panel.tscn deleted file mode 100644 index 0b653cc..0000000 --- a/addons/dialogue_manager/components/errors_panel.tscn +++ /dev/null @@ -1,56 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://cs8pwrxr5vxix"] - -[ext_resource type="Script" uid="uid://d2l8nlb6hhrfp" path="res://addons/dialogue_manager/components/errors_panel.gd" id="1_nfm3c"] - -[sub_resource type="Image" id="Image_w0gko"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_s6fxl"] -image = SubResource("Image_w0gko") - -[node name="ErrorsPanel" type="HBoxContainer"] -visible = false -offset_right = 1024.0 -offset_bottom = 600.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_nfm3c") -metadata/_edit_layout_mode = 1 - -[node name="ErrorButton" type="Button" parent="."] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_colors/font_color = Color(0, 0, 0, 1) -theme_override_colors/font_hover_color = Color(0, 0, 0, 1) -theme_override_constants/h_separation = 3 -icon = SubResource("ImageTexture_s6fxl") -flat = true -alignment = 0 -text_overrun_behavior = 4 - -[node name="Spacer" type="Control" parent="."] -custom_minimum_size = Vector2(40, 0) -layout_mode = 2 - -[node name="PreviousButton" type="Button" parent="."] -layout_mode = 2 -icon = SubResource("ImageTexture_s6fxl") -flat = true - -[node name="CountLabel" type="Label" parent="."] -layout_mode = 2 - -[node name="NextButton" type="Button" parent="."] -layout_mode = 2 -icon = SubResource("ImageTexture_s6fxl") -flat = true - -[connection signal="pressed" from="ErrorButton" to="." method="_on_error_button_pressed"] -[connection signal="pressed" from="PreviousButton" to="." method="_on_previous_button_pressed"] -[connection signal="pressed" from="NextButton" to="." method="_on_next_button_pressed"] diff --git a/addons/dialogue_manager/components/files_list.gd b/addons/dialogue_manager/components/files_list.gd deleted file mode 100644 index 21a4415..0000000 --- a/addons/dialogue_manager/components/files_list.gd +++ /dev/null @@ -1,148 +0,0 @@ -@tool -extends VBoxContainer - - -signal file_selected(file_path: String) -signal file_popup_menu_requested(at_position: Vector2) -signal file_double_clicked(file_path: String) -signal file_middle_clicked(file_path: String) - - -const DialogueConstants = preload("../constants.gd") - -const MODIFIED_SUFFIX = "(*)" - - -@export var icon: Texture2D - -@onready var filter_edit: LineEdit = $FilterEdit -@onready var list: ItemList = $List - -var file_map: Dictionary = {} - -var current_file_path: String = "" -var last_selected_file_path: String = "" - -var files: PackedStringArray = []: - set(next_files): - files = next_files - files.sort() - update_file_map() - apply_filter() - get: - return files - -var unsaved_files: Array[String] = [] - -var filter: String = "": - set(next_filter): - filter = next_filter - apply_filter() - get: - return filter - - -func _ready() -> void: - apply_theme() - - filter_edit.placeholder_text = DialogueConstants.translate(&"files_list.filter") - - -func focus_filter() -> void: - filter_edit.grab_focus() - - -func select_file(file: String) -> void: - list.deselect_all() - for i in range(0, list.get_item_count()): - var item_text = list.get_item_text(i).replace(MODIFIED_SUFFIX, "") - if item_text == get_nice_file(file, item_text.count("/") + 1): - list.select(i) - last_selected_file_path = file - - -func mark_file_as_unsaved(file: String, is_unsaved: bool) -> void: - if not file in unsaved_files and is_unsaved: - unsaved_files.append(file) - elif file in unsaved_files and not is_unsaved: - unsaved_files.erase(file) - apply_filter() - - -func update_file_map() -> void: - file_map = {} - for file in files: - var nice_file: String = get_nice_file(file) - - # See if a value with just the file name is already in the map - for key in file_map.keys(): - if file_map[key] == nice_file: - var bit_count = nice_file.count("/") + 2 - - var existing_nice_file = get_nice_file(key, bit_count) - nice_file = get_nice_file(file, bit_count) - - while nice_file == existing_nice_file: - bit_count += 1 - existing_nice_file = get_nice_file(key, bit_count) - nice_file = get_nice_file(file, bit_count) - - file_map[key] = existing_nice_file - - file_map[file] = nice_file - - -func get_nice_file(file_path: String, path_bit_count: int = 1) -> String: - var bits = file_path.replace("res://", "").replace(".dialogue", "").split("/") - bits = bits.slice(-path_bit_count) - return "/".join(bits) - - -func apply_filter() -> void: - list.clear() - for file in file_map.keys(): - if filter == "" or filter.to_lower() in file.to_lower(): - var nice_file = file_map[file] - if file in unsaved_files: - nice_file += MODIFIED_SUFFIX - var new_id := list.add_item(nice_file) - list.set_item_icon(new_id, icon) - - select_file(current_file_path) - - -func apply_theme() -> void: - if is_instance_valid(filter_edit): - filter_edit.right_icon = get_theme_icon("Search", "EditorIcons") - - -### Signals - - -func _on_theme_changed() -> void: - apply_theme() - - -func _on_filter_edit_text_changed(new_text: String) -> void: - self.filter = new_text - - -func _on_list_item_clicked(index: int, at_position: Vector2, mouse_button_index: int) -> void: - var item_text = list.get_item_text(index).replace(MODIFIED_SUFFIX, "") - var file = file_map.find_key(item_text) - - if mouse_button_index == MOUSE_BUTTON_LEFT or mouse_button_index == MOUSE_BUTTON_RIGHT: - select_file(file) - file_selected.emit(file) - if mouse_button_index == MOUSE_BUTTON_RIGHT: - file_popup_menu_requested.emit(at_position) - - if mouse_button_index == MOUSE_BUTTON_MIDDLE: - file_middle_clicked.emit(file) - - -func _on_list_item_activated(index: int) -> void: - var item_text = list.get_item_text(index).replace(MODIFIED_SUFFIX, "") - var file = file_map.find_key(item_text) - select_file(file) - file_double_clicked.emit(file) diff --git a/addons/dialogue_manager/components/files_list.gd.uid b/addons/dialogue_manager/components/files_list.gd.uid deleted file mode 100644 index 2a1089a..0000000 --- a/addons/dialogue_manager/components/files_list.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dqa4a4wwoo0aa diff --git a/addons/dialogue_manager/components/files_list.tscn b/addons/dialogue_manager/components/files_list.tscn deleted file mode 100644 index e135e60..0000000 --- a/addons/dialogue_manager/components/files_list.tscn +++ /dev/null @@ -1,28 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://dnufpcdrreva3"] - -[ext_resource type="Script" uid="uid://dqa4a4wwoo0aa" path="res://addons/dialogue_manager/components/files_list.gd" id="1_cytii"] -[ext_resource type="Texture2D" uid="uid://d3lr2uas6ax8v" path="res://addons/dialogue_manager/assets/icon.svg" id="2_3ijx1"] - -[node name="FilesList" type="VBoxContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_cytii") -icon = ExtResource("2_3ijx1") - -[node name="FilterEdit" type="LineEdit" parent="."] -layout_mode = 2 -placeholder_text = "Filter files" -clear_button_enabled = true - -[node name="List" type="ItemList" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -allow_rmb_select = true - -[connection signal="theme_changed" from="." to="." method="_on_theme_changed"] -[connection signal="text_changed" from="FilterEdit" to="." method="_on_filter_edit_text_changed"] -[connection signal="item_activated" from="List" to="." method="_on_list_item_activated"] -[connection signal="item_clicked" from="List" to="." method="_on_list_item_clicked"] diff --git a/addons/dialogue_manager/components/find_in_files.gd b/addons/dialogue_manager/components/find_in_files.gd deleted file mode 100644 index 2614eca..0000000 --- a/addons/dialogue_manager/components/find_in_files.gd +++ /dev/null @@ -1,229 +0,0 @@ -@tool -extends Control - -signal result_selected(path: String, cursor: Vector2, length: int) - - -const DialogueConstants = preload("../constants.gd") - - -@export var main_view: Control -@export var code_edit: CodeEdit - -@onready var input: LineEdit = %Input -@onready var search_button: Button = %SearchButton -@onready var match_case_button: CheckBox = %MatchCaseButton -@onready var replace_toggle: CheckButton = %ReplaceToggle -@onready var replace_container: VBoxContainer = %ReplaceContainer -@onready var replace_input: LineEdit = %ReplaceInput -@onready var replace_selected_button: Button = %ReplaceSelectedButton -@onready var replace_all_button: Button = %ReplaceAllButton -@onready var results_container: VBoxContainer = %ResultsContainer -@onready var result_template: HBoxContainer = %ResultTemplate - -var current_results: Dictionary = {}: - set(value): - current_results = value - update_results_view() - if current_results.size() == 0: - replace_selected_button.disabled = true - replace_all_button.disabled = true - else: - replace_selected_button.disabled = false - replace_all_button.disabled = false - get: - return current_results - -var selections: PackedStringArray = [] - - -func prepare() -> void: - input.grab_focus() - - var template_label = result_template.get_node("Label") - template_label.get_theme_stylebox(&"focus").bg_color = code_edit.theme_overrides.current_line_color - template_label.add_theme_font_override(&"normal_font", code_edit.get_theme_font(&"font")) - - replace_toggle.set_pressed_no_signal(false) - replace_container.hide() - - $VBoxContainer/HBoxContainer/FindContainer/Label.text = DialogueConstants.translate(&"search.find") - input.placeholder_text = DialogueConstants.translate(&"search.placeholder") - input.text = "" - search_button.text = DialogueConstants.translate(&"search.find_all") - match_case_button.text = DialogueConstants.translate(&"search.match_case") - replace_toggle.text = DialogueConstants.translate(&"search.toggle_replace") - $VBoxContainer/HBoxContainer/ReplaceContainer/ReplaceLabel.text = DialogueConstants.translate(&"search.replace_with") - replace_input.placeholder_text = DialogueConstants.translate(&"search.replace_placeholder") - replace_input.text = "" - replace_all_button.text = DialogueConstants.translate(&"search.replace_all") - replace_selected_button.text = DialogueConstants.translate(&"search.replace_selected") - - selections.clear() - self.current_results = {} - -#region helpers - - -func update_results_view() -> void: - for child in results_container.get_children(): - child.queue_free() - - for path in current_results.keys(): - var path_label: Label = Label.new() - path_label.text = path - # Show open files - if main_view.open_buffers.has(path): - path_label.text += "(*)" - results_container.add_child(path_label) - for path_result in current_results.get(path): - var result_item: HBoxContainer = result_template.duplicate() - - var checkbox: CheckBox = result_item.get_node("CheckBox") as CheckBox - var key: String = get_selection_key(path, path_result) - checkbox.toggled.connect(func(is_pressed): - if is_pressed: - if not selections.has(key): - selections.append(key) - else: - if selections.has(key): - selections.remove_at(selections.find(key)) - ) - checkbox.set_pressed_no_signal(selections.has(key)) - checkbox.visible = replace_toggle.button_pressed - - var result_label: RichTextLabel = result_item.get_node("Label") as RichTextLabel - var colors: Dictionary = code_edit.theme_overrides - var highlight: String = "" - if replace_toggle.button_pressed: - var matched_word: String = "[bgcolor=" + colors.critical_color.to_html() + "][color=" + colors.text_color.to_html() + "]" + path_result.matched_text + "[/color][/bgcolor]" - highlight = "[s]" + matched_word + "[/s][bgcolor=" + colors.notice_color.to_html() + "][color=" + colors.text_color.to_html() + "]" + replace_input.text + "[/color][/bgcolor]" - else: - highlight = "[bgcolor=" + colors.symbols_color.to_html() + "][color=" + colors.text_color.to_html() + "]" + path_result.matched_text + "[/color][/bgcolor]" - var text: String = path_result.text.substr(0, path_result.index) + highlight + path_result.text.substr(path_result.index + path_result.query.length()) - result_label.text = "%s: %s" % [str(path_result.line).lpad(4), text] - result_label.gui_input.connect(func(event): - if event is InputEventMouseButton and (event as InputEventMouseButton).button_index == MOUSE_BUTTON_LEFT and (event as InputEventMouseButton).double_click: - result_selected.emit(path, Vector2(path_result.index, path_result.line), path_result.query.length()) - ) - - results_container.add_child(result_item) - - -func find_in_files() -> Dictionary: - var results: Dictionary = {} - - var q: String = input.text - var cache = Engine.get_meta("DMCache") - var file: FileAccess - for path in cache.get_files(): - var path_results: Array = [] - var lines: PackedStringArray = [] - - if main_view.open_buffers.has(path): - lines = main_view.open_buffers.get(path).text.split("\n") - else: - file = FileAccess.open(path, FileAccess.READ) - lines = file.get_as_text().split("\n") - - for i in range(0, lines.size()): - var index: int = find_in_line(lines[i], q) - while index > -1: - path_results.append({ - line = i, - index = index, - text = lines[i], - matched_text = lines[i].substr(index, q.length()), - query = q - }) - index = find_in_line(lines[i], q, index + q.length()) - - if file != null and file.is_open(): - file.close() - - if path_results.size() > 0: - results[path] = path_results - - return results - - -func get_selection_key(path: String, path_result: Dictionary) -> String: - return "%s-%d-%d" % [path, path_result.line, path_result.index] - - -func find_in_line(line: String, query: String, from_index: int = 0) -> int: - if match_case_button.button_pressed: - return line.find(query, from_index) - else: - return line.findn(query, from_index) - - -func replace_results(only_selected: bool) -> void: - var file: FileAccess - var lines: PackedStringArray = [] - for path in current_results: - if main_view.open_buffers.has(path): - lines = main_view.open_buffers.get(path).text.split("\n") - else: - file = FileAccess.open(path, FileAccess.READ_WRITE) - lines = file.get_as_text().split("\n") - - # Read the results in reverse because we're going to be modifying them as we go - var path_results: Array = current_results.get(path).duplicate() - path_results.reverse() - for path_result in path_results: - var key: String = get_selection_key(path, path_result) - if not only_selected or (only_selected and selections.has(key)): - lines[path_result.line] = lines[path_result.line].substr(0, path_result.index) + replace_input.text + lines[path_result.line].substr(path_result.index + path_result.matched_text.length()) - - var replaced_text: String = "\n".join(lines) - if file != null and file.is_open(): - file.seek(0) - file.store_string(replaced_text) - file.close() - else: - main_view.open_buffers.get(path).text = replaced_text - if main_view.current_file_path == path: - code_edit.text = replaced_text - - current_results = find_in_files() - - -#endregion - -#region signals - - -func _on_search_button_pressed() -> void: - selections.clear() - self.current_results = find_in_files() - - -func _on_input_text_submitted(new_text: String) -> void: - _on_search_button_pressed() - - -func _on_replace_toggle_toggled(toggled_on: bool) -> void: - replace_container.visible = toggled_on - if toggled_on: - replace_input.grab_focus() - update_results_view() - - -func _on_replace_input_text_changed(new_text: String) -> void: - update_results_view() - - -func _on_replace_selected_button_pressed() -> void: - replace_results(true) - - -func _on_replace_all_button_pressed() -> void: - replace_results(false) - - -func _on_match_case_button_toggled(toggled_on: bool) -> void: - _on_search_button_pressed() - - -#endregion diff --git a/addons/dialogue_manager/components/find_in_files.gd.uid b/addons/dialogue_manager/components/find_in_files.gd.uid deleted file mode 100644 index 380a491..0000000 --- a/addons/dialogue_manager/components/find_in_files.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://q368fmxxa8sd diff --git a/addons/dialogue_manager/components/find_in_files.tscn b/addons/dialogue_manager/components/find_in_files.tscn deleted file mode 100644 index 97fca24..0000000 --- a/addons/dialogue_manager/components/find_in_files.tscn +++ /dev/null @@ -1,139 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://0n7hwviyyly4"] - -[ext_resource type="Script" uid="uid://q368fmxxa8sd" path="res://addons/dialogue_manager/components/find_in_files.gd" id="1_3xicy"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_owohg"] -bg_color = Color(0.266667, 0.278431, 0.352941, 0.243137) -corner_detail = 1 - -[node name="FindInFiles" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -script = ExtResource("1_3xicy") - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] -layout_mode = 2 - -[node name="FindContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/FindContainer"] -layout_mode = 2 -text = "Find:" - -[node name="Input" type="LineEdit" parent="VBoxContainer/HBoxContainer/FindContainer"] -unique_name_in_owner = true -layout_mode = 2 -clear_button_enabled = true - -[node name="FindToolbar" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/FindContainer"] -layout_mode = 2 - -[node name="SearchButton" type="Button" parent="VBoxContainer/HBoxContainer/FindContainer/FindToolbar"] -unique_name_in_owner = true -layout_mode = 2 -text = "Find all..." - -[node name="MatchCaseButton" type="CheckBox" parent="VBoxContainer/HBoxContainer/FindContainer/FindToolbar"] -unique_name_in_owner = true -layout_mode = 2 -text = "Match case" - -[node name="Control" type="Control" parent="VBoxContainer/HBoxContainer/FindContainer/FindToolbar"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="ReplaceToggle" type="CheckButton" parent="VBoxContainer/HBoxContainer/FindContainer/FindToolbar"] -unique_name_in_owner = true -layout_mode = 2 -text = "Replace" - -[node name="ReplaceContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="ReplaceLabel" type="Label" parent="VBoxContainer/HBoxContainer/ReplaceContainer"] -layout_mode = 2 -text = "Replace with:" - -[node name="ReplaceInput" type="LineEdit" parent="VBoxContainer/HBoxContainer/ReplaceContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -clear_button_enabled = true - -[node name="ReplaceToolbar" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/ReplaceContainer"] -layout_mode = 2 - -[node name="ReplaceSelectedButton" type="Button" parent="VBoxContainer/HBoxContainer/ReplaceContainer/ReplaceToolbar"] -unique_name_in_owner = true -layout_mode = 2 -text = "Replace selected" - -[node name="ReplaceAllButton" type="Button" parent="VBoxContainer/HBoxContainer/ReplaceContainer/ReplaceToolbar"] -unique_name_in_owner = true -layout_mode = 2 -text = "Replace all" - -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer"] -layout_mode = 2 - -[node name="ReplaceToolbar" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"] -layout_mode = 2 - -[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -follow_focus = true - -[node name="ResultsContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -theme_override_constants/separation = 0 - -[node name="ResultTemplate" type="HBoxContainer" parent="."] -unique_name_in_owner = true -layout_mode = 0 -offset_left = 155.0 -offset_top = -74.0 -offset_right = 838.0 -offset_bottom = -51.0 - -[node name="CheckBox" type="CheckBox" parent="ResultTemplate"] -layout_mode = 2 - -[node name="Label" type="RichTextLabel" parent="ResultTemplate"] -layout_mode = 2 -size_flags_horizontal = 3 -focus_mode = 2 -theme_override_styles/focus = SubResource("StyleBoxFlat_owohg") -bbcode_enabled = true -text = "Result" -fit_content = true -scroll_active = false - -[connection signal="text_submitted" from="VBoxContainer/HBoxContainer/FindContainer/Input" to="." method="_on_input_text_submitted"] -[connection signal="pressed" from="VBoxContainer/HBoxContainer/FindContainer/FindToolbar/SearchButton" to="." method="_on_search_button_pressed"] -[connection signal="toggled" from="VBoxContainer/HBoxContainer/FindContainer/FindToolbar/MatchCaseButton" to="." method="_on_match_case_button_toggled"] -[connection signal="toggled" from="VBoxContainer/HBoxContainer/FindContainer/FindToolbar/ReplaceToggle" to="." method="_on_replace_toggle_toggled"] -[connection signal="text_changed" from="VBoxContainer/HBoxContainer/ReplaceContainer/ReplaceInput" to="." method="_on_replace_input_text_changed"] -[connection signal="pressed" from="VBoxContainer/HBoxContainer/ReplaceContainer/ReplaceToolbar/ReplaceSelectedButton" to="." method="_on_replace_selected_button_pressed"] -[connection signal="pressed" from="VBoxContainer/HBoxContainer/ReplaceContainer/ReplaceToolbar/ReplaceAllButton" to="." method="_on_replace_all_button_pressed"] diff --git a/addons/dialogue_manager/components/search_and_replace.gd b/addons/dialogue_manager/components/search_and_replace.gd deleted file mode 100644 index e91574e..0000000 --- a/addons/dialogue_manager/components/search_and_replace.gd +++ /dev/null @@ -1,212 +0,0 @@ -@tool -extends VBoxContainer - - -signal open_requested() -signal close_requested() - - -const DialogueConstants = preload("../constants.gd") - - -@onready var input: LineEdit = $Search/Input -@onready var result_label: Label = $Search/ResultLabel -@onready var previous_button: Button = $Search/PreviousButton -@onready var next_button: Button = $Search/NextButton -@onready var match_case_button: CheckBox = $Search/MatchCaseCheckBox -@onready var replace_check_button: CheckButton = $Search/ReplaceCheckButton -@onready var replace_panel: HBoxContainer = $Replace -@onready var replace_input: LineEdit = $Replace/Input -@onready var replace_button: Button = $Replace/ReplaceButton -@onready var replace_all_button: Button = $Replace/ReplaceAllButton - -# The code edit we will be affecting (for some reason exporting this didn't work) -var code_edit: CodeEdit: - set(next_code_edit): - code_edit = next_code_edit - code_edit.gui_input.connect(_on_text_edit_gui_input) - code_edit.text_changed.connect(_on_text_edit_text_changed) - get: - return code_edit - -var results: Array = [] -var result_index: int = -1: - set(next_result_index): - result_index = next_result_index - if results.size() > 0: - var r = results[result_index] - code_edit.set_caret_line(r[0]) - code_edit.select(r[0], r[1], r[0], r[1] + r[2]) - else: - result_index = -1 - if is_instance_valid(code_edit): - code_edit.deselect() - - result_label.text = DialogueConstants.translate(&"n_of_n").format({ index = result_index + 1, total = results.size() }) - get: - return result_index - - -func _ready() -> void: - apply_theme() - - input.placeholder_text = DialogueConstants.translate(&"search.placeholder") - previous_button.tooltip_text = DialogueConstants.translate(&"search.previous") - next_button.tooltip_text = DialogueConstants.translate(&"search.next") - match_case_button.text = DialogueConstants.translate(&"search.match_case") - $Search/ReplaceCheckButton.text = DialogueConstants.translate(&"search.toggle_replace") - replace_button.text = DialogueConstants.translate(&"search.replace") - replace_all_button.text = DialogueConstants.translate(&"search.replace_all") - $Replace/ReplaceLabel.text = DialogueConstants.translate(&"search.replace_with") - - self.result_index = -1 - - replace_panel.hide() - replace_button.disabled = true - replace_all_button.disabled = true - - hide() - - -func focus_line_edit() -> void: - input.grab_focus() - input.select_all() - - -func apply_theme() -> void: - if is_instance_valid(previous_button): - previous_button.icon = get_theme_icon("ArrowLeft", "EditorIcons") - if is_instance_valid(next_button): - next_button.icon = get_theme_icon("ArrowRight", "EditorIcons") - - -# Find text in the code -func search(text: String = "", default_result_index: int = 0) -> void: - results.clear() - - if text == "": - text = input.text - - var lines = code_edit.text.split("\n") - for line_number in range(0, lines.size()): - var line = lines[line_number] - - var column = find_in_line(line, text, 0) - while column > -1: - results.append([line_number, column, text.length()]) - column = find_in_line(line, text, column + 1) - - if results.size() > 0: - replace_button.disabled = false - replace_all_button.disabled = false - else: - replace_button.disabled = true - replace_all_button.disabled = true - - self.result_index = clamp(default_result_index, 0, results.size() - 1) - - -# Find text in a string and match case if requested -func find_in_line(line: String, text: String, from_index: int = 0) -> int: - if match_case_button.button_pressed: - return line.find(text, from_index) - else: - return line.findn(text, from_index) - - -### Signals - - -func _on_text_edit_gui_input(event: InputEvent) -> void: - if event is InputEventKey and event.is_pressed(): - match event.as_text(): - "Ctrl+F", "Command+F": - open_requested.emit() - get_viewport().set_input_as_handled() - "Ctrl+Shift+R", "Command+Shift+R": - replace_check_button.set_pressed(true) - open_requested.emit() - get_viewport().set_input_as_handled() - - -func _on_text_edit_text_changed() -> void: - results.clear() - - -func _on_search_and_replace_theme_changed() -> void: - apply_theme() - - -func _on_input_text_changed(new_text: String) -> void: - search(new_text) - - -func _on_previous_button_pressed() -> void: - self.result_index = wrapi(result_index - 1, 0, results.size()) - - -func _on_next_button_pressed() -> void: - self.result_index = wrapi(result_index + 1, 0, results.size()) - - -func _on_search_and_replace_visibility_changed() -> void: - if is_instance_valid(input): - if visible: - input.grab_focus() - var selection = code_edit.get_selected_text() - if input.text == "" and selection != "": - input.text = selection - search(selection) - else: - search() - else: - input.text = "" - - -func _on_input_gui_input(event: InputEvent) -> void: - if event is InputEventKey and event.is_pressed(): - match event.as_text(): - "Enter": - search(input.text) - "Escape": - emit_signal("close_requested") - - -func _on_replace_button_pressed() -> void: - if result_index == -1: return - - # Replace the selection at result index - var r: Array = results[result_index] - var lines: PackedStringArray = code_edit.text.split("\n") - var line: String = lines[r[0]] - line = line.substr(0, r[1]) + replace_input.text + line.substr(r[1] + r[2]) - lines[r[0]] = line - code_edit.text = "\n".join(lines) - search(input.text, result_index) - code_edit.text_changed.emit() - - -func _on_replace_all_button_pressed() -> void: - if match_case_button.button_pressed: - code_edit.text = code_edit.text.replace(input.text, replace_input.text) - else: - code_edit.text = code_edit.text.replacen(input.text, replace_input.text) - search() - code_edit.text_changed.emit() - - -func _on_replace_check_button_toggled(button_pressed: bool) -> void: - replace_panel.visible = button_pressed - if button_pressed: - replace_input.grab_focus() - - -func _on_input_focus_entered() -> void: - if results.size() == 0: - search() - else: - self.result_index = result_index - - -func _on_match_case_check_box_toggled(button_pressed: bool) -> void: - search() diff --git a/addons/dialogue_manager/components/search_and_replace.gd.uid b/addons/dialogue_manager/components/search_and_replace.gd.uid deleted file mode 100644 index 66ec826..0000000 --- a/addons/dialogue_manager/components/search_and_replace.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cijsmjkq21cdq diff --git a/addons/dialogue_manager/components/search_and_replace.tscn b/addons/dialogue_manager/components/search_and_replace.tscn deleted file mode 100644 index 52721c4..0000000 --- a/addons/dialogue_manager/components/search_and_replace.tscn +++ /dev/null @@ -1,87 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://gr8nakpbrhby"] - -[ext_resource type="Script" uid="uid://cijsmjkq21cdq" path="res://addons/dialogue_manager/components/search_and_replace.gd" id="1_8oj1f"] - -[node name="SearchAndReplace" type="VBoxContainer"] -visible = false -anchors_preset = 10 -anchor_right = 1.0 -offset_bottom = 31.0 -grow_horizontal = 2 -size_flags_horizontal = 3 -script = ExtResource("1_8oj1f") - -[node name="Search" type="HBoxContainer" parent="."] -layout_mode = 2 - -[node name="Input" type="LineEdit" parent="Search"] -layout_mode = 2 -size_flags_horizontal = 3 -placeholder_text = "Text to search for" -metadata/_edit_use_custom_anchors = true - -[node name="MatchCaseCheckBox" type="CheckBox" parent="Search"] -layout_mode = 2 -text = "Match case" - -[node name="VSeparator" type="VSeparator" parent="Search"] -layout_mode = 2 - -[node name="PreviousButton" type="Button" parent="Search"] -layout_mode = 2 -tooltip_text = "Previous" -flat = true - -[node name="ResultLabel" type="Label" parent="Search"] -layout_mode = 2 -text = "0 of 0" - -[node name="NextButton" type="Button" parent="Search"] -layout_mode = 2 -tooltip_text = "Next" -flat = true - -[node name="VSeparator2" type="VSeparator" parent="Search"] -layout_mode = 2 - -[node name="ReplaceCheckButton" type="CheckButton" parent="Search"] -layout_mode = 2 -text = "Replace" - -[node name="Replace" type="HBoxContainer" parent="."] -visible = false -layout_mode = 2 - -[node name="ReplaceLabel" type="Label" parent="Replace"] -layout_mode = 2 -text = "Replace with:" - -[node name="Input" type="LineEdit" parent="Replace"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="ReplaceButton" type="Button" parent="Replace"] -layout_mode = 2 -disabled = true -text = "Replace" -flat = true - -[node name="ReplaceAllButton" type="Button" parent="Replace"] -layout_mode = 2 -disabled = true -text = "Replace all" -flat = true - -[connection signal="theme_changed" from="." to="." method="_on_search_and_replace_theme_changed"] -[connection signal="visibility_changed" from="." to="." method="_on_search_and_replace_visibility_changed"] -[connection signal="focus_entered" from="Search/Input" to="." method="_on_input_focus_entered"] -[connection signal="gui_input" from="Search/Input" to="." method="_on_input_gui_input"] -[connection signal="text_changed" from="Search/Input" to="." method="_on_input_text_changed"] -[connection signal="toggled" from="Search/MatchCaseCheckBox" to="." method="_on_match_case_check_box_toggled"] -[connection signal="pressed" from="Search/PreviousButton" to="." method="_on_previous_button_pressed"] -[connection signal="pressed" from="Search/NextButton" to="." method="_on_next_button_pressed"] -[connection signal="toggled" from="Search/ReplaceCheckButton" to="." method="_on_replace_check_button_toggled"] -[connection signal="focus_entered" from="Replace/Input" to="." method="_on_input_focus_entered"] -[connection signal="gui_input" from="Replace/Input" to="." method="_on_input_gui_input"] -[connection signal="pressed" from="Replace/ReplaceButton" to="." method="_on_replace_button_pressed"] -[connection signal="pressed" from="Replace/ReplaceAllButton" to="." method="_on_replace_all_button_pressed"] diff --git a/addons/dialogue_manager/components/title_list.gd b/addons/dialogue_manager/components/title_list.gd deleted file mode 100644 index ee7cd13..0000000 --- a/addons/dialogue_manager/components/title_list.gd +++ /dev/null @@ -1,67 +0,0 @@ -@tool -extends VBoxContainer - -signal title_selected(title: String) - - -const DialogueConstants = preload("../constants.gd") - - -@onready var filter_edit: LineEdit = $FilterEdit -@onready var list: ItemList = $List - -var titles: PackedStringArray: - set(next_titles): - titles = next_titles - apply_filter() - get: - return titles - -var filter: String: - set(next_filter): - filter = next_filter - apply_filter() - get: - return filter - - -func _ready() -> void: - apply_theme() - - filter_edit.placeholder_text = DialogueConstants.translate(&"titles_list.filter") - - -func select_title(title: String) -> void: - list.deselect_all() - for i in range(0, list.get_item_count()): - if list.get_item_text(i) == title.strip_edges(): - list.select(i) - - -func apply_filter() -> void: - list.clear() - for title in titles: - if filter == "" or filter.to_lower() in title.to_lower(): - list.add_item(title.strip_edges()) - - -func apply_theme() -> void: - if is_instance_valid(filter_edit): - filter_edit.right_icon = get_theme_icon("Search", "EditorIcons") - - -### Signals - - -func _on_theme_changed() -> void: - apply_theme() - - -func _on_filter_edit_text_changed(new_text: String) -> void: - self.filter = new_text - - -func _on_list_item_clicked(index: int, at_position: Vector2, mouse_button_index: int) -> void: - if mouse_button_index == MOUSE_BUTTON_LEFT: - var title = list.get_item_text(index) - title_selected.emit(title) diff --git a/addons/dialogue_manager/components/title_list.gd.uid b/addons/dialogue_manager/components/title_list.gd.uid deleted file mode 100644 index 325ca61..0000000 --- a/addons/dialogue_manager/components/title_list.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d0k2wndjj0ifm diff --git a/addons/dialogue_manager/components/title_list.tscn b/addons/dialogue_manager/components/title_list.tscn deleted file mode 100644 index ac2b983..0000000 --- a/addons/dialogue_manager/components/title_list.tscn +++ /dev/null @@ -1,27 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://ctns6ouwwd68i"] - -[ext_resource type="Script" uid="uid://d0k2wndjj0ifm" path="res://addons/dialogue_manager/components/title_list.gd" id="1_5qqmd"] - -[node name="TitleList" type="VBoxContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -script = ExtResource("1_5qqmd") - -[node name="FilterEdit" type="LineEdit" parent="."] -layout_mode = 2 -placeholder_text = "Filter titles" -clear_button_enabled = true - -[node name="List" type="ItemList" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -allow_reselect = true - -[connection signal="theme_changed" from="." to="." method="_on_theme_changed"] -[connection signal="text_changed" from="FilterEdit" to="." method="_on_filter_edit_text_changed"] -[connection signal="item_clicked" from="List" to="." method="_on_list_item_clicked"] diff --git a/addons/dialogue_manager/components/update_button.gd b/addons/dialogue_manager/components/update_button.gd deleted file mode 100644 index cf3f1b9..0000000 --- a/addons/dialogue_manager/components/update_button.gd +++ /dev/null @@ -1,125 +0,0 @@ -@tool -extends Button - -const DialogueConstants = preload("../constants.gd") -const DialogueSettings = preload("../settings.gd") - -const REMOTE_RELEASES_URL = "https://api.github.com/repos/nathanhoad/godot_dialogue_manager/releases" - - -@onready var http_request: HTTPRequest = $HTTPRequest -@onready var download_dialog: AcceptDialog = $DownloadDialog -@onready var download_update_panel = $DownloadDialog/DownloadUpdatePanel -@onready var needs_reload_dialog: AcceptDialog = $NeedsReloadDialog -@onready var update_failed_dialog: AcceptDialog = $UpdateFailedDialog -@onready var timer: Timer = $Timer - -var needs_reload: bool = false - -# A lambda that gets called just before refreshing the plugin. Return false to stop the reload. -var on_before_refresh: Callable = func(): return true - - -func _ready() -> void: - hide() - apply_theme() - - # Check for updates on GitHub - check_for_update() - - # Check again every few hours - timer.start(60 * 60 * 12) - - -# Convert a version number to an actually comparable number -func version_to_number(version: String) -> int: - var bits = version.split(".") - return bits[0].to_int() * 1000000 + bits[1].to_int() * 1000 + bits[2].to_int() - - -func apply_theme() -> void: - var color: Color = get_theme_color("success_color", "Editor") - - if needs_reload: - color = get_theme_color("error_color", "Editor") - icon = get_theme_icon("Reload", "EditorIcons") - add_theme_color_override("icon_normal_color", color) - add_theme_color_override("icon_focus_color", color) - add_theme_color_override("icon_hover_color", color) - - add_theme_color_override("font_color", color) - add_theme_color_override("font_focus_color", color) - add_theme_color_override("font_hover_color", color) - - -func check_for_update() -> void: - if DialogueSettings.get_user_value("check_for_updates", true): - http_request.request(REMOTE_RELEASES_URL) - - -### Signals - - -func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void: - if result != HTTPRequest.RESULT_SUCCESS: return - - var current_version: String = Engine.get_meta("DialogueManagerPlugin").get_version() - - # Work out the next version from the releases information on GitHub - var response = JSON.parse_string(body.get_string_from_utf8()) - if typeof(response) != TYPE_ARRAY: return - - # GitHub releases are in order of creation, not order of version - var versions = (response as Array).filter(func(release): - var version: String = release.tag_name.substr(1) - var major_version: int = version.split(".")[0].to_int() - var current_major_version: int = current_version.split(".")[0].to_int() - return major_version == current_major_version and version_to_number(version) > version_to_number(current_version) - ) - if versions.size() > 0: - download_update_panel.next_version_release = versions[0] - text = DialogueConstants.translate(&"update.available").format({ version = versions[0].tag_name.substr(1) }) - show() - - -func _on_update_button_pressed() -> void: - if needs_reload: - var will_refresh = on_before_refresh.call() - if will_refresh: - EditorInterface.restart_editor(true) - else: - var scale: float = EditorInterface.get_editor_scale() - download_dialog.min_size = Vector2(300, 250) * scale - download_dialog.popup_centered() - - -func _on_download_dialog_close_requested() -> void: - download_dialog.hide() - - -func _on_download_update_panel_updated(updated_to_version: String) -> void: - download_dialog.hide() - - needs_reload_dialog.dialog_text = DialogueConstants.translate(&"update.needs_reload") - needs_reload_dialog.ok_button_text = DialogueConstants.translate(&"update.reload_ok_button") - needs_reload_dialog.cancel_button_text = DialogueConstants.translate(&"update.reload_cancel_button") - needs_reload_dialog.popup_centered() - - needs_reload = true - text = DialogueConstants.translate(&"update.reload_project") - apply_theme() - - -func _on_download_update_panel_failed() -> void: - download_dialog.hide() - update_failed_dialog.dialog_text = DialogueConstants.translate(&"update.failed") - update_failed_dialog.popup_centered() - - -func _on_needs_reload_dialog_confirmed() -> void: - EditorInterface.restart_editor(true) - - -func _on_timer_timeout() -> void: - if not needs_reload: - check_for_update() diff --git a/addons/dialogue_manager/components/update_button.gd.uid b/addons/dialogue_manager/components/update_button.gd.uid deleted file mode 100644 index 9981132..0000000 --- a/addons/dialogue_manager/components/update_button.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cr1tt12dh5ecr diff --git a/addons/dialogue_manager/components/update_button.tscn b/addons/dialogue_manager/components/update_button.tscn deleted file mode 100644 index 6cff347..0000000 --- a/addons/dialogue_manager/components/update_button.tscn +++ /dev/null @@ -1,42 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://co8yl23idiwbi"] - -[ext_resource type="Script" uid="uid://cr1tt12dh5ecr" path="res://addons/dialogue_manager/components/update_button.gd" id="1_d2tpb"] -[ext_resource type="PackedScene" uid="uid://qdxrxv3c3hxk" path="res://addons/dialogue_manager/components/download_update_panel.tscn" id="2_iwm7r"] - -[node name="UpdateButton" type="Button"] -visible = false -offset_right = 8.0 -offset_bottom = 8.0 -theme_override_colors/font_color = Color(0, 0, 0, 1) -theme_override_colors/font_hover_color = Color(0, 0, 0, 1) -theme_override_colors/font_focus_color = Color(0, 0, 0, 1) -text = "v2.9.0 available" -flat = true -script = ExtResource("1_d2tpb") - -[node name="HTTPRequest" type="HTTPRequest" parent="."] - -[node name="DownloadDialog" type="AcceptDialog" parent="."] -title = "Download update" -size = Vector2i(400, 300) -unresizable = true -min_size = Vector2i(300, 250) -ok_button_text = "Close" - -[node name="DownloadUpdatePanel" parent="DownloadDialog" instance=ExtResource("2_iwm7r")] - -[node name="UpdateFailedDialog" type="AcceptDialog" parent="."] -dialog_text = "You have been updated to version 2.4.3" - -[node name="NeedsReloadDialog" type="ConfirmationDialog" parent="."] - -[node name="Timer" type="Timer" parent="."] -wait_time = 14400.0 - -[connection signal="pressed" from="." to="." method="_on_update_button_pressed"] -[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"] -[connection signal="close_requested" from="DownloadDialog" to="." method="_on_download_dialog_close_requested"] -[connection signal="failed" from="DownloadDialog/DownloadUpdatePanel" to="." method="_on_download_update_panel_failed"] -[connection signal="updated" from="DownloadDialog/DownloadUpdatePanel" to="." method="_on_download_update_panel_updated"] -[connection signal="confirmed" from="NeedsReloadDialog" to="." method="_on_needs_reload_dialog_confirmed"] -[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] diff --git a/addons/dialogue_manager/constants.gd b/addons/dialogue_manager/constants.gd deleted file mode 100644 index ea2844e..0000000 --- a/addons/dialogue_manager/constants.gd +++ /dev/null @@ -1,218 +0,0 @@ -class_name DMConstants extends RefCounted - - -const USER_CONFIG_PATH = "user://dialogue_manager_user_config.json" -const CACHE_PATH = "user://dialogue_manager_cache.json" - - -enum MutationBehaviour { - Wait, - DoNotWait, - Skip -} - -enum TranslationSource { - None, - Guess, - CSV, - PO -} - -# Token types - -const TOKEN_FUNCTION = &"function" -const TOKEN_DICTIONARY_REFERENCE = &"dictionary_reference" -const TOKEN_DICTIONARY_NESTED_REFERENCE = &"dictionary_nested_reference" -const TOKEN_GROUP = &"group" -const TOKEN_ARRAY = &"array" -const TOKEN_DICTIONARY = &"dictionary" -const TOKEN_PARENS_OPEN = &"parens_open" -const TOKEN_PARENS_CLOSE = &"parens_close" -const TOKEN_BRACKET_OPEN = &"bracket_open" -const TOKEN_BRACKET_CLOSE = &"bracket_close" -const TOKEN_BRACE_OPEN = &"brace_open" -const TOKEN_BRACE_CLOSE = &"brace_close" -const TOKEN_COLON = &"colon" -const TOKEN_COMPARISON = &"comparison" -const TOKEN_ASSIGNMENT = &"assignment" -const TOKEN_OPERATOR = &"operator" -const TOKEN_COMMA = &"comma" -const TOKEN_DOT = &"dot" -const TOKEN_CONDITION = &"condition" -const TOKEN_BOOL = &"bool" -const TOKEN_NOT = &"not" -const TOKEN_AND_OR = &"and_or" -const TOKEN_STRING = &"string" -const TOKEN_NUMBER = &"number" -const TOKEN_VARIABLE = &"variable" -const TOKEN_COMMENT = &"comment" - -const TOKEN_VALUE = &"value" -const TOKEN_ERROR = &"error" - -# Line types - -const TYPE_UNKNOWN = &"" -const TYPE_IMPORT = &"import" -const TYPE_COMMENT = &"comment" -const TYPE_RESPONSE = &"response" -const TYPE_TITLE = &"title" -const TYPE_CONDITION = &"condition" -const TYPE_WHILE = &"while" -const TYPE_MATCH = &"match" -const TYPE_WHEN = &"when" -const TYPE_MUTATION = &"mutation" -const TYPE_GOTO = &"goto" -const TYPE_DIALOGUE = &"dialogue" -const TYPE_RANDOM = &"random" -const TYPE_ERROR = &"error" - -# Line IDs - -const ID_NULL = &"" -const ID_ERROR = &"error" -const ID_ERROR_INVALID_TITLE = &"invalid title" -const ID_ERROR_TITLE_HAS_NO_BODY = &"title has no body" -const ID_END = &"end" -const ID_END_CONVERSATION = &"end!" - -# Errors - -const ERR_ERRORS_IN_IMPORTED_FILE = 100 -const ERR_FILE_ALREADY_IMPORTED = 101 -const ERR_DUPLICATE_IMPORT_NAME = 102 -const ERR_EMPTY_TITLE = 103 -const ERR_DUPLICATE_TITLE = 104 -const ERR_TITLE_INVALID_CHARACTERS = 106 -const ERR_UNKNOWN_TITLE = 107 -const ERR_INVALID_TITLE_REFERENCE = 108 -const ERR_TITLE_REFERENCE_HAS_NO_CONTENT = 109 -const ERR_INVALID_EXPRESSION = 110 -const ERR_UNEXPECTED_CONDITION = 111 -const ERR_DUPLICATE_ID = 112 -const ERR_MISSING_ID = 113 -const ERR_INVALID_INDENTATION = 114 -const ERR_INVALID_CONDITION_INDENTATION = 115 -const ERR_INCOMPLETE_EXPRESSION = 116 -const ERR_INVALID_EXPRESSION_FOR_VALUE = 117 -const ERR_UNKNOWN_LINE_SYNTAX = 118 -const ERR_TITLE_BEGINS_WITH_NUMBER = 119 -const ERR_UNEXPECTED_END_OF_EXPRESSION = 120 -const ERR_UNEXPECTED_FUNCTION = 121 -const ERR_UNEXPECTED_BRACKET = 122 -const ERR_UNEXPECTED_CLOSING_BRACKET = 123 -const ERR_MISSING_CLOSING_BRACKET = 124 -const ERR_UNEXPECTED_OPERATOR = 125 -const ERR_UNEXPECTED_COMMA = 126 -const ERR_UNEXPECTED_COLON = 127 -const ERR_UNEXPECTED_DOT = 128 -const ERR_UNEXPECTED_BOOLEAN = 129 -const ERR_UNEXPECTED_STRING = 130 -const ERR_UNEXPECTED_NUMBER = 131 -const ERR_UNEXPECTED_VARIABLE = 132 -const ERR_INVALID_INDEX = 133 -const ERR_UNEXPECTED_ASSIGNMENT = 134 -const ERR_UNKNOWN_USING = 135 -const ERR_EXPECTED_WHEN_OR_ELSE = 136 -const ERR_ONLY_ONE_ELSE_ALLOWED = 137 -const ERR_WHEN_MUST_BELONG_TO_MATCH = 138 -const ERR_CONCURRENT_LINE_WITHOUT_ORIGIN = 139 -const ERR_GOTO_NOT_ALLOWED_ON_CONCURRECT_LINES = 140 - - -## Get the error message -static func get_error_message(error: int) -> String: - match error: - ERR_ERRORS_IN_IMPORTED_FILE: - return translate(&"errors.import_errors") - ERR_FILE_ALREADY_IMPORTED: - return translate(&"errors.already_imported") - ERR_DUPLICATE_IMPORT_NAME: - return translate(&"errors.duplicate_import") - ERR_EMPTY_TITLE: - return translate(&"errors.empty_title") - ERR_DUPLICATE_TITLE: - return translate(&"errors.duplicate_title") - ERR_TITLE_INVALID_CHARACTERS: - return translate(&"errors.invalid_title_string") - ERR_TITLE_BEGINS_WITH_NUMBER: - return translate(&"errors.invalid_title_number") - ERR_UNKNOWN_TITLE: - return translate(&"errors.unknown_title") - ERR_INVALID_TITLE_REFERENCE: - return translate(&"errors.jump_to_invalid_title") - ERR_TITLE_REFERENCE_HAS_NO_CONTENT: - return translate(&"errors.title_has_no_content") - ERR_INVALID_EXPRESSION: - return translate(&"errors.invalid_expression") - ERR_UNEXPECTED_CONDITION: - return translate(&"errors.unexpected_condition") - ERR_DUPLICATE_ID: - return translate(&"errors.duplicate_id") - ERR_MISSING_ID: - return translate(&"errors.missing_id") - ERR_INVALID_INDENTATION: - return translate(&"errors.invalid_indentation") - ERR_INVALID_CONDITION_INDENTATION: - return translate(&"errors.condition_has_no_content") - ERR_INCOMPLETE_EXPRESSION: - return translate(&"errors.incomplete_expression") - ERR_INVALID_EXPRESSION_FOR_VALUE: - return translate(&"errors.invalid_expression_for_value") - ERR_FILE_NOT_FOUND: - return translate(&"errors.file_not_found") - ERR_UNEXPECTED_END_OF_EXPRESSION: - return translate(&"errors.unexpected_end_of_expression") - ERR_UNEXPECTED_FUNCTION: - return translate(&"errors.unexpected_function") - ERR_UNEXPECTED_BRACKET: - return translate(&"errors.unexpected_bracket") - ERR_UNEXPECTED_CLOSING_BRACKET: - return translate(&"errors.unexpected_closing_bracket") - ERR_MISSING_CLOSING_BRACKET: - return translate(&"errors.missing_closing_bracket") - ERR_UNEXPECTED_OPERATOR: - return translate(&"errors.unexpected_operator") - ERR_UNEXPECTED_COMMA: - return translate(&"errors.unexpected_comma") - ERR_UNEXPECTED_COLON: - return translate(&"errors.unexpected_colon") - ERR_UNEXPECTED_DOT: - return translate(&"errors.unexpected_dot") - ERR_UNEXPECTED_BOOLEAN: - return translate(&"errors.unexpected_boolean") - ERR_UNEXPECTED_STRING: - return translate(&"errors.unexpected_string") - ERR_UNEXPECTED_NUMBER: - return translate(&"errors.unexpected_number") - ERR_UNEXPECTED_VARIABLE: - return translate(&"errors.unexpected_variable") - ERR_INVALID_INDEX: - return translate(&"errors.invalid_index") - ERR_UNEXPECTED_ASSIGNMENT: - return translate(&"errors.unexpected_assignment") - ERR_UNKNOWN_USING: - return translate(&"errors.unknown_using") - ERR_EXPECTED_WHEN_OR_ELSE: - return translate(&"errors.expected_when_or_else") - ERR_ONLY_ONE_ELSE_ALLOWED: - return translate(&"errors.only_one_else_allowed") - ERR_WHEN_MUST_BELONG_TO_MATCH: - return translate(&"errors.when_must_belong_to_match") - ERR_CONCURRENT_LINE_WITHOUT_ORIGIN: - return translate(&"errors.concurrent_line_without_origin") - ERR_GOTO_NOT_ALLOWED_ON_CONCURRECT_LINES: - return translate(&"errors.goto_not_allowed_on_concurrect_lines") - - return translate(&"errors.unknown") - - -static func translate(string: String) -> String: - var base_path = new().get_script().resource_path.get_base_dir() - - var language: String = TranslationServer.get_tool_locale() - var translations_path: String = "%s/l10n/%s.po" % [base_path, language] - var fallback_translations_path: String = "%s/l10n/%s.po" % [base_path, TranslationServer.get_tool_locale().substr(0, 2)] - var en_translations_path: String = "%s/l10n/en.po" % base_path - var translations: Translation = load(translations_path if FileAccess.file_exists(translations_path) else (fallback_translations_path if FileAccess.file_exists(fallback_translations_path) else en_translations_path)) - return translations.get_message(string) diff --git a/addons/dialogue_manager/constants.gd.uid b/addons/dialogue_manager/constants.gd.uid deleted file mode 100644 index f431917..0000000 --- a/addons/dialogue_manager/constants.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b1oarbmjtyesf diff --git a/addons/dialogue_manager/dialogue_label.gd b/addons/dialogue_manager/dialogue_label.gd deleted file mode 100644 index da07b45..0000000 --- a/addons/dialogue_manager/dialogue_label.gd +++ /dev/null @@ -1,232 +0,0 @@ -@icon("./assets/icon.svg") - -@tool - -## A RichTextLabel specifically for use with [b]Dialogue Manager[/b] dialogue. -class_name DialogueLabel extends RichTextLabel - - -## Emitted for each letter typed out. -signal spoke(letter: String, letter_index: int, speed: float) - -## Emitted when typing paused for a `[wait]` -signal paused_typing(duration: float) - -## Emitted when the player skips the typing of dialogue. -signal skipped_typing() - -## Emitted when typing finishes. -signal finished_typing() - - -# The action to press to skip typing. -@export var skip_action: StringName = &"ui_cancel" - -## The speed with which the text types out. -@export var seconds_per_step: float = 0.02 - -## Automatically have a brief pause when these characters are encountered. -@export var pause_at_characters: String = ".?!" - -## Don't auto pause if the character after the pause is one of these. -@export var skip_pause_at_character_if_followed_by: String = ")\"" - -## Don't auto pause after these abbreviations (only if "." is in `pause_at_characters`).[br] -## Abbreviations are limitted to 5 characters in length [br] -## Does not support multi-period abbreviations (ex. "p.m.") -@export var skip_pause_at_abbreviations: PackedStringArray = ["Mr", "Mrs", "Ms", "Dr", "etc", "eg", "ex"] - -## The amount of time to pause when exposing a character present in `pause_at_characters`. -@export var seconds_per_pause_step: float = 0.3 - -var _already_mutated_indices: PackedInt32Array = [] - - -## The current line of dialogue. -var dialogue_line: - set(next_dialogue_line): - dialogue_line = next_dialogue_line - custom_minimum_size = Vector2.ZERO - text = "" - text = dialogue_line.text - get: - return dialogue_line - -## Whether the label is currently typing itself out. -var is_typing: bool = false: - set(value): - var is_finished: bool = is_typing != value and value == false - is_typing = value - if is_finished: - finished_typing.emit() - get: - return is_typing - -var _last_wait_index: int = -1 -var _last_mutation_index: int = -1 -var _waiting_seconds: float = 0 -var _is_awaiting_mutation: bool = false - - -func _process(delta: float) -> void: - if self.is_typing: - # Type out text - if visible_ratio < 1: - # See if we are waiting - if _waiting_seconds > 0: - _waiting_seconds = _waiting_seconds - delta - # If we are no longer waiting then keep typing - if _waiting_seconds <= 0: - _type_next(delta, _waiting_seconds) - else: - # Make sure any mutations at the end of the line get run - _mutate_inline_mutations(get_total_character_count()) - self.is_typing = false - - -func _unhandled_input(event: InputEvent) -> void: - # Note: this will no longer be reached if using Dialogue Manager > 2.32.2. To make skip handling - # simpler (so all of mouse/keyboard/joypad are together) it is now the responsibility of the - # dialogue balloon. - if self.is_typing and visible_ratio < 1 and InputMap.has_action(skip_action) and event.is_action_pressed(skip_action): - get_viewport().set_input_as_handled() - skip_typing() - - -## Start typing out the text -func type_out() -> void: - text = dialogue_line.text - visible_characters = 0 - visible_ratio = 0 - _waiting_seconds = 0 - _last_wait_index = -1 - _last_mutation_index = -1 - _already_mutated_indices.clear() - - self.is_typing = true - - # Allow typing listeners a chance to connect - await get_tree().process_frame - - if get_total_character_count() == 0: - self.is_typing = false - elif seconds_per_step == 0: - _mutate_remaining_mutations() - visible_characters = get_total_character_count() - self.is_typing = false - - -## Stop typing out the text and jump right to the end -func skip_typing() -> void: - _mutate_remaining_mutations() - visible_characters = get_total_character_count() - self.is_typing = false - skipped_typing.emit() - - -# Type out the next character(s) -func _type_next(delta: float, seconds_needed: float) -> void: - if _is_awaiting_mutation: return - - if visible_characters == get_total_character_count(): - return - - if _last_mutation_index != visible_characters: - _last_mutation_index = visible_characters - _mutate_inline_mutations(visible_characters) - if _is_awaiting_mutation: return - - var additional_waiting_seconds: float = _get_pause(visible_characters) - - # Pause on characters like "." - if _should_auto_pause(): - additional_waiting_seconds += seconds_per_pause_step - - # Pause at literal [wait] directives - if _last_wait_index != visible_characters and additional_waiting_seconds > 0: - _last_wait_index = visible_characters - _waiting_seconds += additional_waiting_seconds - paused_typing.emit(_get_pause(visible_characters)) - else: - visible_characters += 1 - if visible_characters <= get_total_character_count(): - spoke.emit(get_parsed_text()[visible_characters - 1], visible_characters - 1, _get_speed(visible_characters)) - # See if there's time to type out some more in this frame - seconds_needed += seconds_per_step * (1.0 / _get_speed(visible_characters)) - if seconds_needed > delta: - _waiting_seconds += seconds_needed - else: - _type_next(delta, seconds_needed) - - -# Get the pause for the current typing position if there is one -func _get_pause(at_index: int) -> float: - return dialogue_line.pauses.get(at_index, 0) - - -# Get the speed for the current typing position -func _get_speed(at_index: int) -> float: - var speed: float = 1 - for index in dialogue_line.speeds: - if index > at_index: - return speed - speed = dialogue_line.speeds[index] - return speed - - -# Run any inline mutations that haven't been run yet -func _mutate_remaining_mutations() -> void: - for i in range(visible_characters, get_total_character_count() + 1): - _mutate_inline_mutations(i) - - -# Run any mutations at the current typing position -func _mutate_inline_mutations(index: int) -> void: - for inline_mutation in dialogue_line.inline_mutations: - # inline mutations are an array of arrays in the form of [character index, resolvable function] - if inline_mutation[0] > index: - return - if inline_mutation[0] == index and not _already_mutated_indices.has(index): - _is_awaiting_mutation = true - # The DialogueManager can't be referenced directly here so we need to get it by its path - await Engine.get_singleton("DialogueManager")._mutate(inline_mutation[1], dialogue_line.extra_game_states, true) - _is_awaiting_mutation = false - - _already_mutated_indices.append(index) - - -# Determine if the current autopause character at the cursor should qualify to pause typing. -func _should_auto_pause() -> bool: - if visible_characters == 0: return false - - var parsed_text: String = get_parsed_text() - - # Avoid outofbounds when the label auto-translates and the text changes to one shorter while typing out - # Note: visible characters can be larger than parsed_text after a translation event - if visible_characters >= parsed_text.length(): return false - - # Ignore pause characters if they are next to a non-pause character - if parsed_text[visible_characters] in skip_pause_at_character_if_followed_by.split(): - return false - - # Ignore "." if it's between two numbers - if visible_characters > 3 and parsed_text[visible_characters - 1] == ".": - var possible_number: String = parsed_text.substr(visible_characters - 2, 3) - if str(float(possible_number)).pad_decimals(1) == possible_number: - return false - - # Ignore "." if it's used in an abbreviation - # Note: does NOT support multi-period abbreviations (ex. p.m.) - if "." in pause_at_characters and parsed_text[visible_characters - 1] == ".": - for abbreviation in skip_pause_at_abbreviations: - if visible_characters >= abbreviation.length(): - var previous_characters: String = parsed_text.substr(visible_characters - abbreviation.length() - 1, abbreviation.length()) - if previous_characters == abbreviation: - return false - - # Ignore two non-"." characters next to each other - var other_pause_characters: PackedStringArray = pause_at_characters.replace(".", "").split() - if visible_characters > 1 and parsed_text[visible_characters - 1] in other_pause_characters and parsed_text[visible_characters] in other_pause_characters: - return false - - return parsed_text[visible_characters - 1] in pause_at_characters.split() diff --git a/addons/dialogue_manager/dialogue_label.gd.uid b/addons/dialogue_manager/dialogue_label.gd.uid deleted file mode 100644 index 6bf86b1..0000000 --- a/addons/dialogue_manager/dialogue_label.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://g32um0mltv5d diff --git a/addons/dialogue_manager/dialogue_label.tscn b/addons/dialogue_manager/dialogue_label.tscn deleted file mode 100644 index 0095933..0000000 --- a/addons/dialogue_manager/dialogue_label.tscn +++ /dev/null @@ -1,19 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://ckvgyvclnwggo"] - -[ext_resource type="Script" uid="uid://g32um0mltv5d" path="res://addons/dialogue_manager/dialogue_label.gd" id="1_cital"] - -[node name="DialogueLabel" type="RichTextLabel"] -anchors_preset = 10 -anchor_right = 1.0 -grow_horizontal = 2 -mouse_filter = 1 -bbcode_enabled = true -fit_content = true -scroll_active = false -shortcut_keys_enabled = false -meta_underlined = false -hint_underlined = false -deselect_on_focus_loss_enabled = false -visible_characters_behavior = 1 -script = ExtResource("1_cital") -skip_pause_at_abbreviations = PackedStringArray("Mr", "Mrs", "Ms", "Dr", "etc", "eg", "ex") diff --git a/addons/dialogue_manager/dialogue_line.gd b/addons/dialogue_manager/dialogue_line.gd deleted file mode 100644 index 7213854..0000000 --- a/addons/dialogue_manager/dialogue_line.gd +++ /dev/null @@ -1,99 +0,0 @@ -## A line of dialogue returned from [code]DialogueManager[/code]. -class_name DialogueLine extends RefCounted - - -## The ID of this line -var id: String - -## The internal type of this dialogue object. One of [code]TYPE_DIALOGUE[/code] or [code]TYPE_MUTATION[/code] -var type: String = DMConstants.TYPE_DIALOGUE - -## The next line ID after this line. -var next_id: String = "" - -## The character name that is saying this line. -var character: String = "" - -## A dictionary of variable replacements fo the character name. Generally for internal use only. -var character_replacements: Array[Dictionary] = [] - -## The dialogue being spoken. -var text: String = "" - -## A dictionary of replacements for the text. Generally for internal use only. -var text_replacements: Array[Dictionary] = [] - -## The key to use for translating this line. -var translation_key: String = "" - -## A map for when and for how long to pause while typing out the dialogue text. -var pauses: Dictionary = {} - -## A map for speed changes when typing out the dialogue text. -var speeds: Dictionary = {} - -## A map of any mutations to run while typing out the dialogue text. -var inline_mutations: Array[Array] = [] - -## A list of responses attached to this line of dialogue. -var responses: Array = [] - -## A list of lines that are spoken simultaneously with this one. -var concurrent_lines: Array[DialogueLine] = [] - -## A list of any extra game states to check when resolving variables and mutations. -var extra_game_states: Array = [] - -## How long to show this line before advancing to the next. Either a float (of seconds), [code]"auto"[/code], or [code]null[/code]. -var time: String = "" - -## Any #tags that were included in the line -var tags: PackedStringArray = [] - -## The mutation details if this is a mutation line (where [code]type == TYPE_MUTATION[/code]). -var mutation: Dictionary = {} - -## The conditions to check before including this line in the flow of dialogue. If failed the line will be skipped over. -var conditions: Dictionary = {} - - -func _init(data: Dictionary = {}) -> void: - if data.size() > 0: - id = data.id - next_id = data.next_id - type = data.type - extra_game_states = data.get("extra_game_states", []) - - match type: - DMConstants.TYPE_DIALOGUE: - character = data.character - character_replacements = data.get("character_replacements", [] as Array[Dictionary]) - text = data.text - text_replacements = data.get("text_replacements", [] as Array[Dictionary]) - translation_key = data.get("translation_key", data.text) - pauses = data.get("pauses", {}) - speeds = data.get("speeds", {}) - inline_mutations = data.get("inline_mutations", [] as Array[Array]) - time = data.get("time", "") - tags = data.get("tags", []) - concurrent_lines = data.get("concurrent_lines", [] as Array[DialogueLine]) - - DMConstants.TYPE_MUTATION: - mutation = data.mutation - - -func _to_string() -> String: - match type: - DMConstants.TYPE_DIALOGUE: - return "" % [character, text] - DMConstants.TYPE_MUTATION: - return "" - return "" - - -func get_tag_value(tag_name: String) -> String: - var wrapped := "%s=" % tag_name - for t in tags: - if t.begins_with(wrapped): - return t.replace(wrapped, "").strip_edges() - return "" diff --git a/addons/dialogue_manager/dialogue_line.gd.uid b/addons/dialogue_manager/dialogue_line.gd.uid deleted file mode 100644 index 7ec7029..0000000 --- a/addons/dialogue_manager/dialogue_line.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://rhuq0eyf8ar2 diff --git a/addons/dialogue_manager/dialogue_manager.gd b/addons/dialogue_manager/dialogue_manager.gd deleted file mode 100644 index 868615e..0000000 --- a/addons/dialogue_manager/dialogue_manager.gd +++ /dev/null @@ -1,1422 +0,0 @@ -extends Node - -const DialogueResource = preload("./dialogue_resource.gd") -const DialogueLine = preload("./dialogue_line.gd") -const DialogueResponse = preload("./dialogue_response.gd") - -const DMConstants = preload("./constants.gd") -const Builtins = preload("./utilities/builtins.gd") -const DMSettings = preload("./settings.gd") -const DMCompiler = preload("./compiler/compiler.gd") -const DMCompilerResult = preload("./compiler/compiler_result.gd") -const DMResolvedLineData = preload("./compiler/resolved_line_data.gd") - - -## Emitted when a dialogue balloon is created and dialogue starts -signal dialogue_started(resource: DialogueResource) - -## Emitted when a title is encountered while traversing dialogue, usually when jumping from a -## goto line -signal passed_title(title: String) - -## Emitted when a line of dialogue is encountered. -signal got_dialogue(line: DialogueLine) - -## Emitted when a mutation is encountered. -signal mutated(mutation: Dictionary) - -## Emitted when some dialogue has reached the end. -signal dialogue_ended(resource: DialogueResource) - -## Used internally. -signal bridge_get_next_dialogue_line_completed(line: DialogueLine) - -## Used inernally -signal bridge_mutated() - - -## The list of globals that dialogue can query -var game_states: Array = [] - -## Allow dialogue to call singletons -var include_singletons: bool = true - -## Allow dialogue to call static methods/properties on classes -var include_classes: bool = true - -## Manage translation behaviour -var translation_source: DMConstants.TranslationSource = DMConstants.TranslationSource.Guess - -## Used to resolve the current scene. Override if your game manages the current scene itself. -var get_current_scene: Callable = func(): - var current_scene: Node = Engine.get_main_loop().current_scene - if current_scene == null: - current_scene = Engine.get_main_loop().root.get_child(Engine.get_main_loop().root.get_child_count() - 1) - return current_scene - -var _has_loaded_autoloads: bool = false -var _autoloads: Dictionary = {} - -var _node_properties: Array = [] -var _method_info_cache: Dictionary = {} - -var _dotnet_dialogue_manager: RefCounted - - -func _ready() -> void: - # Cache the known Node2D properties - _node_properties = ["Script Variables"] - var temp_node: Node2D = Node2D.new() - for property in temp_node.get_property_list(): - _node_properties.append(property.name) - temp_node.free() - - # Make the dialogue manager available as a singleton - if not Engine.has_singleton("DialogueManager"): - Engine.register_singleton("DialogueManager", self) - - -## Step through lines and run any mutations until we either hit some dialogue or the end of the conversation -func get_next_dialogue_line(resource: DialogueResource, key: String = "", extra_game_states: Array = [], mutation_behaviour: DMConstants.MutationBehaviour = DMConstants.MutationBehaviour.Wait) -> DialogueLine: - # You have to provide a valid dialogue resource - if resource == null: - assert(false, DMConstants.translate(&"runtime.no_resource")) - if resource.lines.size() == 0: - assert(false, DMConstants.translate(&"runtime.no_content").format({ file_path = resource.resource_path })) - - # Inject any "using" states into the game_states - for state_name in resource.using_states: - var autoload = Engine.get_main_loop().root.get_node_or_null(state_name) - if autoload == null: - printerr(DMConstants.translate(&"runtime.unknown_autoload").format({ autoload = state_name })) - else: - extra_game_states = [autoload] + extra_game_states - - # Inject "self" into the extra game states. - extra_game_states = [{ "self": resource }] + extra_game_states - - # Get the line data - var dialogue: DialogueLine = await get_line(resource, key, extra_game_states) - - # If our dialogue is nothing then we hit the end - if not _is_valid(dialogue): - dialogue_ended.emit.call_deferred(resource) - return null - - # Run the mutation if it is one - if dialogue.type == DMConstants.TYPE_MUTATION: - var actual_next_id: String = dialogue.next_id.split("|")[0] - match mutation_behaviour: - DMConstants.MutationBehaviour.Wait: - await _mutate(dialogue.mutation, extra_game_states) - DMConstants.MutationBehaviour.DoNotWait: - _mutate(dialogue.mutation, extra_game_states) - DMConstants.MutationBehaviour.Skip: - pass - if actual_next_id in [DMConstants.ID_END_CONVERSATION, DMConstants.ID_NULL, null]: - # End the conversation - dialogue_ended.emit.call_deferred(resource) - return null - else: - return await get_next_dialogue_line(resource, dialogue.next_id, extra_game_states, mutation_behaviour) - else: - got_dialogue.emit(dialogue) - return dialogue - - -## Get a line by its ID -func get_line(resource: DialogueResource, key: String, extra_game_states: Array) -> DialogueLine: - key = key.strip_edges() - - # See if we were given a stack instead of just the one key - var stack: Array = key.split("|") - key = stack.pop_front() - var id_trail: String = "" if stack.size() == 0 else "|" + "|".join(stack) - - # Key is blank so just use the first title (or start of file) - if key == null or key == "": - if resource.first_title.is_empty(): - key = resource.lines.keys()[0] - else: - key = resource.first_title - - # See if we just ended the conversation - if key in [DMConstants.ID_END, DMConstants.ID_NULL, null]: - if stack.size() > 0: - return await get_line(resource, "|".join(stack), extra_game_states) - else: - return null - elif key == DMConstants.ID_END_CONVERSATION: - return null - - # See if it is a title - if key.begins_with("~ "): - key = key.substr(2) - if resource.titles.has(key): - key = resource.titles.get(key) - - if key in resource.titles.values(): - passed_title.emit(resource.titles.find_key(key)) - - if not resource.lines.has(key): - assert(false, DMConstants.translate(&"errors.key_not_found").format({ key = key })) - - var data: Dictionary = resource.lines.get(key) - - # If next_id is an expression we need to resolve it. - if data.has(&"next_id_expression"): - data.next_id = await _resolve(data.next_id_expression, extra_game_states) - - # This title key points to another title key so we should jump there instead - if data.type == DMConstants.TYPE_TITLE and data.next_id in resource.titles.values(): - return await get_line(resource, data.next_id + id_trail, extra_game_states) - - # Handle match statements - if data.type == DMConstants.TYPE_MATCH: - var value = await _resolve_condition_value(data, extra_game_states) - var else_cases: Array[Dictionary] = data.cases.filter(func(s): return s.has("is_else")) - var else_case: Dictionary = {} if else_cases.size() == 0 else else_cases.front() - var next_id: String = "" - for case in data.cases: - if case == else_case: - continue - elif await _check_case_value(value, case, extra_game_states): - next_id = case.next_id - # Nothing matched so check for else case - if next_id == "": - if not else_case.is_empty(): - next_id = else_case.next_id - else: - next_id = data.next_id_after - return await get_line(resource, next_id + id_trail, extra_game_states) - - # Check for weighted random lines. - if data.has(&"siblings"): - # Only count siblings that pass their condition (if they have one). - var successful_siblings: Array = data.siblings.filter(func(sibling): return not sibling.has("condition") or await _check_condition(sibling, extra_game_states)) - var target_weight: float = randf_range(0, successful_siblings.reduce(func(total, sibling): return total + sibling.weight, 0)) - var cummulative_weight: float = 0 - for sibling in successful_siblings: - if target_weight < cummulative_weight + sibling.weight: - data = resource.lines.get(sibling.id) - break - else: - cummulative_weight += sibling.weight - - # Find any simultaneously said lines. - var concurrent_lines: Array[DialogueLine] = [] - if data.has(&"concurrent_lines"): - # If the list includes this line then it isn't the origin line so ignore it. - if not data.concurrent_lines.has(data.id): - for concurrent_id: String in data.concurrent_lines: - var concurrent_line: DialogueLine = await get_line(resource, concurrent_id, extra_game_states) - if concurrent_line: - concurrent_lines.append(concurrent_line) - - # If this line is blank and it's the last line then check for returning snippets. - if data.type in [DMConstants.TYPE_COMMENT, DMConstants.TYPE_UNKNOWN]: - if data.next_id in [DMConstants.ID_END, DMConstants.ID_NULL, null]: - if stack.size() > 0: - return await get_line(resource, "|".join(stack), extra_game_states) - else: - return null - else: - return await get_line(resource, data.next_id + id_trail, extra_game_states) - - # If the line is a random block then go to the start of the block. - elif data.type == DMConstants.TYPE_RANDOM: - data = resource.lines.get(data.next_id) - - # Check conditions. - elif data.type in [DMConstants.TYPE_CONDITION, DMConstants.TYPE_WHILE]: - # "else" will have no actual condition. - if await _check_condition(data, extra_game_states): - return await get_line(resource, data.next_id + id_trail, extra_game_states) - elif data.has("next_sibling_id") and not data.next_sibling_id.is_empty(): - return await get_line(resource, data.next_sibling_id + id_trail, extra_game_states) - else: - return await get_line(resource, data.next_id_after + id_trail, extra_game_states) - - # Evaluate jumps. - elif data.type == DMConstants.TYPE_GOTO: - if data.is_snippet and not id_trail.begins_with("|" + data.next_id_after): - id_trail = "|" + data.next_id_after + id_trail - return await get_line(resource, data.next_id + id_trail, extra_game_states) - - elif data.type == DMConstants.TYPE_DIALOGUE: - if not data.has(&"id"): - data.id = key - - # Set up a line object. - var line: DialogueLine = await create_dialogue_line(data, extra_game_states) - line.concurrent_lines = concurrent_lines - - # If the jump point somehow has no content then just end. - if not line: return null - - # If we are the first of a list of responses then get the other ones. - if data.type == DMConstants.TYPE_RESPONSE: - # Note: For some reason C# has occasional issues with using the responses property directly - # so instead we use set and get here. - line.set(&"responses", await _get_responses(data.get(&"responses", []), resource, id_trail, extra_game_states)) - return line - - # Inject the next node's responses if they have any. - if resource.lines.has(line.next_id): - var next_line: Dictionary = resource.lines.get(line.next_id) - - # If the response line is marked as a title then make sure to emit the passed_title signal. - if line.next_id in resource.titles.values(): - passed_title.emit(resource.titles.find_key(line.next_id)) - - # If the responses come from a snippet then we need to come back here afterwards. - if next_line.type == DMConstants.TYPE_GOTO and next_line.is_snippet and not id_trail.begins_with("|" + next_line.next_id_after): - id_trail = "|" + next_line.next_id_after + id_trail - - # If the next line is a title then check where it points to see if that is a set of responses. - while [DMConstants.TYPE_TITLE, DMConstants.TYPE_GOTO].has(next_line.type) and resource.lines.has(next_line.next_id): - next_line = resource.lines.get(next_line.next_id) - - if next_line != null and next_line.type == DMConstants.TYPE_RESPONSE: - # Note: For some reason C# has occasional issues with using the responses property directly - # so instead we use set and get here. - line.set(&"responses", await _get_responses(next_line.get(&"responses", []), resource, id_trail, extra_game_states)) - - line.next_id = "|".join(stack) if line.next_id == DMConstants.ID_NULL else line.next_id + id_trail - return line - -## Replace any variables, etc in the text. -func get_resolved_line_data(data: Dictionary, extra_game_states: Array = []) -> DMResolvedLineData: - var text: String = translate(data) - - # Resolve variables - for replacement in data.get(&"text_replacements", [] as Array[Dictionary]): - var value = await _resolve(replacement.expression.duplicate(true), extra_game_states) - var index: int = text.find(replacement.value_in_text) - if index == -1: - # The replacement wasn't found but maybe the regular quotes have been replaced - # by special quotes while translating. - index = text.replace("“", "\"").replace("”", "\"").find(replacement.value_in_text) - if index > -1: - text = text.substr(0, index) + str(value) + text.substr(index + replacement.value_in_text.length()) - - var compilation: DMCompilation = DMCompilation.new() - - # Resolve random groups - for found in compilation.regex.INLINE_RANDOM_REGEX.search_all(text): - var options = found.get_string(&"options").split(&"|") - text = text.replace(&"[[%s]]" % found.get_string(&"options"), options[randi_range(0, options.size() - 1)]) - - # Do a pass on the markers to find any conditionals - var markers: DMResolvedLineData = DMResolvedLineData.new(text) - - # Resolve any conditionals and update marker positions as needed - if data.type == DMConstants.TYPE_DIALOGUE: - var resolved_text: String = markers.text - var conditionals: Array[RegExMatch] = compilation.regex.INLINE_CONDITIONALS_REGEX.search_all(resolved_text) - var replacements: Array = [] - for conditional in conditionals: - var condition_raw: String = conditional.strings[conditional.names.condition] - var body: String = conditional.strings[conditional.names.body] - var body_else: String = "" - if &"[else]" in body: - var bits = body.split(&"[else]") - body = bits[0] - body_else = bits[1] - var condition: Dictionary = compilation.extract_condition("if " + condition_raw, false, 0) - # If the condition fails then use the else of "" - if not await _check_condition({ condition = condition }, extra_game_states): - body = body_else - replacements.append({ - start = conditional.get_start(), - end = conditional.get_end(), - string = conditional.get_string(), - body = body - }) - - for i in range(replacements.size() - 1, -1, -1): - var r: Dictionary = replacements[i] - resolved_text = resolved_text.substr(0, r.start) + r.body + resolved_text.substr(r.end, 9999) - # Move any other markers now that the text has changed - var offset: int = r.end - r.start - r.body.length() - for key in [&"pauses", &"speeds", &"time"]: - if markers.get(key) == null: continue - var marker = markers.get(key) - var next_marker: Dictionary = {} - for index in marker: - if index < r.start: - next_marker[index] = marker[index] - elif index > r.start: - next_marker[index - offset] = marker[index] - markers.set(key, next_marker) - var mutations: Array[Array] = markers.mutations - var next_mutations: Array[Array] = [] - for mutation in mutations: - var index = mutation[0] - if index < r.start: - next_mutations.append(mutation) - elif index > r.start: - next_mutations.append([index - offset, mutation[1]]) - markers.mutations = next_mutations - - markers.text = resolved_text - - return markers - - -## Replace any variables, etc in the character name -func get_resolved_character(data: Dictionary, extra_game_states: Array = []) -> String: - var character: String = data.get(&"character", "") - - # Resolve variables - for replacement in data.get(&"character_replacements", []): - var value = await _resolve(replacement.expression.duplicate(true), extra_game_states) - var index: int = character.find(replacement.value_in_text) - if index > -1: - character = character.substr(0, index) + str(value) + character.substr(index + replacement.value_in_text.length()) - - # Resolve random groups - var random_regex: RegEx = RegEx.new() - random_regex.compile("\\[\\[(?.*?)\\]\\]") - for found in random_regex.search_all(character): - var options = found.get_string(&"options").split("|") - character = character.replace("[[%s]]" % found.get_string(&"options"), options[randi_range(0, options.size() - 1)]) - - return character - - -## Generate a dialogue resource on the fly from some text -func create_resource_from_text(text: String) -> Resource: - var result: DMCompilerResult = DMCompiler.compile_string(text, "") - - if result.errors.size() > 0: - printerr(DMConstants.translate(&"runtime.errors").format({ count = result.errors.size() })) - for error in result.errors: - printerr(DMConstants.translate(&"runtime.error_detail").format({ - line = error.line_number + 1, - message = DMConstants.get_error_message(error.error) - })) - assert(false, DMConstants.translate(&"runtime.errors_see_details").format({ count = result.errors.size() })) - - var resource: DialogueResource = DialogueResource.new() - resource.using_states = result.using_states - resource.titles = result.titles - resource.first_title = result.first_title - resource.character_names = result.character_names - resource.lines = result.lines - resource.raw_text = text - - return resource - - -#region Balloon helpers - - -## Show the example balloon -func show_example_dialogue_balloon(resource: DialogueResource, title: String = "", extra_game_states: Array = []) -> CanvasLayer: - var balloon: Node = load(_get_example_balloon_path()).instantiate() - _start_balloon.call_deferred(balloon, resource, title, extra_game_states) - return balloon - - -## Show the configured dialogue balloon -func show_dialogue_balloon(resource: DialogueResource, title: String = "", extra_game_states: Array = []) -> Node: - var balloon_path: String = DMSettings.get_setting(DMSettings.BALLOON_PATH, _get_example_balloon_path()) - if not ResourceLoader.exists(balloon_path): - balloon_path = _get_example_balloon_path() - return show_dialogue_balloon_scene(balloon_path, resource, title, extra_game_states) - - -## Show a given balloon scene -func show_dialogue_balloon_scene(balloon_scene, resource: DialogueResource, title: String = "", extra_game_states: Array = []) -> Node: - if balloon_scene is String: - balloon_scene = load(balloon_scene) - if balloon_scene is PackedScene: - balloon_scene = balloon_scene.instantiate() - - var balloon: Node = balloon_scene - _start_balloon.call_deferred(balloon, resource, title, extra_game_states) - return balloon - - -# Call "start" on the given balloon. -func _start_balloon(balloon: Node, resource: DialogueResource, title: String, extra_game_states: Array) -> void: - get_current_scene.call().add_child(balloon) - - if balloon.has_method(&"start"): - balloon.start(resource, title, extra_game_states) - elif balloon.has_method(&"Start"): - balloon.Start(resource, title, extra_game_states) - else: - assert(false, DMConstants.translate(&"runtime.dialogue_balloon_missing_start_method")) - - dialogue_started.emit(resource) - - -# Get the path to the example balloon -func _get_example_balloon_path() -> String: - var is_small_window: bool = ProjectSettings.get_setting("display/window/size/viewport_width") < 400 - var balloon_path: String = "/example_balloon/small_example_balloon.tscn" if is_small_window else "/example_balloon/example_balloon.tscn" - return get_script().resource_path.get_base_dir() + balloon_path - - -#endregion - -#region dotnet bridge - - -func _get_dotnet_dialogue_manager() -> RefCounted: - if not is_instance_valid(_dotnet_dialogue_manager): - _dotnet_dialogue_manager = load(get_script().resource_path.get_base_dir() + "/DialogueManager.cs").new() - return _dotnet_dialogue_manager - - -func _bridge_get_new_instance() -> Node: - # For some reason duplicating the node with its signals doesn't work so we have to copy them over manually - var instance = new() - for s: Dictionary in dialogue_started.get_connections(): - instance.dialogue_started.connect(s.callable) - for s: Dictionary in passed_title.get_connections(): - instance.passed_title.connect(s.callable) - for s: Dictionary in got_dialogue.get_connections(): - instance.got_dialogue.connect(s.callable) - for s: Dictionary in mutated.get_connections(): - instance.mutated.connect(s.callable) - for s: Dictionary in dialogue_ended.get_connections(): - instance.dialogue_ended.connect(s.callable) - instance.get_current_scene = get_current_scene - return instance - - -func _bridge_get_next_dialogue_line(resource: DialogueResource, key: String, extra_game_states: Array = []) -> void: - # dotnet needs at least one await tick of the signal gets called too quickly - await Engine.get_main_loop().process_frame - - var line = await get_next_dialogue_line(resource, key, extra_game_states) - bridge_get_next_dialogue_line_completed.emit(line) - - -func _bridge_mutate(mutation: Dictionary, extra_game_states: Array, is_inline_mutation: bool = false) -> void: - await _mutate(mutation, extra_game_states, is_inline_mutation) - bridge_mutated.emit() - - -#endregion - -#region Internal helpers - - -# Show a message or crash with error -func show_error_for_missing_state_value(message: String, will_show: bool = true) -> void: - if not will_show: return - - if DMSettings.get_setting(DMSettings.IGNORE_MISSING_STATE_VALUES, false): - push_error(message) - elif will_show: - # If you're here then you're missing a method or property in your game state. The error - # message down in the debugger will give you some more information. - assert(false, message) - - -# Translate a string -func translate(data: Dictionary) -> String: - if translation_source == DMConstants.TranslationSource.None: - return data.text - - var translation_key: String = data.get(&"translation_key", data.text) - - if translation_key == "" or translation_key == data.text: - return tr(data.text) - else: - # Line IDs work slightly differently depending on whether the translation came from a - # CSV or a PO file. CSVs use the line ID (or the line itself) as the translatable string - # whereas POs use the ID as context and the line itself as the translatable string. - match translation_source: - DMConstants.TranslationSource.PO: - return tr(data.text, StringName(translation_key)) - - DMConstants.TranslationSource.CSV: - return tr(translation_key) - - DMConstants.TranslationSource.Guess: - var translation_files: Array = ProjectSettings.get_setting(&"internationalization/locale/translations") - if translation_files.filter(func(f: String): return f.get_extension() in [&"po", &"mo"]).size() > 0: - # Assume PO - return tr(data.text, StringName(translation_key)) - else: - # Assume CSV - return tr(translation_key) - - return tr(translation_key) - - -# Create a line of dialogue -func create_dialogue_line(data: Dictionary, extra_game_states: Array) -> DialogueLine: - match data.type: - DMConstants.TYPE_DIALOGUE: - var resolved_data: DMResolvedLineData = await get_resolved_line_data(data, extra_game_states) - return DialogueLine.new({ - id = data.get(&"id", ""), - type = DMConstants.TYPE_DIALOGUE, - next_id = data.next_id, - character = await get_resolved_character(data, extra_game_states), - character_replacements = data.get(&"character_replacements", [] as Array[Dictionary]), - text = resolved_data.text, - text_replacements = data.get(&"text_replacements", [] as Array[Dictionary]), - translation_key = data.get(&"translation_key", data.text), - pauses = resolved_data.pauses, - speeds = resolved_data.speeds, - inline_mutations = resolved_data.mutations, - time = resolved_data.time, - tags = data.get(&"tags", []), - extra_game_states = extra_game_states - }) - - DMConstants.TYPE_RESPONSE: - return DialogueLine.new({ - id = data.get(&"id", ""), - type = DMConstants.TYPE_RESPONSE, - next_id = data.next_id, - tags = data.get(&"tags", []), - extra_game_states = extra_game_states - }) - - DMConstants.TYPE_MUTATION: - return DialogueLine.new({ - id = data.get(&"id", ""), - type = DMConstants.TYPE_MUTATION, - next_id = data.next_id, - mutation = data.mutation, - extra_game_states = extra_game_states - }) - - return null - - -# Create a response -func create_response(data: Dictionary, extra_game_states: Array) -> DialogueResponse: - var resolved_data: DMResolvedLineData = await get_resolved_line_data(data, extra_game_states) - return DialogueResponse.new({ - id = data.get(&"id", ""), - type = DMConstants.TYPE_RESPONSE, - next_id = data.next_id, - is_allowed = data.is_allowed, - character = await get_resolved_character(data, extra_game_states), - character_replacements = data.get(&"character_replacements", [] as Array[Dictionary]), - text = resolved_data.text, - text_replacements = data.get(&"text_replacements", [] as Array[Dictionary]), - tags = data.get(&"tags", []), - translation_key = data.get(&"translation_key", data.text) - }) - - -# Get the current game states -func _get_game_states(extra_game_states: Array) -> Array: - if not _has_loaded_autoloads: - _has_loaded_autoloads = true - # Add any autoloads to a generic state so we can refer to them by name - for child in Engine.get_main_loop().root.get_children(): - # Ignore the dialogue manager - if child.name == &"DialogueManager": continue - # Ignore the current main scene - if Engine.get_main_loop().current_scene and child.name == Engine.get_main_loop().current_scene.name: continue - # Add the node to our known autoloads - _autoloads[child.name] = child - game_states = [_autoloads] - # Add any other state shortcuts from settings - for node_name in DMSettings.get_setting(DMSettings.STATE_AUTOLOAD_SHORTCUTS, ""): - var state: Node = Engine.get_main_loop().root.get_node_or_null(node_name) - if state: - game_states.append(state) - - var current_scene: Node = get_current_scene.call() - var unique_states: Array = [] - for state in extra_game_states + [current_scene] + game_states: - if state != null and not unique_states.has(state): - unique_states.append(state) - return unique_states - - -# Check if a condition is met -func _check_condition(data: Dictionary, extra_game_states: Array) -> bool: - return bool(await _resolve_condition_value(data, extra_game_states)) - - -# Resolve a condition's expression value -func _resolve_condition_value(data: Dictionary, extra_game_states: Array) -> Variant: - if data.get(&"condition", null) == null: return true - if data.condition.is_empty(): return true - - return await _resolve(data.condition.expression.duplicate(true), extra_game_states) - - -# Check if a match value matches a case value -func _check_case_value(match_value: Variant, data: Dictionary, extra_game_states: Array) -> bool: - if data.get(&"condition", null) == null: return true - if data.condition.is_empty(): return true - - var expression: Array[Dictionary] = data.condition.expression.duplicate(true) - - # If the when is a comparison when insert the match value as the first value to compare to - var already_compared: bool = false - if expression[0].type == DMConstants.TOKEN_COMPARISON: - expression.insert(0, { - type = DMConstants.TOKEN_VALUE, - value = match_value - }) - already_compared = true - - var resolved_value = await _resolve(expression, extra_game_states) - - if already_compared: - return resolved_value - else: - return match_value == resolved_value - - -# Make a change to game state or run a method -func _mutate(mutation: Dictionary, extra_game_states: Array, is_inline_mutation: bool = false) -> void: - var expression: Array[Dictionary] = mutation.expression - - # Handle built in mutations - if expression[0].type == DMConstants.TOKEN_FUNCTION and expression[0].function in [&"wait", &"Wait", &"debug", &"Debug"]: - var args: Array = await _resolve_each(expression[0].value, extra_game_states) - match expression[0].function: - &"wait", &"Wait": - mutated.emit(mutation.merged({ is_inline = is_inline_mutation })) - await Engine.get_main_loop().create_timer(float(args[0])).timeout - return - - &"debug", &"Debug": - prints("Debug:", args) - await Engine.get_main_loop().process_frame - - # Or pass through to the resolver - else: - if not _mutation_contains_assignment(mutation.expression) and not is_inline_mutation: - mutated.emit(mutation.merged({ is_inline = is_inline_mutation })) - - if mutation.get("is_blocking", true): - await _resolve(mutation.expression.duplicate(true), extra_game_states) - return - else: - _resolve(mutation.expression.duplicate(true), extra_game_states) - - # Wait one frame to give the dialogue handler a chance to yield - await Engine.get_main_loop().process_frame - - -# Check if a mutation contains an assignment token. -func _mutation_contains_assignment(mutation: Array) -> bool: - for token in mutation: - if token.type == DMConstants.TOKEN_ASSIGNMENT: - return true - return false - - -# Replace an array of line IDs with their response prompts -func _get_responses(ids: Array, resource: DialogueResource, id_trail: String, extra_game_states: Array) -> Array[DialogueResponse]: - var responses: Array[DialogueResponse] = [] - for id in ids: - var data: Dictionary = resource.lines.get(id).duplicate(true) - data.is_allowed = await _check_condition(data, extra_game_states) - var response: DialogueResponse = await create_response(data, extra_game_states) - response.next_id += id_trail - responses.append(response) - - return responses - - -# Get a value on the current scene or game state -func _get_state_value(property: String, extra_game_states: Array): - # Special case for static primitive calls - if property == "Color": - return Color() - elif property == "Vector2": - return Vector2.ZERO - elif property == "Vector3": - return Vector3.ZERO - elif property == "Vector4": - return Vector4.ZERO - elif property == "Quaternion": - return Quaternion() - - var expression = Expression.new() - if expression.parse(property) != OK: - assert(false, DMConstants.translate(&"runtime.invalid_expression").format({ expression = property, error = expression.get_error_text() })) - - # Warn about possible name collisions - _warn_about_state_name_collisions(property, extra_game_states) - - for state in _get_game_states(extra_game_states): - if typeof(state) == TYPE_DICTIONARY: - if state.has(property): - return state.get(property) - else: - var result = expression.execute([], state, false) - if not expression.has_execute_failed(): - return result - - if include_singletons and Engine.has_singleton(property): - return Engine.get_singleton(property) - - if include_classes: - for class_data in ProjectSettings.get_global_class_list(): - if class_data.get(&"class") == property: - return load(class_data.path).new() - - show_error_for_missing_state_value(DMConstants.translate(&"runtime.property_not_found").format({ property = property, states = _get_state_shortcut_names(extra_game_states) })) - - -# Print warnings for top-level state name collisions. -func _warn_about_state_name_collisions(target_key: String, extra_game_states: Array) -> void: - # Don't run the check if this is a release build - if not OS.is_debug_build(): return - # Also don't run if the setting is off - if not DMSettings.get_setting(DMSettings.WARN_ABOUT_METHOD_PROPERTY_OR_SIGNAL_NAME_CONFLICTS, false): return - - # Get the list of state shortcuts. - var state_shortcuts: Array = [] - for node_name in DMSettings.get_setting(DMSettings.STATE_AUTOLOAD_SHORTCUTS, ""): - var state: Node = Engine.get_main_loop().root.get_node_or_null(node_name) - if state: - state_shortcuts.append(state) - - # Check any top level names for a collision - var states_with_key: Array = [] - for state in extra_game_states + [get_current_scene.call()] + state_shortcuts: - if state is Dictionary: - if state.keys().has(target_key): - states_with_key.append("Dictionary") - else: - var script: Script = (state as Object).get_script() - if script == null: - continue - - for method in script.get_script_method_list(): - if method.name == target_key and not states_with_key.has(state.name): - states_with_key.append(state.name) - break - - for property in script.get_script_property_list(): - if property.name == target_key and not states_with_key.has(state.name): - states_with_key.append(state.name) - break - - for signal_info in script.get_script_signal_list(): - if signal_info.name == target_key and not states_with_key.has(state.name): - states_with_key.append(state.name) - break - - if states_with_key.size() > 1: - push_warning(DMConstants.translate(&"runtime.top_level_states_share_name").format({ states = ", ".join(states_with_key), key = target_key })) - - -# Set a value on the current scene or game state -func _set_state_value(property: String, value, extra_game_states: Array) -> void: - for state in _get_game_states(extra_game_states): - if typeof(state) == TYPE_DICTIONARY: - if state.has(property): - state[property] = value - return - elif _thing_has_property(state, property): - state.set(property, value) - return - - if property.to_snake_case() != property: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.property_not_found_missing_export").format({ property = property, states = _get_state_shortcut_names(extra_game_states) })) - else: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.property_not_found").format({ property = property, states = _get_state_shortcut_names(extra_game_states) })) - - -# Get the list of state shortcut names -func _get_state_shortcut_names(extra_game_states: Array) -> String: - var states = _get_game_states(extra_game_states) - states.erase(_autoloads) - return ", ".join(states.map(func(s): return "\"%s\"" % (s.name if "name" in s else s))) - - -# Resolve an array of expressions. -func _resolve_each(array: Array, extra_game_states: Array) -> Array: - var results: Array = [] - for item in array: - if not item[0].type in [DMConstants.TOKEN_BRACE_CLOSE, DMConstants.TOKEN_BRACKET_CLOSE, DMConstants.TOKEN_PARENS_CLOSE]: - results.append(await _resolve(item.duplicate(true), extra_game_states)) - return results - - -# Collapse any expressions -func _resolve(tokens: Array, extra_game_states: Array): - var i: int = 0 - var limit: int = 0 - - # Handle groups first - for token in tokens: - if token.type == DMConstants.TOKEN_GROUP: - token.type = DMConstants.TOKEN_VALUE - token.value = await _resolve(token.value, extra_game_states) - - # Then variables/methods - i = 0 - limit = 0 - while i < tokens.size() and limit < 1000: - limit += 1 - var token: Dictionary = tokens[i] - - if token.type == DMConstants.TOKEN_FUNCTION: - var function_name: String = token.function - var args = await _resolve_each(token.value, extra_game_states) - if tokens[i - 1].type == DMConstants.TOKEN_DOT: - # If we are calling a deeper function then we need to collapse the - # value into the thing we are calling the function on - var caller: Dictionary = tokens[i - 2] - if Builtins.is_supported(caller.value): - caller.type = DMConstants.TOKEN_VALUE - caller.value = Builtins.resolve_method(caller.value, function_name, args) - tokens.remove_at(i) - tokens.remove_at(i - 1) - i -= 2 - elif _thing_has_method(caller.value, function_name, args): - caller.type = DMConstants.TOKEN_VALUE - caller.value = await _resolve_thing_method(caller.value, function_name, args) - tokens.remove_at(i) - tokens.remove_at(i - 1) - i -= 2 - else: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.method_not_callable").format({ method = function_name, object = str(caller.value) })) - else: - var found: bool = false - match function_name: - &"str": - token.type = DMConstants.TOKEN_VALUE - token.value = str(args[0]) - found = true - &"Vector2": - token.type = DMConstants.TOKEN_VALUE - token.value = Vector2(args[0], args[1]) - found = true - &"Vector2i": - token.type = DMConstants.TOKEN_VALUE - token.value = Vector2i(args[0], args[1]) - found = true - &"Vector3": - token.type = DMConstants.TOKEN_VALUE - token.value = Vector3(args[0], args[1], args[2]) - found = true - &"Vector3i": - token.type = DMConstants.TOKEN_VALUE - token.value = Vector3i(args[0], args[1], args[2]) - found = true - &"Vector4": - token.type = DMConstants.TOKEN_VALUE - token.value = Vector4(args[0], args[1], args[2], args[3]) - found = true - &"Vector4i": - token.type = DMConstants.TOKEN_VALUE - token.value = Vector4i(args[0], args[1], args[2], args[3]) - found = true - &"Quaternion": - token.type = DMConstants.TOKEN_VALUE - token.value = Quaternion(args[0], args[1], args[2], args[3]) - found = true - &"Callable": - token.type = DMConstants.TOKEN_VALUE - match args.size(): - 0: - token.value = Callable() - 1: - token.value = Callable(args[0]) - 2: - token.value = Callable(args[0], args[1]) - found = true - &"Color": - token.type = DMConstants.TOKEN_VALUE - match args.size(): - 0: - token.value = Color() - 1: - token.value = Color(args[0]) - 2: - token.value = Color(args[0], args[1]) - 3: - token.value = Color(args[0], args[1], args[2]) - 4: - token.value = Color(args[0], args[1], args[2], args[3]) - found = true - &"load", &"Load": - token.type = DMConstants.TOKEN_VALUE - token.value = load(args[0]) - found = true - &"roll_dice", &"RollDice": - token.type = DMConstants.TOKEN_VALUE - token.value = randi_range(1, args[0]) - found = true - _: - # Check for top level name conflicts - _warn_about_state_name_collisions(function_name, extra_game_states) - - for state in _get_game_states(extra_game_states): - if _thing_has_method(state, function_name, args): - token.type = DMConstants.TOKEN_VALUE - token.value = await _resolve_thing_method(state, function_name, args) - found = true - break - - show_error_for_missing_state_value(DMConstants.translate(&"runtime.method_not_found").format({ - method = args[0] if function_name in ["call", "call_deferred"] else function_name, - states = _get_state_shortcut_names(extra_game_states) - }), not found) - - elif token.type == DMConstants.TOKEN_DICTIONARY_REFERENCE: - var value - if i > 0 and tokens[i - 1].type == DMConstants.TOKEN_DOT: - # If we are deep referencing then we need to get the parent object. - # `parent.value` is the actual object and `token.variable` is the name of - # the property within it. - value = tokens[i - 2].value[token.variable] - # Clean up the previous tokens - token.erase("variable") - tokens.remove_at(i - 1) - tokens.remove_at(i - 2) - i -= 2 - else: - # Otherwise we can just get this variable as a normal state reference - value = _get_state_value(token.variable, extra_game_states) - - var index = await _resolve(token.value, extra_game_states) - if typeof(value) == TYPE_DICTIONARY: - if tokens.size() > i + 1 and tokens[i + 1].type == DMConstants.TOKEN_ASSIGNMENT: - # If the next token is an assignment then we need to leave this as a reference - # so that it can be resolved once everything ahead of it has been resolved - token.type = "dictionary" - token.value = value - token.key = index - else: - if value.has(index): - token.type = DMConstants.TOKEN_VALUE - token.value = value[index] - else: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.key_not_found").format({ key = str(index), dictionary = token.variable })) - elif typeof(value) == TYPE_ARRAY: - if tokens.size() > i + 1 and tokens[i + 1].type == DMConstants.TOKEN_ASSIGNMENT: - # If the next token is an assignment then we need to leave this as a reference - # so that it can be resolved once everything ahead of it has been resolved - token.type = "array" - token.value = value - token.key = index - else: - if index >= 0 and index < value.size(): - token.type = DMConstants.TOKEN_VALUE - token.value = value[index] - else: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.array_index_out_of_bounds").format({ index = index, array = token.variable })) - - elif token.type == DMConstants.TOKEN_DICTIONARY_NESTED_REFERENCE: - var dictionary: Dictionary = tokens[i - 1] - var index = await _resolve(token.value, extra_game_states) - var value = dictionary.value - if typeof(value) == TYPE_DICTIONARY: - if tokens.size() > i + 1 and tokens[i + 1].type == DMConstants.TOKEN_ASSIGNMENT: - # If the next token is an assignment then we need to leave this as a reference - # so that it can be resolved once everything ahead of it has been resolved - dictionary.type = "dictionary" - dictionary.key = index - dictionary.value = value - tokens.remove_at(i) - i -= 1 - else: - if dictionary.value.has(index): - dictionary.value = value.get(index) - tokens.remove_at(i) - i -= 1 - else: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.key_not_found").format({ key = str(index), dictionary = value })) - elif typeof(value) == TYPE_ARRAY: - if tokens.size() > i + 1 and tokens[i + 1].type == DMConstants.TOKEN_ASSIGNMENT: - # If the next token is an assignment then we need to leave this as a reference - # so that it can be resolved once everything ahead of it has been resolved - dictionary.type = "array" - dictionary.value = value - dictionary.key = index - tokens.remove_at(i) - i -= 1 - else: - if index >= 0 and index < value.size(): - dictionary.value = value[index] - tokens.remove_at(i) - i -= 1 - else: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.array_index_out_of_bounds").format({ index = index, array = value })) - - elif token.type == DMConstants.TOKEN_ARRAY: - token.type = DMConstants.TOKEN_VALUE - token.value = await _resolve_each(token.value, extra_game_states) - - elif token.type == DMConstants.TOKEN_DICTIONARY: - token.type = DMConstants.TOKEN_VALUE - var dictionary = {} - for key in token.value.keys(): - var resolved_key = await _resolve([key], extra_game_states) - var preresolved_value = token.value.get(key) - if typeof(preresolved_value) != TYPE_ARRAY: - preresolved_value = [preresolved_value] - var resolved_value = await _resolve(preresolved_value, extra_game_states) - dictionary[resolved_key] = resolved_value - token.value = dictionary - - elif token.type == DMConstants.TOKEN_VARIABLE or token.type == DMConstants.TOKEN_NUMBER: - if str(token.value) == "null": - token.type = DMConstants.TOKEN_VALUE - token.value = null - elif str(token.value) == "self": - token.type = DMConstants.TOKEN_VALUE - token.value = extra_game_states[0].self - elif tokens[i - 1].type == DMConstants.TOKEN_DOT: - var caller: Dictionary = tokens[i - 2] - var property = token.value - if tokens.size() > i + 1 and tokens[i + 1].type == DMConstants.TOKEN_ASSIGNMENT: - # If the next token is an assignment then we need to leave this as a reference - # so that it can be resolved once everything ahead of it has been resolved - caller.type = "property" - caller.property = property - else: - # If we are requesting a deeper property then we need to collapse the - # value into the thing we are referencing from - caller.type = DMConstants.TOKEN_VALUE - if Builtins.is_supported(caller.value): - caller.value = Builtins.resolve_property(caller.value, property) - else: - caller.value = caller.value.get(property) - tokens.remove_at(i) - tokens.remove_at(i - 1) - i -= 2 - elif tokens.size() > i + 1 and tokens[i + 1].type == DMConstants.TOKEN_ASSIGNMENT: - # It's a normal variable but we will be assigning to it so don't resolve - # it until everything after it has been resolved - token.type = "variable" - else: - if token.type == DMConstants.TOKEN_NUMBER: - token.type = DMConstants.TOKEN_VALUE - token.value = token.value - else: - token.type = DMConstants.TOKEN_VALUE - token.value = _get_state_value(str(token.value), extra_game_states) - - i += 1 - - # Then multiply and divide - i = 0 - limit = 0 - while i < tokens.size() and limit < 1000: - limit += 1 - var token: Dictionary = tokens[i] - if token.type == DMConstants.TOKEN_OPERATOR and token.value in ["*", "/", "%"]: - token.type = DMConstants.TOKEN_VALUE - token.value = _apply_operation(token.value, tokens[i - 1].value, tokens[i + 1].value) - tokens.remove_at(i + 1) - tokens.remove_at(i - 1) - i -= 1 - i += 1 - - if limit >= 1000: - assert(false, DMConstants.translate(&"runtime.something_went_wrong")) - - # Then addition and subtraction - i = 0 - limit = 0 - while i < tokens.size() and limit < 1000: - limit += 1 - var token: Dictionary = tokens[i] - if token.type == DMConstants.TOKEN_OPERATOR and token.value in ["+", "-"]: - token.type = DMConstants.TOKEN_VALUE - token.value = _apply_operation(token.value, tokens[i - 1].value, tokens[i + 1].value) - tokens.remove_at(i + 1) - tokens.remove_at(i - 1) - i -= 1 - i += 1 - - if limit >= 1000: - assert(false, DMConstants.translate(&"runtime.something_went_wrong")) - - # Then negations - i = 0 - limit = 0 - while i < tokens.size() and limit < 1000: - limit += 1 - var token: Dictionary = tokens[i] - if token.type == DMConstants.TOKEN_NOT: - token.type = DMConstants.TOKEN_VALUE - token.value = not tokens[i + 1].value - tokens.remove_at(i + 1) - i -= 1 - i += 1 - - if limit >= 1000: - assert(false, DMConstants.translate(&"runtime.something_went_wrong")) - - # Then comparisons - i = 0 - limit = 0 - while i < tokens.size() and limit < 1000: - limit += 1 - var token: Dictionary = tokens[i] - if token.type == DMConstants.TOKEN_COMPARISON: - token.type = DMConstants.TOKEN_VALUE - token.value = _compare(token.value, tokens[i - 1].value, tokens[i + 1].value) - tokens.remove_at(i + 1) - tokens.remove_at(i - 1) - i -= 1 - i += 1 - - if limit >= 1000: - assert(false, DMConstants.translate(&"runtime.something_went_wrong")) - - # Then and/or - i = 0 - limit = 0 - while i < tokens.size() and limit < 1000: - limit += 1 - var token: Dictionary = tokens[i] - if token.type == DMConstants.TOKEN_AND_OR: - token.type = DMConstants.TOKEN_VALUE - token.value = _apply_operation(token.value, tokens[i - 1].value, tokens[i + 1].value) - tokens.remove_at(i + 1) - tokens.remove_at(i - 1) - i -= 1 - i += 1 - - if limit >= 1000: - assert(false, DMConstants.translate(&"runtime.something_went_wrong")) - - # Lastly, resolve any assignments - i = 0 - limit = 0 - while i < tokens.size() and limit < 1000: - limit += 1 - var token: Dictionary = tokens[i] - if token.type == DMConstants.TOKEN_ASSIGNMENT: - var lhs: Dictionary = tokens[i - 1] - var value - - match lhs.type: - &"variable": - value = _apply_operation(token.value, _get_state_value(lhs.value, extra_game_states), tokens[i + 1].value) - _set_state_value(lhs.value, value, extra_game_states) - &"property": - value = _apply_operation(token.value, lhs.value.get(lhs.property), tokens[i + 1].value) - if typeof(lhs.value) == TYPE_DICTIONARY: - lhs.value[lhs.property] = value - else: - lhs.value.set(lhs.property, value) - &"dictionary": - value = _apply_operation(token.value, lhs.value.get(lhs.key, null), tokens[i + 1].value) - lhs.value[lhs.key] = value - &"array": - show_error_for_missing_state_value( - DMConstants.translate(&"runtime.array_index_out_of_bounds").format({ index = lhs.key, array = lhs.value }), - lhs.key >= lhs.value.size() - ) - value = _apply_operation(token.value, lhs.value[lhs.key], tokens[i + 1].value) - lhs.value[lhs.key] = value - _: - show_error_for_missing_state_value(DMConstants.translate(&"runtime.left_hand_size_cannot_be_assigned_to")) - - token.type = DMConstants.TOKEN_VALUE - token.value = value - tokens.remove_at(i + 1) - tokens.remove_at(i - 1) - i -= 1 - i += 1 - - if limit >= 1000: - assert(false, DMConstants.translate(&"runtime.something_went_wrong")) - - return tokens[0].value - - -# Compare two values. -func _compare(operator: String, first_value, second_value) -> bool: - match operator: - &"in": - if first_value == null or second_value == null: - return false - else: - return first_value in second_value - &"<": - if first_value == null: - return true - elif second_value == null: - return false - else: - return first_value < second_value - &">": - if first_value == null: - return false - elif second_value == null: - return true - else: - return first_value > second_value - &"<=": - if first_value == null: - return true - elif second_value == null: - return false - else: - return first_value <= second_value - &">=": - if first_value == null: - return false - elif second_value == null: - return true - else: - return first_value >= second_value - &"==": - if first_value == null: - if typeof(second_value) == TYPE_BOOL: - return second_value == false - else: - return second_value == null - else: - return first_value == second_value - &"!=": - if first_value == null: - if typeof(second_value) == TYPE_BOOL: - return second_value == true - else: - return second_value != null - else: - return first_value != second_value - - return false - - -# Apply an operation from one value to another. -func _apply_operation(operator: String, first_value, second_value): - match operator: - &"=": - return second_value - &"+", &"+=": - return first_value + second_value - &"-", &"-=": - return first_value - second_value - &"/", &"/=": - return first_value / second_value - &"*", &"*=": - return first_value * second_value - &"%": - return first_value % second_value - &"and": - return first_value and second_value - &"or": - return first_value or second_value - - assert(false, DMConstants.translate(&"runtime.unknown_operator")) - - -# Check if a dialogue line contains meaningful information. -func _is_valid(line: DialogueLine) -> bool: - if line == null: - return false - if line.type == DMConstants.TYPE_MUTATION and line.mutation == null: - return false - if line.type == DMConstants.TYPE_RESPONSE and line.get(&"responses").size() == 0: - return false - return true - - -# Check that a thing has a given method. -func _thing_has_method(thing, method: String, args: Array) -> bool: - if Builtins.is_supported(thing, method): - return thing != _autoloads - elif thing is Dictionary: - return false - - if method in [&"call", &"call_deferred"]: - return thing.has_method(args[0]) - - if method == &"emit_signal": - return thing.has_signal(args[0]) - - if thing.has_method(method): - return true - - if method.to_snake_case() != method and DMSettings.check_for_dotnet_solution(): - # If we get this far then the method might be a C# method with a Task return type - return _get_dotnet_dialogue_manager().ThingHasMethod(thing, method) - - return false - - -# Check if a given property exists -func _thing_has_property(thing: Object, property: String) -> bool: - if thing == null: - return false - - for p in thing.get_property_list(): - if _node_properties.has(p.name): - # Ignore any properties on the base Node - continue - if p.name == property: - return true - - return false - - -func _get_method_info_for(thing: Variant, method: String, args: Array) -> Dictionary: - # Use the thing instance id as a key for the caching dictionary. - var thing_instance_id: int = thing.get_instance_id() - if not _method_info_cache.has(thing_instance_id): - var methods: Dictionary = {} - for m in thing.get_method_list(): - methods["%s:%d" % [m.name, m.args.size()]] = m - if not methods.has(m.name): - methods[m.name] = m - _method_info_cache[thing_instance_id] = methods - - var methods: Dictionary = _method_info_cache.get(thing_instance_id, {}) - var method_key: String = "%s:%d" % [method, args.size()] - if methods.has(method_key): - return methods.get(method_key) - else: - return methods.get(method) - - -func _resolve_thing_method(thing, method: String, args: Array): - if Builtins.is_supported(thing): - var result = Builtins.resolve_method(thing, method, args) - if not Builtins.has_resolve_method_failed(): - return result - - if thing.has_method(method): - # Try to convert any literals to the right type - var method_info: Dictionary = _get_method_info_for(thing, method, args) - var method_args: Array = method_info.args - if method_info.flags & METHOD_FLAG_VARARG == 0 and method_args.size() < args.size(): - assert(false, DMConstants.translate(&"runtime.expected_n_got_n_args").format({ expected = method_args.size(), method = method, received = args.size()})) - for i in range(0, min(method_args.size(), args.size())): - var m: Dictionary = method_args[i] - var to_type: int = typeof(args[i]) - if m.type == TYPE_ARRAY: - match m.hint_string: - &"String": - to_type = TYPE_PACKED_STRING_ARRAY - &"int": - to_type = TYPE_PACKED_INT64_ARRAY - &"float": - to_type = TYPE_PACKED_FLOAT64_ARRAY - &"Vector2": - to_type = TYPE_PACKED_VECTOR2_ARRAY - &"Vector3": - to_type = TYPE_PACKED_VECTOR3_ARRAY - _: - if m.hint_string != "": - assert(false, DMConstants.translate(&"runtime.unsupported_array_type").format({ type = m.hint_string})) - if typeof(args[i]) != to_type: - args[i] = convert(args[i], to_type) - - return await thing.callv(method, args) - - # If we get here then it's probably a C# method with a Task return type - var dotnet_dialogue_manager = _get_dotnet_dialogue_manager() - dotnet_dialogue_manager.ResolveThingMethod(thing, method, args) - return await dotnet_dialogue_manager.Resolved diff --git a/addons/dialogue_manager/dialogue_manager.gd.uid b/addons/dialogue_manager/dialogue_manager.gd.uid deleted file mode 100644 index d10762e..0000000 --- a/addons/dialogue_manager/dialogue_manager.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c3rodes2l3gxb diff --git a/addons/dialogue_manager/dialogue_resource.gd b/addons/dialogue_manager/dialogue_resource.gd deleted file mode 100644 index 29ade7b..0000000 --- a/addons/dialogue_manager/dialogue_resource.gd +++ /dev/null @@ -1,42 +0,0 @@ -@tool -@icon("./assets/icon.svg") - -## A collection of dialogue lines for use with [code]DialogueManager[/code]. -class_name DialogueResource extends Resource - - -const DialogueLine = preload("./dialogue_line.gd") - -## A list of state shortcuts -@export var using_states: PackedStringArray = [] - -## A map of titles and the lines they point to. -@export var titles: Dictionary = {} - -## A list of character names. -@export var character_names: PackedStringArray = [] - -## The first title in the file. -@export var first_title: String = "" - -## A map of the encoded lines of dialogue. -@export var lines: Dictionary = {} - -## raw version of the text -@export var raw_text: String - - -## Get the next printable line of dialogue, starting from a referenced line ([code]title[/code] can -## be a title string or a stringified line number). Runs any mutations along the way and then returns -## the first dialogue line encountered. -func get_next_dialogue_line(title: String = "", extra_game_states: Array = [], mutation_behaviour: DMConstants.MutationBehaviour = DMConstants.MutationBehaviour.Wait) -> DialogueLine: - return await Engine.get_singleton("DialogueManager").get_next_dialogue_line(self, title, extra_game_states, mutation_behaviour) - - -## Get the list of any titles found in the file. -func get_titles() -> PackedStringArray: - return titles.keys() - - -func _to_string() -> String: - return "" % [",".join(titles.keys())] diff --git a/addons/dialogue_manager/dialogue_resource.gd.uid b/addons/dialogue_manager/dialogue_resource.gd.uid deleted file mode 100644 index 27b95d0..0000000 --- a/addons/dialogue_manager/dialogue_resource.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dbs4435dsf3ry diff --git a/addons/dialogue_manager/dialogue_response.gd b/addons/dialogue_manager/dialogue_response.gd deleted file mode 100644 index 701ce92..0000000 --- a/addons/dialogue_manager/dialogue_response.gd +++ /dev/null @@ -1,59 +0,0 @@ -## A response to a line of dialogue, usualy attached to a [code]DialogueLine[/code]. -class_name DialogueResponse extends RefCounted - - -## The ID of this response -var id: String - -## The internal type of this dialogue object, always set to [code]TYPE_RESPONSE[/code]. -var type: String = DMConstants.TYPE_RESPONSE - -## The next line ID to use if this response is selected by the player. -var next_id: String = "" - -## [code]true[/code] if the condition of this line was met. -var is_allowed: bool = true - -## A character (depending on the "characters in responses" behaviour setting). -var character: String = "" - -## A dictionary of varialbe replaces for the character name. Generally for internal use only. -var character_replacements: Array[Dictionary] = [] - -## The prompt for this response. -var text: String = "" - -## A dictionary of variable replaces for the text. Generally for internal use only. -var text_replacements: Array[Dictionary] = [] - -## Any #tags -var tags: PackedStringArray = [] - -## The key to use for translating the text. -var translation_key: String = "" - - -func _init(data: Dictionary = {}) -> void: - if data.size() > 0: - id = data.id - type = data.type - next_id = data.next_id - is_allowed = data.is_allowed - character = data.character - character_replacements = data.character_replacements - text = data.text - text_replacements = data.text_replacements - tags = data.tags - translation_key = data.translation_key - - -func _to_string() -> String: - return "" % text - - -func get_tag_value(tag_name: String) -> String: - var wrapped := "%s=" % tag_name - for t in tags: - if t.begins_with(wrapped): - return t.replace(wrapped, "").strip_edges() - return "" diff --git a/addons/dialogue_manager/dialogue_response.gd.uid b/addons/dialogue_manager/dialogue_response.gd.uid deleted file mode 100644 index 9b4532a..0000000 --- a/addons/dialogue_manager/dialogue_response.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cm0xpfeywpqid diff --git a/addons/dialogue_manager/dialogue_responses_menu.gd b/addons/dialogue_manager/dialogue_responses_menu.gd deleted file mode 100644 index 3c92ce1..0000000 --- a/addons/dialogue_manager/dialogue_responses_menu.gd +++ /dev/null @@ -1,143 +0,0 @@ -@icon("./assets/responses_menu.svg") - -## A [Container] for dialogue responses provided by [b]Dialogue Manager[/b]. -class_name DialogueResponsesMenu extends Container - - -## Emitted when a response is selected. -signal response_selected(response) - - -## Optionally specify a control to duplicate for each response -@export var response_template: Control - -## The action for accepting a response (is possibly overridden by parent dialogue balloon). -@export var next_action: StringName = &"" - -## Hide any responses where [code]is_allowed[/code] is false -@export var hide_failed_responses: bool = true - -## The list of dialogue responses. -var responses: Array = []: - get: - return responses - set(value): - responses = value - - # Remove any current items - for item in get_children(): - if item == response_template: continue - - remove_child(item) - item.queue_free() - - # Add new items - if responses.size() > 0: - for response in responses: - if hide_failed_responses and not response.is_allowed: continue - - var item: Control - if is_instance_valid(response_template): - item = response_template.duplicate(DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_SIGNALS) - item.show() - else: - item = Button.new() - item.name = "Response%d" % get_child_count() - if not response.is_allowed: - item.name = item.name + &"Disallowed" - item.disabled = true - - # If the item has a response property then use that - if "response" in item: - item.response = response - # Otherwise assume we can just set the text - else: - item.text = response.text - - item.set_meta("response", response) - - add_child(item) - - _configure_focus() - - -func _ready() -> void: - visibility_changed.connect(func(): - if visible and get_menu_items().size() > 0: - var first_item: Control = get_menu_items()[0] - if first_item.is_inside_tree(): - first_item.grab_focus() - ) - - if is_instance_valid(response_template): - response_template.hide() - - -## Get the selectable items in the menu. -func get_menu_items() -> Array: - var items: Array = [] - for child in get_children(): - if not child.visible: continue - if "Disallowed" in child.name: continue - items.append(child) - - return items - - -#region Internal - - -# Prepare the menu for keyboard and mouse navigation. -func _configure_focus() -> void: - var items = get_menu_items() - for i in items.size(): - var item: Control = items[i] - - item.focus_mode = Control.FOCUS_ALL - - item.focus_neighbor_left = item.get_path() - item.focus_neighbor_right = item.get_path() - - if i == 0: - item.focus_neighbor_top = item.get_path() - item.focus_previous = item.get_path() - else: - item.focus_neighbor_top = items[i - 1].get_path() - item.focus_previous = items[i - 1].get_path() - - if i == items.size() - 1: - item.focus_neighbor_bottom = item.get_path() - item.focus_next = item.get_path() - else: - item.focus_neighbor_bottom = items[i + 1].get_path() - item.focus_next = items[i + 1].get_path() - - item.mouse_entered.connect(_on_response_mouse_entered.bind(item)) - item.gui_input.connect(_on_response_gui_input.bind(item, item.get_meta("response"))) - - items[0].grab_focus() - - -#endregion - -#region Signals - - -func _on_response_mouse_entered(item: Control) -> void: - if "Disallowed" in item.name: return - - item.grab_focus() - - -func _on_response_gui_input(event: InputEvent, item: Control, response) -> void: - if "Disallowed" in item.name: return - - if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT: - get_viewport().set_input_as_handled() - response_selected.emit(response) - elif event.is_action_pressed(&"ui_accept" if next_action.is_empty() else next_action) and item in get_menu_items(): - get_viewport().set_input_as_handled() - response_selected.emit(response) - - -#endregion diff --git a/addons/dialogue_manager/dialogue_responses_menu.gd.uid b/addons/dialogue_manager/dialogue_responses_menu.gd.uid deleted file mode 100644 index 0ae73d9..0000000 --- a/addons/dialogue_manager/dialogue_responses_menu.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bb52rsfwhkxbn diff --git a/addons/dialogue_manager/editor_translation_parser_plugin.gd b/addons/dialogue_manager/editor_translation_parser_plugin.gd deleted file mode 100644 index 801d3a5..0000000 --- a/addons/dialogue_manager/editor_translation_parser_plugin.gd +++ /dev/null @@ -1,61 +0,0 @@ -class_name DMTranslationParserPlugin extends EditorTranslationParserPlugin - - -## Cached result of parsing a dialogue file. -var data: DMCompilerResult -## List of characters that were added. -var translated_character_names: PackedStringArray = [] -var translated_lines: Array[Dictionary] = [] - - -func _parse_file(path: String, msgids: Array, msgids_context_plural: Array) -> void: - var file: FileAccess = FileAccess.open(path, FileAccess.READ) - var text: String = file.get_as_text() - - data = DMCompiler.compile_string(text, path) - - var known_keys: PackedStringArray = PackedStringArray([]) - - # Add all character names if settings ask for it - if DMSettings.get_setting(DMSettings.INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST, true): - translated_character_names = [] as Array[DialogueLine] - for character_name: String in data.character_names: - if character_name in known_keys: continue - - known_keys.append(character_name) - - translated_character_names.append(character_name) - msgids_context_plural.append([character_name.replace('"', '\"'), "dialogue", ""]) - - # Add all dialogue lines and responses - var dialogue: Dictionary = data.lines - for key: String in dialogue.keys(): - var line: Dictionary = dialogue.get(key) - - if not line.type in [DMConstants.TYPE_DIALOGUE, DMConstants.TYPE_RESPONSE]: continue - - var translation_key: String = line.get(&"translation_key", line.text) - - if translation_key in known_keys: continue - - known_keys.append(translation_key) - translated_lines.append(line) - if translation_key == line.text: - msgids_context_plural.append([line.text.replace('"', '\"'), "", ""]) - else: - msgids_context_plural.append([line.text.replace('"', '\"'), line.translation_key.replace('"', '\"'), ""]) - - -func _get_comments(msgids_comment: Array[String], msgids_context_plural_comment: Array[String]) -> void: - # Add all character names if settings ask for it - if DMSettings.get_setting(DMSettings.INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST, true): - for character_name in translated_character_names: - msgids_context_plural_comment.append(DMConstants.translate("translation_plugin.character_name")) - - # Add all dialogue lines and responses - for line: Dictionary in translated_lines: - msgids_context_plural_comment.append(line.get("notes", "")) - - -func _get_recognized_extensions() -> PackedStringArray: - return ["dialogue"] diff --git a/addons/dialogue_manager/editor_translation_parser_plugin.gd.uid b/addons/dialogue_manager/editor_translation_parser_plugin.gd.uid deleted file mode 100644 index 22ddbe9..0000000 --- a/addons/dialogue_manager/editor_translation_parser_plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c6bya881h1egb diff --git a/addons/dialogue_manager/example_balloon/ExampleBalloon.cs b/addons/dialogue_manager/example_balloon/ExampleBalloon.cs deleted file mode 100644 index 980f067..0000000 --- a/addons/dialogue_manager/example_balloon/ExampleBalloon.cs +++ /dev/null @@ -1,223 +0,0 @@ -using Godot; -using Godot.Collections; - -namespace DialogueManagerRuntime -{ - public partial class ExampleBalloon : CanvasLayer - { - [Export] public string NextAction = "ui_accept"; - [Export] public string SkipAction = "ui_cancel"; - - - Control balloon; - RichTextLabel characterLabel; - RichTextLabel dialogueLabel; - VBoxContainer responsesMenu; - - Resource resource; - Array temporaryGameStates = new Array(); - bool isWaitingForInput = false; - bool willHideBalloon = false; - - DialogueLine dialogueLine; - DialogueLine DialogueLine - { - get => dialogueLine; - set - { - if (value == null) - { - QueueFree(); - return; - } - - dialogueLine = value; - ApplyDialogueLine(); - } - } - - Timer MutationCooldown = new Timer(); - - - public override void _Ready() - { - balloon = GetNode("%Balloon"); - characterLabel = GetNode("%CharacterLabel"); - dialogueLabel = GetNode("%DialogueLabel"); - responsesMenu = GetNode("%ResponsesMenu"); - - balloon.Hide(); - - balloon.GuiInput += (@event) => - { - if ((bool)dialogueLabel.Get("is_typing")) - { - bool mouseWasClicked = @event is InputEventMouseButton && (@event as InputEventMouseButton).ButtonIndex == MouseButton.Left && @event.IsPressed(); - bool skipButtonWasPressed = @event.IsActionPressed(SkipAction); - if (mouseWasClicked || skipButtonWasPressed) - { - GetViewport().SetInputAsHandled(); - dialogueLabel.Call("skip_typing"); - return; - } - } - - if (!isWaitingForInput) return; - if (dialogueLine.Responses.Count > 0) return; - - GetViewport().SetInputAsHandled(); - - if (@event is InputEventMouseButton && @event.IsPressed() && (@event as InputEventMouseButton).ButtonIndex == MouseButton.Left) - { - Next(dialogueLine.NextId); - } - else if (@event.IsActionPressed(NextAction) && GetViewport().GuiGetFocusOwner() == balloon) - { - Next(dialogueLine.NextId); - } - }; - - if (string.IsNullOrEmpty((string)responsesMenu.Get("next_action"))) - { - responsesMenu.Set("next_action", NextAction); - } - responsesMenu.Connect("response_selected", Callable.From((DialogueResponse response) => - { - Next(response.NextId); - })); - - - // Hide the balloon when a mutation is running - MutationCooldown.Timeout += () => - { - if (willHideBalloon) - { - willHideBalloon = false; - balloon.Hide(); - } - }; - AddChild(MutationCooldown); - - DialogueManager.Mutated += OnMutated; - } - - - public override void _ExitTree() - { - DialogueManager.Mutated -= OnMutated; - } - - - public override void _UnhandledInput(InputEvent @event) - { - // Only the balloon is allowed to handle input while it's showing - GetViewport().SetInputAsHandled(); - } - - - public override async void _Notification(int what) - { - // Detect a change of locale and update the current dialogue line to show the new language - if (what == NotificationTranslationChanged && IsInstanceValid(dialogueLabel)) - { - float visibleRatio = dialogueLabel.VisibleRatio; - DialogueLine = await DialogueManager.GetNextDialogueLine(resource, DialogueLine.Id, temporaryGameStates); - if (visibleRatio < 1.0f) - { - dialogueLabel.Call("skip_typing"); - } - } - } - - - public async void Start(Resource dialogueResource, string title, Array extraGameStates = null) - { - temporaryGameStates = new Array { this } + (extraGameStates ?? new Array()); - isWaitingForInput = false; - resource = dialogueResource; - - DialogueLine = await DialogueManager.GetNextDialogueLine(resource, title, temporaryGameStates); - } - - - public async void Next(string nextId) - { - DialogueLine = await DialogueManager.GetNextDialogueLine(resource, nextId, temporaryGameStates); - } - - - #region Helpers - - - private async void ApplyDialogueLine() - { - MutationCooldown.Stop(); - - isWaitingForInput = false; - balloon.FocusMode = Control.FocusModeEnum.All; - balloon.GrabFocus(); - - // Set up the character name - characterLabel.Visible = !string.IsNullOrEmpty(dialogueLine.Character); - characterLabel.Text = Tr(dialogueLine.Character, "dialogue"); - - // Set up the dialogue - dialogueLabel.Hide(); - dialogueLabel.Set("dialogue_line", dialogueLine); - - // Set up the responses - responsesMenu.Hide(); - responsesMenu.Set("responses", dialogueLine.Responses); - - // Type out the text - balloon.Show(); - willHideBalloon = false; - dialogueLabel.Show(); - if (!string.IsNullOrEmpty(dialogueLine.Text)) - { - dialogueLabel.Call("type_out"); - await ToSignal(dialogueLabel, "finished_typing"); - } - - // Wait for input - if (dialogueLine.Responses.Count > 0) - { - balloon.FocusMode = Control.FocusModeEnum.None; - responsesMenu.Show(); - } - else if (!string.IsNullOrEmpty(dialogueLine.Time)) - { - float time = 0f; - if (!float.TryParse(dialogueLine.Time, out time)) - { - time = dialogueLine.Text.Length * 0.02f; - } - await ToSignal(GetTree().CreateTimer(time), "timeout"); - Next(dialogueLine.NextId); - } - else - { - isWaitingForInput = true; - balloon.FocusMode = Control.FocusModeEnum.All; - balloon.GrabFocus(); - } - } - - - #endregion - - - #region signals - - - private void OnMutated(Dictionary _mutation) - { - isWaitingForInput = false; - willHideBalloon = true; - MutationCooldown.Start(0.1f); - } - - - #endregion - } -} diff --git a/addons/dialogue_manager/example_balloon/ExampleBalloon.cs.uid b/addons/dialogue_manager/example_balloon/ExampleBalloon.cs.uid deleted file mode 100644 index 4b3783a..0000000 --- a/addons/dialogue_manager/example_balloon/ExampleBalloon.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://5b3w40kwakl3 diff --git a/addons/dialogue_manager/example_balloon/example_balloon.gd b/addons/dialogue_manager/example_balloon/example_balloon.gd deleted file mode 100644 index c7e6d9a..0000000 --- a/addons/dialogue_manager/example_balloon/example_balloon.gd +++ /dev/null @@ -1,176 +0,0 @@ -class_name DialogueManagerExampleBalloon extends CanvasLayer -## A basic dialogue balloon for use with Dialogue Manager. - -## The action to use for advancing the dialogue -@export var next_action: StringName = &"ui_accept" - -## The action to use to skip typing the dialogue -@export var skip_action: StringName = &"ui_cancel" - -## The dialogue resource -var resource: DialogueResource - -## Temporary game states -var temporary_game_states: Array = [] - -## See if we are waiting for the player -var is_waiting_for_input: bool = false - -## See if we are running a long mutation and should hide the balloon -var will_hide_balloon: bool = false - -## A dictionary to store any ephemeral variables -var locals: Dictionary = {} - -var _locale: String = TranslationServer.get_locale() - -## The current line -var dialogue_line: DialogueLine: - set(value): - if value: - dialogue_line = value - apply_dialogue_line() - else: - # The dialogue has finished so close the balloon - queue_free() - get: - return dialogue_line - -## A cooldown timer for delaying the balloon hide when encountering a mutation. -var mutation_cooldown: Timer = Timer.new() - -## The base balloon anchor -@onready var balloon: Control = %Balloon - -## The label showing the name of the currently speaking character -@onready var character_label: RichTextLabel = %CharacterLabel - -## The label showing the currently spoken dialogue -@onready var dialogue_label: DialogueLabel = %DialogueLabel - -## The menu of responses -@onready var responses_menu: DialogueResponsesMenu = %ResponsesMenu - - -func _ready() -> void: - balloon.hide() - Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated) - - # If the responses menu doesn't have a next action set, use this one - if responses_menu.next_action.is_empty(): - responses_menu.next_action = next_action - - mutation_cooldown.timeout.connect(_on_mutation_cooldown_timeout) - add_child(mutation_cooldown) - - -func _unhandled_input(_event: InputEvent) -> void: - # Only the balloon is allowed to handle input while it's showing - get_viewport().set_input_as_handled() - - -func _notification(what: int) -> void: - ## Detect a change of locale and update the current dialogue line to show the new language - if what == NOTIFICATION_TRANSLATION_CHANGED and _locale != TranslationServer.get_locale() and is_instance_valid(dialogue_label): - _locale = TranslationServer.get_locale() - var visible_ratio = dialogue_label.visible_ratio - self.dialogue_line = await resource.get_next_dialogue_line(dialogue_line.id) - if visible_ratio < 1: - dialogue_label.skip_typing() - - -## Start some dialogue -func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void: - temporary_game_states = [self] + extra_game_states - is_waiting_for_input = false - resource = dialogue_resource - self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states) - - -## Apply any changes to the balloon given a new [DialogueLine]. -func apply_dialogue_line() -> void: - mutation_cooldown.stop() - - is_waiting_for_input = false - balloon.focus_mode = Control.FOCUS_ALL - balloon.grab_focus() - - character_label.visible = not dialogue_line.character.is_empty() - character_label.text = tr(dialogue_line.character, "dialogue") - - dialogue_label.hide() - dialogue_label.dialogue_line = dialogue_line - - responses_menu.hide() - responses_menu.responses = dialogue_line.responses - - # Show our balloon - balloon.show() - will_hide_balloon = false - - dialogue_label.show() - if not dialogue_line.text.is_empty(): - dialogue_label.type_out() - await dialogue_label.finished_typing - - # Wait for input - if dialogue_line.responses.size() > 0: - balloon.focus_mode = Control.FOCUS_NONE - responses_menu.show() - elif dialogue_line.time != "": - var time = dialogue_line.text.length() * 0.02 if dialogue_line.time == "auto" else dialogue_line.time.to_float() - await get_tree().create_timer(time).timeout - next(dialogue_line.next_id) - else: - is_waiting_for_input = true - balloon.focus_mode = Control.FOCUS_ALL - balloon.grab_focus() - - -## Go to the next line -func next(next_id: String) -> void: - self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states) - - -#region Signals - - -func _on_mutation_cooldown_timeout() -> void: - if will_hide_balloon: - will_hide_balloon = false - balloon.hide() - - -func _on_mutated(_mutation: Dictionary) -> void: - is_waiting_for_input = false - will_hide_balloon = true - mutation_cooldown.start(0.1) - - -func _on_balloon_gui_input(event: InputEvent) -> void: - # See if we need to skip typing of the dialogue - if dialogue_label.is_typing: - var mouse_was_clicked: bool = event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed() - var skip_button_was_pressed: bool = event.is_action_pressed(skip_action) - if mouse_was_clicked or skip_button_was_pressed: - get_viewport().set_input_as_handled() - dialogue_label.skip_typing() - return - - if not is_waiting_for_input: return - if dialogue_line.responses.size() > 0: return - - # When there are no response options the balloon itself is the clickable thing - get_viewport().set_input_as_handled() - - if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT: - next(dialogue_line.next_id) - elif event.is_action_pressed(next_action) and get_viewport().gui_get_focus_owner() == balloon: - next(dialogue_line.next_id) - - -func _on_responses_menu_response_selected(response: DialogueResponse) -> void: - next(response.next_id) - - -#endregion diff --git a/addons/dialogue_manager/example_balloon/example_balloon.gd.uid b/addons/dialogue_manager/example_balloon/example_balloon.gd.uid deleted file mode 100644 index 6327f9b..0000000 --- a/addons/dialogue_manager/example_balloon/example_balloon.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d1wt4ma6055l8 diff --git a/addons/dialogue_manager/example_balloon/example_balloon.tscn b/addons/dialogue_manager/example_balloon/example_balloon.tscn deleted file mode 100644 index 91d8a7d..0000000 --- a/addons/dialogue_manager/example_balloon/example_balloon.tscn +++ /dev/null @@ -1,149 +0,0 @@ -[gd_scene load_steps=9 format=3 uid="uid://73jm5qjy52vq"] - -[ext_resource type="Script" uid="uid://d1wt4ma6055l8" path="res://addons/dialogue_manager/example_balloon/example_balloon.gd" id="1_36de5"] -[ext_resource type="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="2_a8ve6"] -[ext_resource type="Script" uid="uid://bb52rsfwhkxbn" path="res://addons/dialogue_manager/dialogue_responses_menu.gd" id="3_72ixx"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_spyqn"] -bg_color = Color(0, 0, 0, 1) -border_width_left = 3 -border_width_top = 3 -border_width_right = 3 -border_width_bottom = 3 -border_color = Color(0.329412, 0.329412, 0.329412, 1) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ri4m3"] -bg_color = Color(0.121569, 0.121569, 0.121569, 1) -border_width_left = 3 -border_width_top = 3 -border_width_right = 3 -border_width_bottom = 3 -border_color = Color(1, 1, 1, 1) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e0njw"] -bg_color = Color(0, 0, 0, 1) -border_width_left = 3 -border_width_top = 3 -border_width_right = 3 -border_width_bottom = 3 -border_color = Color(0.6, 0.6, 0.6, 1) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uy0d5"] -bg_color = Color(0, 0, 0, 1) -border_width_left = 3 -border_width_top = 3 -border_width_right = 3 -border_width_bottom = 3 -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - -[sub_resource type="Theme" id="Theme_qq3yp"] -default_font_size = 20 -Button/styles/disabled = SubResource("StyleBoxFlat_spyqn") -Button/styles/focus = SubResource("StyleBoxFlat_ri4m3") -Button/styles/hover = SubResource("StyleBoxFlat_e0njw") -Button/styles/normal = SubResource("StyleBoxFlat_e0njw") -MarginContainer/constants/margin_bottom = 15 -MarginContainer/constants/margin_left = 30 -MarginContainer/constants/margin_right = 30 -MarginContainer/constants/margin_top = 15 -Panel/styles/panel = SubResource("StyleBoxFlat_uy0d5") - -[node name="ExampleBalloon" type="CanvasLayer"] -layer = 100 -script = ExtResource("1_36de5") - -[node name="Balloon" type="Control" parent="."] -unique_name_in_owner = true -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme = SubResource("Theme_qq3yp") - -[node name="Panel" type="Panel" parent="Balloon"] -clip_children = 2 -layout_mode = 1 -anchors_preset = 12 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 21.0 -offset_top = -183.0 -offset_right = -19.0 -offset_bottom = -19.0 -grow_horizontal = 2 -grow_vertical = 0 -mouse_filter = 1 - -[node name="Dialogue" type="MarginContainer" parent="Balloon/Panel"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="Balloon/Panel/Dialogue"] -layout_mode = 2 - -[node name="CharacterLabel" type="RichTextLabel" parent="Balloon/Panel/Dialogue/VBoxContainer"] -unique_name_in_owner = true -modulate = Color(1, 1, 1, 0.501961) -layout_mode = 2 -mouse_filter = 1 -bbcode_enabled = true -text = "Character" -fit_content = true -scroll_active = false - -[node name="DialogueLabel" parent="Balloon/Panel/Dialogue/VBoxContainer" instance=ExtResource("2_a8ve6")] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -text = "Dialogue..." - -[node name="Responses" type="MarginContainer" parent="Balloon"] -layout_mode = 1 -anchors_preset = 7 -anchor_left = 0.5 -anchor_top = 1.0 -anchor_right = 0.5 -anchor_bottom = 1.0 -offset_left = -147.0 -offset_top = -558.0 -offset_right = 494.0 -offset_bottom = -154.0 -grow_horizontal = 2 -grow_vertical = 0 - -[node name="ResponsesMenu" type="VBoxContainer" parent="Balloon/Responses" node_paths=PackedStringArray("response_template")] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 8 -theme_override_constants/separation = 2 -script = ExtResource("3_72ixx") -response_template = NodePath("ResponseExample") - -[node name="ResponseExample" type="Button" parent="Balloon/Responses/ResponsesMenu"] -layout_mode = 2 -text = "Response example" - -[connection signal="gui_input" from="Balloon" to="." method="_on_balloon_gui_input"] -[connection signal="response_selected" from="Balloon/Responses/ResponsesMenu" to="." method="_on_responses_menu_response_selected"] diff --git a/addons/dialogue_manager/example_balloon/small_example_balloon.tscn b/addons/dialogue_manager/example_balloon/small_example_balloon.tscn deleted file mode 100644 index c4d2145..0000000 --- a/addons/dialogue_manager/example_balloon/small_example_balloon.tscn +++ /dev/null @@ -1,174 +0,0 @@ -[gd_scene load_steps=10 format=3 uid="uid://13s5spsk34qu"] - -[ext_resource type="Script" uid="uid://d1wt4ma6055l8" path="res://addons/dialogue_manager/example_balloon/example_balloon.gd" id="1_s2gbs"] -[ext_resource type="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="2_hfvdi"] -[ext_resource type="Script" uid="uid://bb52rsfwhkxbn" path="res://addons/dialogue_manager/dialogue_responses_menu.gd" id="3_1j1j0"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_235ry"] -content_margin_left = 6.0 -content_margin_top = 3.0 -content_margin_right = 6.0 -content_margin_bottom = 3.0 -bg_color = Color(0.0666667, 0.0666667, 0.0666667, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.345098, 0.345098, 0.345098, 1) -corner_radius_top_left = 3 -corner_radius_top_right = 3 -corner_radius_bottom_right = 3 -corner_radius_bottom_left = 3 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ufjut"] -content_margin_left = 6.0 -content_margin_top = 3.0 -content_margin_right = 6.0 -content_margin_bottom = 3.0 -bg_color = Color(0.227451, 0.227451, 0.227451, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(1, 1, 1, 1) -corner_radius_top_left = 3 -corner_radius_top_right = 3 -corner_radius_bottom_right = 3 -corner_radius_bottom_left = 3 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fcbqo"] -content_margin_left = 6.0 -content_margin_top = 3.0 -content_margin_right = 6.0 -content_margin_bottom = 3.0 -bg_color = Color(0.0666667, 0.0666667, 0.0666667, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -corner_radius_top_left = 3 -corner_radius_top_right = 3 -corner_radius_bottom_right = 3 -corner_radius_bottom_left = 3 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_t6i7a"] -content_margin_left = 6.0 -content_margin_top = 3.0 -content_margin_right = 6.0 -content_margin_bottom = 3.0 -bg_color = Color(0.0666667, 0.0666667, 0.0666667, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -corner_radius_top_left = 3 -corner_radius_top_right = 3 -corner_radius_bottom_right = 3 -corner_radius_bottom_left = 3 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uy0d5"] -bg_color = Color(0, 0, 0, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -corner_radius_top_left = 3 -corner_radius_top_right = 3 -corner_radius_bottom_right = 3 -corner_radius_bottom_left = 3 - -[sub_resource type="Theme" id="Theme_qq3yp"] -default_font_size = 8 -Button/styles/disabled = SubResource("StyleBoxFlat_235ry") -Button/styles/focus = SubResource("StyleBoxFlat_ufjut") -Button/styles/hover = SubResource("StyleBoxFlat_fcbqo") -Button/styles/normal = SubResource("StyleBoxFlat_t6i7a") -MarginContainer/constants/margin_bottom = 4 -MarginContainer/constants/margin_left = 8 -MarginContainer/constants/margin_right = 8 -MarginContainer/constants/margin_top = 4 -Panel/styles/panel = SubResource("StyleBoxFlat_uy0d5") - -[node name="ExampleBalloon" type="CanvasLayer"] -layer = 100 -script = ExtResource("1_s2gbs") - -[node name="Balloon" type="Control" parent="."] -unique_name_in_owner = true -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme = SubResource("Theme_qq3yp") - -[node name="Panel" type="Panel" parent="Balloon"] -clip_children = 2 -layout_mode = 1 -anchors_preset = 12 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 3.0 -offset_top = -62.0 -offset_right = -4.0 -offset_bottom = -4.0 -grow_horizontal = 2 -grow_vertical = 0 -mouse_filter = 1 - -[node name="Dialogue" type="MarginContainer" parent="Balloon/Panel"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="Balloon/Panel/Dialogue"] -layout_mode = 2 - -[node name="CharacterLabel" type="RichTextLabel" parent="Balloon/Panel/Dialogue/VBoxContainer"] -unique_name_in_owner = true -modulate = Color(1, 1, 1, 0.501961) -layout_mode = 2 -mouse_filter = 1 -bbcode_enabled = true -text = "Character" -fit_content = true -scroll_active = false - -[node name="DialogueLabel" parent="Balloon/Panel/Dialogue/VBoxContainer" instance=ExtResource("2_hfvdi")] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -text = "Dialogue..." - -[node name="Responses" type="MarginContainer" parent="Balloon"] -layout_mode = 1 -anchors_preset = 7 -anchor_left = 0.5 -anchor_top = 1.0 -anchor_right = 0.5 -anchor_bottom = 1.0 -offset_left = -124.0 -offset_top = -218.0 -offset_right = 125.0 -offset_bottom = -50.0 -grow_horizontal = 2 -grow_vertical = 0 - -[node name="ResponsesMenu" type="VBoxContainer" parent="Balloon/Responses"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 8 -theme_override_constants/separation = 2 -script = ExtResource("3_1j1j0") - -[node name="ResponseExample" type="Button" parent="Balloon/Responses/ResponsesMenu"] -layout_mode = 2 -text = "Response Example" - -[connection signal="gui_input" from="Balloon" to="." method="_on_balloon_gui_input"] -[connection signal="response_selected" from="Balloon/Responses/ResponsesMenu" to="." method="_on_responses_menu_response_selected"] diff --git a/addons/dialogue_manager/import_plugin.gd b/addons/dialogue_manager/import_plugin.gd deleted file mode 100644 index 345fe84..0000000 --- a/addons/dialogue_manager/import_plugin.gd +++ /dev/null @@ -1,107 +0,0 @@ -@tool -class_name DMImportPlugin extends EditorImportPlugin - - -signal compiled_resource(resource: Resource) - - -const COMPILER_VERSION = 14 - - -func _get_importer_name() -> String: - # NOTE: A change to this forces a re-import of all dialogue - return "dialogue_manager_compiler_%s" % COMPILER_VERSION - - -func _get_visible_name() -> String: - return "Dialogue" - - -func _get_import_order() -> int: - return -1000 - - -func _get_priority() -> float: - return 1000.0 - - -func _get_resource_type(): - return "Resource" - - -func _get_recognized_extensions() -> PackedStringArray: - return PackedStringArray(["dialogue"]) - - -func _get_save_extension(): - return "tres" - - -func _get_preset_count() -> int: - return 0 - - -func _get_preset_name(preset_index: int) -> String: - return "Unknown" - - -func _get_import_options(path: String, preset_index: int) -> Array: - # When the options array is empty there is a misleading error on export - # that actually means nothing so let's just have an invisible option. - return [{ - name = "defaults", - default_value = true - }] - - -func _get_option_visibility(path: String, option_name: StringName, options: Dictionary) -> bool: - return false - - -func _import(source_file: String, save_path: String, options: Dictionary, platform_variants: Array[String], gen_files: Array[String]) -> Error: - var cache = Engine.get_meta("DMCache") - - # Get the raw file contents - if not FileAccess.file_exists(source_file): return ERR_FILE_NOT_FOUND - - var file: FileAccess = FileAccess.open(source_file, FileAccess.READ) - var raw_text: String = file.get_as_text() - - cache.file_content_changed.emit(source_file, raw_text) - - # Compile the text - var result: DMCompilerResult = DMCompiler.compile_string(raw_text, source_file) - if result.errors.size() > 0: - printerr("%d errors found in %s" % [result.errors.size(), source_file]) - cache.add_errors_to_file(source_file, result.errors) - return ERR_PARSE_ERROR - - # Get the current addon version - var config: ConfigFile = ConfigFile.new() - config.load("res://addons/dialogue_manager/plugin.cfg") - var version: String = config.get_value("plugin", "version") - - # Save the results to a resource - var resource: DialogueResource = DialogueResource.new() - resource.set_meta("dialogue_manager_version", version) - - resource.using_states = result.using_states - resource.titles = result.titles - resource.first_title = result.first_title - resource.character_names = result.character_names - resource.lines = result.lines - resource.raw_text = result.raw_text - - # Clear errors and possibly trigger any cascade recompiles - cache.add_file(source_file, result) - - var err: Error = ResourceSaver.save(resource, "%s.%s" % [save_path, _get_save_extension()]) - - compiled_resource.emit(resource) - - # Recompile any dependencies - var dependent_paths: PackedStringArray = cache.get_dependent_paths_for_reimport(source_file) - for path in dependent_paths: - append_import_external_resource(path) - - return err diff --git a/addons/dialogue_manager/import_plugin.gd.uid b/addons/dialogue_manager/import_plugin.gd.uid deleted file mode 100644 index e98bfab..0000000 --- a/addons/dialogue_manager/import_plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dhwpj6ed8soyq diff --git a/addons/dialogue_manager/inspector_plugin.gd b/addons/dialogue_manager/inspector_plugin.gd deleted file mode 100644 index 366c1f3..0000000 --- a/addons/dialogue_manager/inspector_plugin.gd +++ /dev/null @@ -1,21 +0,0 @@ -@tool -class_name DMInspectorPlugin extends EditorInspectorPlugin - - -const DialogueEditorProperty = preload("./components/editor_property/editor_property.gd") - - -func _can_handle(object) -> bool: - if object is GDScript: return false - if not object is Node: return false - if "name" in object and object.name == "Dialogue Manager": return false - return true - - -func _parse_property(object: Object, type, name: String, hint_type, hint_string: String, usage_flags: int, wide: bool) -> bool: - if hint_string == "DialogueResource" or ("dialogue" in name.to_lower() and hint_string == "Resource"): - var property_editor = DialogueEditorProperty.new() - add_property_editor(name, property_editor) - return true - - return false diff --git a/addons/dialogue_manager/inspector_plugin.gd.uid b/addons/dialogue_manager/inspector_plugin.gd.uid deleted file mode 100644 index 00c8db8..0000000 --- a/addons/dialogue_manager/inspector_plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://0x31sbqbikov diff --git a/addons/dialogue_manager/l10n/en.mo b/addons/dialogue_manager/l10n/en.mo deleted file mode 100644 index 2ab4fdf..0000000 Binary files a/addons/dialogue_manager/l10n/en.mo and /dev/null differ diff --git a/addons/dialogue_manager/l10n/en.po b/addons/dialogue_manager/l10n/en.po deleted file mode 100644 index bd9a795..0000000 --- a/addons/dialogue_manager/l10n/en.po +++ /dev/null @@ -1,424 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Dialogue Manager\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.2.2\n" - -msgid "start_a_new_file" -msgstr "Start a new file" - -msgid "open_a_file" -msgstr "Open a file" - -msgid "open.open" -msgstr "Open..." - -msgid "open.quick_open" -msgstr "Quick open..." - -msgid "open.no_recent_files" -msgstr "No recent files" - -msgid "open.clear_recent_files" -msgstr "Clear recent files" - -msgid "save_all_files" -msgstr "Save all files" - -msgid "find_in_files" -msgstr "Find in files..." - -msgid "test_dialogue" -msgstr "Test dialogue from start of file" - -msgid "test_dialogue_from_line" -msgstr "Test dialogue from current line" - -msgid "search_for_text" -msgstr "Search for text" - -msgid "insert" -msgstr "Insert" - -msgid "translations" -msgstr "Translations" - -msgid "sponsor" -msgstr "Sponsor" - -msgid "show_support" -msgstr "Support Dialogue Manager" - -msgid "docs" -msgstr "Docs" - -msgid "insert.wave_bbcode" -msgstr "Wave BBCode" - -msgid "insert.shake_bbcode" -msgstr "Shake BBCode" - -msgid "insert.typing_pause" -msgstr "Typing pause" - -msgid "insert.typing_speed_change" -msgstr "Typing speed change" - -msgid "insert.auto_advance" -msgstr "Auto advance" - -msgid "insert.templates" -msgstr "Templates" - -msgid "insert.title" -msgstr "Title" - -msgid "insert.dialogue" -msgstr "Dialogue" - -msgid "insert.response" -msgstr "Response" - -msgid "insert.random_lines" -msgstr "Random lines" - -msgid "insert.random_text" -msgstr "Random text" - -msgid "insert.actions" -msgstr "Actions" - -msgid "insert.jump" -msgstr "Jump to title" - -msgid "insert.end_dialogue" -msgstr "End dialogue" - -msgid "generate_line_ids" -msgstr "Generate line IDs" - -msgid "save_characters_to_csv" -msgstr "Save character names to CSV..." - -msgid "save_to_csv" -msgstr "Save lines to CSV..." - -msgid "import_from_csv" -msgstr "Import line changes from CSV..." - -msgid "confirm_close" -msgstr "Save changes to '{path}'?" - -msgid "confirm_close.save" -msgstr "Save changes" - -msgid "confirm_close.discard" -msgstr "Discard" - -msgid "buffer.save" -msgstr "Save" - -msgid "buffer.save_as" -msgstr "Save as..." - -msgid "buffer.close" -msgstr "Close" - -msgid "buffer.close_all" -msgstr "Close all" - -msgid "buffer.close_other_files" -msgstr "Close other files" - -msgid "buffer.copy_file_path" -msgstr "Copy file path" - -msgid "buffer.show_in_filesystem" -msgstr "Show in FileSystem" - -msgid "n_of_n" -msgstr "{index} of {total}" - -msgid "search.find" -msgstr "Find:" - -msgid "search.find_all" -msgstr "Find all..." - -msgid "search.placeholder" -msgstr "Text to search for" - -msgid "search.replace_placeholder" -msgstr "Text to replace it with" - -msgid "search.replace_selected" -msgstr "Replace selected" - -msgid "search.previous" -msgstr "Previous" - -msgid "search.next" -msgstr "Next" - -msgid "search.match_case" -msgstr "Match case" - -msgid "search.toggle_replace" -msgstr "Replace" - -msgid "search.replace_with" -msgstr "Replace with:" - -msgid "search.replace" -msgstr "Replace" - -msgid "search.replace_all" -msgstr "Replace all" - -msgid "files_list.filter" -msgstr "Filter files" - -msgid "titles_list.filter" -msgstr "Filter titles" - -msgid "errors.key_not_found" -msgstr "Key \"{key}\" not found." - -msgid "errors.line_and_message" -msgstr "Error at {line}, {column}: {message}" - -msgid "errors_in_script" -msgstr "You have errors in your script. Fix them and then try again." - -msgid "errors_with_build" -msgstr "You need to fix dialogue errors before you can run your game." - -msgid "errors.import_errors" -msgstr "There are errors in this imported file." - -msgid "errors.already_imported" -msgstr "File already imported." - -msgid "errors.duplicate_import" -msgstr "Duplicate import name." - -msgid "errors.unknown_using" -msgstr "Unknown autoload in using statement." - -msgid "errors.empty_title" -msgstr "Titles cannot be empty." - -msgid "errors.duplicate_title" -msgstr "There is already a title with that name." - -msgid "errors.invalid_title_string" -msgstr "Titles can only contain alphanumeric characters and numbers." - -msgid "errors.invalid_title_number" -msgstr "Titles cannot begin with a number." - -msgid "errors.unknown_title" -msgstr "Unknown title." - -msgid "errors.jump_to_invalid_title" -msgstr "This jump is pointing to an invalid title." - -msgid "errors.title_has_no_content" -msgstr "That title has no content. Maybe change this to a \"=> END\"." - -msgid "errors.invalid_expression" -msgstr "Expression is invalid." - -msgid "errors.unexpected_condition" -msgstr "Unexpected condition." - -msgid "errors.duplicate_id" -msgstr "This ID is already on another line." - -msgid "errors.missing_id" -msgstr "This line is missing an ID." - -msgid "errors.invalid_indentation" -msgstr "Invalid indentation." - -msgid "errors.condition_has_no_content" -msgstr "A condition line needs an indented line below it." - -msgid "errors.incomplete_expression" -msgstr "Incomplete expression." - -msgid "errors.invalid_expression_for_value" -msgstr "Invalid expression for value." - -msgid "errors.file_not_found" -msgstr "File not found." - -msgid "errors.unexpected_end_of_expression" -msgstr "Unexpected end of expression." - -msgid "errors.unexpected_function" -msgstr "Unexpected function." - -msgid "errors.unexpected_bracket" -msgstr "Unexpected bracket." - -msgid "errors.unexpected_closing_bracket" -msgstr "Unexpected closing bracket." - -msgid "errors.missing_closing_bracket" -msgstr "Missing closing bracket." - -msgid "errors.unexpected_operator" -msgstr "Unexpected operator." - -msgid "errors.unexpected_comma" -msgstr "Unexpected comma." - -msgid "errors.unexpected_colon" -msgstr "Unexpected colon." - -msgid "errors.unexpected_dot" -msgstr "Unexpected dot." - -msgid "errors.unexpected_boolean" -msgstr "Unexpected boolean." - -msgid "errors.unexpected_string" -msgstr "Unexpected string." - -msgid "errors.unexpected_number" -msgstr "Unexpected number." - -msgid "errors.unexpected_variable" -msgstr "Unexpected variable." - -msgid "errors.invalid_index" -msgstr "Invalid index." - -msgid "errors.unexpected_assignment" -msgstr "Unexpected assignment." - -msgid "errors.expected_when_or_else" -msgstr "Expecting a when or an else case." - -msgid "errors.only_one_else_allowed" -msgstr "Only one else case is allowed per match." - -msgid "errors.when_must_belong_to_match" -msgstr "When statements can only appear as children of match statements." - -msgid "errors.concurrent_line_without_origin" -msgstr "Concurrent lines need an origin line that doesn't start with \"| \"." - -msgid "errors.goto_not_allowed_on_concurrect_lines" -msgstr "Goto references are not allowed on concurrent dialogue lines." - -msgid "errors.unknown" -msgstr "Unknown syntax." - -msgid "update.available" -msgstr "v{version} available" - -msgid "update.is_available_for_download" -msgstr "Version %s is available for download!" - -msgid "update.downloading" -msgstr "Downloading..." - -msgid "update.download_update" -msgstr "Download update" - -msgid "update.needs_reload" -msgstr "The project needs to be reloaded to install the update." - -msgid "update.reload_ok_button" -msgstr "Reload project" - -msgid "update.reload_cancel_button" -msgstr "Do it later" - -msgid "update.reload_project" -msgstr "Reload project" - -msgid "update.release_notes" -msgstr "Read release notes" - -msgid "update.success" -msgstr "Dialogue Manager is now v{version}." - -msgid "update.failed" -msgstr "There was a problem downloading the update." - -msgid "runtime.no_resource" -msgstr "No dialogue resource provided." - -msgid "runtime.no_content" -msgstr "\"{file_path}\" has no content." - -msgid "runtime.errors" -msgstr "You have {count} errors in your dialogue text." - -msgid "runtime.error_detail" -msgstr "Line {line}: {message}" - -msgid "runtime.errors_see_details" -msgstr "You have {count} errors in your dialogue text. See Output for details." - -msgid "runtime.invalid_expression" -msgstr "\"{expression}\" is not a valid expression: {error}" - -msgid "runtime.array_index_out_of_bounds" -msgstr "Index {index} out of bounds of array \"{array}\"." - -msgid "runtime.left_hand_size_cannot_be_assigned_to" -msgstr "Left hand side of expression cannot be assigned to." - -msgid "runtime.key_not_found" -msgstr "Key \"{key}\" not found in dictionary \"{dictionary}\"" - -msgid "runtime.property_not_found" -msgstr "\"{property}\" not found. States with directly referenceable properties/methods/signals include {states}. Autoloads need to be referenced by their name to use their properties." - -msgid "runtime.property_not_found_missing_export" -msgstr "\"{property}\" not found. You might need to add an [Export] decorator. States with directly referenceable properties/methods/signals include {states}. Autoloads need to be referenced by their name to use their properties." - -msgid "runtime.method_not_found" -msgstr "Method \"{method}\" not found. States with directly referenceable properties/methods/signals include {states}. Autoloads need to be referenced by their name to use their properties." - -msgid "runtime.signal_not_found" -msgstr "Signal \"{signal_name}\" not found. States with directly referenceable properties/methods/signals include {states}. Autoloads need to be referenced by their name to use their properties." - -msgid "runtime.method_not_callable" -msgstr "\"{method}\" is not a callable method on \"{object}\"" - -msgid "runtime.unknown_operator" -msgstr "Unknown operator." - -msgid "runtime.unknown_autoload" -msgstr "\"{autoload}\" doesn't appear to be a valid autoload." - -msgid "runtime.something_went_wrong" -msgstr "Something went wrong." - -msgid "runtime.expected_n_got_n_args" -msgstr "\"{method}\" was called with {received} arguments but it only has {expected}." - -msgid "runtime.unsupported_array_type" -msgstr "Array[{type}] isn't supported in mutations. Use Array as a type instead." - -msgid "runtime.dialogue_balloon_missing_start_method" -msgstr "Your dialogue balloon is missing a \"start\" or \"Start\" method." - -msgid "runtime.top_level_states_share_name" -msgstr "Multiple top-level states ({states}) share method/property/signal name \"{key}\". Only the first occurance is accessible to dialogue." - -msgid "translation_plugin.character_name" -msgstr "Character name" \ No newline at end of file diff --git a/addons/dialogue_manager/l10n/es.po b/addons/dialogue_manager/l10n/es.po deleted file mode 100644 index ef604e1..0000000 --- a/addons/dialogue_manager/l10n/es.po +++ /dev/null @@ -1,378 +0,0 @@ -# -msgid "" -msgstr "" -"Project-Id-Version: Dialogue Manager\n" -"POT-Creation-Date: 2024-02-25 20:58\n" -"PO-Revision-Date: 2024-02-25 20:58\n" -"Last-Translator: you \n" -"Language-Team: Spanish \n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" - -msgid "start_a_new_file" -msgstr "Crear un nuevo archivo" - -msgid "open_a_file" -msgstr "Abrir un archivo" - -msgid "open.open" -msgstr "Abrir..." - -msgid "open.no_recent_files" -msgstr "No hay archivos recientes" - -msgid "open.clear_recent_files" -msgstr "Limpiar archivos recientes" - -msgid "save_all_files" -msgstr "Guardar todos los archivos" - -msgid "test_dialogue" -msgstr "Diálogo de prueba" - -msgid "search_for_text" -msgstr "Buscar texto" - -msgid "insert" -msgstr "Insertar" - -msgid "translations" -msgstr "Traducciones" - -msgid "show_support" -msgstr "Contribuye con Dialogue Manager" - -msgid "docs" -msgstr "Docs" - -msgid "insert.wave_bbcode" -msgstr "BBCode ondulado" - -msgid "insert.shake_bbcode" -msgstr "BBCode agitado" - -msgid "insert.typing_pause" -msgstr "Pausa de escritura" - -msgid "insert.typing_speed_change" -msgstr "Cambiar la velocidad de escritura" - -msgid "insert.auto_advance" -msgstr "Avance automático" - -msgid "insert.templates" -msgstr "Plantillas" - -msgid "insert.title" -msgstr "Título" - -msgid "insert.dialogue" -msgstr "Diálogo" - -msgid "insert.response" -msgstr "Respuesta" - -msgid "insert.random_lines" -msgstr "Líneas aleatorias" - -msgid "insert.random_text" -msgstr "Texto aleatorio" - -msgid "insert.actions" -msgstr "Acciones" - -msgid "insert.jump" -msgstr "Ir al título" - -msgid "insert.end_dialogue" -msgstr "Finalizar diálogo" - -msgid "generate_line_ids" -msgstr "Generar IDs de línea" - -msgid "save_characters_to_csv" -msgstr "Guardar los nombres de los personajes en un archivo CSV..." - -msgid "save_to_csv" -msgstr "Guardar líneas en CSV..." - -msgid "import_from_csv" -msgstr "Importar cambios de línea desde CSV..." - -msgid "confirm_close" -msgstr "¿Guardar los cambios en '{path}'?" - -msgid "confirm_close.save" -msgstr "Guardar cambios" - -msgid "confirm_close.discard" -msgstr "Descartar" - -msgid "buffer.save" -msgstr "Guardar" - -msgid "buffer.save_as" -msgstr "Guardar como..." - -msgid "buffer.close" -msgstr "Cerrar" - -msgid "buffer.close_all" -msgstr "Cerrar todo" - -msgid "buffer.close_other_files" -msgstr "Cerrar otros archivos" - -msgid "buffer.copy_file_path" -msgstr "Copiar la ruta del archivo" - -msgid "buffer.show_in_filesystem" -msgstr "Mostrar en el sistema de archivos" - -msgid "n_of_n" -msgstr "{index} de {total}" - -msgid "search.previous" -msgstr "Anterior" - -msgid "search.next" -msgstr "Siguiente" - -msgid "search.match_case" -msgstr "Coincidir mayúsculas/minúsculas" - -msgid "search.toggle_replace" -msgstr "Reemplazar" - -msgid "search.replace_with" -msgstr "Reemplazar con:" - -msgid "search.replace" -msgstr "Reemplazar" - -msgid "search.replace_all" -msgstr "Reemplazar todo" - -msgid "files_list.filter" -msgstr "Filtrar archivos" - -msgid "titles_list.filter" -msgstr "Filtrar títulos" - -msgid "errors.key_not_found" -msgstr "La tecla \"{key}\" no se encuentra." - -msgid "errors.line_and_message" -msgstr "Error en {line}, {column}: {message}" - -msgid "errors_in_script" -msgstr "Tienes errores en tu guion. Corrígelos y luego inténtalo de nuevo." - -msgid "errors_with_build" -msgstr "Debes corregir los errores de diálogo antes de poder ejecutar tu juego." - -msgid "errors.import_errors" -msgstr "Hay errores en este archivo importado." - -msgid "errors.already_imported" -msgstr "Archivo ya importado." - -msgid "errors.duplicate_import" -msgstr "Nombre de importación duplicado." - -msgid "errors.unknown_using" -msgstr "Autoload desconocida en la declaración de uso." - -msgid "errors.empty_title" -msgstr "Los títulos no pueden estar vacíos." - -msgid "errors.duplicate_title" -msgstr "Ya hay un título con ese nombre." - -msgid "errors.nested_title" -msgstr "Los títulos no pueden tener sangría." - -msgid "errors.invalid_title_string" -msgstr "Los títulos solo pueden contener caracteres alfanuméricos y números." - -msgid "errors.invalid_title_number" -msgstr "Los títulos no pueden empezar con un número." - -msgid "errors.unknown_title" -msgstr "Título desconocido." - -msgid "errors.jump_to_invalid_title" -msgstr "Este salto está apuntando a un título inválido." - -msgid "errors.title_has_no_content" -msgstr "Ese título no tiene contenido. Quizá cambiarlo a \"=> FIN\"." - -msgid "errors.invalid_expression" -msgstr "La expresión es inválida." - -msgid "errors.unexpected_condition" -msgstr "Condición inesperada." - -msgid "errors.duplicate_id" -msgstr "Este ID ya está en otra línea." - -msgid "errors.missing_id" -msgstr "Esta línea está sin ID." - -msgid "errors.invalid_indentation" -msgstr "Sangría no válida." - -msgid "errors.condition_has_no_content" -msgstr "Una línea de condición necesita una línea sangrada debajo de ella." - -msgid "errors.incomplete_expression" -msgstr "Expresión incompleta." - -msgid "errors.invalid_expression_for_value" -msgstr "Expresión no válida para valor." - -msgid "errors.file_not_found" -msgstr "Archivo no encontrado." - -msgid "errors.unexpected_end_of_expression" -msgstr "Fin de expresión inesperado." - -msgid "errors.unexpected_function" -msgstr "Función inesperada." - -msgid "errors.unexpected_bracket" -msgstr "Corchete inesperado." - -msgid "errors.unexpected_closing_bracket" -msgstr "Bracket de cierre inesperado." - -msgid "errors.missing_closing_bracket" -msgstr "Falta cerrar corchete." - -msgid "errors.unexpected_operator" -msgstr "Operador inesperado." - -msgid "errors.unexpected_comma" -msgstr "Coma inesperada." - -msgid "errors.unexpected_colon" -msgstr "Dos puntos inesperados" - -msgid "errors.unexpected_dot" -msgstr "Punto inesperado." - -msgid "errors.unexpected_boolean" -msgstr "Booleano inesperado." - -msgid "errors.unexpected_string" -msgstr "String inesperado." - -msgid "errors.unexpected_number" -msgstr "Número inesperado." - -msgid "errors.unexpected_variable" -msgstr "Variable inesperada." - -msgid "errors.invalid_index" -msgstr "Índice no válido." - -msgid "errors.unexpected_assignment" -msgstr "Asignación inesperada." - -msgid "errors.unknown" -msgstr "Sintaxis desconocida." - -msgid "update.available" -msgstr "v{version} disponible" - -msgid "update.is_available_for_download" -msgstr "¡La versión %s ya está disponible para su descarga!" - -msgid "update.downloading" -msgstr "Descargando..." - -msgid "update.download_update" -msgstr "Descargar actualización" - -msgid "update.needs_reload" -msgstr "El proyecto debe ser recargado para instalar la actualización." - -msgid "update.reload_ok_button" -msgstr "Recargar proyecto" - -msgid "update.reload_cancel_button" -msgstr "Hazlo más tarde" - -msgid "update.reload_project" -msgstr "Recargar proyecto" - -msgid "update.release_notes" -msgstr "Leer las notas de la versión" - -msgid "update.success" -msgstr "El Gestor de Diálogo ahora es v{versión}." - -msgid "update.failed" -msgstr "Hubo un problema al descargar la actualización." - -msgid "runtime.no_resource" -msgstr "Recurso de diálogo no proporcionado." - -msgid "runtime.no_content" -msgstr "\"{file_path}\" no tiene contenido." - -msgid "runtime.errors" -msgstr "Tienes {count} errores en tu diálogo de texto." - -msgid "runtime.error_detail" -msgstr "Línea {line}: {message}" - -msgid "runtime.errors_see_details" -msgstr "Tienes {count} errores en tu texto de diálogo. Consulta la salida para más detalles." - -msgid "runtime.invalid_expression" -msgstr "\"{expression}\" no es una expresión válida: {error}" - -msgid "runtime.array_index_out_of_bounds" -msgstr "Índice {index} fuera de los límites del array \"{array}\"." - -msgid "runtime.left_hand_size_cannot_be_assigned_to" -msgstr "El lado izquierdo de la expresión no se puede asignar." - -msgid "runtime.key_not_found" -msgstr "Clave \"{key}\" no encontrada en el diccionario \"{dictionary}\"" - -msgid "runtime.property_not_found" -msgstr "\"{property}\" no es una propiedad en ningún estado del juego ({states})." - -msgid "runtime.property_not_found_missing_export" -msgstr "\"{property}\" no es una propiedad en ningún estado del juego ({states}). Es posible que necesites añadir un decorador [Export]." - -msgid "runtime.method_not_found" -msgstr "\"{method}\" no es un método en ningún estado del juego ({states})" - -msgid "runtime.signal_not_found" -msgstr "\"{signal_name}\" no es una señal en ningún estado del juego ({states})" - -msgid "runtime.method_not_callable" -msgstr "\"{method}\" no es un método llamable en \"{object}\"" - -msgid "runtime.unknown_operator" -msgstr "Operador desconocido." - -msgid "runtime.unknown_autoload" -msgstr "\"{autoload}\" parece no ser un autoload válido." - -msgid "runtime.something_went_wrong" -msgstr "Algo salió mal." - -msgid "runtime.expected_n_got_n_args" -msgstr "El método \"{method}\" se llamó con {received} argumentos, pero solo tiene {expected}." - -msgid "runtime.unsupported_array_type" -msgstr "Array[{type}] no está soportado en mutaciones. Utiliza Array como tipo en su lugar." - -msgid "runtime.dialogue_balloon_missing_start_method" -msgstr "Tu globo de diálogo no tiene un método \"start\" o \"Start\"." diff --git a/addons/dialogue_manager/l10n/translations.pot b/addons/dialogue_manager/l10n/translations.pot deleted file mode 100644 index 795b472..0000000 --- a/addons/dialogue_manager/l10n/translations.pot +++ /dev/null @@ -1,414 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Dialogue Manager\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8-bit\n" - -msgid "start_a_new_file" -msgstr "" - -msgid "open_a_file" -msgstr "" - -msgid "open.open" -msgstr "" - -msgid "open.quick_open" -msgstr "" - -msgid "open.no_recent_files" -msgstr "" - -msgid "open.clear_recent_files" -msgstr "" - -msgid "save_all_files" -msgstr "" - -msgid "find_in_files" -msgstr "" - -msgid "test_dialogue" -msgstr "" - -msgid "test_dialogue_from_line" -msgstr "" - -msgid "search_for_text" -msgstr "" - -msgid "insert" -msgstr "" - -msgid "translations" -msgstr "" - -msgid "sponsor" -msgstr "" - -msgid "show_support" -msgstr "" - -msgid "docs" -msgstr "" - -msgid "insert.wave_bbcode" -msgstr "" - -msgid "insert.shake_bbcode" -msgstr "" - -msgid "insert.typing_pause" -msgstr "" - -msgid "insert.typing_speed_change" -msgstr "" - -msgid "insert.auto_advance" -msgstr "" - -msgid "insert.templates" -msgstr "" - -msgid "insert.title" -msgstr "" - -msgid "insert.dialogue" -msgstr "" - -msgid "insert.response" -msgstr "" - -msgid "insert.random_lines" -msgstr "" - -msgid "insert.random_text" -msgstr "" - -msgid "insert.actions" -msgstr "" - -msgid "insert.jump" -msgstr "" - -msgid "insert.end_dialogue" -msgstr "" - -msgid "generate_line_ids" -msgstr "" - -msgid "save_to_csv" -msgstr "" - -msgid "import_from_csv" -msgstr "" - -msgid "confirm_close" -msgstr "" - -msgid "confirm_close.save" -msgstr "" - -msgid "confirm_close.discard" -msgstr "" - -msgid "buffer.save" -msgstr "" - -msgid "buffer.save_as" -msgstr "" - -msgid "buffer.close" -msgstr "" - -msgid "buffer.close_all" -msgstr "" - -msgid "buffer.close_other_files" -msgstr "" - -msgid "buffer.copy_file_path" -msgstr "" - -msgid "buffer.show_in_filesystem" -msgstr "" - -msgid "n_of_n" -msgstr "" - -msgid "search.find" -msgstr "" - -msgid "search.find_all" -msgstr "" - -msgid "search.placeholder" -msgstr "" - -msgid "search.replace_placeholder" -msgstr "" - -msgid "search.replace_selected" -msgstr "" - -msgid "search.previous" -msgstr "" - -msgid "search.next" -msgstr "" - -msgid "search.match_case" -msgstr "" - -msgid "search.toggle_replace" -msgstr "" - -msgid "search.replace_with" -msgstr "" - -msgid "search.replace" -msgstr "" - -msgid "search.replace_all" -msgstr "" - -msgid "files_list.filter" -msgstr "" - -msgid "titles_list.filter" -msgstr "" - -msgid "errors.key_not_found" -msgstr "" - -msgid "errors.line_and_message" -msgstr "" - -msgid "errors_in_script" -msgstr "" - -msgid "errors_with_build" -msgstr "" - -msgid "errors.import_errors" -msgstr "" - -msgid "errors.already_imported" -msgstr "" - -msgid "errors.duplicate_import" -msgstr "" - -msgid "errors.unknown_using" -msgstr "" - -msgid "errors.empty_title" -msgstr "" - -msgid "errors.duplicate_title" -msgstr "" - -msgid "errors.invalid_title_string" -msgstr "" - -msgid "errors.invalid_title_number" -msgstr "" - -msgid "errors.unknown_title" -msgstr "" - -msgid "errors.jump_to_invalid_title" -msgstr "" - -msgid "errors.title_has_no_content" -msgstr "" - -msgid "errors.invalid_expression" -msgstr "" - -msgid "errors.unexpected_condition" -msgstr "" - -msgid "errors.duplicate_id" -msgstr "" - -msgid "errors.missing_id" -msgstr "" - -msgid "errors.invalid_indentation" -msgstr "" - -msgid "errors.condition_has_no_content" -msgstr "" - -msgid "errors.incomplete_expression" -msgstr "" - -msgid "errors.invalid_expression_for_value" -msgstr "" - -msgid "errors.file_not_found" -msgstr "" - -msgid "errors.unexpected_end_of_expression" -msgstr "" - -msgid "errors.unexpected_function" -msgstr "" - -msgid "errors.unexpected_bracket" -msgstr "" - -msgid "errors.unexpected_closing_bracket" -msgstr "" - -msgid "errors.missing_closing_bracket" -msgstr "" - -msgid "errors.unexpected_operator" -msgstr "" - -msgid "errors.unexpected_comma" -msgstr "" - -msgid "errors.unexpected_colon" -msgstr "" - -msgid "errors.unexpected_dot" -msgstr "" - -msgid "errors.unexpected_boolean" -msgstr "" - -msgid "errors.unexpected_string" -msgstr "" - -msgid "errors.unexpected_number" -msgstr "" - -msgid "errors.unexpected_variable" -msgstr "" - -msgid "errors.invalid_index" -msgstr "" - -msgid "errors.unexpected_assignment" -msgstr "" - -msgid "errors.expected_when_or_else" -msgstr "" - -msgid "errors.only_one_else_allowed" -msgstr "" - -msgid "errors.when_must_belong_to_match" -msgstr "" - -msgid "errors.concurrent_line_without_origin" -msgstr "" - -msgid "errors.goto_not_allowed_on_concurrect_lines" -msgstr "" - -msgid "errors.unknown" -msgstr "" - -msgid "update.available" -msgstr "" - -msgid "update.is_available_for_download" -msgstr "" - -msgid "update.downloading" -msgstr "" - -msgid "update.download_update" -msgstr "" - -msgid "update.needs_reload" -msgstr "" - -msgid "update.reload_ok_button" -msgstr "" - -msgid "update.reload_cancel_button" -msgstr "" - -msgid "update.reload_project" -msgstr "" - -msgid "update.release_notes" -msgstr "" - -msgid "update.success" -msgstr "" - -msgid "update.failed" -msgstr "" - -msgid "runtime.no_resource" -msgstr "" - -msgid "runtime.no_content" -msgstr "" - -msgid "runtime.errors" -msgstr "" - -msgid "runtime.error_detail" -msgstr "" - -msgid "runtime.errors_see_details" -msgstr "" - -msgid "runtime.invalid_expression" -msgstr "" - -msgid "runtime.array_index_out_of_bounds" -msgstr "" - -msgid "runtime.left_hand_size_cannot_be_assigned_to" -msgstr "" - -msgid "runtime.key_not_found" -msgstr "" - -msgid "runtime.property_not_found" -msgstr "" - -msgid "runtime.property_not_found_missing_export" -msgstr "" - -msgid "runtime.method_not_found" -msgstr "" - -msgid "runtime.signal_not_found" -msgstr "" - -msgid "runtime.method_not_callable" -msgstr "" - -msgid "runtime.unknown_operator" -msgstr "" - -msgid "runtime.unknown_autoload" -msgstr "" - -msgid "runtime.something_went_wrong" -msgstr "" - -msgid "runtime.expected_n_got_n_args" -msgstr "" - -msgid "runtime.unsupported_array_type" -msgstr "" - -msgid "runtime.dialogue_balloon_missing_start_method" -msgstr "" - -msgid "runtime.top_level_states_share_name" -msgstr "" - -msgid "translation_plugin.character_name" -msgstr "" \ No newline at end of file diff --git a/addons/dialogue_manager/l10n/uk.po b/addons/dialogue_manager/l10n/uk.po deleted file mode 100644 index d0a6df3..0000000 --- a/addons/dialogue_manager/l10n/uk.po +++ /dev/null @@ -1,411 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Dialogue Manager\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Language: uk\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.2.2\n" - -msgid "start_a_new_file" -msgstr "Створити новий файл" - -msgid "open_a_file" -msgstr "Відкрити файл" - -msgid "open.open" -msgstr "Відкрити..." - -msgid "open.quick_open" -msgstr "Швидко відкрити..." - -msgid "open.no_recent_files" -msgstr "Жодних недавніх файлів" - -msgid "open.clear_recent_files" -msgstr "Очистити недавні файли" - -msgid "save_all_files" -msgstr "Зберегти всі файли" - -msgid "find_in_files" -msgstr "Знайти у файлах..." - -msgid "test_dialogue" -msgstr "Протестувати діалог з початку файлу" - -msgid "test_dialogue_from_line" -msgstr "Протестувати діалог з поточного рядка" - -msgid "search_for_text" -msgstr "Пошук тексту" - -msgid "insert" -msgstr "Вставити" - -msgid "translations" -msgstr "Переклади" - -msgid "sponsor" -msgstr "Спонсор" - -msgid "show_support" -msgstr "Підтримка Dialogue Manager" - -msgid "docs" -msgstr "Документація" - -msgid "insert.wave_bbcode" -msgstr "Хвиля BBCode" - -msgid "insert.shake_bbcode" -msgstr "Тряска BBCode" - -msgid "insert.typing_pause" -msgstr "Пауза друку" - -msgid "insert.typing_speed_change" -msgstr "Зміна швидкості друку" - -msgid "insert.auto_advance" -msgstr "Автоматичне просування" - -msgid "insert.templates" -msgstr "Шаблони" - -msgid "insert.title" -msgstr "Заголовок" - -msgid "insert.dialogue" -msgstr "Діалог" - -msgid "insert.response" -msgstr "Відповідь" - -msgid "insert.random_lines" -msgstr "Випадкові рядки" - -msgid "insert.random_text" -msgstr "Випадковий текст" - -msgid "insert.actions" -msgstr "Дії" - -msgid "insert.jump" -msgstr "Перейти до заголовку" - -msgid "insert.end_dialogue" -msgstr "Кінець діалогу" - -msgid "generate_line_ids" -msgstr "Згенерувати ідентифікатори рядків" - -msgid "save_characters_to_csv" -msgstr "Зберегти імена персонажів в CSV..." - -msgid "save_to_csv" -msgstr "Зберегти рядки в CSV..." - -msgid "import_from_csv" -msgstr "Імпортувати зміни рядків з CSV..." - -msgid "confirm_close" -msgstr "Зберегти зміни до «{path}»?" - -msgid "confirm_close.save" -msgstr "Зберегти зміни" - -msgid "confirm_close.discard" -msgstr "Скасувати" - -msgid "buffer.save" -msgstr "Зберегти" - -msgid "buffer.save_as" -msgstr "Зберегти як..." - -msgid "buffer.close" -msgstr "Закрити" - -msgid "buffer.close_all" -msgstr "Закрити все" - -msgid "buffer.close_other_files" -msgstr "Закрити інші файли" - -msgid "buffer.copy_file_path" -msgstr "Копіювати шлях файлу" - -msgid "buffer.show_in_filesystem" -msgstr "Показати у файловій системі" - -msgid "n_of_n" -msgstr "{index} з {total}" - -msgid "search.find" -msgstr "Знайти:" - -msgid "search.find_all" -msgstr "Знайти всі..." - -msgid "search.placeholder" -msgstr "Текст для пошуку" - -msgid "search.replace_placeholder" -msgstr "Текст для заміни" - -msgid "search.replace_selected" -msgstr "Замінити вибране" - -msgid "search.previous" -msgstr "Назад" - -msgid "search.next" -msgstr "Далі" - -msgid "search.match_case" -msgstr "Збіг регістру" - -msgid "search.toggle_replace" -msgstr "Замінити" - -msgid "search.replace_with" -msgstr "Замінити на:" - -msgid "search.replace" -msgstr "Замінити" - -msgid "search.replace_all" -msgstr "Замінити все" - -msgid "files_list.filter" -msgstr "Фільтр файлів" - -msgid "titles_list.filter" -msgstr "Фільтр заголовків" - -msgid "errors.key_not_found" -msgstr "Ключ «{key}» не знайдено." - -msgid "errors.line_and_message" -msgstr "Помилка в {line}, {column}: {message}" - -msgid "errors_in_script" -msgstr "У вашому скрипті є помилки. Виправте їх і спробуйте ще раз." - -msgid "errors_with_build" -msgstr "Вам потрібно виправити помилки в діалогах, перш ніж ви зможете запустити гру." - -msgid "errors.import_errors" -msgstr "В імпортованому файлі є помилки." - -msgid "errors.already_imported" -msgstr "Файл уже імпортовано." - -msgid "errors.duplicate_import" -msgstr "Дублювання назви імпорту." - -msgid "errors.unknown_using" -msgstr "Невідоме автозавантаження в операторі «using»." - -msgid "errors.empty_title" -msgstr "Заголовки не можуть бути порожніми." - -msgid "errors.duplicate_title" -msgstr "Заголовок з такою назвою уже є." - -msgid "errors.invalid_title_string" -msgstr "Заголовки можуть містити лише алфавітно-цифрові символи та цифри." - -msgid "errors.invalid_title_number" -msgstr "Заголовки не можуть починатися з цифри." - -msgid "errors.unknown_title" -msgstr "Невідомий заголовок." - -msgid "errors.jump_to_invalid_title" -msgstr "Цей перехід вказує на недійсний заголовок." - -msgid "errors.title_has_no_content" -msgstr "Цей заголовок не має змісту. Можливо, варто змінити його на «=> END»." - -msgid "errors.invalid_expression" -msgstr "Вираз є недійсним." - -msgid "errors.unexpected_condition" -msgstr "Несподівана умова." - -msgid "errors.duplicate_id" -msgstr "Цей ідентифікатор уже є на іншому рядку." - -msgid "errors.missing_id" -msgstr "У цьому рядку відсутній ідентифікатор." - -msgid "errors.invalid_indentation" -msgstr "Неправильний відступ." - -msgid "errors.condition_has_no_content" -msgstr "Рядок умови потребує відступу під ним." - -msgid "errors.incomplete_expression" -msgstr "Незавершений вираз." - -msgid "errors.invalid_expression_for_value" -msgstr "Недійсний вираз для значення." - -msgid "errors.file_not_found" -msgstr "Файл не знайдено." - -msgid "errors.unexpected_end_of_expression" -msgstr "Несподіваний кінець виразу." - -msgid "errors.unexpected_function" -msgstr "Несподівана функція." - -msgid "errors.unexpected_bracket" -msgstr "Несподівана дужка." - -msgid "errors.unexpected_closing_bracket" -msgstr "Несподівана закриваюча дужка." - -msgid "errors.missing_closing_bracket" -msgstr "Відсутня закриваюча дужка." - -msgid "errors.unexpected_operator" -msgstr "Несподіваний оператор." - -msgid "errors.unexpected_comma" -msgstr "Несподівана кома." - -msgid "errors.unexpected_colon" -msgstr "Несподівана двокрапка." - -msgid "errors.unexpected_dot" -msgstr "Несподівана крапка." - -msgid "errors.unexpected_boolean" -msgstr "Несподіваний логічний вираз." - -msgid "errors.unexpected_string" -msgstr "Несподіваний рядок." - -msgid "errors.unexpected_number" -msgstr "Несподіване число." - -msgid "errors.unexpected_variable" -msgstr "Несподівана змінна." - -msgid "errors.invalid_index" -msgstr "Недійсний індекс." - -msgid "errors.unexpected_assignment" -msgstr "Несподіване призначення." - -msgid "errors.expected_when_or_else" -msgstr "Очікувався випадок «when» або «else»." - -msgid "errors.only_one_else_allowed" -msgstr "Для кожного «match» допускається лише один випадок «else»." - -msgid "errors.when_must_belong_to_match" -msgstr "Оператори «when» можуть з’являтися лише як дочірні операторів «match»." - -msgid "errors.unknown" -msgstr "Невідомий синтаксис." - -msgid "update.available" -msgstr "Доступна версія {version}" - -msgid "update.is_available_for_download" -msgstr "Версія %s доступна для завантаження!" - -msgid "update.downloading" -msgstr "Завантаження..." - -msgid "update.download_update" -msgstr "Завантажити оновлення" - -msgid "update.needs_reload" -msgstr "Щоб установити оновлення, проєкт потрібно перезавантажити." - -msgid "update.reload_ok_button" -msgstr "Перезавантажити проєкт" - -msgid "update.reload_cancel_button" -msgstr "Пізніше" - -msgid "update.reload_project" -msgstr "Перезавантажити проєкт" - -msgid "update.release_notes" -msgstr "Читати зміни оновлення" - -msgid "update.success" -msgstr "Dialogue Manager тепер з версією {version}." - -msgid "update.failed" -msgstr "Виникла проблема із завантаженням оновлення." - -msgid "runtime.no_resource" -msgstr "Ресурс для діалогу не надано." - -msgid "runtime.no_content" -msgstr "«{file_path}» не має вмісту." - -msgid "runtime.errors" -msgstr "У тексті діалогу було виявлено помилки ({count})." - -msgid "runtime.error_detail" -msgstr "Рядок {line}: {message}" - -msgid "runtime.errors_see_details" -msgstr "У тексті діалогу було виявлено помилки ({count}). Див. детальніше у розділі «Вивід»." - -msgid "runtime.invalid_expression" -msgstr "«{expression}» не є допустимим виразом: {error}" - -msgid "runtime.array_index_out_of_bounds" -msgstr "Індекс {index} виходить за межі масиву «{array}»." - -msgid "runtime.left_hand_size_cannot_be_assigned_to" -msgstr "Ліва частина виразу не може бути призначена." - -msgid "runtime.key_not_found" -msgstr "Ключ «{key}» не знайдено у словнику «{dictionary}»" - -msgid "runtime.property_not_found" -msgstr "«{property}» не знайдено. Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей." - -msgid "runtime.property_not_found_missing_export" -msgstr "«{property}» не знайдено. Можливо, вам слід додати декоратор «[Export]». Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей." - -msgid "runtime.method_not_found" -msgstr "Метод «{method}» не знайдено. Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей." - -msgid "runtime.signal_not_found" -msgstr "Сигнал «{signal_name}» не знайдено. Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей." - -msgid "runtime.method_not_callable" -msgstr "«{method}» не є методом, який можна викликати в «{object}»" - -msgid "runtime.unknown_operator" -msgstr "Невідомий оператор." - -msgid "runtime.unknown_autoload" -msgstr "Схоже, «{autoload}» не є дійсним автозавантаженням." - -msgid "runtime.something_went_wrong" -msgstr "Щось пішло не так." - -msgid "runtime.expected_n_got_n_args" -msgstr "«{method}» було викликано з аргументами «{received}», але воно має лише «{expected}»." - -msgid "runtime.unsupported_array_type" -msgstr "Array[{type}] не підтримується у модифікаціях. Натомість використовуйте Array як тип." - -msgid "runtime.dialogue_balloon_missing_start_method" -msgstr "У вашій кулі діалогу відсутній метод «start» або «Start»." diff --git a/addons/dialogue_manager/l10n/zh.po b/addons/dialogue_manager/l10n/zh.po deleted file mode 100644 index bafd1d5..0000000 --- a/addons/dialogue_manager/l10n/zh.po +++ /dev/null @@ -1,378 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Dialogue Manager\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: penghao123456、憨憨羊の宇航鸽鸽、ABShinri\n" -"Language: zh\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.4\n" - -msgid "start_a_new_file" -msgstr "创建新文件" - -msgid "open_a_file" -msgstr "打开已有文件" - -msgid "open.open" -msgstr "打开……" - -msgid "open.no_recent_files" -msgstr "无历史记录" - -msgid "open.clear_recent_files" -msgstr "清空历史记录" - -msgid "save_all_files" -msgstr "保存所有文件" - -msgid "find_in_files" -msgstr "在文件中查找" - -msgid "test_dialogue" -msgstr "测试对话" - -msgid "search_for_text" -msgstr "查找……" - -msgid "insert" -msgstr "插入" - -msgid "translations" -msgstr "翻译" - -msgid "show_support" -msgstr "支持 Dialogue Manager" - -msgid "docs" -msgstr "文档" - -msgid "insert.wave_bbcode" -msgstr "波浪效果" - -msgid "insert.shake_bbcode" -msgstr "抖动效果" - -msgid "insert.typing_pause" -msgstr "输入间隔" - -msgid "insert.typing_speed_change" -msgstr "输入速度变更" - -msgid "insert.auto_advance" -msgstr "自动切行" - -msgid "insert.templates" -msgstr "模板" - -msgid "insert.title" -msgstr "标题" - -msgid "insert.dialogue" -msgstr "对话" - -msgid "insert.response" -msgstr "回复选项" - -msgid "insert.random_lines" -msgstr "随机行" - -msgid "insert.random_text" -msgstr "随机文本" - -msgid "insert.actions" -msgstr "操作" - -msgid "insert.jump" -msgstr "标题间跳转" - -msgid "insert.end_dialogue" -msgstr "结束对话" - -msgid "generate_line_ids" -msgstr "生成行 ID" - -msgid "save_characters_to_csv" -msgstr "保存角色到 CSV" - -msgid "save_to_csv" -msgstr "生成 CSV" - -msgid "import_from_csv" -msgstr "从 CSV 导入" - -msgid "confirm_close" -msgstr "是否要保存到“{path}”?" - -msgid "confirm_close.save" -msgstr "保存" - -msgid "confirm_close.discard" -msgstr "不保存" - -msgid "buffer.save" -msgstr "保存" - -msgid "buffer.save_as" -msgstr "另存为……" - -msgid "buffer.close" -msgstr "关闭" - -msgid "buffer.close_all" -msgstr "全部关闭" - -msgid "buffer.close_other_files" -msgstr "关闭其他文件" - -msgid "buffer.copy_file_path" -msgstr "复制文件路径" - -msgid "buffer.show_in_filesystem" -msgstr "在 Godot 侧边栏中显示" - -msgid "n_of_n" -msgstr "第{index}个,共{total}个" - -msgid "search.find" -msgstr "查找:" - -msgid "search.find_all" -msgstr "查找全部..." - -msgid "search.placeholder" -msgstr "请输入查找的内容" - -msgid "search.replace_placeholder" -msgstr "请输入替换的内容" - -msgid "search.replace_selected" -msgstr "替换勾选" - -msgid "search.previous" -msgstr "查找上一个" - -msgid "search.next" -msgstr "查找下一个" - -msgid "search.match_case" -msgstr "大小写敏感" - -msgid "search.toggle_replace" -msgstr "替换" - -msgid "search.replace_with" -msgstr "替换为" - -msgid "search.replace" -msgstr "替换" - -msgid "search.replace_all" -msgstr "全部替换" - -msgid "files_list.filter" -msgstr "查找文件" - -msgid "titles_list.filter" -msgstr "查找标题" - -msgid "errors.key_not_found" -msgstr "键“{key}”未找到" - -msgid "errors.line_and_message" -msgstr "第{line}行第{colume}列发生错误:{message}" - -msgid "errors_in_script" -msgstr "你的脚本中存在错误。请修复错误,然后重试。" - -msgid "errors_with_build" -msgstr "请先解决 Dialogue 中的错误。" - -msgid "errors.import_errors" -msgstr "被导入的文件存在问题。" - -msgid "errors.already_imported" -msgstr "文件已被导入。" - -msgid "errors.duplicate_import" -msgstr "导入名不能重复。" - -msgid "errors.empty_title" -msgstr "标题名不能为空。" - -msgid "errors.duplicate_title" -msgstr "标题名不能重复。" - -msgid "errors.invalid_title_string" -msgstr "标题名无效。" - -msgid "errors.invalid_title_number" -msgstr "标题不能以数字开始。" - -msgid "errors.unknown_title" -msgstr "标题未定义。" - -msgid "errors.jump_to_invalid_title" -msgstr "标题名无效。" - -msgid "errors.title_has_no_content" -msgstr "目标标题为空。请替换为“=> END”。" - -msgid "errors.invalid_expression" -msgstr "表达式无效。" - -msgid "errors.unexpected_condition" -msgstr "未知条件。" - -msgid "errors.duplicate_id" -msgstr "ID 重复。" - -msgid "errors.missing_id" -msgstr "ID 不存在。" - -msgid "errors.invalid_indentation" -msgstr "缩进无效。" - -msgid "errors.condition_has_no_content" -msgstr "条件下方不能为空。" - -msgid "errors.incomplete_expression" -msgstr "不完整的表达式。" - -msgid "errors.invalid_expression_for_value" -msgstr "无效的赋值表达式。" - -msgid "errors.file_not_found" -msgstr "文件不存在。" - -msgid "errors.unexpected_end_of_expression" -msgstr "表达式 end 不应存在。" - -msgid "errors.unexpected_function" -msgstr "函数不应存在。" - -msgid "errors.unexpected_bracket" -msgstr "方括号不应存在。" - -msgid "errors.unexpected_closing_bracket" -msgstr "方括号不应存在。" - -msgid "errors.missing_closing_bracket" -msgstr "闭方括号不存在。" - -msgid "errors.unexpected_operator" -msgstr "操作符不应存在。" - -msgid "errors.unexpected_comma" -msgstr "逗号不应存在。" - -msgid "errors.unexpected_colon" -msgstr "冒号不应存在。" - -msgid "errors.unexpected_dot" -msgstr "句号不应存在。" - -msgid "errors.unexpected_boolean" -msgstr "布尔值不应存在。" - -msgid "errors.unexpected_string" -msgstr "字符串不应存在。" - -msgid "errors.unexpected_number" -msgstr "数字不应存在。" - -msgid "errors.unexpected_variable" -msgstr "标识符不应存在。" - -msgid "errors.invalid_index" -msgstr "索引无效。" - -msgid "errors.unexpected_assignment" -msgstr "不应在条件判断中使用 = ,应使用 == 。" - -msgid "errors.unknown" -msgstr "语法错误。" - -msgid "update.available" -msgstr "v{version} 更新可用。" - -msgid "update.is_available_for_download" -msgstr "v%s 已经可以下载。" - -msgid "update.downloading" -msgstr "正在下载更新……" - -msgid "update.download_update" -msgstr "下载" - -msgid "update.needs_reload" -msgstr "需要重新加载项目以应用更新。" - -msgid "update.reload_ok_button" -msgstr "重新加载" - -msgid "update.reload_cancel_button" -msgstr "暂不重新加载" - -msgid "update.reload_project" -msgstr "重新加载" - -msgid "update.release_notes" -msgstr "查看发行注记" - -msgid "update.success" -msgstr "v{version} 已成功安装并应用。" - -msgid "update.failed" -msgstr "更新失败。" - -msgid "runtime.no_resource" -msgstr "找不到资源。" - -msgid "runtime.no_content" -msgstr "资源“{file_path}”为空。" - -msgid "runtime.errors" -msgstr "文件中存在{errrors}个错误。" - -msgid "runtime.error_detail" -msgstr "第{index}行:{message}" - -msgid "runtime.errors_see_details" -msgstr "文件中存在{errrors}个错误。请查看调试输出。" - -msgid "runtime.invalid_expression" -msgstr "表达式“{expression}”无效:{error}" - -msgid "runtime.array_index_out_of_bounds" -msgstr "数组索引“{index}”越界。(数组名:“{array}”)" - -msgid "runtime.left_hand_size_cannot_be_assigned_to" -msgstr "表达式左侧的变量无法被赋值。" - -msgid "runtime.key_not_found" -msgstr "键“{key}”在字典“{dictionary}”中不存在。" - -msgid "runtime.property_not_found" -msgstr "“{property}”不存在。(全局变量:{states})" - -msgid "runtime.property_not_found_missing_export" -msgstr "“{property}”不存在。(全局变量:{states})你可能需要添加一个修饰词 [Export]。" - -msgid "runtime.method_not_found" -msgstr "“{method}”不存在。(全局变量:{states})" - -msgid "runtime.signal_not_found" -msgstr "“{sighal_name}”不存在。(全局变量:{states})" - -msgid "runtime.method_not_callable" -msgstr "{method}不是对象“{object}”上的函数。" - -msgid "runtime.unknown_operator" -msgstr "未知操作符。" - -msgid "runtime.something_went_wrong" -msgstr "有什么出错了。" diff --git a/addons/dialogue_manager/l10n/zh_TW.po b/addons/dialogue_manager/l10n/zh_TW.po deleted file mode 100644 index e20feee..0000000 --- a/addons/dialogue_manager/l10n/zh_TW.po +++ /dev/null @@ -1,378 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Dialogue Manager\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: 憨憨羊の宇航鴿鴿、ABShinri\n" -"Language: zh_TW\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.4\n" - -msgid "start_a_new_file" -msgstr "創建新檔案" - -msgid "open_a_file" -msgstr "開啟已有檔案" - -msgid "open.open" -msgstr "開啟……" - -msgid "open.no_recent_files" -msgstr "無歷史記錄" - -msgid "open.clear_recent_files" -msgstr "清空歷史記錄" - -msgid "save_all_files" -msgstr "儲存所有檔案" - -msgid "find_in_files" -msgstr "在檔案中查找" - -msgid "test_dialogue" -msgstr "測試對話" - -msgid "search_for_text" -msgstr "搜尋……" - -msgid "insert" -msgstr "插入" - -msgid "translations" -msgstr "翻譯" - -msgid "show_support" -msgstr "支援 Dialogue Manager" - -msgid "docs" -msgstr "文檔" - -msgid "insert.wave_bbcode" -msgstr "波浪特效" - -msgid "insert.shake_bbcode" -msgstr "震動特效" - -msgid "insert.typing_pause" -msgstr "輸入間隔" - -msgid "insert.typing_speed_change" -msgstr "輸入速度變更" - -msgid "insert.auto_advance" -msgstr "自動切行" - -msgid "insert.templates" -msgstr "模板" - -msgid "insert.title" -msgstr "標題" - -msgid "insert.dialogue" -msgstr "對話" - -msgid "insert.response" -msgstr "回覆選項" - -msgid "insert.random_lines" -msgstr "隨機行" - -msgid "insert.random_text" -msgstr "隨機文本" - -msgid "insert.actions" -msgstr "操作" - -msgid "insert.jump" -msgstr "標題間跳轉" - -msgid "insert.end_dialogue" -msgstr "結束對話" - -msgid "generate_line_ids" -msgstr "生成行 ID" - -msgid "save_characters_to_csv" -msgstr "保存角色到 CSV" - -msgid "save_to_csv" -msgstr "生成 CSV" - -msgid "import_from_csv" -msgstr "從 CSV 匯入" - -msgid "confirm_close" -msgstr "是否要儲存到“{path}”?" - -msgid "confirm_close.save" -msgstr "儲存" - -msgid "confirm_close.discard" -msgstr "不儲存" - -msgid "buffer.save" -msgstr "儲存" - -msgid "buffer.save_as" -msgstr "儲存爲……" - -msgid "buffer.close" -msgstr "關閉" - -msgid "buffer.close_all" -msgstr "全部關閉" - -msgid "buffer.close_other_files" -msgstr "關閉其他檔案" - -msgid "buffer.copy_file_path" -msgstr "複製檔案位置" - -msgid "buffer.show_in_filesystem" -msgstr "在 Godot 側邊欄中顯示" - -msgid "n_of_n" -msgstr "第{index}個,共{total}個" - -msgid "search.find" -msgstr "搜尋:" - -msgid "search.find_all" -msgstr "搜尋全部..." - -msgid "search.placeholder" -msgstr "請輸入搜尋的內容" - -msgid "search.replace_placeholder" -msgstr "請輸入替換的內容" - -msgid "search.replace_selected" -msgstr "替換勾選" - -msgid "search.previous" -msgstr "搜尋上一個" - -msgid "search.next" -msgstr "搜尋下一個" - -msgid "search.match_case" -msgstr "大小寫敏感" - -msgid "search.toggle_replace" -msgstr "替換" - -msgid "search.replace_with" -msgstr "替換爲" - -msgid "search.replace" -msgstr "替換" - -msgid "search.replace_all" -msgstr "全部替換" - -msgid "files_list.filter" -msgstr "搜尋檔案" - -msgid "titles_list.filter" -msgstr "搜尋標題" - -msgid "errors.key_not_found" -msgstr "鍵“{key}”未找到" - -msgid "errors.line_and_message" -msgstr "第{line}行第{colume}列發生錯誤:{message}" - -msgid "errors_in_script" -msgstr "你的腳本中存在錯誤。請修復錯誤,然後重試。" - -msgid "errors_with_build" -msgstr "請先解決 Dialogue 中的錯誤。" - -msgid "errors.import_errors" -msgstr "被匯入的檔案存在問題。" - -msgid "errors.already_imported" -msgstr "檔案已被匯入。" - -msgid "errors.duplicate_import" -msgstr "匯入名不能重複。" - -msgid "errors.empty_title" -msgstr "標題名不能爲空。" - -msgid "errors.duplicate_title" -msgstr "標題名不能重複。" - -msgid "errors.invalid_title_string" -msgstr "標題名無效。" - -msgid "errors.invalid_title_number" -msgstr "標題不能以數字開始。" - -msgid "errors.unknown_title" -msgstr "標題未定義。" - -msgid "errors.jump_to_invalid_title" -msgstr "標題名無效。" - -msgid "errors.title_has_no_content" -msgstr "目標標題爲空。請替換爲“=> END”。" - -msgid "errors.invalid_expression" -msgstr "表達式無效。" - -msgid "errors.unexpected_condition" -msgstr "未知條件。" - -msgid "errors.duplicate_id" -msgstr "ID 重複。" - -msgid "errors.missing_id" -msgstr "ID 不存在。" - -msgid "errors.invalid_indentation" -msgstr "縮進無效。" - -msgid "errors.condition_has_no_content" -msgstr "條件下方不能爲空。" - -msgid "errors.incomplete_expression" -msgstr "不完整的表達式。" - -msgid "errors.invalid_expression_for_value" -msgstr "無效的賦值表達式。" - -msgid "errors.file_not_found" -msgstr "檔案不存在。" - -msgid "errors.unexpected_end_of_expression" -msgstr "表達式 end 不應存在。" - -msgid "errors.unexpected_function" -msgstr "函數不應存在。" - -msgid "errors.unexpected_bracket" -msgstr "方括號不應存在。" - -msgid "errors.unexpected_closing_bracket" -msgstr "方括號不應存在。" - -msgid "errors.missing_closing_bracket" -msgstr "閉方括號不存在。" - -msgid "errors.unexpected_operator" -msgstr "操作符不應存在。" - -msgid "errors.unexpected_comma" -msgstr "逗號不應存在。" - -msgid "errors.unexpected_colon" -msgstr "冒號不應存在。" - -msgid "errors.unexpected_dot" -msgstr "句號不應存在。" - -msgid "errors.unexpected_boolean" -msgstr "布爾值不應存在。" - -msgid "errors.unexpected_string" -msgstr "字符串不應存在。" - -msgid "errors.unexpected_number" -msgstr "數字不應存在。" - -msgid "errors.unexpected_variable" -msgstr "標識符不應存在。" - -msgid "errors.invalid_index" -msgstr "索引無效。" - -msgid "errors.unexpected_assignment" -msgstr "不應在條件判斷中使用 = ,應使用 == 。" - -msgid "errors.unknown" -msgstr "語法錯誤。" - -msgid "update.available" -msgstr "v{version} 更新可用。" - -msgid "update.is_available_for_download" -msgstr "v%s 已經可以下載。" - -msgid "update.downloading" -msgstr "正在下載更新……" - -msgid "update.download_update" -msgstr "下載" - -msgid "update.needs_reload" -msgstr "需要重新加載項目以套用更新。" - -msgid "update.reload_ok_button" -msgstr "重新加載" - -msgid "update.reload_cancel_button" -msgstr "暫不重新加載" - -msgid "update.reload_project" -msgstr "重新加載" - -msgid "update.release_notes" -msgstr "查看發行註記" - -msgid "update.success" -msgstr "v{version} 已成功安裝並套用。" - -msgid "update.failed" -msgstr "更新失敗。" - -msgid "runtime.no_resource" -msgstr "找不到資源。" - -msgid "runtime.no_content" -msgstr "資源“{file_path}”爲空。" - -msgid "runtime.errors" -msgstr "檔案中存在{errrors}個錯誤。" - -msgid "runtime.error_detail" -msgstr "第{index}行:{message}" - -msgid "runtime.errors_see_details" -msgstr "檔案中存在{errrors}個錯誤。請查看調試輸出。" - -msgid "runtime.invalid_expression" -msgstr "表達式“{expression}”無效:{error}" - -msgid "runtime.array_index_out_of_bounds" -msgstr "數組索引“{index}”越界。(數組名:“{array}”)" - -msgid "runtime.left_hand_size_cannot_be_assigned_to" -msgstr "表達式左側的變量無法被賦值。" - -msgid "runtime.key_not_found" -msgstr "鍵“{key}”在字典“{dictionary}”中不存在。" - -msgid "runtime.property_not_found" -msgstr "“{property}”不存在。(全局變量:{states})" - -msgid "runtime.method_not_found" -msgstr "“{method}”不存在。(全局變量:{states})" - -msgid "runtime.signal_not_found" -msgstr "“{sighal_name}”不存在。(全局變量:{states})" - -msgid "runtime.property_not_found_missing_export" -msgstr "“{property}”不存在。(全局變量:{states})你可能需要添加一個修飾詞 [Export]。" - -msgid "runtime.method_not_callable" -msgstr "{method}不是對象“{object}”上的函數。" - -msgid "runtime.unknown_operator" -msgstr "未知操作符。" - -msgid "runtime.something_went_wrong" -msgstr "有什麼出錯了。" diff --git a/addons/dialogue_manager/plugin.cfg b/addons/dialogue_manager/plugin.cfg deleted file mode 100644 index 6559879..0000000 --- a/addons/dialogue_manager/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Dialogue Manager" -description="A powerful nonlinear dialogue system" -author="Nathan Hoad" -version="3.3.2" -script="plugin.gd" diff --git a/addons/dialogue_manager/plugin.cfg.uid b/addons/dialogue_manager/plugin.cfg.uid deleted file mode 100644 index 312d0cf..0000000 --- a/addons/dialogue_manager/plugin.cfg.uid +++ /dev/null @@ -1 +0,0 @@ -uid://hrny2utekhei diff --git a/addons/dialogue_manager/plugin.gd b/addons/dialogue_manager/plugin.gd deleted file mode 100644 index bb3bd15..0000000 --- a/addons/dialogue_manager/plugin.gd +++ /dev/null @@ -1,384 +0,0 @@ -@tool -extends EditorPlugin - - -const MainView = preload("./views/main_view.tscn") - - -var import_plugin: DMImportPlugin -var inspector_plugin: DMInspectorPlugin -var translation_parser_plugin: DMTranslationParserPlugin -var main_view -var dialogue_cache: DMCache - - -func _enter_tree() -> void: - add_autoload_singleton("DialogueManager", get_plugin_path() + "/dialogue_manager.gd") - - if Engine.is_editor_hint(): - Engine.set_meta("DialogueManagerPlugin", self) - - DMSettings.prepare() - - dialogue_cache = DMCache.new() - Engine.set_meta("DMCache", dialogue_cache) - - import_plugin = DMImportPlugin.new() - add_import_plugin(import_plugin) - - inspector_plugin = DMInspectorPlugin.new() - add_inspector_plugin(inspector_plugin) - - translation_parser_plugin = DMTranslationParserPlugin.new() - add_translation_parser_plugin(translation_parser_plugin) - - main_view = MainView.instantiate() - EditorInterface.get_editor_main_screen().add_child(main_view) - _make_visible(false) - main_view.add_child(dialogue_cache) - - _update_localization() - - EditorInterface.get_file_system_dock().files_moved.connect(_on_files_moved) - EditorInterface.get_file_system_dock().file_removed.connect(_on_file_removed) - - add_tool_menu_item("Create copy of dialogue example balloon...", _copy_dialogue_balloon) - - # Automatically make any changes to the known custom balloon if there is one. - var balloon_path: String = DMSettings.get_setting(DMSettings.BALLOON_PATH, "") - if balloon_path != "" and FileAccess.file_exists(balloon_path): - var is_small_window: bool = ProjectSettings.get_setting("display/window/size/viewport_width") < 400 - var example_balloon_file_name: String = "small_example_balloon.tscn" if is_small_window else "example_balloon.tscn" - var example_balloon_path: String = get_plugin_path() + "/example_balloon/" + example_balloon_file_name - - var contents: String = FileAccess.get_file_as_string(balloon_path) - var has_changed: bool = false - - # Make sure the current balloon has a UID unique from the example balloon's - var example_balloon_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(example_balloon_path)) - var balloon_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(balloon_path)) - if example_balloon_uid == balloon_uid: - var new_balloon_uid: String = ResourceUID.id_to_text(ResourceUID.create_id()) - contents = contents.replace(example_balloon_uid, new_balloon_uid) - has_changed = true - - # Make sure the example balloon copy has the correct renaming of the responses menu - if "reponses" in contents: - contents = contents.replace("reponses", "responses") - has_changed = true - - # Save any changes - if has_changed: - var balloon_file: FileAccess = FileAccess.open(balloon_path, FileAccess.WRITE) - balloon_file.store_string(contents) - balloon_file.close() - - -func _exit_tree() -> void: - remove_autoload_singleton("DialogueManager") - - remove_import_plugin(import_plugin) - import_plugin = null - - remove_inspector_plugin(inspector_plugin) - inspector_plugin = null - - remove_translation_parser_plugin(translation_parser_plugin) - translation_parser_plugin = null - - if is_instance_valid(main_view): - main_view.queue_free() - - Engine.remove_meta("DialogueManagerPlugin") - Engine.remove_meta("DMCache") - - EditorInterface.get_file_system_dock().files_moved.disconnect(_on_files_moved) - EditorInterface.get_file_system_dock().file_removed.disconnect(_on_file_removed) - - remove_tool_menu_item("Create copy of dialogue example balloon...") - - -func _has_main_screen() -> bool: - return true - - -func _make_visible(next_visible: bool) -> void: - if is_instance_valid(main_view): - main_view.visible = next_visible - - -func _get_plugin_name() -> String: - return "Dialogue" - - -func _get_plugin_icon() -> Texture2D: - return load(get_plugin_path() + "/assets/icon.svg") - - -func _handles(object) -> bool: - var editor_settings: EditorSettings = EditorInterface.get_editor_settings() - var external_editor: String = editor_settings.get_setting("text_editor/external/exec_path") - var use_external_editor: bool = editor_settings.get_setting("text_editor/external/use_external_editor") and external_editor != "" - if object is DialogueResource and use_external_editor and DMSettings.get_user_value("open_in_external_editor", false): - var project_path: String = ProjectSettings.globalize_path("res://") - var file_path: String = ProjectSettings.globalize_path(object.resource_path) - OS.create_process(external_editor, [project_path, file_path]) - return false - - return object is DialogueResource - - -func _edit(object) -> void: - if is_instance_valid(main_view) and is_instance_valid(object): - main_view.open_resource(object) - - -func _apply_changes() -> void: - if is_instance_valid(main_view): - main_view.apply_changes() - _update_localization() - - -func _build() -> bool: - # If this is the dotnet Godot then we need to check if the solution file exists - DMSettings.check_for_dotnet_solution() - - # Ignore errors in other files if we are just running the test scene - if DMSettings.get_user_value("is_running_test_scene", true): return true - - if dialogue_cache != null: - dialogue_cache.reimport_files() - - var files_with_errors = dialogue_cache.get_files_with_errors() - if files_with_errors.size() > 0: - for dialogue_file in files_with_errors: - push_error("You have %d error(s) in %s" % [dialogue_file.errors.size(), dialogue_file.path]) - EditorInterface.edit_resource(load(files_with_errors[0].path)) - main_view.show_build_error_dialog() - return false - - return true - - -## Get the shortcuts used by the plugin -func get_editor_shortcuts() -> Dictionary: - var shortcuts: Dictionary = { - toggle_comment = [ - _create_event("Ctrl+K"), - _create_event("Ctrl+Slash") - ], - delete_line = [ - _create_event("Ctrl+Shift+K") - ], - move_up = [ - _create_event("Alt+Up") - ], - move_down = [ - _create_event("Alt+Down") - ], - save = [ - _create_event("Ctrl+Alt+S") - ], - close_file = [ - _create_event("Ctrl+W") - ], - find_in_files = [ - _create_event("Ctrl+Shift+F") - ], - - run_test_scene = [ - _create_event("Ctrl+F5") - ], - text_size_increase = [ - _create_event("Ctrl+Equal") - ], - text_size_decrease = [ - _create_event("Ctrl+Minus") - ], - text_size_reset = [ - _create_event("Ctrl+0") - ] - } - - var paths = EditorInterface.get_editor_paths() - var settings - if FileAccess.file_exists(paths.get_config_dir() + "/editor_settings-4.3.tres"): - settings = load(paths.get_config_dir() + "/editor_settings-4.3.tres") - elif FileAccess.file_exists(paths.get_config_dir() + "/editor_settings-4.tres"): - settings = load(paths.get_config_dir() + "/editor_settings-4.tres") - else: - return shortcuts - - for s in settings.get("shortcuts"): - for key in shortcuts: - if s.name == "script_text_editor/%s" % key or s.name == "script_editor/%s" % key: - shortcuts[key] = [] - for event in s.shortcuts: - if event is InputEventKey: - shortcuts[key].append(event) - - return shortcuts - - -func _create_event(string: String) -> InputEventKey: - var event: InputEventKey = InputEventKey.new() - var bits = string.split("+") - event.keycode = OS.find_keycode_from_string(bits[bits.size() - 1]) - event.shift_pressed = bits.has("Shift") - event.alt_pressed = bits.has("Alt") - if bits.has("Ctrl") or bits.has("Command"): - event.command_or_control_autoremap = true - return event - - -## Get the editor shortcut that matches an event -func get_editor_shortcut(event: InputEventKey) -> String: - var shortcuts: Dictionary = get_editor_shortcuts() - for key in shortcuts: - for shortcut in shortcuts.get(key, []): - if event.as_text().split(" ")[0] == shortcut.as_text().split(" ")[0]: - return key - return "" - - -## Get the current version -func get_version() -> String: - var config: ConfigFile = ConfigFile.new() - config.load(get_plugin_path() + "/plugin.cfg") - return config.get_value("plugin", "version") - - -## Get the current path of the plugin -func get_plugin_path() -> String: - return get_script().resource_path.get_base_dir() - - -## Update references to a moved file -func update_import_paths(from_path: String, to_path: String) -> void: - dialogue_cache.move_file_path(from_path, to_path) - - # Reopen the file if it's already open - if main_view.current_file_path == from_path: - if to_path == "": - main_view.close_file(from_path) - else: - main_view.current_file_path = "" - main_view.open_file(to_path) - - # Update any other files that import the moved file - var dependents = dialogue_cache.get_files_with_dependency(from_path) - for dependent in dependents: - dependent.dependencies.remove_at(dependent.dependencies.find(from_path)) - dependent.dependencies.append(to_path) - - # Update the live buffer - if main_view.current_file_path == dependent.path: - main_view.code_edit.text = main_view.code_edit.text.replace(from_path, to_path) - main_view.open_buffers[main_view.current_file_path].pristine_text = main_view.code_edit.text - - # Open the file and update the path - var file: FileAccess = FileAccess.open(dependent.path, FileAccess.READ) - var text = file.get_as_text().replace(from_path, to_path) - file.close() - - file = FileAccess.open(dependent.path, FileAccess.WRITE) - file.store_string(text) - file.close() - - -func _update_localization() -> void: - var dialogue_files = dialogue_cache.get_files() - - # Add any new files to POT generation - var files_for_pot: PackedStringArray = ProjectSettings.get_setting("internationalization/locale/translations_pot_files", []) - var files_for_pot_changed: bool = false - for path in dialogue_files: - if not files_for_pot.has(path): - files_for_pot.append(path) - files_for_pot_changed = true - - # Remove any POT references that don't exist any more - for i in range(files_for_pot.size() - 1, -1, -1): - var file_for_pot: String = files_for_pot[i] - if file_for_pot.get_extension() == "dialogue" and not dialogue_files.has(file_for_pot): - files_for_pot.remove_at(i) - files_for_pot_changed = true - - # Update project settings if POT changed - if files_for_pot_changed: - ProjectSettings.set_setting("internationalization/locale/translations_pot_files", files_for_pot) - ProjectSettings.save() - - -### Callbacks - - -func _copy_dialogue_balloon() -> void: - var scale: float = EditorInterface.get_editor_scale() - var directory_dialog: FileDialog = FileDialog.new() - var label: Label = Label.new() - label.text = "Dialogue balloon files will be copied into chosen directory." - directory_dialog.get_vbox().add_child(label) - directory_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR - directory_dialog.min_size = Vector2(600, 500) * scale - directory_dialog.dir_selected.connect(func(path): - var plugin_path: String = get_plugin_path() - - var is_dotnet: bool = DMSettings.check_for_dotnet_solution() - var balloon_path: String = path + ("/Balloon.tscn" if is_dotnet else "/balloon.tscn") - var balloon_script_path: String = path + ("/DialogueBalloon.cs" if is_dotnet else "/balloon.gd") - - # Copy the balloon scene file and change the script reference - var is_small_window: bool = ProjectSettings.get_setting("display/window/size/viewport_width") < 400 - var example_balloon_file_name: String = "small_example_balloon.tscn" if is_small_window else "example_balloon.tscn" - var example_balloon_path: String = plugin_path + "/example_balloon/" + example_balloon_file_name - var example_balloon_script_file_name: String = "ExampleBalloon.cs" if is_dotnet else "example_balloon.gd" - var example_balloon_script_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(plugin_path + "/example_balloon/example_balloon.gd")) - var example_balloon_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(example_balloon_path)) - - # Copy the script file - var file: FileAccess = FileAccess.open(plugin_path + "/example_balloon/" + example_balloon_script_file_name, FileAccess.READ) - var file_contents: String = file.get_as_text() - if is_dotnet: - file_contents = file_contents.replace("class ExampleBalloon", "class DialogueBalloon") - else: - file_contents = file_contents.replace("class_name DialogueManagerExampleBalloon ", "") - file = FileAccess.open(balloon_script_path, FileAccess.WRITE) - file.store_string(file_contents) - file.close() - var new_balloon_script_uid_raw: int = ResourceUID.create_id() - ResourceUID.add_id(new_balloon_script_uid_raw, balloon_script_path) - var new_balloon_script_uid: String = ResourceUID.id_to_text(new_balloon_script_uid_raw) - - # Save the new balloon - file_contents = FileAccess.get_file_as_string(example_balloon_path).replace(plugin_path + "/example_balloon/example_balloon.gd", balloon_script_path) - var new_balloon_uid: String = ResourceUID.id_to_text(ResourceUID.create_id()) - file_contents = file_contents.replace(example_balloon_uid, new_balloon_uid).replace(example_balloon_script_uid, new_balloon_script_uid) - file = FileAccess.open(balloon_path, FileAccess.WRITE) - file.store_string(file_contents) - file.close() - - EditorInterface.get_resource_filesystem().scan() - EditorInterface.get_file_system_dock().call_deferred("navigate_to_path", balloon_path) - - DMSettings.set_setting(DMSettings.BALLOON_PATH, balloon_path) - - directory_dialog.queue_free() - ) - EditorInterface.get_base_control().add_child(directory_dialog) - directory_dialog.popup_centered() - - -### Signals - - -func _on_files_moved(old_file: String, new_file: String) -> void: - update_import_paths(old_file, new_file) - DMSettings.move_recent_file(old_file, new_file) - - -func _on_file_removed(file: String) -> void: - update_import_paths(file, "") - if is_instance_valid(main_view): - main_view.close_file(file) - _update_localization() diff --git a/addons/dialogue_manager/plugin.gd.uid b/addons/dialogue_manager/plugin.gd.uid deleted file mode 100644 index 40573b0..0000000 --- a/addons/dialogue_manager/plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bpv426rpvrafa diff --git a/addons/dialogue_manager/settings.gd b/addons/dialogue_manager/settings.gd deleted file mode 100644 index 0a0c12f..0000000 --- a/addons/dialogue_manager/settings.gd +++ /dev/null @@ -1,299 +0,0 @@ -@tool -class_name DMSettings extends Node - - -#region Editor - - - -## Wrap lines in the dialogue editor. -const WRAP_LONG_LINES = "editor/wrap_long_lines" -## The template to start new dialogue files with. -const NEW_FILE_TEMPLATE = "editor/new_file_template" - -## Show lines without statis IDs as errors. -const MISSING_TRANSLATIONS_ARE_ERRORS = "editor/translations/missing_translations_are_errors" -## Include character names in the list of translatable strings. -const INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST = "editor/translations/include_characters_in_translatable_strings_list" -## The default locale to use when exporting CSVs -const DEFAULT_CSV_LOCALE = "editor/translations/default_csv_locale" -## Any extra CSV locales to append to the exported translation CSV -const EXTRA_CSV_LOCALES = "editor/translations/extra_csv_locales" -## Includes a "_character" column in CSV exports. -const INCLUDE_CHARACTER_IN_TRANSLATION_EXPORTS = "editor/translations/include_character_in_translation_exports" -## Includes a "_notes" column in CSV exports -const INCLUDE_NOTES_IN_TRANSLATION_EXPORTS = "editor/translations/include_notes_in_translation_exports" - -## A custom test scene to use when testing dialogue. -const CUSTOM_TEST_SCENE_PATH = "editor/advanced/custom_test_scene_path" - -## The custom balloon for this game. -const BALLOON_PATH = "runtime/balloon_path" -## The names of any autoloads to shortcut into all dialogue files (so you don't have to write `using SomeGlobal` in each file). -const STATE_AUTOLOAD_SHORTCUTS = "runtime/state_autoload_shortcuts" -## Check for possible naming conflicts in state shortcuts. -const WARN_ABOUT_METHOD_PROPERTY_OR_SIGNAL_NAME_CONFLICTS = "runtime/warn_about_method_property_or_signal_name_conflicts" - -## Bypass any missing state when running dialogue. -const IGNORE_MISSING_STATE_VALUES = "runtime/advanced/ignore_missing_state_values" -## Whether or not the project is utilising dotnet. -const USES_DOTNET = "runtime/advanced/uses_dotnet" - - -const SETTINGS_CONFIGURATION = { - WRAP_LONG_LINES: { - value = false, - type = TYPE_BOOL, - }, - NEW_FILE_TEMPLATE: { - value = "~ start\nNathan: [[Hi|Hello|Howdy]], this is some dialogue.\nNathan: Here are some choices.\n- First one\n\tNathan: You picked the first one.\n- Second one\n\tNathan: You picked the second one.\n- Start again => start\n- End the conversation => END\nNathan: For more information see the online documentation.\n=> END", - type = TYPE_STRING, - hint = PROPERTY_HINT_MULTILINE_TEXT, - }, - - MISSING_TRANSLATIONS_ARE_ERRORS: { - value = false, - type = TYPE_BOOL, - is_advanced = true - }, - INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST: { - value = true, - type = TYPE_BOOL, - }, - DEFAULT_CSV_LOCALE: { - value = "en", - type = TYPE_STRING, - hint = PROPERTY_HINT_LOCALE_ID, - }, - EXTRA_CSV_LOCALES: { - value = [], - type = TYPE_PACKED_STRING_ARRAY, - is_advanced = true - }, - INCLUDE_CHARACTER_IN_TRANSLATION_EXPORTS: { - value = false, - type = TYPE_BOOL, - is_advanced = true - }, - INCLUDE_NOTES_IN_TRANSLATION_EXPORTS: { - value = false, - type = TYPE_BOOL, - is_advanced = true - }, - - CUSTOM_TEST_SCENE_PATH: { - value = preload("./test_scene.tscn").resource_path, - type = TYPE_STRING, - hint = PROPERTY_HINT_FILE, - is_advanced = true - }, - - BALLOON_PATH: { - value = "", - type = TYPE_STRING, - hint = PROPERTY_HINT_FILE, - }, - STATE_AUTOLOAD_SHORTCUTS: { - value = [], - type = TYPE_PACKED_STRING_ARRAY, - }, - WARN_ABOUT_METHOD_PROPERTY_OR_SIGNAL_NAME_CONFLICTS: { - value = false, - type = TYPE_BOOL, - is_advanced = true - }, - - IGNORE_MISSING_STATE_VALUES: { - value = false, - type = TYPE_BOOL, - is_advanced = true - }, - USES_DOTNET: { - value = false, - type = TYPE_BOOL, - is_hidden = true - } -} - - -static func prepare() -> void: - var should_save_settings: bool = false - - # Remap any old settings into their new keys - var legacy_map: Dictionary = { - states = STATE_AUTOLOAD_SHORTCUTS, - missing_translations_are_errors = MISSING_TRANSLATIONS_ARE_ERRORS, - export_characters_in_translation = INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST, - wrap_lines = WRAP_LONG_LINES, - new_with_template = null, - new_template = NEW_FILE_TEMPLATE, - include_all_responses = null, - ignore_missing_state_values = IGNORE_MISSING_STATE_VALUES, - custom_test_scene_path = CUSTOM_TEST_SCENE_PATH, - default_csv_locale = DEFAULT_CSV_LOCALE, - balloon_path = BALLOON_PATH, - create_lines_for_responses_with_characters = null, - include_character_in_translation_exports = INCLUDE_CHARACTER_IN_TRANSLATION_EXPORTS, - include_notes_in_translation_exports = INCLUDE_NOTES_IN_TRANSLATION_EXPORTS, - uses_dotnet = USES_DOTNET, - try_suppressing_startup_unsaved_indicator = null - } - - for legacy_key: String in legacy_map: - if ProjectSettings.has_setting("dialogue_manager/general/%s" % legacy_key): - should_save_settings = true - # Remove the old setting - var value = ProjectSettings.get_setting("dialogue_manager/general/%s" % legacy_key) - ProjectSettings.set_setting("dialogue_manager/general/%s" % legacy_key, null) - if legacy_map.get(legacy_key) != null: - prints("Migrating Dialogue Manager setting %s to %s with value %s" % [legacy_key, legacy_map.get(legacy_key), str(value)]) - ProjectSettings.set_setting("dialogue_manager/%s" % [legacy_map.get(legacy_key)], value) - - # Set up initial settings - for key: String in SETTINGS_CONFIGURATION: - var setting_config: Dictionary = SETTINGS_CONFIGURATION[key] - var setting_name: String = "dialogue_manager/%s" % key - if not ProjectSettings.has_setting(setting_name): - ProjectSettings.set_setting(setting_name, setting_config.value) - ProjectSettings.set_initial_value(setting_name, setting_config.value) - ProjectSettings.add_property_info({ - "name" = setting_name, - "type" = setting_config.type, - "hint" = setting_config.get("hint", PROPERTY_HINT_NONE), - "hint_string" = setting_config.get("hint_string", "") - }) - ProjectSettings.set_as_basic(setting_name, not setting_config.has("is_advanced")) - ProjectSettings.set_as_internal(setting_name, setting_config.has("is_hidden")) - - if should_save_settings: - ProjectSettings.save() - - -static func set_setting(key: String, value) -> void: - if get_setting(key, value) != value: - ProjectSettings.set_setting("dialogue_manager/%s" % key, value) - ProjectSettings.set_initial_value("dialogue_manager/%s" % key, SETTINGS_CONFIGURATION[key].value) - ProjectSettings.save() - - -static func get_setting(key: String, default): - if ProjectSettings.has_setting("dialogue_manager/%s" % key): - return ProjectSettings.get_setting("dialogue_manager/%s" % key) - else: - return default - - -static func get_settings(only_keys: PackedStringArray = []) -> Dictionary: - var settings: Dictionary = {} - for key in SETTINGS_CONFIGURATION.keys(): - if only_keys.is_empty() or key in only_keys: - settings[key] = get_setting(key, SETTINGS_CONFIGURATION[key].value) - return settings - - -#endregion - -#region User - - -static func get_user_config() -> Dictionary: - var user_config: Dictionary = { - check_for_updates = true, - just_refreshed = null, - recent_files = [], - reopen_files = [], - most_recent_reopen_file = "", - carets = {}, - run_title = "", - run_resource_path = "", - is_running_test_scene = false, - has_dotnet_solution = false, - open_in_external_editor = false - } - - if FileAccess.file_exists(DMConstants.USER_CONFIG_PATH): - var file: FileAccess = FileAccess.open(DMConstants.USER_CONFIG_PATH, FileAccess.READ) - user_config.merge(JSON.parse_string(file.get_as_text()), true) - - return user_config - - -static func save_user_config(user_config: Dictionary) -> void: - var file: FileAccess = FileAccess.open(DMConstants.USER_CONFIG_PATH, FileAccess.WRITE) - file.store_string(JSON.stringify(user_config)) - - -static func set_user_value(key: String, value) -> void: - var user_config: Dictionary = get_user_config() - user_config[key] = value - save_user_config(user_config) - - -static func get_user_value(key: String, default = null): - return get_user_config().get(key, default) - - -static func add_recent_file(path: String) -> void: - var recent_files: Array = get_user_value("recent_files", []) - if path in recent_files: - recent_files.erase(path) - recent_files.insert(0, path) - set_user_value("recent_files", recent_files) - - -static func move_recent_file(from_path: String, to_path: String) -> void: - var recent_files: Array = get_user_value("recent_files", []) - for i in range(0, recent_files.size()): - if recent_files[i] == from_path: - recent_files[i] = to_path - set_user_value("recent_files", recent_files) - - -static func remove_recent_file(path: String) -> void: - var recent_files: Array = get_user_value("recent_files", []) - if path in recent_files: - recent_files.erase(path) - set_user_value("recent_files", recent_files) - - -static func get_recent_files() -> Array: - return get_user_value("recent_files", []) - - -static func clear_recent_files() -> void: - set_user_value("recent_files", []) - set_user_value("carets", {}) - - -static func set_caret(path: String, cursor: Vector2) -> void: - var carets: Dictionary = get_user_value("carets", {}) - carets[path] = { - x = cursor.x, - y = cursor.y - } - set_user_value("carets", carets) - - -static func get_caret(path: String) -> Vector2: - var carets = get_user_value("carets", {}) - if carets.has(path): - var caret = carets.get(path) - return Vector2(caret.x, caret.y) - else: - return Vector2.ZERO - - -static func check_for_dotnet_solution() -> bool: - if Engine.is_editor_hint(): - var has_dotnet_solution: bool = false - if ProjectSettings.has_setting("dotnet/project/solution_directory"): - var directory: String = ProjectSettings.get("dotnet/project/solution_directory") - var file_name: String = ProjectSettings.get("dotnet/project/assembly_name") - has_dotnet_solution = FileAccess.file_exists("res://%s/%s.sln" % [directory, file_name]) - set_setting(DMSettings.USES_DOTNET, has_dotnet_solution) - return has_dotnet_solution - - return get_setting(DMSettings.USES_DOTNET, false) - - -#endregion diff --git a/addons/dialogue_manager/settings.gd.uid b/addons/dialogue_manager/settings.gd.uid deleted file mode 100644 index c93da98..0000000 --- a/addons/dialogue_manager/settings.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ce1nk88365m52 diff --git a/addons/dialogue_manager/test_scene.gd b/addons/dialogue_manager/test_scene.gd deleted file mode 100644 index 20fe115..0000000 --- a/addons/dialogue_manager/test_scene.gd +++ /dev/null @@ -1,43 +0,0 @@ -class_name BaseDialogueTestScene extends Node2D - - -const DialogueSettings = preload("./settings.gd") -const DialogueResource = preload("./dialogue_resource.gd") - - -@onready var title: String = DialogueSettings.get_user_value("run_title") -@onready var resource: DialogueResource = load(DialogueSettings.get_user_value("run_resource_path")) - - -func _ready(): - # Is this running in Godot >=4.4? - if Engine.has_method("is_embedded_in_editor"): - if not Engine.call("is_embedded_in_editor"): - var window: Window = get_viewport() - var screen_index: int = DisplayServer.get_primary_screen() - window.position = Vector2(DisplayServer.screen_get_position(screen_index)) + (DisplayServer.screen_get_size(screen_index) - window.size) * 0.5 - window.mode = Window.MODE_WINDOWED - else: - var screen_index: int = DisplayServer.get_primary_screen() - DisplayServer.window_set_position(Vector2(DisplayServer.screen_get_position(screen_index)) + (DisplayServer.screen_get_size(screen_index) - DisplayServer.window_get_size()) * 0.5) - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) - - # Normally you can just call DialogueManager directly but doing so before the plugin has been - # enabled in settings will throw a compiler error here so I'm using `get_singleton` instead. - var dialogue_manager = Engine.get_singleton("DialogueManager") - dialogue_manager.dialogue_ended.connect(_on_dialogue_ended) - dialogue_manager.show_dialogue_balloon(resource, title if not title.is_empty() else resource.first_title) - - -func _enter_tree() -> void: - DialogueSettings.set_user_value("is_running_test_scene", false) - - -#region Signals - - -func _on_dialogue_ended(_resource: DialogueResource): - get_tree().quit() - - -#endregion diff --git a/addons/dialogue_manager/test_scene.gd.uid b/addons/dialogue_manager/test_scene.gd.uid deleted file mode 100644 index 1bee7a1..0000000 --- a/addons/dialogue_manager/test_scene.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c8e16qdgu40wo diff --git a/addons/dialogue_manager/test_scene.tscn b/addons/dialogue_manager/test_scene.tscn deleted file mode 100644 index f0786ba..0000000 --- a/addons/dialogue_manager/test_scene.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://ugd552efvil0"] - -[ext_resource type="Script" uid="uid://c8e16qdgu40wo" path="res://addons/dialogue_manager/test_scene.gd" id="1_yupoh"] - -[node name="TestScene" type="Node2D"] -script = ExtResource("1_yupoh") diff --git a/addons/dialogue_manager/utilities/builtins.gd b/addons/dialogue_manager/utilities/builtins.gd deleted file mode 100644 index 1f8f8ba..0000000 --- a/addons/dialogue_manager/utilities/builtins.gd +++ /dev/null @@ -1,505 +0,0 @@ -extends Object - - -const DialogueConstants = preload("../constants.gd") - -const SUPPORTED_BUILTIN_TYPES = [ - TYPE_STRING, - TYPE_STRING_NAME, - TYPE_ARRAY, - TYPE_PACKED_STRING_ARRAY, - TYPE_VECTOR2, - TYPE_VECTOR3, - TYPE_VECTOR4, - TYPE_DICTIONARY, - TYPE_QUATERNION, - TYPE_COLOR, - TYPE_SIGNAL, - TYPE_CALLABLE -] - - -static var resolve_method_error: Error = OK - - -static func is_supported(thing, with_method: String = "") -> bool: - if not typeof(thing) in SUPPORTED_BUILTIN_TYPES: return false - - # If given a Dictionary and a method then make sure it's a known Dictionary method. - if typeof(thing) == TYPE_DICTIONARY and with_method != "": - return with_method in [ - &"clear", - &"duplicate", - &"erase", - &"find_key", - &"get", - &"get_or_add", - &"has", - &"has_all", - &"hash", - &"is_empty", - &"is_read_only", - &"keys", - &"make_read_only", - &"merge", - &"merged", - &"recursive_equal", - &"size", - &"values"] - - return true - - -static func resolve_property(builtin, property: String): - match typeof(builtin): - TYPE_ARRAY, TYPE_PACKED_STRING_ARRAY, TYPE_DICTIONARY, TYPE_QUATERNION, TYPE_STRING, TYPE_STRING_NAME: - return builtin[property] - - # Some types have constants that we need to manually resolve - - TYPE_VECTOR2: - return resolve_vector2_property(builtin, property) - TYPE_VECTOR3: - return resolve_vector3_property(builtin, property) - TYPE_VECTOR4: - return resolve_vector4_property(builtin, property) - TYPE_COLOR: - return resolve_color_property(builtin, property) - - -static func resolve_method(thing, method_name: String, args: Array): - resolve_method_error = OK - - # Resolve static methods manually - match typeof(thing): - TYPE_VECTOR2: - match method_name: - "from_angle": - return Vector2.from_angle(args[0]) - - TYPE_COLOR: - match method_name: - "from_hsv": - return Color.from_hsv(args[0], args[1], args[2]) if args.size() == 3 else Color.from_hsv(args[0], args[1], args[2], args[3]) - "from_ok_hsl": - return Color.from_ok_hsl(args[0], args[1], args[2]) if args.size() == 3 else Color.from_ok_hsl(args[0], args[1], args[2], args[3]) - "from_rgbe9995": - return Color.from_rgbe9995(args[0]) - "from_string": - return Color.from_string(args[0], args[1]) - - TYPE_QUATERNION: - match method_name: - "from_euler": - return Quaternion.from_euler(args[0]) - - # Anything else can be evaulatated automatically - var references: Array = ["thing"] - for i in range(0, args.size()): - references.append("arg%d" % i) - var expression = Expression.new() - if expression.parse("thing.%s(%s)" % [method_name, ",".join(references.slice(1))], references) != OK: - assert(false, expression.get_error_text()) - var result = expression.execute([thing] + args, null, false) - if expression.has_execute_failed(): - resolve_method_error = ERR_CANT_RESOLVE - return null - - return result - - -static func has_resolve_method_failed() -> bool: - return resolve_method_error != OK - - -static func resolve_color_property(color: Color, property: String): - match property: - "ALICE_BLUE": - return Color.ALICE_BLUE - "ANTIQUE_WHITE": - return Color.ANTIQUE_WHITE - "AQUA": - return Color.AQUA - "AQUAMARINE": - return Color.AQUAMARINE - "AZURE": - return Color.AZURE - "BEIGE": - return Color.BEIGE - "BISQUE": - return Color.BISQUE - "BLACK": - return Color.BLACK - "BLANCHED_ALMOND": - return Color.BLANCHED_ALMOND - "BLUE": - return Color.BLUE - "BLUE_VIOLET": - return Color.BLUE_VIOLET - "BROWN": - return Color.BROWN - "BURLYWOOD": - return Color.BURLYWOOD - "CADET_BLUE": - return Color.CADET_BLUE - "CHARTREUSE": - return Color.CHARTREUSE - "CHOCOLATE": - return Color.CHOCOLATE - "CORAL": - return Color.CORAL - "CORNFLOWER_BLUE": - return Color.CORNFLOWER_BLUE - "CORNSILK": - return Color.CORNSILK - "CRIMSON": - return Color.CRIMSON - "CYAN": - return Color.CYAN - "DARK_BLUE": - return Color.DARK_BLUE - "DARK_CYAN": - return Color.DARK_CYAN - "DARK_GOLDENROD": - return Color.DARK_GOLDENROD - "DARK_GRAY": - return Color.DARK_GRAY - "DARK_GREEN": - return Color.DARK_GREEN - "DARK_KHAKI": - return Color.DARK_KHAKI - "DARK_MAGENTA": - return Color.DARK_MAGENTA - "DARK_OLIVE_GREEN": - return Color.DARK_OLIVE_GREEN - "DARK_ORANGE": - return Color.DARK_ORANGE - "DARK_ORCHID": - return Color.DARK_ORCHID - "DARK_RED": - return Color.DARK_RED - "DARK_SALMON": - return Color.DARK_SALMON - "DARK_SEA_GREEN": - return Color.DARK_SEA_GREEN - "DARK_SLATE_BLUE": - return Color.DARK_SLATE_BLUE - "DARK_SLATE_GRAY": - return Color.DARK_SLATE_GRAY - "DARK_TURQUOISE": - return Color.DARK_TURQUOISE - "DARK_VIOLET": - return Color.DARK_VIOLET - "DEEP_PINK": - return Color.DEEP_PINK - "DEEP_SKY_BLUE": - return Color.DEEP_SKY_BLUE - "DIM_GRAY": - return Color.DIM_GRAY - "DODGER_BLUE": - return Color.DODGER_BLUE - "FIREBRICK": - return Color.FIREBRICK - "FLORAL_WHITE": - return Color.FLORAL_WHITE - "FOREST_GREEN": - return Color.FOREST_GREEN - "FUCHSIA": - return Color.FUCHSIA - "GAINSBORO": - return Color.GAINSBORO - "GHOST_WHITE": - return Color.GHOST_WHITE - "GOLD": - return Color.GOLD - "GOLDENROD": - return Color.GOLDENROD - "GRAY": - return Color.GRAY - "GREEN": - return Color.GREEN - "GREEN_YELLOW": - return Color.GREEN_YELLOW - "HONEYDEW": - return Color.HONEYDEW - "HOT_PINK": - return Color.HOT_PINK - "INDIAN_RED": - return Color.INDIAN_RED - "INDIGO": - return Color.INDIGO - "IVORY": - return Color.IVORY - "KHAKI": - return Color.KHAKI - "LAVENDER": - return Color.LAVENDER - "LAVENDER_BLUSH": - return Color.LAVENDER_BLUSH - "LAWN_GREEN": - return Color.LAWN_GREEN - "LEMON_CHIFFON": - return Color.LEMON_CHIFFON - "LIGHT_BLUE": - return Color.LIGHT_BLUE - "LIGHT_CORAL": - return Color.LIGHT_CORAL - "LIGHT_CYAN": - return Color.LIGHT_CYAN - "LIGHT_GOLDENROD": - return Color.LIGHT_GOLDENROD - "LIGHT_GRAY": - return Color.LIGHT_GRAY - "LIGHT_GREEN": - return Color.LIGHT_GREEN - "LIGHT_PINK": - return Color.LIGHT_PINK - "LIGHT_SALMON": - return Color.LIGHT_SALMON - "LIGHT_SEA_GREEN": - return Color.LIGHT_SEA_GREEN - "LIGHT_SKY_BLUE": - return Color.LIGHT_SKY_BLUE - "LIGHT_SLATE_GRAY": - return Color.LIGHT_SLATE_GRAY - "LIGHT_STEEL_BLUE": - return Color.LIGHT_STEEL_BLUE - "LIGHT_YELLOW": - return Color.LIGHT_YELLOW - "LIME": - return Color.LIME - "LIME_GREEN": - return Color.LIME_GREEN - "LINEN": - return Color.LINEN - "MAGENTA": - return Color.MAGENTA - "MAROON": - return Color.MAROON - "MEDIUM_AQUAMARINE": - return Color.MEDIUM_AQUAMARINE - "MEDIUM_BLUE": - return Color.MEDIUM_BLUE - "MEDIUM_ORCHID": - return Color.MEDIUM_ORCHID - "MEDIUM_PURPLE": - return Color.MEDIUM_PURPLE - "MEDIUM_SEA_GREEN": - return Color.MEDIUM_SEA_GREEN - "MEDIUM_SLATE_BLUE": - return Color.MEDIUM_SLATE_BLUE - "MEDIUM_SPRING_GREEN": - return Color.MEDIUM_SPRING_GREEN - "MEDIUM_TURQUOISE": - return Color.MEDIUM_TURQUOISE - "MEDIUM_VIOLET_RED": - return Color.MEDIUM_VIOLET_RED - "MIDNIGHT_BLUE": - return Color.MIDNIGHT_BLUE - "MINT_CREAM": - return Color.MINT_CREAM - "MISTY_ROSE": - return Color.MISTY_ROSE - "MOCCASIN": - return Color.MOCCASIN - "NAVAJO_WHITE": - return Color.NAVAJO_WHITE - "NAVY_BLUE": - return Color.NAVY_BLUE - "OLD_LACE": - return Color.OLD_LACE - "OLIVE": - return Color.OLIVE - "OLIVE_DRAB": - return Color.OLIVE_DRAB - "ORANGE": - return Color.ORANGE - "ORANGE_RED": - return Color.ORANGE_RED - "ORCHID": - return Color.ORCHID - "PALE_GOLDENROD": - return Color.PALE_GOLDENROD - "PALE_GREEN": - return Color.PALE_GREEN - "PALE_TURQUOISE": - return Color.PALE_TURQUOISE - "PALE_VIOLET_RED": - return Color.PALE_VIOLET_RED - "PAPAYA_WHIP": - return Color.PAPAYA_WHIP - "PEACH_PUFF": - return Color.PEACH_PUFF - "PERU": - return Color.PERU - "PINK": - return Color.PINK - "PLUM": - return Color.PLUM - "POWDER_BLUE": - return Color.POWDER_BLUE - "PURPLE": - return Color.PURPLE - "REBECCA_PURPLE": - return Color.REBECCA_PURPLE - "RED": - return Color.RED - "ROSY_BROWN": - return Color.ROSY_BROWN - "ROYAL_BLUE": - return Color.ROYAL_BLUE - "SADDLE_BROWN": - return Color.SADDLE_BROWN - "SALMON": - return Color.SALMON - "SANDY_BROWN": - return Color.SANDY_BROWN - "SEA_GREEN": - return Color.SEA_GREEN - "SEASHELL": - return Color.SEASHELL - "SIENNA": - return Color.SIENNA - "SILVER": - return Color.SILVER - "SKY_BLUE": - return Color.SKY_BLUE - "SLATE_BLUE": - return Color.SLATE_BLUE - "SLATE_GRAY": - return Color.SLATE_GRAY - "SNOW": - return Color.SNOW - "SPRING_GREEN": - return Color.SPRING_GREEN - "STEEL_BLUE": - return Color.STEEL_BLUE - "TAN": - return Color.TAN - "TEAL": - return Color.TEAL - "THISTLE": - return Color.THISTLE - "TOMATO": - return Color.TOMATO - "TRANSPARENT": - return Color.TRANSPARENT - "TURQUOISE": - return Color.TURQUOISE - "VIOLET": - return Color.VIOLET - "WEB_GRAY": - return Color.WEB_GRAY - "WEB_GREEN": - return Color.WEB_GREEN - "WEB_MAROON": - return Color.WEB_MAROON - "WEB_PURPLE": - return Color.WEB_PURPLE - "WHEAT": - return Color.WHEAT - "WHITE": - return Color.WHITE - "WHITE_SMOKE": - return Color.WHITE_SMOKE - "YELLOW": - return Color.YELLOW - "YELLOW_GREEN": - return Color.YELLOW_GREEN - - return color[property] - - -static func resolve_vector2_property(vector: Vector2, property: String): - match property: - "AXIS_X": - return Vector2.AXIS_X - "AXIS_Y": - return Vector2.AXIS_Y - "ZERO": - return Vector2.ZERO - "ONE": - return Vector2.ONE - "INF": - return Vector2.INF - "LEFT": - return Vector2.LEFT - "RIGHT": - return Vector2.RIGHT - "UP": - return Vector2.UP - "DOWN": - return Vector2.DOWN - - "DOWN_LEFT": - return Vector2(-1, 1) - "DOWN_RIGHT": - return Vector2(1, 1) - "UP_LEFT": - return Vector2(-1, -1) - "UP_RIGHT": - return Vector2(1, -1) - - return vector[property] - - -static func resolve_vector3_property(vector: Vector3, property: String): - match property: - "AXIS_X": - return Vector3.AXIS_X - "AXIS_Y": - return Vector3.AXIS_Y - "AXIS_Z": - return Vector3.AXIS_Z - "ZERO": - return Vector3.ZERO - "ONE": - return Vector3.ONE - "INF": - return Vector3.INF - "LEFT": - return Vector3.LEFT - "RIGHT": - return Vector3.RIGHT - "UP": - return Vector3.UP - "DOWN": - return Vector3.DOWN - "FORWARD": - return Vector3.FORWARD - "BACK": - return Vector3.BACK - "MODEL_LEFT": - return Vector3(1, 0, 0) - "MODEL_RIGHT": - return Vector3(-1, 0, 0) - "MODEL_TOP": - return Vector3(0, 1, 0) - "MODEL_BOTTOM": - return Vector3(0, -1, 0) - "MODEL_FRONT": - return Vector3(0, 0, 1) - "MODEL_REAR": - return Vector3(0, 0, -1) - - return vector[property] - - -static func resolve_vector4_property(vector: Vector4, property: String): - match property: - "AXIS_X": - return Vector4.AXIS_X - "AXIS_Y": - return Vector4.AXIS_Y - "AXIS_Z": - return Vector4.AXIS_Z - "AXIS_W": - return Vector4.AXIS_W - "ZERO": - return Vector4.ZERO - "ONE": - return Vector4.ONE - "INF": - return Vector4.INF - - return vector[property] diff --git a/addons/dialogue_manager/utilities/builtins.gd.uid b/addons/dialogue_manager/utilities/builtins.gd.uid deleted file mode 100644 index af8698c..0000000 --- a/addons/dialogue_manager/utilities/builtins.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bnfhuubdv5k20 diff --git a/addons/dialogue_manager/utilities/dialogue_cache.gd b/addons/dialogue_manager/utilities/dialogue_cache.gd deleted file mode 100644 index dd1da44..0000000 --- a/addons/dialogue_manager/utilities/dialogue_cache.gd +++ /dev/null @@ -1,170 +0,0 @@ -class_name DMCache extends Node - - -signal file_content_changed(path: String, new_content: String) - - -# Keep track of errors and dependencies -# { -# = { -# path = , -# dependencies = [, ], -# errors = [, ] -# } -# } -var _cache: Dictionary = {} - -var _update_dependency_timer: Timer = Timer.new() -var _update_dependency_paths: PackedStringArray = [] - -var _files_marked_for_reimport: PackedStringArray = [] - - -func _ready() -> void: - add_child(_update_dependency_timer) - _update_dependency_timer.timeout.connect(_on_update_dependency_timeout) - - _build_cache() - - -func mark_files_for_reimport(files: PackedStringArray) -> void: - for file in files: - if not _files_marked_for_reimport.has(file): - _files_marked_for_reimport.append(file) - - -func reimport_files(and_files: PackedStringArray = []) -> void: - for file in and_files: - if not _files_marked_for_reimport.has(file): - _files_marked_for_reimport.append(file) - - if _files_marked_for_reimport.is_empty(): return - - EditorInterface.get_resource_filesystem().reimport_files(_files_marked_for_reimport) - - -## Add a dialogue file to the cache. -func add_file(path: String, compile_result: DMCompilerResult = null) -> void: - _cache[path] = { - path = path, - dependencies = [], - errors = [] - } - - if compile_result != null: - _cache[path].dependencies = Array(compile_result.imported_paths).filter(func(d): return d != path) - _cache[path].compiled_at = Time.get_ticks_msec() - - # If this is a fresh cache entry, check for dependencies - if compile_result == null and not _update_dependency_paths.has(path): - queue_updating_dependencies(path) - - -## Get the file paths in the cache -func get_files() -> PackedStringArray: - return _cache.keys() - - -## Check if a file is known to the cache -func has_file(path: String) -> bool: - return _cache.has(path) - - -## Remember any errors in a dialogue file -func add_errors_to_file(path: String, errors: Array[Dictionary]) -> void: - if _cache.has(path): - _cache[path].errors = errors - else: - _cache[path] = { - path = path, - resource_path = "", - dependencies = [], - errors = errors - } - - -## Get a list of files that have errors -func get_files_with_errors() -> Array[Dictionary]: - var files_with_errors: Array[Dictionary] = [] - for dialogue_file in _cache.values(): - if dialogue_file and dialogue_file.errors.size() > 0: - files_with_errors.append(dialogue_file) - return files_with_errors - - -## Queue a file to have its dependencies checked -func queue_updating_dependencies(of_path: String) -> void: - _update_dependency_timer.stop() - if not _update_dependency_paths.has(of_path): - _update_dependency_paths.append(of_path) - _update_dependency_timer.start(0.5) - - -## Update any references to a file path that has moved -func move_file_path(from_path: String, to_path: String) -> void: - if not _cache.has(from_path): return - - if to_path != "": - _cache[to_path] = _cache[from_path].duplicate() - _cache.erase(from_path) - - -## Get every dialogue file that imports on a file of a given path -func get_files_with_dependency(imported_path: String) -> Array: - return _cache.values().filter(func(d): return d.dependencies.has(imported_path)) - - -## Get any paths that are dependent on a given path -func get_dependent_paths_for_reimport(on_path: String) -> PackedStringArray: - return get_files_with_dependency(on_path) \ - .filter(func(d): return Time.get_ticks_msec() - d.get("compiled_at", 0) > 3000) \ - .map(func(d): return d.path) - - -# Build the initial cache for dialogue files -func _build_cache() -> void: - var current_files: PackedStringArray = _get_dialogue_files_in_filesystem() - for file in current_files: - add_file(file) - - -# Recursively find any dialogue files in a directory -func _get_dialogue_files_in_filesystem(path: String = "res://") -> PackedStringArray: - var files: PackedStringArray = [] - - if DirAccess.dir_exists_absolute(path): - var dir = DirAccess.open(path) - dir.list_dir_begin() - var file_name = dir.get_next() - while file_name != "": - var file_path: String = (path + "/" + file_name).simplify_path() - if dir.current_is_dir(): - if not file_name in [".godot", ".tmp"]: - files.append_array(_get_dialogue_files_in_filesystem(file_path)) - elif file_name.get_extension() == "dialogue": - files.append(file_path) - file_name = dir.get_next() - - return files - - -#region Signals - - -func _on_update_dependency_timeout() -> void: - _update_dependency_timer.stop() - var import_regex: RegEx = RegEx.create_from_string("import \"(?.*?)\"") - var file: FileAccess - var found_imports: Array[RegExMatch] - for path in _update_dependency_paths: - # Open the file and check for any "import" lines - file = FileAccess.open(path, FileAccess.READ) - found_imports = import_regex.search_all(file.get_as_text()) - var dependencies: PackedStringArray = [] - for found in found_imports: - dependencies.append(found.strings[found.names.path]) - _cache[path].dependencies = dependencies - _update_dependency_paths.clear() - - -#endregion diff --git a/addons/dialogue_manager/utilities/dialogue_cache.gd.uid b/addons/dialogue_manager/utilities/dialogue_cache.gd.uid deleted file mode 100644 index e572006..0000000 --- a/addons/dialogue_manager/utilities/dialogue_cache.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d3c83yd6bjp43 diff --git a/addons/dialogue_manager/views/main_view.gd b/addons/dialogue_manager/views/main_view.gd deleted file mode 100644 index 2330081..0000000 --- a/addons/dialogue_manager/views/main_view.gd +++ /dev/null @@ -1,1140 +0,0 @@ -@tool -extends Control - - -const OPEN_OPEN = 100 -const OPEN_QUICK = 101 -const OPEN_CLEAR = 102 - -const TRANSLATIONS_GENERATE_LINE_IDS = 100 -const TRANSLATIONS_SAVE_CHARACTERS_TO_CSV = 201 -const TRANSLATIONS_SAVE_TO_CSV = 202 -const TRANSLATIONS_IMPORT_FROM_CSV = 203 - -const ITEM_SAVE = 100 -const ITEM_SAVE_AS = 101 -const ITEM_CLOSE = 102 -const ITEM_CLOSE_ALL = 103 -const ITEM_CLOSE_OTHERS = 104 -const ITEM_COPY_PATH = 200 -const ITEM_SHOW_IN_FILESYSTEM = 201 - -enum TranslationSource { - CharacterNames, - Lines -} - - -signal confirmation_closed() - - -@onready var parse_timer: Timer = $ParseTimer - -# Dialogs -@onready var new_dialog: FileDialog = $NewDialog -@onready var save_dialog: FileDialog = $SaveDialog -@onready var open_dialog: FileDialog = $OpenDialog -@onready var quick_open_dialog: ConfirmationDialog = $QuickOpenDialog -@onready var quick_open_files_list: VBoxContainer = $QuickOpenDialog/QuickOpenFilesList -@onready var export_dialog: FileDialog = $ExportDialog -@onready var import_dialog: FileDialog = $ImportDialog -@onready var errors_dialog: AcceptDialog = $ErrorsDialog -@onready var build_error_dialog: AcceptDialog = $BuildErrorDialog -@onready var close_confirmation_dialog: ConfirmationDialog = $CloseConfirmationDialog -@onready var updated_dialog: AcceptDialog = $UpdatedDialog -@onready var find_in_files_dialog: AcceptDialog = $FindInFilesDialog -@onready var find_in_files: Control = $FindInFilesDialog/FindInFiles - -# Toolbar -@onready var new_button: Button = %NewButton -@onready var open_button: MenuButton = %OpenButton -@onready var save_all_button: Button = %SaveAllButton -@onready var find_in_files_button: Button = %FindInFilesButton -@onready var test_button: Button = %TestButton -@onready var test_line_button: Button = %TestLineButton -@onready var search_button: Button = %SearchButton -@onready var insert_button: MenuButton = %InsertButton -@onready var translations_button: MenuButton = %TranslationsButton -@onready var support_button: Button = %SupportButton -@onready var docs_button: Button = %DocsButton -@onready var version_label: Label = %VersionLabel -@onready var update_button: Button = %UpdateButton - -@onready var search_and_replace := %SearchAndReplace - -# Code editor -@onready var content: HSplitContainer = %Content -@onready var files_list := %FilesList -@onready var files_popup_menu: PopupMenu = %FilesPopupMenu -@onready var title_list := %TitleList -@onready var code_edit: DMCodeEdit = %CodeEdit -@onready var errors_panel := %ErrorsPanel - -# The currently open file -var current_file_path: String = "": - set(next_current_file_path): - current_file_path = next_current_file_path - files_list.current_file_path = current_file_path - if current_file_path == "" or not open_buffers.has(current_file_path): - save_all_button.disabled = true - test_button.disabled = true - test_line_button.disabled = true - search_button.disabled = true - insert_button.disabled = true - translations_button.disabled = true - content.dragger_visibility = SplitContainer.DRAGGER_HIDDEN - files_list.hide() - title_list.hide() - code_edit.hide() - errors_panel.hide() - else: - test_button.disabled = false - test_line_button.disabled = false - search_button.disabled = false - insert_button.disabled = false - translations_button.disabled = false - content.dragger_visibility = SplitContainer.DRAGGER_VISIBLE - files_list.show() - title_list.show() - code_edit.show() - - code_edit.text = open_buffers[current_file_path].text - code_edit.errors = [] - code_edit.clear_undo_history() - code_edit.set_cursor(DMSettings.get_caret(current_file_path)) - code_edit.grab_focus() - - _on_code_edit_text_changed() - - errors_panel.errors = [] - code_edit.errors = [] - get: - return current_file_path - -# A reference to the currently open files and their last saved text -var open_buffers: Dictionary = {} - -# Which thing are we exporting translations for? -var translation_source: TranslationSource = TranslationSource.Lines - -var plugin: EditorPlugin - - -func _ready() -> void: - plugin = Engine.get_meta("DialogueManagerPlugin") - - apply_theme() - - # Start with nothing open - self.current_file_path = "" - - # Set up the update checker - version_label.text = "v%s" % plugin.get_version() - update_button.on_before_refresh = func on_before_refresh(): - # Save everything - DMSettings.set_user_value("just_refreshed", { - current_file_path = current_file_path, - open_buffers = open_buffers - }) - return true - - # Did we just load from an addon version refresh? - var just_refreshed = DMSettings.get_user_value("just_refreshed", null) - if just_refreshed != null: - DMSettings.set_user_value("just_refreshed", null) - call_deferred("load_from_version_refresh", just_refreshed) - - # Hook up the search toolbar - search_and_replace.code_edit = code_edit - - # Connect menu buttons - insert_button.get_popup().id_pressed.connect(_on_insert_button_menu_id_pressed) - translations_button.get_popup().id_pressed.connect(_on_translations_button_menu_id_pressed) - - code_edit.main_view = self - code_edit.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY if DMSettings.get_setting(DMSettings.WRAP_LONG_LINES, false) else TextEdit.LINE_WRAPPING_NONE - var editor_settings: EditorSettings = EditorInterface.get_editor_settings() - editor_settings.settings_changed.connect(_on_editor_settings_changed) - _on_editor_settings_changed() - - # Reopen any files that were open when Godot was closed - if editor_settings.get_setting("text_editor/behavior/files/restore_scripts_on_load"): - var reopen_files: Array = DMSettings.get_user_value("reopen_files", []) - for reopen_file in reopen_files: - open_file(reopen_file) - - self.current_file_path = DMSettings.get_user_value("most_recent_reopen_file", "") - - save_all_button.disabled = true - - close_confirmation_dialog.ok_button_text = DMConstants.translate(&"confirm_close.save") - close_confirmation_dialog.add_button(DMConstants.translate(&"confirm_close.discard"), true, "discard") - - errors_dialog.dialog_text = DMConstants.translate(&"errors_in_script") - - # Update the buffer if a file was modified externally (retains undo step) - Engine.get_meta("DMCache").file_content_changed.connect(_on_cache_file_content_changed) - - EditorInterface.get_file_system_dock().files_moved.connect(_on_files_moved) - - -func _exit_tree() -> void: - DMSettings.set_user_value("reopen_files", open_buffers.keys()) - DMSettings.set_user_value("most_recent_reopen_file", self.current_file_path) - - -func _unhandled_input(event: InputEvent) -> void: - if not visible: return - - if event is InputEventKey and event.is_pressed(): - var shortcut: String = plugin.get_editor_shortcut(event) - match shortcut: - "close_file": - get_viewport().set_input_as_handled() - close_file(current_file_path) - "save": - get_viewport().set_input_as_handled() - save_file(current_file_path) - "find_in_files": - get_viewport().set_input_as_handled() - _on_find_in_files_button_pressed() - "run_test_scene": - get_viewport().set_input_as_handled() - _on_test_button_pressed() - - -func apply_changes() -> void: - save_files() - - -# Load back to the previous buffer regardless of if it was actually saved -func load_from_version_refresh(just_refreshed: Dictionary) -> void: - if just_refreshed.has("current_file_content"): - # We just loaded from a version before multiple buffers - var file: FileAccess = FileAccess.open(just_refreshed.current_file_path, FileAccess.READ) - var file_text: String = file.get_as_text() - open_buffers[just_refreshed.current_file_path] = { - pristine_text = file_text, - text = just_refreshed.current_file_content - } - else: - open_buffers = just_refreshed.open_buffers - - if just_refreshed.current_file_path != "": - EditorInterface.edit_resource(load(just_refreshed.current_file_path)) - else: - EditorInterface.set_main_screen_editor("Dialogue") - - updated_dialog.dialog_text = DMConstants.translate(&"update.success").format({ version = update_button.get_version() }) - updated_dialog.popup_centered() - - -func new_file(path: String, content: String = "") -> void: - if open_buffers.has(path): - remove_file_from_open_buffers(path) - - var file: FileAccess = FileAccess.open(path, FileAccess.WRITE) - if content == "": - file.store_string(DMSettings.get_setting(DMSettings.NEW_FILE_TEMPLATE, "")) - else: - file.store_string(content) - - EditorInterface.get_resource_filesystem().scan() - - -# Open a dialogue resource for editing -func open_resource(resource: DialogueResource) -> void: - open_file(resource.resource_path) - - -func open_file(path: String) -> void: - if not FileAccess.file_exists(path): return - - if not open_buffers.has(path): - var file: FileAccess = FileAccess.open(path, FileAccess.READ) - var text = file.get_as_text() - - open_buffers[path] = { - cursor = Vector2.ZERO, - text = text, - pristine_text = text - } - - DMSettings.add_recent_file(path) - build_open_menu() - - files_list.files = open_buffers.keys() - files_list.select_file(path) - - self.current_file_path = path - - -func show_file_in_filesystem(path: String) -> void: - EditorInterface.get_file_system_dock().navigate_to_path(path) - - -# Save any open files -func save_files() -> void: - save_all_button.disabled = true - - var saved_files: PackedStringArray = [] - for path in open_buffers: - if open_buffers[path].text != open_buffers[path].pristine_text: - saved_files.append(path) - save_file(path, false) - - if saved_files.size() > 0: - Engine.get_meta("DMCache").mark_files_for_reimport(saved_files) - - -# Save a file -func save_file(path: String, rescan_file_system: bool = true) -> void: - var buffer = open_buffers[path] - - files_list.mark_file_as_unsaved(path, false) - save_all_button.disabled = files_list.unsaved_files.size() == 0 - - # Don't bother saving if there is nothing to save - if buffer.text == buffer.pristine_text: - return - - buffer.pristine_text = buffer.text - - # Save the current text - var file: FileAccess = FileAccess.open(path, FileAccess.WRITE) - file.store_string(buffer.text) - file.close() - - if rescan_file_system: - EditorInterface.get_resource_filesystem().scan() - - -func close_file(path: String) -> void: - if not path in open_buffers.keys(): return - - var buffer = open_buffers[path] - - if buffer.text == buffer.pristine_text: - remove_file_from_open_buffers(path) - await get_tree().process_frame - else: - close_confirmation_dialog.dialog_text = DMConstants.translate(&"confirm_close").format({ path = path.get_file() }) - close_confirmation_dialog.popup_centered() - await confirmation_closed - - -func remove_file_from_open_buffers(path: String) -> void: - if not path in open_buffers.keys(): return - - var current_index = open_buffers.keys().find(current_file_path) - - open_buffers.erase(path) - if open_buffers.size() == 0: - self.current_file_path = "" - else: - current_index = clamp(current_index, 0, open_buffers.size() - 1) - self.current_file_path = open_buffers.keys()[current_index] - - files_list.files = open_buffers.keys() - - -# Apply theme colors and icons to the UI -func apply_theme() -> void: - if is_instance_valid(plugin) and is_instance_valid(code_edit): - var scale: float = EditorInterface.get_editor_scale() - var editor_settings = EditorInterface.get_editor_settings() - code_edit.theme_overrides = { - scale = scale, - - background_color = editor_settings.get_setting("text_editor/theme/highlighting/background_color"), - current_line_color = editor_settings.get_setting("text_editor/theme/highlighting/current_line_color"), - error_line_color = editor_settings.get_setting("text_editor/theme/highlighting/mark_color"), - - critical_color = editor_settings.get_setting("text_editor/theme/highlighting/comment_markers/critical_color"), - notice_color = editor_settings.get_setting("text_editor/theme/highlighting/comment_markers/notice_color"), - - titles_color = editor_settings.get_setting("text_editor/theme/highlighting/control_flow_keyword_color"), - text_color = editor_settings.get_setting("text_editor/theme/highlighting/text_color"), - conditions_color = editor_settings.get_setting("text_editor/theme/highlighting/keyword_color"), - mutations_color = editor_settings.get_setting("text_editor/theme/highlighting/function_color"), - members_color = editor_settings.get_setting("text_editor/theme/highlighting/member_variable_color"), - strings_color = editor_settings.get_setting("text_editor/theme/highlighting/string_color"), - numbers_color = editor_settings.get_setting("text_editor/theme/highlighting/number_color"), - symbols_color = editor_settings.get_setting("text_editor/theme/highlighting/symbol_color"), - comments_color = editor_settings.get_setting("text_editor/theme/highlighting/comment_color"), - jumps_color = Color(editor_settings.get_setting("text_editor/theme/highlighting/control_flow_keyword_color"), 0.7), - - font_size = editor_settings.get_setting("interface/editor/code_font_size") - } - - new_button.icon = get_theme_icon("New", "EditorIcons") - new_button.tooltip_text = DMConstants.translate(&"start_a_new_file") - - open_button.icon = get_theme_icon("Load", "EditorIcons") - open_button.tooltip_text = DMConstants.translate(&"open_a_file") - - save_all_button.icon = get_theme_icon("Save", "EditorIcons") - save_all_button.tooltip_text = DMConstants.translate(&"start_all_files") - - find_in_files_button.icon = get_theme_icon("ViewportZoom", "EditorIcons") - find_in_files_button.tooltip_text = DMConstants.translate(&"find_in_files") - - test_button.icon = get_theme_icon("DebugNext", "EditorIcons") - test_button.tooltip_text = DMConstants.translate(&"test_dialogue") - - test_line_button.icon = get_theme_icon("DebugStep", "EditorIcons") - test_line_button.tooltip_text = DMConstants.translate(&"test_dialogue_from_line") - - search_button.icon = get_theme_icon("Search", "EditorIcons") - search_button.tooltip_text = DMConstants.translate(&"search_for_text") - - insert_button.icon = get_theme_icon("RichTextEffect", "EditorIcons") - insert_button.text = DMConstants.translate(&"insert") - - translations_button.icon = get_theme_icon("Translation", "EditorIcons") - translations_button.text = DMConstants.translate(&"translations") - - support_button.icon = get_theme_icon("Heart", "EditorIcons") - support_button.text = DMConstants.translate(&"sponsor") - support_button.tooltip_text = DMConstants.translate(&"show_support") - - docs_button.icon = get_theme_icon("Help", "EditorIcons") - docs_button.text = DMConstants.translate(&"docs") - - update_button.apply_theme() - - # Set up the effect menu - var popup: PopupMenu = insert_button.get_popup() - popup.clear() - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.wave_bbcode"), 0) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.shake_bbcode"), 1) - popup.add_separator() - popup.add_icon_item(get_theme_icon("Time", "EditorIcons"), DMConstants.translate(&"insert.typing_pause"), 3) - popup.add_icon_item(get_theme_icon("ViewportSpeed", "EditorIcons"), DMConstants.translate(&"insert.typing_speed_change"), 4) - popup.add_icon_item(get_theme_icon("DebugNext", "EditorIcons"), DMConstants.translate(&"insert.auto_advance"), 5) - popup.add_separator(DMConstants.translate(&"insert.templates")) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.title"), 6) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.dialogue"), 7) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.response"), 8) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.random_lines"), 9) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.random_text"), 10) - popup.add_separator(DMConstants.translate(&"insert.actions")) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.jump"), 11) - popup.add_icon_item(get_theme_icon("RichTextEffect", "EditorIcons"), DMConstants.translate(&"insert.end_dialogue"), 12) - - # Set up the translations menu - popup = translations_button.get_popup() - popup.clear() - popup.add_icon_item(get_theme_icon("Translation", "EditorIcons"), DMConstants.translate(&"generate_line_ids"), TRANSLATIONS_GENERATE_LINE_IDS) - popup.add_separator() - popup.add_icon_item(get_theme_icon("FileList", "EditorIcons"), DMConstants.translate(&"save_characters_to_csv"), TRANSLATIONS_SAVE_CHARACTERS_TO_CSV) - popup.add_icon_item(get_theme_icon("FileList", "EditorIcons"), DMConstants.translate(&"save_to_csv"), TRANSLATIONS_SAVE_TO_CSV) - popup.add_icon_item(get_theme_icon("AssetLib", "EditorIcons"), DMConstants.translate(&"import_from_csv"), TRANSLATIONS_IMPORT_FROM_CSV) - - # Dialog sizes - new_dialog.min_size = Vector2(600, 500) * scale - save_dialog.min_size = Vector2(600, 500) * scale - open_dialog.min_size = Vector2(600, 500) * scale - quick_open_dialog.min_size = Vector2(400, 600) * scale - export_dialog.min_size = Vector2(600, 500) * scale - import_dialog.min_size = Vector2(600, 500) * scale - find_in_files_dialog.min_size = Vector2(800, 600) * scale - - -### Helpers - - -# Refresh the open menu with the latest files -func build_open_menu() -> void: - var menu = open_button.get_popup() - menu.clear() - menu.add_icon_item(get_theme_icon("Load", "EditorIcons"), DMConstants.translate(&"open.open"), OPEN_OPEN) - menu.add_icon_item(get_theme_icon("Load", "EditorIcons"), DMConstants.translate(&"open.quick_open"), OPEN_QUICK) - menu.add_separator() - - var recent_files = DMSettings.get_recent_files() - if recent_files.size() == 0: - menu.add_item(DMConstants.translate(&"open.no_recent_files")) - menu.set_item_disabled(2, true) - else: - for path in recent_files: - if FileAccess.file_exists(path): - menu.add_icon_item(get_theme_icon("File", "EditorIcons"), path) - - menu.add_separator() - menu.add_item(DMConstants.translate(&"open.clear_recent_files"), OPEN_CLEAR) - if menu.id_pressed.is_connected(_on_open_menu_id_pressed): - menu.id_pressed.disconnect(_on_open_menu_id_pressed) - menu.id_pressed.connect(_on_open_menu_id_pressed) - - -# Get the last place a CSV, etc was exported -func get_last_export_path(extension: String) -> String: - var filename = current_file_path.get_file().replace(".dialogue", "." + extension) - return DMSettings.get_user_value("last_export_path", current_file_path.get_base_dir()) + "/" + filename - - -# Check the current text for errors -func compile() -> void: - # Skip if nothing to parse - if current_file_path == "": return - - var result: DMCompilerResult = DMCompiler.compile_string(code_edit.text, current_file_path) - code_edit.errors = result.errors - errors_panel.errors = result.errors - title_list.titles = code_edit.get_titles() - - -func show_build_error_dialog() -> void: - build_error_dialog.dialog_text = DMConstants.translate(&"errors_with_build") - build_error_dialog.popup_centered() - - -# Generate translation line IDs for any line that doesn't already have one -func generate_translations_keys() -> void: - randomize() - seed(Time.get_unix_time_from_system()) - - var cursor: Vector2 = code_edit.get_cursor() - var lines: PackedStringArray = code_edit.text.split("\n") - - var key_regex = RegEx.new() - key_regex.compile("\\[ID:(?.*?)\\]") - - # Make list of known keys - var known_keys = {} - for i in range(0, lines.size()): - var line = lines[i] - var found = key_regex.search(line) - if found: - var text = "" - var l = line.replace(found.strings[0], "").strip_edges().strip_edges() - if l.begins_with("- "): - text = DMCompiler.extract_translatable_string(l) - elif ":" in l: - text = l.split(":")[1] - else: - text = l - known_keys[found.strings[found.names.get("key")]] = text - - # Add in any that are missing - for i in lines.size(): - var line = lines[i] - var l = line.strip_edges() - - if not [DMConstants.TYPE_DIALOGUE, DMConstants.TYPE_RESPONSE].has(DMCompiler.get_line_type(l)): continue - - if "[ID:" in line: continue - - var text = "" - if l.begins_with("- "): - text = DMCompiler.extract_translatable_string(l) - else: - text = l.substr(l.find(":") + 1) - - var key: String = "" - if known_keys.values().has(text): - key = known_keys.find_key(text) - else: - var regex: DMCompilerRegEx = DMCompilerRegEx.new() - key = regex.ALPHA_NUMERIC.sub(text.strip_edges(), "_", true).substr(0, 30) - if key.begins_with("_"): - key = key.substr(1) - if key.ends_with("_"): - key = key.substr(0, key.length() - 1) - - # Make sure key is unique - var hashed_key: String = key + "_" + str(randi() % 1000000).sha1_text().substr(0, 6) - while hashed_key in known_keys and text != known_keys.get(hashed_key): - hashed_key = key + "_" + str(randi() % 1000000).sha1_text().substr(0, 6) - key = hashed_key.to_upper() - - line = line.replace("\\n", "!NEWLINE!") - text = text.replace("\n", "!NEWLINE!") - lines[i] = line.replace(text, text + " [ID:%s]" % [key]).replace("!NEWLINE!", "\\n") - - known_keys[key] = text - - code_edit.text = "\n".join(lines) - code_edit.set_cursor(cursor) - _on_code_edit_text_changed() - - -# Add a translation file to the project settings -func add_path_to_project_translations(path: String) -> void: - var translations: PackedStringArray = ProjectSettings.get_setting("internationalization/locale/translations") - if not path in translations: - translations.append(path) - ProjectSettings.save() - - -# Export dialogue and responses to CSV -func export_translations_to_csv(path: String) -> void: - var default_locale: String = DMSettings.get_setting(DMSettings.DEFAULT_CSV_LOCALE, "en") - - var file: FileAccess - - # If the file exists, open it first and work out which keys are already in it - var existing_csv: Dictionary = {} - var column_count: int = 2 - var default_locale_column: int = 1 - var character_column: int = -1 - var notes_column: int = -1 - if FileAccess.file_exists(path): - file = FileAccess.open(path, FileAccess.READ) - var is_first_line = true - var line: Array - while !file.eof_reached(): - line = file.get_csv_line() - if is_first_line: - is_first_line = false - column_count = line.size() - for i in range(1, line.size()): - if line[i] == default_locale: - default_locale_column = i - elif line[i] == "_character": - character_column = i - elif line[i] == "_notes": - notes_column = i - - # Make sure the line isn't empty before adding it - if line.size() > 0 and line[0].strip_edges() != "": - existing_csv[line[0]] = line - - # The character column wasn't found in the existing file but the setting is turned on - if character_column == -1 and DMSettings.get_setting(DMSettings.INCLUDE_CHARACTER_IN_TRANSLATION_EXPORTS, false): - character_column = column_count - column_count += 1 - existing_csv["keys"].append("_character") - - # The notes column wasn't found in the existing file but the setting is turned on - if notes_column == -1 and DMSettings.get_setting(DMSettings.INCLUDE_NOTES_IN_TRANSLATION_EXPORTS, false): - notes_column = column_count - column_count += 1 - existing_csv["keys"].append("_notes") - - # Start a new file - file = FileAccess.open(path, FileAccess.WRITE) - - if not FileAccess.file_exists(path): - var headings: PackedStringArray = ["keys", default_locale] + DMSettings.get_setting(DMSettings.EXTRA_CSV_LOCALES, []) - if DMSettings.get_setting(DMSettings.INCLUDE_CHARACTER_IN_TRANSLATION_EXPORTS, false): - character_column = headings.size() - headings.append("_character") - if DMSettings.get_setting(DMSettings.INCLUDE_NOTES_IN_TRANSLATION_EXPORTS, false): - notes_column = headings.size() - headings.append("_notes") - file.store_csv_line(headings) - column_count = headings.size() - - # Write our translations to file - var known_keys: PackedStringArray = [] - - var dialogue = DMCompiler.compile_string(code_edit.text, current_file_path).lines - - # Make a list of stuff that needs to go into the file - var lines_to_save = [] - for key in dialogue.keys(): - var line: Dictionary = dialogue.get(key) - - if not line.type in [DMConstants.TYPE_DIALOGUE, DMConstants.TYPE_RESPONSE]: continue - - var translation_key: String = line.get(&"translation_key", line.text) - - if translation_key in known_keys: continue - - known_keys.append(translation_key) - - var line_to_save: PackedStringArray = [] - if existing_csv.has(translation_key): - line_to_save = existing_csv.get(translation_key) - line_to_save.resize(column_count) - existing_csv.erase(translation_key) - else: - line_to_save.resize(column_count) - line_to_save[0] = translation_key - - line_to_save[default_locale_column] = line.text - if character_column > -1: - line_to_save[character_column] = "(response)" if line.type == DMConstants.TYPE_RESPONSE else line.character - if notes_column > -1: - line_to_save[notes_column] = line.notes - - lines_to_save.append(line_to_save) - - # Store lines in the file, starting with anything that already exists that hasn't been touched - for line in existing_csv.values(): - file.store_csv_line(line) - for line in lines_to_save: - file.store_csv_line(line) - - file.close() - - EditorInterface.get_resource_filesystem().scan() - EditorInterface.get_file_system_dock().call_deferred("navigate_to_path", path) - - # Add it to the project l10n settings if it's not already there - var language_code: RegExMatch = RegEx.create_from_string("^[a-z]{2,3}").search(default_locale) - var translation_path: String = path.replace(".csv", ".%s.translation" % language_code.get_string()) - call_deferred("add_path_to_project_translations", translation_path) - - -func export_character_names_to_csv(path: String) -> void: - var file: FileAccess - - # If the file exists, open it first and work out which keys are already in it - var existing_csv = {} - var commas = [] - if FileAccess.file_exists(path): - file = FileAccess.open(path, FileAccess.READ) - var is_first_line = true - var line: Array - while !file.eof_reached(): - line = file.get_csv_line() - if is_first_line: - is_first_line = false - for i in range(2, line.size()): - commas.append("") - # Make sure the line isn't empty before adding it - if line.size() > 0 and line[0].strip_edges() != "": - existing_csv[line[0]] = line - - # Start a new file - file = FileAccess.open(path, FileAccess.WRITE) - - if not file.file_exists(path): - file.store_csv_line(["keys", DMSettings.get_setting(DMSettings.DEFAULT_CSV_LOCALE, "en")]) - - # Write our translations to file - var known_keys: PackedStringArray = [] - - var character_names: PackedStringArray = DMCompiler.compile_string(code_edit.text, current_file_path).character_names - - # Make a list of stuff that needs to go into the file - var lines_to_save = [] - for character_name in character_names: - if character_name in known_keys: continue - - known_keys.append(character_name) - - if existing_csv.has(character_name): - var existing_line = existing_csv.get(character_name) - existing_line[1] = character_name - lines_to_save.append(existing_line) - existing_csv.erase(character_name) - else: - lines_to_save.append(PackedStringArray([character_name, character_name] + commas)) - - # Store lines in the file, starting with anything that already exists that hasn't been touched - for line in existing_csv.values(): - file.store_csv_line(line) - for line in lines_to_save: - file.store_csv_line(line) - - file.close() - - EditorInterface.get_resource_filesystem().scan() - EditorInterface.get_file_system_dock().call_deferred("navigate_to_path", path) - - # Add it to the project l10n settings if it's not already there - var translation_path: String = path.replace(".csv", ".en.translation") - call_deferred("add_path_to_project_translations", translation_path) - - -# Import changes back from an exported CSV by matching translation keys -func import_translations_from_csv(path: String) -> void: - var cursor: Vector2 = code_edit.get_cursor() - - if not FileAccess.file_exists(path): return - - # Open the CSV file and build a dictionary of the known keys - var keys: Dictionary = {} - var file: FileAccess = FileAccess.open(path, FileAccess.READ) - var csv_line: Array - while !file.eof_reached(): - csv_line = file.get_csv_line() - if csv_line.size() > 1: - keys[csv_line[0]] = csv_line[1] - - # Now look over each line in the dialogue and replace the content for matched keys - var lines: PackedStringArray = code_edit.text.split("\n") - var start_index: int = 0 - var end_index: int = 0 - for i in range(0, lines.size()): - var line: String = lines[i] - var translation_key: String = DMCompiler.get_static_line_id(line) - if keys.has(translation_key): - if DMCompiler.get_line_type(line) == DMConstants.TYPE_DIALOGUE: - start_index = 0 - # See if we need to skip over a character name - line = line.replace("\\:", "!ESCAPED_COLON!") - if ": " in line: - start_index = line.find(": ") + 2 - lines[i] = (line.substr(0, start_index) + keys.get(translation_key) + " [ID:" + translation_key + "]").replace("!ESCAPED_COLON!", ":") - - elif DMCompiler.get_line_type(line) == DMConstants.TYPE_RESPONSE: - start_index = line.find("- ") + 2 - # See if we need to skip over a character name - line = line.replace("\\:", "!ESCAPED_COLON!") - if ": " in line: - start_index = line.find(": ") + 2 - end_index = line.length() - if " =>" in line: - end_index = line.find(" =>") - if " [if " in line: - end_index = line.find(" [if ") - lines[i] = (line.substr(0, start_index) + keys.get(translation_key) + " [ID:" + translation_key + "]" + line.substr(end_index)).replace("!ESCAPED_COLON!", ":") - - code_edit.text = "\n".join(lines) - code_edit.set_cursor(cursor) - - -func show_search_form(is_enabled: bool) -> void: - if code_edit.last_selected_text: - search_and_replace.input.text = code_edit.last_selected_text - - search_and_replace.visible = is_enabled - search_button.set_pressed_no_signal(is_enabled) - search_and_replace.focus_line_edit() - - -### Signals - - -func _on_files_moved(old_file: String, new_file: String) -> void: - if open_buffers.has(old_file): - open_buffers[new_file] = open_buffers[old_file] - open_buffers.erase(old_file) - open_buffers[new_file] - - -func _on_cache_file_content_changed(path: String, new_content: String) -> void: - if open_buffers.has(path): - var buffer = open_buffers[path] - if buffer.text == buffer.pristine_text and buffer.text != new_content: - buffer.text = new_content - code_edit.text = new_content - title_list.titles = code_edit.get_titles() - buffer.pristine_text = new_content - - -func _on_editor_settings_changed() -> void: - var editor_settings: EditorSettings = EditorInterface.get_editor_settings() - code_edit.minimap_draw = editor_settings.get_setting("text_editor/appearance/minimap/show_minimap") - code_edit.minimap_width = editor_settings.get_setting("text_editor/appearance/minimap/minimap_width") - code_edit.scroll_smooth = editor_settings.get_setting("text_editor/behavior/navigation/smooth_scrolling") - - -func _on_open_menu_id_pressed(id: int) -> void: - match id: - OPEN_OPEN: - open_dialog.popup_centered() - OPEN_QUICK: - quick_open_files_list.files = Engine.get_meta("DMCache").get_files() - quick_open_dialog.popup_centered() - quick_open_files_list.focus_filter() - OPEN_CLEAR: - DMSettings.clear_recent_files() - build_open_menu() - _: - var menu = open_button.get_popup() - var item = menu.get_item_text(menu.get_item_index(id)) - open_file(item) - - -func _on_files_list_file_selected(file_path: String) -> void: - self.current_file_path = file_path - - -func _on_insert_button_menu_id_pressed(id: int) -> void: - match id: - 0: - code_edit.insert_bbcode("[wave amp=25 freq=5]", "[/wave]") - 1: - code_edit.insert_bbcode("[shake rate=20 level=10]", "[/shake]") - 3: - code_edit.insert_bbcode("[wait=1]") - 4: - code_edit.insert_bbcode("[speed=0.2]") - 5: - code_edit.insert_bbcode("[next=auto]") - 6: - code_edit.insert_text_at_cursor("~ title") - 7: - code_edit.insert_text_at_cursor("Nathan: This is Some Dialogue") - 8: - code_edit.insert_text_at_cursor("Nathan: Choose a Response...\n- Option 1\n\tNathan: You chose option 1\n- Option 2\n\tNathan: You chose option 2") - 9: - code_edit.insert_text_at_cursor("% Nathan: This is random line 1.\n% Nathan: This is random line 2.\n%1 Nathan: This is weighted random line 3.") - 10: - code_edit.insert_text_at_cursor("Nathan: [[Hi|Hello|Howdy]]") - 11: - code_edit.insert_text_at_cursor("=> title") - 12: - code_edit.insert_text_at_cursor("=> END") - - -func _on_translations_button_menu_id_pressed(id: int) -> void: - match id: - TRANSLATIONS_GENERATE_LINE_IDS: - generate_translations_keys() - - TRANSLATIONS_SAVE_CHARACTERS_TO_CSV: - translation_source = TranslationSource.CharacterNames - export_dialog.filters = PackedStringArray(["*.csv ; Translation CSV"]) - export_dialog.current_path = get_last_export_path("csv") - export_dialog.popup_centered() - - TRANSLATIONS_SAVE_TO_CSV: - translation_source = TranslationSource.Lines - export_dialog.filters = PackedStringArray(["*.csv ; Translation CSV"]) - export_dialog.current_path = get_last_export_path("csv") - export_dialog.popup_centered() - - TRANSLATIONS_IMPORT_FROM_CSV: - import_dialog.current_path = get_last_export_path("csv") - import_dialog.popup_centered() - - -func _on_export_dialog_file_selected(path: String) -> void: - DMSettings.set_user_value("last_export_path", path.get_base_dir()) - match path.get_extension(): - "csv": - match translation_source: - TranslationSource.CharacterNames: - export_character_names_to_csv(path) - TranslationSource.Lines: - export_translations_to_csv(path) - - -func _on_import_dialog_file_selected(path: String) -> void: - DMSettings.set_user_value("last_export_path", path.get_base_dir()) - import_translations_from_csv(path) - - -func _on_main_view_theme_changed(): - apply_theme() - - -func _on_main_view_visibility_changed() -> void: - if visible and is_instance_valid(code_edit): - code_edit.grab_focus() - - -func _on_new_button_pressed() -> void: - new_dialog.current_file = "dialogue" - new_dialog.popup_centered() - - -func _on_new_dialog_confirmed() -> void: - if new_dialog.current_file.get_basename() == "": - var path = "res://untitled.dialogue" - new_file(path) - open_file(path) - - -func _on_new_dialog_file_selected(path: String) -> void: - new_file(path) - open_file(path) - - -func _on_save_dialog_file_selected(path: String) -> void: - if path == "": path = "res://untitled.dialogue" - - new_file(path, code_edit.text) - open_file(path) - - -func _on_open_button_about_to_popup() -> void: - build_open_menu() - - -func _on_open_dialog_file_selected(path: String) -> void: - open_file(path) - - -func _on_quick_open_files_list_file_double_clicked(file_path: String) -> void: - quick_open_dialog.hide() - open_file(file_path) - - -func _on_quick_open_dialog_confirmed() -> void: - if quick_open_files_list.current_file_path: - open_file(quick_open_files_list.current_file_path) - - -func _on_save_all_button_pressed() -> void: - save_files() - - -func _on_find_in_files_button_pressed() -> void: - find_in_files_dialog.popup_centered() - find_in_files.prepare() - - -func _on_code_edit_text_changed() -> void: - var buffer = open_buffers[current_file_path] - buffer.text = code_edit.text - - files_list.mark_file_as_unsaved(current_file_path, buffer.text != buffer.pristine_text) - save_all_button.disabled = open_buffers.values().filter(func(d): return d.text != d.pristine_text).size() == 0 - - parse_timer.start(1) - - -func _on_code_edit_active_title_change(title: String) -> void: - title_list.select_title(title) - - -func _on_code_edit_caret_changed() -> void: - DMSettings.set_caret(current_file_path, code_edit.get_cursor()) - - -func _on_code_edit_error_clicked(line_number: int) -> void: - errors_panel.show_error_for_line_number(line_number) - - -func _on_title_list_title_selected(title: String) -> void: - code_edit.go_to_title(title) - code_edit.grab_focus() - - -func _on_parse_timer_timeout() -> void: - parse_timer.stop() - compile() - - -func _on_errors_panel_error_pressed(line_number: int, column_number: int) -> void: - code_edit.set_caret_line(line_number - 1) - code_edit.set_caret_column(column_number) - code_edit.grab_focus() - - -func _on_search_button_toggled(button_pressed: bool) -> void: - show_search_form(button_pressed) - - -func _on_search_and_replace_open_requested() -> void: - show_search_form(true) - - -func _on_search_and_replace_close_requested() -> void: - search_button.set_pressed_no_signal(false) - search_and_replace.visible = false - code_edit.grab_focus() - - -func _on_test_button_pressed() -> void: - save_file(current_file_path, false) - Engine.get_meta("DMCache").reimport_files([current_file_path]) - - if errors_panel.errors.size() > 0: - errors_dialog.popup_centered() - return - - DMSettings.set_user_value("run_title", "") - DMSettings.set_user_value("is_running_test_scene", true) - DMSettings.set_user_value("run_resource_path", current_file_path) - var test_scene_path: String = DMSettings.get_setting(DMSettings.CUSTOM_TEST_SCENE_PATH, "res://addons/dialogue_manager/test_scene.tscn") - EditorInterface.play_custom_scene(test_scene_path) - - -func _on_test_line_button_pressed() -> void: - save_file(current_file_path) - - if errors_panel.errors.size() > 0: - errors_dialog.popup_centered() - return - - # Find next non-empty line - var line_to_run: int = 0 - for i in range(code_edit.get_cursor().y, code_edit.get_line_count()): - if not code_edit.get_line(i).is_empty(): - line_to_run = i - break; - DMSettings.set_user_value("run_title", str(line_to_run)) - DMSettings.set_user_value("is_running_test_scene", true) - DMSettings.set_user_value("run_resource_path", current_file_path) - var test_scene_path: String = DMSettings.get_setting(DMSettings.CUSTOM_TEST_SCENE_PATH, "res://addons/dialogue_manager/test_scene.tscn") - EditorInterface.play_custom_scene(test_scene_path) - - -func _on_support_button_pressed() -> void: - OS.shell_open("https://patreon.com/nathanhoad") - - -func _on_docs_button_pressed() -> void: - OS.shell_open("https://github.com/nathanhoad/godot_dialogue_manager") - - -func _on_files_list_file_popup_menu_requested(at_position: Vector2) -> void: - files_popup_menu.position = Vector2(get_viewport().position) + files_list.global_position + at_position - files_popup_menu.popup() - - -func _on_files_list_file_middle_clicked(path: String): - close_file(path) - - -func _on_files_popup_menu_about_to_popup() -> void: - files_popup_menu.clear() - - var shortcuts: Dictionary = plugin.get_editor_shortcuts() - - files_popup_menu.add_item(DMConstants.translate(&"buffer.save"), ITEM_SAVE, OS.find_keycode_from_string(shortcuts.get("save")[0].as_text_keycode())) - files_popup_menu.add_item(DMConstants.translate(&"buffer.save_as"), ITEM_SAVE_AS) - files_popup_menu.add_item(DMConstants.translate(&"buffer.close"), ITEM_CLOSE, OS.find_keycode_from_string(shortcuts.get("close_file")[0].as_text_keycode())) - files_popup_menu.add_item(DMConstants.translate(&"buffer.close_all"), ITEM_CLOSE_ALL) - files_popup_menu.add_item(DMConstants.translate(&"buffer.close_other_files"), ITEM_CLOSE_OTHERS) - files_popup_menu.add_separator() - files_popup_menu.add_item(DMConstants.translate(&"buffer.copy_file_path"), ITEM_COPY_PATH) - files_popup_menu.add_item(DMConstants.translate(&"buffer.show_in_filesystem"), ITEM_SHOW_IN_FILESYSTEM) - - -func _on_files_popup_menu_id_pressed(id: int) -> void: - match id: - ITEM_SAVE: - save_file(current_file_path) - ITEM_SAVE_AS: - save_dialog.popup_centered() - ITEM_CLOSE: - close_file(current_file_path) - ITEM_CLOSE_ALL: - for path in open_buffers.keys(): - close_file(path) - ITEM_CLOSE_OTHERS: - var current_current_file_path: String = current_file_path - for path in open_buffers.keys(): - if path != current_current_file_path: - await close_file(path) - - ITEM_COPY_PATH: - DisplayServer.clipboard_set(current_file_path) - ITEM_SHOW_IN_FILESYSTEM: - show_file_in_filesystem(current_file_path) - - -func _on_code_edit_external_file_requested(path: String, title: String) -> void: - open_file(path) - if title != "": - code_edit.go_to_title(title) - else: - code_edit.set_caret_line(0) - - -func _on_close_confirmation_dialog_confirmed() -> void: - save_file(current_file_path) - remove_file_from_open_buffers(current_file_path) - confirmation_closed.emit() - - -func _on_close_confirmation_dialog_custom_action(action: StringName) -> void: - if action == "discard": - remove_file_from_open_buffers(current_file_path) - close_confirmation_dialog.hide() - confirmation_closed.emit() - - -func _on_find_in_files_result_selected(path: String, cursor: Vector2, length: int) -> void: - open_file(path) - code_edit.select(cursor.y, cursor.x, cursor.y, cursor.x + length) diff --git a/addons/dialogue_manager/views/main_view.gd.uid b/addons/dialogue_manager/views/main_view.gd.uid deleted file mode 100644 index 10e66f4..0000000 --- a/addons/dialogue_manager/views/main_view.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cipjcc7bkh1pc diff --git a/addons/dialogue_manager/views/main_view.tscn b/addons/dialogue_manager/views/main_view.tscn deleted file mode 100644 index 4e70b26..0000000 --- a/addons/dialogue_manager/views/main_view.tscn +++ /dev/null @@ -1,430 +0,0 @@ -[gd_scene load_steps=15 format=3 uid="uid://cbuf1q3xsse3q"] - -[ext_resource type="Script" uid="uid://cipjcc7bkh1pc" path="res://addons/dialogue_manager/views/main_view.gd" id="1_h6qfq"] -[ext_resource type="PackedScene" uid="uid://civ6shmka5e8u" path="res://addons/dialogue_manager/components/code_edit.tscn" id="2_f73fm"] -[ext_resource type="PackedScene" uid="uid://dnufpcdrreva3" path="res://addons/dialogue_manager/components/files_list.tscn" id="2_npj2k"] -[ext_resource type="PackedScene" uid="uid://ctns6ouwwd68i" path="res://addons/dialogue_manager/components/title_list.tscn" id="2_onb4i"] -[ext_resource type="PackedScene" uid="uid://co8yl23idiwbi" path="res://addons/dialogue_manager/components/update_button.tscn" id="2_ph3vs"] -[ext_resource type="PackedScene" uid="uid://gr8nakpbrhby" path="res://addons/dialogue_manager/components/search_and_replace.tscn" id="6_ylh0t"] -[ext_resource type="PackedScene" uid="uid://cs8pwrxr5vxix" path="res://addons/dialogue_manager/components/errors_panel.tscn" id="7_5cvl4"] -[ext_resource type="Script" uid="uid://klpiq4tk3t7a" path="res://addons/dialogue_manager/components/code_edit_syntax_highlighter.gd" id="7_necsa"] -[ext_resource type="PackedScene" uid="uid://0n7hwviyyly4" path="res://addons/dialogue_manager/components/find_in_files.tscn" id="10_yold3"] - -[sub_resource type="Image" id="Image_faxki"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_ka3gk"] -image = SubResource("Image_faxki") - -[sub_resource type="Image" id="Image_y6rqu"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_57eek"] -image = SubResource("Image_y6rqu") - -[sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_kb7f8"] -script = ExtResource("7_necsa") - -[node name="MainView" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -script = ExtResource("1_h6qfq") - -[node name="ParseTimer" type="Timer" parent="."] - -[node name="Margin" type="MarginContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_vertical = 3 -theme_override_constants/margin_left = 5 -theme_override_constants/margin_right = 5 -theme_override_constants/margin_bottom = 5 -metadata/_edit_layout_mode = 1 - -[node name="Content" type="HSplitContainer" parent="Margin"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -dragger_visibility = 1 - -[node name="SidePanel" type="VBoxContainer" parent="Margin/Content"] -custom_minimum_size = Vector2(150, 0) -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Toolbar" type="HBoxContainer" parent="Margin/Content/SidePanel"] -layout_mode = 2 - -[node name="NewButton" type="Button" parent="Margin/Content/SidePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Start a new file" -flat = true - -[node name="OpenButton" type="MenuButton" parent="Margin/Content/SidePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Open a file" -item_count = 9 -popup/item_0/text = "Open..." -popup/item_0/icon = SubResource("ImageTexture_ka3gk") -popup/item_0/id = 100 -popup/item_1/icon = SubResource("ImageTexture_ka3gk") -popup/item_1/id = 101 -popup/item_2/id = -1 -popup/item_2/separator = true -popup/item_3/text = "res://examples/dialogue.dialogue" -popup/item_3/icon = SubResource("ImageTexture_ka3gk") -popup/item_3/id = 3 -popup/item_4/text = "res://examples/dialogue_with_input.dialogue" -popup/item_4/icon = SubResource("ImageTexture_ka3gk") -popup/item_4/id = 4 -popup/item_5/text = "res://examples/dialogue_for_point_n_click.dialogue" -popup/item_5/icon = SubResource("ImageTexture_ka3gk") -popup/item_5/id = 5 -popup/item_6/text = "res://examples/dialogue_for_visual_novel.dialogue" -popup/item_6/icon = SubResource("ImageTexture_ka3gk") -popup/item_6/id = 6 -popup/item_7/id = -1 -popup/item_7/separator = true -popup/item_8/text = "Clear recent files" -popup/item_8/id = 102 - -[node name="SaveAllButton" type="Button" parent="Margin/Content/SidePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -disabled = true -flat = true - -[node name="FindInFilesButton" type="Button" parent="Margin/Content/SidePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Find in files..." -flat = true - -[node name="Bookmarks" type="VSplitContainer" parent="Margin/Content/SidePanel"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="FilesList" parent="Margin/Content/SidePanel/Bookmarks" instance=ExtResource("2_npj2k")] -unique_name_in_owner = true -visible = false -layout_mode = 2 -size_flags_vertical = 3 - -[node name="FilesPopupMenu" type="PopupMenu" parent="Margin/Content/SidePanel/Bookmarks/FilesList"] -unique_name_in_owner = true - -[node name="TitleList" parent="Margin/Content/SidePanel/Bookmarks" instance=ExtResource("2_onb4i")] -unique_name_in_owner = true -visible = false -layout_mode = 2 - -[node name="CodePanel" type="VBoxContainer" parent="Margin/Content"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_stretch_ratio = 4.0 - -[node name="Toolbar" type="HBoxContainer" parent="Margin/Content/CodePanel"] -layout_mode = 2 - -[node name="InsertButton" type="MenuButton" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -disabled = true -text = "Insert" -item_count = 15 -popup/item_0/text = "Wave BBCode" -popup/item_0/icon = SubResource("ImageTexture_57eek") -popup/item_1/text = "Shake BBCode" -popup/item_1/icon = SubResource("ImageTexture_57eek") -popup/item_1/id = 1 -popup/item_2/id = -1 -popup/item_2/separator = true -popup/item_3/text = "Typing pause" -popup/item_3/icon = SubResource("ImageTexture_57eek") -popup/item_3/id = 3 -popup/item_4/text = "Typing speed change" -popup/item_4/icon = SubResource("ImageTexture_57eek") -popup/item_4/id = 4 -popup/item_5/text = "Auto advance" -popup/item_5/icon = SubResource("ImageTexture_57eek") -popup/item_5/id = 5 -popup/item_6/text = "Templates" -popup/item_6/id = -1 -popup/item_6/separator = true -popup/item_7/text = "Title" -popup/item_7/icon = SubResource("ImageTexture_57eek") -popup/item_7/id = 6 -popup/item_8/text = "Dialogue" -popup/item_8/icon = SubResource("ImageTexture_57eek") -popup/item_8/id = 7 -popup/item_9/text = "Response" -popup/item_9/icon = SubResource("ImageTexture_57eek") -popup/item_9/id = 8 -popup/item_10/text = "Random lines" -popup/item_10/icon = SubResource("ImageTexture_57eek") -popup/item_10/id = 9 -popup/item_11/text = "Random text" -popup/item_11/icon = SubResource("ImageTexture_57eek") -popup/item_11/id = 10 -popup/item_12/text = "Actions" -popup/item_12/id = -1 -popup/item_12/separator = true -popup/item_13/text = "Jump to title" -popup/item_13/icon = SubResource("ImageTexture_57eek") -popup/item_13/id = 11 -popup/item_14/text = "End dialogue" -popup/item_14/icon = SubResource("ImageTexture_57eek") -popup/item_14/id = 12 - -[node name="TranslationsButton" type="MenuButton" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -disabled = true -text = "Translations" -item_count = 5 -popup/item_0/text = "Generate line IDs" -popup/item_0/icon = SubResource("ImageTexture_57eek") -popup/item_0/id = 100 -popup/item_1/id = -1 -popup/item_1/separator = true -popup/item_2/text = "Save character names to CSV..." -popup/item_2/icon = SubResource("ImageTexture_57eek") -popup/item_2/id = 201 -popup/item_3/text = "Save lines to CSV..." -popup/item_3/icon = SubResource("ImageTexture_57eek") -popup/item_3/id = 202 -popup/item_4/text = "Import line changes from CSV..." -popup/item_4/icon = SubResource("ImageTexture_57eek") -popup/item_4/id = 203 - -[node name="Separator" type="VSeparator" parent="Margin/Content/CodePanel/Toolbar"] -layout_mode = 2 - -[node name="SearchButton" type="Button" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Search for text" -disabled = true -toggle_mode = true -flat = true - -[node name="Separator2" type="VSeparator" parent="Margin/Content/CodePanel/Toolbar"] -layout_mode = 2 - -[node name="TestButton" type="Button" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Test dialogue" -disabled = true -flat = true - -[node name="TestLineButton" type="Button" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Test dialogue" -disabled = true -flat = true - -[node name="Spacer2" type="Control" parent="Margin/Content/CodePanel/Toolbar"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="SupportButton" type="Button" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Support Dialogue Manager" -text = "Sponsor" -flat = true - -[node name="Separator4" type="VSeparator" parent="Margin/Content/CodePanel/Toolbar"] -layout_mode = 2 - -[node name="DocsButton" type="Button" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -layout_mode = 2 -text = "Docs" -flat = true - -[node name="VersionLabel" type="Label" parent="Margin/Content/CodePanel/Toolbar"] -unique_name_in_owner = true -modulate = Color(1, 1, 1, 0.490196) -layout_mode = 2 -text = "v2.42.2" -vertical_alignment = 1 - -[node name="UpdateButton" parent="Margin/Content/CodePanel/Toolbar" instance=ExtResource("2_ph3vs")] -unique_name_in_owner = true -layout_mode = 2 -text = "v2.44.1 available" - -[node name="SearchAndReplace" parent="Margin/Content/CodePanel" instance=ExtResource("6_ylh0t")] -unique_name_in_owner = true -layout_mode = 2 - -[node name="CodeEdit" parent="Margin/Content/CodePanel" instance=ExtResource("2_f73fm")] -unique_name_in_owner = true -visible = false -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -theme_override_colors/current_line_color = Color(0.266667, 0.278431, 0.352941, 0.243137) -theme_override_colors/background_color = Color(0.156863, 0.164706, 0.211765, 1) -theme_override_colors/font_color = Color(0.972549, 0.972549, 0.94902, 1) -theme_override_font_sizes/font_size = 14 -theme_override_colors/bookmark_color = Color(1, 0.333333, 0.333333, 1) -text = "~ start - -Nathan: Hi, I'm Nathan and this is Coco. -Coco: Meow. -Nathan: Here are some response options. -- First one - Nathan: You picked the first one. -- Second one - Nathan: You picked the second one. -- Start again => start -- End the conversation => END -Nathan: I hope this example is helpful. -Coco: Meow. - -=> END" -scroll_smooth = true -syntax_highlighter = SubResource("SyntaxHighlighter_kb7f8") - -[node name="ErrorsPanel" parent="Margin/Content/CodePanel" instance=ExtResource("7_5cvl4")] -unique_name_in_owner = true -layout_mode = 2 - -[node name="NewDialog" type="FileDialog" parent="."] -size = Vector2i(900, 750) -min_size = Vector2i(600, 500) -dialog_hide_on_ok = true -filters = PackedStringArray("*.dialogue ; Dialogue") - -[node name="SaveDialog" type="FileDialog" parent="."] -size = Vector2i(900, 750) -min_size = Vector2i(600, 500) -dialog_hide_on_ok = true -filters = PackedStringArray("*.dialogue ; Dialogue") - -[node name="OpenDialog" type="FileDialog" parent="."] -title = "Open a File" -size = Vector2i(900, 750) -min_size = Vector2i(600, 500) -ok_button_text = "Open" -dialog_hide_on_ok = true -file_mode = 0 -filters = PackedStringArray("*.dialogue ; Dialogue") - -[node name="QuickOpenDialog" type="ConfirmationDialog" parent="."] -title = "Quick open" -size = Vector2i(600, 900) -min_size = Vector2i(400, 600) -ok_button_text = "Open" - -[node name="QuickOpenFilesList" parent="QuickOpenDialog" instance=ExtResource("2_npj2k")] - -[node name="ExportDialog" type="FileDialog" parent="."] -size = Vector2i(900, 750) -min_size = Vector2i(600, 500) - -[node name="ImportDialog" type="FileDialog" parent="."] -title = "Open a File" -size = Vector2i(900, 750) -min_size = Vector2i(600, 500) -ok_button_text = "Open" -file_mode = 0 -filters = PackedStringArray("*.csv ; Translation CSV") - -[node name="ErrorsDialog" type="AcceptDialog" parent="."] -title = "Error" -dialog_text = "You have errors in your script. Fix them and then try again." - -[node name="BuildErrorDialog" type="AcceptDialog" parent="."] -title = "Errors" -dialog_text = "You need to fix dialogue errors before you can run your game." - -[node name="CloseConfirmationDialog" type="ConfirmationDialog" parent="."] -title = "Unsaved changes" -ok_button_text = "Save changes" - -[node name="UpdatedDialog" type="AcceptDialog" parent="."] -title = "Updated" -size = Vector2i(191, 100) -dialog_text = "You're now up to date!" - -[node name="FindInFilesDialog" type="AcceptDialog" parent="."] -title = "Find in files" -size = Vector2i(1200, 900) -min_size = Vector2i(800, 600) -ok_button_text = "Done" - -[node name="FindInFiles" parent="FindInFilesDialog" node_paths=PackedStringArray("main_view", "code_edit") instance=ExtResource("10_yold3")] -custom_minimum_size = Vector2(400, 400) -offset_left = 8.0 -offset_top = 8.0 -offset_right = -8.0 -offset_bottom = -49.0 -main_view = NodePath("../..") -code_edit = NodePath("../../Margin/Content/CodePanel/CodeEdit") - -[connection signal="theme_changed" from="." to="." method="_on_main_view_theme_changed"] -[connection signal="visibility_changed" from="." to="." method="_on_main_view_visibility_changed"] -[connection signal="timeout" from="ParseTimer" to="." method="_on_parse_timer_timeout"] -[connection signal="pressed" from="Margin/Content/SidePanel/Toolbar/NewButton" to="." method="_on_new_button_pressed"] -[connection signal="about_to_popup" from="Margin/Content/SidePanel/Toolbar/OpenButton" to="." method="_on_open_button_about_to_popup"] -[connection signal="pressed" from="Margin/Content/SidePanel/Toolbar/SaveAllButton" to="." method="_on_save_all_button_pressed"] -[connection signal="pressed" from="Margin/Content/SidePanel/Toolbar/FindInFilesButton" to="." method="_on_find_in_files_button_pressed"] -[connection signal="file_middle_clicked" from="Margin/Content/SidePanel/Bookmarks/FilesList" to="." method="_on_files_list_file_middle_clicked"] -[connection signal="file_popup_menu_requested" from="Margin/Content/SidePanel/Bookmarks/FilesList" to="." method="_on_files_list_file_popup_menu_requested"] -[connection signal="file_selected" from="Margin/Content/SidePanel/Bookmarks/FilesList" to="." method="_on_files_list_file_selected"] -[connection signal="about_to_popup" from="Margin/Content/SidePanel/Bookmarks/FilesList/FilesPopupMenu" to="." method="_on_files_popup_menu_about_to_popup"] -[connection signal="id_pressed" from="Margin/Content/SidePanel/Bookmarks/FilesList/FilesPopupMenu" to="." method="_on_files_popup_menu_id_pressed"] -[connection signal="title_selected" from="Margin/Content/SidePanel/Bookmarks/TitleList" to="." method="_on_title_list_title_selected"] -[connection signal="toggled" from="Margin/Content/CodePanel/Toolbar/SearchButton" to="." method="_on_search_button_toggled"] -[connection signal="pressed" from="Margin/Content/CodePanel/Toolbar/TestButton" to="." method="_on_test_button_pressed"] -[connection signal="pressed" from="Margin/Content/CodePanel/Toolbar/TestLineButton" to="." method="_on_test_line_button_pressed"] -[connection signal="pressed" from="Margin/Content/CodePanel/Toolbar/SupportButton" to="." method="_on_support_button_pressed"] -[connection signal="pressed" from="Margin/Content/CodePanel/Toolbar/DocsButton" to="." method="_on_docs_button_pressed"] -[connection signal="close_requested" from="Margin/Content/CodePanel/SearchAndReplace" to="." method="_on_search_and_replace_close_requested"] -[connection signal="open_requested" from="Margin/Content/CodePanel/SearchAndReplace" to="." method="_on_search_and_replace_open_requested"] -[connection signal="active_title_change" from="Margin/Content/CodePanel/CodeEdit" to="." method="_on_code_edit_active_title_change"] -[connection signal="caret_changed" from="Margin/Content/CodePanel/CodeEdit" to="." method="_on_code_edit_caret_changed"] -[connection signal="error_clicked" from="Margin/Content/CodePanel/CodeEdit" to="." method="_on_code_edit_error_clicked"] -[connection signal="external_file_requested" from="Margin/Content/CodePanel/CodeEdit" to="." method="_on_code_edit_external_file_requested"] -[connection signal="text_changed" from="Margin/Content/CodePanel/CodeEdit" to="." method="_on_code_edit_text_changed"] -[connection signal="error_pressed" from="Margin/Content/CodePanel/ErrorsPanel" to="." method="_on_errors_panel_error_pressed"] -[connection signal="confirmed" from="NewDialog" to="." method="_on_new_dialog_confirmed"] -[connection signal="file_selected" from="NewDialog" to="." method="_on_new_dialog_file_selected"] -[connection signal="file_selected" from="SaveDialog" to="." method="_on_save_dialog_file_selected"] -[connection signal="file_selected" from="OpenDialog" to="." method="_on_open_dialog_file_selected"] -[connection signal="confirmed" from="QuickOpenDialog" to="." method="_on_quick_open_dialog_confirmed"] -[connection signal="file_double_clicked" from="QuickOpenDialog/QuickOpenFilesList" to="." method="_on_quick_open_files_list_file_double_clicked"] -[connection signal="file_selected" from="ExportDialog" to="." method="_on_export_dialog_file_selected"] -[connection signal="file_selected" from="ImportDialog" to="." method="_on_import_dialog_file_selected"] -[connection signal="confirmed" from="CloseConfirmationDialog" to="." method="_on_close_confirmation_dialog_confirmed"] -[connection signal="custom_action" from="CloseConfirmationDialog" to="." method="_on_close_confirmation_dialog_custom_action"] -[connection signal="result_selected" from="FindInFilesDialog/FindInFiles" to="." method="_on_find_in_files_result_selected"] diff --git a/addons/godot-git-plugin/LICENSE b/addons/godot-git-plugin/LICENSE deleted file mode 100644 index f153fb8..0000000 --- a/addons/godot-git-plugin/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016-2023 The Godot Engine community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/addons/godot-git-plugin/THIRDPARTY.md b/addons/godot-git-plugin/THIRDPARTY.md deleted file mode 100644 index 837488f..0000000 --- a/addons/godot-git-plugin/THIRDPARTY.md +++ /dev/null @@ -1,1349 +0,0 @@ -# Third-Party Notices - -The Godot Git Plugin source code uses the following third-party source code: - -1. godotengine/godot-cpp - MIT License - https://github.com/godotengine/godot-cpp/tree/02336831735fd6affbe0a6fa252ec98d3e78120c -2. libgit2/libgit2 - GPLv2 with a special Linking Exception - https://github.com/libgit2/libgit2/tree/b7bad55e4bb0a285b073ba5e02b01d3f522fc95d -3. libssh2/libssh2 - BSD-3-Clause License - https://github.com/libssh2/libssh2/tree/635caa90787220ac3773c1d5ba11f1236c22eae8 - -We also link to these third-party libraries (only in the compiled binary form): - -1. OpenSSL - Only on Linux and MacOS - OpenSSL License - http://www.openssl.org/source/openssl-1.1.1s.tar.gz - -## License Texts - -### godotengine/godot-cpp - -``` -# MIT License - -Copyright (c) 2017-2022 Godot Engine contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -``` - -### libgit2/libgit2 - -``` - libgit2 is Copyright (C) the libgit2 contributors, - unless otherwise stated. See the AUTHORS file for details. - - Note that the only valid version of the GPL as far as this project - is concerned is _this_ particular version of the license (ie v2, not - v2.2 or v3.x or whatever), unless explicitly otherwise stated. - ----------------------------------------------------------------------- - - LINKING EXCEPTION - - In addition to the permissions in the GNU General Public License, - the authors give you unlimited permission to link the compiled - version of this library into combinations with other programs, - and to distribute those combinations without any restriction - coming from the use of this file. (The General Public License - restrictions do apply in other respects; for example, they cover - modification of the file, and distribution when not linked into - a combined executable.) - ----------------------------------------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. - ----------------------------------------------------------------------- - -The bundled ZLib code is licensed under the ZLib license: - -Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - ----------------------------------------------------------------------- - -The Clar framework is licensed under the ISC license: - -Copyright (c) 2011-2015 Vicent Marti - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ----------------------------------------------------------------------- - -The regex library (deps/regex/) is licensed under the GNU LGPL -(available at the end of this file). - -Definitions for data structures and routines for the regular -expression library. - -Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008 -Free Software Foundation, Inc. -This file is part of the GNU C Library. - -The GNU C Library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -The GNU C Library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with the GNU C Library; if not, write to the Free -Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - ----------------------------------------------------------------------- - -The bundled winhttp definition files (deps/winhttp/) are licensed under -the GNU LGPL (available at the end of this file). - -Copyright (C) 2007 Francois Gouget - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - ----------------------------------------------------------------------- - - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - ----------------------------------------------------------------------- - -The bundled SHA1 collision detection code is licensed under the MIT license: - -MIT License - -Copyright (c) 2017: - Marc Stevens - Cryptology Group - Centrum Wiskunde & Informatica - P.O. Box 94079, 1090 GB Amsterdam, Netherlands - marc@marc-stevens.nl - - Dan Shumow - Microsoft Research - danshu@microsoft.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ----------------------------------------------------------------------- - -The bundled wildmatch code is licensed under the BSD license: - -Copyright Rich Salz. -All rights reserved. - -Redistribution and use in any form are permitted provided that the -following restrictions are are met: - -1. Source distributions must retain this entire copyright notice - and comment. -2. Binary distributions must include the acknowledgement ``This - product includes software developed by Rich Salz'' in the - documentation or other materials provided with the - distribution. This must not be represented as an endorsement - or promotion without specific prior written permission. -3. The origin of this software must not be misrepresented, either - by explicit claim or by omission. Credits must appear in the - source and documentation. -4. Altered versions must be plainly marked as such in the source - and documentation and must not be misrepresented as being the - original software. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - ----------------------------------------------------------------------- - -Portions of the OpenSSL headers are included under the OpenSSL license: - -Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) -All rights reserved. - -This package is an SSL implementation written -by Eric Young (eay@cryptsoft.com). -The implementation was written so as to conform with Netscapes SSL. - -This library is free for commercial and non-commercial use as long as -the following conditions are aheared to. The following conditions -apply to all code found in this distribution, be it the RC4, RSA, -lhash, DES, etc., code; not just the SSL code. The SSL documentation -included with this distribution is covered by the same copyright terms -except that the holder is Tim Hudson (tjh@cryptsoft.com). - -Copyright remains Eric Young's, and as such any Copyright notices in -the code are not to be removed. -If this package is used in a product, Eric Young should be given attribution -as the author of the parts of the library used. -This can be in the form of a textual message at program startup or -in documentation (online or textual) provided with the package. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - "This product includes cryptographic software written by - Eric Young (eay@cryptsoft.com)" - The word 'cryptographic' can be left out if the rouines from the library - being used are not cryptographic related :-). -4. If you include any Windows specific code (or a derivative thereof) from - the apps directory (application code) you must include an acknowledgement: - "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - -THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - -The licence and distribution terms for any publically available version or -derivative of this code cannot be changed. i.e. this code cannot simply be -copied and put under another distribution licence -[including the GNU Public Licence.] - -==================================================================== -Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR -ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. -``` - -### libssh2/libssh2 - -``` -/* Copyright (c) 2004-2007 Sara Golemon - * Copyright (c) 2005,2006 Mikhail Gusarov - * Copyright (c) 2006-2007 The Written Word, Inc. - * Copyright (c) 2007 Eli Fant - * Copyright (c) 2009-2021 Daniel Stenberg - * Copyright (C) 2008, 2009 Simon Josefsson - * Copyright (c) 2000 Markus Friedl - * Copyright (c) 2015 Microsoft Corp. - * All rights reserved. - * - * Redistribution and use in source and binary forms, - * with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * Neither the name of the copyright holder nor the names - * of any other contributors may be used to endorse or - * promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. - */ -``` - -### OpenSSL - -``` - - LICENSE ISSUES - ============== - - The OpenSSL toolkit stays under a double license, i.e. both the conditions of - the OpenSSL License and the original SSLeay license apply to the toolkit. - See below for the actual license texts. - - OpenSSL License - --------------- - -/* ==================================================================== - * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * openssl-core@openssl.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). - * - */ - - Original SSLeay License - ----------------------- - -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ -``` diff --git a/addons/godot-git-plugin/git_plugin.gdextension b/addons/godot-git-plugin/git_plugin.gdextension deleted file mode 100644 index 49fffbf..0000000 --- a/addons/godot-git-plugin/git_plugin.gdextension +++ /dev/null @@ -1,12 +0,0 @@ -[configuration] - -entry_symbol = "git_plugin_init" -compatibility_minimum = "4.1.0" - -[libraries] - -macos.editor = "macos/libgit_plugin.macos.editor.universal.dylib" -windows.editor.x86_64 = "win64/libgit_plugin.windows.editor.x86_64.dll" -linux.editor.x86_64 = "linux/libgit_plugin.linux.editor.x86_64.so" -linux.editor.arm64 = "linux/libgit_plugin.linux.editor.arm64.so" -linux.editor.rv64 = "" diff --git a/addons/godot-git-plugin/linux/libgit_plugin.linux.editor.x86_64.so b/addons/godot-git-plugin/linux/libgit_plugin.linux.editor.x86_64.so deleted file mode 100644 index 8dab6db..0000000 Binary files a/addons/godot-git-plugin/linux/libgit_plugin.linux.editor.x86_64.so and /dev/null differ diff --git a/addons/godot-git-plugin/macos/libgit_plugin.macos.editor.universal.dylib b/addons/godot-git-plugin/macos/libgit_plugin.macos.editor.universal.dylib deleted file mode 100644 index 644aa2a..0000000 Binary files a/addons/godot-git-plugin/macos/libgit_plugin.macos.editor.universal.dylib and /dev/null differ diff --git a/addons/godot-git-plugin/plugin.cfg b/addons/godot-git-plugin/plugin.cfg deleted file mode 100644 index 9c4e36f..0000000 --- a/addons/godot-git-plugin/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Godot Git Plugin" -description="This plugin lets you interact with Git without leaving the Godot editor. More information can be found at https://github.com/godotengine/godot-git-plugin/wiki" -author="twaritwaikar" -version="v3.1.1" -script="godot-git-plugin.gd" diff --git a/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.dll b/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.dll deleted file mode 100644 index 47bbb1d..0000000 Binary files a/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.dll and /dev/null differ diff --git a/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.exp b/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.exp deleted file mode 100644 index 6c68d89..0000000 Binary files a/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.exp and /dev/null differ diff --git a/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.lib b/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.lib deleted file mode 100644 index 4537929..0000000 Binary files a/addons/godot-git-plugin/win64/libgit_plugin.windows.editor.x86_64.lib and /dev/null differ diff --git a/current_patient.gd b/current_patient.gd deleted file mode 100644 index f39fa9b..0000000 --- a/current_patient.gd +++ /dev/null @@ -1,56 +0,0 @@ -extends Node - -var emotion: String -var dialog_flags : Dictionary = { - "history_of_present_illness": [], - "appearance": [], - "behaviour": [], - "speech": [], - "thought_process": [], - "thought_content": [], - "perceptions": [], - "insight": [], - "sleep": [], - "mood": [], - "suicidal_ideation": [], - "notes": [], - } -var dialog_tree: Dictionary -var marker: Array[int] - - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - marker.resize(10) - marker.fill(0) - pass - - -func build_dialog_tree(tree_model: Array[int]) -> Dictionary: - - var branch : Dictionary - var tree : Dictionary - - for branch_id in tree_model: - branch = {branch_id: "not_asked"} - tree.merge(branch) - - return tree - - -func change_emotion(new_emotion: String) -> void: - emotion = new_emotion - pass - - -func send_to_reset() -> void: - pass - - -func send_to_isolation() -> void: - pass - - -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta: float) -> void: - pass diff --git a/dialog_main.tscn b/dialog_main.tscn deleted file mode 100644 index ba710c7..0000000 --- a/dialog_main.tscn +++ /dev/null @@ -1,12 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://cx2nwe1mk2cod"] - -[ext_resource type="Script" path="res://dialog_main.gd" id="1_kjkxp"] - -[node name="DialogMain" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_kjkxp") diff --git a/dialogues/mr_orleg.dialogue b/dialogues/mr_orleg.dialogue deleted file mode 100644 index d45d70c..0000000 --- a/dialogues/mr_orleg.dialogue +++ /dev/null @@ -1,257 +0,0 @@ -~ mr_orleg_day_1_dialogue_start - -do CurrentPatient.marker.fill(0) -set CurrentPatient.dialog_tree = CurrentPatient.build_dialog_tree([1100,1200,1210,1220, 1300, 1400, 1500, 1610, 1620]) - -: The patient remains still, watching you expectantly. - -Psychiatrist: Greetings Mr Orleg. You shall be my in care for the duration of your stay in this facility. - -Mr Orleg: Greetings Doctor. I need your help, i don't know why i'm here, there must have been a mistake ! - -~ day_1_choice_1 - -- Tell me why you were brought here. [if CurrentPatient.dialog_tree[1100] == "not_asked"] - - set CurrentPatient.dialog_tree[1100] = "asked" - do CurrentPatient.marker[0] += 1 - do CurrentPatient.change_emotion("neutral") - do CurrentPatient.dialog_flags["insight"].append("Anosognosia") - do CurrentPatient.dialog_flags["notes"].append("Doesn't think he's sick") - - Mr Orleg: Sure. I was at the usual grocery store next door, and there was that cashier who was threatening me. I was busy explaining her nobody can talk to Mr Orleg this way, when these damned cops came out of nowhere and brutally restrained me ! Can you imagine ?! - - => day_1_choice_1 - -- Do you feel safe in this room ? [if CurrentPatient.dialog_tree[1200] == "not_asked"] - - set CurrentPatient.dialog_tree[1200] = "asked" - do CurrentPatient.marker[0] += 1 - do CurrentPatient.change_emotion("scared") - do CurrentPatient.dialog_flags["thought_content"].append("Persecutory delusions") - do CurrentPatient.dialog_flags["notes"].append("Afraid of medical staff") - - Mr Orleg: Honestly ? I don't. Some of your colleagues have been giving me the heeby jeebies. Gotta stay sharp, i'm pretty sure he's here. - - ~ day_1_choice_1_2 - - - Who's "he" ? [if CurrentPatient.dialog_tree[1210] == "not_asked"] - - set CurrentPatient.dialog_tree[1210] = "asked" - do CurrentPatient.marker[1] += 1 - do CurrentPatient.dialog_flags["thought_content"].append("Mystical delusions") - do CurrentPatient.dialog_flags["notes"].append("Thinks the Devil is after him") - - Mr Orleg: Shhh... you musntn't speak his name aloud. "He" is the Harbinger. The Prophet and the Reaper. The Messiah and the Deceiver. - - if CurrentPatient.marker[1] == 2 - set CurrentPatient.marker[1] = 0 - => day_1_choice_1 - else - => day_1_choice_1_2 - - - Which of my colleagues are you talking about ? [if CurrentPatient.dialog_tree[1220] == "not_asked"] - - set CurrentPatient.dialog_tree[1220] = "asked" - do CurrentPatient.marker[1] += 1 - do CurrentPatient.dialog_flags["thought_content"].append("Fregoli syndrome") - do CurrentPatient.dialog_flags["notes"].append("Recognizes the Devil in everyone") - - Mr Orleg: Every single one of them... He's been using their shapes to contact me. I need to remain strong, Doctor. - - if CurrentPatient.marker[1] == 2 - set CurrentPatient.marker[1] = 0 - => day_1_choice_1 - else - => day_1_choice_1_2 - -- How has your sleep been recently ? [if CurrentPatient.dialog_tree[1300] == "not_asked"] - - set CurrentPatient.dialog_tree[1300] = "asked" - do CurrentPatient.marker[0] += 1 - do CurrentPatient.change_emotion("neutral") - do CurrentPatient.dialog_flags["sleep"].append("Insomnia") - do CurrentPatient.dialog_flags["notes"].append("Doesn't get much sleep") - - Mr Orleg: Hard to keep my eyes shut lately, i'll admit. I definitely could use some rest. - - => day_1_choice_1 - -- How are you feeling lately ? [if CurrentPatient.dialog_tree[1400] == "not_asked"] - - set CurrentPatient.dialog_tree[1400] = "asked" - do CurrentPatient.marker[0] += 1 - do CurrentPatient.change_emotion("neutral") - do CurrentPatient.dialog_flags["mood"].append("Euthymia") - do CurrentPatient.dialog_flags["notes"].append("Feels quite happy") - - Mr Orleg: I'm feeling rather fine, actually. My life's quite enjoyable, except for the whole "i'm locked in here cause people think i'm crazy" situation. You've got to help me Doctor. - - - Have you had any thoughts about wanting to end your life ? - - do CurrentPatient.dialog_flags["suicidal_ideation"].append("None") - do CurrentPatient.dialog_flags["notes"].append("No suicidal ideations") - - Mr Orleg: I would never dare end the life God has given me. - - => day_1_choice_1 - -- Examine [if CurrentPatient.dialog_tree[1500] == "not_asked"] - - set CurrentPatient.dialog_tree[1500] = "asked" - do CurrentPatient.marker[0] += 1 - do CurrentPatient.dialog_flags["appearance"].append("Fixed gaze") - do CurrentPatient.dialog_flags["notes"].append("Stare was a bit fixed") - - : The patient appears to be calm and collected. His garment is clean, if a bit crumpled. His gaze switches back and forth between a wide-eyed focus on your face, and vacantly staring at the wall. - - => day_1_choice_1 - -- Don't you feel anxious about this otherwordly being targeting you ? [if CurrentPatient.marker[0] == 5] - - set CurrentPatient.marker[0] = 0 - do CurrentPatient.change_emotion("happy") - do CurrentPatient.dialog_flags["thought_process"].append("Ideo-affective dissociation") - do CurrentPatient.dialog_flags["thought_content"].append("Megalomaniac delusions") - do CurrentPatient.dialog_flags["notes"].append("Thinks God protects him") - - Mr Orleg: Not really. I'm protected by God. No one can hurt me. - - ~ day_1_choice_1_6 - - - How do you know God protects you ? [if CurrentPatient.dialog_tree[1610] == "not_asked"] - - set CurrentPatient.dialog_tree[1610] = "asked" - set CurrentPatient.marker[2] += 1 - do CurrentPatient.dialog_flags["perceptions"].append("Intra-psychic auditory hallucinations") - do CurrentPatient.dialog_flags["notes"].append("Hears God directly inside his head") - - Mr Orleg: He shares his Will with me directly inside my head. We have a special connection. - - if CurrentPatient.marker[2] == 2 - set CurrentPatient.marker[2] = 0 - => day_1_choice_2 - else - => day_1_choice_1_6 - - - Have you ever seen God or this evil being with your own eyes ? [if CurrentPatient.dialog_tree[1620] == "not_asked"] - - set CurrentPatient.dialog_tree[1620] = "asked" - set CurrentPatient.marker[2] += 1 - - Mr Orleg: Never. I don't need to see them to know they're here. - - if CurrentPatient.marker[2] == 2 - set CurrentPatient.marker[2] = 0 - => day_1_choice_2 - else - => day_1_choice_1_6 - -~ day_1_choice_2 - -- Send to Reset - - Psychiatrist: You are in need of psychiatric care, Mr Orleg. My colleagues here will take you to the resetting room for adequate treatment. - - do CurrentPatient.change_emotion("scared") - - Mr Orleg: You can't do that Doctor ! This is a mistake ! - - Mr Orleg: I swear i'm not crazy ! - - do CurrentPatient.send_to_reset() - - => END - -- Send to Isolation - - Psychiatrist: I have assessed that you are currently unstable, Mr Orleg. You may be dangerous to other people, and even to yourself. We are going to Isolate you to ensure your safety. - - do CurrentPatient.change_emotion("scared") - - Mr Orleg: What ?? Th- This can't be... - - Mr Orleg: Please, have mercy... - - do CurrentPatient.send_to_isolation - - => END - -- End the evaluation - - Psychiatrist: Thank you for answering my questions, Mr Orleg. You are to remain here for further observation. - - do CurrentPatient.change_emotion("angry") - - Mr Orleg: I knew it. You are just like the others, faking compassion for profit. You disgust me. - - Psychiatrist: I am here to help you, I just need more time to make the right decision. Please be patient, sir. - - Mr Orleg: This whole thing's a joke... - - => END - -=> END - - - -~ mr_orleg_day_2_dialogue_start - -do CurrentPatient.marker.fill(0) -set CurrentPatient.dialog_tree = CurrentPatient.build_dialog_tree([2100,2110,2120]) - -: The patient is hurriedly mumbling something to himself in hushed tones. He abruptly stops as you step in, watching you with a blank, fixated stare. - -Psychiatrist: Good morning Mr Orleg. Let's pick up where we left off yesterday. - -~ day_2_choice_1 - -- How was your night here ? [if CurrentPatient.dialog_tree[2100] == "not_asked"] - - set CurrentPatient.dialog_tree[2100] = "asked" - do CurrentPatient.marker[0] += 1 - do CurrentPatient.change_emotion("scared") - do CurrentPatient.dialog_flags["sleep"].append("Insomnia") - do CurrentPatient.dialog_flags["perceptions"].append("Intra-psychic auditory hallucinations") - do CurrentPatient.dialog_flags["notes"].append("Heard whispers all night") - - Mr Orleg: I couldn't sleep, Doc. He found me. I could hear Him whispering in my head all night. He's getting closer as we speak ! - - ~ day_2_choice_1_1 - - - Did you take your sleep medication last evening ? [if CurrentPatient.dialog_tree[2110] == "not_asked"] - - set CurrentPatient.dialog_tree[2110] = "asked" - do CurrentPatient.marker[1] += 1 - do CurrentPatient.change_emotion("neutral") - do CurrentPatient.dialog_flags["sleep"].append("Medication taken") - do CurrentPatient.dialog_flags["notes"].append("Took his pills, still couldn't sleep") - - Mr Orleg: Aye, aye, i took all the pills you gave me. Might as well've had some booze, that would've knocked me out better. - - if CurrentPatient.marker[1] == 2 - set CurrentPatient.marker[1] = 0 - => day_2_choice_1 - else - => day_2_choice_1_1 - - - You told me God was watching over you. Doesn't his protection make you feel safe ? [if CurrentPatient.dialog_tree[2120] == "not_asked"] - - set CurrentPatient.dialog_tree[2120] = "asked" - do CurrentPatient.marker[1] += 1 - do CurrentPatient.change_emotion("scared") - do CurrentPatient.dialog_flags["thought_content"].append("Mystical delusions") - do CurrentPatient.dialog_flags["notes"].append("Divine protection is weakening") - - Mr Orleg: I don't know... I haven't felt his divine presence that much lately... Especially last night. - - if CurrentPatient.marker[1] == 2 - set CurrentPatient.marker[1] = 0 - => day_2_choice_1 - else - => day_2_choice_1_1 - - - => day_2_choice_1 - -=> END \ No newline at end of file diff --git a/dialogues/mr_orleg.dialogue.import b/dialogues/mr_orleg.dialogue.import deleted file mode 100644 index 1e6f256..0000000 --- a/dialogues/mr_orleg.dialogue.import +++ /dev/null @@ -1,15 +0,0 @@ -[remap] - -importer="dialogue_manager_compiler_14" -type="Resource" -uid="uid://j4cb2hniy7mk" -path="res://.godot/imported/mr_orleg.dialogue-de858637a612f91e5bc83867aa2a9e9f.tres" - -[deps] - -source_file="res://dialogues/mr_orleg.dialogue" -dest_files=["res://.godot/imported/mr_orleg.dialogue-de858637a612f91e5bc83867aa2a9e9f.tres"] - -[params] - -defaults=true diff --git a/icon.svg b/icon.svg deleted file mode 100644 index 9d8b7fa..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/introduction.gd b/introduction.gd new file mode 100644 index 0000000..d078d4a --- /dev/null +++ b/introduction.gd @@ -0,0 +1,13 @@ +extends CanvasLayer +@onready var text = $RichTextLabel + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + text.add_text("Hello guys") + + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/introduction.tscn b/introduction.tscn new file mode 100644 index 0000000..fece4d2 --- /dev/null +++ b/introduction.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://up2loaxt0lju"] + +[ext_resource type="Script" path="res://introduction.gd" id="1_60qbv"] + +[node name="Introduction" type="CanvasLayer"] +script = ExtResource("1_60qbv") + +[node name="RichTextLabel" type="RichTextLabel" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/mai743.tmp b/mai743.tmp new file mode 100644 index 0000000..ac82bda --- /dev/null +++ b/mai743.tmp @@ -0,0 +1,22 @@ +[gd_scene load_steps=4 format=3 uid="uid://buoev4bra11fe"] + +[ext_resource type="Script" path="res://main_hub.gd" id="1_3hsyh"] +[ext_resource type="PackedScene" uid="uid://dm1uxhosij20s" path="res://patient_room_1.tscn" id="2_fwkkb"] +[ext_resource type="PackedScene" uid="uid://chytk2cctjk3x" path="res://corridor.tscn" id="2_jvc8d"] + +[node name="MainHub" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_3hsyh") + +[node name="PatientRoom1" parent="." instance=ExtResource("2_fwkkb")] +layout_mode = 1 + +[node name="Corridor" parent="." instance=ExtResource("2_jvc8d")] +layout_mode = 1 + +[connection signal="patient_room_1_pressed" from="Corridor" to="." method="_on_corridor_patient_room_1_pressed"] diff --git a/maiCA0A.tmp b/maiCA0A.tmp new file mode 100644 index 0000000..ac82bda --- /dev/null +++ b/maiCA0A.tmp @@ -0,0 +1,22 @@ +[gd_scene load_steps=4 format=3 uid="uid://buoev4bra11fe"] + +[ext_resource type="Script" path="res://main_hub.gd" id="1_3hsyh"] +[ext_resource type="PackedScene" uid="uid://dm1uxhosij20s" path="res://patient_room_1.tscn" id="2_fwkkb"] +[ext_resource type="PackedScene" uid="uid://chytk2cctjk3x" path="res://corridor.tscn" id="2_jvc8d"] + +[node name="MainHub" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_3hsyh") + +[node name="PatientRoom1" parent="." instance=ExtResource("2_fwkkb")] +layout_mode = 1 + +[node name="Corridor" parent="." instance=ExtResource("2_jvc8d")] +layout_mode = 1 + +[connection signal="patient_room_1_pressed" from="Corridor" to="." method="_on_corridor_patient_room_1_pressed"] diff --git a/maiE1B3.tmp b/maiE1B3.tmp new file mode 100644 index 0000000..ac82bda --- /dev/null +++ b/maiE1B3.tmp @@ -0,0 +1,22 @@ +[gd_scene load_steps=4 format=3 uid="uid://buoev4bra11fe"] + +[ext_resource type="Script" path="res://main_hub.gd" id="1_3hsyh"] +[ext_resource type="PackedScene" uid="uid://dm1uxhosij20s" path="res://patient_room_1.tscn" id="2_fwkkb"] +[ext_resource type="PackedScene" uid="uid://chytk2cctjk3x" path="res://corridor.tscn" id="2_jvc8d"] + +[node name="MainHub" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_3hsyh") + +[node name="PatientRoom1" parent="." instance=ExtResource("2_fwkkb")] +layout_mode = 1 + +[node name="Corridor" parent="." instance=ExtResource("2_jvc8d")] +layout_mode = 1 + +[connection signal="patient_room_1_pressed" from="Corridor" to="." method="_on_corridor_patient_room_1_pressed"] diff --git a/maiE7DA.tmp b/maiE7DA.tmp new file mode 100644 index 0000000..ac82bda --- /dev/null +++ b/maiE7DA.tmp @@ -0,0 +1,22 @@ +[gd_scene load_steps=4 format=3 uid="uid://buoev4bra11fe"] + +[ext_resource type="Script" path="res://main_hub.gd" id="1_3hsyh"] +[ext_resource type="PackedScene" uid="uid://dm1uxhosij20s" path="res://patient_room_1.tscn" id="2_fwkkb"] +[ext_resource type="PackedScene" uid="uid://chytk2cctjk3x" path="res://corridor.tscn" id="2_jvc8d"] + +[node name="MainHub" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_3hsyh") + +[node name="PatientRoom1" parent="." instance=ExtResource("2_fwkkb")] +layout_mode = 1 + +[node name="Corridor" parent="." instance=ExtResource("2_jvc8d")] +layout_mode = 1 + +[connection signal="patient_room_1_pressed" from="Corridor" to="." method="_on_corridor_patient_room_1_pressed"] diff --git a/main_hub.gd b/main_hub.gd new file mode 100644 index 0000000..433ee20 --- /dev/null +++ b/main_hub.gd @@ -0,0 +1,48 @@ +extends Control +@export var title_screen: PackedScene +@export var office: PackedScene +@export var bedroom: PackedScene + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + + #Adding Scenes + var title_screen = title_screen.instantiate() + var office = office.instantiate() + var bedroom = bedroom.instantiate() + + add_child(title_screen) + add_child(office) + add_child(bedroom) + + title_screen.name = "TitleScreen" + office.name = "Office" + bedroom.name = "Bedroom" + + title_screen.hide() + office.hide() + bedroom.hide() + + title_screen.show() + + #Connecting Signals + title_screen.connect("new_game_pressed", _on_new_game_pressed) + + + + #get_tree().change_scene_to_file("res://corridor.tscn") + pass + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass + + +func _on_new_game_pressed(): + TransitionScreen.transition() + await TransitionScreen.on_transition_finished + $TitleScreen.hide() + $Office.show() + diff --git a/office.gd b/office.gd new file mode 100644 index 0000000..ba30e8a --- /dev/null +++ b/office.gd @@ -0,0 +1,22 @@ +extends Control + +signal patient_room_1_pressed +signal patient_room_2_pressed + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass + +func _on_patient_room_1_pressed(): + emit_signal("patient_room_1_pressed") + print("Going to Patient Room 1...") + +func _on_patient_room_2_pressed(): + emit_signal("patient_room_2_pressed") + print("Going to Patient Room 2...") diff --git a/patient_room_1.tscn b/patient_room_1.tscn new file mode 100644 index 0000000..501df3f --- /dev/null +++ b/patient_room_1.tscn @@ -0,0 +1,93 @@ +[gd_scene load_steps=4 format=3 uid="uid://beybtmx1t3gy2"] + +[ext_resource type="Texture2D" uid="uid://c3je4gir1qlot" path="res://Art/BlackScreen.png" id="1_8gyxv"] +[ext_resource type="Texture2D" uid="uid://c7vyp0s2ci7qf" path="res://Art/PatientRoom.jpg" id="2_qrcs6"] + +[sub_resource type="GDScript" id="GDScript_ek6ji"] +script/source = "extends Control + +signal leave_pressed + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass + +func _on_leave_pressed(): + emit_signal(\"leave_pressed\") + print(\"Leaving Patient Room 1...\") +" + +[node name="PatientRoom1" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = SubResource("GDScript_ek6ji") + +[node name="Background" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="UpperBorder" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.25 +texture = ExtResource("1_8gyxv") +expand_mode = 1 + +[node name="Background" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +texture = ExtResource("2_qrcs6") +expand_mode = 1 + +[node name="LowerBorder" type="TextureRect" parent="Background"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.25 +texture = ExtResource("1_8gyxv") +expand_mode = 1 + +[node name="Choices" type="GridContainer" parent="."] +layout_mode = 0 +offset_left = 659.0 +offset_top = 788.0 +offset_right = 1288.0 +offset_bottom = 893.0 +columns = 3 + +[node name="Talk" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Talk" + +[node name="Observe" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Observe +" + +[node name="Leave" type="Button" parent="Choices"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 25 +text = "Leave" + +[connection signal="pressed" from="Choices/Leave" to="." method="_on_leave_pressed"] diff --git a/patient_room_2.tscn b/patient_room_2.tscn new file mode 100644 index 0000000..85bfb4c --- /dev/null +++ b/patient_room_2.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=3 format=3 uid="uid://csa7fjp03r83h"] + +[ext_resource type="Texture2D" uid="uid://c1bq2kroefeoy" path="res://Art/PatientRoom2.jpg" id="3_echgr"] + +[sub_resource type="GDScript" id="GDScript_xp8y3"] +script/source = "extends Control + +signal leave_pressed + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass + +func _on_leave_pressed(): + emit_signal(\"leave_pressed\") + print(\"Leaving Patient Room 2...\") +" + +[node name="PatientRoom2"] +script = SubResource("GDScript_xp8y3") + +[node name="Background" parent="Background" index="1"] +texture = ExtResource("3_echgr") diff --git a/project.godot b/project.godot index 654cdd9..44ba6e3 100644 --- a/project.godot +++ b/project.godot @@ -10,39 +10,18 @@ config_version=5 [application] -config/name="dialog_tree" -run/main_scene="res://dialog_main.tscn" +config/name="im_not_sick" +run/main_scene="res://MainHub.tscn" config/features=PackedStringArray("4.3", "Forward Plus") -config/icon="res://icon.svg" [autoload] -DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd" -CurrentPatient="*res://current_patient.gd" +Global="*res://global.gd" +TransitionScreen="*res://transition_screen.tscn" -[dialogue_manager] +[display] -editor/new_file_template="~ this_is_a_node_title -Nathan: [[Hi|Hello|Howdy]], this is some dialogue. -Nathan: Here are some choices. -- First one - Nathan: You picked the first one. -- Second one - Nathan: You picked the second one. -- Start again => this_is_a_node_title -- End the conversation => END -Nathan: For more information see the online documentation. -=> END" - -[editor] - -version_control/plugin_name="GitPlugin" -version_control/autoload_on_startup=true - -[editor_plugins] - -enabled=PackedStringArray("res://addons/dialogue_manager/plugin.cfg") - -[internationalization] - -locale/translations_pot_files=PackedStringArray("res://dialogues/mr_orleg.dialogue") +window/size/viewport_width=1920 +window/size/viewport_height=1080 +window/size/borderless=true +window/stretch/mode="canvas_items" diff --git a/test.txt b/test.txt deleted file mode 100644 index a042389..0000000 --- a/test.txt +++ /dev/null @@ -1 +0,0 @@ -hello world! diff --git a/dialog_main.gd b/title_screen.gd similarity index 51% rename from dialog_main.gd rename to title_screen.gd index 3acc08d..c3d3768 100644 --- a/dialog_main.gd +++ b/title_screen.gd @@ -1,14 +1,18 @@ extends Control +signal new_game_pressed + # Called when the node enters the scene tree for the first time. func _ready() -> void: - - var dialogue_resource = load("res://dialogues/mr_orleg.dialogue") - var dialogue_start = "mr_orleg_day_2_dialogue_start" - - var dialogue = DialogueManager.show_dialogue_balloon(dialogue_resource, dialogue_start) + pass # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: pass + + +func _on_new_game_pressed() -> void: + emit_signal("new_game_pressed") + print("Starting New Game...") + pass # Replace with function body. diff --git a/transition_screen.gd b/transition_screen.gd new file mode 100644 index 0000000..6bf40d5 --- /dev/null +++ b/transition_screen.gd @@ -0,0 +1,26 @@ +extends CanvasLayer + +signal on_transition_finished + +@onready var color_rect = $ColorRect +@onready var animation_player = $AnimationPlayer + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + + color_rect.visible = false + animation_player.connect("animation_finished", _on_animation_finished) + + +func _on_animation_finished(anim_name): + if anim_name == "fade_to_black": + on_transition_finished.emit() + animation_player.play("fade_to_normal") + elif anim_name == "fade_to_normal": + color_rect.visible = false + + +func transition(): + color_rect.visible = true + animation_player.play("fade_to_black") + diff --git a/transition_screen.tscn b/transition_screen.tscn new file mode 100644 index 0000000..14b2d7d --- /dev/null +++ b/transition_screen.tscn @@ -0,0 +1,72 @@ +[gd_scene load_steps=6 format=3 uid="uid://drk1l7ijo4xq5"] + +[ext_resource type="Script" path="res://transition_screen.gd" id="1_hbjtk"] + +[sub_resource type="Animation" id="Animation_20bx3"] +resource_name = "fade_to_black" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("ColorRect:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(0, 0, 0, 0), Color(0, 0, 0, 1)] +} + +[sub_resource type="Animation" id="Animation_n85x3"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("ColorRect:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(0, 0, 0, 0)] +} + +[sub_resource type="Animation" id="Animation_in743"] +resource_name = "fade_to_normal" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("ColorRect:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(0, 0, 0, 1), Color(0, 0, 0, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_5yx4m"] +_data = { +"RESET": SubResource("Animation_n85x3"), +"fade_to_black": SubResource("Animation_20bx3"), +"fade_to_normal": SubResource("Animation_in743") +} + +[node name="transition_screen" type="CanvasLayer"] +script = ExtResource("1_hbjtk") + +[node name="ColorRect" type="ColorRect" parent="."] +modulate = Color(0, 0, 0, 0) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_5yx4m") +}