Skip to content

Commit

Permalink
Add sub-Index for mid-text-event loading (dialogic-godot#2045)
Browse files Browse the repository at this point in the history
* Clear any remaining event-connections with Timeline.clean()

Adds a method that clears any remaining connections so we can safely reuse all the event resources.

* Fix and improve text event

Does:
- allows mid-text event loading (only between sections)
- makes sure textbox visibility is correct when loading

* Small typo fix
  • Loading branch information
Jowan-Spooner authored and Invertex committed Jan 26, 2024
1 parent 0d8d69e commit 2ea0359
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 60 deletions.
133 changes: 79 additions & 54 deletions addons/dialogic/Modules/Text/event_text.gd
Original file line number Diff line number Diff line change
Expand Up @@ -39,47 +39,12 @@ var regex := RegEx.create_from_string("((\")?(?<name>(?(2)[^\"\\n]*|[^(: \\n]*))
var split_regex := RegEx.create_from_string("((\\[n\\]|\\[n\\+\\])?((?!(\\[n\\]|\\[n\\+\\]))(.|\\n))*)")

enum States {REVEALING, IDLE, DONE}
var state = States.IDLE
var state := States.IDLE
signal advance

################################################################################
## EXECUTION
################################################################################

func _mark_as_read(final_text: String) -> void:
if dialogic.has_subsystem('History'):
if character:
dialogic.History.store_simple_history_entry(final_text, event_name, {'character':character.display_name, 'character_color':character.color})
else:
dialogic.History.store_simple_history_entry(final_text, event_name)
dialogic.History.event_was_read(self)

func _connect_signals() -> void:
if not dialogic.Inputs.dialogic_action.is_connected(_on_dialogic_input_action):
dialogic.Inputs.dialogic_action.connect(_on_dialogic_input_action)

dialogic.Inputs.auto_skip.toggled.connect(_on_auto_skip_enable)

if not dialogic.Inputs.auto_advance.autoadvance.is_connected(_on_dialogic_input_autoadvance):
dialogic.Inputs.auto_advance.autoadvance.connect(_on_dialogic_input_autoadvance)

## If the event is done, this method can clean-up signal connections.
func _disconnect_signals() -> void:
dialogic.Inputs.dialogic_action.disconnect(_on_dialogic_input_action)
dialogic.Inputs.auto_advance.autoadvance.disconnect(_on_dialogic_input_autoadvance)
dialogic.Inputs.auto_skip.toggled.disconnect(_on_auto_skip_enable)

## Tries to play the voice clip for the current line.
func _try_play_current_line_voice() -> void:
# If Auto-Skip is enabled and we skip voice clips, we don't want to play.
if (dialogic.Inputs.auto_skip.enabled
and dialogic.Inputs.auto_skip.skip_voice):
return

# Plays the audio region for the current line.
if (dialogic.has_subsystem('Voice')
and dialogic.Voice.is_voiced(dialogic.current_event_idx)):
dialogic.Voice.play_voice()
#region EXECUTION
################################################################################

func _execute() -> void:
if text.is_empty():
Expand All @@ -101,7 +66,7 @@ func _execute() -> void:
if portrait and dialogic.has_subsystem('Portraits') and dialogic.Portraits.is_character_joined(character):
dialogic.Portraits.change_character_portrait(character, portrait)
dialogic.Portraits.change_speaker(character, portrait)
var check_portrait :String = portrait if !portrait.is_empty() else dialogic.current_state_info['portraits'].get(character.resource_path, {}).get('portrait', '')
var check_portrait: String = portrait if !portrait.is_empty() else dialogic.current_state_info['portraits'].get(character.resource_path, {}).get('portrait', '')

if check_portrait and character.portraits.get(check_portrait, {}).get('sound_mood', '') in character.custom_info.get('sound_moods', {}):
dialogic.Text.update_typing_sound_mood(character.custom_info.get('sound_moods', {}).get(character.portraits[check_portrait].get('sound_mood', {}), {}))
Expand All @@ -118,7 +83,7 @@ func _execute() -> void:

_connect_signals()

var final_text :String= get_property_translated('text')
var final_text: String = get_property_translated('text')
if ProjectSettings.get_setting('dialogic/text/split_at_new_lines', false):
match ProjectSettings.get_setting('dialogic/text/split_at_new_lines_as', 0):
0:
Expand All @@ -131,17 +96,21 @@ func _execute() -> void:
split_text.append([i.get_string().trim_prefix('[n]').trim_prefix('[n+]')])
split_text[-1].append(i.get_string().begins_with('[n+]'))

for section_idx in range(len(split_text)):
dialogic.current_state_info['text_sub_idx'] = dialogic.current_state_info.get('text_sub_idx', 0)

for section_idx in range(min(dialogic.current_state_info['text_sub_idx'], len(split_text)-1), len(split_text)):
dialogic.current_state_info
dialogic.Text.hide_next_indicators()
state = States.REVEALING
var segment: String = dialogic.Text.parse_text(split_text[section_idx][0])

dialogic.current_state_info['text_sub_idx'] = section_idx
var segment: String = dialogic.Text.parse_text(split_text[section_idx][0])
var is_append: bool = split_text[section_idx][1]

final_text = segment
dialogic.Text.about_to_show_text.emit({'text':final_text, 'character':character, 'portrait':portrait, 'append': is_append})

await dialogic.Text.update_text_boxes(final_text, false)
await dialogic.Text.update_textbox(final_text, false)
_try_play_current_line_voice()
final_text = dialogic.Text.update_dialog_text(final_text, false, is_append)

Expand Down Expand Up @@ -183,10 +152,55 @@ func _execute() -> void:
else:
await advance

dialogic.current_state_info['text_sub_idx'] = 0

_disconnect_signals()
finish()

func _on_dialogic_input_action():

func _mark_as_read(final_text: String) -> void:
if dialogic.has_subsystem('History'):
if character:
dialogic.History.store_simple_history_entry(final_text, event_name, {'character':character.display_name, 'character_color':character.color})
else:
dialogic.History.store_simple_history_entry(final_text, event_name)
dialogic.History.event_was_read(self)


func _connect_signals() -> void:
if not dialogic.Inputs.dialogic_action.is_connected(_on_dialogic_input_action):
dialogic.Inputs.dialogic_action.connect(_on_dialogic_input_action)

dialogic.Inputs.auto_skip.toggled.connect(_on_auto_skip_enable)

if not dialogic.Inputs.auto_advance.autoadvance.is_connected(_on_dialogic_input_autoadvance):
dialogic.Inputs.auto_advance.autoadvance.connect(_on_dialogic_input_autoadvance)


## If the event is done, this method can clean-up signal connections.
func _disconnect_signals() -> void:
if dialogic.Inputs.dialogic_action.is_connected(_on_dialogic_input_action):
dialogic.Inputs.dialogic_action.disconnect(_on_dialogic_input_action)
if dialogic.Inputs.auto_advance.autoadvance.is_connected(_on_dialogic_input_autoadvance):
dialogic.Inputs.auto_advance.autoadvance.disconnect(_on_dialogic_input_autoadvance)
if dialogic.Inputs.auto_skip.toggled.is_connected(_on_auto_skip_enable):
dialogic.Inputs.auto_skip.toggled.disconnect(_on_auto_skip_enable)


## Tries to play the voice clip for the current line.
func _try_play_current_line_voice() -> void:
# If Auto-Skip is enabled and we skip voice clips, we don't want to play.
if (dialogic.Inputs.auto_skip.enabled
and dialogic.Inputs.auto_skip.skip_voice):
return

# Plays the audio region for the current line.
if (dialogic.has_subsystem('Voice')
and dialogic.Voice.is_voiced(dialogic.current_event_idx)):
dialogic.Voice.play_voice()


func _on_dialogic_input_action() -> void:
match state:
States.REVEALING:
if dialogic.Text.is_text_reveal_skippable():
Expand All @@ -200,12 +214,12 @@ func _on_dialogic_input_action():
dialogic.Inputs.block_input(ProjectSettings.get_setting('dialogic/text/text_reveal_skip_delay', 0.1))


func _on_dialogic_input_autoadvance():
func _on_dialogic_input_autoadvance() -> void:
if state == States.IDLE or state == States.DONE:
advance.emit()


func _on_auto_skip_enable(enabled: bool):
func _on_auto_skip_enable(enabled: bool) -> void:
if not enabled:
return

Expand All @@ -220,9 +234,10 @@ func _on_auto_skip_enable(enabled: bool):
States.REVEALING:
dialogic.Text.skip_text_reveal()

#endregion

################################################################################
## INITIALIZE

#region INITIALIZE
################################################################################

func _init() -> void:
Expand Down Expand Up @@ -305,9 +320,10 @@ func get_shortcode_parameters() -> Dictionary:
"character" : {"property": "character_identifier", "default": ""},
"portrait" : {"property": "portrait", "default": ""},
}
#endregion

################################################################################
## TRANSLATIONS

#region TRANSLATIONS
################################################################################

func _get_translatable_properties() -> Array:
Expand All @@ -321,8 +337,10 @@ func _get_property_original_translation(property:String) -> String:
return ''


################################################################################
## EVENT EDITOR
#endregion


#region EVENT EDITOR
################################################################################

func _enter_visual_editor(editor:DialogicEditor):
Expand Down Expand Up @@ -376,8 +394,10 @@ func get_portrait_suggestions(search_text:String) -> Dictionary:
suggestions[portrait] = {'value':portrait, 'icon':icon}
return suggestions

#endregion


####################### CODE COMPLETION ########################################
#region CODE COMPLETION
################################################################################

var completion_text_character_getter_regex := RegEx.new()
Expand Down Expand Up @@ -422,7 +442,10 @@ func suggest_bbcode(text:CodeEdit):
for i in [['new event', 'n'],['new event (same box)', 'n+']]:
text.add_code_completion_option(CodeEdit.KIND_MEMBER, i[0], i[1], text.syntax_highlighter.normal_color, text.get_theme_icon("ArrowRight", "EditorIcons"),)

#################### SYNTAX HIGHLIGHTING #######################################
#endregion


#region SYNTAX HIGHLIGHTING
################################################################################

var text_effects := ""
Expand Down Expand Up @@ -474,3 +497,5 @@ func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, li
offset += 1
dict[replace_mod_match.get_end()+result.get_start('text')] = {'color':Highlighter.normal_color}
return dict

#endregion
4 changes: 2 additions & 2 deletions addons/dialogic/Modules/Text/subsystem_text.gd
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) ->


