更新 dialogue manager v3.4.0 for Godot 4.4
This commit is contained in:
parent
996d826877
commit
a0792afb81
1
addons/debug_menu/debug_menu.gd.uid
Normal file
1
addons/debug_menu/debug_menu.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://pjsl6kq3jfwh
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"]
|
[gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"]
|
[ext_resource type="Script" uid="uid://pjsl6kq3jfwh" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"]
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"]
|
||||||
bg_color = Color(0, 0, 0, 0.25098)
|
bg_color = Color(0, 0, 0, 0.25098)
|
||||||
|
1
addons/debug_menu/plugin.gd.uid
Normal file
1
addons/debug_menu/plugin.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://7hcv3wxdt3h0
|
@ -16,13 +16,15 @@ namespace DialogueManagerRuntime
|
|||||||
PO
|
PO
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class DialogueManager : Node
|
public partial class DialogueManager : RefCounted
|
||||||
{
|
{
|
||||||
|
public delegate void DialogueStartedEventHandler(Resource dialogueResource);
|
||||||
public delegate void PassedTitleEventHandler(string title);
|
public delegate void PassedTitleEventHandler(string title);
|
||||||
public delegate void GotDialogueEventHandler(DialogueLine dialogueLine);
|
public delegate void GotDialogueEventHandler(DialogueLine dialogueLine);
|
||||||
public delegate void MutatedEventHandler(Dictionary mutation);
|
public delegate void MutatedEventHandler(Dictionary mutation);
|
||||||
public delegate void DialogueEndedEventHandler(Resource dialogueResource);
|
public delegate void DialogueEndedEventHandler(Resource dialogueResource);
|
||||||
|
|
||||||
|
public static DialogueStartedEventHandler? DialogueStarted;
|
||||||
public static PassedTitleEventHandler? PassedTitle;
|
public static PassedTitleEventHandler? PassedTitle;
|
||||||
public static GotDialogueEventHandler? GotDialogue;
|
public static GotDialogueEventHandler? GotDialogue;
|
||||||
public static MutatedEventHandler? Mutated;
|
public static MutatedEventHandler? Mutated;
|
||||||
@ -38,6 +40,7 @@ namespace DialogueManagerRuntime
|
|||||||
if (instance == null)
|
if (instance == null)
|
||||||
{
|
{
|
||||||
instance = Engine.GetSingleton("DialogueManager");
|
instance = Engine.GetSingleton("DialogueManager");
|
||||||
|
instance.Connect("bridge_dialogue_started", Callable.From((Resource dialogueResource) => DialogueStarted?.Invoke(dialogueResource)));
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@ -86,11 +89,6 @@ namespace DialogueManagerRuntime
|
|||||||
instance.Connect("dialogue_ended", Callable.From((Resource dialogueResource) => DialogueEnded?.Invoke(dialogueResource)));
|
instance.Connect("dialogue_ended", Callable.From((Resource dialogueResource) => DialogueEnded?.Invoke(dialogueResource)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Prepare()
|
|
||||||
{
|
|
||||||
Prepare(Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static async Task<GodotObject> GetSingleton()
|
public static async Task<GodotObject> GetSingleton()
|
||||||
{
|
{
|
||||||
@ -170,16 +168,32 @@ namespace DialogueManagerRuntime
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool ThingHasMethod(GodotObject thing, string method)
|
public bool ThingHasMethod(GodotObject thing, string method, Array<Variant> args)
|
||||||
{
|
{
|
||||||
MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
|
var methodInfos = thing.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly);
|
||||||
return info != null;
|
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<Variant> args)
|
public async void ResolveThingMethod(GodotObject thing, string method, Array<Variant> args)
|
||||||
{
|
{
|
||||||
MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
|
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;
|
if (info == null) return;
|
||||||
|
|
||||||
@ -216,18 +230,15 @@ namespace DialogueManagerRuntime
|
|||||||
|
|
||||||
if (result is Task taskResult)
|
if (result is Task taskResult)
|
||||||
{
|
{
|
||||||
// await Tasks and handle result if it is a Task<T>
|
|
||||||
await taskResult;
|
await taskResult;
|
||||||
var taskType = taskResult.GetType();
|
try
|
||||||
if (taskType.IsGenericType && taskType.GetGenericTypeDefinition() == typeof(Task<>))
|
|
||||||
{
|
{
|
||||||
var resultProperty = taskType.GetProperty("Result");
|
Variant value = (Variant)taskResult.GetType().GetProperty("Result").GetValue(taskResult);
|
||||||
var taskResultValue = resultProperty.GetValue(taskResult);
|
EmitSignal(SignalName.Resolved, value);
|
||||||
EmitSignal(SignalName.Resolved, (Variant)taskResultValue);
|
|
||||||
}
|
}
|
||||||
else
|
catch (Exception err)
|
||||||
{
|
{
|
||||||
EmitSignal(SignalName.Resolved, null);
|
EmitSignal(SignalName.Resolved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -313,6 +324,12 @@ namespace DialogueManagerRuntime
|
|||||||
get => inline_mutations;
|
get => inline_mutations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Array<DialogueLine> concurrent_lines = new Array<DialogueLine>();
|
||||||
|
public Array<DialogueLine> ConcurrentLines
|
||||||
|
{
|
||||||
|
get => concurrent_lines;
|
||||||
|
}
|
||||||
|
|
||||||
private Array<Variant> extra_game_states = new Array<Variant>();
|
private Array<Variant> extra_game_states = new Array<Variant>();
|
||||||
public Array<Variant> ExtraGameStates
|
public Array<Variant> ExtraGameStates
|
||||||
{
|
{
|
||||||
@ -338,6 +355,11 @@ namespace DialogueManagerRuntime
|
|||||||
time = (string)data.Get("time");
|
time = (string)data.Get("time");
|
||||||
tags = (Array<string>)data.Get("tags");
|
tags = (Array<string>)data.Get("tags");
|
||||||
|
|
||||||
|
foreach (var concurrent_line_data in (Array<RefCounted>)data.Get("concurrent_lines"))
|
||||||
|
{
|
||||||
|
concurrent_lines.Add(new DialogueLine(concurrent_line_data));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var response in (Array<RefCounted>)data.Get("responses"))
|
foreach (var response in (Array<RefCounted>)data.Get("responses"))
|
||||||
{
|
{
|
||||||
responses.Add(new DialogueResponse(response));
|
responses.Add(new DialogueResponse(response));
|
||||||
|
1
addons/dialogue_manager/DialogueManager.cs.uid
Normal file
1
addons/dialogue_manager/DialogueManager.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c4c5lsrwy3opj
|
1078
addons/dialogue_manager/compiler/compilation.gd
Normal file
1078
addons/dialogue_manager/compiler/compilation.gd
Normal file
File diff suppressed because it is too large
Load Diff
1
addons/dialogue_manager/compiler/compilation.gd.uid
Normal file
1
addons/dialogue_manager/compiler/compilation.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dsgpnyqg6cprg
|
157
addons/dialogue_manager/compiler/compiled_line.gd
Normal file
157
addons/dialogue_manager/compiler/compiled_line.gd
Normal file
@ -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
|
1
addons/dialogue_manager/compiler/compiled_line.gd.uid
Normal file
1
addons/dialogue_manager/compiler/compiled_line.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dg8j5hudp4210
|
51
addons/dialogue_manager/compiler/compiler.gd
Normal file
51
addons/dialogue_manager/compiler/compiler.gd
Normal file
@ -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
|
1
addons/dialogue_manager/compiler/compiler.gd.uid
Normal file
1
addons/dialogue_manager/compiler/compiler.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://chtfdmr0cqtp4
|
49
addons/dialogue_manager/compiler/compiler_regex.gd
Normal file
49
addons/dialogue_manager/compiler/compiler_regex.gd
Normal file
@ -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 \"(?<path>[^\"]+)\" as (?<prefix>[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 (?<state>.*)$")
|
||||||
|
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) (?<expression>.*)\\:?")
|
||||||
|
var WRAPPED_CONDITION_REGEX: RegEx = RegEx.create_from_string("\\[if (?<expression>.*)\\]")
|
||||||
|
var MUTATION_REGEX: RegEx = RegEx.create_from_string("(?<keyword>do|do!|set) (?<expression>.*)")
|
||||||
|
var STATIC_LINE_ID_REGEX: RegEx = RegEx.create_from_string("\\[ID:(?<id>.*?)\\]")
|
||||||
|
var WEIGHTED_RANDOM_SIBLINGS_REGEX: RegEx = RegEx.create_from_string("^\\%(?<weight>[\\d.]+)?( \\[if (?<condition>.+?)\\])? ")
|
||||||
|
var GOTO_REGEX: RegEx = RegEx.create_from_string("=><? (?<goto>.*)")
|
||||||
|
|
||||||
|
var INLINE_RANDOM_REGEX: RegEx = RegEx.create_from_string("\\[\\[(?<options>.*?)\\]\\]")
|
||||||
|
var INLINE_CONDITIONALS_REGEX: RegEx = RegEx.create_from_string("\\[if (?<condition>.+?)\\](?<body>.*?)\\[\\/if\\]")
|
||||||
|
|
||||||
|
var TAGS_REGEX: RegEx = RegEx.create_from_string("\\[#(?<tags>.*?)\\]")
|
||||||
|
|
||||||
|
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)")
|
||||||
|
}
|
1
addons/dialogue_manager/compiler/compiler_regex.gd.uid
Normal file
1
addons/dialogue_manager/compiler/compiler_regex.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://d3tvcrnicjibp
|
27
addons/dialogue_manager/compiler/compiler_result.gd
Normal file
27
addons/dialogue_manager/compiler/compiler_result.gd
Normal file
@ -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 = ""
|
1
addons/dialogue_manager/compiler/compiler_result.gd.uid
Normal file
1
addons/dialogue_manager/compiler/compiler_result.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dmk74tknimqvg
|
497
addons/dialogue_manager/compiler/expression_parser.gd
Normal file
497
addons/dialogue_manager/compiler/expression_parser.gd
Normal file
@ -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
|
@ -0,0 +1 @@
|
|||||||
|
uid://dbi4hbar8ubwu
|
68
addons/dialogue_manager/compiler/resolved_goto_data.gd
Normal file
68
addons/dialogue_manager/compiler/resolved_goto_data.gd
Normal file
@ -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]
|
@ -0,0 +1 @@
|
|||||||
|
uid://llhl5pt47eoq
|
167
addons/dialogue_manager/compiler/resolved_line_data.gd
Normal file
167
addons/dialogue_manager/compiler/resolved_line_data.gd
Normal file
@ -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
|
@ -0,0 +1 @@
|
|||||||
|
uid://0k6q8kukq0qa
|
26
addons/dialogue_manager/compiler/resolved_tag_data.gd
Normal file
26
addons/dialogue_manager/compiler/resolved_tag_data.gd
Normal file
@ -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
|
@ -0,0 +1 @@
|
|||||||
|
uid://cqai3ikuilqfq
|
44
addons/dialogue_manager/compiler/tree_line.gd
Normal file
44
addons/dialogue_manager/compiler/tree_line.gd
Normal file
@ -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",
|
||||||
|
"}"])
|
1
addons/dialogue_manager/compiler/tree_line.gd.uid
Normal file
1
addons/dialogue_manager/compiler/tree_line.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dsu4i84dpif14
|
@ -1,5 +1,5 @@
|
|||||||
@tool
|
@tool
|
||||||
extends CodeEdit
|
class_name DMCodeEdit extends CodeEdit
|
||||||
|
|
||||||
|
|
||||||
signal active_title_change(title: String)
|
signal active_title_change(title: String)
|
||||||
@ -7,10 +7,6 @@ signal error_clicked(line_number: int)
|
|||||||
signal external_file_requested(path: String, title: String)
|
signal external_file_requested(path: String, title: String)
|
||||||
|
|
||||||
|
|
||||||
const DialogueManagerParser = preload("./parser.gd")
|
|
||||||
const DialogueSyntaxHighlighter = preload("./code_edit_syntax_highlighter.gd")
|
|
||||||
|
|
||||||
|
|
||||||
# A link back to the owner `MainView`
|
# A link back to the owner `MainView`
|
||||||
var main_view
|
var main_view
|
||||||
|
|
||||||
@ -19,7 +15,7 @@ var theme_overrides: Dictionary:
|
|||||||
set(value):
|
set(value):
|
||||||
theme_overrides = value
|
theme_overrides = value
|
||||||
|
|
||||||
syntax_highlighter = DialogueSyntaxHighlighter.new()
|
syntax_highlighter = DMSyntaxHighlighter.new()
|
||||||
|
|
||||||
# General UI
|
# General UI
|
||||||
add_theme_color_override("font_color", theme_overrides.text_color)
|
add_theme_color_override("font_color", theme_overrides.text_color)
|
||||||
@ -67,7 +63,7 @@ func _ready() -> void:
|
|||||||
if not has_comment_delimiter("#"):
|
if not has_comment_delimiter("#"):
|
||||||
add_comment_delimiter("#", "", true)
|
add_comment_delimiter("#", "", true)
|
||||||
|
|
||||||
syntax_highlighter = DialogueSyntaxHighlighter.new()
|
syntax_highlighter = DMSyntaxHighlighter.new()
|
||||||
|
|
||||||
|
|
||||||
func _gui_input(event: InputEvent) -> void:
|
func _gui_input(event: InputEvent) -> void:
|
||||||
@ -162,17 +158,18 @@ func _request_code_completion(force: bool) -> void:
|
|||||||
add_code_completion_option(CodeEdit.KIND_CLASS, "END!", "END!".substr(prompt.length()), theme_overrides.text_color, get_theme_icon("Stop", "EditorIcons"))
|
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
|
# Get all titles, including those in imports
|
||||||
var parser: DialogueManagerParser = DialogueManagerParser.new()
|
for title: String in DMCompiler.get_titles_in_text(text, main_view.current_file_path):
|
||||||
parser.prepare(text, main_view.current_file_path, false)
|
# Ignore any imported titles that aren't resolved to human readable.
|
||||||
for title in parser.titles:
|
if title.to_int() > 0:
|
||||||
if "/" in title:
|
continue
|
||||||
|
|
||||||
|
elif "/" in title:
|
||||||
var bits = title.split("/")
|
var bits = title.split("/")
|
||||||
if matches_prompt(prompt, bits[0]) or matches_prompt(prompt, bits[1]):
|
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"))
|
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):
|
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"))
|
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)
|
update_code_completion_options(true)
|
||||||
parser.free()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
var name_so_far: String = WEIGHTED_RANDOM_PREFIX.sub(current_line.strip_edges(), "")
|
var name_so_far: String = WEIGHTED_RANDOM_PREFIX.sub(current_line.strip_edges(), "")
|
||||||
@ -232,6 +229,7 @@ func get_titles() -> PackedStringArray:
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
if line.strip_edges().begins_with("~ "):
|
if line.strip_edges().begins_with("~ "):
|
||||||
titles.append(line.strip_edges().substr(2))
|
titles.append(line.strip_edges().substr(2))
|
||||||
|
|
||||||
return titles
|
return titles
|
||||||
|
|
||||||
|
|
||||||
@ -270,6 +268,11 @@ func get_character_names(beginning_with: String) -> PackedStringArray:
|
|||||||
|
|
||||||
# Mark a line as an error or not
|
# Mark a line as an error or not
|
||||||
func mark_line_as_error(line_number: int, is_error: bool) -> void:
|
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:
|
if is_error:
|
||||||
set_line_background_color(line_number, theme_overrides.error_line_color)
|
set_line_background_color(line_number, theme_overrides.error_line_color)
|
||||||
set_line_gutter_icon(line_number, 0, get_theme_icon("StatusError", "EditorIcons"))
|
set_line_gutter_icon(line_number, 0, get_theme_icon("StatusError", "EditorIcons"))
|
||||||
|
1
addons/dialogue_manager/components/code_edit.gd.uid
Normal file
1
addons/dialogue_manager/components/code_edit.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://djeybvlb332mp
|
@ -1,7 +1,7 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://civ6shmka5e8u"]
|
[gd_scene load_steps=4 format=3 uid="uid://civ6shmka5e8u"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/code_edit_syntax_highlighter.gd" id="1_58cfo"]
|
[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" path="res://addons/dialogue_manager/components/code_edit.gd" id="1_g324i"]
|
[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"]
|
[sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_cobxx"]
|
||||||
script = ExtResource("1_58cfo")
|
script = ExtResource("1_58cfo")
|
||||||
|
@ -1,55 +1,18 @@
|
|||||||
@tool
|
@tool
|
||||||
extends SyntaxHighlighter
|
class_name DMSyntaxHighlighter extends SyntaxHighlighter
|
||||||
|
|
||||||
|
|
||||||
const DialogueManagerParser = preload("./parser.gd")
|
var regex: DMCompilerRegEx = DMCompilerRegEx.new()
|
||||||
|
var compilation: DMCompilation = DMCompilation.new()
|
||||||
|
var expression_parser = DMExpressionParser.new()
|
||||||
enum ExpressionType {DO, SET, IF}
|
|
||||||
|
|
||||||
|
|
||||||
var dialogue_manager_parser: DialogueManagerParser = DialogueManagerParser.new()
|
|
||||||
|
|
||||||
var regex_titles: RegEx = RegEx.create_from_string("^\\s*(?<title>~\\s+[^\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\\-\\=\\+\\{\\}\\[\\]\\;\\:\\\"\\'\\,\\.\\<\\>\\?\\/\\s]+)")
|
|
||||||
var regex_comments: RegEx = RegEx.create_from_string("(?:(?>\"(?:\\\\\"|[^\"\\n])*\")[^\"\\n]*?\\s*(?<comment>#[^\\n]*)$|^[^\"#\\n]*?\\s*(?<comment2>#[^\\n]*))")
|
|
||||||
var regex_mutation: RegEx = RegEx.create_from_string("^\\s*(do|do!|set) (?<mutation>.*)")
|
|
||||||
var regex_condition: RegEx = RegEx.create_from_string("^\\s*(if|elif|while|else if) (?<condition>.*)")
|
|
||||||
var regex_wcondition: RegEx = RegEx.create_from_string("\\[if (?<condition>((?:[^\\[\\]]*)|(?:\\[(?1)\\]))*?)\\]")
|
|
||||||
var regex_wendif: RegEx = RegEx.create_from_string("\\[(\\/if|else)\\]")
|
|
||||||
var regex_rgroup: RegEx = RegEx.create_from_string("\\[\\[(?<options>.*?)\\]\\]")
|
|
||||||
var regex_endconditions: RegEx = RegEx.create_from_string("^\\s*(endif|else):?\\s*$")
|
|
||||||
var regex_tags: RegEx = RegEx.create_from_string("\\[(?<tag>(?!(?:ID:.*)|if)[a-zA-Z_][a-zA-Z0-9_]*!?)(?:[= ](?<val>[^\\[\\]]+))?\\](?:(?<text>(?!\\[\\/\\k<tag>\\]).*?)?(?<end>\\[\\/\\k<tag>\\]))?")
|
|
||||||
var regex_dialogue: RegEx = RegEx.create_from_string("^\\s*(?:(?<random>\\%[\\d.]* )|(?<response>- ))?(?:(?<character>[^#:]*): )?(?<dialogue>.*)$")
|
|
||||||
var regex_goto: RegEx = RegEx.create_from_string("=><? (?:(?<file>[^\\/]+)\\/)?(?<title>[^\\/]*)")
|
|
||||||
var regex_string: RegEx = RegEx.create_from_string("^&?(?<delimiter>[\"'])(?<content>(?:\\\\{2})*|(?:.*?[^\\\\](?:\\\\{2})*))\\1$")
|
|
||||||
var regex_escape: RegEx = RegEx.create_from_string("\\\\.")
|
|
||||||
var regex_number: RegEx = RegEx.create_from_string("^-?(?:(?:0x(?:[0-9A-Fa-f]{2})+)|(?:0b[01]+)|(?:\\d+(?:(?:[\\.]\\d*)?(?:e\\d+)?)|(?:_\\d+)+)?)$")
|
|
||||||
var regex_array: RegEx = RegEx.create_from_string("\\[((?>[^\\[\\]]+|(?R))*)\\]")
|
|
||||||
var regex_dict: RegEx = RegEx.create_from_string("^\\{((?>[^\\{\\}]+|(?R))*)\\}$")
|
|
||||||
var regex_kvdict: RegEx = RegEx.create_from_string("^\\s*(?<left>.*?)\\s*(?<colon>:|=)\\s*(?<right>[^\\/]+)$")
|
|
||||||
var regex_commas: RegEx = RegEx.create_from_string("([^,]+)(?:\\s*,\\s*)?")
|
|
||||||
var regex_assignment: RegEx = RegEx.create_from_string("^\\s*(?<var>[a-zA-Z_][a-zA-Z_0-9]*)(?:(?<attr>(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)+)|(?:\\[(?<key>[^\\]]+)\\]))?\\s*(?<op>(?:\\/|\\*|-|\\+)?=)\\s*(?<val>.*)$")
|
|
||||||
var regex_varname: RegEx = RegEx.create_from_string("^\\s*(?!true|false|and|or|&&|\\|\\|not|in|null)(?<var>[a-zA-Z_][a-zA-Z_0-9]*)(?:(?<attr>(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)+)|(?:\\[(?<key>[^\\]]+)\\]))?\\s*$")
|
|
||||||
var regex_keyword: RegEx = RegEx.create_from_string("^\\s*(true|false|null)\\s*$")
|
|
||||||
var regex_function: RegEx = RegEx.create_from_string("^\\s*([a-zA-Z_][a-zA-Z_0-9]*\\s*)\\(")
|
|
||||||
var regex_comparison: RegEx = RegEx.create_from_string("^(?<left>.*?)\\s*(?<op>==|>=|<=|<|>|!=)\\s*(?<right>.*)$")
|
|
||||||
var regex_blogical: RegEx = RegEx.create_from_string("^(?<left>.*?)\\s+(?<op>and|or|in|&&|\\|\\|)\\s+(?<right>.*)$")
|
|
||||||
var regex_ulogical: RegEx = RegEx.create_from_string("^\\s*(?<op>not)\\s+(?<right>.*)$")
|
|
||||||
var regex_paren: RegEx = RegEx.create_from_string("\\((?<paren>((?:[^\\(\\)]*)|(?:\\((?1)\\)))*?)\\)")
|
|
||||||
|
|
||||||
var cache: Dictionary = {}
|
var cache: Dictionary = {}
|
||||||
|
|
||||||
|
|
||||||
func _notification(what: int) -> void:
|
|
||||||
if what == NOTIFICATION_PREDELETE:
|
|
||||||
dialogue_manager_parser.free()
|
|
||||||
|
|
||||||
|
|
||||||
func _clear_highlighting_cache() -> void:
|
func _clear_highlighting_cache() -> void:
|
||||||
cache = {}
|
cache.clear()
|
||||||
|
|
||||||
|
|
||||||
## Returns the syntax coloring for a dialogue file line
|
|
||||||
func _get_line_syntax_highlighting(line: int) -> Dictionary:
|
func _get_line_syntax_highlighting(line: int) -> Dictionary:
|
||||||
var colors: Dictionary = {}
|
var colors: Dictionary = {}
|
||||||
var text_edit: TextEdit = get_text_edit()
|
var text_edit: TextEdit = get_text_edit()
|
||||||
@ -63,323 +26,183 @@ func _get_line_syntax_highlighting(line: int) -> Dictionary:
|
|||||||
if text in cache:
|
if text in cache:
|
||||||
return cache[text]
|
return cache[text]
|
||||||
|
|
||||||
# Comments have to be removed to make the remaining processing easier.
|
var theme: Dictionary = text_edit.theme_overrides
|
||||||
# Count both end-of-line and single-line comments
|
|
||||||
# Comments are not allowed within dialogue lines or response lines, so we ask the parser what it thinks the current line is
|
|
||||||
if not (dialogue_manager_parser.is_dialogue_line(text) or dialogue_manager_parser.is_response_line(text)) or dialogue_manager_parser.is_line_empty(text) or dialogue_manager_parser.is_import_line(text):
|
|
||||||
var comment_matches: Array[RegExMatch] = regex_comments.search_all(text)
|
|
||||||
for comment_match in comment_matches:
|
|
||||||
for i in ["comment", "comment2"]:
|
|
||||||
if i in comment_match.names:
|
|
||||||
colors[comment_match.get_start(i)] = {"color": text_edit.theme_overrides.comments_color}
|
|
||||||
text = text.substr(0, comment_match.get_start(i))
|
|
||||||
|
|
||||||
# Dialogues
|
var index: int = 0
|
||||||
var dialogue_matches: Array[RegExMatch] = regex_dialogue.search_all(text)
|
|
||||||
for dialogue_match in dialogue_matches:
|
|
||||||
if "random" in dialogue_match.names:
|
|
||||||
colors[dialogue_match.get_start("random")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[dialogue_match.get_end("random")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
if "response" in dialogue_match.names:
|
|
||||||
colors[dialogue_match.get_start("response")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[dialogue_match.get_end("response")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
if "character" in dialogue_match.names:
|
|
||||||
colors[dialogue_match.get_start("character")] = {"color": text_edit.theme_overrides.members_color}
|
|
||||||
colors[dialogue_match.get_end("character")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors.merge(_get_dialogue_syntax_highlighting(dialogue_match.get_start("dialogue"), dialogue_match.get_string("dialogue")), true)
|
|
||||||
|
|
||||||
# Title lines
|
match DMCompiler.get_line_type(text):
|
||||||
if dialogue_manager_parser.is_title_line(text):
|
DMConstants.TYPE_COMMENT:
|
||||||
var title_matches: Array[RegExMatch] = regex_titles.search_all(text)
|
colors[index] = { color = theme.comments_color }
|
||||||
for title_match in title_matches:
|
|
||||||
colors[title_match.get_start("title")] = {"color": text_edit.theme_overrides.titles_color}
|
|
||||||
|
|
||||||
# Import lines
|
DMConstants.TYPE_TITLE:
|
||||||
var import_matches: Array[RegExMatch] = dialogue_manager_parser.IMPORT_REGEX.search_all(text)
|
colors[index] = { color = theme.titles_color }
|
||||||
for import_match in import_matches:
|
|
||||||
colors[import_match.get_start(0)] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
colors[import_match.get_start("path") - 1] = {"color": text_edit.theme_overrides.strings_color}
|
|
||||||
colors[import_match.get_end("path") + 1] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
colors[import_match.get_start("prefix")] = {"color": text_edit.theme_overrides.members_color}
|
|
||||||
colors[import_match.get_end("prefix")] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
|
|
||||||
# Using clauses
|
DMConstants.TYPE_CONDITION, DMConstants.TYPE_WHILE, DMConstants.TYPE_MATCH, DMConstants.TYPE_WHEN:
|
||||||
var using_matches: Array[RegExMatch] = dialogue_manager_parser.USING_REGEX.search_all(text)
|
colors[0] = { color = theme.conditions_color }
|
||||||
for using_match in using_matches:
|
index = text.find(" ")
|
||||||
colors[using_match.get_start(0)] = {"color": text_edit.theme_overrides.conditions_color}
|
if index > -1:
|
||||||
colors[using_match.get_start("state") - 1] = {"color": text_edit.theme_overrides.text_color}
|
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)
|
||||||
|
|
||||||
# Condition keywords and expressions
|
DMConstants.TYPE_MUTATION:
|
||||||
var condition_matches: Array[RegExMatch] = regex_condition.search_all(text)
|
colors[0] = { color = theme.mutations_color }
|
||||||
for condition_match in condition_matches:
|
index = text.find(" ")
|
||||||
colors[condition_match.get_start(0)] = {"color": text_edit.theme_overrides.conditions_color}
|
var expression: Array = expression_parser.tokenise(text.substr(index), DMConstants.TYPE_MUTATION, 0)
|
||||||
colors[condition_match.get_end(1)] = {"color": text_edit.theme_overrides.text_color}
|
if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR:
|
||||||
colors.merge(_get_expression_syntax_highlighting(condition_match.get_start("condition"), ExpressionType.IF, condition_match.get_string("condition")), true)
|
colors[index] = { color = theme.critical_color }
|
||||||
# endif/else
|
else:
|
||||||
var endcondition_matches: Array[RegExMatch] = regex_endconditions.search_all(text)
|
_highlight_expression(expression, colors, index)
|
||||||
for endcondition_match in endcondition_matches:
|
|
||||||
colors[endcondition_match.get_start(1)] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
colors[endcondition_match.get_end(1)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
|
|
||||||
# Mutations
|
DMConstants.TYPE_GOTO:
|
||||||
var mutation_matches: Array[RegExMatch] = regex_mutation.search_all(text)
|
if text.strip_edges().begins_with("%"):
|
||||||
for mutation_match in mutation_matches:
|
colors[index] = { color = theme.symbols_color }
|
||||||
colors[mutation_match.get_start(0)] = {"color": text_edit.theme_overrides.mutations_color}
|
index = text.find(" ")
|
||||||
colors.merge(_get_expression_syntax_highlighting(mutation_match.get_start("mutation"), ExpressionType.DO if mutation_match.strings[1].begins_with("do") else ExpressionType.SET, mutation_match.get_string("mutation")), true)
|
_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
|
# Order the dictionary keys to prevent CodeEdit from having issues
|
||||||
var new_colors: Dictionary = {}
|
var ordered_colors: Dictionary = {}
|
||||||
var ordered_keys: Array = colors.keys()
|
var ordered_keys: Array = colors.keys()
|
||||||
ordered_keys.sort()
|
ordered_keys.sort()
|
||||||
for index in ordered_keys:
|
for key_index: int in ordered_keys:
|
||||||
new_colors[index] = colors[index]
|
ordered_colors[key_index] = colors[key_index]
|
||||||
|
|
||||||
cache[text] = new_colors
|
cache[text] = ordered_colors
|
||||||
return new_colors
|
return ordered_colors
|
||||||
|
|
||||||
|
|
||||||
## Return the syntax highlighting for a dialogue line
|
func _highlight_expression(tokens: Array, colors: Dictionary, index: int) -> int:
|
||||||
func _get_dialogue_syntax_highlighting(start_index: int, text: String) -> Dictionary:
|
var theme: Dictionary = get_text_edit().theme_overrides
|
||||||
var text_edit: TextEdit = get_text_edit()
|
var last_index: int = index
|
||||||
var colors: Dictionary = {}
|
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 }
|
||||||
|
|
||||||
# #tag style tags
|
DMConstants.TOKEN_VARIABLE:
|
||||||
var hashtag_matches: Array[RegExMatch] = dialogue_manager_parser.TAGS_REGEX.search_all(text)
|
if token.value in ["true", "false"]:
|
||||||
for hashtag_match in hashtag_matches:
|
colors[index + token.i] = { color = theme.conditions_color }
|
||||||
colors[start_index + hashtag_match.get_start(0)] = { "color": text_edit.theme_overrides.comments_color }
|
else:
|
||||||
colors[start_index + hashtag_match.get_end(0)] = { "color": text_edit.theme_overrides.text_color }
|
colors[index + token.i] = { color = theme.members_color }
|
||||||
|
|
||||||
# bbcode-like global tags
|
DMConstants.TOKEN_OPERATOR, DMConstants.TOKEN_COLON, DMConstants.TOKEN_COMMA, DMConstants.TOKEN_NUMBER, DMConstants.TOKEN_ASSIGNMENT:
|
||||||
var tag_matches: Array[RegExMatch] = regex_tags.search_all(text)
|
colors[index + token.i] = { color = theme.symbols_color }
|
||||||
for tag_match in tag_matches:
|
|
||||||
colors[start_index + tag_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
if "val" in tag_match.names:
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + tag_match.get_start("val"), tag_match.get_string("val")), true)
|
|
||||||
colors[start_index + tag_match.get_end("val")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
# Show the text color straight in the editor for better ease-of-use
|
|
||||||
if tag_match.get_string("tag") == "color":
|
|
||||||
colors[start_index + tag_match.get_start("val")] = {"color": Color.from_string(tag_match.get_string("val"), text_edit.theme_overrides.text_color)}
|
|
||||||
if "text" in tag_match.names:
|
|
||||||
colors[start_index + tag_match.get_start("text")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
# Text can still contain tags if several effects are applied ([center][b]Something[/b][/center], so recursing
|
|
||||||
colors.merge(_get_dialogue_syntax_highlighting(start_index + tag_match.get_start("text"), tag_match.get_string("text")), true)
|
|
||||||
colors[start_index + tag_match.get_end("text")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
if "end" in tag_match.names:
|
|
||||||
colors[start_index + tag_match.get_start("end")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + tag_match.get_end("end")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors[start_index + tag_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
|
|
||||||
# ID tag
|
DMConstants.TOKEN_STRING:
|
||||||
var translation_matches: Array[RegExMatch] = dialogue_manager_parser.TRANSLATION_REGEX.search_all(text)
|
colors[index + token.i] = { color = theme.strings_color }
|
||||||
for translation_match in translation_matches:
|
|
||||||
colors[start_index + translation_match.get_start(0)] = {"color": text_edit.theme_overrides.comments_color}
|
|
||||||
colors[start_index + translation_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
|
|
||||||
# Replacements
|
DMConstants.TOKEN_FUNCTION:
|
||||||
var replacement_matches: Array[RegExMatch] = dialogue_manager_parser.REPLACEMENTS_REGEX.search_all(text)
|
colors[index + token.i] = { color = theme.mutations_color }
|
||||||
for replacement_match in replacement_matches:
|
colors[index + token.i + token.function.length()] = { color = theme.symbols_color }
|
||||||
colors[start_index + replacement_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
for parameter: Array in token.value:
|
||||||
colors[start_index + replacement_match.get_start(1)] = {"color": text_edit.theme_overrides.text_color}
|
last_index = _highlight_expression(parameter, colors, index)
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + replacement_match.get_start(1), replacement_match.strings[1]), true)
|
DMConstants.TOKEN_PARENS_CLOSE:
|
||||||
colors[start_index + replacement_match.get_end(1)] = {"color": text_edit.theme_overrides.symbols_color}
|
colors[index + token.i] = { color = theme.symbols_color }
|
||||||
colors[start_index + replacement_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
|
|
||||||
# Jump at the end of a response
|
DMConstants.TOKEN_DICTIONARY_REFERENCE:
|
||||||
var goto_matches: Array[RegExMatch] = regex_goto.search_all(text)
|
colors[index + token.i] = { color = theme.members_color }
|
||||||
for goto_match in goto_matches:
|
colors[index + token.i + token.variable.length()] = { color = theme.symbols_color }
|
||||||
colors[start_index + goto_match.get_start(0)] = {"color": text_edit.theme_overrides.jumps_color}
|
last_index = _highlight_expression(token.value, colors, index)
|
||||||
if "file" in goto_match.names:
|
DMConstants.TOKEN_ARRAY:
|
||||||
colors[start_index + goto_match.get_start("file")] = {"color": text_edit.theme_overrides.jumps_color}
|
colors[index + token.i] = { color = theme.symbols_color }
|
||||||
colors[start_index + goto_match.get_end("file")] = {"color": text_edit.theme_overrides.symbols_color}
|
for item: Array in token.value:
|
||||||
colors[start_index + goto_match.get_start("title")] = {"color": text_edit.theme_overrides.jumps_color}
|
last_index = _highlight_expression(item, colors, index)
|
||||||
colors[start_index + goto_match.get_end("title")] = {"color": text_edit.theme_overrides.jumps_color}
|
DMConstants.TOKEN_BRACKET_CLOSE:
|
||||||
colors[start_index + goto_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
colors[index + token.i] = { color = theme.symbols_color }
|
||||||
|
|
||||||
# Wrapped condition
|
DMConstants.TOKEN_DICTIONARY:
|
||||||
var wcondition_matches: Array[RegExMatch] = regex_wcondition.search_all(text)
|
colors[index + token.i] = { color = theme.symbols_color }
|
||||||
for wcondition_match in wcondition_matches:
|
last_index = _highlight_expression(token.value.keys() + token.value.values(), colors, index)
|
||||||
colors[start_index + wcondition_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
DMConstants.TOKEN_BRACE_CLOSE:
|
||||||
colors[start_index + wcondition_match.get_start(0) + 1] = {"color": text_edit.theme_overrides.conditions_color}
|
colors[index + token.i] = { color = theme.symbols_color }
|
||||||
colors[start_index + wcondition_match.get_start(0) + 3] = {"color": text_edit.theme_overrides.text_color}
|
last_index += 1
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + wcondition_match.get_start("condition"), wcondition_match.get_string("condition")), true)
|
|
||||||
colors[start_index + wcondition_match.get_end("condition")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + wcondition_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
# [/if] tag for color matching with the opening tag
|
|
||||||
var wendif_matches: Array[RegExMatch] = regex_wendif.search_all(text)
|
|
||||||
for wendif_match in wendif_matches:
|
|
||||||
colors[start_index + wendif_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + wendif_match.get_start(1)] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
colors[start_index + wendif_match.get_end(1)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + wendif_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
|
|
||||||
# Random groups
|
DMConstants.TOKEN_GROUP:
|
||||||
var rgroup_matches: Array[RegExMatch] = regex_rgroup.search_all(text)
|
last_index = _highlight_expression(token.value, colors, index)
|
||||||
for rgroup_match in rgroup_matches:
|
|
||||||
colors[start_index + rgroup_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + rgroup_match.get_start("options")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
var separator_matches: Array[RegExMatch] = RegEx.create_from_string("\\|").search_all(rgroup_match.get_string("options"))
|
|
||||||
for separator_match in separator_matches:
|
|
||||||
colors[start_index + rgroup_match.get_start("options") + separator_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + rgroup_match.get_start("options") + separator_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors[start_index + rgroup_match.get_end("options")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + rgroup_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
|
|
||||||
return colors
|
return last_index
|
||||||
|
|
||||||
|
|
||||||
## Returns the syntax highlighting for an expression (mutation set/do, or condition)
|
func _highlight_goto(text: String, colors: Dictionary, index: int) -> int:
|
||||||
func _get_expression_syntax_highlighting(start_index: int, type: ExpressionType, text: String) -> Dictionary:
|
var theme: Dictionary = get_text_edit().theme_overrides
|
||||||
var text_edit: TextEdit = get_text_edit()
|
var goto_data: DMResolvedGotoData = DMResolvedGotoData.new(text, {})
|
||||||
var colors: Dictionary = {}
|
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 }
|
||||||
|
|
||||||
if type == ExpressionType.SET:
|
return index
|
||||||
var assignment_matches: Array[RegExMatch] = regex_assignment.search_all(text)
|
|
||||||
for assignment_match in assignment_matches:
|
|
||||||
colors[start_index + assignment_match.get_start("var")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
if "attr" in assignment_match.names:
|
|
||||||
colors[start_index + assignment_match.get_start("attr")] = {"color": text_edit.theme_overrides.members_color}
|
|
||||||
colors[start_index + assignment_match.get_end("attr")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
if "key" in assignment_match.names:
|
|
||||||
# Braces are outside of the key, so coloring them symbols_color
|
|
||||||
colors[start_index + assignment_match.get_start("key") - 1] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + assignment_match.get_start("key"), assignment_match.get_string("key")), true)
|
|
||||||
colors[start_index + assignment_match.get_end("key")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + assignment_match.get_end("key") + 1] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
|
|
||||||
colors[start_index + assignment_match.get_start("op")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + assignment_match.get_end("op")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + assignment_match.get_start("val"), assignment_match.get_string("val")), true)
|
|
||||||
else:
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index, text), true)
|
|
||||||
|
|
||||||
return colors
|
|
||||||
|
|
||||||
|
|
||||||
## Return the syntax highlighting for a literal
|
|
||||||
## For this purpose, "literal" refers to a regular code line that could be used to get a value out of:
|
|
||||||
## - function calls
|
|
||||||
## - real literals (bool, string, int, float, etc.)
|
|
||||||
## - logical operators (>, <, >=, or, and, not, etc.)
|
|
||||||
func _get_literal_syntax_highlighting(start_index: int, text: String) -> Dictionary:
|
|
||||||
var text_edit: TextEdit = get_text_edit()
|
|
||||||
var colors: Dictionary = {}
|
|
||||||
|
|
||||||
# Remove spaces at start/end of the literal
|
|
||||||
var text_length: int = text.length()
|
|
||||||
text = text.lstrip(" ")
|
|
||||||
start_index += text_length - text.length()
|
|
||||||
text = text.rstrip(" ")
|
|
||||||
|
|
||||||
# Parenthesis expression.
|
|
||||||
var paren_matches: Array[RegExMatch] = regex_paren.search_all(text)
|
|
||||||
for paren_match in paren_matches:
|
|
||||||
colors[start_index + paren_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + paren_match.get_start(0) + 1] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + paren_match.get_start("paren"), paren_match.get_string("paren")), true)
|
|
||||||
colors[start_index + paren_match.get_end(0) - 1] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
|
|
||||||
# Strings
|
|
||||||
var string_matches: Array[RegExMatch] = regex_string.search_all(text)
|
|
||||||
for string_match in string_matches:
|
|
||||||
colors[start_index + string_match.get_start(0)] = {"color": text_edit.theme_overrides.strings_color}
|
|
||||||
if "content" in string_match.names:
|
|
||||||
var escape_matches: Array[RegExMatch] = regex_escape.search_all(string_match.get_string("content"))
|
|
||||||
for escape_match in escape_matches:
|
|
||||||
colors[start_index + string_match.get_start("content") + escape_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + string_match.get_start("content") + escape_match.get_end(0)] = {"color": text_edit.theme_overrides.strings_color}
|
|
||||||
|
|
||||||
# Numbers
|
|
||||||
var number_matches: Array[RegExMatch] = regex_number.search_all(text)
|
|
||||||
for number_match in number_matches:
|
|
||||||
colors[start_index + number_match.get_start(0)] = {"color": text_edit.theme_overrides.numbers_color}
|
|
||||||
|
|
||||||
# Arrays
|
|
||||||
var array_matches: Array[RegExMatch] = regex_array.search_all(text)
|
|
||||||
for array_match in array_matches:
|
|
||||||
colors[start_index + array_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors.merge(_get_list_syntax_highlighting(start_index + array_match.get_start(1), array_match.strings[1]), true)
|
|
||||||
colors[start_index + array_match.get_end(1)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
|
|
||||||
# Dictionaries
|
|
||||||
var dict_matches: Array[RegExMatch] = regex_dict.search_all(text)
|
|
||||||
for dict_match in dict_matches:
|
|
||||||
colors[start_index + dict_match.get_start(0)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors.merge(_get_list_syntax_highlighting(start_index + dict_match.get_start(1), dict_match.strings[1]), true)
|
|
||||||
colors[start_index + dict_match.get_end(1)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
|
|
||||||
# Dictionary key: value pairs
|
|
||||||
var kvdict_matches: Array[RegExMatch] = regex_kvdict.search_all(text)
|
|
||||||
for kvdict_match in kvdict_matches:
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + kvdict_match.get_start("left"), kvdict_match.get_string("left")), true)
|
|
||||||
colors[start_index + kvdict_match.get_start("colon")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + kvdict_match.get_end("colon")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + kvdict_match.get_start("right"), kvdict_match.get_string("right")), true)
|
|
||||||
|
|
||||||
# Booleans
|
|
||||||
var bool_matches: Array[RegExMatch] = regex_keyword.search_all(text)
|
|
||||||
for bool_match in bool_matches:
|
|
||||||
colors[start_index + bool_match.get_start(0)] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
|
|
||||||
# Functions
|
|
||||||
var function_matches: Array[RegExMatch] = regex_function.search_all(text)
|
|
||||||
for function_match in function_matches:
|
|
||||||
var last_brace_index: int = text.rfind(")")
|
|
||||||
colors[start_index + function_match.get_start(1)] = {"color": text_edit.theme_overrides.mutations_color}
|
|
||||||
colors[start_index + function_match.get_end(1)] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors.merge(_get_list_syntax_highlighting(start_index + function_match.get_end(0), text.substr(function_match.get_end(0), last_brace_index - function_match.get_end(0))), true)
|
|
||||||
colors[start_index + last_brace_index] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
var varname_matches: Array[RegExMatch] = regex_varname.search_all(text)
|
|
||||||
for varname_match in varname_matches:
|
|
||||||
colors[start_index + varname_match.get_start("var")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
if "attr" in varname_match.names:
|
|
||||||
colors[start_index + varname_match.get_start("attr")] = {"color": text_edit.theme_overrides.members_color}
|
|
||||||
colors[start_index + varname_match.get_end("attr")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
if "key" in varname_match.names:
|
|
||||||
# Braces are outside of the key, so coloring them symbols_color
|
|
||||||
colors[start_index + varname_match.get_start("key") - 1] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + varname_match.get_start("key"), varname_match.get_string("key")), true)
|
|
||||||
colors[start_index + varname_match.get_end("key")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
|
|
||||||
# Comparison operators
|
|
||||||
var comparison_matches: Array[RegExMatch] = regex_comparison.search_all(text)
|
|
||||||
for comparison_match in comparison_matches:
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + comparison_match.get_start("left"), comparison_match.get_string("left")), true)
|
|
||||||
colors[start_index + comparison_match.get_start("op")] = {"color": text_edit.theme_overrides.symbols_color}
|
|
||||||
colors[start_index + comparison_match.get_end("op")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
var right = comparison_match.get_string("right")
|
|
||||||
if right.ends_with(":"):
|
|
||||||
right = right.substr(0, right.length() - 1)
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + comparison_match.get_start("right"), right), true)
|
|
||||||
colors[start_index + comparison_match.get_start("right") + right.length()] = { "color": text_edit.theme_overrides.symbols_color }
|
|
||||||
|
|
||||||
# Logical binary operators
|
|
||||||
var blogical_matches: Array[RegExMatch] = regex_blogical.search_all(text)
|
|
||||||
for blogical_match in blogical_matches:
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + blogical_match.get_start("left"), blogical_match.get_string("left")), true)
|
|
||||||
colors[start_index + blogical_match.get_start("op")] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
colors[start_index + blogical_match.get_end("op")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + blogical_match.get_start("right"), blogical_match.get_string("right")), true)
|
|
||||||
|
|
||||||
# Logical unary operators
|
|
||||||
var ulogical_matches: Array[RegExMatch] = regex_ulogical.search_all(text)
|
|
||||||
for ulogical_match in ulogical_matches:
|
|
||||||
colors[start_index + ulogical_match.get_start("op")] = {"color": text_edit.theme_overrides.conditions_color}
|
|
||||||
colors[start_index + ulogical_match.get_end("op")] = {"color": text_edit.theme_overrides.text_color}
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + ulogical_match.get_start("right"), ulogical_match.get_string("right")), true)
|
|
||||||
|
|
||||||
return colors
|
|
||||||
|
|
||||||
|
|
||||||
## Returns the syntax coloring for a list of literals separated by commas
|
|
||||||
func _get_list_syntax_highlighting(start_index: int, text: String) -> Dictionary:
|
|
||||||
var text_edit: TextEdit = get_text_edit()
|
|
||||||
var colors: Dictionary = {}
|
|
||||||
|
|
||||||
# Comma-separated list of literals (for arrays and function arguments)
|
|
||||||
var element_matches: Array[RegExMatch] = regex_commas.search_all(text)
|
|
||||||
for element_match in element_matches:
|
|
||||||
colors.merge(_get_literal_syntax_highlighting(start_index + element_match.get_start(1), element_match.strings[1]), true)
|
|
||||||
|
|
||||||
return colors
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://klpiq4tk3t7a
|
@ -34,7 +34,7 @@ func _ready() -> void:
|
|||||||
|
|
||||||
func _on_download_button_pressed() -> void:
|
func _on_download_button_pressed() -> void:
|
||||||
# Safeguard the actual dialogue manager repo from accidentally updating itself
|
# Safeguard the actual dialogue manager repo from accidentally updating itself
|
||||||
if FileAccess.file_exists("res://examples/test_scenes/test_scene.gd"):
|
if FileAccess.file_exists("res://tests/test_basic_dialogue.gd"):
|
||||||
prints("You can't update the addon from within itself.")
|
prints("You can't update the addon from within itself.")
|
||||||
failed.emit()
|
failed.emit()
|
||||||
return
|
return
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://kpwo418lb2t2
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://qdxrxv3c3hxk"]
|
[gd_scene load_steps=3 format=3 uid="uid://qdxrxv3c3hxk"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/download_update_panel.gd" id="1_4tm1k"]
|
[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"]
|
[ext_resource type="Texture2D" uid="uid://d3baj6rygkb3f" path="res://addons/dialogue_manager/assets/update.svg" id="2_4o2m6"]
|
||||||
|
|
||||||
[node name="DownloadUpdatePanel" type="Control"]
|
[node name="DownloadUpdatePanel" type="Control"]
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://nyypeje1a036
|
@ -63,7 +63,7 @@ func build_menu() -> void:
|
|||||||
func _on_new_dialog_file_selected(path: String) -> void:
|
func _on_new_dialog_file_selected(path: String) -> void:
|
||||||
editor_plugin.main_view.new_file(path)
|
editor_plugin.main_view.new_file(path)
|
||||||
is_waiting_for_file = false
|
is_waiting_for_file = false
|
||||||
if Engine.get_meta("DialogueCache").has_file(path):
|
if Engine.get_meta("DMCache").has_file(path):
|
||||||
resource_changed.emit(load(path))
|
resource_changed.emit(load(path))
|
||||||
else:
|
else:
|
||||||
var next_resource: Resource = await editor_plugin.import_plugin.compiled_resource
|
var next_resource: Resource = await editor_plugin.import_plugin.compiled_resource
|
||||||
@ -81,7 +81,7 @@ func _on_file_dialog_canceled() -> void:
|
|||||||
|
|
||||||
func _on_resource_button_pressed() -> void:
|
func _on_resource_button_pressed() -> void:
|
||||||
if is_instance_valid(resource):
|
if is_instance_valid(resource):
|
||||||
editor_plugin.get_editor_interface().call_deferred("edit_resource", resource)
|
EditorInterface.call_deferred("edit_resource", resource)
|
||||||
else:
|
else:
|
||||||
build_menu()
|
build_menu()
|
||||||
menu.position = get_viewport().position + Vector2i(
|
menu.position = get_viewport().position + Vector2i(
|
||||||
@ -112,7 +112,7 @@ func _on_menu_id_pressed(id: int) -> void:
|
|||||||
|
|
||||||
ITEM_QUICK_LOAD:
|
ITEM_QUICK_LOAD:
|
||||||
quick_selected_file = ""
|
quick_selected_file = ""
|
||||||
files_list.files = Engine.get_meta("DialogueCache").get_files()
|
files_list.files = Engine.get_meta("DMCache").get_files()
|
||||||
if resource:
|
if resource:
|
||||||
files_list.select_file(resource.resource_path)
|
files_list.select_file(resource.resource_path)
|
||||||
quick_open_dialog.popup_centered()
|
quick_open_dialog.popup_centered()
|
||||||
@ -123,13 +123,13 @@ func _on_menu_id_pressed(id: int) -> void:
|
|||||||
open_dialog.popup_centered()
|
open_dialog.popup_centered()
|
||||||
|
|
||||||
ITEM_EDIT:
|
ITEM_EDIT:
|
||||||
editor_plugin.get_editor_interface().call_deferred("edit_resource", resource)
|
EditorInterface.call_deferred("edit_resource", resource)
|
||||||
|
|
||||||
ITEM_CLEAR:
|
ITEM_CLEAR:
|
||||||
resource_changed.emit(null)
|
resource_changed.emit(null)
|
||||||
|
|
||||||
ITEM_FILESYSTEM:
|
ITEM_FILESYSTEM:
|
||||||
var file_system = editor_plugin.get_editor_interface().get_file_system_dock()
|
var file_system = EditorInterface.get_file_system_dock()
|
||||||
file_system.navigate_to_path(resource.resource_path)
|
file_system.navigate_to_path(resource.resource_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://dooe2pflnqtve
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://ycn6uaj7dsrh"]
|
[gd_scene load_steps=4 format=3 uid="uid://ycn6uaj7dsrh"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/editor_property/editor_property_control.gd" id="1_het12"]
|
[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://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"]
|
[ext_resource type="PackedScene" uid="uid://dnufpcdrreva3" path="res://addons/dialogue_manager/components/files_list.tscn" id="3_l8fp6"]
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://damhqta55t67c
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://b16uuqjuof3n5"]
|
[gd_scene load_steps=2 format=3 uid="uid://b16uuqjuof3n5"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/editor_property/resource_button.gd" id="1_7u2i7"]
|
[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"]
|
[node name="ResourceButton" type="Button"]
|
||||||
offset_right = 8.0
|
offset_right = 8.0
|
||||||
|
@ -59,7 +59,7 @@ func show_error() -> void:
|
|||||||
show()
|
show()
|
||||||
count_label.text = DialogueConstants.translate(&"n_of_n").format({ index = error_index + 1, total = errors.size() })
|
count_label.text = DialogueConstants.translate(&"n_of_n").format({ index = error_index + 1, total = errors.size() })
|
||||||
var error = errors[error_index]
|
var error = errors[error_index]
|
||||||
error_button.text = DialogueConstants.translate(&"errors.line_and_message").format({ line = error.line_number + 1, column = error.column_number, message = DialogueConstants.get_error_message(error.error) })
|
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"):
|
if error.has("external_error"):
|
||||||
error_button.text += " " + DialogueConstants.get_error_message(error.external_error)
|
error_button.text += " " + DialogueConstants.get_error_message(error.external_error)
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ func _on_errors_panel_theme_changed() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _on_error_button_pressed() -> void:
|
func _on_error_button_pressed() -> void:
|
||||||
emit_signal("error_pressed", errors[error_index].line_number, errors[error_index].column_number)
|
error_pressed.emit(errors[error_index].line_number, errors[error_index].column_number)
|
||||||
|
|
||||||
|
|
||||||
func _on_previous_button_pressed() -> void:
|
func _on_previous_button_pressed() -> void:
|
||||||
|
1
addons/dialogue_manager/components/errors_panel.gd.uid
Normal file
1
addons/dialogue_manager/components/errors_panel.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://d2l8nlb6hhrfp
|
@ -1,8 +1,8 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://cs8pwrxr5vxix"]
|
[gd_scene load_steps=4 format=3 uid="uid://cs8pwrxr5vxix"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/errors_panel.gd" id="1_nfm3c"]
|
[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_wy5pj"]
|
[sub_resource type="Image" id="Image_w0gko"]
|
||||||
data = {
|
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),
|
"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",
|
"format": "RGBA8",
|
||||||
@ -12,7 +12,7 @@ data = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id="ImageTexture_s6fxl"]
|
[sub_resource type="ImageTexture" id="ImageTexture_s6fxl"]
|
||||||
image = SubResource("Image_wy5pj")
|
image = SubResource("Image_w0gko")
|
||||||
|
|
||||||
[node name="ErrorsPanel" type="HBoxContainer"]
|
[node name="ErrorsPanel" type="HBoxContainer"]
|
||||||
visible = false
|
visible = false
|
||||||
|
1
addons/dialogue_manager/components/files_list.gd.uid
Normal file
1
addons/dialogue_manager/components/files_list.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dqa4a4wwoo0aa
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://dnufpcdrreva3"]
|
[gd_scene load_steps=3 format=3 uid="uid://dnufpcdrreva3"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/files_list.gd" id="1_cytii"]
|
[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"]
|
[ext_resource type="Texture2D" uid="uid://d3lr2uas6ax8v" path="res://addons/dialogue_manager/assets/icon.svg" id="2_3ijx1"]
|
||||||
|
|
||||||
[node name="FilesList" type="VBoxContainer"]
|
[node name="FilesList" type="VBoxContainer"]
|
||||||
|
@ -114,7 +114,7 @@ func find_in_files() -> Dictionary:
|
|||||||
var results: Dictionary = {}
|
var results: Dictionary = {}
|
||||||
|
|
||||||
var q: String = input.text
|
var q: String = input.text
|
||||||
var cache = Engine.get_meta("DialogueCache")
|
var cache = Engine.get_meta("DMCache")
|
||||||
var file: FileAccess
|
var file: FileAccess
|
||||||
for path in cache.get_files():
|
for path in cache.get_files():
|
||||||
var path_results: Array = []
|
var path_results: Array = []
|
||||||
|
1
addons/dialogue_manager/components/find_in_files.gd.uid
Normal file
1
addons/dialogue_manager/components/find_in_files.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://q368fmxxa8sd
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://0n7hwviyyly4"]
|
[gd_scene load_steps=3 format=3 uid="uid://0n7hwviyyly4"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/find_in_files.gd" id="1_3xicy"]
|
[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"]
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_owohg"]
|
||||||
bg_color = Color(0.266667, 0.278431, 0.352941, 0.243137)
|
bg_color = Color(0.266667, 0.278431, 0.352941, 0.243137)
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
class_name DialogueManagerParseResult extends RefCounted
|
|
||||||
|
|
||||||
var imported_paths: PackedStringArray = []
|
|
||||||
var using_states: PackedStringArray = []
|
|
||||||
var titles: Dictionary = {}
|
|
||||||
var character_names: PackedStringArray = []
|
|
||||||
var first_title: String = ""
|
|
||||||
var lines: Dictionary = {}
|
|
||||||
var errors: Array[Dictionary] = []
|
|
||||||
var raw_text: String = ""
|
|
File diff suppressed because it is too large
Load Diff
@ -1,15 +0,0 @@
|
|||||||
extends RefCounted
|
|
||||||
|
|
||||||
var text: String = ""
|
|
||||||
var pauses: Dictionary = {}
|
|
||||||
var speeds: Dictionary = {}
|
|
||||||
var mutations: Array[Array] = []
|
|
||||||
var time: String = ""
|
|
||||||
|
|
||||||
|
|
||||||
func _init(data: Dictionary) -> void:
|
|
||||||
text = data.text
|
|
||||||
pauses = data.pauses
|
|
||||||
speeds = data.speeds
|
|
||||||
mutations = data.mutations
|
|
||||||
time = data.time
|
|
@ -1,10 +0,0 @@
|
|||||||
extends RefCounted
|
|
||||||
|
|
||||||
|
|
||||||
var tags: PackedStringArray = []
|
|
||||||
var line_without_tags: String = ""
|
|
||||||
|
|
||||||
|
|
||||||
func _init(data: Dictionary) -> void:
|
|
||||||
tags = data.tags
|
|
||||||
line_without_tags = data.line_without_tags
|
|
@ -0,0 +1 @@
|
|||||||
|
uid://cijsmjkq21cdq
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://gr8nakpbrhby"]
|
[gd_scene load_steps=2 format=3 uid="uid://gr8nakpbrhby"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/search_and_replace.gd" id="1_8oj1f"]
|
[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"]
|
[node name="SearchAndReplace" type="VBoxContainer"]
|
||||||
visible = false
|
visible = false
|
||||||
|
1
addons/dialogue_manager/components/title_list.gd.uid
Normal file
1
addons/dialogue_manager/components/title_list.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://d0k2wndjj0ifm
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://ctns6ouwwd68i"]
|
[gd_scene load_steps=2 format=3 uid="uid://ctns6ouwwd68i"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/title_list.gd" id="1_5qqmd"]
|
[ext_resource type="Script" uid="uid://d0k2wndjj0ifm" path="res://addons/dialogue_manager/components/title_list.gd" id="1_5qqmd"]
|
||||||
|
|
||||||
[node name="TitleList" type="VBoxContainer"]
|
[node name="TitleList" type="VBoxContainer"]
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
@ -86,9 +86,9 @@ func _on_update_button_pressed() -> void:
|
|||||||
if needs_reload:
|
if needs_reload:
|
||||||
var will_refresh = on_before_refresh.call()
|
var will_refresh = on_before_refresh.call()
|
||||||
if will_refresh:
|
if will_refresh:
|
||||||
Engine.get_meta("DialogueManagerPlugin").get_editor_interface().restart_editor(true)
|
EditorInterface.restart_editor(true)
|
||||||
else:
|
else:
|
||||||
var scale: float = Engine.get_meta("DialogueManagerPlugin").get_editor_interface().get_editor_scale()
|
var scale: float = EditorInterface.get_editor_scale()
|
||||||
download_dialog.min_size = Vector2(300, 250) * scale
|
download_dialog.min_size = Vector2(300, 250) * scale
|
||||||
download_dialog.popup_centered()
|
download_dialog.popup_centered()
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ func _on_download_update_panel_failed() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _on_needs_reload_dialog_confirmed() -> void:
|
func _on_needs_reload_dialog_confirmed() -> void:
|
||||||
Engine.get_meta("DialogueManagerPlugin").get_editor_interface().restart_editor(true)
|
EditorInterface.restart_editor(true)
|
||||||
|
|
||||||
|
|
||||||
func _on_timer_timeout() -> void:
|
func _on_timer_timeout() -> void:
|
||||||
|
1
addons/dialogue_manager/components/update_button.gd.uid
Normal file
1
addons/dialogue_manager/components/update_button.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cr1tt12dh5ecr
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://co8yl23idiwbi"]
|
[gd_scene load_steps=3 format=3 uid="uid://co8yl23idiwbi"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/components/update_button.gd" id="1_d2tpb"]
|
[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"]
|
[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"]
|
[node name="UpdateButton" type="Button"]
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
extends Node
|
class_name DMConstants extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
const USER_CONFIG_PATH = "user://dialogue_manager_user_config.json"
|
const USER_CONFIG_PATH = "user://dialogue_manager_user_config.json"
|
||||||
const CACHE_PATH = "user://dialogue_manager_cache.json"
|
const CACHE_PATH = "user://dialogue_manager_cache.json"
|
||||||
|
|
||||||
|
|
||||||
|
enum MutationBehaviour {
|
||||||
|
Wait,
|
||||||
|
DoNotWait,
|
||||||
|
Skip
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TranslationSource {
|
||||||
|
None,
|
||||||
|
Guess,
|
||||||
|
CSV,
|
||||||
|
PO
|
||||||
|
}
|
||||||
|
|
||||||
# Token types
|
# Token types
|
||||||
|
|
||||||
const TOKEN_FUNCTION = &"function"
|
const TOKEN_FUNCTION = &"function"
|
||||||
@ -33,21 +47,26 @@ const TOKEN_NUMBER = &"number"
|
|||||||
const TOKEN_VARIABLE = &"variable"
|
const TOKEN_VARIABLE = &"variable"
|
||||||
const TOKEN_COMMENT = &"comment"
|
const TOKEN_COMMENT = &"comment"
|
||||||
|
|
||||||
|
const TOKEN_VALUE = &"value"
|
||||||
const TOKEN_ERROR = &"error"
|
const TOKEN_ERROR = &"error"
|
||||||
|
|
||||||
# Line types
|
# Line types
|
||||||
|
|
||||||
const TYPE_UNKNOWN = &"unknown"
|
const TYPE_UNKNOWN = &""
|
||||||
|
const TYPE_IMPORT = &"import"
|
||||||
|
const TYPE_COMMENT = &"comment"
|
||||||
const TYPE_RESPONSE = &"response"
|
const TYPE_RESPONSE = &"response"
|
||||||
const TYPE_TITLE = &"title"
|
const TYPE_TITLE = &"title"
|
||||||
const TYPE_CONDITION = &"condition"
|
const TYPE_CONDITION = &"condition"
|
||||||
|
const TYPE_WHILE = &"while"
|
||||||
|
const TYPE_MATCH = &"match"
|
||||||
|
const TYPE_WHEN = &"when"
|
||||||
const TYPE_MUTATION = &"mutation"
|
const TYPE_MUTATION = &"mutation"
|
||||||
const TYPE_GOTO = &"goto"
|
const TYPE_GOTO = &"goto"
|
||||||
const TYPE_DIALOGUE = &"dialogue"
|
const TYPE_DIALOGUE = &"dialogue"
|
||||||
|
const TYPE_RANDOM = &"random"
|
||||||
const TYPE_ERROR = &"error"
|
const TYPE_ERROR = &"error"
|
||||||
|
|
||||||
const TYPE_ELSE = &"else"
|
|
||||||
|
|
||||||
# Line IDs
|
# Line IDs
|
||||||
|
|
||||||
const ID_NULL = &""
|
const ID_NULL = &""
|
||||||
@ -94,6 +113,11 @@ const ERR_UNEXPECTED_VARIABLE = 132
|
|||||||
const ERR_INVALID_INDEX = 133
|
const ERR_INVALID_INDEX = 133
|
||||||
const ERR_UNEXPECTED_ASSIGNMENT = 134
|
const ERR_UNEXPECTED_ASSIGNMENT = 134
|
||||||
const ERR_UNKNOWN_USING = 135
|
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
|
## Get the error message
|
||||||
@ -169,14 +193,22 @@ static func get_error_message(error: int) -> String:
|
|||||||
return translate(&"errors.unexpected_assignment")
|
return translate(&"errors.unexpected_assignment")
|
||||||
ERR_UNKNOWN_USING:
|
ERR_UNKNOWN_USING:
|
||||||
return translate(&"errors.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")
|
return translate(&"errors.unknown")
|
||||||
|
|
||||||
|
|
||||||
static func translate(string: String) -> String:
|
static func translate(string: String) -> String:
|
||||||
var temp_node = new()
|
var base_path = new().get_script().resource_path.get_base_dir()
|
||||||
var base_path = temp_node.get_script().resource_path.get_base_dir()
|
|
||||||
temp_node.free()
|
|
||||||
|
|
||||||
var language: String = TranslationServer.get_tool_locale()
|
var language: String = TranslationServer.get_tool_locale()
|
||||||
var translations_path: String = "%s/l10n/%s.po" % [base_path, language]
|
var translations_path: String = "%s/l10n/%s.po" % [base_path, language]
|
||||||
|
1
addons/dialogue_manager/constants.gd.uid
Normal file
1
addons/dialogue_manager/constants.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://b1oarbmjtyesf
|
@ -187,12 +187,13 @@ func _mutate_inline_mutations(index: int) -> void:
|
|||||||
if inline_mutation[0] > index:
|
if inline_mutation[0] > index:
|
||||||
return
|
return
|
||||||
if inline_mutation[0] == index and not _already_mutated_indices.has(index):
|
if inline_mutation[0] == index and not _already_mutated_indices.has(index):
|
||||||
_already_mutated_indices.append(index)
|
|
||||||
_is_awaiting_mutation = true
|
_is_awaiting_mutation = true
|
||||||
# The DialogueManager can't be referenced directly here so we need to get it by its path
|
# 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)
|
await Engine.get_singleton("DialogueManager")._mutate(inline_mutation[1], dialogue_line.extra_game_states, true)
|
||||||
_is_awaiting_mutation = false
|
_is_awaiting_mutation = false
|
||||||
|
|
||||||
|
_already_mutated_indices.append(index)
|
||||||
|
|
||||||
|
|
||||||
# Determine if the current autopause character at the cursor should qualify to pause typing.
|
# Determine if the current autopause character at the cursor should qualify to pause typing.
|
||||||
func _should_auto_pause() -> bool:
|
func _should_auto_pause() -> bool:
|
||||||
|
1
addons/dialogue_manager/dialogue_label.gd.uid
Normal file
1
addons/dialogue_manager/dialogue_label.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://g32um0mltv5d
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://ckvgyvclnwggo"]
|
[gd_scene load_steps=2 format=3 uid="uid://ckvgyvclnwggo"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/dialogue_label.gd" id="1_cital"]
|
[ext_resource type="Script" uid="uid://g32um0mltv5d" path="res://addons/dialogue_manager/dialogue_label.gd" id="1_cital"]
|
||||||
|
|
||||||
[node name="DialogueLabel" type="RichTextLabel"]
|
[node name="DialogueLabel" type="RichTextLabel"]
|
||||||
anchors_preset = 10
|
anchors_preset = 10
|
||||||
|
17
addons/dialogue_manager/dialogue_line.gd
Normal file → Executable file
17
addons/dialogue_manager/dialogue_line.gd
Normal file → Executable file
@ -2,14 +2,11 @@
|
|||||||
class_name DialogueLine extends RefCounted
|
class_name DialogueLine extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
const _DialogueConstants = preload("./constants.gd")
|
|
||||||
|
|
||||||
|
|
||||||
## The ID of this line
|
## The ID of this line
|
||||||
var id: String
|
var id: String
|
||||||
|
|
||||||
## The internal type of this dialogue object. One of [code]TYPE_DIALOGUE[/code] or [code]TYPE_MUTATION[/code]
|
## The internal type of this dialogue object. One of [code]TYPE_DIALOGUE[/code] or [code]TYPE_MUTATION[/code]
|
||||||
var type: String = _DialogueConstants.TYPE_DIALOGUE
|
var type: String = DMConstants.TYPE_DIALOGUE
|
||||||
|
|
||||||
## The next line ID after this line.
|
## The next line ID after this line.
|
||||||
var next_id: String = ""
|
var next_id: String = ""
|
||||||
@ -41,6 +38,9 @@ var inline_mutations: Array[Array] = []
|
|||||||
## A list of responses attached to this line of dialogue.
|
## A list of responses attached to this line of dialogue.
|
||||||
var responses: Array = []
|
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.
|
## A list of any extra game states to check when resolving variables and mutations.
|
||||||
var extra_game_states: Array = []
|
var extra_game_states: Array = []
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ func _init(data: Dictionary = {}) -> void:
|
|||||||
extra_game_states = data.get("extra_game_states", [])
|
extra_game_states = data.get("extra_game_states", [])
|
||||||
|
|
||||||
match type:
|
match type:
|
||||||
_DialogueConstants.TYPE_DIALOGUE:
|
DMConstants.TYPE_DIALOGUE:
|
||||||
character = data.character
|
character = data.character
|
||||||
character_replacements = data.get("character_replacements", [] as Array[Dictionary])
|
character_replacements = data.get("character_replacements", [] as Array[Dictionary])
|
||||||
text = data.text
|
text = data.text
|
||||||
@ -76,16 +76,17 @@ func _init(data: Dictionary = {}) -> void:
|
|||||||
inline_mutations = data.get("inline_mutations", [] as Array[Array])
|
inline_mutations = data.get("inline_mutations", [] as Array[Array])
|
||||||
time = data.get("time", "")
|
time = data.get("time", "")
|
||||||
tags = data.get("tags", [])
|
tags = data.get("tags", [])
|
||||||
|
concurrent_lines = data.get("concurrent_lines", [] as Array[DialogueLine])
|
||||||
|
|
||||||
_DialogueConstants.TYPE_MUTATION:
|
DMConstants.TYPE_MUTATION:
|
||||||
mutation = data.mutation
|
mutation = data.mutation
|
||||||
|
|
||||||
|
|
||||||
func _to_string() -> String:
|
func _to_string() -> String:
|
||||||
match type:
|
match type:
|
||||||
_DialogueConstants.TYPE_DIALOGUE:
|
DMConstants.TYPE_DIALOGUE:
|
||||||
return "<DialogueLine character=\"%s\" text=\"%s\">" % [character, text]
|
return "<DialogueLine character=\"%s\" text=\"%s\">" % [character, text]
|
||||||
_DialogueConstants.TYPE_MUTATION:
|
DMConstants.TYPE_MUTATION:
|
||||||
return "<DialogueLine mutation>"
|
return "<DialogueLine mutation>"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
1
addons/dialogue_manager/dialogue_line.gd.uid
Normal file
1
addons/dialogue_manager/dialogue_line.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://rhuq0eyf8ar2
|
File diff suppressed because it is too large
Load Diff
1
addons/dialogue_manager/dialogue_manager.gd.uid
Normal file
1
addons/dialogue_manager/dialogue_manager.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c3rodes2l3gxb
|
@ -5,7 +5,6 @@
|
|||||||
class_name DialogueResource extends Resource
|
class_name DialogueResource extends Resource
|
||||||
|
|
||||||
|
|
||||||
const _DialogueManager = preload("./dialogue_manager.gd")
|
|
||||||
const DialogueLine = preload("./dialogue_line.gd")
|
const DialogueLine = preload("./dialogue_line.gd")
|
||||||
|
|
||||||
## A list of state shortcuts
|
## A list of state shortcuts
|
||||||
@ -30,7 +29,7 @@ const DialogueLine = preload("./dialogue_line.gd")
|
|||||||
## Get the next printable line of dialogue, starting from a referenced line ([code]title[/code] can
|
## 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
|
## be a title string or a stringified line number). Runs any mutations along the way and then returns
|
||||||
## the first dialogue line encountered.
|
## the first dialogue line encountered.
|
||||||
func get_next_dialogue_line(title: String, extra_game_states: Array = [], mutation_behaviour: _DialogueManager.MutationBehaviour = _DialogueManager.MutationBehaviour.Wait) -> DialogueLine:
|
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)
|
return await Engine.get_singleton("DialogueManager").get_next_dialogue_line(self, title, extra_game_states, mutation_behaviour)
|
||||||
|
|
||||||
|
|
||||||
@ -39,23 +38,5 @@ func get_titles() -> PackedStringArray:
|
|||||||
return titles.keys()
|
return titles.keys()
|
||||||
|
|
||||||
|
|
||||||
func get_ordered_titles() -> Array:
|
|
||||||
var splitted = raw_text.split("\n")
|
|
||||||
var ordered_titles = []
|
|
||||||
for line in splitted:
|
|
||||||
if line.begins_with("~ "):
|
|
||||||
ordered_titles.append(line.substr(2).strip_edges())
|
|
||||||
# # check ordered_titles consistency to titles
|
|
||||||
# for title in ordered_titles:
|
|
||||||
# if not titles.has(title):
|
|
||||||
# printerr("Title %s not found in titles" % title)
|
|
||||||
# ordered_titles.remove(title)
|
|
||||||
# for title in titles.keys():
|
|
||||||
# if not ordered_titles.has(title):
|
|
||||||
# printerr("Title %s not found in ordered_titles" % title)
|
|
||||||
# ordered_titles.append(title)
|
|
||||||
return ordered_titles
|
|
||||||
|
|
||||||
|
|
||||||
func _to_string() -> String:
|
func _to_string() -> String:
|
||||||
return "<DialogueResource titles=\"%s\">" % [",".join(titles.keys())]
|
return "<DialogueResource titles=\"%s\">" % [",".join(titles.keys())]
|
||||||
|
1
addons/dialogue_manager/dialogue_resource.gd.uid
Normal file
1
addons/dialogue_manager/dialogue_resource.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dbs4435dsf3ry
|
@ -2,14 +2,11 @@
|
|||||||
class_name DialogueResponse extends RefCounted
|
class_name DialogueResponse extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
const _DialogueConstants = preload("./constants.gd")
|
|
||||||
|
|
||||||
|
|
||||||
## The ID of this response
|
## The ID of this response
|
||||||
var id: String
|
var id: String
|
||||||
|
|
||||||
## The internal type of this dialogue object, always set to [code]TYPE_RESPONSE[/code].
|
## The internal type of this dialogue object, always set to [code]TYPE_RESPONSE[/code].
|
||||||
var type: String = _DialogueConstants.TYPE_RESPONSE
|
var type: String = DMConstants.TYPE_RESPONSE
|
||||||
|
|
||||||
## The next line ID to use if this response is selected by the player.
|
## The next line ID to use if this response is selected by the player.
|
||||||
var next_id: String = ""
|
var next_id: String = ""
|
||||||
|
1
addons/dialogue_manager/dialogue_response.gd.uid
Normal file
1
addons/dialogue_manager/dialogue_response.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cm0xpfeywpqid
|
@ -14,6 +14,9 @@ signal response_selected(response)
|
|||||||
## The action for accepting a response (is possibly overridden by parent dialogue balloon).
|
## The action for accepting a response (is possibly overridden by parent dialogue balloon).
|
||||||
@export var next_action: StringName = &""
|
@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.
|
## The list of dialogue responses.
|
||||||
var responses: Array = []:
|
var responses: Array = []:
|
||||||
get:
|
get:
|
||||||
@ -31,6 +34,8 @@ var responses: Array = []:
|
|||||||
# Add new items
|
# Add new items
|
||||||
if responses.size() > 0:
|
if responses.size() > 0:
|
||||||
for response in responses:
|
for response in responses:
|
||||||
|
if hide_failed_responses and not response.is_allowed: continue
|
||||||
|
|
||||||
var item: Control
|
var item: Control
|
||||||
if is_instance_valid(response_template):
|
if is_instance_valid(response_template):
|
||||||
item = response_template.duplicate(DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_SIGNALS)
|
item = response_template.duplicate(DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_SIGNALS)
|
||||||
@ -39,7 +44,7 @@ var responses: Array = []:
|
|||||||
item = Button.new()
|
item = Button.new()
|
||||||
item.name = "Response%d" % get_child_count()
|
item.name = "Response%d" % get_child_count()
|
||||||
if not response.is_allowed:
|
if not response.is_allowed:
|
||||||
item.name = String(item.name) + "Disallowed"
|
item.name = item.name + &"Disallowed"
|
||||||
item.disabled = true
|
item.disabled = true
|
||||||
|
|
||||||
# If the item has a response property then use that
|
# If the item has a response property then use that
|
||||||
@ -59,7 +64,9 @@ var responses: Array = []:
|
|||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
visibility_changed.connect(func():
|
visibility_changed.connect(func():
|
||||||
if visible and get_menu_items().size() > 0:
|
if visible and get_menu_items().size() > 0:
|
||||||
get_menu_items()[0].grab_focus()
|
var first_item: Control = get_menu_items()[0]
|
||||||
|
if first_item.is_inside_tree():
|
||||||
|
first_item.grab_focus()
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_instance_valid(response_template):
|
if is_instance_valid(response_template):
|
||||||
@ -77,11 +84,6 @@ func get_menu_items() -> Array:
|
|||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
## [b]DEPRECATED[/b]. Do not use.
|
|
||||||
func set_responses(next_responses: Array) -> void:
|
|
||||||
self.responses = next_responses
|
|
||||||
|
|
||||||
|
|
||||||
#region Internal
|
#region Internal
|
||||||
|
|
||||||
|
|
1
addons/dialogue_manager/dialogue_responses_menu.gd.uid
Normal file
1
addons/dialogue_manager/dialogue_responses_menu.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bb52rsfwhkxbn
|
@ -1,43 +1,52 @@
|
|||||||
extends EditorTranslationParserPlugin
|
class_name DMTranslationParserPlugin extends EditorTranslationParserPlugin
|
||||||
|
|
||||||
|
|
||||||
const DialogueConstants = preload("./constants.gd")
|
## Cached result of parsing a dialogue file.
|
||||||
const DialogueSettings = preload("./settings.gd")
|
var data: DMCompilerResult
|
||||||
const DialogueManagerParser = preload("./components/parser.gd")
|
## List of characters that were added.
|
||||||
const DialogueManagerParseResult = preload("./components/parse_result.gd")
|
var translated_character_names: PackedStringArray = []
|
||||||
|
var translated_lines: Array[Dictionary] = []
|
||||||
|
|
||||||
|
|
||||||
func _parse_file(path: String, msgids: Array, msgids_context_plural: Array) -> void:
|
func _parse_file(path: String) -> Array[PackedStringArray]:
|
||||||
|
var msgs: Array[PackedStringArray] = []
|
||||||
var file: FileAccess = FileAccess.open(path, FileAccess.READ)
|
var file: FileAccess = FileAccess.open(path, FileAccess.READ)
|
||||||
var text: String = file.get_as_text()
|
var text: String = file.get_as_text()
|
||||||
|
|
||||||
var data: DialogueManagerParseResult = DialogueManagerParser.parse_string(text, path)
|
data = DMCompiler.compile_string(text, path)
|
||||||
|
|
||||||
var known_keys: PackedStringArray = PackedStringArray([])
|
var known_keys: PackedStringArray = PackedStringArray([])
|
||||||
|
|
||||||
# Add all character names if settings ask for it
|
# Add all character names if settings ask for it
|
||||||
if DialogueSettings.get_setting("export_characters_in_translation", true):
|
if DMSettings.get_setting(DMSettings.INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST, true):
|
||||||
var character_names: PackedStringArray = data.character_names
|
translated_character_names = [] as Array[DialogueLine]
|
||||||
for character_name in character_names:
|
for character_name: String in data.character_names:
|
||||||
if character_name in known_keys: continue
|
if character_name in known_keys: continue
|
||||||
|
|
||||||
known_keys.append(character_name)
|
known_keys.append(character_name)
|
||||||
|
|
||||||
msgids_context_plural.append([character_name.replace('"', '\"'), "dialogue", ""])
|
translated_character_names.append(character_name)
|
||||||
|
msgs.append(PackedStringArray([character_name.replace('"', '\"'), "dialogue", "", DMConstants.translate("translation_plugin.character_name")]))
|
||||||
|
|
||||||
# Add all dialogue lines and responses
|
# Add all dialogue lines and responses
|
||||||
var dialogue: Dictionary = data.lines
|
var dialogue: Dictionary = data.lines
|
||||||
for key in dialogue.keys():
|
for key: String in dialogue.keys():
|
||||||
var line: Dictionary = dialogue.get(key)
|
var line: Dictionary = dialogue.get(key)
|
||||||
|
|
||||||
if not line.type in [DialogueConstants.TYPE_DIALOGUE, DialogueConstants.TYPE_RESPONSE]: continue
|
if not line.type in [DMConstants.TYPE_DIALOGUE, DMConstants.TYPE_RESPONSE]: continue
|
||||||
if line.translation_key in known_keys: continue
|
|
||||||
|
|
||||||
known_keys.append(line.translation_key)
|
var translation_key: String = line.get(&"translation_key", line.text)
|
||||||
|
|
||||||
if line.translation_key == "" or line.translation_key == line.text:
|
if translation_key in known_keys: continue
|
||||||
msgids_context_plural.append([line.text.replace('"', '\"'), "", ""])
|
|
||||||
|
known_keys.append(translation_key)
|
||||||
|
translated_lines.append(line)
|
||||||
|
if translation_key == line.text:
|
||||||
|
msgs.append(PackedStringArray([line.text.replace('"', '\"'), "", "", line.get("notes", "")]))
|
||||||
else:
|
else:
|
||||||
msgids_context_plural.append([line.text.replace('"', '\"'), line.translation_key.replace('"', '\"'), ""])
|
msgs.append(PackedStringArray([line.text.replace('"', '\"'), line.translation_key.replace('"', '\"'), "", line.get("notes", "")]))
|
||||||
|
|
||||||
|
return msgs
|
||||||
|
|
||||||
|
|
||||||
func _get_recognized_extensions() -> PackedStringArray:
|
func _get_recognized_extensions() -> PackedStringArray:
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://c6bya881h1egb
|
@ -25,10 +25,6 @@ namespace DialogueManagerRuntime
|
|||||||
get => dialogueLine;
|
get => dialogueLine;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
isWaitingForInput = false;
|
|
||||||
balloon.FocusMode = Control.FocusModeEnum.All;
|
|
||||||
balloon.GrabFocus();
|
|
||||||
|
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
QueueFree();
|
QueueFree();
|
||||||
@ -36,10 +32,12 @@ namespace DialogueManagerRuntime
|
|||||||
}
|
}
|
||||||
|
|
||||||
dialogueLine = value;
|
dialogueLine = value;
|
||||||
UpdateDialogue();
|
ApplyDialogueLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer MutationCooldown = new Timer();
|
||||||
|
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
@ -88,6 +86,18 @@ namespace DialogueManagerRuntime
|
|||||||
Next(response.NextId);
|
Next(response.NextId);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// Hide the balloon when a mutation is running
|
||||||
|
MutationCooldown.Timeout += () =>
|
||||||
|
{
|
||||||
|
if (willHideBalloon)
|
||||||
|
{
|
||||||
|
willHideBalloon = false;
|
||||||
|
balloon.Hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddChild(MutationCooldown);
|
||||||
|
|
||||||
DialogueManager.Mutated += OnMutated;
|
DialogueManager.Mutated += OnMutated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,12 +132,7 @@ namespace DialogueManagerRuntime
|
|||||||
|
|
||||||
public async void Start(Resource dialogueResource, string title, Array<Variant> extraGameStates = null)
|
public async void Start(Resource dialogueResource, string title, Array<Variant> extraGameStates = null)
|
||||||
{
|
{
|
||||||
if (!IsNodeReady())
|
temporaryGameStates = new Array<Variant> { this } + (extraGameStates ?? new Array<Variant>());
|
||||||
{
|
|
||||||
await ToSignal(this, SignalName.Ready);
|
|
||||||
}
|
|
||||||
|
|
||||||
temporaryGameStates = extraGameStates ?? new Array<Variant>();
|
|
||||||
isWaitingForInput = false;
|
isWaitingForInput = false;
|
||||||
resource = dialogueResource;
|
resource = dialogueResource;
|
||||||
|
|
||||||
@ -144,12 +149,13 @@ namespace DialogueManagerRuntime
|
|||||||
#region Helpers
|
#region Helpers
|
||||||
|
|
||||||
|
|
||||||
private async void UpdateDialogue()
|
private async void ApplyDialogueLine()
|
||||||
{
|
{
|
||||||
if (!IsNodeReady())
|
MutationCooldown.Stop();
|
||||||
{
|
|
||||||
await ToSignal(this, SignalName.Ready);
|
isWaitingForInput = false;
|
||||||
}
|
balloon.FocusMode = Control.FocusModeEnum.All;
|
||||||
|
balloon.GrabFocus();
|
||||||
|
|
||||||
// Set up the character name
|
// Set up the character name
|
||||||
characterLabel.Visible = !string.IsNullOrEmpty(dialogueLine.Character);
|
characterLabel.Visible = !string.IsNullOrEmpty(dialogueLine.Character);
|
||||||
@ -208,14 +214,7 @@ namespace DialogueManagerRuntime
|
|||||||
{
|
{
|
||||||
isWaitingForInput = false;
|
isWaitingForInput = false;
|
||||||
willHideBalloon = true;
|
willHideBalloon = true;
|
||||||
GetTree().CreateTimer(0.1f).Timeout += () =>
|
MutationCooldown.Start(0.1f);
|
||||||
{
|
|
||||||
if (willHideBalloon)
|
|
||||||
{
|
|
||||||
willHideBalloon = false;
|
|
||||||
balloon.Hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://5b3w40kwakl3
|
@ -26,55 +26,19 @@ var _locale: String = TranslationServer.get_locale()
|
|||||||
|
|
||||||
## The current line
|
## The current line
|
||||||
var dialogue_line: DialogueLine:
|
var dialogue_line: DialogueLine:
|
||||||
set(next_dialogue_line):
|
set(value):
|
||||||
is_waiting_for_input = false
|
if value:
|
||||||
balloon.focus_mode = Control.FOCUS_ALL
|
dialogue_line = value
|
||||||
balloon.grab_focus()
|
apply_dialogue_line()
|
||||||
|
|
||||||
# The dialogue has finished so close the balloon
|
|
||||||
if not next_dialogue_line:
|
|
||||||
queue_free()
|
|
||||||
return
|
|
||||||
|
|
||||||
# If the node isn't ready yet then none of the labels will be ready yet either
|
|
||||||
if not is_node_ready():
|
|
||||||
await ready
|
|
||||||
|
|
||||||
dialogue_line = next_dialogue_line
|
|
||||||
|
|
||||||
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.set_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:
|
else:
|
||||||
is_waiting_for_input = true
|
# The dialogue has finished so close the balloon
|
||||||
balloon.focus_mode = Control.FOCUS_ALL
|
queue_free()
|
||||||
balloon.grab_focus()
|
|
||||||
get:
|
get:
|
||||||
return dialogue_line
|
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
|
## The base balloon anchor
|
||||||
@onready var balloon: Control = %Balloon
|
@onready var balloon: Control = %Balloon
|
||||||
|
|
||||||
@ -96,6 +60,9 @@ func _ready() -> void:
|
|||||||
if responses_menu.next_action.is_empty():
|
if responses_menu.next_action.is_empty():
|
||||||
responses_menu.next_action = next_action
|
responses_menu.next_action = next_action
|
||||||
|
|
||||||
|
mutation_cooldown.timeout.connect(_on_mutation_cooldown_timeout)
|
||||||
|
add_child(mutation_cooldown)
|
||||||
|
|
||||||
|
|
||||||
func _unhandled_input(_event: InputEvent) -> void:
|
func _unhandled_input(_event: InputEvent) -> void:
|
||||||
# Only the balloon is allowed to handle input while it's showing
|
# Only the balloon is allowed to handle input while it's showing
|
||||||
@ -114,14 +81,52 @@ func _notification(what: int) -> void:
|
|||||||
|
|
||||||
## Start some dialogue
|
## Start some dialogue
|
||||||
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
|
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
|
||||||
if not is_node_ready():
|
temporary_game_states = [self] + extra_game_states
|
||||||
await ready
|
|
||||||
temporary_game_states = [self] + extra_game_states
|
|
||||||
is_waiting_for_input = false
|
is_waiting_for_input = false
|
||||||
resource = dialogue_resource
|
resource = dialogue_resource
|
||||||
self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states)
|
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
|
## Go to the next line
|
||||||
func next(next_id: String) -> void:
|
func next(next_id: String) -> void:
|
||||||
self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states)
|
self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states)
|
||||||
@ -130,14 +135,16 @@ func next(next_id: String) -> void:
|
|||||||
#region Signals
|
#region Signals
|
||||||
|
|
||||||
|
|
||||||
|
func _on_mutation_cooldown_timeout() -> void:
|
||||||
|
if will_hide_balloon:
|
||||||
|
will_hide_balloon = false
|
||||||
|
balloon.hide()
|
||||||
|
|
||||||
|
|
||||||
func _on_mutated(_mutation: Dictionary) -> void:
|
func _on_mutated(_mutation: Dictionary) -> void:
|
||||||
is_waiting_for_input = false
|
is_waiting_for_input = false
|
||||||
will_hide_balloon = true
|
will_hide_balloon = true
|
||||||
get_tree().create_timer(0.1).timeout.connect(func():
|
mutation_cooldown.start(0.1)
|
||||||
if will_hide_balloon:
|
|
||||||
will_hide_balloon = false
|
|
||||||
balloon.hide()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_balloon_gui_input(event: InputEvent) -> void:
|
func _on_balloon_gui_input(event: InputEvent) -> void:
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uid://d1wt4ma6055l8
|
@ -1,8 +1,8 @@
|
|||||||
[gd_scene load_steps=9 format=3 uid="uid://73jm5qjy52vq"]
|
[gd_scene load_steps=9 format=3 uid="uid://73jm5qjy52vq"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/example_balloon/example_balloon.gd" id="1_36de5"]
|
[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="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="2_a8ve6"]
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/dialogue_reponses_menu.gd" id="3_72ixx"]
|
[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"]
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_spyqn"]
|
||||||
bg_color = Color(0, 0, 0, 1)
|
bg_color = Color(0, 0, 0, 1)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
[gd_scene load_steps=10 format=3 uid="uid://13s5spsk34qu"]
|
[gd_scene load_steps=10 format=3 uid="uid://13s5spsk34qu"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/example_balloon/example_balloon.gd" id="1_s2gbs"]
|
[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="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="2_hfvdi"]
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/dialogue_reponses_menu.gd" id="3_1j1j0"]
|
[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"]
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_235ry"]
|
||||||
content_margin_left = 6.0
|
content_margin_left = 6.0
|
||||||
@ -104,6 +104,7 @@ grow_vertical = 2
|
|||||||
theme = SubResource("Theme_qq3yp")
|
theme = SubResource("Theme_qq3yp")
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="Balloon"]
|
[node name="Panel" type="Panel" parent="Balloon"]
|
||||||
|
clip_children = 2
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 12
|
anchors_preset = 12
|
||||||
anchor_top = 1.0
|
anchor_top = 1.0
|
||||||
@ -115,6 +116,7 @@ offset_right = -4.0
|
|||||||
offset_bottom = -4.0
|
offset_bottom = -4.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 0
|
grow_vertical = 0
|
||||||
|
mouse_filter = 1
|
||||||
|
|
||||||
[node name="Dialogue" type="MarginContainer" parent="Balloon/Panel"]
|
[node name="Dialogue" type="MarginContainer" parent="Balloon/Panel"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
@ -142,7 +144,6 @@ unique_name_in_owner = true
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
text = "Dialogue..."
|
text = "Dialogue..."
|
||||||
skip_pause_at_abbreviations = PackedStringArray("Mr", "Mrs", "Ms", "Dr", "etc", "eg", "ex")
|
|
||||||
|
|
||||||
[node name="Responses" type="MarginContainer" parent="Balloon"]
|
[node name="Responses" type="MarginContainer" parent="Balloon"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
@tool
|
@tool
|
||||||
extends EditorImportPlugin
|
class_name DMImportPlugin extends EditorImportPlugin
|
||||||
|
|
||||||
|
|
||||||
signal compiled_resource(resource: Resource)
|
signal compiled_resource(resource: Resource)
|
||||||
|
|
||||||
|
|
||||||
const DialogueResource = preload("./dialogue_resource.gd")
|
const COMPILER_VERSION = 14
|
||||||
const DialogueManagerParser = preload("./components/parser.gd")
|
|
||||||
const DialogueManagerParseResult = preload("./components/parse_result.gd")
|
|
||||||
|
|
||||||
const compiler_version = 13
|
|
||||||
|
|
||||||
|
|
||||||
func _get_importer_name() -> String:
|
func _get_importer_name() -> String:
|
||||||
# NOTE: A change to this forces a re-import of all dialogue
|
# NOTE: A change to this forces a re-import of all dialogue
|
||||||
return "dialogue_manager_compiler_%s" % compiler_version
|
return "dialogue_manager_compiler_%s" % COMPILER_VERSION
|
||||||
|
|
||||||
|
|
||||||
func _get_visible_name() -> String:
|
func _get_visible_name() -> String:
|
||||||
@ -63,7 +59,7 @@ func _get_option_visibility(path: String, option_name: StringName, options: Dict
|
|||||||
|
|
||||||
|
|
||||||
func _import(source_file: String, save_path: String, options: Dictionary, platform_variants: Array[String], gen_files: Array[String]) -> Error:
|
func _import(source_file: String, save_path: String, options: Dictionary, platform_variants: Array[String], gen_files: Array[String]) -> Error:
|
||||||
var cache = Engine.get_meta("DialogueCache")
|
var cache = Engine.get_meta("DMCache")
|
||||||
|
|
||||||
# Get the raw file contents
|
# Get the raw file contents
|
||||||
if not FileAccess.file_exists(source_file): return ERR_FILE_NOT_FOUND
|
if not FileAccess.file_exists(source_file): return ERR_FILE_NOT_FOUND
|
||||||
@ -73,17 +69,12 @@ func _import(source_file: String, save_path: String, options: Dictionary, platfo
|
|||||||
|
|
||||||
cache.file_content_changed.emit(source_file, raw_text)
|
cache.file_content_changed.emit(source_file, raw_text)
|
||||||
|
|
||||||
# Parse the text
|
# Compile the text
|
||||||
var parser: DialogueManagerParser = DialogueManagerParser.new()
|
var result: DMCompilerResult = DMCompiler.compile_string(raw_text, source_file)
|
||||||
var err: Error = parser.parse(raw_text, source_file)
|
if result.errors.size() > 0:
|
||||||
var data: DialogueManagerParseResult = parser.get_data()
|
printerr("%d errors found in %s" % [result.errors.size(), source_file])
|
||||||
var errors: Array[Dictionary] = parser.get_errors()
|
cache.add_errors_to_file(source_file, result.errors)
|
||||||
parser.free()
|
return ERR_PARSE_ERROR
|
||||||
|
|
||||||
if err != OK:
|
|
||||||
printerr("%d errors found in %s" % [errors.size(), source_file])
|
|
||||||
cache.add_errors_to_file(source_file, errors)
|
|
||||||
return err
|
|
||||||
|
|
||||||
# Get the current addon version
|
# Get the current addon version
|
||||||
var config: ConfigFile = ConfigFile.new()
|
var config: ConfigFile = ConfigFile.new()
|
||||||
@ -94,17 +85,17 @@ func _import(source_file: String, save_path: String, options: Dictionary, platfo
|
|||||||
var resource: DialogueResource = DialogueResource.new()
|
var resource: DialogueResource = DialogueResource.new()
|
||||||
resource.set_meta("dialogue_manager_version", version)
|
resource.set_meta("dialogue_manager_version", version)
|
||||||
|
|
||||||
resource.using_states = data.using_states
|
resource.using_states = result.using_states
|
||||||
resource.titles = data.titles
|
resource.titles = result.titles
|
||||||
resource.first_title = data.first_title
|
resource.first_title = result.first_title
|
||||||
resource.character_names = data.character_names
|
resource.character_names = result.character_names
|
||||||
resource.lines = data.lines
|
resource.lines = result.lines
|
||||||
resource.raw_text = data.raw_text
|
resource.raw_text = result.raw_text
|
||||||
|
|
||||||
# Clear errors and possibly trigger any cascade recompiles
|
# Clear errors and possibly trigger any cascade recompiles
|
||||||
cache.add_file(source_file, data)
|
cache.add_file(source_file, result)
|
||||||
|
|
||||||
err = ResourceSaver.save(resource, "%s.%s" % [save_path, _get_save_extension()])
|
var err: Error = ResourceSaver.save(resource, "%s.%s" % [save_path, _get_save_extension()])
|
||||||
|
|
||||||
compiled_resource.emit(resource)
|
compiled_resource.emit(resource)
|
||||||
|
|
||||||
|
1
addons/dialogue_manager/import_plugin.gd.uid
Normal file
1
addons/dialogue_manager/import_plugin.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dhwpj6ed8soyq
|
@ -1,5 +1,5 @@
|
|||||||
@tool
|
@tool
|
||||||
extends EditorInspectorPlugin
|
class_name DMInspectorPlugin extends EditorInspectorPlugin
|
||||||
|
|
||||||
|
|
||||||
const DialogueEditorProperty = preload("./components/editor_property/editor_property.gd")
|
const DialogueEditorProperty = preload("./components/editor_property/editor_property.gd")
|
||||||
|
1
addons/dialogue_manager/inspector_plugin.gd.uid
Normal file
1
addons/dialogue_manager/inspector_plugin.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://0x31sbqbikov
|
@ -37,7 +37,10 @@ msgid "find_in_files"
|
|||||||
msgstr "Find in files..."
|
msgstr "Find in files..."
|
||||||
|
|
||||||
msgid "test_dialogue"
|
msgid "test_dialogue"
|
||||||
msgstr "Test dialogue"
|
msgstr "Test dialogue from start of file"
|
||||||
|
|
||||||
|
msgid "test_dialogue_from_line"
|
||||||
|
msgstr "Test dialogue from current line"
|
||||||
|
|
||||||
msgid "search_for_text"
|
msgid "search_for_text"
|
||||||
msgstr "Search for text"
|
msgstr "Search for text"
|
||||||
@ -48,9 +51,6 @@ msgstr "Insert"
|
|||||||
msgid "translations"
|
msgid "translations"
|
||||||
msgstr "Translations"
|
msgstr "Translations"
|
||||||
|
|
||||||
msgid "settings"
|
|
||||||
msgstr "Settings"
|
|
||||||
|
|
||||||
msgid "sponsor"
|
msgid "sponsor"
|
||||||
msgstr "Sponsor"
|
msgstr "Sponsor"
|
||||||
|
|
||||||
@ -144,84 +144,6 @@ msgstr "Copy file path"
|
|||||||
msgid "buffer.show_in_filesystem"
|
msgid "buffer.show_in_filesystem"
|
||||||
msgstr "Show in FileSystem"
|
msgstr "Show in FileSystem"
|
||||||
|
|
||||||
msgid "settings.invalid_test_scene"
|
|
||||||
msgstr "\"{path}\" does not extend BaseDialogueTestScene."
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_test_scene"
|
|
||||||
msgstr "Revert to default test scene"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_hint"
|
|
||||||
msgstr "Custom balloon to use when calling \"DialogueManager.show_balloon()\""
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_balloon"
|
|
||||||
msgstr "Revert to default balloon"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_path"
|
|
||||||
msgstr "<example balloon>"
|
|
||||||
|
|
||||||
msgid "settings.autoload"
|
|
||||||
msgstr "Autoload"
|
|
||||||
|
|
||||||
msgid "settings.path"
|
|
||||||
msgstr "Path"
|
|
||||||
|
|
||||||
msgid "settings.new_template"
|
|
||||||
msgstr "New dialogue files will start with template text"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys"
|
|
||||||
msgstr "Treat missing translation keys as errors"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys_hint"
|
|
||||||
msgstr "If you are using static translation keys then having this enabled will help you find any lines that you haven't added a key to yet."
|
|
||||||
|
|
||||||
msgid "settings.characters_translations"
|
|
||||||
msgstr "Export character names in translation files"
|
|
||||||
|
|
||||||
msgid "settings.wrap_long_lines"
|
|
||||||
msgstr "Wrap long lines"
|
|
||||||
|
|
||||||
msgid "settings.include_failed_responses"
|
|
||||||
msgstr "Include responses with failed conditions"
|
|
||||||
|
|
||||||
msgid "settings.ignore_missing_state_values"
|
|
||||||
msgstr "Skip over missing state value errors (not recommended)"
|
|
||||||
|
|
||||||
msgid "settings.custom_test_scene"
|
|
||||||
msgstr "Custom test scene (must extend BaseDialogueTestScene)"
|
|
||||||
|
|
||||||
msgid "settings.default_csv_locale"
|
|
||||||
msgstr "Default CSV Locale"
|
|
||||||
|
|
||||||
msgid "settings.states_shortcuts"
|
|
||||||
msgstr "State Shortcuts"
|
|
||||||
|
|
||||||
msgid "settings.states_message"
|
|
||||||
msgstr "If an autoload is enabled here you can refer to its properties, methods, and signals without having to use its name."
|
|
||||||
|
|
||||||
msgid "settings.states_hint"
|
|
||||||
msgstr "ie. Instead of \"SomeState.some_property\" you could just use \"some_property\""
|
|
||||||
|
|
||||||
msgid "settings.recompile_warning"
|
|
||||||
msgstr "Changing these settings will force a recompile of all dialogue. Only change them if you know what you are doing."
|
|
||||||
|
|
||||||
msgid "settings.create_lines_for_responses_with_characters"
|
|
||||||
msgstr "Create child dialogue line for responses with character names in them"
|
|
||||||
|
|
||||||
msgid "settings.open_in_external_editor"
|
|
||||||
msgstr "Open dialogue files in external editor"
|
|
||||||
|
|
||||||
msgid "settings.external_editor_warning"
|
|
||||||
msgstr "Note: Syntax highlighting and detailed error checking are not supported in external editors."
|
|
||||||
|
|
||||||
msgid "settings.include_characters_in_translations"
|
|
||||||
msgstr "Include character names in translation exports"
|
|
||||||
|
|
||||||
msgid "settings.include_notes_in_translations"
|
|
||||||
msgstr "Include notes (## comments) in translation exports"
|
|
||||||
|
|
||||||
msgid "settings.check_for_updates"
|
|
||||||
msgstr "Check for updates"
|
|
||||||
|
|
||||||
msgid "n_of_n"
|
msgid "n_of_n"
|
||||||
msgstr "{index} of {total}"
|
msgstr "{index} of {total}"
|
||||||
|
|
||||||
@ -384,6 +306,21 @@ msgstr "Invalid index."
|
|||||||
msgid "errors.unexpected_assignment"
|
msgid "errors.unexpected_assignment"
|
||||||
msgstr "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"
|
msgid "errors.unknown"
|
||||||
msgstr "Unknown syntax."
|
msgstr "Unknown syntax."
|
||||||
|
|
||||||
@ -479,3 +416,9 @@ msgstr "Array[{type}] isn't supported in mutations. Use Array as a type instead.
|
|||||||
|
|
||||||
msgid "runtime.dialogue_balloon_missing_start_method"
|
msgid "runtime.dialogue_balloon_missing_start_method"
|
||||||
msgstr "Your dialogue balloon is missing a \"start\" or \"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"
|
@ -41,9 +41,6 @@ msgstr "Insertar"
|
|||||||
msgid "translations"
|
msgid "translations"
|
||||||
msgstr "Traducciones"
|
msgstr "Traducciones"
|
||||||
|
|
||||||
msgid "settings"
|
|
||||||
msgstr "Ajustes"
|
|
||||||
|
|
||||||
msgid "show_support"
|
msgid "show_support"
|
||||||
msgstr "Contribuye con Dialogue Manager"
|
msgstr "Contribuye con Dialogue Manager"
|
||||||
|
|
||||||
@ -134,82 +131,6 @@ msgstr "Copiar la ruta del archivo"
|
|||||||
msgid "buffer.show_in_filesystem"
|
msgid "buffer.show_in_filesystem"
|
||||||
msgstr "Mostrar en el sistema de archivos"
|
msgstr "Mostrar en el sistema de archivos"
|
||||||
|
|
||||||
msgid "settings.invalid_test_scene"
|
|
||||||
msgstr "\"{path}\" no extiende BaseDialogueTestScene."
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_test_scene"
|
|
||||||
msgstr "Revertir a la escena de prueba por defecto"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_hint"
|
|
||||||
msgstr ""
|
|
||||||
"Globo personalizado para usar al llamar a \"DialogueManager.show_balloon()\""
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_balloon"
|
|
||||||
msgstr "Volver al globo predeterminado"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_path"
|
|
||||||
msgstr "<globo de ejemplo>"
|
|
||||||
|
|
||||||
msgid "settings.autoload"
|
|
||||||
msgstr "Autocarga"
|
|
||||||
|
|
||||||
msgid "settings.path"
|
|
||||||
msgstr "Ruta"
|
|
||||||
|
|
||||||
msgid "settings.new_template"
|
|
||||||
msgstr "Los nuevos archivos de diálogo empezarán con una plantilla"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys"
|
|
||||||
msgstr "Tratar las claves de traducción faltantes como errores"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys_hint"
|
|
||||||
msgstr "Si estás utilizando claves de traducción estáticas, tener esta opción habilitada te ayudará a encontrar cualquier línea a la que aún no le hayas añadido una clave."
|
|
||||||
|
|
||||||
msgid "settings.characters_translations"
|
|
||||||
msgstr "Exportar nombres de personajes en archivos de traducción"
|
|
||||||
|
|
||||||
msgid "settings.wrap_long_lines"
|
|
||||||
msgstr "Romper líneas largas"
|
|
||||||
|
|
||||||
msgid "settings.include_failed_responses"
|
|
||||||
msgstr "Incluir respuestas con condiciones fallidas"
|
|
||||||
|
|
||||||
msgid "settings.ignore_missing_state_values"
|
|
||||||
msgstr "Omitir errores de valores de estado faltantes (no recomendado)"
|
|
||||||
|
|
||||||
msgid "settings.custom_test_scene"
|
|
||||||
msgstr "Escena de prueba personalizada (debe extender BaseDialogueTestScene)"
|
|
||||||
|
|
||||||
msgid "settings.default_csv_locale"
|
|
||||||
msgstr "Localización CSV por defecto"
|
|
||||||
|
|
||||||
msgid "settings.states_shortcuts"
|
|
||||||
msgstr "Atajos de teclado"
|
|
||||||
|
|
||||||
msgid "settings.states_message"
|
|
||||||
msgstr "Si un autoload está habilitado aquí, puedes referirte a sus propiedades y métodos sin tener que usar su nombre."
|
|
||||||
|
|
||||||
msgid "settings.states_hint"
|
|
||||||
msgstr "ie. En lugar de \"SomeState.some_property\" podría simplemente usar \"some_property\""
|
|
||||||
|
|
||||||
msgid "settings.recompile_warning"
|
|
||||||
msgstr "Cambiar estos ajustes obligará a recompilar todo el diálogo. Hazlo solo si sabes lo que estás haciendo."
|
|
||||||
|
|
||||||
msgid "settings.create_lines_for_responses_with_characters"
|
|
||||||
msgstr "Crear línea de diálogo para respuestas con nombres de personajes dentro."
|
|
||||||
|
|
||||||
msgid "settings.open_in_external_editor"
|
|
||||||
msgstr "Abrir archivos de diálogo en el editor externo"
|
|
||||||
|
|
||||||
msgid "settings.external_editor_warning"
|
|
||||||
msgstr "Nota: El resaltado de sintaxis y la verificación detallada de errores no están soportados en editores externos."
|
|
||||||
|
|
||||||
msgid "settings.include_characters_in_translations"
|
|
||||||
msgstr "Incluir nombres de personajes en las exportaciones de traducción"
|
|
||||||
|
|
||||||
msgid "settings.include_notes_in_translations"
|
|
||||||
msgstr "Incluir notas (## comentarios) en las exportaciones de traducción"
|
|
||||||
|
|
||||||
msgid "n_of_n"
|
msgid "n_of_n"
|
||||||
msgstr "{index} de {total}"
|
msgstr "{index} de {total}"
|
||||||
|
|
||||||
|
@ -32,6 +32,9 @@ msgstr ""
|
|||||||
msgid "test_dialogue"
|
msgid "test_dialogue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "test_dialogue_from_line"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "search_for_text"
|
msgid "search_for_text"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -41,9 +44,6 @@ msgstr ""
|
|||||||
msgid "translations"
|
msgid "translations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "settings"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "sponsor"
|
msgid "sponsor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -134,84 +134,6 @@ msgstr ""
|
|||||||
msgid "buffer.show_in_filesystem"
|
msgid "buffer.show_in_filesystem"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "settings.invalid_test_scene"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_test_scene"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_hint"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_balloon"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_path"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.autoload"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.path"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.new_template"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.missing_keys"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.missing_keys_hint"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.characters_translations"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.wrap_long_lines"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.include_failed_responses"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.ignore_missing_state_values"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.custom_test_scene"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.default_csv_locale"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.states_shortcuts"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.states_message"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.states_hint"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.recompile_warning"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.create_lines_for_responses_with_characters"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.open_in_external_editor"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.external_editor_warning"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.include_characters_in_translations"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.include_notes_in_translations"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "settings.check_for_updates"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "n_of_n"
|
msgid "n_of_n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -374,6 +296,21 @@ msgstr ""
|
|||||||
msgid "errors.unexpected_assignment"
|
msgid "errors.unexpected_assignment"
|
||||||
msgstr ""
|
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"
|
msgid "errors.unknown"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -469,3 +406,9 @@ msgstr ""
|
|||||||
|
|
||||||
msgid "runtime.dialogue_balloon_missing_start_method"
|
msgid "runtime.dialogue_balloon_missing_start_method"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "runtime.top_level_states_share_name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "translation_plugin.character_name"
|
||||||
|
msgstr ""
|
@ -3,13 +3,13 @@ msgstr ""
|
|||||||
"Project-Id-Version: Dialogue Manager\n"
|
"Project-Id-Version: Dialogue Manager\n"
|
||||||
"POT-Creation-Date: \n"
|
"POT-Creation-Date: \n"
|
||||||
"PO-Revision-Date: \n"
|
"PO-Revision-Date: \n"
|
||||||
"Last-Translator: veydzh3r <veydzherdgswift008@gmail.com>\n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: uk\n"
|
"Language: uk\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 3.5\n"
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
|
|
||||||
msgid "start_a_new_file"
|
msgid "start_a_new_file"
|
||||||
msgstr "Створити новий файл"
|
msgstr "Створити новий файл"
|
||||||
@ -36,7 +36,10 @@ msgid "find_in_files"
|
|||||||
msgstr "Знайти у файлах..."
|
msgstr "Знайти у файлах..."
|
||||||
|
|
||||||
msgid "test_dialogue"
|
msgid "test_dialogue"
|
||||||
msgstr "Відтворити діалог"
|
msgstr "Протестувати діалог з початку файлу"
|
||||||
|
|
||||||
|
msgid "test_dialogue_from_line"
|
||||||
|
msgstr "Протестувати діалог з поточного рядка"
|
||||||
|
|
||||||
msgid "search_for_text"
|
msgid "search_for_text"
|
||||||
msgstr "Пошук тексту"
|
msgstr "Пошук тексту"
|
||||||
@ -47,9 +50,6 @@ msgstr "Вставити"
|
|||||||
msgid "translations"
|
msgid "translations"
|
||||||
msgstr "Переклади"
|
msgstr "Переклади"
|
||||||
|
|
||||||
msgid "settings"
|
|
||||||
msgstr "Налаштування"
|
|
||||||
|
|
||||||
msgid "sponsor"
|
msgid "sponsor"
|
||||||
msgstr "Спонсор"
|
msgstr "Спонсор"
|
||||||
|
|
||||||
@ -143,97 +143,6 @@ msgstr "Копіювати шлях файлу"
|
|||||||
msgid "buffer.show_in_filesystem"
|
msgid "buffer.show_in_filesystem"
|
||||||
msgstr "Показати у файловій системі"
|
msgstr "Показати у файловій системі"
|
||||||
|
|
||||||
msgid "settings.invalid_test_scene"
|
|
||||||
msgstr "«{path}» не у розширенні BaseDialogueTestScene."
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_test_scene"
|
|
||||||
msgstr "Повернути до типової тестової сцени"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_hint"
|
|
||||||
msgstr ""
|
|
||||||
"Нестандартна куля для використання під час виклику «DialogueManager."
|
|
||||||
"show_balloon()»"
|
|
||||||
|
|
||||||
msgid "settings.revert_to_default_balloon"
|
|
||||||
msgstr "Повернути до типової кулі"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_path"
|
|
||||||
msgstr "<приклад кулі>"
|
|
||||||
|
|
||||||
msgid "settings.autoload"
|
|
||||||
msgstr "Автозавантаження"
|
|
||||||
|
|
||||||
msgid "settings.path"
|
|
||||||
msgstr "Шлях"
|
|
||||||
|
|
||||||
msgid "settings.new_template"
|
|
||||||
msgstr "Нові файли діалогів починатимуться з шаблонного тексту"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys"
|
|
||||||
msgstr "Вважати відсутні ключі перекладу як помилками"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys_hint"
|
|
||||||
msgstr ""
|
|
||||||
"Якщо ви використовуєте статичні ключі перекладу, увімкнення цього параметра "
|
|
||||||
"допоможе вам знайти рядки, до яких ви ще не додали ключ."
|
|
||||||
|
|
||||||
msgid "settings.characters_translations"
|
|
||||||
msgstr "Експортовувати імена персонажів у файли перекладу"
|
|
||||||
|
|
||||||
msgid "settings.wrap_long_lines"
|
|
||||||
msgstr "Переносити довгі рядки"
|
|
||||||
|
|
||||||
msgid "settings.include_failed_responses"
|
|
||||||
msgstr "Включати відповіді з невдалими умовами"
|
|
||||||
|
|
||||||
msgid "settings.ignore_missing_state_values"
|
|
||||||
msgstr "Пропускати помилки пропущених значень стану (не рекомендується)"
|
|
||||||
|
|
||||||
msgid "settings.custom_test_scene"
|
|
||||||
msgstr ""
|
|
||||||
"Користувацька тестова сцена (має мати розширення «BaseDialogueTestScene»)"
|
|
||||||
|
|
||||||
msgid "settings.default_csv_locale"
|
|
||||||
msgstr "Типова мова файлу CSV"
|
|
||||||
|
|
||||||
msgid "settings.states_shortcuts"
|
|
||||||
msgstr "Скорочення станів"
|
|
||||||
|
|
||||||
msgid "settings.states_message"
|
|
||||||
msgstr ""
|
|
||||||
"Якщо автозавантаження увімкнено, ви можете звертатися до його властивостей і "
|
|
||||||
"методів без необхідності використовувати його назву."
|
|
||||||
|
|
||||||
msgid "settings.states_hint"
|
|
||||||
msgstr ""
|
|
||||||
"Тобто, замість «ЯкийсьСтан.якась_властивість» ви можете просто "
|
|
||||||
"використовувати «якась_властивість»"
|
|
||||||
|
|
||||||
msgid "settings.recompile_warning"
|
|
||||||
msgstr ""
|
|
||||||
"Зміна цих параметрів призведе до перекомпіляції усіх діалогів. Змінюйте їх, "
|
|
||||||
"тільки якщо ви знаєте, що робите."
|
|
||||||
|
|
||||||
msgid "settings.create_lines_for_responses_with_characters"
|
|
||||||
msgstr "Створювати дочірній рядок діалогу для відповідей з іменами персонажів"
|
|
||||||
|
|
||||||
msgid "settings.open_in_external_editor"
|
|
||||||
msgstr "Відкрити файли діалогів у зовнішньому редакторі"
|
|
||||||
|
|
||||||
msgid "settings.external_editor_warning"
|
|
||||||
msgstr ""
|
|
||||||
"Примітка: підсвічування синтаксису та детальна перевірка помилок не "
|
|
||||||
"підтримуються у зовнішніх редакторах."
|
|
||||||
|
|
||||||
msgid "settings.include_characters_in_translations"
|
|
||||||
msgstr "Включати імена персонажів до експорту перекладу"
|
|
||||||
|
|
||||||
msgid "settings.include_notes_in_translations"
|
|
||||||
msgstr "Включати примітки (## коментарі) до експорту перекладу"
|
|
||||||
|
|
||||||
msgid "settings.check_for_updates"
|
|
||||||
msgstr "Перевіряти наявність оновлень"
|
|
||||||
|
|
||||||
msgid "n_of_n"
|
msgid "n_of_n"
|
||||||
msgstr "{index} з {total}"
|
msgstr "{index} з {total}"
|
||||||
|
|
||||||
@ -289,8 +198,7 @@ msgid "errors_in_script"
|
|||||||
msgstr "У вашому скрипті є помилки. Виправте їх і спробуйте ще раз."
|
msgstr "У вашому скрипті є помилки. Виправте їх і спробуйте ще раз."
|
||||||
|
|
||||||
msgid "errors_with_build"
|
msgid "errors_with_build"
|
||||||
msgstr ""
|
msgstr "Вам потрібно виправити помилки в діалогах, перш ніж ви зможете запустити гру."
|
||||||
"Вам потрібно виправити помилки в діалогах, перш ніж ви зможете запустити гру."
|
|
||||||
|
|
||||||
msgid "errors.import_errors"
|
msgid "errors.import_errors"
|
||||||
msgstr "В імпортованому файлі є помилки."
|
msgstr "В імпортованому файлі є помилки."
|
||||||
@ -397,6 +305,21 @@ msgstr "Недійсний індекс."
|
|||||||
msgid "errors.unexpected_assignment"
|
msgid "errors.unexpected_assignment"
|
||||||
msgstr "Несподіване призначення."
|
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"
|
msgid "errors.unknown"
|
||||||
msgstr "Невідомий синтаксис."
|
msgstr "Невідомий синтаксис."
|
||||||
|
|
||||||
@ -446,9 +369,7 @@ msgid "runtime.error_detail"
|
|||||||
msgstr "Рядок {line}: {message}"
|
msgstr "Рядок {line}: {message}"
|
||||||
|
|
||||||
msgid "runtime.errors_see_details"
|
msgid "runtime.errors_see_details"
|
||||||
msgstr ""
|
msgstr "У тексті діалогу було виявлено помилки ({count}). Див. детальніше у розділі «Вивід»."
|
||||||
"У тексті діалогу було виявлено помилки ({count}). Див. детальніше у розділі "
|
|
||||||
"«Вивід»."
|
|
||||||
|
|
||||||
msgid "runtime.invalid_expression"
|
msgid "runtime.invalid_expression"
|
||||||
msgstr "«{expression}» не є допустимим виразом: {error}"
|
msgstr "«{expression}» не є допустимим виразом: {error}"
|
||||||
@ -463,29 +384,16 @@ msgid "runtime.key_not_found"
|
|||||||
msgstr "Ключ «{key}» не знайдено у словнику «{dictionary}»"
|
msgstr "Ключ «{key}» не знайдено у словнику «{dictionary}»"
|
||||||
|
|
||||||
msgid "runtime.property_not_found"
|
msgid "runtime.property_not_found"
|
||||||
msgstr ""
|
msgstr "«{property}» не знайдено. Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей."
|
||||||
"«{property}» не знайдено. Стани з безпосередньо доступними властивостями/"
|
|
||||||
"методами/сигналами включають {states}. На автозавантаження потрібно "
|
|
||||||
"посилатися за їхніми назвами для використання їхніх властивостей."
|
|
||||||
|
|
||||||
msgid "runtime.property_not_found_missing_export"
|
msgid "runtime.property_not_found_missing_export"
|
||||||
msgstr ""
|
msgstr "«{property}» не знайдено. Можливо, вам слід додати декоратор «[Export]». Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей."
|
||||||
"«{property}» не знайдено. Можливо, вам слід додати декоратор «[Export]». "
|
|
||||||
"Стани з безпосередньо доступними властивостями/методами/сигналами включають "
|
|
||||||
"{states}. На автозавантаження потрібно посилатися за їхніми назвами для "
|
|
||||||
"використання їхніх властивостей."
|
|
||||||
|
|
||||||
msgid "runtime.method_not_found"
|
msgid "runtime.method_not_found"
|
||||||
msgstr ""
|
msgstr "Метод «{method}» не знайдено. Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей."
|
||||||
"Метод «{method}» не знайдено. Стани з безпосередньо доступними властивостями/"
|
|
||||||
"методами/сигналами включають {states}. На автозавантаження потрібно "
|
|
||||||
"посилатися за їхніми назвами для використання їхніх властивостей."
|
|
||||||
|
|
||||||
msgid "runtime.signal_not_found"
|
msgid "runtime.signal_not_found"
|
||||||
msgstr ""
|
msgstr "Сигнал «{signal_name}» не знайдено. Стани з безпосередньо доступними властивостями/методами/сигналами включають {states}. На автозавантаження потрібно посилатися за їхніми назвами для використання їхніх властивостей."
|
||||||
"Сигнал «{signal_name}» не знайдено. Стани з безпосередньо доступними "
|
|
||||||
"властивостями/методами/сигналами включають {states}. На автозавантаження "
|
|
||||||
"потрібно посилатися за їхніми назвами для використання їхніх властивостей."
|
|
||||||
|
|
||||||
msgid "runtime.method_not_callable"
|
msgid "runtime.method_not_callable"
|
||||||
msgstr "«{method}» не є методом, який можна викликати в «{object}»"
|
msgstr "«{method}» не є методом, який можна викликати в «{object}»"
|
||||||
@ -500,14 +408,16 @@ msgid "runtime.something_went_wrong"
|
|||||||
msgstr "Щось пішло не так."
|
msgstr "Щось пішло не так."
|
||||||
|
|
||||||
msgid "runtime.expected_n_got_n_args"
|
msgid "runtime.expected_n_got_n_args"
|
||||||
msgstr ""
|
msgstr "«{method}» було викликано з аргументами «{received}», але воно має лише «{expected}»."
|
||||||
"«{method}» було викликано з аргументами «{received}», але воно має лише "
|
|
||||||
"«{expected}»."
|
|
||||||
|
|
||||||
msgid "runtime.unsupported_array_type"
|
msgid "runtime.unsupported_array_type"
|
||||||
msgstr ""
|
msgstr "Array[{type}] не підтримується у модифікаціях. Натомість використовуйте Array як тип."
|
||||||
"Array[{type}] не підтримується у модифікаціях. Натомість використовуйте "
|
|
||||||
"Array як тип."
|
|
||||||
|
|
||||||
msgid "runtime.dialogue_balloon_missing_start_method"
|
msgid "runtime.dialogue_balloon_missing_start_method"
|
||||||
msgstr "У вашій кулі діалогу відсутній метод «start» або «Start»."
|
msgstr "У вашій кулі діалогу відсутній метод «start» або «Start»."
|
||||||
|
|
||||||
|
msgid "runtime.top_level_states_share_name"
|
||||||
|
msgstr "Кілька станів верхнього рівня ({states}) мають спільну назву методу/властивості/сигналу «{key}». Для діалогу доступний лише перший випадок."
|
||||||
|
|
||||||
|
msgid "translation_plugin.character_name"
|
||||||
|
msgstr "Ім’я персонажа"
|
||||||
|
@ -44,9 +44,6 @@ msgstr "插入"
|
|||||||
msgid "translations"
|
msgid "translations"
|
||||||
msgstr "翻译"
|
msgstr "翻译"
|
||||||
|
|
||||||
msgid "settings"
|
|
||||||
msgstr "设置"
|
|
||||||
|
|
||||||
msgid "show_support"
|
msgid "show_support"
|
||||||
msgstr "支持 Dialogue Manager"
|
msgstr "支持 Dialogue Manager"
|
||||||
|
|
||||||
@ -137,69 +134,6 @@ msgstr "复制文件路径"
|
|||||||
msgid "buffer.show_in_filesystem"
|
msgid "buffer.show_in_filesystem"
|
||||||
msgstr "在 Godot 侧边栏中显示"
|
msgstr "在 Godot 侧边栏中显示"
|
||||||
|
|
||||||
msgid "settings.revert_to_default_test_scene"
|
|
||||||
msgstr "重置测试场景设定"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_hint"
|
|
||||||
msgstr "设置调用 \"DialogueManager.show_balloon()\" 时使用的对话框"
|
|
||||||
|
|
||||||
msgid "settings.autoload"
|
|
||||||
msgstr "Autoload"
|
|
||||||
|
|
||||||
msgid "settings.path"
|
|
||||||
msgstr "路径"
|
|
||||||
|
|
||||||
msgid "settings.new_template"
|
|
||||||
msgstr "新建文件时自动插入模板"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys"
|
|
||||||
msgstr "将翻译键缺失视为错误"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys_hint"
|
|
||||||
msgstr "如果你使用静态键,这将会帮助你寻找未添加至翻译文件的键。"
|
|
||||||
|
|
||||||
msgid "settings.characters_translations"
|
|
||||||
msgstr "在翻译文件中导出角色名"
|
|
||||||
|
|
||||||
msgid "settings.wrap_long_lines"
|
|
||||||
msgstr "文本编辑器自动换行"
|
|
||||||
|
|
||||||
msgid "settings.include_failed_responses"
|
|
||||||
msgstr "在判断条件失败时仍显示回复选项"
|
|
||||||
|
|
||||||
msgid "settings.ignore_missing_state_values"
|
|
||||||
msgstr "忽略全局变量缺失错误(不建议)"
|
|
||||||
|
|
||||||
msgid "settings.custom_test_scene"
|
|
||||||
msgstr "自定义测试场景(必须继承自BaseDialogueTestScene)"
|
|
||||||
|
|
||||||
msgid "settings.default_csv_locale"
|
|
||||||
msgstr "默认 CSV 区域格式"
|
|
||||||
|
|
||||||
msgid "settings.states_shortcuts"
|
|
||||||
msgstr "全局变量映射"
|
|
||||||
|
|
||||||
msgid "settings.states_message"
|
|
||||||
msgstr "当一个 Autoload 在这里被勾选,他的所有成员会被映射为全局变量。"
|
|
||||||
|
|
||||||
msgid "settings.states_hint"
|
|
||||||
msgstr "比如,当你开启对于“Foo”的映射时,你可以将“Foo.bar”简写成“bar”。"
|
|
||||||
|
|
||||||
msgid "settings.recompile_warning"
|
|
||||||
msgstr "更改这些选项会强制重新编译所有的对话框,当你清楚在做什么的时候更改。"
|
|
||||||
|
|
||||||
msgid "settings.create_lines_for_responses_with_characters"
|
|
||||||
msgstr "回复项带角色名时(- char: response),会自动生成为选择后的下一句对话"
|
|
||||||
|
|
||||||
msgid "settings.include_characters_in_translations"
|
|
||||||
msgstr "导出 CSV 时包括角色名"
|
|
||||||
|
|
||||||
msgid "settings.include_notes_in_translations"
|
|
||||||
msgstr "导出 CSV 时包括注释(## comments)"
|
|
||||||
|
|
||||||
msgid "settings.check_for_updates"
|
|
||||||
msgstr "检查升级"
|
|
||||||
|
|
||||||
msgid "n_of_n"
|
msgid "n_of_n"
|
||||||
msgstr "第{index}个,共{total}个"
|
msgstr "第{index}个,共{total}个"
|
||||||
|
|
||||||
|
@ -44,9 +44,6 @@ msgstr "插入"
|
|||||||
msgid "translations"
|
msgid "translations"
|
||||||
msgstr "翻譯"
|
msgstr "翻譯"
|
||||||
|
|
||||||
msgid "settings"
|
|
||||||
msgstr "設定"
|
|
||||||
|
|
||||||
msgid "show_support"
|
msgid "show_support"
|
||||||
msgstr "支援 Dialogue Manager"
|
msgstr "支援 Dialogue Manager"
|
||||||
|
|
||||||
@ -137,69 +134,6 @@ msgstr "複製檔案位置"
|
|||||||
msgid "buffer.show_in_filesystem"
|
msgid "buffer.show_in_filesystem"
|
||||||
msgstr "在 Godot 側邊欄中顯示"
|
msgstr "在 Godot 側邊欄中顯示"
|
||||||
|
|
||||||
msgid "settings.revert_to_default_test_scene"
|
|
||||||
msgstr "重置測試場景設定"
|
|
||||||
|
|
||||||
msgid "settings.default_balloon_hint"
|
|
||||||
msgstr "設置使用 \"DialogueManager.show_balloon()\" 时的对话框"
|
|
||||||
|
|
||||||
msgid "settings.autoload"
|
|
||||||
msgstr "Autoload"
|
|
||||||
|
|
||||||
msgid "settings.path"
|
|
||||||
msgstr "路徑"
|
|
||||||
|
|
||||||
msgid "settings.new_template"
|
|
||||||
msgstr "新建檔案時自動插入模板"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys"
|
|
||||||
msgstr "將翻譯鍵缺失視爲錯誤"
|
|
||||||
|
|
||||||
msgid "settings.missing_keys_hint"
|
|
||||||
msgstr "如果你使用靜態鍵,這將會幫助你尋找未添加至翻譯檔案的鍵。"
|
|
||||||
|
|
||||||
msgid "settings.wrap_long_lines"
|
|
||||||
msgstr "自動折行"
|
|
||||||
|
|
||||||
msgid "settings.characters_translations"
|
|
||||||
msgstr "在翻譯檔案中匯出角色名。"
|
|
||||||
|
|
||||||
msgid "settings.include_failed_responses"
|
|
||||||
msgstr "在判斷條件失敗時仍顯示回復選項"
|
|
||||||
|
|
||||||
msgid "settings.ignore_missing_state_values"
|
|
||||||
msgstr "忽略全局變量缺失錯誤(不建議)"
|
|
||||||
|
|
||||||
msgid "settings.custom_test_scene"
|
|
||||||
msgstr "自訂測試場景(必須繼承自BaseDialogueTestScene)"
|
|
||||||
|
|
||||||
msgid "settings.default_csv_locale"
|
|
||||||
msgstr "預設 CSV 區域格式"
|
|
||||||
|
|
||||||
msgid "settings.states_shortcuts"
|
|
||||||
msgstr "全局變量映射"
|
|
||||||
|
|
||||||
msgid "settings.states_message"
|
|
||||||
msgstr "當一個 Autoload 在這裏被勾選,他的所有成員會被映射爲全局變量。"
|
|
||||||
|
|
||||||
msgid "settings.states_hint"
|
|
||||||
msgstr "比如,當你開啓對於“Foo”的映射時,你可以將“Foo.bar”簡寫成“bar”。"
|
|
||||||
|
|
||||||
msgid "settings.recompile_warning"
|
|
||||||
msgstr "更改這些選項會強制重新編譯所有的對話框,當你清楚在做什麼的時候更改。"
|
|
||||||
|
|
||||||
msgid "settings.create_lines_for_responses_with_characters"
|
|
||||||
msgstr "回覆項目帶角色名稱時(- char: response),會自動產生為選擇後的下一句對話"
|
|
||||||
|
|
||||||
msgid "settings.include_characters_in_translations"
|
|
||||||
msgstr "匯出 CSV 時包含角色名"
|
|
||||||
|
|
||||||
msgid "settings.include_notes_in_translations"
|
|
||||||
msgstr "匯出 CSV 時包括註解(## comments)"
|
|
||||||
|
|
||||||
msgid "settings.check_for_updates"
|
|
||||||
msgstr "檢查升級"
|
|
||||||
|
|
||||||
msgid "n_of_n"
|
msgid "n_of_n"
|
||||||
msgstr "第{index}個,共{total}個"
|
msgstr "第{index}個,共{total}個"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[plugin]
|
[plugin]
|
||||||
|
|
||||||
name="Dialogue Manager"
|
name="Dialogue Manager"
|
||||||
description="A simple but powerful branching dialogue system"
|
description="A powerful nonlinear dialogue system"
|
||||||
author="Nathan Hoad"
|
author="Nathan Hoad"
|
||||||
version="2.45.0"
|
version="3.4.0"
|
||||||
script="plugin.gd"
|
script="plugin.gd"
|
||||||
|
1
addons/dialogue_manager/plugin.cfg.uid
Normal file
1
addons/dialogue_manager/plugin.cfg.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://hrny2utekhei
|
@ -2,21 +2,14 @@
|
|||||||
extends EditorPlugin
|
extends EditorPlugin
|
||||||
|
|
||||||
|
|
||||||
const DialogueConstants = preload("./constants.gd")
|
|
||||||
const DialogueImportPlugin = preload("./import_plugin.gd")
|
|
||||||
const DialogueInspectorPlugin = preload("./inspector_plugin.gd")
|
|
||||||
const DialogueTranslationParserPlugin = preload("./editor_translation_parser_plugin.gd")
|
|
||||||
const DialogueSettings = preload("./settings.gd")
|
|
||||||
const DialogueCache = preload("./components/dialogue_cache.gd")
|
|
||||||
const MainView = preload("./views/main_view.tscn")
|
const MainView = preload("./views/main_view.tscn")
|
||||||
const DialogueResource = preload("./dialogue_resource.gd")
|
|
||||||
|
|
||||||
|
|
||||||
var import_plugin: DialogueImportPlugin
|
var import_plugin: DMImportPlugin
|
||||||
var inspector_plugin: DialogueInspectorPlugin
|
var inspector_plugin: DMInspectorPlugin
|
||||||
var translation_parser_plugin: DialogueTranslationParserPlugin
|
var translation_parser_plugin: DMTranslationParserPlugin
|
||||||
var main_view
|
var main_view
|
||||||
var dialogue_cache: DialogueCache
|
var dialogue_cache: DMCache
|
||||||
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
func _enter_tree() -> void:
|
||||||
@ -25,63 +18,87 @@ func _enter_tree() -> void:
|
|||||||
if Engine.is_editor_hint():
|
if Engine.is_editor_hint():
|
||||||
Engine.set_meta("DialogueManagerPlugin", self)
|
Engine.set_meta("DialogueManagerPlugin", self)
|
||||||
|
|
||||||
DialogueSettings.prepare()
|
DMSettings.prepare()
|
||||||
|
|
||||||
dialogue_cache = DialogueCache.new()
|
dialogue_cache = DMCache.new()
|
||||||
Engine.set_meta("DialogueCache", dialogue_cache)
|
Engine.set_meta("DMCache", dialogue_cache)
|
||||||
|
|
||||||
import_plugin = DialogueImportPlugin.new()
|
import_plugin = DMImportPlugin.new()
|
||||||
add_import_plugin(import_plugin)
|
add_import_plugin(import_plugin)
|
||||||
|
|
||||||
inspector_plugin = DialogueInspectorPlugin.new()
|
inspector_plugin = DMInspectorPlugin.new()
|
||||||
add_inspector_plugin(inspector_plugin)
|
add_inspector_plugin(inspector_plugin)
|
||||||
|
|
||||||
translation_parser_plugin = DialogueTranslationParserPlugin.new()
|
translation_parser_plugin = DMTranslationParserPlugin.new()
|
||||||
add_translation_parser_plugin(translation_parser_plugin)
|
add_translation_parser_plugin(translation_parser_plugin)
|
||||||
|
|
||||||
main_view = MainView.instantiate()
|
main_view = MainView.instantiate()
|
||||||
get_editor_interface().get_editor_main_screen().add_child(main_view)
|
EditorInterface.get_editor_main_screen().add_child(main_view)
|
||||||
_make_visible(false)
|
_make_visible(false)
|
||||||
main_view.add_child(dialogue_cache)
|
main_view.add_child(dialogue_cache)
|
||||||
|
|
||||||
_update_localization()
|
_update_localization()
|
||||||
|
|
||||||
get_editor_interface().get_file_system_dock().files_moved.connect(_on_files_moved)
|
EditorInterface.get_file_system_dock().files_moved.connect(_on_files_moved)
|
||||||
get_editor_interface().get_file_system_dock().file_removed.connect(_on_file_removed)
|
EditorInterface.get_file_system_dock().file_removed.connect(_on_file_removed)
|
||||||
|
|
||||||
add_tool_menu_item("Create copy of dialogue example balloon...", _copy_dialogue_balloon)
|
add_tool_menu_item("Create copy of dialogue example balloon...", _copy_dialogue_balloon)
|
||||||
|
|
||||||
# Make sure the current balloon has a UID unique from the example balloon's
|
# Automatically swap the script on the example balloon depending on if dotnet is being used.
|
||||||
var balloon_path: String = DialogueSettings.get_setting("balloon_path", "")
|
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):
|
if balloon_path != "" and FileAccess.file_exists(balloon_path):
|
||||||
var is_small_window: bool = ProjectSettings.get_setting("display/window/size/viewport_width") < 400
|
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_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 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 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))
|
var balloon_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(balloon_path))
|
||||||
if example_balloon_uid == balloon_uid:
|
if example_balloon_uid == balloon_uid:
|
||||||
var new_balloon_uid: String = ResourceUID.id_to_text(ResourceUID.create_id())
|
var new_balloon_uid: String = ResourceUID.id_to_text(ResourceUID.create_id())
|
||||||
var contents: String = FileAccess.get_file_as_string(balloon_path)
|
|
||||||
contents = contents.replace(example_balloon_uid, new_balloon_uid)
|
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)
|
var balloon_file: FileAccess = FileAccess.open(balloon_path, FileAccess.WRITE)
|
||||||
balloon_file.store_string(contents)
|
balloon_file.store_string(contents)
|
||||||
balloon_file.close()
|
balloon_file.close()
|
||||||
|
|
||||||
# Prevent the project from showing as unsaved even though it was only just opened
|
|
||||||
if DialogueSettings.get_setting("try_suppressing_startup_unsaved_indicator", false) \
|
|
||||||
and Engine.get_physics_frames() == 0 \
|
|
||||||
and get_editor_interface().has_method("save_all_scenes"):
|
|
||||||
var timer: Timer = Timer.new()
|
|
||||||
var suppress_unsaved_marker: Callable
|
|
||||||
suppress_unsaved_marker = func():
|
|
||||||
if Engine.get_frames_per_second() >= 10:
|
|
||||||
timer.stop()
|
|
||||||
get_editor_interface().call("save_all_scenes")
|
|
||||||
timer.queue_free()
|
|
||||||
timer.timeout.connect(suppress_unsaved_marker)
|
|
||||||
add_child(timer)
|
|
||||||
timer.start(0.1)
|
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
remove_autoload_singleton("DialogueManager")
|
remove_autoload_singleton("DialogueManager")
|
||||||
@ -99,10 +116,10 @@ func _exit_tree() -> void:
|
|||||||
main_view.queue_free()
|
main_view.queue_free()
|
||||||
|
|
||||||
Engine.remove_meta("DialogueManagerPlugin")
|
Engine.remove_meta("DialogueManagerPlugin")
|
||||||
Engine.remove_meta("DialogueCache")
|
Engine.remove_meta("DMCache")
|
||||||
|
|
||||||
get_editor_interface().get_file_system_dock().files_moved.disconnect(_on_files_moved)
|
EditorInterface.get_file_system_dock().files_moved.disconnect(_on_files_moved)
|
||||||
get_editor_interface().get_file_system_dock().file_removed.disconnect(_on_file_removed)
|
EditorInterface.get_file_system_dock().file_removed.disconnect(_on_file_removed)
|
||||||
|
|
||||||
remove_tool_menu_item("Create copy of dialogue example balloon...")
|
remove_tool_menu_item("Create copy of dialogue example balloon...")
|
||||||
|
|
||||||
@ -125,10 +142,10 @@ func _get_plugin_icon() -> Texture2D:
|
|||||||
|
|
||||||
|
|
||||||
func _handles(object) -> bool:
|
func _handles(object) -> bool:
|
||||||
var editor_settings: EditorSettings = get_editor_interface().get_editor_settings()
|
var editor_settings: EditorSettings = EditorInterface.get_editor_settings()
|
||||||
var external_editor: String = editor_settings.get_setting("text_editor/external/exec_path")
|
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 != ""
|
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 DialogueSettings.get_user_value("open_in_external_editor", false):
|
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 project_path: String = ProjectSettings.globalize_path("res://")
|
||||||
var file_path: String = ProjectSettings.globalize_path(object.resource_path)
|
var file_path: String = ProjectSettings.globalize_path(object.resource_path)
|
||||||
OS.create_process(external_editor, [project_path, file_path])
|
OS.create_process(external_editor, [project_path, file_path])
|
||||||
@ -150,10 +167,10 @@ func _apply_changes() -> void:
|
|||||||
|
|
||||||
func _build() -> bool:
|
func _build() -> bool:
|
||||||
# If this is the dotnet Godot then we need to check if the solution file exists
|
# If this is the dotnet Godot then we need to check if the solution file exists
|
||||||
DialogueSettings.check_for_dotnet_solution()
|
DMSettings.check_for_dotnet_solution()
|
||||||
|
|
||||||
# Ignore errors in other files if we are just running the test scene
|
# Ignore errors in other files if we are just running the test scene
|
||||||
if DialogueSettings.get_user_value("is_running_test_scene", true): return true
|
if DMSettings.get_user_value("is_running_test_scene", true): return true
|
||||||
|
|
||||||
if dialogue_cache != null:
|
if dialogue_cache != null:
|
||||||
dialogue_cache.reimport_files()
|
dialogue_cache.reimport_files()
|
||||||
@ -162,7 +179,7 @@ func _build() -> bool:
|
|||||||
if files_with_errors.size() > 0:
|
if files_with_errors.size() > 0:
|
||||||
for dialogue_file in files_with_errors:
|
for dialogue_file in files_with_errors:
|
||||||
push_error("You have %d error(s) in %s" % [dialogue_file.errors.size(), dialogue_file.path])
|
push_error("You have %d error(s) in %s" % [dialogue_file.errors.size(), dialogue_file.path])
|
||||||
get_editor_interface().edit_resource(load(files_with_errors[0].path))
|
EditorInterface.edit_resource(load(files_with_errors[0].path))
|
||||||
main_view.show_build_error_dialog()
|
main_view.show_build_error_dialog()
|
||||||
return false
|
return false
|
||||||
|
|
||||||
@ -209,7 +226,7 @@ func get_editor_shortcuts() -> Dictionary:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths = get_editor_interface().get_editor_paths()
|
var paths = EditorInterface.get_editor_paths()
|
||||||
var settings
|
var settings
|
||||||
if FileAccess.file_exists(paths.get_config_dir() + "/editor_settings-4.3.tres"):
|
if FileAccess.file_exists(paths.get_config_dir() + "/editor_settings-4.3.tres"):
|
||||||
settings = load(paths.get_config_dir() + "/editor_settings-4.3.tres")
|
settings = load(paths.get_config_dir() + "/editor_settings-4.3.tres")
|
||||||
@ -283,7 +300,7 @@ func update_import_paths(from_path: String, to_path: String) -> void:
|
|||||||
# Update the live buffer
|
# Update the live buffer
|
||||||
if main_view.current_file_path == dependent.path:
|
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.code_edit.text = main_view.code_edit.text.replace(from_path, to_path)
|
||||||
main_view.pristine_text = main_view.code_edit.text
|
main_view.open_buffers[main_view.current_file_path].pristine_text = main_view.code_edit.text
|
||||||
|
|
||||||
# Open the file and update the path
|
# Open the file and update the path
|
||||||
var file: FileAccess = FileAccess.open(dependent.path, FileAccess.READ)
|
var file: FileAccess = FileAccess.open(dependent.path, FileAccess.READ)
|
||||||
@ -323,7 +340,7 @@ func _update_localization() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _copy_dialogue_balloon() -> void:
|
func _copy_dialogue_balloon() -> void:
|
||||||
var scale: float = get_editor_interface().get_editor_scale()
|
var scale: float = EditorInterface.get_editor_scale()
|
||||||
var directory_dialog: FileDialog = FileDialog.new()
|
var directory_dialog: FileDialog = FileDialog.new()
|
||||||
var label: Label = Label.new()
|
var label: Label = Label.new()
|
||||||
label.text = "Dialogue balloon files will be copied into chosen directory."
|
label.text = "Dialogue balloon files will be copied into chosen directory."
|
||||||
@ -332,8 +349,8 @@ func _copy_dialogue_balloon() -> void:
|
|||||||
directory_dialog.min_size = Vector2(600, 500) * scale
|
directory_dialog.min_size = Vector2(600, 500) * scale
|
||||||
directory_dialog.dir_selected.connect(func(path):
|
directory_dialog.dir_selected.connect(func(path):
|
||||||
var plugin_path: String = get_plugin_path()
|
var plugin_path: String = get_plugin_path()
|
||||||
|
var is_dotnet: bool = DMSettings.check_for_dotnet_solution()
|
||||||
|
|
||||||
var is_dotnet: bool = DialogueSettings.check_for_dotnet_solution()
|
|
||||||
var balloon_path: String = path + ("/Balloon.tscn" if is_dotnet else "/balloon.tscn")
|
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")
|
var balloon_script_path: String = path + ("/DialogueBalloon.cs" if is_dotnet else "/balloon.gd")
|
||||||
|
|
||||||
@ -342,19 +359,12 @@ func _copy_dialogue_balloon() -> void:
|
|||||||
var example_balloon_file_name: String = "small_example_balloon.tscn" if is_small_window else "example_balloon.tscn"
|
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_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_file_name: String = "ExampleBalloon.cs" if is_dotnet else "example_balloon.gd"
|
||||||
var file_contents: String = FileAccess.get_file_as_string(example_balloon_path).replace(plugin_path + "/example_balloon/example_balloon.gd", balloon_script_path)
|
var example_balloon_script_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(plugin_path + "/example_balloon/example_balloon.gd"))
|
||||||
# Give the balloon a unique UID
|
|
||||||
var example_balloon_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(example_balloon_path))
|
var example_balloon_uid: String = ResourceUID.id_to_text(ResourceLoader.get_resource_uid(example_balloon_path))
|
||||||
var new_balloon_uid: String = ResourceUID.id_to_text(ResourceUID.create_id())
|
|
||||||
file_contents = file_contents.replace(example_balloon_uid, new_balloon_uid)
|
|
||||||
# Save the new balloon
|
|
||||||
var file: FileAccess = FileAccess.open(balloon_path, FileAccess.WRITE)
|
|
||||||
file.store_string(file_contents)
|
|
||||||
file.close()
|
|
||||||
|
|
||||||
# Copy the script file
|
# Copy the script file
|
||||||
file = FileAccess.open(plugin_path + "/example_balloon/" + example_balloon_script_file_name, FileAccess.READ)
|
var file: FileAccess = FileAccess.open(plugin_path + "/example_balloon/" + example_balloon_script_file_name, FileAccess.READ)
|
||||||
file_contents = file.get_as_text()
|
var file_contents: String = file.get_as_text()
|
||||||
if is_dotnet:
|
if is_dotnet:
|
||||||
file_contents = file_contents.replace("class ExampleBalloon", "class DialogueBalloon")
|
file_contents = file_contents.replace("class ExampleBalloon", "class DialogueBalloon")
|
||||||
else:
|
else:
|
||||||
@ -362,15 +372,30 @@ func _copy_dialogue_balloon() -> void:
|
|||||||
file = FileAccess.open(balloon_script_path, FileAccess.WRITE)
|
file = FileAccess.open(balloon_script_path, FileAccess.WRITE)
|
||||||
file.store_string(file_contents)
|
file.store_string(file_contents)
|
||||||
file.close()
|
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)
|
||||||
|
|
||||||
get_editor_interface().get_resource_filesystem().scan()
|
# Save the new balloon
|
||||||
get_editor_interface().get_file_system_dock().call_deferred("navigate_to_path", balloon_path)
|
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()
|
||||||
|
|
||||||
DialogueSettings.set_setting("balloon_path", balloon_path)
|
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()
|
directory_dialog.queue_free()
|
||||||
)
|
)
|
||||||
get_editor_interface().get_base_control().add_child(directory_dialog)
|
EditorInterface.get_base_control().add_child(directory_dialog)
|
||||||
directory_dialog.popup_centered()
|
directory_dialog.popup_centered()
|
||||||
|
|
||||||
|
|
||||||
@ -379,7 +404,7 @@ func _copy_dialogue_balloon() -> void:
|
|||||||
|
|
||||||
func _on_files_moved(old_file: String, new_file: String) -> void:
|
func _on_files_moved(old_file: String, new_file: String) -> void:
|
||||||
update_import_paths(old_file, new_file)
|
update_import_paths(old_file, new_file)
|
||||||
DialogueSettings.move_recent_file(old_file, new_file)
|
DMSettings.move_recent_file(old_file, new_file)
|
||||||
|
|
||||||
|
|
||||||
func _on_file_removed(file: String) -> void:
|
func _on_file_removed(file: String) -> void:
|
||||||
|
1
addons/dialogue_manager/plugin.gd.uid
Normal file
1
addons/dialogue_manager/plugin.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bpv426rpvrafa
|
@ -1,91 +1,199 @@
|
|||||||
@tool
|
@tool
|
||||||
extends Node
|
class_name DMSettings extends Node
|
||||||
|
|
||||||
|
|
||||||
const DialogueConstants = preload("./constants.gd")
|
#region Editor
|
||||||
|
|
||||||
|
|
||||||
### Editor config
|
|
||||||
|
|
||||||
const DEFAULT_SETTINGS = {
|
## Wrap lines in the dialogue editor.
|
||||||
states = [],
|
const WRAP_LONG_LINES = "editor/wrap_long_lines"
|
||||||
missing_translations_are_errors = false,
|
## The template to start new dialogue files with.
|
||||||
export_characters_in_translation = true,
|
const NEW_FILE_TEMPLATE = "editor/new_file_template"
|
||||||
wrap_lines = false,
|
|
||||||
new_with_template = true,
|
## Show lines without statis IDs as errors.
|
||||||
new_template = "~ this_is_a_node_title\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 => this_is_a_node_title\n- End the conversation => END\nNathan: For more information see the online documentation.\n=> END",
|
const MISSING_TRANSLATIONS_ARE_ERRORS = "editor/translations/missing_translations_are_errors"
|
||||||
include_all_responses = false,
|
## Include character names in the list of translatable strings.
|
||||||
ignore_missing_state_values = false,
|
const INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST = "editor/translations/include_characters_in_translatable_strings_list"
|
||||||
custom_test_scene_path = preload("./test_scene.tscn").resource_path,
|
## The default locale to use when exporting CSVs
|
||||||
default_csv_locale = "en",
|
const DEFAULT_CSV_LOCALE = "editor/translations/default_csv_locale"
|
||||||
balloon_path = "",
|
## Any extra CSV locales to append to the exported translation CSV
|
||||||
create_lines_for_responses_with_characters = true,
|
const EXTRA_CSV_LOCALES = "editor/translations/extra_csv_locales"
|
||||||
include_character_in_translation_exports = false,
|
## Includes a "_character" column in CSV exports.
|
||||||
include_notes_in_translation_exports = false,
|
const INCLUDE_CHARACTER_IN_TRANSLATION_EXPORTS = "editor/translations/include_character_in_translation_exports"
|
||||||
uses_dotnet = false,
|
## Includes a "_notes" column in CSV exports
|
||||||
try_suppressing_startup_unsaved_indicator = false
|
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:
|
static func prepare() -> void:
|
||||||
# Migrate previous keys
|
var should_save_settings: bool = false
|
||||||
for key in [
|
|
||||||
"states",
|
# Remap any old settings into their new keys
|
||||||
"missing_translations_are_errors",
|
var legacy_map: Dictionary = {
|
||||||
"export_characters_in_translation",
|
states = STATE_AUTOLOAD_SHORTCUTS,
|
||||||
"wrap_lines",
|
missing_translations_are_errors = MISSING_TRANSLATIONS_ARE_ERRORS,
|
||||||
"new_with_template",
|
export_characters_in_translation = INCLUDE_CHARACTERS_IN_TRANSLATABLE_STRINGS_LIST,
|
||||||
"include_all_responses",
|
wrap_lines = WRAP_LONG_LINES,
|
||||||
"custom_test_scene_path"
|
new_with_template = null,
|
||||||
]:
|
new_template = NEW_FILE_TEMPLATE,
|
||||||
if ProjectSettings.has_setting("dialogue_manager/%s" % key):
|
include_all_responses = null,
|
||||||
var value = ProjectSettings.get_setting("dialogue_manager/%s" % key)
|
ignore_missing_state_values = IGNORE_MISSING_STATE_VALUES,
|
||||||
ProjectSettings.set_setting("dialogue_manager/%s" % key, null)
|
custom_test_scene_path = CUSTOM_TEST_SCENE_PATH,
|
||||||
set_setting(key, value)
|
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
|
# Set up initial settings
|
||||||
for setting in DEFAULT_SETTINGS:
|
for key: String in SETTINGS_CONFIGURATION:
|
||||||
var setting_name: String = "dialogue_manager/general/%s" % setting
|
var setting_config: Dictionary = SETTINGS_CONFIGURATION[key]
|
||||||
|
var setting_name: String = "dialogue_manager/%s" % key
|
||||||
if not ProjectSettings.has_setting(setting_name):
|
if not ProjectSettings.has_setting(setting_name):
|
||||||
set_setting(setting, DEFAULT_SETTINGS[setting])
|
ProjectSettings.set_setting(setting_name, setting_config.value)
|
||||||
ProjectSettings.set_initial_value(setting_name, DEFAULT_SETTINGS[setting])
|
ProjectSettings.set_initial_value(setting_name, setting_config.value)
|
||||||
if setting.ends_with("_path"):
|
ProjectSettings.add_property_info({
|
||||||
ProjectSettings.add_property_info({
|
"name" = setting_name,
|
||||||
"name": setting_name,
|
"type" = setting_config.type,
|
||||||
"type": TYPE_STRING,
|
"hint" = setting_config.get("hint", PROPERTY_HINT_NONE),
|
||||||
"hint": PROPERTY_HINT_FILE,
|
"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"))
|
||||||
|
|
||||||
# Some settings shouldn't be edited directly in the Project Settings window
|
if should_save_settings:
|
||||||
ProjectSettings.set_as_internal("dialogue_manager/general/states", true)
|
ProjectSettings.save()
|
||||||
ProjectSettings.set_as_internal("dialogue_manager/general/custom_test_scene_path", true)
|
|
||||||
ProjectSettings.set_as_internal("dialogue_manager/general/uses_dotnet", true)
|
|
||||||
|
|
||||||
ProjectSettings.save()
|
|
||||||
|
|
||||||
|
|
||||||
static func set_setting(key: String, value) -> void:
|
static func set_setting(key: String, value) -> void:
|
||||||
ProjectSettings.set_setting("dialogue_manager/general/%s" % key, value)
|
if get_setting(key, value) != value:
|
||||||
ProjectSettings.set_initial_value("dialogue_manager/general/%s" % key, DEFAULT_SETTINGS[key])
|
ProjectSettings.set_setting("dialogue_manager/%s" % key, value)
|
||||||
ProjectSettings.save()
|
ProjectSettings.set_initial_value("dialogue_manager/%s" % key, SETTINGS_CONFIGURATION[key].value)
|
||||||
|
ProjectSettings.save()
|
||||||
|
|
||||||
|
|
||||||
static func get_setting(key: String, default):
|
static func get_setting(key: String, default):
|
||||||
if ProjectSettings.has_setting("dialogue_manager/general/%s" % key):
|
if ProjectSettings.has_setting("dialogue_manager/%s" % key):
|
||||||
return ProjectSettings.get_setting("dialogue_manager/general/%s" % key)
|
return ProjectSettings.get_setting("dialogue_manager/%s" % key)
|
||||||
else:
|
else:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
static func get_settings(only_keys: PackedStringArray = []) -> Dictionary:
|
static func get_settings(only_keys: PackedStringArray = []) -> Dictionary:
|
||||||
var settings: Dictionary = {}
|
var settings: Dictionary = {}
|
||||||
for key in DEFAULT_SETTINGS.keys():
|
for key in SETTINGS_CONFIGURATION.keys():
|
||||||
if only_keys.is_empty() or key in only_keys:
|
if only_keys.is_empty() or key in only_keys:
|
||||||
settings[key] = get_setting(key, DEFAULT_SETTINGS[key])
|
settings[key] = get_setting(key, SETTINGS_CONFIGURATION[key].value)
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
|
||||||
### User config
|
#endregion
|
||||||
|
|
||||||
|
#region User
|
||||||
|
|
||||||
|
|
||||||
static func get_user_config() -> Dictionary:
|
static func get_user_config() -> Dictionary:
|
||||||
@ -103,15 +211,15 @@ static func get_user_config() -> Dictionary:
|
|||||||
open_in_external_editor = false
|
open_in_external_editor = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if FileAccess.file_exists(DialogueConstants.USER_CONFIG_PATH):
|
if FileAccess.file_exists(DMConstants.USER_CONFIG_PATH):
|
||||||
var file: FileAccess = FileAccess.open(DialogueConstants.USER_CONFIG_PATH, FileAccess.READ)
|
var file: FileAccess = FileAccess.open(DMConstants.USER_CONFIG_PATH, FileAccess.READ)
|
||||||
user_config.merge(JSON.parse_string(file.get_as_text()), true)
|
user_config.merge(JSON.parse_string(file.get_as_text()), true)
|
||||||
|
|
||||||
return user_config
|
return user_config
|
||||||
|
|
||||||
|
|
||||||
static func save_user_config(user_config: Dictionary) -> void:
|
static func save_user_config(user_config: Dictionary) -> void:
|
||||||
var file: FileAccess = FileAccess.open(DialogueConstants.USER_CONFIG_PATH, FileAccess.WRITE)
|
var file: FileAccess = FileAccess.open(DMConstants.USER_CONFIG_PATH, FileAccess.WRITE)
|
||||||
file.store_string(JSON.stringify(user_config))
|
file.store_string(JSON.stringify(user_config))
|
||||||
|
|
||||||
|
|
||||||
@ -182,7 +290,10 @@ static func check_for_dotnet_solution() -> bool:
|
|||||||
var directory: String = ProjectSettings.get("dotnet/project/solution_directory")
|
var directory: String = ProjectSettings.get("dotnet/project/solution_directory")
|
||||||
var file_name: String = ProjectSettings.get("dotnet/project/assembly_name")
|
var file_name: String = ProjectSettings.get("dotnet/project/assembly_name")
|
||||||
has_dotnet_solution = FileAccess.file_exists("res://%s/%s.sln" % [directory, file_name])
|
has_dotnet_solution = FileAccess.file_exists("res://%s/%s.sln" % [directory, file_name])
|
||||||
set_setting("uses_dotnet", has_dotnet_solution)
|
set_setting(DMSettings.USES_DOTNET, has_dotnet_solution)
|
||||||
return has_dotnet_solution
|
return has_dotnet_solution
|
||||||
|
|
||||||
return get_setting("uses_dotnet", false)
|
return get_setting(DMSettings.USES_DOTNET, false)
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
1
addons/dialogue_manager/settings.gd.uid
Normal file
1
addons/dialogue_manager/settings.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://ce1nk88365m52
|
@ -10,23 +10,34 @@ const DialogueResource = preload("./dialogue_resource.gd")
|
|||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
var screen_index: int = DisplayServer.get_primary_screen()
|
# Is this running in Godot >=4.4?
|
||||||
DisplayServer.window_set_position(Vector2(DisplayServer.screen_get_position(screen_index)) + (DisplayServer.screen_get_size(screen_index) - DisplayServer.window_get_size()) * 0.5)
|
if Engine.has_method("is_embedded_in_editor"):
|
||||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
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
|
# 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.
|
# enabled in settings will throw a compiler error here so I'm using `get_singleton` instead.
|
||||||
var dialogue_manager = Engine.get_singleton("DialogueManager")
|
var dialogue_manager = Engine.get_singleton("DialogueManager")
|
||||||
dialogue_manager.dialogue_ended.connect(_on_dialogue_ended)
|
dialogue_manager.dialogue_ended.connect(_on_dialogue_ended)
|
||||||
dialogue_manager.show_dialogue_balloon(resource, title)
|
dialogue_manager.show_dialogue_balloon(resource, title if not title.is_empty() else resource.first_title)
|
||||||
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
func _enter_tree() -> void:
|
||||||
DialogueSettings.set_user_value("is_running_test_scene", false)
|
DialogueSettings.set_user_value("is_running_test_scene", false)
|
||||||
|
|
||||||
|
|
||||||
### Signals
|
#region Signals
|
||||||
|
|
||||||
|
|
||||||
func _on_dialogue_ended(_resource: DialogueResource):
|
func _on_dialogue_ended(_resource: DialogueResource):
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
1
addons/dialogue_manager/test_scene.gd.uid
Normal file
1
addons/dialogue_manager/test_scene.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c8e16qdgu40wo
|
@ -1,7 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3]
|
[gd_scene load_steps=2 format=3 uid="uid://ugd552efvil0"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/test_scene.gd" id="1_yupoh"]
|
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c8e16qdgu40wo" path="res://addons/dialogue_manager/test_scene.gd" id="1_yupoh"]
|
||||||
|
|
||||||
[node name="TestScene" type="Node2D"]
|
[node name="TestScene" type="Node2D"]
|
||||||
script = ExtResource("1_yupoh")
|
script = ExtResource("1_yupoh")
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user