mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-16 10:03:47 +00:00
Updated dialogic
This commit is contained in:
parent
1d11462073
commit
cbb82512ee
483 changed files with 5743 additions and 2177 deletions
397
addons/dialogic/Modules/Audio/event_audio.gd
Normal file
397
addons/dialogic/Modules/Audio/event_audio.gd
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
@tool
|
||||
## Event that can play audio on a channel. The channel can be prededinfed
|
||||
## (with default settings defined in the settings) or created on the spot.
|
||||
## If no channel is given will play as a One-Shot SFX.
|
||||
class_name DialogicAudioEvent
|
||||
extends DialogicEvent
|
||||
|
||||
### Settings
|
||||
|
||||
## The file to play. If empty, the previous audio will be faded out.
|
||||
var file_path := "":
|
||||
set(value):
|
||||
if file_path != value:
|
||||
file_path = value
|
||||
ui_update_needed.emit()
|
||||
## The channel name to use. If none given plays as a One-Shot SFX.
|
||||
var channel_name := "":
|
||||
set(value):
|
||||
if channel_name != channel_name_regex.sub(value, '', true):
|
||||
channel_name = channel_name_regex.sub(value, '', true)
|
||||
var defaults: Dictionary = DialogicUtil.get_audio_channel_defaults().get(channel_name, {})
|
||||
if defaults:
|
||||
fade_length = defaults.fade_length
|
||||
volume = defaults.volume
|
||||
audio_bus = defaults.audio_bus
|
||||
loop = defaults.loop
|
||||
ui_update_needed.emit()
|
||||
|
||||
## The length of the fade. If 0 it's an instant change.
|
||||
var fade_length: float = 0.0
|
||||
## The volume in decibel.
|
||||
var volume: float = 0.0
|
||||
## The audio bus the audio will be played on.
|
||||
var audio_bus := ""
|
||||
## If true, the audio will loop, otherwise only play once.
|
||||
var loop := true
|
||||
## Sync starting time with different channel (if playing audio on that channel)
|
||||
var sync_channel := ""
|
||||
|
||||
## Helpers. Set automatically
|
||||
var set_fade_length := false
|
||||
var set_volume := false
|
||||
var set_audio_bus := false
|
||||
var set_loop := false
|
||||
var set_sync_channel := false
|
||||
|
||||
var regex := RegEx.create_from_string(r'(?:audio)\s*(?<channel>[\w-]{2,}|[\w]*)?\s*(")?(?<file_path>(?(2)[^"\n]*|[^(: \n]*))(?(2)"|)(?:\s*\[(?<shortcode>.*)\])?')
|
||||
var channel_name_regex := RegEx.create_from_string(r'(?<dash_only>^-$)|(?<invalid>[^\w-]{1})')
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
var audio_settings_overrides := {}
|
||||
if set_audio_bus:
|
||||
audio_settings_overrides["audio_bus"] = audio_bus
|
||||
if set_volume:
|
||||
audio_settings_overrides["volume"] = volume
|
||||
if set_fade_length:
|
||||
audio_settings_overrides["fade_length"] = fade_length
|
||||
if set_loop:
|
||||
audio_settings_overrides["loop"] = loop
|
||||
audio_settings_overrides["sync_channel"] = sync_channel
|
||||
dialogic.Audio.update_audio(channel_name, file_path, audio_settings_overrides)
|
||||
|
||||
finish()
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Audio"
|
||||
set_default_color('Color7')
|
||||
event_category = "Audio"
|
||||
event_sorting_index = 2
|
||||
|
||||
|
||||
func _get_icon() -> Resource:
|
||||
return load(this_folder.path_join('icon_music.png'))
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func to_text () -> String:
|
||||
var result_string := "audio "
|
||||
|
||||
if not channel_name.is_empty():
|
||||
result_string += channel_name + " "
|
||||
else:
|
||||
loop = false
|
||||
|
||||
if not file_path.is_empty():
|
||||
result_string += "\"" + file_path + "\""
|
||||
else:
|
||||
result_string += "-"
|
||||
|
||||
var shortcode := store_to_shortcode_parameters()
|
||||
if not shortcode.is_empty():
|
||||
result_string += " [" + shortcode + "]"
|
||||
|
||||
return result_string
|
||||
|
||||
|
||||
func from_text(string:String) -> void:
|
||||
# Pre Alpha 17 Conversion
|
||||
if string.begins_with('[music'):
|
||||
_music_from_text(string)
|
||||
return
|
||||
elif string.begins_with('[sound'):
|
||||
_sound_from_text(string)
|
||||
return
|
||||
|
||||
var result := regex.search(string)
|
||||
|
||||
channel_name = result.get_string('channel')
|
||||
|
||||
if result.get_string('file_path') == '-':
|
||||
file_path = ""
|
||||
else:
|
||||
file_path = result.get_string('file_path')
|
||||
|
||||
if not result.get_string('shortcode'):
|
||||
return
|
||||
|
||||
load_from_shortcode_parameters(result.get_string('shortcode'))
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"path" : {"property": "file_path", "default": "", "custom_stored":true, "ext_file":true},
|
||||
"channel" : {"property": "channel_name", "default": "", "custom_stored":true},
|
||||
"fade" : {"property": "fade_length", "default": 0.0},
|
||||
"volume" : {"property": "volume", "default": 0.0},
|
||||
"bus" : {"property": "audio_bus", "default": "",
|
||||
"suggestions": DialogicUtil.get_audio_bus_suggestions},
|
||||
"loop" : {"property": "loop", "default": true},
|
||||
"sync" : {"property": "sync_channel", "default": "",
|
||||
"suggestions": get_sync_audio_channel_suggestions},
|
||||
}
|
||||
|
||||
|
||||
## Returns a string with all the shortcode parameters.
|
||||
func store_to_shortcode_parameters(params:Dictionary = {}) -> String:
|
||||
if params.is_empty():
|
||||
params = get_shortcode_parameters()
|
||||
var custom_defaults: Dictionary = DialogicUtil.get_custom_event_defaults(event_name)
|
||||
var channel_defaults := DialogicUtil.get_audio_channel_defaults()
|
||||
var result_string := ""
|
||||
for parameter in params.keys():
|
||||
var parameter_info: Dictionary = params[parameter]
|
||||
var value: Variant = get(parameter_info.property)
|
||||
var default_value: Variant = custom_defaults.get(parameter_info.property, parameter_info.default)
|
||||
|
||||
if parameter_info.get('custom_stored', false):
|
||||
continue
|
||||
|
||||
if "set_" + parameter_info.property in self and not get("set_" + parameter_info.property):
|
||||
continue
|
||||
|
||||
if channel_name in channel_defaults.keys():
|
||||
default_value = channel_defaults[channel_name].get(parameter_info.property, default_value)
|
||||
|
||||
if typeof(value) == typeof(default_value) and value == default_value:
|
||||
if not "set_" + parameter_info.property in self or not get("set_" + parameter_info.property):
|
||||
continue
|
||||
|
||||
result_string += " " + parameter + '="' + value_to_string(value, parameter_info.get("suggestions", Callable())) + '"'
|
||||
|
||||
return result_string.strip_edges()
|
||||
|
||||
|
||||
func is_valid_event(string:String) -> bool:
|
||||
if string.begins_with("audio"):
|
||||
return true
|
||||
# Pre Alpha 17 Converter
|
||||
if string.strip_edges().begins_with('[music '):
|
||||
return true
|
||||
if string.strip_edges().begins_with('[sound '):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
#region PreAlpha17 Conversion
|
||||
|
||||
func _music_from_text(string:String) -> void:
|
||||
var data := parse_shortcode_parameters(string)
|
||||
|
||||
if data.has('channel') and data['channel'].to_int() > 0:
|
||||
channel_name = 'music' + str(data['channel'].to_int() + 1)
|
||||
else:
|
||||
channel_name = 'music'
|
||||
|
||||
# Reapply original defaults as setting channel name may have overridden them
|
||||
fade_length = 0.0
|
||||
volume = 0.0
|
||||
audio_bus = ''
|
||||
loop = true
|
||||
|
||||
# Apply any custom event defaults
|
||||
for default_prop in DialogicUtil.get_custom_event_defaults('music'):
|
||||
if default_prop in self:
|
||||
set(default_prop, DialogicUtil.get_custom_event_defaults('music')[default_prop])
|
||||
|
||||
# Apply shortcodes that exist
|
||||
if data.has('path'):
|
||||
file_path = data['path']
|
||||
if data.has('fade'):
|
||||
set_fade_length = true
|
||||
fade_length = data['fade'].to_float()
|
||||
if data.has('volume'):
|
||||
set_volume = true
|
||||
volume = data['volume'].to_float()
|
||||
if data.has('bus'):
|
||||
set_audio_bus = true
|
||||
audio_bus = data['bus']
|
||||
if data.has('loop'):
|
||||
set_loop = true
|
||||
loop = str_to_var(data['loop'])
|
||||
update_text_version()
|
||||
|
||||
|
||||
func _sound_from_text(string:String) -> void:
|
||||
var data := parse_shortcode_parameters(string)
|
||||
|
||||
channel_name = ''
|
||||
|
||||
# Reapply original defaults as setting channel name may have overridden them
|
||||
fade_length = 0.0
|
||||
volume = 0.0
|
||||
audio_bus = ''
|
||||
loop = false
|
||||
|
||||
# Apply any custom event defaults
|
||||
for default_prop in DialogicUtil.get_custom_event_defaults('sound'):
|
||||
if default_prop in self:
|
||||
set(default_prop, DialogicUtil.get_custom_event_defaults('sound')[default_prop])
|
||||
|
||||
# Apply shortcodes that exist
|
||||
if data.has('path'):
|
||||
file_path = data['path']
|
||||
if data.has('volume'):
|
||||
set_volume = true
|
||||
volume = data['volume'].to_float()
|
||||
if data.has('bus'):
|
||||
set_audio_bus = true
|
||||
audio_bus = data['bus']
|
||||
if data.has('loop'):
|
||||
set_loop = true
|
||||
loop = str_to_var(data['loop'])
|
||||
update_text_version()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor() -> void:
|
||||
add_header_edit('file_path', ValueType.FILE, {
|
||||
'left_text' : 'Play',
|
||||
'file_filter' : "*.mp3, *.ogg, *.wav; Supported Audio Files",
|
||||
'placeholder' : "Nothing",
|
||||
'editor_icon' : ["AudioStreamMP3", "EditorIcons"]})
|
||||
add_header_edit('file_path', ValueType.AUDIO_PREVIEW)
|
||||
|
||||
add_header_edit('channel_name', ValueType.DYNAMIC_OPTIONS, {
|
||||
'left_text' :"on",
|
||||
"right_text" : "channel.",
|
||||
'placeholder' : '(One-Shot SFX)',
|
||||
'mode' : 3,
|
||||
'suggestions_func' : get_audio_channel_suggestions,
|
||||
'validation_func' : DialogicUtil.validate_audio_channel_name,
|
||||
'tooltip' : 'Use an existing channel or type the name for a new channel.',
|
||||
})
|
||||
|
||||
add_header_button('', _open_audio_settings, 'Edit Audio Channels',
|
||||
editor_node.get_theme_icon("ExternalLink", "EditorIcons"))
|
||||
|
||||
add_body_edit("set_fade_length", ValueType.BOOL_BUTTON,{
|
||||
"editor_icon" : ["FadeCross", "EditorIcons"],
|
||||
"tooltip" : "Overwrite Fade Length"
|
||||
},"!channel_name.is_empty() and has_channel_defaults()")
|
||||
add_body_edit('fade_length', ValueType.NUMBER, {'left_text':'Fade Time:'},
|
||||
'!channel_name.is_empty() and (not has_channel_defaults() or set_fade_length)')
|
||||
|
||||
add_body_edit("set_volume", ValueType.BOOL_BUTTON,{
|
||||
"editor_icon" : ["AudioStreamPlayer", "EditorIcons"],
|
||||
"tooltip" : "Overwrite Volume"
|
||||
},"!file_path.is_empty() and has_channel_defaults()")
|
||||
add_body_edit('volume', ValueType.NUMBER, {'left_text':'Volume:', 'mode':2},
|
||||
'!file_path.is_empty() and (not has_channel_defaults() or set_volume)')
|
||||
add_body_edit("set_audio_bus", ValueType.BOOL_BUTTON,{
|
||||
"editor_icon" : ["AudioBusBypass", "EditorIcons"],
|
||||
"tooltip" : "Overwrite Audio Bus"
|
||||
},"!file_path.is_empty() and has_channel_defaults()")
|
||||
add_body_edit('audio_bus', ValueType.DYNAMIC_OPTIONS, {
|
||||
'left_text':'Audio Bus:',
|
||||
'placeholder' : 'Master',
|
||||
'mode' : 2,
|
||||
'suggestions_func' : DialogicUtil.get_audio_bus_suggestions,
|
||||
}, '!file_path.is_empty() and (not has_channel_defaults() or set_audio_bus)')
|
||||
add_body_edit("set_loop", ValueType.BOOL_BUTTON,{
|
||||
"editor_icon" : ["Loop", "EditorIcons"],
|
||||
"tooltip" : "Overwrite Loop"
|
||||
},"!channel_name.is_empty() and !file_path.is_empty() and has_channel_defaults()")
|
||||
add_body_edit('loop', ValueType.BOOL, {'left_text':'Loop:'},
|
||||
'!channel_name.is_empty() and !file_path.is_empty() and (not has_channel_defaults() or set_loop)')
|
||||
add_body_line_break("!channel_name.is_empty() and !file_path.is_empty()")
|
||||
add_body_edit("set_sync_channel", ValueType.BOOL_BUTTON,{
|
||||
"editor_icon" : ["TransitionSync", "EditorIcons"],
|
||||
"tooltip" : "Enable Syncing"
|
||||
},"!channel_name.is_empty() and !file_path.is_empty()")
|
||||
|
||||
add_body_edit('sync_channel', ValueType.DYNAMIC_OPTIONS, {
|
||||
'left_text' :'Sync with:',
|
||||
'placeholder' : '(No Sync)',
|
||||
'mode' : 3,
|
||||
'suggestions_func' : get_sync_audio_channel_suggestions,
|
||||
'validation_func' : DialogicUtil.validate_audio_channel_name,
|
||||
'tooltip' : "Use an existing channel or type the name for a new channel. If channel doesn't exist, this setting will be ignored.",
|
||||
}, '!channel_name.is_empty() and !file_path.is_empty() and set_sync_channel')
|
||||
|
||||
|
||||
## Used by the button on the visual event
|
||||
func _open_audio_settings() -> void:
|
||||
var editor_manager := editor_node.find_parent('EditorsManager')
|
||||
if editor_manager:
|
||||
editor_manager.open_editor(editor_manager.editors['Settings']['node'], true, "Audio")
|
||||
|
||||
|
||||
## Helper for the visibility conditions
|
||||
func has_channel_defaults() -> bool:
|
||||
var defaults := DialogicUtil.get_audio_channel_defaults()
|
||||
return defaults.has(channel_name)
|
||||
|
||||
|
||||
func get_audio_channel_suggestions(filter:String) -> Dictionary:
|
||||
var suggestions := {}
|
||||
suggestions["(One-Shot SFX)"] = {
|
||||
"value":"",
|
||||
"tooltip": "Used for one shot sounds effects. Plays each sound in its own AudioStreamPlayer.",
|
||||
"editor_icon": ["GuiRadioUnchecked", "EditorIcons"]
|
||||
}
|
||||
# TODO use .merged after dropping 4.2 support
|
||||
suggestions.merge(DialogicUtil.get_audio_channel_suggestions(filter))
|
||||
return suggestions
|
||||
|
||||
func get_sync_audio_channel_suggestions(filter:="") -> Dictionary:
|
||||
return DialogicUtil.get_audio_channel_suggestions(filter)
|
||||
|
||||
|
||||
|
||||
####################### CODE COMPLETION ########################################
|
||||
################################################################################
|
||||
|
||||
func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void:
|
||||
var line_until: String = CodeCompletionHelper.get_line_untill_caret(line)
|
||||
if symbol == ' ':
|
||||
if line_until.count(' ') == 1:
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, "One-Shot SFX", ' ', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.6))
|
||||
for i in DialogicUtil.get_audio_channel_suggestions(""):
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i, event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.6), null, " ")
|
||||
elif line_until.count(" ") == 2:
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, '"', '"', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.6))
|
||||
|
||||
if symbol == "[" or (symbol == " " and line.count("[")):
|
||||
for i in ["fade", "volume", "bus", "loop", "sync"]:
|
||||
if not i+"=" in line:
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'="', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.6))
|
||||
|
||||
if (symbol == '"' or symbol == "=") and line.count("["):
|
||||
CodeCompletionHelper.suggest_shortcode_values(TextNode, self, line, word)
|
||||
|
||||
|
||||
func _get_start_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit) -> void:
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'audio', 'audio ', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3))
|
||||
|
||||
|
||||
#################### SYNTAX HIGHLIGHTING #######################################
|
||||
################################################################################
|
||||
|
||||
func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary:
|
||||
var result := regex.search(line)
|
||||
|
||||
dict[result.get_start()] = {"color":event_color.lerp(Highlighter.normal_color, 0.3)}
|
||||
dict[result.get_start("channel")] = {"color":event_color.lerp(Highlighter.normal_color, 0.8)}
|
||||
dict[result.get_start("file_path")] = {"color":event_color.lerp(Highlighter.string_color, 0.8)}
|
||||
if result.get_string("shortcode"):
|
||||
dict[result.get_start("shortcode")-1] = {"color":Highlighter.normal_color}
|
||||
dict = Highlighter.color_shortcode_content(dict, line, result.get_start("shortcode"), 0, event_color)
|
||||
|
||||
return dict
|
||||
1
addons/dialogic/Modules/Audio/event_audio.gd.uid
Normal file
1
addons/dialogic/Modules/Audio/event_audio.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://8p4qchmcuj68
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
@tool
|
||||
## Event that can change the currently playing background music.
|
||||
## This event won't play new music if it's already playing.
|
||||
class_name DialogicMusicEvent
|
||||
extends DialogicEvent
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## The file to play. If empty, the previous music will be faded out.
|
||||
var file_path := "":
|
||||
set(value):
|
||||
if file_path != value:
|
||||
file_path = value
|
||||
ui_update_needed.emit()
|
||||
## The channel to use.
|
||||
var channel_id: int = 0
|
||||
## The length of the fade. If 0 (by default) it's an instant change.
|
||||
var fade_length: float = 0
|
||||
## The volume the music will be played at.
|
||||
var volume: float = 0
|
||||
## The audio bus the music will be played at.
|
||||
var audio_bus := ""
|
||||
## If true, the audio will loop, otherwise only play once.
|
||||
var loop := true
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
if not dialogic.Audio.is_music_playing_resource(file_path, channel_id):
|
||||
dialogic.Audio.update_music(file_path, volume, audio_bus, fade_length, loop, channel_id)
|
||||
|
||||
finish()
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Music"
|
||||
set_default_color('Color7')
|
||||
event_category = "Audio"
|
||||
event_sorting_index = 2
|
||||
|
||||
|
||||
func _get_icon() -> Resource:
|
||||
return load(self.get_script().get_path().get_base_dir().path_join('icon_music.png'))
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "music"
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"path" : {"property": "file_path", "default": ""},
|
||||
"channel" : {"property": "channel_id", "default": 0},
|
||||
"fade" : {"property": "fade_length", "default": 0},
|
||||
"volume" : {"property": "volume", "default": 0},
|
||||
"bus" : {"property": "audio_bus", "default": "",
|
||||
"suggestions": get_bus_suggestions},
|
||||
"loop" : {"property": "loop", "default": true},
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor() -> void:
|
||||
add_header_edit('file_path', ValueType.FILE, {
|
||||
'left_text' : 'Play',
|
||||
'file_filter' : "*.mp3, *.ogg, *.wav; Supported Audio Files",
|
||||
'placeholder' : "No music",
|
||||
'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]})
|
||||
add_header_edit('channel_id', ValueType.FIXED_OPTIONS, {'left_text':'on:', 'options': get_channel_list()})
|
||||
add_header_edit('file_path', ValueType.AUDIO_PREVIEW)
|
||||
add_body_edit('fade_length', ValueType.NUMBER, {'left_text':'Fade Time:'})
|
||||
add_body_edit('volume', ValueType.NUMBER, {'left_text':'Volume:', 'mode':2}, '!file_path.is_empty()')
|
||||
add_body_edit('audio_bus', ValueType.SINGLELINE_TEXT, {'left_text':'Audio Bus:'}, '!file_path.is_empty()')
|
||||
add_body_edit('loop', ValueType.BOOL, {'left_text':'Loop:'}, '!file_path.is_empty() and not file_path.to_lower().ends_with(".wav")')
|
||||
|
||||
|
||||
func get_bus_suggestions() -> Dictionary:
|
||||
var bus_name_list := {}
|
||||
for i in range(AudioServer.bus_count):
|
||||
bus_name_list[AudioServer.get_bus_name(i)] = {'value':AudioServer.get_bus_name(i)}
|
||||
return bus_name_list
|
||||
|
||||
|
||||
func get_channel_list() -> Array:
|
||||
var channel_name_list := []
|
||||
for i in ProjectSettings.get_setting('dialogic/audio/max_channels', 4):
|
||||
channel_name_list.append({
|
||||
'label': 'Channel %s' % (i + 1),
|
||||
'value': i,
|
||||
})
|
||||
return channel_name_list
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bqnhktqs2281
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
@tool
|
||||
class_name DialogicSoundEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that allows to play a sound effect. Requires the Audio subsystem!
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## The path to the file to play.
|
||||
var file_path := "":
|
||||
set(value):
|
||||
if file_path != value:
|
||||
file_path = value
|
||||
ui_update_needed.emit()
|
||||
## The volume to play the sound at.
|
||||
var volume: float = 0
|
||||
## The bus to play the sound on.
|
||||
var audio_bus := ""
|
||||
## If true, the sound will loop infinitely. Not recommended (as there is no way to stop it).
|
||||
var loop := false
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
dialogic.Audio.play_sound(file_path, volume, audio_bus, loop)
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Sound"
|
||||
set_default_color('Color7')
|
||||
event_category = "Audio"
|
||||
event_sorting_index = 3
|
||||
help_page_path = "https://dialogic.coppolaemilio.com"
|
||||
|
||||
|
||||
func _get_icon() -> Resource:
|
||||
return load(self.get_script().get_path().get_base_dir().path_join('icon_sound.png'))
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "sound"
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_name
|
||||
"path" : {"property": "file_path", "default": "",},
|
||||
"volume" : {"property": "volume", "default": 0},
|
||||
"bus" : {"property": "audio_bus", "default": "",
|
||||
"suggestions": get_bus_suggestions},
|
||||
"loop" : {"property": "loop", "default": false},
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor() -> void:
|
||||
add_header_edit('file_path', ValueType.FILE,
|
||||
{'left_text' : 'Play',
|
||||
'file_filter' : '*.mp3, *.ogg, *.wav; Supported Audio Files',
|
||||
'placeholder' : "Select file",
|
||||
'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]})
|
||||
add_header_edit('file_path', ValueType.AUDIO_PREVIEW)
|
||||
add_body_edit('volume', ValueType.NUMBER, {'left_text':'Volume:', 'mode':2}, '!file_path.is_empty()')
|
||||
add_body_edit('audio_bus', ValueType.SINGLELINE_TEXT, {'left_text':'Audio Bus:'}, '!file_path.is_empty()')
|
||||
|
||||
|
||||
func get_bus_suggestions() -> Dictionary:
|
||||
var bus_name_list := {}
|
||||
for i in range(AudioServer.bus_count):
|
||||
bus_name_list[AudioServer.get_bus_name(i)] = {'value':AudioServer.get_bus_name(i)}
|
||||
return bus_name_list
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://w1ofu2qjmefo
|
||||
|
|
@ -3,7 +3,7 @@ extends DialogicIndexer
|
|||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_music.gd'), this_folder.path_join('event_sound.gd')]
|
||||
return [this_folder.path_join('event_audio.gd')]
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://c7po0y8sx23v4
|
||||
uid://dk46l1toqeswc
|
||||
|
|
|
|||
|
|
@ -3,16 +3,19 @@ extends DialogicSettingsPage
|
|||
|
||||
## Settings page that contains settings for the audio subsystem
|
||||
|
||||
const MUSIC_MAX_CHANNELS := "dialogic/audio/max_channels"
|
||||
const TYPE_SOUND_AUDIO_BUS := "dialogic/audio/type_sound_bus"
|
||||
const CHANNEL_DEFAULTS := "dialogic/audio/channel_defaults"
|
||||
|
||||
var channel_defaults := {}
|
||||
var _revalidate_channel_names := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
%MusicChannelCount.value_changed.connect(_on_music_channel_count_value_changed)
|
||||
%TypeSoundBus.item_selected.connect(_on_type_sound_bus_item_selected)
|
||||
$Panel.add_theme_stylebox_override('panel', get_theme_stylebox("Background", "EditorStyles"))
|
||||
|
||||
|
||||
func _refresh() -> void:
|
||||
%MusicChannelCount.value = ProjectSettings.get_setting(MUSIC_MAX_CHANNELS, 4)
|
||||
%TypeSoundBus.clear()
|
||||
var idx := 0
|
||||
for i in range(AudioServer.bus_count):
|
||||
|
|
@ -21,12 +24,218 @@ func _refresh() -> void:
|
|||
idx = i
|
||||
%TypeSoundBus.select(idx)
|
||||
|
||||
|
||||
func _on_music_channel_count_value_changed(value:float) -> void:
|
||||
ProjectSettings.set_setting(MUSIC_MAX_CHANNELS, value)
|
||||
ProjectSettings.save()
|
||||
load_channel_defaults(DialogicUtil.get_audio_channel_defaults())
|
||||
|
||||
|
||||
func _about_to_close() -> void:
|
||||
save_channel_defaults()
|
||||
|
||||
|
||||
## TYPE SOUND AUDIO BUS
|
||||
func _on_type_sound_bus_item_selected(index:int) -> void:
|
||||
ProjectSettings.set_setting(TYPE_SOUND_AUDIO_BUS, %TypeSoundBus.get_item_text(index))
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
#region AUDIO CHANNELS
|
||||
################################################################################
|
||||
|
||||
func load_channel_defaults(dictionary:Dictionary) -> void:
|
||||
channel_defaults.clear()
|
||||
for i in %AudioChannelDefaults.get_children():
|
||||
i.queue_free()
|
||||
|
||||
var column_names := [
|
||||
"Channel Name",
|
||||
"Volume",
|
||||
"Audio Bus",
|
||||
"Fade",
|
||||
"Loop",
|
||||
""
|
||||
]
|
||||
|
||||
for column in column_names:
|
||||
var label := Label.new()
|
||||
label.text = column
|
||||
label.theme_type_variation = 'DialogicHintText2'
|
||||
%AudioChannelDefaults.add_child(label)
|
||||
|
||||
var channel_names := dictionary.keys()
|
||||
channel_names.sort()
|
||||
|
||||
for channel_name in channel_names:
|
||||
add_channel_defaults(
|
||||
channel_name,
|
||||
dictionary[channel_name].volume,
|
||||
dictionary[channel_name].audio_bus,
|
||||
dictionary[channel_name].fade_length,
|
||||
dictionary[channel_name].loop)
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
_revalidate_channel_names = true
|
||||
revalidate_channel_names.call_deferred()
|
||||
|
||||
|
||||
func save_channel_defaults() -> void:
|
||||
var dictionary := {}
|
||||
|
||||
for i in channel_defaults:
|
||||
if is_instance_valid(channel_defaults[i].channel_name):
|
||||
var channel_name := ""
|
||||
if not channel_defaults[i].channel_name is Label:
|
||||
if channel_defaults[i].channel_name.current_value.is_empty():
|
||||
continue
|
||||
|
||||
channel_name = channel_defaults[i].channel_name.current_value
|
||||
#channel_name = DialogicUtil.channel_name_regex.sub(channel_name, '', true)
|
||||
|
||||
if channel_name.is_empty():
|
||||
dictionary[channel_name] = {
|
||||
'volume': channel_defaults[i].volume.get_value(),
|
||||
'audio_bus': channel_defaults[i].audio_bus.current_value,
|
||||
'fade_length': 0.0,
|
||||
'loop': false,
|
||||
}
|
||||
else:
|
||||
dictionary[channel_name] = {
|
||||
'volume': channel_defaults[i].volume.get_value(),
|
||||
'audio_bus': channel_defaults[i].audio_bus.current_value,
|
||||
'fade_length': channel_defaults[i].fade_length.get_value(),
|
||||
'loop': channel_defaults[i].loop.button_pressed,
|
||||
}
|
||||
|
||||
ProjectSettings.set_setting(CHANNEL_DEFAULTS, dictionary)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_add_channel_defaults_pressed() -> void:
|
||||
var added_node := add_channel_defaults('new_channel_name', 0.0, '', 0.0, true)
|
||||
if added_node:
|
||||
added_node.take_autofocus()
|
||||
_revalidate_channel_names = true
|
||||
revalidate_channel_names.call_deferred()
|
||||
|
||||
|
||||
func add_channel_defaults(channel_name: String, volume: float, audio_bus: String, fade_length: float, loop: bool) -> Control:
|
||||
var info := {}
|
||||
|
||||
for i in %AudioChannelDefaultRow.get_children():
|
||||
var x := i.duplicate()
|
||||
%AudioChannelDefaults.add_child(x)
|
||||
info[i.name] = x
|
||||
|
||||
|
||||
if channel_name.is_empty():
|
||||
var channel_label := Label.new()
|
||||
channel_label.text = &"One-Shot SFX"
|
||||
channel_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
|
||||
%AudioChannelDefaults.add_child(channel_label)
|
||||
%AudioChannelDefaults.move_child(channel_label, info.channel_name.get_index())
|
||||
info.channel_name.queue_free()
|
||||
info.channel_name = channel_label
|
||||
|
||||
var HintTooltip := preload("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn")
|
||||
var fade_hint := HintTooltip.instantiate()
|
||||
fade_hint.hint_text = "Fading is disabled for this channel."
|
||||
%AudioChannelDefaults.add_child(fade_hint)
|
||||
%AudioChannelDefaults.move_child(fade_hint, info.fade_length.get_index())
|
||||
info.fade_length.queue_free()
|
||||
info.fade_length = fade_hint
|
||||
|
||||
var loop_hint := HintTooltip.instantiate()
|
||||
loop_hint.hint_text = "Looping is disabled for this channel."
|
||||
%AudioChannelDefaults.add_child(loop_hint)
|
||||
%AudioChannelDefaults.move_child(loop_hint, info.loop.get_index())
|
||||
info.loop.queue_free()
|
||||
info.loop = loop_hint
|
||||
|
||||
info.delete.disabled = true
|
||||
|
||||
else:
|
||||
info.channel_name.suggestions_func = get_audio_channel_suggestions
|
||||
info.channel_name.validation_func = validate_channel_names.bind(info.channel_name)
|
||||
info.channel_name.set_value(channel_name)
|
||||
|
||||
info.fade_length.set_value(fade_length)
|
||||
|
||||
info.loop.set_pressed_no_signal(loop)
|
||||
|
||||
info.audio_bus.suggestions_func = DialogicUtil.get_audio_bus_suggestions
|
||||
info.audio_bus.set_value(audio_bus)
|
||||
|
||||
info.delete.icon = get_theme_icon(&"Remove", &"EditorIcons")
|
||||
|
||||
channel_defaults[len(channel_defaults)] = info
|
||||
return info['channel_name']
|
||||
|
||||
|
||||
func _on_remove_channel_defaults_pressed(index: int) -> void:
|
||||
for key in channel_defaults[index]:
|
||||
channel_defaults[index][key].queue_free()
|
||||
channel_defaults.erase(index)
|
||||
|
||||
|
||||
func get_audio_channel_suggestions(search_text:String) -> Dictionary:
|
||||
var suggestions := DialogicUtil.get_audio_channel_suggestions(search_text)
|
||||
|
||||
for i in channel_defaults.values():
|
||||
if i.channel_name is DialogicVisualEditorField:
|
||||
suggestions.erase(i.channel_name.current_value)
|
||||
|
||||
for key in suggestions.keys():
|
||||
suggestions[key].erase('tooltip')
|
||||
suggestions[key]['editor_icon'] = ["AudioStreamPlayer", "EditorIcons"]
|
||||
|
||||
return suggestions
|
||||
|
||||
|
||||
func revalidate_channel_names() -> void:
|
||||
_revalidate_channel_names = false
|
||||
for i in channel_defaults:
|
||||
if (is_instance_valid(channel_defaults[i].channel_name)
|
||||
and not channel_defaults[i].channel_name is Label):
|
||||
channel_defaults[i].channel_name.validate()
|
||||
|
||||
|
||||
func validate_channel_names(search_text: String, field_node: Control) -> Dictionary:
|
||||
var channel_cache = {}
|
||||
var result := {}
|
||||
var tooltips := []
|
||||
|
||||
if search_text.is_empty():
|
||||
result['error_tooltip'] = 'Must not be empty.'
|
||||
return result
|
||||
|
||||
if field_node:
|
||||
channel_cache[search_text] = [field_node]
|
||||
if field_node.current_value != search_text:
|
||||
_revalidate_channel_names = true
|
||||
revalidate_channel_names.call_deferred()
|
||||
|
||||
# Collect all channel names entered
|
||||
for i in channel_defaults:
|
||||
if (is_instance_valid(channel_defaults[i].channel_name)
|
||||
and not channel_defaults[i].channel_name is Label
|
||||
and channel_defaults[i].channel_name != field_node):
|
||||
var text := channel_defaults[i].channel_name.current_value as String
|
||||
if not channel_cache.has(text):
|
||||
channel_cache[text] = []
|
||||
|
||||
channel_cache[text].append(channel_defaults[i].channel_name)
|
||||
|
||||
# Check for duplicate names
|
||||
if channel_cache.has(search_text) and channel_cache[search_text].size() > 1:
|
||||
tooltips.append("Duplicate channel name.")
|
||||
|
||||
# Check for invalid characters
|
||||
result = DialogicUtil.validate_audio_channel_name(search_text)
|
||||
if result:
|
||||
tooltips.append(result.error_tooltip)
|
||||
result.error_tooltip = "\n".join(tooltips)
|
||||
elif not tooltips.is_empty():
|
||||
result['error_tooltip'] = "\n".join(tooltips)
|
||||
|
||||
return result
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://1w40lwv540il
|
||||
uid://cqyhm6offcitc
|
||||
|
|
|
|||
|
|
@ -1,36 +1,28 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://c2qgetjc3mfo3"]
|
||||
[gd_scene load_steps=6 format=3 uid="uid://c2qgetjc3mfo3"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://1w40lwv540il" path="res://addons/dialogic/Modules/Audio/settings_audio.gd" id="1_2iyyr"]
|
||||
[ext_resource type="Script" uid="uid://cqyhm6offcitc" path="res://addons/dialogic/Modules/Audio/settings_audio.gd" id="1_2iyyr"]
|
||||
[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_o1ban"]
|
||||
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="3_bx557"]
|
||||
[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/field_number.tscn" id="4_xfyvc"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_m57ns"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(1, 0.365, 0.365, 1)
|
||||
draw_center = false
|
||||
border_width_left = 2
|
||||
border_width_top = 2
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
corner_detail = 1
|
||||
|
||||
[node name="Audio" type="VBoxContainer"]
|
||||
offset_right = 121.0
|
||||
offset_bottom = 58.0
|
||||
script = ExtResource("1_2iyyr")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"DialogicSettingsSection"
|
||||
text = "Music Channels"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Max music channels"
|
||||
|
||||
[node name="HintTooltip" parent="HBoxContainer" instance=ExtResource("2_o1ban")]
|
||||
layout_mode = 2
|
||||
texture = null
|
||||
hint_text = "Lowering this value may invalidate existing music events!"
|
||||
|
||||
[node name="MusicChannelCount" type="SpinBox" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
min_value = 1.0
|
||||
value = 1.0
|
||||
|
||||
[node name="TypingSoundsTitle" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"DialogicSettingsSection"
|
||||
|
|
@ -45,10 +37,77 @@ text = "Audio Bus"
|
|||
|
||||
[node name="HintTooltip" parent="HBoxContainer2" instance=ExtResource("2_o1ban")]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Lowering this value may invalidate existing music events!"
|
||||
texture = null
|
||||
hint_text = "The default audio bus used by TypeSound nodes."
|
||||
|
||||
[node name="TypeSoundBus" type="OptionButton" parent="HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer3" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="HBoxContainer3"]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"DialogicSettingsSection"
|
||||
text = "Audio Channel Defaults"
|
||||
|
||||
[node name="HintTooltip" parent="HBoxContainer3" instance=ExtResource("2_o1ban")]
|
||||
layout_mode = 2
|
||||
texture = null
|
||||
hint_text = "Default settings for named audio channels."
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_m57ns")
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Panel"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="AudioChannelDefaults" type="GridContainer" parent="Panel/VBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
columns = 6
|
||||
|
||||
[node name="AudioChannelDefaultRow" type="HBoxContainer" parent="Panel/VBox"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="channel_name" parent="Panel/VBox/AudioChannelDefaultRow" instance=ExtResource("3_bx557")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Enter Channel Name"
|
||||
mode = 3
|
||||
|
||||
[node name="volume" parent="Panel/VBox/AudioChannelDefaultRow" instance=ExtResource("4_xfyvc")]
|
||||
layout_mode = 2
|
||||
mode = 2
|
||||
min = -80.0
|
||||
max = 6.0
|
||||
suffix = "dB"
|
||||
|
||||
[node name="audio_bus" parent="Panel/VBox/AudioChannelDefaultRow" instance=ExtResource("3_bx557")]
|
||||
layout_mode = 2
|
||||
placeholder_text = "Master"
|
||||
mode = 2
|
||||
|
||||
[node name="fade_length" parent="Panel/VBox/AudioChannelDefaultRow" instance=ExtResource("4_xfyvc")]
|
||||
layout_mode = 2
|
||||
mode = 0
|
||||
enforce_step = false
|
||||
min = 0.0
|
||||
|
||||
[node name="loop" type="CheckButton" parent="Panel/VBox/AudioChannelDefaultRow"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="delete" type="Button" parent="Panel/VBox/AudioChannelDefaultRow"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Add" type="Button" parent="Panel/VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
text = "Add channel"
|
||||
|
||||
[connection signal="pressed" from="Panel/VBox/Add" to="." method="_on_add_channel_defaults_pressed"]
|
||||
|
|
|
|||
|
|
@ -1,101 +1,77 @@
|
|||
extends DialogicSubsystem
|
||||
## Subsystem for managing background music and one-shot sound effects.
|
||||
## Subsystem for managing background audio and one-shot sound effects.
|
||||
##
|
||||
## This subsystem has many different helper methods for managing audio
|
||||
## in your timeline.
|
||||
## For instance, you can listen to music changes via [signal music_started].
|
||||
## For instance, you can listen to audio changes via [signal audio_started].
|
||||
|
||||
|
||||
## Whenever a new background music is started, this signal is emitted and
|
||||
## Whenever a new audio event is started, this signal is emitted and
|
||||
## contains a dictionary with the following keys: [br]
|
||||
## [br]
|
||||
## Key | Value Type | Value [br]
|
||||
## ----------- | ------------- | ----- [br]
|
||||
## `path` | [type String] | The path to the audio resource file. [br]
|
||||
## `volume` | [type float] | The volume of the audio resource that will be set to the [member base_music_player]. [br]
|
||||
## `audio_bus` | [type String] | The audio bus name that the [member base_music_player] will use. [br]
|
||||
## `channel` | [type String] | The channel name to play the audio on. [br]
|
||||
## `volume` | [type float] | The volume in `db` of the audio resource that will be set to the [AudioStreamPlayer]. [br]
|
||||
## `audio_bus` | [type String] | The audio bus name that the [AudioStreamPlayer] will use. [br]
|
||||
## `loop` | [type bool] | Whether the audio resource will loop or not once it finishes playing. [br]
|
||||
## `channel` | [type int] | The channel ID to play the audio on. [br]
|
||||
signal music_started(info: Dictionary)
|
||||
signal audio_started(info: Dictionary)
|
||||
|
||||
|
||||
## Whenever a new sound effect is set, this signal is emitted and contains a
|
||||
## dictionary with the following keys: [br]
|
||||
## [br]
|
||||
## Key | Value Type | Value [br]
|
||||
## ----------- | ------------- | ----- [br]
|
||||
## `path` | [type String] | The path to the audio resource file. [br]
|
||||
## `volume` | [type float] | The volume of the audio resource that will be set to [member base_sound_player]. [br]
|
||||
## `audio_bus` | [type String] | The audio bus name that the [member base_sound_player] will use. [br]
|
||||
## `loop` | [type bool] | Whether the audio resource will loop or not once it finishes playing. [br]
|
||||
signal sound_started(info: Dictionary)
|
||||
|
||||
|
||||
var max_channels: int:
|
||||
set(value):
|
||||
if max_channels != value:
|
||||
max_channels = value
|
||||
ProjectSettings.set_setting('dialogic/audio/max_channels', value)
|
||||
ProjectSettings.save()
|
||||
current_music_player.resize(value)
|
||||
get:
|
||||
return ProjectSettings.get_setting('dialogic/audio/max_channels', 4)
|
||||
|
||||
## Audio player base duplicated to play background music.
|
||||
##
|
||||
## Background music is long audio.
|
||||
var base_music_player := AudioStreamPlayer.new()
|
||||
## Reference to the last used music player.
|
||||
var current_music_player: Array[AudioStreamPlayer] = []
|
||||
## Audio player base, that will be duplicated to play sound effects.
|
||||
##
|
||||
## Sound effects are short audio.
|
||||
var base_sound_player := AudioStreamPlayer.new()
|
||||
|
||||
## Audio node for holding audio players
|
||||
var audio_node := Node.new()
|
||||
## Sound node for holding sound players
|
||||
var one_shot_audio_node := Node.new()
|
||||
## Dictionary with info of all current audio channels
|
||||
var current_audio_channels: Dictionary = {}
|
||||
|
||||
#region STATE
|
||||
####################################################################################################
|
||||
|
||||
## Clears the state on this subsystem and stops all audio.
|
||||
##
|
||||
## If you want to stop sounds only, use [method stop_all_sounds].
|
||||
func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
|
||||
for idx in max_channels:
|
||||
update_music('', 0.0, '', 0.0, true, idx)
|
||||
stop_all_sounds()
|
||||
stop_all_channels()
|
||||
stop_all_one_shot_sounds()
|
||||
|
||||
|
||||
## Loads the state on this subsystem from the current state info.
|
||||
func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void:
|
||||
if load_flag == LoadFlags.ONLY_DNODES:
|
||||
return
|
||||
var info: Dictionary = dialogic.current_state_info.get("music", {})
|
||||
if not info.is_empty() and info.has('path'):
|
||||
update_music(info.path, info.volume, info.audio_bus, 0, info.loop, 0)
|
||||
else:
|
||||
for channel_id in info.keys():
|
||||
if info[channel_id].is_empty() or info[channel_id].path.is_empty():
|
||||
update_music('', 0.0, '', 0.0, true, channel_id)
|
||||
else:
|
||||
update_music(info[channel_id].path, info[channel_id].volume, info[channel_id].audio_bus, 0, info[channel_id].loop, channel_id)
|
||||
|
||||
# Pre Alpha 17 Converter
|
||||
_convert_state_info()
|
||||
|
||||
var info: Dictionary = dialogic.current_state_info.get("audio", {})
|
||||
|
||||
for channel_name in info.keys():
|
||||
if info[channel_name].path.is_empty():
|
||||
update_audio(channel_name)
|
||||
else:
|
||||
update_audio(channel_name, info[channel_name].path, info[channel_name].settings_overrides)
|
||||
|
||||
|
||||
## Pauses playing audio.
|
||||
func pause() -> void:
|
||||
for child in get_children():
|
||||
for child in audio_node.get_children():
|
||||
child.stream_paused = true
|
||||
for child in one_shot_audio_node.get_children():
|
||||
child.stream_paused = true
|
||||
|
||||
|
||||
## Resumes playing audio.
|
||||
func resume() -> void:
|
||||
for child in get_children():
|
||||
for child in audio_node.get_children():
|
||||
child.stream_paused = false
|
||||
for child in one_shot_audio_node.get_children():
|
||||
child.stream_paused = false
|
||||
|
||||
|
||||
func _on_dialogic_timeline_ended() -> void:
|
||||
if not dialogic.Styles.get_layout_node():
|
||||
clear_game_state()
|
||||
pass
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
|
@ -105,115 +81,204 @@ func _on_dialogic_timeline_ended() -> void:
|
|||
func _ready() -> void:
|
||||
dialogic.timeline_ended.connect(_on_dialogic_timeline_ended)
|
||||
|
||||
base_music_player.name = "Music"
|
||||
add_child(base_music_player)
|
||||
|
||||
base_sound_player.name = "Sound"
|
||||
add_child(base_sound_player)
|
||||
|
||||
current_music_player.resize(max_channels)
|
||||
audio_node.name = "Audio"
|
||||
add_child(audio_node)
|
||||
one_shot_audio_node.name = "OneShotAudios"
|
||||
add_child(one_shot_audio_node)
|
||||
|
||||
|
||||
## Updates the background music. Will fade out previous music.
|
||||
func update_music(path := "", volume := 0.0, audio_bus := "", fade_time := 0.0, loop := true, channel_id := 0) -> void:
|
||||
|
||||
if channel_id > max_channels:
|
||||
printerr("\tChannel ID (%s) higher than Max Music Channels (%s)" % [channel_id, max_channels])
|
||||
dialogic.print_debug_moment()
|
||||
## Plays the given file (or nothing) on the given channel.
|
||||
## No channel given defaults to the "One-Shot SFX" channel,
|
||||
## which does not save audio but can have multiple audios playing simultaneously.
|
||||
func update_audio(channel_name:= "", path := "", settings_overrides := {}) -> void:
|
||||
#volume := 0.0, audio_bus := "", fade_time := 0.0, loop := true, sync_channel := "") -> void:
|
||||
if not is_channel_playing(channel_name) and path.is_empty():
|
||||
return
|
||||
|
||||
if not dialogic.current_state_info.has('music'):
|
||||
dialogic.current_state_info['music'] = {}
|
||||
## Determine audio settings
|
||||
## TODO use .merged after dropping 4.2 support
|
||||
var audio_settings: Dictionary = DialogicUtil.get_audio_channel_defaults().get(channel_name, {})
|
||||
audio_settings.merge(
|
||||
{"volume":0, "audio_bus":"", "fade_length":0.0, "loop":true, "sync_channel":""}
|
||||
)
|
||||
audio_settings.merge(settings_overrides, true)
|
||||
|
||||
dialogic.current_state_info['music'][channel_id] = {'path':path, 'volume':volume, 'audio_bus':audio_bus, 'loop':loop, 'channel':channel_id}
|
||||
music_started.emit(dialogic.current_state_info['music'][channel_id])
|
||||
## Handle previous audio on channel
|
||||
if is_channel_playing(channel_name):
|
||||
var prev_audio_node: AudioStreamPlayer = current_audio_channels[channel_name]
|
||||
prev_audio_node.name += "_Prev"
|
||||
if audio_settings.fade_length > 0.0:
|
||||
var fade_out_tween: Tween = create_tween()
|
||||
fade_out_tween.tween_method(
|
||||
interpolate_volume_linearly.bind(prev_audio_node),
|
||||
db_to_linear(prev_audio_node.volume_db),
|
||||
0.0,
|
||||
audio_settings.fade_length)
|
||||
fade_out_tween.tween_callback(prev_audio_node.queue_free)
|
||||
|
||||
var fader: Tween = null
|
||||
if is_instance_valid(current_music_player[channel_id]) and current_music_player[channel_id].playing or !path.is_empty():
|
||||
fader = create_tween()
|
||||
else:
|
||||
prev_audio_node.queue_free()
|
||||
|
||||
var prev_node: Node = null
|
||||
if is_instance_valid(current_music_player[channel_id]) and current_music_player[channel_id].playing:
|
||||
prev_node = current_music_player[channel_id]
|
||||
fader.tween_method(interpolate_volume_linearly.bind(prev_node), db_to_linear(prev_node.volume_db),0.0,fade_time)
|
||||
## Set state
|
||||
if not dialogic.current_state_info.has('audio'):
|
||||
dialogic.current_state_info['audio'] = {}
|
||||
|
||||
if path:
|
||||
current_music_player[channel_id] = base_music_player.duplicate()
|
||||
add_child(current_music_player[channel_id])
|
||||
current_music_player[channel_id].stream = load(path)
|
||||
current_music_player[channel_id].volume_db = volume
|
||||
if audio_bus:
|
||||
current_music_player[channel_id].bus = audio_bus
|
||||
if not current_music_player[channel_id].stream is AudioStreamWAV:
|
||||
if "loop" in current_music_player[channel_id].stream:
|
||||
current_music_player[channel_id].stream.loop = loop
|
||||
elif "loop_mode" in current_music_player[channel_id].stream:
|
||||
if loop:
|
||||
current_music_player[channel_id].stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
else:
|
||||
current_music_player[channel_id].stream.loop_mode = AudioStreamWAV.LOOP_DISABLED
|
||||
if not path:
|
||||
dialogic.current_state_info['audio'].erase(channel_name)
|
||||
return
|
||||
|
||||
current_music_player[channel_id].play(0)
|
||||
fader.parallel().tween_method(interpolate_volume_linearly.bind(current_music_player[channel_id]), 0.0, db_to_linear(volume),fade_time)
|
||||
dialogic.current_state_info['audio'][channel_name] = {'path':path, 'settings_overrides':settings_overrides}
|
||||
audio_started.emit(dialogic.current_state_info['audio'][channel_name])
|
||||
|
||||
if prev_node:
|
||||
fader.tween_callback(prev_node.queue_free)
|
||||
var new_player := AudioStreamPlayer.new()
|
||||
if channel_name:
|
||||
new_player.name = channel_name.validate_node_name()
|
||||
audio_node.add_child(new_player)
|
||||
else:
|
||||
new_player.name = "OneShotSFX"
|
||||
one_shot_audio_node.add_child(new_player)
|
||||
|
||||
var file := load(path)
|
||||
if file == null:
|
||||
printerr("[Dialogic] Audio file \"%s\" failed to load." % path)
|
||||
return
|
||||
|
||||
new_player.stream = load(path)
|
||||
|
||||
## Apply audio settings
|
||||
|
||||
## Volume & Fade
|
||||
if audio_settings.fade_length > 0.0:
|
||||
new_player.volume_db = linear_to_db(0.0)
|
||||
var fade_in_tween := create_tween()
|
||||
fade_in_tween.tween_method(
|
||||
interpolate_volume_linearly.bind(new_player),
|
||||
0.0,
|
||||
db_to_linear(audio_settings.volume),
|
||||
audio_settings.fade_length)
|
||||
|
||||
else:
|
||||
new_player.volume_db = audio_settings.volume
|
||||
|
||||
## Audio Bus
|
||||
new_player.bus = audio_settings.audio_bus
|
||||
|
||||
## Loop
|
||||
if "loop" in new_player.stream:
|
||||
new_player.stream.loop = audio_settings.loop
|
||||
elif "loop_mode" in new_player.stream:
|
||||
if audio_settings.loop:
|
||||
new_player.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
new_player.stream.loop_begin = 0
|
||||
new_player.stream.loop_end = new_player.stream.mix_rate * new_player.stream.get_length()
|
||||
else:
|
||||
new_player.stream.loop_mode = AudioStreamWAV.LOOP_DISABLED
|
||||
|
||||
## Sync & start player
|
||||
if audio_settings.sync_channel and is_channel_playing(audio_settings.sync_channel):
|
||||
var play_position: float = current_audio_channels[audio_settings.sync_channel].get_playback_position()
|
||||
new_player.play(play_position)
|
||||
|
||||
# TODO Remove this once https://github.com/godotengine/godot/issues/18878 is fixed
|
||||
if new_player.stream is AudioStreamWAV and new_player.stream.format == AudioStreamWAV.FORMAT_IMA_ADPCM:
|
||||
printerr("[Dialogic] WAV files using Ima-ADPCM compression cannot be synced. Reimport the file using a different compression mode.")
|
||||
dialogic.print_debug_moment()
|
||||
else:
|
||||
new_player.play()
|
||||
|
||||
new_player.finished.connect(_on_audio_finished.bind(new_player, channel_name, path))
|
||||
|
||||
if channel_name:
|
||||
current_audio_channels[channel_name] = new_player
|
||||
|
||||
|
||||
## Whether music is playing.
|
||||
func has_music(channel_id := 0) -> bool:
|
||||
return !dialogic.current_state_info.get('music', {}).get(channel_id, {}).get('path', '').is_empty()
|
||||
## Returns `true` if any audio is playing on the given [param channel_name].
|
||||
func is_channel_playing(channel_name: String) -> bool:
|
||||
return (current_audio_channels.has(channel_name)
|
||||
and is_instance_valid(current_audio_channels[channel_name])
|
||||
and current_audio_channels[channel_name].is_playing())
|
||||
|
||||
|
||||
## Plays a given sound file.
|
||||
func play_sound(path: String, volume := 0.0, audio_bus := "", loop := false) -> void:
|
||||
if base_sound_player != null and !path.is_empty():
|
||||
sound_started.emit({'path':path, 'volume':volume, 'audio_bus':audio_bus, 'loop':loop})
|
||||
|
||||
var new_sound_node := base_sound_player.duplicate()
|
||||
new_sound_node.name += "Sound"
|
||||
new_sound_node.stream = load(path)
|
||||
|
||||
if "loop" in new_sound_node.stream:
|
||||
new_sound_node.stream.loop = loop
|
||||
elif "loop_mode" in new_sound_node.stream:
|
||||
if loop:
|
||||
new_sound_node.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
else:
|
||||
new_sound_node.stream.loop_mode = AudioStreamWAV.LOOP_DISABLED
|
||||
|
||||
new_sound_node.volume_db = volume
|
||||
if audio_bus:
|
||||
new_sound_node.bus = audio_bus
|
||||
|
||||
add_child(new_sound_node)
|
||||
new_sound_node.play()
|
||||
new_sound_node.finished.connect(new_sound_node.queue_free)
|
||||
## Stops audio on all channels.
|
||||
func stop_all_channels(fade := 0.0) -> void:
|
||||
for channel_name in current_audio_channels.keys():
|
||||
update_audio(channel_name, '', {"fade_length":fade})
|
||||
|
||||
|
||||
## Stops all audio.
|
||||
func stop_all_sounds() -> void:
|
||||
for node in get_children():
|
||||
if node == base_sound_player:
|
||||
continue
|
||||
if "Sound" in node.name:
|
||||
node.queue_free()
|
||||
### Stops all one-shot sounds.
|
||||
func stop_all_one_shot_sounds() -> void:
|
||||
for i in one_shot_audio_node.get_children():
|
||||
i.queue_free()
|
||||
|
||||
|
||||
## Converts a linear loudness value to decibel and sets that volume to
|
||||
## the given [param node].
|
||||
func interpolate_volume_linearly(value: float, node: Node) -> void:
|
||||
func interpolate_volume_linearly(value: float, node: AudioStreamPlayer) -> void:
|
||||
node.volume_db = linear_to_db(value)
|
||||
|
||||
|
||||
## Returns whether the currently playing audio resource is the same as this
|
||||
## event's [param resource_path], for [param channel_id].
|
||||
func is_music_playing_resource(resource_path: String, channel_id := 0) -> bool:
|
||||
var is_playing_resource: bool = (current_music_player.size() > channel_id
|
||||
and is_instance_valid(current_music_player[channel_id])
|
||||
and current_music_player[channel_id].is_playing()
|
||||
and current_music_player[channel_id].stream.resource_path == resource_path)
|
||||
## event's [param resource_path], for [param channel_name].
|
||||
func is_channel_playing_file(file_path: String, channel_name: String) -> bool:
|
||||
return (is_channel_playing(channel_name)
|
||||
and current_audio_channels[channel_name].stream.resource_path == file_path)
|
||||
|
||||
return is_playing_resource
|
||||
|
||||
## Returns `true` if any channel is playing.
|
||||
func is_any_channel_playing() -> bool:
|
||||
for channel in current_audio_channels:
|
||||
if is_channel_playing(channel):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _on_audio_finished(player: AudioStreamPlayer, channel_name: String, path: String) -> void:
|
||||
if current_audio_channels.has(channel_name) and current_audio_channels[channel_name] == player:
|
||||
current_audio_channels.erase(channel_name)
|
||||
player.queue_free()
|
||||
if dialogic.current_state_info.get('audio', {}).get(channel_name, {}).get('path', '') == path:
|
||||
dialogic.current_state_info['audio'].erase(channel_name)
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Pre Alpha 17 Conversion
|
||||
|
||||
func _convert_state_info() -> void:
|
||||
var info: Dictionary = dialogic.current_state_info.get("music", {})
|
||||
if info.is_empty():
|
||||
return
|
||||
|
||||
var new_info := {}
|
||||
if info.has("path"):
|
||||
# Pre Alpha 16 Save Data Conversion
|
||||
new_info['music'] = {
|
||||
"path":info.path,
|
||||
"settings_overrides": {
|
||||
"volume":info.volume,
|
||||
"audio_bus":info.audio_bus,
|
||||
"loop":info.loop}
|
||||
}
|
||||
|
||||
else:
|
||||
# Pre Alpha 17 Save Data Conversion
|
||||
for channel_id in info.keys():
|
||||
if info[channel_id].is_empty():
|
||||
continue
|
||||
|
||||
var channel_name = "music"
|
||||
if channel_id > 0:
|
||||
channel_name += str(channel_id + 1)
|
||||
new_info[channel_name] = {
|
||||
"path": info[channel_id].path,
|
||||
"settings_overrides":{
|
||||
'volume': info[channel_id].volume,
|
||||
'audio_bus': info[channel_id].audio_bus,
|
||||
'loop': info[channel_id].loop,
|
||||
}
|
||||
}
|
||||
|
||||
dialogic.current_state_info['audio'] = new_info
|
||||
dialogic.current_state_info.erase('music')
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://7pht2qiwn6xf
|
||||
uid://do8vgqtp35d6w
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ func _update_background(argument:String, _time:float) -> void:
|
|||
if argument.begins_with('res://'):
|
||||
image_node.texture = load(argument)
|
||||
color_node.color = Color.TRANSPARENT
|
||||
elif argument.begins_with('user://'):
|
||||
var ext_image = Image.load_from_file(argument)
|
||||
image_node.texture = ImageTexture.create_from_image(ext_image)
|
||||
color_node.color = Color.TRANSPARENT
|
||||
elif argument.is_valid_html_color():
|
||||
image_node.texture = null
|
||||
color_node.color = Color(argument, 1)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://xwj105ltniqb
|
||||
uid://ci7s5odxo7543
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cl6g6ymkhjven"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://xwj105ltniqb" path="res://addons/dialogic/Modules/Background/DefaultBackgroundScene/default_background.gd" id="1_nkdrp"]
|
||||
[ext_resource type="Script" uid="uid://ci7s5odxo7543" path="res://addons/dialogic/Modules/Background/DefaultBackgroundScene/default_background.gd" id="1_nkdrp"]
|
||||
|
||||
[node name="DefaultBackground" type="Control"]
|
||||
layout_mode = 3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://dhqujsj8oqa7p
|
||||
uid://blaaa6obvwknl
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://020a4llqpjlv
|
||||
uid://6f7qewx7aga
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b6f6s6rsoy2bw
|
||||
uid://m3anyujei6ro
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://y8jr5c8l3e0a
|
||||
uid://dnuvmtb036bi3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bjqdjfxjcvj7s
|
||||
uid://bed16hbuh4atn
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://d2ghxhx2432ro
|
||||
uid://ctoc2p12vahcc
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://c3bd283fo14yy
|
||||
uid://dknape5pbyevn
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://c63rtrqaasjey
|
||||
uid://dwhod30peco4c
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ var time: float
|
|||
var bg_holder: DialogicNode_BackgroundHolder
|
||||
|
||||
|
||||
@warning_ignore("unused_signal") # Used by scripts inheriting this class
|
||||
signal transition_finished
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cn1cj14nuhrk6
|
||||
uid://cf47aj5eivati
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b2ybl1gbf0jej
|
||||
uid://clabj6a02r7iv
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b7oxjroxkqi5q
|
||||
uid://cuj1xsi7d7r5y
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://5kc4er8aj0s3
|
||||
uid://bue1pfm6eu7ww
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bd73gvgsuaxay
|
||||
uid://bkj1kaqcq5208
|
||||
|
|
|
|||
|
|
@ -35,3 +35,4 @@ func _custom_fade_in(_time:float) -> bool:
|
|||
## If you return false (by default) it will attempt to animate the "modulate" property.
|
||||
func _custom_fade_out(_time:float) -> bool:
|
||||
return false
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://5rp6alg02rv0
|
||||
uid://blsjcvm6gvd78
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ extends DialogicEvent
|
|||
var scene := ""
|
||||
## The argument that is passed to the background scene.
|
||||
## For the default scene it's the path to the image to show.
|
||||
var argument := ""
|
||||
var argument := "":
|
||||
set(value):
|
||||
if argument != value:
|
||||
argument = value
|
||||
ui_update_needed.emit()
|
||||
## The time the fade animation will take. Leave at 0 for instant change.
|
||||
var fade: float = 0.0
|
||||
## Name of the transition to use.
|
||||
|
|
@ -82,8 +86,8 @@ func get_shortcode() -> String:
|
|||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"scene" : {"property": "scene", "default": ""},
|
||||
"arg" : {"property": "argument", "default": ""},
|
||||
"scene" : {"property": "scene", "default": "", "ext_file":true},
|
||||
"arg" : {"property": "argument", "default": "", "ext_file":true},
|
||||
"fade" : {"property": "fade", "default": 0},
|
||||
"transition" : {"property": "transition", "default": "",
|
||||
"suggestions": get_transition_suggestions},
|
||||
|
|
@ -138,6 +142,10 @@ func build_event_editor() -> void:
|
|||
'_arg_type == ArgumentTypes.IMAGE or _scene_type == SceneTypes.DEFAULT')
|
||||
add_header_edit('argument', ValueType.SINGLELINE_TEXT, {}, '_arg_type == ArgumentTypes.CUSTOM')
|
||||
|
||||
add_body_edit("argument", ValueType.IMAGE_PREVIEW, {'left_text':'Preview:'},
|
||||
'(_arg_type == ArgumentTypes.IMAGE or _scene_type == SceneTypes.DEFAULT) and !argument.is_empty()')
|
||||
add_body_line_break('(_arg_type == ArgumentTypes.IMAGE or _scene_type == SceneTypes.DEFAULT) and !argument.is_empty()')
|
||||
|
||||
add_body_edit("transition", ValueType.DYNAMIC_OPTIONS,
|
||||
{'left_text':'Transition:',
|
||||
'empty_text':'Simple Fade',
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://ccksowqutf0np
|
||||
uid://sioj2uwexnwx
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cpl0roawa546c
|
||||
uid://bj085abnvwkyh
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bisx1nk1bjyjc
|
||||
uid://oxcjhq2817c7
|
||||
|
|
|
|||
|
|
@ -99,6 +99,12 @@ func update_background(scene := "", argument := "", fade_time := 0.0, transition
|
|||
else:
|
||||
new_viewport = null
|
||||
|
||||
# if there is still a transition going on, stop it now
|
||||
for node in get_children():
|
||||
if node is DialogicBackgroundTransition:
|
||||
node.queue_free()
|
||||
|
||||
|
||||
var trans_script: Script = load(DialogicResourceUtil.guess_special_resource("BackgroundTransition", transition_path, {"path":default_transition}).path)
|
||||
var trans_node := Node.new()
|
||||
trans_node.set_script(trans_script)
|
||||
|
|
@ -107,6 +113,7 @@ func update_background(scene := "", argument := "", fade_time := 0.0, transition
|
|||
trans_node.time = fade_time
|
||||
|
||||
if old_viewport:
|
||||
old_viewport.name = "OldBackground"
|
||||
trans_node.prev_scene = old_viewport.get_meta('node', null)
|
||||
trans_node.prev_texture = old_viewport.get_child(0).get_texture()
|
||||
old_viewport.get_meta('node')._custom_fade_out(fade_time)
|
||||
|
|
@ -115,6 +122,7 @@ func update_background(scene := "", argument := "", fade_time := 0.0, transition
|
|||
old_viewport.get_child(0).render_target_update_mode = SubViewport.UPDATE_ALWAYS
|
||||
trans_node.transition_finished.connect(old_viewport.queue_free)
|
||||
if new_viewport:
|
||||
new_viewport.name = "NewBackground"
|
||||
trans_node.next_scene = new_viewport.get_meta('node', null)
|
||||
trans_node.next_texture = new_viewport.get_child(0).get_texture()
|
||||
new_viewport.get_meta('node')._update_background(argument, fade_time)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b41q2p73ce000
|
||||
uid://5uwbnllu1kfv
|
||||
|
|
|
|||
|
|
@ -139,53 +139,17 @@ func get_shortcode_parameters() -> Dictionary:
|
|||
func build_event_editor() -> void:
|
||||
add_header_edit('autoload_name', ValueType.DYNAMIC_OPTIONS, {'left_text':'On autoload',
|
||||
'empty_text':'Autoload',
|
||||
'suggestions_func':get_autoload_suggestions,
|
||||
'suggestions_func': DialogicUtil.get_autoload_suggestions,
|
||||
'editor_icon':["Node", "EditorIcons"]})
|
||||
add_header_edit('method', ValueType.DYNAMIC_OPTIONS, {'left_text':'call',
|
||||
'empty_text':'Method',
|
||||
'suggestions_func':get_method_suggestions,
|
||||
'suggestions_func': get_method_suggestions,
|
||||
'editor_icon':["Callable", "EditorIcons"]}, 'autoload_name')
|
||||
add_body_edit('arguments', ValueType.ARRAY, {'left_text':'Arguments:'}, 'not autoload_name.is_empty() and not method.is_empty()')
|
||||
|
||||
|
||||
|
||||
func get_autoload_suggestions(filter:String="") -> Dictionary:
|
||||
var suggestions := {}
|
||||
|
||||
for prop in ProjectSettings.get_property_list():
|
||||
if prop.name.begins_with('autoload/'):
|
||||
var autoload: String = prop.name.trim_prefix('autoload/')
|
||||
suggestions[autoload] = {'value': autoload, 'tooltip':autoload, 'editor_icon': ["Node", "EditorIcons"]}
|
||||
if filter.begins_with(autoload):
|
||||
suggestions[filter] = {'value': filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]}
|
||||
return suggestions
|
||||
|
||||
|
||||
func get_method_suggestions(filter:String="", temp_autoload:String = "") -> Dictionary:
|
||||
var suggestions := {}
|
||||
|
||||
var script: Script
|
||||
if temp_autoload and ProjectSettings.has_setting('autoload/'+temp_autoload):
|
||||
script = load(ProjectSettings.get_setting('autoload/'+temp_autoload).trim_prefix('*'))
|
||||
|
||||
elif autoload_name and ProjectSettings.has_setting('autoload/'+autoload_name):
|
||||
var loaded_autoload := load(ProjectSettings.get_setting('autoload/'+autoload_name).trim_prefix('*'))
|
||||
|
||||
if loaded_autoload is PackedScene:
|
||||
var packed_scene: PackedScene = loaded_autoload
|
||||
script = packed_scene.instantiate().get_script()
|
||||
|
||||
else:
|
||||
script = loaded_autoload
|
||||
|
||||
if script:
|
||||
for script_method in script.get_script_method_list():
|
||||
if script_method.name.begins_with('@') or script_method.name.begins_with('_'):
|
||||
continue
|
||||
suggestions[script_method.name] = {'value': script_method.name, 'tooltip':script_method.name, 'editor_icon': ["Callable", "EditorIcons"]}
|
||||
if !filter.is_empty():
|
||||
suggestions[filter] = {'value': filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]}
|
||||
return suggestions
|
||||
func get_method_suggestions(filter:="") -> Dictionary:
|
||||
return DialogicUtil.get_autoload_method_suggestions(filter, autoload_name)
|
||||
|
||||
|
||||
func update_argument_info() -> void:
|
||||
|
|
@ -236,13 +200,21 @@ func check_arguments_and_update_warning() -> void:
|
|||
####################### CODE COMPLETION ########################################
|
||||
################################################################################
|
||||
|
||||
func _get_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void:
|
||||
func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void:
|
||||
var autoloads := DialogicUtil.get_autoload_suggestions()
|
||||
var line_until_caret: String = CodeCompletionHelper.get_line_untill_caret(line)
|
||||
|
||||
if line.count(' ') == 1 and not '.' in line:
|
||||
for i in get_autoload_suggestions():
|
||||
for i in autoloads:
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'.', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), TextNode.get_theme_icon("Node", "EditorIcons"))
|
||||
elif symbol == '.' and not '(' in line:
|
||||
for i in get_method_suggestions('', line.get_slice('.', 0).trim_prefix('do ')):
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'(', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), TextNode.get_theme_icon("Callable", "EditorIcons"))
|
||||
|
||||
elif (line_until_caret.ends_with(".") or symbol == "."):
|
||||
var some_autoload := line_until_caret.split(" ")[-1].split(".")[0]
|
||||
if some_autoload in autoloads:
|
||||
var methods := DialogicUtil.get_autoload_method_suggestions("", some_autoload)
|
||||
for i in methods.keys():
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'(', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), TextNode.get_theme_icon("MemberMethod", "EditorIcons"))
|
||||
|
||||
|
||||
|
||||
func _get_start_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit) -> void:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cx3saaa1iyvre
|
||||
uid://uhicnbvlk57s
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://huqsmyyipv62
|
||||
uid://bthb47untmgoo
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://fgk8hal7nugw
|
||||
uid://qruxugkg6y8w
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cb4xhtqnifsrf
|
||||
uid://rfgxn0xtuen3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://ckqo0ncifb40s
|
||||
uid://bcs0jdci4mngb
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cobrccabo638p
|
||||
uid://fekbbs23rj4m
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://df6ex8wqhaxix
|
||||
uid://dwnfbyjtc2anb
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://dttqyslijg03r
|
||||
uid://8ro2ayitmjcp
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://clwy8nsaw1phe
|
||||
uid://cn4yveni7rdr7
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ func animate() -> void:
|
|||
tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1)
|
||||
tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.1)
|
||||
tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1)
|
||||
tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.2)
|
||||
tween.tween_method(bound_multitween, Vector2(), Vector2(0, 0)*strength, time*0.2)
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
||||
func _get_named_variations() -> Dictionary:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://jp8nhoprsqdh
|
||||
uid://3tqien23j50t
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://j0si5xem8teo
|
||||
uid://lur75holg34f
|
||||
|
|
|
|||
|
|
@ -4,16 +4,17 @@ func animate() -> void:
|
|||
var tween := (node.create_tween() as Tween)
|
||||
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
|
||||
|
||||
var target_position := base_position.y
|
||||
var start_position: float = -node.get_viewport().size.y
|
||||
var end_position_y: float = base_position.y + node.get_parent().global_position.y
|
||||
var start_position: float = -get_node_size().y + get_node_origin().y
|
||||
|
||||
if is_reversed:
|
||||
target_position = -node.get_viewport().size.y
|
||||
tween.set_ease(Tween.EASE_IN)
|
||||
end_position_y = -get_node_size().y + get_node_origin().y
|
||||
start_position = base_position.y
|
||||
|
||||
node.position.y = start_position
|
||||
|
||||
tween.tween_property(node, 'position:y', target_position, time)
|
||||
tween.tween_property(node, 'global_position:y', end_position_y, time)
|
||||
|
||||
await tween.finished
|
||||
finished_once.emit()
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b3cvsvayqobsk
|
||||
uid://d0a5sgbr5imas
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ func animate() -> void:
|
|||
var tween := (node.create_tween() as Tween)
|
||||
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
|
||||
|
||||
var end_position_x: float = base_position.x
|
||||
var end_position_x: float = base_position.x + node.get_parent().global_position.x
|
||||
|
||||
if is_reversed:
|
||||
end_position_x = -node.get_viewport().size.x / 2
|
||||
end_position_x = - get_node_size().x + get_node_origin().x
|
||||
tween.set_ease(Tween.EASE_IN)
|
||||
|
||||
else:
|
||||
node.position.x = -node.get_viewport().size.x / 5
|
||||
node.global_position.x = -get_node_size().x + get_node_origin().x
|
||||
|
||||
tween.tween_property(node, 'position:x', end_position_x, time)
|
||||
tween.tween_property(node, 'global_position:x', end_position_x, time)
|
||||
|
||||
await tween.finished
|
||||
finished_once.emit()
|
||||
|
|
@ -21,6 +22,6 @@ func animate() -> void:
|
|||
|
||||
func _get_named_variations() -> Dictionary:
|
||||
return {
|
||||
"slide in left": {"reversed": false, "type": AnimationType.IN},
|
||||
"slide out right": {"reversed": true, "type": AnimationType.OUT},
|
||||
"slide from left": {"reversed": false, "type": AnimationType.IN},
|
||||
"slide to left": {"reversed": true, "type": AnimationType.OUT},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bs763k4612fht
|
||||
uid://c8il877nw3xqw
|
||||
|
|
|
|||
|
|
@ -4,24 +4,21 @@ func animate() -> void:
|
|||
var tween := (node.create_tween() as Tween)
|
||||
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
|
||||
|
||||
var viewport_x: float = node.get_viewport().size.x
|
||||
|
||||
var start_position_x: float = viewport_x + viewport_x / 5
|
||||
var end_position_x := base_position.x
|
||||
var viewport_x: float = get_viewport_size().x
|
||||
var end_position_x : float = base_position.x + node.get_parent().global_position.x
|
||||
|
||||
if is_reversed:
|
||||
start_position_x = base_position.x
|
||||
end_position_x = viewport_x + node.get_viewport().size.x / 5
|
||||
|
||||
|
||||
node.position.x = start_position_x
|
||||
tween.tween_property(node, 'position:x', end_position_x, time)
|
||||
end_position_x = viewport_x + get_node_origin().x
|
||||
tween.set_ease(Tween.EASE_IN)
|
||||
else:
|
||||
node.global_position.x = viewport_x + get_node_origin().x
|
||||
|
||||
tween.tween_property(node, 'global_position:x', end_position_x, time)
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
||||
|
||||
func _get_named_variations() -> Dictionary:
|
||||
return {
|
||||
"slide in right": {"reversed": false, "type": AnimationType.IN},
|
||||
"slide out left": {"reversed": true, "type": AnimationType.OUT},
|
||||
"slide from right": {"reversed": false, "type": AnimationType.IN},
|
||||
"slide to right": {"reversed": true, "type": AnimationType.OUT},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cswjq5jlqrn3g
|
||||
uid://daj7cqft5hnxg
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ func animate() -> void:
|
|||
var tween := (node.create_tween() as Tween)
|
||||
tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
|
||||
|
||||
var start_position_y: float = node.get_viewport().size.y * 2
|
||||
var end_position_y := base_position.y
|
||||
var start_position_y: float = get_viewport_size().y + get_node_origin().y
|
||||
var end_position_y: float = base_position.y + node.get_parent().global_position.y
|
||||
|
||||
if is_reversed:
|
||||
start_position_y = base_position.y
|
||||
end_position_y = node.get_viewport().size.y * 2
|
||||
tween.set_ease(Tween.EASE_IN)
|
||||
start_position_y = end_position_y
|
||||
end_position_y = get_viewport_size().y + get_node_origin().y
|
||||
|
||||
node.position.y = start_position_y
|
||||
tween.tween_property(node, 'position:y', end_position_y, time)
|
||||
node.global_position.y = start_position_y
|
||||
tween.tween_property(node, 'global_position:y', end_position_y, time)
|
||||
|
||||
await tween.finished
|
||||
finished_once.emit()
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b43hklc4t83ic
|
||||
uid://bj5ak53i7s8ux
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b4iouoy0lsmg3
|
||||
uid://crv1pn60clrvx
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://guke2u7jqwkt
|
||||
uid://cjwdb0jkjrcxe
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bb2xwtcht84yq
|
||||
uid://bl5sdpj631mtt
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b7mq20hi3v6m8
|
||||
uid://j2k3uogf5715
|
||||
|
|
|
|||
|
|
@ -75,3 +75,28 @@ func get_modulation_property() -> String:
|
|||
return "self_modulate"
|
||||
else:
|
||||
return "modulate"
|
||||
|
||||
|
||||
## Tries to return the size of the node to be animated.
|
||||
## For portraits this uses the portrait containers size.
|
||||
## This is useful if your animation depends on the size of the node.
|
||||
func get_node_size() -> Vector2:
|
||||
if not node:
|
||||
return Vector2()
|
||||
if node.get_parent() is DialogicNode_PortraitContainer:
|
||||
return node.get_parent().size
|
||||
if "size" in node:
|
||||
return node.size * node.scale
|
||||
return node.get_viewport().size
|
||||
|
||||
|
||||
func get_node_origin() -> Vector2:
|
||||
if not node:
|
||||
return Vector2()
|
||||
if node.get_parent() is DialogicNode_PortraitContainer:
|
||||
return node.get_parent()._get_origin_position()
|
||||
return Vector2()
|
||||
|
||||
|
||||
func get_viewport_size() -> Vector2:
|
||||
return node.get_viewport().get_visible_rect().size
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://blu2it3fdcxr8
|
||||
uid://0hsjlurlblou
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://c3a0wujs6kev3
|
||||
uid://cork0heubbx7f
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://b32paf0ll6um8"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c3a0wujs6kev3" path="res://addons/dialogic/Modules/Character/default_portrait.gd" id="1_wn77n"]
|
||||
[ext_resource type="Script" uid="uid://cork0heubbx7f" path="res://addons/dialogic/Modules/Character/default_portrait.gd" id="1_wn77n"]
|
||||
|
||||
[node name="DefaultPortrait" type="Node2D"]
|
||||
script = ExtResource("1_wn77n")
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://du4fl3cae5jml
|
||||
uid://djbg8sc0q67ow
|
||||
|
|
|
|||
|
|
@ -82,14 +82,14 @@ var character_identifier: String:
|
|||
if character_identifier == '--All--':
|
||||
return '--All--'
|
||||
if character:
|
||||
var identifier := DialogicResourceUtil.get_unique_identifier(character.resource_path)
|
||||
var identifier := character.get_identifier()
|
||||
if not identifier.is_empty():
|
||||
return identifier
|
||||
return character_identifier
|
||||
set(value):
|
||||
character_identifier = value
|
||||
character = DialogicResourceUtil.get_character_resource(value)
|
||||
if character and not character.portraits.has(portrait):
|
||||
if (not character) or (character and not character.portraits.has(portrait)):
|
||||
portrait = ""
|
||||
ui_update_needed.emit()
|
||||
|
||||
|
|
@ -154,10 +154,10 @@ func _execute() -> void:
|
|||
finish()
|
||||
return
|
||||
|
||||
dialogic.Portraits.change_character_extradata(character, extra_data)
|
||||
|
||||
if set_portrait:
|
||||
dialogic.Portraits.change_character_portrait(character, portrait, fade_animation, fade_length)
|
||||
await dialogic.Portraits.change_character_portrait(character, portrait, fade_animation, fade_length)
|
||||
|
||||
dialogic.Portraits.change_character_extradata(character, extra_data)
|
||||
|
||||
if set_mirrored:
|
||||
dialogic.Portraits.change_character_mirror(character, mirrored)
|
||||
|
|
@ -225,7 +225,7 @@ func to_text() -> String:
|
|||
if action == Actions.LEAVE and character_identifier == '--All--':
|
||||
result_string += "--All--"
|
||||
elif character:
|
||||
var name := DialogicResourceUtil.get_unique_identifier(character.resource_path)
|
||||
var name := character.get_character_name()
|
||||
|
||||
if name.count(" ") > 0:
|
||||
name = '"' + name + '"'
|
||||
|
|
@ -302,7 +302,7 @@ func get_shortcode_parameters() -> Dictionary:
|
|||
{'value':Actions.JOIN},
|
||||
'Leave':{'value':Actions.LEAVE},
|
||||
'Update':{'value':Actions.UPDATE}}},
|
||||
"character" : {"property": "character_identifier", "default": "", "custom_stored":true,},
|
||||
"character" : {"property": "character_identifier", "default": "", "custom_stored":true, "ext_file":true},
|
||||
"portrait" : {"property": "portrait", "default": "", "custom_stored":true,},
|
||||
"transform" : {"property": "transform", "default": "center", "custom_stored":true,},
|
||||
|
||||
|
|
@ -478,8 +478,8 @@ func get_fade_suggestions(search_text:String='') -> Dictionary:
|
|||
|
||||
func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void:
|
||||
var line_until_caret: String = CodeCompletionHelper.get_line_untill_caret(line)
|
||||
if symbol == ' ' and line_until_caret.count(' ') == 1:
|
||||
CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_MEMBER)
|
||||
if symbol == ' ' and line_until_caret.count(" ") == 1:
|
||||
CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_MEMBER, self)
|
||||
if line.begins_with('leave'):
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'All', '--All-- ', event_color, TextNode.get_theme_icon("GuiEllipsis", "EditorIcons"))
|
||||
|
||||
|
|
@ -487,10 +487,11 @@ func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:Str
|
|||
var completion_character := regex.search(line).get_string('name')
|
||||
CodeCompletionHelper.suggest_portraits(TextNode, completion_character)
|
||||
|
||||
elif not '[' in line_until_caret and symbol == ' ':
|
||||
elif not '[' in line_until_caret and symbol == ' ' and line_until_caret.split(" ", false).size() > 1:
|
||||
if not line.begins_with("leave"):
|
||||
for position in get_position_suggestions():
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, position, position+' ', TextNode.syntax_highlighter.normal_color)
|
||||
if not line_until_caret.split(" ", false)[-1] in get_position_suggestions():
|
||||
for position in get_position_suggestions():
|
||||
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, position, position+' ', TextNode.syntax_highlighter.normal_color)
|
||||
|
||||
# Shortcode Part
|
||||
if '[' in line_until_caret:
|
||||
|
|
@ -504,8 +505,10 @@ func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:Str
|
|||
if line.begins_with('update'):
|
||||
suggest_parameter("repeat", line, TextNode)
|
||||
if line.begins_with("update"):
|
||||
for param in ["move_time", "move_trans", "move_ease"]:
|
||||
for param in ["move_time", "move_trans", "move_ease", "fade"]:
|
||||
suggest_parameter(param, line, TextNode)
|
||||
if "fade=" in line_until_caret:
|
||||
suggest_parameter("fade_length", line, TextNode)
|
||||
if not line.begins_with('leave'):
|
||||
for param in ["mirrored", "z_index", "extra_data"]:
|
||||
suggest_parameter(param, line, TextNode)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://duu0jekiba20n
|
||||
uid://b88y7tdin2uu5
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://drl3os1svksa2
|
||||
uid://4tu24b2ex257
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cfcx0of1aekhk
|
||||
uid://d0ptqnbudhkyj
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_resource type="Resource" script_class="DialogicCharacter" load_steps=2 format=3 uid="uid://dykf1j17ct5mo"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://w3vtr3asq7b3" path="res://addons/dialogic/Resources/character.gd" id="1_qsljv"]
|
||||
[ext_resource type="Script" uid="uid://don4ds5f38byo" path="res://addons/dialogic/Resources/character.gd" id="1_qsljv"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_qsljv")
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ const ANIMATION_CROSSFADE_DEFAULT_LENGTH_KEY:= 'dialogic/animations/cross_fade_d
|
|||
|
||||
|
||||
func _ready():
|
||||
%JoinDefault.get_suggestions_func = get_join_animation_suggestions
|
||||
%JoinDefault.suggestions_func = get_join_animation_suggestions
|
||||
%JoinDefault.mode = 1
|
||||
%LeaveDefault.get_suggestions_func = get_leave_animation_suggestions
|
||||
%LeaveDefault.suggestions_func = get_leave_animation_suggestions
|
||||
%LeaveDefault.mode = 1
|
||||
%CrossFadeDefault.get_suggestions_func = get_crossfade_animation_suggestions
|
||||
%CrossFadeDefault.suggestions_func = get_crossfade_animation_suggestions
|
||||
%CrossFadeDefault.mode = 1
|
||||
|
||||
%PositionSuggestions.text_submitted.connect(save_setting.bind(POSITION_SUGGESTION_KEY))
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://0hdug2yw0j7u
|
||||
uid://c3hdycwp0hrdm
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://cp463rpri5j8a"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://0hdug2yw0j7u" path="res://addons/dialogic/Modules/Character/settings_portraits.gd" id="2"]
|
||||
[ext_resource type="Script" uid="uid://c3hdycwp0hrdm" path="res://addons/dialogic/Modules/Character/settings_portraits.gd" id="2"]
|
||||
[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_dce78"]
|
||||
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="3"]
|
||||
[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="3_m06d8"]
|
||||
|
|
@ -19,8 +19,7 @@ size_flags_stretch_ratio = 0.5
|
|||
[node name="Title2" type="Label" parent="PositionsTitle"]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"DialogicSettingsSection"
|
||||
text = "Position Suggestions
|
||||
"
|
||||
text = "Position Suggestions"
|
||||
|
||||
[node name="HintTooltip" parent="PositionsTitle" instance=ExtResource("2_dce78")]
|
||||
layout_mode = 2
|
||||
|
|
@ -40,8 +39,7 @@ size_flags_stretch_ratio = 0.5
|
|||
[node name="Title2" type="Label" parent="DefaultSceneTitle"]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"DialogicSettingsSection"
|
||||
text = "Default Portrait Scene
|
||||
"
|
||||
text = "Default Portrait Scene"
|
||||
|
||||
[node name="HintTooltip" parent="DefaultSceneTitle" instance=ExtResource("2_dce78")]
|
||||
layout_mode = 2
|
||||
|
|
@ -70,8 +68,7 @@ size_flags_stretch_ratio = 0.5
|
|||
[node name="Title2" type="Label" parent="Animations2"]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"DialogicSettingsSection"
|
||||
text = "Default Animations
|
||||
"
|
||||
text = "Default Animations"
|
||||
|
||||
[node name="HintTooltip" parent="Animations2" instance=ExtResource("2_dce78")]
|
||||
layout_mode = 2
|
||||
|
|
|
|||
|
|
@ -182,7 +182,8 @@ func resize_container(container: DialogicNode_PortraitContainer, rect_size: Vari
|
|||
tween.finished.connect(save_position_container.bind(container))
|
||||
else:
|
||||
container.position = container.position + relative_position_change
|
||||
container.size = final_rect_resize
|
||||
#container.size = final_rect_resize
|
||||
container.set_deferred("size", final_rect_resize)
|
||||
save_position_container(container)
|
||||
|
||||
position_changed.emit({&'change':'resized', &'container_node':container})
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://c8ug08wx4f1d0
|
||||
uid://c5dk5rh4vj8rd
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ var default_portrait_scene: PackedScene = load(get_script().resource_path.get_ba
|
|||
####################################################################################################
|
||||
|
||||
func clear_game_state(_clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
|
||||
for character in dialogic.current_state_info.get('portraits', {}).keys():
|
||||
remove_character(load(character))
|
||||
for character_identifier in dialogic.current_state_info.get('portraits', {}).keys():
|
||||
remove_character(DialogicResourceUtil.get_character_resource(character_identifier))
|
||||
dialogic.current_state_info['portraits'] = {}
|
||||
|
||||
|
||||
|
|
@ -31,21 +31,24 @@ func load_game_state(_load_flag:=LoadFlags.FULL_LOAD) -> void:
|
|||
# Load Position Portraits
|
||||
var portraits_info: Dictionary = dialogic.current_state_info.portraits.duplicate()
|
||||
dialogic.current_state_info.portraits = {}
|
||||
for character_path in portraits_info:
|
||||
var character_info: Dictionary = portraits_info[character_path]
|
||||
var character: DialogicCharacter = load(character_path)
|
||||
var container := dialogic.PortraitContainers.load_position_container(character.get_character_name())
|
||||
add_character(character, container, character_info.portrait, character_info.position_id)
|
||||
change_character_mirror(character, character_info.get('custom_mirror', false))
|
||||
change_character_z_index(character, character_info.get('z_index', 0))
|
||||
change_character_extradata(character, character_info.get('extra_data', ""))
|
||||
for character_identifier in portraits_info:
|
||||
var character_info: Dictionary = portraits_info[character_identifier]
|
||||
var character: DialogicCharacter = DialogicResourceUtil.get_character_resource(character_identifier)
|
||||
if character:
|
||||
var container := dialogic.PortraitContainers.load_position_container(character.get_character_name())
|
||||
add_character(character, container, character_info.portrait, character_info.position_id)
|
||||
change_character_mirror(character, character_info.get('custom_mirror', false))
|
||||
change_character_z_index(character, character_info.get('z_index', 0))
|
||||
change_character_extradata(character, character_info.get('extra_data', ""))
|
||||
else:
|
||||
push_error('[Dialogic] Failed to load character "' + str(character_identifier) + '".')
|
||||
|
||||
# Load Speaker Portrait
|
||||
var speaker: Variant = dialogic.current_state_info.get('speaker', "")
|
||||
var speaker: Variant = dialogic.current_state_info.get("speaker", "")
|
||||
if speaker:
|
||||
dialogic.current_state_info['speaker'] = ""
|
||||
change_speaker(load(speaker))
|
||||
dialogic.current_state_info['speaker'] = speaker
|
||||
dialogic.current_state_info["speaker"] = ""
|
||||
change_speaker(DialogicResourceUtil.get_character_resource(speaker))
|
||||
dialogic.current_state_info["speaker"] = speaker
|
||||
|
||||
|
||||
func pause() -> void:
|
||||
|
|
@ -117,6 +120,7 @@ func _change_portrait(character_node: Node2D, portrait: String, fade_animation:=
|
|||
if (not previous_portrait == null and
|
||||
previous_portrait.get_meta('scene', '') == scene_path and
|
||||
# Also check if the scene supports changing to the given portrait.
|
||||
previous_portrait.has_method('_should_do_portrait_update') and
|
||||
previous_portrait._should_do_portrait_update(character, portrait)):
|
||||
portrait_node = previous_portrait
|
||||
info['same_scene'] = true
|
||||
|
|
@ -124,12 +128,21 @@ func _change_portrait(character_node: Node2D, portrait: String, fade_animation:=
|
|||
else:
|
||||
|
||||
if ResourceLoader.exists(scene_path):
|
||||
var packed_scene: PackedScene = load(scene_path)
|
||||
ResourceLoader.load_threaded_request(scene_path)
|
||||
|
||||
if packed_scene:
|
||||
portrait_node = packed_scene.instantiate()
|
||||
var load_status := ResourceLoader.load_threaded_get_status(scene_path)
|
||||
while load_status == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
|
||||
await get_tree().process_frame
|
||||
load_status = ResourceLoader.load_threaded_get_status(scene_path)
|
||||
|
||||
if load_status == ResourceLoader.THREAD_LOAD_LOADED:
|
||||
var packed_scene: PackedScene = ResourceLoader.load_threaded_get(scene_path)
|
||||
if packed_scene:
|
||||
portrait_node = packed_scene.instantiate()
|
||||
else:
|
||||
push_error('[Dialogic] Portrait node "' + str(scene_path) + '" for character [' + character.display_name + '] could not be loaded. Your portrait might not show up on the screen. Confirm the path is correct.')
|
||||
else:
|
||||
push_error('[Dialogic] Portrait node "' + str(scene_path) + '" for character [' + character.display_name + '] could not be loaded. Your portrait might not show up on the screen. Confirm the path is correct.')
|
||||
push_error('[Dialogic] Failed to load portrait node "' + str(scene_path) + '" for character [' + character.display_name + '].')
|
||||
|
||||
if !portrait_node:
|
||||
portrait_node = default_portrait_scene.instantiate()
|
||||
|
|
@ -167,20 +180,26 @@ func _change_portrait(character_node: Node2D, portrait: String, fade_animation:=
|
|||
## Unless @force is false, this will take into consideration the character mirror,
|
||||
## portrait mirror and portrait position mirror settings.
|
||||
func _change_portrait_mirror(character_node: Node2D, mirrored := false, force := false) -> void:
|
||||
var latest_portrait := character_node.get_child(-1)
|
||||
var latest_portrait := character_node.get_child(-1) if character_node.get_child_count() > 0 else null
|
||||
|
||||
if latest_portrait.has_method('_set_mirror'):
|
||||
if latest_portrait and latest_portrait.has_method("_set_mirror"):
|
||||
var character: DialogicCharacter = character_node.get_meta('character')
|
||||
var current_portrait_info := character.get_portrait_info(character_node.get_meta('portrait'))
|
||||
latest_portrait._set_mirror(force or (mirrored != character.mirror != character_node.get_parent().mirrored != current_portrait_info.get('mirror', false)))
|
||||
|
||||
|
||||
func _change_portrait_extradata(character_node: Node2D, extra_data := "") -> void:
|
||||
var latest_portrait := character_node.get_child(-1)
|
||||
if not is_instance_valid(character_node):
|
||||
push_error("[Dialogic] Invalid character node provided.")
|
||||
return
|
||||
|
||||
if latest_portrait.has_method('_set_extra_data'):
|
||||
latest_portrait._set_extra_data(extra_data)
|
||||
if character_node.get_child_count() > 0:
|
||||
var latest_portrait := character_node.get_child(-1)
|
||||
|
||||
if latest_portrait and latest_portrait.has_method("_set_extra_data"):
|
||||
latest_portrait._set_extra_data(extra_data)
|
||||
else:
|
||||
push_warning("[Dialogic] No portrait found for character node: " + character_node.name)
|
||||
|
||||
func _update_character_transform(character_node:Node, time := 0.0) -> void:
|
||||
for child in character_node.get_children():
|
||||
|
|
@ -287,7 +306,7 @@ func _change_portrait_z_index(character_node: Node, z_index:int, update_zindex:=
|
|||
## fully visible yet.
|
||||
func get_character_portrait(character: DialogicCharacter) -> DialogicPortrait:
|
||||
if is_character_joined(character):
|
||||
var portrait_node: DialogicPortrait = dialogic.current_state_info['portraits'][character.resource_path].node.get_child(-1)
|
||||
var portrait_node: DialogicPortrait = dialogic.current_state_info['portraits'][character.get_identifier()].node.get_child(-1)
|
||||
return portrait_node
|
||||
|
||||
return null
|
||||
|
|
@ -342,7 +361,7 @@ func get_valid_portrait(character:DialogicCharacter, portrait:String) -> String:
|
|||
|
||||
if not portrait in character.portraits:
|
||||
if not portrait.is_empty():
|
||||
printerr('[Dialogic] Tried to use invalid portrait "', portrait, '" on character "', DialogicResourceUtil.get_unique_identifier(character.resource_path), '". Using default portrait instead.')
|
||||
printerr('[Dialogic] Tried to use invalid portrait "', portrait, '" on character "', character.get_character_name(), '". Using default portrait instead.')
|
||||
dialogic.print_debug_moment()
|
||||
portrait = character.default_portrait
|
||||
|
||||
|
|
@ -379,18 +398,18 @@ func join_character(character:DialogicCharacter, portrait:String, position_id:S
|
|||
return
|
||||
|
||||
var container := dialogic.PortraitContainers.add_container(character.get_character_name())
|
||||
var character_node := add_character(character, container, portrait, position_id)
|
||||
var character_node := await add_character(character, container, portrait, position_id)
|
||||
if character_node == null:
|
||||
return null
|
||||
|
||||
dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_id':position_id, 'custom_mirror':mirrored}
|
||||
dialogic.current_state_info['portraits'][character.get_identifier()] = {'portrait':portrait, 'node':character_node, 'position_id':position_id, 'custom_mirror':mirrored}
|
||||
|
||||
_change_portrait_mirror(character_node, mirrored)
|
||||
_change_portrait_extradata(character_node, extra_data)
|
||||
_change_portrait_z_index(character_node, z_index)
|
||||
|
||||
var info := {'character':character}
|
||||
info.merge(dialogic.current_state_info['portraits'][character.resource_path])
|
||||
info.merge(dialogic.current_state_info['portraits'][character.get_identifier()])
|
||||
character_joined.emit(info)
|
||||
|
||||
if animation_name.is_empty():
|
||||
|
|
@ -410,9 +429,9 @@ func join_character(character:DialogicCharacter, portrait:String, position_id:S
|
|||
return character_node
|
||||
|
||||
|
||||
func add_character(character:DialogicCharacter, container: DialogicNode_PortraitContainer, portrait:String, position_id:String) -> Node:
|
||||
func add_character(character: DialogicCharacter, container: DialogicNode_PortraitContainer, portrait: String, position_id: String) -> Node:
|
||||
if is_character_joined(character):
|
||||
printerr('[DialogicError] Cannot add a already joined character. If this is intended call _create_character_node manually.')
|
||||
printerr('[DialogicError] Cannot add an already joined character. If this is intended, call _create_character_node manually.')
|
||||
return null
|
||||
|
||||
portrait = get_valid_portrait(character, portrait)
|
||||
|
|
@ -430,36 +449,35 @@ func add_character(character:DialogicCharacter, container: DialogicNode_Portrait
|
|||
printerr('[Dialogic] Failed to join character to position ', position_id, ". Could not find position container.")
|
||||
return null
|
||||
|
||||
|
||||
dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_id':position_id}
|
||||
dialogic.current_state_info['portraits'][character.get_identifier()] = {'portrait': portrait, 'node': character_node, 'position_id': position_id}
|
||||
|
||||
_move_character(character_node, position_id)
|
||||
_change_portrait(character_node, portrait)
|
||||
await _change_portrait(character_node, portrait)
|
||||
|
||||
return character_node
|
||||
|
||||
|
||||
## Changes the portrait of a character. Only works with joined characters.
|
||||
func change_character_portrait(character: DialogicCharacter, portrait: String, fade_animation:="DEFAULT", fade_length := -1.0) -> void:
|
||||
func change_character_portrait(character: DialogicCharacter, portrait: String, fade_animation:="", fade_length := -1.0) -> void:
|
||||
if not is_character_joined(character):
|
||||
return
|
||||
|
||||
portrait = get_valid_portrait(character, portrait)
|
||||
|
||||
if dialogic.current_state_info.portraits[character.resource_path].portrait == portrait:
|
||||
if dialogic.current_state_info.portraits[character.get_identifier()].portrait == portrait:
|
||||
return
|
||||
|
||||
if fade_animation == "DEFAULT":
|
||||
if fade_animation == "":
|
||||
fade_animation = ProjectSettings.get_setting('dialogic/animations/cross_fade_default', "Fade Cross")
|
||||
fade_length = ProjectSettings.get_setting('dialogic/animations/cross_fade_default_length', 0.5)
|
||||
|
||||
fade_animation = DialogicPortraitAnimationUtil.guess_animation(fade_animation, DialogicPortraitAnimationUtil.AnimationType.CROSSFADE)
|
||||
|
||||
var info := _change_portrait(dialogic.current_state_info.portraits[character.resource_path].node, portrait, fade_animation, fade_length)
|
||||
dialogic.current_state_info.portraits[character.resource_path].portrait = info.portrait
|
||||
var info := await _change_portrait(dialogic.current_state_info.portraits[character.get_identifier()].node, portrait, fade_animation, fade_length)
|
||||
dialogic.current_state_info.portraits[character.get_identifier()].portrait = info.portrait
|
||||
_change_portrait_mirror(
|
||||
dialogic.current_state_info.portraits[character.resource_path].node,
|
||||
dialogic.current_state_info.portraits[character.resource_path].get('custom_mirror', false)
|
||||
dialogic.current_state_info.portraits[character.get_identifier()].node,
|
||||
dialogic.current_state_info.portraits[character.get_identifier()].get('custom_mirror', false)
|
||||
)
|
||||
character_portrait_changed.emit(info)
|
||||
|
||||
|
|
@ -469,8 +487,8 @@ func change_character_mirror(character:DialogicCharacter, mirrored:= false, forc
|
|||
if !is_character_joined(character):
|
||||
return
|
||||
|
||||
_change_portrait_mirror(dialogic.current_state_info.portraits[character.resource_path].node, mirrored, force)
|
||||
dialogic.current_state_info.portraits[character.resource_path]['custom_mirror'] = mirrored
|
||||
_change_portrait_mirror(dialogic.current_state_info.portraits[character.get_identifier()].node, mirrored, force)
|
||||
dialogic.current_state_info.portraits[character.get_identifier()]['custom_mirror'] = mirrored
|
||||
|
||||
|
||||
## Changes the z_index of a character. Only works with joined characters
|
||||
|
|
@ -478,17 +496,17 @@ func change_character_z_index(character:DialogicCharacter, z_index:int, update_z
|
|||
if !is_character_joined(character):
|
||||
return
|
||||
|
||||
_change_portrait_z_index(dialogic.current_state_info.portraits[character.resource_path].node, z_index, update_zindex)
|
||||
_change_portrait_z_index(dialogic.current_state_info.portraits[character.get_identifier()].node, z_index, update_zindex)
|
||||
if update_zindex:
|
||||
dialogic.current_state_info.portraits[character.resource_path]['z_index'] = z_index
|
||||
dialogic.current_state_info.portraits[character.get_identifier()]['z_index'] = z_index
|
||||
|
||||
|
||||
## Changes the extra data on the given character. Only works with joined characters
|
||||
func change_character_extradata(character:DialogicCharacter, extra_data:="") -> void:
|
||||
if !is_character_joined(character):
|
||||
return
|
||||
_change_portrait_extradata(dialogic.current_state_info.portraits[character.resource_path].node, extra_data)
|
||||
dialogic.current_state_info.portraits[character.resource_path]['extra_data'] = extra_data
|
||||
_change_portrait_extradata(dialogic.current_state_info.portraits[character.get_identifier()].node, extra_data)
|
||||
dialogic.current_state_info.portraits[character.get_identifier()]['extra_data'] = extra_data
|
||||
|
||||
|
||||
## Starts the given animation on the given character. Only works with joined characters
|
||||
|
|
@ -498,7 +516,7 @@ func animate_character(character: DialogicCharacter, animation_path: String, len
|
|||
|
||||
animation_path = DialogicPortraitAnimationUtil.guess_animation(animation_path)
|
||||
|
||||
var character_node: Node = dialogic.current_state_info.portraits[character.resource_path].node
|
||||
var character_node: Node = dialogic.current_state_info.portraits[character.get_identifier()].node
|
||||
|
||||
return _animate_node(character_node, animation_path, length, repeats, is_reversed)
|
||||
|
||||
|
|
@ -508,11 +526,11 @@ func move_character(character:DialogicCharacter, position_id:String, time:= 0.0,
|
|||
if !is_character_joined(character):
|
||||
return
|
||||
|
||||
if dialogic.current_state_info.portraits[character.resource_path].position_id == position_id:
|
||||
if dialogic.current_state_info.portraits[character.get_identifier()].position_id == position_id:
|
||||
return
|
||||
|
||||
_move_character(dialogic.current_state_info.portraits[character.resource_path].node, position_id, time, easing, trans)
|
||||
dialogic.current_state_info.portraits[character.resource_path].position_id = position_id
|
||||
_move_character(dialogic.current_state_info.portraits[character.get_identifier()].node, position_id, time, easing, trans)
|
||||
dialogic.current_state_info.portraits[character.get_identifier()].position_id = position_id
|
||||
character_moved.emit({'character':character, 'position_id':position_id, 'time':time})
|
||||
|
||||
|
||||
|
|
@ -554,8 +572,8 @@ func leave_all_characters(animation_name:="", animation_length:=0.0, animation_w
|
|||
## Return `null` if the [param character] is not part of the scene.
|
||||
func get_character_node(character: DialogicCharacter) -> Node:
|
||||
if is_character_joined(character):
|
||||
if is_instance_valid(dialogic.current_state_info['portraits'][character.resource_path].node):
|
||||
return dialogic.current_state_info['portraits'][character.resource_path].node
|
||||
if is_instance_valid(dialogic.current_state_info['portraits'][character.get_identifier()].node):
|
||||
return dialogic.current_state_info['portraits'][character.get_identifier()].node
|
||||
return null
|
||||
|
||||
|
||||
|
|
@ -571,19 +589,12 @@ func remove_character(character: DialogicCharacter) -> void:
|
|||
character_node.queue_free()
|
||||
character_left.emit({'character': character})
|
||||
|
||||
dialogic.current_state_info['portraits'].erase(character.resource_path)
|
||||
|
||||
|
||||
func get_current_character() -> DialogicCharacter:
|
||||
if dialogic.current_state_info.get('speaker', null):
|
||||
return load(dialogic.current_state_info.speaker)
|
||||
return null
|
||||
|
||||
dialogic.current_state_info['portraits'].erase(character.get_identifier())
|
||||
|
||||
|
||||
## Returns true if the given character is currently joined.
|
||||
func is_character_joined(character: DialogicCharacter) -> bool:
|
||||
if character == null or not character.resource_path in dialogic.current_state_info['portraits']:
|
||||
if character == null or not character.get_identifier() in dialogic.current_state_info['portraits']:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
|
@ -593,8 +604,8 @@ func is_character_joined(character: DialogicCharacter) -> bool:
|
|||
func get_joined_characters() -> Array[DialogicCharacter]:
|
||||
var chars: Array[DialogicCharacter] = []
|
||||
|
||||
for char_path: String in dialogic.current_state_info.get('portraits', {}).keys():
|
||||
chars.append(load(char_path))
|
||||
for char_identifier: String in dialogic.current_state_info.get('portraits', {}).keys():
|
||||
chars.append(DialogicResourceUtil.get_character_resource(char_identifier))
|
||||
|
||||
return chars
|
||||
|
||||
|
|
@ -604,7 +615,7 @@ func get_joined_characters() -> Array[DialogicCharacter]:
|
|||
## Only joined is included (and false) for not joined characters
|
||||
func get_character_info(character:DialogicCharacter) -> Dictionary:
|
||||
if is_character_joined(character):
|
||||
var info: Dictionary = dialogic.current_state_info['portraits'][character.resource_path]
|
||||
var info: Dictionary = dialogic.current_state_info['portraits'][character.get_identifier()]
|
||||
info['joined'] = true
|
||||
return info
|
||||
else:
|
||||
|
|
@ -629,7 +640,8 @@ func change_speaker(speaker: DialogicCharacter = null, portrait := "") -> void:
|
|||
|
||||
if leave_animation and leave_animation_length:
|
||||
var animate_out := _animate_node(character_node, leave_animation, leave_animation_length, 1, true)
|
||||
animate_out.finished.connect(character_node.queue_free)
|
||||
await animate_out.finished
|
||||
character_node.queue_free()
|
||||
else:
|
||||
character_node.get_parent().remove_child(character_node)
|
||||
character_node.queue_free()
|
||||
|
|
@ -655,10 +667,10 @@ func change_speaker(speaker: DialogicCharacter = null, portrait := "") -> void:
|
|||
|
||||
fade_animation = DialogicPortraitAnimationUtil.guess_animation(fade_animation, DialogicPortraitAnimationUtil.AnimationType.CROSSFADE)
|
||||
|
||||
if container.portrait_prefix+portrait in speaker.portraits:
|
||||
portrait = container.portrait_prefix+portrait
|
||||
if container.portrait_prefix + portrait in speaker.portraits:
|
||||
portrait = container.portrait_prefix + portrait
|
||||
|
||||
_change_portrait(character_node, portrait, fade_animation, fade_length)
|
||||
await _change_portrait(character_node, portrait, fade_animation, fade_length)
|
||||
|
||||
# if the character has no portraits _change_portrait won't actually add a child node
|
||||
if character_node.get_child_count() == 0:
|
||||
|
|
@ -671,26 +683,28 @@ func change_speaker(speaker: DialogicCharacter = null, portrait := "") -> void:
|
|||
character_node.hide()
|
||||
if not container.is_visible_in_tree():
|
||||
await get_tree().process_frame
|
||||
|
||||
# There is chance that the style changed (due to a speaker style) and thus the character node is gone now.
|
||||
# In that case, just give up.
|
||||
if not is_instance_valid(character_node):
|
||||
return
|
||||
character_node.show()
|
||||
var join_animation: String = ProjectSettings.get_setting('dialogic/animations/join_default', "Fade In Up")
|
||||
join_animation = DialogicPortraitAnimationUtil.guess_animation(join_animation, DialogicPortraitAnimationUtil.AnimationType.IN)
|
||||
var join_animation_length := _get_join_default_length()
|
||||
|
||||
if join_animation and join_animation_length:
|
||||
_animate_node(character_node, join_animation, join_animation_length)
|
||||
await _animate_node(character_node, join_animation, join_animation_length).finished
|
||||
|
||||
_change_portrait_mirror(character_node)
|
||||
|
||||
if speaker:
|
||||
if speaker.resource_path != dialogic.current_state_info['speaker']:
|
||||
if dialogic.current_state_info['speaker'] and is_character_joined(load(dialogic.current_state_info['speaker'])):
|
||||
dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(-1)._unhighlight()
|
||||
var prev_speaker: DialogicCharacter = dialogic.Text.get_current_speaker()
|
||||
if speaker != prev_speaker:
|
||||
if is_character_joined(prev_speaker):
|
||||
dialogic.current_state_info["portraits"][prev_speaker.get_identifier()].node.get_child(-1)._unhighlight()
|
||||
|
||||
if speaker and is_character_joined(speaker):
|
||||
dialogic.current_state_info['portraits'][speaker.resource_path].node.get_child(-1)._highlight()
|
||||
|
||||
elif dialogic.current_state_info['speaker'] and is_character_joined(load(dialogic.current_state_info['speaker'])):
|
||||
dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(-1)._unhighlight()
|
||||
if is_character_joined(speaker):
|
||||
dialogic.current_state_info["portraits"][speaker.get_identifier()].node.get_child(-1)._highlight()
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -701,14 +715,15 @@ func change_speaker(speaker: DialogicCharacter = null, portrait := "") -> void:
|
|||
## Called from the [portrait=something] text effect.
|
||||
func text_effect_portrait(_text_node:Control, _skipped:bool, argument:String) -> void:
|
||||
if argument:
|
||||
if dialogic.current_state_info.get('speaker', null):
|
||||
change_character_portrait(load(dialogic.current_state_info.speaker), argument)
|
||||
change_speaker(load(dialogic.current_state_info.speaker), argument)
|
||||
var current_speaker := dialogic.Text.get_current_speaker()
|
||||
if current_speaker:
|
||||
change_character_portrait(current_speaker, argument)
|
||||
change_speaker(current_speaker, argument)
|
||||
|
||||
|
||||
## Called from the [extra_data=something] text effect.
|
||||
func text_effect_extradata(_text_node:Control, _skipped:bool, argument:String) -> void:
|
||||
if argument:
|
||||
if dialogic.current_state_info.get('speaker', null):
|
||||
change_character_extradata(load(dialogic.current_state_info.speaker), argument)
|
||||
if dialogic.Text.get_current_speaker():
|
||||
change_character_extradata(dialogic.Text.get_current_speaker(), argument)
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://c1531cn2fwxf6
|
||||
uid://fyhfur7bpp4v
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ func _execute() -> void:
|
|||
dialogic.Choices.show_current_question(false)
|
||||
dialogic.current_state = dialogic.States.AWAITING_CHOICE
|
||||
|
||||
|
||||
func _is_branch_starter() -> bool:
|
||||
return dialogic.Choices.is_question(dialogic.current_timeline_events.find(self))
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
|
@ -53,7 +57,7 @@ func _init() -> void:
|
|||
|
||||
|
||||
# return a control node that should show on the END BRANCH node
|
||||
func get_end_branch_control() -> Control:
|
||||
func _get_end_branch_control() -> Control:
|
||||
return load(get_script().resource_path.get_base_dir().path_join('ui_choice_end.tscn')).instantiate()
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://evfcdip3607c
|
||||
uid://cltu1tykths0n
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cpp7qjji6ck8p
|
||||
uid://bo0dqybasbqd
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bijk2aslmrx2r
|
||||
uid://b1stj4ljd2vo7
|
||||
|
|
|
|||
|
|
@ -1,15 +1,28 @@
|
|||
class_name DialogicNode_ChoiceButton
|
||||
extends Button
|
||||
## The button allows the player to make a choice in the Dialogic system.
|
||||
## This button allows the player to make a choice in the Dialogic system.
|
||||
##
|
||||
## This class is used in the Choice Layer. [br]
|
||||
## You may change the [member text_node] to any [class Node] that has a
|
||||
## `text` property. [br]
|
||||
## If you don't set the [member text_node], the text will be set on this
|
||||
## button instead.
|
||||
##
|
||||
## Using a different node may allow using rich text effects; they are
|
||||
## not supported on buttons at this point.
|
||||
## When a choice is reached Dialogic will automatically show ChoiceButtons
|
||||
## and call their [code]_load_info()[/code] method which will display the choices.
|
||||
## You will need to ensure that enough choice buttons are available in the tree
|
||||
## to display all choices.[br]
|
||||
##
|
||||
## [br]
|
||||
## You can extend this node and implement some custom logic by overwriting
|
||||
## the [code]_load_info(info:Dictionary)[/code] method. [br]
|
||||
## [br]
|
||||
## If you need RichText support, consider adding a RichTextLabel child and setting it as the [member text_node].[br]
|
||||
##
|
||||
## [br]
|
||||
## DialogicChoiceButtons will grab the focus when hovered to avoid a confusing
|
||||
## focus style being present for players who use the mouse.[br]
|
||||
## To avoid the opposite situation, when the focus is changed by the player
|
||||
## and a different button is still hovered the mouse pointer will be moved
|
||||
## to the now focused button as well.
|
||||
|
||||
|
||||
## Emitted when the choice is selected. Unless overridden, this is when the button or its shortcut is pressed.
|
||||
signal choice_selected
|
||||
|
||||
|
||||
## Used to identify what choices to put on. If you leave it at -1, choices will be distributed automatically.
|
||||
|
|
@ -21,7 +34,9 @@ extends Button
|
|||
@export var sound_hover: AudioStream
|
||||
## Can be set to play this sound when focused. Requires a sibling DialogicNode_ButtonSound node.
|
||||
@export var sound_focus: AudioStream
|
||||
## If set, the text will be set on this node's `text` property instead.
|
||||
|
||||
## If set, the text will be set on this node's `text` property instead.
|
||||
## This can be used to have a custom text rendering child, like a RichTextLabel.
|
||||
@export var text_node: Node
|
||||
|
||||
|
||||
|
|
@ -29,8 +44,34 @@ func _ready() -> void:
|
|||
add_to_group('dialogic_choice_button')
|
||||
shortcut_in_tooltip = false
|
||||
hide()
|
||||
|
||||
# For players who use a mouse to make choices, mouse hover should grab focus.
|
||||
# Otherwise the auto-focused button will always show a highlighted color when
|
||||
# the mouse cursor is hovering on another button.
|
||||
if not mouse_entered.is_connected(grab_focus):
|
||||
mouse_entered.connect(grab_focus)
|
||||
if not focus_entered.is_connected(_on_choice_button_focus_entred):
|
||||
focus_entered.connect(_on_choice_button_focus_entred.bind(self))
|
||||
|
||||
|
||||
## Custom choice buttons can override this for specialized behavior when the choice button is pressed.
|
||||
func _pressed():
|
||||
choice_selected.emit()
|
||||
|
||||
|
||||
## Custom choice buttons can override this if their behavior should change
|
||||
## based on event data. If the custom choice button does not override
|
||||
## visibility, disabled-ness, nor the choice text, consider
|
||||
## calling super(choice_info) at the start of the override.
|
||||
##
|
||||
## The choice_info Dictionary has the following keys:
|
||||
## - event_index: The index of the choice event in the timeline.
|
||||
## - button_index: The relative index of the choice (starts from 1).
|
||||
## - visible: If the choice should be visible.
|
||||
## - disabled: If the choice should be selectable.
|
||||
## - text: The text of the choice.
|
||||
## - visited_before: If the choice has been selected before. Only available is the History submodule is enabled.
|
||||
## - *: Information from the event's additional info.
|
||||
func _load_info(choice_info: Dictionary) -> void:
|
||||
set_choice_text(choice_info.text)
|
||||
visible = choice_info.visible
|
||||
|
|
@ -43,3 +84,37 @@ func set_choice_text(new_text: String) -> void:
|
|||
text_node.text = new_text
|
||||
else:
|
||||
text = new_text
|
||||
|
||||
|
||||
## This method moves the mouse to the focused choice when the focus changes
|
||||
## while a choice button was hovered. [br]
|
||||
## For players who use many devices (mouse/keyboard/gamepad, etc) at the same time to make choices,
|
||||
## a grabing-focus triggered by keyboard/gamepad should also change the mouse cursor's
|
||||
## position otherwise two buttons will have highlighted color(one highlighted button
|
||||
## triggered by mouse hover and another highlighted button triggered by other devices' choice).
|
||||
func _on_choice_button_focus_entred(focused_button: Button):
|
||||
var global_mouse_pos = get_global_mouse_position()
|
||||
var focused_button_rect = focused_button.get_global_rect()
|
||||
if focused_button_rect.has_point(global_mouse_pos):
|
||||
return
|
||||
# Only change mouse curor position when an unfocused button' rect has the cursor.
|
||||
for node in get_tree().get_nodes_in_group('dialogic_choice_button'):
|
||||
if node is Button:
|
||||
if node != focused_button and node.get_global_rect().has_point(global_mouse_pos):
|
||||
# We prefer to change only mouse_position.y or mouse_position.x to warp the
|
||||
# mouse to the focused button's rect to achieve the best visual effect.
|
||||
var modify_y_pos = Vector2(global_mouse_pos.x, focused_button.get_global_rect().get_center().y)
|
||||
if focused_button_rect.has_point(modify_y_pos):
|
||||
get_viewport().warp_mouse(modify_y_pos)
|
||||
return
|
||||
|
||||
var modify_x_pos = Vector2(focused_button.get_global_rect().get_center().x, global_mouse_pos.y)
|
||||
if focused_button_rect.has_point(modify_x_pos):
|
||||
get_viewport().warp_mouse(modify_x_pos)
|
||||
return
|
||||
|
||||
# Maybe the buttons are not aligned as vertically or horizontlly.
|
||||
# Or perhaps the length difference between the two buttons is quite large.
|
||||
# So we just make the cursor hover on the center of the focused button.
|
||||
get_viewport().warp_mouse(focused_button.get_global_rect().get_center())
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bcirqrep7rvr4
|
||||
uid://bldt7xlfum7ov
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cyog6egbjdhg0
|
||||
uid://dbbbq1hcbhmi2
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://uarvgnbrcltm"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cyog6egbjdhg0" path="res://addons/dialogic/Modules/Choice/settings_choices.gd" id="2"]
|
||||
[ext_resource type="Script" uid="uid://dbbbq1hcbhmi2" path="res://addons/dialogic/Modules/Choice/settings_choices.gd" id="2"]
|
||||
[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_nxutt"]
|
||||
|
||||
[sub_resource type="Image" id="Image_xvnnc"]
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ func post_install() -> void:
|
|||
func hide_all_choices() -> void:
|
||||
for node in get_tree().get_nodes_in_group('dialogic_choice_button'):
|
||||
node.hide()
|
||||
if node.is_connected('button_up', _on_choice_selected):
|
||||
node.disconnect('button_up', _on_choice_selected)
|
||||
if node.choice_selected.is_connected(_on_choice_selected):
|
||||
node.choice_selected.disconnect(_on_choice_selected)
|
||||
|
||||
|
||||
## Collects information on all the choices of the current question.
|
||||
|
|
@ -120,7 +120,7 @@ func get_current_question_info() -> Dictionary:
|
|||
if not hide:
|
||||
button_idx += 1
|
||||
|
||||
choice_info.text = dialogic.Text.parse_text(choice_info.text, true, true, false, true, false, false)
|
||||
choice_info.text = dialogic.Text.parse_text(choice_info.text, 1)
|
||||
|
||||
choice_info.merge(choice_event.extra_data)
|
||||
|
||||
|
|
@ -128,6 +128,7 @@ func get_current_question_info() -> Dictionary:
|
|||
choice_info['visited_before'] = dialogic.History.has_event_been_visited(choice_index)
|
||||
|
||||
question_info['choices'].append(choice_info)
|
||||
last_question_info['choices'].append(choice_info['text'])
|
||||
|
||||
return question_info
|
||||
|
||||
|
|
@ -155,7 +156,7 @@ func show_current_question(instant:=true) -> void:
|
|||
var question_info := get_current_question_info()
|
||||
|
||||
for choice in question_info.choices:
|
||||
var node: DialogicNode_ChoiceButton = get_choice_button_node(choice.button_index)
|
||||
var node: DialogicNode_ChoiceButton = get_choice_button(choice.button_index)
|
||||
|
||||
if not node:
|
||||
missing_button = true
|
||||
|
|
@ -181,9 +182,9 @@ func show_current_question(instant:=true) -> void:
|
|||
shortcut.events.append(input_key)
|
||||
node.shortcut = shortcut
|
||||
|
||||
if node.pressed.is_connected(_on_choice_selected):
|
||||
node.pressed.disconnect(_on_choice_selected)
|
||||
node.pressed.connect(_on_choice_selected.bind(choice))
|
||||
if node.choice_selected.is_connected(_on_choice_selected):
|
||||
node.choice_selected.disconnect(_on_choice_selected)
|
||||
node.choice_selected.connect(_on_choice_selected.bind(choice))
|
||||
|
||||
_choice_blocker.start(block_delay)
|
||||
question_shown.emit(question_info)
|
||||
|
|
@ -192,8 +193,24 @@ func show_current_question(instant:=true) -> void:
|
|||
printerr("[Dialogic] The layout you are using doesn't have enough Choice Buttons for the choices you are trying to display.")
|
||||
|
||||
|
||||
func focus_choice(button_index:int) -> void:
|
||||
var node: DialogicNode_ChoiceButton = get_choice_button(button_index)
|
||||
if node:
|
||||
node.grab_focus()
|
||||
|
||||
func get_choice_button_node(button_index:int) -> DialogicNode_ChoiceButton:
|
||||
|
||||
func select_choice(button_index:int) -> void:
|
||||
var node: DialogicNode_ChoiceButton = get_choice_button(button_index)
|
||||
if node:
|
||||
node.choice_selected.emit()
|
||||
|
||||
|
||||
func select_focused_choice() -> void:
|
||||
if get_viewport().gui_get_focus_owner() is DialogicNode_ChoiceButton:
|
||||
(get_viewport().gui_get_focus_owner() as DialogicNode_ChoiceButton).choice_selected.emit()
|
||||
|
||||
|
||||
func get_choice_button(button_index:int) -> DialogicNode_ChoiceButton:
|
||||
var idx := 1
|
||||
for node: DialogicNode_ChoiceButton in get_tree().get_nodes_in_group('dialogic_choice_button'):
|
||||
if !node.get_parent().is_visible_in_tree():
|
||||
|
|
@ -227,33 +244,29 @@ func _on_choice_selected(choice_info := {}) -> void:
|
|||
dialogic.handle_event(choice_info.event_index + 1)
|
||||
|
||||
|
||||
|
||||
## Returns the indexes of the choice events related to the current question.
|
||||
func get_current_choice_indexes() -> Array:
|
||||
var choices := []
|
||||
var evt_idx := dialogic.current_event_idx
|
||||
var ignore := 0
|
||||
var index := dialogic.current_event_idx-1
|
||||
while true:
|
||||
if evt_idx >= len(dialogic.current_timeline_events):
|
||||
index += 1
|
||||
if index >= len(dialogic.current_timeline_events):
|
||||
break
|
||||
|
||||
var event: DialogicEvent = dialogic.current_timeline_events[index]
|
||||
if event is DialogicChoiceEvent:
|
||||
choices.append(index)
|
||||
index = event.get_end_branch_index()
|
||||
else:
|
||||
break
|
||||
if dialogic.current_timeline_events[evt_idx] is DialogicChoiceEvent:
|
||||
if ignore == 0:
|
||||
choices.append(evt_idx)
|
||||
ignore += 1
|
||||
elif dialogic.current_timeline_events[evt_idx].can_contain_events:
|
||||
ignore += 1
|
||||
else:
|
||||
if ignore == 0:
|
||||
break
|
||||
|
||||
if dialogic.current_timeline_events[evt_idx] is DialogicEndBranchEvent:
|
||||
ignore -= 1
|
||||
evt_idx += 1
|
||||
return choices
|
||||
|
||||
|
||||
## Forward the dialogic action to the focused button
|
||||
func _on_dialogic_action() -> void:
|
||||
if get_viewport().gui_get_focus_owner() is DialogicNode_ChoiceButton and use_input_action and not dialogic.Inputs.input_was_mouse_input:
|
||||
get_viewport().gui_get_focus_owner().pressed.emit()
|
||||
if use_input_action and not dialogic.Inputs.input_was_mouse_input:
|
||||
select_focused_choice()
|
||||
|
||||
|
||||
#endregion
|
||||
|
|
@ -262,20 +275,27 @@ func _on_dialogic_action() -> void:
|
|||
#region HELPERS
|
||||
####################################################################################################
|
||||
|
||||
## Returns `true` if the given index is a text event before a question or the first choice event of a question.
|
||||
func is_question(index:int) -> bool:
|
||||
if dialogic.current_timeline_events[index] is DialogicTextEvent:
|
||||
var event: DialogicEvent = dialogic.current_timeline_events[index]
|
||||
if event is DialogicTextEvent:
|
||||
if len(dialogic.current_timeline_events)-1 != index:
|
||||
if dialogic.current_timeline_events[index+1] is DialogicChoiceEvent:
|
||||
var next_event: DialogicEvent = dialogic.current_timeline_events[index+1]
|
||||
if next_event is DialogicChoiceEvent:
|
||||
return true
|
||||
|
||||
if dialogic.current_timeline_events[index] is DialogicChoiceEvent:
|
||||
if index != 0 and dialogic.current_timeline_events[index-1] is DialogicEndBranchEvent:
|
||||
if dialogic.current_timeline_events[dialogic.current_timeline_events[index-1].find_opening_index(index-1)] is DialogicChoiceEvent:
|
||||
return false
|
||||
else:
|
||||
return true
|
||||
if event is DialogicChoiceEvent:
|
||||
if index == 0:
|
||||
return true
|
||||
var prev_event: DialogicEvent = dialogic.current_timeline_events[index-1]
|
||||
if not prev_event is DialogicEndBranchEvent:
|
||||
return true
|
||||
var prev_event_opener: DialogicEvent = dialogic.current_timeline_events[prev_event.get_opening_index()]
|
||||
if prev_event_opener is DialogicChoiceEvent:
|
||||
return false
|
||||
else:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://70q8j1ji8wm
|
||||
uid://cewv4d3aw0kj3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://yt5h64x4n67s
|
||||
uid://d28x7h2ufh3dd
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cn0wbb2lk0s22"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://yt5h64x4n67s" path="res://addons/dialogic/Modules/Choice/ui_choice_end.gd" id="1_7qd85"]
|
||||
[ext_resource type="Script" uid="uid://d28x7h2ufh3dd" path="res://addons/dialogic/Modules/Choice/ui_choice_end.gd" id="1_7qd85"]
|
||||
|
||||
[node name="Choice_End" type="HBoxContainer"]
|
||||
anchors_preset = 15
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ func _execute() -> void:
|
|||
var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event
|
||||
final_time = min(time, time_per_event)
|
||||
|
||||
if clear_textbox and dialogic.has_subsystem("Text"):
|
||||
if clear_textbox and dialogic.has_subsystem("Text") and dialogic.Text.is_textbox_visible():
|
||||
dialogic.Text.update_dialog_text('')
|
||||
dialogic.Text.hide_textbox()
|
||||
dialogic.Text.hide_textbox(final_time == 0)
|
||||
dialogic.current_state = dialogic.States.IDLE
|
||||
if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
|
||||
|
||||
|
|
@ -44,10 +44,10 @@ func _execute() -> void:
|
|||
if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
|
||||
|
||||
if clear_music and dialogic.has_subsystem('Audio'):
|
||||
for channel_id in dialogic.Audio.max_channels:
|
||||
if dialogic.Audio.has_music(channel_id):
|
||||
dialogic.Audio.update_music('', 0.0, "", final_time, channel_id)
|
||||
if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
|
||||
dialogic.Audio.stop_all_one_shot_sounds()
|
||||
if dialogic.Audio.is_any_channel_playing():
|
||||
dialogic.Audio.stop_all_channels(final_time)
|
||||
if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
|
||||
|
||||
if clear_style and dialogic.has_subsystem('Styles'):
|
||||
dialogic.Styles.change_style()
|
||||
|
|
@ -109,6 +109,6 @@ func build_event_editor() -> void:
|
|||
add_body_edit('clear_textbox', ValueType.BOOL_BUTTON, {'left_text':'Clear:', 'icon':load("res://addons/dialogic/Modules/Clear/clear_textbox.svg"), 'tooltip':'Clear Textbox'})
|
||||
add_body_edit('clear_portraits', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_characters.svg"), 'tooltip':'Clear Portraits'})
|
||||
add_body_edit('clear_background', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_background.svg"), 'tooltip':'Clear Background'})
|
||||
add_body_edit('clear_music', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_music.svg"), 'tooltip':'Clear Music'})
|
||||
add_body_edit('clear_music', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_music.svg"), 'tooltip':'Clear Audio'})
|
||||
add_body_edit('clear_style', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_style.svg"), 'tooltip':'Clear Style'})
|
||||
add_body_edit('clear_portrait_positions', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_positions.svg"), 'tooltip':'Clear Portrait Positions'})
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://dny1dhrq768ul
|
||||
uid://7aikid38is1o
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://cj5o6vkiga2r6
|
||||
uid://cyftouhvjfun2
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://drrny0bn1d8ls
|
||||
uid://dbcesveorhh6m
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bi1ru7ecbb7ag
|
||||
uid://b27oui35aoff4
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ extends DialogicEvent
|
|||
enum ConditionTypes {IF, ELIF, ELSE}
|
||||
|
||||
### Settings
|
||||
## condition type (see [ConditionTypes]). Defaults to if.
|
||||
|
||||
## Condition type (see [ConditionTypes]). Defaults to if.
|
||||
var condition_type := ConditionTypes.IF
|
||||
## The condition as a string. Will be executed as an Expression.
|
||||
var condition := ""
|
||||
|
|
@ -26,24 +27,12 @@ func _execute() -> void:
|
|||
|
||||
var result: bool = dialogic.Expressions.execute_condition(condition)
|
||||
if not result:
|
||||
var idx: int = dialogic.current_event_idx
|
||||
var ignore := 1
|
||||
while true:
|
||||
idx += 1
|
||||
if not dialogic.current_timeline.get_event(idx) or ignore == 0:
|
||||
break
|
||||
elif dialogic.current_timeline.get_event(idx).can_contain_events:
|
||||
ignore += 1
|
||||
elif dialogic.current_timeline.get_event(idx) is DialogicEndBranchEvent:
|
||||
ignore -= 1
|
||||
dialogic.current_event_idx = get_end_branch_index()
|
||||
|
||||
dialogic.current_event_idx = idx-1
|
||||
finish()
|
||||
|
||||
|
||||
## only called if the previous event was an end-branch event
|
||||
## return true if this event should be executed if the previous event was an end-branch event
|
||||
func should_execute_this_branch() -> bool:
|
||||
func _is_branch_starter() -> bool:
|
||||
return condition_type == ConditionTypes.IF
|
||||
|
||||
|
||||
|
|
@ -60,7 +49,7 @@ func _init() -> void:
|
|||
|
||||
|
||||
# return a control node that should show on the END BRANCH node
|
||||
func get_end_branch_control() -> Control:
|
||||
func _get_end_branch_control() -> Control:
|
||||
return load(get_script().resource_path.get_base_dir().path_join('ui_condition_end.tscn')).instantiate()
|
||||
|
||||
################################################################################
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://doeh3g7s7d4h8
|
||||
uid://g8gaor7ewun6
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b1mfu6jjyirnm
|
||||
uid://bsn2cv832qlam
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue