First work on dialogic, resized guild, and started implementing portraits.

This commit is contained in:
2025-08-14 10:26:24 -04:00
parent 95a7db036b
commit 3aeb3d44e6
959 changed files with 47688 additions and 46 deletions

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.3636 2.90912H3.63635V13H7V11.6364H5.0909V10.1818H7V9.5V9H10V8.7273H5.0909V7.27275H10V6H12.3636V2.90912ZM10.9091 4.36366H5.0909V5.81821H10.9091V4.36366Z" fill="white"/>
<path d="M13 10H16V12H13V15H11V12H8V10H11V7H13V10Z" fill="#A5EFAC"/>
</svg>

After

Width:  |  Height:  |  Size: 393 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cenut3sc5cul0"
path="res://.godot/imported/add-glossary.svg-1cde77c043d3874d9bc84cc14d0ec9dc.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Glossary/add-glossary.svg"
dest_files=["res://.godot/imported/add-glossary.svg-1cde77c043d3874d9bc84cc14d0ec9dc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,42 @@
@tool
class_name DialogicGlossaryEvent
extends DialogicEvent
## Event that does nothing right now.
################################################################################
## EXECUTE
################################################################################
func _execute() -> void:
pass
################################################################################
## INITIALIZE
################################################################################
func _init() -> void:
event_name = "Glossary"
set_default_color('Color6')
event_category = "Other"
event_sorting_index = 0
################################################################################
## SAVING/LOADING
################################################################################
func get_shortcode() -> String:
return "glossary"
func get_shortcode_parameters() -> Dictionary:
return {
}
################################################################################
## EDITOR REPRESENTATION
################################################################################
func build_event_editor() -> void:
pass

View File

@@ -0,0 +1 @@
uid://w0m5xtx6nia0

View File

@@ -0,0 +1,461 @@
@tool
extends DialogicEditor
var current_glossary: DialogicGlossary = null
var current_entry_name := ""
var current_entry := {}
################################################################################
## BASICS
################################################################################
func _get_title() -> String:
return "Glossary"
func _get_icon() -> Texture:
var base_directory: String = self.get_script().get_path().get_base_dir()
var icon_path := base_directory + "/icon.svg"
return load(icon_path)
func _register() -> void:
editors_manager.register_simple_editor(self)
alternative_text = "Create and edit glossaries."
func _ready() -> void:
var add_glossary_icon_path: String = self.get_script().get_path().get_base_dir() + "/add-glossary.svg"
var add_glossary_icon := load(add_glossary_icon_path)
%AddGlossaryFile.icon = add_glossary_icon
%LoadGlossaryFile.icon = get_theme_icon('Folder', 'EditorIcons')
%DeleteGlossaryFile.icon = get_theme_icon('Remove', 'EditorIcons')
%DeleteGlossaryEntry.icon = get_theme_icon('Remove', 'EditorIcons')
%DeleteGlossaryFile.pressed.connect(_on_delete_glossary_file_pressed)
%AddGlossaryEntry.icon = get_theme_icon('Add', 'EditorIcons')
%EntrySearch.right_icon = get_theme_icon('Search', 'EditorIcons')
%GlossaryList.item_selected.connect(_on_GlossaryList_item_selected)
%EntryList.item_selected.connect(_on_EntryList_item_selected)
%DefaultColor.color_changed.connect(set_setting.bind('dialogic/glossary/default_color'))
%DefaultCaseSensitive.toggled.connect(set_setting.bind('dialogic/glossary/default_case_sensitive'))
%EntryCaseSensitive.icon = get_theme_icon("MatchCase", "EditorIcons")
%EntryAlternatives.text_changed.connect(_on_entry_alternatives_text_changed)
func set_setting(value: Variant, setting: String) -> void:
ProjectSettings.set_setting(setting, value)
ProjectSettings.save()
func _open(_argument: Variant = null) -> void:
%DefaultColor.color = ProjectSettings.get_setting('dialogic/glossary/default_color', Color.POWDER_BLUE)
%DefaultCaseSensitive.button_pressed = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true)
%GlossaryList.clear()
var idx := 0
for file: String in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []):
if ResourceLoader.exists(file):
%GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileList', 'EditorIcons'))
else:
%GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileDead', 'EditorIcons'))
%GlossaryList.set_item_tooltip(idx, file)
idx += 1
%EntryList.clear()
if %GlossaryList.item_count != 0:
%GlossaryList.select(0)
_on_GlossaryList_item_selected(0)
else:
current_glossary = null
hide_entry_editor()
################################################################################
## GLOSSARY LIST
################################################################################
func _on_GlossaryList_item_selected(idx: int) -> void:
%EntryList.clear()
var tooltip_item: String = %GlossaryList.get_item_tooltip(idx)
if ResourceLoader.exists(tooltip_item):
var glossary_item := load(tooltip_item)
if not glossary_item is DialogicGlossary:
return
current_glossary = load(tooltip_item)
if not current_glossary is DialogicGlossary:
return
var entry_idx := 0
for entry_key: String in current_glossary.entries.keys():
var entry: Variant = current_glossary.entries.get(entry_key)
if entry is String:
continue
# Older glossary entries may not have the name property and the
# alternatives may not be set up as alias entries.
if not entry.has(DialogicGlossary.NAME_PROPERTY):
entry[DialogicGlossary.NAME_PROPERTY] = entry_key
var alternatives_array: Array = entry.get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
var alternatives := ",".join(alternatives_array)
_on_entry_alternatives_text_changed(alternatives)
ResourceSaver.save(current_glossary)
%EntryList.add_item(entry.get(DialogicGlossary.NAME_PROPERTY, str(DialogicGlossary.NAME_PROPERTY)), get_theme_icon("Breakpoint", "EditorIcons"))
var modulate_color: Color = entry.get('color', %DefaultColor.color)
%EntryList.set_item_metadata(entry_idx, entry)
%EntryList.set_item_icon_modulate(entry_idx, modulate_color)
entry_idx += 1
if %EntryList.item_count != 0:
%EntryList.select(0)
_on_EntryList_item_selected(0)
else:
hide_entry_editor()
func _on_add_glossary_file_pressed() -> void:
find_parent('EditorView').godot_file_dialog(create_new_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_SAVE_FILE, 'Create new glossary resource')
func create_new_glossary_file(path:String) -> void:
var glossary := DialogicGlossary.new()
glossary.resource_path = path
ResourceSaver.save(glossary, path)
load_glossary_file(path)
func _on_load_glossary_file_pressed() -> void:
find_parent('EditorView').godot_file_dialog(load_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_OPEN_FILE, 'Select glossary resource')
func load_glossary_file(path:String) -> void:
var list: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
if not path in list:
list.append(path)
ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
ProjectSettings.save()
%GlossaryList.add_item(DialogicUtil.pretty_name(path), get_theme_icon('FileList', 'EditorIcons'))
var selected_item_index: int = %GlossaryList.item_count - 1
%GlossaryList.set_item_tooltip(selected_item_index, path)
%GlossaryList.select(selected_item_index)
_on_GlossaryList_item_selected(selected_item_index)
func _on_delete_glossary_file_pressed() -> void:
var selected_items: PackedInt32Array = %GlossaryList.get_selected_items()
if not selected_items.is_empty():
var list: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
var selected_item_index := selected_items[0]
list.remove_at(selected_item_index)
ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
ProjectSettings.save()
_open()
################################################################################
## ENTRY LIST
################################################################################
func _on_EntryList_item_selected(idx: int) -> void:
current_entry_name = %EntryList.get_item_text(idx)
var entry_info: Dictionary = current_glossary.get_entry(current_entry_name)
current_entry = entry_info
%EntrySettings.show()
%EntryName.text = current_entry_name
%EntryCaseSensitive.button_pressed = entry_info.get('case_sensitive', %DefaultCaseSensitive.button_pressed)
var alternative_property: Array = entry_info.get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
var alternatives := ", ".join(alternative_property)
%EntryAlternatives.text = alternatives
%EntryTitle.text = entry_info.get('title', '')
%EntryText.text = entry_info.get('text', '')
%EntryExtra.text = entry_info.get('extra', '')
%EntryEnabled.button_pressed = entry_info.get('enabled', true)
%EntryColor.color = entry_info.get('color', %DefaultColor.color)
%EntryCustomColor.button_pressed = entry_info.has('color')
%EntryColor.disabled = !entry_info.has('color')
_check_entry_alternatives(alternatives)
_check_entry_name(current_entry_name, current_entry)
func _on_add_glossary_entry_pressed() -> void:
if !current_glossary:
return
var entry_count := current_glossary.entries.size() + 1
var new_name := "New Entry " + str(entry_count)
if new_name in current_glossary.entries.keys():
var random_hex_number := str(randi() % 0xFFFFFF)
new_name = new_name + " " + str(random_hex_number)
var new_glossary := {}
new_glossary[DialogicGlossary.NAME_PROPERTY] = new_name
if not current_glossary.try_add_entry(new_glossary):
print_rich("[color=red]Failed adding '" + new_name + "', exists already.[/color]")
return
ResourceSaver.save(current_glossary)
%EntryList.add_item(new_name, get_theme_icon("Breakpoint", "EditorIcons"))
var item_count: int = %EntryList.item_count - 1
%EntryList.set_item_metadata(item_count, new_name)
%EntryList.set_item_icon_modulate(item_count, %DefaultColor.color)
%EntryList.select(item_count)
_on_EntryList_item_selected(item_count)
%EntryList.ensure_current_is_visible()
%EntryName.grab_focus()
func _on_delete_glossary_entry_pressed() -> void:
var selected_items: Array = %EntryList.get_selected_items()
if not selected_items.is_empty():
var selected_item_index: int = selected_items[0]
if not current_glossary == null:
current_glossary.remove_entry(current_entry_name)
ResourceSaver.save(current_glossary)
%EntryList.remove_item(selected_item_index)
var entries_count: int = %EntryList.item_count
if entries_count > 0:
var previous_item_index := selected_item_index - 1
%EntryList.select(previous_item_index)
func _on_entry_search_text_changed(new_text: String) -> void:
if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(%EntryList.get_selected_items()[0]).to_lower():
return
for i: int in %EntryList.item_count:
if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(i).to_lower():
%EntryList.select(i)
_on_EntryList_item_selected(i)
%EntryList.ensure_current_is_visible()
################################################################################
## ENTRY EDITOR
################################################################################
func hide_entry_editor() -> void:
%EntrySettings.hide()
func _update_alias_entries(old_alias_value_key: String, new_alias_value_key: String) -> void:
for entry_key: String in current_glossary.entries.keys():
var entry_value: Variant = current_glossary.entries.get(entry_key)
if not entry_value is String:
continue
if not entry_value == old_alias_value_key:
continue
current_glossary.entries[entry_key] = new_alias_value_key
## Checks if the [param entry_name] is already used as a key for another entry
## and returns true if it doesn't.
## The [param entry] will be used to check if found entry uses the same
## reference in memory.
func _check_entry_name(entry_name: String, entry: Dictionary) -> bool:
var selected_item: int = %EntryList.get_selected_items()[0]
var raised_error: bool = false
var entry_assigned: Variant = current_glossary.entries.get(entry_name, {})
# Alternative entry uses the entry name already.
if entry_assigned is String:
raised_error = true
if entry_assigned is Dictionary and not entry_assigned.is_empty():
var entry_name_assigned: String = entry_assigned.get(DialogicGlossary.NAME_PROPERTY, "")
# Another entry uses the entry name already.
if not entry_name_assigned == entry_name:
raised_error = true
# Not the same memory reference.
if not entry == entry_assigned:
raised_error = true
if raised_error:
%EntryList.set_item_custom_bg_color(selected_item,
get_theme_color("warning_color", "Editor").darkened(0.8))
%EntryName.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
%EntryName.right_icon = get_theme_icon("StatusError", "EditorIcons")
return false
else:
%EntryName.add_theme_color_override("font_color", get_theme_color("font_color", "Editor"))
%EntryName.add_theme_color_override("caret_color", get_theme_color("font_color", "Editor"))
%EntryName.right_icon = null
%EntryList.set_item_custom_bg_color(
selected_item,
Color.TRANSPARENT
)
return true
func _on_entry_name_text_changed(new_name: String) -> void:
new_name = new_name.strip_edges()
if current_entry_name != new_name:
var selected_item: int = %EntryList.get_selected_items()[0]
if not _check_entry_name(new_name, current_entry):
return
print_rich("[color=green]Renaming entry '" + current_entry_name + "'' to '" + new_name + "'[/color]")
_update_alias_entries(current_entry_name, new_name)
current_glossary.replace_entry_key(current_entry_name, new_name)
%EntryList.set_item_text(selected_item, new_name)
%EntryList.set_item_metadata(selected_item, new_name)
ResourceSaver.save(current_glossary)
current_entry_name = new_name
func _on_entry_case_sensitive_toggled(button_pressed: bool) -> void:
current_glossary.get_entry(current_entry_name)['case_sensitive'] = button_pressed
ResourceSaver.save(current_glossary)
## Checks if the [param new_alternatives] has any alternatives that are already
## used as a key for another entry and returns true if it doesn't.
func _can_change_alternative(new_alternatives: String) -> bool:
for alternative: String in new_alternatives.split(',', false):
var stripped_alternative := alternative.strip_edges()
var value: Variant = current_glossary.entries.get(stripped_alternative, null)
if value == null:
continue
if value is String:
value = current_glossary.entries.get(value, null)
var value_name: String = value[DialogicGlossary.NAME_PROPERTY]
if not current_entry_name == value_name:
return false
return true
## Checks if [entry_alternatives] has any alternatives that are already
## used by any entry and returns true if it doesn't.
## If false, it will set the alternatives text field to a warning color and
## set an icon.
## If true, the alternatives text field will be set to the default color and
## the icon will be removed.
func _check_entry_alternatives(entry_alternatives: String) -> bool:
if not _can_change_alternative(entry_alternatives):
%EntryAlternatives.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
%EntryAlternatives.right_icon = get_theme_icon("StatusError", "EditorIcons")
return false
else:
%EntryAlternatives.add_theme_color_override("font_color", get_theme_color("font_color", "Editor"))
%EntryAlternatives.right_icon = null
return true
## The [param new_alternatives] is a passed as a string of comma separated
## values form the Dialogic editor.
##
## Saves the glossary resource file.
func _on_entry_alternatives_text_changed(new_alternatives: String) -> void:
var current_alternatives: Array = current_glossary.get_entry(current_entry_name).get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
if not _check_entry_alternatives(new_alternatives):
return
for current_alternative: String in current_alternatives:
current_glossary._remove_entry_alias(current_alternative)
var alternatives := []
for new_alternative: String in new_alternatives.split(',', false):
var stripped_alternative := new_alternative.strip_edges()
alternatives.append(stripped_alternative)
current_glossary._add_entry_key_alias(current_entry_name, stripped_alternative)
current_glossary.get_entry(current_entry_name)[DialogicGlossary.ALTERNATIVE_PROPERTY] = alternatives
ResourceSaver.save(current_glossary)
func _on_entry_title_text_changed(new_text:String) -> void:
current_glossary.get_entry(current_entry_name)['title'] = new_text
ResourceSaver.save(current_glossary)
func _on_entry_text_text_changed() -> void:
current_glossary.get_entry(current_entry_name)['text'] = %EntryText.text
ResourceSaver.save(current_glossary)
func _on_entry_extra_text_changed() -> void:
current_glossary.get_entry(current_entry_name)['extra'] = %EntryExtra.text
ResourceSaver.save(current_glossary)
func _on_entry_enabled_toggled(button_pressed:bool) -> void:
current_glossary.get_entry(current_entry_name)['enabled'] = button_pressed
ResourceSaver.save(current_glossary)
func _on_entry_custom_color_toggled(button_pressed:bool) -> void:
%EntryColor.disabled = !button_pressed
if !button_pressed:
current_glossary.get_entry(current_entry_name).erase('color')
%EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %DefaultColor.color)
else:
current_glossary.get_entry(current_entry_name)['color'] = %EntryColor.color
%EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %EntryColor.color)
func _on_entry_color_color_changed(color:Color) -> void:
current_glossary.get_entry(current_entry_name)['color'] = color
%EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], color)
ResourceSaver.save(current_glossary)

