Skip to content

Commit 3308160

Browse files
Improve runtime resources (timelines and characters created through code) (#2537)
* Fix for dictionary field in custom portrait export overrides * Allow registering identifiers for runtime resources This makes both DialogicTimeline and DialogicCharacter extend DialogicIdentifiableResource which basically adds a get_identifier and set_identifier method. If an identifier is set at runtime for a non-saved resource, the resource is registered to the directories directly (without a path) allowing for the use of those resources in timelines from then on. This also once again allows using "invalid" character names in timelines and will now even interpret the portrait as a color, which allows to have one-off characters say something. However the portrait picker is not shown in the visual editor for those characters for now so this is kinda a text-editor exclusive rn.
1 parent 8355cac commit 3308160

18 files changed

+142
-49
lines changed

addons/dialogic/Core/DialogicGameHandler.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,6 @@ func print_debug_moment() -> void:
439439
if not current_timeline:
440440
return
441441

442-
printerr("\tAt event ", current_event_idx+1, " (",current_timeline_events[current_event_idx].event_name, ' Event) in timeline "', DialogicResourceUtil.get_unique_identifier(current_timeline.resource_path), '" (',current_timeline.resource_path,').')
442+
printerr("\tAt event ", current_event_idx+1, " (",current_timeline_events[current_event_idx].event_name, ' Event) in timeline "', current_timeline.get_identifier(), '" (',current_timeline.resource_path,').')
443443
print("\n")
444444
#endregion

addons/dialogic/Core/DialogicResourceUtil.gd

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ static func add_resource_to_directory(file_path:String, directory:Dictionary) ->
6767

6868
## Returns the unique identifier for the given resource path.
6969
## Returns an empty string if no identifier was found.
70-
static func get_unique_identifier(file_path:String) -> String:
70+
static func get_unique_identifier_by_path(file_path:String) -> String:
7171
if not file_path: return ""
7272
var identifier: Variant = get_directory(file_path.get_extension()).find_key(file_path)
7373
if typeof(identifier) == TYPE_STRING:
@@ -78,12 +78,15 @@ static func get_unique_identifier(file_path:String) -> String:
7878
## Returns the resource associated with the given unique identifier.
7979
## The expected extension is needed to use the right directory.
8080
static func get_resource_from_identifier(identifier:String, extension:String) -> Resource:
81-
var path: String = get_directory(extension).get(identifier, '')
82-
if ResourceLoader.exists(path):
83-
return load(path)
81+
var value: Variant = get_directory(extension).get(identifier, '')
82+
if typeof(value) == TYPE_STRING and ResourceLoader.exists(value):
83+
return load(value)
84+
elif value is Resource:
85+
return value
8486
return null
8587

8688

89+
## Editor Only
8790
static func change_unique_identifier(file_path:String, new_identifier:String) -> void:
8891
var directory := get_directory(file_path.get_extension())
8992
var key: String = directory.find_key(file_path)
@@ -117,6 +120,21 @@ static func remove_resource(file_path:String) -> void:
117120
static func is_identifier_unused(extension:String, identifier:String) -> bool:
118121
return not identifier in get_directory(extension)
119122

123+
124+
## While usually the directory maps identifiers to paths, this method (only supposed to be used at runtime)
125+
## allows mapping resources that are not saved to an identifier.
126+
static func register_runtime_resource(resource:Resource, identifier:String, extension:String) -> void:
127+
var directory := get_directory(extension)
128+
directory[identifier] = resource
129+
set_directory(extension, directory)
130+
131+
132+
static func get_runtime_unique_identifier(resource:Resource, extension:String) -> String:
133+
var identifier: Variant = get_directory(extension).find_key(resource)
134+
if typeof(identifier) == TYPE_STRING:
135+
return identifier
136+
return ""
137+
120138
#endregion
121139

122140
#region LABEL CACHE

addons/dialogic/Core/DialogicUtil.gd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ static func setup_script_property_edit_node(property_info: Dictionary, value:Var
528528
TYPE_DICTIONARY:
529529
input = load("res://addons/dialogic/Editor/Events/Fields/field_dictionary.tscn").instantiate()
530530
input.property_name = property_info["name"]
531+
input.set_value(value)
531532
input.value_changed.connect(_on_export_dict_submitted.bind(property_changed))
532533
TYPE_OBJECT:
533534
input = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate()

addons/dialogic/Editor/CharacterEditor/character_editor.gd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ func _open_resource(resource:Resource) -> void:
7171
load_portrait_tree()
7272

7373
loading = false
74-
character_loaded.emit(resource.resource_path)
74+
character_loaded.emit(current_resource.resource_path)
7575

76-
%CharacterName.text = DialogicResourceUtil.get_unique_identifier(resource.resource_path)
76+
%CharacterName.text = current_resource.get_identifier()
7777

7878
$NoCharacterScreen.hide()
7979
%PortraitChangeInfo.hide()
@@ -549,7 +549,7 @@ func report_name_change(item: TreeItem) -> void:
549549
editors_manager.reference_manager.add_portrait_ref_change(
550550
item.get_meta('previous_name'),
551551
%PortraitTree.get_full_item_name(item),
552-
[DialogicResourceUtil.get_unique_identifier(current_resource.resource_path)])
552+
[current_resource.get_identifier()])
553553
item.set_meta('previous_name', %PortraitTree.get_full_item_name(item))
554554
%PortraitChangeInfo.show()
555555

addons/dialogic/Editor/Common/sidebar.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ func _on_right_click_menu_id_pressed(id: int) -> void:
433433
)
434434
4: # COPY IDENTIFIER
435435
DisplayServer.clipboard_set(
436-
DialogicResourceUtil.get_unique_identifier(
436+
DialogicResourceUtil.get_unique_identifier_by_path(
437437
%RightClickMenu.get_meta("item_clicked").get_metadata(0)
438438
)
439439
)

addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func _set_value(value:Variant) -> void:
5050
Modes.PRETTY_PATH:
5151
%Search.text = DialogicUtil.pretty_name(value)
5252
Modes.IDENTIFIER when value.begins_with("res://"):
53-
%Search.text = DialogicResourceUtil.get_unique_identifier(value)
53+
%Search.text = DialogicResourceUtil.get_unique_identifier_by_path(value)
5454
Modes.ANY_VALID_STRING when validation_func:
5555
%Search.text = validation_func.call(value).get('valid_text', value)
5656
_:
@@ -361,7 +361,7 @@ func _can_drop_data(position:Vector2, data:Variant) -> bool:
361361
func _drop_data(position:Vector2, data:Variant) -> void:
362362
var path := str(data.files[0])
363363
if mode == Modes.IDENTIFIER:
364-
path = DialogicResourceUtil.get_unique_identifier(path)
364+
path = DialogicResourceUtil.get_unique_identifier_by_path(path)
365365
_set_value(path)
366366
value_changed.emit(property_name, path)
367367
current_value_updated = false

addons/dialogic/Editor/Events/Fields/field_options_fixed.gd

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ extends DialogicVisualEditorField
33

44
## Event block field for constant options. For varying options use ComplexPicker.
55

6-
var options: Array = []
6+
var options: Array = []:
7+
set(o):
8+
options = o
9+
if current_value != -1:
10+
set_value(current_value)
711

812
## if true, only the symbol will be displayed. In the dropdown text will be visible.
913
## Useful for making UI simpler
@@ -21,6 +25,7 @@ func _ready() -> void:
2125
call("get_popup").index_pressed.connect(index_pressed)
2226

2327

28+
2429
func _load_display_info(info:Dictionary) -> void:
2530
options = info.get('options', [])
2631
self.disabled = info.get('disabled', false)
@@ -35,7 +40,7 @@ func _set_value(value:Variant) -> void:
3540
if !symbol_only:
3641
self.text = option['label']
3742
self.icon = option.get('icon', null)
38-
current_value = value
43+
current_value = value
3944

4045

4146
func get_value() -> Variant:

addons/dialogic/Editor/Inspector/timeline_inspector_field.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func _update_property() -> void:
6161
updating = true
6262
current_value = new_value
6363
if current_value:
64-
field.set_value(DialogicResourceUtil.get_unique_identifier(current_value.resource_path))
64+
field.set_value(current_value.get_identifier())
6565
button.show()
6666
else:
6767
button.hide()

addons/dialogic/Editor/TimelineEditor/timeline_editor.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func _open_resource(resource:Resource) -> void:
7373
EditorMode.TEXT:
7474
%TextEditor.load_timeline(current_resource)
7575
$NoTimelineScreen.hide()
76-
%TimelineName.text = DialogicResourceUtil.get_unique_identifier(current_resource.resource_path)
76+
%TimelineName.text = current_resource.get_identifier()
7777
play_timeline_button.disabled = false
7878

7979

addons/dialogic/Modules/Character/event_character.gd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ var character_identifier: String:
8282
if character_identifier == '--All--':
8383
return '--All--'
8484
if character:
85-
var identifier := DialogicResourceUtil.get_unique_identifier(character.resource_path)
85+
var identifier := character.get_identifier()
8686
if not identifier.is_empty():
8787
return identifier
8888
return character_identifier
@@ -225,7 +225,7 @@ func to_text() -> String:
225225
if action == Actions.LEAVE and character_identifier == '--All--':
226226
result_string += "--All--"
227227
elif character:
228-
var name := DialogicResourceUtil.get_unique_identifier(character.resource_path)
228+
var name := character.get_character_name()
229229

230230
if name.count(" ") > 0:
231231
name = '"' + name + '"'

addons/dialogic/Modules/Character/subsystem_portraits.gd

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func load_game_state(_load_flag:=LoadFlags.FULL_LOAD) -> void:
3535
var character_info: Dictionary = portraits_info[character_path]
3636
var character: DialogicCharacter = load(character_path)
3737
var container := dialogic.PortraitContainers.load_position_container(character.get_character_name())
38-
38+
3939
ResourceLoader.load_threaded_request(character_path)
4040

4141
var load_status = ResourceLoader.load_threaded_get_status(character_path)
@@ -138,7 +138,7 @@ func _change_portrait(character_node: Node2D, portrait: String, fade_animation:=
138138

139139
if ResourceLoader.exists(scene_path):
140140
ResourceLoader.load_threaded_request(scene_path)
141-
141+
142142
var load_status = ResourceLoader.load_threaded_get_status(scene_path)
143143
while load_status == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
144144
await get_tree().process_frame
@@ -371,7 +371,7 @@ func get_valid_portrait(character:DialogicCharacter, portrait:String) -> String:
371371

372372
if not portrait in character.portraits:
373373
if not portrait.is_empty():
374-
printerr('[Dialogic] Tried to use invalid portrait "', portrait, '" on character "', DialogicResourceUtil.get_unique_identifier(character.resource_path), '". Using default portrait instead.')
374+
printerr('[Dialogic] Tried to use invalid portrait "', portrait, '" on character "', character.get_character_name(), '". Using default portrait instead.')
375375
dialogic.print_debug_moment()
376376
portrait = character.default_portrait
377377

@@ -452,9 +452,9 @@ func add_character(character: DialogicCharacter, container: DialogicNode_Portrai
452452
if not character:
453453
printerr('[DialogicError] Cannot call add_portrait() with null character.')
454454
return null
455-
455+
456456
ResourceLoader.load_threaded_request(character.resource_path)
457-
457+
458458
var load_status = ResourceLoader.load_threaded_get_status(character.resource_path)
459459
while load_status == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
460460
await get_tree().process_frame

addons/dialogic/Modules/Jump/event_jump.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var label_name := ""
1919
var timeline_identifier := "":
2020
get:
2121
if timeline:
22-
var identifier := DialogicResourceUtil.get_unique_identifier(timeline.resource_path)
22+
var identifier := timeline.get_identifier()
2323
if not identifier.is_empty():
2424
return identifier
2525
return timeline_identifier

addons/dialogic/Modules/Jump/event_label.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func _execute() -> void:
2424
"identifier": name,
2525
"display_name": get_property_translated("display_name"),
2626
"display_name_orig": display_name,
27-
"timeline": DialogicResourceUtil.get_unique_identifier(dialogic.current_timeline.resource_path)
27+
"timeline": dialogic.current_timeline.get_identifier()
2828
})
2929
finish()
3030

addons/dialogic/Modules/Text/event_text.gd

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var portrait := ""
2525
var character_identifier: String:
2626
get:
2727
if character:
28-
var identifier := DialogicResourceUtil.get_unique_identifier(character.resource_path)
28+
var identifier := character.get_identifier()
2929
if not identifier.is_empty():
3030
return identifier
3131
return character_identifier
@@ -274,8 +274,10 @@ func to_text() -> String:
274274
if result.is_empty():
275275
result = "<Empty Text Event>"
276276

277-
if character:
278-
var name := DialogicResourceUtil.get_unique_identifier(character.resource_path)
277+
if character or character_identifier:
278+
var name := character_identifier
279+
if character:
280+
name = character.get_identifier()
279281
if name.count(" ") > 0:
280282
name = '"' + name + '"'
281283
if not portrait.is_empty():
@@ -296,6 +298,10 @@ func from_text(string:String) -> void:
296298
character = DialogicResourceUtil.get_character_resource(character_identifier)
297299

298300
var result := regex.search(string.trim_prefix('\\'))
301+
302+
if result.get_string('portrait'):
303+
portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')')
304+
299305
if result and not result.get_string('name').is_empty():
300306
var name := result.get_string('name').strip_edges()
301307

