From ab988eaaacbd0411337a0295abe8f062ca87335f Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 5 Mar 2025 21:40:07 +0100 Subject: [PATCH] plein de trucs dont des dialogues! et ouai! appuye sur e pour parler au npc --- addons/dialogue_manager/DialogueManager.cs | 462 ++++++ .../dialogue_manager/DialogueManager.cs.uid | 1 + addons/dialogue_manager/LICENSE | 21 + addons/dialogue_manager/assets/icon.svg | 52 + .../dialogue_manager/assets/icon.svg.import | 38 + .../assets/responses_menu.svg | 52 + .../assets/responses_menu.svg.import | 38 + addons/dialogue_manager/assets/update.svg | 71 + .../dialogue_manager/assets/update.svg.import | 37 + .../dialogue_manager/compiler/compilation.gd | 1078 +++++++++++++ .../compiler/compilation.gd.uid | 1 + .../compiler/compiled_line.gd | 157 ++ .../compiler/compiled_line.gd.uid | 1 + addons/dialogue_manager/compiler/compiler.gd | 51 + .../dialogue_manager/compiler/compiler.gd.uid | 1 + .../compiler/compiler_regex.gd | 49 + .../compiler/compiler_regex.gd.uid | 1 + .../compiler/compiler_result.gd | 27 + .../compiler/compiler_result.gd.uid | 1 + .../compiler/expression_parser.gd | 497 ++++++ .../compiler/expression_parser.gd.uid | 1 + .../compiler/resolved_goto_data.gd | 68 + .../compiler/resolved_goto_data.gd.uid | 1 + .../compiler/resolved_line_data.gd | 167 ++ .../compiler/resolved_line_data.gd.uid | 1 + .../compiler/resolved_tag_data.gd | 26 + .../compiler/resolved_tag_data.gd.uid | 1 + addons/dialogue_manager/compiler/tree_line.gd | 44 + .../compiler/tree_line.gd.uid | 1 + .../dialogue_manager/components/code_edit.gd | 461 ++++++ .../components/code_edit.gd.uid | 1 + .../components/code_edit.tscn | 56 + .../code_edit_syntax_highlighter.gd | 208 +++ .../code_edit_syntax_highlighter.gd.uid | 1 + .../components/download_update_panel.gd | 84 + .../components/download_update_panel.gd.uid | 1 + .../components/download_update_panel.tscn | 60 + .../editor_property/editor_property.gd | 48 + .../editor_property/editor_property.gd.uid | 1 + .../editor_property_control.gd | 147 ++ .../editor_property_control.gd.uid | 1 + .../editor_property_control.tscn | 58 + .../editor_property/resource_button.gd | 48 + .../editor_property/resource_button.gd.uid | 1 + .../editor_property/resource_button.tscn | 9 + .../components/errors_panel.gd | 85 + .../components/errors_panel.gd.uid | 1 + .../components/errors_panel.tscn | 56 + .../dialogue_manager/components/files_list.gd | 148 ++ .../components/files_list.gd.uid | 1 + .../components/files_list.tscn | 28 + .../components/find_in_files.gd | 229 +++ .../components/find_in_files.gd.uid | 1 + .../components/find_in_files.tscn | 139 ++ .../components/search_and_replace.gd | 212 +++ .../components/search_and_replace.gd.uid | 1 + .../components/search_and_replace.tscn | 87 + .../dialogue_manager/components/title_list.gd | 67 + .../components/title_list.gd.uid | 1 + .../components/title_list.tscn | 27 + .../components/update_button.gd | 125 ++ .../components/update_button.gd.uid | 1 + .../components/update_button.tscn | 42 + addons/dialogue_manager/constants.gd | 218 +++ addons/dialogue_manager/constants.gd.uid | 1 + addons/dialogue_manager/dialogue_label.gd | 232 +++ addons/dialogue_manager/dialogue_label.gd.uid | 1 + addons/dialogue_manager/dialogue_label.tscn | 19 + addons/dialogue_manager/dialogue_line.gd | 99 ++ addons/dialogue_manager/dialogue_line.gd.uid | 1 + addons/dialogue_manager/dialogue_manager.gd | 1426 +++++++++++++++++ .../dialogue_manager/dialogue_manager.gd.uid | 1 + addons/dialogue_manager/dialogue_resource.gd | 42 + .../dialogue_manager/dialogue_resource.gd.uid | 1 + addons/dialogue_manager/dialogue_response.gd | 59 + .../dialogue_manager/dialogue_response.gd.uid | 1 + .../dialogue_responses_menu.gd | 143 ++ .../dialogue_responses_menu.gd.uid | 1 + .../editor_translation_parser_plugin.gd | 61 + .../editor_translation_parser_plugin.gd.uid | 1 + .../example_balloon/ExampleBalloon.cs | 223 +++ .../example_balloon/ExampleBalloon.cs.uid | 1 + .../example_balloon/example_balloon.gd | 176 ++ .../example_balloon/example_balloon.gd.uid | 1 + .../example_balloon/example_balloon.tscn | 149 ++ .../small_example_balloon.tscn | 174 ++ addons/dialogue_manager/import_plugin.gd | 107 ++ addons/dialogue_manager/import_plugin.gd.uid | 1 + addons/dialogue_manager/inspector_plugin.gd | 21 + .../dialogue_manager/inspector_plugin.gd.uid | 1 + addons/dialogue_manager/l10n/en.mo | Bin 0 -> 9770 bytes addons/dialogue_manager/l10n/en.po | 424 +++++ addons/dialogue_manager/l10n/es.po | 378 +++++ addons/dialogue_manager/l10n/translations.pot | 414 +++++ addons/dialogue_manager/l10n/uk.po | 423 +++++ addons/dialogue_manager/l10n/zh.po | 378 +++++ addons/dialogue_manager/l10n/zh_TW.po | 378 +++++ addons/dialogue_manager/plugin.cfg | 7 + addons/dialogue_manager/plugin.cfg.uid | 1 + addons/dialogue_manager/plugin.gd | 414 +++++ addons/dialogue_manager/plugin.gd.uid | 1 + addons/dialogue_manager/settings.gd | 299 ++++ addons/dialogue_manager/settings.gd.uid | 1 + addons/dialogue_manager/test_scene.gd | 43 + addons/dialogue_manager/test_scene.gd.uid | 1 + addons/dialogue_manager/test_scene.tscn | 6 + addons/dialogue_manager/utilities/builtins.gd | 505 ++++++ .../utilities/builtins.gd.uid | 1 + .../utilities/dialogue_cache.gd | 170 ++ .../utilities/dialogue_cache.gd.uid | 1 + addons/dialogue_manager/views/main_view.gd | 1140 +++++++++++++ .../dialogue_manager/views/main_view.gd.uid | 1 + addons/dialogue_manager/views/main_view.tscn | 430 +++++ animations/human/human_state_machine.tres | 4 +- assest/persos/bob.png | Bin 0 -> 227322 bytes assest/persos/bob.png.import | 34 + assest/tilesets/exterieur.tres | 8 +- caracters/bob/bob.dialogue | 11 + caracters/bob/bob.dialogue.import | 15 + caracters/bob/bob.tscn | 94 ++ caracters/bob/bob76A8.tmp | 93 ++ caracters/bob/interactable.gd | 5 + caracters/human.gd | 75 + caracters/npc.gd | 73 + caracters/player/player.tscn | 25 +- caracters/player/player_controler.gd | 74 +- export_presets.cfg | 2 +- icon.svg.import | 2 +- maps/world.tscn | 78 +- project.godot | 13 + scenes/pathFollow.gd | 5 +- scenes/start.tscn | 8 +- 132 files changed, 14528 insertions(+), 50 deletions(-) create mode 100644 addons/dialogue_manager/DialogueManager.cs create mode 100644 addons/dialogue_manager/DialogueManager.cs.uid create mode 100644 addons/dialogue_manager/LICENSE create mode 100644 addons/dialogue_manager/assets/icon.svg create mode 100644 addons/dialogue_manager/assets/icon.svg.import create mode 100644 addons/dialogue_manager/assets/responses_menu.svg create mode 100644 addons/dialogue_manager/assets/responses_menu.svg.import create mode 100644 addons/dialogue_manager/assets/update.svg create mode 100644 addons/dialogue_manager/assets/update.svg.import create mode 100644 addons/dialogue_manager/compiler/compilation.gd create mode 100644 addons/dialogue_manager/compiler/compilation.gd.uid create mode 100644 addons/dialogue_manager/compiler/compiled_line.gd create mode 100644 addons/dialogue_manager/compiler/compiled_line.gd.uid create mode 100644 addons/dialogue_manager/compiler/compiler.gd create mode 100644 addons/dialogue_manager/compiler/compiler.gd.uid create mode 100644 addons/dialogue_manager/compiler/compiler_regex.gd create mode 100644 addons/dialogue_manager/compiler/compiler_regex.gd.uid create mode 100644 addons/dialogue_manager/compiler/compiler_result.gd create mode 100644 addons/dialogue_manager/compiler/compiler_result.gd.uid create mode 100644 addons/dialogue_manager/compiler/expression_parser.gd create mode 100644 addons/dialogue_manager/compiler/expression_parser.gd.uid create mode 100644 addons/dialogue_manager/compiler/resolved_goto_data.gd create mode 100644 addons/dialogue_manager/compiler/resolved_goto_data.gd.uid create mode 100644 addons/dialogue_manager/compiler/resolved_line_data.gd create mode 100644 addons/dialogue_manager/compiler/resolved_line_data.gd.uid create mode 100644 addons/dialogue_manager/compiler/resolved_tag_data.gd create mode 100644 addons/dialogue_manager/compiler/resolved_tag_data.gd.uid create mode 100644 addons/dialogue_manager/compiler/tree_line.gd create mode 100644 addons/dialogue_manager/compiler/tree_line.gd.uid create mode 100644 addons/dialogue_manager/components/code_edit.gd create mode 100644 addons/dialogue_manager/components/code_edit.gd.uid create mode 100644 addons/dialogue_manager/components/code_edit.tscn create mode 100644 addons/dialogue_manager/components/code_edit_syntax_highlighter.gd create mode 100644 addons/dialogue_manager/components/code_edit_syntax_highlighter.gd.uid create mode 100644 addons/dialogue_manager/components/download_update_panel.gd create mode 100644 addons/dialogue_manager/components/download_update_panel.gd.uid create mode 100644 addons/dialogue_manager/components/download_update_panel.tscn create mode 100644 addons/dialogue_manager/components/editor_property/editor_property.gd create mode 100644 addons/dialogue_manager/components/editor_property/editor_property.gd.uid create mode 100644 addons/dialogue_manager/components/editor_property/editor_property_control.gd create mode 100644 addons/dialogue_manager/components/editor_property/editor_property_control.gd.uid create mode 100644 addons/dialogue_manager/components/editor_property/editor_property_control.tscn create mode 100644 addons/dialogue_manager/components/editor_property/resource_button.gd create mode 100644 addons/dialogue_manager/components/editor_property/resource_button.gd.uid create mode 100644 addons/dialogue_manager/components/editor_property/resource_button.tscn create mode 100644 addons/dialogue_manager/components/errors_panel.gd create mode 100644 addons/dialogue_manager/components/errors_panel.gd.uid create mode 100644 addons/dialogue_manager/components/errors_panel.tscn create mode 100644 addons/dialogue_manager/components/files_list.gd create mode 100644 addons/dialogue_manager/components/files_list.gd.uid create mode 100644 addons/dialogue_manager/components/files_list.tscn create mode 100644 addons/dialogue_manager/components/find_in_files.gd create mode 100644 addons/dialogue_manager/components/find_in_files.gd.uid create mode 100644 addons/dialogue_manager/components/find_in_files.tscn create mode 100644 addons/dialogue_manager/components/search_and_replace.gd create mode 100644 addons/dialogue_manager/components/search_and_replace.gd.uid create mode 100644 addons/dialogue_manager/components/search_and_replace.tscn create mode 100644 addons/dialogue_manager/components/title_list.gd create mode 100644 addons/dialogue_manager/components/title_list.gd.uid create mode 100644 addons/dialogue_manager/components/title_list.tscn create mode 100644 addons/dialogue_manager/components/update_button.gd create mode 100644 addons/dialogue_manager/components/update_button.gd.uid create mode 100644 addons/dialogue_manager/components/update_button.tscn create mode 100644 addons/dialogue_manager/constants.gd create mode 100644 addons/dialogue_manager/constants.gd.uid create mode 100644 addons/dialogue_manager/dialogue_label.gd create mode 100644 addons/dialogue_manager/dialogue_label.gd.uid create mode 100644 addons/dialogue_manager/dialogue_label.tscn create mode 100644 addons/dialogue_manager/dialogue_line.gd create mode 100644 addons/dialogue_manager/dialogue_line.gd.uid create mode 100644 addons/dialogue_manager/dialogue_manager.gd create mode 100644 addons/dialogue_manager/dialogue_manager.gd.uid create mode 100644 addons/dialogue_manager/dialogue_resource.gd create mode 100644 addons/dialogue_manager/dialogue_resource.gd.uid create mode 100644 addons/dialogue_manager/dialogue_response.gd create mode 100644 addons/dialogue_manager/dialogue_response.gd.uid create mode 100644 addons/dialogue_manager/dialogue_responses_menu.gd create mode 100644 addons/dialogue_manager/dialogue_responses_menu.gd.uid create mode 100644 addons/dialogue_manager/editor_translation_parser_plugin.gd create mode 100644 addons/dialogue_manager/editor_translation_parser_plugin.gd.uid create mode 100644 addons/dialogue_manager/example_balloon/ExampleBalloon.cs create mode 100644 addons/dialogue_manager/example_balloon/ExampleBalloon.cs.uid create mode 100644 addons/dialogue_manager/example_balloon/example_balloon.gd create mode 100644 addons/dialogue_manager/example_balloon/example_balloon.gd.uid create mode 100644 addons/dialogue_manager/example_balloon/example_balloon.tscn create mode 100644 addons/dialogue_manager/example_balloon/small_example_balloon.tscn create mode 100644 addons/dialogue_manager/import_plugin.gd create mode 100644 addons/dialogue_manager/import_plugin.gd.uid create mode 100644 addons/dialogue_manager/inspector_plugin.gd create mode 100644 addons/dialogue_manager/inspector_plugin.gd.uid create mode 100644 addons/dialogue_manager/l10n/en.mo create mode 100644 addons/dialogue_manager/l10n/en.po create mode 100644 addons/dialogue_manager/l10n/es.po create mode 100644 addons/dialogue_manager/l10n/translations.pot create mode 100644 addons/dialogue_manager/l10n/uk.po create mode 100644 addons/dialogue_manager/l10n/zh.po create mode 100644 addons/dialogue_manager/l10n/zh_TW.po create mode 100644 addons/dialogue_manager/plugin.cfg create mode 100644 addons/dialogue_manager/plugin.cfg.uid create mode 100644 addons/dialogue_manager/plugin.gd create mode 100644 addons/dialogue_manager/plugin.gd.uid create mode 100644 addons/dialogue_manager/settings.gd create mode 100644 addons/dialogue_manager/settings.gd.uid create mode 100644 addons/dialogue_manager/test_scene.gd create mode 100644 addons/dialogue_manager/test_scene.gd.uid create mode 100644 addons/dialogue_manager/test_scene.tscn create mode 100644 addons/dialogue_manager/utilities/builtins.gd create mode 100644 addons/dialogue_manager/utilities/builtins.gd.uid create mode 100644 addons/dialogue_manager/utilities/dialogue_cache.gd create mode 100644 addons/dialogue_manager/utilities/dialogue_cache.gd.uid create mode 100644 addons/dialogue_manager/views/main_view.gd create mode 100644 addons/dialogue_manager/views/main_view.gd.uid create mode 100644 addons/dialogue_manager/views/main_view.tscn create mode 100644 assest/persos/bob.png create mode 100644 assest/persos/bob.png.import create mode 100644 caracters/bob/bob.dialogue create mode 100644 caracters/bob/bob.dialogue.import create mode 100644 caracters/bob/bob.tscn create mode 100644 caracters/bob/bob76A8.tmp create mode 100644 caracters/bob/interactable.gd create mode 100644 caracters/human.gd create mode 100644 caracters/npc.gd diff --git a/addons/dialogue_manager/DialogueManager.cs b/addons/dialogue_manager/DialogueManager.cs new file mode 100644 index 0000000..20351c0 --- /dev/null +++ b/addons/dialogue_manager/DialogueManager.cs @@ -0,0 +1,462 @@ +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"); + instance.Connect("bridge_dialogue_started", Callable.From((Resource dialogueResource) => DialogueStarted?.Invoke(dialogueResource))); + } + 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("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 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, Array args) + { + var methodInfos = thing.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); + foreach (var methodInfo in methodInfos) + { + if (methodInfo.Name == method && args.Count == methodInfo.GetParameters().Length) + { + return true; + } + } + + return false; + } + + + public async void ResolveThingMethod(GodotObject thing, string method, Array args) + { + MethodInfo? info = null; + var methodInfos = thing.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); + foreach (var methodInfo in methodInfos) + { + if (methodInfo.Name == method && args.Count == methodInfo.GetParameters().Length) + { + info = methodInfo; + } + } + + 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 concurrent_lines = new Array(); + public Array ConcurrentLines + { + get => concurrent_lines; + } + + 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 concurrent_line_data in (Array)data.Get("concurrent_lines")) + { + concurrent_lines.Add(new DialogueLine(concurrent_line_data)); + } + + 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/icon.svg.import b/addons/dialogue_manager/assets/icon.svg.import new file mode 100644 index 0000000..3b6fd5e --- /dev/null +++ b/addons/dialogue_manager/assets/icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d3lr2uas6ax8v" +path="res://.godot/imported/icon.svg-17eb5d3e2a3cfbe59852220758c5b7bd.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"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +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/addons/dialogue_manager/assets/responses_menu.svg b/addons/dialogue_manager/assets/responses_menu.svg new file mode 100644 index 0000000..4e4089d --- /dev/null +++ b/addons/dialogue_manager/assets/responses_menu.svg @@ -0,0 +1,52 @@ + + + + + + + + + + diff --git a/addons/dialogue_manager/assets/responses_menu.svg.import b/addons/dialogue_manager/assets/responses_menu.svg.import new file mode 100644 index 0000000..83355fc --- /dev/null +++ b/addons/dialogue_manager/assets/responses_menu.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://drjfciwitjm83" +path="res://.godot/imported/responses_menu.svg-87cf63ca685d53616205049572f4eb8f.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"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +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/addons/dialogue_manager/assets/update.svg b/addons/dialogue_manager/assets/update.svg new file mode 100644 index 0000000..a5b80ee --- /dev/null +++ b/addons/dialogue_manager/assets/update.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + diff --git a/addons/dialogue_manager/assets/update.svg.import b/addons/dialogue_manager/assets/update.svg.import new file mode 100644 index 0000000..2d8171a --- /dev/null +++ b/addons/dialogue_manager/assets/update.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d3baj6rygkb3f" +path="res://.godot/imported/update.svg-f1628866ed4eb2e13e3b81f75443687e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogue_manager/assets/update.svg" +dest_files=["res://.godot/imported/update.svg-f1628866ed4eb2e13e3b81f75443687e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +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/addons/dialogue_manager/compiler/compilation.gd b/addons/dialogue_manager/compiler/compilation.gd new file mode 100644 index 0000000..32b4ffb --- /dev/null +++ b/addons/dialogue_manager/compiler/compilation.gd @@ -0,0 +1,1078 @@ +## 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 = weakref(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 new file mode 100644 index 0000000..24a13ee --- /dev/null +++ b/addons/dialogue_manager/compiler/compilation.gd.uid @@ -0,0 +1 @@ +uid://dsgpnyqg6cprg diff --git a/addons/dialogue_manager/compiler/compiled_line.gd b/addons/dialogue_manager/compiler/compiled_line.gd new file mode 100644 index 0000000..972fd5c --- /dev/null +++ b/addons/dialogue_manager/compiler/compiled_line.gd @@ -0,0 +1,157 @@ +## 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 new file mode 100644 index 0000000..17ec55e --- /dev/null +++ b/addons/dialogue_manager/compiler/compiled_line.gd.uid @@ -0,0 +1 @@ +uid://dg8j5hudp4210 diff --git a/addons/dialogue_manager/compiler/compiler.gd b/addons/dialogue_manager/compiler/compiler.gd new file mode 100644 index 0000000..a370ef6 --- /dev/null +++ b/addons/dialogue_manager/compiler/compiler.gd @@ -0,0 +1,51 @@ +## 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 new file mode 100644 index 0000000..e041f10 --- /dev/null +++ b/addons/dialogue_manager/compiler/compiler.gd.uid @@ -0,0 +1 @@ +uid://chtfdmr0cqtp4 diff --git a/addons/dialogue_manager/compiler/compiler_regex.gd b/addons/dialogue_manager/compiler/compiler_regex.gd new file mode 100644 index 0000000..ead998b --- /dev/null +++ b/addons/dialogue_manager/compiler/compiler_regex.gd @@ -0,0 +1,49 @@ +## 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 new file mode 100644 index 0000000..bd969df --- /dev/null +++ b/addons/dialogue_manager/compiler/compiler_regex.gd.uid @@ -0,0 +1 @@ +uid://d3tvcrnicjibp diff --git a/addons/dialogue_manager/compiler/compiler_result.gd b/addons/dialogue_manager/compiler/compiler_result.gd new file mode 100644 index 0000000..acbf60f --- /dev/null +++ b/addons/dialogue_manager/compiler/compiler_result.gd @@ -0,0 +1,27 @@ +## 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 new file mode 100644 index 0000000..f1f76fd --- /dev/null +++ b/addons/dialogue_manager/compiler/compiler_result.gd.uid @@ -0,0 +1 @@ +uid://dmk74tknimqvg diff --git a/addons/dialogue_manager/compiler/expression_parser.gd b/addons/dialogue_manager/compiler/expression_parser.gd new file mode 100644 index 0000000..384340f --- /dev/null +++ b/addons/dialogue_manager/compiler/expression_parser.gd @@ -0,0 +1,497 @@ +## 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 new file mode 100644 index 0000000..0793701 --- /dev/null +++ b/addons/dialogue_manager/compiler/expression_parser.gd.uid @@ -0,0 +1 @@ +uid://dbi4hbar8ubwu diff --git a/addons/dialogue_manager/compiler/resolved_goto_data.gd b/addons/dialogue_manager/compiler/resolved_goto_data.gd new file mode 100644 index 0000000..16bca6f --- /dev/null +++ b/addons/dialogue_manager/compiler/resolved_goto_data.gd @@ -0,0 +1,68 @@ +## 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 new file mode 100644 index 0000000..cb05e08 --- /dev/null +++ b/addons/dialogue_manager/compiler/resolved_goto_data.gd.uid @@ -0,0 +1 @@ +uid://llhl5pt47eoq diff --git a/addons/dialogue_manager/compiler/resolved_line_data.gd b/addons/dialogue_manager/compiler/resolved_line_data.gd new file mode 100644 index 0000000..1d1a716 --- /dev/null +++ b/addons/dialogue_manager/compiler/resolved_line_data.gd @@ -0,0 +1,167 @@ +## 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 new file mode 100644 index 0000000..bbea7d2 --- /dev/null +++ b/addons/dialogue_manager/compiler/resolved_line_data.gd.uid @@ -0,0 +1 @@ +uid://0k6q8kukq0qa diff --git a/addons/dialogue_manager/compiler/resolved_tag_data.gd b/addons/dialogue_manager/compiler/resolved_tag_data.gd new file mode 100644 index 0000000..e926ada --- /dev/null +++ b/addons/dialogue_manager/compiler/resolved_tag_data.gd @@ -0,0 +1,26 @@ +## 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 new file mode 100644 index 0000000..98c6f51 --- /dev/null +++ b/addons/dialogue_manager/compiler/resolved_tag_data.gd.uid @@ -0,0 +1 @@ +uid://cqai3ikuilqfq diff --git a/addons/dialogue_manager/compiler/tree_line.gd b/addons/dialogue_manager/compiler/tree_line.gd new file mode 100644 index 0000000..f172a8a --- /dev/null +++ b/addons/dialogue_manager/compiler/tree_line.gd @@ -0,0 +1,44 @@ +## 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. +## This is stored as a Weak Reference so that this RefCounted can elegantly free itself. +## Without it being a Weak Reference, this can easily cause a cyclical reference that keeps this resource alive. +var parent: WeakRef +## 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 new file mode 100644 index 0000000..fe1db3a --- /dev/null +++ b/addons/dialogue_manager/compiler/tree_line.gd.uid @@ -0,0 +1 @@ +uid://dsu4i84dpif14 diff --git a/addons/dialogue_manager/components/code_edit.gd b/addons/dialogue_manager/components/code_edit.gd new file mode 100644 index 0000000..e180af4 --- /dev/null +++ b/addons/dialogue_manager/components/code_edit.gd @@ -0,0 +1,461 @@ +@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 new file mode 100644 index 0000000..ab2b9e5 --- /dev/null +++ b/addons/dialogue_manager/components/code_edit.gd.uid @@ -0,0 +1 @@ +uid://djeybvlb332mp diff --git a/addons/dialogue_manager/components/code_edit.tscn b/addons/dialogue_manager/components/code_edit.tscn new file mode 100644 index 0000000..0c25707 --- /dev/null +++ b/addons/dialogue_manager/components/code_edit.tscn @@ -0,0 +1,56 @@ +[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 new file mode 100644 index 0000000..6f73794 --- /dev/null +++ b/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd @@ -0,0 +1,208 @@ +@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(" ", text.find("%")) + colors[index] = { color = theme.text_color.lerp(theme.symbols_color, 0.5) } + + var dialogue_text: String = text.substr(index, text.find("=>")) + + # Highlight character name + var split_index: int = dialogue_text.replace("\\:", "??").find(":") + colors[index + split_index + 1] = { color = theme.text_color } + + # 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[index + bbcode.start] = { color = theme.symbols_color } + colors[index + bbcode.start + 2] = { color = theme.text_color } + var pipe_cursor: int = code.find("|") + while pipe_cursor > -1: + colors[index + bbcode.start + pipe_cursor + 1] = { color = theme.symbols_color } + colors[index + bbcode.start + pipe_cursor + 2] = { color = theme.text_color } + pipe_cursor = code.find("|", pipe_cursor + 1) + colors[index + bbcode.end - 1] = { color = theme.symbols_color } + colors[index + bbcode.end + 1] = { color = theme.text_color } + else: + colors[index + 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[index + bbcode.start + 1] = { color = theme.conditions_color } + else: + colors[index + 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[index + 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[index + bbcode.start + 1] = { color = theme.conditions_color } + colors[index + bbcode.end] = { color = theme.symbols_color } + colors[index + 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 new file mode 100644 index 0000000..9bad8cc --- /dev/null +++ b/addons/dialogue_manager/components/code_edit_syntax_highlighter.gd.uid @@ -0,0 +1 @@ +uid://klpiq4tk3t7a diff --git a/addons/dialogue_manager/components/download_update_panel.gd b/addons/dialogue_manager/components/download_update_panel.gd new file mode 100644 index 0000000..e67a93f --- /dev/null +++ b/addons/dialogue_manager/components/download_update_panel.gd @@ -0,0 +1,84 @@ +@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://tests/test_basic_dialogue.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 new file mode 100644 index 0000000..7910ab4 --- /dev/null +++ b/addons/dialogue_manager/components/download_update_panel.gd.uid @@ -0,0 +1 @@ +uid://kpwo418lb2t2 diff --git a/addons/dialogue_manager/components/download_update_panel.tscn b/addons/dialogue_manager/components/download_update_panel.tscn new file mode 100644 index 0000000..540abd3 --- /dev/null +++ b/addons/dialogue_manager/components/download_update_panel.tscn @@ -0,0 +1,60 @@ +[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 new file mode 100644 index 0000000..5deef65 --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/editor_property.gd @@ -0,0 +1,48 @@ +@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 new file mode 100644 index 0000000..283cc43 --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/editor_property.gd.uid @@ -0,0 +1 @@ +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 new file mode 100644 index 0000000..d063d0e --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/editor_property_control.gd @@ -0,0 +1,147 @@ +@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 new file mode 100644 index 0000000..aab7d8d --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/editor_property_control.gd.uid @@ -0,0 +1 @@ +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 new file mode 100644 index 0000000..7cb02e8 --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/editor_property_control.tscn @@ -0,0 +1,58 @@ +[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 new file mode 100644 index 0000000..5ba33dc --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/resource_button.gd @@ -0,0 +1,48 @@ +@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 new file mode 100644 index 0000000..b1b9d26 --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/resource_button.gd.uid @@ -0,0 +1 @@ +uid://damhqta55t67c diff --git a/addons/dialogue_manager/components/editor_property/resource_button.tscn b/addons/dialogue_manager/components/editor_property/resource_button.tscn new file mode 100644 index 0000000..691e527 --- /dev/null +++ b/addons/dialogue_manager/components/editor_property/resource_button.tscn @@ -0,0 +1,9 @@ +[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 new file mode 100644 index 0000000..0b72d37 --- /dev/null +++ b/addons/dialogue_manager/components/errors_panel.gd @@ -0,0 +1,85 @@ +@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 new file mode 100644 index 0000000..c305a80 --- /dev/null +++ b/addons/dialogue_manager/components/errors_panel.gd.uid @@ -0,0 +1 @@ +uid://d2l8nlb6hhrfp diff --git a/addons/dialogue_manager/components/errors_panel.tscn b/addons/dialogue_manager/components/errors_panel.tscn new file mode 100644 index 0000000..0b653cc --- /dev/null +++ b/addons/dialogue_manager/components/errors_panel.tscn @@ -0,0 +1,56 @@ +[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 new file mode 100644 index 0000000..21a4415 --- /dev/null +++ b/addons/dialogue_manager/components/files_list.gd @@ -0,0 +1,148 @@ +@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 new file mode 100644 index 0000000..2a1089a --- /dev/null +++ b/addons/dialogue_manager/components/files_list.gd.uid @@ -0,0 +1 @@ +uid://dqa4a4wwoo0aa diff --git a/addons/dialogue_manager/components/files_list.tscn b/addons/dialogue_manager/components/files_list.tscn new file mode 100644 index 0000000..e135e60 --- /dev/null +++ b/addons/dialogue_manager/components/files_list.tscn @@ -0,0 +1,28 @@ +[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 new file mode 100644 index 0000000..2614eca --- /dev/null +++ b/addons/dialogue_manager/components/find_in_files.gd @@ -0,0 +1,229 @@ +@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 new file mode 100644 index 0000000..380a491 --- /dev/null +++ b/addons/dialogue_manager/components/find_in_files.gd.uid @@ -0,0 +1 @@ +uid://q368fmxxa8sd diff --git a/addons/dialogue_manager/components/find_in_files.tscn b/addons/dialogue_manager/components/find_in_files.tscn new file mode 100644 index 0000000..97fca24 --- /dev/null +++ b/addons/dialogue_manager/components/find_in_files.tscn @@ -0,0 +1,139 @@ +[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 new file mode 100644 index 0000000..e91574e --- /dev/null +++ b/addons/dialogue_manager/components/search_and_replace.gd @@ -0,0 +1,212 @@ +@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 new file mode 100644 index 0000000..66ec826 --- /dev/null +++ b/addons/dialogue_manager/components/search_and_replace.gd.uid @@ -0,0 +1 @@ +uid://cijsmjkq21cdq diff --git a/addons/dialogue_manager/components/search_and_replace.tscn b/addons/dialogue_manager/components/search_and_replace.tscn new file mode 100644 index 0000000..52721c4 --- /dev/null +++ b/addons/dialogue_manager/components/search_and_replace.tscn @@ -0,0 +1,87 @@ +[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 new file mode 100644 index 0000000..ee7cd13 --- /dev/null +++ b/addons/dialogue_manager/components/title_list.gd @@ -0,0 +1,67 @@ +@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 new file mode 100644 index 0000000..325ca61 --- /dev/null +++ b/addons/dialogue_manager/components/title_list.gd.uid @@ -0,0 +1 @@ +uid://d0k2wndjj0ifm diff --git a/addons/dialogue_manager/components/title_list.tscn b/addons/dialogue_manager/components/title_list.tscn new file mode 100644 index 0000000..ac2b983 --- /dev/null +++ b/addons/dialogue_manager/components/title_list.tscn @@ -0,0 +1,27 @@ +[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 new file mode 100644 index 0000000..cf3f1b9 --- /dev/null +++ b/addons/dialogue_manager/components/update_button.gd @@ -0,0 +1,125 @@ +@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 new file mode 100644 index 0000000..9981132 --- /dev/null +++ b/addons/dialogue_manager/components/update_button.gd.uid @@ -0,0 +1 @@ +uid://cr1tt12dh5ecr diff --git a/addons/dialogue_manager/components/update_button.tscn b/addons/dialogue_manager/components/update_button.tscn new file mode 100644 index 0000000..6cff347 --- /dev/null +++ b/addons/dialogue_manager/components/update_button.tscn @@ -0,0 +1,42 @@ +[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 new file mode 100644 index 0000000..ea2844e --- /dev/null +++ b/addons/dialogue_manager/constants.gd @@ -0,0 +1,218 @@ +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 new file mode 100644 index 0000000..f431917 --- /dev/null +++ b/addons/dialogue_manager/constants.gd.uid @@ -0,0 +1 @@ +uid://b1oarbmjtyesf diff --git a/addons/dialogue_manager/dialogue_label.gd b/addons/dialogue_manager/dialogue_label.gd new file mode 100644 index 0000000..da07b45 --- /dev/null +++ b/addons/dialogue_manager/dialogue_label.gd @@ -0,0 +1,232 @@ +@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 new file mode 100644 index 0000000..6bf86b1 --- /dev/null +++ b/addons/dialogue_manager/dialogue_label.gd.uid @@ -0,0 +1 @@ +uid://g32um0mltv5d diff --git a/addons/dialogue_manager/dialogue_label.tscn b/addons/dialogue_manager/dialogue_label.tscn new file mode 100644 index 0000000..0095933 --- /dev/null +++ b/addons/dialogue_manager/dialogue_label.tscn @@ -0,0 +1,19 @@ +[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 new file mode 100644 index 0000000..7213854 --- /dev/null +++ b/addons/dialogue_manager/dialogue_line.gd @@ -0,0 +1,99 @@ +## 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 new file mode 100644 index 0000000..7ec7029 --- /dev/null +++ b/addons/dialogue_manager/dialogue_line.gd.uid @@ -0,0 +1 @@ +uid://rhuq0eyf8ar2 diff --git a/addons/dialogue_manager/dialogue_manager.gd b/addons/dialogue_manager/dialogue_manager.gd new file mode 100644 index 0000000..ab9a17e --- /dev/null +++ b/addons/dialogue_manager/dialogue_manager.gd @@ -0,0 +1,1426 @@ +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 internally +signal bridge_dialogue_started(resource: DialogueResource) + +## 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) + bridge_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, args) + + 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 new file mode 100644 index 0000000..d10762e --- /dev/null +++ b/addons/dialogue_manager/dialogue_manager.gd.uid @@ -0,0 +1 @@ +uid://c3rodes2l3gxb diff --git a/addons/dialogue_manager/dialogue_resource.gd b/addons/dialogue_manager/dialogue_resource.gd new file mode 100644 index 0000000..29ade7b --- /dev/null +++ b/addons/dialogue_manager/dialogue_resource.gd @@ -0,0 +1,42 @@ +@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 new file mode 100644 index 0000000..27b95d0 --- /dev/null +++ b/addons/dialogue_manager/dialogue_resource.gd.uid @@ -0,0 +1 @@ +uid://dbs4435dsf3ry diff --git a/addons/dialogue_manager/dialogue_response.gd b/addons/dialogue_manager/dialogue_response.gd new file mode 100644 index 0000000..701ce92 --- /dev/null +++ b/addons/dialogue_manager/dialogue_response.gd @@ -0,0 +1,59 @@ +## 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 new file mode 100644 index 0000000..9b4532a --- /dev/null +++ b/addons/dialogue_manager/dialogue_response.gd.uid @@ -0,0 +1 @@ +uid://cm0xpfeywpqid diff --git a/addons/dialogue_manager/dialogue_responses_menu.gd b/addons/dialogue_manager/dialogue_responses_menu.gd new file mode 100644 index 0000000..cd66ae5 --- /dev/null +++ b/addons/dialogue_manager/dialogue_responses_menu.gd @@ -0,0 +1,143 @@ +@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 = false + +## 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 new file mode 100644 index 0000000..0ae73d9 --- /dev/null +++ b/addons/dialogue_manager/dialogue_responses_menu.gd.uid @@ -0,0 +1 @@ +uid://bb52rsfwhkxbn diff --git a/addons/dialogue_manager/editor_translation_parser_plugin.gd b/addons/dialogue_manager/editor_translation_parser_plugin.gd new file mode 100644 index 0000000..801d3a5 --- /dev/null +++ b/addons/dialogue_manager/editor_translation_parser_plugin.gd @@ -0,0 +1,61 @@ +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 new file mode 100644 index 0000000..22ddbe9 --- /dev/null +++ b/addons/dialogue_manager/editor_translation_parser_plugin.gd.uid @@ -0,0 +1 @@ +uid://c6bya881h1egb diff --git a/addons/dialogue_manager/example_balloon/ExampleBalloon.cs b/addons/dialogue_manager/example_balloon/ExampleBalloon.cs new file mode 100644 index 0000000..980f067 --- /dev/null +++ b/addons/dialogue_manager/example_balloon/ExampleBalloon.cs @@ -0,0 +1,223 @@ +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 new file mode 100644 index 0000000..4b3783a --- /dev/null +++ b/addons/dialogue_manager/example_balloon/ExampleBalloon.cs.uid @@ -0,0 +1 @@ +uid://5b3w40kwakl3 diff --git a/addons/dialogue_manager/example_balloon/example_balloon.gd b/addons/dialogue_manager/example_balloon/example_balloon.gd new file mode 100644 index 0000000..c7e6d9a --- /dev/null +++ b/addons/dialogue_manager/example_balloon/example_balloon.gd @@ -0,0 +1,176 @@ +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 new file mode 100644 index 0000000..6327f9b --- /dev/null +++ b/addons/dialogue_manager/example_balloon/example_balloon.gd.uid @@ -0,0 +1 @@ +uid://d1wt4ma6055l8 diff --git a/addons/dialogue_manager/example_balloon/example_balloon.tscn b/addons/dialogue_manager/example_balloon/example_balloon.tscn new file mode 100644 index 0000000..91d8a7d --- /dev/null +++ b/addons/dialogue_manager/example_balloon/example_balloon.tscn @@ -0,0 +1,149 @@ +[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 new file mode 100644 index 0000000..c4d2145 --- /dev/null +++ b/addons/dialogue_manager/example_balloon/small_example_balloon.tscn @@ -0,0 +1,174 @@ +[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 new file mode 100644 index 0000000..345fe84 --- /dev/null +++ b/addons/dialogue_manager/import_plugin.gd @@ -0,0 +1,107 @@ +@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 new file mode 100644 index 0000000..e98bfab --- /dev/null +++ b/addons/dialogue_manager/import_plugin.gd.uid @@ -0,0 +1 @@ +uid://dhwpj6ed8soyq diff --git a/addons/dialogue_manager/inspector_plugin.gd b/addons/dialogue_manager/inspector_plugin.gd new file mode 100644 index 0000000..366c1f3 --- /dev/null +++ b/addons/dialogue_manager/inspector_plugin.gd @@ -0,0 +1,21 @@ +@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 new file mode 100644 index 0000000..00c8db8 --- /dev/null +++ b/addons/dialogue_manager/inspector_plugin.gd.uid @@ -0,0 +1 @@ +uid://0x31sbqbikov diff --git a/addons/dialogue_manager/l10n/en.mo b/addons/dialogue_manager/l10n/en.mo new file mode 100644 index 0000000000000000000000000000000000000000..2ab4fdfdac7d7f52d3d107052e417714a09b6bdc GIT binary patch literal 9770 zcmb7|e~=|tRmU654>HjZFn|zEZYJRF#>`8yQCViQEZLdeWZ3Kvc4m`+N~h;_&rE0D z>wa|io7u@YBoHBGiKz+{1qJ^S!Lm$QT1xy;VkL@2Sx~i>f5Z~8fI_eo3$c`xTG7w< z{&=t7>_B1HJMY`~++XLOd(OG{^jBVe*+(OuC!n8%mM)8;-v>YR3jTN=x;lz}6imQt z!2Zi;+C91=ar+Q2kDTny>3|2fT^$$3TtqhaNA2>i18eL9Oq3Q1ZP1PJ`bB?*gaTv|GS?K=rRc?ccA0Ocgx?N-uxu%U=f%Q~n=N?GI5Y zJ)ZH{1^Gpr{7Ek#@%S_-eSQ{{++PGG-`|6h@4rFm=~|3i<(om35Bc&*Q2ibPwT=OJ z7x*Z61pE_F`us0Y^Ipl|iodT1<-czMrT4diYIiRvJ3jBr?*?Tb>%P1N%3nSRs@;b@ z|C6Bh{WGBK;CDU$Sx|cT3sC+37L@$o@c1Gq`L8AzOOES7wYwR-4ZIDMK4Y*0Zh$Ai zr$O1nzkmn98(7qx;5$I^KL8#AKMOKN^c|3<(KW{DWya$=DEr ztiq$9^m_r+I8T7ukKX`g&wmWcUOx{?FMkDUJ%0mgJzoZ;$8UN5e}Y=~OQ70cgYl{T z>%gA@Zv=l1JPH0h_&#ty_^ijTf{#<4!WnJ{p9D4k=RmeP`g8Ck_*L*>a0cVmd>{At z8BlioEU36~5tMvi^Y|T5_V5y@bzV&{Qhx(V54VGo^C-wKTIcW0;8URX_Y0t1H>mdC z^7uUvk)kVpI*MKf>XCg&X6@qzNRQPC_?QYjk2|n3XrJ`F4SGAI`91)xK@1;lLbApC zAnEWukRAi=KpT+OJb-FQ&wC;HLj|os;gJr{Ko3CggMJA*0O|Q4^eEI=ocY9=ONj)9_^L%xC+TW2^02_uQbp+ z^n~SH`ga%>ValFEggYB2kFLpih$+JfYl=b&GPM$htaWhJXR z-CkK|QM(W)z1}3FY}T?WUdek|J*p@NTb2_Kl4dP%>ttgzmfquJ)S+H0H{-l;qioep z){nYnv65H)*oiPv>ZEzyO{#RVO~;O(mfbqas;aE&PSUHgB;AVh{-CUyEDd}HOY}J}f#Ko|`oK<_*)lHQbtD)}UVSf-eC7bHzn^47iwsol{dU=6K6lvUNy~*lG;(pHZ zi&bj~!sROIu4kjo3f1GSDl!Zxom9RPfA8%c7AQIEs!zgHt3|(M3$4oKvg~EaNL}rk zNs(GLvfe#}Znr3V<1Lur_xnlE<^4YYlCgBW`)GcU}8^sT1Vc+R;rR{Qg1|gQD;@-p_ABk)5>x?$q=JW(v76( zW`URHNv~YRhkP3;bT?1-9_lNsn7JgtXk5>0vJ0b`@#{g!tf5=ImaJ#-@^ZIKM?50o zpvP)P<2XO^f;3wL8P^~gjz{sldXQny-L<5^lYH|{1zkUWA(JQ~_OKwzXGs-TSr@za zf!y+mjF&Q1W4pzPZ5vg?qRIPNC#kB0SgYU>m%~QmFDug3qZT&E#c9?gd2f=rt4$r( zS>_v#t3oJgg~TxTWIzerUbfN@;@Pb_e~r zHLP{PaYa2GxYI6n8{%Yd^&B@yb8GuawMER;>i2B*o+0*E6*?|`D|dMJdfrxS5lbTo z(y&(4mz!C3c*nrHYn^`5bl2DithII=LT?k^Ip{X>a#)9Qm0|j5(Kq!)djZ+SQa}jJ znsRlOgLbmBJqNAt)mhV^R88VutjG7tBpq|wYr$`89FWzwx^#zigM;Ii4WWsEGw#cF zmQ@yqQ>o^N&D;sx>d*P!8>U&jf+JvsArQ1$g#H-z_ur)T$k`XW@>&yO*iXtMl}tG`($0YzBivB@i4cL(7GOW zyIh2#vyMHpkj~tXcVURLX5Jr2=2TK}Q>v!Uo|!p^^Qd-a9@Wg6sr!<;nOX8$D=W(> zR);j7S;~@rt2}GcZ0gj)spI1b?&#b;HRmqHGfP_o#*_2hhCk-TVI%**0*;(tIx%x- z+)i_mG|U|5_L`#WS#xMPZ>G-nhE>v=IZ;;qde#&JTda>9JZ#+Sk^P0a>4>@Gz~QL} zXYO&Qx8yxrX57-vot=Z7gHzF*%|z_KQ3~STZ0=#~B_Nt3>9DWHTK%H34gHy3|AI(a7)q{ooGHJB7P=QBpkGK&8n>3 zF)`L-jN+ha){-VPR7z4Cckr3z%-FP~;}*e8#-3(jz=T0v9wI$)_SB;VTD^9n1(%m# zU}k&>ccSB?97RI;V_L24)xXu0`6^@6k#TJ+5t(FOK(1Z#uOgRSV`AK`wB5v%MQ=;n z!1V$}_6D5%!+uufU2CxJa6-YU?^|o#$g7*1( zGi_R$JFZOApzv+&*v_qHyIb~#{bFa7v|dw z({${eOKa4L>D+#k&mfFlGd9*C9Bk3hS+v_gt;I|qd8;{odVabSogbw-ctuEcCfGj7 zndG_jW`cLgT3SP+g0h%wU6Rb~;`lUXf=dE3QRb#?%9WM{P1J^@Wr7(Dqa{zcB~7$* zDar&ZBqw6pJjB#nSQCaA%H9~>Sq{ybLd)1)wz%;bm!@=xF&9vO*0_qCU=pZ{=pRd=> z7L~=AW5?!{#Vn1|mnHjkgtJ5*<`NcPu$hWC+UNmk)py>uxx|hPV7n=sq_;!V=h|po zQsQ-Rx|}SZQAWc`h1kB%23Ac{Vn*W)8;$9b6TR7sGTv=*2RFG-5!`un+mE$#cH41s zlAfKgV}u*KLqTWuuyh;!!#6jJS!T`*n*q`(Ou96QAT_-`j-s}Hg$8o-gt?io8-KTmz?|b2T97|mA8c&3j<+`RlyX2!iX8B? zlE1Sd>pSPuJ&*R57v6Q~&qkreqRkzxm)O{|7;-wonZ2QNAt4_bANyW1JfM|E_%+Pg zTn?A5(XDbhv%e1u$KsQ4YK|dj2#ia#SyYn)(c*eOFeT~K=-9Tl>Ml1SF6@`160A7< z7%EE-5QuEz2h)A}soizPWh3J&WF0MW!E8ojx20A_$l>khh!$4tNuj&jP+uCY^Kj+4 zq>DX<2uJpYqkB(QD0NDafc;#{dILMq3PIQUgWoq@HgpszkjWU5l!Xv|5JRWJR>pyS zI@=sOpd3}pVo;mqxfO=g)P~0BY(lGmG2)!BnKBYBZHdBbJUeX+?| zih`mGQf{~Swk2&?J(hMtb#OOxE21IQ@Y(PKB*8{Wus<;u-N%_r61a$t=z?o4s;r3ZIdzW!G&gBPjifm5Qy&z`UDTz_?y|#INDlFyZbqZg&uN2w lCarTVtC5u*F#70OVIy|S{K&hD!7oD^?9RqbGge6_`aihw)SUnT literal 0 HcmV?d00001 diff --git a/addons/dialogue_manager/l10n/en.po b/addons/dialogue_manager/l10n/en.po new file mode 100644 index 0000000..bd9a795 --- /dev/null +++ b/addons/dialogue_manager/l10n/en.po @@ -0,0 +1,424 @@ +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 new file mode 100644 index 0000000..ef604e1 --- /dev/null +++ b/addons/dialogue_manager/l10n/es.po @@ -0,0 +1,378 @@ +# +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 new file mode 100644 index 0000000..795b472 --- /dev/null +++ b/addons/dialogue_manager/l10n/translations.pot @@ -0,0 +1,414 @@ +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 new file mode 100644 index 0000000..8cd41ac --- /dev/null +++ b/addons/dialogue_manager/l10n/uk.po @@ -0,0 +1,423 @@ +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.concurrent_line_without_origin" +msgstr "Паралельні рядки потребують початкового рядка, який не починається з «|»." + +msgid "errors.goto_not_allowed_on_concurrect_lines" +msgstr "У паралельних діалогових рядках не допускаються Goto посилання." + +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»." + +msgid "runtime.top_level_states_share_name" +msgstr "Кілька станів верхнього рівня ({states}) мають спільну назву методу/властивості/сигналу «{key}». Для діалогу доступний лише перший випадок." + +msgid "translation_plugin.character_name" +msgstr "Ім’я персонажа" diff --git a/addons/dialogue_manager/l10n/zh.po b/addons/dialogue_manager/l10n/zh.po new file mode 100644 index 0000000..bafd1d5 --- /dev/null +++ b/addons/dialogue_manager/l10n/zh.po @@ -0,0 +1,378 @@ +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 new file mode 100644 index 0000000..e20feee --- /dev/null +++ b/addons/dialogue_manager/l10n/zh_TW.po @@ -0,0 +1,378 @@ +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 new file mode 100644 index 0000000..4b58dac --- /dev/null +++ b/addons/dialogue_manager/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Dialogue Manager" +description="A powerful nonlinear dialogue system" +author="Nathan Hoad" +version="3.3.3" +script="plugin.gd" diff --git a/addons/dialogue_manager/plugin.cfg.uid b/addons/dialogue_manager/plugin.cfg.uid new file mode 100644 index 0000000..312d0cf --- /dev/null +++ b/addons/dialogue_manager/plugin.cfg.uid @@ -0,0 +1 @@ +uid://hrny2utekhei diff --git a/addons/dialogue_manager/plugin.gd b/addons/dialogue_manager/plugin.gd new file mode 100644 index 0000000..992ea3d --- /dev/null +++ b/addons/dialogue_manager/plugin.gd @@ -0,0 +1,414 @@ +@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 swap the script on the example balloon depending on if dotnet is being used. + if not FileAccess.file_exists("res://tests/test_basic_dialogue.gd"): + var plugin_path: String = get_plugin_path() + var balloon_file_names: PackedStringArray = ["example_balloon.tscn", "small_example_balloon.tscn"] + for balloon_file_name: String in balloon_file_names: + var balloon_path: String = plugin_path + "/example_balloon/" + balloon_file_name + var balloon_content: String = FileAccess.get_file_as_string(balloon_path) + if "example_balloon.gd" in balloon_content and DMSettings.check_for_dotnet_solution(): + balloon_content = balloon_content \ + # Replace script path with the C# one + .replace("example_balloon.gd", "ExampleBalloon.cs") \ + # Replace script UID with the C# one + .replace(ResourceUID.id_to_text(ResourceLoader.get_resource_uid(plugin_path + "/example_balloon/example_balloon.gd")), ResourceUID.id_to_text(ResourceLoader.get_resource_uid(plugin_path + "/example_balloon/ExampleBalloon.cs"))) + var balloon_file: FileAccess = FileAccess.open(balloon_path, FileAccess.WRITE) + balloon_file.store_string(balloon_content) + balloon_file.close() + elif "ExampleBalloon.cs" in balloon_content and not DMSettings.check_for_dotnet_solution(): + balloon_content = balloon_content \ + # Replace script path with the GDScript one + .replace("ExampleBalloon.cs", "example_balloon.gd") \ + # Replace script UID with the GDScript one + .replace(ResourceUID.id_to_text(ResourceLoader.get_resource_uid(plugin_path + "/example_balloon/ExampleBalloon.cs")), ResourceUID.id_to_text(ResourceLoader.get_resource_uid(plugin_path + "/example_balloon/example_balloon.gd"))) + var balloon_file: FileAccess = FileAccess.open(balloon_path, FileAccess.WRITE) + balloon_file.store_string(balloon_content) + balloon_file.close() + + # 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) + if "example_balloon.gd" in file_contents: + file_contents = file_contents.replace(plugin_path + "/example_balloon/example_balloon.gd", balloon_script_path) + else: + file_contents = file_contents.replace(plugin_path + "/example_balloon/ExampleBalloon.cs", 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 new file mode 100644 index 0000000..40573b0 --- /dev/null +++ b/addons/dialogue_manager/plugin.gd.uid @@ -0,0 +1 @@ +uid://bpv426rpvrafa diff --git a/addons/dialogue_manager/settings.gd b/addons/dialogue_manager/settings.gd new file mode 100644 index 0000000..0a0c12f --- /dev/null +++ b/addons/dialogue_manager/settings.gd @@ -0,0 +1,299 @@ +@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 new file mode 100644 index 0000000..c93da98 --- /dev/null +++ b/addons/dialogue_manager/settings.gd.uid @@ -0,0 +1 @@ +uid://ce1nk88365m52 diff --git a/addons/dialogue_manager/test_scene.gd b/addons/dialogue_manager/test_scene.gd new file mode 100644 index 0000000..20fe115 --- /dev/null +++ b/addons/dialogue_manager/test_scene.gd @@ -0,0 +1,43 @@ +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 new file mode 100644 index 0000000..1bee7a1 --- /dev/null +++ b/addons/dialogue_manager/test_scene.gd.uid @@ -0,0 +1 @@ +uid://c8e16qdgu40wo diff --git a/addons/dialogue_manager/test_scene.tscn b/addons/dialogue_manager/test_scene.tscn new file mode 100644 index 0000000..f0786ba --- /dev/null +++ b/addons/dialogue_manager/test_scene.tscn @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 0000000..1f8f8ba --- /dev/null +++ b/addons/dialogue_manager/utilities/builtins.gd @@ -0,0 +1,505 @@ +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 new file mode 100644 index 0000000..af8698c --- /dev/null +++ b/addons/dialogue_manager/utilities/builtins.gd.uid @@ -0,0 +1 @@ +uid://bnfhuubdv5k20 diff --git a/addons/dialogue_manager/utilities/dialogue_cache.gd b/addons/dialogue_manager/utilities/dialogue_cache.gd new file mode 100644 index 0000000..dd1da44 --- /dev/null +++ b/addons/dialogue_manager/utilities/dialogue_cache.gd @@ -0,0 +1,170 @@ +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 new file mode 100644 index 0000000..e572006 --- /dev/null +++ b/addons/dialogue_manager/utilities/dialogue_cache.gd.uid @@ -0,0 +1 @@ +uid://d3c83yd6bjp43 diff --git a/addons/dialogue_manager/views/main_view.gd b/addons/dialogue_manager/views/main_view.gd new file mode 100644 index 0000000..2330081 --- /dev/null +++ b/addons/dialogue_manager/views/main_view.gd @@ -0,0 +1,1140 @@ +@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 new file mode 100644 index 0000000..10e66f4 --- /dev/null +++ b/addons/dialogue_manager/views/main_view.gd.uid @@ -0,0 +1 @@ +uid://cipjcc7bkh1pc diff --git a/addons/dialogue_manager/views/main_view.tscn b/addons/dialogue_manager/views/main_view.tscn new file mode 100644 index 0000000..4e70b26 --- /dev/null +++ b/addons/dialogue_manager/views/main_view.tscn @@ -0,0 +1,430 @@ +[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/animations/human/human_state_machine.tres b/animations/human/human_state_machine.tres index 7d3c0df..318ee32 100644 --- a/animations/human/human_state_machine.tres +++ b/animations/human/human_state_machine.tres @@ -80,10 +80,10 @@ advance_mode = 2 states/End/position = Vector2(946, 81.8025) states/Start/position = Vector2(161.333, 82) states/grabing/node = SubResource("AnimationNodeBlendSpace2D_8okss") -states/grabing/position = Vector2(629, 81.037) +states/grabing/position = Vector2(667.667, 81.7696) states/idling/node = SubResource("AnimationNodeBlendSpace2D_epue7") states/idling/position = Vector2(383.148, 81.5555) states/walking/node = ExtResource("1_wk1fq") states/walking/position = Vector2(382.556, -106.667) transitions = ["Start", "walking", SubResource("AnimationNodeStateMachineTransition_qfvli"), "walking", "idling", SubResource("AnimationNodeStateMachineTransition_8rhh4"), "idling", "walking", SubResource("AnimationNodeStateMachineTransition_b5dux"), "Start", "idling", SubResource("AnimationNodeStateMachineTransition_8q1xr"), "idling", "grabing", SubResource("AnimationNodeStateMachineTransition_on1es"), "walking", "grabing", SubResource("AnimationNodeStateMachineTransition_pxk2l"), "grabing", "idling", SubResource("AnimationNodeStateMachineTransition_t6jft")] -graph_offset = Vector2(-54, -204) +graph_offset = Vector2(1, -190) diff --git a/assest/persos/bob.png b/assest/persos/bob.png new file mode 100644 index 0000000000000000000000000000000000000000..6926a82e3d38c9e8e4afc63f31c4962d6276d001 GIT binary patch literal 227322 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®(*ohEiB{CaMoZ(|i-#WhRW! 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 EA4Ju5$N&HU literal 0 HcmV?d00001 diff --git a/assest/persos/bob.png.import b/assest/persos/bob.png.import new file mode 100644 index 0000000..e421607 --- /dev/null +++ b/assest/persos/bob.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c3lal83o1trpd" +path="res://.godot/imported/bob.png-063efc4db86776344372dad1029fd7fa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assest/persos/bob.png" +dest_files=["res://.godot/imported/bob.png-063efc4db86776344372dad1029fd7fa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assest/tilesets/exterieur.tres b/assest/tilesets/exterieur.tres index cf707a4..1583b03 100644 --- a/assest/tilesets/exterieur.tres +++ b/assest/tilesets/exterieur.tres @@ -44459,9 +44459,9 @@ texture_region_size = Vector2i(48, 48) 14:5/0 = 0 15:5/0 = 0 17:5/0 = 0 -17:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-9.36442, -24, 24, -24, 24, 24, -8.11583, 24) +17:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(-20.8514, -24, -20.6017, 24, 24, 24, 7.61639, -9.23956) 18:5/0 = 0 -18:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(9.36442, -24, -24, -24, -24, 24, 8.11583, 24) +18:5/0/physics_layer_0/polygon_0/points = PackedVector2Array(20.8514, -24, 20.6017, 24, -24, 24, -7.61639, -9.23956) 21:5/0 = 0 22:5/0 = 0 23:5/0 = 0 @@ -44502,9 +44502,9 @@ texture_region_size = Vector2i(48, 48) 14:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-6.86724, 7.49154, 5.86837, 6.49266, 4.8695, 21.9752, -5.36893, 21.226) 15:6/0 = 0 17:6/0 = 0 -17:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-9.36442, -24, 24, -24, 24, 24, -8.11583, 24) +17:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(-20.8514, 24, -20.6017, -24, 24, -24, 7.61639, 9.23956) 18:6/0 = 0 -18:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(9.36442, -24, -24, -24, -24, 24, 8.11583, 24) +18:6/0/physics_layer_0/polygon_0/points = PackedVector2Array(20.8514, 24, 20.6017, -24, -24, -24, -7.61639, 9.23956) 21:6/0 = 0 22:6/0 = 0 23:6/0 = 0 diff --git a/caracters/bob/bob.dialogue b/caracters/bob/bob.dialogue new file mode 100644 index 0000000..7a80a77 --- /dev/null +++ b/caracters/bob/bob.dialogue @@ -0,0 +1,11 @@ +~ start +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 => start +- End the conversation => END +Nathan: For more information see the online documentation. +=> END \ No newline at end of file diff --git a/caracters/bob/bob.dialogue.import b/caracters/bob/bob.dialogue.import new file mode 100644 index 0000000..e8dcf0f --- /dev/null +++ b/caracters/bob/bob.dialogue.import @@ -0,0 +1,15 @@ +[remap] + +importer="dialogue_manager_compiler_14" +type="Resource" +uid="uid://vg4mssby1i6p" +path="res://.godot/imported/bob.dialogue-14d9e78eeb4886b9f5556385117b67e2.tres" + +[deps] + +source_file="res://caracters/bob/bob.dialogue" +dest_files=["res://.godot/imported/bob.dialogue-14d9e78eeb4886b9f5556385117b67e2.tres"] + +[params] + +defaults=true diff --git a/caracters/bob/bob.tscn b/caracters/bob/bob.tscn new file mode 100644 index 0000000..1ce7de3 --- /dev/null +++ b/caracters/bob/bob.tscn @@ -0,0 +1,94 @@ +[gd_scene load_steps=13 format=3 uid="uid://bleadp4yrdgj"] + +[ext_resource type="Script" path="res://caracters/human.gd" id="1_x3vfc"] +[ext_resource type="AnimationNodeStateMachine" uid="uid://ddr1ltkievtku" path="res://animations/human/human_state_machine.tres" id="2_86nrf"] +[ext_resource type="PackedScene" uid="uid://bvsendl25xjju" path="res://animations/human/human_animation_player.tscn" id="3_lb4ws"] +[ext_resource type="PackedScene" uid="uid://cg4dhp7qe68pt" path="res://animations/human/human.tscn" id="4_25owg"] +[ext_resource type="Texture2D" uid="uid://c3lal83o1trpd" path="res://assest/persos/bob.png" id="5_e15yd"] +[ext_resource type="PackedScene" uid="uid://brh7cqaxc13ie" path="res://zindex/ZIndexControler.tscn" id="5_g2w7l"] +[ext_resource type="Script" path="res://caracters/npc.gd" id="7_xosjn"] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_a4vmx"] +radius = 5.0 +height = 48.0 + +[sub_resource type="AnimationNodeTimeScale" id="AnimationNodeTimeScale_85jde"] + +[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_iwsa7"] +graph_offset = Vector2(0, -4) +nodes/HumanState/node = ExtResource("2_86nrf") +nodes/HumanState/position = Vector2(133.333, 120) +nodes/TimeScale/node = SubResource("AnimationNodeTimeScale_85jde") +nodes/TimeScale/position = Vector2(453.333, 53.3333) +nodes/output/position = Vector2(640, 146.667) +node_connections = [&"TimeScale", 0, &"HumanState", &"output", 0, &"TimeScale"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_1kv0e"] +size = Vector2(40, 10) + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_6n4r3"] +radius = 52.0 +height = 108.0 + +[node name="Bob" type="CharacterBody2D"] +z_index = 100 +motion_mode = 1 +script = ExtResource("1_x3vfc") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, 43) +rotation = 1.5708 +shape = SubResource("CapsuleShape2D_a4vmx") + +[node name="AnimationTree" type="AnimationTree" parent="."] +tree_root = SubResource("AnimationNodeBlendTree_iwsa7") +advance_expression_base_node = NodePath("..") +anim_player = NodePath("../AnimationPlayer") +parameters/HumanState/grabing/blend_position = Vector2(0, 0) +parameters/HumanState/idling/blend_position = Vector2(0, 0) +parameters/HumanState/walking/blend_position = Vector2(0, 0) +parameters/TimeScale/scale = 1.0 + +[node name="AnimationPlayer" parent="." instance=ExtResource("3_lb4ws")] + +[node name="Sprite2D" parent="." instance=ExtResource("4_25owg")] +texture = ExtResource("5_e15yd") +frame = 1 + +[node name="ZIndexControler" parent="." instance=ExtResource("5_g2w7l")] +position = Vector2(-1, 37) + +[node name="ShapeCast2D" type="ShapeCast2D" parent="ZIndexControler"] +position = Vector2(1, -13) +shape = SubResource("RectangleShape2D_1kv0e") +target_position = Vector2(0, -48) + +[node name="Area2D" type="Area2D" parent="."] +collision_layer = 4 +collision_mask = 4 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] +position = Vector2(0, 43) +rotation = 1.5708 +shape = SubResource("CapsuleShape2D_a4vmx") + +[node name="npcControler" type="Node2D" parent="." node_paths=PackedStringArray("controled")] +script = ExtResource("7_xosjn") +controled = NodePath("..") + +[node name="Label" type="Label" parent="npcControler"] +visible = false +offset_right = 40.0 +offset_bottom = 23.0 + +[node name="interactable" type="Area2D" parent="."] +collision_layer = 8 +collision_mask = 8 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="interactable"] +position = Vector2(0, 12) +shape = SubResource("CapsuleShape2D_6n4r3") + +[connection signal="start_intracting" from="." to="npcControler" method="_on_character_body_2d_start_intracting"] +[connection signal="area_entered" from="Area2D" to="." method="_on_area_2d_area_entered"] +[connection signal="body_entered" from="Area2D" to="." method="_on_area_2d_body_entered"] diff --git a/caracters/bob/bob76A8.tmp b/caracters/bob/bob76A8.tmp new file mode 100644 index 0000000..599fd50 --- /dev/null +++ b/caracters/bob/bob76A8.tmp @@ -0,0 +1,93 @@ +[gd_scene load_steps=13 format=3 uid="uid://bleadp4yrdgj"] + +[ext_resource type="Script" path="res://caracters/human.gd" id="1_x3vfc"] +[ext_resource type="AnimationNodeStateMachine" uid="uid://ddr1ltkievtku" path="res://animations/human/human_state_machine.tres" id="2_86nrf"] +[ext_resource type="PackedScene" uid="uid://bvsendl25xjju" path="res://animations/human/human_animation_player.tscn" id="3_lb4ws"] +[ext_resource type="PackedScene" uid="uid://cg4dhp7qe68pt" path="res://animations/human/human.tscn" id="4_25owg"] +[ext_resource type="Texture2D" uid="uid://c3lal83o1trpd" path="res://assest/persos/bob.png" id="5_e15yd"] +[ext_resource type="PackedScene" uid="uid://brh7cqaxc13ie" path="res://zindex/ZIndexControler.tscn" id="5_g2w7l"] +[ext_resource type="Script" path="res://caracters/npc.gd" id="7_xosjn"] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_a4vmx"] +radius = 5.0 +height = 48.0 + +[sub_resource type="AnimationNodeTimeScale" id="AnimationNodeTimeScale_85jde"] + +[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_iwsa7"] +graph_offset = Vector2(0, -4) +nodes/HumanState/node = ExtResource("2_86nrf") +nodes/HumanState/position = Vector2(133.333, 120) +nodes/TimeScale/node = SubResource("AnimationNodeTimeScale_85jde") +nodes/TimeScale/position = Vector2(453.333, 53.3333) +nodes/output/position = Vector2(640, 146.667) +node_connections = [&"TimeScale", 0, &"HumanState", &"output", 0, &"TimeScale"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_1kv0e"] +size = Vector2(40, 10) + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_6n4r3"] +radius = 52.0 +height = 108.0 + +[node name="Bob" type="CharacterBody2D"] +z_index = 100 +motion_mode = 1 +script = ExtResource("1_x3vfc") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, 43) +rotation = 1.5708 +shape = SubResource("CapsuleShape2D_a4vmx") + +[node name="AnimationTree" type="AnimationTree" parent="."] +tree_root = SubResource("AnimationNodeBlendTree_iwsa7") +advance_expression_base_node = NodePath("..") +anim_player = NodePath("../AnimationPlayer") +parameters/HumanState/grabing/blend_position = Vector2(0, 0) +parameters/HumanState/idling/blend_position = Vector2(0, 0) +parameters/HumanState/walking/blend_position = Vector2(0, 0) +parameters/TimeScale/scale = 1.0 + +[node name="AnimationPlayer" parent="." instance=ExtResource("3_lb4ws")] + +[node name="Sprite2D" parent="." instance=ExtResource("4_25owg")] +texture = ExtResource("5_e15yd") +frame = 1 + +[node name="ZIndexControler" parent="." instance=ExtResource("5_g2w7l")] +position = Vector2(-1, 37) + +[node name="ShapeCast2D" type="ShapeCast2D" parent="ZIndexControler"] +position = Vector2(1, -13) +shape = SubResource("RectangleShape2D_1kv0e") +target_position = Vector2(0, -48) + +[node name="Area2D" type="Area2D" parent="."] +collision_layer = 4 +collision_mask = 4 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] +position = Vector2(0, 43) +rotation = 1.5708 +shape = SubResource("CapsuleShape2D_a4vmx") + +[node name="npcControler" type="Node2D" parent="." node_paths=PackedStringArray("controled")] +script = ExtResource("7_xosjn") +controled = NodePath("..") + +[node name="Label" type="Label" parent="npcControler"] +offset_right = 40.0 +offset_bottom = 23.0 + +[node name="interactable" type="Area2D" parent="."] +collision_layer = 8 +collision_mask = 8 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="interactable"] +position = Vector2(0, 12) +shape = SubResource("CapsuleShape2D_6n4r3") + +[connection signal="start_intracting" from="." to="npcControler" method="_on_character_body_2d_start_intracting"] +[connection signal="area_entered" from="Area2D" to="." method="_on_area_2d_area_entered"] +[connection signal="body_entered" from="Area2D" to="." method="_on_area_2d_body_entered"] diff --git a/caracters/bob/interactable.gd b/caracters/bob/interactable.gd new file mode 100644 index 0000000..05229bd --- /dev/null +++ b/caracters/bob/interactable.gd @@ -0,0 +1,5 @@ +extends Area2D + + +func start_interaction(askingForInteraction: Human): + (get_parent() as Human).start_interaction(askingForInteraction) diff --git a/caracters/human.gd b/caracters/human.gd new file mode 100644 index 0000000..4a236eb --- /dev/null +++ b/caracters/human.gd @@ -0,0 +1,75 @@ +class_name Human +extends CharacterBody2D + +@export var speed = 250 # How fast the player will move (pixels/sec). + +# intensions of the player turner into a boolean +@export var wants_to_grab = false; +var wants_to_interact_with: Node2D +var humanInteractionTarget: Human = null + +@onready var animation_tree := $AnimationTree +@onready var animation_player := $AnimationPlayer +@onready var state_machine := animation_tree.get("parameters/HumanState/playback") as AnimationNodeStateMachinePlayback + +signal start_intracting + +var last_facing_direction = Vector2(0,1) # facing south +var velocityVector = Vector2(0, 0) +var targetGlobalPosition = null + +func moveTo(p: Vector2) -> void: + targetGlobalPosition = p + +func moveFeetTo(p: Vector2) -> void: + targetGlobalPosition = p - Vector2(0, 43) + +func face(whereToFace: Vector2) -> void: + last_facing_direction = (whereToFace - global_position).normalized() + +func decideAction() -> void: + pass + +func updateFacingDirectionInAnimationTree(): + animation_tree.set("parameters/HumanState/grabing/blend_position", last_facing_direction) + animation_tree.set("parameters/HumanState/idling/blend_position", last_facing_direction) + animation_tree.set("parameters/HumanState/walking/blend_position", last_facing_direction) + +func _physics_process(delta): + decideAction() + + if (targetGlobalPosition != null): + velocity = (targetGlobalPosition - global_position).normalized() *speed + targetGlobalPosition = null + else: + velocity = velocityVector * speed + + if state_machine.get_current_node() == "grabing": + velocity = Vector2(0,0); + + # move the caracter + move_and_slide() + + # compute the direction the player wants to look at + if velocity: + last_facing_direction = velocity.normalized() + stop_interaction() + + updateFacingDirectionInAnimationTree() + + if wants_to_interact_with and wants_to_interact_with.get_parent().has_method("start_interaction"): + if humanInteractionTarget == null: + humanInteractionTarget = wants_to_interact_with.get_parent() as Human + humanInteractionTarget.start_interaction(self) + +func _on_area_2d_body_entered(body: Node2D) -> void: + print(body) + +func start_interaction(askingForInteraction: Human): + emit_signal("start_intracting", askingForInteraction) + +func stop_interaction(): + humanInteractionTarget = null + +func get_feet_global_position(): + return global_position + Vector2(0, 43) diff --git a/caracters/npc.gd b/caracters/npc.gd new file mode 100644 index 0000000..00418a6 --- /dev/null +++ b/caracters/npc.gd @@ -0,0 +1,73 @@ +extends Node + +var astar_grid: AStarGrid2D +var toFollow: Array[Vector2i] +@onready var world: TileMapLayer = get_parent().get_parent(); +@onready var obstacles: TileMapLayer = world.get_children()[2] + +@export var controled:Human +var destination:Node2D +var HumanLayer = 0 + +func _ready() -> void: + astar_grid = AStarGrid2D.new() + astar_grid.region = world.get_used_rect() + astar_grid.cell_size = Vector2(48, 48) + astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER + astar_grid.default_compute_heuristic = AStarGrid2D.HEURISTIC_EUCLIDEAN + astar_grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_EUCLIDEAN + astar_grid.update() + + # only take into account the tiles that are marked with a navigation layer for cars + for x in world.get_used_rect().size.x: + for y in world.get_used_rect().size.y: + var tile_position = Vector2( + x + world.get_used_rect().position.x, + y + world.get_used_rect().position.y, + ) + var tile_data = world.get_cell_tile_data(tile_position) + if tile_data == null or tile_data.get_navigation_polygon(HumanLayer) == null: + astar_grid.set_point_solid(tile_position) + + if obstacles.get_cell_tile_data(tile_position) != null and obstacles.get_cell_tile_data(tile_position).get_collision_polygons_count(0): + astar_grid.set_point_solid(tile_position) + +func _process(delta: float) -> void: + if !controled: + return + if destination: + var my_global_position = controled.get_feet_global_position() + var target_global_position = destination.get_feet_global_position() + + # make the wanted position on the track move ahead by a certain amount + #if toFollow == null or toFollow.is_empty(): + # compute the new navigation points the car should follow + var points = astar_grid.get_id_path( + world.local_to_map(world.to_local(my_global_position)), + world.local_to_map(world.to_local(target_global_position)) + ).slice(1, -1) + if !points.is_empty(): + toFollow = points + + if $Label.visible: + $Label.text = ( + "position "+str(world.local_to_map(world.to_local(my_global_position)))+" , "+str(my_global_position)+ + "\ntarget position "+ str(world.local_to_map(world.to_local(target_global_position)))+" , "+str(target_global_position)+ + "\npoint to follow "+str(toFollow) + ) + + if !toFollow.is_empty(): + if world.local_to_map(world.to_local(my_global_position)) == toFollow.front(): + toFollow.pop_front() + + if !toFollow.is_empty(): + controled.moveFeetTo(world.to_global(world.map_to_local(toFollow.front()))); + else: + controled.face(target_global_position) + +func _on_character_body_2d_start_intracting(interactingWith: Human) -> void: + controled.face(interactingWith.global_position) + print("pouet") + # ouvre un dialogue + var resource = load("res://caracters/bob/bob.dialogue") + DialogueManager.show_dialogue_balloon(resource, "start") diff --git a/caracters/player/player.tscn b/caracters/player/player.tscn index a679323..21cd626 100644 --- a/caracters/player/player.tscn +++ b/caracters/player/player.tscn @@ -1,5 +1,6 @@ -[gd_scene load_steps=10 format=3 uid="uid://vclpg4e4ql54"] +[gd_scene load_steps=12 format=3 uid="uid://vclpg4e4ql54"] +[ext_resource type="Script" path="res://caracters/human.gd" id="1_l1sti"] [ext_resource type="Script" path="res://caracters/player/player_controler.gd" id="1_oapm5"] [ext_resource type="AnimationNodeStateMachine" uid="uid://ddr1ltkievtku" path="res://animations/human/human_state_machine.tres" id="3_1y7fn"] [ext_resource type="PackedScene" uid="uid://bvsendl25xjju" path="res://animations/human/human_animation_player.tscn" id="3_c286j"] @@ -23,12 +24,18 @@ node_connections = [&"TimeScale", 0, &"HumanState", &"output", 0, &"TimeScale"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_1kv0e"] size = Vector2(40, 10) +[sub_resource type="RectangleShape2D" id="RectangleShape2D_11ib5"] +size = Vector2(20, 150) + [node name="CharacterBody2D" type="CharacterBody2D"] z_index = 100 motion_mode = 1 -script = ExtResource("1_oapm5") +script = ExtResource("1_l1sti") +metadata/_edit_vertical_guides_ = [-20.0] +metadata/_edit_horizontal_guides_ = [48.0] [node name="Camera2D" type="Camera2D" parent="."] +zoom = Vector2(1.5, 1.5) position_smoothing_enabled = true drag_horizontal_enabled = true drag_vertical_enabled = true @@ -43,7 +50,7 @@ tree_root = SubResource("AnimationNodeBlendTree_iwsa7") advance_expression_base_node = NodePath("..") anim_player = NodePath("../AnimationPlayer") parameters/HumanState/grabing/blend_position = Vector2(0, 0) -parameters/HumanState/idling/blend_position = Vector2(0, 0) +parameters/HumanState/idling/blend_position = Vector2(0.000657439, 1.0284) parameters/HumanState/walking/blend_position = Vector2(0, 0) parameters/TimeScale/scale = 1.0 @@ -69,5 +76,17 @@ position = Vector2(0, 43) rotation = 1.5708 shape = SubResource("CapsuleShape2D_a4vmx") +[node name="controleur" type="Node2D" parent="." node_paths=PackedStringArray("human", "ray")] +script = ExtResource("1_oapm5") +human = NodePath("..") +ray = NodePath("../ShapeCast2D") + +[node name="ShapeCast2D" type="ShapeCast2D" parent="."] +shape = SubResource("RectangleShape2D_11ib5") +target_position = Vector2(1, 90) +collision_mask = 8 +collide_with_areas = true +collide_with_bodies = false + [connection signal="area_entered" from="Area2D" to="." method="_on_area_2d_area_entered"] [connection signal="body_entered" from="Area2D" to="." method="_on_area_2d_body_entered"] diff --git a/caracters/player/player_controler.gd b/caracters/player/player_controler.gd index 275b48d..af8598f 100644 --- a/caracters/player/player_controler.gd +++ b/caracters/player/player_controler.gd @@ -1,39 +1,47 @@ -class_name PlayerControler -extends CharacterBody2D +extends Node -@export var speed = 250 # How fast the player will move (pixels/sec). +@export var human: Human +@export var ray : ShapeCast2D +var interactable : Node2D -# intensions of the player turner into a boolean -@export var wants_to_grab = false; - -@onready var animation_tree := $AnimationTree -@onready var state_machine := animation_tree.get("parameters/HumanState/playback") as AnimationNodeStateMachinePlayback - -var last_facing_direction = Vector2(0,-1) # facing south - -func readInputs(): - wants_to_grab = Input.is_action_pressed("grab"); - if state_machine.get_current_node() == "grabing": - velocity = Vector2(0,0); +func _unhandled_input(event: InputEvent) -> void: + if ( + event.is_action("move_left") or + event.is_action("move_right") or + event.is_action("move_up") or + event.is_action("move_down") + ): + human.velocityVector = Input.get_vector("move_left", "move_right", "move_up", "move_down") + if event.is_action_pressed("grab"): + if interactable: + human.stop_interaction() + human.wants_to_interact_with = interactable + else: + human.wants_to_grab = true else: - velocity = Input.get_vector("move_left", "move_right", "move_up", "move_down") * speed + human.wants_to_grab = false + human.wants_to_interact_with = null -func updateFacingDirectionInAnimationTree(): - animation_tree.set("parameters/HumanState/grabing/blend_position", last_facing_direction) - animation_tree.set("parameters/HumanState/idling/blend_position", last_facing_direction) - animation_tree.set("parameters/HumanState/walking/blend_position", last_facing_direction) +func _process(delta) -> void: + ray.target_position = human.last_facing_direction * 48 -func _physics_process(delta): - readInputs() + if human.last_facing_direction.y > 0 : + ray.target_position = Vector2(0, 48) + (ray.shape as RectangleShape2D).size = Vector2(50, 20) + elif human.last_facing_direction.y < 0: + ray.target_position = Vector2(0, -48) + (ray.shape as RectangleShape2D).size = Vector2(50, 20) + elif human.last_facing_direction.x > 0 : + ray.target_position = Vector2(48, 0) + (ray.shape as RectangleShape2D).size = Vector2(20, 50) + else: + ray.target_position = Vector2(-48, 0) + (ray.shape as RectangleShape2D).size = Vector2(20, 50) - # move the caracter - move_and_slide() - - # compute the direction the player wants to look at - if velocity: - last_facing_direction = velocity.normalized() - - updateFacingDirectionInAnimationTree() - -func _on_area_2d_body_entered(body: Node2D) -> void: - print(body) + interactable = null + if ray.is_colliding(): + var nbCollisions = ray.get_collision_count() + for n in range(nbCollisions): + var colider = ray.get_collider(n) as Node2D + if colider != null and colider != get_parent(): + interactable = colider diff --git a/export_presets.cfg b/export_presets.cfg index 43b54d1..044695b 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -23,7 +23,7 @@ custom_template/release="" variant/extensions_support=false variant/thread_support=false vram_texture_compression/for_desktop=false -vram_texture_compression/for_mobile=true +vram_texture_compression/for_mobile=false html/export_icon=true html/custom_html_shell="" html/head_include="" diff --git a/icon.svg.import b/icon.svg.import index bf4648f..fcec47d 100644 --- a/icon.svg.import +++ b/icon.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://btxy7eqifmh2o" +uid="uid://by4miql86xujf" path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" metadata={ "vram_texture": false diff --git a/maps/world.tscn b/maps/world.tscn index cf01e10..bf46081 100644 --- a/maps/world.tscn +++ b/maps/world.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=4 uid="uid://d1oqt6sbjvopi"] +[gd_scene load_steps=14 format=4 uid="uid://d1oqt6sbjvopi"] [ext_resource type="TileSet" uid="uid://ckj00wy20rkfx" path="res://assest/tilesets/exterieur.tres" id="1_s310m"] [ext_resource type="PackedScene" uid="uid://yn8fq44nqwd2" path="res://buildings/generic1.tscn" id="2_cfh67"] @@ -10,10 +10,81 @@ [ext_resource type="PackedScene" uid="uid://dotr3vifbinfy" path="res://urban_furnitures/semaphores/up_pedestrian_semaphore.tscn" id="8_4w1r0"] [ext_resource type="PackedScene" uid="uid://djlp0et1giup2" path="res://urban_furnitures/semaphores/down_semaphore.tscn" id="9_l0y5u"] [ext_resource type="PackedScene" uid="uid://btvlfkbqe6f4j" path="res://urban_furnitures/semaphores/up_semaphore.tscn" id="10_vuise"] +[ext_resource type="Texture2D" uid="uid://by4miql86xujf" path="res://icon.svg" id="11_ui070"] + +[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_dybla"] +texture = ExtResource("11_ui070") +0:0/0 = 0 +1:0/0 = 0 +2:0/0 = 0 +3:0/0 = 0 +4:0/0 = 0 +5:0/0 = 0 +6:0/0 = 0 +7:0/0 = 0 +0:1/0 = 0 +1:1/0 = 0 +2:1/0 = 0 +3:1/0 = 0 +4:1/0 = 0 +5:1/0 = 0 +6:1/0 = 0 +7:1/0 = 0 +0:2/0 = 0 +1:2/0 = 0 +2:2/0 = 0 +3:2/0 = 0 +4:2/0 = 0 +5:2/0 = 0 +6:2/0 = 0 +7:2/0 = 0 +0:3/0 = 0 +1:3/0 = 0 +2:3/0 = 0 +3:3/0 = 0 +4:3/0 = 0 +5:3/0 = 0 +6:3/0 = 0 +7:3/0 = 0 +0:4/0 = 0 +1:4/0 = 0 +2:4/0 = 0 +3:4/0 = 0 +4:4/0 = 0 +5:4/0 = 0 +6:4/0 = 0 +7:4/0 = 0 +0:5/0 = 0 +1:5/0 = 0 +2:5/0 = 0 +3:5/0 = 0 +4:5/0 = 0 +5:5/0 = 0 +6:5/0 = 0 +7:5/0 = 0 +0:6/0 = 0 +1:6/0 = 0 +2:6/0 = 0 +3:6/0 = 0 +4:6/0 = 0 +5:6/0 = 0 +6:6/0 = 0 +7:6/0 = 0 +0:7/0 = 0 +1:7/0 = 0 +2:7/0 = 0 +3:7/0 = 0 +4:7/0 = 0 +5:7/0 = 0 +6:7/0 = 0 +7:7/0 = 0 + +[sub_resource type="TileSet" id="TileSet_l1bso"] +sources/0 = SubResource("TileSetAtlasSource_dybla") [node name="fond" type="TileMapLayer"] position = Vector2(64, 92) -tile_map_data = PackedByteArray("AAD2//z/AwAfAAEAAAD2//3/AwAfAAEAAAD2//7/AwAeAAMAAAD2////AwAeAAQAAAD2/wAAAwAqAAYAAAD2/wEAAwAhAAUAAAD2/wIAAwAfAAAAAAD2/wMAAwAoAAIAAGD2/wQAAwAnAAIAAGD3//z/AwAfAAEAAAD3//3/AwAfAAEAAAD3//7/AwAeAAMAAAD3////AwAeAAcAAAD3/wAAAwAqAAYAAAD3/wEAAwAgAAUAAAD3/wIAAwAfAAAAAAD3/wMAAwAoAAMAAGD3/wQAAwAnAAMAAGD4//z/AwAfAAEAAAD4//3/AwAfAAEAAAD4//7/AwAeAAMAAAD4////AwAgAAYAAAD4/wAAAwAqAAYAAAD4/wEAAwAfAAUAAAD4/wIAAwAfAAAAAAD4/wMAAwAnAAIAAAD4/wQAAwAnAAMAAAD5//z/AwAfAAEAAAD5//3/AwAfAAEAAAD5//7/AwAeAAMAAAD5////AwAeAAQAAAD5/wAAAwAqAAYAAAD5/wEAAwAfAAQAAAD5/wIAAwAfAAAAAAD5/wMAAwAoAAIAAAD5/wQAAwAoAAMAAAD6//z/AwAnAAEAAAD6//3/AwAfAAEAAAD6//7/AwAfAAMAAAD6////AwAiAAcAAAD6/wAAAwAqAAYAAAD6/wEAAwAgAAcAAAD6/wIAAwAfAAAAAAD6/wMAAwAoAAIAAGD6/wQAAwAnAAIAAGD7//z/AwAoAAEAAAD7//3/AwAoAAEAAAD7//7/AwAeAAMAAAD7////AwAgAAQAAAD7/wAAAwAqAAYAAAD7/wEAAwAeAAQAAAD7/wIAAwAfAAAAAAD7/wMAAwAoAAMAAGD7/wQAAwAnAAMAAGD8//z/AwAnAAAAAAD8//3/AwAnAAEAAAD8//7/AwAeAAMAAAD8////AwAgAAQAAAD8/wAAAwAoAAUAAAD8/wEAAwAgAAYAAAD8/wIAAwAfAAAAAAD8/wMAAwAnAAAAAAD8/wQAAwAnAAEAAAD9//z/AwAoAAAAAAD9//3/AwAoAAEAAAD9//7/AwAfAAMAAAD9////AwAhAAQAAAD9/wAAAwAoAAUAAAD9/wEAAwAhAAYAAAD9/wIAAwAfAAAAAAD9/wMAAwAoAAAAAAD9/wQAAwAoAAEAAAD+//z/AwAnAAAAAAD+//3/AwAnAAEAAAD+//7/AwAeAAMAAAD+////AwAiAAQAAAD+/wAAAwAoAAUAAAD+/wEAAwAiAAYAAAD+/wIAAwAfAAAAAAD+/wMAAwAnAAAAAAD+/wQAAwAnAAEAAAD///z/AwAoAAAAAAD///3/AwAoAAEAAAD///7/AwAeAAMAAAD/////AwAeAAQAAAD//wAAAwAoAAUAAAD//wEAAwAeAAYAAAD//wIAAwAfAAAAAAD//wMAAwAoAAAAAAD//wQAAwAoAAEAAAAAAPz/AwAnAAAAAAAAAP3/AwAnAAEAAAAAAP7/AwAeAAMAAAAAAP//AwAfAAQAAAAAAAAAAwAoAAUAAAAAAAEAAwAfAAYAAAAAAAIAAwAfAAAAAAAAAAMAAwAnAAAAAAAAAAQAAwAnAAEAAAABAPz/AwAoAAAAAAABAP3/AwAoAAEAAAABAP7/AwAfAAMAAAABAP//AwAgAAQAAAABAAAAAwAoAAUAAAABAAEAAwAgAAYAAAABAAIAAwAfAAAAAAABAAMAAwAoAAAAAAABAAQAAwAoAAEAAAACAPz/AwAnAAAAAAACAP3/AwAnAAEAAAACAP7/AwAeAAMAAAACAP//AwAhAAQAAAACAAAAAwAoAAUAAAACAAEAAwAhAAYAAAACAAIAAwAfAAAAAAACAAMAAwAnAAAAAAACAAQAAwAnAAEAAAADAPz/AwAoAAAAAAADAP3/AwAoAAEAAAADAP7/AwAeAAMAAAADAP//AwAiAAQAAAADAAAAAwAoAAUAAAADAAEAAwAiAAYAAAADAAIAAwAfAAAAAAADAAMAAwAoAAAAAAADAAQAAwAoAAEAAAAEAPz/AwAnAAAAAAAEAP3/AwAnAAEAAAAEAP7/AwAfAAMAAAAEAP//AwAhAAQAAAAEAAAAAwAoAAUAAAAEAAEAAwAhAAYAAAAEAAIAAwAfAAAAAAAEAAMAAwAoAAAAAAAEAAQAAwAoAAEAAAAFAPz/AwAoAAAAAAAFAP3/AwAoAAEAAAAFAP7/AwAeAAMAAAAFAP//AwAiAAQAAAAFAAAAAwAoAAUAAAAFAAEAAwAiAAYAAAAFAAIAAwAfAAAAAAAFAAMAAwAnAAAAAAAFAAQAAwAnAAEAAAAGAPz/AwAnAAAAAAAGAP3/AwAnAAEAAAAGAP7/AwAeAAMAAAAGAP//AwAeAAQAAAAGAAAAAwAoAAUAAAAGAAEAAwAeAAYAAAAGAAIAAwAfAAAAAAAGAAMAAwAoAAAAAAAGAAQAAwAoAAEAAAAHAPz/AwAoAAAAAAAHAP3/AwAoAAEAAAAHAP7/AwAeAAMAAAAHAP//AwAfAAQAAAAHAAAAAwAoAAUAAAAHAAEAAwAfAAYAAAAHAAIAAwAfAAAAAAAHAAMAAwAnAAAAAAAHAAQAAwAnAAEAAAAIAPz/AwAnAAAAAAAIAP3/AwAnAAEAAAAIAP7/AwAfAAMAAAAIAP//AwAgAAQAAAAIAAAAAwAoAAUAAAAIAAEAAwAgAAYAAAAIAAIAAwAfAAAAAAAIAAMAAwAoAAAAAAAIAAQAAwAoAAEAAAAJAPz/AwAoAAAAAAAJAP3/AwAoAAEAAAAJAP7/AwAeAAMAAAAJAP//AwAhAAQAAAAJAAAAAwAoAAUAAAAJAAEAAwAhAAYAAAAJAAIAAwAfAAAAAAAJAAMAAwAnAAAAAAAJAAQAAwAnAAEAAAAKAPz/AwAnAAAAAAAKAP3/AwAnAAEAAAAKAP7/AwAeAAMAAAAKAP//AwAiAAQAAAAKAAAAAwAoAAUAAAAKAAEAAwAiAAYAAAAKAAIAAwAfAAAAAAAKAAMAAwAoAAAAAAAKAAQAAwAoAAEAAAALAPz/AwAoAAAAAAALAP3/AwAoAAEAAAALAP7/AwAeAAMAAAALAP//AwAiAAUAAAALAAAAAwAqAAYAAAALAAEAAwAgAAUAAAALAAIAAwAfAAAAAAALAAMAAwAoAAMAAGALAAQAAwAnAAMAAGAMAPz/AwAnAAAAAAAMAP3/AwAnAAEAAAANAPz/AwAoAAAAAAANAP3/AwAoAAEAAAAMAPr/AwAoAAAAAGAMAPv/AwAnAAAAAGANAPr/AwAoAAEAAGANAPv/AwAnAAEAAGAMAPn/AwAnAAAAAGANAPn/AwAnAAEAAGAMAAMAAwAnAAAAAAAMAAQAAwAnAAEAAAANAAMAAwAoAAAAAAANAAQAAwAoAAEAAAAMAAUAAwAnAAAAAAAMAAYAAwAnAAEAAAANAAUAAwAoAAAAAAANAAYAAwAoAAEAAAAMAAcAAwAoAAAAAGAMAAgAAwAnAAAAAGANAAcAAwAoAAEAAGANAAgAAwAnAAEAAGAMAAkAAwAoAAAAAGAMAAoAAwAnAAAAAGANAAkAAwAoAAEAAGANAAoAAwAnAAEAAGAMAP7/AwAjAAQAAAANAP7/AwAkAAQAAAAMAP//AwAlAAUAAAAMAAAAAwAlAAQAAAAMAAEAAwAlAAUAAAAMAAIAAwAjAAUAAAANAP//AwAmAAUAAAANAAAAAwAmAAQAAAANAAEAAwAmAAUAAAANAAIAAwAkAAUAAAAOAP//AwAgAAUAAAAOAAAAAwAoAAUAAAAOAAEAAwAiAAQAAAAOAAIAAwAgAAAAAAAPAP//AwAfAAQAAAAPAAAAAwAoAAUAAAAPAAEAAwAiAAUAAAAPAAIAAwAgAAUAAAAQAP//AwApAAYAAAAQAAAAAwAqAAUAAAAQAAEAAwApAAYAAAAQAAIAAwApAAYAAAARAP//AwAfAAcAAAARAAAAAwAoAAUAAAARAAEAAwAgAAYAAAARAAIAAwAhAAcAAAASAP//AwAgAAQAAAASAAAAAwAoAAUAAAASAAEAAwAfAAQAAAASAAIAAwAeAAAAAAAOAP7/AwAgAAIAAAAPAP7/AwAiAAcAAAAQAP7/AwApAAYAAAARAP7/AwAhAAQAAAASAP7/AwAeAAIAAAATAP7/AwAjAAQAAAAUAP7/AwAkAAQAAAATAP//AwAlAAUAAAATAAAAAwAlAAQAAAATAAEAAwAlAAUAAAATAAIAAwAjAAUAAAAUAP//AwAmAAUAAAAUAAAAAwAmAAQAAAAUAAEAAwAmAAUAAAAUAAIAAwAkAAUAAAATAPz/AwAnAAAAAAATAP3/AwAnAAEAAAAUAPz/AwAoAAAAAAAUAP3/AwAoAAEAAAATAPr/AwAoAAAAAGATAPv/AwAnAAEAAAAUAPr/AwAoAAEAAGAUAPv/AwAoAAEAAAATAPn/AwAnAAAAAGAUAPn/AwAnAAEAAGAOAPr/AwAgAAEAAAAOAPv/AwAgAAEAAAAOAPz/AwAjAAYAAAAOAP3/AwAjAAcAAAAPAPr/AwAeAAQAAGAPAPv/AwAiAAQAAGAPAPz/AwAlAAYAAAAPAP3/AwAlAAcAAAAQAPv/AwAoAAUAAGAQAPz/AwAlAAYAAAAQAP3/AwAlAAcAAAARAPr/AwAeAAYAAGARAPv/AwAiAAYAAGARAPz/AwAlAAYAAAARAP3/AwAlAAcAAAASAPr/AwAeAAEAAAASAPv/AwAeAAEAAAASAPz/AwAkAAYAAAASAP3/AwAkAAcAAAAOAPn/AwAgAAEAAAAPAPn/AwAiAAQAAGAQAPn/AwAoAAUAAGARAPn/AwAiAAYAAGASAPn/AwAeAAEAAAAOAAMAAwAjAAYAAAAOAAQAAwAjAAcAAAAOAAUAAwAgAAEAAAAOAAYAAwAgAAEAAAAPAAMAAwAlAAYAAAAPAAQAAwAlAAcAAAAPAAUAAwAfAAUAAAAPAAYAAwAhAAcAAAAQAAMAAwAlAAYAAAAQAAQAAwAlAAcAAAAQAAUAAwApAAYAAAAQAAYAAwApAAYAAAARAAMAAwAlAAYAAAARAAQAAwAlAAcAAAARAAUAAwAgAAYAAAARAAYAAwAfAAQAAAASAAMAAwAkAAYAAAASAAQAAwAkAAcAAAASAAUAAwAeAAEAAAASAAYAAwAeAAEAAAAPAAcAAwAiAAQAAGAPAAgAAwAhAAQAAGAPAAkAAwAgAAQAAGAPAAoAAwAfAAQAAGAQAAcAAwAoAAUAAGAQAAgAAwAoAAUAAGAQAAkAAwAoAAUAAGAQAAoAAwAoAAUAAGARAAcAAwAiAAYAAGARAAgAAwAhAAYAAGARAAkAAwAgAAYAAGARAAoAAwAfAAYAAGASAAcAAwAeAAEAAAASAAgAAwAeAAEAAAASAAkAAwAeAAEAAAASAAoAAwAeAAEAAAATAAcAAwAoAAAAAGATAAgAAwAnAAAAAGATAAkAAwAoAAAAAGATAAoAAwAnAAAAAGAOAAcAAwAgAAEAAAAOAAgAAwAgAAEAAAAOAAkAAwAgAAEAAAAOAAoAAwAgAAEAAAATAAMAAwAnAAAAAAATAAQAAwAnAAEAAAAUAAMAAwAoAAAAAAAUAAQAAwAoAAEAAAATAAUAAwAnAAAAAAATAAYAAwAnAAEAAAAUAAUAAwAoAAAAAAAUAAYAAwAoAAEAAAAUAAcAAwAoAAEAAGAUAAgAAwAnAAEAAGAUAAkAAwAoAAEAAGAUAAoAAwAnAAEAAGAVAP7/AwAeAAMAAAAWAP7/AwAfAAMAAAAXAP7/AwAeAAMAAAAYAP7/AwAeAAMAAAAZAP7/AwAeAAMAAAAaAP7/AwAfAAMAAAAbAP7/AwAeAAMAAAAcAP7/AwAeAAMAAAAVAP//AwAgAAQAAAAVAAAAAwAoAAUAAAAVAAEAAwAgAAYAAAAVAAIAAwAfAAAAAAAWAP//AwAhAAQAAAAWAAAAAwAoAAUAAAAWAAEAAwAhAAYAAAAWAAIAAwAfAAAAAAAXAP//AwAiAAQAAAAXAAAAAwAoAAUAAAAXAAEAAwAiAAYAAAAXAAIAAwAfAAAAAAAYAP//AwAeAAQAAAAYAAAAAwAoAAUAAAAYAAEAAwAeAAYAAAAYAAIAAwAfAAAAAAAZAP//AwAfAAQAAAAZAAAAAwAoAAUAAAAZAAEAAwAfAAYAAAAZAAIAAwAfAAAAAAAaAP//AwAgAAQAAAAaAAAAAwAoAAUAAAAaAAEAAwAgAAYAAAAaAAIAAwAfAAAAAAAbAP//AwAhAAQAAAAbAAAAAwAoAAUAAAAbAAEAAwAhAAYAAAAbAAIAAwAfAAAAAAAcAP//AwAiAAQAAAAcAAAAAwAoAAUAAAAcAAEAAwAiAAYAAAAcAAIAAwAfAAAAAAAVAAMAAwAnAAAAAAAVAAQAAwAnAAEAAAAWAAMAAwAoAAAAAAAWAAQAAwAoAAEAAAAXAAMAAwAnAAAAAAAXAAQAAwAnAAEAAAAYAAMAAwAoAAAAAAAYAAQAAwAoAAEAAAAZAAMAAwAnAAAAAAAZAAQAAwAnAAEAAAAaAAMAAwAoAAAAAAAaAAQAAwAoAAEAAAAbAAMAAwAnAAAAAAAbAAQAAwAnAAEAAAAcAAMAAwAoAAAAAAAcAAQAAwAoAAEAAAAVAPz/AwAnAAAAAAAVAP3/AwAnAAEAAAAWAPz/AwAoAAAAAAAWAP3/AwAoAAEAAAAXAPz/AwAnAAAAAAAXAP3/AwAnAAEAAAAYAPz/AwAoAAAAAAAYAP3/AwAoAAEAAAAZAPz/AwAnAAAAAAAZAP3/AwAnAAEAAAAaAPz/AwAoAAAAAAAaAP3/AwAoAAEAAAAbAPz/AwAnAAAAAAAbAP3/AwAnAAEAAAAcAPz/AwAoAAAAAAAcAP3/AwAoAAEAAAAMABIAAwAnAAEAAAAMABEAAwAnAAAAAAAMABAAAwAnAAEAAAAMAA8AAwAoAAAAAGAMAA4AAwAnAAAAAGAMAA0AAwAoAAAAAGAMAAwAAwAnAAAAAGAMAAsAAwAoAAAAAGANABIAAwAoAAEAAAANABEAAwAoAAAAAAANABAAAwAoAAEAAAANAA8AAwAoAAEAAGANAA4AAwAnAAEAAGANAA0AAwAoAAEAAGANAAwAAwAnAAEAAGANAAsAAwAoAAEAAGAOABIAAwAgAAEAAAAOABEAAwAgAAEAAAAOABAAAwAgAAEAAAAOAA8AAwAgAAEAAAAOAA4AAwAgAAEAAAAOAA0AAwAgAAEAAAAOAAwAAwAgAAEAAAAOAAsAAwAgAAEAAAAPABIAAwAgAAQAAGAPABEAAwAhAAQAAGAPABAAAwAiAAQAAGAPAA8AAwAeAAQAAGAPAA4AAwAgAAQAAGAPAA0AAwAhAAQAAGAPAAwAAwAiAAQAAGAPAAsAAwAeAAQAAGAQABIAAwAoAAUAAGAQABEAAwAoAAUAAGAQABAAAwAoAAUAAGAQAA8AAwAoAAUAAGAQAA4AAwAoAAUAAGAQAA0AAwAoAAUAAGAQAAwAAwAoAAUAAGAQAAsAAwAoAAUAAGARABIAAwAgAAYAAGARABEAAwAhAAYAAGARABAAAwAiAAYAAGARAA8AAwAeAAYAAGARAA4AAwAgAAYAAGARAA0AAwAhAAYAAGARAAwAAwAiAAYAAGARAAsAAwAeAAYAAGASABIAAwAeAAEAAAASABEAAwAeAAEAAAASABAAAwAeAAEAAAASAA8AAwAeAAEAAAASAA4AAwAeAAEAAAASAA0AAwAeAAEAAAASAAwAAwAeAAEAAAASAAsAAwAeAAEAAAATABIAAwAnAAEAAAATABEAAwAoAAAAAGATABAAAwAnAAAAAGATAA8AAwAoAAAAAGATAA4AAwAnAAAAAGATAA0AAwAoAAAAAGATAAwAAwAnAAAAAGATAAsAAwAoAAAAAGAUABIAAwAoAAEAAAAUABEAAwAoAAEAAGAUABAAAwAnAAEAAGAUAA8AAwAoAAEAAGAUAA4AAwAnAAEAAGAUAA0AAwAoAAEAAGAUAAwAAwAnAAEAAGAUAAsAAwAoAAEAAGAMAPT/AwAoAAAAAGAMAPP/AwAnAAAAAGAMAPL/AwAoAAAAAGAMAPH/AwAnAAAAAGAMAPD/AwAoAAAAAGAMAO//AwAnAAEAAAAMAO7/AwAnAAAAAAANAPT/AwAoAAEAAGANAPP/AwAnAAEAAGANAPL/AwAoAAEAAGANAPH/AwAnAAEAAGANAPD/AwAoAAEAAGANAO//AwAoAAEAAAANAO7/AwAoAAAAAAAOAPT/AwAgAAEAAAAOAPP/AwAgAAEAAAAOAPL/AwAgAAEAAAAOAPH/AwAgAAEAAAAOAPD/AwAgAAEAAAAOAO//AwAjAAcAAAAOAO7/AwAjAAYAAAAPAPT/AwAeAAQAAGAPAPP/AwAiAAQAAGAPAPL/AwAeAAQAAGAPAPH/AwAfAAQAAGAPAPD/AwAgAAQAAGAPAO//AwAlAAcAAAAPAO7/AwAlAAYAAAAQAPT/AwAoAAUAAGAQAPP/AwAoAAUAAGAQAPL/AwAoAAUAAGAQAPH/AwAoAAUAAGAQAPD/AwAoAAUAAGAQAO//AwAlAAcAAAAQAO7/AwAlAAYAAAARAPT/AwAeAAYAAGARAPP/AwAiAAYAAGARAPL/AwAeAAYAAGARAPH/AwAfAAYAAGARAPD/AwAgAAYAAGARAO//AwAlAAcAAAARAO7/AwAlAAYAAAASAPT/AwAeAAEAAAASAPP/AwAeAAEAAAASAPL/AwAeAAEAAAASAPH/AwAeAAEAAAASAPD/AwAeAAEAAAASAO//AwAkAAcAAAASAO7/AwAkAAYAAAATAPT/AwAoAAAAAGATAPP/AwAnAAAAAGATAPL/AwAoAAAAAGATAPH/AwAnAAAAAGATAPD/AwAoAAAAAGATAO//AwAnAAEAAAATAO7/AwAnAAAAAAAUAPT/AwAoAAEAAGAUAPP/AwAnAAEAAGAUAPL/AwAoAAEAAGAUAPH/AwAnAAEAAGAUAPD/AwAoAAEAAGAUAO//AwAoAAEAAAAUAO7/AwAoAAAAAAAMABMAAwAnAAAAAAAMABQAAwAnAAEAAAAMABUAAwAnAAAAAAAMABYAAwAnAAEAAAAMABcAAwAeAAMAAAAMABgAAwAeAAcAAAAMABkAAwAoAAUAAAAMABoAAwAeAAcAAAAMABsAAwAfAAAAAAAMABwAAwAnAAAAAAAMAB0AAwAnAAEAAAAMAB8AAwAnAAEAAAANABMAAwAoAAAAAAANABQAAwAoAAEAAAANABUAAwAoAAAAAAANABYAAwAoAAEAAAANABcAAwAfAAMAAAANABgAAwAfAAQAAAANABkAAwAoAAUAAAANABoAAwAfAAYAAAANABsAAwAfAAAAAAANABwAAwAoAAAAAAANAB0AAwAoAAEAAAANAB4AAwAoAAAAAAANAB8AAwAoAAEAAAAOABMAAwAgAAEAAAAOABQAAwAgAAEAAAAOABUAAwAgAAEAAAAOABYAAwAgAAEAAAAOABcAAwAgAAIAAAAOABgAAwAgAAUAAAAOABkAAwAoAAUAAAAOABoAAwAiAAQAAAAOABsAAwAgAAAAAAAOABwAAwAgAAEAAAAOAB0AAwAgAAEAAAAOAB4AAwAgAAEAAAAOAB8AAwAgAAEAAAAPABMAAwAfAAQAAAAPABQAAwAfAAUAAAAPABUAAwAfAAYAAAAPABYAAwAfAAcAAAAPABcAAwAiAAcAAAAPABgAAwAfAAQAAAAPABkAAwAoAAUAAAAPABoAAwAiAAUAAAAPABsAAwAgAAUAAAAPABwAAwAgAAUAAAAPAB0AAwAhAAcAAAAPAB4AAwAfAAUAAAAPAB8AAwAhAAcAAAAQABMAAwApAAYAAAAQABQAAwApAAYAAAAQABUAAwApAAYAAAAQABYAAwApAAYAAAAQABcAAwApAAYAAAAQABgAAwApAAYAAAAQABkAAwAqAAUAAAAQABoAAwApAAYAAAAQABsAAwApAAYAAAAQABwAAwApAAYAAAAQAB0AAwApAAYAAAAQAB4AAwApAAYAAAAQAB8AAwApAAYAAAARABMAAwAeAAUAAAARABQAAwAeAAQAAAARABUAAwAeAAQAAAARABYAAwAeAAUAAAARABcAAwAhAAQAAAARABgAAwAfAAcAAAARABkAAwAoAAUAAAARABoAAwAgAAYAAAARABsAAwAhAAcAAAARABwAAwAhAAQAAAARAB0AAwAiAAYAAAARAB4AAwAgAAYAAAARAB8AAwAfAAQAAAASABMAAwAeAAEAAAASABQAAwAeAAEAAAASABUAAwAeAAEAAAASABYAAwAeAAEAAAASABcAAwAeAAIAAAASABgAAwAgAAQAAAASABkAAwAoAAUAAAASABoAAwAfAAQAAAASABsAAwAeAAAAAAASABwAAwAeAAEAAAASAB0AAwAeAAEAAAASAB4AAwAeAAEAAAASAB8AAwAeAAEAAAATABMAAwAnAAAAAAATABQAAwAnAAEAAAATABUAAwAnAAAAAAATABYAAwAnAAEAAAATABcAAwAeAAMAAAATABgAAwAeAAQAAAATABkAAwAoAAUAAAATABoAAwAeAAYAAAATABsAAwAfAAAAAAATABwAAwAnAAAAAAATAB0AAwAnAAEAAAATAB4AAwAnAAAAAAATAB8AAwAnAAEAAAAUABMAAwAoAAAAAAAUABQAAwAoAAEAAAAUABUAAwAoAAAAAAAUABYAAwAoAAEAAAAUABcAAwAeAAMAAAAUABgAAwAfAAQAAAAUABkAAwAoAAUAAAAUABoAAwAfAAYAAAAUABsAAwAfAAAAAAAUABwAAwAoAAAAAAAUAB0AAwAoAAEAAAAUAB4AAwAoAAAAAAAUAB8AAwAoAAEAAAAVABUAAwAnAAAAAAAVABYAAwAnAAEAAAAVABcAAwAeAAMAAAAVABgAAwAgAAQAAAAVABkAAwAoAAUAAAAVABoAAwAgAAYAAAAVABsAAwAfAAAAAAAVABwAAwAnAAAAAAAVAB0AAwAnAAEAAAAWABUAAwAoAAAAAAAWABYAAwAoAAEAAAAWABcAAwAfAAMAAAAWABgAAwAhAAQAAAAWABkAAwAoAAUAAAAWABoAAwAhAAYAAAAWABsAAwAfAAAAAAAWABwAAwAoAAAAAAAWAB0AAwAoAAEAAAAMAB4AAwAnAAAAAAAKAOP/AwApAAAAAAAKAOT/AwApAAEAAAAKAOX/AwAeAAMAAAAKAOb/AwAfAAQAAAAKAOf/AwAqAAYAAAAKAOj/AwAfAAUAAAAKAOn/AwAfAAAAAAAKAOr/AwAoAAIAAGALAOP/AwAqAAAAAAALAOT/AwAqAAEAAAALAOX/AwAeAAMAAAALAOb/AwAiAAUAAAALAOf/AwAqAAYAAAALAOj/AwAgAAUAAAALAOn/AwAfAAAAAAALAOr/AwAoAAMAAGAMAOH/AwAnAAAAAAAMAOL/AwAnAAEAAAAMAOP/AwAnAAAAAAAMAOT/AwAnAAEAAAAMAOX/AwAeAAMAAAAMAOb/AwAeAAcAAAAMAOf/AwAoAAUAAAAMAOj/AwAeAAcAAAAMAOn/AwAfAAAAAAAMAOr/AwAnAAAAAAANAOH/AwAoAAAAAAANAOL/AwAoAAEAAAANAOP/AwAoAAAAAAANAOT/AwAoAAEAAAANAOX/AwAfAAMAAAANAOb/AwAfAAQAAAANAOf/AwAoAAUAAAANAOj/AwAfAAYAAAANAOn/AwAfAAAAAAANAOr/AwAoAAAAAAAOAOH/AwAgAAEAAAAOAOL/AwAgAAEAAAAOAOP/AwAgAAEAAAAOAOT/AwAgAAEAAAAOAOX/AwAgAAIAAAAOAOb/AwAgAAUAAAAOAOf/AwAoAAUAAAAOAOj/AwAiAAQAAAAOAOn/AwAgAAAAAAAOAOr/AwAgAAEAAAAPAOH/AwAfAAQAAAAPAOL/AwAfAAUAAAAPAOP/AwAfAAYAAAAPAOT/AwAfAAcAAAAPAOX/AwAiAAcAAAAPAOb/AwAfAAQAAAAPAOf/AwAoAAUAAAAPAOj/AwAiAAUAAAAPAOn/AwAgAAUAAAAPAOr/AwAgAAUAAAAQAOH/AwApAAYAAAAQAOL/AwApAAYAAAAQAOP/AwApAAYAAAAQAOT/AwApAAYAAAAQAOX/AwApAAYAAAAQAOb/AwApAAYAAAAQAOf/AwAoAAQAAAAQAOj/AwApAAYAAAAQAOn/AwApAAYAAAAQAOr/AwApAAYAAAARAOH/AwAeAAUAAAARAOL/AwAeAAQAAAARAOP/AwAeAAQAAAARAOT/AwAeAAUAAAARAOX/AwAhAAQAAAARAOb/AwAfAAcAAAARAOf/AwAfAAUAAAARAOj/AwAgAAYAAAARAOn/AwAhAAcAAAARAOr/AwAhAAQAAAASAOH/AwAeAAEAAAASAOL/AwAeAAEAAAASAOP/AwAeAAEAAAASAOT/AwAeAAEAAAASAOX/AwAeAAEAAAASAOb/AwAeAAEAAAASAOf/AwAeAAEAAAASAOj/AwAeAAEAAAASAOv/AwAeAAEAAAATAOH/AwAnAAAAAAATAOL/AwAnAAEAAAATAOP/AwAnAAAAAAATAOT/AwAnAAEAAAATAOX/AwAnAAAAAAATAOb/AwAnAAAAAAATAOf/AwAnAAAAAAATAOj/AwAnAAAAAAATAOn/AwAnAAEAAAATAOr/AwAnAAAAAAATAOv/AwAnAAEAAAAUAOH/AwAoAAAAAAAUAOL/AwAoAAEAAAAUAOP/AwAoAAAAAAAUAOT/AwAoAAEAAAAUAOX/AwAoAAAAAAAUAOb/AwAoAAAAAAAUAOf/AwAoAAAAAAAUAOj/AwAoAAAAAAAUAOn/AwAoAAEAAAAUAOr/AwAoAAAAAAAUAOv/AwAoAAEAAAASAOn/AwAeAAEAAAASAOr/AwAeAAEAAAAXABUAAwAnAAAAAAAXABYAAwAnAAEAAAAXABcAAwAeAAMAAAAXABgAAwAhAAUAAAAXABkAAwAqAAYAAAAXABoAAwAhAAQAAAAXABsAAwAfAAAAAAAXABwAAwAnAAIAAAAXAB0AAwAnAAMAAAAYABUAAwAoAAAAAAAYABYAAwAoAAEAAAAYABcAAwAsAAAAAAAYABgAAwAsAAEAAAAYABkAAwAsAAIAAAAYABoAAwAgAAYAAAAYABsAAwAfAAAAAAAYABwAAwAoAAIAAAAYAB0AAwAoAAMAAAAZABUAAwApAAIAAAAZABYAAwApAAMAAAAZABcAAwAtAAAAAAAZABgAAwAtAAEAAAAZABkAAwAtAAIAAAAZABoAAwAeAAUAAAAZABsAAwAfAAAAAAAZABwAAwAoAAIAAGAZAB0AAwAnAAIAAGAaABUAAwAqAAIAAAAaABYAAwAqAAMAAAAaABcAAwAuAAAAAAAaABgAAwAuAAEAAAAaABkAAwAuAAIAAAAaABoAAwAfAAUAAAAaABsAAwAfAAAAAAAaABwAAwAoAAMAAGAaAB0AAwAnAAMAAGAbABUAAwAnAAAAAAAbABYAAwAnAAEAAAAbABcAAwAvAAAAAAAbABgAAwAvAAEAAAAbABkAAwAvAAIAAAAbABoAAwAiAAYAAAAbABsAAwAfAAAAAAAbABwAAwAoAAIAAGAbAB0AAwAnAAIAAGAcABUAAwAoAAAAAAAcABYAAwAoAAEAAAAcABcAAwAwAAAAAAAcABgAAwAwAAEAAAAcABkAAwAwAAIAAAAcABoAAwAfAAUAAAAcABsAAwAfAAAAAAAcABwAAwAoAAMAAGAcAB0AAwAnAAMAAGAdABUAAwApAAAAAAAdABYAAwApAAEAAAAdABcAAwAxAAAAAAAdABgAAwAxAAEAAAAdABkAAwAxAAIAAAAdABoAAwAgAAcAAAAdABsAAwAfAAAAAAAdABwAAwAoAAIAAGAdAB0AAwAnAAIAAGAeABUAAwAqAAAAAAAeABYAAwAqAAEAAAAeABcAAwAyAAAAAAAeABgAAwAyAAEAAAAeABkAAwAyAAIAAAAeABoAAwAeAAYAAAAeABsAAwAfAAAAAAAeABwAAwAoAAMAAGAeAB0AAwAnAAMAAGAfABUAAwApAAAAAAAfABYAAwApAAEAAAAfABcAAwAeAAMAAAAfABgAAwAfAAQAAAAfABkAAwAqAAYAAAAfABoAAwAfAAUAAAAfABsAAwAfAAAAAAAfABwAAwAoAAIAAGAfAB0AAwAnAAIAAGAgABUAAwAqAAAAAAAgABYAAwAqAAEAAAAgABcAAwAeAAMAAAAgABgAAwAiAAUAAAAgABkAAwAqAAYAAAAgABoAAwAgAAUAAAAgABsAAwAfAAAAAAAgABwAAwAoAAMAAGAgAB0AAwAnAAMAAGAhABUAAwAnAAAAAAAhABYAAwAnAAEAAAAhABcAAwAeAAMAAAAhABgAAwAeAAcAAAAhABkAAwAoAAUAAAAhABoAAwAeAAcAAAAhABsAAwAfAAAAAAAhABwAAwAnAAAAAAAhAB0AAwAnAAEAAAAiABUAAwAoAAAAAAAiABYAAwAoAAEAAAAiABcAAwAfAAMAAAAiABgAAwAfAAQAAAAiABkAAwAoAAUAAAAiABoAAwAfAAYAAAAiABsAAwAfAAAAAAAiABwAAwAoAAAAAAAiAB0AAwAoAAEAAAAjABUAAwAgAAEAAAAjABYAAwAgAAEAAAAjABcAAwAgAAIAAAAjABgAAwAgAAUAAAAjABkAAwAoAAUAAAAjABoAAwAiAAQAAAAjABsAAwAgAAAAAAAjABwAAwAgAAEAAAAjAB0AAwAgAAEAAAAkABUAAwAfAAYAAAAkABYAAwAfAAcAAAAkABcAAwAiAAcAAAAkABgAAwAfAAQAAAAkABkAAwAoAAUAAAAkABoAAwAiAAUAAAAkABsAAwAgAAUAAAAkABwAAwAgAAUAAAAkAB0AAwAhAAcAAAAlABUAAwApAAYAAAAlABYAAwApAAYAAAAlABcAAwApAAYAAAAlABgAAwApAAYAAAAlABkAAwAqAAUAAAAlABoAAwApAAYAAAAlABsAAwApAAYAAAAlABwAAwApAAYAAAAlAB0AAwApAAYAAAAmABUAAwAeAAQAAAAmABYAAwAeAAUAAAAmABcAAwAhAAQAAAAmABgAAwAfAAcAAAAmABkAAwAoAAUAAAAmABoAAwAgAAYAAAAmABsAAwAhAAcAAAAmABwAAwAhAAQAAAAmAB0AAwAiAAYAAAAnABUAAwAeAAEAAAAnABYAAwAeAAEAAAAnABcAAwAeAAIAAAAnABgAAwAgAAQAAAAnABkAAwAoAAUAAAAnABoAAwAfAAQAAAAnABsAAwAeAAAAAAAnABwAAwAeAAEAAAAnAB0AAwAeAAEAAAAoABUAAwAnAAAAAAAoABYAAwAnAAEAAAAoABcAAwAeAAMAAAAoABgAAwAeAAQAAAAoABkAAwAoAAUAAAAoABoAAwAeAAYAAAAoABsAAwAfAAAAAAAoABwAAwAnAAAAAAAoAB0AAwAnAAEAAAApABUAAwAoAAAAAAApABYAAwAoAAEAAAApABcAAwAeAAMAAAApABgAAwAfAAQAAAApABkAAwAoAAUAAAApABoAAwAfAAYAAAApABsAAwAfAAAAAAApABwAAwAoAAAAAAApAB0AAwAoAAEAAAAqABUAAwAnAAAAAAAqABYAAwAnAAEAAAAqABcAAwAeAAMAAAAqABgAAwAgAAQAAAAqABkAAwAoAAUAAAAqABoAAwAgAAYAAAAqABsAAwAfAAAAAAAqABwAAwAnAAAAAAAqAB0AAwAnAAEAAAArABUAAwAoAAAAAAArABYAAwAoAAEAAAArABcAAwAfAAMAAAArABgAAwAhAAQAAAArABkAAwAoAAUAAAArABoAAwAhAAYAAAArABsAAwAfAAAAAAArABwAAwAoAAAAAAArAB0AAwAoAAEAAAAsABUAAwAoAAAAAAAsABYAAwAoAAEAAAAsABcAAwAeAAMAAAAsABgAAwAiAAQAAAAsABkAAwAoAAUAAAAsABoAAwAiAAYAAAAsABsAAwAfAAAAAAAsABwAAwAoAAAAAAAsAB0AAwAoAAEAAAAtABUAAwAnAAAAAAAtABYAAwAnAAEAAAAtABcAAwAeAAMAAAAtABgAAwAgAAQAAAAtABkAAwAoAAUAAAAtABoAAwAgAAYAAAAtABsAAwAfAAAAAAAtABwAAwAnAAAAAAAtAB0AAwAnAAEAAAAuABUAAwAoAAAAAAAuABYAAwAoAAEAAAAuABcAAwAfAAMAAAAuABgAAwAhAAQAAAAuABkAAwAoAAUAAAAuABoAAwAhAAYAAAAuABsAAwAfAAAAAAAuABwAAwAoAAAAAAAuAB0AAwAoAAEAAAAvABUAAwAnAAAAAAAvABYAAwAnAAEAAAAvABcAAwAeAAMAAAAvABgAAwAiAAQAAAAvABkAAwAoAAUAAAAvABoAAwAiAAYAAAAvABsAAwAfAAAAAAAvABwAAwAnAAAAAAAvAB0AAwAnAAEAAAAwABUAAwAoAAAAAAAwABYAAwAoAAEAAAAwABcAAwAeAAMAAAAwABgAAwAeAAQAAAAwABkAAwAoAAUAAAAwABoAAwAeAAYAAAAwABsAAwAfAAAAAAAwABwAAwAoAAAAAAAwAB0AAwAoAAEAAAAxABUAAwAnAAAAAAAxABYAAwAnAAEAAAAxABcAAwAeAAMAAAAxABgAAwAfAAQAAAAxABkAAwAoAAUAAAAxABoAAwAfAAYAAAAxABsAAwAfAAAAAAAxABwAAwAnAAAAAAAxAB0AAwAnAAEAAAAyABUAAwAoAAAAAAAyABYAAwAoAAEAAAAyABcAAwAfAAMAAAAyABgAAwAgAAQAAAAyABkAAwAoAAUAAAAyABoAAwAgAAYAAAAyABsAAwAfAAAAAAAyABwAAwAoAAAAAAAyAB0AAwAoAAEAAAAzABUAAwAnAAAAAAAzABYAAwAnAAEAAAAzABcAAwAeAAMAAAAzABgAAwAhAAQAAAAzABkAAwAoAAUAAAAzABoAAwAhAAYAAAAzABsAAwAfAAAAAAAzABwAAwAnAAAAAAAzAB0AAwAnAAEAAAA0ABUAAwAoAAAAAAA0ABYAAwAoAAEAAAA0ABcAAwAeAAMAAAA0ABgAAwAiAAQAAAA0ABkAAwAoAAUAAAA0ABoAAwAiAAYAAAA0ABsAAwAfAAAAAAA0ABwAAwAoAAAAAAA0AB0AAwAoAAEAAAA1ABUAAwAnAAAAAAA1ABYAAwAnAAEAAAA1ABcAAwAeAAMAAAA1ABgAAwAgAAQAAAA1ABkAAwAoAAUAAAA1ABoAAwAgAAYAAAA1ABsAAwAfAAAAAAA1ABwAAwAnAAAAAAA1AB0AAwAnAAEAAAA2ABUAAwAoAAAAAAA2ABYAAwAoAAEAAAA2ABcAAwAfAAMAAAA2ABgAAwAhAAQAAAA2ABkAAwAoAAUAAAA2ABoAAwAhAAYAAAA2ABsAAwAfAAAAAAA2ABwAAwAoAAAAAAA2AB0AAwAoAAEAAAA3ABUAAwAnAAAAAAA3ABYAAwAnAAEAAAA3ABcAAwAeAAMAAAA3ABgAAwAiAAQAAAA3ABkAAwAoAAUAAAA3ABoAAwAiAAYAAAA3ABsAAwAfAAAAAAA3ABwAAwAnAAAAAAA3AB0AAwAnAAEAAAA4ABUAAwAoAAAAAAA4ABYAAwAoAAEAAAA4ABcAAwAeAAMAAAA4ABgAAwAeAAQAAAA4ABkAAwAoAAUAAAA4ABoAAwAeAAYAAAA4ABsAAwAfAAAAAAA4ABwAAwAoAAAAAAA4AB0AAwAoAAEAAAA5ABUAAwAnAAAAAAA5ABYAAwAnAAEAAAA5ABcAAwAeAAMAAAA5ABgAAwAfAAQAAAA5ABkAAwAoAAUAAAA5ABoAAwAfAAYAAAA5ABsAAwAfAAAAAAA5ABwAAwAnAAAAAAA5AB0AAwAnAAEAAAA6ABUAAwAoAAAAAAA6ABYAAwAoAAEAAAA6ABcAAwAfAAMAAAA6ABgAAwAgAAQAAAA6ABkAAwAoAAUAAAA6ABoAAwAgAAYAAAA6ABsAAwAfAAAAAAA6ABwAAwAoAAAAAAA6AB0AAwAoAAEAAAA7ABUAAwAnAAAAAAA7ABYAAwAnAAEAAAA7ABcAAwAeAAMAAAA7ABgAAwAhAAQAAAA7ABkAAwAoAAUAAAA7ABoAAwAhAAYAAAA7ABsAAwAfAAAAAAA7ABwAAwAnAAAAAAA7AB0AAwAnAAEAAAA8ABUAAwAoAAAAAAA8ABYAAwAoAAEAAAA8ABcAAwAeAAMAAAA8ABgAAwAiAAQAAAA8ABkAAwAoAAUAAAA8ABoAAwAiAAYAAAA8ABsAAwAfAAAAAAA8ABwAAwAoAAAAAAA8AB0AAwAoAAEAAAA9ABUAAwAnAAAAAAA9ABYAAwAnAAEAAAA9ABcAAwAeAAMAAAA9ABgAAwAgAAQAAAA9ABkAAwAoAAUAAAA9ABoAAwAgAAYAAAA9ABsAAwAfAAAAAAA9ABwAAwAnAAAAAAA9AB0AAwAnAAEAAAA+ABUAAwAoAAAAAAA+ABYAAwAoAAEAAAA+ABcAAwAfAAMAAAA+ABgAAwAhAAQAAAA+ABkAAwAoAAUAAAA+ABoAAwAhAAYAAAA+ABsAAwAfAAAAAAA+ABwAAwAoAAAAAAA+AB0AAwAoAAEAAAA/ABUAAwAnAAAAAAA/ABYAAwAnAAEAAAA/ABcAAwAeAAMAAAA/ABgAAwAiAAQAAAA/ABkAAwAoAAUAAAA/ABoAAwAiAAYAAAA/ABsAAwAfAAAAAAA/ABwAAwAnAAAAAAA/AB0AAwAnAAEAAABAABUAAwAoAAAAAABAABYAAwAoAAEAAABAABcAAwAeAAMAAABAABgAAwAeAAQAAABAABkAAwAoAAUAAABAABoAAwAeAAYAAABAABsAAwAfAAAAAABAABwAAwAoAAAAAABAAB0AAwAoAAEAAABBABUAAwAnAAAAAABBABYAAwAnAAEAAABBABcAAwAeAAMAAABBABgAAwAfAAQAAABBABkAAwAoAAUAAABBABoAAwAfAAYAAABBABsAAwAfAAAAAABBABwAAwAnAAAAAABBAB0AAwAnAAEAAABCABUAAwAoAAAAAABCABYAAwAoAAEAAABCABcAAwAfAAMAAABCABgAAwAgAAQAAABCABkAAwAoAAUAAABCABoAAwAgAAYAAABCABsAAwAfAAAAAABCABwAAwAoAAAAAABCAB0AAwAoAAEAAABDABUAAwAnAAAAAABDABYAAwAnAAEAAABDABcAAwAeAAMAAABDABgAAwAhAAQAAABDABkAAwAoAAUAAABDABoAAwAhAAYAAABDABsAAwAfAAAAAABDABwAAwAnAAAAAABDAB0AAwAnAAEAAABEABUAAwAoAAAAAABEABYAAwAoAAEAAABEABcAAwAeAAMAAABEABgAAwAiAAQAAABEABkAAwAoAAUAAABEABoAAwAiAAYAAABEABsAAwAfAAAAAABEABwAAwAoAAAAAABEAB0AAwAoAAEAAAACAOP/AwAoAAAAAAACAOT/AwAoAAEAAAACAOX/AwAeAAMAAAACAOb/AwAiAAQAAAACAOf/AwAoAAUAAAACAOj/AwAiAAYAAAACAOn/AwAhAAYAAAACAOr/AwAgAAYAAAACAOv/AwAiAAkAAAADAOP/AwAoAAAAAAADAOT/AwAoAAEAAAADAOX/AwAfAAMAAAADAOb/AwAhAAQAAAADAOf/AwAoAAUAAAADAOj/AwAhAAYAAAADAOn/AwAeAAAAAAADAOr/AwAmAAkAAAADAOv/AwAmAAkAAAAEAOP/AwAnAAAAAAAEAOT/AwAnAAEAAAAEAOX/AwAeAAMAAAAEAOb/AwAiAAQAAAAEAOf/AwAoAAUAAAAEAOj/AwAiAAYAAAAEAOn/AwAfAAAAAAAEAOr/AwAnAAAAAAAEAOv/AwAnAAEAAAAFAOP/AwAoAAAAAAAFAOT/AwAoAAEAAAAFAOX/AwAeAAMAAAAFAOb/AwAeAAQAAAAFAOf/AwAoAAUAAAAFAOj/AwAeAAYAAAAFAOn/AwAfAAAAAAAFAOr/AwAoAAAAAAAGAOP/AwAnAAAAAAAGAOT/AwAnAAEAAAAGAOX/AwAeAAMAAAAGAOb/AwAfAAQAAAAGAOf/AwAoAAUAAAAGAOj/AwAfAAYAAAAGAOn/AwAfAAAAAAAGAOr/AwAoAAAAAAAHAOP/AwAoAAAAAAAHAOT/AwAoAAEAAAAHAOX/AwAfAAMAAAAHAOb/AwAgAAQAAAAHAOf/AwAoAAUAAAAHAOj/AwAgAAYAAAAHAOn/AwAfAAAAAAAHAOr/AwAoAAAAAAAIAOP/AwAnAAAAAAAIAOT/AwAnAAEAAAAIAOX/AwAeAAMAAAAIAOb/AwAhAAQAAAAIAOf/AwAoAAUAAAAIAOj/AwAhAAYAAAAIAOn/AwAfAAAAAAAIAOr/AwAnAAAAAAAJAOP/AwAoAAAAAAAJAOT/AwAoAAEAAAAJAOX/AwAeAAMAAAAJAOb/AwAiAAQAAAAJAOf/AwAoAAUAAAAJAOj/AwAiAAYAAAAJAOn/AwAfAAAAAAAJAOr/AwAoAAAAAAD7/+P/AwAoAAAAAAD7/+T/AwAoAAEAAAD7/+X/AwAeAAMAAAD7/+b/AwAiAAQAAAD7/+f/AwAoAAUAAAD7/+j/AwAiAAYAAAD7/+n/AwAfAAAAAAD7/+r/AwAoAAAAAAD7/+v/AwAoAAEAAAD8/+P/AwAoAAAAAAD8/+T/AwAoAAEAAAD8/+X/AwAjAAQAAAD8/+b/AwAlAAUAAAD8/+f/AwAlAAQAAAD8/+j/AwAlAAUAAAD8/+n/AwAjAAUAAAD8/+r/AwAnAAAAAAD8/+v/AwAnAAEAAAD9/+P/AwAnAAAAAAD9/+T/AwAnAAEAAAD9/+X/AwAkAAQAAAD9/+b/AwAmAAUAAAD9/+f/AwAmAAQAAAD9/+j/AwAmAAUAAAD9/+n/AwAkAAUAAAD9/+r/AwAoAAAAAAD9/+v/AwAoAAEAAAD+/+P/AwAoAAAAAAD+/+T/AwAoAAEAAAD+/+X/AwAeAAMAAAD+/+b/AwAeAAQAAAD+/+f/AwAoAAUAAAD+/+j/AwAeAAYAAAD+/+n/AwAgAAAAAAD+/+r/AwAgAAEAAAD+/+v/AwAeAAkAAAD//+P/AwAnAAAAAAD//+T/AwAnAAEAAAD//+X/AwAeAAMAAAD//+b/AwAfAAQAAAD//+f/AwAoAAUAAAD//+j/AwAfAAYAAAD//+n/AwAeAAUAAAD//+r/AwAfAAUAAAD//+v/AwAfAAYAAAAAAOP/AwAoAAAAAAAAAOT/AwAoAAEAAAAAAOX/AwAfAAMAAAAAAOb/AwAgAAQAAAAAAOf/AwAoAAUAAAAAAOj/AwAgAAYAAAAAAOn/AwAfAAUAAAAAAOr/AwAgAAkAAAAAAOv/AwAgAAoAAAABAOP/AwAnAAAAAAABAOT/AwAnAAEAAAABAOX/AwAeAAMAAAABAOb/AwAhAAQAAAABAOf/AwAoAAUA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AAAgAAQAAwAvABIAAAAgAAUAAwAvABMAAAAgAAYAAwAvABQAAAAgAAcAAwAvABUAAAAhAAIAAwAwABAAAAAhAAMAAwAwABEAAAAhAAQAAwAwABIAAAAhAAUAAwAwABMAAAAhAAYAAwAwABQAAAAhAAcAAwAwABUAAAAiAAIAAwAxABAAAAAiAAMAAwAxABEAAAAiAAQAAwAxABIAAAAiAAUAAwAxABMAAAAiAAYAAwAxABQAAAAiAAcAAwAxABUAAAAjAAMAAwAyABIAAAAjAAQAAwAyABIAAAAjAAUAAwAyABMAAAAjAAYAAwAyABQAAAAjAAcAAwAyABUAAAAhAPH/AwAoAAAAAGAhAPL/AwAnAAAAAGAhAPP/AwAoAAAAAGAhAPT/AwAnAAAAAGAhAPX/AwAoAAAAAGAhAPb/AwAnAAAAAGAhAPf/AwAoAAAAAGAhAPj/AwAnAAAAAGAiAPH/AwAoAAEAAGAiAPL/AwAnAAEAAGAiAPP/AwAoAAEAAGAiAPT/AwAnAAEAAGAiAPX/AwAoAAEAAGAiAPb/AwAnAAEAAGAiAPf/AwAoAAEAAGAiAPj/AwAnAAEAAGAjAPH/AwAgAAEAAAAjAPL/AwAgAAEAAAAjAPP/AwAgAAEAAAAjAPT/AwAgAAEAAAAjAPX/AwAgAAEAAAAjAPb/AwAgAAEAAAAjAPf/AwAgAAEAAAAjAPj/AwAgAAEAAAAkAPH/AwAiAAQAAGAkAPL/AwAhAAQAAGAkAPP/AwAgAAQAAGAkAPT/AwAfAAQAAGAkAPX/AwAeAAQAAGAkAPb/AwAiAAQAAGAkAPf/AwAhAAQAAGAkAPj/AwAgAAQAAGAlAPH/AwAoAAUAAGAlAPL/AwAoAAUAAGAlAPP/AwAoAAUAAGAlAPT/AwAoAAUAAGAlAPX/AwAoAAUAAGAlAPb/AwAoAAUAAGAlAPf/AwAoAAUAAGAlAPj/AwAoAAUAAGAmAPH/AwAiAAYAAGAmAPL/AwAhAAYAAGAmAPP/AwAgAAYAAGAmAPT/AwAfAAYAAGAmAPX/AwAeAAYAAGAmAPb/AwAiAAYAAGAmAPf/AwAhAAYAAGAmAPj/AwAgAAYAAGAnAPH/AwAeAAEAAAAnAPL/AwAeAAEAAAAnAPP/AwAeAAEAAAAnAPT/AwAeAAEAAAAnAPX/AwAeAAEAAAAnAPb/AwAeAAEAAAAnAPf/AwAeAAEAAAAnAPj/AwAeAAEAAAAoAPH/AwAoAAAAAGAoAPL/AwAnAAAAAGAoAPP/AwAoAAAAAGAoAPT/AwAnAAAAAGAoAPX/AwAoAAAAAGAoAPb/AwAnAAAAAGAoAPf/AwAoAAAAAGAoAPj/AwAnAAAAAGApAPH/AwAoAAEAAGApAPL/AwAnAAEAAGApAPP/AwAoAAEAAGApAPT/AwAnAAEAAGApAPX/AwAoAAEAAGApAPb/AwAnAAEAAGApAPf/AwAoAAEAAGApAPj/AwAnAAEAAGAoAPn/AwA1AAkAAAAoAPr/AwA1AAoAAAAoAPv/AwA1AAsAAAAoAPz/AwA1AAwAAAAoAP3/AwA1AA0AAAAoAP7/AwA1AA4AAAApAPn/AwA2AAkAAAApAPr/AwA2AAoAAAApAPv/AwA2AAsAAAApAPz/AwA2AAwAAAApAP3/AwA2AA0AAAApAP7/AwA2AA4AAAAqAPn/AwA3AAkAAAAqAPr/AwA3AAoAAAAqAPv/AwA3AAsAAAAqAPz/AwA3AAwAAAAqAP3/AwA3AA0AAAAqAP7/AwA3AA4AAAArAPn/AwA4AAkAAAArAPr/AwA4AAoAAAArAPv/AwA4AAsAAAArAPz/AwA4AAwAAAArAP3/AwA4AA0AAAArAP7/AwA4AA4AAAAsAPn/AwA5AAkAAAAsAPr/AwA5AAoAAAAsAPv/AwA5AAsAAAAsAPz/AwA5AAwAAAAsAP3/AwA5AA0AAAAsAP7/AwA5AA4AAAAtAPn/AwA6AAkAAAAtAPr/AwA6AAoAAAAtAPv/AwA6AAsAAAAtAPz/AwA6AAwAAAAtAP3/AwA6AA0AAAAtAP7/AwAeAAMAAAAhAAgAAwAoAAAAAGAhAAkAAwAnAAAAAGAhAAoAAwAoAAAAAGAhAAsAAwAnAAAAAGAhAAwAAwAoAAAAAGAhAA0AAwAnAAAAAGAhAA4AAwAoAAAAAGAhAA8AAwAnAAAAAAAiAAgAAwAoAAEAAGAiAAkAAwAnAAEAAGAiAAoAAwAoAAEAAGAiAAsAAwAnAAEAAGAiAAwAAwAoAAEAAGAiAA0AAwAnAAEAAGAiAA4AAwAoAAEAAGAiAA8AAwAoAAAAAAAjAAgAAwAgAAEAAAAjAAkAAwAgAAEAAAAjAAoAAwAgAAEAAAAjAAsAAwAgAAEAAAAjAAwAAwAgAAEAAAAjAA0AAwAgAAEAAAAjAA4AAwAgAAEAAAAjAA8AAwAgAAEAAAAkAAgAAwAiAAQAAGAkAAkAAwAhAAQAAGAkAAoAAwAgAAQAAGAkAAsAAwAfAAQAAGAkAAwAAwAeAAQAAGAkAA0AAwAiAAQAAGAkAA4AAwAhAAQAAGAkAA8AAwAfAAQAAAAlAAgAAwAoAAUAAGAlAAkAAwAoAAUAAGAlAAoAAwAoAAUAAGAlAAsAAwAoAAUAAGAlAAwAAwAoAAUAAGAlAA0AAwAoAAUAAGAlAA4AAwAoAAUAAGAlAA8AAwApAAYAAAAmAAgAAwAiAAYAAGAmAAkAAwAhAAYAAGAmAAoAAwAgAAYAAGAmAAsAAwAfAAYAAGAmAAwAAwAeAAYAAGAmAA0AAwAiAAYAAGAmAA4AAwAhAAYAAGAmAA8AAwAfAAYAAAAnAAgAAwAeAAEAAAAnAAkAAwAeAAEAAAAnAAoAAwAeAAEAAAAnAAsAAwAeAAEAAAAnAAwAAwAeAAEAAAAnAA0AAwAeAAEAAAAnAA4AAwAeAAEAAAAnAA8AAwAeAAEAAAAoAAgAAwAoAAAAAGAoAAkAAwAnAAAAAGAoAAoAAwAoAAAAAGAoAAsAAwAnAAAAAGAoAAwAAwAoAAAAAGAoAA0AAwAnAAAAAGAoAA4AAwAoAAAAAGAoAA8AAwAnAAAAAAApAAgAAwAoAAEAAGApAAkAAwAnAAEAAGApAAoAAwAoAAEAAGApAAsAAwAnAAEAAGApAAwAAwAoAAEAAGApAA0AAwAnAAEAAGApAA4AAwAoAAEAAGApAA8AAwAoAAAAAAAnAAMAAwAzABIAAAAnAAQAAwA0ABIAAAAnAAUAAwA0ABMAAAAnAAYAAwA0ABQAAAAnAAcAAwA0ABUAAAAoAAIAAwAyABIAAAAoAAMAAwA0ABIAAAAoAAQAAwA1ABIAAAAoAAUAAwA1ABMAAAAoAAYAAwA1ABQAAAAoAAcAAwA1ABUAAAApAAIAAwAzABIAAAApAAMAAwA2ABEAAAApAAQAAwA2ABIAAAApAAUAAwA2ABMAAAApAAYAAwA2ABQAAAApAAcAAwA2ABUAAAAqAAIAAwA0ABIAAAAqAAMAAwA3ABEAAAAqAAQAAwA3ABIAAAAqAAUAAwA3ABMAAAAqAAYAAwA3ABQAAAAqAAcAAwA3ABUAAAArAAIAAwAyABIAAAArAAMAAwA4ABEAAAArAAQAAwA4ABIAAAArAAUAAwA4ABMAAAArAAYAAwA4ABQAAAArAAcAAwA4ABUAAAAsAAIAAwAzABIAAAAsAAMAAwA5ABEAAAAsAAQAAwA5ABIAAAAsAAUAAwA5ABMAAAAsAAYAAwA5ABQAAAAsAAcAAwA5ABUAAAAtAAIAAwA0ABIAAAAtAAMAAwA6ABEAAAAtAAQAAwA6ABIAAAAtAAUAAwA6ABMAAAAtAAYAAwA6ABQAAAAtAAcAAwA6ABUAAAAnAPn/AwA0AAkAAAAnAPr/AwA0AAoAAAAnAPv/AwA0AAsAAAAnAPz/AwA0AAwAAAAnAP3/AwA0AA0AAAAjAP7/AwAiAA0AAAAjAP//AwAiAA4AAAAjAAAAAwAiAA8AAAAjAAEAAwAiABAAAAAkAP7/AwAjAA0AAAAkAP//AwAjAA4AAAAkAAAAAwAjAA8AAAAkAAEAAwAjABAAAAAlAP7/AwAlAA0AAAAlAP//AwAlAA4AAAAlAAAAAwAlAA8AAAAlAAEAAwAlABAAAAAmAP7/AwAgAA0AAAAmAP//AwAgAA4AAAAmAAAAAwAgAA8AAAAmAAEAAwAgABAAAAAnAP7/AwAhAA0AAAAnAP//AwAhAA4AAAAnAAAAAwAhAA8AAAAnAAEAAwAhABAAAAAjAAIAAwAyABEAAAAkAAIAAwAzABEAAAAkAAMAAwAzABIAAAAlAAIAAwA0ABEAAAAlAAMAAwA0ABIAAAAmAAIAAwAyABEAAAAmAAMAAwAyABIAAAAnAAIAAwAzABEAAAAkAAQAAwAyABEAAAAkAAUAAwAyABIAAAAlAAQAAwAzABEAAAAlAAUAAwAzABIAAAAmAAQAAwA0ABEAAAAmAAUAAwA0ABIAAAAdAP//AwAyABEAAAAdAAAAAwAoAAUAAAAeAP//AwAzABEAAAAeAAAAAwAzABEAAAAfAP//AwAyABEAAAAfAAAAAwA0ABEAAAAgAP//AwAyABEAAAAgAAAAAwAyABIAAAAhAP//AwAzABEAAAAhAAAAAwAzABIAAAAgAAEAAwAyABIAAAAhAAEAAwAzABIAAAAiAAAAAwA0ABIAAAAiAAEAAwA0ABIAAAAdAAEAAwAyABIAAAAeAAEAAwAzABIAAAAfAAEAAwA0ABIAAAAiAP//AwA0ABEAAAAkAPn/AwAyABEAAAAkAPr/AwAyABIAAAAlAPn/AwAzABEAAAAlAPr/AwAzABIAAAAmAPn/AwA0ABEAAAAmAPr/AwA0ABIAAAAkAPv/AwAyABEAAAAkAPz/AwAyABEAAAAlAPv/AwAzABEAAAAlAPz/AwAzABEAAAAmAPv/AwA0ABEAAAAmAPz/AwA0ABEAAAAkAP3/AwAyABIAAAAlAP3/AwAzABIAAAAmAP3/AwA0ABIAAAAoAP//AwAyABEAAAAoAAAAAwAyABIAAAApAP//AwAzABEAAAApAAAAAwAzABIAAAAqAP//AwA0ABEAAAAqAAAAAwA0ABIAAAArAP//AwAyABEAAAArAAAAAwAyABIAAAAsAP//AwAzABEAAAAtAP//AwA0ABEAAAAtAAAAAwAoAAUAAAArAAEAAwAyABEAAAAsAAEAAwAzABEAAAAtAAEAAwA0ABEAAAAoAAEAAwAyABEAAAApAAEAAwAzABEAAAAqAAEAAwA0ABEAAAAkAAYAAwAyABEAAAAkAAcAAwAyABIAAAAlAAYAAwAzABEAAAAlAAcAAwAzABIAAAAmAAYAAwA0ABEAAAAmAAcAAwA0ABIAAAAsAAAAAwAzABIAAADl//3/AwAoAAEAAADl//7/AwAeAAMAAADl////AwAiAAQAAADl/wAAAwAoAAUAAADl/wEAAwAiAAYAAADl/wIAAwAfAAAAAADl/wMAAwAoAAAAAADl/wQAAwAoAAEAAADl/wUAAwAsABMAAADl/wYAAwAsABQAAADl/wcAAwAsABUAAADm//n/AgAcAAoAAADm//r/AgAcAAoAAADm//v/AwAtAAsAAADm//z/AwAtAAwAAADm//3/AwAtAA0AAADm//7/AwAtAA4AAADm////AwAzABEAAADm/wAAAwAzABEAAADm/wEAAwAzABIAAADm/wIAAwAtABAAAADm/wMAAwAtABEAAADm/wQAAwAtABIAAADm/wUAAwAtABMAAADm/wYAAwAtABQAAADm/wcAAwAtABUAAADn//n/AgAbAAoAAADn//r/AwAuAAoAAADn//v/AwAuAAsAAADn//z/AwAuAAwAAADn//3/AwAuAA0AAADn//7/AwAuAA4AAADn////AwAyABEAAADn/wAAAwA0ABEAAADn/wEAAwA0ABIAAADn/wIAAwAuABAAAADn/wMAAwAuABEAAADn/wQAAwAuABIAAADn/wUAAwAuABMAAADn/wYAAwAuABQAAADn/wcAAwAuABUAAADo//n/AwAvAAkAAADo//r/AwAvAAoAAADo//v/AwAvAAsAAADo//z/AwAvAAwAAADo//3/AwAvAA0AAADo//7/AwAvAA4AAADo////AwAyABEAAADo/wAAAwAyABIAAADo/wEAAwAyABIAAADo/wIAAwAvABAAAADo/wMAAwAvABEAAADo/wQAAwAvABIAAADo/wUAAwAvABMAAADo/wYAAwAvABQAAADo/wcAAwAvABUAAADp//n/AwAwAAkAAADp//r/AwAwAAoAAADp//v/AwAwAAsAAADp//z/AwAwAAwAAADp//3/AwAwAA0AAADp//7/AwAwAA4AAADp////AwAzABEAAADp/wAAAwAzABIAAADp/wEAAwAzABIAAADp/wIAAwAwABAAAADp/wMAAwAwABEAAADp/wQAAwAwABIAAADp/wUAAwAwABMAAADp/wYAAwAwABQAAADp/wcAAwAwABUAAADq//n/AwAxAAkAAADq//r/AwAxAAoAAADq//v/AwAxAAsAAADq//z/AwAxAAwAAADq//3/AwAxAA0AAADq//7/AwAxAA4AAADq////AwA0ABEAAADq/wAAAwA0ABIAAADq/wEAAwA0ABIAAADq/wIAAwAxABAAAADq/wMAAwAxABEAAADq/wQAAwAxABIAAADq/wUAAwAxABMAAADq/wYAAwAxABQAAADq/wcAAwAxABUAAADr//n/AwAyAAkAAADr//r/AwAyAAoAAADr//v/AwAyAAsAAADr//z/AwAyAAwAAADr//3/AwAyAA0AAADr//7/AwAiAA0AAADr////AwAiAA4AAADr/wAAAwAiAA8AAADr/wEAAwAiABAAAADr/wIAAwAyABEAAADr/wMAAwAyABIAAADr/wQAAwAyABIAAADr/wUAAwAyABMAAADr/wYAAwAyABQAAADr/wcAAwAyABUAAADs//n/AwAyABEAAADs//r/AwAyABIAAADs//v/AwAyABEAAADs//z/AwAyABEAAADs//3/AwAyABIAAADs//7/AwAjAA0AAADs////AwAjAA4AAADs/wAAAwAjAA8AAADs/wEAAwAjABAAAADs/wIAAwAzABEAAADs/wMAAwAzABIAAADs/wQAAwAyABEAAADs/wUAAwAyABIAAADs/wYAAwAyABEAAADs/wcAAwAyABIAAADt//n/AwAzABEAAADt//r/AwAzABIAAADt//v/AwAzABEAAADt//z/AwAzABEAAADt//3/AwAzABIAAADt//7/AwAlAA0AAADt////AwAlAA4AAADt/wAAAwAlAA8AAADt/wEAAwAlABAAAADt/wIAAwA0ABEAAADt/wMAAwA0ABIAAADt/wQAAwAzABEAAADt/wUAAwAzABIAAADt/wYAAwAzABEAAADt/wcAAwAzABIAAADu//n/AwA0ABEAAADu//r/AwA0ABIAAADu//v/AwA0ABEAAADu//z/AwA0ABEAAADu//3/AwA0ABIAAADu//7/AwAoAA0AAADu////AwAoAA4AAADu/wAAAwAoAA8AAADu/wEAAwAoABAAAADu/wIAAwAyABEAAADu/wMAAwAyABIAAADu/wQAAwA0ABEAAADu/wUAAwA0ABIAAADu/wYAAwA0ABEAAADu/wcAAwA0ABIAAADv//n/AwA0AAkAAADv//r/AwA0AAoAAADv//v/AwA0AAsAAADv//z/AwA0AAwAAADv//3/AwA0AA0AAADv//7/AwApAA0AAADv////AwApAA4AAADv/wAAAwApAA8AAADv/wEAAwApABAAAADv/wIAAwAzABEAAADv/wMAAwAzABIAAADv/wQAAwA0ABIAAADv/wUAAwA0ABMAAADv/wYAAwA0ABQAAADv/wcAAwA0ABUAAADw//n/AwA1AAkAAADw//r/AwA1AAoAAADw//v/AwA1AAsAAADw//z/AwA1AAwAAADw//3/AwA1AA0AAADw//7/AwA1AA4AAADw////AwAyABEAAADw/wAAAwAyABIAAADw/wEAAwAyABEAAADw/wIAAwAyABIAAADw/wMAAwA0ABIAAADw/wQAAwA1ABIAAADw/wUAAwA1ABMAAADw/wYAAwA1ABQAAADw/wcAAwA1ABUAAADx//n/AwA2AAkAAADx//r/AwA2AAoAAADx//v/AwA2AAsAAADx//z/AwA2AAwAAADx//3/AwA2AA0AAADx//7/AwA2AA4AAADx////AwAzABEAAADx/wAAAwAzABIAAADx/wEAAwAzABEAAADx/wIAAwAzABIAAADx/wMAAwA2ABEAAADx/wQAAwA2ABIAAADx/wUAAwA2ABMAAADx/wYAAwA2ABQAAADx/wcAAwA2ABUAAADy//n/AwAnAAEAAADy//r/AwA3AAoAAADy//v/AwA3AAsAAADy//z/AwA3AAwAAADy//3/AwA3AA0AAADy//7/AwA3AA4AAADy////AwA0ABEAAADy/wAAAwA0ABIAAADy/wEAAwA0ABEAAADy/wIAAwA0ABIAAADy/wMAAwA3ABEAAADy/wQAAwA3ABIAAADy/wUAAwA3ABMAAADy/wYAAwA3ABQAAADy/wcAAwA3ABUAAADz//r/AwA4AAoAAADz//v/AwA4AAsAAADz//z/AwA4AAwAAADz//3/AwA4AA0AAADz//7/AwA4AA4AAADz////AwAyABEAAADz/wAAAwAyABIAAADz/wEAAwAyABEAAADz/wIAAwAyABIAAADz/wMAAwA4ABEAAADz/wQAAwA4ABIAAADz/wUAAwA4ABMAAADz/wYAAwA4ABQAAADz/wcAAwA4ABUAAAD0//7/AwA5AA4AAAD0////AwAzABEAAAD0/wAAAwAzABIAAAD0/wEAAwAzABEAAAD0/wIAAwAeAAAAAAD0/wMAAwA5ABEAAAD0/wQAAwA5ABIAAAD0/wUAAwA5ABMAAAD0/wYAAwA5ABQAAAD0/wcAAwA5ABUAAAD1//7/AwAeAAMAAAD1////AwA0ABEAAAD1/wAAAwAoAAUAAAD1/wEAAwA0ABEAAAD1/wIAAwAfAAAAAAD1/wMAAwA6ABEAAAD1/wQAAwA6ABIAAAD1/wUAAwA6ABMAAAD1/wYAAwA6ABQAAAD1/wcAAwA6ABUAAADp//H/AwAnAAAAAGDp//L/AwAnAAAAAGDp//P/AwAoAAAAAGDp//T/AwAnAAAAAGDp//X/AwAoAAAAAGDp//b/AwAnAAAAAGDp//f/AwAoAAAAAGDp//j/AwAnAAAAAGDq//H/AwAnAAEAAGDq//L/AwAnAAEAAGDq//P/AwAoAAEAAGDq//T/AwAnAAEAAGDq//X/AwAoAAEAAGDq//b/AwAnAAEAAGDq//f/AwAoAAEAAGDq//j/AwAnAAEAAGDr//H/AwAgAAEAAADr//L/AwAgAAEAAADr//P/AwAgAAEAAADr//T/AwAgAAEAAADr//X/AwAgAAEAAADr//b/AwAgAAEAAADr//f/AwAgAAEAAADr//j/AwAgAAEAAADs//H/AwAgAAQAAGDs//L/AwAhAAQAAGDs//P/AwAgAAQAAGDs//T/AwAfAAQAAGDs//X/AwAeAAQAAGDs//b/AwAiAAQAAGDs//f/AwAhAAQAAGDs//j/AwAgAAQAAGDt//H/AwAoAAUAAGDt//L/AwAoAAUAAGDt//P/AwAoAAUAAGDt//T/AwAoAAUAAGDt//X/AwAoAAUAAGDt//b/AwAoAAUAAGDt//f/AwAoAAUAAGDt//j/AwAoAAUAAGDu//H/AwAgAAYAAGDu//L/AwAhAAYAAGDu//P/AwAgAAYAAGDu//T/AwAfAAYAAGDu//X/AwAeAAYAAGDu//b/AwAiAAYAAGDu//f/AwAhAAYAAGDu//j/AwAgAAYAAGDv//H/AwAeAAEAAADv//L/AwAeAAEAAADv//P/AwAeAAEAAADv//T/AwAeAAEAAADv//X/AwAeAAEAAADv//b/AwAeAAEAAADv//f/AwAeAAEAAADv//j/AwAeAAEAAADw//H/AwAnAAAAAGDw//L/AwAnAAAAAGDw//P/AwAoAAAAAGDw//T/AwAnAAAAAGDw//X/AwAoAAAAAGDw//b/AwAnAAAAAGDw//f/AwAoAAAAAGDw//j/AwAnAAAAAGDx//H/AwAnAAEAAGDx//L/AwAnAAEAAGDx//P/AwAoAAEAAGDx//T/AwAnAAEAAGDx//X/AwAoAAEAAGDx//b/AwAnAAEAAGDx//f/AwAoAAEAAGDx//j/AwAnAAEAAGDp/+r/AwAnAAAAAADp/+v/AwAnAAEAAADp/+z/AwAnAAAAAADp/+3/AwAnAAEAAADp/+7/AwAoAAAAAGDp/+//AwAnAAAAAGDp//D/AwAoAAAAAGDq/+r/AwAoAAAAAADq/+v/AwAoAAEAAADq/+z/AwAoAAAAAADq/+3/AwAoAAEAAADq/+7/AwAoAAEAAGDq/+//AwAnAAEAAGDq//D/AwAoAAEAAGDr/+r/AwAgAAEAAADr/+v/AwAgAAEAAADr/+z/AwAgAAEAAADr/+3/AwAgAAEAAADr/+7/AwAgAAEAAADr/+//AwAgAAEAAADr//D/AwAgAAEAAADs/+r/AwAgAAUAAADs/+v/AwAhAAcAAADs/+z/AwAfAAUAAADs/+3/AwAhAAcAAADs/+7/AwAeAAQAAGDs/+//AwAiAAQAAGDs//D/AwAhAAQAAGDt/+r/AwApAAYAAADt/+v/AwApAAYAAADt/+z/AwApAAYAAADt/+3/AwApAAYAAADt/+7/AwAoAAUAAGDt/+//AwAoAAUAAGDt//D/AwAoAAUAAGDu/+r/AwAhAAQAAADu/+v/AwAiAAYAAADu/+z/AwAgAAYAAADu/+3/AwAfAAQAAADu/+7/AwAeAAYAAGDu/+//AwAiAAYAAGDu//D/AwAhAAYAAGDv/+r/AwAeAAEAAADv/+v/AwAeAAEAAADv/+z/AwAeAAEAAADv/+3/AwAeAAEAAADv/+7/AwAeAAEAAADv/+//AwAeAAEAAADv//D/AwAeAAEAAADw/+r/AwAnAAAAAADw/+v/AwAnAAEAAADw/+z/AwAnAAAAAADw/+3/AwAnAAEAAADw/+7/AwAoAAAAAGDw/+//AwAnAAAAAGDw//D/AwAoAAAAAGDx/+r/AwAoAAAAAADx/+v/AwAoAAEAAADx/+z/AwAoAAAAAADx/+3/AwAoAAEAAADx/+7/AwAoAAEAAGDx/+//AwAnAAEAAGDx//D/AwAoAAEAAGDp/+H/AwAnAAAAAADp/+L/AwAnAAEAAADp/+P/AwAnAAAAAADp/+T/AwAnAAEAAADp/+X/AwAnAAAAAADp/+b/AwAnAAAAAADp/+f/AwAnAAAAAADp/+j/AwAnAAAAAADp/+n/AwAnAAEAAADq/+H/AwAoAAAAAADq/+L/AwAoAAEAAADq/+P/AwAoAAAAAADq/+T/AwAoAAEAAADq/+X/AwAoAAAAAADq/+b/AwAoAAAAAADq/+f/AwAoAAAAAADq/+j/AwAoAAAAAADq/+n/AwAoAAEAAADr/+H/AwAgAAEAAADr/+L/AwAgAAEAAADr/+P/AwAgAAEAAADr/+T/AwAgAAEAAADr/+X/AwAgAAEAAADr/+b/AwAgAAEAAADr/+f/AwAgAAEAAADr/+j/AwAgAAEAAADr/+n/AwAgAAEAAADs/+H/AwAfAAQAAADs/+L/AwAfAAUAAADs/+P/AwAfAAYAAADs/+T/AwAfAAcAAADs/+X/AwAiAAcAAADs/+b/AwAfAAQAAADs/+f/AwAeAAYAAADs/+j/AwAiAAUAAADs/+n/AwAgAAUAAADt/+H/AwApAAYAAADt/+L/AwApAAYAAADt/+P/AwApAAYAAADt/+T/AwApAAYAAADt/+X/AwApAAYAAADt/+b/AwApAAYAAADt/+f/AwAnAAQAAADt/+j/AwApAAYAAADt/+n/AwApAAYAAADu/+H/AwAeAAUAAADu/+L/AwAeAAQAAADu/+P/AwAeAAQAAADu/+T/AwAeAAUAAADu/+X/AwAhAAQAAADu/+b/AwAfAAcAAADu/+f/AwAoAAUAAADu/+j/AwAgAAYAAADu/+n/AwAhAAcAAADv/+P/AwAeAAEAAADv/+T/AwAeAAEAAADv/+X/AwAeAAIAAADv/+b/AwAgAAQAAADv/+f/AwAoAAUAAADv/+j/AwAfAAQAAADv/+n/AwAeAAAAAADw/+P/AwAnAAAAAADw/+T/AwAnAAEAAADw/+X/AwAeAAMAAADw/+b/AwAeAAQAAADw/+n/AwAfAAAAAADx/+P/AwAoAAAAAADx/+T/AwAoAAEAAADx/+X/AwAeAAMAAADx/+b/AwAfAAQAAADx/+f/AwAoAAUAAADx/+n/AwAfAAAAAADy/+P/AwAnAAAAAADy/+T/AwAnAAEAAADy/+X/AwAeAAMAAADy/+b/AwAgAAQAAADy/+f/AwAoAAUAAADy/+n/AwAfAAAAAADy/+r/AwAnAAAAAADy/+v/AwAnAAEAAADz/+P/AwAoAAAAAADz/+T/AwAoAAEAAADz/+X/AwAfAAMAAADz/+b/AwAhAAQAAADz/+f/AwAoAAUAAADz/+n/AwAfAAAAAADz/+r/AwAoAAAAAADz/+v/AwAoAAEAAAAhABMAAwAoAAAAAGAhABQAAwAnAAAAAGAhAB4AAwAnAAAAAAAhAB8AAwAnAAEAAAAiABMAAwAoAAEAAGAiABQAAwAnAAEAAGAiAB4AAwAoAAAAAAAiAB8AAwAoAAEAAAAjABMAAwAgAAEAAAAjABQAAwAgAAEAAAAjAB4AAwAgAAEAAAAjAB8AAwAgAAEAAAAkABMAAwAiAAQAAGAkABQAAwAhAAQAAGAkAB4AAwAfAAUAAAAkAB8AAwAhAAcAAAAlABMAAwAoAAUAAGAlABQAAwAoAAUAAGAlAB4AAwApAAYAAAAlAB8AAwApAAYAAAAmABMAAwAiAAYAAGAmABQAAwAhAAYAAGAmAB4AAwAgAAYAAAAmAB8AAwAfAAQAAAAnABMAAwAeAAEAAAAnABQAAwAeAAEAAAAnAB4AAwAeAAEAAAAnAB8AAwAeAAEAAAAoABMAAwAoAAAAAGAoABQAAwAnAAAAAGAoAB4AAwAnAAAAAAAoAB8AAwAnAAEAAAApABMAAwAoAAEAAGApABQAAwAnAAEAAGApAB4AAwAoAAAAAAApAB8AAwAoAAEAAAAhABAAAwAnAAEAAAAhABEAAwAnAAAAAAAhABIAAwAnAAEAAAAiABAAAwAoAAEAAAAiABEAAwAoAAAAAAAiABIAAwAoAAEAAAAjABAAAwAgAAEAAAAjABEAAwAjAAYAAAAjABIAAwAjAAcAAAAkABAAAwAfAAUAAAAkABEAAwAlAAYAAAAkABIAAwAlAAcAAAAlABAAAwApAAYAAAAlABEAAwAlAAYAAAAlABIAAwAlAAcAAAAmABAAAwAfAAcAAAAmABEAAwAlAAYAAAAmABIAAwAlAAcAAAAnABAAAwAeAAEAAAAnABEAAwAkAAYAAAAnABIAAwAkAAcAAAAoABAAAwAnAAEAAAAoABEAAwAnAAAAAAAoABIAAwAnAAEAAAApABAAAwAoAAEAAAApABEAAwAoAAAAAAApABIAAwAoAAEAAAAhAOv/AwAnAAAAAAAhAOz/AwAnAAEAAAAhAO3/AwAnAAAAAAAhAO7/AwAnAAEAAAAhAO//AwAoAAAAAGAhAPD/AwAnAAAAAGAiAOv/AwAoAAAAAAAiAOz/AwAoAAEAAAAiAO3/AwAoAAAAAAAiAO7/AwAoAAEAAAAiAO//AwAoAAEAAGAiAPD/AwAnAAEAAGAjAOv/AwAgAAEAAAAjAOz/AwAgAAEAAAAjAO3/AwAjAAYAAAAjAO7/AwAjAAcAAAAjAO//AwAgAAEAAAAjAPD/AwAgAAEAAAAkAOv/AwAfAAQAAAAkAOz/AwAfAAUAAAAkAO3/AwAlAAYAAAAkAO7/AwAlAAcAAAAkAO//AwAiAAQAAGAkAPD/AwAhAAQAAGAlAOv/AwApAAYAAAAlAOz/AwApAAYAAAAlAO3/AwAlAAYAAAAlAO7/AwAlAAcAAAAlAO//AwAoAAUAAGAlAPD/AwAoAAUAAGAmAOv/AwAfAAYAAAAmAOz/AwAfAAcAAAAmAO3/AwAlAAYAAAAmAO7/AwAlAAcAAAAmAO//AwAiAAYAAGAmAPD/AwAhAAYAAGAnAOv/AwAeAAEAAAAnAOz/AwAeAAEAAAAnAO3/AwAkAAYAAAAnAO7/AwAkAAcAAAAnAO//AwAeAAEAAAAnAPD/AwAeAAEAAAAoAOv/AwAnAAAAAAAoAOz/AwAnAAEAAAAoAO3/AwAnAAAAAAAoAO7/AwAnAAEAAAAoAO//AwAoAAAAAGAoAPD/AwAnAAAAAGApAOv/AwAoAAAAAAApAOz/AwAoAAEAAAApAO3/AwAoAAAAAAApAO7/AwAoAAEAAAApAO//AwAoAAEAAGApAPD/AwAnAAEAAGD+/+//AwAnAAAAAAD+//D/AwAnAAEAAAAMAPX/AwAnAAAAAGANAPX/AwAnAAEAAGAOAPX/AwAgAAEAAAAPAPX/AwAiAAQAAGAQAPX/AwAoAAUAAGARAPX/AwAiAAYAAGASAPX/AwAeAAEAAAATAPX/AwAnAAAAAGAUAPX/AwAnAAEAAGAMAPb/AwAoAAAAAGAMAPf/AwAnAAAAAGANAPb/AwAoAAEAAGANAPf/AwAnAAEAAGAOAPb/AwAgAAEAAAAOAPf/AwAgAAEAAAAPAPb/AwAeAAQAAGAPAPf/AwAiAAQAAGAQAPb/AwAoAAUAAGAQAPf/AwAoAAUAAGARAPb/AwAeAAYAAGARAPf/AwAiAAYAAGASAPb/AwAeAAEAAAASAPf/AwAeAAEAAAATAPb/AwAoAAAAAGATAPf/AwAnAAAAAGAUAPb/AwAoAAEAAGAUAPf/AwAnAAEAAGAMAPj/AwAoAAAAAGANAPj/AwAoAAEAAGAOAPj/AwAgAAEAAAAPAPj/AwAeAAQAAGAQAPj/AwAoAAUAAGARAPj/AwAeAAYAAGASAPj/AwAeAAEAAAATAPj/AwAoAAAAAGAUAPj/AwAoAAEAAGAMAOv/AwAnAAEAAAANAOv/AwAoAAEAAAAOAOv/AwAgAAEAAAAPAOv/AwAgAAQAAGAQAOv/AwAoAAUAAGARAOv/AwAgAAYAAGAMAOz/AwAnAAAAAAAMAO3/AwAnAAEAAAANAOz/AwAoAAAAAAANAO3/AwAoAAEAAAAOAOz/AwAgAAEAAAAOAO3/AwAgAAEAAAAPAOz/AwAgAAQAAGAPAO3/AwAfAAQAAGAQAOz/AwAoAAUAAGAQAO3/AwAoAAUAAGARAOz/AwAgAAYAAGARAO3/AwAfAAYAAGASAOz/AwAeAAEA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AADR/+n/AgAdAAUAAADR/+j/GAAGAAkAAADR/+f/GAALAAoAAADR/+b/GAALAAoAAADR/+X/GAALAAgAAADQ/+b/GAAJAAoAAADQ/+X/GAAGAAkAAADQ/+T/GAALAAgAAADS//T/GAACABsAAADS//P/GAAJAAsAAADS//L/GAAJAAoAAADS//H/GAAJAAoAAADS//D/GAAJAAoAAADS/+//GAAJAAoAAADS/+7/GAAJAAoAAADR//T/GAABABgAAADR//P/GAAJAAsAAADR//L/GAAJAAoAAADR//H/GAAJAAoAAADR//D/GAAFAAkAAADR/+//GAAIAAkAAADR/+7/GAAIAAkAAADQ//T/GAALAAoAAADQ//P/GAAGAAoAAADQ//L/GAAFAAkAAADQ//H/GAAIAAkAAADQ//D/GAAFAAgAAADP//T/GAAJAAoAAADP//P/GAAFAAkAAADP//L/GAAIAAgAAADP//H/GAADAAMAAADP//D/GAABAAsAAADO//T/GAAJAAoAAADO//P/GAAKAAgAAADF//X/GAAJAAoAAADF//T/AgAdAAUAAADF//P/GAAJAAoAAADF//L/GAAGAAkAAADF//H/GAALAAoAAADF//D/GAALAAgAAADE//X/GAAJAAoAAADE//T/AgAbAAUAAADE//P/GAAJAAoAAADE//L/GAAJAAoAAADE//H/GAAJAAoAAADE//D/GAAGAAkAAADE/+//GAALAAoAAADD//X/GAAJAAoAAADD//T/AgAdAAUAAADD//P/AgAZAAUAAADD//L/AgAcAAUAAADD//H/GAAJAAoAAADD//D/GAAJAAoAAADD/+//GAAJAAoAAADC//X/GAAJAAoAAADC//T/GAAJAAoAAADC//P/GAAJAAoAAADC//L/AgAbAAUAAADC//H/AgAdAAUAAADC//D/GAAJAAoAAADC/+//GAAJAAoAAADB//X/GAAJAAoAAADB//T/GAAJAAoAAADB//P/GAAJAAoAAADB//L/GAAJAAoAAADB//H/AgAaAAUAAADB//D/GAAJAAoAAADB/+//GAAJAAoAAADA//X/GAAIAAkAAADA//T/GAAFAAoAAADA//P/GAAJAAoAAADA//L/GAAJAAoAAADA//H/GAAJAAoAAADA//D/GAAJAAoAAADA/+//GAAJAAoAAAC///T/GAAIAAsAAAC///P/GAAFAAoAAAC///L/GAAJAAoAAAC///H/GAAJAAoAAAC///D/GAAJAAoAAAC//+//GAAJAAoAAADP//r/GAAJAAsAAADP//n/AgAZAAcAAADP//j/GAAKAAgAAADP//f/GAABABoAAADP//b/GAADABgAAADO//r/GAAIAAsAAADO//n/GAAFAAoAAADO//j/GAAGAAkAAADO//f/GAALAAgAAADO//b/GAACABsAAADN//f/GAAKAAgAAADN//b/GAACABsAAADM//f/GAAKAAgAAADM//b/GAACABoAAADL//r/GAALAAoAAADL//n/GAALAAoAAADL//j/GAAGAAoAAADL//f/GAAKAAgAAADL//b/GAACABsAAADK//r/AgAaAAcAAADK//n/GAAFAAkAAADK//j/GAAIAAkAAADK//f/GAAIAAgAAADK//b/GAACABsAAADJ//r/GAAJAAoAAADJ//n/GAAKAAgAAADJ//j/GAADABoAAADJ//f/GAAAABkAAADJ//b/GAABABgAAADK//X/GAAJAAsAAADK//T/GAAGAAkAAADK//P/GAALAAgAAADJ//X/GAAJAAsAAADJ//T/GAAJAAoAAADJ//P/GAAKAAgAAADI//X/GAAGAAoAAADI//T/GAAJAAoAAADI//P/GAAKAAgAAADH//X/GAAJAAoAAADH//T/GAAJAAoAAADN//P/GAAIAAgAAADL//P/GAAIAAMAAADG/+v/GAALAAsAAADF/+z/GAAKAAAAAADF/+v/GAAJAAsAAADZ/+//GAAJAAsAAADZ/+7/GAAJAAoAAADZ/+3/AgAcAAUAAADZ/+z/AgAZAAUAAADZ/+v/GAAJAAoAAADY/+//GAAGAAoAAADY/+7/AgAZAAUAAADY/+3/AgAZAAUAAADY/+z/AgAcAAUAAADY/+v/GAAJAAoAAADX/+//GAAJAAoAAADX/+7/AgAaAAUAAADX/+3/GAAJAAoAAADX/+z/GAAJAAoAAADX/+v/GAAJAAoAAADW/+//AgAdAAUAAADW/+7/AgAcAAUAAADW/+3/GAAJAAoAAADW/+z/GAAJAAoAAADW/+v/GAAJAAoAAADV/+//GAAJAAoAAADV/+7/GAAJAAoAAADV/+3/GAAJAAoAAADV/+z/GAAJAAoAAADV/+v/GAAJAAoAAADU/+//GAAJAAoAAADU/+7/GAAJAAoAAADU/+3/GAAJAAoAAADU/+z/GAAJAAoAAADU/+v/GAAJAAoAAADT/+//GAAJAAoAAADT/+7/GAAJAAoAAADT/+3/GAAJAAoAAADT/+z/GAAJAAoAAADT/+v/GAAJAAoAAADT//D/GAAJAAoAAADS/+3/GAAJAAoAAADR/+3/GAAFAAoAAADQ/+3/GAAIAAsAAADV//b/GAABAAAAAADV//X/GAADAAsAAADV//T/GAACABsAAADV//P/GAALAAsAAADV//L/GAAGAAoAAADi//D/GAALAAsAAADi/+//GAALAAoAAADi/+7/GAALAAoAAADh//L/GAALAAsAAADh//H/GAALAAoAAADh//D/GAAGAAoAAADh/+//GAAJAAoAAADh/+7/GAAJAAoAAADg//L/GAAIAAsAAADg//H/GAAFAAoAAADg//D/GAAJAAoAAADg/+//GAAJAAoAAADg/+7/GAAJAAoAAADg/+z/GAAJAAoAAADg/+v/GAAJAAoAAADg/+r/GAAJAAoAAADf/+z/GAAJAAoAAADf/+v/GAAJAAoAAADf/+r/GAAJAAoAAADe/+z/GAAJAAoAAADe/+v/GAAJAAoAAADe/+r/GAAJAAoAAADh/+z/GAAJAAoAAADh/+v/GAAJAAoAAADh/+r/GAAJAAoAAADi/+3/GAALAAoAAADi/+z/GAALAAkAAADh/+3/GAAJAAoAAADg/+3/GAAJAAoAAADf/+7/GAAJAAoAAADf/+3/GAAJAAoAAADd/+//GAAFAAoAAADc/+//GAAJAAsAAADb/+//GAAJAAsAAADa/+//GAAJAAsAAADX/+P/GAAIAAgAAADU/+P/GAADACsAAADT/+P/GAABACgAAADS/+z/GAAJAAoAAADS/+v/GAAJAAoAAADS/+P/GAAAAAoAAADR/+z/GAAJAAoAAADR/+v/GAAJAAoAAADQ/+z/GAAFAAoAAADP/+z/GAAIAAsAAADP/+b/AgAZAAUAAADP/+X/GAAJAAoAAADP/+T/GAAKAAgAAADO/+b/GAAJAAoAAADO/+X/AgAcAAUAAADO/+T/GAAGAAkAAADO/+P/GAALAAgAAADN/+b/AgAdAAUAAADM/+b/GAAJAAoAAADI/+b/GAAJAAoA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AADb/+3/GAAJAAoAAADb/+z/AgAbAAUAAADb/+v/AgAcAAUAAADb/+f/GAAJAAoAAADb/+b/AgAaAAUAAADb/+X/AgAbAAUAAADb/+T/AgAbAAUAAADb/+P/GAAJAAoAAADb/+L/GAAJAAoAAADb/+H/AgAbAAUAAADb/+D/AgAaAAUAAADb/97/GAAJAAoAAADb/93/GAAJAAoAAADa/+7/GAAJAAoAAADa/+3/GAAJAAoAAADa/+z/AgAbAAUAAADa/+v/GAAJAAoAAADa/+r/GAAJAAoAAADa/+n/GAAJAAoAAADa/+j/GAAJAAoAAADa/+f/AgAaAAUAAADa/+b/AgAaAAUAAADa/+X/GAAJAAoAAADa/+T/GAAJAAoAAADa/+P/GAAJAAoAAADa/+L/GAAJAAoAAADa/+H/GAAJAAoAAADa/+D/AgAdAAUAAADZ/+L/GAAJAAoAAADZ/+H/GAAJAAoAAADZ/+D/GAAJAAoAAADZ/97/AgAZAAUAAADZ/93/GAAJAAoAAADZ/9v/GAAGAAkAAADZ/9r/GAALAAoAAADZ/9n/GAALAAgAAADY/+L/GAAIAAkAAADY/+H/GAAIAAkAAADY/+D/GAAIAAkAAADY/97/AgAaAAUAAADY/93/AgAaAAUAAADY/9v/GAAJAAoAAADY/9r/GAAJAAoAAADY/9n/GAAGAAkAAADY/9j/GAALAAgAAADX/+L/GAADABoAAADX/9v/GAAJAAoAAADX/9r/GAAJAAoAAADX/9n/GAAJAAoAAADX/9j/GAAGAAkAAADX/9f/GAALAAoAAADX/9b/GAALAAgAAADW/93/GAAJAAoAAADW/9r/AgAaAAUAAADW/9n/GAAJAAoAAADW/9j/GAAJAAoAAADW/9f/GAAJAAoAAADW/9b/GAAKAAgAAADV//H/GAAJAAoAAADV//D/GAAJAAoAAADV/9r/GAAJAAoAAADV/9n/GAAJAAoAAADV/9j/AgAbAAcAAADV/9f/GAAJAAoAAADV/9b/GAAKAAgAAADU//b/GAALAAsAAADU//X/GAAGAAgAAADU//T/GAACABsAAADU//P/GAAJAAsAAADU//L/GAAJAAoAAADU//H/GAAJAAoAAADU//D/GAAJAAoAAADU/+L/GAAGAAsAAADU/9r/AgAdAAUAAADU/9n/GAAJAAoAAADU/9j/GAAJAAoAAADU/9f/GAAJAAoAAADU/9b/GAAKAAgAAADT//f/GAALAAsAAADT//b/GAAGAAoAAADT//X/GAAKAAgAAADT//T/GAACABsAAADT//P/GAAJAAsAAADT//L/GAAJAAoAAADT//H/GAAJAAoAAADT/+L/GAAJAAsAAADT/9v/AgAcAAUAAADT/9r/GAAJAAoAAADT/9n/AgAbAAUAAADT/9j/AgAdAAUAAADT/9f/GAAJAAoAAADT/9b/GAAKAAgAAADS//n/GAALAAsAAADS//j/GAAHAAkAAADS//f/GAAEAAoAAADS//b/GAAIAAkAAADS//X/GAAIAAgAAADS/+L/GAAKAAsAAADS/+H/GAAJAAoAAADS/9n/AgAbAAUAAADS/9j/GAAJAAoAAADS/9f/GAAJAAoAAADS/9b/GAAKAAgAAADR//r/GAALAAsAAADR//n/GAAGAAoAAADR//j/GAAKAAgAAADR//f/GAADABoAAADR//b/GAAAABkAAADR//X/GAAAABkAAADR/+L/GAAJAAsAAADR/+H/GAAJAAoAAADR/9n/AgAbAAUAAADR/9j/GAAJAAoAAADR/9f/GAAFAAkAAADR/9b/GAAIAAgAAADQ//r/GAAJAAsAAADQ//n/AgAZAAUAAADQ//j/GAAKAAgAAADQ//f/GAACABsAAADQ//b/GAAAAAoAAADQ//X/GAAHAAoAAADQ/+L/GAAIAAsAAADQ/+H/GAAIAAkAAADQ/9n/GAAJAAoAAADQ/9j/GAAFAAkAAADQ/9f/GAAIAAgAAADP//X/GAAJAAsAAADP/9n/GAAFAAkAAADP/9j/GAAIAAgAAADO//v/GAAKAAAAAADO//X/GAAJAAsAAADO/9n/GAAIAAgAAADN//X/GAAIAAsAAADN//T/GAAIAAkAAADM//X/GAAAABkAAADM//T/GAAAABkAAADL//v/GAALAAsAAADL//X/GAALAAsAAADL//T/GAALAAgAAADK//v/GAAJAAsAAADJ//v/GAAJAAsAAADJ/9z/GAALAAsAAADJ/9v/GAALAAoAAADI//v/GAAIAAsAAADI//r/GAAFAAoAAADI//n/GAAKAAgAAADI//j/GAACABsAAADI//f/GAALAAsAAADI//b/GAALAAoAAADI/97/GAALAAsAAADI/93/GAALAAoAAADI/9z/GAAGAAoAAADI/9v/GAAJAAoAAADH//v/GAALAAAAAADH//r/GAAIAAsAAADH//n/GAAIAAgAAADH//j/GAACABsAAADH//f/GAAJAAsAAADH//b/GAAJAAoAAADH/97/GAAJAAsAAADH/93/GAAJAAoAAADH/9z/GAAJAAoAAADH/9v/GAAJAAoAAADG//r/GAADABoAAADG//n/GAAAABkAAADG//j/GAABABgAAADG//f/GAAJAAsAAADG//b/GAAJAAoAAADG//X/GAAJAAoAAADG/+H/GAADAAsAAADG/+D/GAAIABsAAADG/97/GAAJAAsAAADG/93/GAAJAAoAAADG/9z/GAAJAAoAAADG/9v/GAAJAAoAAADF//n/GAAAAAoAAADF//j/GAAHAAoAAADF//f/GAAGAAoAAADF//b/GAAJAAoAAADF/+H/GAAGAAsAAADF/+D/GAALAAgAAADF/97/GAAJAAsAAADF/93/GAAJAAoAAADF/9z/AgAaAAUAAADF/9v/GAAJAAoAAADE//n/GAADAAMAAADE//j/GAAJAAsAAADE//f/GAAJAAoAAADE//b/GAAJAAoAAADE/+L/GAALAAoAAADE/+H/GAAGAAoAAADE/+D/GAAGAAkAAADE/97/GAAGAAoAAADE/93/GAAJAAoAAADE/9z/AgAaAAUAAADE/9v/GAAJAAoAAADE/9r/GAAGAAkAAADE/9n/GAALAAoAAADE/9j/GAALAAgAAADD//j/GAAIAAsAAADD//f/GAAFAAoAAADD//b/GAAJAAoAAADD/+7/GAAGAAkAAADD/+3/GAALAAoAAADD/+z/GAALAAoAAADD/+v/GAAGAAoAAADD/+r/GAAJAAoAAADD/+n/AgAaAAUAAADD/+b/GAAJAAoAAADD/+X/GAAJAAoAAADD/+T/GAAJAAoAAADD/+P/AgAaAAUAAADD/+L/AgAcAAUAAADD/+H/AgAbAAUAAADD/+D/GAAJAAoAAADD/9//GAAJAAoAAADD/97/GAAJAAoAAADD/93/GAAJAAoAAADD/9z/AgAcAAUAAADD/9v/GAAJAAoAAADD/9r/GAAJAAoAAADD/9n/GAAJAAoAAADD/9j/GAAKAAgAAADC//f/GAAJAAsAAADC//b/GAAJAAoAAADC/+7/GAAJAAoAAADC/+3/GAAJAAoAAADC/+L/GAAJAAoAAADC/+H/GAAFAAkAAADC/9//GAAIAAkAAADC/97/GAAFAAoAAADC/93/GAAJAAoAAADC/9z/GAAJAAoAAADC/9v/GAAJAAoAAADC/9r/AgAbAAUAAADC/9n/GAAJAAoAAADC/9j/GAAGAAkAAADC/9f/GAALAAgAAADB//b/GAAJAAoAAADB/+7/GAAJAAoAAADB/+3/GAAJAAoAAADB/+L/GAAFAAkAAADB/+H/GAAIAAgAAADB/97/GAAIAAsAAADB/93/GAAFAAoAAADB/9z/GAAJAAoAAADB/9v/GAAJAAoAAADB/9r/AgAdAAUAAADB/9n/GAAJAAoAAADB/9j/GAAJAAoAAADB/9f/GAAJAAgAAADB/9b/GAAAAAgAAADA/+7/GAAJAAoAAADA/+3/GAAJAAoAAADA/+L/GAAKAAgAAADA/93/GAAJAAsAAADA/9z/GAAJAAoAAADA/9v/AgAaAAcAAADA/9r/AgAbAAUAAADA/9n/GAAJAAoAAADA/9j/GAAFAAkAAADA/9f/GAAIAAgAAAC//+7/GAAJAAoAAAC//+3/GAAJAAoAAAC//9v/AgAdAAUAAAC//9r/AgAbAAUAAAC//9n/GAAJAAoAAAC//9j/GAAKAAgAAAC+//P/GAAIAAsAAAC+//L/GAAIAAoAAAC+/+3/GAAJAAoAAAC+/9v/GAAJAAoAAAC+/9r/GAAJAAoAAAC+/9n/GAAFAAkAAAC+/9j/GAAIAAgAAAC9/+3/GAAIAAkAAAC9/9v/GAAJAAoAAAC9/9r/GAAFAAkAAAC9/9n/GAAIAAgAAAC8/9v/GAAFAAkAAAC8/9r/GAAIAAgAAAC7/+P/GAAIAAsAAAC7/93/GAAJAAoAAAC7/9z/GAAFAAkAAAC7/9v/GAAIAAgAAAC6/93/GAAIAAkAAAC6/9z/GAAIAAgAAAC5/9z/AgADAAgAAAC//+P/GAAIAAkAAAC//+L/GAAIAAgAAAC//9//GAALAAsAAAC//97/GAALAAoAAAC//93/GAAGAAoAAAC//9z/GAAJAAoAAAC+/+P/GAADAAIAAAC+/+D/GAALAAsAAAC+/9//GAAGAAoAAAC+/97/GAAJAAoAAAC+/93/AgAbAAUAAAC+/9z/AgAbAAUAAAC9/+L/GAALAAsAAAC9/+H/GAALAAoAAAC9/+D/GAAGAAoAAAC9/9z/GAAJAAoAAAC8/+P/GAALAAsAAAC8/+L/GAAGAAoAAAC8/+H/GAAJAAoAAAC8/93/GAAJAAoAAAC8/9z/GAAJAAoAAADD/+f/GAAJAAoAAADD/+j/GAAJAAoAAADC/+j/GAAJAAoAAADC/+f/AgAZAAcAAADE/+j/AgAcAAUAAADE/+f/AgAcAAUAAADF/+f/AgAZAAUAAADF/+j/GAAJAAoAAADG/+j/GAAJAAoAAADG/+f/AgAaAAUAAADH/+f/GAAJAAoAAADI/+f/GAAJAAoAAADJ/+b/GAAJAAoAAADK/+b/GAAJAAoAAADL/+b/GAAJAAoAAADJ/+f/GAAJAAoAAADH/+j/GAAGAAoAAADI/+j/GAAJAAsAAADJ/+j/GAAJAAsAAADL/+X/GAAKAAgAAADK/+X/GAAKAAgAAADJ/+X/GAAKAAgAAADI/+X/GAAJAAgAAADH/+X/GAAKAAgAAADG/+X/GAAKAAgAAADF/+X/GAAKAAgAAADE/9//GAALAAoAAADC/+D/GAAIAAkAAADI/9r/GAAJAAoAAADH/9r/GAAFAAkAAADF/9r/GAAKAAgAAADG/9r/GAAKAAgAAADH/9n/GAAIAAgAAADI/9n/GAAKAAgAAADJ/9n/GAAKAAgAAADJ/9r/GAAGAAoAAADK/9r/GAALAAsAAADK/9n/GAALAAgAAADW/+r/GAAFAAkAAADW/+j/GAAIAAgAAADW/+n/GAAIAAkAAADV/+r/GAAKAAgAAADS/+r/GAAJAAoAAADU/+r/GAAKAAgAAADT/+r/GAAGAAkAAADT/+n/GAALAAgAAADS/+n/GAAGAAkAAADS/+j/GAALAAgAAADH//P/GAAKAAgAAADG//P/GAAGAAkAAADG//L/GAALAAgAAADG//T/GAAJAAoAAADM//j/GAAJAAsAAADN//j/GAAFAAoAAADN//n/GAAIAAsAAADX//D/GAAGAAoAAADX//H/GAALAAsAAADX//L/GAABABgAAADW//P/GAABABgAAADW//T/GAADABoAAADW//H/GAAGAAoAAADW//D/GAAJAAoAAADY//D/GAALAAsAAADW//L/GAALAAsAAADf//D/GAAFAAoAAADf//H/GAAIAAsAAADe//D/GAAJAAsAAADd//D/GAAIAAsAAADe/+b/GAALAAgAAADe/+H/GAAJAAoAAADe/+L/GAAGAAoAAADf/+H/GAALAAoAAADf/+L/GAALAAsAAADe/+P/GAALAAsAAADa/9z/GAAGAAkAAADc/9z/GAALAAgAAADb/9z/GAAKAAgAAADa/9v/GAALAAgAAADR/93/GAAIAAsAAADO/93/GAAIAAsAAADP/93/GAALAAsAAADH/+r/GAAIAAAAAADI/+r/GAAFAAEAAADI/+n/GAAIAAAAAADJ/+n/GAALAAAAAADJ/+r/GAAGAAEAAADK/+r/GAAKAAAAAADL/+r/GAAKAAAAAADM/+r/GAAKAAAAAADN/+r/GAALAAAAAADJ/+v/GAAJAAIAAADI/+v/GAAJAAIAAADH/+v/GAAIAAEAAADK/+v/GAAJAAIAAADL/+v/GAAJAAIAAADM/+v/GAAJAAIAAADN/+v/GAAGAAEAAADO/+v/GAALAAAAAADO/+z/GAALAAIAAADN/+z/GAAJAAIAAADM/+z/GAAJAAIAAADL/+z/GAAJAAIAAADK/+z/GAAJAAIAAADJ/+z/GAAJAAIAAADI/+z/GAAJAAIAAADH/+z/GAAFAAEAAADG/+z/GAAKAAAAAADG/+3/GAAJAAIAAADF/+3/GAAFAAIAAADE/+z/GAAIAAAAAADE/+3/GAAIAAMAAADH/+3/GAAJAAIAAADI/+3/GAAJAAIAAADJ/+3/GAAJAAIAAADK/+3/GAAJAAIAAADL/+3/GAAJAAIAAADM/+3/GAAJAAIAAADN/+3/GAAJAAIAAADO/+3/GAAGAAEAAADP/+3/GAALAAAAAADP/+7/GAAGAAEAAADQ/+7/GAALAAAAAADO/+7/GAAJAAIAAADN/+7/GAAJAAIAAADM/+7/GAAJAAIAAADL/+7/GAAJAAIAAADK/+7/GAAJAAIAAADJ/+7/GAAJAAIAAADI/+7/GAAJAAIAAADH/+7/GAAJAAIAAADG/+7/GAAJAAIAAADF/+7/GAAIAAEAAADF/+//GAAIAAMAAADG/+//GAAFAAIAAADH/+//GAAJAAIAAADI/+//GAAJAAIAAADJ/+//GAAJAAIAAADK/+//GAAJAAIAAADL/+//GAAJAAIAAADM/+//GAAJAAIAAADN/+//GAAJAAIAAADO/+//GAAGAAIAAADP/+//GAAJAAMAAADQ/+//GAALAAMAAADO//D/GAALAAIAAADN//D/GAAJAAIAAADM//D/GAAJAAIAAADL//D/GAAJAAIAAADK//D/GAAJAAIAAADJ//D/GAAJAAIAAADI//D/GAAJAAIAAADH//D/GAAJAAIAAADG//D/GAAIAAEAAADG//H/GAAIAAMAAADH//H/GAAFAAIAAADI//H/GAAJAAIAAADJ//H/GAAJAAIAAADK//H/GAAJAAIAAADL//H/GAAJAAIAAADM//H/GAAJAAIAAADN//H/GAAJAAIAAADO//H/GAALAAEAAADO//L/GAALAAMAAADN//L/GAAJAAMAAADM//L/GAAGAAIAAADL//L/GAAFAAIAAADK//L/GAAJAAMAAADJ//L/GAAJAAMAAADI//L/GAAJAAMAAADH//L/GAAIAAMAAADM//P/GAALAAMAAAC9/+P/GAABAAAAAAC+/+L/GAAAAAEAAAC+/+H/GAABAAAAAAC//+H/GAAFAAMAAAC//+D/GAAIAAAAAADA/+H/GAALAAMAAADA/+D/GAAJAAEAAADA/9//GAAEAAEAAADA/97/GAAAAAAAAADB/9//GAALAAAAAADB/+D/GAALAAMAAAC8/+T/GAACAAMAAAC7/+T/GAAGAAAAAAC6/+T/GAAGAAEAAAC6/+P/GAALAAIAAAC6/+X/GAAJAAIAAAC7/+b/GAALAAIAAAC6/+b/GAAJAAIAAAC7/+f/GAALAAMAAAC7/+j/GAABAAsAAAC7/+n/GAALAAAAAAC6/+n/GAAGAAEAAAC6/+j/GAALAAIAAAC6/+f/GAAGAAIAAAC7/+r/GAALAAIAAAC6/+r/GAAJAAIAAAC7/+v/GAAGAAEAAAC8/+v/GAALAAAAAAC6/+v/GAAJAAIAAAC7/+z/GAAFAAIAAAC8/+z/GAALAAIAAAC8/+3/GAALAAIAAAC8/+7/GAALAAMAAAC8/+//GAABAAsAAAC8//D/GAAFAAAAAAC9//D/GAALAAAAAAC9//H/GAALAAMAAAC9//L/GAABAAsAAAC9//P/GAALAAAAAAC9//T/GAAKAAIAAAC+//T/GAALAAAAAAC+//X/GAAKAAIAAAC///X/GAALAAAAAAC///b/GAAHAAIAAADA//b/GAAIAAkAAADA//f/GAAEAAoAAADB//f/GAAJAAsAAADB//j/GAAIAAAAAADC//j/GAALAAAAAADC//n/GAAKAAIAAADD//n/GAAGAAAAAADE//r/GAABABgAAADF//r/GAACABsAAADF//v/GAAIAAAAAADG//v/GAAKAAAAAADH//z/AwAoAAAAAADI//z/AwAnAAAAAADJ//z/AwAoAAAAAADK//z/AwAnAAAAAADL//z/AwAoAAAAAADG//z/AwAnAAAAAADE//v/GAADABoAAADD//r/GAALAAMAAAC9//X/GAAIAAMAAAC+//b/GAAIAAMAAAC///f/GAAAAAIAAADA//j/GAAAAAoAAADB//n/GAAIAAMAAADC//r/GAAIAAMAAADD//v/GAACABsAAADF//z/AwAoAAAAAAC8//T/GAAIAAMAAAC8//P/GAAEAAEAAAC8//L/GAAAAAEAAAC8//H/GAAEAAIAAAC7//D/GAABAAIAAAC7/+//GAAAAAEAAAC7/+7/GAAEAAIAAAC7/+3/GAAIAAEAAAC6/+z/GAAJAAMAAADF/9//GAABABoAAADG/9//GAAFABgAAADH/9//GAAKABgAAADI/9//GAAKABgAAADH/+D/GAAFABoAAADI/+D/GAAJABoAAADI/+H/GAAJABoAAADH/+H/GAAIABkAAADH/+L/GAAFABkAAADG/+L/GAAKABgAAADF/+L/GAAIABgAAADF/+P/GAAIABkAAADF/+T/GAAIABsAAADG/+T/GAAJABsAAADG/+P/GAAJABoAAADH/+T/GAALABsAAADH/+P/GAAGABoAAADI/+L/GAAJABoAAADI/+P/GAAJABsAAADJ/+P/GAAFABoAAADJ/+L/GAAJABoAAADJ/+T/GAAIABsAAADI/+T/GAAAAAgAAADK/+T/GAAJABsAAADK/+P/GAAJABoAAADJ/9//GAAFABkAAADJ/97/GAAIABkAAADJ/93/GAAIABgAAADK/93/GAAFABkAAADK/9z/GAAIABkAAADL/93/GAAJABoAAADL/9z/GAAGABkAAADL/9v/GAAHABkAAADK/9v/GAAIABgAAADL/9r/GAAAABkAAADL/9n/GAAAABgAAADM/9z/GAAHABkAAADM/9v/GAAAABgAAADM/93/GAAGABkAAADN/93/GAALABgAAADN/97/GAAGABkAAADM/97/GAAJABoAAADL/97/GAAJABoAAADK/97/GAAJABoAAADJ/+D/GAAJABoAAADK/+D/GAAJABoAAADK/9//GAAJABoAAADL/+D/GAAJABoAAADL/9//GAAJABoAAADM/9//GAAJABoAAADN/9//GAAJABoAAADO/97/GAAKABgAAADO/9//GAAJABoAAADM/+D/GAAJABoAAADJ/+H/GAAJABoAAADK/+L/GAAJABoAAADL/+H/GAAGABoAAADK/+H/GAAJABoAAADN/+D/GAAJABoAAADN/+H/GAAJABsAAADM/+H/GAAJABsAAADL/+L/GAALABoAAADL/+P/GAALABoAAADL/+T/GAALABsAAADO/+D/GAAJABoAAADO/+H/GAAFABoAAADP/+H/GAALABoAAADP/+D/GAALABoAAADP/9//GAALABoAAADP/97/GAAGABgAAADQ/97/GAACABoAAADQ/93/GAAAABgAAADR/97/GAADABoAAADP/+L/GAALABsAAADO/+L/GAAIABsAAADP/+P/GAABACsAAADQ/+P/GAACACsAAADR/+P/GAADACgAAADR/+T/GAABACoAAADS/+T/GAACACgAAADT/+X/GAAIAAgAAADT/+b/GAAIAAsAAADS/+b/GAAAACkAAADS/+X/GAAAACkAAADS/+f/GAABACoAAADT/+f/GAADACgAAADU/+f/GAAAAAoAAADU/+b/GAAKAAsAAADU/+X/GAAFAAkAAADU/+T/GAAIAAgAAADV/+T/GAALAAgAAADV/+P/GAACABsAAADV/+L/GAADAAsAAADW/+L/GAABABgAAADW/+H/GAAAAAoAAADX/+H/GAAAABkAAADX/+D/GAAAABkAAADW/+P/GAADABkAAADW/+X/GAAAACgAAADW/+b/GAAAACkAAADW/+f/GAADACoAAADV/+f/GAABACgAAADV/+b/GAALAAsAAADV/+X/GAALAAoAAADV/+j/GAALACgAAADU/+j/GAAFACgAAADV/+n/GAALACsAAADU/+n/GAAIACsAAADT/+j/GAABACoAAADM//n/GAAAAAAAAADM//r/GAAEAAEAAADN//r/GAALAAAAAADM//v/GAAIAAEAAADM//z/AwAnAAAAAADN//z/AwAoAAAAAADN//v/GAAGAAEAAADO//z/AwAnAAAAAADP//z/AwAoAAAAAADQ//z/AwAnAAAAAADQ//v/GAAKAAAAAADR//v/GAAKAAAAAADP//v/GAAKAAAAAADS//v/GAAHAAEAAADT//v/GAABABoAAADR//z/AwAoAAAAAADS//z/AwAnAAAAAADU//v/GAAFABsAAADU//r/GAAIABkAAADT//r/GAADAAIAAADS//r/GAABAAAAAADT//n/GAAAAAEAAADU//n/GAAIABgAAADU//j/GAADAAIAAADT//j/GAABAAAAAADV//j/GAAIABgAAADV//f/GAADAAIAAADW//f/GAAAABkAAADW//b/GAAFAAMAAADU//f/GAABAAAAAADW//j/GAAHABkAAADX//f/GAAAAAEAAADX//b/GAAHAAIAAADX//X/GAAJAAEAAADX//T/GAAIAAAAAADY//T/GAAHAAEAAADW//X/GAAIAAAAAADY//P/GAAAAAAAAADY//X/GAALAAMAAADY//b/GAAIAAgAAADX//j/GAAAAAEAAADW//n/GAALABoAAADV//n/GAAFABkAAADV//r/GAAJABoAAADW//r/GAAGABkAAADX//r/GAAKABgAAADY//r/GAAKABgAAADZ//r/GAAKABgAAADa//r/GAAKABgAAADb//r/GAAGABgAAADc//r/GAACABoAAADd//r/GAAFABgAAADe//r/AgAfAAkAAADj//b/GAAJAAIAAADj//X/GAAJAAIAAADj//T/GAAFAAEAAADj//P/GAAIAAEAAADj//L/GAAIAAEAAADj//H/GAAEAAEAAADk//H/GAAKAAAAAADk//D/GAAFABsAAADk/+//GAAIABkAAADk/+7/GAAIABgAAADl/+7/GAAHABkAAADl/+3/GAAAABkAAADl/+z/GAAAABkAAADl/+v/GAAAABkAAADl/+r/GAAHABoAAADl/+n/GAALABoAAADl/+j/GAALABoAAADk/+j/GAAIABkAAADk/+f/GAAKABoAAADk/+b/GAALABoAAADk/+X/GAALABoAAADk/+T/GAALABoAAADk/+P/GAALABoAAADk/+L/GAALABoAAADk/+H/GAALABgAAADk/+D/GAAFAAMAAADk/9//GAAIAAEAAADk/97/GAAIAAEAAADk/93/GAAFAAAAAADk/9z/GAALABsAAADk/9v/GAALABoAAADk/9r/GAALABoAAADk/9n/GAAGABoAAADk/9j/GAAJABoAAAC5//P/AgADAAgAAAC5//L/AgADAAgAAAC5//H/AgADAAgAAAC5//D/AgADAAgAAAC5/+//AgADAAgAAAC5/+7/AgADAAgAAAC5/+3/AgADAAgAAAC5/+z/AgADAAgAAAC5/+v/AgADAAgAAAC5/+r/AgADAAgAAAC5/+n/AgADAAgAAAC5/+j/AgADAAgAAAC5/+f/AgADAAgAAAC5/+b/AgADAAgAAAC5/+X/AgADAAgAAAC5/+T/AgADAAgAAAC5/93/AgADAAgAAAC5/9v/AgADAAgAAAC5/9r/AgADAAgAAADl/9T/GAADAAAAAADk/9T/GAACAAMAAADj/9T/GAACAAMAAADi/9T/GAACAAMAAADh/9T/GAACAAMAAADg/9T/GAACAAMAAADf/9T/GAACAAMAAADe/9T/GAACAAMAAADd/9T/GAACAAMAAADc/9T/GAACAAMAAADb/9T/GAACAAMAAADa/9T/GAACAAMAAADZ/9T/GAACAAMAAADY/9T/GAAGAAAAAADX/9T/GAAKAAAAAADW/9T/GAAKAAAAAADV/9T/GAAKAAAAAADU/9T/GAAKAAAAAADT/9T/GAAKAAAAAADS/9T/GAAKAAAAAADR/9T/GAAKAAAAAADQ/9T/GAAFAAAAAADP/9T/GAACAAMAAADO/9T/GAACAAMAAADN/9T/GAACAAMAAADM/9T/GAACAAMAAADL/9T/GAACAAMAAADK/9T/GAACAAMAAADJ/9T/GAACAAMAAADI/9T/GAACAAMAAADH/9T/GAACAAMAAADG/9T/GAACAAMAAADF/9T/GAACAAMAAADE/9T/GAACAAMAAADD/9T/GAACAAMAAADC/9T/GAAGAAAAAADB/9T/GAAKAAAAAADA/9T/GAAFAAAAAAC//9T/GAACAAMAAAC+/9T/GAACAAMAAAC9/9T/GAACAAMAAADl/9f/GAAJABoAAADl/9j/GAAGABoAAADl/9n/GAALABsAAADl/9r/GAAIAAAAAADl/9v/GAAIAAEAAADl/9z/GAAEAAIAAADl/93/GAAHAAEAAADl/97/GAALAAIAAADl/9//GAALAAIAAADl/+D/GAAHAAIAAADl/+H/GAAAAAEAAADl/+L/GAAAAAEAAADl/+P/GAAAAAEAAADl/+T/GAAAAAEAAADl/+X/GAAAAAEAAADl/+b/GAABAAIAAADl/+f/GAALABgAAADl/+//GAAGABkAAADl//H/GAALAAAAAADl//L/GAAGAAEAAADl//P/GAAJAAIAAADl//T/GAAJAAIAAADl//X/GAAJAAIAAADl/9b/GAAKABgAAADl/9X/GAABAAIAAAC5/9n/AgADAAgAAAC5//z/AwAnAAAAAAC5//v/AgADAAgAAAC5//r/AgADAAgAAAC5//n/AgADAAgAAAC5//j/AgADAAgAAAC5//f/AgADAAgAAAC5//b/AgADAAgAAAC5//X/AgADAAgAAAC5//T/AgADAAgAAADc//z/AwAnAAAAAADb//z/AwAoAAAAAADa//z/AwAnAAAAAADZ//z/AwAoAAAAAADY//z/AwAnAAAAAADX//z/AwAoAAAAAADW//z/AwAnAAAAAADV//z/AwAoAAAAAADU//z/AwAnAAAAAADT//z/AwAoAAAAAADE//z/AwAnAAAAAADD//z/AwAoAAAAAADC//z/AwAnAAAAAADB//z/AwAoAAAAAADA//z/AwAnAAAAAAC///z/AwAoAAAAAAC+//z/AwAoAAAAAAC9//z/AwAnAAAAAAC8//z/AwAoAAAAAAC7//z/AwAnAAAAAAC6//z/AwAoAAAAAADC//v/GAACABsAAADB//v/GAAGABsAAADB//r/GAALABgAAADA//v/GAAFABsAAADA//r/GAAEABkAAADA//n/GAADABgAAAC///v/GAACABsAAAC///r/GAADAAsAAAC///n/GAABABoAAAC///j/GAADABgAAAC+//v/GAACABsAAAC+//r/GAAGAAsAAAC+//n/GAALAAgAAAC+//j/GAABABoAAAC+//f/GAADABgAAAC9//v/GAACABsAAAC9//r/GAAJAAsAAAC9//n/GAAGAAkAAAC9//j/GAALAAgAAAC9//f/GAABABoAAAC9//b/GAADABgAAAC8//v/GAACABsAAAC8//r/GAAJAAsAAAC8//n/GAAJAAoAAAC8//j/GAAGAAkAAAC8//f/GAALAAgAAAC8//b/GAABABoAAAC8//X/GAADABgAAAC7//v/GAACABsAAAC7//r/GAAIAAsAAAC7//n/GAAIAAkAAAC7//j/GAAIAAkAAAC7//f/GAAEAAkAAAC7//b/GAAAAAgAAAC7//X/GAAGABsAAAC7//T/GAALABoAAAC7//P/GAALABoAAAC7//L/GAALABoAAAC7//H/GAALABgAAAC6//v/GAABABoAAAC6//r/GAAAABkAAAC6//n/GAAAABkAAAC6//j/GAAAABkAAAC6//f/GAAAABkAAAC6//b/GAAAABkAAAC6//X/GAAEABoAAAC6//T/GAAIABkAAAC6//P/GAAIABkAAAC6//L/GAAIABkAAAC6//H/GAAEABkAAAC6//D/GAAAABkAAAC6/+//GAAAABkAAAC6/+7/GAAAABkAAAC6/+3/GAAAABkAAADd//v/GAAIABsAAADc//v/GAAAAAAAAADb//v/GAALABsAAADa//v/GAAJABsAAADZ//v/GAAJABsAAADY//v/GAAJABsAAADX//v/GAAJABsAAADW//v/GAAJABsAAADV//v/GAAJABsAAADi//f/GAAJAAMAAADi//b/GAAJAAIAAADi//X/GAAJAAIAAADi//T/GAAKAAAAAADi//P/GAADABoAAADi//L/GAAAABkAAADi//H/GAAAABkAAADh//T/GAAIAAAAAADh//P/GAACABsAAADg//X/GAALAAsAAADg//T/GAALAAgAAADg//P/GAACABsAAADf//b/GAAIAAAAAADf//X/GAAJAAsAAADf//T/GAAKAAgAAADf//P/GAABABoAAADf//L/GAADABgAAADe//n/AgAfAAkAAADe//j/AgAfAAwAAADe//f/GAALAAsAAADe//b/GAALAAoAAADe//X/GAAGAAoAAADe//T/GAAGAAkAAADe//P/GAALAAgAAADe//L/GAABABoAAADe//H/GAADABgAAADd//n/GAADAAMAAADd//j/GAALAAsAAADd//f/GAAGAAoAAADd//b/GAAJAAoAAADd//X/GAAJAAoAAADd//T/GAAJAAoAAADd//P/GAAGAAkAAADd//L/GAALAAgAAADd//H/GAACABsAAADc//n/GAACAAMAAADc//j/GAAJAAsAAADc//f/GAAJAAoAAADc//b/GAAJAAoAAADc//X/GAAJAAoAAADc//T/GAAJAAoAAADc//P/GAAJAAoAAADc//L/GAAKAAgAAADc//H/GAABABoAAADc//D/GAADABgAAADb//n/GAACAAMAAADb//j/GAAJAAsAAADb//f/GAAJAAoAAADb//b/GAAJAAoAAADb//X/GAAJAAoAAADb//T/GAAJAAoAAADb//P/GAAJAAoAAADb//L/GAAGAAkAAADb//H/GAALAAgAAADb//D/GAACABsAAADa//n/GAACAAMAAADa//j/GAAJAAsAAADa//f/GAAJAAoAAADa//b/GAAJAAoAAADa//X/GAAJAAoAAADa//T/GAAJAAoAAADa//P/GAAFAAkAAADa//L/GAAIAAkAAADa//H/GAAIAAgAAADa//D/GAACABsAAADZ//n/GAACAAMAAADZ//j/GAAJAAsAAADZ//f/GAAJAAoAAADZ//b/GAAFAAkAAADZ//X/GAAIAAkAAADZ//T/GAAIAAkAAADZ//P/GAAIAAgAAADZ//L/GAALABsAAADZ//H/GAAHABkAAADZ//D/GAABABgAAADY//n/GAACAAMAAADY//j/GAAIAAsAAADY//f/GAAIAAkAAADY//L/GAAFABsAAADY//H/GAAIABgAAADX//n/GAABAAIAAADX//P/GAADABoAAADk//X/GAAJAAIAAADk//T/GAAJAAIAAADk//P/GAAJAAIAAADk//L/GAAJAAIAAADk/+3/GAADAAIAAADk/+z/GAAAAAEAAADk/+v/GAADAAAAAADk/+r/GAAIABsAAADk/+n/GAAIABkAAADj//D/GAAAAAEAAADj/+//GAAAAAEAAADj/+7/GAAAAAEAAADj/+3/GAABAAAAAADj/+z/GAADAAsAAADj/+v/GAABAAIAAADj/+r/GAAAAAEAAADj/+n/GAAAAAEAAADj/+j/GAADAAAAAADj/+f/GAAIABsAAADj/+b/GAAFABoAAADj/+X/GAAJABoAAADj/+T/GAAJABoAAADj/+P/GAAJABoAAADj/+L/GAAFABkAAADj/+H/GAAIABgAAADj/+D/GAACAAMAAADi/+j/GAABAAIAAADi/+f/GAADAAAAAADi/+b/GAAJABsAAADi/+X/GAAJABoAAADi/+T/GAAJABoAAADi/+P/GAAJABoAAADi/+L/GAAKABgAAADi/+H/GAADAAIAAADi/+D/GAABAAAAAADh/+f/GAACAAMAAADh/+b/GAAIABsAAADh/+X/GAAFABoAAADh/+T/GAAFABkAAADh/+P/GAAIABkAAADh/+L/GAAIABgAAADh/+H/GAACAAMAAADg/+f/GAABAAIAAADg/+b/GAADAAAAAADg/+X/GAAIABsAAADg/+T/GAAIABgAAADg/+P/GAADAAIAAADg/+L/GAAAAAEAAADg/+H/GAABAAAAAADf/+b/GAABAAIAAADf/+X/GAAHAAIAAADf/+T/GAAHAAEAAADf/+P/GAABAAAAAADe/+X/GAAIAAMAAADe/+T/GAAIAAAAAADk/9f/GAAJABoAAADk/9b/GAAGABkAAADk/9X/GAALABgAAADj/93/GAACAAMAAADj/9z/GAAIABsAAADj/9v/GAAFABoAAADj/9r/GAAJABoAAADj/9n/GAAJABoAAADj/9j/GAAJABoAAADj/9f/GAAJABoAAADj/9b/GAAJABoAAADj/9X/GAAKABgAAADi/93/GAABAAIAAADi/9z/GAADAAAAAADi/9v/GAAJABsAAADi/9r/GAAJABoAAADi/9n/GAAJABoAAADi/9j/GAAJABoAAADi/9f/GAAJABoAAADi/9b/GAAJABoAAADi/9X/GAAKABgAAADh/9z/GAACAAMAAADh/9v/GAAJABsAAADh/9r/GAAJABoAAADh/9n/GAAJABoAAADh/9j/GAAJABoAAADh/9f/GAAJABoAAADh/9b/GAAJABoAAADh/9X/GAAKABgAAADg/9z/GAACAAMAAADg/9v/GAAJABsAAADg/9r/GAAJABoAAADg/9n/GAAJABoAAADg/9j/GAAJABoAAADg/9f/GAAJABoAAADg/9b/GAAJABoAAADg/9X/GAAKABgAAADf/9z/GAACAAMAAADf/9v/GAAJABsAAADf/9r/GAAJABoAAADf/9n/GAAJABoAAADf/9j/GAAJABoAAADf/9f/GAAJABoAAADf/9b/GAAJABoAAADf/9X/GAAKABgAAADe/9z/GAACAAMAAADe/9v/GAAIABsAAADe/9r/GAAFABoAAADe/9n/GAAJABoAAADe/9j/GAAJABoAAADe/9f/GAAJABoAAADe/9b/GAAJABoAAADe/9X/GAAKABgAAADd/9z/GAABAAIAAADd/9v/GAADAAAAAADd/9r/GAAJABsAAADd/9n/GAAJABoAAADd/9j/GAAJABoAAADd/9f/GAAJABoAAADd/9b/GAAJABoAAADd/9X/GAAKABgAAADc/9v/GAACAAMAAADc/9r/GAAIABsAAADc/9n/GAAFABoAAADc/9j/GAAJABoAAADc/9f/GAAJABoAAADc/9b/GAAJABoAAADc/9X/GAAKABgAAADb/9v/GAABAAIAAADb/9r/GAADAAAAAADb/9n/GAAIABsAAADb/9j/GAAIABkAAADb/9f/GAAFABoAAADb/9b/GAAJABoAAADb/9X/GAAKABgAAADa/9r/GAABAAIAAADa/9n/GAAAAAEAAADa/9j/GAADAAAAAADa/9f/GAAIABsAAADa/9b/GAAFABoAAADa/9X/GAAKABgAAADZ/9j/GAABAAIAAADZ/9f/GAADAAAAAADZ/9b/GAAIABsAAADZ/9X/GAAIABgAAADY/9f/GAABAAIAAADY/9b/GAAAAAEAAADY/9X/GAAHAAIAAADX/9X/GAAJAAMAAADW/9X/GAAJAAMAAADV/9X/GAAJAAMAAADU/9X/GAAJAAMAAADT/9X/GAAJAAMAAADS/9X/GAAJAAMAAADR/9X/GAAJAAMAAADQ/9b/GAADAAIAAADQ/9X/GAAEAAIAAADP/9f/GAADAAIAAADP/9b/GAABAAAAAADP/9X/GAADABoAAADO/9j/GAADAAIAAADO/9f/GAABAAAAAADO/9b/GAALABsAAADO/9X/GAAGABgAAADN/9r/GAALAAMAAADN/9n/GAALAAIAAADN/9j/GAAGAAAAAADN/9f/GAALABsAAADN/9b/GAAGABoAAADN/9X/GAAKABgAAADM/9r/GAAIAAMAAADM/9n/GAAIAAEAAADM/9j/GAAFAAAAAADM/9f/GAAJABsAAADM/9b/GAAJABoAAADM/9X/GAAKABgAAADL/9j/GAACAAMAAADL/9f/GAAJABsAAADL/9b/GAAJABoAAADL/9X/GAAKABgAAADK/9j/GAACAAMAAADK/9f/GAAJABsAAADK/9b/GAAJABoAAADK/9X/GAAKABgAAADJ/9j/GAACAAMAAADJ/9f/GAAJABsAAADJ/9b/GAAJABoAAADJ/9X/GAAKABgAAADI/9j/GAACAAMAAADI/9f/GAAJABsAAADI/9b/GAAJABoAAADI/9X/GAAKABgAAADH/9j/GAACAAMAAADH/9f/GAAJABsAAADH/9b/GAAJABoAAADH/9X/GAAKABgAAADG/9n/GAALAAMAAADG/9j/GAAGAAAAAADG/9f/GAAIABsAAADG/9b/GAAFABoAAADG/9X/GAAKABgAAADF/9n/GAAIAAMAAADF/9j/GAAEAAEAAADF/9f/GAADAAAAAADF/9b/GAAJABsAAADF/9X/GAAKABgAAADE/9f/GAACAAMAAADE/9b/GAAIABsAAADE/9X/GAAFABgAAADD/9f/GAABAAIAAADD/9b/GAADAAAAAADD/9X/GAABABoAAADC/9b/GAABAAIAAADC/9X/GAAHAAIAAADB/9X/GAAJAAMAAADA/9b/GAADAAIAAADA/9X/GAAEAAIAAAC//9f/GAADAAIAAAC//9b/GAABAAAAAAC//9X/GAADABoAAAC+/9f/GAACAAMAAAC+/9b/GAALABsAAAC+/9X/GAAGABgAAAC9/9j/GAADAAIAAAC9/9f/GAABAAAAAAC9/9b/GAAJABsAAAC9/9X/GAAKABgAAAC8/9n/GAADAAIAAAC8/9j/GAABAAAAAAC8/9f/GAALABsAAAC8/9b/GAAGABoAAAC8/9X/GAAKABgAAAC7/9r/GAADAAIAAAC7/9n/GAABAAAAAAC7/9j/GAALABsAAAC7/9f/GAAGABoAAAC6/9v/GAALAAMAAAC6/9r/GAAGAAAAAAC6/9n/GAADABoAAAC6/9j/GAAEABoAAAC5/9T/AgADAAgAAAC6/9T/GAACAAMAAAC5/9b/AgADAAgAAAC7/9T/GAACAAMAAAC5/9f/AgADAAgAAAC5/9j/AgADAAgAAAC6/9f/GAAIABkAAAC6/9b/GAAIABkAAAC7/9b/GAAJABoAAAC6/9X/GAAIABgAAAC7/9X/GAAKABgAAAC8/9T/GAACAAMAAAC5/+P/AgADAAgAAAC7/+L/GAAIAAkAAAC7/+H/GAAFAAoAAAC6/+H/GAAIAAsAAAC6/+D/GAAIAAkAAAC7/+D/GAAJAAoAAAC8/+D/GAAJAAoAAAC7/9//GAAJAAoAAAC8/9//GAAJAAoAAAC9/9//GAAJAAoAAAC7/97/GAAJAAoAAAC8/97/GAAJAAoAAAC9/97/GAAJAAoAAAC5/+D/AgADAAgAAAC9/93/AgAZAAUAAADf//n/AgAcAAoAAADf//j/AgAeAAoAAADg//j/AgAeAAoAAADg//f/GAAJAAMAAADg//b/GAAKAAAAAADh//f/GAAKAAMAAADh//b/GAAFAAEAAADh//X/GAAIAAEAAADf//f/GAAIAAMAAADf//r/AgAcAAoAAADg//n/AgAcAAoAAADh//j/AgAeAAoAAADe//v/AgAfAAsAAADk//b/GAAGAAIAAAC5/83/DAALAJcAAAC5/87/DAALAJgAAAC5/8//DAALAJkAAAC5/9D/DAALAJoAAAC5/9H/DAALAJsAAAC5/9L/DAALAJwAAAC5/9P/DAALAJ0AAAC6/83/DAAMAJcAAAC6/87/DAAMAJgAAAC6/8//DAAMAJkAAAC6/9D/DAAMAJoAAAC6/9H/DAAMAJsAAAC6/9L/DAAMAJwAAAC6/9P/DAAMAJ0AAAC7/83/DAANAJcAAAC7/87/DAANAJgAAAC7/8//DAANAJkAAAC7/9D/DAANAJoAAAC7/9H/DAANAJsAAAC7/9L/DAANAJwAAAC7/9P/DAANAJ0AAAC8/83/DAAOAJcAAAC8/87/DAAOAJgAAAC8/8//DAAOAJkAAAC8/9D/DAAOAJoAAAC8/9H/DAAOAJsAAAC8/9L/DAAOAJwAAAC8/9P/DAAOAJ0AAAC9/83/DAAPAJcAAAC9/87/DAAPAJgAAAC9/8//DAAPAJkAAAC9/9D/DAAPAJoAAAC9/9H/DAAPAJsAAAC9/9L/DAAPAJwAAAC9/9P/DAAPAJ0AAAC+/83/DAAQAJcAAAC+/87/DAAQAJgAAAC+/8//DAAQAJkAAAC+/9D/DAAQAJoAAAC+/9H/DAAQAJsAAAC+/9L/DAAQAJwAAAC+/9P/DAAQAJ0AAAC//83/DAARAJcAAAC//87/DAARAJgAAAC//8//DAARAJkAAAC//9D/DAARAJoAAAC//9H/DAARAJsAAAC//9L/DAARAJwAAAC//9P/DAARAJ0AAADA/83/DAALAJcAAADA/87/DAALAJgAAADA/8//DAALAJkAAADA/9D/DAALAJoAAADA/9H/DAALAJsAAADA/9L/DAALAJwAAADA/9P/DAALAJ0AAADB/83/DAAMAJcAAADB/87/DAAMAJgAAADB/8//DAAMAJkAAADB/9D/DAAMAJoAAADB/9H/DAAMAJsAAADB/9L/DAAMAJwAAADB/9P/DAAMAJ0AAADC/83/DAANAJcAAADC/87/DAANAJgAAADC/8//DAANAJkAAADC/9D/DAANAJoAAADC/9H/DAANAJsAAADC/9L/DAANAJwAAADC/9P/DAANAJ0AAADD/83/DAAOAJcAAADD/87/DAAOAJgAAADD/8//DAAOAJkAAADD/9D/DAAOAJoAAADD/9H/DAAOAJsAAADD/9L/DAAOAJwAAADD/9P/DAAOAJ0AAADE/83/DAAPAJcAAADE/87/DAAPAJgAAADE/8//DAAPAJkAAADE/9D/DAAPAJoAAADE/9H/DAAPAJsAAADE/9L/DAAPAJwAAADE/9P/DAAPAJ0AAADF/83/DAAQAJcAAADF/87/DAAQAJgAAADF/8//DAAQAJkAAADF/9D/DAAQAJoAAADF/9H/DAAQAJsAAADF/9L/DAAQAJwAAADF/9P/DAAQAJ0AAADG/83/DAARAJcAAADG/87/DAARAJgAAADG/8//DAARAJkAAADG/9D/DAARAJoAAADG/9H/DAARAJsAAADG/9L/DAARAJwAAADG/9P/DAARAJ0AAADH/83/DAASAJcAAADH/87/DAASAJgAAADH/8//DAASAJkAAADH/9D/DAASAJoAAADH/9H/DAASAJsAAADH/9L/DAASAJwAAADH/9P/DAASAJ0AAADI/83/DAALAJcAAADI/87/DAALAJgAAADI/8//DAALAJkAAADI/9D/DAALAJoAAADI/9H/DAALAJsAAADI/9L/DAALAJwAAADI/9P/DAALAJ0AAADJ/83/DAAMAJcAAADJ/87/DAAMAJgAAADJ/8//DAAMAJkAAADJ/9D/DAAMAJoAAADJ/9H/DAAMAJsAAADJ/9L/DAAMAJwAAADJ/9P/DAAMAJ0AAADK/83/DAANAJcAAADK/87/DAANAJgAAADK/8//DAANAJkAAADK/9D/DAANAJoAAADK/9H/DAANAJsAAADK/9L/DAANAJwAAADK/9P/DAANAJ0AAADL/83/DAAOAJcAAADL/87/DAAOAJgAAADL/8//DAAOAJkAAADL/9D/DAAOAJoAAADL/9H/DAAOAJsAAADL/9L/DAAOAJwAAADL/9P/DAAOAJ0AAADM/83/DAAPAJcAAADM/87/DAAPAJgAAADM/8//DAAPAJkAAADM/9D/DAAPAJoAAADM/9H/DAAPAJsAAADM/9L/DAAPAJwAAADM/9P/DAAPAJ0AAADN/83/DAAQAJcAAADN/87/DAAQAJgAAADN/8//DAAQAJkAAADN/9D/DAAQAJoAAADN/9H/DAAQAJsAAADN/9L/DAAQAJwAAADN/9P/DAAQAJ0AAADO/83/DAARAJcAAADO/87/DAARAJgAAADO/8//DAARAJkAAADO/9D/DAARAJoAAADO/9H/DAARAJsAAADO/9L/DAARAJwAAADO/9P/DAARAJ0AAADP/83/DAASAJcAAADP/87/DAASAJgAAADP/8//DAASAJkAAADP/9D/DAASAJoAAADP/9H/DAASAJsAAADP/9L/DAASAJwAAADP/9P/DAASAJ0AAADQ/83/DAALAJcAAADQ/87/DAALAJgAAADQ/8//DAALAJkAAADQ/9D/DAALAJoAAADQ/9H/DAALAJsAAADQ/9L/DAALAJwAAADQ/9P/DAALAJ0AAADR/83/DAAMAJcAAADR/87/DAAMAJgAAADR/8//DAAMAJkAAADR/9D/DAAMAJoAAADR/9H/DAAMAJsAAADR/9L/DAAMAJwAAADR/9P/DAAMAJ0AAADS/83/DAANAJcAAADS/87/DAANAJgAAADS/8//DAANAJkAAADS/9D/DAANAJoAAADS/9H/DAANAJsAAADS/9L/DAANAJwAAADS/9P/DAANAJ0AAADT/83/DAAOAJcAAADT/87/DAAOAJgAAADT/8//DAAOAJkAAADT/9D/DAAOAJoAAADT/9H/DAAOAJsAAADT/9L/DAAOAJwAAADT/9P/DAAOAJ0AAADU/83/DAAPAJcAAADU/87/DAAPAJgAAADU/8//DAAPAJkAAADU/9D/DAAPAJoAAADU/9H/DAAPAJsAAADU/9L/DAAPAJwAAADU/9P/DAAPAJ0AAADV/83/DAAQAJcAAADV/87/DAAQAJgAAADV/8//DAAQAJkAAADV/9D/DAAQAJoAAADV/9H/DAAQAJsAAADV/9L/DAAQAJwAAADV/9P/DAAQAJ0AAADW/83/DAARAJcAAADW/87/DAARAJgAAADW/8//DAARAJkAAADW/9D/DAARAJoAAADW/9H/DAARAJsAAADW/9L/DAARAJwAAADW/9P/DAARAJ0AAADX/83/DAASAJcAAADX/87/DAASAJgAAADX/8//DAASAJkAAADX/9D/DAASAJoAAADX/9H/DAASAJsAAADX/9L/DAASAJwAAADX/9P/DAASAJ0AAADY/83/DAALAJcAAADY/87/DAALAJgAAADY/8//DAALAJkAAADY/9D/DAALAJoAAADY/9H/DAALAJsAAADY/9L/DAALAJwAAADY/9P/DAALAJ0AAADZ/83/DAAMAJcAAADZ/87/DAAMAJgAAADZ/8//DAAMAJkAAADZ/9D/DAAMAJoAAADZ/9H/DAAMAJsAAADZ/9L/DAAMAJwAAADZ/9P/DAAMAJ0AAADa/83/DAANAJcAAADa/87/DAANAJgAAADa/8//DAANAJkAAADa/9D/DAANAJoAAADa/9H/DAANAJsAAADa/9L/DAANAJwAAADa/9P/DAANAJ0AAADb/83/DAAOAJcAAADb/87/DAAOAJgAAADb/8//DAAOAJkAAADb/9D/DAAOAJoAAADb/9H/DAAOAJsAAADb/9L/DAAOAJwAAADb/9P/DAAOAJ0AAADc/83/DAAPAJcAAADc/87/DAAPAJgAAADc/8//DAAPAJkAAADc/9D/DAAPAJoAAADc/9H/DAAPAJsAAADc/9L/DAAPAJwAAADc/9P/DAAPAJ0AAADd/83/DAAQAJcAAADd/87/DAAQAJgAAADd/8//DAAQAJkAAADd/9D/DAAQAJoAAADd/9H/DAAQAJsAAADd/9L/DAAQAJwAAADd/9P/DAAQAJ0AAADe/8//DAARAJkAAADe/9D/DAARAJoAAADe/9H/DAARAJsAAADe/9L/DAARAJwAAADe/9P/DAARAJ0AAADf/8//DAASAJkAAADf/9D/DAASAJoAAADf/9H/DAASAJsAAADf/9L/DAASAJwAAADf/9P/DAASAJ0AAADg/8//DAALAJkAAADg/9D/DAALAJoAAADg/9H/DAALAJsAAADg/9L/DAALAJwAAADg/9P/DAALAJ0AAADh/8//DAAMAJkAAADh/9D/DAAMAJoAAADh/9H/DAAMAJsAAADh/9L/DAAMAJwAAADh/9P/DAAMAJ0AAADi/8//DAANAJkAAADi/9D/DAANAJoAAADi/9H/DAANAJsAAADi/9L/DAANAJwAAADi/9P/DAANAJ0AAADj/8//DAAOAJkAAADj/9D/DAAOAJoAAADj/9H/DAAOAJsAAADj/9L/DAAOAJwAAADj/9P/DAAOAJ0AAADk/8//DAAPAJkAAADk/9D/DAAPAJoAAADk/9H/DAAPAJsAAADk/9L/DAAPAJwAAADk/9P/DAAPAJ0AAADe/83/DAARAJcAAADe/87/DAARAJgAAADf/83/DAASAJcAAADf/87/DAASAJgAAADg/83/DAALAJcAAADg/87/DAALAJgAAADh/83/DAAMAJcAAADh/87/DAAMAJgAAADi/83/DAANAJcAAADi/87/DAANAJgAAADj/83/DAAOAJcAAADj/87/DAAOAJgAAADk/83/DAAPAJcAAADk/87/DAAPAJgAAADl/83/DAAQAJcAAADl/87/DAAQAJgAAADl/8//DAAQAJkAAADl/9D/DAAQAJoAAADl/9H/DAAQAJsAAADl/9L/DAAQAJwAAADm/87/DAARAJgAAADm/8//DAARAJkAAAC2/87/DAAHAJgAAAC3/83/DAAIAJcAAAC3/87/DAAIAJgAAAC4/83/DAAJAJcAAAC4/87/DAAJAJgAAAC4/8//DAAJAJkAAAC4/9D/DAAJAJoAAAC4/9H/DAAJAJsAAAC4/9L/DAAJAJwAAADl/9P/DAAQAJ0AAADm/83/DAARAJcAAADm/9D/DAARAJoAAADm/9H/DAARAJsAAADm/9L/DAARAJwAAADm/9P/DAARAJ0AAADn/83/DAASAJcAAADn/87/DAASAJgAAADn/8//DAASAJkAAADn/9D/DAASAJoAAADn/9H/DAASAJsAAADn/9L/DAASAJwAAADn/9P/DAASAJ0AAADo/83/DAAUAJcAAADo/87/DAAUAJgAAADo/8//DAAUAJkAAADo/9D/DAAUAJoAAADo/9H/DAAUAJsAAADo/9L/DAAUAJwAAADp/87/AwAnAAAAAGDp/8//AwAoAAAAAGC3/+P/AgADAAgAAAC3/+T/AgADAAgAAAC4/9P/DAAJAJ0AAAC5/9X/AgADAAgAAAC5/97/AgADAAgAAAC6/97/GAAIAAkAAAC6/9//GAAIAAkAAAC5/9//AgADAAgAAAC5/+H/AgADAAgAAAC5/+L/AgADAAgAAAC6/+L/GAALAAAAAAC3/9L/DAAIAJwAAAC2/9L/DAAHAJwAAAC2/9H/DAAHAJsAAAC3/9H/DAAIAJsAAAC3/9D/DAAIAJoAAAC3/8//DAAIAJkAAAC2/9D/DAAHAJoAAAC2/8//DAAHAJkAAAC0/9T/AgADAAgAAAC0/9X/AgADAAgAAAC0/9b/AgADAAgAAAC0/9f/AgADAAgAAAC0/9j/AgADAAgAAAC0/9n/AgADAAgAAAC0/9r/AgADAAgAAAC0/9v/AgADAAgAAAC0/9z/AgADAAgAAAC0/93/AgADAAgAAAC0/97/AgADAAgAAAC0/9//AgADAAgAAAC0/+D/AgADAAgAAAC0/+H/AgADAAgAAAC0/+L/AgADAAgAAAC0/+P/AgADAAgAAAC0/+T/AgADAAgAAAC0/+X/AgADAAgAAAC0/+b/AgADAAgAAAC0/+f/AgADAAgAAAC0/+j/AgADAAgAAAC0/+n/AgADAAgAAAC0/+r/AgADAAgAAAC0/+v/AgADAAgAAAC0/+z/AgADAAgAAAC0/+3/AgADAAgAAAC0/+7/AgADAAgAAAC0/+//AgADAAgAAAC0//D/AgADAAgAAAC0//H/AgADAAgAAAC0//L/AgADAAgAAAC0//P/AgADAAgAAAC0//T/AgADAAgAAAC0//X/AgADAAgAAAC0//b/AgADAAgAAAC0//f/AgADAAgAAAC0//j/AgADAAgAAAC0//n/AgADAAgAAAC0//r/AgADAAgAAAC0//v/AgADAAgAAAC1/9T/AgADAAgAAAC1/9X/AgADAAgAAAC1/9b/AgADAAgAAAC1/9f/AgADAAgAAAC1/9j/AgADAAgAAAC1/9n/AgADAAgAAAC1/9r/AgADAAgAAAC1/9v/AgADAAgAAAC1/9z/AgADAAgAAAC1/93/AgADAAgAAAC1/97/AgADAAgAAAC1/9//AgADAAgAAAC1/+D/AgADAAgAAAC1/+H/AgADAAgAAAC1/+L/AgADAAgAAAC1/+P/AgADAAgAAAC1/+T/AgADAAgAAAC1/+X/AgADAAgAAAC1/+b/AgADAAgAAAC1/+f/AgADAAgAAAC1/+j/AgADAAgAAAC1/+n/AgADAAgAAAC1/+r/AgADAAgAAAC1/+v/AgADAAgAAAC1/+z/AgADAAgAAAC1/+3/AgADAAgAAAC1/+7/AgADAAgAAAC1/+//AgADAAgAAAC1//D/AgADAAgAAAC1//H/AgADAAgAAAC1//L/AgADAAgAAAC1//P/AgADAAgAAAC1//T/AgADAAgAAAC1//X/AgADAAgAAAC1//b/AgADAAgAAAC1//f/AgADAAgAAAC1//j/AgADAAgAAAC1//n/AgADAAgAAAC1//r/AgADAAgAAAC1//v/AgADAAgAAAC2/9T/AgADAAgAAAC2/9X/AgADAAgAAAC2/9b/AgADAAgAAAC2/9f/AgADAAgAAAC2/9j/AgADAAgAAAC2/9n/AgADAAgAAAC2/9r/AgADAAgAAAC2/9v/AgADAAgAAAC2/9z/AgADAAgAAAC2/93/AgADAAgAAAC2/97/AgADAAgAAAC2/9//AgADAAgAAAC2/+D/AgADAAgAAAC2/+H/AgADAAgAAAC2/+L/AgADAAgAAAC2/+P/AgADAAgAAAC2/+T/AgADAAgAAAC2/+X/AgADAAgAAAC2/+b/AgADAAgAAAC2/+f/AgADAAgAAAC2/+j/AgADAAgAAAC2/+n/AgADAAgAAAC2/+r/AgADAAgAAAC2/+v/AgADAAgAAAC2/+z/AgADAAgAAAC2/+3/AgADAAgAAAC2/+7/AgADAAgAAAC2/+//AgADAAgAAAC2//D/AgADAAgAAAC2//H/AgADAAgAAAC2//L/AgADAAgAAAC2//P/AgADAAgAAAC2//T/AgADAAgAAAC2//X/AgADAAgAAAC2//b/AgADAAgAAAC2//f/AgADAAgAAAC2//j/AgADAAgAAAC2//n/AgADAAgAAAC2//r/AgADAAgAAAC2//v/AgADAAgAAAC3/9T/AgADAAgAAAC3/9X/AgADAAgAAAC3/9b/AgADAAgAAAC3/9f/AgADAAgAAAC3/9j/AgADAAgAAAC3/9n/AgADAAgAAAC3/9r/AgADAAgAAAC3/9v/AgADAAgAAAC3/9z/AgADAAgAAAC3/93/AgADAAgAAAC3/97/AgADAAgAAAC3/9//AgADAAgAAAC3/+D/AgADAAgAAAC3/+H/AgADAAgAAAC3/+L/AgADAAgAAAC3/+X/AgADAAgAAAC3/+b/AgADAAgAAAC3/+f/AgADAAgAAAC3/+j/AgADAAgAAAC3/+n/AgADAAgAAAC3/+r/AgADAAgAAAC3/+v/AgADAAgAAAC3/+z/AgADAAgAAAC3/+3/AgADAAgAAAC3/+7/AgADAAgAAAC3/+//AgADAAgAAAC3//D/AgADAAgAAAC3//H/AgADAAgAAAC3//L/AgADAAgAAAC3//P/AgADAAgAAAC3//T/AgADAAgAAAC3//X/AgADAAgAAAC3//b/AgADAAgAAAC3//f/AgADAAgAAAC3//j/AgADAAgAAAC3//n/AgADAAgAAAC3//r/AgADAAgAAAC3//v/AgADAAgAAAC4/9T/AgADAAgAAAC4/9X/AgADAAgAAAC4/9b/AgADAAgAAAC4/9f/AgADAAgAAAC4/9j/AgADAAgAAAC4/9n/AgADAAgAAAC4/9r/AgADAAgAAAC4/9v/AgADAAgAAAC4/9z/AgADAAgAAAC4/93/AgADAAgAAAC4/97/AgADAAgAAAC4/9//AgADAAgAAAC4/+D/AgADAAgAAAC4/+H/AgADAAgAAAC4/+L/AgADAAgAAAC4/+P/AgADAAgAAAC4/+T/AgADAAgAAAC4/+X/AgADAAgAAAC4/+b/AgADAAgAAAC4/+f/AgADAAgAAAC4/+j/AgADAAgAAAC4/+n/AgADAAgAAAC4/+r/AgADAAgAAAC4/+v/AgADAAgAAAC4/+z/AgADAAgAAAC4/+3/AgADAAgAAAC4/+7/AgADAAgAAAC4/+//AgADAAgAAAC4//D/AgADAAgAAAC4//H/AgADAAgAAAC4//L/AgADAAgAAAC4//P/AgADAAgAAAC4//T/AgADAAgAAAC4//X/AgADAAgAAAC4//b/AgADAAgAAAC4//f/AgADAAgAAAC4//j/AgADAAgAAAC4//n/AgADAAgAAAC4//r/AgADAAgAAAC4//v/AgADAAgAAADm/9T/AgADAAgAAADm/9X/GAACAAMAAADm/9b/GAALABgAAADm/9f/GAALABoAAADm/9j/GAALABsAAADm/9n/AgADAAgAAADm/9r/GAAGAAAAAADm/9v/GAALAAIAAADm/9z/GAALAAMAAADm/93/AgADAAgAAADm/97/AgADAAgAAADm/9//AgADAAgAAADm/+D/AgADAAgAAADm/+H/AgADAAgAAADm/+L/AgADAAgAAADm/+P/AgADAAgAAADm/+T/AgADAAgAAADm/+X/AgADAAgAAADm/+b/GAADAAAAAADm/+f/GAAAAAEAAADm/+j/GAAAAAEAAADm/+n/GAAAAAEAAADm/+r/GAAAAAEAAADm/+v/GAAAAAEAAADm/+z/GAAAAAEAAADm/+3/GAAAAAEAAADm/+7/GAABAAIAAADm/+//GAALABgAAADm//D/GAALABsAAADm//H/AgADAAgAAADm//L/GAAKAAAAAADm//P/GAAJAAIAAADm//T/GAAJAAIAAADm//X/GAAJAAIAAADm//b/GAAJAAMAAADm//f/AgADAAgAAADm//j/AgAeAAoAAADn/9T/AgADAAgAAADn/9X/GAADAAAAAADn/9b/GAAAAAEAAADn/9f/GAAAAAEAAADn/9j/GAAAAAEAAADn/9n/GAAAAAEAAADn/9r/GAADAAIAAADn/9v/AgADAAgAAADn/9z/AgADAAgAAADn/93/AgADAAgAAADn/97/AgADAAgAAADn/9//AgADAAgAAADn/+D/AgADAAgAAADn/+H/AgADAAgAAADn/+L/AgADAAgAAADn/+P/AgADAAgAAADn/+T/AgADAAgAAADn/+X/AgADAAgAAADn/+b/AgADAAgAAADn/+f/AgADAAgAAADn/+j/AgADAAgAAADn/+n/AgADAAgAAADn/+r/AgADAAgAAADn/+v/AgADAAgAAADn/+z/AgADAAgAAADn/+3/AgADAAgAAADn/+7/GAADAAAAAADn/+//GAAAAAEAAADn//D/GAAAAAEAAADn//H/GAAAAAEAAADn//L/GAAHAAEAAADn//P/GAALAAIAAADn//T/GAAGAAEAAADn//X/GAAGAAIAAADn//b/GAALAAMAAADn//f/AgADAAgAAADn//j/AgAeAAoAAADo/+H/AgADAAgAAADo/+L/AgADAAgAAADo/+P/AgADAAgAAADo/+T/AgADAAgAAADo/+X/AgADAAgAAADo/+b/AgADAAgAAADo/+f/AgADAAgAAADo/+j/AgADAAgAAADo/+n/AgADAAgAAADo/+r/AgADAAgAAADo/+v/AgADAAgAAADo/+z/AgADAAgAAADo/+3/AgADAAgAAADo/+7/AgADAAgAAADo/+//AgADAAgAAADo//D/AgADAAgAAADo//H/AgADAAgAAADo//L/AgADAAgAAADo//P/AgADAAgAAADo//T/GAALAAAAAADo//X/GAALAAMAAADo//b/AgADAAgAAADo//f/AgADAAgAAADo//j/AgAeAAwAAADj//f/GAAJAAMAAADj//j/AgAeAAoAAADj//n/AgAcAAoAAADj//r/AgAcAAoAAADj//v/AgAeAAkAAADj//z/AwAoAAAAAADk//f/GAALAAMAAADk//j/AgAeAAoAAADk//n/AgAbAAoAAADk//r/AgAcAAoAAADk//v/AgAeAAkAAADk//z/AwAnAAAAAADl//f/AgADAAgAAADl//j/AgAeAAoAAADl//n/AgAcAAoAAADl//r/AgAcAAoAAADl//v/AgAeAAkAAADl//z/AwAoAAAAAADl//b/GAAJAAMAAADd//z/AwAoAAAAAADe//z/AwAnAAAAAADf//z/AwAoAAAAAADg//z/AwAnAAAAAADh//z/AwAoAAAAAADi//z/AwAnAAAAAADf//v/AgAeAAkAAADg//v/AgAeAAkAAADh//v/AgAeAAkAAADi//v/AgAeAAkAAADg//r/AgAbAAoAAADh//r/AgAcAAoAAADi//r/AgAcAAoAAADo/9T/AgADAAgAAADo/9X/AgADAAgAAADo/9b/AgADAAgAAADo/9f/AgADAAgAAADo/9j/AgADAAgAAADo/9n/AgADAAgAAADo/9r/AgADAAgAAADo/9v/AgADAAgAAADo/9z/AgADAAgAAADo/93/AgADAAgAAADo/97/AgADAAgAAADo/9//AgADAAgAAADo/+D/AgADAAgAAADi//j/AgAeAAoAAADi//n/AgAcAAoAAADh//n/AgAcAAoAAADd//3/AwAoAAEAAADd//7/AwAeAAMAAADd////AwAiAAQAAADd/wAAAwAoAAUAAADd/wEAAwAiAAYAAADd/wIAAwAfAAAAAADd/wMAAwAoAAAAAADd/wQAAwAoAAEAAADe//3/AwAnAAEAAADe//7/AwAeAAMAAADe////AwAgAAQAAADe/wAAAwAoAAUAAADe/wEAAwAgAAYAAADe/wIAAwAfAAAAAADe/wMAAwAnAAAAAADe/wQAAwAnAAEAAADf//3/AwAoAAEAAADf//7/AwAfAAMAAADf////AwAhAAQAAADf/wAAAwAoAAUAAADf/wEAAwAhAAYAAADf/wIAAwAfAAAAAADf/wMAAwAoAAAAAADf/wQAAwAoAAEAAADg//3/AwAnAAEAAADg//7/AwAeAAMAAADg////AwAiAAQAAADg/wAAAwAoAAUAAADg/wEAAwAiAAYAAADg/wIAAwAfAAAAAADg/wMAAwAnAAAAAADg/wQAAwAnAAEAAADh//3/AwAoAAEAAADh//7/AwAeAAMAAADh////AwAeAAQAAADh/wAAAwAoAAUAAADh/wEAAwAeAAYAAADh/wIAAwAfAAAAAADh/wMAAwAoAAAAAADh/wQAAwAoAAEAAADi//3/AwAnAAEAAADi//7/AwAeAAMAAADi////AwAfAAQAAADi/wAAAwAoAAUAAADi/wEAAwAfAAYAAADi/wIAAwAfAAAAAADi/wMAAwAnAAAAAADi/wQAAwAnAAEAAADj//3/AwAoAAEAAADj//7/AwAfAAMAAADj////AwAgAAQAAADj/wAAAwAoAAUAAADj/wEAAwAgAAYAAADj/wIAAwAfAAAAAADj/wMAAwAoAAAAAADj/wQAAwAoAAEAAADk//3/AwAnAAEAAADk//7/AwAeAAMAAADk////AwAhAAQAAADk/wAAAwAoAAUAAADk/wEAAwAhAAYAAADk/wIAAwAfAAAAAADk/wMAAwAnAAAAAADk/wQAAwAnAAEAAADW//3/AwAnAAEAAADW//7/AwAeAAMAAADW////AwAgAAQAAADW/wAAAwAoAAUAAADW/wEAAwAgAAYAAADW/wIAAwAfAAAAAADW/wMAAwAnAAAAAADW/wQAAwAnAAEAAADX//3/AwAoAAEAAADX//7/AwAfAAMAAADX////AwAhAAQAAADX/wAAAwAoAAUAAADX/wEAAwAhAAYAAADX/wIAAwAfAAAAAADX/wMAAwAoAAAAAADX/wQAAwAoAAEAAADY//3/AwAnAAEAAADY//7/AwAeAAMAAADY////AwAiAAQAAADY/wAAAwAoAAUAAADY/wEAAwAiAAYAAADY/wIAAwAfAAAAAADY/wMAAwAnAAAAAADY/wQAAwAnAAEAAADZ//3/AwAoAAEAAADZ//7/AwAeAAMAAADZ////AwAeAAQAAADZ/wAAAwAoAAUAAADZ/wEAAwAeAAYAAADZ/wIAAwAfAAAAAADZ/wMAAwAoAAAAAADZ/wQAAwAoAAEAAADa//3/AwAnAAEAAADa//7/AwAeAAMAAADa////AwAfAAQAAADa/wAAAwAoAAUAAADa/wEAAwAfAAYAAADa/wIAAwAfAAAAAADa/wMAAwAnAAAAAADa/wQAAwAnAAEAAADb//3/AwAoAAEAAADb//7/AwAfAAMAAADb////AwAgAAQAAADb/wAAAwAoAAUAAADb/wEAAwAgAAYAAADb/wIAAwAfAAAAAADb/wMAAwAoAAAAAADb/wQAAwAoAAEAAADc//3/AwAnAAEAAADc//7/AwAeAAMAAADc////AwAhAAQAAADc/wAAAwAoAAUAAADc/wEAAwAhAAYAAADc/wIAAwAfAAAAAADc/wMAAwAnAAAAAADc/wQAAwAnAAEAAADO//3/AwAnAAEAAADO//7/AwAeAAMAAADO////AwAgAAQAAADO/wAAAwAoAAUAAADO/wEAAwAgAAYAAADO/wIAAwAfAAAAAADO/wMAAwAnAAAAAADO/wQAAwAnAAEAAADP//3/AwAoAAEAAADP//7/AwAfAAMAAADP////AwAhAAQAAADP/wAAAwAoAAUAAADP/wEAAwAhAAYAAADP/wIAAwAfAAAAAADP/wMAAwAoAAAAAADP/wQAAwAoAAEAAADQ//3/AwAnAAEAAADQ//7/AwAeAAMAAADQ////AwAiAAQAAADQ/wAAAwAoAAUAAADQ/wEAAwAiAAYAAADQ/wIAAwAfAAAAAADQ/wMAAwAnAAAAAADQ/wQAAwAnAAEAAADR//3/AwAoAAEAAADR//7/AwAeAAMAAADR////AwAeAAQAAADR/wAAAwAoAAUAAADR/wEAAwAeAAYAAADR/wIAAwAfAAAAAADR/wMAAwAoAAAAAADR/wQAAwAoAAEAAADS//3/AwAnAAEAAADS//7/AwAeAAMAAADS////AwAfAAQAAADS/wAAAwAoAAUAAADS/wEAAwAfAAYAAADS/wIAAwAfAAAAAADS/wMAAwAnAAAAAADS/wQAAwAnAAEAAADT//3/AwAoAAEAAADT//7/AwAfAAMAAADT////AwAgAAQAAADT/wAAAwAoAAUAAADT/wEAAwAgAAYAAADT/wIAAwAfAAAAAADT/wMAAwAoAAAAAADT/wQAAwAoAAEAAADU//3/AwAnAAEAAADU//7/AwAeAAMAAADU////AwAhAAQAAADU/wAAAwAoAAUAAADU/wEAAwAhAAYAAADU/wIAAwAfAAAAAADU/wMAAwAnAAAAAADU/wQAAwAnAAEAAADV//3/AwAoAAEAAADV//7/AwAeAAMAAADV////AwAiAAQAAADV/wAAAwAoAAUAAADV/wEAAwAiAAYAAADV/wIAAwAfAAAAAADV/wMAAwAoAAAAAADV/wQAAwAoAAEAAADG//3/AwAnAAEAAADG//7/AwAeAAMAAADG////AwAgAAQAAADG/wAAAwAoAAUAAADG/wEAAwAgAAYAAADG/wIAAwAfAAAAAADG/wMAAwAnAAAAAADG/wQAAwAnAAEAAADH//3/AwAoAAEAAADH//7/AwAfAAMAAADH////AwAhAAQAAADH/wAAAwAoAAUAAADH/wEAAwAhAAYAAADH/wIAAwAfAAAAAADH/wMAAwAoAAAAAADH/wQAAwAoAAEAAADI//3/AwAnAAEAAADI//7/AwAeAAMAAADI////AwAiAAQAAADI/wAAAwAoAAUAAADI/wEAAwAiAAYAAADI/wIAAwAfAAAAAADI/wMAAwAnAAAAAADI/wQAAwAnAAEAAADJ//3/AwAoAAEAAADJ//7/AwAeAAMAAADJ////AwAeAAQAAADJ/wAAAwAoAAUAAADJ/wEAAwAeAAYAAADJ/wIAAwAfAAAAAADJ/wMAAwAoAAAAAADJ/wQAAwAoAAEAAADK//3/AwAnAAEAAADK//7/AwAeAAMAAADK////AwAfAAQAAADK/wAAAwAoAAUAAADK/wEAAwAfAAYAAADK/wIAAwAfAAAAAADK/wMAAwAnAAAAAADK/wQAAwAnAAEAAADL//3/AwAoAAEAAADL//7/AwAfAAMAAADL////AwAgAAQAAADL/wAAAwAoAAUAAADL/wEAAwAgAAYAAADL/wIAAwAfAAAAAADL/wMAAwAoAAAAAADL/wQAAwAoAAEAAADM//3/AwAnAAEAAADM//7/AwAeAAMAAADM////AwAhAAQAAADM/wAAAwAoAAUAAADM/wEAAwAhAAYAAADM/wIAAwAfAAAAAADM/wMAAwAnAAAAAADM/wQAAwAnAAEAAADN//3/AwAoAAEAAADN//7/AwAeAAMAAADN////AwAiAAQAAADN/wAAAwAoAAUAAADN/wEAAwAiAAYAAADN/wIAAwAfAAAAAADN/wMAAwAoAAAAAADN/wQAAwAoAAEAAAC+//3/AwAoAAEAAAC+//7/AwAeAAMAAAC+////AwAiAAQAAAC+/wAAAwAoAAUAAAC+/wEAAwAiAAYAAAC+/wIAAwAfAAAAAAC+/wMAAwAoAAAAAAC+/wQAAwAoAAEAAAC///3/AwAoAAEAAAC///7/AwAfAAMAAAC/////AwAhAAQAAAC//wAAAwAoAAUAAAC//wEAAwAhAAYAAAC//wIAAwAfAAAAAAC//wMAAwAoAAAAAAC//wQAAwAoAAEAAADA//3/AwAnAAEAAADA//7/AwAeAAMAAADA////AwAiAAQAAADA/wAAAwAoAAUAAADA/wEAAwAiAAYAAADA/wIAAwAfAAAAAADA/wMAAwAnAAAAAADA/wQAAwAnAAEAAADB//3/AwAoAAEAAADB//7/AwAeAAMAAADB////AwAeAAQAAADB/wAAAwAoAAUAAADB/wEAAwAeAAYAAADB/wIAAwAfAAAAAADB/wMAAwAoAAAAAADB/wQAAwAoAAEAAADC//3/AwAnAAEAAADC//7/AwAeAAMAAADC////AwAfAAQAAADC/wAAAwAoAAUAAADC/wEAAwAfAAYAAADC/wIAAwAfAAAAAADC/wMAAwAnAAAAAADC/wQAAwAnAAEAAADD//3/AwAoAAEAAADD//7/AwAfAAMAAADD////AwAgAAQAAADD/wAAAwAoAAUAAADD/wEAAwAgAAYAAADD/wIAAwAfAAAAAADD/wMAAwAoAAAAAADD/wQAAwAoAAEAAADE//3/AwAnAAEAAADE//7/AwAeAAMAAADE////AwAhAAQAAADE/wAAAwAoAAUAAADE/wEAAwAhAAYAAADE/wIAAwAfAAAAAADE/wMAAwAnAAAAAADE/wQAAwAnAAEAAADF//3/AwAoAAEAAADF//7/AwAeAAMAAADF////AwAiAAQAAADF/wAAAwAoAAUAAADF/wEAAwAiAAYAAADF/wIAAwAfAAAAAADF/wMAAwAoAAAAAADF/wQAAwAoAAEAAAC3//z/AwAnAAAAAAC3//3/AwAnAAEAAAC3//7/AwAeAAMAAAC3////AwAgAAQAAAC3/wAAAwAoAAUAAAC3/wEAAwAgAAYAAAC3/wIAAwAfAAAAAAC3/wMAAwAnAAAAAAC3/wQAAwAnAAEAAAC4//z/AwAoAAAAAAC4//3/AwAoAAEAAAC4//7/AwAfAAMAAAC4////AwAhAAQAAAC4/wAAAwAoAAUAAAC4/wEAAwAhAAYAAAC4/wIAAwAfAAAAAAC4/wMAAwAoAAAAAAC4/wQAAwAoAAEAAAC5//3/AwAnAAEAAAC5//7/AwAeAAMAAAC5////AwAiAAQAAAC5/wAAAwAoAAUAAAC5/wEAAwAiAAYAAAC5/wIAAwAfAAAAAAC5/wMAAwAnAAAAAAC5/wQAAwAnAAEAAAC6//3/AwAoAAEAAAC6//7/AwAeAAMAAAC6////AwAeAAQAAAC6/wAAAwAoAAUAAAC6/wEAAwAeAAYAAAC6/wIAAwAfAAAAAAC6/wMAAwAoAAAAAAC6/wQAAwAoAAEAAAC7//3/AwAnAAEAAAC7//7/AwAeAAMAAAC7////AwAfAAQAAAC7/wAAAwAoAAUAAAC7/wEAAwAfAAYAAAC7/wIAAwAfAAAAAAC7/wMAAwAnAAAAAAC7/wQAAwAnAAEAAAC8//3/AwAoAAEAAAC8//7/AwAfAAMAAAC8////AwAgAAQAAAC8/wAAAwAoAAUAAAC8/wEAAwAgAAYAAAC8/wIAAwAfAAAAAAC8/wMAAwAoAAAAAAC8/wQAAwAoAAEAAAC9//3/AwAnAAEAAAC9//7/AwAeAAMAAAC9////AwAhAAQAAAC9/wAAAwAoAAUAAAC9/wEAAwAhAAYAAAC9/wIAAwAfAAAAAAC9/wMAAwAnAAAAAAC9/wQAAwAnAAEAAACv//z/AwApAAYAAACv//3/AwApAAYAAACv//7/AwApAAYAAACv////AwApAAYAAACv/wAAAwAqAAUAAACv/wEAAwApAAYAAACv/wIAAwApAAYAAACv/wMAAwApAAYAAACv/wQAAwApAAYAAACw//z/AwAeAAQAAACw//3/AwAeAAUAAACw//7/AwAhAAQAAACw////AwAfAAcAAACw/wAAAwAoAAUAAACw/wEAAwAgAAYAAACw/wIAAwAhAAcAAACw/wMAAwAhAAQAAACw/wQAAwAiAAYAAACx//z/AwAeAAEAAACx//3/AwAeAAEAAACx//7/AwAeAAIAAACx////AwAgAAQAAACx/wAAAwAoAAUAAACx/wEAAwAfAAQAAACx/wIAAwAeAAAAAACx/wMAAwAeAAEAAACx/wQAAwAeAAEAAACy//z/AwAnAAAAAACy//3/AwAnAAEAAACy//7/AwAeAAMAAACy////AwAeAAQAAACy/wAAAwAoAAUAAACy/wEAAwAeAAYAAACy/wIAAwAfAAAAAACy/wMAAwAnAAAAAACy/wQAAwAnAAEAAACz//z/AwAoAAAAAACz//3/AwAoAAEAAACz//7/AwAeAAMAAACz////AwAfAAQAAACz/wAAAwAoAAUAAACz/wEAAwAfAAYAAACz/wIAAwAfAAAAAACz/wMAAwAoAAAAAACz/wQAAwAoAAEAAAC0//z/AwAnAAAAAAC0//3/AwAnAAEAAAC0//7/AwAeAAMAAAC0////AwAgAAQAAAC0/wAAAwAoAAUAAAC0/wEAAwAgAAYAAAC0/wIAAwAfAAAAAAC0/wMAAwAnAAAAAAC0/wQAAwAnAAEAAAC1//z/AwAoAAAAAAC1//3/AwAoAAEAAAC1//7/AwAfAAMAAAC1////AwAhAAQAAAC1/wAAAwAoAAUAAAC1/wEAAwAhAAYAAAC1/wIAAwAfAAAAAAC1/wMAAwAoAAAAAAC1/wQAAwAoAAEAAAC2//z/AwAoAAAAAAC2//3/AwAoAAEAAAC2//7/AwAeAAMAAAC2////AwAiAAQAAAC2/wAAAwAoAAUAAAC2/wEAAwAiAAYAAAC2/wIAAwAfAAAAAAC2/wMAAwAoAAAAAAC2/wQAAwAoAAEAAACm//z/AwApAAAAAACm//3/AwApAAEAAACm//7/AwAeAAMAAACm////AwAfAAQAAACm/wAAAwAqAAYAAACm/wEAAwAfAAUAAACm/wIAAwAfAAAAAACm/wMAAwAoAAIAAGCm/wQAAwAnAAIAAGCn//z/AwAqAAAAAACn//3/AwAqAAEAAACn//7/AwAeAAMAAACn////AwAiAAUAAACn/wAAAwAqAAYAAACn/wEAAwAgAAUAAACn/wIAAwAfAAAAAACn/wMAAwAoAAMAAGCn/wQAAwAnAAMAAGCo//z/AwAnAAAAAACo//3/AwAnAAEAAACo//7/AwAeAAMAAACo////AwAeAAcAAACo/wAAAwAoAAUAAACo/wEAAwAeAAcAAACo/wIAAwAfAAAAAACo/wMAAwAnAAAAAACo/wQAAwAnAAEAAACp//z/AwApAAAAAACp//3/AwApAAEAAACp//7/AwAeAAMAAACp////AwAfAAQAAACp/wAAAwAqAAYAAACp/wEAAwAfAAUAAACp/wIAAwAfAAAAAACp/wMAAwAoAAIAAGCp/wQAAwAnAAIAAGCq//z/AwAqAAAAAACq//3/AwAqAAEAAACq//7/AwAeAAMAAACq////AwAiAAUAAACq/wAAAwAqAAYAAACq/wEAAwAgAAUAAACq/wIAAwAfAAAAAACq/wMAAwAoAAMAAGCq/wQAAwAnAAMAAGCr//r/AwAnAAAAAACr//v/AwAnAAEAAACr//z/AwAnAAAAAACr//3/AwAnAAEAAACr//7/AwAeAAMAAACr////AwAeAAcAAACr/wAAAwAoAAUAAACr/wEAAwAeAAcAAACr/wIAAwAfAAAAAACr/wMAAwAnAAAAAACr/wQAAwAnAAEAAACr/wUAAwAnAAAAAACr/wYAAwAnAAEAAACs//r/AwAoAAAAAACs//v/AwAoAAEAAACs//z/AwAoAAAAAACs//3/AwAoAAEAAACs//7/AwAfAAMAAACs////AwAfAAQAAACs/wAAAwAoAAUAAACs/wEAAwAfAAYAAACs/wIAAwAfAAAAAACs/wMAAwAoAAAAAACs/wQAAwAoAAEAAACs/wUAAwAoAAAAAACs/wYAAwAoAAEAAACt//r/AwAgAAEAAACt//v/AwAgAAEAAACt//z/AwAgAAEAAACt//3/AwAgAAEAAACt//7/AwAgAAIAAACt////AwAgAAUAAACt/wAAAwAoAAUAAACt/wEAAwAiAAQAAACt/wIAAwAgAAAAAACt/wMAAwAgAAEAAACt/wQAAwAgAAEAAACt/wUAAwAgAAEAAACt/wYAAwAgAAEAAACu//r/AwAfAAQAAACu//v/AwAfAAUAAACu//z/AwAfAAYAAACu//3/AwAfAAcAAACu//7/AwAiAAcAAACu////AwAfAAQAAACu/wAAAwAoAAUAAACu/wEAAwAiAAUAAACu/wIAAwAgAAUAAACu/wMAAwAgAAUAAACu/wQAAwAhAAcAAACu/wUAAwAfAAUAAACu/wYAAwAhAAcAAACv//r/AwApAAYAAACv//v/AwApAAYAAACv/wUAAwApAAYAAACv/wYAAwApAAYAAACw//r/AwAeAAUAAACw//v/AwAeAAQAAACw/wUAAwAgAAYAAACw/wYAAwAfAAQAAACx//r/AwAeAAEAAACx//v/AwAeAAEAAACx/wUAAwAeAAEAAACx/wYAAwAeAAEAAACy//r/AwAnAAAAAACy//v/AwAnAAEAAACy/wUAAwAnAAAAAACy/wYAAwAnAAEAAACz//r/AwAoAAAAAACz//v/AwAoAAEAAACz/wUAAwAoAAAAAACz/wYAAwAoAAEAAACr//L/AwAoAAAAAGCr//P/AwAnAAAAAGCr//T/AwAoAAAAAGCr//X/AwAnAAAAAGCr//b/AwAoAAAAAGCr//f/AwAnAAAAAGCr//j/AwAoAAAAAGCr//n/AwAnAAAAAGCs//L/AwAoAAEAAGCs//P/AwAnAAEAAGCs//T/AwAoAAEAAGCs//X/AwAnAAEAAGCs//b/AwAoAAEAAGCs//f/AwAnAAEAAGCs//j/AwAoAAEAAGCs//n/AwAnAAEAAGCt//L/AwAgAAEAAACt//P/AwAgAAEAAACt//T/AwAgAAEAAACt//X/AwAgAAEAAACt//b/AwAgAAEAAACt//f/AwAgAAEAAACt//j/AwAgAAEAAACt//n/AwAgAAEAAACu//L/AwAhAAQAAGCu//P/AwAgAAQAAGCu//T/AwAgAAQAAGCu//X/AwAfAAQAAGCu//b/AwAeAAQAAGCu//f/AwAiAAQAAGCu//j/AwAhAAQAAGCu//n/AwAgAAQAAGCv//L/AwAoAAUAAGCv//P/AwAoAAUAAGCv//T/AwAoAAUAAGCv//X/AwAoAAUAAGCv//b/AwAoAAUAAGCv//f/AwAoAAUAAGCv//j/AwAoAAUAAGCv//n/AwAoAAUAAGCw//L/AwAhAAYAAGCw//P/AwAgAAYAAGCw//T/AwAgAAYAAGCw//X/AwAfAAYAAGCw//b/AwAeAAYAAGCw//f/AwAiAAYAAGCw//j/AwAhAAYAAGCw//n/AwAgAAYAAGCx//L/AwAeAAEAAACx//P/AwAeAAEAAACx//T/AwAeAAEAAACx//X/AwAeAAEAAACx//b/AwAeAAEAAACx//f/AwAeAAEAAACx//j/AwAeAAEAAACx//n/AwAeAAEAAACy//L/AwAoAAAAAGCy//P/AwAnAAAAAGCy//T/AwAoAAAAAGCy//X/AwAnAAAAAGCy//b/AwAoAAAAAGCy//f/AwAnAAAAAGCy//j/AwAoAAAAAGCy//n/AwAnAAAAAGCz//L/AwAoAAEAAGCz//P/AwAnAAEAAGCz//T/AwAoAAEAAGCz//X/AwAnAAEAAGCz//b/AwAoAAEAAGCz//f/AwAnAAEAAGCz//j/AwAoAAEAAGCz//n/AwAnAAEAAGCr/+z/AwAoAAAAAGCr/+3/AwAnAAAAAGCr/+7/AwAoAAAAAGCr/+//AwAnAAAAAGCr//D/AwAoAAAAAGCr//H/AwAnAAAAAGCs/+z/AwAoAAEAAGCs/+3/AwAnAAEAAGCs/+7/AwAoAAEAAGCs/+//AwAnAAEAAGCs//D/AwAoAAEAAGCs//H/AwAnAAEAAGCt/+z/AwAgAAEAAACt/+3/AwAgAAEAAACt/+7/AwAgAAEAAACt/+//AwAgAAEAAACt//D/AwAgAAEAAACt//H/AwAgAAEAAACu/+z/AwAiAAQAAGCu/+3/AwAhAAQAAGCu/+7/AwAgAAQAAGCu/+//AwAfAAQAAGCu//D/AwAeAAQAAGCu//H/AwAiAAQAAGCv/+z/AwAoAAUAAGCv/+3/AwAoAAUAAGCv/+7/AwAoAAUAAGCv/+//AwAoAAUAAGCv//D/AwAoAAUAAGCv//H/AwAoAAUAAGCw/+z/AwAiAAYAAGCw/+3/AwAhAAYAAGCw/+7/AwAgAAYAAGCw/+//AwAfAAYAAGCw//D/AwAeAAYAAGCw//H/AwAiAAYAAGCx/+z/AwAeAAEAAACx/+3/AwAeAAEAAACx/+7/AwAeAAEAAACx/+//AwAeAAEAAACx//D/AwAeAAEAAACx//H/AwAeAAEAAACy/+z/AwAoAAAAAGCy/+3/AwAnAAAAAGCy/+7/AwAoAAAAAGCy/+//AwAnAAAAAGCy//D/AwAoAAAAAGCy//H/AwAnAAAAAGCz/+z/AwAoAAEAAGCz/+3/AwAnAAEAAGCz/+7/AwAoAAEAAGCz/+//AwAnAAEAAGCz//D/AwAoAAEAAGCz//H/AwAnAAEAAGCr/+T/AwAnAAAAAGCr/+X/AwAoAAAAAGCr/+b/AwAnAAAAAGCr/+f/AwAoAAAAAGCr/+j/AwAnAAAAAGCr/+n/AwAoAAAAAGCr/+r/AwAnAAAAAGCr/+v/AwAnAAAAAGCs/+T/AwAnAAEAAGCs/+X/AwAoAAEAAGCs/+b/AwAnAAEAAGCs/+f/AwAoAAEAAGCs/+j/AwAnAAEAAGCs/+n/AwAoAAEAAGCs/+r/AwAnAAEAAGCs/+v/AwAnAAEAAGCt/+T/AwAgAAEAAACt/+X/AwAgAAEAAACt/+b/AwAgAAEAAACt/+f/AwAgAAEAAACt/+j/AwAgAAEAAACt/+n/AwAgAAEAAACt/+r/AwAgAAEAAACt/+v/AwAgAAEAAACu/+T/AwAhAAQAAGCu/+X/AwAgAAQAAGCu/+b/AwAfAAQAAGCu/+f/AwAeAAQAAGCu/+j/AwAiAAQAAGCu/+n/AwAhAAQAAGCu/+r/AwAgAAQAAGCu/+v/AwAgAAQAAGCv/+T/AwAoAAUAAGCv/+X/AwAoAAUAAGCv/+b/AwAoAAUAAGCv/+f/AwAoAAUAAGCv/+j/AwAoAAUAAGCv/+n/AwAoAAUAAGCv/+r/AwAoAAUAAGCv/+v/AwAoAAUAAGCw/+T/AwAhAAYAAGCw/+X/AwAgAAYAAGCw/+b/AwAfAAYAAGCw/+f/AwAeAAYAAGCw/+j/AwAiAAYAAGCw/+n/AwAhAAYAAGCw/+r/AwAgAAYAAGCw/+v/AwAgAAYAAGCx/+T/AwAeAAEAAACx/+X/AwAeAAEAAACx/+b/AwAeAAEAAACx/+f/AwAeAAEAAACx/+j/AwAeAAEAAACx/+n/AwAeAAEAAACx/+r/AwAeAAEAAACx/+v/AwAeAAEAAACy/+T/AwAnAAAAAGCy/+X/AwAoAAAAAGCy/+b/AwAnAAAAAGCy/+f/AwAoAAAAAGCy/+j/AwAnAAAAAGCy/+n/AwAoAAAAAGCy/+r/AwAnAAAAAGCy/+v/AwAnAAAAAGCz/+T/AwAnAAEAAGCz/+X/AwAoAAEAAGCz/+b/AwAnAAEAAGCz/+f/AwAoAAEAAGCz/+j/AwAnAAEAAGCz/+n/AwAoAAEAAGCz/+r/AwAnAAEAAGCz/+v/AwAnAAEAAGCr/9v/AwAoAAAAAGCr/9z/AwAnAAAAAGCr/93/AwAoAAAAAGCr/97/AwAnAAAAAGCr/9//AwAoAAAAAGCr/+D/AwAnAAAAAGCr/+H/AwAoAAAAAGCr/+L/AwAnAAAAAGCs/9v/AwAoAAEAAGCs/9z/AwAnAAEAAGCs/93/AwAoAAEAAGCs/97/AwAnAAEAAGCs/9//AwAoAAEAAGCs/+D/AwAnAAEAAGCs/+H/AwAoAAEAAGCs/+L/AwAnAAEAAGCt/9v/AwAgAAEAAACt/9z/AwAgAAEAAACt/93/AwAgAAEAAACt/97/AwAgAAEAAACt/9//AwAgAAEAAACt/+D/AwAgAAEAAACt/+H/AwAgAAEAAACt/+L/AwAgAAEAAACu/9v/AwAgAAQAAGCu/9z/AwAfAAQAAGCu/93/AwAeAAQAAGCu/97/AwAiAAQAAGCu/9//AwAhAAQAAGCu/+D/AwAgAAQAAGCu/+H/AwAhAAQAAGCu/+L/AwAgAAQAAGCv/9v/AwAoAAUAAGCv/9z/AwAoAAUAAGCv/93/AwAoAAUAAGCv/97/AwAoAAUAAGCv/9//AwAoAAUAAGCv/+D/AwAoAAUAAGCv/+H/AwAoAAUAAGCv/+L/AwAoAAUAAGCw/9v/AwAgAAYAAGCw/9z/AwAfAAYAAGCw/93/AwAeAAYAAGCw/97/AwAiAAYAAGCw/9//AwAhAAYAAGCw/+D/AwAgAAYAAGCw/+H/AwAhAAYAAGCw/+L/AwAgAAYAAGCx/9v/AwAeAAEAAACx/9z/AwAeAAEAAACx/93/AwAeAAEAAACx/97/AwAeAAEAAACx/9//AwAeAAEAAACx/+D/AwAeAAEAAACx/+H/AwAeAAEAAACx/+L/AwAeAAEAAACy/9v/AwAoAAAAAGCy/9z/AwAnAAAAAGCy/93/AwAoAAAAAGCy/97/AwAnAAAAAGCy/9//AwAoAAAAAGCy/+D/AwAnAAAAAGCy/+H/AwAoAAAAAGCy/+L/AwAnAAAAAGCz/9v/AwAoAAEAAGCz/9z/AwAnAAEAAGCz/93/AwAoAAEAAGCz/97/AwAnAAEAAGCz/9//AwAoAAEAAGCz/+D/AwAnAAEAAGCz/+H/AwAoAAEAAGCz/+L/AwAnAAEAAGCr/+P/AwAoAAAAAGCs/+P/AwAoAAEAAGCt/+P/AwAgAAEAAACu/+P/AwAiAAQAAGCv/+P/AwAoAAUAAGCw/+P/AwAiAAYAAGCx/+P/AwAeAAEAAACy/+P/AwAoAAAAAGCz/+P/AwAoAAEAAGCr/9n/AwAoAAAAAGCr/9r/AwAnAAAAAGCs/9n/AwAoAAEAAGCs/9r/AwAnAAEAAGCt/9n/AwAgAAEAAACt/9r/AwAgAAEAAACu/9n/AwAiAAQAAGCu/9r/AwAhAAQAAGCv/9n/AwAoAAUAAGCv/9r/AwAoAAUAAGCw/9n/AwAiAAYAAGCw/9r/AwAhAAYAAGCx/9n/AwAeAAEAAACx/9r/AwAeAAEAAACy/9n/AwAoAAAAAGCy/9r/AwAnAAAAAGCz/9n/AwAoAAEAAGCz/9r/AwAnAAEAAGCr/9H/AwAoAAAAAGCr/9L/AwAnAAAAAGCr/9P/AwAoAAAAAGCr/9T/AwAnAAAAAGCr/9X/AwAoAAAAAGCr/9b/AwAnAAAAAGCr/9f/AwAoAAAAAGCr/9j/AwAnAAAAAGCs/9H/AwAoAAEAAGCs/9L/AwAnAAEAAGCs/9P/AwAoAAEAAGCs/9T/AwAnAAEAAGCs/9X/AwAoAAEAAGCs/9b/AwAnAAEAAGCs/9f/AwAoAAEAAGCs/9j/AwAnAAEAAGCt/9H/AwAgAAEAAACt/9L/AwAgAAEAAACt/9P/AwAgAAEAAACt/9T/AwAgAAEAAACt/9X/AwAgAAEAAACt/9b/AwAgAAEAAACt/9f/AwAgAAEAAACt/9j/AwAgAAEAAACu/9H/AwAiAAQAAGCu/9L/AwAhAAQAAGCu/9P/AwAgAAQAAGCu/9T/AwAfAAQAAGCu/9X/AwAeAAQAAGCu/9b/AwAiAAQAAGCu/9f/AwAhAAQAAGCu/9j/AwAgAAQAAGCv/9H/AwAoAAUAAGCv/9L/AwAoAAUAAGCv/9P/AwAoAAUAAGCv/9T/AwAoAAUAAGCv/9X/AwAoAAUAAGCv/9b/AwAoAAUAAGCv/9f/AwAoAAUAAGCv/9j/AwAoAAUAAGCw/9H/AwAiAAYAAGCw/9L/AwAhAAYAAGCw/9P/AwAgAAYAAGCw/9T/AwAfAAYAAGCw/9X/AwAeAAYAAGCw/9b/AwAiAAYAAGCw/9f/AwAhAAYAAGCw/9j/AwAgAAYAAGCx/9H/AwAeAAEAAACx/9L/AwAeAAEAAACx/9P/AwAeAAEAAACx/9T/AwAeAAEAAACx/9X/AwAeAAEAAACx/9b/AwAeAAEAAACx/9f/AwAeAAEAAACx/9j/AwAeAAEAAACy/9H/AwAoAAAAAGCy/9L/AwAnAAAAAGCy/9P/AwAoAAAAAGCy/9T/AwAnAAAAAGCy/9X/AwAoAAAAAGCy/9b/AwAnAAAAAGCy/9f/AwAoAAAAAGCy/9j/AwAnAAAAAGCz/9H/AwAoAAEAAGCz/9L/AwAnAAEAAGCz/9P/AwAoAAEAAGCz/9T/AwAnAAEAAGCz/9X/AwAoAAEAAGCz/9b/AwAnAAEAAGCz/9f/AwAoAAEAAGCz/9j/AwAnAAEAAGCr/8n/AwAoAAAAAGCr/8r/AwAnAAAAAGCr/8v/AwAoAAAAAGCr/8z/AwAnAAAAAGCr/83/AwAoAAAAAGCr/87/AwAnAAAAAGCr/8//AwAoAAAAAGCr/9D/AwAnAAAAAGCs/8n/AwAoAAEAAGCs/8r/AwAnAAEAAGCs/8v/AwAoAAEAAGCs/8z/AwAnAAEAAGCs/83/AwAoAAEAAGCs/87/AwAnAAEAAGCs/8//AwAoAAEAAGCs/9D/AwAnAAEAAGCt/8n/AwAgAAEAAACt/8r/AwAgAAEAAACt/8v/AwAgAAEAAACt/8z/AwAgAAEAAACt/83/AwAgAAEAAACt/87/AwAgAAEAAACt/8//AwAgAAEAAACt/9D/AwAgAAEAAACu/8n/AwAiAAQAAGCu/8r/AwAhAAQAAGCu/8v/AwAgAAQAAGCu/8z/AwAfAAQAAGCu/83/AwAeAAQAAGCu/87/AwAiAAQAAGCu/8//AwAhAAQAAGCu/9D/AwAgAAQAAGCv/8n/AwAoAAUAAGCv/8r/AwAoAAUAAGCv/8v/AwAoAAUAAGCv/8z/AwAoAAUAAGCv/83/AwAoAAUAAGCv/87/AwAoAAUAAGCv/8//AwAoAAUAAGCv/9D/AwAoAAUAAGCw/8n/AwAiAAYAAGCw/8r/AwAhAAYAAGCw/8v/AwAgAAYAAGCw/8z/AwAfAAYAAGCw/83/AwAeAAYAAGCw/87/AwAiAAYAAGCw/8//AwAhAAYAAGCw/9D/AwAgAAYAAGCx/8n/AwAeAAEAAACx/8r/AwAeAAEAAACx/8v/AwAeAAEAAACx/8z/AwAeAAEAAACx/83/AwAeAAEAAACx/87/AwAeAAEAAACx/8//AwAeAAEAAACx/9D/AwAeAAEAAACy/8n/AwAoAAAAAGCy/8r/AwAnAAAAAGCy/8v/AwAoAAAAAGCy/8z/AwAnAAAAAGCy/83/AwAoAAAAAGCy/87/AwAnAAAAAGCy/8//AwAoAAAAAGCy/9D/AwAnAAAAAGCz/8n/AwAoAAEAAGCz/8r/AwAnAAEAAGCz/8v/AwAoAAEAAGCz/8z/AwAnAAEAAGCz/83/AwAoAAEAAGCz/87/AwAnAAEAAGCz/8//AwAoAAEAAGCz/9D/AwAnAAEAAGDp/9n/AwAoAAAAAGDp/9r/AwAnAAAAAGDp/9v/AwAoAAAAAGDp/9z/AwAnAAAAAGDp/93/AwAoAAAAAGDp/97/AwAnAAAAAGDp/9//AwAoAAAAAGDp/+D/AwAnAAAAAGDq/9n/AwAoAAEAAGDq/9r/AwAnAAEAAGDq/9v/AwAoAAEAAGDq/9z/AwAnAAEAAGDq/93/AwAoAAEAAGDq/97/AwAnAAEAAGDq/9//AwAoAAEAAGDq/+D/AwAnAAEAAGDr/9n/AwAgAAEAAADr/9r/AwAgAAEAAADr/9v/AwAgAAEAAADr/9z/AwAgAAEAAADr/93/AwAgAAEAAADr/97/AwAgAAEAAADr/9//AwAgAAEAAADr/+D/AwAgAAEAAADs/9n/AwAiAAQAAGDs/9r/AwAhAAQAAGDs/9v/AwAgAAQAAGDs/9z/AwAfAAQAAGDs/93/AwAeAAQAAGDs/97/AwAiAAQAAGDs/9//AwAhAAQAAGDs/+D/AwAgAAQAAGDt/9n/AwAoAAUAAGDt/9r/AwAoAAUAAGDt/9v/AwAoAAUAAGDt/9z/AwAoAAUAAGDt/93/AwAoAAUAAGDt/97/AwAoAAUAAGDt/9//AwAoAAUAAGDt/+D/AwAoAAUAAGDu/9n/AwAiAAYAAGDu/9r/AwAhAAYAAGDu/9v/AwAgAAYAAGDu/9z/AwAfAAYAAGDu/93/AwAeAAYAAGDu/97/AwAiAAYAAGDu/9//AwAhAAYAAGDu/+D/AwAgAAYAAGDv/9n/AwAeAAEAAADv/9r/AwAeAAEAAADv/9v/AwAeAAEAAADv/9z/AwAeAAEAAADv/93/AwAeAAEAAADv/97/AwAeAAEAAADv/9//AwAeAAEAAADv/+D/AwAeAAEAAADw/9n/AwAoAAAAAGDw/9r/AwAnAAAAAGDw/9v/AwAoAAAAAGDw/9z/AwAnAAAAAGDw/93/AwAoAAAAAGDw/97/AwAnAAAAAGDw/9//AwAoAAAAAGDw/+D/AwAnAAAAAGDx/9n/AwAoAAEAAGDx/9r/AwAnAAEAAGDx/9v/AwAoAAEAAGDx/9z/AwAnAAEAAGDx/93/AwAoAAEAAGDx/97/AwAnAAEAAGDx/9//AwAoAAEAAGDx/+D/AwAnAAEAAGDp/9H/AwAoAAAAAGDp/9L/AwAnAAAAAGDp/9P/AwAoAAAAAGDp/9T/AwAnAAAAAGDp/9X/AwAoAAAAAGDp/9b/AwAnAAAAAGDp/9f/AwAoAAAAAGDp/9j/AwAnAAAAAGDq/9H/AwAoAAEAAGDq/9L/AwAnAAEAAGDq/9P/AwAoAAEAAGDq/9T/AwAnAAEAAGDq/9X/AwAoAAEAAGDq/9b/AwAnAAEAAGDq/9f/AwAoAAEAAGDq/9j/AwAnAAEAAGDr/9H/AwAgAAEAAADr/9L/AwAgAAEAAADr/9P/AwAgAAEAAADr/9T/AwAgAAEAAADr/9X/AwAgAAEAAADr/9b/AwAgAAEAAADr/9f/AwAgAAEAAADr/9j/AwAgAAEAAADs/9H/AwAiAAQAAGDs/9L/AwAhAAQAAGDs/9P/AwAgAAQAAGDs/9T/AwAfAAQAAGDs/9X/AwAeAAQAAGDs/9b/AwAiAAQAAGDs/9f/AwAhAAQAAGDs/9j/AwAgAAQAAGDt/9H/AwAoAAUAAGDt/9L/AwAoAAUAAGDt/9P/AwAoAAUAAGDt/9T/AwAoAAUAAGDt/9X/AwAoAAUAAGDt/9b/AwAoAAUAAGDt/9f/AwAoAAUAAGDt/9j/AwAoAAUAAGDu/9H/AwAiAAYAAGDu/9L/AwAhAAYAAGDu/9P/AwAgAAYAAGDu/9T/AwAfAAYAAGDu/9X/AwAeAAYAAGDu/9b/AwAiAAYAAGDu/9f/AwAhAAYAAGDu/9j/AwAgAAYAAGDv/9H/AwAeAAEAAADv/9L/AwAeAAEAAADv/9P/AwAeAAEAAADv/9T/AwAeAAEAAADv/9X/AwAeAAEAAADv/9b/AwAeAAEAAADv/9f/AwAeAAEAAADv/9j/AwAeAAEAAADw/9H/AwAoAAAAAGDw/9L/AwAnAAAAAGDw/9P/AwAoAAAAAGDw/9T/AwAnAAAAAGDw/9X/AwAoAAAAAGDw/9b/AwAnAAAAAGDw/9f/AwAoAAAAAGDw/9j/AwAnAAAAAGDx/9H/AwAoAAEAAGDx/9L/AwAnAAEAAGDx/9P/AwAoAAEAAGDx/9T/AwAnAAEAAGDx/9X/AwAoAAEAAGDx/9b/AwAnAAEAAGDx/9f/AwAoAAEAAGDx/9j/AwAnAAEAAGDp/8n/AwAoAAAAAGDp/8r/AwAnAAAAAGDp/8v/AwAoAAAAAGDp/8z/AwAnAAAAAGDp/83/AwAoAAAAAGDp/9D/AwAnAAAAAGDq/8n/AwAoAAEAAGDq/8r/AwAnAAEAAGDq/8v/AwAoAAEAAGDq/8z/AwAnAAEAAGDq/83/AwAoAAEAAGDq/87/AwAnAAEAAGDq/8//AwAoAAEAAGDq/9D/AwAnAAEAAGDr/8n/AwAgAAEAAADr/8r/AwAgAAEAAADr/8v/AwAgAAEAAADr/8z/AwAgAAEAAADr/83/AwAgAAEAAADr/87/AwAgAAEAAADr/8//AwAgAAEAAADr/9D/AwAgAAEAAADs/8n/AwAiAAQAAGDs/8r/AwAhAAQAAGDs/8v/AwAgAAQAAGDs/8z/AwAfAAQAAGDs/83/AwAeAAQAAGDs/87/AwAiAAQAAGDs/8//AwAhAAQAAGDs/9D/AwAgAAQAAGDt/8n/AwAoAAUAAGDt/8r/AwAoAAUAAGDt/8v/AwAoAAUAAGDt/8z/AwAoAAUAAGDt/83/AwAoAAUAAGDt/87/AwAoAAUAAGDt/8//AwAoAAUAAGDt/9D/AwAoAAUAAGDu/8n/AwAiAAYAAGDu/8r/AwAhAAYAAGDu/8v/AwAgAAYAAGDu/8z/AwAfAAYAAGDu/83/AwAeAAYAAGDu/87/AwAiAAYAAGDu/8//AwAhAAYAAGDu/9D/AwAgAAYAAGDv/8n/AwAeAAEAAADv/8r/AwAeAAEAAADv/8v/AwAeAAEAAADv/8z/AwAeAAEAAADv/83/AwAeAAEAAADv/87/AwAeAAEAAADv/8//AwAeAAEAAADv/9D/AwAeAAEAAADw/8n/AwAoAAAAAGDw/8r/AwAnAAAAAGDw/8v/AwAoAAAAAGDw/8z/AwAnAAAAAGDw/83/AwAoAAAAAGDw/87/AwAnAAAAAGDw/8//AwAoAAAAAGDw/9D/AwAnAAAAAGDx/8n/AwAoAAEAAGDx/8r/AwAnAAEAAGDx/8v/AwAoAAEAAGDx/8z/AwAnAAEAAGDx/83/AwAoAAEAAGDx/87/AwAnAAEAAGDx/8//AwAoAAEAAGDx/9D/AwAnAAEAAGCr/wcAAwAoAAAAAGCr/wgAAwAnAAAAAGCr/wkAAwAoAAAAAGCr/woAAwAnAAAAAGCr/wsAAwAoAAAAAGCr/wwAAwAnAAAAAGCr/w0AAwAoAAAAAGCr/w4AAwAnAAAAAGCs/wcAAwAoAAEAAGCs/wgAAwAnAAEAAGCs/wkAAwAoAAEAAGCs/woAAwAnAAEAAGCs/wsAAwAoAAEAAGCs/wwAAwAnAAEAAGCs/w0AAwAoAAEAAGCs/w4AAwAnAAEAAGCt/wcAAwAgAAEAAACt/wgAAwAgAAEAAACt/wkAAwAgAAEAAACt/woAAwAgAAEAAACt/wsAAwAgAAEAAACt/wwAAwAgAAEAAACt/w0AAwAgAAEAAACt/w4AAwAgAAEAAACu/wcAAwAiAAQAAGCu/wgAAwAhAAQAAGCu/wkAAwAgAAQAAGCu/woAAwAfAAQAAGCu/wsAAwAeAAQAAGCu/wwAAwAiAAQAAGCu/w0AAwAhAAQAAGCu/w4AAwAgAAQAAGCv/wcAAwAoAAUAAGCv/wgAAwAoAAUAAGCv/wkAAwAoAAUAAGCv/woAAwAoAAUAAGCv/wsAAwAoAAUAAGCv/wwAAwAoAAUAAGCv/w0AAwAoAAUAAGCv/w4AAwAoAAUAAGCw/wcAAwAiAAYAAGCw/wgAAwAhAAYAAGCw/wkAAwAgAAYAAGCw/woAAwAfAAYAAGCw/wsAAwAeAAYAAGCw/wwAAwAiAAYAAGCw/w0AAwAhAAYAAGCw/w4AAwAgAAYAAGCx/wcAAwAeAAEAAACx/wgAAwAeAAEAAACx/wkAAwAeAAEAAACx/woAAwAeAAEAAACx/wsAAwAeAAEAAACx/wwAAwAeAAEAAACx/w0AAwAeAAEAAACx/w4AAwAeAAEAAACy/wcAAwAoAAAAAGCy/wgAAwAnAAAAAGCy/wkAAwAoAAAAAGCy/woAAwAnAAAAAGCy/wsAAwAoAAAAAGCy/wwAAwAnAAAAAGCy/w0AAwAoAAAAAGCy/w4AAwAnAAAAAGCz/wcAAwAoAAEAAGCz/wgAAwAnAAEAAGCz/wkAAwAoAAEAAGCz/woAAwAnAAEAAGCz/wsAAwAoAAEAAGCz/wwAAwAnAAEAAGCz/w0AAwAoAAEAAGCz/w4AAwAnAAEAAGDp/wgAAwAoAAAAAGDp/wkAAwAnAAAAAGDp/woAAwAoAAAAAGDp/wsAAwAoAAAAAGDp/wwAAwAnAAAAAGDp/w0AAwAoAAAAAGDp/w4AAwAnAAAAAGDp/w8AAwAoAAAAAGDq/wgAAwAoAAEAAGDq/wkAAwAnAAEAAGDq/woAAwAoAAEAAGDq/wsAAwAoAAEAAGDq/wwAAwAnAAEAAGDq/w0AAwAoAAEAAGDq/w4AAwAnAAEAAGDq/w8AAwAoAAEAAGDr/wgAAwAgAAEAAADr/wkAAwAgAAEAAADr/woAAwAgAAEAAADr/wsAAwAgAAEAAADr/wwAAwAgAAEAAADr/w0AAwAgAAEAAADr/w4AAwAgAAEAAADr/w8AAwAgAAEAAADs/wgAAwAiAAQAAGDs/wkAAwAhAAQAAGDs/woAAwAgAAQAAGDs/wsAAwAiAAQAAGDs/wwAAwAhAAQAAGDs/w0AAwAgAAQAAGDs/w4AAwAfAAQAAGDs/w8AAwAeAAQAAGDt/wgAAwAoAAUAAGDt/wkAAwAoAAUAAGDt/woAAwAoAAUAAGDt/wsAAwAoAAUAAGDt/wwAAwAoAAUAAGDt/w0AAwAoAAUAAGDt/w4AAwAoAAUAAGDt/w8AAwAoAAUAAGDu/wgAAwAiAAYAAGDu/wkAAwAhAAYAAGDu/woAAwAgAAYAAGDu/wsAAwAiAAYAAGDu/wwAAwAhAAYAAGDu/w0AAwAgAAYAAGDu/w4AAwAfAAYAAGDu/w8AAwAeAAYAAGDv/wgAAwAeAAEAAADv/wkAAwAeAAEAAADv/woAAwAeAAEAAADv/wsAAwAeAAEAAADv/wwAAwAeAAEAAADv/w0AAwAeAAEAAADv/w4AAwAeAAEAAADv/w8AAwAeAAEAAADw/wgAAwAoAAAAAGDw/wkAAwAnAAAAAGDw/woAAwAoAAAAAGDw/wsAAwAoAAAAAGDw/wwAAwAnAAAAAGDw/w0AAwAoAAAAAGDw/w4AAwAnAAAAAGDw/w8AAwAoAAAAAGDx/wgAAwAoAAEAAGDx/wkAAwAnAAEAAGDx/woAAwAoAAEAAGDx/wsAAwAoAAEAAGDx/wwAAwAnAAEAAGDx/w0AAwAoAAEAAGDx/w4AAwAnAAEAAGDx/w8AAwAoAAEAAGAMANn/AwAoAAAAAGAMANr/AwAnAAAAAGAMANv/AwAoAAAAAGAMANz/AwAnAAAAAGAMAN3/AwAoAAAAAGAMAN7/AwAnAAAAAGAMAN//AwAoAAAAAGAMAOD/AwAnAAAAAGANANn/AwAoAAEAAGANANr/AwAnAAEAAGANANv/AwAoAAEAAGANANz/AwAnAAEAAGANAN3/AwAoAAEAAGANAN7/AwAnAAEAAGANAN//AwAoAAEAAGANAOD/AwAnAAEAAGAOANn/AwAgAAEAAAAOANr/AwAgAAEAAAAOANv/AwAgAAEAAAAOANz/AwAgAAEAAAAOAN3/AwAgAAEAAAAOAN7/AwAgAAEAAAAOAN//AwAgAAEAAAAOAOD/AwAgAAEAAAAPANn/AwAiAAQAAGAPANr/AwAhAAQAAGAPANv/AwAgAAQAAGAPANz/AwAfAAQAAGAPAN3/AwAeAAQAAGAPAN7/AwAiAAQAAGAPAN//AwAhAAQAAGAPAOD/AwAgAAQAAGAQANn/AwAoAAUAAGAQANr/AwAoAAUAAGAQANv/AwAoAAUAAGAQANz/AwAoAAUAAGAQAN3/AwAoAAUAAGAQAN7/AwAoAAUAAGAQAN//AwAoAAUAAGAQAOD/AwAoAAUAAGARANn/AwAiAAYAAGARANr/AwAhAAYAAGARANv/AwAgAAYAAGARANz/AwAfAAYAAGARAN3/AwAeAAYAAGARAN7/AwAiAAYAAGARAN//AwAhAAYAAGARAOD/AwAgAAYAAGASANn/AwAeAAEAAAASANr/AwAeAAEAAAASANv/AwAeAAEAAAASANz/AwAeAAEAAAASAN3/AwAeAAEAAAASAN7/AwAeAAEAAAASAN//AwAeAAEAAAASAOD/AwAeAAEAAAATANn/AwAoAAAAAGATANr/AwAnAAAAAGATANv/AwAoAAAAAGATANz/AwAnAAAAAGATAN3/AwAoAAAAAGATAN7/AwAnAAAAAGATAN//AwAoAAAAAGATAOD/AwAnAAAAAGAUANn/AwAoAAEAAGAUANr/AwAnAAEAAGAUANv/AwAoAAEAAGAUANz/AwAnAAEAAGAUAN3/AwAoAAEAAGAUAN7/AwAnAAEAAGAUAN//AwAoAAEAAGAUAOD/AwAnAAEAAGAMANH/AwAoAAAAAGAMANL/AwAnAAAAAGAMANP/AwAoAAAAAGAMANT/AwAnAAAAAGAMANX/AwAoAAAAAGAMANb/AwAnAAAAAGAMANf/AwAoAAAAAGAMANj/AwAnAAAAAGANANH/AwAoAAEAAGANANL/AwAnAAEAAGANANP/AwAoAAEAAGANANT/AwAnAAEAAGANANX/AwAoAAEAAGANANb/AwAnAAEAAGANANf/AwAoAAEAAGANANj/AwAnAAEAAGAOANH/AwAgAAEAAAAOANL/AwAgAAEAAAAOANP/AwAgAAEAAAAOANT/AwAgAAEAAAAOANX/AwAgAAEAAAAOANb/AwAgAAEAAAAOANf/AwAgAAEAAAAOANj/AwAgAAEAAAAPANH/AwAiAAQAAGAPANL/AwAhAAQAAGAPANP/AwAgAAQAAGAPANT/AwAfAAQAAGAPANX/AwAeAAQAAGAPANb/AwAiAAQAAGAPANf/AwAhAAQAAGAPANj/AwAgAAQAAGAQANH/AwAoAAUAAGAQANL/AwAoAAUAAGAQANP/AwAoAAUAAGAQANT/AwAoAAUAAGAQANX/AwAoAAUAAGAQANb/AwAoAAUAAGAQANf/AwAoAAUAAGAQANj/AwAoAAUAAGARANH/AwAiAAYAAGARANL/AwAhAAYAAGARANP/AwAgAAYAAGARANT/AwAfAAYAAGARANX/AwAeAAYAAGARANb/AwAiAAYAAGARANf/AwAhAAYAAGARANj/AwAgAAYAAGASANH/AwAeAAEAAAASANL/AwAeAAEAAAASANP/AwAeAAEAAAASANT/AwAeAAEAAAASANX/AwAeAAEAAAASANb/AwAeAAEAAAASANf/AwAeAAEAAAASANj/AwAeAAEAAAATANH/AwAoAAAAAGATANL/AwAnAAAAAGATANP/AwAoAAAAAGATANT/AwAnAAAAAGATANX/AwAoAAAAAGATANb/AwAnAAAAAGATANf/AwAoAAAAAGATANj/AwAnAAAAAGAUANH/AwAoAAEAAGAUANL/AwAnAAEAAGAUANP/AwAoAAEAAGAUANT/AwAnAAEAAGAUANX/AwAoAAEAAGAUANb/AwAnAAEAAGAUANf/AwAoAAEAAGAUANj/AwAnAAEAAGAMAMn/AwAoAAAAAGAMAMr/AwAnAAAAAGAMAMv/AwAoAAAAAGAMAMz/AwAnAAAAAGAMAM3/AwAoAAAAAGAMAM7/AwAnAAAAAGAMAM//AwAoAAAAAGAMAND/AwAnAAAAAGANAMn/AwAoAAEAAGANAMr/AwAnAAEAAGANAMv/AwAoAAEAAGANAMz/AwAnAAEAAGANAM3/AwAoAAEAAGANAM7/AwAnAAEAAGANAM//AwAoAAEAAGANAND/AwAnAAEAAGAOAMn/AwAgAAEAAAAOAMr/AwAgAAEAAAAOAMv/AwAgAAEAAAAOAMz/AwAgAAEAAAAOAM3/AwAgAAEAAAAOAM7/AwAgAAEAAAAOAM//AwAgAAEAAAAOAND/AwAgAAEAAAAPAMn/AwAiAAQAAGAPAMr/AwAhAAQAAGAPAMv/AwAgAAQAAGAPAMz/AwAfAAQAAGAPAM3/AwAeAAQAAGAPAM7/AwAiAAQAAGAPAM//AwAhAAQAAGAPAND/AwAgAAQAAGAQAMn/AwAoAAUAAGAQAMr/AwAoAAUAAGAQAMv/AwAoAAUAAGAQAMz/AwAoAAUAAGAQAM3/AwAoAAUAAGAQAM7/AwAoAAUAAGAQAM//AwAoAAUAAGAQAND/AwAoAAUAAGARAMn/AwAiAAYAAGARAMr/AwAhAAYAAGARAMv/AwAgAAYAAGARAMz/AwAfAAYAAGARAM3/AwAeAAYAAGARAM7/AwAiAAYAAGARAM//AwAhAAYAAGARAND/AwAgAAYAAGASAMn/AwAeAAEAAAASAMr/AwAeAAEAAAASAMv/AwAeAAEAAAASAMz/AwAeAAEAAAASAM3/AwAeAAEAAAASAM7/AwAeAAEAAAASAM//AwAeAAEAAAASAND/AwAeAAEAAAATAMn/AwAoAAAAAGATAMr/AwAnAAAAAGATAMv/AwAoAAAAAGATAMz/AwAnAAAAAGATAM3/AwAoAAAAAGATAM7/AwAnAAAAAGATAM//AwAoAAAAAGATAND/AwAnAAAAAGAUAMn/AwAoAAEAAGAUAMr/AwAnAAEAAGAUAMv/AwAoAAEAAGAUAMz/AwAnAAEAAGAUAM3/AwAoAAEAAGAUAM7/AwAnAAEAAGAUAM//AwAoAAEAAGAUAND/AwAnAAEAAGAKABUAAwApAAAAAAAKABYAAwApAAEAAAAKABcAAwAeAAMAAAAKABgAAwAfAAQAAAAKABkAAwAqAAYAAAAKABoAAwAfAAUAAAAKABsAAwAfAAAAAAAKABwAAwAoAAIAAGAKAB0AAwAnAAIAAGALABUAAwAqAAAAAAALABYAAwAqAAEAAAALABcAAwAeAAMAAAALABgAAwAiAAUAAAALABkAAwAqAAYAAAALABoAAwAgAAUAAAALABsAAwAfAAAAAAALABwAAwAoAAMAAGALAB0AAwAnAAMAAGACABUAAwAnAAAAAAACABYAAwAnAAEAAAACABcAAwAeAAMAAAACABgAAwAgAAQAAAACABkAAwAoAAUAAAACABoAAwAgAAYAAAACABsAAwAfAAAAAAACABwAAwAnAAAAAAACAB0AAwAnAAEAAAADABUAAwAoAAAAAAADABYAAwAoAAEAAAADABcAAwAfAAMAAAADABgAAwAhAAQAAAADABkAAwAoAAUAAAADABoAAwAhAAYAAAADABsAAwAfAAAAAAADABwAAwAoAAAAAAADAB0AAwAoAAEAAAAEABUAAwAnAAAAAAAEABYAAwAnAAEAAAAEABcAAwAeAAMAAAAEABgAAwAiAAQAAAAEABkAAwAoAAUAAAAEABoAAwAiAAYAAAAEABsAAwAfAAAAAAAEABwAAwAnAAAAAAAEAB0AAwAnAAEAAAAFABUAAwAoAAAAAAAFABYAAwAoAAEAAAAFABcAAwAeAAMAAAAFABgAAwAeAAQAAAAFABkAAwAoAAUAAAAFABoAAwAeAAYAAAAFABsAAwAfAAAAAAAFABwAAwAoAAAAAAAFAB0AAwAoAAEAAAAGABUAAwAnAAAAAAAGABYAAwAnAAEAAAAGABcAAwAeAAMAAAAGABgAAwAfAAQAAAAGABkAAwAoAAUAAAAGABoAAwAfAAYAAAAGABsAAwAfAAAAAAAGABwAAwAnAAAAAAAGAB0AAwAnAAEAAAAHABUAAwAoAAAAAAAHABYAAwAoAAEAAAAHABcAAwAfAAMAAAAHABgAAwAgAAQAAAAHABkAAwAoAAUAAAAHABoAAwAgAAYAAAAHABsAAwAfAAAAAAAHABwAAwAoAAAAAAAHAB0AAwAoAAEAAAAIABUAAwAnAAAAAAAIABYAAwAnAAEAAAAIABcAAwAeAAMAAAAIABgAAwAhAAQAAAAIABkAAwAoAAUAAAAIABoAAwAhAAYAAAAIABsAAwAfAAAAAAAIABwAAwAnAAAAAAAIAB0AAwAnAAEAAAAJABUAAwAoAAAAAAAJABYAAwAoAAEAAAAJABcAAwAeAAMAAAAJABgAAwAiAAQAAAAJABkAAwAoAAUAAAAJABoAAwAiAAYAAAAJABsAAwAfAAAAAAAJABwAAwAoAAAAAAAJAB0AAwAoAAEAAAD6/xUAAwAoAAAAAAD6/xYAAwAoAAEAAAD6/xcAAwAeAAMAAAD6/xgAAwAiAAQAAAD6/xkAAwAoAAUAAAD6/xoAAwAiAAYAAAD6/xsAAwAfAAAAAAD6/xwAAwAoAAAAAAD6/x0AAwAoAAEAAAD7/xUAAwAoAAAAAAD7/xYAAwAoAAEAAAD7/xcAAwAfAAMAAAD7/xgAAwAhAAQAAAD7/xkAAwAoAAUAAAD7/xoAAwAhAAYAAAD7/xsAAwAfAAAAAAD7/xwAAwAoAAAAAAD7/x0AAwAoAAEAAAD8/xUAAwAnAAAAAAD8/xYAAwAnAAEAAAD8/xcAAwAeAAMAAAD8/xgAAwAiAAQAAAD8/xkAAwAoAAUAAAD8/xoAAwAiAAYAAAD8/xsAAwAfAAAAAAD8/xwAAwAnAAAAAAD8/x0AAwAnAAEAAAD9/xUAAwAoAAAAAAD9/xYAAwAoAAEAAAD9/xcAAwAeAAMAAAD9/xgAAwAeAAQAAAD9/xkAAwAoAAUAAAD9/xoAAwAeAAYAAAD9/xsAAwAfAAAAAAD9/xwAAwAoAAAAAAD9/x0AAwAoAAEAAAD+/xUAAwAnAAAAAAD+/xYAAwAnAAEAAAD+/xcAAwAeAAMAAAD+/xgAAwAfAAQAAAD+/xkAAwAoAAUAAAD+/xoAAwAfAAYAAAD+/xsAAwAfAAAAAAD+/xwAAwAnAAAAAAD+/x0AAwAnAAEAAAD//xUAAwAoAAAAAAD//xYAAwAoAAEAAAD//xcAAwAfAAMAAAD//xgAAwAgAAQAAAD//xkAAwAoAAUAAAD//xoAAwAgAAYAAAD//xsAAwAfAAAAAAD//xwAAwAoAAAAAAD//x0AAwAoAAEAAAAAABUAAwAnAAAAAAAAABYAAwAnAAEAAAAAABcAAwAeAAMAAAAAABgAAwAhAAQAAAAAABkAAwAoAAUAAAAAABoAAwAhAAYAAAAAABsAAwAfAAAAAAAAABwAAwAnAAAAAAAAAB0AAwAnAAEAAAABABUAAwAoAAAAAAABABYAAwAoAAEAAAABABcAAwAeAAMAAAABABgAAwAiAAQAAAABABkAAwAoAAUAAAABABoAAwAiAAYAAAABABsAAwAfAAAAAAABABwAAwAoAAAAAAABAB0AAwAoAAEAAADn/xUAAwApAAAAAADn/xYAAwApAAEAAADn/xcAAwAeAAMAAADn/xgAAwAfAAQAAADn/xkAAwAqAAYAAADn/xoAAwAfAAUAAADn/xsAAwAfAAAAAADn/xwAAwAoAAIAAGDn/x0AAwAnAAIAAGDo/xUAAwAqAAAAAADo/xYAAwAqAAEAAADo/xcAAwAeAAMAAADo/xgAAwAiAAUAAADo/xkAAwAqAAYAAADo/xoAAwAgAAUAAADo/xsAAwAfAAAAAADo/xwAAwAoAAMAAGDo/x0AAwAnAAMAAGDp/xMAAwAnAAAAAADp/xQAAwAnAAEAAADp/xUAAwAnAAAAAADp/xYAAwAnAAEAAADp/xcAAwAeAAMAAADp/xgAAwAeAAcAAADp/xkAAwAoAAUAAADp/xoAAwAeAAcAAADp/xsAAwAfAAAAAADp/xwAAwAnAAAAAADp/x0AAwAnAAEAAADp/x4AAwAnAAAAAADp/x8AAwAnAAEAAADq/xMAAwAoAAAAAADq/xQAAwAoAAEAAADq/xUAAwAoAAAAAADq/xYAAwAoAAEAAADq/xcAAwAfAAMAAADq/xgAAwAfAAQAAADq/xkAAwAoAAUAAADq/xoAAwAfAAYAAADq/xsAAwAfAAAAAADq/xwAAwAoAAAAAADq/x0AAwAoAAEAAADq/x4AAwAoAAAAAADq/x8AAwAoAAEAAADr/xMAAwAgAAEAAADr/xQAAwAgAAEAAADr/xUAAwAgAAEAAADr/xYAAwAgAAEAAADr/xcAAwAgAAIAAADr/xgAAwAgAAUAAADr/xkAAwAoAAUAAADr/xoAAwAiAAQAAADr/xsAAwAgAAAAAADr/xwAAwAgAAEAAADr/x0AAwAgAAEAAADr/x4AAwAgAAEAAADr/x8AAwAgAAEAAADs/xMAAwAfAAQAAADs/xQAAwAfAAUAAADs/xUAAwAfAAYAAADs/xYAAwAfAAcAAADs/xcAAwAiAAcAAADs/xgAAwAfAAQAAADs/xkAAwAoAAUAAADs/xoAAwAiAAUAAADs/xsAAwAgAAUAAADs/xwAAwAgAAUAAADs/x0AAwAhAAcAAADs/x4AAwAfAAUAAADs/x8AAwAhAAcAAADt/xMAAwApAAYAAADt/xQAAwApAAYAAADt/xUAAwApAAYAAADt/xYAAwApAAYAAADt/xcAAwApAAYAAADt/xgAAwApAAYAAADt/xkAAwAqAAUAAADt/xoAAwApAAYAAADt/xsAAwApAAYAAADt/xwAAwApAAYAAADt/x0AAwApAAYAAADt/x4AAwApAAYAAADt/x8AAwApAAYAAADu/xMAAwAeAAUAAADu/xQAAwAeAAQAAADu/xUAAwAeAAQAAADu/xYAAwAeAAUAAADu/xcAAwAhAAQAAADu/xgAAwAfAAcAAADu/xkAAwAoAAUAAADu/xoAAwAgAAYAAADu/xsAAwAhAAcAAADu/xwAAwAhAAQAAADu/x0AAwAiAAYAAADu/x4AAwAgAAYAAADu/x8AAwAfAAQAAADv/xMAAwAeAAEAAADv/xQAAwAeAAEAAADv/xUAAwAeAAEAAADv/xYAAwAeAAEAAADv/xcAAwAeAAIAAADv/xgAAwAgAAQAAADv/xkAAwAoAAUAAADv/xoAAwAfAAQAAADv/xsAAwAeAAAAAADv/xwAAwAeAAEAAADv/x0AAwAeAAEAAADv/x4AAwAeAAEAAADv/x8AAwAeAAEAAADw/xMAAwAnAAAAAADw/xQAAwAnAAEAAADw/xUAAwAnAAAAAADw/xYAAwAnAAEAAADw/xcAAwAeAAMAAADw/xgAAwAeAAQAAADw/xkAAwAoAAUAAADw/xoAAwAeAAYAAADw/xsAAwAfAAAAAADw/xwAAwAnAAAAAADw/x0AAwAnAAEAAADw/x4AAwAnAAAAAADw/x8AAwAnAAEAAADx/xMAAwAoAAAAAADx/xQAAwAoAAEAAADx/xUAAwAoAAAAAADx/xYAAwAoAAEAAADx/xcAAwAeAAMAAADx/xgAAwAfAAQAAADx/xkAAwAoAAUAAADx/xoAAwAfAAYAAADx/xsAAwAfAAAAAADx/xwAAwAoAAAAAADx/x0AAwAoAAEAAADx/x4AAwAoAAAAAADx/x8AAwAoAAEAAADy/xUAAwAnAAAAAADy/xYAAwAnAAEAAADy/xcAAwAeAAMAAADy/xgAAwAgAAQAAADy/xkAAwAoAAUAAADy/xoAAwAgAAYAAADy/xsAAwAfAAAAAADy/xwAAwAnAAAAAADy/x0AAwAnAAEAAADz/xUAAwAnAAAAAADz/xYAAwAnAAEAAADz/xcAAwAeAAMAAADz/xgAAwAgAAQAAADz/xkAAwAoAAUAAADz/xoAAwAgAAYAAADz/xsAAwAfAAAAAADz/xwAAwAnAAAAAADz/x0AAwAnAAEAAADp/xAAAwAnAAAAAGDp/xEAAwAoAAAAAGDp/xIAAwAnAAAAAGDq/xAAAwAnAAEAAGDq/xEAAwAoAAEAAGDq/xIAAwAnAAEAAGDr/xAAAwAgAAEAAADr/xEAAwAgAAEAAADr/xIAAwAgAAEAAADs/xAAAwAiAAQAAGDs/xEAAwAhAAQAAGDs/xIAAwAgAAQAAGDt/xAAAwAoAAUAAGDt/xEAAwAoAAUAAGDt/xIAAwAoAAUAAGDu/xAAAwAiAAYAAGDu/xEAAwAhAAYAAGDu/xIAAwAgAAYAAGDv/xAAAwAeAAEAAADv/xEAAwAeAAEAAADv/xIAAwAeAAEAAADw/xAAAwAnAAAAAGDw/xEAAwAoAAAAAGDw/xIAAwAnAAAAAGDx/xAAAwAnAAEAAGDx/xEAAwAoAAEAAGDx/xIAAwAnAAEAAGD0/xUAAwAoAAAAAAD0/xYAAwAoAAEAAAD0/xcAAwAfAAMAAAD0/xgAAwAhAAQAAAD0/xkAAwAoAAUAAAD0/xoAAwAhAAYAAAD0/xsAAwAfAAAAAAD0/xwAAwAoAAAAAAD0/x0AAwAoAAEAAAD1/xUAAwAnAAAAAAD1/xYAAwAnAAEAAAD1/xcAAwAeAAMAAAD1/xgAAwAiAAQAAAD1/xkAAwAoAAUAAAD1/xoAAwAiAAYAAAD1/xsAAwAfAAAAAAD1/xwAAwAnAAAAAAD1/x0AAwAnAAEAAAD2/xUAAwAoAAAAAAD2/xYAAwAoAAEAAAD2/xcAAwAeAAMAAAD2/xgAAwAeAAQAAAD2/xkAAwAoAAUAAAD2/xoAAwAeAAYAAAD2/xsAAwAfAAAAAAD2/xwAAwAoAAAAAAD2/x0AAwAoAAEAAAD3/xUAAwAnAAAAAAD3/xYAAwAnAAEAAAD3/xcAAwAeAAMAAAD3/xgAAwAfAAQAAAD3/xkAAwAoAAUAAAD3/xoAAwAfAAYAAAD3/xsAAwAfAAAAAAD3/xwAAwAnAAAAAAD3/x0AAwAnAAEAAAD4/xUAAwAoAAAAAAD4/xYAAwAoAAEAAAD4/xcAAwAfAAMAAAD4/xgAAwAgAAQAAAD4/xkAAwAoAAUAAAD4/xoAAwAgAAYAAAD4/xsAAwAfAAAAAAD4/xwAAwAoAAAAAAD4/x0AAwAoAAEAAAD5/xUAAwAnAAAAAAD5/xYAAwAnAAEAAAD5/xcAAwAeAAMAAAD5/xgAAwAhAAQAAAD5/xkAAwAoAAUAAAD5/xoAAwAhAAYAAAD5/xsAAwAfAAAAAAD5/xwAAwAnAAAAAAD5/x0AAwAnAAEAAADl//D/GAAJABsAAAAQAPr/AwAoAAUAAGADAO//AwAoAAAAAAADAPD/AwAoAAEAAAAEAO//AwAnAAAAAAAEAPD/AwAnAAEAAAABAO//AwAoAAAAAAABAPD/AwAoAAEAAAACAO//AwAnAAAAAAACAPD/AwAnAAEAAAD//+//AwAoAAAAAAD///D/AwAoAAEAAAAAAO//AwAnAAAAAAAAAPD/AwAnAAEAAAD8/+z/AwAnAAAAAAD8/+3/AwAnAAEAAAD8/+7/AwAnAAAAAAD8/+//AwAnAAAAAAD8//D/AwAnAAEAAAD8//H/AwAnAAEAAAAFAOv/AwAoAAEAAAAFAOz/AwAoAAAAAAAFAO3/AwAoAAEAAAAFAO7/AwAoAAAAAAAFAO//AwAoAAAAAAAFAPD/AwAoAAEAAAD0//n/AwAfAAEAAAD0//r/AwAfAAEAAAD1//n/AwAnAAEAAAD1//r/AwAnAAEAAAD1//v/AwAnAAEAAAD2//r/AwAoAAEAAAD2//v/AwAoAAEAAADy/+z/AwAnAAAAAADy/+3/AwAnAAEAAADz/+z/AwAoAAAAAADz/+3/AwAoAAEAAADy/+7/AwAnAAAAAADy/+//AwAnAAEAAADz/+7/AwAoAAAAAADz/+//AwAoAAEAAADy//D/AwAnAAAAAADy//H/AwAnAAEAAADz//D/AwAoAAAAAADz//H/AwAoAAEAAADy//L/AwAnAAAAAADy//P/AwAnAAEAAADz//L/AwAoAAAAAADz//P/AwAoAAEAAADy//T/AwAnAAAAAADy//X/AwAnAAEAAADz//T/AwAoAAAAAADz//X/AwAoAAEAAADy//b/AwAnAAAAAADy//f/AwAnAAEAAADz//b/AwAoAAAAAADz//f/AwAoAAEAAADy//j/AwAnAAAAAADz//j/AwAoAAAAAADz//n/AwAoAAEAAAD4/+z/AwAnAAAAAAD4/+3/AwAnAAEAAAD5/+z/AwAoAAAAAAD5/+3/AwAoAAEAAAD6/+z/AwAnAAAAAAD6/+3/AwAnAAEAAAD7/+z/AwAoAAAAAAD7/+3/AwAoAAEAAAD4/+7/AwAnAAAAAAD4/+//AwAnAAEAAAD5/+7/AwAoAAAAAAD5/+//AwAoAAEAAAD6/+7/AwAnAAAAAAD6/+//AwAnAAEAAAD7/+7/AwAoAAAAAAD7/+//AwAoAAEAAAD2//n/AwAoAAEAAAD3//n/AwAnAAAAAAD3//r/AwAnAAEAAAD4//n/AwAnAAAAAAD4//r/AwAnAAEAAAD5//n/AwAoAAAAAAD5//r/AwAoAAEAAAD6//n/AwAnAAAAAAD6//r/AwAnAAAAAAD7//n/AwAoAAAAAAD7//r/AwAoAAAAAAD8//n/AwAnAAAAAAD8//r/AwAnAAAAAAD9//n/AwAoAAAAAAD9//r/AwAoAAAAAAD3//v/AwAnAAEAAAD4//v/AwAnAAEAAAD5//v/AwAoAAEAAAD6//v/AwAnAAEAAAD7//v/AwAoAAEAAAD8//v/AwAnAAEAAAD9//v/AwAoAAEAAAD4/+X/AwAeAAMAAAD4/+b/AwAiAAQAAAD4/+f/AwAoAAUAAAD4/+j/AwAiAAYAAAD4/+n/AwAfAAAAAAD5/+X/AwAeAAMAAAD5/+b/AwAeAAQAAAD5/+f/AwAoAAUAAAD5/+j/AwAeAAYAAAD5/+n/AwAfAAAAAAAFAPH/AwAoAAAAAAAFAPL/AwAoAAEAAAAEAPP/AwAnAAAAAAAFAPP/AwAoAAAAAAAEAPT/AwAnAAEAAAAEAPX/AwAnAAAAAAAFAPT/AwAoAAEAAAAFAPX/AwAoAAAAAAAEAPb/AwAnAAAAAAAEAPf/AwAnAAEAAAAFAPb/AwAoAAAAAAAFAPf/AwAoAAEAAAAEAPj/AwAJAAAAAAAEAPn/AwAnAAAAAAAFAPj/AwAnAAAAAAAFAPn/AwAoAAAAAAAEAPr/AwAnAAAAAAAEAPv/AwAnAAEAAAAFAPr/AwAoAAAAAAAFAPv/AwAoAAEAAAAGAPj/AwAoAAAAAAAGAPn/AwAoAAEAAAD+//r/AwAnAAAAAAD+//v/AwAnAAEAAAD///r/AwAoAAAAAAD///v/AwAoAAEAAAAAAPr/AwAnAAAAAAAAAPv/AwAnAAEAAAABAPr/AwAoAAAAAAABAPv/AwAoAAEAAAD+//n/AwAnAAAAAAD///n/AwAoAAAAAAAAAPn/AwAnAAAAAAABAPn/AwAoAAAAAAACAPn/AwAnAAAAAAACAPr/AwAnAAAAAAADAPn/AwAoAAAAAAADAPr/AwAoAAAAAAACAPv/AwAnAAEAAAADAPv/AwAoAAEAAAD1//j/AwAnAAAAAAD2//j/AwAoAAAAAAA=") +tile_map_data = PackedByteArray("AAD2//z/AwAfAAEAAAD2//3/AwAfAAEAAAD2//7/AwAeAAMAAAD2////AwAeAAQAAAD2/wAAAwAqAAYAAAD2/wEAAwAhAAUAAAD2/wIAAwAfAAAAAAD2/wMAAwAoAAIAAGD2/wQAAwAnAAIAAGD3//z/AwAfAAEAAAD3//3/AwAfAAEAAAD3//7/AwAeAAMAAAD3////AwAeAAcAAAD3/wAAAwAqAAYAAAD3/wEAAwAgAAUAAAD3/wIAAwAfAAAAAAD3/wMAAwAoAAMAAGD3/wQAAwAnAAMAAGD4//z/AwAfAAEAAAD4//3/AwAfAAEAAAD4//7/AwAeAAMAAAD4////AwAgAAYAAAD4/wAAAwAqAAYAAAD4/wEAAwAfAAUAAAD4/wIAAwAfAAAAAAD4/wMAAwAnAAIAAAD4/wQAAwAnAAMAAAD5//z/AwAfAAEAAAD5//3/AwAfAAEAAAD5//7/AwAeAAMAAAD5////AwAeAAQAAAD5/wAAAwAqAAYAAAD5/wEAAwAfAAQAAAD5/wIAAwAfAAAAAAD5/wMAAwAoAAIAAAD5/wQAAwAoAAMAAAD6//z/AwAnAAEAAAD6//3/AwAfAAEAAAD6//7/AwAfAAMAAAD6////AwAiAAcAAAD6/wAAAwAqAAYAAAD6/wEAAwAgAAcAAAD6/wIAAwAfAAAAAAD6/wMAAwAoAAIAAGD6/wQAAwAnAAIAAGD7//z/AwAoAAEAAAD7//3/AwAoAAEAAAD7//7/AwAeAAMAAAD7////AwAgAAQAAAD7/wAAAwAqAAYAAAD7/wEAAwAeAAQAAAD7/wIAAwAfAAAAAAD7/wMAAwAoAAMAAGD7/wQAAwAnAAMAAGD8//z/AwAnAAAAAAD8//3/AwAnAAEAAAD8//7/AwAeAAMAAAD8////AwAgAAQAAAD8/wAAAwAoAAUAAAD8/wEAAwAgAAYAAAD8/wIAAwAfAAAAAAD8/wMAAwAnAAAAAAD8/wQAAwAnAAEAAAD9//z/AwAoAAAAAAD9//3/AwAoAAEAAAD9//7/AwAfAAMAAAD9////AwAhAAQAAAD9/wAAAwAoAAUAAAD9/wEAAwAhAAYAAAD9/wIAAwAfAAAAAAD9/wMAAwAoAAAAAAD9/wQAAwAoAAEAAAD+//z/AwAnAAAAAAD+//3/AwAnAAEAAAD+//7/AwAeAAMAAAD+////AwAiAAQAAAD+/wAAAwAoAAUAAAD+/wEAAwAiAAYAAAD+/wIAAwAfAAAAAAD+/wMAAwAnAAAAAAD+/wQAAwAnAAEAAAD///z/AwAoAAAAAAD///3/AwAoAAEAAAD///7/AwAeAAMAAAD/////AwAeAAQAAAD//wAAAwAoAAUAAAD//wEAAwAeAAYAAAD//wIAAwAfAAAAAAD//wMAAwAoAAAAAAD//wQAAwAoAAEAAAAAAPz/AwAnAAAAAAAAAP3/AwAnAAEAAAAAAP7/AwAeAAMAAAAAAP//AwAfAAQAAAAAAAAAAwAoAAUAAAAAAAEAAwAfAAYAAAAAAAIAAwAfAAAAAAAAAAMAAwAnAAAAAAAAAAQAAwAnAAEAAAABAPz/AwAoAAAAAAABAP3/AwAoAAEAAAABAP7/AwAfAAMAAAABAP//AwAgAAQAAAABAAAAAwAoAAUAAAABAAEAAwAgAAYAAAABAAIAAwAfAAAAAAABAAMAAwAoAAAAAAABAAQAAwAoAAEAAAACAPz/AwAnAAAAAAACAP3/AwAnAAEAAAACAP7/AwAeAAMAAAACAP//AwAhAAQAAAACAAAAAwAoAAUAAAACAAEAAwAhAAYAAAACAAIAAwAfAAAAAAACAAMAAwAnAAAAAAACAAQAAwAnAAEAAAADAPz/AwAoAAAAAAADAP3/AwAoAAEAAAADAP7/AwAeAAMAAAADAP//AwAiAAQAAAADAAAAAwAoAAUAAAADAAEAAwAiAAYAAAADAAIAAwAfAAAAAAADAAMAAwAoAAAAAAADAAQAAwAoAAEAAAAEAPz/AwAnAAAAAAAEAP3/AwAnAAEAAAAEAP7/AwAfAAMAAAAEAP//AwAhAAQAAAAEAAAAAwAoAAUAAAAEAAEAAwAhAAYAAAAEAAIAAwAfAAAAAAAEAAMAAwAoAAAAAAAEAAQAAwAoAAEAAAAFAPz/AwAoAAAAAAAFAP3/AwAoAAEAAAAFAP7/AwAeAAMAAAAFAP//AwAiAAQAAAAFAAAAAwAoAAUAAAAFAAEAAwAiAAYAAAAFAAIAAwAfAAAAAAAFAAMAAwAnAAAAAAAFAAQAAwAnAAEAAAAGAPz/AwAnAAAAAAAGAP3/AwAnAAEAAAAGAP7/AwAeAAMAAAAGAP//AwAeAAQAAAAGAAAAAwAoAAUAAAAGAAEAAwAeAAYAAAAGAAIAAwAfAAAAAAAGAAMAAwAoAAAAAAAGAAQAAwAoAAEAAAAHAPz/AwAoAAAAAAAHAP3/AwAoAAEAAAAHAP7/AwAeAAMAAAAHAP//AwAfAAQAAAAHAAAAAwAoAAUAAAAHAAEAAwAfAAYAAAAHAAIAAwAfAAAAAAAHAAMAAwAnAAAAAAAHAAQAAwAnAAEAAAAIAPz/AwAnAAAAAAAIAP3/AwAnAAEAAAAIAP7/AwAfAAMAAAAIAP//AwAgAAQAAAAIAAAAAwAoAAUAAAAIAAEAAwAgAAYAAAAIAAIAAwAfAAAAAAAIAAMAAwAoAAAAAAAIAAQAAwAoAAEAAAAJAPz/AwAoAAAAAAAJAP3/AwAoAAEAAAAJAP7/AwAeAAMAAAAJAP//AwAhAAQAAAAJAAAAAwAoAAUAAAAJAAEAAwAhAAYAAAAJAAIAAwAfAAAAAAAJAAMAAwAnAAAAAAAJAAQAAwAnAAEAAAAKAPz/AwAnAAAAAAAKAP3/AwAnAAEAAAAKAP7/AwAeAAMAAAAKAP//AwAiAAQAAAAKAAAAAwAoAAUAAAAKAAEAAwAiAAYAAAAKAAIAAwAfAAAAAAAKAAMAAwAoAAAAAAAKAAQAAwAoAAEAAAALAPz/AwAoAAAAAAALAP3/AwAoAAEAAAALAP7/AwAeAAMAAAALAP//AwAiAAUAAAALAAAAAwAqAAYAAAALAAEAAwAgAAUAAAALAAIAAwAfAAAAAAALAAMAAwAoAAMAAGALAAQAAwAnAAMAAGAMAPz/AwAnAAAAAAAMAP3/AwAnAAEAAAANAPz/AwAoAAAAAAANAP3/AwAoAAEAAAAMAPr/AwAoAAAAAGAMAPv/AwAnAAAAAGANAPr/AwAoAAEAAGANAPv/AwAnAAEAAGAMAPn/AwAnAAAAAGANAPn/AwAnAAEAAGAMAAMAAwAnAAAAAAAMAAQAAwAnAAEAAAANAAMAAwAoAAAAAAANAAQAAwAoAAEAAAAMAAUAAwAnAAAAAAAMAAYAAwAnAAEAAAANAAUAAwAoAAAAAAANAAYAAwAoAAEAAAAMAAcAAwAoAAAAAGAMAAgAAwAnAAAAAGANAAcAAwAoAAEAAGANAAgAAwAnAAEAAGAMAAkAAwAoAAAAAGAMAAoAAwAnAAAAAGANAAkAAwAoAAEAAGANAAoAAwAnAAEAAGAMAP7/AwAjAAQAAAANAP7/AwAkAAQAAAAMAP//AwAlAAUAAAAMAAAAAwAlAAQAAAAMAAEAAwAlAAUAAAAMAAIAAwAjAAUAAAANAP//AwAmAAUAAAANAAAAAwAmAAQAAAANAAEAAwAmAAUAAAANAAIAAwAkAAUAAAAOAP//AwAgAAUAAAAOAAAAAwAoAAUAAAAOAAEAAwAiAAQAAAAOAAIAAwAgAAAAAAAPAP//AwAfAAQAAAAPAAAAAwAoAAUAAAAPAAEAAwAiAAUAAAAPAAIAAwAgAAUAAAAQAP//AwApAAYAAAAQAAAAAwAqAAUAAAAQAAEAAwApAAYAAAAQAAIAAwApAAYAAAARAP//AwAfAAcAAAARAAAAAwAoAAUAAAARAAEAAwAgAAYAAAARAAIAAwAhAAcAAAASAP//AwAgAAQAAAASAAAAAwAoAAUAAAASAAEAAwAfAAQAAAASAAIAAwAeAAAAAAAOAP7/AwAgAAIAAAAPAP7/AwAiAAcAAAAQAP7/AwApAAYAAAARAP7/AwAhAAQAAAASAP7/AwAeAAIAAAATAP7/AwAjAAQAAAAUAP7/AwAkAAQAAAATAP//AwAlAAUAAAATAAAAAwAlAAQAAAATAAEAAwAlAAUAAAATAAIAAwAjAAUAAAAUAP//AwAmAAUAAAAUAAAAAwAmAAQAAAAUAAEAAwAmAAUAAAAUAAIAAwAkAAUAAAATAPz/AwAnAAAAAAATAP3/AwAnAAEAAAAUAPz/AwAoAAAAAAAUAP3/AwAoAAEAAAATAPr/AwAoAAAAAGATAPv/AwAnAAEAAAAUAPr/AwAoAAEAAGAUAPv/AwAoAAEAAAATAPn/AwAnAAAAAGAUAPn/AwAnAAEAAGAOAPr/AwAgAAEAAAAOAPv/AwAgAAEAAAAOAPz/AwAjAAYAAAAOAP3/AwAjAAcAAAAPAPr/AwAeAAQAAGAPAPv/AwAiAAQAAGAPAPz/AwAlAAYAAAAPAP3/AwAlAAcAAAAQAPv/AwAoAAUAAGAQAPz/AwAlAAYAAAAQAP3/AwAlAAcAAAARAPr/AwAeAAYAAGARAPv/AwAiAAYAAGARAPz/AwAlAAYAAAARAP3/AwAlAAcAAAASAPr/AwAeAAEAAAASAPv/AwAeAAEAAAASAPz/AwAkAAYAAAASAP3/AwAkAAcAAAAOAPn/AwAgAAEAAAAPAPn/AwAiAAQAAGAQAPn/AwAoAAUAAGARAPn/AwAiAAYAAGASAPn/AwAeAAEAAAAOAAMAAwAjAAYAAAAOAAQAAwAjAAcAAAAOAAUAAwAgAAEAAAAOAAYAAwAgAAEAAAAPAAMAAwAlAAYAAAAPAAQAAwAlAAcAAAAPAAUAAwAfAAUAAAAPAAYAAwAhAAcAAAAQAAMAAwAlAAYAAAAQAAQAAwAlAAcAAAAQAAUAAwApAAYAAAAQAAYAAwApAAYAAAARAAMAAwAlAAYAAAARAAQAAwAlAAcAAAARAAUAAwAgAAYAAAARAAYAAwAfAAQAAAASAAMAAwAkAAYAAAASAAQAAwAkAAcAAAASAAUAAwAeAAEAAAASAAYAAwAeAAEAAAAPAAcAAwAiAAQAAGAPAAgAAwAhAAQAAGAPAAkAAwAgAAQAAGAPAAoAAwAfAAQAAGAQAAcAAwAoAAUAAGAQAAgAAwAoAAUAAGAQAAkAAwAoAAUAAGAQAAoAAwAoAAUAAGARAAcAAwAiAAYAAGARAAgAAwAhAAYAAGARAAkAAwAgAAYAAGARAAoAAwAfAAYAAGASAAcAAwAeAAEAAAASAAgAAwAeAAEAAAASAAkAAwAeAAEAAAASAAoAAwAeAAEAAAATAAcAAwAoAAAAAGATAAgAAwAnAAAAAGATAAkAAwAoAAAAAGATAAoAAwAnAAAAAGAOAAcAAwAgAAEAAAAOAAgAAwAgAAEAAAAOAAkAAwAgAAEAAAAOAAoAAwAgAAEAAAATAAMAAwAnAAAAAAATAAQAAwAnAAEAAAAUAAMAAwAoAAAAAAAUAAQAAwAoAAEAAAATAAUAAwAnAAAAAAATAAYAAwAnAAEAAAAUAAUAAwAoAAAAAAAUAAYAAwAoAAEAAAAUAAcAAwAoAAEAAGAUAAgAAwAnAAEAAGAUAAkAAwAoAAEAAGAUAAoAAwAnAAEAAGAVAP7/AwAeAAMAAAAWAP7/AwAfAAMAAAAXAP7/AwAeAAMAAAAYAP7/AwAeAAMAAAAZAP7/AwAeAAMAAAAaAP7/AwAfAAMAAAAbAP7/AwAeAAMAAAAcAP7/AwAeAAMAAAAVAP//AwAgAAQAAAAVAAAAAwAoAAUAAAAVAAEAAwAgAAYAAAAVAAIAAwAfAAAAAAAWAP//AwAhAAQAAAAWAAAAAwAoAAUAAAAWAAEAAwAhAAYAAAAWAAIAAwAfAAAAAAAXAP//AwAiAAQAAAAXAAAAAwAoAAUAAAAXAAEAAwAiAAYAAAAXAAIAAwAfAAAAAAAYAP//AwAeAAQAAAAYAAAAAwAoAAUAAAAYAAEAAwAeAAYAAAAYAAIAAwAfAAAAAAAZAP//AwAfAAQAAAAZAAAAAwAoAAUAAAAZAAEAAwAfAAYAAAAZAAIAAwAfAAAAAAAaAP//AwAgAAQAAAAaAAAAAwAoAAUAAAAaAAEAAwAgAAYAAAAaAAIAAwAfAAAAAAAbAP//AwAhAAQAAAAbAAAAAwAoAAUAAAAbAAEAAwAhAAYAAAAbAAIAAwAfAAAAAAAcAP//AwAiAAQAAAAcAAAAAwAoAAUAAAAcAAEAAwAiAAYAAAAcAAIAAwAfAAAAAAAVAAMAAwAnAAAAAAAVAAQAAwAnAAEAAAAWAAMAAwAoAAAAAAAWAAQAAwAoAAEAAAAXAAMAAwAnAAAAAAAXAAQAAwAnAAEAAAAYAAMAAwAoAAAAAAAYAAQAAwAoAAEAAAAZAAMAAwAnAAAAAAAZAAQAAwAnAAEAAAAaAAMAAwAoAAAAAAAaAAQAAwAoAAEAAAAbAAMAAwAnAAAAAAAbAAQAAwAnAAEAAAAcAAMAAwAoAAAAAAAcAAQAAwAoAAEAAAAVAPz/AwAnAAAAAAAVAP3/AwAnAAEAAAAWAPz/AwAoAAAAAAAWAP3/AwAoAAEAAAAXAPz/AwAnAAAAAAAXAP3/AwAnAAEAAAAYAPz/AwAoAAAAAAAYAP3/AwAoAAEAAAAZAPz/AwAnAAAAAAAZAP3/AwAnAAEAAAAaAPz/AwAoAAAAAAAaAP3/AwAoAAEAAAAbAPz/AwAnAAAAAAAbAP3/AwAnAAEAAAAcAPz/AwAoAAAAAAAcAP3/AwAoAAEAAAAMABIAAwAnAAEAAAAMABEAAwAnAAAAAAAMABAAAwAnAAEAAAAMAA8AAwAoAAAAAGAMAA4AAwAnAAAAAGAMAA0AAwAoAAAAAGAMAAwAAwAnAAAAAGAMAAsAAwAoAAAAAGANABIAAwAoAAEAAAANABEAAwAoAAAAAAANABAAAwAoAAEAAAANAA8AAwAoAAEAAGANAA4AAwAnAAEAAGANAA0AAwAoAAEAAGANAAwAAwAnAAEAAGANAAsAAwAoAAEAAGAOABIAAwAgAAEAAAAOABEAAwAgAAEAAAAOABAAAwAgAAEAAAAOAA8AAwAgAAEAAAAOAA4AAwAgAAEAAAAOAA0AAwAgAAEAAAAOAAwAAwAgAAEAAAAOAAsAAwAgAAEAAAAPABIAAwAgAAQAAGAPABEAAwAhAAQAAGAPABAAAwAiAAQAAGAPAA8AAwAeAAQAAGAPAA4AAwAgAAQAAGAPAA0AAwAhAAQAAGAPAAwAAwAiAAQAAGAPAAsAAwAeAAQAAGAQABIAAwAoAAUAAGAQABEAAwAoAAUAAGAQABAAAwAoAAUAAGAQAA8AAwAoAAUAAGAQAA4AAwAoAAUAAGAQAA0AAwAoAAUAAGAQAAwAAwAoAAUAAGAQAAsAAwAoAAUAAGARABIAAwAgAAYAAGARABEAAwAhAAYAAGARABAAAwAiAAYAAGARAA8AAwAeAAYAAGARAA4AAwAgAAYAAGARAA0AAwAhAAYAAGARAAwAAwAiAAYAAGARAAsAAwAeAAYAAGASABIAAwAeAAEAAAASABEAAwAeAAEAAAASABAAAwAeAAEAAAASAA8AAwAeAAEAAAASAA4AAwAeAAEAAAASAA0AAwAeAAEAAAASAAwAAwAeAAEAAAASAAsAAwAeAAEAAAATABIAAwAnAAEAAAATABEAAwAoAAAAAGATABAAAwAnAAAAAGATAA8AAwAoAAAAAGATAA4AAwAnAAAAAGATAA0AAwAoAAAAAGATAAwAAwAnAAAAAGATAAsAAwAoAAAAAGAUABIAAwAoAAEAAAAUABEAAwAoAAEAAGAUABAAAwAnAAEAAGAUAA8AAwAoAAEAAGAUAA4AAwAnAAEAAGAUAA0AAwAoAAEAAGAUAAwAAwAnAAEAAGAUAAsAAwAoAAEAAGAMAPT/AwAoAAAAAGAMAPP/AwAnAAAAAGAMAPL/AwAoAAAAAGAMAPH/AwAnAAAAAGAMAPD/AwAoAAAAAGAMAO//AwAnAAEAAAAMAO7/AwAnAAAAAAANAPT/AwAoAAEAAGANAPP/AwAnAAEAAGANAPL/AwAoAAEAAGANAPH/AwAnAAEAAGANAPD/AwAoAAEAAGANAO//AwAoAAEAAAANAO7/AwAoAAAAAAAOAPT/AwAgAAEAAAAOAPP/AwAgAAEAAAAOAPL/AwAgAAEAAAAOAPH/AwAgAAEAAAAOAPD/AwAgAAEAAAAOAO//AwAjAAcAAAAOAO7/AwAjAAYAAAAPAPT/AwAeAAQAAGAPAPP/AwAiAAQAAGAPAPL/AwAeAAQAAGAPAPH/AwAfAAQAAGAPAPD/AwAgAAQAAGAPAO//AwAlAAcAAAAPAO7/AwAlAAYAAAAQAPT/AwAoAAUAAGAQAPP/AwAoAAUAAGAQAPL/AwAoAAUAAGAQAPH/AwAoAAUAAGAQAPD/AwAoAAUAAGAQAO//AwAlAAcAAAAQAO7/AwAlAAYAAAARAPT/AwAeAAYAAGARAPP/AwAiAAYAAGARAPL/AwAeAAYAAGARAPH/AwAfAAYAAGARAPD/AwAgAAYAAGARAO//AwAlAAcAAAARAO7/AwAlAAYAAAASAPT/AwAeAAEAAAASAPP/AwAeAAEAAAASAPL/AwAeAAEAAAASAPH/AwAeAAEAAAASAPD/AwAeAAEAAAASAO//AwAkAAcAAAASAO7/AwAkAAYAAAATAPT/AwAoAAAAAGATAPP/AwAnAAAAAGATAPL/AwAoAAAAAGATAPH/AwAnAAAAAGATAPD/AwAoAAAAAGATAO//AwAnAAEAAAATAO7/AwAnAAAAAAAUAPT/AwAoAAEAAGAUAPP/AwAnAAEAAGAUAPL/AwAoAAEAAGAUAPH/AwAnAAEAAGAUAPD/AwAoAAEAAGAUAO//AwAoAAEAAAAUAO7/AwAoAAAAAAAMABMAAwAnAAAAAAAMABQAAwAnAAEAAAAMABUAAwAnAAAAAAAMABYAAwAnAAEAAAAMABcAAwAeAAMAAAAMABgAAwAeAAcAAAAMABkAAwAoAAUAAAAMABoAAwAeAAcAAAAMABsAAwAfAAAAAAAMABwAAwAnAAAAAAAMAB0AAwAnAAEAAAAMAB8AAwAnAAEAAAANABMAAwAoAAAAAAANABQAAwAoAAEAAAANABUAAwAoAAAAAAANABYAAwAoAAEAAAANABcAAwAfAAMAAAANABgAAwAfAAQAAAANABkAAwAoAAUAAAANABoAAwAfAAYAAAANABsAAwAfAAAAAAANABwAAwAoAAAAAAANAB0AAwAoAAEAAAANAB4AAwAoAAAAAAANAB8AAwAoAAEAAAAOABMAAwAgAAEAAAAOABQAAwAgAAEAAAAOABUAAwAgAAEAAAAOABYAAwAgAAEAAAAOABcAAwAgAAIAAAAOABgAAwAgAAUAAAAOABkAAwAoAAUAAAAOABoAAwAiAAQAAAAOABsAAwAgAAAAAAAOABwAAwAgAAEAAAAOAB0AAwAgAAEAAAAOAB4AAwAgAAEAAAAOAB8AAwAgAAEAAAAPABMAAwAfAAQAAAAPABQAAwAfAAUAAAAPABUAAwAfAAYAAAAPABYAAwAfAAcAAAAPABcAAwAiAAcAAAAPABgAAwAfAAQAAAAPABkAAwAoAAUAAAAPABoAAwAiAAUAAAAPABsAAwAgAAUAAAAPABwAAwAgAAUAAAAPAB0AAwAhAAcAAAAPAB4AAwAfAAUAAAAPAB8AAwAhAAcAAAAQABMAAwApAAYAAAAQABQAAwApAAYAAAAQABUAAwApAAYAAAAQABYAAwApAAYAAAAQABcAAwApAAYAAAAQABgAAwApAAYAAAAQABkAAwAqAAUAAAAQABoAAwApAAYAAAAQABsAAwApAAYAAAAQABwAAwApAAYAAAAQAB0AAwApAAYAAAAQAB4AAwApAAYAAAAQAB8AAwApAAYAAAARABMAAwAeAAUAAAARABQAAwAeAAQAAAARABUAAwAeAAQAAAARABYAAwAeAAUAAAARABcAAwAhAAQAAAARABgAAwAfAAcAAAARABkAAwAoAAUAAAARABoAAwAgAAYAAAARABsAAwAhAAcAAAARABwAAwAhAAQAAAARAB0AAwAiAAYAAAARAB4AAwAgAAYAAAARAB8AAwAfAAQAAAASABMAAwAeAAEAAAASABQAAwAeAAEAAAASABUAAwAeAAEAAAASABYAAwAeAAEAAAASABcAAwAeAAIAAAASABgAAwAgAAQAAAASABkAAwAoAAUAAAASABoAAwAfAAQAAAASABsAAwAeAAAAAAASABwAAwAeAAEAAAASAB0AAwAeAAEAAAASAB4AAwAeAAEAAAASAB8AAwAeAAEAAAATABMAAwAnAAAAAAATABQAAwAnAAEAAAATABUAAwAnAAAAAAATABYAAwAnAAEAAAATABcAAwAeAAMAAAATABgAAwAeAAQAAAATABkAAwAoAAUAAAATABoAAwAeAAYAAAATABsAAwAfAAAAAAATABwAAwAnAAAAAAATAB0AAwAnAAEAAAATAB4AAwAnAAAAAAATAB8AAwAnAAEAAAAUABMAAwAoAAAAAAAUABQAAwAoAAEAAAAUABUAAwAoAAAAAAAUABYAAwAoAAEAAAAUABcAAwAeAAMAAAAUABgAAwAfAAQAAAAUABkAAwAoAAUAAAAUABoAAwAfAAYAAAAUABsAAwAfAAAAAAAUABwAAwAoAAAAAAAUAB0AAwAoAAEAAAAUAB4AAwAoAAAAAAAUAB8AAwAoAAEAAAAVABUAAwAnAAAAAAAVABYAAwAnAAEAAAAVABcAAwAeAAMAAAAVABgAAwAgAAQAAAAVABkAAwAoAAUAAAAVABoAAwAgAAYAAAAVABsAAwAfAAAAAAAVABwAAwAnAAAAAAAVAB0AAwAnAAEAAAAWABUAAwAoAAAAAAAWABYAAwAoAAEAAAAWABcAAwAfAAMAAAAWABgAAwAhAAQAAAAWABkAAwAoAAUAAAAWABoAAwAhAAYAAAAWABsAAwAfAAAAAAAWABwAAwAoAAAAAAAWAB0AAwAoAAEAAAAMAB4AAwAnAAAAAAAKAOP/AwApAAAAAAAKAOT/AwApAAEAAAAKAOX/AwAeAAMAAAAKAOb/AwAfAAQAAAAKAOf/AwAqAAYAAAAKAOj/AwAfAAUAAAAKAOn/AwAfAAAAAAAKAOr/AwAoAAIAAGALAOP/AwAqAAAAAAALAOT/AwAqAAEAAAALAOX/AwAeAAMAAAALAOb/AwAiAAUAAAALAOf/AwAqAAYAAAALAOj/AwAgAAUAAAALAOn/AwAfAAAAAAALAOr/AwAoAAMAAGAMAOH/AwAnAAAAAAAMAOL/AwAnAAEAAAAMAOP/AwAnAAAAAAAMAOT/AwAnAAEAAAAMAOX/AwAeAAMAAAAMAOb/AwAeAAcAAAAMAOf/AwAoAAUAAAAMAOj/AwAeAAcAAAAMAOn/AwAfAAAAAAAMAOr/AwAnAAAAAAANAOH/AwAoAAAAAAANAOL/AwAoAAEAAAANAOP/AwAoAAAAAAANAOT/AwAoAAEAAAANAOX/AwAfAAMAAAANAOb/AwAfAAQAAAANAOf/AwAoAAUAAAANAOj/AwAfAAYAAAANAOn/AwAfAAAAAAANAOr/AwAoAAAAAAAOAOH/AwAgAAEAAAAOAOL/AwAgAAEAAAAOAOP/AwAgAAEAAAAOAOT/AwAgAAEAAAAOAOX/AwAgAAIAAAAOAOb/AwAgAAUAAAAOAOf/AwAoAAUAAAAOAOj/AwAiAAQAAAAOAOn/AwAgAAAAAAAOAOr/AwAgAAEAAAAPAOH/AwAfAAQAAAAPAOL/AwAfAAUAAAAPAOP/AwAfAAYAAAAPAOT/AwAfAAcAAAAPAOX/AwAiAAcAAAAPAOb/AwAfAAQAAAAPAOf/AwAoAAUAAAAPAOj/AwAiAAUAAAAPAOn/AwAgAAUAAAAPAOr/AwAgAAUAAAAQAOH/AwApAAYAAAAQAOL/AwApAAYAAAAQAOP/AwApAAYAAAAQAOT/AwApAAYAAAAQAOX/AwApAAYAAAAQAOb/AwApAAYAAAAQAOf/AwAoAAQAAAAQAOj/AwApAAYAAAAQAOn/AwApAAYAAAAQAOr/AwApAAYAAAARAOH/AwAeAAUAAAARAOL/AwAeAAQAAAARAOP/AwAeAAQAAAARAOT/AwAeAAUAAAARAOX/AwAhAAQAAAARAOb/AwAfAAcAAAARAOf/AwAfAAUAAAARAOj/AwAgAAYAAAARAOn/AwAhAAcAAAARAOr/AwAhAAQAAAASAOH/AwAeAAEAAAASAOL/AwAeAAEAAAASAOP/AwAeAAEAAAASAOT/AwAeAAEAAAASAOX/AwAeAAEAAAASAOb/AwAeAAEAAAASAOf/AwAeAAEAAAASAOj/AwAeAAEAAAASAOv/AwAeAAEAAAATAOH/AwAnAAAAAAATAOL/AwAnAAEAAAATAOP/AwAnAAAAAAATAOT/AwAnAAEAAAATAOX/AwAnAAAAAAATAOb/AwAnAAAAAAATAOf/AwAnAAAAAAATAOj/AwAnAAAAAAATAOn/AwAnAAEAAAATAOr/AwAnAAAAAAATAOv/AwAnAAEAAAAUAOH/AwAoAAAAAAAUAOL/AwAoAAEAAAAUAOP/AwAoAAAAAAAUAOT/AwAoAAEAAAAUAOX/AwAoAAAAAAAUAOb/AwAoAAAAAAAUAOf/AwAoAAAAAAAUAOj/AwAoAAAAAAAUAOn/AwAoAAEAAAAUAOr/AwAoAAAAAAAUAOv/AwAoAAEAAAASAOn/AwAeAAEAAAASAOr/AwAeAAEAAAAXABUAAwAnAAAAAAAXABYAAwAnAAEAAAAXABcAAwAeAAMAAAAXABgAAwAhAAUAAAAXABkAAwAqAAYAAAAXABoAAwAhAAQAAAAXABsAAwAfAAAAAAAXABwAAwAnAAIAAAAXAB0AAwAnAAMAAAAYABUAAwAoAAAAAAAYABYAAwAoAAEAAAAYABcAAwAsAAAAAAAYABgAAwAsAAEAAAAYABkAAwAsAAIAAAAYABoAAwAgAAYAAAAYABsAAwAfAAAAAAAYABwAAwAoAAIAAAAYAB0AAwAoAAMAAAAZABUAAwApAAIAAAAZABYAAwApAAMAAAAZABcAAwAtAAAAAAAZABgAAwAtAAEAAAAZABkAAwAtAAIAAAAZABoAAwAeAAUAAAAZABsAAwAfAAAAAAAZABwAAwAoAAIAAGAZAB0AAwAnAAIAAGAaABUAAwAqAAIAAAAaABYAAwAqAAMAAAAaABcAAwAuAAAAAAAaABgAAwAuAAEAAAAaABkAAwAuAAIAAAAaABoAAwAfAAUAAAAaABsAAwAfAAAAAAAaABwAAwAoAAMAAGAaAB0AAwAnAAMAAGAbABUAAwAnAAAAAAAbABYAAwAnAAEAAAAbABcAAwAvAAAAAAAbABgAAwAvAAEAAAAbABkAAwAvAAIAAAAbABoAAwAiAAYAAAAbABsAAwAfAAAAAAAbABwAAwAoAAIAAGAbAB0AAwAnAAIAAGAcABUAAwAoAAAAAAAcABYAAwAoAAEAAAAcABcAAwAwAAAAAAAcABgAAwAwAAEAAAAcABkAAwAwAAIAAAAcABoAAwAfAAUAAAAcABsAAwAfAAAAAAAcABwAAwAoAAMAAGAcAB0AAwAnAAMAAGAdABUAAwApAAAAAAAdABYAAwApAAEAAAAdABcAAwAxAAAAAAAdABgAAwAxAAEAAAAdABkAAwAxAAIAAAAdABoAAwAgAAcAAAAdABsAAwAfAAAAAAAdABwAAwAoAAIAAGAdAB0AAwAnAAIAAGAeABUAAwAqAAAAAAAeABYAAwAqAAEAAAAeABcAAwAyAAAAAAAeABgAAwAyAAEAAAAeABkAAwAyAAIAAAAeABoAAwAeAAYAAAAeABsAAwAfAAAAAAAeABwAAwAoAAMAAGAeAB0AAwAnAAMAAGAfABUAAwApAAAAAAAfABYAAwApAAEAAAAfABcAAwAeAAMAAAAfABgAAwAfAAQAAAAfABkAAwAqAAYAAAAfABoAAwAfAAUAAAAfABsAAwAfAAAAAAAfABwAAwAoAAIAAGAfAB0AAwAnAAIAAGAgABUAAwAqAAAAAAAgABYAAwAqAAEAAAAgABcAAwAeAAMAAAAgABgAAwAiAAUAAAAgABkAAwAqAAYAAAAgABoAAwAgAAUAAAAgABsAAwAfAAAAAAAgABwAAwAoAAMAAGAgAB0AAwAnAAMAAGAhABUAAwAnAAAAAAAhABYAAwAnAAEAAAAhABcAAwAeAAMAAAAhABgAAwAeAAcAAAAhABkAAwAoAAUAAAAhABoAAwAeAAcAAAAhABsAAwAfAAAAAAAhABwAAwAnAAAAAAAhAB0AAwAnAAEAAAAiABUAAwAoAAAAAAAiABYAAwAoAAEAAAAiABcAAwAfAAMAAAAiABgAAwAfAAQAAAAiABkAAwAoAAUAAAAiABoAAwAfAAYAAAAiABsAAwAfAAAAAAAiABwAAwAoAAAAAAAiAB0AAwAoAAEAAAAjABUAAwAgAAEAAAAjABYAAwAgAAEAAAAjABcAAwAgAAIAAAAjABgAAwAgAAUAAAAjABkAAwAoAAUAAAAjABoAAwAiAAQAAAAjABsAAwAgAAAAAAAjABwAAwAgAAEAAAAjAB0AAwAgAAEAAAAkABUAAwAfAAYAAAAkABYAAwAfAAcAAAAkABcAAwAiAAcAAAAkABgAAwAfAAQAAAAkABkAAwAoAAUAAAAkABoAAwAiAAUAAAAkABsAAwAgAAUAAAAkABwAAwAgAAUAAAAkAB0AAwAhAAcAAAAlABUAAwApAAYAAAAlABYAAwApAAYAAAAlABcAAwApAAYAAAAlABgAAwApAAYAAAAlABkAAwAqAAUAAAAlABoAAwApAAYAAAAlABsAAwApAAYAAAAlABwAAwApAAYAAAAlAB0AAwApAAYAAAAmABUAAwAeAAQAAAAmABYAAwAeAAUAAAAmABcAAwAhAAQAAAAmABgAAwAfAAcAAAAmABkAAwAoAAUAAAAmABoAAwAgAAYAAAAmABsAAwAhAAcAAAAmABwAAwAhAAQAAAAmAB0AAwAiAAYAAAAnABUAAwAeAAEAAAAnABYAAwAeAAEAAAAnABcAAwAeAAIAAAAnABgAAwAgAAQAAAAnABkAAwAoAAUAAAAnABoAAwAfAAQAAAAnABsAAwAeAAAAAAAnABwAAwAeAAEAAAAnAB0AAwAeAAEAAAAoABUAAwAnAAAAAAAoABYAAwAnAAEAAAAoABcAAwAeAAMAAAAoABgAAwAeAAQAAAAoABkAAwAoAAUAAAAoABoAAwAeAAYAAAAoABsAAwAfAAAAAAAoABwAAwAnAAAAAAAoAB0AAwAnAAEAAAApABUAAwAoAAAAAAApABYAAwAoAAEAAAApABcAAwAeAAMAAAApABgAAwAfAAQAAAApABkAAwAoAAUAAAApABoAAwAfAAYAAAApABsAAwAfAAAAAAApABwAAwAoAAAAAAApAB0AAwAoAAEAAAAqABUAAwAnAAAAAAAqABYAAwAnAAEAAAAqABcAAwAeAAMAAAAqABgAAwAgAAQAAAAqABkAAwAoAAUAAAAqABoAAwAgAAYAAAAqABsAAwAfAAAAAAAqABwAAwAnAAAAAAAqAB0AAwAnAAEAAAArABUAAwAoAAAAAAArABYAAwAoAAEAAAArABcAAwAfAAMAAAArABgAAwAhAAQAAAArABkAAwAoAAUAAAArABoAAwAhAAYAAAArABsAAwAfAAAAAAArABwAAwAoAAAAAAArAB0AAwAoAAEAAAAsABUAAwAoAAAAAAAsABYAAwAoAAEAAAAsABcAAwAeAAMAAAAsABgAAwAiAAQAAAAsABkAAwAoAAUAAAAsABoAAwAiAAYAAAAsABsAAwAfAAAAAAAsABwAAwAoAAAAAAAsAB0AAwAoAAEAAAAtABUAAwAnAAAAAAAtABYAAwAnAAEAAAAtABcAAwAeAAMAAAAtABgAAwAgAAQAAAAtABkAAwAoAAUAAAAtABoAAwAgAAYAAAAtABsAAwAfAAAAAAAtABwAAwAnAAAAAAAtAB0AAwAnAAEAAAAuABUAAwAoAAAAAAAuABYAAwAoAAEAAAAuABcAAwAfAAMAAAAuABgAAwAhAAQAAAAuABkAAwAoAAUAAAAuABoAAwAhAAYAAAAuABsAAwAfAAAAAAAuABwAAwAoAAAAAAAuAB0AAwAoAAEAAAAvABUAAwAnAAAAAAAvABYAAwAnAAEAAAAvABcAAwAeAAMAAAAvABgAAwAiAAQAAAAvABkAAwAoAAUAAAAvABoAAwAiAAYAAAAvABsAAwAfAAAAAAAvABwAAwAnAAAAAAAvAB0AAwAnAAEAAAAwABUAAwAoAAAAAAAwABYAAwAoAAEAAAAwABcAAwAeAAMAAAAwABgAAwAeAAQAAAAwABkAAwAoAAUAAAAwABoAAwAeAAYAAAAwABsAAwAfAAAAAAAwABwAAwAoAAAAAAAwAB0AAwAoAAEAAAAxABUAAwAnAAAAAAAxABYAAwAnAAEAAAAxABcAAwAeAAMAAAAxABgAAwAfAAQAAAAxABkAAwAoAAUAAAAxABoAAwAfAAYAAAAxABsAAwAfAAAAAAAxABwAAwAnAAAAAAAxAB0AAwAnAAEAAAAyABUAAwAoAAAAAAAyABYAAwAoAAEAAAAyABcAAwAfAAMAAAAyABgAAwAgAAQAAAAyABkAAwAoAAUAAAAyABoAAwAgAAYAAAAyABsAAwAfAAAAAAAyABwAAwAoAAAAAAAyAB0AAwAoAAEAAAAzABUAAwAnAAAAAAAzABYAAwAnAAEAAAAzABcAAwAeAAMAAAAzABgAAwAhAAQAAAAzABkAAwAoAAUAAAAzABoAAwAhAAYAAAAzABsAAwAfAAAAAAAzABwAAwAnAAAAAAAzAB0AAwAnAAEAAAA0ABUAAwAoAAAAAAA0ABYAAwAoAAEAAAA0ABcAAwAeAAMAAAA0ABgAAwAiAAQAAAA0ABkAAwAoAAUAAAA0ABoAAwAiAAYAAAA0ABsAAwAfAAAAAAA0ABwAAwAoAAAAAAA0AB0AAwAoAAEAAAA1ABUAAwAnAAAAAAA1ABYAAwAnAAEAAAA1ABcAAwAeAAMAAAA1ABgAAwAgAAQAAAA1ABkAAwAoAAUAAAA1ABoAAwAgAAYAAAA1ABsAAwAfAAAAAAA1ABwAAwAnAAAAAAA1AB0AAwAnAAEAAAA2ABUAAwAoAAAAAAA2ABYAAwAoAAEAAAA2ABcAAwAfAAMAAAA2ABgAAwAhAAQAAAA2ABkAAwAoAAUAAAA2ABoAAwAhAAYAAAA2ABsAAwAfAAAAAAA2ABwAAwAoAAAAAAA2AB0AAwAoAAEAAAA3ABUAAwAnAAAAAAA3ABYAAwAnAAEAAAA3ABcAAwAeAAMAAAA3ABgAAwAiAAQAAAA3ABkAAwAoAAUAAAA3ABoAAwAiAAYAAAA3ABsAAwAfAAAAAAA3ABwAAwAnAAAAAAA3AB0AAwAnAAEAAAA4ABUAAwAoAAAAAAA4ABYAAwAoAAEAAAA4ABcAAwAeAAMAAAA4ABgAAwAeAAQAAAA4ABkAAwAoAAUAAAA4ABoAAwAeAAYAAAA4ABsAAwAfAAAAAAA4ABwAAwAoAAAAAAA4AB0AAwAoAAEAAAA5ABUAAwAnAAAAAAA5ABYAAwAnAAEAAAA5ABcAAwAeAAMAAAA5ABgAAwAfAAQAAAA5ABkAAwAoAAUAAAA5ABoAAwAfAAYAAAA5ABsAAwAfAAAAAAA5ABwAAwAnAAAAAAA5AB0AAwAnAAEAAAA6ABUAAwAoAAAAAAA6ABYAAwAoAAEAAAA6ABcAAwAfAAMAAAA6ABgAAwAgAAQAAAA6ABkAAwAoAAUAAAA6ABoAAwAgAAYAAAA6ABsAAwAfAAAAAAA6ABwAAwAoAAAAAAA6AB0AAwAoAAEAAAA7ABUAAwAnAAAAAAA7ABYAAwAnAAEAAAA7ABcAAwAeAAMAAAA7ABgAAwAhAAQAAAA7ABkAAwAoAAUAAAA7ABoAAwAhAAYAAAA7ABsAAwAfAAAAAAA7ABwAAwAnAAAAAAA7AB0AAwAnAAEAAAA8ABUAAwAoAAAAAAA8ABYAAwAoAAEAAAA8ABcAAwAeAAMAAAA8ABgAAwAiAAQAAAA8ABkAAwAoAAUAAAA8ABoAAwAiAAYAAAA8ABsAAwAfAAAAAAA8ABwAAwAoAAAAAAA8AB0AAwAoAAEAAAA9ABUAAwAnAAAAAAA9ABYAAwAnAAEAAAA9ABcAAwAeAAMAAAA9ABgAAwAgAAQAAAA9ABkAAwAoAAUAAAA9ABoAAwAgAAYAAAA9ABsAAwAfAAAAAAA9ABwAAwAnAAAAAAA9AB0AAwAnAAEAAAA+ABUAAwAoAAAAAAA+ABYAAwAoAAEAAAA+ABcAAwAfAAMAAAA+ABgAAwAhAAQAAAA+ABkAAwAoAAUAAAA+ABoAAwAhAAYAAAA+ABsAAwAfAAAAAAA+ABwAAwAoAAAAAAA+AB0AAwAoAAEAAAA/ABUAAwAnAAAAAAA/ABYAAwAnAAEAAAA/ABcAAwAeAAMAAAA/ABgAAwAiAAQAAAA/ABkAAwAoAAUAAAA/ABoAAwAiAAYAAAA/ABsAAwAfAAAAAAA/ABwAAwAnAAAAAAA/AB0AAwAnAAEAAABAABUAAwAoAAAAAABAABYAAwAoAAEAAABAABcAAwAeAAMAAABAABgAAwAeAAQAAABAABkAAwAoAAUAAABAABoAAwAeAAYAAABAABsAAwAfAAAAAABAABwAAwAoAAAAAABAAB0AAwAoAAEAAABBABUAAwAnAAAAAABBABYAAwAnAAEAAABBABcAAwAeAAMAAABBABgAAwAfAAQAAABBABkAAwAoAAUAAABBABoAAwAfAAYAAABBABsAAwAfAAAAAABBABwAAwAnAAAAAABBAB0AAwAnAAEAAABCABUAAwAoAAAAAABCABYAAwAoAAEAAABCABcAAwAfAAMAAABCABgAAwAgAAQAAABCABkAAwAoAAUAAABCABoAAwAgAAYAAABCABsAAwAfAAAAAABCABwAAwAoAAAAAABCAB0AAwAoAAEAAABDABUAAwAnAAAAAABDABYAAwAnAAEAAABDABcAAwAeAAMAAABDABgAAwAhAAQAAABDABkAAwAoAAUAAABDABoAAwAhAAYAAABDABsAAwAfAAAAAABDABwAAwAnAAAAAABDAB0AAwAnAAEAAABEABUAAwAoAAAAAABEABYAAwAoAAEAAABEABcAAwAeAAMAAABEABgAAwAiAAQAAABEABkAAwAoAAUAAABEABoAAwAiAAYAAABEABsAAwAfAAAAAABEABwAAwAoAAAAAABEAB0AAwAoAAEAAAACAOP/AwAoAAAAAAACAOT/AwAoAAEAAAACAOX/AwAeAAMAAAACAOb/AwAiAAQAAAACAOf/AwAoAAUAAAACAOj/AwAiAAYAAAACAOn/AwAhAAYAAAACAOr/AwAgAAYAAAACAOv/AwAiAAkAAAADAOP/AwAoAAAAAAADAOT/AwAoAAEAAAADAOX/AwAfAAMAAAADAOb/AwAhAAQAAAADAOf/AwAoAAUAAAADAOj/AwAhAAYAAAADAOn/AwAeAAAAAAADAOr/AwAmAAkAAAADAOv/AwAmAAkAAAAEAOP/AwAnAAAAAAAEAOT/AwAnAAEAAAAEAOX/AwAeAAMAAAAEAOb/AwAiAAQAAAAEAOf/AwAoAAUAAAAEAOj/AwAiAAYAAAAEAOn/AwAfAAAAAAAEAOr/AwAnAAAAAAAEAOv/AwAnAAEAAAAFAOP/AwAoAAAAAAAFAOT/AwAoAAEAAAAFAOX/AwAeAAMAAAAFAOb/AwAeAAQAAAAFAOf/AwAoAAUAAAAFAOj/AwAeAAYAAAAFAOn/AwAfAAAAAAAFAOr/AwAoAAAAAAAGAOP/AwAnAAAAAAAGAOT/AwAnAAEAAAAGAOX/AwAeAAMAAAAGAOb/AwAfAAQAAAAGAOf/AwAoAAUAAAAGAOj/AwAfAAYAAAAGAOn/AwAfAAAAAAAGAOr/AwAoAAAAAAAHAOP/AwAoAAAAAAAHAOT/AwAoAAEAAAAHAOX/AwAfAAMAAAAHAOb/AwAgAAQAAAAHAOf/AwAoAAUAAAAHAOj/AwAgAAYAAAAHAOn/AwAfAAAAAAAHAOr/AwAoAAAAAAAIAOP/AwAnAAAAAAAIAOT/AwAnAAEAAAAIAOX/AwAeAAMAAAAIAOb/AwAhAAQAAAAIAOf/AwAoAAUAAAAIAOj/AwAhAAYAAAAIAOn/AwAfAAAAAAAIAOr/AwAnAAAAAAAJAOP/AwAoAAAAAAAJAOT/AwAoAAEAAAAJAOX/AwAeAAMAAAAJAOb/AwAiAAQAAAAJAOf/AwAoAAUAAAAJAOj/AwAiAAYAAAAJAOn/AwAfAAAAAAAJAOr/AwAoAAAAAAD7/+P/AwAoAAAAAAD7/+T/AwAoAAEAAAD7/+X/AwAeAAMAAAD7/+b/AwAiAAQAAAD7/+f/AwAoAAUAAAD7/+j/AwAiAAYAAAD7/+n/AwAfAAAAAAD7/+r/AwAoAAAAAAD7/+v/AwAoAAEAAAD8/+P/AwAoAAAAAAD8/+T/AwAoAAEAAAD8/+X/AwAjAAQAAAD8/+b/AwAlAAUAAAD8/+f/AwAlAAQAAAD8/+j/AwAlAAUAAAD8/+n/AwAjAAUAAAD8/+r/AwAnAAAAAAD8/+v/AwAnAAEAAAD9/+P/AwAnAAAAAAD9/+T/AwAnAAEAAAD9/+X/AwAkAAQAAAD9/+b/AwAmAAUAAAD9/+f/AwAmAAQAAAD9/+j/AwAmAAUAAAD9/+n/AwAkAAUAAAD9/+r/AwAoAAAAAAD9/+v/AwAoAAEAAAD+/+P/AwAoAAAAAAD+/+T/AwAoAAEAAAD+/+X/AwAeAAMAAAD+/+b/AwAeAAQAAAD+/+f/AwAoAAUAAAD+/+j/AwAeAAYAAAD+/+n/AwAgAAAAAAD+/+r/AwAgAAEAAAD+/+v/AwAeAAkAAAD//+P/AwAnAAAAAAD//+T/AwAnAAEAAAD//+X/AwAeAAMAAAD//+b/AwAfAAQAAAD//+f/AwAoAAUAAAD//+j/AwAfAAYAAAD//+n/AwAeAAUAAAD//+r/AwAfAAUAAAD//+v/AwAfAAYAAAAAAOP/AwAoAAAAAAAAAOT/AwAoAAEAAAAAAOX/AwAfAAMAAAAAAOb/AwAgAAQAAAAAAOf/AwAoAAUAAAAAAOj/AwAgAAYAAAAAAOn/AwAfAAUAAAAAAOr/AwAgAAkAAAAAAOv/AwAgAAoAAAABAOP/AwAnAAAAAAABAOT/AwAnAAEAAAABAOX/AwAeAAMAAAABAOb/AwAhAAQAAAABAOf/AwAoAAUA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AAAgAAQAAwAvABIAAAAgAAUAAwAvABMAAAAgAAYAAwAvABQAAAAgAAcAAwAvABUAAAAhAAIAAwAwABAAAAAhAAMAAwAwABEAAAAhAAQAAwAwABIAAAAhAAUAAwAwABMAAAAhAAYAAwAwABQAAAAhAAcAAwAwABUAAAAiAAIAAwAxABAAAAAiAAMAAwAxABEAAAAiAAQAAwAxABIAAAAiAAUAAwAxABMAAAAiAAYAAwAxABQAAAAiAAcAAwAxABUAAAAjAAMAAwAyABIAAAAjAAQAAwAyABIAAAAjAAUAAwAyABMAAAAjAAYAAwAyABQAAAAjAAcAAwAyABUAAAAhAPH/AwAoAAAAAGAhAPL/AwAnAAAAAGAhAPP/AwAoAAAAAGAhAPT/AwAnAAAAAGAhAPX/AwAoAAAAAGAhAPb/AwAnAAAAAGAhAPf/AwAoAAAAAGAhAPj/AwAnAAAAAGAiAPH/AwAoAAEAAGAiAPL/AwAnAAEAAGAiAPP/AwAoAAEAAGAiAPT/AwAnAAEAAGAiAPX/AwAoAAEAAGAiAPb/AwAnAAEAAGAiAPf/AwAoAAEAAGAiAPj/AwAnAAEAAGAjAPH/AwAgAAEAAAAjAPL/AwAgAAEAAAAjAPP/AwAgAAEAAAAjAPT/AwAgAAEAAAAjAPX/AwAgAAEAAAAjAPb/AwAgAAEAAAAjAPf/AwAgAAEAAAAjAPj/AwAgAAEAAAAkAPH/AwAiAAQAAGAkAPL/AwAhAAQAAGAkAPP/AwAgAAQAAGAkAPT/AwAfAAQAAGAkAPX/AwAeAAQAAGAkAPb/AwAiAAQAAGAkAPf/AwAhAAQAAGAkAPj/AwAgAAQAAGAlAPH/AwAoAAUAAGAlAPL/AwAoAAUAAGAlAPP/AwAoAAUAAGAlAPT/AwAoAAUAAGAlAPX/AwAoAAUAAGAlAPb/AwAoAAUAAGAlAPf/AwAoAAUAAGAlAPj/AwAoAAUAAGAmAPH/AwAiAAYAAGAmAPL/AwAhAAYAAGAmAPP/AwAgAAYAAGAmAPT/AwAfAAYAAGAmAPX/AwAeAAYAAGAmAPb/AwAiAAYAAGAmAPf/AwAhAAYAAGAmAPj/AwAgAAYAAGAnAPH/AwAeAAEAAAAnAPL/AwAeAAEAAAAnAPP/AwAeAAEAAAAnAPT/AwAeAAEAAAAnAPX/AwAeAAEAAAAnAPb/AwAeAAEAAAAnAPf/AwAeAAEAAAAnAPj/AwAeAAEAAAAoAPH/AwAoAAAAAGAoAPL/AwAnAAAAAGAoAPP/AwAoAAAAAGAoAPT/AwAnAAAAAGAoAPX/AwAoAAAAAGAoAPb/AwAnAAAAAGAoAPf/AwAoAAAAAGAoAPj/AwAnAAAAAGApAPH/AwAoAAEAAGApAPL/AwAnAAEAAGApAPP/AwAoAAEAAGApAPT/AwAnAAEAAGApAPX/AwAoAAEAAGApAPb/AwAnAAEAAGApAPf/AwAoAAEAAGApAPj/AwAnAAEAAGAoAPn/AwA1AAkAAAAoAPr/AwA1AAoAAAAoAPv/AwA1AAsAAAAoAPz/AwA1AAwAAAAoAP3/AwA1AA0AAAAoAP7/AwA1AA4AAAApAPn/AwA2AAkAAAApAPr/AwA2AAoAAAApAPv/AwA2AAsAAAApAPz/AwA2AAwAAAApAP3/AwA2AA0AAAApAP7/AwA2AA4AAAAqAPn/AwA3AAkAAAAqAPr/AwA3AAoAAAAqAPv/AwA3AAsAAAAqAPz/AwA3AAwAAAAqAP3/AwA3AA0AAAAqAP7/AwA3AA4AAAArAPn/AwA4AAkAAAArAPr/AwA4AAoAAAArAPv/AwA4AAsAAAArAPz/AwA4AAwAAAArAP3/AwA4AA0AAAArAP7/AwA4AA4AAAAsAPn/AwA5AAkAAAAsAPr/AwA5AAoAAAAsAPv/AwA5AAsAAAAsAPz/AwA5AAwAAAAsAP3/AwA5AA0AAAAsAP7/AwA5AA4AAAAtAPn/AwA6AAkAAAAtAPr/AwA6AAoAAAAtAPv/AwA6AAsAAAAtAPz/AwA6AAwAAAAtAP3/AwA6AA0AAAAtAP7/AwAeAAMAAAAhAAgAAwAoAAAAAGAhAAkAAwAnAAAAAGAhAAoAAwAoAAAAAGAhAAsAAwAnAAAAAGAhAAwAAwAoAAAAAGAhAA0AAwAnAAAAAGAhAA4AAwAoAAAAAGAhAA8AAwAnAAAAAAAiAAgAAwAoAAEAAGAiAAkAAwAnAAEAAGAiAAoAAwAoAAEAAGAiAAsAAwAnAAEAAGAiAAwAAwAoAAEAAGAiAA0AAwAnAAEAAGAiAA4AAwAoAAEAAGAiAA8AAwAoAAAAAAAjAAgAAwAgAAEAAAAjAAkAAwAgAAEAAAAjAAoAAwAgAAEAAAAjAAsAAwAgAAEAAAAjAAwAAwAgAAEAAAAjAA0AAwAgAAEAAAAjAA4AAwAgAAEAAAAjAA8AAwAgAAEAAAAkAAgAAwAiAAQAAGAkAAkAAwAhAAQAAGAkAAoAAwAgAAQAAGAkAAsAAwAfAAQAAGAkAAwAAwAeAAQAAGAkAA0AAwAiAAQAAGAkAA4AAwAhAAQAAGAkAA8AAwAfAAQAAAAlAAgAAwAoAAUAAGAlAAkAAwAoAAUAAGAlAAoAAwAoAAUAAGAlAAsAAwAoAAUAAGAlAAwAAwAoAAUAAGAlAA0AAwAoAAUAAGAlAA4AAwAoAAUAAGAlAA8AAwApAAYAAAAmAAgAAwAiAAYAAGAmAAkAAwAhAAYAAGAmAAoAAwAgAAYAAGAmAAsAAwAfAAYAAGAmAAwAAwAeAAYAAGAmAA0AAwAiAAYAAGAmAA4AAwAhAAYAAGAmAA8AAwAfAAYAAAAnAAgAAwAeAAEAAAAnAAkAAwAeAAEAAAAnAAoAAwAeAAEAAAAnAAsAAwAeAAEAAAAnAAwAAwAeAAEAAAAnAA0AAwAeAAEAAAAnAA4AAwAeAAEAAAAnAA8AAwAeAAEAAAAoAAgAAwAoAAAAAGAoAAkAAwAnAAAAAGAoAAoAAwAoAAAAAGAoAAsAAwAnAAAAAGAoAAwAAwAoAAAAAGAoAA0AAwAnAAAAAGAoAA4AAwAoAAAAAGAoAA8AAwAnAAAAAAApAAgAAwAoAAEAAGApAAkAAwAnAAEAAGApAAoAAwAoAAEAAGApAAsAAwAnAAEAAGApAAwAAwAoAAEAAGApAA0AAwAnAAEAAGApAA4AAwAoAAEAAGApAA8AAwAoAAAAAAAnAAMAAwAzABIAAAAnAAQAAwA0ABIAAAAnAAUAAwA0ABMAAAAnAAYAAwA0ABQAAAAnAAcAAwA0ABUAAAAoAAIAAwAyABIAAAAoAAMAAwA0ABIAAAAoAAQAAwA1ABIAAAAoAAUAAwA1ABMAAAAoAAYAAwA1ABQAAAAoAAcAAwA1ABUAAAApAAIAAwAzABIAAAApAAMAAwA2ABEAAAApAAQAAwA2ABIAAAApAAUAAwA2ABMAAAApAAYAAwA2ABQAAAApAAcAAwA2ABUAAAAqAAIAAwA0ABIAAAAqAAMAAwA3ABEAAAAqAAQAAwA3ABIAAAAqAAUAAwA3ABMAAAAqAAYAAwA3ABQAAAAqAAcAAwA3ABUAAAArAAIAAwAyABIAAAArAAMAAwA4ABEAAAArAAQAAwA4ABIAAAArAAUAAwA4ABMAAAArAAYAAwA4ABQAAAArAAcAAwA4ABUAAAAsAAIAAwAzABIAAAAsAAMAAwA5ABEAAAAsAAQAAwA5ABIAAAAsAAUAAwA5ABMAAAAsAAYAAwA5ABQAAAAsAAcAAwA5ABUAAAAtAAIAAwA0ABIAAAAtAAMAAwA6ABEAAAAtAAQAAwA6ABIAAAAtAAUAAwA6ABMAAAAtAAYAAwA6ABQAAAAtAAcAAwA6ABUAAAAnAPn/AwA0AAkAAAAnAPr/AwA0AAoAAAAnAPv/AwA0AAsAAAAnAPz/AwA0AAwAAAAnAP3/AwA0AA0AAAAjAP7/AwAiAA0AAAAjAP//AwAiAA4AAAAjAAAAAwAiAA8AAAAjAAEAAwAiABAAAAAkAP7/AwAjAA0AAAAkAP//AwAjAA4AAAAkAAAAAwAjAA8AAAAkAAEAAwAjABAAAAAlAP7/AwAlAA0AAAAlAP//AwAlAA4AAAAlAAAAAwAlAA8AAAAlAAEAAwAlABAAAAAmAP7/AwAgAA0AAAAmAP//AwAgAA4AAAAmAAAAAwAgAA8AAAAmAAEAAwAgABAAAAAnAP7/AwAhAA0AAAAnAP//AwAhAA4AAAAnAAAAAwAhAA8AAAAnAAEAAwAhABAAAAAjAAIAAwAyABEAAAAkAAIAAwAzABEAAAAkAAMAAwAzABIAAAAlAAIAAwA0ABEAAAAlAAMAAwA0ABIAAAAmAAIAAwAyABEAAAAmAAMAAwAyABIAAAAnAAIAAwAzABEAAAAkAAQAAwAyABEAAAAkAAUAAwAyABIAAAAlAAQAAwAzABEAAAAlAAUAAwAzABIAAAAmAAQAAwA0ABEAAAAmAAUAAwA0ABIAAAAdAP//AwAyABEAAAAdAAAAAwAoAAUAAAAeAP//AwAzABEAAAAeAAAAAwAzABEAAAAfAP//AwAyABEAAAAfAAAAAwA0ABEAAAAgAP//AwAyABEAAAAgAAAAAwAyABIAAAAhAP//AwAzABEAAAAhAAAAAwAzABIAAAAgAAEAAwAyABIAAAAhAAEAAwAzABIAAAAiAAAAAwA0ABIAAAAiAAEAAwA0ABIAAAAdAAEAAwAyABIAAAAeAAEAAwAzABIAAAAfAAEAAwA0ABIAAAAiAP//AwA0ABEAAAAkAPn/AwAyABEAAAAkAPr/AwAyABIAAAAlAPn/AwAzABEAAAAlAPr/AwAzABIAAAAmAPn/AwA0ABEAAAAmAPr/AwA0ABIAAAAkAPv/AwAyABEAAAAkAPz/AwAyABEAAAAlAPv/AwAzABEAAAAlAPz/AwAzABEAAAAmAPv/AwA0ABEAAAAmAPz/AwA0ABEAAAAkAP3/AwAyABIAAAAlAP3/AwAzABIAAAAmAP3/AwA0ABIAAAAoAP//AwAyABEAAAAoAAAAAwAyABIAAAApAP//AwAzABEAAAApAAAAAwAzABIAAAAqAP//AwA0ABEAAAAqAAAAAwA0ABIAAAArAP//AwAyABEAAAArAAAAAwAyABIAAAAsAP//AwAzABEAAAAtAP//AwA0ABEAAAAtAAAAAwAoAAUAAAArAAEAAwAyABEAAAAsAAEAAwAzABEAAAAtAAEAAwA0ABEAAAAoAAEAAwAyABEAAAApAAEAAwAzABEAAAAqAAEAAwA0ABEAAAAkAAYAAwAyABEAAAAkAAcAAwAyABIAAAAlAAYAAwAzABEAAAAlAAcAAwAzABIAAAAmAAYAAwA0ABEAAAAmAAcAAwA0ABIAAAAsAAAAAwAzABIAAADl//3/AwAoAAEAAADl//7/AwAeAAMAAADl////AwAiAAQAAADl/wAAAwAoAAUAAADl/wEAAwAiAAYAAADl/wIAAwAfAAAAAADl/wMAAwAoAAAAAADl/wQAAwAoAAEAAADl/wUAAwAsABMAAADl/wYAAwAsABQAAADl/wcAAwAsABUAAADm//n/AgAcAAoAAADm//r/AgAcAAoAAADm//v/AwAtAAsAAADm//z/AwAtAAwAAADm//3/AwAtAA0AAADm//7/AwAtAA4AAADm////AwAzABEAAADm/wAAAwAzABEAAADm/wEAAwAzABIAAADm/wIAAwAtABAAAADm/wMAAwAtABEAAADm/wQAAwAtABIAAADm/wUAAwAtABMAAADm/wYAAwAtABQAAADm/wcAAwAtABUAAADn//n/AgAbAAoAAADn//r/AwAuAAoAAADn//v/AwAuAAsAAADn//z/AwAuAAwAAADn//3/AwAuAA0AAADn//7/AwAuAA4AAADn////AwAyABEAAADn/wAAAwA0ABEAAADn/wEAAwA0ABIAAADn/wIAAwAuABAAAADn/wMAAwAuABEAAADn/wQAAwAuABIAAADn/wUAAwAuABMAAADn/wYAAwAuABQAAADn/wcAAwAuABUAAADo//n/AwAvAAkAAADo//r/AwAvAAoAAADo//v/AwAvAAsAAADo//z/AwAvAAwAAADo//3/AwAvAA0AAADo//7/AwAvAA4AAADo////AwAyABEAAADo/wAAAwAyABIAAADo/wEAAwAyABIAAADo/wIAAwAvABAAAADo/wMAAwAvABEAAADo/wQAAwAvABIAAADo/wUAAwAvABMAAADo/wYAAwAvABQAAADo/wcAAwAvABUAAADp//n/AwAwAAkAAADp//r/AwAwAAoAAADp//v/AwAwAAsAAADp//z/AwAwAAwAAADp//3/AwAwAA0AAADp//7/AwAwAA4AAADp////AwAzABEAAADp/wAAAwAzABIAAADp/wEAAwAzABIAAADp/wIAAwAwABAAAADp/wMAAwAwABEAAADp/wQAAwAwABIAAADp/wUAAwAwABMAAADp/wYAAwAwABQAAADp/wcAAwAwABUAAADq//n/AwAxAAkAAADq//r/AwAxAAoAAADq//v/AwAxAAsAAADq//z/AwAxAAwAAADq//3/AwAxAA0AAADq//7/AwAxAA4AAADq////AwA0ABEAAADq/wAAAwA0ABIAAADq/wEAAwA0ABIAAADq/wIAAwAxABAAAADq/wMAAwAxABEAAADq/wQAAwAxABIAAADq/wUAAwAxABMAAADq/wYAAwAxABQAAADq/wcAAwAxABUAAADr//n/AwAyAAkAAADr//r/AwAyAAoAAADr//v/AwAyAAsAAADr//z/AwAyAAwAAADr//3/AwAyAA0AAADr//7/AwAiAA0AAADr////AwAiAA4AAADr/wAAAwAiAA8AAADr/wEAAwAiABAAAADr/wIAAwAyABEAAADr/wMAAwAyABIAAADr/wQAAwAyABIAAADr/wUAAwAyABMAAADr/wYAAwAyABQAAADr/wcAAwAyABUAAADs//n/AwAyABEAAADs//r/AwAyABIAAADs//v/AwAyABEAAADs//z/AwAyABEAAADs//3/AwAyABIAAADs//7/AwAjAA0AAADs////AwAjAA4AAADs/wAAAwAjAA8AAADs/wEAAwAjABAAAADs/wIAAwAzABEAAADs/wMAAwAzABIAAADs/wQAAwAyABEAAADs/wUAAwAyABIAAADs/wYAAwAyABEAAADs/wcAAwAyABIAAADt//n/AwAzABEAAADt//r/AwAzABIAAADt//v/AwAzABEAAADt//z/AwAzABEAAADt//3/AwAzABIAAADt//7/AwAlAA0AAADt////AwAlAA4AAADt/wAAAwAlAA8AAADt/wEAAwAlABAAAADt/wIAAwA0ABEAAADt/wMAAwA0ABIAAADt/wQAAwAzABEAAADt/wUAAwAzABIAAADt/wYAAwAzABEAAADt/wcAAwAzABIAAADu//n/AwA0ABEAAADu//r/AwA0ABIAAADu//v/AwA0ABEAAADu//z/AwA0ABEAAADu//3/AwA0ABIAAADu//7/AwAoAA0AAADu////AwAoAA4AAADu/wAAAwAoAA8AAADu/wEAAwAoABAAAADu/wIAAwAyABEAAADu/wMAAwAyABIAAADu/wQAAwA0ABEAAADu/wUAAwA0ABIAAADu/wYAAwA0ABEAAADu/wcAAwA0ABIAAADv//n/AwA0AAkAAADv//r/AwA0AAoAAADv//v/AwA0AAsAAADv//z/AwA0AAwAAADv//3/AwA0AA0AAADv//7/AwApAA0AAADv////AwApAA4AAADv/wAAAwApAA8AAADv/wEAAwApABAAAADv/wIAAwAzABEAAADv/wMAAwAzABIAAADv/wQAAwA0ABIAAADv/wUAAwA0ABMAAADv/wYAAwA0ABQAAADv/wcAAwA0ABUAAADw//n/AwA1AAkAAADw//r/AwA1AAoAAADw//v/AwA1AAsAAADw//z/AwA1AAwAAADw//3/AwA1AA0AAADw//7/AwA1AA4AAADw////AwAyABEAAADw/wAAAwAyABIAAADw/wEAAwAyABEAAADw/wIAAwAyABIAAADw/wMAAwA0ABIAAADw/wQAAwA1ABIAAADw/wUAAwA1ABMAAADw/wYAAwA1ABQAAADw/wcAAwA1ABUAAADx//n/AwA2AAkAAADx//r/AwA2AAoAAADx//v/AwA2AAsAAADx//z/AwA2AAwAAADx//3/AwA2AA0AAADx//7/AwA2AA4AAADx////AwAzABEAAADx/wAAAwAzABIAAADx/wEAAwAzABEAAADx/wIAAwAzABIAAADx/wMAAwA2ABEAAADx/wQAAwA2ABIAAADx/wUAAwA2ABMAAADx/wYAAwA2ABQAAADx/wcAAwA2ABUAAADy//n/AwAnAAEAAADy//r/AwA3AAoAAADy//v/AwA3AAsAAADy//z/AwA3AAwAAADy//3/AwA3AA0AAADy//7/AwA3AA4AAADy////AwA0ABEAAADy/wAAAwA0ABIAAADy/wEAAwA0ABEAAADy/wIAAwA0ABIAAADy/wMAAwA3ABEAAADy/wQAAwA3ABIAAADy/wUAAwA3ABMAAADy/wYAAwA3ABQAAADy/wcAAwA3ABUAAADz//r/AwA4AAoAAADz//v/AwA4AAsAAADz//z/AwA4AAwAAADz//3/AwA4AA0AAADz//7/AwA4AA4AAADz////AwAyABEAAADz/wAAAwAyABIAAADz/wEAAwAyABEAAADz/wIAAwAyABIAAADz/wMAAwA4ABEAAADz/wQAAwA4ABIAAADz/wUAAwA4ABMAAADz/wYAAwA4ABQAAADz/wcAAwA4ABUAAAD0//7/AwA5AA4AAAD0////AwAzABEAAAD0/wAAAwAzABIAAAD0/wEAAwAzABEAAAD0/wIAAwAeAAAAAAD0/wMAAwA5ABEAAAD0/wQAAwA5ABIAAAD0/wUAAwA5ABMAAAD0/wYAAwA5ABQAAAD0/wcAAwA5ABUAAAD1//7/AwAeAAMAAAD1////AwA0ABEAAAD1/wAAAwAoAAUAAAD1/wEAAwA0ABEAAAD1/wIAAwAfAAAAAAD1/wMAAwA6ABEAAAD1/wQAAwA6ABIAAAD1/wUAAwA6ABMAAAD1/wYAAwA6ABQAAAD1/wcAAwA6ABUAAADp//H/AwAnAAAAAGDp//L/AwAnAAAAAGDp//P/AwAoAAAAAGDp//T/AwAnAAAAAGDp//X/AwAoAAAAAGDp//b/AwAnAAAAAGDp//f/AwAoAAAAAGDp//j/AwAnAAAAAGDq//H/AwAnAAEAAGDq//L/AwAnAAEAAGDq//P/AwAoAAEAAGDq//T/AwAnAAEAAGDq//X/AwAoAAEAAGDq//b/AwAnAAEAAGDq//f/AwAoAAEAAGDq//j/AwAnAAEAAGDr//H/AwAgAAEAAADr//L/AwAgAAEAAADr//P/AwAgAAEAAADr//T/AwAgAAEAAADr//X/AwAgAAEAAADr//b/AwAgAAEAAADr//f/AwAgAAEAAADr//j/AwAgAAEAAADs//H/AwAgAAQAAGDs//L/AwAhAAQAAGDs//P/AwAgAAQAAGDs//T/AwAfAAQAAGDs//X/AwAeAAQAAGDs//b/AwAiAAQAAGDs//f/AwAhAAQAAGDs//j/AwAgAAQAAGDt//H/AwAoAAUAAGDt//L/AwAoAAUAAGDt//P/AwAoAAUAAGDt//T/AwAoAAUAAGDt//X/AwAoAAUAAGDt//b/AwAoAAUAAGDt//f/AwAoAAUAAGDt//j/AwAoAAUAAGDu//H/AwAgAAYAAGDu//L/AwAhAAYAAGDu//P/AwAgAAYAAGDu//T/AwAfAAYAAGDu//X/AwAeAAYAAGDu//b/AwAiAAYAAGDu//f/AwAhAAYAAGDu//j/AwAgAAYAAGDv//H/AwAeAAEAAADv//L/AwAeAAEAAADv//P/AwAeAAEAAADv//T/AwAeAAEAAADv//X/AwAeAAEAAADv//b/AwAeAAEAAADv//f/AwAeAAEAAADv//j/AwAeAAEAAADw//H/AwAnAAAAAGDw//L/AwAnAAAAAGDw//P/AwAoAAAAAGDw//T/AwAnAAAAAGDw//X/AwAoAAAAAGDw//b/AwAnAAAAAGDw//f/AwAoAAAAAGDw//j/AwAnAAAAAGDx//H/AwAnAAEAAGDx//L/AwAnAAEAAGDx//P/AwAoAAEAAGDx//T/AwAnAAEAAGDx//X/AwAoAAEAAGDx//b/AwAnAAEAAGDx//f/AwAoAAEAAGDp/+r/AwAnAAAAAADp/+v/AwAnAAEAAADp/+z/AwAnAAAAAADp/+3/AwAnAAEAAADp/+7/AwAoAAAAAGDp/+//AwAnAAAAAGDp//D/AwAoAAAAAGDq/+r/AwAoAAAAAADq/+v/AwAoAAEAAADq/+z/AwAoAAAAAADq/+3/AwAoAAEAAADq/+7/AwAoAAEAAGDq/+//AwAnAAEAAGDq//D/AwAoAAEAAGDr/+r/AwAgAAEAAADr/+v/AwAgAAEAAADr/+z/AwAgAAEAAADr/+3/AwAgAAEAAADr/+7/AwAgAAEAAADr/+//AwAgAAEAAADr//D/AwAgAAEAAADs/+r/AwAgAAUAAADs/+v/AwAhAAcAAADs/+z/AwAfAAUAAADs/+3/AwAhAAcAAADs/+7/AwAeAAQAAGDs/+//AwAiAAQAAGDs//D/AwAhAAQAAGDt/+r/AwApAAYAAADt/+v/AwApAAYAAADt/+z/AwApAAYAAADt/+3/AwApAAYAAADt/+7/AwAoAAUAAGDt/+//AwAoAAUAAGDt//D/AwAoAAUAAGDu/+r/AwAhAAQAAADu/+v/AwAiAAYAAADu/+z/AwAgAAYAAADu/+3/AwAfAAQAAADu/+7/AwAeAAYAAGDu/+//AwAiAAYAAGDu//D/AwAhAAYAAGDv/+r/AwAeAAEAAADv/+v/AwAeAAEAAADv/+z/AwAeAAEAAADv/+3/AwAeAAEAAADv/+7/AwAeAAEAAADv/+//AwAeAAEAAADv//D/AwAeAAEAAADw/+r/AwAnAAAAAADw/+v/AwAnAAEAAADw/+z/AwAnAAAAAADw/+3/AwAnAAEAAADw/+7/AwAoAAAAAGDw/+//AwAnAAAAAGDw//D/AwAoAAAAAGDx/+r/AwAoAAAAAADx/+v/AwAoAAEAAADx/+z/AwAoAAAAAADx/+3/AwAoAAEAAADx/+7/AwAoAAEAAGDx/+//AwAnAAEAAGDx//D/AwAoAAEAAGDp/+H/AwAnAAAAAADp/+L/AwAnAAEAAADp/+P/AwAnAAAAAADp/+T/AwAnAAEAAADp/+X/AwAnAAAAAADp/+b/AwAnAAAAAADp/+f/AwAnAAAAAADp/+j/AwAnAAAAAADp/+n/AwAnAAEAAADq/+H/AwAoAAAAAADq/+L/AwAoAAEAAADq/+P/AwAoAAAAAADq/+T/AwAoAAEAAADq/+X/AwAoAAAAAADq/+b/AwAoAAAAAADq/+f/AwAoAAAAAADq/+j/AwAoAAAAAADq/+n/AwAoAAEAAADr/+H/AwAgAAEAAADr/+L/AwAgAAEAAADr/+P/AwAgAAEAAADr/+T/AwAgAAEAAADr/+X/AwAgAAEAAADr/+b/AwAgAAEAAADr/+f/AwAgAAEAAADr/+j/AwAgAAEAAADr/+n/AwAgAAEAAADs/+H/AwAfAAQAAADs/+L/AwAfAAUAAADs/+P/AwAfAAYAAADs/+T/AwAfAAcAAADs/+X/AwAiAAcAAADs/+b/AwAfAAQAAADs/+f/AwAeAAYAAADs/+j/AwAiAAUAAADs/+n/AwAgAAUAAADt/+H/AwApAAYAAADt/+L/AwApAAYAAADt/+P/AwApAAYAAADt/+T/AwApAAYAAADt/+X/AwApAAYAAADt/+b/AwApAAYAAADt/+f/AwAnAAQAAADt/+j/AwApAAYAAADt/+n/AwApAAYAAADu/+H/AwAeAAUAAADu/+L/AwAeAAQAAADu/+P/AwAeAAQAAADu/+T/AwAeAAUAAADu/+X/AwAhAAQAAADu/+b/AwAfAAcAAADu/+f/AwAoAAUAAADu/+j/AwAgAAYAAADu/+n/AwAhAAcAAADv/+P/AwAeAAEAAADv/+T/AwAeAAEAAADv/+X/AwAeAAIAAADv/+b/AwAgAAQAAADv/+f/AwAoAAUAAADv/+j/AwAfAAQAAADv/+n/AwAeAAAAAADw/+P/AwAnAAAAAADw/+T/AwAnAAEAAADw/+X/AwAeAAMAAADw/+b/AwAeAAQAAADw/+n/AwAfAAAAAADx/+P/AwAoAAAAAADx/+T/AwAoAAEAAADx/+X/AwAeAAMAAADx/+b/AwAfAAQAAADx/+f/AwAoAAUAAADx/+n/AwAfAAAAAADy/+P/AwAnAAAAAADy/+T/AwAnAAEAAADy/+X/AwAeAAMAAADy/+b/AwAgAAQAAADy/+f/AwAoAAUAAADy/+n/AwAfAAAAAADy/+r/AwAnAAAAAADy/+v/AwAnAAEAAADz/+P/AwAoAAAAAADz/+T/AwAoAAEAAADz/+X/AwAfAAMAAADz/+b/AwAhAAQAAADz/+f/AwAoAAUAAADz/+n/AwAfAAAAAADz/+r/AwAoAAAAAADz/+v/AwAoAAEAAAAhABMAAwAoAAAAAGAhABQAAwAnAAAAAGAhAB4AAwAnAAAAAAAhAB8AAwAnAAEAAAAiABMAAwAoAAEAAGAiABQAAwAnAAEAAGAiAB4AAwAoAAAAAAAiAB8AAwAoAAEAAAAjABMAAwAgAAEAAAAjABQAAwAgAAEAAAAjAB4AAwAgAAEAAAAjAB8AAwAgAAEAAAAkABMAAwAiAAQAAGAkABQAAwAhAAQAAGAkAB4AAwAfAAUAAAAkAB8AAwAhAAcAAAAlABMAAwAoAAUAAGAlABQAAwAoAAUAAGAlAB4AAwApAAYAAAAlAB8AAwApAAYAAAAmABMAAwAiAAYAAGAmABQAAwAhAAYAAGAmAB4AAwAgAAYAAAAmAB8AAwAfAAQAAAAnABMAAwAeAAEAAAAnABQAAwAeAAEAAAAnAB4AAwAeAAEAAAAnAB8AAwAeAAEAAAAoABMAAwAoAAAAAGAoABQAAwAnAAAAAGAoAB4AAwAnAAAAAAAoAB8AAwAnAAEAAAApABMAAwAoAAEAAGApABQAAwAnAAEAAGApAB4AAwAoAAAAAAApAB8AAwAoAAEAAAAhABAAAwAnAAEAAAAhABEAAwAnAAAAAAAhABIAAwAnAAEAAAAiABAAAwAoAAEAAAAiABEAAwAoAAAAAAAiABIAAwAoAAEAAAAjABAAAwAgAAEAAAAjABEAAwAjAAYAAAAjABIAAwAjAAcAAAAkABAAAwAfAAUAAAAkABEAAwAlAAYAAAAkABIAAwAlAAcAAAAlABAAAwApAAYAAAAlABEAAwAlAAYAAAAlABIAAwAlAAcAAAAmABAAAwAfAAcAAAAmABEAAwAlAAYAAAAmABIAAwAlAAcAAAAnABAAAwAeAAEAAAAnABEAAwAkAAYAAAAnABIAAwAkAAcAAAAoABAAAwAnAAEAAAAoABEAAwAnAAAAAAAoABIAAwAnAAEAAAApABAAAwAoAAEAAAApABEAAwAoAAAAAAApABIAAwAoAAEAAAAhAOv/AwAnAAAAAAAhAOz/AwAnAAEAAAAhAO3/AwAnAAAAAAAhAO7/AwAnAAEAAAAhAO//AwAoAAAAAGAhAPD/AwAnAAAAAGAiAOv/AwAoAAAAAAAiAOz/AwAoAAEAAAAiAO3/AwAoAAAAAAAiAO7/AwAoAAEAAAAiAO//AwAoAAEAAGAiAPD/AwAnAAEAAGAjAOv/AwAgAAEAAAAjAOz/AwAgAAEAAAAjAO3/AwAjAAYAAAAjAO7/AwAjAAcAAAAjAO//AwAgAAEAAAAjAPD/AwAgAAEAAAAkAOv/AwAfAAQAAAAkAOz/AwAfAAUAAAAkAO3/AwAlAAYAAAAkAO7/AwAlAAcAAAAkAO//AwAiAAQAAGAkAPD/AwAhAAQAAGAlAOv/AwApAAYAAAAlAOz/AwApAAYAAAAlAO3/AwAlAAYAAAAlAO7/AwAlAAcAAAAlAO//AwAoAAUAAGAlAPD/AwAoAAUAAGAmAOv/AwAfAAYAAAAmAOz/AwAfAAcAAAAmAO3/AwAlAAYAAAAmAO7/AwAlAAcAAAAmAO//AwAiAAYAAGAmAPD/AwAhAAYAAGAnAOv/AwAeAAEAAAAnAOz/AwAeAAEAAAAnAO3/AwAkAAYAAAAnAO7/AwAkAAcAAAAnAO//AwAeAAEAAAAnAPD/AwAeAAEAAAAoAOv/AwAnAAAAAAAoAOz/AwAnAAEAAAAoAO3/AwAnAAAAAAAoAO7/AwAnAAEAAAAoAO//AwAoAAAAAGAoAPD/AwAnAAAAAGApAOv/AwAoAAAAAAApAOz/AwAoAAEAAAApAO3/AwAoAAAAAAApAO7/AwAoAAEAAAApAO//AwAoAAEAAGApAPD/AwAnAAEAAGD+/+//AwAnAAAAAAD+//D/AwAnAAEAAAAMAPX/AwAnAAAAAGANAPX/AwAnAAEAAGAOAPX/AwAgAAEAAAAPAPX/AwAiAAQAAGAQAPX/AwAoAAUAAGARAPX/AwAiAAYAAGASAPX/AwAeAAEAAAATAPX/AwAnAAAAAGAUAPX/AwAnAAEAAGAMAPb/AwAoAAAAAGAMAPf/AwAnAAAAAGANAPb/AwAoAAEAAGANAPf/AwAnAAEAAGAOAPb/AwAgAAEAAAAOAPf/AwAgAAEAAAAPAPb/AwAeAAQAAGAPAPf/AwAiAAQAAGAQAPb/AwAoAAUAAGAQAPf/AwAoAAUAAGARAPb/AwAeAAYAAGARAPf/AwAiAAYAAGASAPb/AwAeAAEAAAASAPf/AwAeAAEAAAATAPb/AwAoAAAAAGATAPf/AwAnAAAAAGAUAPb/AwAoAAEAAGAUAPf/AwAnAAEAAGAMAPj/AwAoAAAAAGANAPj/AwAoAAEAAGAOAPj/AwAgAAEAAAAPAPj/AwAeAAQAAGAQAPj/AwAoAAUAAGARAPj/AwAeAAYAAGASAPj/AwAeAAEAAAATAPj/AwAoAAAAAGAUAPj/AwAoAAEAAGAMAOv/AwAnAAEAAAANAOv/AwAoAAEAAAAOAOv/AwAgAAEAAAAPAOv/AwAgAAQAAGAQAOv/AwAoAAUAAGARAOv/AwAgAAYAAGAMAOz/AwAnAAAAAAAMAO3/AwAnAAEAAAANAOz/AwAoAAAAAAANAO3/AwAoAAEAAAAOAOz/AwAgAAEAAAAOAO3/AwAgAAEAAAAPAOz/AwAgAAQAAGAPAO3/AwAfAAQAAGAQAOz/AwAoAAUAAGAQAO3/AwAoAAUAAGARAOz/AwAgAAYAAGARAO3/AwAfAAYAAGASAOz/AwAeAAEAAAASAO3/AwAeAAEA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AADR/+j/GAAGAAkAAADR/+f/GAALAAoAAADR/+b/GAALAAoAAADR/+X/GAALAAgAAADQ/+b/GAAJAAoAAADQ/+X/GAAGAAkAAADQ/+T/GAALAAgAAADS//T/GAACABsAAADS//P/GAAJAAsAAADS//L/GAAJAAoAAADS//H/GAAJAAoAAADS//D/GAAJAAoAAADS/+//GAAJAAoAAADS/+7/GAAJAAoAAADR//T/GAABABgAAADR//P/GAAJAAsAAADR//L/GAAJAAoAAADR//H/GAAJAAoAAADR//D/GAAFAAkAAADR/+//GAAIAAkAAADR/+7/GAAIAAkAAADQ//T/GAALAAoAAADQ//P/GAAGAAoAAADQ//L/GAAFAAkAAADQ//H/GAAIAAkAAADQ//D/GAAFAAgAAADP//T/GAAJAAoAAADP//P/GAAFAAkAAADP//L/GAAIAAgAAADP//H/GAADAAMAAADP//D/GAABAAsAAADO//T/GAAJAAoAAADO//P/GAAKAAgAAADF//X/GAAJAAoAAADF//T/AgAdAAUAAADF//P/GAAJAAoAAADF//L/GAAGAAkAAADF//H/GAALAAoAAADF//D/GAALAAgAAADE//X/GAAJAAoAAADE//T/AgAbAAUAAADE//P/GAAJAAoAAADE//L/GAAJAAoAAADE//H/GAAJAAoAAADE//D/GAAGAAkAAADE/+//GAALAAoAAADD//X/GAAJAAoAAADD//T/AgAdAAUAAADD//P/AgAZAAUAAADD//L/AgAcAAUAAADD//H/GAAJAAoAAADD//D/GAAJAAoAAADD/+//GAAJAAoAAADC//X/GAAJAAoAAADC//T/GAAJAAoAAADC//P/GAAJAAoAAADC//L/AgAbAAUAAADC//H/AgAdAAUAAADC//D/GAAJAAoAAADC/+//GAAJAAoAAADB//X/GAAJAAoAAADB//T/GAAJAAoAAADB//P/GAAJAAoAAADB//L/GAAJAAoAAADB//H/AgAaAAUAAADB//D/GAAJAAoAAADB/+//GAAJAAoAAADA//X/GAAIAAkAAADA//T/GAAFAAoAAADA//P/GAAJAAoAAADA//L/GAAJAAoAAADA//H/GAAJAAoAAADA//D/GAAJAAoAAADA/+//GAAJAAoAAAC///T/GAAIAAsAAAC///P/GAAFAAoAAAC///L/GAAJAAoAAAC///H/GAAJAAoAAAC///D/GAAJAAoAAAC//+//GAAJAAoAAADP//r/GAAJAAsAAADP//n/AgAZAAcAAADP//j/GAAKAAgAAADP//f/GAABABoAAADP//b/GAADABgAAADO//r/GAAIAAsAAADO//n/GAAFAAoAAADO//j/GAAGAAkAAADO//f/GAALAAgAAADO//b/GAACABsAAADN//f/GAAKAAgAAADN//b/GAACABsAAADM//f/GAAKAAgAAADM//b/GAACABoAAADL//r/GAALAAoAAADL//n/GAALAAoAAADL//j/GAAGAAoAAADL//f/GAAKAAgAAADL//b/GAACABsAAADK//r/AgAaAAcAAADK//n/GAAFAAkAAADK//j/GAAIAAkAAADK//f/GAAIAAgAAADK//b/GAACABsAAADJ//r/GAAJAAoAAADJ//n/GAAKAAgAAADJ//j/GAADABoAAADJ//f/GAAAABkAAADJ//b/GAABABgAAADK//X/GAAJAAsAAADK//T/GAAGAAkAAADK//P/GAALAAgAAADJ//X/GAAJAAsAAADJ//T/GAAJAAoAAADJ//P/GAAKAAgAAADI//X/GAAGAAoAAADI//T/GAAJAAoAAADI//P/GAAKAAgAAADH//X/GAAJAAoAAADH//T/GAAJAAoAAADN//P/GAAIAAgAAADL//P/GAAIAAMAAADG/+v/GAALAAsAAADF/+z/GAAKAAAAAADF/+v/GAAJAAsAAADZ/+//GAAJAAsAAADZ/+7/GAAJAAoAAADZ/+3/AgAcAAUAAADZ/+z/AgAZAAUAAADZ/+v/GAAJAAoAAADY/+//GAAGAAoAAADY/+7/AgAZAAUAAADY/+3/AgAZAAUAAADY/+z/AgAcAAUAAADY/+v/GAAJAAoAAADX/+//GAAJAAoAAADX/+7/AgAaAAUAAADX/+3/GAAJAAoAAADX/+z/GAAJAAoAAADX/+v/GAAJAAoAAADW/+//AgAdAAUAAADW/+7/AgAcAAUAAADW/+3/GAAJAAoAAADW/+z/GAAJAAoAAADW/+v/GAAJAAoAAADV/+//GAAJAAoAAADV/+7/GAAJAAoAAADV/+3/GAAJAAoAAADV/+z/GAAJAAoAAADV/+v/GAAJAAoAAADU/+//GAAJAAoAAADU/+7/GAAJAAoAAADU/+3/GAAJAAoAAADU/+z/GAAJAAoAAADU/+v/GAAJAAoAAADT/+//GAAJAAoAAADT/+7/GAAJAAoAAADT/+3/GAAJAAoAAADT/+z/GAAJAAoAAADT/+v/GAAJAAoAAADT//D/GAAJAAoAAADS/+3/GAAJAAoAAADR/+3/GAAFAAoAAADQ/+3/GAAIAAsAAADV//b/GAABAAAAAADV//X/GAADAAsAAADV//T/GAACABsAAADV//P/GAALAAsAAADV//L/GAAGAAoAAADi//D/GAALAAsAAADi/+//GAALAAoAAADi/+7/GAALAAoAAADh//L/GAALAAsAAADh//H/GAALAAoAAADh//D/GAAGAAoAAADh/+//GAAJAAoAAADh/+7/GAAJAAoAAADg//L/GAAIAAsAAADg//H/GAAFAAoAAADg//D/GAAJAAoAAADg/+//GAAJAAoAAADg/+7/GAAJAAoAAADg/+z/GAAJAAoAAADg/+v/GAAJAAoAAADg/+r/GAAJAAoAAADf/+z/GAAJAAoAAADf/+v/GAAJAAoAAADf/+r/GAAJAAoAAADe/+z/GAAJAAoAAADe/+v/GAAJAAoAAADe/+r/GAAJAAoAAADh/+z/GAAJAAoAAADh/+v/GAAJAAoAAADh/+r/GAAJAAoAAADi/+3/GAALAAoAAADi/+z/GAALAAkAAADh/+3/GAAJAAoAAADg/+3/GAAJAAoAAADf/+7/GAAJAAoAAADf/+3/GAAJAAoAAADd/+//GAAFAAoAAADc/+//GAAJAAsAAADb/+//GAAJAAsAAADa/+//GAAJAAsAAADX/+P/GAAIAAgAAADU/+P/GAADACsAAADT/+P/GAABACgAAADS/+z/GAAJAAoAAADS/+v/GAAJAAoAAADS/+P/GAAAAAoAAADR/+z/GAAJAAoAAADR/+v/GAAJAAoAAADQ/+z/GAAFAAoAAADP/+z/GAAIAAsAAADP/+b/AgAZAAUAAADP/+X/GAAJAAoAAADP/+T/GAAKAAgAAADO/+b/GAAJAAoAAADO/+X/AgAcAAUAAADO/+T/GAAGAAkAAADO/+P/GAALAAgAAADN/+b/AgAdAAUAAADM/+b/GAAJAAoAAADI/+b/GAAJAAoAAADE/+7/GAALAAgA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AADb/+z/AgAbAAUAAADb/+v/AgAcAAUAAADb/+f/GAAJAAoAAADb/+b/AgAaAAUAAADb/+X/AgAbAAUAAADb/+T/AgAbAAUAAADb/+P/GAAJAAoAAADb/+L/GAAJAAoAAADb/+H/AgAbAAUAAADb/+D/AgAaAAUAAADb/97/GAAJAAoAAADb/93/GAAJAAoAAADa/+7/GAAJAAoAAADa/+3/GAAJAAoAAADa/+z/AgAbAAUAAADa/+v/GAAJAAoAAADa/+r/GAAJAAoAAADa/+n/GAAJAAoAAADa/+j/GAAJAAoAAADa/+f/AgAaAAUAAADa/+b/AgAaAAUAAADa/+X/GAAJAAoAAADa/+T/GAAJAAoAAADa/+P/GAAJAAoAAADa/+L/GAAJAAoAAADa/+H/GAAJAAoAAADa/+D/AgAdAAUAAADZ/+L/GAAJAAoAAADZ/+H/GAAJAAoAAADZ/+D/GAAJAAoAAADZ/97/AgAZAAUAAADZ/93/GAAJAAoAAADZ/9v/GAAGAAkAAADZ/9r/GAALAAoAAADZ/9n/GAALAAgAAADY/+L/GAAIAAkAAADY/+H/GAAIAAkAAADY/+D/GAAIAAkAAADY/97/AgAaAAUAAADY/93/AgAaAAUAAADY/9v/GAAJAAoAAADY/9r/GAAJAAoAAADY/9n/GAAGAAkAAADY/9j/GAALAAgAAADX/+L/GAADABoAAADX/9v/GAAJAAoAAADX/9r/GAAJAAoAAADX/9n/GAAJAAoAAADX/9j/GAAGAAkAAADX/9f/GAALAAoAAADX/9b/GAALAAgAAADW/93/GAAJAAoAAADW/9r/AgAaAAUAAADW/9n/GAAJAAoAAADW/9j/GAAJAAoAAADW/9f/GAAJAAoAAADW/9b/GAAKAAgAAADV//H/GAAJAAoAAADV//D/GAAJAAoAAADV/9r/GAAJAAoAAADV/9n/GAAJAAoAAADV/9j/AgAbAAcAAADV/9f/GAAJAAoAAADV/9b/GAAKAAgAAADU//b/GAALAAsAAADU//X/GAAGAAgAAADU//T/GAACABsAAADU//P/GAAJAAsAAADU//L/GAAJAAoAAADU//H/GAAJAAoAAADU//D/GAAJAAoAAADU/+L/GAAGAAsAAADU/9r/AgAdAAUAAADU/9n/GAAJAAoAAADU/9j/GAAJAAoAAADU/9f/GAAJAAoAAADU/9b/GAAKAAgAAADT//f/GAALAAsAAADT//b/GAAGAAoAAADT//X/GAAKAAgAAADT//T/GAACABsAAADT//P/GAAJAAsAAADT//L/GAAJAAoAAADT//H/GAAJAAoAAADT/+L/GAAJAAsAAADT/9v/AgAcAAUAAADT/9r/GAAJAAoAAADT/9n/AgAbAAUAAADT/9j/AgAdAAUAAADT/9f/GAAJAAoAAADT/9b/GAAKAAgAAADS//n/GAALAAsAAADS//j/GAAHAAkAAADS//f/GAAEAAoAAADS//b/GAAIAAkAAADS//X/GAAIAAgAAADS/+L/GAAKAAsAAADS/+H/GAAJAAoAAADS/9n/AgAbAAUAAADS/9j/GAAJAAoAAADS/9f/GAAJAAoAAADS/9b/GAAKAAgAAADR//r/GAALAAsAAADR//n/GAAGAAoAAADR//j/GAAKAAgAAADR//f/GAADABoAAADR//b/GAAAABkAAADR//X/GAAAABkAAADR/+L/GAAJAAsAAADR/+H/GAAJAAoAAADR/9n/AgAbAAUAAADR/9j/GAAJAAoAAADR/9f/GAAFAAkAAADR/9b/GAAIAAgAAADQ//r/GAAJAAsAAADQ//n/AgAZAAUAAADQ//j/GAAKAAgAAADQ//f/GAACABsAAADQ//b/GAAAAAoAAADQ//X/GAAHAAoAAADQ/+L/GAAIAAsAAADQ/+H/GAAIAAkAAADQ/9n/GAAJAAoAAADQ/9j/GAAFAAkAAADQ/9f/GAAIAAgAAADP//X/GAAJAAsAAADP/9n/GAAFAAkAAADP/9j/GAAIAAgAAADO//v/GAAKAAAAAADO//X/GAAJAAsAAADO/9n/GAAIAAgAAADN//X/GAAIAAsAAADN//T/GAAIAAkAAADM//X/GAAAABkAAADM//T/GAAAABkAAADL//v/GAALAAsAAADL//X/GAALAAsAAADL//T/GAALAAgAAADK//v/GAAJAAsAAADJ//v/GAAJAAsAAADJ/9z/GAALAAsAAADJ/9v/GAALAAoAAADI//v/GAAIAAsAAADI//r/GAAFAAoAAADI//n/GAAKAAgAAADI//j/GAACABsAAADI//f/GAALAAsAAADI//b/GAALAAoAAADI/97/GAALAAsAAADI/93/GAALAAoAAADI/9z/GAAGAAoAAADI/9v/GAAJAAoAAADH//v/GAALAAAAAADH//r/GAAIAAsAAADH//n/GAAIAAgAAADH//j/GAACABsAAADH//f/GAAJAAsAAADH//b/GAAJAAoAAADH/97/GAAJAAsAAADH/93/GAAJAAoAAADH/9z/GAAJAAoAAADH/9v/GAAJAAoAAADG//r/GAADABoAAADG//n/GAAAABkAAADG//j/GAABABgAAADG//f/GAAJAAsAAADG//b/GAAJAAoAAADG//X/GAAJAAoAAADG/+H/GAADAAsAAADG/+D/GAAIABsAAADG/97/GAAJAAsAAADG/93/GAAJAAoAAADG/9z/GAAJAAoAAADG/9v/GAAJAAoAAADF//n/GAAAAAoAAADF//j/GAAHAAoAAADF//f/GAAGAAoAAADF//b/GAAJAAoAAADF/+H/GAAGAAsAAADF/+D/GAALAAgAAADF/97/GAAJAAsAAADF/93/GAAJAAoAAADF/9z/AgAaAAUAAADF/9v/GAAJAAoAAADE//n/GAADAAMAAADE//j/GAAJAAsAAADE//f/GAAJAAoAAADE//b/GAAJAAoAAADE/+L/GAALAAoAAADE/+H/GAAGAAoAAADE/+D/GAAGAAkAAADE/97/GAAGAAoAAADE/93/GAAJAAoAAADE/9z/AgAaAAUAAADE/9v/GAAJAAoAAADE/9r/GAAGAAkAAADE/9n/GAALAAoAAADE/9j/GAALAAgAAADD//j/GAAIAAsAAADD//f/GAAFAAoAAADD//b/GAAJAAoAAADD/+7/GAAGAAkAAADD/+3/GAALAAoAAADD/+z/GAALAAoAAADD/+v/GAAGAAoAAADD/+r/GAAJAAoAAADD/+n/AgAaAAUAAADD/+b/GAAJAAoAAADD/+X/GAAJAAoAAADD/+T/GAAJAAoAAADD/+P/AgAaAAUAAADD/+L/AgAcAAUAAADD/+H/AgAbAAUAAADD/+D/GAAJAAoAAADD/9//GAAJAAoAAADD/97/GAAJAAoAAADD/93/GAAJAAoAAADD/9z/AgAcAAUAAADD/9v/GAAJAAoAAADD/9r/GAAJAAoAAADD/9n/GAAJAAoAAADD/9j/GAAKAAgAAADC//f/GAAJAAsAAADC//b/GAAJAAoAAADC/+7/GAAJAAoAAADC/+3/GAAJAAoAAADC/+L/GAAJAAoAAADC/+H/GAAFAAkAAADC/9//GAAIAAkAAADC/97/GAAFAAoAAADC/93/GAAJAAoAAADC/9z/GAAJAAoAAADC/9v/GAAJAAoAAADC/9r/AgAbAAUAAADC/9n/GAAJAAoAAADC/9j/GAAGAAkAAADC/9f/GAALAAgAAADB//b/GAAJAAoAAADB/+7/GAAJAAoAAADB/+3/GAAJAAoAAADB/+L/GAAFAAkAAADB/+H/GAAIAAgAAADB/97/GAAIAAsAAADB/93/GAAFAAoAAADB/9z/GAAJAAoAAADB/9v/GAAJAAoAAADB/9r/AgAdAAUAAADB/9n/GAAJAAoAAADB/9j/GAAJAAoAAADB/9f/GAAJAAgAAADB/9b/GAAAAAgAAADA/+7/GAAJAAoAAADA/+3/GAAJAAoAAADA/+L/GAAKAAgAAADA/93/GAAJAAsAAADA/9z/GAAJAAoAAADA/9v/AgAaAAcAAADA/9r/AgAbAAUAAADA/9n/GAAJAAoAAADA/9j/GAAFAAkAAADA/9f/GAAIAAgAAAC//+7/GAAJAAoAAAC//+3/GAAJAAoAAAC//9v/AgAdAAUAAAC//9r/AgAbAAUAAAC//9n/GAAJAAoAAAC//9j/GAAKAAgAAAC+//P/GAAIAAsAAAC+//L/GAAIAAoAAAC+/+3/GAAJAAoAAAC+/9v/GAAJAAoAAAC+/9r/GAAJAAoAAAC+/9n/GAAFAAkAAAC+/9j/GAAIAAgAAAC9/+3/GAAIAAkAAAC9/9v/GAAJAAoAAAC9/9r/GAAFAAkAAAC9/9n/GAAIAAgAAAC8/9v/GAAFAAkAAAC8/9r/GAAIAAgAAAC7/+P/GAAIAAsAAAC7/93/GAAJAAoAAAC7/9z/GAAFAAkAAAC7/9v/GAAIAAgAAAC6/93/GAAIAAkAAAC6/9z/GAAIAAgAAAC5/9z/AgADAAgAAAC//+P/GAAIAAkAAAC//+L/GAAIAAgAAAC//9//GAALAAsAAAC//97/GAALAAoAAAC//93/GAAGAAoAAAC//9z/GAAJAAoAAAC+/+P/GAADAAIAAAC+/+D/GAALAAsAAAC+/9//GAAGAAoAAAC+/97/GAAJAAoAAAC+/93/AgAbAAUAAAC+/9z/AgAbAAUAAAC9/+L/GAALAAsAAAC9/+H/GAALAAoAAAC9/+D/GAAGAAoAAAC9/9z/GAAJAAoAAAC8/+P/GAALAAsAAAC8/+L/GAAGAAoAAAC8/+H/GAAJAAoAAAC8/93/GAAJAAoAAAC8/9z/GAAJAAoAAADD/+f/GAAJAAoAAADD/+j/GAAJAAoAAADC/+j/GAAJAAoAAADC/+f/AgAZAAcAAADE/+j/AgAcAAUAAADE/+f/AgAcAAUAAADF/+f/AgAZAAUAAADF/+j/GAAJAAoAAADG/+j/GAAJAAoAAADG/+f/AgAaAAUAAADH/+f/GAAJAAoAAADI/+f/GAAJAAoAAADJ/+b/GAAJAAoAAADK/+b/GAAJAAoAAADL/+b/GAAJAAoAAADJ/+f/GAAJAAoAAADH/+j/GAAGAAoAAADI/+j/GAAJAAsAAADJ/+j/GAAJAAsAAADL/+X/GAAKAAgAAADK/+X/GAAKAAgAAADJ/+X/GAAKAAgAAADI/+X/GAAJAAgAAADH/+X/GAAKAAgAAADG/+X/GAAKAAgAAADF/+X/GAAKAAgAAADE/9//GAALAAoAAADC/+D/GAAIAAkAAADI/9r/GAAJAAoAAADH/9r/GAAFAAkAAADF/9r/GAAKAAgAAADG/9r/GAAKAAgAAADH/9n/GAAIAAgAAADI/9n/GAAKAAgAAADJ/9n/GAAKAAgAAADJ/9r/GAAGAAoAAADK/9r/GAALAAsAAADK/9n/GAALAAgAAADW/+r/GAAFAAkAAADW/+j/GAAIAAgAAADW/+n/GAAIAAkAAADV/+r/GAAKAAgAAADS/+r/GAAJAAoAAADU/+r/GAAKAAgAAADT/+r/GAAGAAkAAADT/+n/GAALAAgAAADS/+n/GAAGAAkAAADS/+j/GAALAAgAAADH//P/GAAKAAgAAADG//P/GAAGAAkAAADG//L/GAALAAgAAADG//T/GAAJAAoAAADM//j/GAAJAAsAAADN//j/GAAFAAoAAADN//n/GAAIAAsAAADX//D/GAAGAAoAAADX//H/GAALAAsAAADX//L/GAABABgAAADW//P/GAABABgAAADW//T/GAADABoAAADW//H/GAAGAAoAAADW//D/GAAJAAoAAADY//D/GAALAAsAAADW//L/GAALAAsAAADf//D/GAAFAAoAAADf//H/GAAIAAsAAADe//D/GAAJAAsAAADd//D/GAAIAAsAAADe/+b/GAALAAgAAADe/+H/GAAJAAoAAADe/+L/GAAGAAoAAADf/+H/GAALAAoAAADf/+L/GAALAAsAAADe/+P/GAALAAsAAADa/9z/GAAGAAkAAADc/9z/GAALAAgAAADb/9z/GAAKAAgAAADa/9v/GAALAAgAAADR/93/GAAIAAsAAADO/93/GAAIAAsAAADP/93/GAALAAsAAADH/+r/GAAIAAAAAADI/+r/GAAFAAEAAADI/+n/GAAIAAAAAADJ/+n/GAALAAAAAADJ/+r/GAAGAAEAAADK/+r/GAAKAAAAAADL/+r/GAAKAAAAAADM/+r/GAAKAAAAAADN/+r/GAALAAAAAADJ/+v/GAAJAAIAAADI/+v/GAAJAAIAAADH/+v/GAAIAAEAAADK/+v/GAAJAAIAAADL/+v/GAAJAAIAAADM/+v/GAAJAAIAAADN/+v/GAAGAAEAAADO/+v/GAALAAAAAADO/+z/GAALAAIAAADN/+z/GAAJAAIAAADM/+z/GAAJAAIAAADL/+z/GAAJAAIAAADK/+z/GAAJAAIAAADJ/+z/GAAJAAIAAADI/+z/GAAJAAIAAADH/+z/GAAFAAEAAADG/+z/GAAKAAAAAADG/+3/GAAJAAIAAADF/+3/GAAFAAIAAADE/+z/GAAIAAAAAADE/+3/GAAIAAMAAADH/+3/GAAJAAIAAADI/+3/GAAJAAIAAADJ/+3/GAAJAAIAAADK/+3/GAAJAAIAAADL/+3/GAAJAAIAAADM/+3/GAAJAAIAAADN/+3/GAAJAAIAAADO/+3/GAAGAAEAAADP/+3/GAALAAAAAADP/+7/GAAGAAEAAADQ/+7/GAALAAAAAADO/+7/GAAJAAIAAADN/+7/GAAJAAIAAADM/+7/GAAJAAIAAADL/+7/GAAJAAIAAADK/+7/GAAJAAIAAADJ/+7/GAAJAAIAAADI/+7/GAAJAAIAAADH/+7/GAAJAAIAAADG/+7/GAAJAAIAAADF/+7/GAAIAAEAAADF/+//GAAIAAMAAADG/+//GAAFAAIAAADH/+//GAAJAAIAAADI/+//GAAJAAIAAADJ/+//GAAJAAIAAADK/+//GAAJAAIAAADL/+//GAAJAAIAAADM/+//GAAJAAIAAADN/+//GAAJAAIAAADO/+//GAAGAAIAAADP/+//GAAJAAMAAADQ/+//GAALAAMAAADO//D/GAALAAIAAADN//D/GAAJAAIAAADM//D/GAAJAAIAAADL//D/GAAJAAIAAADK//D/GAAJAAIAAADJ//D/GAAJAAIAAADI//D/GAAJAAIAAADH//D/GAAJAAIAAADG//D/GAAIAAEAAADG//H/GAAIAAMAAADH//H/GAAFAAIAAADI//H/GAAJAAIAAADJ//H/GAAJAAIAAADK//H/GAAJAAIAAADL//H/GAAJAAIAAADM//H/GAAJAAIAAADN//H/GAAJAAIAAADO//H/GAALAAEAAADO//L/GAALAAMAAADN//L/GAAJAAMAAADM//L/GAAGAAIAAADL//L/GAAFAAIAAADK//L/GAAJAAMAAADJ//L/GAAJAAMAAADI//L/GAAJAAMAAADH//L/GAAIAAMAAADM//P/GAALAAMAAAC9/+P/GAABAAAAAAC+/+L/GAAAAAEAAAC+/+H/GAABAAAAAAC//+H/GAAFAAMAAAC//+D/GAAIAAAAAADA/+H/GAALAAMAAADA/+D/GAAJAAEAAADA/9//GAAEAAEAAADA/97/GAAAAAAAAADB/9//GAALAAAAAADB/+D/GAALAAMAAAC8/+T/GAACAAMAAAC7/+T/GAAGAAAAAAC6/+T/GAAGAAEAAAC6/+P/GAALAAIAAAC6/+X/GAAJAAIAAAC7/+b/GAALAAIAAAC6/+b/GAAJAAIAAAC7/+f/GAALAAMAAAC7/+j/GAABAAsAAAC7/+n/GAALAAAAAAC6/+n/GAAGAAEAAAC6/+j/GAALAAIAAAC6/+f/GAAGAAIAAAC7/+r/GAALAAIAAAC6/+r/GAAJAAIAAAC7/+v/GAAGAAEAAAC8/+v/GAALAAAAAAC6/+v/GAAJAAIAAAC7/+z/GAAFAAIAAAC8/+z/GAALAAIAAAC8/+3/GAALAAIAAAC8/+7/GAALAAMAAAC8/+//GAABAAsAAAC8//D/GAAFAAAAAAC9//D/GAALAAAAAAC9//H/GAALAAMAAAC9//L/GAABAAsAAAC9//P/GAALAAAAAAC9//T/GAAKAAIAAAC+//T/GAALAAAAAAC+//X/GAAKAAIAAAC///X/GAALAAAAAAC///b/GAAHAAIAAADA//b/GAAIAAkAAADA//f/GAAEAAoAAADB//f/GAAJAAsAAADB//j/GAAIAAAAAADC//j/GAALAAAAAADC//n/GAAKAAIAAADD//n/GAAGAAAAAADE//r/GAABABgAAADF//r/GAACABsAAADF//v/GAAIAAAAAADG//v/GAAKAAAAAADH//z/AwAoAAAAAADI//z/AwAnAAAAAADJ//z/AwAoAAAAAADK//z/AwAnAAAAAADL//z/AwAoAAAAAADG//z/AwAnAAAAAADE//v/GAADABoAAADD//r/GAALAAMAAAC9//X/GAAIAAMAAAC+//b/GAAIAAMAAAC///f/GAAAAAIAAADA//j/GAAAAAoAAADB//n/GAAIAAMAAADC//r/GAAIAAMAAADD//v/GAACABsAAADF//z/AwAoAAAAAAC8//T/GAAIAAMAAAC8//P/GAAEAAEAAAC8//L/GAAAAAEAAAC8//H/GAAEAAIAAAC7//D/GAABAAIAAAC7/+//GAAAAAEAAAC7/+7/GAAEAAIAAAC7/+3/GAAIAAEAAAC6/+z/GAAJAAMAAADF/9//GAABABoAAADG/9//GAAFABgAAADH/9//GAAKABgAAADI/9//GAAKABgAAADH/+D/GAAFABoAAADI/+D/GAAJABoAAADI/+H/GAAJABoAAADH/+H/GAAIABkAAADH/+L/GAAFABkAAADG/+L/GAAKABgAAADF/+L/GAAIABgAAADF/+P/GAAIABkAAADF/+T/GAAIABsAAADG/+T/GAAJABsAAADG/+P/GAAJABoAAADH/+T/GAALABsAAADH/+P/GAAGABoAAADI/+L/GAAJABoAAADI/+P/GAAJABsAAADJ/+P/GAAFABoAAADJ/+L/GAAJABoAAADJ/+T/GAAIABsAAADI/+T/GAAAAAgAAADK/+T/GAAJABsAAADK/+P/GAAJABoAAADJ/9//GAAFABkAAADJ/97/GAAIABkAAADJ/93/GAAIABgAAADK/93/GAAFABkAAADK/9z/GAAIABkAAADL/93/GAAJABoAAADL/9z/GAAGABkAAADL/9v/GAAHABkAAADK/9v/GAAIABgAAADL/9r/GAAAABkAAADL/9n/GAAAABgAAADM/9z/GAAHABkAAADM/9v/GAAAABgAAADM/93/GAAGABkAAADN/93/GAALABgAAADN/97/GAAGABkAAADM/97/GAAJABoAAADL/97/GAAJABoAAADK/97/GAAJABoAAADJ/+D/GAAJABoAAADK/+D/GAAJABoAAADK/9//GAAJABoAAADL/+D/GAAJABoAAADL/9//GAAJABoAAADM/9//GAAJABoAAADN/9//GAAJABoAAADO/97/GAAKABgAAADO/9//GAAJABoAAADM/+D/GAAJABoAAADJ/+H/GAAJABoAAADK/+L/GAAJABoAAADL/+H/GAAGABoAAADK/+H/GAAJABoAAADN/+D/GAAJABoAAADN/+H/GAAJABsAAADM/+H/GAAJABsAAADL/+L/GAALABoAAADL/+P/GAALABoAAADL/+T/GAALABsAAADO/+D/GAAJABoAAADO/+H/GAAFABoAAADP/+H/GAALABoAAADP/+D/GAALABoAAADP/9//GAALABoAAADP/97/GAAGABgAAADQ/97/GAACABoAAADQ/93/GAAAABgAAADR/97/GAADABoAAADP/+L/GAALABsAAADO/+L/GAAIABsAAADP/+P/GAABACsAAADQ/+P/GAACACsAAADR/+P/GAADACgAAADR/+T/GAABACoAAADS/+T/GAACACgAAADT/+X/GAAIAAgAAADT/+b/GAAIAAsAAADS/+b/GAAAACkAAADS/+X/GAAAACkAAADS/+f/GAABACoAAADT/+f/GAADACgAAADU/+f/GAAAAAoAAADU/+b/GAAKAAsAAADU/+X/GAAFAAkAAADU/+T/GAAIAAgAAADV/+T/GAALAAgAAADV/+P/GAACABsAAADV/+L/GAADAAsAAADW/+L/GAABABgAAADW/+H/GAAAAAoAAADX/+H/GAAAABkAAADX/+D/GAAAABkAAADW/+P/GAADABkAAADW/+X/GAAAACgAAADW/+b/GAAAACkAAADW/+f/GAADACoAAADV/+f/GAABACgAAADV/+b/GAALAAsAAADV/+X/GAALAAoAAADV/+j/GAALACgAAADU/+j/GAAFACgAAADV/+n/GAALACsAAADU/+n/GAAIACsAAADT/+j/GAABACoAAADM//n/GAAAAAAAAADM//r/GAAEAAEAAADN//r/GAALAAAAAADM//v/GAAIAAEAAADM//z/AwAnAAAAAADN//z/AwAoAAAAAADN//v/GAAGAAEAAADO//z/AwAnAAAAAADP//z/AwAoAAAAAADQ//z/AwAnAAAAAADQ//v/GAAKAAAAAADR//v/GAAKAAAAAADP//v/GAAKAAAAAADS//v/GAAHAAEAAADT//v/GAABABoAAADR//z/AwAoAAAAAADS//z/AwAnAAAAAADU//v/GAAFABsAAADU//r/GAAIABkAAADT//r/GAADAAIAAADS//r/GAABAAAAAADT//n/GAAAAAEAAADU//n/GAAIABgAAADU//j/GAADAAIAAADT//j/GAABAAAAAADV//j/GAAIABgAAADV//f/GAADAAIAAADW//f/GAAAABkAAADW//b/GAAFAAMAAADU//f/GAABAAAAAADW//j/GAAHABkAAADX//f/GAAAAAEAAADX//b/GAAHAAIAAADX//X/GAAJAAEAAADX//T/GAAIAAAAAADY//T/GAAHAAEAAADW//X/GAAIAAAAAADY//P/GAAAAAAAAADY//X/GAALAAMAAADY//b/GAAIAAgAAADX//j/GAAAAAEAAADW//n/GAALABoAAADV//n/GAAFABkAAADV//r/GAAJABoAAADW//r/GAAGABkAAADX//r/GAAKABgAAADY//r/GAAKABgAAADZ//r/GAAKABgAAADa//r/GAAKABgAAADb//r/GAAGABgAAADc//r/GAACABoAAADd//r/GAAFABgAAADe//r/AgAfAAkAAADj//b/GAAJAAIAAADj//X/GAAJAAIAAADj//T/GAAFAAEAAADj//P/GAAIAAEAAADj//L/GAAIAAEAAADj//H/GAAEAAEAAADk//H/GAAKAAAAAADk//D/GAAFABsAAADk/+//GAAIABkAAADk/+7/GAAIABgAAADl/+7/GAAHABkAAADl/+3/GAAAABkAAADl/+z/GAAAABkAAADl/+v/GAAAABkAAADl/+r/GAAHABoAAADl/+n/GAALABoAAADl/+j/GAALABoAAADk/+j/GAAIABkAAADk/+f/GAAKABoAAADk/+b/GAALABoAAADk/+X/GAALABoAAADk/+T/GAALABoAAADk/+P/GAALABoAAADk/+L/GAALABoAAADk/+H/GAALABgAAADk/+D/GAAFAAMAAADk/9//GAAIAAEAAADk/97/GAAIAAEAAADk/93/GAAFAAAAAADk/9z/GAALABsAAADk/9v/GAALABoAAADk/9r/GAALABoAAADk/9n/GAAGABoAAADk/9j/GAAJABoAAAC5//P/AgADAAgAAAC5//L/AgADAAgAAAC5//H/AgADAAgAAAC5//D/AgADAAgAAAC5/+//AgADAAgAAAC5/+7/AgADAAgAAAC5/+3/AgADAAgAAAC5/+z/AgADAAgAAAC5/+v/AgADAAgAAAC5/+r/AgADAAgAAAC5/+n/AgADAAgAAAC5/+j/AgADAAgAAAC5/+f/AgADAAgAAAC5/+b/AgADAAgAAAC5/+X/AgADAAgAAAC5/+T/AgADAAgAAAC5/93/AgADAAgAAAC5/9v/AgADAAgAAAC5/9r/AgADAAgAAADl/9T/GAADAAAAAADk/9T/GAACAAMAAADj/9T/GAACAAMAAADi/9T/GAACAAMAAADh/9T/GAACAAMAAADg/9T/GAACAAMAAADf/9T/GAACAAMAAADe/9T/GAACAAMAAADd/9T/GAACAAMAAADc/9T/GAACAAMAAADb/9T/GAACAAMAAADa/9T/GAACAAMAAADZ/9T/GAACAAMAAADY/9T/GAAGAAAAAADX/9T/GAAKAAAAAADW/9T/GAAKAAAAAADV/9T/GAAKAAAAAADU/9T/GAAKAAAAAADT/9T/GAAKAAAAAADS/9T/GAAKAAAAAADR/9T/GAAKAAAAAADQ/9T/GAAFAAAAAADP/9T/GAACAAMAAADO/9T/GAACAAMAAADN/9T/GAACAAMAAADM/9T/GAACAAMAAADL/9T/GAACAAMAAADK/9T/GAACAAMAAADJ/9T/GAACAAMAAADI/9T/GAACAAMAAADH/9T/GAACAAMAAADG/9T/GAACAAMAAADF/9T/GAACAAMAAADE/9T/GAACAAMAAADD/9T/GAACAAMAAADC/9T/GAAGAAAAAADB/9T/GAAKAAAAAADA/9T/GAAFAAAAAAC//9T/GAACAAMAAAC+/9T/GAACAAMAAAC9/9T/GAACAAMAAADl/9f/GAAJABoAAADl/9j/GAAGABoAAADl/9n/GAALABsAAADl/9r/GAAIAAAAAADl/9v/GAAIAAEAAADl/9z/GAAEAAIAAADl/93/GAAHAAEAAADl/97/GAALAAIAAADl/9//GAALAAIAAADl/+D/GAAHAAIAAADl/+H/GAAAAAEAAADl/+L/GAAAAAEAAADl/+P/GAAAAAEAAADl/+T/GAAAAAEAAADl/+X/GAAAAAEAAADl/+b/GAABAAIAAADl/+f/GAALABgAAADl/+//GAAGABkAAADl//H/GAALAAAAAADl//L/GAAGAAEAAADl//P/GAAJAAIAAADl//T/GAAJAAIAAADl//X/GAAJAAIAAADl/9b/GAAKABgAAADl/9X/GAABAAIAAAC5/9n/AgADAAgAAAC5//z/AwAnAAAAAAC5//v/AgADAAgAAAC5//r/AgADAAgAAAC5//n/AgADAAgAAAC5//j/AgADAAgAAAC5//f/AgADAAgAAAC5//b/AgADAAgAAAC5//X/AgADAAgAAAC5//T/AgADAAgAAADc//z/AwAnAAAAAADb//z/AwAoAAAAAADa//z/AwAnAAAAAADZ//z/AwAoAAAAAADY//z/AwAnAAAAAADX//z/AwAoAAAAAADW//z/AwAnAAAAAADV//z/AwAoAAAAAADU//z/AwAnAAAAAADT//z/AwAoAAAAAADE//z/AwAnAAAAAADD//z/AwAoAAAAAADC//z/AwAnAAAAAADB//z/AwAoAAAAAADA//z/AwAnAAAAAAC///z/AwAoAAAAAAC+//z/AwAoAAAAAAC9//z/AwAnAAAAAAC8//z/AwAoAAAAAAC7//z/AwAnAAAAAAC6//z/AwAoAAAAAADC//v/GAACABsAAADB//v/GAAGABsAAADB//r/GAALABgAAADA//v/GAAFABsAAADA//r/GAAEABkAAADA//n/GAADABgAAAC///v/GAACABsAAAC///r/GAADAAsAAAC///n/GAABABoAAAC///j/GAADABgAAAC+//v/GAACABsAAAC+//r/GAAGAAsAAAC+//n/GAALAAgAAAC+//j/GAABABoAAAC+//f/GAADABgAAAC9//v/GAACABsAAAC9//r/GAAJAAsAAAC9//n/GAAGAAkAAAC9//j/GAALAAgAAAC9//f/GAABABoAAAC9//b/GAADABgAAAC8//v/GAACABsAAAC8//r/GAAJAAsAAAC8//n/GAAJAAoAAAC8//j/GAAGAAkAAAC8//f/GAALAAgAAAC8//b/GAABABoAAAC8//X/GAADABgAAAC7//v/GAACABsAAAC7//r/GAAIAAsAAAC7//n/GAAIAAkAAAC7//j/GAAIAAkAAAC7//f/GAAEAAkAAAC7//b/GAAAAAgAAAC7//X/GAAGABsAAAC7//T/GAALABoAAAC7//P/GAALABoAAAC7//L/GAALABoAAAC7//H/GAALABgAAAC6//v/GAABABoAAAC6//r/GAAAABkAAAC6//n/GAAAABkAAAC6//j/GAAAABkAAAC6//f/GAAAABkAAAC6//b/GAAAABkAAAC6//X/GAAEABoAAAC6//T/GAAIABkAAAC6//P/GAAIABkAAAC6//L/GAAIABkAAAC6//H/GAAEABkAAAC6//D/GAAAABkAAAC6/+//GAAAABkAAAC6/+7/GAAAABkAAAC6/+3/GAAAABkAAADd//v/GAAIABsAAADc//v/GAAAAAAAAADb//v/GAALABsAAADa//v/GAAJABsAAADZ//v/GAAJABsAAADY//v/GAAJABsAAADX//v/GAAJABsAAADW//v/GAAJABsAAADV//v/GAAJABsAAADi//f/GAAJAAMAAADi//b/GAAJAAIAAADi//X/GAAJAAIAAADi//T/GAAKAAAAAADi//P/GAADABoAAADi//L/GAAAABkAAADi//H/GAAAABkAAADh//T/GAAIAAAAAADh//P/GAACABsAAADg//X/GAALAAsAAADg//T/GAALAAgAAADg//P/GAACABsAAADf//b/GAAIAAAAAADf//X/GAAJAAsAAADf//T/GAAKAAgAAADf//P/GAABABoAAADf//L/GAADABgAAADe//n/AgAfAAkAAADe//j/AgAfAAwAAADe//f/GAALAAsAAADe//b/GAALAAoAAADe//X/GAAGAAoAAADe//T/GAAGAAkAAADe//P/GAALAAgAAADe//L/GAABABoAAADe//H/GAADABgAAADd//n/GAADAAMAAADd//j/GAALAAsAAADd//f/GAAGAAoAAADd//b/GAAJAAoAAADd//X/GAAJAAoAAADd//T/GAAJAAoAAADd//P/GAAGAAkAAADd//L/GAALAAgAAADd//H/GAACABsAAADc//n/GAACAAMAAADc//j/GAAJAAsAAADc//f/GAAJAAoAAADc//b/GAAJAAoAAADc//X/GAAJAAoAAADc//T/GAAJAAoAAADc//P/GAAJAAoAAADc//L/GAAKAAgAAADc//H/GAABABoAAADc//D/GAADABgAAADb//n/GAACAAMAAADb//j/GAAJAAsAAADb//f/GAAJAAoAAADb//b/GAAJAAoAAADb//X/GAAJAAoAAADb//T/GAAJAAoAAADb//P/GAAJAAoAAADb//L/GAAGAAkAAADb//H/GAALAAgAAADb//D/GAACABsAAADa//n/GAACAAMAAADa//j/GAAJAAsAAADa//f/GAAJAAoAAADa//b/GAAJAAoAAADa//X/GAAJAAoAAADa//T/GAAJAAoAAADa//P/GAAFAAkAAADa//L/GAAIAAkAAADa//H/GAAIAAgAAADa//D/GAACABsAAADZ//n/GAACAAMAAADZ//j/GAAJAAsAAADZ//f/GAAJAAoAAADZ//b/GAAFAAkAAADZ//X/GAAIAAkAAADZ//T/GAAIAAkAAADZ//P/GAAIAAgAAADZ//L/GAALABsAAADZ//H/GAAHABkAAADZ//D/GAABABgAAADY//n/GAACAAMAAADY//j/GAAIAAsAAADY//f/GAAIAAkAAADY//L/GAAFABsAAADY//H/GAAIABgAAADX//n/GAABAAIAAADX//P/GAADABoAAADk//X/GAAJAAIAAADk//T/GAAJAAIAAADk//P/GAAJAAIAAADk//L/GAAJAAIAAADk/+3/GAADAAIAAADk/+z/GAAAAAEAAADk/+v/GAADAAAAAADk/+r/GAAIABsAAADk/+n/GAAIABkAAADj//D/GAAAAAEAAADj/+//GAAAAAEAAADj/+7/GAAAAAEAAADj/+3/GAABAAAAAADj/+z/GAADAAsAAADj/+v/GAABAAIAAADj/+r/GAAAAAEAAADj/+n/GAAAAAEAAADj/+j/GAADAAAAAADj/+f/GAAIABsAAADj/+b/GAAFABoAAADj/+X/GAAJABoAAADj/+T/GAAJABoAAADj/+P/GAAJABoAAADj/+L/GAAFABkAAADj/+H/GAAIABgAAADj/+D/GAACAAMAAADi/+j/GAABAAIAAADi/+f/GAADAAAAAADi/+b/GAAJABsAAADi/+X/GAAJABoAAADi/+T/GAAJABoAAADi/+P/GAAJABoAAADi/+L/GAAKABgAAADi/+H/GAADAAIAAADi/+D/GAABAAAAAADh/+f/GAACAAMAAADh/+b/GAAIABsAAADh/+X/GAAFABoAAADh/+T/GAAFABkAAADh/+P/GAAIABkAAADh/+L/GAAIABgAAADh/+H/GAACAAMAAADg/+f/GAABAAIAAADg/+b/GAADAAAAAADg/+X/GAAIABsAAADg/+T/GAAIABgAAADg/+P/GAADAAIAAADg/+L/GAAAAAEAAADg/+H/GAABAAAAAADf/+b/GAABAAIAAADf/+X/GAAHAAIAAADf/+T/GAAHAAEAAADf/+P/GAABAAAAAADe/+X/GAAIAAMAAADe/+T/GAAIAAAAAADk/9f/GAAJABoAAADk/9b/GAAGABkAAADk/9X/GAALABgAAADj/93/GAACAAMAAADj/9z/GAAIABsAAADj/9v/GAAFABoAAADj/9r/GAAJABoAAADj/9n/GAAJABoAAADj/9j/GAAJABoAAADj/9f/GAAJABoAAADj/9b/GAAJABoAAADj/9X/GAAKABgAAADi/93/GAABAAIAAADi/9z/GAADAAAAAADi/9v/GAAJABsAAADi/9r/GAAJABoAAADi/9n/GAAJABoAAADi/9j/GAAJABoAAADi/9f/GAAJABoAAADi/9b/GAAJABoAAADi/9X/GAAKABgAAADh/9z/GAACAAMAAADh/9v/GAAJABsAAADh/9r/GAAJABoAAADh/9n/GAAJABoAAADh/9j/GAAJABoAAADh/9f/GAAJABoAAADh/9b/GAAJABoAAADh/9X/GAAKABgAAADg/9z/GAACAAMAAADg/9v/GAAJABsAAADg/9r/GAAJABoAAADg/9n/GAAJABoAAADg/9j/GAAJABoAAADg/9f/GAAJABoAAADg/9b/GAAJABoAAADg/9X/GAAKABgAAADf/9z/GAACAAMAAADf/9v/GAAJABsAAADf/9r/GAAJABoAAADf/9n/GAAJABoAAADf/9j/GAAJABoAAADf/9f/GAAJABoAAADf/9b/GAAJABoAAADf/9X/GAAKABgAAADe/9z/GAACAAMAAADe/9v/GAAIABsAAADe/9r/GAAFABoAAADe/9n/GAAJABoAAADe/9j/GAAJABoAAADe/9f/GAAJABoAAADe/9b/GAAJABoAAADe/9X/GAAKABgAAADd/9z/GAABAAIAAADd/9v/GAADAAAAAADd/9r/GAAJABsAAADd/9n/GAAJABoAAADd/9j/GAAJABoAAADd/9f/GAAJABoAAADd/9b/GAAJABoAAADd/9X/GAAKABgAAADc/9v/GAACAAMAAADc/9r/GAAIABsAAADc/9n/GAAFABoAAADc/9j/GAAJABoAAADc/9f/GAAJABoAAADc/9b/GAAJABoAAADc/9X/GAAKABgAAADb/9v/GAABAAIAAADb/9r/GAADAAAAAADb/9n/GAAIABsAAADb/9j/GAAIABkAAADb/9f/GAAFABoAAADb/9b/GAAJABoAAADb/9X/GAAKABgAAADa/9r/GAABAAIAAADa/9n/GAAAAAEAAADa/9j/GAADAAAAAADa/9f/GAAIABsAAADa/9b/GAAFABoAAADa/9X/GAAKABgAAADZ/9j/GAABAAIAAADZ/9f/GAADAAAAAADZ/9b/GAAIABsAAADZ/9X/GAAIABgAAADY/9f/GAABAAIAAADY/9b/GAAAAAEAAADY/9X/GAAHAAIAAADX/9X/GAAJAAMAAADW/9X/GAAJAAMAAADV/9X/GAAJAAMAAADU/9X/GAAJAAMAAADT/9X/GAAJAAMAAADS/9X/GAAJAAMAAADR/9X/GAAJAAMAAADQ/9b/GAADAAIAAADQ/9X/GAAEAAIAAADP/9f/GAADAAIAAADP/9b/GAABAAAAAADP/9X/GAADABoAAADO/9j/GAADAAIAAADO/9f/GAABAAAAAADO/9b/GAALABsAAADO/9X/GAAGABgAAADN/9r/GAALAAMAAADN/9n/GAALAAIAAADN/9j/GAAGAAAAAADN/9f/GAALABsAAADN/9b/GAAGABoAAADN/9X/GAAKABgAAADM/9r/GAAIAAMAAADM/9n/GAAIAAEAAADM/9j/GAAFAAAAAADM/9f/GAAJABsAAADM/9b/GAAJABoAAADM/9X/GAAKABgAAADL/9j/GAACAAMAAADL/9f/GAAJABsAAADL/9b/GAAJABoAAADL/9X/GAAKABgAAADK/9j/GAACAAMAAADK/9f/GAAJABsAAADK/9b/GAAJABoAAADK/9X/GAAKABgAAADJ/9j/GAACAAMAAADJ/9f/GAAJABsAAADJ/9b/GAAJABoAAADJ/9X/GAAKABgAAADI/9j/GAACAAMAAADI/9f/GAAJABsAAADI/9b/GAAJABoAAADI/9X/GAAKABgAAADH/9j/GAACAAMAAADH/9f/GAAJABsAAADH/9b/GAAJABoAAADH/9X/GAAKABgAAADG/9n/GAALAAMAAADG/9j/GAAGAAAAAADG/9f/GAAIABsAAADG/9b/GAAFABoAAADG/9X/GAAKABgAAADF/9n/GAAIAAMAAADF/9j/GAAEAAEAAADF/9f/GAADAAAAAADF/9b/GAAJABsAAADF/9X/GAAKABgAAADE/9f/GAACAAMAAADE/9b/GAAIABsAAADE/9X/GAAFABgAAADD/9f/GAABAAIAAADD/9b/GAADAAAAAADD/9X/GAABABoAAADC/9b/GAABAAIAAADC/9X/GAAHAAIAAADB/9X/GAAJAAMAAADA/9b/GAADAAIAAADA/9X/GAAEAAIAAAC//9f/GAADAAIAAAC//9b/GAABAAAAAAC//9X/GAADABoAAAC+/9f/GAACAAMAAAC+/9b/GAALABsAAAC+/9X/GAAGABgAAAC9/9j/GAADAAIAAAC9/9f/GAABAAAAAAC9/9b/GAAJABsAAAC9/9X/GAAKABgAAAC8/9n/GAADAAIAAAC8/9j/GAABAAAAAAC8/9f/GAALABsAAAC8/9b/GAAGABoAAAC8/9X/GAAKABgAAAC7/9r/GAADAAIAAAC7/9n/GAABAAAAAAC7/9j/GAALABsAAAC7/9f/GAAGABoAAAC6/9v/GAALAAMAAAC6/9r/GAAGAAAAAAC6/9n/GAADABoAAAC6/9j/GAAEABoAAAC5/9T/AgADAAgAAAC6/9T/GAACAAMAAAC5/9b/AgADAAgAAAC7/9T/GAACAAMAAAC5/9f/AgADAAgAAAC5/9j/AgADAAgAAAC6/9f/GAAIABkAAAC6/9b/GAAIABkAAAC7/9b/GAAJABoAAAC6/9X/GAAIABgAAAC7/9X/GAAKABgAAAC8/9T/GAACAAMAAAC5/+P/AgADAAgAAAC7/+L/GAAIAAkAAAC7/+H/GAAFAAoAAAC6/+H/GAAIAAsAAAC6/+D/GAAIAAkAAAC7/+D/GAAJAAoAAAC8/+D/GAAJAAoAAAC7/9//GAAJAAoAAAC8/9//GAAJAAoAAAC9/9//GAAJAAoAAAC7/97/GAAJAAoAAAC8/97/GAAJAAoAAAC9/97/GAAJAAoAAAC5/+D/AgADAAgAAAC9/93/AgAZAAUAAADf//n/AgAcAAoAAADf//j/AgAeAAoAAADg//j/AgAeAAoAAADg//f/GAAJAAMAAADg//b/GAAKAAAAAADh//f/GAAKAAMAAADh//b/GAAFAAEAAADh//X/GAAIAAEAAADf//f/GAAIAAMAAADf//r/AgAcAAoAAADg//n/AgAcAAoAAADh//j/AgAeAAoAAADe//v/AgAfAAsAAADk//b/GAAGAAIAAAC5/83/DAALAJcAAAC5/87/DAALAJgAAAC5/8//DAALAJkAAAC5/9D/DAALAJoAAAC5/9H/DAALAJsAAAC5/9L/DAALAJwAAAC5/9P/DAALAJ0AAAC6/83/DAAMAJcAAAC6/87/DAAMAJgAAAC6/8//DAAMAJkAAAC6/9D/DAAMAJoAAAC6/9H/DAAMAJsAAAC6/9L/DAAMAJwAAAC6/9P/DAAMAJ0AAAC7/83/DAANAJcAAAC7/87/DAANAJgAAAC7/8//DAANAJkAAAC7/9D/DAANAJoAAAC7/9H/DAANAJsAAAC7/9L/DAANAJwAAAC7/9P/DAANAJ0AAAC8/83/DAAOAJcAAAC8/87/DAAOAJgAAAC8/8//DAAOAJkAAAC8/9D/DAAOAJoAAAC8/9H/DAAOAJsAAAC8/9L/DAAOAJwAAAC8/9P/DAAOAJ0AAAC9/83/DAAPAJcAAAC9/87/DAAPAJgAAAC9/8//DAAPAJkAAAC9/9D/DAAPAJoAAAC9/9H/DAAPAJsAAAC9/9L/DAAPAJwAAAC9/9P/DAAPAJ0AAAC+/83/DAAQAJcAAAC+/87/DAAQAJgAAAC+/8//DAAQAJkAAAC+/9D/DAAQAJoAAAC+/9H/DAAQAJsAAAC+/9L/DAAQAJwAAAC+/9P/DAAQAJ0AAAC//83/DAARAJcAAAC//87/DAARAJgAAAC//8//DAARAJkAAAC//9D/DAARAJoAAAC//9H/DAARAJsAAAC//9L/DAARAJwAAAC//9P/DAARAJ0AAADA/83/DAALAJcAAADA/87/DAALAJgAAADA/8//DAALAJkAAADA/9D/DAALAJoAAADA/9H/DAALAJsAAADA/9L/DAALAJwAAADA/9P/DAALAJ0AAADB/83/DAAMAJcAAADB/87/DAAMAJgAAADB/8//DAAMAJkAAADB/9D/DAAMAJoAAADB/9H/DAAMAJsAAADB/9L/DAAMAJwAAADB/9P/DAAMAJ0AAADC/83/DAANAJcAAADC/87/DAANAJgAAADC/8//DAANAJkAAADC/9D/DAANAJoAAADC/9H/DAANAJsAAADC/9L/DAANAJwAAADC/9P/DAANAJ0AAADD/83/DAAOAJcAAADD/87/DAAOAJgAAADD/8//DAAOAJkAAADD/9D/DAAOAJoAAADD/9H/DAAOAJsAAADD/9L/DAAOAJwAAADD/9P/DAAOAJ0AAADE/83/DAAPAJcAAADE/87/DAAPAJgAAADE/8//DAAPAJkAAADE/9D/DAAPAJoAAADE/9H/DAAPAJsAAADE/9L/DAAPAJwAAADE/9P/DAAPAJ0AAADF/83/DAAQAJcAAADF/87/DAAQAJgAAADF/8//DAAQAJkAAADF/9D/DAAQAJoAAADF/9H/DAAQAJsAAADF/9L/DAAQAJwAAADF/9P/DAAQAJ0AAADG/83/DAARAJcAAADG/87/DAARAJgAAADG/8//DAARAJkAAADG/9D/DAARAJoAAADG/9H/DAARAJsAAADG/9L/DAARAJwAAADG/9P/DAARAJ0AAADH/83/DAASAJcAAADH/87/DAASAJgAAADH/8//DAASAJkAAADH/9D/DAASAJoAAADH/9H/DAASAJsAAADH/9L/DAASAJwAAADH/9P/DAASAJ0AAADI/83/DAALAJcAAADI/87/DAALAJgAAADI/8//DAALAJkAAADI/9D/DAALAJoAAADI/9H/DAALAJsAAADI/9L/DAALAJwAAADI/9P/DAALAJ0AAADJ/83/DAAMAJcAAADJ/87/DAAMAJgAAADJ/8//DAAMAJkAAADJ/9D/DAAMAJoAAADJ/9H/DAAMAJsAAADJ/9L/DAAMAJwAAADJ/9P/DAAMAJ0AAADK/83/DAANAJcAAADK/87/DAANAJgAAADK/8//DAANAJkAAADK/9D/DAANAJoAAADK/9H/DAANAJsAAADK/9L/DAANAJwAAADK/9P/DAANAJ0AAADL/83/DAAOAJcAAADL/87/DAAOAJgAAADL/8//DAAOAJkAAADL/9D/DAAOAJoAAADL/9H/DAAOAJsAAADL/9L/DAAOAJwAAADL/9P/DAAOAJ0AAADM/83/DAAPAJcAAADM/87/DAAPAJgAAADM/8//DAAPAJkAAADM/9D/DAAPAJoAAADM/9H/DAAPAJsAAADM/9L/DAAPAJwAAADM/9P/DAAPAJ0AAADN/83/DAAQAJcAAADN/87/DAAQAJgAAADN/8//DAAQAJkAAADN/9D/DAAQAJoAAADN/9H/DAAQAJsAAADN/9L/DAAQAJwAAADN/9P/DAAQAJ0AAADO/83/DAARAJcAAADO/87/DAARAJgAAADO/8//DAARAJkAAADO/9D/DAARAJoAAADO/9H/DAARAJsAAADO/9L/DAARAJwAAADO/9P/DAARAJ0AAADP/83/DAASAJcAAADP/87/DAASAJgAAADP/8//DAASAJkAAADP/9D/DAASAJoAAADP/9H/DAASAJsAAADP/9L/DAASAJwAAADP/9P/DAASAJ0AAADQ/83/DAALAJcAAADQ/87/DAALAJgAAADQ/8//DAALAJkAAADQ/9D/DAALAJoAAADQ/9H/DAALAJsAAADQ/9L/DAALAJwAAADQ/9P/DAALAJ0AAADR/83/DAAMAJcAAADR/87/DAAMAJgAAADR/8//DAAMAJkAAADR/9D/DAAMAJoAAADR/9H/DAAMAJsAAADR/9L/DAAMAJwAAADR/9P/DAAMAJ0AAADS/83/DAANAJcAAADS/87/DAANAJgAAADS/8//DAANAJkAAADS/9D/DAANAJoAAADS/9H/DAANAJsAAADS/9L/DAANAJwAAADS/9P/DAANAJ0AAADT/83/DAAOAJcAAADT/87/DAAOAJgAAADT/8//DAAOAJkAAADT/9D/DAAOAJoAAADT/9H/DAAOAJsAAADT/9L/DAAOAJwAAADT/9P/DAAOAJ0AAADU/83/DAAPAJcAAADU/87/DAAPAJgAAADU/8//DAAPAJkAAADU/9D/DAAPAJoAAADU/9H/DAAPAJsAAADU/9L/DAAPAJwAAADU/9P/DAAPAJ0AAADV/83/DAAQAJcAAADV/87/DAAQAJgAAADV/8//DAAQAJkAAADV/9D/DAAQAJoAAADV/9H/DAAQAJsAAADV/9L/DAAQAJwAAADV/9P/DAAQAJ0AAADW/83/DAARAJcAAADW/87/DAARAJgAAADW/8//DAARAJkAAADW/9D/DAARAJoAAADW/9H/DAARAJsAAADW/9L/DAARAJwAAADW/9P/DAARAJ0AAADX/83/DAASAJcAAADX/87/DAASAJgAAADX/8//DAASAJkAAADX/9D/DAASAJoAAADX/9H/DAASAJsAAADX/9L/DAASAJwAAADX/9P/DAASAJ0AAADY/83/DAALAJcAAADY/87/DAALAJgAAADY/8//DAALAJkAAADY/9D/DAALAJoAAADY/9H/DAALAJsAAADY/9L/DAALAJwAAADY/9P/DAALAJ0AAADZ/83/DAAMAJcAAADZ/87/DAAMAJgAAADZ/8//DAAMAJkAAADZ/9D/DAAMAJoAAADZ/9H/DAAMAJsAAADZ/9L/DAAMAJwAAADZ/9P/DAAMAJ0AAADa/83/DAANAJcAAADa/87/DAANAJgAAADa/8//DAANAJkAAADa/9D/DAANAJoAAADa/9H/DAANAJsAAADa/9L/DAANAJwAAADa/9P/DAANAJ0AAADb/83/DAAOAJcAAADb/87/DAAOAJgAAADb/8//DAAOAJkAAADb/9D/DAAOAJoAAADb/9H/DAAOAJsAAADb/9L/DAAOAJwAAADb/9P/DAAOAJ0AAADc/83/DAAPAJcAAADc/87/DAAPAJgAAADc/8//DAAPAJkAAADc/9D/DAAPAJoAAADc/9H/DAAPAJsAAADc/9L/DAAPAJwAAADc/9P/DAAPAJ0AAADd/83/DAAQAJcAAADd/87/DAAQAJgAAADd/8//DAAQAJkAAADd/9D/DAAQAJoAAADd/9H/DAAQAJsAAADd/9L/DAAQAJwAAADd/9P/DAAQAJ0AAADe/8//DAARAJkAAADe/9D/DAARAJoAAADe/9H/DAARAJsAAADe/9L/DAARAJwAAADe/9P/DAARAJ0AAADf/8//DAASAJkAAADf/9D/DAASAJoAAADf/9H/DAASAJsAAADf/9L/DAASAJwAAADf/9P/DAASAJ0AAADg/8//DAALAJkAAADg/9D/DAALAJoAAADg/9H/DAALAJsAAADg/9L/DAALAJwAAADg/9P/DAALAJ0AAADh/8//DAAMAJkAAADh/9D/DAAMAJoAAADh/9H/DAAMAJsAAADh/9L/DAAMAJwAAADh/9P/DAAMAJ0AAADi/8//DAANAJkAAADi/9D/DAANAJoAAADi/9H/DAANAJsAAADi/9L/DAANAJwAAADi/9P/DAANAJ0AAADj/8//DAAOAJkAAADj/9D/DAAOAJoAAADj/9H/DAAOAJsAAADj/9L/DAAOAJwAAADj/9P/DAAOAJ0AAADk/8//DAAPAJkAAADk/9D/DAAPAJoAAADk/9H/DAAPAJsAAADk/9L/DAAPAJwAAADk/9P/DAAPAJ0AAADe/83/DAARAJcAAADe/87/DAARAJgAAADf/83/DAASAJcAAADf/87/DAASAJgAAADg/83/DAALAJcAAADg/87/DAALAJgAAADh/83/DAAMAJcAAADh/87/DAAMAJgAAADi/83/DAANAJcAAADi/87/DAANAJgAAADj/83/DAAOAJcAAADj/87/DAAOAJgAAADk/83/DAAPAJcAAADk/87/DAAPAJgAAADl/83/DAAQAJcAAADl/87/DAAQAJgAAADl/8//DAAQAJkAAADl/9D/DAAQAJoAAADl/9H/DAAQAJsAAADl/9L/DAAQAJwAAADm/87/DAARAJgAAADm/8//DAARAJkAAAC2/87/DAAHAJgAAAC3/83/DAAIAJcAAAC3/87/DAAIAJgAAAC4/83/DAAJAJcAAAC4/87/DAAJAJgAAAC4/8//DAAJAJkAAAC4/9D/DAAJAJoAAAC4/9H/DAAJAJsAAAC4/9L/DAAJAJwAAADl/9P/DAAQAJ0AAADm/83/DAARAJcAAADm/9D/DAARAJoAAADm/9H/DAARAJsAAADm/9L/DAARAJwAAADm/9P/DAARAJ0AAADn/83/DAASAJcAAADn/87/DAASAJgAAADn/8//DAASAJkAAADn/9D/DAASAJoAAADn/9H/DAASAJsAAADn/9L/DAASAJwAAADn/9P/DAASAJ0AAADo/83/DAAUAJcAAADo/87/DAAUAJgAAADo/8//DAAUAJkAAADo/9D/DAAUAJoAAADo/9H/DAAUAJsAAADo/9L/DAAUAJwAAADp/87/AwAnAAAAAGDp/8//AwAoAAAAAGC3/+P/AgADAAgAAAC3/+T/AgADAAgAAAC4/9P/DAAJAJ0AAAC5/9X/AgADAAgAAAC5/97/AgADAAgAAAC6/97/GAAIAAkAAAC6/9//GAAIAAkAAAC5/9//AgADAAgAAAC5/+H/AgADAAgAAAC5/+L/AgADAAgAAAC6/+L/GAALAAAAAAC3/9L/DAAIAJwAAAC2/9L/DAAHAJwAAAC2/9H/DAAHAJsAAAC3/9H/DAAIAJsAAAC3/9D/DAAIAJoAAAC3/8//DAAIAJkAAAC2/9D/DAAHAJoAAAC2/8//DAAHAJkAAAC0/9T/AgADAAgAAAC0/9X/AgADAAgAAAC0/9b/AgADAAgAAAC0/9f/AgADAAgAAAC0/9j/AgADAAgAAAC0/9n/AgADAAgAAAC0/9r/AgADAAgAAAC0/9v/AgADAAgAAAC0/9z/AgADAAgAAAC0/93/AgADAAgAAAC0/97/AgADAAgAAAC0/9//AgADAAgAAAC0/+D/AgADAAgAAAC0/+H/AgADAAgAAAC0/+L/AgADAAgAAAC0/+P/AgADAAgAAAC0/+T/AgADAAgAAAC0/+X/AgADAAgAAAC0/+b/AgADAAgAAAC0/+f/AgADAAgAAAC0/+j/AgADAAgAAAC0/+n/AgADAAgAAAC0/+r/AgADAAgAAAC0/+v/AgADAAgAAAC0/+z/AgADAAgAAAC0/+3/AgADAAgAAAC0/+7/AgADAAgAAAC0/+//AgADAAgAAAC0//D/AgADAAgAAAC0//H/AgADAAgAAAC0//L/AgADAAgAAAC0//P/AgADAAgAAAC0//T/AgADAAgAAAC0//X/AgADAAgAAAC0//b/AgADAAgAAAC0//f/AgADAAgAAAC0//j/AgADAAgAAAC0//n/AgADAAgAAAC0//r/AgADAAgAAAC0//v/AgADAAgAAAC1/9T/AgADAAgAAAC1/9X/AgADAAgAAAC1/9b/AgADAAgAAAC1/9f/AgADAAgAAAC1/9j/AgADAAgAAAC1/9n/AgADAAgAAAC1/9r/AgADAAgAAAC1/9v/AgADAAgAAAC1/9z/AgADAAgAAAC1/93/AgADAAgAAAC1/97/AgADAAgAAAC1/9//AgADAAgAAAC1/+D/AgADAAgAAAC1/+H/AgADAAgAAAC1/+L/AgADAAgAAAC1/+P/AgADAAgAAAC1/+T/AgADAAgAAAC1/+X/AgADAAgAAAC1/+b/AgADAAgAAAC1/+f/AgADAAgAAAC1/+j/AgADAAgAAAC1/+n/AgADAAgAAAC1/+r/AgADAAgAAAC1/+v/AgADAAgAAAC1/+z/AgADAAgAAAC1/+3/AgADAAgAAAC1/+7/AgADAAgAAAC1/+//AgADAAgAAAC1//D/AgADAAgAAAC1//H/AgADAAgAAAC1//L/AgADAAgAAAC1//P/AgADAAgAAAC1//T/AgADAAgAAAC1//X/AgADAAgAAAC1//b/AgADAAgAAAC1//f/AgADAAgAAAC1//j/AgADAAgAAAC1//n/AgADAAgAAAC1//r/AgADAAgAAAC1//v/AgADAAgAAAC2/9T/AgADAAgAAAC2/9X/AgADAAgAAAC2/9b/AgADAAgAAAC2/9f/AgADAAgAAAC2/9j/AgADAAgAAAC2/9n/AgADAAgAAAC2/9r/AgADAAgAAAC2/9v/AgADAAgAAAC2/9z/AgADAAgAAAC2/93/AgADAAgAAAC2/97/AgADAAgAAAC2/9//AgADAAgAAAC2/+D/AgADAAgAAAC2/+H/AgADAAgAAAC2/+L/AgADAAgAAAC2/+P/AgADAAgAAAC2/+T/AgADAAgAAAC2/+X/AgADAAgAAAC2/+b/AgADAAgAAAC2/+f/AgADAAgAAAC2/+j/AgADAAgAAAC2/+n/AgADAAgAAAC2/+r/AgADAAgAAAC2/+v/AgADAAgAAAC2/+z/AgADAAgAAAC2/+3/AgADAAgAAAC2/+7/AgADAAgAAAC2/+//AgADAAgAAAC2//D/AgADAAgAAAC2//H/AgADAAgAAAC2//L/AgADAAgAAAC2//P/AgADAAgAAAC2//T/AgADAAgAAAC2//X/AgADAAgAAAC2//b/AgADAAgAAAC2//f/AgADAAgAAAC2//j/AgADAAgAAAC2//n/AgADAAgAAAC2//r/AgADAAgAAAC2//v/AgADAAgAAAC3/9T/AgADAAgAAAC3/9X/AgADAAgAAAC3/9b/AgADAAgAAAC3/9f/AgADAAgAAAC3/9j/AgADAAgAAAC3/9n/AgADAAgAAAC3/9r/AgADAAgAAAC3/9v/AgADAAgAAAC3/9z/AgADAAgAAAC3/93/AgADAAgAAAC3/97/AgADAAgAAAC3/9//AgADAAgAAAC3/+D/AgADAAgAAAC3/+H/AgADAAgAAAC3/+L/AgADAAgAAAC3/+X/AgADAAgAAAC3/+b/AgADAAgAAAC3/+f/AgADAAgAAAC3/+j/AgADAAgAAAC3/+n/AgADAAgAAAC3/+r/AgADAAgAAAC3/+v/AgADAAgAAAC3/+z/AgADAAgAAAC3/+3/AgADAAgAAAC3/+7/AgADAAgAAAC3/+//AgADAAgAAAC3//D/AgADAAgAAAC3//H/AgADAAgAAAC3//L/AgADAAgAAAC3//P/AgADAAgAAAC3//T/AgADAAgAAAC3//X/AgADAAgAAAC3//b/AgADAAgAAAC3//f/AgADAAgAAAC3//j/AgADAAgAAAC3//n/AgADAAgAAAC3//r/AgADAAgAAAC3//v/AgADAAgAAAC4/9T/AgADAAgAAAC4/9X/AgADAAgAAAC4/9b/AgADAAgAAAC4/9f/AgADAAgAAAC4/9j/AgADAAgAAAC4/9n/AgADAAgAAAC4/9r/AgADAAgAAAC4/9v/AgADAAgAAAC4/9z/AgADAAgAAAC4/93/AgADAAgAAAC4/97/AgADAAgAAAC4/9//AgADAAgAAAC4/+D/AgADAAgAAAC4/+H/AgADAAgAAAC4/+L/AgADAAgAAAC4/+P/AgADAAgAAAC4/+T/AgADAAgAAAC4/+X/AgADAAgAAAC4/+b/AgADAAgAAAC4/+f/AgADAAgAAAC4/+j/AgADAAgAAAC4/+n/AgADAAgAAAC4/+r/AgADAAgAAAC4/+v/AgADAAgAAAC4/+z/AgADAAgAAAC4/+3/AgADAAgAAAC4/+7/AgADAAgAAAC4/+//AgADAAgAAAC4//D/AgADAAgAAAC4//H/AgADAAgAAAC4//L/AgADAAgAAAC4//P/AgADAAgAAAC4//T/AgADAAgAAAC4//X/AgADAAgAAAC4//b/AgADAAgAAAC4//f/AgADAAgAAAC4//j/AgADAAgAAAC4//n/AgADAAgAAAC4//r/AgADAAgAAAC4//v/AgADAAgAAADm/9T/AgADAAgAAADm/9X/GAACAAMAAADm/9b/GAALABgAAADm/9f/GAALABoAAADm/9j/GAALABsAAADm/9n/AgADAAgAAADm/9r/GAAGAAAAAADm/9v/GAALAAIAAADm/9z/GAALAAMAAADm/93/AgADAAgAAADm/97/AgADAAgAAADm/9//AgADAAgAAADm/+D/AgADAAgAAADm/+H/AgADAAgAAADm/+L/AgADAAgAAADm/+P/AgADAAgAAADm/+T/AgADAAgAAADm/+X/AgADAAgAAADm/+b/GAADAAAAAADm/+f/GAAAAAEAAADm/+j/GAAAAAEAAADm/+n/GAAAAAEAAADm/+r/GAAAAAEAAADm/+v/GAAAAAEAAADm/+z/GAAAAAEAAADm/+3/GAAAAAEAAADm/+7/GAABAAIAAADm/+//GAALABgAAADm//D/GAALABsAAADm//H/AgADAAgAAADm//L/GAAKAAAAAADm//P/GAAJAAIAAADm//T/GAAJAAIAAADm//X/GAAJAAIAAADm//b/GAAJAAMAAADm//f/AgADAAgAAADm//j/AgAeAAoAAADn/9T/AgADAAgAAADn/9X/GAADAAAAAADn/9b/GAAAAAEAAADn/9f/GAAAAAEAAADn/9j/GAAAAAEAAADn/9n/GAAAAAEAAADn/9r/GAADAAIAAADn/9v/AgADAAgAAADn/9z/AgADAAgAAADn/93/AgADAAgAAADn/97/AgADAAgAAADn/9//AgADAAgAAADn/+D/AgADAAgAAADn/+H/AgADAAgAAADn/+L/AgADAAgAAADn/+P/AgADAAgAAADn/+T/AgADAAgAAADn/+X/AgADAAgAAADn/+b/AgADAAgAAADn/+f/AgADAAgAAADn/+j/AgADAAgAAADn/+n/AgADAAgAAADn/+r/AgADAAgAAADn/+v/AgADAAgAAADn/+z/AgADAAgAAADn/+3/AgADAAgAAADn/+7/GAADAAAAAADn/+//GAAAAAEAAADn//D/GAAAAAEAAADn//H/GAAAAAEAAADn//L/GAAHAAEAAADn//P/GAALAAIAAADn//T/GAAGAAEAAADn//X/GAAGAAIAAADn//b/GAALAAMAAADn//f/AgADAAgAAADn//j/AgAeAAoAAADo/+H/AgADAAgAAADo/+L/AgADAAgAAADo/+P/AgADAAgAAADo/+T/AgADAAgAAADo/+X/AgADAAgAAADo/+b/AgADAAgAAADo/+f/AgADAAgAAADo/+j/AgADAAgAAADo/+n/AgADAAgAAADo/+r/AgADAAgAAADo/+v/AgADAAgAAADo/+z/AgADAAgAAADo/+3/AgADAAgAAADo/+7/AgADAAgAAADo/+//AgADAAgAAADo//D/AgADAAgAAADo//H/AgADAAgAAADo//L/AgADAAgAAADo//P/AgADAAgAAADo//T/GAALAAAAAADo//X/GAALAAMAAADo//b/AgADAAgAAADo//f/AgADAAgAAADo//j/AgAeAAwAAADj//f/GAAJAAMAAADj//j/AgAeAAoAAADj//n/AgAcAAoAAADj//r/AgAcAAoAAADj//v/AgAeAAkAAADj//z/AwAoAAAAAADk//f/GAALAAMAAADk//j/AgAeAAoAAADk//n/AgAbAAoAAADk//r/AgAcAAoAAADk//v/AgAeAAkAAADk//z/AwAnAAAAAADl//f/AgADAAgAAADl//j/AgAeAAoAAADl//n/AgAcAAoAAADl//r/AgAcAAoAAADl//v/AgAeAAkAAADl//z/AwAoAAAAAADl//b/GAAJAAMAAADd//z/AwAoAAAAAADe//z/AwAnAAAAAADf//z/AwAoAAAAAADg//z/AwAnAAAAAADh//z/AwAoAAAAAADi//z/AwAnAAAAAADf//v/AgAeAAkAAADg//v/AgAeAAkAAADh//v/AgAeAAkAAADi//v/AgAeAAkAAADg//r/AgAbAAoAAADh//r/AgAcAAoAAADi//r/AgAcAAoAAADo/9T/AgADAAgAAADo/9X/AgADAAgAAADo/9b/AgADAAgAAADo/9f/AgADAAgAAADo/9j/AgADAAgAAADo/9n/AgADAAgAAADo/9r/AgADAAgAAADo/9v/AgADAAgAAADo/9z/AgADAAgAAADo/93/AgADAAgAAADo/97/AgADAAgAAADo/9//AgADAAgAAADo/+D/AgADAAgAAADi//j/AgAeAAoAAADi//n/AgAcAAoAAADh//n/AgAcAAoAAADd//3/AwAoAAEAAADd//7/AwAeAAMAAADd////AwAiAAQAAADd/wAAAwAoAAUAAADd/wEAAwAiAAYAAADd/wIAAwAfAAAAAADd/wMAAwAoAAAAAADd/wQAAwAoAAEAAADe//3/AwAnAAEAAADe//7/AwAeAAMAAADe////AwAgAAQAAADe/wAAAwAoAAUAAADe/wEAAwAgAAYAAADe/wIAAwAfAAAAAADe/wMAAwAnAAAAAADe/wQAAwAnAAEAAADf//3/AwAoAAEAAADf//7/AwAfAAMAAADf////AwAhAAQAAADf/wAAAwAoAAUAAADf/wEAAwAhAAYAAADf/wIAAwAfAAAAAADf/wMAAwAoAAAAAADf/wQAAwAoAAEAAADg//3/AwAnAAEAAADg//7/AwAeAAMAAADg////AwAiAAQAAADg/wAAAwAoAAUAAADg/wEAAwAiAAYAAADg/wIAAwAfAAAAAADg/wMAAwAnAAAAAADg/wQAAwAnAAEAAADh//3/AwAoAAEAAADh//7/AwAeAAMAAADh////AwAeAAQAAADh/wAAAwAoAAUAAADh/wEAAwAeAAYAAADh/wIAAwAfAAAAAADh/wMAAwAoAAAAAADh/wQAAwAoAAEAAADi//3/AwAnAAEAAADi//7/AwAeAAMAAADi////AwAfAAQAAADi/wAAAwAoAAUAAADi/wEAAwAfAAYAAADi/wIAAwAfAAAAAADi/wMAAwAnAAAAAADi/wQAAwAnAAEAAADj//3/AwAoAAEAAADj//7/AwAfAAMAAADj////AwAgAAQAAADj/wAAAwAoAAUAAADj/wEAAwAgAAYAAADj/wIAAwAfAAAAAADj/wMAAwAoAAAAAADj/wQAAwAoAAEAAADk//3/AwAnAAEAAADk//7/AwAeAAMAAADk////AwAhAAQAAADk/wAAAwAoAAUAAADk/wEAAwAhAAYAAADk/wIAAwAfAAAAAADk/wMAAwAnAAAAAADk/wQAAwAnAAEAAADW//3/AwAnAAEAAADW//7/AwAeAAMAAADW////AwAgAAQAAADW/wAAAwAoAAUAAADW/wEAAwAgAAYAAADW/wIAAwAfAAAAAADW/wMAAwAnAAAAAADW/wQAAwAnAAEAAADX//3/AwAoAAEAAADX//7/AwAfAAMAAADX////AwAhAAQAAADX/wAAAwAoAAUAAADX/wEAAwAhAAYAAADX/wIAAwAfAAAAAADX/wMAAwAoAAAAAADX/wQAAwAoAAEAAADY//3/AwAnAAEAAADY//7/AwAeAAMAAADY////AwAiAAQAAADY/wAAAwAoAAUAAADY/wEAAwAiAAYAAADY/wIAAwAfAAAAAADY/wMAAwAnAAAAAADY/wQAAwAnAAEAAADZ//3/AwAoAAEAAADZ//7/AwAeAAMAAADZ////AwAeAAQAAADZ/wAAAwAoAAUAAADZ/wEAAwAeAAYAAADZ/wIAAwAfAAAAAADZ/wMAAwAoAAAAAADZ/wQAAwAoAAEAAADa//3/AwAnAAEAAADa//7/AwAeAAMAAADa////AwAfAAQAAADa/wAAAwAoAAUAAADa/wEAAwAfAAYAAADa/wIAAwAfAAAAAADa/wMAAwAnAAAAAADa/wQAAwAnAAEAAADb//3/AwAoAAEAAADb//7/AwAfAAMAAADb////AwAgAAQAAADb/wAAAwAoAAUAAADb/wEAAwAgAAYAAADb/wIAAwAfAAAAAADb/wMAAwAoAAAAAADb/wQAAwAoAAEAAADc//3/AwAnAAEAAADc//7/AwAeAAMAAADc////AwAhAAQAAADc/wAAAwAoAAUAAADc/wEAAwAhAAYAAADc/wIAAwAfAAAAAADc/wMAAwAnAAAAAADc/wQAAwAnAAEAAADO//3/AwAnAAEAAADO//7/AwAeAAMAAADO////AwAgAAQAAADO/wAAAwAoAAUAAADO/wEAAwAgAAYAAADO/wIAAwAfAAAAAADO/wMAAwAnAAAAAADO/wQAAwAnAAEAAADP//3/AwAoAAEAAADP//7/AwAfAAMAAADP////AwAhAAQAAADP/wAAAwAoAAUAAADP/wEAAwAhAAYAAADP/wIAAwAfAAAAAADP/wMAAwAoAAAAAADP/wQAAwAoAAEAAADQ//3/AwAnAAEAAADQ//7/AwAeAAMAAADQ////AwAiAAQAAADQ/wAAAwAoAAUAAADQ/wEAAwAiAAYAAADQ/wIAAwAfAAAAAADQ/wMAAwAnAAAAAADQ/wQAAwAnAAEAAADR//3/AwAoAAEAAADR//7/AwAeAAMAAADR////AwAeAAQAAADR/wAAAwAoAAUAAADR/wEAAwAeAAYAAADR/wIAAwAfAAAAAADR/wMAAwAoAAAAAADR/wQAAwAoAAEAAADS//3/AwAnAAEAAADS//7/AwAeAAMAAADS////AwAfAAQAAADS/wAAAwAoAAUAAADS/wEAAwAfAAYAAADS/wIAAwAfAAAAAADS/wMAAwAnAAAAAADS/wQAAwAnAAEAAADT//3/AwAoAAEAAADT//7/AwAfAAMAAADT////AwAgAAQAAADT/wAAAwAoAAUAAADT/wEAAwAgAAYAAADT/wIAAwAfAAAAAADT/wMAAwAoAAAAAADT/wQAAwAoAAEAAADU//3/AwAnAAEAAADU//7/AwAeAAMAAADU////AwAhAAQAAADU/wAAAwAoAAUAAADU/wEAAwAhAAYAAADU/wIAAwAfAAAAAADU/wMAAwAnAAAAAADU/wQAAwAnAAEAAADV//3/AwAoAAEAAADV//7/AwAeAAMAAADV////AwAiAAQAAADV/wAAAwAoAAUAAADV/wEAAwAiAAYAAADV/wIAAwAfAAAAAADV/wMAAwAoAAAAAADV/wQAAwAoAAEAAADG//3/AwAnAAEAAADG//7/AwAeAAMAAADG////AwAgAAQAAADG/wAAAwAoAAUAAADG/wEAAwAgAAYAAADG/wIAAwAfAAAAAADG/wMAAwAnAAAAAADG/wQAAwAnAAEAAADH//3/AwAoAAEAAADH//7/AwAfAAMAAADH////AwAhAAQAAADH/wAAAwAoAAUAAADH/wEAAwAhAAYAAADH/wIAAwAfAAAAAADH/wMAAwAoAAAAAADH/wQAAwAoAAEAAADI//3/AwAnAAEAAADI//7/AwAeAAMAAADI////AwAiAAQAAADI/wAAAwAoAAUAAADI/wEAAwAiAAYAAADI/wIAAwAfAAAAAADI/wMAAwAnAAAAAADI/wQAAwAnAAEAAADJ//3/AwAoAAEAAADJ//7/AwAeAAMAAADJ////AwAeAAQAAADJ/wAAAwAoAAUAAADJ/wEAAwAeAAYAAADJ/wIAAwAfAAAAAADJ/wMAAwAoAAAAAADJ/wQAAwAoAAEAAADK//3/AwAnAAEAAADK//7/AwAeAAMAAADK////AwAfAAQAAADK/wAAAwAoAAUAAADK/wEAAwAfAAYAAADK/wIAAwAfAAAAAADK/wMAAwAnAAAAAADK/wQAAwAnAAEAAADL//3/AwAoAAEAAADL//7/AwAfAAMAAADL////AwAgAAQAAADL/wAAAwAoAAUAAADL/wEAAwAgAAYAAADL/wIAAwAfAAAAAADL/wMAAwAoAAAAAADL/wQAAwAoAAEAAADM//3/AwAnAAEAAADM//7/AwAeAAMAAADM////AwAhAAQAAADM/wAAAwAoAAUAAADM/wEAAwAhAAYAAADM/wIAAwAfAAAAAADM/wMAAwAnAAAAAADM/wQAAwAnAAEAAADN//3/AwAoAAEAAADN//7/AwAeAAMAAADN////AwAiAAQAAADN/wAAAwAoAAUAAADN/wEAAwAiAAYAAADN/wIAAwAfAAAAAADN/wMAAwAoAAAAAADN/wQAAwAoAAEAAAC+//3/AwAoAAEAAAC+//7/AwAeAAMAAAC+////AwAiAAQAAAC+/wAAAwAoAAUAAAC+/wEAAwAiAAYAAAC+/wIAAwAfAAAAAAC+/wMAAwAoAAAAAAC+/wQAAwAoAAEAAAC///3/AwAoAAEAAAC///7/AwAfAAMAAAC/////AwAhAAQAAAC//wAAAwAoAAUAAAC//wEAAwAhAAYAAAC//wIAAwAfAAAAAAC//wMAAwAoAAAAAAC//wQAAwAoAAEAAADA//3/AwAnAAEAAADA//7/AwAeAAMAAADA////AwAiAAQAAADA/wAAAwAoAAUAAADA/wEAAwAiAAYAAADA/wIAAwAfAAAAAADA/wMAAwAnAAAAAADA/wQAAwAnAAEAAADB//3/AwAoAAEAAADB//7/AwAeAAMAAADB////AwAeAAQAAADB/wAAAwAoAAUAAADB/wEAAwAeAAYAAADB/wIAAwAfAAAAAADB/wMAAwAoAAAAAADB/wQAAwAoAAEAAADC//3/AwAnAAEAAADC//7/AwAeAAMAAADC////AwAfAAQAAADC/wAAAwAoAAUAAADC/wEAAwAfAAYAAADC/wIAAwAfAAAAAADC/wMAAwAnAAAAAADC/wQAAwAnAAEAAADD//3/AwAoAAEAAADD//7/AwAfAAMAAADD////AwAgAAQAAADD/wAAAwAoAAUAAADD/wEAAwAgAAYAAADD/wIAAwAfAAAAAADD/wMAAwAoAAAAAADD/wQAAwAoAAEAAADE//3/AwAnAAEAAADE//7/AwAeAAMAAADE////AwAhAAQAAADE/wAAAwAoAAUAAADE/wEAAwAhAAYAAADE/wIAAwAfAAAAAADE/wMAAwAnAAAAAADE/wQAAwAnAAEAAADF//3/AwAoAAEAAADF//7/AwAeAAMAAADF////AwAiAAQAAADF/wAAAwAoAAUAAADF/wEAAwAiAAYAAADF/wIAAwAfAAAAAADF/wMAAwAoAAAAAADF/wQAAwAoAAEAAAC3//z/AwAnAAAAAAC3//3/AwAnAAEAAAC3//7/AwAeAAMAAAC3////AwAgAAQAAAC3/wAAAwAoAAUAAAC3/wEAAwAgAAYAAAC3/wIAAwAfAAAAAAC3/wMAAwAnAAAAAAC3/wQAAwAnAAEAAAC4//z/AwAoAAAAAAC4//3/AwAoAAEAAAC4//7/AwAfAAMAAAC4////AwAhAAQAAAC4/wAAAwAoAAUAAAC4/wEAAwAhAAYAAAC4/wIAAwAfAAAAAAC4/wMAAwAoAAAAAAC4/wQAAwAoAAEAAAC5//3/AwAnAAEAAAC5//7/AwAeAAMAAAC5////AwAiAAQAAAC5/wAAAwAoAAUAAAC5/wEAAwAiAAYAAAC5/wIAAwAfAAAAAAC5/wMAAwAnAAAAAAC5/wQAAwAnAAEAAAC6//3/AwAoAAEAAAC6//7/AwAeAAMAAAC6////AwAeAAQAAAC6/wAAAwAoAAUAAAC6/wEAAwAeAAYAAAC6/wIAAwAfAAAAAAC6/wMAAwAoAAAAAAC6/wQAAwAoAAEAAAC7//3/AwAnAAEAAAC7//7/AwAeAAMAAAC7////AwAfAAQAAAC7/wAAAwAoAAUAAAC7/wEAAwAfAAYAAAC7/wIAAwAfAAAAAAC7/wMAAwAnAAAAAAC7/wQAAwAnAAEAAAC8//3/AwAoAAEAAAC8//7/AwAfAAMAAAC8////AwAgAAQAAAC8/wAAAwAoAAUAAAC8/wEAAwAgAAYAAAC8/wIAAwAfAAAAAAC8/wMAAwAoAAAAAAC8/wQAAwAoAAEAAAC9//3/AwAnAAEAAAC9//7/AwAeAAMAAAC9////AwAhAAQAAAC9/wAAAwAoAAUAAAC9/wEAAwAhAAYAAAC9/wIAAwAfAAAAAAC9/wMAAwAnAAAAAAC9/wQAAwAnAAEAAACv//z/AwApAAYAAACv//3/AwApAAYAAACv//7/AwApAAYAAACv////AwApAAYAAACv/wAAAwAqAAUAAACv/wEAAwApAAYAAACv/wIAAwApAAYAAACv/wMAAwApAAYAAACv/wQAAwApAAYAAACw//z/AwAeAAQAAACw//3/AwAeAAUAAACw//7/AwAhAAQAAACw////AwAfAAcAAACw/wAAAwAoAAUAAACw/wEAAwAgAAYAAACw/wIAAwAhAAcAAACw/wMAAwAhAAQAAACw/wQAAwAiAAYAAACx//z/AwAeAAEAAACx//3/AwAeAAEAAACx//7/AwAeAAIAAACx////AwAgAAQAAACx/wAAAwAoAAUAAACx/wEAAwAfAAQAAACx/wIAAwAeAAAAAACx/wMAAwAeAAEAAACx/wQAAwAeAAEAAACy//z/AwAnAAAAAACy//3/AwAnAAEAAACy//7/AwAeAAMAAACy////AwAeAAQAAACy/wAAAwAoAAUAAACy/wEAAwAeAAYAAACy/wIAAwAfAAAAAACy/wMAAwAnAAAAAACy/wQAAwAnAAEAAACz//z/AwAoAAAAAACz//3/AwAoAAEAAACz//7/AwAeAAMAAACz////AwAfAAQAAACz/wAAAwAoAAUAAACz/wEAAwAfAAYAAACz/wIAAwAfAAAAAACz/wMAAwAoAAAAAACz/wQAAwAoAAEAAAC0//z/AwAnAAAAAAC0//3/AwAnAAEAAAC0//7/AwAeAAMAAAC0////AwAgAAQAAAC0/wAAAwAoAAUAAAC0/wEAAwAgAAYAAAC0/wIAAwAfAAAAAAC0/wMAAwAnAAAAAAC0/wQAAwAnAAEAAAC1//z/AwAoAAAAAAC1//3/AwAoAAEAAAC1//7/AwAfAAMAAAC1////AwAhAAQAAAC1/wAAAwAoAAUAAAC1/wEAAwAhAAYAAAC1/wIAAwAfAAAAAAC1/wMAAwAoAAAAAAC1/wQAAwAoAAEAAAC2//z/AwAoAAAAAAC2//3/AwAoAAEAAAC2//7/AwAeAAMAAAC2////AwAiAAQAAAC2/wAAAwAoAAUAAAC2/wEAAwAiAAYAAAC2/wIAAwAfAAAAAAC2/wMAAwAoAAAAAAC2/wQAAwAoAAEAAACm//z/AwApAAAAAACm//3/AwApAAEAAACm//7/AwAeAAMAAACm////AwAfAAQAAACm/wAAAwAqAAYAAACm/wEAAwAfAAUAAACm/wIAAwAfAAAAAACm/wMAAwAoAAIAAGCm/wQAAwAnAAIAAGCn//z/AwAqAAAAAACn//3/AwAqAAEAAACn//7/AwAeAAMAAACn////AwAiAAUAAACn/wAAAwAqAAYAAACn/wEAAwAgAAUAAACn/wIAAwAfAAAAAACn/wMAAwAoAAMAAGCn/wQAAwAnAAMAAGCo//z/AwAnAAAAAACo//3/AwAnAAEAAACo//7/AwAeAAMAAACo////AwAeAAcAAACo/wAAAwAoAAUAAACo/wEAAwAeAAcAAACo/wIAAwAfAAAAAACo/wMAAwAnAAAAAACo/wQAAwAnAAEAAACp//z/AwApAAAAAACp//3/AwApAAEAAACp//7/AwAeAAMAAACp////AwAfAAQAAACp/wAAAwAqAAYAAACp/wEAAwAfAAUAAACp/wIAAwAfAAAAAACp/wMAAwAoAAIAAGCp/wQAAwAnAAIAAGCq//z/AwAqAAAAAACq//3/AwAqAAEAAACq//7/AwAeAAMAAACq////AwAiAAUAAACq/wAAAwAqAAYAAACq/wEAAwAgAAUAAACq/wIAAwAfAAAAAACq/wMAAwAoAAMAAGCq/wQAAwAnAAMAAGCr//r/AwAnAAAAAACr//v/AwAnAAEAAACr//z/AwAnAAAAAACr//3/AwAnAAEAAACr//7/AwAeAAMAAACr////AwAeAAcAAACr/wAAAwAoAAUAAACr/wEAAwAeAAcAAACr/wIAAwAfAAAAAACr/wMAAwAnAAAAAACr/wQAAwAnAAEAAACr/wUAAwAnAAAAAACr/wYAAwAnAAEAAACs//r/AwAoAAAAAACs//v/AwAoAAEAAACs//z/AwAoAAAAAACs//3/AwAoAAEAAACs//7/AwAfAAMAAACs////AwAfAAQAAACs/wAAAwAoAAUAAACs/wEAAwAfAAYAAACs/wIAAwAfAAAAAACs/wMAAwAoAAAAAACs/wQAAwAoAAEAAACs/wUAAwAoAAAAAACs/wYAAwAoAAEAAACt//r/AwAgAAEAAACt//v/AwAgAAEAAACt//z/AwAgAAEAAACt//3/AwAgAAEAAACt//7/AwAgAAIAAACt////AwAgAAUAAACt/wAAAwAoAAUAAACt/wEAAwAiAAQAAACt/wIAAwAgAAAAAACt/wMAAwAgAAEAAACt/wQAAwAgAAEAAACt/wUAAwAgAAEAAACt/wYAAwAgAAEAAACu//r/AwAfAAQAAACu//v/AwAfAAUAAACu//z/AwAfAAYAAACu//3/AwAfAAcAAACu//7/AwAiAAcAAACu////AwAfAAQAAACu/wAAAwAoAAUAAACu/wEAAwAiAAUAAACu/wIAAwAgAAUAAACu/wMAAwAgAAUAAACu/wQAAwAhAAcAAACu/wUAAwAfAAUAAACu/wYAAwAhAAcAAACv//r/AwApAAYAAACv//v/AwApAAYAAACv/wUAAwApAAYAAACv/wYAAwApAAYAAACw//r/AwAeAAUAAACw//v/AwAeAAQAAACw/wUAAwAgAAYAAACw/wYAAwAfAAQAAACx//r/AwAeAAEAAACx//v/AwAeAAEAAACx/wUAAwAeAAEAAACx/wYAAwAeAAEAAACy//r/AwAnAAAAAACy//v/AwAnAAEAAACy/wUAAwAnAAAAAACy/wYAAwAnAAEAAACz//r/AwAoAAAAAACz//v/AwAoAAEAAACz/wUAAwAoAAAAAACz/wYAAwAoAAEAAACr//L/AwAoAAAAAGCr//P/AwAnAAAAAGCr//T/AwAoAAAAAGCr//X/AwAnAAAAAGCr//b/AwAoAAAAAGCr//f/AwAnAAAAAGCr//j/AwAoAAAAAGCr//n/AwAnAAAAAGCs//L/AwAoAAEAAGCs//P/AwAnAAEAAGCs//T/AwAoAAEAAGCs//X/AwAnAAEAAGCs//b/AwAoAAEAAGCs//f/AwAnAAEAAGCs//j/AwAoAAEAAGCs//n/AwAnAAEAAGCt//L/AwAgAAEAAACt//P/AwAgAAEAAACt//T/AwAgAAEAAACt//X/AwAgAAEAAACt//b/AwAgAAEAAACt//f/AwAgAAEAAACt//j/AwAgAAEAAACt//n/AwAgAAEAAACu//L/AwAhAAQAAGCu//P/AwAgAAQAAGCu//T/AwAgAAQAAGCu//X/AwAfAAQAAGCu//b/AwAeAAQAAGCu//f/AwAiAAQAAGCu//j/AwAhAAQAAGCu//n/AwAgAAQAAGCv//L/AwAoAAUAAGCv//P/AwAoAAUAAGCv//T/AwAoAAUAAGCv//X/AwAoAAUAAGCv//b/AwAoAAUAAGCv//f/AwAoAAUAAGCv//j/AwAoAAUAAGCv//n/AwAoAAUAAGCw//L/AwAhAAYAAGCw//P/AwAgAAYAAGCw//T/AwAgAAYAAGCw//X/AwAfAAYAAGCw//b/AwAeAAYAAGCw//f/AwAiAAYAAGCw//j/AwAhAAYAAGCw//n/AwAgAAYAAGCx//L/AwAeAAEAAACx//P/AwAeAAEAAACx//T/AwAeAAEAAACx//X/AwAeAAEAAACx//b/AwAeAAEAAACx//f/AwAeAAEAAACx//j/AwAeAAEAAACx//n/AwAeAAEAAACy//L/AwAoAAAAAGCy//P/AwAnAAAAAGCy//T/AwAoAAAAAGCy//X/AwAnAAAAAGCy//b/AwAoAAAAAGCy//f/AwAnAAAAAGCy//j/AwAoAAAAAGCy//n/AwAnAAAAAGCz//L/AwAoAAEAAGCz//P/AwAnAAEAAGCz//T/AwAoAAEAAGCz//X/AwAnAAEAAGCz//b/AwAoAAEAAGCz//f/AwAnAAEAAGCz//j/AwAoAAEAAGCz//n/AwAnAAEAAGCr/+z/AwAoAAAAAGCr/+3/AwAnAAAAAGCr/+7/AwAoAAAAAGCr/+//AwAnAAAAAGCr//D/AwAoAAAAAGCr//H/AwAnAAAAAGCs/+z/AwAoAAEAAGCs/+3/AwAnAAEAAGCs/+7/AwAoAAEAAGCs/+//AwAnAAEAAGCs//D/AwAoAAEAAGCs//H/AwAnAAEAAGCt/+z/AwAgAAEAAACt/+3/AwAgAAEAAACt/+7/AwAgAAEAAACt/+//AwAgAAEAAACt//D/AwAgAAEAAACt//H/AwAgAAEAAACu/+z/AwAiAAQAAGCu/+3/AwAhAAQAAGCu/+7/AwAgAAQAAGCu/+//AwAfAAQAAGCu//D/AwAeAAQAAGCu//H/AwAiAAQAAGCv/+z/AwAoAAUAAGCv/+3/AwAoAAUAAGCv/+7/AwAoAAUAAGCv/+//AwAoAAUAAGCv//D/AwAoAAUAAGCv//H/AwAoAAUAAGCw/+z/AwAiAAYAAGCw/+3/AwAhAAYAAGCw/+7/AwAgAAYAAGCw/+//AwAfAAYAAGCw//D/AwAeAAYAAGCw//H/AwAiAAYAAGCx/+z/AwAeAAEAAACx/+3/AwAeAAEAAACx/+7/AwAeAAEAAACx/+//AwAeAAEAAACx//D/AwAeAAEAAACx//H/AwAeAAEAAACy/+z/AwAoAAAAAGCy/+3/AwAnAAAAAGCy/+7/AwAoAAAAAGCy/+//AwAnAAAAAGCy//D/AwAoAAAAAGCy//H/AwAnAAAAAGCz/+z/AwAoAAEAAGCz/+3/AwAnAAEAAGCz/+7/AwAoAAEAAGCz/+//AwAnAAEAAGCz//D/AwAoAAEAAGCz//H/AwAnAAEAAGCr/+T/AwAnAAAAAGCr/+X/AwAoAAAAAGCr/+b/AwAnAAAAAGCr/+f/AwAoAAAAAGCr/+j/AwAnAAAAAGCr/+n/AwAoAAAAAGCr/+r/AwAnAAAAAGCr/+v/AwAnAAAAAGCs/+T/AwAnAAEAAGCs/+X/AwAoAAEAAGCs/+b/AwAnAAEAAGCs/+f/AwAoAAEAAGCs/+j/AwAnAAEAAGCs/+n/AwAoAAEAAGCs/+r/AwAnAAEAAGCs/+v/AwAnAAEAAGCt/+T/AwAgAAEAAACt/+X/AwAgAAEAAACt/+b/AwAgAAEAAACt/+f/AwAgAAEAAACt/+j/AwAgAAEAAACt/+n/AwAgAAEAAACt/+r/AwAgAAEAAACt/+v/AwAgAAEAAACu/+T/AwAhAAQAAGCu/+X/AwAgAAQAAGCu/+b/AwAfAAQAAGCu/+f/AwAeAAQAAGCu/+j/AwAiAAQAAGCu/+n/AwAhAAQAAGCu/+r/AwAgAAQAAGCu/+v/AwAgAAQAAGCv/+T/AwAoAAUAAGCv/+X/AwAoAAUAAGCv/+b/AwAoAAUAAGCv/+f/AwAoAAUAAGCv/+j/AwAoAAUAAGCv/+n/AwAoAAUAAGCv/+r/AwAoAAUAAGCv/+v/AwAoAAUAAGCw/+T/AwAhAAYAAGCw/+X/AwAgAAYAAGCw/+b/AwAfAAYAAGCw/+f/AwAeAAYAAGCw/+j/AwAiAAYAAGCw/+n/AwAhAAYAAGCw/+r/AwAgAAYAAGCw/+v/AwAgAAYAAGCx/+T/AwAeAAEAAACx/+X/AwAeAAEAAACx/+b/AwAeAAEAAACx/+f/AwAeAAEAAACx/+j/AwAeAAEAAACx/+n/AwAeAAEAAACx/+r/AwAeAAEAAACx/+v/AwAeAAEAAACy/+T/AwAnAAAAAGCy/+X/AwAoAAAAAGCy/+b/AwAnAAAAAGCy/+f/AwAoAAAAAGCy/+j/AwAnAAAAAGCy/+n/AwAoAAAAAGCy/+r/AwAnAAAAAGCy/+v/AwAnAAAAAGCz/+T/AwAnAAEAAGCz/+X/AwAoAAEAAGCz/+b/AwAnAAEAAGCz/+f/AwAoAAEAAGCz/+j/AwAnAAEAAGCz/+n/AwAoAAEAAGCz/+r/AwAnAAEAAGCz/+v/AwAnAAEAAGCr/9v/AwAoAAAAAGCr/9z/AwAnAAAAAGCr/93/AwAoAAAAAGCr/97/AwAnAAAAAGCr/9//AwAoAAAAAGCr/+D/AwAnAAAAAGCr/+H/AwAoAAAAAGCr/+L/AwAnAAAAAGCs/9v/AwAoAAEAAGCs/9z/AwAnAAEAAGCs/93/AwAoAAEAAGCs/97/AwAnAAEAAGCs/9//AwAoAAEAAGCs/+D/AwAnAAEAAGCs/+H/AwAoAAEAAGCs/+L/AwAnAAEAAGCt/9v/AwAgAAEAAACt/9z/AwAgAAEAAACt/93/AwAgAAEAAACt/97/AwAgAAEAAACt/9//AwAgAAEAAACt/+D/AwAgAAEAAACt/+H/AwAgAAEAAACt/+L/AwAgAAEAAACu/9v/AwAgAAQAAGCu/9z/AwAfAAQAAGCu/93/AwAeAAQAAGCu/97/AwAiAAQAAGCu/9//AwAhAAQAAGCu/+D/AwAgAAQAAGCu/+H/AwAhAAQAAGCu/+L/AwAgAAQAAGCv/9v/AwAoAAUAAGCv/9z/AwAoAAUAAGCv/93/AwAoAAUAAGCv/97/AwAoAAUAAGCv/9//AwAoAAUAAGCv/+D/AwAoAAUAAGCv/+H/AwAoAAUAAGCv/+L/AwAoAAUAAGCw/9v/AwAgAAYAAGCw/9z/AwAfAAYAAGCw/93/AwAeAAYAAGCw/97/AwAiAAYAAGCw/9//AwAhAAYAAGCw/+D/AwAgAAYAAGCw/+H/AwAhAAYAAGCw/+L/AwAgAAYAAGCx/9v/AwAeAAEAAACx/9z/AwAeAAEAAACx/93/AwAeAAEAAACx/97/AwAeAAEAAACx/9//AwAeAAEAAACx/+D/AwAeAAEAAACx/+H/AwAeAAEAAACx/+L/AwAeAAEAAACy/9v/AwAoAAAAAGCy/9z/AwAnAAAAAGCy/93/AwAoAAAAAGCy/97/AwAnAAAAAGCy/9//AwAoAAAAAGCy/+D/AwAnAAAAAGCy/+H/AwAoAAAAAGCy/+L/AwAnAAAAAGCz/9v/AwAoAAEAAGCz/9z/AwAnAAEAAGCz/93/AwAoAAEAAGCz/97/AwAnAAEAAGCz/9//AwAoAAEAAGCz/+D/AwAnAAEAAGCz/+H/AwAoAAEAAGCz/+L/AwAnAAEAAGCr/+P/AwAoAAAAAGCs/+P/AwAoAAEAAGCt/+P/AwAgAAEAAACu/+P/AwAiAAQAAGCv/+P/AwAoAAUAAGCw/+P/AwAiAAYAAGCx/+P/AwAeAAEAAACy/+P/AwAoAAAAAGCz/+P/AwAoAAEAAGCr/9n/AwAoAAAAAGCr/9r/AwAnAAAAAGCs/9n/AwAoAAEAAGCs/9r/AwAnAAEAAGCt/9n/AwAgAAEAAACt/9r/AwAgAAEAAACu/9n/AwAiAAQAAGCu/9r/AwAhAAQAAGCv/9n/AwAoAAUAAGCv/9r/AwAoAAUAAGCw/9n/AwAiAAYAAGCw/9r/AwAhAAYAAGCx/9n/AwAeAAEAAACx/9r/AwAeAAEAAACy/9n/AwAoAAAAAGCy/9r/AwAnAAAAAGCz/9n/AwAoAAEAAGCz/9r/AwAnAAEAAGCr/9H/AwAoAAAAAGCr/9L/AwAnAAAAAGCr/9P/AwAoAAAAAGCr/9T/AwAnAAAAAGCr/9X/AwAoAAAAAGCr/9b/AwAnAAAAAGCr/9f/AwAoAAAAAGCr/9j/AwAnAAAAAGCs/9H/AwAoAAEAAGCs/9L/AwAnAAEAAGCs/9P/AwAoAAEAAGCs/9T/AwAnAAEAAGCs/9X/AwAoAAEAAGCs/9b/AwAnAAEAAGCs/9f/AwAoAAEAAGCs/9j/AwAnAAEAAGCt/9H/AwAgAAEAAACt/9L/AwAgAAEAAACt/9P/AwAgAAEAAACt/9T/AwAgAAEAAACt/9X/AwAgAAEAAACt/9b/AwAgAAEAAACt/9f/AwAgAAEAAACt/9j/AwAgAAEAAACu/9H/AwAiAAQAAGCu/9L/AwAhAAQAAGCu/9P/AwAgAAQAAGCu/9T/AwAfAAQAAGCu/9X/AwAeAAQAAGCu/9b/AwAiAAQAAGCu/9f/AwAhAAQAAGCu/9j/AwAgAAQAAGCv/9H/AwAoAAUAAGCv/9L/AwAoAAUAAGCv/9P/AwAoAAUAAGCv/9T/AwAoAAUAAGCv/9X/AwAoAAUAAGCv/9b/AwAoAAUAAGCv/9f/AwAoAAUAAGCv/9j/AwAoAAUAAGCw/9H/AwAiAAYAAGCw/9L/AwAhAAYAAGCw/9P/AwAgAAYAAGCw/9T/AwAfAAYAAGCw/9X/AwAeAAYAAGCw/9b/AwAiAAYAAGCw/9f/AwAhAAYAAGCw/9j/AwAgAAYAAGCx/9H/AwAeAAEAAACx/9L/AwAeAAEAAACx/9P/AwAeAAEAAACx/9T/AwAeAAEAAACx/9X/AwAeAAEAAACx/9b/AwAeAAEAAACx/9f/AwAeAAEAAACx/9j/AwAeAAEAAACy/9H/AwAoAAAAAGCy/9L/AwAnAAAAAGCy/9P/AwAoAAAAAGCy/9T/AwAnAAAAAGCy/9X/AwAoAAAAAGCy/9b/AwAnAAAAAGCy/9f/AwAoAAAAAGCy/9j/AwAnAAAAAGCz/9H/AwAoAAEAAGCz/9L/AwAnAAEAAGCz/9P/AwAoAAEAAGCz/9T/AwAnAAEAAGCz/9X/AwAoAAEAAGCz/9b/AwAnAAEAAGCz/9f/AwAoAAEAAGCz/9j/AwAnAAEAAGCr/8n/AwAoAAAAAGCr/8r/AwAnAAAAAGCr/8v/AwAoAAAAAGCr/8z/AwAnAAAAAGCr/83/AwAoAAAAAGCr/87/AwAnAAAAAGCr/8//AwAoAAAAAGCr/9D/AwAnAAAAAGCs/8n/AwAoAAEAAGCs/8r/AwAnAAEAAGCs/8v/AwAoAAEAAGCs/8z/AwAnAAEAAGCs/83/AwAoAAEAAGCs/87/AwAnAAEAAGCs/8//AwAoAAEAAGCs/9D/AwAnAAEAAGCt/8n/AwAgAAEAAACt/8r/AwAgAAEAAACt/8v/AwAgAAEAAACt/8z/AwAgAAEAAACt/83/AwAgAAEAAACt/87/AwAgAAEAAACt/8//AwAgAAEAAACt/9D/AwAgAAEAAACu/8n/AwAiAAQAAGCu/8r/AwAhAAQAAGCu/8v/AwAgAAQAAGCu/8z/AwAfAAQAAGCu/83/AwAeAAQAAGCu/87/AwAiAAQAAGCu/8//AwAhAAQAAGCu/9D/AwAgAAQAAGCv/8n/AwAoAAUAAGCv/8r/AwAoAAUAAGCv/8v/AwAoAAUAAGCv/8z/AwAoAAUAAGCv/83/AwAoAAUAAGCv/87/AwAoAAUAAGCv/8//AwAoAAUAAGCv/9D/AwAoAAUAAGCw/8n/AwAiAAYAAGCw/8r/AwAhAAYAAGCw/8v/AwAgAAYAAGCw/8z/AwAfAAYAAGCw/83/AwAeAAYAAGCw/87/AwAiAAYAAGCw/8//AwAhAAYAAGCw/9D/AwAgAAYAAGCx/8n/AwAeAAEAAACx/8r/AwAeAAEAAACx/8v/AwAeAAEAAACx/8z/AwAeAAEAAACx/83/AwAeAAEAAACx/87/AwAeAAEAAACx/8//AwAeAAEAAACx/9D/AwAeAAEAAACy/8n/AwAoAAAAAGCy/8r/AwAnAAAAAGCy/8v/AwAoAAAAAGCy/8z/AwAnAAAAAGCy/83/AwAoAAAAAGCy/87/AwAnAAAAAGCy/8//AwAoAAAAAGCy/9D/AwAnAAAAAGCz/8n/AwAoAAEAAGCz/8r/AwAnAAEAAGCz/8v/AwAoAAEAAGCz/8z/AwAnAAEAAGCz/83/AwAoAAEAAGCz/87/AwAnAAEAAGCz/8//AwAoAAEAAGCz/9D/AwAnAAEAAGDp/9n/AwAoAAAAAGDp/9r/AwAnAAAAAGDp/9v/AwAoAAAAAGDp/9z/AwAnAAAAAGDp/93/AwAoAAAAAGDp/97/AwAnAAAAAGDp/9//AwAoAAAAAGDp/+D/AwAnAAAAAGDq/9n/AwAoAAEAAGDq/9r/AwAnAAEAAGDq/9v/AwAoAAEAAGDq/9z/AwAnAAEAAGDq/93/AwAoAAEAAGDq/97/AwAnAAEAAGDq/9//AwAoAAEAAGDq/+D/AwAnAAEAAGDr/9n/AwAgAAEAAADr/9r/AwAgAAEAAADr/9v/AwAgAAEAAADr/9z/AwAgAAEAAADr/93/AwAgAAEAAADr/97/AwAgAAEAAADr/9//AwAgAAEAAADr/+D/AwAgAAEAAADs/9n/AwAiAAQAAGDs/9r/AwAhAAQAAGDs/9v/AwAgAAQAAGDs/9z/AwAfAAQAAGDs/93/AwAeAAQAAGDs/97/AwAiAAQAAGDs/9//AwAhAAQAAGDs/+D/AwAgAAQAAGDt/9n/AwAoAAUAAGDt/9r/AwAoAAUAAGDt/9v/AwAoAAUAAGDt/9z/AwAoAAUAAGDt/93/AwAoAAUAAGDt/97/AwAoAAUAAGDt/9//AwAoAAUAAGDt/+D/AwAoAAUAAGDu/9n/AwAiAAYAAGDu/9r/AwAhAAYAAGDu/9v/AwAgAAYAAGDu/9z/AwAfAAYAAGDu/93/AwAeAAYAAGDu/97/AwAiAAYAAGDu/9//AwAhAAYAAGDu/+D/AwAgAAYAAGDv/9n/AwAeAAEAAADv/9r/AwAeAAEAAADv/9v/AwAeAAEAAADv/9z/AwAeAAEAAADv/93/AwAeAAEAAADv/97/AwAeAAEAAADv/9//AwAeAAEAAADv/+D/AwAeAAEAAADw/9n/AwAoAAAAAGDw/9r/AwAnAAAAAGDw/9v/AwAoAAAAAGDw/9z/AwAnAAAAAGDw/93/AwAoAAAAAGDw/97/AwAnAAAAAGDw/9//AwAoAAAAAGDw/+D/AwAnAAAAAGDx/9n/AwAoAAEAAGDx/9r/AwAnAAEAAGDx/9v/AwAoAAEAAGDx/9z/AwAnAAEAAGDx/93/AwAoAAEAAGDx/97/AwAnAAEAAGDx/9//AwAoAAEAAGDx/+D/AwAnAAEAAGDp/9H/AwAoAAAAAGDp/9L/AwAnAAAAAGDp/9P/AwAoAAAAAGDp/9T/AwAnAAAAAGDp/9X/AwAoAAAAAGDp/9b/AwAnAAAAAGDp/9f/AwAoAAAAAGDp/9j/AwAnAAAAAGDq/9H/AwAoAAEAAGDq/9L/AwAnAAEAAGDq/9P/AwAoAAEAAGDq/9T/AwAnAAEAAGDq/9X/AwAoAAEAAGDq/9b/AwAnAAEAAGDq/9f/AwAoAAEAAGDq/9j/AwAnAAEAAGDr/9H/AwAgAAEAAADr/9L/AwAgAAEAAADr/9P/AwAgAAEAAADr/9T/AwAgAAEAAADr/9X/AwAgAAEAAADr/9b/AwAgAAEAAADr/9f/AwAgAAEAAADr/9j/AwAgAAEAAADs/9H/AwAiAAQAAGDs/9L/AwAhAAQAAGDs/9P/AwAgAAQAAGDs/9T/AwAfAAQAAGDs/9X/AwAeAAQAAGDs/9b/AwAiAAQAAGDs/9f/AwAhAAQAAGDs/9j/AwAgAAQAAGDt/9H/AwAoAAUAAGDt/9L/AwAoAAUAAGDt/9P/AwAoAAUAAGDt/9T/AwAoAAUAAGDt/9X/AwAoAAUAAGDt/9b/AwAoAAUAAGDt/9f/AwAoAAUAAGDt/9j/AwAoAAUAAGDu/9H/AwAiAAYAAGDu/9L/AwAhAAYAAGDu/9P/AwAgAAYAAGDu/9T/AwAfAAYAAGDu/9X/AwAeAAYAAGDu/9b/AwAiAAYAAGDu/9f/AwAhAAYAAGDu/9j/AwAgAAYAAGDv/9H/AwAeAAEAAADv/9L/AwAeAAEAAADv/9P/AwAeAAEAAADv/9T/AwAeAAEAAADv/9X/AwAeAAEAAADv/9b/AwAeAAEAAADv/9f/AwAeAAEAAADv/9j/AwAeAAEAAADw/9H/AwAoAAAAAGDw/9L/AwAnAAAAAGDw/9P/AwAoAAAAAGDw/9T/AwAnAAAAAGDw/9X/AwAoAAAAAGDw/9b/AwAnAAAAAGDw/9f/AwAoAAAAAGDw/9j/AwAnAAAAAGDx/9H/AwAoAAEAAGDx/9L/AwAnAAEAAGDx/9P/AwAoAAEAAGDx/9T/AwAnAAEAAGDx/9X/AwAoAAEAAGDx/9b/AwAnAAEAAGDx/9f/AwAoAAEAAGDx/9j/AwAnAAEAAGDp/8n/AwAoAAAAAGDp/8r/AwAnAAAAAGDp/8v/AwAoAAAAAGDp/8z/AwAnAAAAAGDp/83/AwAoAAAAAGDp/9D/AwAnAAAAAGDq/8n/AwAoAAEAAGDq/8r/AwAnAAEAAGDq/8v/AwAoAAEAAGDq/8z/AwAnAAEAAGDq/83/AwAoAAEAAGDq/87/AwAnAAEAAGDq/8//AwAoAAEAAGDq/9D/AwAnAAEAAGDr/8n/AwAgAAEAAADr/8r/AwAgAAEAAADr/8v/AwAgAAEAAADr/8z/AwAgAAEAAADr/83/AwAgAAEAAADr/87/AwAgAAEAAADr/8//AwAgAAEAAADr/9D/AwAgAAEAAADs/8n/AwAiAAQAAGDs/8r/AwAhAAQAAGDs/8v/AwAgAAQAAGDs/8z/AwAfAAQAAGDs/83/AwAeAAQAAGDs/87/AwAiAAQAAGDs/8//AwAhAAQAAGDs/9D/AwAgAAQAAGDt/8n/AwAoAAUAAGDt/8r/AwAoAAUAAGDt/8v/AwAoAAUAAGDt/8z/AwAoAAUAAGDt/83/AwAoAAUAAGDt/87/AwAoAAUAAGDt/8//AwAoAAUAAGDt/9D/AwAoAAUAAGDu/8n/AwAiAAYAAGDu/8r/AwAhAAYAAGDu/8v/AwAgAAYAAGDu/8z/AwAfAAYAAGDu/83/AwAeAAYAAGDu/87/AwAiAAYAAGDu/8//AwAhAAYAAGDu/9D/AwAgAAYAAGDv/8n/AwAeAAEAAADv/8r/AwAeAAEAAADv/8v/AwAeAAEAAADv/8z/AwAeAAEAAADv/83/AwAeAAEAAADv/87/AwAeAAEAAADv/8//AwAeAAEAAADv/9D/AwAeAAEAAADw/8n/AwAoAAAAAGDw/8r/AwAnAAAAAGDw/8v/AwAoAAAAAGDw/8z/AwAnAAAAAGDw/83/AwAoAAAAAGDw/87/AwAnAAAAAGDw/8//AwAoAAAAAGDw/9D/AwAnAAAAAGDx/8n/AwAoAAEAAGDx/8r/AwAnAAEAAGDx/8v/AwAoAAEAAGDx/8z/AwAnAAEAAGDx/83/AwAoAAEAAGDx/87/AwAnAAEAAGDx/8//AwAoAAEAAGDx/9D/AwAnAAEAAGCr/wcAAwAoAAAAAGCr/wgAAwAnAAAAAGCr/wkAAwAoAAAAAGCr/woAAwAnAAAAAGCr/wsAAwAoAAAAAGCr/wwAAwAnAAAAAGCr/w0AAwAoAAAAAGCr/w4AAwAnAAAAAGCs/wcAAwAoAAEAAGCs/wgAAwAnAAEAAGCs/wkAAwAoAAEAAGCs/woAAwAnAAEAAGCs/wsAAwAoAAEAAGCs/wwAAwAnAAEAAGCs/w0AAwAoAAEAAGCs/w4AAwAnAAEAAGCt/wcAAwAgAAEAAACt/wgAAwAgAAEAAACt/wkAAwAgAAEAAACt/woAAwAgAAEAAACt/wsAAwAgAAEAAACt/wwAAwAgAAEAAACt/w0AAwAgAAEAAACt/w4AAwAgAAEAAACu/wcAAwAiAAQAAGCu/wgAAwAhAAQAAGCu/wkAAwAgAAQAAGCu/woAAwAfAAQAAGCu/wsAAwAeAAQAAGCu/wwAAwAiAAQAAGCu/w0AAwAhAAQAAGCu/w4AAwAgAAQAAGCv/wcAAwAoAAUAAGCv/wgAAwAoAAUAAGCv/wkAAwAoAAUAAGCv/woAAwAoAAUAAGCv/wsAAwAoAAUAAGCv/wwAAwAoAAUAAGCv/w0AAwAoAAUAAGCv/w4AAwAoAAUAAGCw/wcAAwAiAAYAAGCw/wgAAwAhAAYAAGCw/wkAAwAgAAYAAGCw/woAAwAfAAYAAGCw/wsAAwAeAAYAAGCw/wwAAwAiAAYAAGCw/w0AAwAhAAYAAGCw/w4AAwAgAAYAAGCx/wcAAwAeAAEAAACx/wgAAwAeAAEAAACx/wkAAwAeAAEAAACx/woAAwAeAAEAAACx/wsAAwAeAAEAAACx/wwAAwAeAAEAAACx/w0AAwAeAAEAAACx/w4AAwAeAAEAAACy/wcAAwAoAAAAAGCy/wgAAwAnAAAAAGCy/wkAAwAoAAAAAGCy/woAAwAnAAAAAGCy/wsAAwAoAAAAAGCy/wwAAwAnAAAAAGCy/w0AAwAoAAAAAGCy/w4AAwAnAAAAAGCz/wcAAwAoAAEAAGCz/wgAAwAnAAEAAGCz/wkAAwAoAAEAAGCz/woAAwAnAAEAAGCz/wsAAwAoAAEAAGCz/wwAAwAnAAEAAGCz/w0AAwAoAAEAAGCz/w4AAwAnAAEAAGDp/wgAAwAoAAAAAGDp/wkAAwAnAAAAAGDp/woAAwAoAAAAAGDp/wsAAwAoAAAAAGDp/wwAAwAnAAAAAGDp/w0AAwAoAAAAAGDp/w4AAwAnAAAAAGDp/w8AAwAoAAAAAGDq/wgAAwAoAAEAAGDq/wkAAwAnAAEAAGDq/woAAwAoAAEAAGDq/wsAAwAoAAEAAGDq/wwAAwAnAAEAAGDq/w0AAwAoAAEAAGDq/w4AAwAnAAEAAGDq/w8AAwAoAAEAAGDr/wgAAwAgAAEAAADr/wkAAwAgAAEAAADr/woAAwAgAAEAAADr/wsAAwAgAAEAAADr/wwAAwAgAAEAAADr/w0AAwAgAAEAAADr/w4AAwAgAAEAAADr/w8AAwAgAAEAAADs/wgAAwAiAAQAAGDs/wkAAwAhAAQAAGDs/woAAwAgAAQAAGDs/wsAAwAiAAQAAGDs/wwAAwAhAAQAAGDs/w0AAwAgAAQAAGDs/w4AAwAfAAQAAGDs/w8AAwAeAAQAAGDt/wgAAwAoAAUAAGDt/wkAAwAoAAUAAGDt/woAAwAoAAUAAGDt/wsAAwAoAAUAAGDt/wwAAwAoAAUAAGDt/w0AAwAoAAUAAGDt/w4AAwAoAAUAAGDt/w8AAwAoAAUAAGDu/wgAAwAiAAYAAGDu/wkAAwAhAAYAAGDu/woAAwAgAAYAAGDu/wsAAwAiAAYAAGDu/wwAAwAhAAYAAGDu/w0AAwAgAAYAAGDu/w4AAwAfAAYAAGDu/w8AAwAeAAYAAGDv/wgAAwAeAAEAAADv/wkAAwAeAAEAAADv/woAAwAeAAEAAADv/wsAAwAeAAEAAADv/wwAAwAeAAEAAADv/w0AAwAeAAEAAADv/w4AAwAeAAEAAADv/w8AAwAeAAEAAADw/wgAAwAoAAAAAGDw/wkAAwAnAAAAAGDw/woAAwAoAAAAAGDw/wsAAwAoAAAAAGDw/wwAAwAnAAAAAGDw/w0AAwAoAAAAAGDw/w4AAwAnAAAAAGDw/w8AAwAoAAAAAGDx/wgAAwAoAAEAAGDx/wkAAwAnAAEAAGDx/woAAwAoAAEAAGDx/wsAAwAoAAEAAGDx/wwAAwAnAAEAAGDx/w0AAwAoAAEAAGDx/w4AAwAnAAEAAGDx/w8AAwAoAAEAAGAMANn/AwAoAAAAAGAMANr/AwAnAAAAAGAMANv/AwAoAAAAAGAMANz/AwAnAAAAAGAMAN3/AwAoAAAAAGAMAN7/AwAnAAAAAGAMAN//AwAoAAAAAGAMAOD/AwAnAAAAAGANANn/AwAoAAEAAGANANr/AwAnAAEAAGANANv/AwAoAAEAAGANANz/AwAnAAEAAGANAN3/AwAoAAEAAGANAN7/AwAnAAEAAGANAN//AwAoAAEAAGANAOD/AwAnAAEAAGAOANn/AwAgAAEAAAAOANr/AwAgAAEAAAAOANv/AwAgAAEAAAAOANz/AwAgAAEAAAAOAN3/AwAgAAEAAAAOAN7/AwAgAAEAAAAOAN//AwAgAAEAAAAOAOD/AwAgAAEAAAAPANn/AwAiAAQAAGAPANr/AwAhAAQAAGAPANv/AwAgAAQAAGAPANz/AwAfAAQAAGAPAN3/AwAeAAQAAGAPAN7/AwAiAAQAAGAPAN//AwAhAAQAAGAPAOD/AwAgAAQAAGAQANn/AwAoAAUAAGAQANr/AwAoAAUAAGAQANv/AwAoAAUAAGAQANz/AwAoAAUAAGAQAN3/AwAoAAUAAGAQAN7/AwAoAAUAAGAQAN//AwAoAAUAAGAQAOD/AwAoAAUAAGARANn/AwAiAAYAAGARANr/AwAhAAYAAGARANv/AwAgAAYAAGARANz/AwAfAAYAAGARAN3/AwAeAAYAAGARAN7/AwAiAAYAAGARAN//AwAhAAYAAGARAOD/AwAgAAYAAGASANn/AwAeAAEAAAASANr/AwAeAAEAAAASANv/AwAeAAEAAAASANz/AwAeAAEAAAASAN3/AwAeAAEAAAASAN7/AwAeAAEAAAASAN//AwAeAAEAAAASAOD/AwAeAAEAAAATANn/AwAoAAAAAGATANr/AwAnAAAAAGATANv/AwAoAAAAAGATANz/AwAnAAAAAGATAN3/AwAoAAAAAGATAN7/AwAnAAAAAGATAN//AwAoAAAAAGATAOD/AwAnAAAAAGAUANn/AwAoAAEAAGAUANr/AwAnAAEAAGAUANv/AwAoAAEAAGAUANz/AwAnAAEAAGAUAN3/AwAoAAEAAGAUAN7/AwAnAAEAAGAUAN//AwAoAAEAAGAUAOD/AwAnAAEAAGAMANH/AwAoAAAAAGAMANL/AwAnAAAAAGAMANP/AwAoAAAAAGAMANT/AwAnAAAAAGAMANX/AwAoAAAAAGAMANb/AwAnAAAAAGAMANf/AwAoAAAAAGAMANj/AwAnAAAAAGANANH/AwAoAAEAAGANANL/AwAnAAEAAGANANP/AwAoAAEAAGANANT/AwAnAAEAAGANANX/AwAoAAEAAGANANb/AwAnAAEAAGANANf/AwAoAAEAAGANANj/AwAnAAEAAGAOANH/AwAgAAEAAAAOANL/AwAgAAEAAAAOANP/AwAgAAEAAAAOANT/AwAgAAEAAAAOANX/AwAgAAEAAAAOANb/AwAgAAEAAAAOANf/AwAgAAEAAAAOANj/AwAgAAEAAAAPANH/AwAiAAQAAGAPANL/AwAhAAQAAGAPANP/AwAgAAQAAGAPANT/AwAfAAQAAGAPANX/AwAeAAQAAGAPANb/AwAiAAQAAGAPANf/AwAhAAQAAGAPANj/AwAgAAQAAGAQANH/AwAoAAUAAGAQANL/AwAoAAUAAGAQANP/AwAoAAUAAGAQANT/AwAoAAUAAGAQANX/AwAoAAUAAGAQANb/AwAoAAUAAGAQANf/AwAoAAUAAGAQANj/AwAoAAUAAGARANH/AwAiAAYAAGARANL/AwAhAAYAAGARANP/AwAgAAYAAGARANT/AwAfAAYAAGARANX/AwAeAAYAAGARANb/AwAiAAYAAGARANf/AwAhAAYAAGARANj/AwAgAAYAAGASANH/AwAeAAEAAAASANL/AwAeAAEAAAASANP/AwAeAAEAAAASANT/AwAeAAEAAAASANX/AwAeAAEAAAASANb/AwAeAAEAAAASANf/AwAeAAEAAAASANj/AwAeAAEAAAATANH/AwAoAAAAAGATANL/AwAnAAAAAGATANP/AwAoAAAAAGATANT/AwAnAAAAAGATANX/AwAoAAAAAGATANb/AwAnAAAAAGATANf/AwAoAAAAAGATANj/AwAnAAAAAGAUANH/AwAoAAEAAGAUANL/AwAnAAEAAGAUANP/AwAoAAEAAGAUANT/AwAnAAEAAGAUANX/AwAoAAEAAGAUANb/AwAnAAEAAGAUANf/AwAoAAEAAGAUANj/AwAnAAEAAGAMAMn/AwAoAAAAAGAMAMr/AwAnAAAAAGAMAMv/AwAoAAAAAGAMAMz/AwAnAAAAAGAMAM3/AwAoAAAAAGAMAM7/AwAnAAAAAGAMAM//AwAoAAAAAGAMAND/AwAnAAAAAGANAMn/AwAoAAEAAGANAMr/AwAnAAEAAGANAMv/AwAoAAEAAGANAMz/AwAnAAEAAGANAM3/AwAoAAEAAGANAM7/AwAnAAEAAGANAM//AwAoAAEAAGANAND/AwAnAAEAAGAOAMn/AwAgAAEAAAAOAMr/AwAgAAEAAAAOAMv/AwAgAAEAAAAOAMz/AwAgAAEAAAAOAM3/AwAgAAEAAAAOAM7/AwAgAAEAAAAOAM//AwAgAAEAAAAOAND/AwAgAAEAAAAPAMn/AwAiAAQAAGAPAMr/AwAhAAQAAGAPAMv/AwAgAAQAAGAPAMz/AwAfAAQAAGAPAM3/AwAeAAQAAGAPAM7/AwAiAAQAAGAPAM//AwAhAAQAAGAPAND/AwAgAAQAAGAQAMn/AwAoAAUAAGAQAMr/AwAoAAUAAGAQAMv/AwAoAAUAAGAQAMz/AwAoAAUAAGAQAM3/AwAoAAUAAGAQAM7/AwAoAAUAAGAQAM//AwAoAAUAAGAQAND/AwAoAAUAAGARAMn/AwAiAAYAAGARAMr/AwAhAAYAAGARAMv/AwAgAAYAAGARAMz/AwAfAAYAAGARAM3/AwAeAAYAAGARAM7/AwAiAAYAAGARAM//AwAhAAYAAGARAND/AwAgAAYAAGASAMn/AwAeAAEAAAASAMr/AwAeAAEAAAASAMv/AwAeAAEAAAASAMz/AwAeAAEAAAASAM3/AwAeAAEAAAASAM7/AwAeAAEAAAASAM//AwAeAAEAAAASAND/AwAeAAEAAAATAMn/AwAoAAAAAGATAMr/AwAnAAAAAGATAMv/AwAoAAAAAGATAMz/AwAnAAAAAGATAM3/AwAoAAAAAGATAM7/AwAnAAAAAGATAM//AwAoAAAAAGATAND/AwAnAAAAAGAUAMn/AwAoAAEAAGAUAMr/AwAnAAEAAGAUAMv/AwAoAAEAAGAUAMz/AwAnAAEAAGAUAM3/AwAoAAEAAGAUAM7/AwAnAAEAAGAUAM//AwAoAAEAAGAUAND/AwAnAAEAAGAKABUAAwApAAAAAAAKABYAAwApAAEAAAAKABcAAwAeAAMAAAAKABgAAwAfAAQAAAAKABkAAwAqAAYAAAAKABoAAwAfAAUAAAAKABsAAwAfAAAAAAAKABwAAwAoAAIAAGAKAB0AAwAnAAIAAGALABUAAwAqAAAAAAALABYAAwAqAAEAAAALABcAAwAeAAMAAAALABgAAwAiAAUAAAALABkAAwAqAAYAAAALABoAAwAgAAUAAAALABsAAwAfAAAAAAALABwAAwAoAAMAAGALAB0AAwAnAAMAAGACABUAAwAnAAAAAAACABYAAwAnAAEAAAACABcAAwAeAAMAAAACABgAAwAgAAQAAAACABkAAwAoAAUAAAACABoAAwAgAAYAAAACABsAAwAfAAAAAAACABwAAwAnAAAAAAACAB0AAwAnAAEAAAADABUAAwAoAAAAAAADABYAAwAoAAEAAAADABcAAwAfAAMAAAADABgAAwAhAAQAAAADABkAAwAoAAUAAAADABoAAwAhAAYAAAADABsAAwAfAAAAAAADABwAAwAoAAAAAAADAB0AAwAoAAEAAAAEABUAAwAnAAAAAAAEABYAAwAnAAEAAAAEABcAAwAeAAMAAAAEABgAAwAiAAQAAAAEABkAAwAoAAUAAAAEABoAAwAiAAYAAAAEABsAAwAfAAAAAAAEABwAAwAnAAAAAAAEAB0AAwAnAAEAAAAFABUAAwAoAAAAAAAFABYAAwAoAAEAAAAFABcAAwAeAAMAAAAFABgAAwAeAAQAAAAFABkAAwAoAAUAAAAFABoAAwAeAAYAAAAFABsAAwAfAAAAAAAFABwAAwAoAAAAAAAFAB0AAwAoAAEAAAAGABUAAwAnAAAAAAAGABYAAwAnAAEAAAAGABcAAwAeAAMAAAAGABgAAwAfAAQAAAAGABkAAwAoAAUAAAAGABoAAwAfAAYAAAAGABsAAwAfAAAAAAAGABwAAwAnAAAAAAAGAB0AAwAnAAEAAAAHABUAAwAoAAAAAAAHABYAAwAoAAEAAAAHABcAAwAfAAMAAAAHABgAAwAgAAQAAAAHABkAAwAoAAUAAAAHABoAAwAgAAYAAAAHABsAAwAfAAAAAAAHABwAAwAoAAAAAAAHAB0AAwAoAAEAAAAIABUAAwAnAAAAAAAIABYAAwAnAAEAAAAIABcAAwAeAAMAAAAIABgAAwAhAAQAAAAIABkAAwAoAAUAAAAIABoAAwAhAAYAAAAIABsAAwAfAAAAAAAIABwAAwAnAAAAAAAIAB0AAwAnAAEAAAAJABUAAwAoAAAAAAAJABYAAwAoAAEAAAAJABcAAwAeAAMAAAAJABgAAwAiAAQAAAAJABkAAwAoAAUAAAAJABoAAwAiAAYAAAAJABsAAwAfAAAAAAAJABwAAwAoAAAAAAAJAB0AAwAoAAEAAAD6/xUAAwAoAAAAAAD6/xYAAwAoAAEAAAD6/xcAAwAeAAMAAAD6/xgAAwAiAAQAAAD6/xkAAwAoAAUAAAD6/xoAAwAiAAYAAAD6/xsAAwAfAAAAAAD6/xwAAwAoAAAAAAD6/x0AAwAoAAEAAAD7/xUAAwAoAAAAAAD7/xYAAwAoAAEAAAD7/xcAAwAfAAMAAAD7/xgAAwAhAAQAAAD7/xkAAwAoAAUAAAD7/xoAAwAhAAYAAAD7/xsAAwAfAAAAAAD7/xwAAwAoAAAAAAD7/x0AAwAoAAEAAAD8/xUAAwAnAAAAAAD8/xYAAwAnAAEAAAD8/xcAAwAeAAMAAAD8/xgAAwAiAAQAAAD8/xkAAwAoAAUAAAD8/xoAAwAiAAYAAAD8/xsAAwAfAAAAAAD8/xwAAwAnAAAAAAD8/x0AAwAnAAEAAAD9/xUAAwAoAAAAAAD9/xYAAwAoAAEAAAD9/xcAAwAeAAMAAAD9/xgAAwAeAAQAAAD9/xkAAwAoAAUAAAD9/xoAAwAeAAYAAAD9/xsAAwAfAAAAAAD9/xwAAwAoAAAAAAD9/x0AAwAoAAEAAAD+/xUAAwAnAAAAAAD+/xYAAwAnAAEAAAD+/xcAAwAeAAMAAAD+/xgAAwAfAAQAAAD+/xkAAwAoAAUAAAD+/xoAAwAfAAYAAAD+/xsAAwAfAAAAAAD+/xwAAwAnAAAAAAD+/x0AAwAnAAEAAAD//xUAAwAoAAAAAAD//xYAAwAoAAEAAAD//xcAAwAfAAMAAAD//xgAAwAgAAQAAAD//xkAAwAoAAUAAAD//xoAAwAgAAYAAAD//xsAAwAfAAAAAAD//xwAAwAoAAAAAAD//x0AAwAoAAEAAAAAABUAAwAnAAAAAAAAABYAAwAnAAEAAAAAABcAAwAeAAMAAAAAABgAAwAhAAQAAAAAABkAAwAoAAUAAAAAABoAAwAhAAYAAAAAABsAAwAfAAAAAAAAABwAAwAnAAAAAAAAAB0AAwAnAAEAAAABABUAAwAoAAAAAAABABYAAwAoAAEAAAABABcAAwAeAAMAAAABABgAAwAiAAQAAAABABkAAwAoAAUAAAABABoAAwAiAAYAAAABABsAAwAfAAAAAAABABwAAwAoAAAAAAABAB0AAwAoAAEAAADn/xUAAwApAAAAAADn/xYAAwApAAEAAADn/xcAAwAeAAMAAADn/xgAAwAfAAQAAADn/xkAAwAqAAYAAADn/xoAAwAfAAUAAADn/xsAAwAfAAAAAADn/xwAAwAoAAIAAGDn/x0AAwAnAAIAAGDo/xUAAwAqAAAAAADo/xYAAwAqAAEAAADo/xcAAwAeAAMAAADo/xgAAwAiAAUAAADo/xkAAwAqAAYAAADo/xoAAwAgAAUAAADo/xsAAwAfAAAAAADo/xwAAwAoAAMAAGDo/x0AAwAnAAMAAGDp/xMAAwAnAAAAAADp/xQAAwAnAAEAAADp/xUAAwAnAAAAAADp/xYAAwAnAAEAAADp/xcAAwAeAAMAAADp/xgAAwAeAAcAAADp/xkAAwAoAAUAAADp/xoAAwAeAAcAAADp/xsAAwAfAAAAAADp/xwAAwAnAAAAAADp/x0AAwAnAAEAAADp/x4AAwAnAAAAAADp/x8AAwAnAAEAAADq/xMAAwAoAAAAAADq/xQAAwAoAAEAAADq/xUAAwAoAAAAAADq/xYAAwAoAAEAAADq/xcAAwAfAAMAAADq/xgAAwAfAAQAAADq/xkAAwAoAAUAAADq/xoAAwAfAAYAAADq/xsAAwAfAAAAAADq/xwAAwAoAAAAAADq/x0AAwAoAAEAAADq/x4AAwAoAAAAAADq/x8AAwAoAAEAAADr/xMAAwAgAAEAAADr/xQAAwAgAAEAAADr/xUAAwAgAAEAAADr/xYAAwAgAAEAAADr/xcAAwAgAAIAAADr/xgAAwAgAAUAAADr/xkAAwAoAAUAAADr/xoAAwAiAAQAAADr/xsAAwAgAAAAAADr/xwAAwAgAAEAAADr/x0AAwAgAAEAAADr/x4AAwAgAAEAAADr/x8AAwAgAAEAAADs/xMAAwAfAAQAAADs/xQAAwAfAAUAAADs/xUAAwAfAAYAAADs/xYAAwAfAAcAAADs/xcAAwAiAAcAAADs/xgAAwAfAAQAAADs/xkAAwAoAAUAAADs/xoAAwAiAAUAAADs/xsAAwAgAAUAAADs/xwAAwAgAAUAAADs/x0AAwAhAAcAAADs/x4AAwAfAAUAAADs/x8AAwAhAAcAAADt/xMAAwApAAYAAADt/xQAAwApAAYAAADt/xUAAwApAAYAAADt/xYAAwApAAYAAADt/xcAAwApAAYAAADt/xgAAwApAAYAAADt/xkAAwAqAAUAAADt/xoAAwApAAYAAADt/xsAAwApAAYAAADt/xwAAwApAAYAAADt/x0AAwApAAYAAADt/x4AAwApAAYAAADt/x8AAwApAAYAAADu/xMAAwAeAAUAAADu/xQAAwAeAAQAAADu/xUAAwAeAAQAAADu/xYAAwAeAAUAAADu/xcAAwAhAAQAAADu/xgAAwAfAAcAAADu/xkAAwAoAAUAAADu/xoAAwAgAAYAAADu/xsAAwAhAAcAAADu/xwAAwAhAAQAAADu/x0AAwAiAAYAAADu/x4AAwAgAAYAAADu/x8AAwAfAAQAAADv/xMAAwAeAAEAAADv/xQAAwAeAAEAAADv/xUAAwAeAAEAAADv/xYAAwAeAAEAAADv/xcAAwAeAAIAAADv/xgAAwAgAAQAAADv/xkAAwAoAAUAAADv/xoAAwAfAAQAAADv/xsAAwAeAAAAAADv/xwAAwAeAAEAAADv/x0AAwAeAAEAAADv/x4AAwAeAAEAAADv/x8AAwAeAAEAAADw/xMAAwAnAAAAAADw/xQAAwAnAAEAAADw/xUAAwAnAAAAAADw/xYAAwAnAAEAAADw/xcAAwAeAAMAAADw/xgAAwAeAAQAAADw/xkAAwAoAAUAAADw/xoAAwAeAAYAAADw/xsAAwAfAAAAAADw/xwAAwAnAAAAAADw/x0AAwAnAAEAAADw/x4AAwAnAAAAAADw/x8AAwAnAAEAAADx/xMAAwAoAAAAAADx/xQAAwAoAAEAAADx/xUAAwAoAAAAAADx/xYAAwAoAAEAAADx/xcAAwAeAAMAAADx/xgAAwAfAAQAAADx/xkAAwAoAAUAAADx/xoAAwAfAAYAAADx/xsAAwAfAAAAAADx/xwAAwAoAAAAAADx/x0AAwAoAAEAAADx/x4AAwAoAAAAAADx/x8AAwAoAAEAAADy/xUAAwAnAAAAAADy/xYAAwAnAAEAAADy/xcAAwAeAAMAAADy/xgAAwAgAAQAAADy/xkAAwAoAAUAAADy/xoAAwAgAAYAAADy/xsAAwAfAAAAAADy/xwAAwAnAAAAAADy/x0AAwAnAAEAAADz/xUAAwAnAAAAAADz/xYAAwAnAAEAAADz/xcAAwAeAAMAAADz/xgAAwAgAAQAAADz/xkAAwAoAAUAAADz/xoAAwAgAAYAAADz/xsAAwAfAAAAAADz/xwAAwAnAAAAAADz/x0AAwAnAAEAAADp/xAAAwAnAAAAAGDp/xEAAwAoAAAAAGDp/xIAAwAnAAAAAGDq/xAAAwAnAAEAAGDq/xEAAwAoAAEAAGDq/xIAAwAnAAEAAGDr/xAAAwAgAAEAAADr/xEAAwAgAAEAAADr/xIAAwAgAAEAAADs/xAAAwAiAAQAAGDs/xEAAwAhAAQAAGDs/xIAAwAgAAQAAGDt/xAAAwAoAAUAAGDt/xEAAwAoAAUAAGDt/xIAAwAoAAUAAGDu/xAAAwAiAAYAAGDu/xEAAwAhAAYAAGDu/xIAAwAgAAYAAGDv/xAAAwAeAAEAAADv/xEAAwAeAAEAAADv/xIAAwAeAAEAAADw/xAAAwAnAAAAAGDw/xEAAwAoAAAAAGDw/xIAAwAnAAAAAGDx/xAAAwAnAAEAAGDx/xEAAwAoAAEAAGDx/xIAAwAnAAEAAGD0/xUAAwAoAAAAAAD0/xYAAwAoAAEAAAD0/xcAAwAfAAMAAAD0/xgAAwAhAAQAAAD0/xkAAwAoAAUAAAD0/xoAAwAhAAYAAAD0/xsAAwAfAAAAAAD0/xwAAwAoAAAAAAD0/x0AAwAoAAEAAAD1/xUAAwAnAAAAAAD1/xYAAwAnAAEAAAD1/xcAAwAeAAMAAAD1/xgAAwAiAAQAAAD1/xkAAwAoAAUAAAD1/xoAAwAiAAYAAAD1/xsAAwAfAAAAAAD1/xwAAwAnAAAAAAD1/x0AAwAnAAEAAAD2/xUAAwAoAAAAAAD2/xYAAwAoAAEAAAD2/xcAAwAeAAMAAAD2/xgAAwAeAAQAAAD2/xkAAwAoAAUAAAD2/xoAAwAeAAYAAAD2/xsAAwAfAAAAAAD2/xwAAwAoAAAAAAD2/x0AAwAoAAEAAAD3/xUAAwAnAAAAAAD3/xYAAwAnAAEAAAD3/xcAAwAeAAMAAAD3/xgAAwAfAAQAAAD3/xkAAwAoAAUAAAD3/xoAAwAfAAYAAAD3/xsAAwAfAAAAAAD3/xwAAwAnAAAAAAD3/x0AAwAnAAEAAAD4/xUAAwAoAAAAAAD4/xYAAwAoAAEAAAD4/xcAAwAfAAMAAAD4/xgAAwAgAAQAAAD4/xkAAwAoAAUAAAD4/xoAAwAgAAYAAAD4/xsAAwAfAAAAAAD4/xwAAwAoAAAAAAD4/x0AAwAoAAEAAAD5/xUAAwAnAAAAAAD5/xYAAwAnAAEAAAD5/xcAAwAeAAMAAAD5/xgAAwAhAAQAAAD5/xkAAwAoAAUAAAD5/xoAAwAhAAYAAAD5/xsAAwAfAAAAAAD5/xwAAwAnAAAAAAD5/x0AAwAnAAEAAADl//D/GAAJABsAAAAQAPr/AwAoAAUAAGADAO//AwAoAAAAAAADAPD/AwAoAAEAAAAEAO//AwAnAAAAAAAEAPD/AwAnAAEAAAABAO//AwAoAAAAAAABAPD/AwAoAAEAAAACAO//AwAnAAAAAAACAPD/AwAnAAEAAAD//+//AwAoAAAAAAD///D/AwAoAAEAAAAAAO//AwAnAAAAAAAAAPD/AwAnAAEAAAD8/+z/AwAnAAAAAAD8/+3/AwAnAAEAAAD8/+7/AwAnAAAAAAD8/+//AwAnAAAAAAD8//D/AwAnAAEAAAD8//H/AwAnAAEAAAAFAOv/AwAoAAEAAAAFAOz/AwAoAAAAAAAFAO3/AwAoAAEAAAAFAO7/AwAoAAAAAAAFAO//AwAoAAAAAAAFAPD/AwAoAAEAAAD0//n/AwAfAAEAAAD0//r/AwAfAAEAAAD1//n/AwAnAAEAAAD1//r/AwAnAAEAAAD1//v/AwAnAAEAAAD2//r/AwAoAAEAAAD2//v/AwAoAAEAAADy/+z/AwAnAAAAAADy/+3/AwAnAAEAAADz/+z/AwAoAAAAAADz/+3/AwAoAAEAAADy/+7/AwAnAAAAAADy/+//AwAnAAEAAADz/+7/AwAoAAAAAADz/+//AwAoAAEAAADy//D/AwAnAAAAAADy//H/AwAnAAEAAADz//D/AwAoAAAAAADz//H/AwAoAAEAAADy//L/AwAnAAAAAADy//P/AwAnAAEAAADz//L/AwAoAAAAAADz//P/AwAoAAEAAADy//T/AwAnAAAAAADy//X/AwAnAAEAAADz//T/AwAoAAAAAADz//X/AwAoAAEAAADy//b/AwAnAAAAAADy//f/AwAnAAEAAADz//b/AwAoAAAAAADz//f/AwAoAAEAAADz//n/AwAoAAEAAAD4/+z/AwAnAAAAAAD4/+3/AwAnAAEAAAD5/+z/AwAoAAAAAAD5/+3/AwAoAAEAAAD6/+z/AwAnAAAAAAD6/+3/AwAnAAEAAAD7/+z/AwAoAAAAAAD7/+3/AwAoAAEAAAD4/+7/AwAnAAAAAAD4/+//AwAnAAEAAAD5/+7/AwAoAAAAAAD5/+//AwAoAAEAAAD6/+7/AwAnAAAAAAD6/+//AwAnAAEAAAD7/+7/AwAoAAAAAAD7/+//AwAoAAEAAAD2//n/AwAoAAEAAAD3//r/AwAnAAEAAAD4//r/AwAnAAEAAAD5//n/AwAoAAAAAAD5//r/AwAoAAEAAAD6//n/AwAnAAAAAAD6//r/AwAnAAAAAAD7//n/AwAoAAAAAAD7//r/AwAoAAAAAAD8//n/AwAnAAAAAAD8//r/AwAnAAAAAAD9//n/AwAoAAAAAAD3//v/AwAnAAEAAAD4//v/AwAnAAEAAAD5//v/AwAoAAEAAAD6//v/AwAnAAEAAAD7//v/AwAoAAEAAAD8//v/AwAnAAEAAAD4/+X/AwAeAAMAAAD4/+b/AwAiAAQAAAD4/+f/AwAoAAUAAAD4/+j/AwAiAAYAAAD4/+n/AwAfAAAAAAD5/+X/AwAeAAMAAAD5/+b/AwAeAAQAAAD5/+f/AwAoAAUAAAD5/+j/AwAeAAYAAAD5/+n/AwAfAAAAAAAEAPn/AwAnAAAAAAAEAPr/AwAnAAAAAAAEAPv/AwAnAAEAAAD///r/AwAoAAAAAAD///v/AwAoAAEAAAAAAPr/AwAnAAAAAAAAAPv/AwAnAAEAAAABAPr/AwAoAAAAAAABAPv/AwAoAAEAAAD+//n/AwAnAAAAAAD///n/AwAoAAAAAAAAAPn/AwAnAAAAAAABAPn/AwAoAAAAAAACAPn/AwAnAAAAAAACAPr/AwAnAAAAAAADAPn/AwAnAAAAAAADAPr/AwAoAAAAAAACAPv/AwAnAAEAAAADAPv/AwAoAAEAAAD9//r/AwAJAAAAAAD9//v/AwAJAAEAAAD+//r/AwAJAAAAAAD+//v/AwAJAAEAAAD3//n/AwAnAAAAAAD4//n/AwAnAAAAAAADAPj/AwAnAAAAAAA=") tile_set = ExtResource("1_s310m") [node name="maisons" type="Node" parent="."] @@ -98,3 +169,6 @@ tile_set = ExtResource("1_s310m") [node name="grounded decorations" type="TileMapLayer" parent="."] tile_map_data = PackedByteArray("AADb/9X/DAAdADUAAADb/9b/DAAdADYAAADc/9X/DAAeADUAAADc/9b/DAAeADYAAADP/+7/DAAbADcAAADP/+//DAAbADgAAADQ/+7/DAAcADcAAADQ/+//DAAcADgAAAC9//L/DAAaADUAAAC+/9f/DAAaADUAAAC+//f/DAAaADUAAADH//r/DAAFADUAAADC//v/DAAGADUAAADn/+L/DAAaADcAAADn/+f/DAAaADcAAADh/+b/DAAaADcAAADj/+f/DAAaADUAAADk/+r/DAAaADcAAADj//D/DAAaADYAAADg//L/DAAaADcAAADb//D/DAAaADYAAADY//D/DAAaADYAAADW//b/DAAaADYAAADT//T/DAAaADYAAADS//T/DAAaADYAAADM//b/DAAaADcAAADG//f/DAAaADYAAADD//r/DAAaADYAAADG//r/DAAaADcAAADB//n/DAAaADYAAADG//D/DAAaADYAAADE/+7/DAAaADUAAADE/+3/DAAaADcAAADJ/+r/DAAaADcAAADK/+//DAAaADUAAADH/+z/DAAaADcAAADL/+3/DAAaADYAAADL/+7/DAAaADcAAADI/+z/DAAaADYAAADH/+//DAAaADUAAADL//L/DAAaADUAAADN//H/DAAaADUAAADN//D/DAAaADUAAADM/+H/DAAaADUAAADG/+L/DAAaADcAAADK/9z/DAAaADUAAADH/9//DAAaADcAAADO/93/DAAaADYAAADP/93/DAAaADUAAADQ/97/DAAaADUAAAC+/+H/DAAaADYAAADB/97/DAAaADUAAADA/+D/DAAaADUAAADA/+H/DAAaADYAAAC+/+P/DAAaADYAAAC8/+X/DAAaADcAAAC8/+r/DAAaADYAAAC8/+v/DAAaADcAAAC6/+j/DAAaADcAAAC8//D/DAAaADcAAAC///T/DAAaADUAAAC///X/DAAaADUAAAC9//j/DAAaADcAAAC6//r/DAAaADYAAAC+//v/DAAaADcAAADT//r/DAAaADUAAADS//r/DAAaADcAAADO//r/DAAaADYAAADd//j/DAAaADUAAADd//n/DAAaADcAAADY//f/DAAaADYAAADi//P/DAAaADYAAADn//L/DAAaADUAAADo//L/DAAaADYAAADn//X/DAAaADUAAADg//j/DAAaADcAAADm/+r/DAAaADcAAADn/+z/DAAaADYAAADk/+T/DAAaADcAAADm/+P/DAAaADcAAADn/97/DAAaADYAAADm/93/DAAaADcAAADj/9f/DAAaADUAAADg/9r/DAAaADYAAADl/9r/DAAaADcAAADo/9X/DAAaADUAAADU/+7/DAAHAKoAAADU/+//DAAHAKsAAADb//P/DAAHAKoAAADb//T/DAAHAKsAAADe/+v/DAAIAKoAAADe/+z/DAAIAKsAAADd/+v/DAAIAKoAAADd/+z/DAAIAKsAAADc/+z/DAAIAKoAAADc/+3/DAAIAKsAAADf/+z/DAAIAKoAAADf/+3/DAAIAKsAAADd//X/DAAHAKoAAADd//b/DAAHAKsAAADb//b/DAAHAKoAAADb//f/DAAHAKsAAADU/+z/DAAHAKoAAADU/+3/DAAHAKsAAADS/+7/DAAHAKoAAADS/+//DAAHAKsAAADE//T/DAAKAKwAAADE//X/DAAKAK0AAADF//T/DAALAKwAAADF//X/DAALAK0AAADA//D/DAAKAKwAAADA//H/DAAKAK0AAADB//D/DAALAKwAAADB//H/DAALAK0AAADU/93/DAAKAKwAAADU/97/DAAKAK0AAADV/93/DAALAKwAAADV/97/DAALAK0AAADU/+T/DAAMAKsAAADU/+X/DAAMAKwAAADV/+T/DAANAKsAAADV/+X/DAANAKwAAAD1//z/BAAYACAAAAD1//3/BAAYACEAAAD2//z/BAAZACAAAAD2//3/BAAZACEAAAD3//z/BAAaACAAAAD3//3/BAAaACEAAAABAPz/BAAYACAAAAABAP3/BAAYACEAAAACAPz/BAAZACAAAAACAP3/BAAZACEAAAADAPz/BAAaACAAAAADAP3/BAAaACEAAADq//H/BAAXABEAAADq/+r/BAAXABEAAADx/+T/BAAXABEAAAD3/+T/BAAXABEAAAAFAOT/BAAXABEAAAAKAOT/BAAXABEAAAD5//3/BAAXABEAAAAHAP3/BAAXABEAAAD4/wMABAAXABEAAAABAAMABAAXABIAAAAJAAMABAAXABEAAAAYAP3/BAAXABIAAAAbAAMABAAXABIAAAA=") tile_set = ExtResource("1_s310m") + +[node name="debug" type="TileMapLayer" parent="."] +tile_set = SubResource("TileSet_l1bso") diff --git a/project.godot b/project.godot index b84765c..ffc427a 100644 --- a/project.godot +++ b/project.godot @@ -15,12 +15,20 @@ run/main_scene="res://scenes/start.tscn" config/features=PackedStringArray("4.3", "GL Compatibility") config/icon="res://icon.svg" +[autoload] + +DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd" + [display] window/size/viewport_width=1920 window/size/viewport_height=1080 window/stretch/mode="viewport" +[editor_plugins] + +enabled=PackedStringArray("res://addons/dialogue_manager/plugin.cfg") + [file_customization] folder_colors={} @@ -53,6 +61,10 @@ grab={ ] } +[internationalization] + +locale/translations_pot_files=PackedStringArray("res://caracters/bob/bob.dialogue") + [layer_names] 2d_physics/layer_1="impact" @@ -60,6 +72,7 @@ grab={ 2d_physics/layer_2="freinage" 2d_navigation/layer_2="véhicules" 2d_physics/layer_3="déclencheurs" +2d_physics/layer_4="npc" [rendering] diff --git a/scenes/pathFollow.gd b/scenes/pathFollow.gd index 91a7725..500b059 100644 --- a/scenes/pathFollow.gd +++ b/scenes/pathFollow.gd @@ -14,8 +14,9 @@ func _ready() -> void: astar_grid = AStarGrid2D.new() astar_grid.region = world.get_used_rect() astar_grid.cell_size = Vector2(48, 48) - astar_grid.set_default_compute_heuristic(AStarGrid2D.HEURISTIC_MANHATTAN) - astar_grid.set_default_estimate_heuristic(AStarGrid2D.HEURISTIC_MANHATTAN) + astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE + astar_grid.default_compute_heuristic = AStarGrid2D.HEURISTIC_MANHATTAN + astar_grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_MANHATTAN astar_grid.update() # only take into account the tiles that are marked with a navigation layer for cars diff --git a/scenes/start.tscn b/scenes/start.tscn index 83c4e4b..002442a 100644 --- a/scenes/start.tscn +++ b/scenes/start.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=7 format=3 uid="uid://b4ydi1vv8dvwr"] +[gd_scene load_steps=8 format=3 uid="uid://b4ydi1vv8dvwr"] [ext_resource type="PackedScene" uid="uid://d1oqt6sbjvopi" path="res://maps/world.tscn" id="1_6vs81"] [ext_resource type="PackedScene" uid="uid://vclpg4e4ql54" path="res://caracters/player/player.tscn" id="2_5x6b5"] [ext_resource type="PackedScene" uid="uid://cl201baro5y5" path="res://vehicules/npc_car.tscn" id="3_yuakw"] [ext_resource type="PackedScene" uid="uid://bt1p311rn1h6q" path="res://vehicules/car.tscn" id="4_bqm78"] +[ext_resource type="PackedScene" uid="uid://bleadp4yrdgj" path="res://caracters/bob/bob.tscn" id="5_n64eb"] [sub_resource type="Curve2D" id="Curve2D_shblg"] _data = { @@ -21,10 +22,13 @@ point_count = 20 [node name="world" parent="." instance=ExtResource("1_6vs81")] +[node name="bob" parent="world" instance=ExtResource("5_n64eb")] +position = Vector2(-333, -262) + [node name="movibles" type="Node2D" parent="."] [node name="player" parent="movibles" instance=ExtResource("2_5x6b5")] -position = Vector2(87, 74) +position = Vector2(48, -79) [node name="cars" type="Node" parent="movibles"]