func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void:
update_textbox(dialogic.current_state_info.get('text', ''), true)
update_dialog_text(dialogic.current_state_info.get('text', ''), true)
var character: DialogicCharacter = null
if dialogic.current_state_info.get('speaker', ""):
Expand Down Expand Up @@ -99,7 +100,7 @@ func parse_text(text:String, type:int=TextTypes.DIALOG_TEXT, variables := true,
## When an event updates the text spoken, this can adjust the state of
## the dialog text box.
## This method is async.
func update_text_boxes(text: String, instant := false) -> void:
func update_textbox(text: String, instant := false) -> void:
if text.is_empty():
await hide_textbox(instant)
else:
Expand All @@ -112,7 +113,6 @@ func update_text_boxes(text: String, instant := false) -> void:
await dialogic.Animations.finished



## Shows the given text on all visible DialogText nodes.
## Instant can be used to skip all revieling.
## If additional is true, the previous text will be kept.
Expand Down
9 changes: 5 additions & 4 deletions addons/dialogic/Other/DialogicGameHandler.gd
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,6 @@ func preload_timeline(timeline_resource:Variant) -> Variant:


func end_timeline() -> void:
current_timeline = null
current_timeline_events = []
clear(ClearFlags.TIMLEINE_INFO_ONLY)
timeline_ended.emit()

Expand Down Expand Up @@ -273,6 +271,8 @@ func clear(clear_flags:=ClearFlags.FULL_CLEAR) -> bool:
(subsystem as DialogicSubsystem).clear_game_state(clear_flags)

# Resetting variables
if current_timeline:
current_timeline.clean()
current_timeline = null
current_event_idx = -1
current_timeline_events = []
Expand Down Expand Up @@ -305,8 +305,6 @@ func load_full_state(state_info:Dictionary) -> void:
get_subsystem('Styles').load_game_state()
scene = self.Styles.get_layout_node()

if current_state_info.get('current_timeline', null):
start_timeline(current_state_info.current_timeline, current_state_info.get('current_event_idx', 0))

var load_subsystems := func() -> void:
for subsystem in get_children():
Expand All @@ -320,6 +318,9 @@ func load_full_state(state_info:Dictionary) -> void:
await get_tree().process_frame
load_subsystems.call()

if current_state_info.get('current_timeline', null):
start_timeline(current_state_info.current_timeline, current_state_info.get('current_event_idx', 0))

#endregion


Expand Down
14 changes: 14 additions & 0 deletions addons/dialogic/Resources/timeline.gd
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,17 @@ func process() -> void:

events = processed_events
events_processed = true


## This method makes sure that all events in a timeline are correctly reset
func clean() -> void:
if not events_processed:
return

for event:DialogicEvent in events:
for con_in in event.get_incoming_connections():
con_in.signal.disconnect(con_in.callable)

for sig in event.get_signal_list():
for con_out in event.get_signal_connection_list(sig.name):
con_out.signal.disconnect(con_out.callable)

0 comments on commit 2ea0359

Please sign in to comment.