@@ -307,16 +313,16 @@ func from_text(string:String) -> void:
307313
if character == null and Engine.is_editor_hint() == false:
308314
character = DialogicCharacter.new()
309315
character.display_name = name
310-
character.resource_path = "user://"+name+".dch"
311-
DialogicResourceUtil.add_resource_to_directory(character.resource_path, DialogicResourceUtil.get_character_directory())
316+
character.set_identifier(name)
317+
if portrait:
318+
character.color = Color(portrait)
312319

313-
if !result.get_string('portrait').is_empty():
314-
portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')')
320+
if not result:
321+
return
315322

316-
if result:
317-
text = result.get_string('text').replace("\\\n", "\n").replace('\\:', ':').strip_edges().trim_prefix('\\')
318-
if text == '<Empty Text Event>':
319-
text = ""
323+
text = result.get_string('text').replace("\\\n", "\n").replace('\\:', ':').strip_edges().trim_prefix('\\')
324+
if text == '<Empty Text Event>':
325+
text = ""
320326

321327

322328
func is_valid_event(_string:String) -> bool:
@@ -367,7 +373,7 @@ func build_event_editor() -> void:
367373
{'file_extension' : '.dch',
368374
'mode' : 2,
369375
'suggestions_func' : get_character_suggestions,
370-
'empty_text' : '(No one)',
376+
'placeholder' : '(No one)',
371377
'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg")}, 'do_any_characters_exist()')
372378
add_header_edit('portrait', ValueType.DYNAMIC_OPTIONS,
373379
{'suggestions_func' : get_portrait_suggestions,
@@ -387,8 +393,13 @@ func do_any_characters_exist() -> bool:
387393

388394

389395
func get_character_suggestions(search_text:String) -> Dictionary:
390-
return DialogicUtil.get_character_suggestions(search_text, character, true, false, editor_node)
391-
396+
var suggestions := DialogicUtil.get_character_suggestions(search_text, character, true, false, editor_node)
397+
if search_text and not search_text in suggestions:
398+
suggestions[search_text] = {
399+
"value":search_text,
400+
"tooltip": "A temporary character, created on the spot.",
401+
"editor_icon":["GuiEllipsis", "EditorIcons"]}
402+
return suggestions
392403

393404
func get_portrait_suggestions(search_text:String) -> Dictionary:
394405
return DialogicUtil.get_portrait_suggestions(search_text, character, true, "Don't change")

addons/dialogic/Modules/Text/subsystem_text.gd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,8 @@ func collect_character_names() -> void:
507507

508508
character_colors = {}
509509

510-
for dch_path in DialogicResourceUtil.get_character_directory().values():
511-
var character := (load(dch_path) as DialogicCharacter)
510+
for dch_identifier in DialogicResourceUtil.get_character_directory():
511+
var character := (DialogicResourceUtil.get_character_resource(dch_identifier) as DialogicCharacter)
512512

513513
if character.display_name:
514514
if "{" in character.display_name and "}" in character.display_name:

addons/dialogic/Resources/character.gd

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@tool
2-
extends Resource
2+
extends "res://addons/dialogic/Resources/dialogic_identifiable_resource.gd"
33
class_name DialogicCharacter
44

55

@@ -30,8 +30,12 @@ enum TranslatedProperties {
3030
var _translation_id := ""
3131

3232

33-
func _to_string() -> String:
34-
return "[{name}:{id}]".format({"name":get_character_name(), "id":get_instance_id()})
33+
func _get_extension() -> String:
34+
return "dch"
35+
36+
37+
func _get_resource_name() -> String:
38+
return "DialogicCharacter"
3539

3640

3741
## Adds a translation ID to the character.
@@ -125,7 +129,7 @@ func get_display_name_translated() -> String:
125129

126130
## Returns the best name for this character.
127131
func get_character_name() -> String:
128-
var unique_identifier := DialogicResourceUtil.get_unique_identifier(resource_path)
132+
var unique_identifier := get_identifier()
129133
if not unique_identifier.is_empty():
130134
return unique_identifier
131135
if not resource_path.is_empty():
@@ -135,6 +139,19 @@ func get_character_name() -> String:
135139
else:
136140
return "UnnamedCharacter"
137141

142+
#
143+
### Sets the unique identifier-string of this resource.
144+
### In editor (if the resource is already saved) the identifier will be stored.
145+
### In game (if the resource is not stored) the resource will be temporarily registered.
146+
#func set_identifier(new_identifier:String) -> bool:
147+
#if resource_path and Engine.is_editor_hint():
148+
#DialogicResourceUtil.change_unique_identifier(resource_path, new_identifier)
149+
#return true
150+
#if not resource_path and not Engine.is_editor_hint():
151+
#DialogicResourceUtil.register_runtime_resource(self, new_identifier, "dch")
152+
#return true
153+
#return false
154+
138155

139156
## Returns the info of the given portrait.
140157
## Uses the default portrait if the given portrait doesn't exist.

0 commit comments

Comments
 (0)