View File

@@ -0,0 +1 @@
uid://cs4mst3x1cpho

View File

@@ -0,0 +1,319 @@
[gd_scene load_steps=5 format=3 uid="uid://due48ce7jiudt"]
[ext_resource type="Script" path="res://addons/dialogic/Modules/Glossary/glossary_editor.gd" id="1_tf3p1"]
[ext_resource type="Texture2D" uid="uid://cenut3sc5cul0" path="res://addons/dialogic/Modules/Glossary/add-glossary.svg" id="2_0elx7"]
[sub_resource type="Image" id="Image_puu06"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_dfvxn"]
image = SubResource("Image_puu06")
[node name="GlossaryEditor" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource("1_tf3p1")
[node name="Entries" type="HSplitContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
split_offset = -200
[node name="Settings" type="VBoxContainer" parent="Entries"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.3
[node name="Label" type="Label" parent="Entries/Settings"]
layout_mode = 2
theme_type_variation = &"DialogicSection"
text = "Glossaries"
[node name="Glossaries" type="PanelContainer" parent="Entries/Settings"]
layout_mode = 2
size_flags_vertical = 3
theme_type_variation = &"DialogicPanelA"
[node name="Glossaries" type="VBoxContainer" parent="Entries/Settings/Glossaries"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
size_flags_stretch_ratio = 0.69
[node name="HBox" type="HBoxContainer" parent="Entries/Settings/Glossaries/Glossaries"]
layout_mode = 2
[node name="AddGlossaryFile" type="Button" parent="Entries/Settings/Glossaries/Glossaries/HBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 4
tooltip_text = "New Glossary"
icon = ExtResource("2_0elx7")
[node name="LoadGlossaryFile" type="Button" parent="Entries/Settings/Glossaries/Glossaries/HBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 4
tooltip_text = "Import Glossary File"
icon = SubResource("ImageTexture_dfvxn")
[node name="DeleteGlossaryFile" type="Button" parent="Entries/Settings/Glossaries/Glossaries/HBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 4
tooltip_text = "Delete Glossary"
icon = SubResource("ImageTexture_dfvxn")
[node name="ScrollContainer" type="ScrollContainer" parent="Entries/Settings/Glossaries/Glossaries"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="GlossaryList" type="ItemList" parent="Entries/Settings/Glossaries/Glossaries/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Label2" type="Label" parent="Entries/Settings"]
layout_mode = 2
theme_type_variation = &"DialogicSection"
text = "Defaults"
[node name="Defaults" type="VBoxContainer" parent="Entries/Settings"]
layout_mode = 2
[node name="DefaultsColor" type="HBoxContainer" parent="Entries/Settings/Defaults"]
layout_mode = 2
[node name="Label" type="Label" parent="Entries/Settings/Defaults/DefaultsColor"]
layout_mode = 2
size_flags_horizontal = 3
text = "Color"
[node name="DefaultColor" type="ColorPickerButton" parent="Entries/Settings/Defaults/DefaultsColor"]
unique_name_in_owner = true
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
size_flags_horizontal = 8
[node name="DefCaseSensitive" type="HBoxContainer" parent="Entries/Settings/Defaults"]
layout_mode = 2
[node name="Label" type="Label" parent="Entries/Settings/Defaults/DefCaseSensitive"]
layout_mode = 2
size_flags_horizontal = 3
text = "Case sensitive"
[node name="DefaultCaseSensitive" type="CheckBox" parent="Entries/Settings/Defaults/DefCaseSensitive"]
unique_name_in_owner = true
layout_mode = 2
[node name="HSplit" type="HSplitContainer" parent="Entries"]
layout_mode = 2
size_flags_horizontal = 3
[node name="VBoxContainer" type="VBoxContainer" parent="Entries/HSplit"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Label2" type="Label" parent="Entries/HSplit/VBoxContainer"]
layout_mode = 2
theme_type_variation = &"DialogicSection"
text = "Entries"
[node name="Tabs" type="PanelContainer" parent="Entries/HSplit/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_type_variation = &"DialogicPanelA"
[node name="Entries" type="VBoxContainer" parent="Entries/HSplit/VBoxContainer/Tabs"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.69
[node name="HBox" type="HBoxContainer" parent="Entries/HSplit/VBoxContainer/Tabs/Entries"]
layout_mode = 2
[node name="AddGlossaryEntry" type="Button" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 4
tooltip_text = "New Glossary Entry"
icon = SubResource("ImageTexture_dfvxn")
[node name="DeleteGlossaryEntry" type="Button" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 4
tooltip_text = "Delete Glossary Entry"
icon = SubResource("ImageTexture_dfvxn")
[node name="EntrySearch" type="LineEdit" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Search"
right_icon = SubResource("ImageTexture_dfvxn")
[node name="ScrollContainer" type="ScrollContainer" parent="Entries/HSplit/VBoxContainer/Tabs/Entries"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="EntryList" type="ItemList" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
focus_neighbor_right = NodePath("../../../../EntryEditor/Tabs/Entry Settings/EntrySettings/HBox/EntryName")
[node name="EntryEditor" type="ScrollContainer" parent="Entries/HSplit"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
horizontal_scroll_mode = 0
metadata/_edit_layout_mode = 1
[node name="VBox" type="VBoxContainer" parent="Entries/HSplit/EntryEditor"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Label" type="Label" parent="Entries/HSplit/EntryEditor/VBox"]
layout_mode = 2
theme_type_variation = &"DialogicSection"
text = "Entry Settings"
[node name="Entry Settings" type="VBoxContainer" parent="Entries/HSplit/EntryEditor/VBox"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="EntrySettings" type="GridContainer" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/h_separation = 13
columns = 2
[node name="Label2" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
size_flags_vertical = 0
text = "Name"
[node name="HBox2" type="HBoxContainer" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
[node name="EntryName" type="LineEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
focus_neighbor_left = NodePath("../../../../../../VBoxContainer/Tabs/Entries/ScrollContainer/EntryList")
theme_override_colors/caret_color = Color(0, 0, 0, 1)
placeholder_text = "Enter unique name..."
caret_blink = true
[node name="EntryCaseSensitive" type="Button" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Case sensitive"
toggle_mode = true
icon = SubResource("ImageTexture_dfvxn")
flat = true
[node name="Label3" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
size_flags_vertical = 0
text = "Alternatives"
[node name="EntryAlternatives" type="LineEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
caret_blink = true
[node name="Label4" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
size_flags_vertical = 0
text = "Title"
[node name="EntryTitle" type="LineEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
caret_blink = true
[node name="Label5" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
size_flags_vertical = 0
text = "Description"
[node name="EntryText" type="TextEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
focus_next = NodePath("../EntryExtra")
wrap_mode = 1
[node name="Label6" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
size_flags_vertical = 0
text = "Extra"
[node name="EntryExtra" type="TextEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
wrap_mode = 1
[node name="Label8" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
size_flags_vertical = 0
text = "Enabled"
[node name="EntryEnabled" type="CheckBox" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
unique_name_in_owner = true
layout_mode = 2
button_pressed = true
[node name="Label7" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
size_flags_vertical = 0
text = "Color"
[node name="HBox" type="HBoxContainer" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"]
layout_mode = 2
[node name="EntryCustomColor" type="CheckBox" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox"]
unique_name_in_owner = true
layout_mode = 2
[node name="EntryColor" type="ColorPickerButton" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/AddGlossaryFile" to="." method="_on_add_glossary_file_pressed"]
[connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/LoadGlossaryFile" to="." method="_on_load_glossary_file_pressed"]
[connection signal="pressed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/AddGlossaryEntry" to="." method="_on_add_glossary_entry_pressed"]
[connection signal="pressed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/DeleteGlossaryEntry" to="." method="_on_delete_glossary_entry_pressed"]
[connection signal="text_changed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/EntrySearch" to="." method="_on_entry_search_text_changed"]
[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2/EntryName" to="." method="_on_entry_name_text_changed"]
[connection signal="toggled" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2/EntryCaseSensitive" to="." method="_on_entry_case_sensitive_toggled"]
[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryTitle" to="." method="_on_entry_title_text_changed"]
[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryText" to="." method="_on_entry_text_text_changed"]
[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryExtra" to="." method="_on_entry_extra_text_changed"]
[connection signal="toggled" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryEnabled" to="." method="_on_entry_enabled_toggled"]
[connection signal="toggled" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox/EntryCustomColor" to="." method="_on_entry_custom_color_toggled"]
[connection signal="color_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox/EntryColor" to="." method="_on_entry_color_color_changed"]

View File

@@ -0,0 +1,340 @@
@tool
## Resource used to store glossary entries. Can be saved to disc and used as a glossary.
## Add/create glossaries fom the glossaries editor
class_name DialogicGlossary
extends Resource
## Stores all entries for the glossary.
##
## The value may either be a dictionary, representing an entry, or
## a string, representing the actual key for the key used.
## The string key-value pairs are the alias keys, they allow to redirect
## the actual glossary entry.
@export var entries := {}
## If false, no entries from this glossary will be shown
@export var enabled: bool = true
## Refers to the translation type of this resource used for CSV translation files.
const RESOURCE_NAME := "Glossary"
## The name of glossary entries, the value is the key in [member entries].
## This constant is used for CSV translation files.
const NAME_PROPERTY := "name"
## Property in a glossary entry. Alternative words for the entry name.
const ALTERNATIVE_PROPERTY := "alternatives"
## Property in a glossary entry.
const TITLE_PROPERTY := "title"
## Property in a glossary entry.
const TEXT_PROPERTY := "text"
## Property in a glossary entry.
const EXTRA_PROPERTY := "extra"
## Property in a glossary entry. The translation ID of the entry.
## May be empty if the entry has not been translated yet.
const TRANSLATION_PROPERTY := "_translation_id"
## Property in a glossary entry.
const REGEX_OPTION_PROPERTY := "regex_options"
## Prefix used for private properties in entries.
## Ignored when entries are translated.
const PRIVATE_PROPERTY_PREFIX := "_"
## Private ID assigned when this glossary is translated.
@export var _translation_id := ""
## Private lookup table used to find the translation ID of a glossary entry.
## The keys (String) are all translated words that may trigger a glossary entry to
## be shown.
## The values (String) are the translation ID.
@export var _translation_keys := {}
## Removes an entry and all its aliases (alternative property) from
## the glossary.
## [param entry_key] may be an entry name or an alias.
##
## Returns true if the entry matching the given [param entry_key] was found.
func remove_entry(entry_key: String) -> bool:
var entry: Dictionary = get_entry(entry_key)
if entry.is_empty():
return false
var aliases: Array = entry.get(ALTERNATIVE_PROPERTY, [])
for alias: String in aliases:
_remove_entry_alias(alias)
entries.erase(entry_key)
return true
## This is an internal method.
## Erases an entry alias key based the given [param entry_key].
##
## Returns true if [param entry_key] lead to a value and the value
## was an alias.
##
## This method does not update the entry's alternative property.
func _remove_entry_alias(entry_key: String) -> bool:
var value: Variant = entries.get(entry_key, null)
if value == null or value is Dictionary:
return false
entries.erase(entry_key)
return true
## Updates the glossary entry's name and related alias keys.
## The [param old_entry_key] is the old unique name of the entry.
## The [param new_entry_key] is the new unique name of the entry.
##
## This method fails if the [param old_entry_key] does not exist.
## Do not use this to update alternative names.
## In order to update alternative names, delete all with
## [method _remove_entry_alias] and then add them again with
## [method _add_entry_key_alias].
func replace_entry_key(old_entry_key: String, new_entry_key: String) -> void:
var entry := get_entry(old_entry_key)
if entry == null:
return
entry.name = new_entry_key
entries.erase(old_entry_key)
entries[new_entry_key] = entry
## Gets the glossary entry for the given [param entry_key].
## If there is no matching entry, an empty Dictionary will be returned.
## Valid glossary entry dictionaries will never be empty.
func get_entry(entry_key: String) -> Dictionary:
var entry: Variant = entries.get(entry_key, {})
# Handle alias value.
if entry is String:
entry = entries.get(entry, {})
return entry
## This is an internal method.
## The [param entry_key] must be valid entry key for an entry.
## Adds the [param alias] as a valid entry key for that entry.
##
## Returns the index of the entry, -1 if the entry does not exist.
func _add_entry_key_alias(entry_key: String, alias: String) -> bool:
var entry := get_entry(entry_key)
var alias_entry := get_entry(alias)
if not entry.is_empty() and alias_entry.is_empty():
entries[alias] = entry_key
return true
return false
## Adds [param entry] to the glossary if it does not exist.
## If it does exist, returns false.
func try_add_entry(entry: Dictionary) -> bool:
var entry_key: String = entry[NAME_PROPERTY]
if entries.has(entry_key):
return false
entries[entry_key] = entry
for alternative: String in entry.get(ALTERNATIVE_PROPERTY, []):
entries[alternative.strip_edges()] = entry_key
return true
## Returns an array of words that can trigger the glossary popup.
## This method respects whether translation is enabled or not.
## The words may be: The entry key and the alternative words.
func _get_word_options(entry_key: String) -> Array:
var word_options: Array = []
var translation_enabled: bool = ProjectSettings.get_setting("dialogic/translation/enabled", false)
if not translation_enabled:
word_options.append(entry_key)
for alternative: String in get_entry(entry_key).get(ALTERNATIVE_PROPERTY, []):
word_options.append(alternative.strip_edges())
return word_options
var translation_entry_key_id: String = get_property_translation_key(entry_key, NAME_PROPERTY)
if translation_entry_key_id.is_empty():
return []
var translated_entry_key := tr(translation_entry_key_id)
if not translated_entry_key == translation_entry_key_id:
word_options.append(translated_entry_key)
var translation_alternatives_id: String = get_property_translation_key(entry_key, ALTERNATIVE_PROPERTY)
var translated_alternatives_str := tr(translation_alternatives_id)
if not translated_alternatives_str == translation_alternatives_id:
var translated_alternatives := translated_alternatives_str.split(",")
for alternative: String in translated_alternatives:
word_options.append(alternative.strip_edges())
return word_options
## Gets the regex option for the given [param entry_key].
## If the regex option does not exist, it will be generated.
##
## A regex option is the accumulation of valid words that can trigger the
## glossary popup.
##
## The [param entry_key] must be valid or an error will occur.
func get_set_regex_option(entry_key: String) -> String:
var entry: Dictionary = get_entry(entry_key)
var regex_options: Dictionary = entry.get(REGEX_OPTION_PROPERTY, {})
if regex_options.is_empty():
entry[REGEX_OPTION_PROPERTY] = regex_options
var locale_key: String = TranslationServer.get_locale()
var regex_option: String = regex_options.get(locale_key, "")
if not regex_option.is_empty():
return regex_option
var word_options: Array = _get_word_options(entry_key)
regex_option = "|".join(word_options)
regex_options[locale_key] = regex_option
return regex_option
#region ADD AND CLEAR TRANSLATION KEYS
## This is automatically called, no need to use this.
func add_translation_id() -> String:
_translation_id = DialogicUtil.get_next_translation_id()
return _translation_id
## Removes the translation ID of this glossary.
func remove_translation_id() -> void:
_translation_id = ""
## Removes the translation ID of all glossary entries.
func remove_entry_translation_ids() -> void:
for entry: Variant in entries.values():
# Ignore aliases.
if entry is String:
continue
if entry.has(TRANSLATION_PROPERTY):
entry[TRANSLATION_PROPERTY] = ""
## Clears the lookup tables using translation keys.
func clear_translation_keys() -> void:
const RESOURCE_NAME_KEY := RESOURCE_NAME + "/"
for translation_key: String in entries.keys():
if translation_key.begins_with(RESOURCE_NAME_KEY):
entries.erase(translation_key)
_translation_keys.clear()
#endregion
#region GET AND SET TRANSLATION IDS AND KEYS
## Returns a key used to reference this glossary in the translation CSV file.
##
## Time complexity: O(1)
func get_property_translation_key(entry_key: String, property: String) -> String:
var entry := get_entry(entry_key)
if entry == null:
return ""
var entry_translation_key: String = entry.get(TRANSLATION_PROPERTY, "")
if entry_translation_key.is_empty() or _translation_id.is_empty():
return ""
var glossary_csv_key := (RESOURCE_NAME
.path_join(_translation_id)
.path_join(entry_translation_key)
.path_join(property))
return glossary_csv_key
## Returns the translation key prefix for this glossary.
## The resulting format will look like this: Glossary/a2/
## This prefix can be used to find translations for this glossary.
func _get_glossary_translation_id_prefix() -> String:
return (
DialogicGlossary.RESOURCE_NAME
.path_join(_translation_id)
)
## Returns the translation key for the given [param glossary_translation_id] and
## [param entry_translation_id].
##
## By key, we refer to the uniquely named property per translation entry.
##
## The resulting format will look like this: Glossary/a2/b4/name
func _get_glossary_translation_key(entry_translation_id: String, property: String) -> String:
return (
DialogicGlossary.RESOURCE_NAME
.path_join(_translation_id)
.path_join(entry_translation_id)
.path_join(property)
)
## Tries to get the glossary entry's translation ID.
## If it does not exist, a new one will be generated.
func get_set_glossary_entry_translation_id(entry_key: String) -> String:
var glossary_entry: Dictionary = get_entry(entry_key)
var entry_translation_id := ""
var glossary_translation_id: String = glossary_entry.get(TRANSLATION_PROPERTY, "")
if glossary_translation_id.is_empty():
entry_translation_id = DialogicUtil.get_next_translation_id()
glossary_entry[TRANSLATION_PROPERTY] = entry_translation_id
else:
entry_translation_id = glossary_entry[TRANSLATION_PROPERTY]
return entry_translation_id
## Tries to get the glossary's translation ID.
## If it does not exist, a new one will be generated.
func get_set_glossary_translation_id() -> String:
if _translation_id == null or _translation_id.is_empty():
add_translation_id()
return _translation_id
#endregion

View File

@@ -0,0 +1 @@
uid://dnc50oyb36tch

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b6wqvg2qcjxs"
path="res://.godot/imported/icon.png-624eb6dbf7e3ab27845a397653fa2fbb.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Events/Glossary/icon.png"
dest_files=["res://.godot/imported/icon.png-624eb6dbf7e3ab27845a397653fa2fbb.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.90906 2.18182H13.0909V13.8182H2.90906V2.18182ZM4.60603 3.84416H11.3939V5.5065H4.60603V3.84416ZM11.3939 7.16884H4.60603V8.83117H11.3939V7.16884ZM4.60603 10.4935H11.3939V12.1558H4.60603V10.4935Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 364 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b5xwnxdb7064n"
path="res://.godot/imported/icon.svg-4fc0c12c53379638e37d654e7bbaea1a.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Modules/Glossary/icon.svg"
dest_files=["res://.godot/imported/icon.svg-4fc0c12c53379638e37d654e7bbaea1a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,14 @@
@tool
extends DialogicIndexer
func _get_events() -> Array:
return []
# return [this_folder.path_join('event_glossary.gd')]
func _get_editors() -> Array:
return [this_folder.path_join('glossary_editor.tscn')]
func _get_subsystems() -> Array:
return [{'name':'Glossary', 'script':this_folder.path_join('subsystem_glossary.gd')}]

View File

@@ -0,0 +1 @@
uid://dhet2wfie3ua4

View File

@@ -0,0 +1,174 @@
extends DialogicSubsystem
## Subsystem that handles glossaries.
## List of glossary resources that are used.
var glossaries := []
## If false, no parsing will be done.
var enabled := true
## Any key in this dictionary will overwrite the color for any item with that name.
var color_overrides := {}
const SETTING_DEFAULT_COLOR := 'dialogic/glossary/default_color'
#region STATE
####################################################################################################
func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
glossaries = []
for path: String in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []):
add_glossary(path)
#endregion
#region MAIN METHODS
####################################################################################################
func parse_glossary(text: String) -> String:
if not enabled:
return text
var def_case_sensitive: bool = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true)
var def_color: Color = ProjectSettings.get_setting(SETTING_DEFAULT_COLOR, Color.POWDER_BLUE)
var regex := RegEx.new()
for glossary: DialogicGlossary in glossaries:
if !glossary.enabled:
continue
for entry_value: Variant in glossary.entries.values():
if not entry_value is Dictionary:
continue
var entry: Dictionary = entry_value
var entry_key: String = entry.get(DialogicGlossary.NAME_PROPERTY, "")
# Older versions of the glossary resource do not have a property
# for their name, we must skip these.
# They can be updated by opening the resource in the glossary
# editor.
if entry_key.is_empty():
continue
if not entry.get('enabled', true):
continue
var regex_options := glossary.get_set_regex_option(entry_key)
if regex_options.is_empty():
continue
var pattern: String = '(?<=\\W|^)(?<!\\\\)(?<word>' + regex_options + ')(?!])(?=\\W|$)'
if entry.get('case_sensitive', def_case_sensitive):
regex.compile(pattern)
else:
regex.compile('(?i)'+pattern)
var color: String = entry.get('color', def_color).to_html()
if entry_key in color_overrides:
color = color_overrides[entry_key].to_html()
text = regex.sub(text,
'[url=' + entry_key + ']' +
'[color=' + color + ']${word}[/color]' +
'[/url]',
true
)
return text
func add_glossary(path:String) -> void:
if ResourceLoader.exists(path):
var resource: DialogicGlossary = load(path)
if resource is DialogicGlossary:
glossaries.append(resource)
else:
printerr('[Dialogic] The glossary file "' + path + '" is missing. Make sure it exists.')
## Iterates over all glossaries and returns the first one that matches the
## [param entry_key].
##
## Runtime complexity:
## O(n), where n is the number of glossaries.
func find_glossary(entry_key: String) -> DialogicGlossary:
for glossary: DialogicGlossary in glossaries:
if glossary.entries.has(entry_key):
return glossary
return null
## Returns the first match for a given entry key.
## If translation is available and enabled, it will be translated
func get_entry(entry_key: String) -> Dictionary:
var glossary: DialogicGlossary = dialogic.Glossary.find_glossary(entry_key)
var result := {
"title": "",
"text": "",
"extra": "",
"color": Color.WHITE,
}
if glossary == null:
return {}
var is_translation_enabled: bool = ProjectSettings.get_setting('dialogic/translation/enabled', false)
var entry := glossary.get_entry(entry_key)
if entry.is_empty():
return {}
result.color = entry.get("color")
if result.color == null:
result.color = ProjectSettings.get_setting(SETTING_DEFAULT_COLOR, Color.POWDER_BLUE)
if is_translation_enabled and not glossary._translation_id.is_empty():
var translation_key: String = glossary._translation_keys.get(entry_key)
var last_slash := translation_key.rfind('/')
if last_slash == -1:
return {}
var tr_base := translation_key.substr(0, last_slash)
result.title = translate(tr_base, "title", entry)
result.text = translate(tr_base, "text", entry)
result.extra = translate(tr_base, "extra", entry)
else:
result.title = entry.get("title", "")
result.text = entry.get("text", "")
result.extra = entry.get("extra", "")
## PARSE TEXTS FOR VARIABLES
result.title = dialogic.VAR.parse_variables(result.title)
result.text = dialogic.VAR.parse_variables(result.text)
result.extra = dialogic.VAR.parse_variables(result.extra)
return result
## Tries to translate the property with the given
func translate(tr_base: String, property: StringName, fallback_entry: Dictionary) -> String:
var tr_key := tr_base.path_join(property)
var tr_value := tr(tr_key)
if tr_key == tr_value:
tr_value = fallback_entry.get(property, "")
return tr_value

View File

@@ -0,0 +1 @@
uid://b3ekwbydijnpu