Add addons
86
Sprout/addons/dialogic/Events/Audio/event_music.gd
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
@tool
|
||||
class_name DialogicMusicEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that can change the currently playing background music.
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## The file to play. If empty, the previous music will be faded out.
|
||||
var file_path: String = ""
|
||||
## 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: String = "Master"
|
||||
## If true, the audio will loop, otherwise only play once.
|
||||
var loop: bool = true
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
dialogic.Audio.update_music(file_path, volume, audio_bus, fade_length, loop)
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Music"
|
||||
set_default_color('Color5')
|
||||
event_category = Category.AudioVisual
|
||||
event_sorting_index = 2
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
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": ""},
|
||||
"fade" : {"property": "fade_length", "default": 0},
|
||||
"volume" : {"property": "volume", "default": 0},
|
||||
"bus" : {"property": "audio_bus", "default": "Master",
|
||||
"suggestions": get_bus_suggestions},
|
||||
"loop" : {"property": "loop", "default": true,
|
||||
"suggestions": func(): return {'True':{'value':'true'}, 'False':{'value':'false'}}},
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('file_path', ValueType.File, 'Play', '',
|
||||
{'file_filter' : "*.mp3, *.ogg, *.wav",
|
||||
'placeholder' : "No music",
|
||||
'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]})
|
||||
add_body_edit('fade_length', ValueType.Float, 'Fade Time:')
|
||||
add_body_edit('volume', ValueType.Decibel, 'volume:', '', {}, '!file_path.is_empty()')
|
||||
add_body_edit('audio_bus', ValueType.SinglelineText, 'audio_bus:', '', {}, '!file_path.is_empty()')
|
||||
add_body_edit('loop', ValueType.Bool, 'loop:', '', {}, '!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
|
||||
81
Sprout/addons/dialogic/Events/Audio/event_sound.gd
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
@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: String = ""
|
||||
## The volume to play the sound at.
|
||||
var volume: float = 0
|
||||
## The bus to play the sound on.
|
||||
var audio_bus: String = "Master"
|
||||
## If true, the sound will loop infinitely. Not recommended (as there is no way to stop it).
|
||||
var loop: bool = 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('Color5')
|
||||
event_category = Category.AudioVisual
|
||||
event_sorting_index = 3
|
||||
expand_by_default = false
|
||||
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": "Master",
|
||||
"suggestions": get_bus_suggestions},
|
||||
"loop" : {"property": "loop", "default": false,
|
||||
"suggestions": func(): return {'True':{'value':'true'}, 'False':{'value':'false'}}},
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('file_path', ValueType.File, '', '',
|
||||
{'file_filter' : '*.mp3, *.ogg, *.wav',
|
||||
'placeholder' : "Select file",
|
||||
'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]})
|
||||
add_body_edit('volume', ValueType.Decibel, 'volume:', '', {}, '!file_path.is_empty()')
|
||||
add_body_edit('audio_bus', ValueType.SinglelineText, '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
|
||||
BIN
Sprout/addons/dialogic/Events/Audio/icon_music.png
Normal file
|
After Width: | Height: | Size: 552 B |
34
Sprout/addons/dialogic/Events/Audio/icon_music.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://buvpjsvdt4evk"
|
||||
path="res://.godot/imported/icon_music.png-9629b0cbb1d40f2776216a2874ccbd27.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Audio/icon_music.png"
|
||||
dest_files=["res://.godot/imported/icon_music.png-9629b0cbb1d40f2776216a2874ccbd27.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
Sprout/addons/dialogic/Events/Audio/icon_sound.png
Normal file
|
After Width: | Height: | Size: 524 B |
34
Sprout/addons/dialogic/Events/Audio/icon_sound.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d3ookrkto0yh6"
|
||||
path="res://.godot/imported/icon_sound.png-00b9f09aba60dcc77e5ef32a9d1ac490.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Audio/icon_sound.png"
|
||||
dest_files=["res://.godot/imported/icon_sound.png-00b9f09aba60dcc77e5ef32a9d1ac490.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
10
Sprout/addons/dialogic/Events/Audio/index.gd
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_music.gd'), this_folder.path_join('event_sound.gd')]
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
return [{'name':'Audio', 'script':this_folder.path_join('subsystem_audio.gd')}]
|
||||
7
Sprout/addons/dialogic/Events/Audio/node_music_player.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class_name DialogicNode_MusicPlayer
|
||||
extends AudioStreamPlayer
|
||||
|
||||
## An audio stream player that is used by dialogic to play music.
|
||||
|
||||
func _ready():
|
||||
add_to_group('dialogic_music_player')
|
||||
7
Sprout/addons/dialogic/Events/Audio/node_sound_player.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class_name DialogicNode_SoundPlayer
|
||||
extends AudioStreamPlayer
|
||||
|
||||
## An audio stream player that is used by dialogic to play sounds.
|
||||
|
||||
func _ready():
|
||||
add_to_group('dialogic_sound_player')
|
||||
95
Sprout/addons/dialogic/Events/Audio/subsystem_audio.gd
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
extends DialogicSubsystem
|
||||
|
||||
## Subsystem that manages music and sounds.
|
||||
|
||||
####################################################################################################
|
||||
## STATE
|
||||
####################################################################################################
|
||||
|
||||
func clear_game_state():
|
||||
update_music()
|
||||
stop_all_sounds()
|
||||
|
||||
func load_game_state():
|
||||
var info = dialogic.current_state_info.get('music')
|
||||
if info == null or info.path.is_empty():
|
||||
update_music()
|
||||
else:
|
||||
update_music(info.path, info.volume, info.audio_bus, 0, info.loop)
|
||||
|
||||
func pause() -> void:
|
||||
for node in get_tree().get_nodes_in_group('dialogic_music_player'):
|
||||
node.stream_paused = true
|
||||
for child in get_children():
|
||||
child.stream_paused = true
|
||||
|
||||
func resume() -> void:
|
||||
for node in get_tree().get_nodes_in_group('dialogic_music_player'):
|
||||
node.stream_paused = false
|
||||
for child in get_children():
|
||||
child.stream_paused = false
|
||||
|
||||
####################################################################################################
|
||||
## MAIN METHODS
|
||||
####################################################################################################
|
||||
|
||||
## Updates the background music. Will fade out previous music.
|
||||
func update_music(path:String = '', volume:float = 0.0, audio_bus:String = "Master", fade_time:float = 0.0, loop:bool = true) -> void:
|
||||
dialogic.current_state_info['music'] = {'path':path, 'volume':volume, 'audio_bus':audio_bus, 'loop':loop}
|
||||
for node in get_tree().get_nodes_in_group('dialogic_music_player'):
|
||||
var fader: Tween = null
|
||||
if node.playing or path:
|
||||
fader = create_tween()
|
||||
var prev_node = null
|
||||
if node.playing:
|
||||
prev_node = node.duplicate()
|
||||
add_child(prev_node)
|
||||
prev_node.play(node.get_playback_position())
|
||||
prev_node.remove_from_group('dialogic_music_player')
|
||||
fader.tween_method(interpolate_volume_linearly.bind(prev_node), db_to_linear(prev_node.volume_db),0.0,fade_time)
|
||||
if path:
|
||||
node.stream = load(path)
|
||||
node.volume_db = volume
|
||||
node.bus = audio_bus
|
||||
if "loop" in node.stream:
|
||||
node.stream.loop = loop
|
||||
elif "loop_mode" in node.stream:
|
||||
if loop:
|
||||
node.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
else:
|
||||
node.stream.loop_mode = AudioStreamWAV.LOOP_DISABLED
|
||||
|
||||
node.play()
|
||||
fader.parallel().tween_method(interpolate_volume_linearly.bind(node), 0.0,db_to_linear(volume),fade_time)
|
||||
else:
|
||||
node.stop()
|
||||
if prev_node:
|
||||
fader.tween_callback(prev_node.queue_free)
|
||||
|
||||
## Plays a given sound file.
|
||||
func play_sound(path:String, volume:float = 0.0, audio_bus:String = "Master", loop :bool= false) -> void:
|
||||
var sound_node: DialogicNode_SoundPlayer = get_tree().get_nodes_in_group('dialogic_sound_player').front()
|
||||
if sound_node != null and !path.is_empty():
|
||||
var new_sound_node: DialogicNode_SoundPlayer = sound_node.duplicate()
|
||||
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
|
||||
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)
|
||||
|
||||
func stop_all_sounds() -> void:
|
||||
var sound_nodes: Array = get_tree().get_nodes_in_group('dialogic_sound_player')
|
||||
if !sound_nodes.is_empty():
|
||||
for sound_player in sound_nodes.front().get_children():
|
||||
sound_player.queue_free()
|
||||
|
||||
func interpolate_volume_linearly(value :float, node:Node) -> void:
|
||||
node.volume_db = linear_to_db(value)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
extends TextureRect
|
||||
|
||||
## This is the DefaultBackground scene that can simply display a image
|
||||
##
|
||||
## You can create your own scenes. They can override the following methods:
|
||||
## - _update_background(@argument:String, @time:float)
|
||||
## - _fade_in(@time) (if not overriden modulate is animated)
|
||||
## - _fade_out(@time) (if not overriden modulate is animated)
|
||||
## make sure to free the scene at the end of the fade
|
||||
## - _should_do_background_update(@arg) -> bool
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
ignore_texture_size = true
|
||||
stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
|
||||
|
||||
anchor_right = 1
|
||||
anchor_bottom = 1
|
||||
|
||||
# load the new background in here.
|
||||
# The time argument is given for when [_should_do_background_update] returns true
|
||||
# (then you have to do the transition in here)
|
||||
func _update_background(argument:String, time:float) -> void:
|
||||
texture = load(argument)
|
||||
|
||||
|
||||
# if a Background event with this scene is encountered while this background is used,
|
||||
# this decides whether to create a new instance and call fade_out or just call [_update_background] # on this scene. Default is false
|
||||
func _should_do_background_update(argument:String) -> bool:
|
||||
return false
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://bgoisbbnvcfbf"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Background/default_background.gd" id="1_nkdrp"]
|
||||
|
||||
[node name="DefaultBackground" type="TextureRect"]
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
script = ExtResource("1_nkdrp")
|
||||
74
Sprout/addons/dialogic/Events/Background/event_background.gd
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
@tool
|
||||
class_name DialogicBackgroundEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event to show scenes in the background and switch between them.
|
||||
|
||||
### Settings
|
||||
|
||||
## The scene to use. If empty, this will default to the DefaultBackground.gd scene.
|
||||
## This scene supports images and fading.
|
||||
## If you set it to a scene path, then that scene will be instanced.
|
||||
## Learn more about custom backgrounds in the Subsystem_Background.gd docs.
|
||||
var scene: String = ""
|
||||
## The argument that is passed to the background scene.
|
||||
## For the default scene it's the path to the image to show.
|
||||
var argument: String = ""
|
||||
## The time the fade animation will take. Leave at 0 for instant change.
|
||||
var fade: float = 0.0
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTION
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
dialogic.Backgrounds.update_background(scene, argument, fade)
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Background"
|
||||
set_default_color('Color4')
|
||||
event_category = Category.AudioVisual
|
||||
event_sorting_index = 0
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "background"
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"scene" : {"property": "scene", "default": ""},
|
||||
"arg" : {"property": "argument", "default": ""},
|
||||
"fade" : {"property": "fade", "default": 0},
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('argument', ValueType.File, '', '',
|
||||
{'file_filter':'*.tscn, *.scn, *.jpg, *.jpeg, *.png, *.webp, *.tga, *svg, *.bmp, *.dds, *.exr, *.hdr',
|
||||
'placeholder': "No background",
|
||||
'editor_icon':["Image", "EditorIcons"]},
|
||||
'scene == ""')
|
||||
add_header_edit('argument', ValueType.SinglelineText, 'Argument:', '', {}, 'scene != ""')
|
||||
add_body_edit("fade", ValueType.Float, "fade time: ")
|
||||
add_body_edit("scene", ValueType.File, 'scene:', '',
|
||||
{'file_filter':'*.tscn, *.scn',
|
||||
'placeholder': "Default scene",
|
||||
'editor_icon':["PackedScene", "EditorIcons"]})
|
||||
BIN
Sprout/addons/dialogic/Events/Background/icon.png
Normal file
|
After Width: | Height: | Size: 512 B |
34
Sprout/addons/dialogic/Events/Background/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://517mp8gfj811"
|
||||
path="res://.godot/imported/icon.png-5701ef95f478df093cee0e38709c3b42.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Background/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-5701ef95f478df093cee0e38709c3b42.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
10
Sprout/addons/dialogic/Events/Background/index.gd
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_background.gd')]
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
return [{'name':'Backgrounds', 'script':this_folder.path_join('subsystem_backgrounds.gd')}]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class_name DialogicNode_BackgroundHolder
|
||||
extends CanvasLayer
|
||||
|
||||
|
||||
func _ready():
|
||||
add_to_group('dialogic_background_holders')
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
extends DialogicSubsystem
|
||||
|
||||
## Subsystem for managing backgrounds.
|
||||
|
||||
var default_background_scene = load(get_script().resource_path.get_base_dir().path_join('default_background.tscn'))
|
||||
####################################################################################################
|
||||
## STATE
|
||||
####################################################################################################
|
||||
|
||||
func clear_game_state():
|
||||
update_background()
|
||||
|
||||
|
||||
func load_game_state():
|
||||
update_background(dialogic.current_state_info.get('background_scene', ''), dialogic.current_state_info.get('background_argument', ''))
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## MAIN METHODS
|
||||
####################################################################################################
|
||||
|
||||
## Method that adds a given scene as child of the DialogicNode_BackgroundHolder.
|
||||
## It will call [_update_background()] on that scene with the given argument [argument].
|
||||
## It will call [_fade_in()] on that scene with the given fade time.
|
||||
## Will call fade_out on previous backgrounds scene.
|
||||
##
|
||||
## If the scene is the same as the last background you can bypass another instantiating
|
||||
## and use the same scene.
|
||||
## To do so implement [_should_do_background_update()] on the custom background scene.
|
||||
## Then [_update_background()] will be called directly on that previous scene.
|
||||
func update_background(scene:String = '', argument:String = '', fade_time:float = 0.0) -> void:
|
||||
for node in get_tree().get_nodes_in_group('dialogic_background_holders'):
|
||||
if node.visible:
|
||||
var bg_set: bool = false
|
||||
if scene == dialogic.current_state_info['background_scene']:
|
||||
for old_bg in node.get_children():
|
||||
if old_bg.has_method("_should_do_background_update") and old_bg._should_do_background_update(argument):
|
||||
if old_bg.has_method('_update_background'):
|
||||
old_bg._update_background(argument, fade_time)
|
||||
bg_set = true
|
||||
if !bg_set:
|
||||
# remove previous backgrounds
|
||||
for old_bg in node.get_children():
|
||||
|
||||
if old_bg.has_method('_fade_out'):
|
||||
old_bg._fade_out(fade_time)
|
||||
elif "modulate" in old_bg:
|
||||
var tween := old_bg.create_tween()
|
||||
tween.tween_property(old_bg, "modulate", Color.TRANSPARENT, fade_time)
|
||||
tween.tween_callback(old_bg.queue_free)
|
||||
else:
|
||||
old_bg.queue_free()
|
||||
|
||||
var new_node:Node
|
||||
if scene.ends_with('.tscn'):
|
||||
new_node = load(scene).instantiate()
|
||||
elif argument:
|
||||
new_node = default_background_scene.instantiate()
|
||||
else:
|
||||
new_node = null
|
||||
|
||||
if new_node:
|
||||
node.add_child(new_node)
|
||||
|
||||
if new_node.has_method('_update_background'):
|
||||
new_node._update_background(argument, fade_time)
|
||||
|
||||
if new_node.has_method('_fade_in'):
|
||||
new_node._fade_in(fade_time)
|
||||
|
||||
elif "modulate" in new_node:
|
||||
new_node.modulate = Color.TRANSPARENT
|
||||
var tween = new_node.create_tween()
|
||||
tween.tween_property(new_node, "modulate", Color.WHITE, fade_time)
|
||||
|
||||
dialogic.current_state_info['background_scene'] = scene
|
||||
dialogic.current_state_info['background_argument'] = argument
|
||||
116
Sprout/addons/dialogic/Events/Call Node/event_call_node.gd
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
@tool
|
||||
class_name DialogicCallNodeEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that allows calling a method in a node or autoload.
|
||||
|
||||
### Settings
|
||||
|
||||
## The path to the node you want to call. You can access autoloads directly. No need to add /root.
|
||||
var path: String = ""
|
||||
## The name of the method to call on the given node.
|
||||
var method: String = ""
|
||||
## A list of arguments to give to the call.
|
||||
var arguments: Array = []
|
||||
## If wait is true, the dialog will only continue once the called method is finished.
|
||||
var wait: bool = false
|
||||
## If this is true, the method will not be called.
|
||||
## Instead you can activate it during a following text event with the [signal] command.
|
||||
## Use together with [signal_name] property.
|
||||
var inline: bool = false
|
||||
## Only necessary if [inline] is true. Sets the argument to listen for.
|
||||
var inline_signal_argument: String
|
||||
## Only usefull if [inline] is true. If true, the command can only be used once.
|
||||
## If false it will not stop listening to the signal (with that argument).
|
||||
var inline_single_use: bool = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTION
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
if inline:
|
||||
dialogic.timeline_ended.connect(_disconnect_signal)
|
||||
|
||||
if path.begins_with('root'):
|
||||
path = "/"+path
|
||||
if not "/" in path and dialogic.get_node('/root').has_node(path):
|
||||
path = "/root/"+path
|
||||
|
||||
var n = dialogic.get_node_or_null(path)
|
||||
if n:
|
||||
if n.has_method(method):
|
||||
if inline:
|
||||
dialogic.text_signal.connect(_call_on_signal, CONNECT_PERSIST)
|
||||
elif wait:
|
||||
await n.callv(method, arguments).completed
|
||||
else:
|
||||
n.callv(method, arguments)
|
||||
|
||||
finish()
|
||||
|
||||
|
||||
func _call_on_signal(arg:String) -> void:
|
||||
if arg != inline_signal_argument:
|
||||
return
|
||||
if inline_single_use:
|
||||
dialogic.disconnect("text_signal", _call_on_signal)
|
||||
var n = dialogic.get_node_or_null(path)
|
||||
n.callv(method, arguments)
|
||||
|
||||
|
||||
func _disconnect_signal():
|
||||
if dialogic.text_signal.is_connected(_call_on_signal):
|
||||
dialogic.text_signal.disconnect(_call_on_signal)
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Call Node"
|
||||
set_default_color('Color1')
|
||||
event_category = Category.Godot
|
||||
event_sorting_index = 3
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "call_node"
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"path" : {"property": "path", "default": ""},
|
||||
"method" : {"property": "method", "default": ""},
|
||||
"args" : {"property": "arguments", "default": []},
|
||||
"wait" : {"property": "wait", "default": false,
|
||||
"suggestions": func(): return {'True':{'value':'true'}, 'False':{'value':'false'}}},
|
||||
"inline" : {"property": "inline", "default": false,
|
||||
"suggestions": func(): return {'True':{'value':'true'}, 'False':{'value':'false'}}},
|
||||
"signal" : {"property": "inline_signal_argument", "default": ""},
|
||||
"single_use": {"property": "inline_single_use", "default": false,
|
||||
"suggestions": func(): return {'True':{'value':'true'}, 'False':{'value':'false'}}}
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('method', ValueType.SinglelineText, 'Call method')
|
||||
add_header_edit('path', ValueType.SinglelineText, 'in object')
|
||||
add_body_edit('wait', ValueType.Bool, 'wait for method to finsih:')
|
||||
add_body_edit('inline', ValueType.Bool, 'Use as inline Command:')
|
||||
add_body_edit('inline_signal_argument', ValueType.SinglelineText, 'inline Signal Name', '', {}, 'inline == true')
|
||||
add_body_edit('inline_single_use', ValueType.Bool, 'Single Use:', '', {}, 'inline == true')
|
||||
add_body_line_break()
|
||||
add_body_edit('arguments', ValueType.StringArray, 'arguments:')
|
||||
BIN
Sprout/addons/dialogic/Events/Call Node/icon.png
Normal file
|
After Width: | Height: | Size: 554 B |
34
Sprout/addons/dialogic/Events/Call Node/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://duvcdvtgy4h4b"
|
||||
path="res://.godot/imported/icon.png-0303bccfcf0a602bb81277f73307dc34.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Call Node/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-0303bccfcf0a602bb81277f73307dc34.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
6
Sprout/addons/dialogic/Events/Call Node/index.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_call_node.gd')]
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
extends DialogicAnimation
|
||||
|
||||
func animate():
|
||||
var tween = (node.create_tween() as Tween)
|
||||
node.scale = Vector2()
|
||||
node.modulate.a = 0
|
||||
|
||||
tween.set_ease(Tween.EASE_IN_OUT)
|
||||
tween.set_trans(Tween.TRANS_LINEAR)
|
||||
tween.set_parallel()
|
||||
|
||||
tween.tween_property(node, 'scale', Vector2(1,1), time).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
||||
tween.tween_property(node, 'modulate:a', 1.0, time)
|
||||
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
extends DialogicAnimation
|
||||
|
||||
func animate():
|
||||
var tween = (node.create_tween() as Tween)
|
||||
node.scale = Vector2(1,1)
|
||||
node.modulate.a = 1
|
||||
|
||||
tween.set_ease(Tween.EASE_IN_OUT)
|
||||
tween.set_trans(Tween.TRANS_LINEAR)
|
||||
tween.set_parallel()
|
||||
|
||||
tween.tween_property(node, 'scale', Vector2(), time).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_IN)
|
||||
tween.tween_property(node, 'modulate:a', 0.0, time)
|
||||
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
extends DialogicAnimation
|
||||
|
||||
func animate():
|
||||
var tween = (node.create_tween() as Tween)
|
||||
node.position.y = orig_pos.y + node.get_viewport().size.y/5
|
||||
node.modulate.a = 0
|
||||
tween.set_ease(Tween.EASE_IN_OUT)
|
||||
tween.set_trans(Tween.TRANS_SINE)
|
||||
tween.set_parallel()
|
||||
|
||||
tween.tween_property(node, 'position', orig_pos, time)
|
||||
tween.tween_property(node, 'modulate:a', 1.0, time)
|
||||
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
extends DialogicAnimation
|
||||
|
||||
func animate():
|
||||
var tween = (node.create_tween() as Tween)
|
||||
tween.set_ease(Tween.EASE_IN_OUT)
|
||||
tween.set_trans(Tween.TRANS_SINE)
|
||||
tween.set_parallel()
|
||||
|
||||
tween.tween_property(node, 'position:y', orig_pos.y + node.get_viewport().size.y/5, time)
|
||||
tween.tween_property(node, 'modulate:a', 0.0, time)
|
||||
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
extends DialogicAnimation
|
||||
|
||||
func animate():
|
||||
var tween = (node.create_tween() as Tween)
|
||||
#tween.set_parallel(true)
|
||||
#node.scale = Vector2(1,1)*1.2
|
||||
tween.tween_property(node, 'scale', Vector2(1,1)*1.2, time*0.5).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
||||
#tween.tween_property(node, 'scale', Vector2(1,1)*1.1, time*0.5).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
||||
tween.tween_property(node, 'scale', Vector2(1,1), time*0.5).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
||||
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
extends DialogicAnimation
|
||||
|
||||
func animate():
|
||||
await node.get_tree().process_frame
|
||||
emit_signal('finished')
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
extends DialogicAnimation
|
||||
|
||||
func animate():
|
||||
var tween = (node.create_tween() as Tween)
|
||||
tween.set_parallel(true)
|
||||
tween.set_trans(Tween.TRANS_SINE)
|
||||
tween.set_ease(Tween.EASE_OUT)
|
||||
|
||||
tween.tween_property(node, 'scale', Vector2(1,1)*1.1, time*0.3)
|
||||
tween.tween_property(node, 'rotation', -0.1, time*0.1).set_delay(time*0.2)
|
||||
tween.tween_property(node, 'rotation', 0.1, time*0.1).set_delay(time*0.3)
|
||||
tween.tween_property(node, 'rotation', -0.1, time*0.1).set_delay(time*0.4)
|
||||
tween.tween_property(node, 'rotation', 0.1, time*0.1).set_delay(time*0.5)
|
||||
tween.tween_property(node, 'rotation', -0.1, time*0.1).set_delay(time*0.6)
|
||||
tween.chain().tween_property(node, 'scale', Vector2(1,1), time*0.3)
|
||||
tween.parallel().tween_property(node, 'rotation', 0.0, time*0.3)
|
||||
tween.finished.connect(emit_signal.bind('finished_once'))
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
class_name DialogicAnimation
|
||||
extends Node
|
||||
|
||||
## Class that can be used to animate portraits. Can be extended to create animations.
|
||||
|
||||
signal finished_once
|
||||
signal finished
|
||||
|
||||
## Set at runtime, will be the node to animate.
|
||||
var node :Node
|
||||
## Set at runtime, will be the length of the animation.
|
||||
var time : float
|
||||
## Set at runtime, will be the position at which to end the animation.
|
||||
var end_position : Vector2
|
||||
## Set at runtime. The position the node started at.
|
||||
var orig_pos : Vector2
|
||||
|
||||
## Used to repeate the animation for a number of times.
|
||||
var repeats : int
|
||||
|
||||
|
||||
func _ready():
|
||||
connect('finished_once', finished_one_loop)
|
||||
|
||||
|
||||
## To be overridden. Do the actual animating/tweening in here.
|
||||
## Use the properties [node], [time], [end_position], [orig_pos].
|
||||
func animate():
|
||||
pass
|
||||
|
||||
|
||||
func finished_one_loop():
|
||||
repeats -= 1
|
||||
if repeats > 0:
|
||||
animate()
|
||||
elif repeats == 0:
|
||||
emit_signal("finished")
|
||||
|
||||
|
||||
func pause():
|
||||
if node:
|
||||
node.process_mode = Node.PROCESS_MODE_DISABLED
|
||||
|
||||
|
||||
func resume():
|
||||
if node:
|
||||
node.process_mode = Node.PROCESS_MODE_INHERIT
|
||||
48
Sprout/addons/dialogic/Events/Character/default_portrait.gd
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
@tool
|
||||
class_name DialogicDefaultPortrait # TODO: Question should this have a class_name? To be extended or what?
|
||||
extends Node
|
||||
|
||||
## Default portrait scene.
|
||||
|
||||
## Stores the character that this scene displays.
|
||||
var character: DialogicCharacter
|
||||
## Stores the name of the current portrait.
|
||||
var portrait: String
|
||||
|
||||
|
||||
## Function to accept and use the extra data, if the custom portrait wants to accept it
|
||||
func _set_extra_data(data: String) -> void:
|
||||
pass
|
||||
|
||||
|
||||
## This function can be overridden. Defaults to true, if not overridden!
|
||||
func _should_do_portrait_update(character:DialogicCharacter, portrait:String) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
## If the custom portrait accepts a change, then accept it here
|
||||
func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void:
|
||||
if passed_portrait == "" or not passed_portrait in passed_character.portraits.keys():
|
||||
passed_portrait = passed_character['default_portrait']
|
||||
portrait = passed_portrait
|
||||
if passed_character != null:
|
||||
if character == null or character != passed_character:
|
||||
character = passed_character
|
||||
|
||||
var path :String = character.portraits[portrait].get('image', '')
|
||||
$Portrait.texture = null
|
||||
if !path.is_empty(): $Portrait.texture = load(path)
|
||||
$Portrait.centered = false
|
||||
$Portrait.position = $Portrait.get_rect().size * Vector2(-0.5, -1)
|
||||
|
||||
|
||||
## If implemented, this is called when the mirror changes
|
||||
func _set_mirror(mirror:bool) -> void:
|
||||
$Portrait.flip_h = mirror
|
||||
|
||||
|
||||
## If implemented, this is used by the editor for the "full view" mode
|
||||
func _get_covered_rect() -> Rect2:
|
||||
if $Portrait.texture == null:
|
||||
return Rect2()
|
||||
return Rect2($Portrait.position, $Portrait.get_rect().size)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://c4yp65n7ju1br"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Character/default_portrait.gd" id="1_wn77n"]
|
||||
|
||||
[sub_resource type="Texture2D" id="Texture2D_sny5f"]
|
||||
resource_local_to_scene = false
|
||||
resource_name = ""
|
||||
|
||||
[node name="DefaultPortrait" type="Node2D"]
|
||||
script = ExtResource("1_wn77n")
|
||||
|
||||
[node name="Portrait" type="Sprite2D" parent="."]
|
||||
texture = SubResource("Texture2D_sny5f")
|
||||
centered = false
|
||||
471
Sprout/addons/dialogic/Events/Character/event_character.gd
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
@tool
|
||||
class_name DialogicCharacterEvent
|
||||
extends DialogicEvent
|
||||
## Event that allows to manipulate character portraits.
|
||||
|
||||
enum PortraitModes {VisualNovel, RPG}
|
||||
enum ActionTypes {Join, Leave, Update}
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## The type of action of this event (JOIN/LEAVE/UPDATE). See [ActionTypes].
|
||||
var action_type : int = ActionTypes.Join
|
||||
## The character that will join/leave/update.
|
||||
var character : DialogicCharacter = null
|
||||
## For Join/Update, this will be the portrait of the character that is shown.
|
||||
## Not used on Leave.
|
||||
## If empty, the default portrait will be used.
|
||||
var portrait: String = ""
|
||||
## The index of the position this character should move to
|
||||
var position: int = 1
|
||||
## Path to an animation script (extending DialogicAnimation).
|
||||
## On Join/Leave empty (default) will fallback to the animations set in the settings.
|
||||
## On Update empty will mean no animation.
|
||||
var animation_name: String = ""
|
||||
## Length of the animation.
|
||||
var animation_length: float = 0.5
|
||||
## How often the animation is repeated. Only for Update events.
|
||||
var animation_repeats: int = 1
|
||||
## If true, the events waits for the animation to finish before the next event starts.
|
||||
var animation_wait: bool = false
|
||||
## For Update only. If bigger then 0, the portrait will tween to the
|
||||
## new position (if changed) in this time (in seconds).
|
||||
var position_move_time: float = 0.0
|
||||
## The z_index that the portrait should have.
|
||||
var z_index: int = 0
|
||||
## If true, the portrait will be set to mirrored.
|
||||
var mirrored: bool = false
|
||||
## If set, will be passed to the portrait scene.
|
||||
var extra_data: String = ""
|
||||
|
||||
|
||||
### Helpers
|
||||
|
||||
## Indicates if the z_index should be updated.
|
||||
var _update_zindex: bool = false
|
||||
## Used to set the character resource from the unique name identifier and vice versa
|
||||
var _character_from_directory: String:
|
||||
get:
|
||||
if _character_from_directory == '--All--':
|
||||
return '--All--'
|
||||
for item in _character_directory.keys():
|
||||
if _character_directory[item]['resource'] == character:
|
||||
return item
|
||||
break
|
||||
return ""
|
||||
set(value):
|
||||
_character_from_directory = value
|
||||
if value in _character_directory.keys():
|
||||
character = _character_directory[value]['resource']
|
||||
else:
|
||||
character = null
|
||||
## Used by [_character_from_directory]
|
||||
var _character_directory: Dictionary = {}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTION
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
match action_type:
|
||||
ActionTypes.Join:
|
||||
|
||||
if character:
|
||||
if !dialogic.Portraits.is_character_joined(character):
|
||||
var n = dialogic.Portraits.add_portrait(character, portrait, position, mirrored, z_index, extra_data)
|
||||
if n:
|
||||
if animation_name.is_empty():
|
||||
animation_name = DialogicUtil.get_project_setting('dialogic/animations/join_default',
|
||||
get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_in_up.gd'))
|
||||
animation_length = DialogicUtil.get_project_setting('dialogic/animations/join_default_length', 0.5)
|
||||
animation_wait = DialogicUtil.get_project_setting('dialogic/animations/join_default_wait', true)
|
||||
if animation_name:
|
||||
var anim:DialogicAnimation = dialogic.Portraits.animate_portrait(character, animation_name, animation_length, animation_repeats)
|
||||
|
||||
if animation_wait:
|
||||
dialogic.current_state = Dialogic.states.ANIMATING
|
||||
await anim.finished
|
||||
dialogic.current_state = Dialogic.states.IDLE
|
||||
else:
|
||||
dialogic.Portraits.change_portrait(character, portrait)
|
||||
if animation_name.is_empty():
|
||||
animation_length = DialogicUtil.get_project_setting('dialogic/animations/join_default_length', 0.5)
|
||||
dialogic.Portraits.move_portrait(character, position, z_index, false, animation_length)
|
||||
ActionTypes.Leave:
|
||||
if _character_from_directory == '--All--':
|
||||
if animation_name.is_empty():
|
||||
animation_name = DialogicUtil.get_project_setting('dialogic/animations/leave_default',
|
||||
get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_out_down.gd'))
|
||||
animation_length = DialogicUtil.get_project_setting('dialogic/animations/leave_default_length', 0.5)
|
||||
animation_wait = DialogicUtil.get_project_setting('dialogic/animations/leave_default_wait', true)
|
||||
|
||||
if animation_name:
|
||||
for chara in dialogic.Portraits.get_joined_characters():
|
||||
var anim = dialogic.Portraits.animate_portrait(chara, animation_name, animation_length, animation_repeats)
|
||||
|
||||
anim.finished.connect(dialogic.Portraits.remove_portrait.bind(chara))
|
||||
|
||||
if animation_wait:
|
||||
dialogic.current_state = Dialogic.states.ANIMATING
|
||||
await anim.finished
|
||||
dialogic.current_state = Dialogic.states.IDLE
|
||||
|
||||
else:
|
||||
for chara in dialogic.Portraits.get_joined_characters():
|
||||
dialogic.Portraits.remove_portrait(chara)
|
||||
elif character:
|
||||
if dialogic.Portraits.is_character_joined(character):
|
||||
if animation_name.is_empty():
|
||||
animation_name = DialogicUtil.get_project_setting('dialogic/animations/leave_default',
|
||||
get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_out_down.gd'))
|
||||
animation_length = DialogicUtil.get_project_setting('dialogic/animations/leave_default_length', 0.5)
|
||||
animation_wait = DialogicUtil.get_project_setting('dialogic/animations/leave_default_wait', true)
|
||||
|
||||
if animation_name:
|
||||
var anim = dialogic.Portraits.animate_portrait(character, animation_name, animation_length, animation_repeats)
|
||||
|
||||
anim.finished.connect(dialogic.Portraits.remove_portrait.bind(character))
|
||||
|
||||
if animation_wait:
|
||||
dialogic.current_state = Dialogic.states.ANIMATING
|
||||
await anim.finished
|
||||
dialogic.current_state = Dialogic.states.IDLE
|
||||
|
||||
else:
|
||||
dialogic.Portraits.remove_portrait(character)
|
||||
|
||||
ActionTypes.Update:
|
||||
if character:
|
||||
if dialogic.Portraits.is_character_joined(character):
|
||||
dialogic.Portraits.change_portrait(character, portrait, mirrored, z_index, _update_zindex, extra_data)
|
||||
if position != 0:
|
||||
dialogic.Portraits.move_portrait(character, position, z_index, _update_zindex, position_move_time)
|
||||
|
||||
if animation_name:
|
||||
var anim = dialogic.Portraits.animate_portrait(character, animation_name, animation_length, animation_repeats)
|
||||
|
||||
if animation_wait:
|
||||
dialogic.current_state = Dialogic.states.ANIMATING
|
||||
await anim.finished
|
||||
dialogic.current_state = Dialogic.states.IDLE
|
||||
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Character"
|
||||
set_default_color('Color2')
|
||||
event_category = Category.Main
|
||||
event_sorting_index = 2
|
||||
continue_at_end = true
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
func _get_icon() -> Resource:
|
||||
return load(self.get_script().get_path().get_base_dir().path_join('icon_character.png'))
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func to_text() -> String:
|
||||
var result_string := ""
|
||||
|
||||
match action_type:
|
||||
ActionTypes.Join: result_string += "Join "
|
||||
ActionTypes.Leave: result_string += "Leave "
|
||||
ActionTypes.Update: result_string += "Update "
|
||||
|
||||
print('to_string')
|
||||
|
||||
print('character ', character)
|
||||
print('characterfrom ', _character_from_directory)
|
||||
|
||||
if character or _character_from_directory == '--All--':
|
||||
if action_type == ActionTypes.Leave and _character_from_directory == '--All--':
|
||||
result_string += "--All--"
|
||||
else:
|
||||
var name := ""
|
||||
for path in _character_directory.keys():
|
||||
if _character_directory[path]['resource'] == character:
|
||||
name = path
|
||||
break
|
||||
if name.count(" ") > 0:
|
||||
name = '"' + name + '"'
|
||||
result_string += name
|
||||
if !portrait.is_empty() and action_type != ActionTypes.Leave:
|
||||
result_string+= " ("+portrait+")"
|
||||
|
||||
if position and action_type != ActionTypes.Leave:
|
||||
result_string += " "+str(position)
|
||||
if animation_name != "" or z_index != 0 or mirrored != false or position_move_time != 0.0 or extra_data != "":
|
||||
result_string += " ["
|
||||
if animation_name:
|
||||
result_string += 'animation="'+DialogicUtil.pretty_name(animation_name)+'"'
|
||||
|
||||
if animation_length != 0.5:
|
||||
result_string += ' length="'+str(animation_length)+'"'
|
||||
|
||||
if animation_wait:
|
||||
result_string += ' wait="'+str(animation_wait)+'"'
|
||||
|
||||
if animation_repeats != 1:
|
||||
result_string += ' repeat="'+str(animation_repeats)+'"'
|
||||
if z_index != 0:
|
||||
result_string += ' z-index="' + str(z_index) + '"'
|
||||
|
||||
if mirrored:
|
||||
result_string += ' mirrored="' + str(mirrored) + '"'
|
||||
|
||||
if position_move_time != 0:
|
||||
result_string += ' move_time="' + str(position_move_time) + '"'
|
||||
|
||||
if extra_data != "":
|
||||
result_string += ' extra_data="' + extra_data + '"'
|
||||
|
||||
result_string += "]"
|
||||
return result_string
|
||||
|
||||
|
||||
func from_text(string:String) -> void:
|
||||
if Engine.is_editor_hint() == false:
|
||||
_character_directory = Dialogic.character_directory
|
||||
else:
|
||||
_character_directory = self.get_meta("editor_character_directory")
|
||||
|
||||
var regex := RegEx.new()
|
||||
|
||||
# Reference regex without Godot escapes: (?<type>Join|Update|Leave)\s*(")?(?<name>(?(2)[^"\n]*|[^(: \n]*))(?(2)"|)(\W*\((?<portrait>.*)\))?(\s*(?<position>\d))?(\s*\[(?<shortcode>.*)\])?
|
||||
regex.compile("(?<type>Join|Update|Leave)\\s*(\")?(?<name>(?(2)[^\"\\n]*|[^(: \\n]*))(?(2)\"|)(\\W*\\((?<portrait>.*)\\))?(\\s*(?<position>\\d))?(\\s*\\[(?<shortcode>.*)\\])?")
|
||||
|
||||
var result := regex.search(string)
|
||||
|
||||
match result.get_string('type'):
|
||||
"Join":
|
||||
action_type = ActionTypes.Join
|
||||
"Leave":
|
||||
action_type = ActionTypes.Leave
|
||||
"Update":
|
||||
action_type = ActionTypes.Update
|
||||
|
||||
if result.get_string('name').strip_edges():
|
||||
if action_type == ActionTypes.Leave and result.get_string('name').strip_edges() == "--All--":
|
||||
_character_from_directory = '--All--'
|
||||
else:
|
||||
var name := result.get_string('name').strip_edges()
|
||||
|
||||
|
||||
if _character_directory != null:
|
||||
if _character_directory.size() > 0:
|
||||
character = null
|
||||
if _character_directory.has(name):
|
||||
character = _character_directory[name]['resource']
|
||||
else:
|
||||
name = name.replace('"', "")
|
||||
# First do a full search to see if more of the path is there then necessary:
|
||||
for character in _character_directory:
|
||||
if name in _character_directory[character]['full_path']:
|
||||
character = _character_directory[character]['resource']
|
||||
break
|
||||
|
||||
# If it doesn't exist, we'll consider it a guest and create a temporary character
|
||||
if character == null:
|
||||
if Engine.is_editor_hint() == false:
|
||||
character = DialogicCharacter.new()
|
||||
character.display_name = name
|
||||
var entry:Dictionary = {}
|
||||
entry['resource'] = character
|
||||
entry['full_path'] = "runtime://" + name
|
||||
Dialogic.character_directory[name] = entry
|
||||
|
||||
if result.get_string('portrait').strip_edges():
|
||||
portrait = result.get_string('portrait').strip_edges()
|
||||
|
||||
if result.get_string('position'):
|
||||
position = result.get_string('position').to_int()
|
||||
elif action_type == ActionTypes.Update:
|
||||
# Override the normal default if it's an Update
|
||||
position = 0
|
||||
|
||||
if result.get_string('shortcode'):
|
||||
var shortcode_params = parse_shortcode_parameters(result.get_string('shortcode'))
|
||||
animation_name = shortcode_params.get('animation', '')
|
||||
if animation_name != "":
|
||||
if !animation_name.ends_with('.gd'):
|
||||
animation_name = guess_animation_file(animation_name)
|
||||
if !animation_name.ends_with('.gd'):
|
||||
printerr("[Dialogic] Couldn't identify animation '"+animation_name+"'.")
|
||||
animation_name = ""
|
||||
|
||||
var animLength = shortcode_params.get('length', '0.5').to_float()
|
||||
if typeof(animLength) == TYPE_FLOAT:
|
||||
animation_length = animLength
|
||||
else:
|
||||
animation_length = animLength.to_float()
|
||||
|
||||
animation_wait = DialogicUtil.str_to_bool(shortcode_params.get('wait', 'false'))
|
||||
|
||||
#repeat is supported on Update, the other two should not be checking this
|
||||
if action_type == ActionTypes.Update:
|
||||
animation_repeats = shortcode_params.get('repeat', 1).to_int()
|
||||
position_move_time = shortcode_params.get('move_time', 0.0)
|
||||
#move time is only supported on Update, but it isnt part of the animations so its separate
|
||||
if action_type == ActionTypes.Update:
|
||||
if typeof(shortcode_params.get('move_time', 0)) == TYPE_STRING:
|
||||
position_move_time = shortcode_params.get('move_time', 0.0).to_float()
|
||||
|
||||
if typeof(shortcode_params.get('z-index', 0)) == TYPE_STRING:
|
||||
z_index = shortcode_params.get('z-index', 0).to_int()
|
||||
_update_zindex = true
|
||||
mirrored = DialogicUtil.str_to_bool(shortcode_params.get('mirrored', 'false'))
|
||||
extra_data = shortcode_params.get('extra_data', "")
|
||||
|
||||
|
||||
func is_valid_event(string:String) -> bool:
|
||||
if string.begins_with("Join ") or string.begins_with("Leave ") or string.begins_with("Update "):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor() -> void:
|
||||
add_header_edit('action_type', ValueType.FixedOptionSelector, '', '', {
|
||||
'selector_options': [
|
||||
{
|
||||
'label': 'Join',
|
||||
'value': ActionTypes.Join,
|
||||
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/join.svg")
|
||||
},
|
||||
{
|
||||
'label': 'Leave',
|
||||
'value': ActionTypes.Leave,
|
||||
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/leave.svg")
|
||||
},
|
||||
{
|
||||
'label': 'Update',
|
||||
'value': ActionTypes.Update,
|
||||
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/update.svg")
|
||||
}
|
||||
]
|
||||
})
|
||||
add_header_edit('_character_from_directory', ValueType.ComplexPicker, '', '',
|
||||
{'empty_text' : 'Character',
|
||||
'file_extension' : '.dch',
|
||||
'suggestions_func' : get_character_suggestions,
|
||||
'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg")})
|
||||
add_header_button('', _on_character_edit_pressed, 'Edit character', ["Edit", "EditorIcons"], 'character != null and _character_from_directory != "--All--"')
|
||||
|
||||
add_header_edit('portrait', ValueType.ComplexPicker, '', '',
|
||||
{'empty_text' : 'Default',
|
||||
'suggestions_func' : get_portrait_suggestions,
|
||||
'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")},
|
||||
'character != null and !has_no_portraits() and action_type != %s' %ActionTypes.Leave)
|
||||
add_header_edit('position', ValueType.Integer, ' at position', '', {},
|
||||
'character != null and !has_no_portraits() and action_type != %s' %ActionTypes.Leave)
|
||||
|
||||
# Body
|
||||
add_body_edit('animation_name', ValueType.ComplexPicker, 'Animation:', '',
|
||||
{'suggestions_func' : get_animation_suggestions,
|
||||
'editor_icon' : ["Animation", "EditorIcons"],
|
||||
'placeholder' : 'Default',
|
||||
'enable_pretty_name' : true},
|
||||
'should_show_animation_options()')
|
||||
add_body_edit('animation_length', ValueType.Float, 'Length:', '', {},
|
||||
'should_show_animation_options() and !animation_name.is_empty()')
|
||||
add_body_edit('animation_wait', ValueType.Bool, 'Wait for animation to finish:', '', {},
|
||||
'should_show_animation_options() and !animation_name.is_empty()')
|
||||
add_body_edit('animation_repeats', ValueType.Integer, 'Repeat:', '', {},
|
||||
'should_show_animation_options() and !animation_name.is_empty() and action_type == %s)' %ActionTypes.Update)
|
||||
add_body_edit('z_index', ValueType.Integer, 'portrait z-index:', "",{},
|
||||
'action_type != %s' %ActionTypes.Leave)
|
||||
add_body_edit('mirrored', ValueType.Bool, 'mirrored:', "",{},
|
||||
'action_type != %s' %ActionTypes.Leave)
|
||||
add_body_edit('position_move_time', ValueType.Float, 'Transiton time to change position:', '', {},
|
||||
'action_type == %s' %ActionTypes.Update)
|
||||
|
||||
|
||||
func should_show_animation_options() -> bool:
|
||||
return (character != null and !character.portraits.is_empty()) or _character_from_directory == '--All--'
|
||||
|
||||
func has_no_portraits() -> bool:
|
||||
return character and character.portraits.is_empty()
|
||||
|
||||
|
||||
func get_character_suggestions(search_text:String) -> Dictionary:
|
||||
var suggestions := {}
|
||||
|
||||
#override the previous _character_directory with the meta, specifically for searching otherwise new nodes wont work
|
||||
_character_directory = Engine.get_meta('dialogic_character_directory')
|
||||
|
||||
var icon = load("res://addons/dialogic/Editor/Images/Resources/character.svg")
|
||||
|
||||
suggestions['(No one)'] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
|
||||
if action_type == ActionTypes.Leave:
|
||||
suggestions['ALL'] = {'value':'--All--', 'tooltip':'All currently joined characters leave', 'editor_icon':["GuiEllipsis", "EditorIcons"]}
|
||||
for resource in _character_directory.keys():
|
||||
suggestions[resource] = {'value': resource, 'tooltip': _character_directory[resource]['full_path'], 'icon': icon.duplicate()}
|
||||
return suggestions
|
||||
|
||||
|
||||
func get_portrait_suggestions(search_text:String) -> Dictionary:
|
||||
var suggestions := {}
|
||||
var icon = load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")
|
||||
if action_type == ActionTypes.Update:
|
||||
suggestions["Don't Change"] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
|
||||
if action_type == ActionTypes.Join:
|
||||
suggestions["Default portrait"] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
|
||||
if character != null:
|
||||
for portrait in character.portraits:
|
||||
suggestions[portrait] = {'value':portrait, 'icon':icon.duplicate()}
|
||||
return suggestions
|
||||
|
||||
|
||||
func get_animation_suggestions(search_text:String) -> Dictionary:
|
||||
var suggestions := {}
|
||||
|
||||
match action_type:
|
||||
ActionTypes.Join, ActionTypes.Leave:
|
||||
suggestions['Default'] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
|
||||
ActionTypes.Update:
|
||||
suggestions['None'] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
|
||||
|
||||
for anim in list_animations():
|
||||
match action_type:
|
||||
ActionTypes.Join:
|
||||
if '_in' in anim.get_file():
|
||||
suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'editor_icon':["Animation", "EditorIcons"]}
|
||||
ActionTypes.Leave:
|
||||
if '_out' in anim.get_file():
|
||||
suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'editor_icon':["Animation", "EditorIcons"]}
|
||||
ActionTypes.Update:
|
||||
if not ('_in' in anim.get_file() or '_out' in anim.get_file()):
|
||||
suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'editor_icon':["Animation", "EditorIcons"]}
|
||||
continue
|
||||
return suggestions
|
||||
|
||||
|
||||
func list_animations() -> Array:
|
||||
var list := DialogicUtil.listdir(get_script().resource_path.get_base_dir().path_join('DefaultAnimations'), true, false, true)
|
||||
list.append_array(DialogicUtil.listdir(DialogicUtil.get_project_setting('dialogic/animations/custom_folder', 'res://addons/dialogic_additions/Animations'), true, false, true))
|
||||
return list
|
||||
|
||||
|
||||
func guess_animation_file(animation_name: String) -> String:
|
||||
for file in list_animations():
|
||||
if DialogicUtil.pretty_name(animation_name) == DialogicUtil.pretty_name(file):
|
||||
return file
|
||||
return animation_name
|
||||
|
||||
func _on_character_edit_pressed() -> void:
|
||||
var editor_manager := _editor_node.find_parent('EditorsManager')
|
||||
if editor_manager:
|
||||
editor_manager.edit_resource(character)
|
||||
111
Sprout/addons/dialogic/Events/Character/event_position.gd
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
@tool
|
||||
class_name DialogicPositionEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that allows moving of positions (and characters that are on that position).
|
||||
## Requires the Portraits subsystem to be present!
|
||||
|
||||
enum ActionTypes {SetRelative, SetAbsolute, Reset, ResetAll}
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## The type of action: SetRelative, SetAbsolute, Reset, ResetAll
|
||||
var action_type := ActionTypes.SetRelative
|
||||
## The position that should be affected
|
||||
var position: int = 0
|
||||
## A vector representing a relative change or an absolute position (for SetRelative and SetAbsolute)
|
||||
var vector: Vector2 = Vector2()
|
||||
## The time the tweening will take.
|
||||
var movement_time: float = 0
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
func _execute() -> void:
|
||||
# If for some someone sets it to 0, ignore it entirely. 0 position is used for Update to indicate no change.
|
||||
if action_type != ActionTypes.ResetAll and position == 0:
|
||||
finish()
|
||||
return
|
||||
|
||||
match action_type:
|
||||
ActionTypes.SetRelative:
|
||||
dialogic.Portraits.move_portrait_position(position, vector, true, movement_time)
|
||||
ActionTypes.SetAbsolute:
|
||||
dialogic.Portraits.move_portrait_position(position, vector, false, movement_time)
|
||||
ActionTypes.ResetAll:
|
||||
dialogic.Portraits.reset_portrait_positions(movement_time)
|
||||
ActionTypes.Reset:
|
||||
dialogic.Portraits.reset_portrait_position(position, movement_time)
|
||||
|
||||
finish()
|
||||
|
||||
|
||||
##############################################file_path##################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "position"
|
||||
set_default_color('Color2')
|
||||
event_category = Category.Main
|
||||
event_sorting_index = 2
|
||||
continue_at_end = true
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
func _get_icon() -> Resource:
|
||||
return load(self.get_script().get_path().get_base_dir().path_join('icon_position.png'))
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "update_position"
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"action" : {"sproperty": "action_type", "default": ActionTypes.SetRelative,
|
||||
"suggestions": func(): return {"Set Relative":{'value':'0'}, "Set Absolute":{'value':'1'}, "Reset":{'value':'2'}, "Reset All":{'value':'3'}}},
|
||||
"position" : {"property": "position", "default": 0},
|
||||
"vector" : {"property": "vector", "default": Vector2()},
|
||||
"time" : {"property": "movement_time", "default": 0},
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('action_type', ValueType.FixedOptionSelector, '', '', {
|
||||
'selector_options': [
|
||||
{
|
||||
'label': 'Change',
|
||||
'value': ActionTypes.SetRelative,
|
||||
},
|
||||
{
|
||||
'label': 'Set',
|
||||
'value': ActionTypes.SetAbsolute,
|
||||
},
|
||||
{
|
||||
'label': 'Reset',
|
||||
'value': ActionTypes.Reset,
|
||||
},
|
||||
{
|
||||
'label': 'Reset All',
|
||||
'value': ActionTypes.ResetAll,
|
||||
}
|
||||
]
|
||||
})
|
||||
add_header_edit("position", ValueType.Integer, "position", '', {},
|
||||
'action_type != ActionTypes.ResetAll')
|
||||
add_header_label('to (absolute)', 'action_type == ActionTypes.SetAbsolute')
|
||||
add_header_label('by (relative)', 'action_type == ActionTypes.SetRelative')
|
||||
add_header_edit("vector", ValueType.Vector2, "", '', {},
|
||||
'action_type != ActionTypes.Reset and action_type != ActionTypes.ResetAll')
|
||||
add_body_edit("movement_time", ValueType.Float, "AnimationTime:", "(0 for instant)")
|
||||
34
Sprout/addons/dialogic/Events/Character/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dn5jx2ucynfio"
|
||||
path="res://.godot/imported/icon.png-a6ef7c3eeb0fb100c7d0b0c505ea4b6f.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Character/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-a6ef7c3eeb0fb100c7d0b0c505ea4b6f.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
Sprout/addons/dialogic/Events/Character/icon_character.png
Normal file
|
After Width: | Height: | Size: 569 B |
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b1i3ttk36kghm"
|
||||
path="res://.godot/imported/icon_character.png-93ea754ecc6f43d01c1116ffee9f80e3.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Character/icon_character.png"
|
||||
dest_files=["res://.godot/imported/icon_character.png-93ea754ecc6f43d01c1116ffee9f80e3.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
Sprout/addons/dialogic/Events/Character/icon_position.png
Normal file
|
After Width: | Height: | Size: 1,017 B |
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cu6cj55m6dr78"
|
||||
path="res://.godot/imported/icon_position.png-18fc8e57ed28c84781c862b6ca4492f7.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Character/icon_position.png"
|
||||
dest_files=["res://.godot/imported/icon_position.png-18fc8e57ed28c84781c862b6ca4492f7.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
14
Sprout/addons/dialogic/Events/Character/index.gd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_character.gd'), this_folder.path_join('event_position.gd')]
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
return [{'name':'Portraits', 'script':this_folder.path_join('subsystem_portraits.gd')}]
|
||||
|
||||
|
||||
func _get_settings_pages() -> Array:
|
||||
return [this_folder.path_join('settings_portraits.tscn')]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class_name DialogicNode_PortraitHolder
|
||||
extends CanvasLayer
|
||||
|
||||
|
||||
func _ready():
|
||||
add_to_group('dialogic_portrait_holder')
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
class_name DialogicNode_PortraitPosition
|
||||
extends Marker2D
|
||||
|
||||
## Used to identify positions in the [DialogicCharacterEvent].
|
||||
@export var position_index = 0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group('dialogic_portrait_position')
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
var current_animation_path = ""
|
||||
|
||||
func _ready():
|
||||
%JoinDefault.get_suggestions_func = get_join_animation_suggestions
|
||||
%JoinDefault.enable_pretty_name = true
|
||||
%LeaveDefault.get_suggestions_func = get_leave_animation_suggestions
|
||||
%LeaveDefault.enable_pretty_name = true
|
||||
|
||||
func refresh():
|
||||
%CustomAnimationsFolderOpener.icon = get_theme_icon("Folder", "EditorIcons")
|
||||
%CustomAnimationsFolder.text = DialogicUtil.get_project_setting('dialogic/animations/custom_folder', 'res://addons/dialogic_additions/Animations')
|
||||
%PortraitMode.select(DialogicUtil.get_project_setting('dialogic/portrait_mode', 0))
|
||||
|
||||
%JoinDefault.resource_icon = get_theme_icon("Animation", "EditorIcons")
|
||||
%LeaveDefault.resource_icon = get_theme_icon("Animation", "EditorIcons")
|
||||
%JoinDefault.set_value(DialogicUtil.pretty_name(DialogicUtil.get_project_setting('dialogic/animations/join_default',
|
||||
get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_in_up.gd'))))
|
||||
%LeaveDefault.set_value(DialogicUtil.get_project_setting('dialogic/animations/leave_default',
|
||||
get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_out_down.gd')))
|
||||
%JoinDefaultLength.set_value(DialogicUtil.get_project_setting('dialogic/animations/join_default_length', 0.5))
|
||||
%LeaveDefaultLength.set_value(DialogicUtil.get_project_setting('dialogic/animations/leave_default_length', 0.5))
|
||||
%LeaveDefaultWait.button_pressed = DialogicUtil.get_project_setting('dialogic/animations/leave_default_wait', true)
|
||||
%JoinDefaultWait.button_pressed = DialogicUtil.get_project_setting('dialogic/animations/join_default_wait', true)
|
||||
|
||||
|
||||
func _on_LeaveDefault_value_changed(property_name, value):
|
||||
ProjectSettings.set_setting('dialogic/animations/leave_default', value)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_JoinDefault_value_changed(property_name, value):
|
||||
ProjectSettings.set_setting('dialogic/animations/join_default', value)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_JoinDefaultLength_value_changed(value):
|
||||
ProjectSettings.set_setting('dialogic/animations/join_default_length', value)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_LeaveDefaultLength_value_changed(value):
|
||||
ProjectSettings.set_setting('dialogic/animations/leave_default_length', value)
|
||||
ProjectSettings.save()
|
||||
|
||||
func _on_JoinDefaultWait_toggled(button_pressed):
|
||||
ProjectSettings.set_setting('dialogic/animations/join_default_wait', button_pressed)
|
||||
ProjectSettings.save()
|
||||
|
||||
func _on_LeaveDefaultWait_toggled(button_pressed):
|
||||
ProjectSettings.set_setting('dialogic/animations/leave_default_wait', button_pressed)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func get_join_animation_suggestions(search_text:String) -> Dictionary:
|
||||
var suggestions = {}
|
||||
for anim in list_animations():
|
||||
if '_in' in anim.get_file():
|
||||
suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'icon':get_theme_icon('Animation', 'EditorIcons')}
|
||||
return suggestions
|
||||
|
||||
func get_leave_animation_suggestions(search_text:String) -> Dictionary:
|
||||
var suggestions = {}
|
||||
for anim in list_animations():
|
||||
if '_out' in anim.get_file():
|
||||
suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'icon':get_theme_icon('Animation', 'EditorIcons')}
|
||||
return suggestions
|
||||
|
||||
func list_animations() -> Array:
|
||||
var list = DialogicUtil.listdir(get_script().resource_path.get_base_dir().path_join('DefaultAnimations'), true, false, true)
|
||||
list.append_array(DialogicUtil.listdir(DialogicUtil.get_project_setting('dialogic/animations/custom_folder', 'res://addons/dialogic_additions/Animations'), true, false, true))
|
||||
return list
|
||||
|
||||
|
||||
func _on_CustomAnimationsFolderOpener_pressed():
|
||||
find_parent('EditorView').godot_file_dialog(custom_anims_folder_selected, '', EditorFileDialog.FILE_MODE_OPEN_DIR, 'Select custom animation folder')
|
||||
|
||||
func custom_anims_folder_selected(path):
|
||||
%CustomAnimationsFolder.text = path
|
||||
ProjectSettings.set_setting('dialogic/animations/custom_folder', path)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_PortraitMode_item_selected(index):
|
||||
ProjectSettings.set_setting('dialogic/portrait_mode', index)
|
||||
ProjectSettings.save()
|
||||
232
Sprout/addons/dialogic/Events/Character/settings_portraits.tscn
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
[gd_scene load_steps=7 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Character/settings_portraits.gd" id="2"]
|
||||
[ext_resource type="StyleBox" uid="uid://dmsjhgv22dns8" path="res://addons/dialogic/Editor/Common/TitleBgStylebox.tres" id="2_i6l15"]
|
||||
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="3"]
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/HintLabelStylingScript.gd" id="3_jknrx"]
|
||||
|
||||
[sub_resource type="Image" id="Image_t2ykh"]
|
||||
data = {
|
||||
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
"format": "LumAlpha8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="2"]
|
||||
image = SubResource("Image_t2ykh")
|
||||
|
||||
[node name="Portraits" type="HBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="General" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_right = 650.0
|
||||
offset_bottom = 648.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Title" type="Label" parent="General"]
|
||||
layout_mode = 2
|
||||
offset_right = 650.0
|
||||
offset_bottom = 36.0
|
||||
theme_override_styles/normal = ExtResource("2_i6l15")
|
||||
text = "Portrait Settings"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="General"]
|
||||
layout_mode = 2
|
||||
offset_top = 40.0
|
||||
offset_right = 650.0
|
||||
offset_bottom = 71.0
|
||||
|
||||
[node name="Label" type="Label" parent="General/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
offset_top = 2.0
|
||||
offset_right = 460.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Portraits mode"
|
||||
|
||||
[node name="PortraitMode" type="OptionButton" parent="General/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 464.0
|
||||
offset_right = 650.0
|
||||
offset_bottom = 31.0
|
||||
item_count = 2
|
||||
selected = 0
|
||||
popup/item_0/text = "Visual Novel Mode"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "RPG Mode"
|
||||
popup/item_1/id = 1
|
||||
|
||||
[node name="Info" type="Label" parent="General"]
|
||||
layout_mode = 2
|
||||
offset_top = 75.0
|
||||
offset_right = 650.0
|
||||
offset_bottom = 205.0
|
||||
text = "Visual Novel: Multiple characters can show up in different positions at the same time. They have to be joined and leaved manually.
|
||||
|
||||
RPG: Only the character that speaks is visible (always in the same location). Does not have to be joined/left manually. Uses only position_idx 1."
|
||||
autowrap_mode = 3
|
||||
script = ExtResource("3_jknrx")
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="."]
|
||||
layout_mode = 2
|
||||
offset_left = 654.0
|
||||
offset_right = 658.0
|
||||
offset_bottom = 648.0
|
||||
|
||||
[node name="Animations" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_left = 662.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 648.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.5
|
||||
|
||||
[node name="Title" type="Label" parent="Animations"]
|
||||
layout_mode = 2
|
||||
offset_right = 490.0
|
||||
offset_bottom = 36.0
|
||||
theme_override_styles/normal = ExtResource("2_i6l15")
|
||||
text = "Animations
|
||||
"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Animations"]
|
||||
layout_mode = 2
|
||||
offset_top = 40.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 71.0
|
||||
|
||||
[node name="Label2" type="Label" parent="Animations/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
offset_top = 2.0
|
||||
offset_right = 229.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Custom anims folder"
|
||||
|
||||
[node name="CustomAnimationsFolder" type="LineEdit" parent="Animations/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 233.0
|
||||
offset_right = 462.0
|
||||
offset_bottom = 31.0
|
||||
size_flags_horizontal = 3
|
||||
caret_blink = true
|
||||
|
||||
[node name="CustomAnimationsFolderOpener" type="Button" parent="Animations/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 466.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 31.0
|
||||
icon = SubResource("2")
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Animations"]
|
||||
layout_mode = 2
|
||||
offset_top = 75.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 79.0
|
||||
|
||||
[node name="Title2" type="Label" parent="Animations"]
|
||||
layout_mode = 2
|
||||
offset_top = 83.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 119.0
|
||||
theme_override_styles/normal = ExtResource("2_i6l15")
|
||||
text = "Default Animations
|
||||
"
|
||||
|
||||
[node name="DefaultIn" type="HBoxContainer" parent="Animations"]
|
||||
layout_mode = 2
|
||||
offset_top = 123.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 156.0
|
||||
|
||||
[node name="Label3" type="Label" parent="Animations/DefaultIn"]
|
||||
layout_mode = 2
|
||||
offset_top = 3.0
|
||||
offset_right = 107.0
|
||||
offset_bottom = 29.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Default Join"
|
||||
|
||||
[node name="JoinDefault" parent="Animations/DefaultIn" instance=ExtResource("3")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
anchors_preset = 0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_left = 111.0
|
||||
offset_right = 311.0
|
||||
offset_bottom = 33.0
|
||||
|
||||
[node name="JoinDefaultLength" type="SpinBox" parent="Animations/DefaultIn"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 315.0
|
||||
offset_right = 398.0
|
||||
offset_bottom = 33.0
|
||||
step = 0.1
|
||||
|
||||
[node name="JoinDefaultWait" type="CheckButton" parent="Animations/DefaultIn"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 402.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 33.0
|
||||
text = "Wait:"
|
||||
|
||||
[node name="DefaultOut" type="HBoxContainer" parent="Animations"]
|
||||
layout_mode = 2
|
||||
offset_top = 160.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 193.0
|
||||
|
||||
[node name="Label4" type="Label" parent="Animations/DefaultOut"]
|
||||
layout_mode = 2
|
||||
offset_top = 3.0
|
||||
offset_right = 107.0
|
||||
offset_bottom = 29.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Default Leave"
|
||||
|
||||
[node name="LeaveDefault" parent="Animations/DefaultOut" instance=ExtResource("3")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
anchors_preset = 0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_left = 111.0
|
||||
offset_right = 311.0
|
||||
offset_bottom = 33.0
|
||||
|
||||
[node name="LeaveDefaultLength" type="SpinBox" parent="Animations/DefaultOut"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 315.0
|
||||
offset_right = 398.0
|
||||
offset_bottom = 33.0
|
||||
step = 0.1
|
||||
|
||||
[node name="LeaveDefaultWait" type="CheckButton" parent="Animations/DefaultOut"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 402.0
|
||||
offset_right = 490.0
|
||||
offset_bottom = 33.0
|
||||
text = "Wait:"
|
||||
|
||||
[connection signal="item_selected" from="General/HBoxContainer/PortraitMode" to="." method="_on_PortraitMode_item_selected"]
|
||||
[connection signal="pressed" from="Animations/HBoxContainer/CustomAnimationsFolderOpener" to="." method="_on_CustomAnimationsFolderOpener_pressed"]
|
||||
[connection signal="value_changed" from="Animations/DefaultIn/JoinDefault" to="." method="_on_JoinDefault_value_changed"]
|
||||
[connection signal="value_changed" from="Animations/DefaultIn/JoinDefaultLength" to="." method="_on_JoinDefaultLength_value_changed"]
|
||||
[connection signal="toggled" from="Animations/DefaultIn/JoinDefaultWait" to="." method="_on_JoinDefaultWait_toggled"]
|
||||
[connection signal="value_changed" from="Animations/DefaultOut/LeaveDefault" to="." method="_on_LeaveDefault_value_changed"]
|
||||
[connection signal="value_changed" from="Animations/DefaultOut/LeaveDefaultLength" to="." method="_on_LeaveDefaultLength_value_changed"]
|
||||
[connection signal="toggled" from="Animations/DefaultOut/LeaveDefaultWait" to="." method="_on_LeaveDefaultWait_toggled"]
|
||||
306
Sprout/addons/dialogic/Events/Character/subsystem_portraits.gd
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
extends DialogicSubsystem
|
||||
|
||||
## Subsystem that manages portraits and portrait positions.
|
||||
|
||||
## The default portrait scene.
|
||||
var default_portrait_scene = load(get_script().resource_path.get_base_dir().path_join('default_portrait.tscn'))
|
||||
## Temporarily stores the default positions.
|
||||
var _default_positions: Dictionary = {}
|
||||
## Stores the current positions.
|
||||
var current_positions: Dictionary = {}
|
||||
|
||||
## A reference to the current [DialogicNode_PortraitHolder].
|
||||
var _portrait_holder_reference: Node = null
|
||||
|
||||
####################################################################################################
|
||||
## STATE
|
||||
####################################################################################################
|
||||
|
||||
func clear_game_state():
|
||||
for character in dialogic.current_state_info.get('portraits', {}).keys():
|
||||
remove_portrait(load(character))
|
||||
dialogic.current_state_info['portraits'] = {}
|
||||
|
||||
|
||||
func load_game_state():
|
||||
for character_path in dialogic.current_state_info['portraits']:
|
||||
add_portrait(
|
||||
load(character_path),
|
||||
dialogic.current_state_info['portraits'][character_path].portrait, dialogic.current_state_info['portraits'][character_path].position_index,
|
||||
false)
|
||||
|
||||
|
||||
func pause() -> void:
|
||||
for portrait in dialogic.current_state_info['portraits'].values():
|
||||
if portrait.has('animation_node'):
|
||||
portrait.animation_node.pause()
|
||||
|
||||
|
||||
func resume() -> void:
|
||||
for portrait in dialogic.current_state_info['portraits'].values():
|
||||
if portrait.has('animation_node'):
|
||||
portrait.animation_node.resume()
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## MAIN METHODS
|
||||
####################################################################################################
|
||||
|
||||
## Joins a character and then calls change_portrait().
|
||||
func add_portrait(character:DialogicCharacter, portrait:String, position_idx:int, mirrored: bool = false, z_index: int = 0, extra_data:String = "") -> Node:
|
||||
var character_node = null
|
||||
|
||||
if portrait.is_empty():
|
||||
portrait = character.default_portrait
|
||||
|
||||
if not character:
|
||||
print_debug('[DialogicError] Cannot call add_portrait() with null character.')
|
||||
return null
|
||||
if not portrait in character.portraits:
|
||||
print_debug("[DialogicError] Tried joining ",character.display_name, " wit not-existing portrait '", portrait, "'. Will use default portrait instead.")
|
||||
portrait = character.default_portrait
|
||||
if portrait.is_empty():
|
||||
print_debug("[DialogicError] Character ",character.display_name, " has no default portrait to use.")
|
||||
return null
|
||||
|
||||
check_positions_and_holder()
|
||||
|
||||
character_node = Node2D.new()
|
||||
character_node.name = character.get_character_name()
|
||||
character_node.position = current_positions[position_idx]
|
||||
character_node.z_index = z_index
|
||||
character_node.set_meta("position", position_idx)
|
||||
_portrait_holder_reference.add_child(character_node)
|
||||
|
||||
if character_node:
|
||||
dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_index':position_idx}
|
||||
if portrait:
|
||||
change_portrait(character, portrait, mirrored, z_index, false, extra_data)
|
||||
|
||||
return character_node
|
||||
|
||||
## Changes the portrait of a character. Only works with characters that joined previously.
|
||||
func change_portrait(character:DialogicCharacter, portrait:String, mirrored:bool = false, z_index: int = 0, update_zindex:bool = false, extra_data:String = "") -> void:
|
||||
if not character or not is_character_joined(character):
|
||||
print_debug('[DialogicError] Cannot change portrait of null/not joined character.')
|
||||
return null
|
||||
|
||||
if portrait.is_empty():
|
||||
portrait = character.default_portrait
|
||||
|
||||
if not portrait in character.portraits.keys():
|
||||
print_debug('[Dialogic] Change to not-existing portrait will be ignored!')
|
||||
return
|
||||
|
||||
var char_node :Node = dialogic.current_state_info.portraits[character.resource_path].node
|
||||
|
||||
if update_zindex:
|
||||
char_node.z_index = z_index
|
||||
|
||||
# path to the scene to use
|
||||
var scene_path :String = character.portraits[portrait].get('scene', '')
|
||||
|
||||
var portrait_node = null
|
||||
|
||||
# check if the scene is the same as the currently loaded scene
|
||||
if (char_node.get_child_count() and
|
||||
character.portraits[dialogic.current_state_info['portraits'][character.resource_path]['portrait']].get('scene', '') == scene_path and
|
||||
# also check if the scene supports changing to the given portrait
|
||||
(!char_node.get_child(0).has_method('_should_do_portrait_update') or char_node.get_child(0)._should_do_portrait_update(character, portrait))):
|
||||
portrait_node = char_node.get_child(0)
|
||||
else:
|
||||
# remove previous portrait
|
||||
if char_node.get_child_count():
|
||||
char_node.get_child(0).queue_free()
|
||||
|
||||
if scene_path.is_empty():
|
||||
portrait_node = default_portrait_scene.instantiate()
|
||||
else:
|
||||
var p = load(scene_path)
|
||||
if p:
|
||||
portrait_node = p.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.')
|
||||
|
||||
if portrait_node:
|
||||
portrait_node.position = character.offset + character.portraits[portrait].get('offset', Vector2())
|
||||
|
||||
# ignore the character scale on custom portraits that have 'ignore_char_scale' set to true
|
||||
if scene_path.is_empty() or !character.portraits[portrait].get('ignore_char_scale', false):
|
||||
portrait_node.scale = Vector2(1,1)*character.scale * character.portraits[portrait].get('scale', 1)
|
||||
else:
|
||||
portrait_node.scale = Vector2(1,1)*character.portraits[portrait].get('scale', 1)
|
||||
|
||||
for property in character.portraits[portrait].get('export_overrides', {}).keys():
|
||||
portrait_node.set(property, str_to_var(character.portraits[portrait]['export_overrides'][property]))
|
||||
|
||||
if portrait_node.has_method('_update_portrait'):
|
||||
portrait_node._update_portrait(character, portrait)
|
||||
if portrait_node.has_method('_set_mirror'):
|
||||
portrait_node._set_mirror(mirrored)
|
||||
if portrait_node.has_method('_set_extra_data'):
|
||||
portrait_node._set_extra_data(extra_data)
|
||||
|
||||
if !portrait_node.is_inside_tree():
|
||||
char_node.add_child(portrait_node)
|
||||
dialogic.current_state_info['portraits'][character.resource_path]['portrait'] = portrait
|
||||
|
||||
## Animates the portrait of the given character with the given animation.
|
||||
func animate_portrait(character:DialogicCharacter, animation_path:String, length:float, repeats = 1) -> DialogicAnimation:
|
||||
if not character or not is_character_joined(character):
|
||||
print_debug('[DialogicError] Cannot animate portrait of null/not joined character.')
|
||||
return null
|
||||
|
||||
var char_node = dialogic.current_state_info['portraits'][character.resource_path].node
|
||||
|
||||
if dialogic.current_state_info['portraits'][character.resource_path].get('animation_node', null):
|
||||
if is_instance_valid(dialogic.current_state_info['portraits'][character.resource_path].animation_node):
|
||||
dialogic.current_state_info['portraits'][character.resource_path].animation_node.queue_free()
|
||||
var anim_script = load(animation_path)
|
||||
var anim_node = Node.new()
|
||||
anim_node.set_script(anim_script)
|
||||
anim_node = (anim_node as DialogicAnimation)
|
||||
anim_node.node = char_node
|
||||
anim_node.orig_pos = char_node.position
|
||||
anim_node.end_position = char_node.position
|
||||
anim_node.time = length
|
||||
anim_node.repeats = repeats
|
||||
add_child(anim_node)
|
||||
anim_node.animate()
|
||||
dialogic.current_state_info['portraits'][character.resource_path]['animation_node'] = anim_node
|
||||
return anim_node
|
||||
|
||||
|
||||
## Moves the portrait of the given character to the given positions. Also allows updating the z_index.
|
||||
## TODO: Question, why the z_index is set here and in change_portrait and doesn't have it's own method.
|
||||
func move_portrait(character:DialogicCharacter, position_idx:int, z_index:int = 0, update_zindex:bool = false, time:float = 0.0):
|
||||
if not character or not is_character_joined(character):
|
||||
print_debug('[DialogicError] Cannot move portrait of null/not joined character.')
|
||||
return null
|
||||
|
||||
var char_node = dialogic.current_state_info.portraits[character.resource_path].node
|
||||
|
||||
if update_zindex:
|
||||
char_node.z_index = z_index
|
||||
|
||||
char_node.set_meta('position', position_idx)
|
||||
|
||||
if time == 0.0:
|
||||
char_node.position = current_positions[position_idx]
|
||||
else:
|
||||
var tween = char_node.create_tween()
|
||||
tween.tween_property(char_node, "position", current_positions[position_idx], time)
|
||||
|
||||
dialogic.current_state_info.portraits[character.resource_path].position_index = position_idx
|
||||
|
||||
|
||||
func remove_portrait(character:DialogicCharacter) -> void:
|
||||
dialogic.current_state_info['portraits'][character.resource_path].node.queue_free()
|
||||
dialogic.current_state_info['portraits'].erase(character.resource_path)
|
||||
|
||||
|
||||
## Creates additional positions either from timeline or at runtime
|
||||
## If it's an existing position, will move that position to the coordinates instead
|
||||
## There's no need to actually remove them once added, but saves will need to track position updates as well, so the whole current_positions array will need to be saved
|
||||
## This will always be an absolute value for new positions, existing positions will be updated as absolute values by this
|
||||
func add_portrait_position(position_number: int, position:Vector2) -> void:
|
||||
|
||||
check_positions_and_holder()
|
||||
|
||||
if position_number in current_positions:
|
||||
move_portrait_position(position_number, position)
|
||||
else:
|
||||
# Add to both current and default positions
|
||||
_default_positions[position_number] = position
|
||||
current_positions[position_number] = position
|
||||
|
||||
func reset_portrait_positions(time:float = 0.0) -> void:
|
||||
for position in current_positions:
|
||||
move_portrait_position(position, _default_positions[position], false, time)
|
||||
|
||||
func reset_portrait_position(position:int, time:float = 0.0) -> void:
|
||||
move_portrait_position(position, _default_positions[position], false, time)
|
||||
|
||||
|
||||
func move_portrait_position(position_number: int, vector:Vector2, relative:bool = false, time:float = 0.0) -> void:
|
||||
check_positions_and_holder()
|
||||
|
||||
if !position_number in current_positions:
|
||||
if !relative:
|
||||
add_portrait_position(position_number, vector)
|
||||
else:
|
||||
print_debug('[DialogicError] Cannot move non-existent position. (Use SetAbsolute to create a new position)')
|
||||
return null
|
||||
|
||||
if !relative:
|
||||
current_positions[position_number] = vector
|
||||
else:
|
||||
current_positions[position_number] += vector
|
||||
|
||||
for child in _portrait_holder_reference.get_children():
|
||||
if child.get_meta('position') == position_number:
|
||||
if time != 0.0:
|
||||
var tween = child.create_tween()
|
||||
tween.tween_property(child, "position", current_positions[position_number], time)
|
||||
else:
|
||||
child.position = current_positions[position_number]
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## HELPERS
|
||||
####################################################################################################
|
||||
|
||||
func is_character_joined(character:DialogicCharacter) -> bool:
|
||||
return character.resource_path in dialogic.current_state_info['portraits']
|
||||
|
||||
func get_joined_characters() -> Array:
|
||||
var chars = []
|
||||
for char_path in dialogic.current_state_info.get('portraits', {}).keys():
|
||||
chars.append(load(char_path))
|
||||
return chars
|
||||
|
||||
func update_rpg_portrait_mode(character:DialogicCharacter = null, portrait:String = "") -> void:
|
||||
if DialogicUtil.get_project_setting('dialogic/portrait_mode', 0) == DialogicCharacterEvent.PortraitModes.RPG:
|
||||
if !Dialogic.current_state_info.has('rpg_last_portrait'):
|
||||
Dialogic.current_state_info['rpg_last_portraits'] = {}
|
||||
|
||||
if character != null:
|
||||
if portrait == "":
|
||||
if Dialogic.current_state_info['rpg_last_portraits'].has(character.resource_path):
|
||||
portrait = Dialogic.current_state_info['rpg_last_portraits'][character.resource_path]
|
||||
else:
|
||||
portrait = character.default_portrait
|
||||
Dialogic.current_state_info['rpg_last_portraits'][character.resource_path] = portrait
|
||||
var char_joined = false
|
||||
for joined_character in dialogic.current_state_info.portraits:
|
||||
if not character or (joined_character != character.resource_path):
|
||||
var AnimationName = DialogicUtil.get_project_setting('dialogic/animations/leave_default',
|
||||
get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_out_down.gd'))
|
||||
var AnimationLength = DialogicUtil.get_project_setting('dialogic/animations/leave_default_length', 0.5)
|
||||
|
||||
var anim = animate_portrait(load(joined_character), AnimationName, AnimationLength)
|
||||
|
||||
anim.finished.connect(remove_portrait.bind(load(joined_character)))
|
||||
else:
|
||||
char_joined = true
|
||||
|
||||
if (not char_joined) and character and portrait in character.portraits:
|
||||
var AnimationName = DialogicUtil.get_project_setting('dialogic/animations/join_default',
|
||||
get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_in_up.gd'))
|
||||
var AnimationLength = DialogicUtil.get_project_setting('dialogic/animations/join_default_length', 0.5)
|
||||
add_portrait(character, portrait, 1, false)
|
||||
var anim = animate_portrait(character, AnimationName, AnimationLength)
|
||||
|
||||
# makes sure positions are listed and can be accessed
|
||||
func check_positions_and_holder() -> void:
|
||||
if _portrait_holder_reference == null and len(get_tree().get_nodes_in_group('dialogic_portrait_holder')) == 0:
|
||||
assert(false, '[Dialogic] If you want to display portraits, you need a PortraitHolder scene!')
|
||||
else:
|
||||
if _portrait_holder_reference == null:
|
||||
_portrait_holder_reference = get_tree().get_first_node_in_group('dialogic_portrait_holder')
|
||||
if _default_positions.size() == 0:
|
||||
for node in get_tree().get_nodes_in_group('dialogic_portrait_position'):
|
||||
_default_positions[node['position_index']] = node['position']
|
||||
|
||||
if current_positions.size() == 0:
|
||||
current_positions = _default_positions.duplicate()
|
||||
153
Sprout/addons/dialogic/Events/Choice/event_choice.gd
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
@tool
|
||||
class_name DialogicChoiceEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that allows adding choices. Needs to go after a text event (or another choices EndBranch).
|
||||
|
||||
enum ElseActions {Hide, Disable, Default}
|
||||
|
||||
|
||||
### Settings
|
||||
## The text that is displayed on the choice button.
|
||||
var text :String = ""
|
||||
## If not empty this condition will determine if this choice is active.
|
||||
var condition: String = ""
|
||||
## Determines what happens if [condition] is false. Default will use the action set in the settings.
|
||||
var else_action: = ElseActions.Default
|
||||
## The text that is displayed if [condition] is false and [else_action] is Disable.
|
||||
## If empty [text] will be used for disabled button as well.
|
||||
var disabled_text: String = ""
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTION
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
# This event is basically a placeholder only used to indicate a position.
|
||||
# It is never really reached. Instead the Subsystem_Choices queries the events
|
||||
# to find the choices that belong to the question.
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Choice"
|
||||
set_default_color('Color3')
|
||||
event_category = Category.Logic
|
||||
event_sorting_index = 0
|
||||
can_contain_events = true
|
||||
needs_parent_event = true
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
# if needs_parent_event is true, this needs to return true if the event is that event
|
||||
func is_expected_parent_event(event:DialogicEvent) -> bool:
|
||||
return event is DialogicTextEvent
|
||||
|
||||
|
||||
# return a control node that should show on the END BRANCH node
|
||||
func get_end_branch_control() -> Control:
|
||||
return load(get_script().resource_path.get_base_dir().path_join('ui_choice_end.tscn')).instantiate()
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func to_text() -> String:
|
||||
var result_string := ""
|
||||
|
||||
result_string = "- "+text.strip_edges()
|
||||
if condition:
|
||||
result_string += " [if "+condition+"]"
|
||||
|
||||
|
||||
var shortcode = '['
|
||||
if else_action == ElseActions.Hide:
|
||||
shortcode += 'else="hide"'
|
||||
elif else_action == ElseActions.Disable:
|
||||
shortcode += 'else="disable"'
|
||||
|
||||
if disabled_text:
|
||||
shortcode += " alt_text="+'"'+disabled_text+'"'
|
||||
|
||||
if len(shortcode) > 1:
|
||||
result_string += shortcode + "]"
|
||||
return result_string
|
||||
|
||||
|
||||
func from_text(string:String) -> void:
|
||||
var regex = RegEx.new()
|
||||
regex.compile('- (?<text>[^\\[]*)(\\[if (?<condition>[^\\]]+)])?\\s?(\\s*\\[(?<shortcode>.*)\\])?')
|
||||
var result = regex.search(string.strip_edges())
|
||||
if result == null:
|
||||
return
|
||||
text = result.get_string('text')
|
||||
condition = result.get_string('condition')
|
||||
if result.get_string('shortcode'):
|
||||
var shortcode_params = parse_shortcode_parameters(result.get_string('shortcode'))
|
||||
else_action = {
|
||||
'default':ElseActions.Default,
|
||||
'hide':ElseActions.Hide,
|
||||
'disable':ElseActions.Disable}.get(shortcode_params.get('else', ''), ElseActions.Default)
|
||||
|
||||
disabled_text = shortcode_params.get('alt_text', '')
|
||||
|
||||
|
||||
func is_valid_event(string:String) -> bool:
|
||||
if string.strip_edges().begins_with("-"):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
################################################################################
|
||||
## TRANSLATIONS
|
||||
################################################################################
|
||||
|
||||
func _get_translatable_properties() -> Array:
|
||||
return ['text', 'disabled_text']
|
||||
|
||||
|
||||
func _get_property_original_translation(property:String) -> String:
|
||||
match property:
|
||||
'text':
|
||||
return text
|
||||
'disabled_text':
|
||||
return disabled_text
|
||||
return ''
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor() -> void:
|
||||
add_header_edit("text", ValueType.SinglelineText)
|
||||
add_body_edit("condition", ValueType.Condition, 'if ')
|
||||
add_body_edit("else_action", ValueType.FixedOptionSelector, 'else ', '', {
|
||||
'selector_options': [
|
||||
{
|
||||
'label': 'Default',
|
||||
'value': ElseActions.Default,
|
||||
},
|
||||
{
|
||||
'label': 'Hide',
|
||||
'value': ElseActions.Hide,
|
||||
},
|
||||
{
|
||||
'label': 'Disable',
|
||||
'value': ElseActions.Disable,
|
||||
}
|
||||
]}, '!condition.is_empty()')
|
||||
add_body_edit("disabled_text", ValueType.SinglelineText, 'Disabled text:', '',
|
||||
{'placeholder':'(Empty for same)'}, 'allow_alt_text()')
|
||||
|
||||
|
||||
func allow_alt_text() -> bool:
|
||||
return condition and (
|
||||
else_action == ElseActions.Disable or
|
||||
(else_action == ElseActions.Default and
|
||||
DialogicUtil.get_project_setting("dialogic/choices/def_false_behaviour", 0) == 1))
|
||||
BIN
Sprout/addons/dialogic/Events/Choice/icon.png
Normal file
|
After Width: | Height: | Size: 621 B |
34
Sprout/addons/dialogic/Events/Choice/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d0p5oj7m0vp15"
|
||||
path="res://.godot/imported/icon.png-5b61c4d2ebb9ccd2a839f9719c30446b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Choice/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-5b61c4d2ebb9ccd2a839f9719c30446b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
14
Sprout/addons/dialogic/Events/Choice/index.gd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_choice.gd')]
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
return [{'name':'Choices', 'script':this_folder.path_join('subsystem_choices.gd')}]
|
||||
|
||||
|
||||
func _get_settings_pages() -> Array:
|
||||
return [this_folder.path_join('settings_choices.tscn')]
|
||||
52
Sprout/addons/dialogic/Events/Choice/node_button_sound.gd
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
class_name DialogicNode_ButtonSound
|
||||
extends AudioStreamPlayer
|
||||
|
||||
## Node that is used for playing sound effects on hover/focus/press of sibling DialogicNode_ChoiceButtons.
|
||||
|
||||
## Sound to be played if one of the sibling ChoiceButtons is pressed.
|
||||
## If sibling ChoiceButton has a sound_pressed set, that is prioritized.
|
||||
@export var sound_pressed:AudioStream
|
||||
## Sound to be played on hover. See [sound_pressed] for more.
|
||||
@export var sound_hover:AudioStream
|
||||
## Sound to be played on focus. See [sound_pressed] for more.
|
||||
@export var sound_focus:AudioStream
|
||||
|
||||
func _ready():
|
||||
add_to_group('dialogic_button_sound')
|
||||
_connect_all_buttons()
|
||||
|
||||
#basic play sound
|
||||
func play_sound(sound) -> void:
|
||||
if sound != null:
|
||||
stream = sound
|
||||
play()
|
||||
|
||||
func _connect_all_buttons():
|
||||
for child in get_parent().get_children():
|
||||
if child is DialogicNode_ChoiceButton:
|
||||
child.button_up.connect(_on_pressed.bind(child.sound_pressed))
|
||||
child.mouse_entered.connect(_on_hover.bind(child.sound_hover))
|
||||
child.focus_entered.connect(_on_focus.bind(child.sound_focus))
|
||||
|
||||
|
||||
#the custom_sound argument comes from the specifec button and get used
|
||||
#if none are found, it uses the above sounds
|
||||
|
||||
func _on_pressed(custom_sound) -> void:
|
||||
if custom_sound != null:
|
||||
play_sound(custom_sound)
|
||||
else:
|
||||
play_sound(sound_pressed)
|
||||
|
||||
func _on_hover(custom_sound) -> void:
|
||||
if custom_sound != null:
|
||||
play_sound(custom_sound)
|
||||
else:
|
||||
play_sound(sound_hover)
|
||||
|
||||
func _on_focus(custom_sound) -> void:
|
||||
if custom_sound != null:
|
||||
play_sound(custom_sound)
|
||||
else:
|
||||
play_sound(sound_focus)
|
||||
|
||||
20
Sprout/addons/dialogic/Events/Choice/node_choice_button.gd
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
class_name DialogicNode_ChoiceButton
|
||||
extends Button
|
||||
|
||||
## Dialogic Node that displays choices.
|
||||
|
||||
## Used to identify what choices to put on. If you leave it at -1, choices will be distributed automatically.
|
||||
@export var choice_index:int = -1
|
||||
|
||||
## Can be set to play this sound when pressed. Requires a sibling DialogicNode_ButtonSound node.
|
||||
@export var sound_pressed: AudioStream
|
||||
## Can be set to play this sound when hovered. Requires a sibling DialogicNode_ButtonSound node.
|
||||
@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
|
||||
|
||||
|
||||
func _ready():
|
||||
add_to_group('dialogic_choice_button')
|
||||
shortcut_in_tooltip = false
|
||||
hide()
|
||||
29
Sprout/addons/dialogic/Events/Choice/settings_choices.gd
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
func refresh() -> void:
|
||||
%Autofocus.button_pressed = DialogicUtil.get_project_setting('dialogic/choices/autofocus_first', true)
|
||||
var delay_value:float = DialogicUtil.get_project_setting('dialogic/choices/delay', 0.2)
|
||||
%Delay.value = delay_value
|
||||
%FalseBehaviour.select(DialogicUtil.get_project_setting('dialogic/choices/def_false_behaviour', 0))
|
||||
%HotkeyType.select(DialogicUtil.get_project_setting('dialogic/choices/hotkey_behaviour', 0))
|
||||
|
||||
|
||||
func _on_Autofocus_toggled(button_pressed: bool) -> void:
|
||||
ProjectSettings.set_setting('dialogic/choices/autofocus_first', button_pressed)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_FalseBehaviour_item_selected(index) -> void:
|
||||
ProjectSettings.set_setting('dialogic/choices/def_false_behaviour', index)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_HotkeyType_item_selected(index) -> void:
|
||||
ProjectSettings.set_setting('dialogic/choices/hotkey_behaviour', index)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_Delay_value_changed(value) -> void:
|
||||
ProjectSettings.set_setting('dialogic/choices/delay', value)
|
||||
ProjectSettings.save()
|
||||
157
Sprout/addons/dialogic/Events/Choice/settings_choices.tscn
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
[gd_scene load_steps=4 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Choice/settings_choices.gd" id="2"]
|
||||
[ext_resource type="StyleBox" uid="uid://dmsjhgv22dns8" path="res://addons/dialogic/Editor/Common/TitleBgStylebox.tres" id="2_cg22x"]
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/HintLabelStylingScript.gd" id="3_mpugv"]
|
||||
|
||||
[node name="Choices" type="HBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_right = 570.0
|
||||
offset_bottom = 648.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Title" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
offset_right = 570.0
|
||||
offset_bottom = 36.0
|
||||
theme_override_styles/normal = ExtResource("2_cg22x")
|
||||
text = "Choice Settings"
|
||||
|
||||
[node name="Autofocus" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
offset_top = 40.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 66.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/Autofocus"]
|
||||
layout_mode = 2
|
||||
offset_right = 542.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Autofocus first choice"
|
||||
|
||||
[node name="Autofocus" type="CheckBox" parent="VBoxContainer/Autofocus"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 546.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 26.0
|
||||
|
||||
[node name="Delay" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
offset_top = 70.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 101.0
|
||||
|
||||
[node name="Label2" type="Label" parent="VBoxContainer/Delay"]
|
||||
layout_mode = 2
|
||||
offset_top = 2.0
|
||||
offset_right = 483.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Delay before choices can be pressed"
|
||||
|
||||
[node name="Delay" type="SpinBox" parent="VBoxContainer/Delay"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 487.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 31.0
|
||||
step = 0.01
|
||||
|
||||
[node name="DefaultFalseBehaviour" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
offset_top = 105.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 136.0
|
||||
|
||||
[node name="Label3" type="Label" parent="VBoxContainer/DefaultFalseBehaviour"]
|
||||
layout_mode = 2
|
||||
offset_top = 2.0
|
||||
offset_right = 466.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Default behaviour for false choices"
|
||||
|
||||
[node name="FalseBehaviour" type="OptionButton" parent="VBoxContainer/DefaultFalseBehaviour"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 470.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 31.0
|
||||
item_count = 2
|
||||
selected = 0
|
||||
popup/item_0/text = "Hide"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Disable"
|
||||
popup/item_1/id = 1
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="."]
|
||||
layout_mode = 2
|
||||
offset_left = 574.0
|
||||
offset_right = 578.0
|
||||
offset_bottom = 648.0
|
||||
|
||||
[node name="VBoxContainer2" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_left = 582.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 648.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Title2" type="Label" parent="VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
offset_right = 570.0
|
||||
offset_bottom = 36.0
|
||||
theme_override_styles/normal = ExtResource("2_cg22x")
|
||||
text = "Choice Hotkeys"
|
||||
|
||||
[node name="HotkeySelection" type="HBoxContainer" parent="VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
offset_top = 40.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 71.0
|
||||
|
||||
[node name="Label4" type="Label" parent="VBoxContainer2/HotkeySelection"]
|
||||
layout_mode = 2
|
||||
offset_top = 2.0
|
||||
offset_right = 428.0
|
||||
offset_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Hotkey type"
|
||||
|
||||
[node name="HotkeyType" type="OptionButton" parent="VBoxContainer2/HotkeySelection"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
offset_left = 432.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 31.0
|
||||
item_count = 2
|
||||
selected = 0
|
||||
popup/item_0/text = "No Hotkeys"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Default (1-9)"
|
||||
popup/item_1/id = 1
|
||||
|
||||
[node name="Info" type="Label" parent="VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
offset_top = 75.0
|
||||
offset_right = 570.0
|
||||
offset_bottom = 127.0
|
||||
size_flags_horizontal = 3
|
||||
text = "You can add more complex hotkeys by editing the \"shortcut\" property of a ChoiceButton. "
|
||||
autowrap_mode = 3
|
||||
script = ExtResource("3_mpugv")
|
||||
|
||||
[connection signal="toggled" from="VBoxContainer/Autofocus/Autofocus" to="." method="_on_Autofocus_toggled"]
|
||||
[connection signal="value_changed" from="VBoxContainer/Delay/Delay" to="." method="_on_Delay_value_changed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/DefaultFalseBehaviour/FalseBehaviour" to="." method="_on_FalseBehaviour_item_selected"]
|
||||
[connection signal="item_selected" from="VBoxContainer2/HotkeySelection/HotkeyType" to="." method="_on_HotkeyType_item_selected"]
|
||||
132
Sprout/addons/dialogic/Events/Choice/subsystem_choices.gd
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
extends DialogicSubsystem
|
||||
|
||||
## Subsystem that manages showing and activating of choices.
|
||||
|
||||
## Used to block choices from being clicked for a couple of seconds (if delay is set in settings).
|
||||
var choice_blocker = Timer.new()
|
||||
|
||||
func _ready():
|
||||
choice_blocker.one_shot = true
|
||||
DialogicUtil.update_timer_process_callback(choice_blocker)
|
||||
add_child(choice_blocker)
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## STATE
|
||||
####################################################################################################
|
||||
|
||||
func clear_game_state():
|
||||
hide_all_choices()
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## MAIN METHODS
|
||||
####################################################################################################
|
||||
|
||||
## Hides all choice buttons.
|
||||
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', self.choice_selected):
|
||||
node.disconnect('button_up', self.choice_selected)
|
||||
|
||||
|
||||
## Lists all current choices and shows buttons.
|
||||
func show_current_choices() -> void:
|
||||
hide_all_choices()
|
||||
var button_idx = 1
|
||||
for choice_index in get_current_choice_indexes():
|
||||
var choice_event = dialogic.current_timeline_events[choice_index]
|
||||
# check if condition is false
|
||||
if not choice_event.condition.is_empty() and not dialogic.execute_condition(choice_event.condition):
|
||||
if choice_event.else_action == DialogicChoiceEvent.ElseActions.Default:
|
||||
choice_event.else_action = DialogicUtil.get_project_setting('dialogic/choices/def_false_behaviour', 0)
|
||||
|
||||
# check what to do in this case
|
||||
if choice_event.else_action == DialogicChoiceEvent.ElseActions.Disable:
|
||||
if !choice_event.disabled_text.is_empty():
|
||||
show_choice(button_idx, choice_event.get_property_translated('disabled_text'), false, choice_index)
|
||||
else:
|
||||
show_choice(button_idx, choice_event.get_property_translated('text'), false, choice_index)
|
||||
button_idx += 1
|
||||
# else just show it
|
||||
else:
|
||||
show_choice(button_idx, choice_event.get_property_translated('text'), true, choice_index)
|
||||
button_idx += 1
|
||||
|
||||
if typeof(DialogicUtil.get_project_setting('dialogic/choices/delay')) != TYPE_FLOAT:
|
||||
choice_blocker.start(DialogicUtil.get_project_setting('dialogic/choices/delay', 0.2).to_float())
|
||||
else:
|
||||
choice_blocker.start(DialogicUtil.get_project_setting('dialogic/choices/delay', 0.2))
|
||||
|
||||
|
||||
## Adds a button with the given text that leads to the given event.
|
||||
func show_choice(button_index:int, text:String, enabled:bool, event_index:int) -> void:
|
||||
var idx = 1
|
||||
for node in get_tree().get_nodes_in_group('dialogic_choice_button'):
|
||||
if !node.get_parent().is_visible_in_tree():
|
||||
continue
|
||||
if (node.choice_index == button_index) or (idx == button_index and node.choice_index == -1):
|
||||
node.show()
|
||||
if dialogic.has_subsystem('VAR'):
|
||||
node.text = dialogic.VAR.parse_variables(text)
|
||||
else:
|
||||
node.text = text
|
||||
|
||||
if idx == 1 and DialogicUtil.get_project_setting('dialogic/choices/autofocus_first', true):
|
||||
node.grab_focus()
|
||||
|
||||
if DialogicUtil.get_project_setting('dialogic/choices/hotkey_behaviour', 0) == 1 and idx < 10:
|
||||
var shortcut = Shortcut.new()
|
||||
var input_key = InputEventKey.new()
|
||||
input_key.scancode = OS.find_keycode_from_string(str(idx))
|
||||
shortcut.shortcut = input_key
|
||||
node.shortcut = shortcut
|
||||
|
||||
node.disabled = not enabled
|
||||
node.button_up.connect(choice_selected.bind(event_index))
|
||||
|
||||
if node.choice_index > 0:
|
||||
idx = node.choice_index
|
||||
idx += 1
|
||||
|
||||
####################################################################################################
|
||||
## HELPERS
|
||||
####################################################################################################
|
||||
func choice_selected(event_index:int) -> void:
|
||||
if Dialogic.paused or not choice_blocker.is_stopped():
|
||||
return
|
||||
hide_all_choices()
|
||||
dialogic.current_state = dialogic.states.IDLE
|
||||
dialogic.handle_event(event_index)
|
||||
|
||||
## QUESTION/CHOICES
|
||||
func is_question(index:int) -> bool:
|
||||
if dialogic.current_timeline_events[index] is DialogicTextEvent:
|
||||
if len(dialogic.current_timeline_events)-1 != index:
|
||||
if dialogic.current_timeline_events[index+1] is DialogicChoiceEvent:
|
||||
return true
|
||||
return false
|
||||
|
||||
func get_current_choice_indexes() -> Array:
|
||||
var choices = []
|
||||
var evt_idx = dialogic.current_event_idx
|
||||
var ignore = 0
|
||||
while true:
|
||||
|
||||
evt_idx += 1
|
||||
if evt_idx >= len(dialogic.current_timeline_events):
|
||||
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
|
||||
return choices
|
||||
11
Sprout/addons/dialogic/Events/Choice/ui_choice_end.gd
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
var parent_resource: DialogicChoiceEvent = null
|
||||
|
||||
func refresh():
|
||||
if parent_resource is DialogicChoiceEvent:
|
||||
show()
|
||||
$Label.text = "End of choice ("+parent_resource.text+")"
|
||||
else:
|
||||
hide()
|
||||
13
Sprout/addons/dialogic/Events/Choice/ui_choice_end.tscn
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Choice/ui_choice_end.gd" id="1_7qd85"]
|
||||
|
||||
[node name="Choice_End" type="HBoxContainer"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("1_7qd85")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
offset_top = 288.0
|
||||
offset_right = 1.0
|
||||
offset_bottom = 311.0
|
||||
59
Sprout/addons/dialogic/Events/Comment/event_comment.gd
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
@tool
|
||||
class_name DialogicCommentEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that does nothing but store a comment string. Will print the comment in debug builds.
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## Content of the comment.
|
||||
var text :String = ""
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
print("[Dialogic Comment] #", text)
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Comment"
|
||||
set_default_color('Color6')
|
||||
event_category = Category.Other
|
||||
event_sorting_index = 0
|
||||
continue_at_end = true
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func to_text() -> String:
|
||||
var result_string = "# "+text
|
||||
return result_string
|
||||
|
||||
|
||||
func from_text(string:String) -> void:
|
||||
text = string.trim_prefix("# ")
|
||||
|
||||
|
||||
func is_valid_event(string:String) -> bool:
|
||||
if string.strip_edges().begins_with('#'):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('text', ValueType.SinglelineText, '#')
|
||||
BIN
Sprout/addons/dialogic/Events/Comment/icon.png
Normal file
|
After Width: | Height: | Size: 398 B |
34
Sprout/addons/dialogic/Events/Comment/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://hbx10ru6sxfb"
|
||||
path="res://.godot/imported/icon.png-0db0f9a86499a14eda3ca834beda5746.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Comment/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-0db0f9a86499a14eda3ca834beda5746.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
6
Sprout/addons/dialogic/Events/Comment/index.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_comment.gd')]
|
||||
126
Sprout/addons/dialogic/Events/Condition/event_condition.gd
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
@tool
|
||||
class_name DialogicConditionEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that allows branching a timeline based on a condition.
|
||||
|
||||
enum ConditionTypes {If, Elif, Else}
|
||||
|
||||
### Settings
|
||||
## 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: String = ""
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
if condition_type == ConditionTypes.Else:
|
||||
finish()
|
||||
return
|
||||
|
||||
if condition.is_empty(): condition = "true"
|
||||
|
||||
var result = dialogic.execute_condition(condition)
|
||||
if not result:
|
||||
var idx = dialogic.current_event_idx
|
||||
var ignore = 1
|
||||
while true:
|
||||
idx += 1
|
||||
if not dialogic.current_timeline.get_event(idx):
|
||||
break
|
||||
if ignore == 0 and dialogic.current_timeline.get_event(idx) is DialogicConditionEvent:
|
||||
break
|
||||
if dialogic.current_timeline.get_event(idx).can_contain_events:
|
||||
ignore += 1
|
||||
elif dialogic.current_timeline.get_event(idx) is DialogicEndBranchEvent:
|
||||
ignore -= 1
|
||||
elif ignore == 0:
|
||||
break
|
||||
|
||||
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:
|
||||
return condition_type == ConditionTypes.If
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Condition"
|
||||
set_default_color('Color3')
|
||||
event_category = Category.Logic
|
||||
event_sorting_index = 0
|
||||
can_contain_events = true
|
||||
continue_at_end = true
|
||||
|
||||
|
||||
# return a control node that should show on the END BRANCH node
|
||||
func get_end_branch_control() -> Control:
|
||||
return load(get_script().resource_path.get_base_dir().path_join('ui_condition_end.tscn')).instantiate()
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func to_text() -> String:
|
||||
var result_string := ""
|
||||
|
||||
match condition_type:
|
||||
ConditionTypes.If:
|
||||
result_string = 'if '+condition+':'
|
||||
ConditionTypes.Elif:
|
||||
result_string = 'elif '+condition+':'
|
||||
ConditionTypes.Else:
|
||||
result_string = 'else:'
|
||||
|
||||
return result_string
|
||||
|
||||
|
||||
func from_text(string:String) -> void:
|
||||
if string.strip_edges().begins_with('if'):
|
||||
condition = string.strip_edges().trim_prefix('if ').trim_suffix(':').strip_edges()
|
||||
condition_type = ConditionTypes.If
|
||||
elif string.strip_edges().begins_with('elif'):
|
||||
condition = string.strip_edges().trim_prefix('elif ').trim_suffix(':').strip_edges()
|
||||
condition_type = ConditionTypes.Elif
|
||||
elif string.strip_edges().begins_with('else'):
|
||||
condition = ""
|
||||
condition_type = ConditionTypes.Else
|
||||
|
||||
|
||||
func is_valid_event(string:String) -> bool:
|
||||
if (string.strip_edges().begins_with('if ') or string.strip_edges().begins_with('elif ') or string.strip_edges().begins_with('else')) and string.strip_edges().ends_with(':'):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('condition_type', ValueType.FixedOptionSelector, '', '', {
|
||||
'selector_options': [
|
||||
{
|
||||
'label': 'IF',
|
||||
'value': ConditionTypes.If,
|
||||
},
|
||||
{
|
||||
'label': 'ELIF',
|
||||
'value': ConditionTypes.Elif,
|
||||
},
|
||||
{
|
||||
'label': 'ELSE',
|
||||
'value': ConditionTypes.Else,
|
||||
}
|
||||
], 'disabled':true})
|
||||
add_header_edit('condition', ValueType.Condition, '', '', {}, 'condition_type != %s'%ConditionTypes.Else)
|
||||
BIN
Sprout/addons/dialogic/Events/Condition/icon.png
Normal file
|
After Width: | Height: | Size: 637 B |
34
Sprout/addons/dialogic/Events/Condition/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b6bevon73e8mo"
|
||||
path="res://.godot/imported/icon.png-e7bc292be22002b6dc9192913250e17f.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Condition/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-e7bc292be22002b6dc9192913250e17f.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
6
Sprout/addons/dialogic/Events/Condition/index.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_condition.gd')]
|
||||
45
Sprout/addons/dialogic/Events/Condition/ui_condition_end.gd
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
var parent_resource = null
|
||||
|
||||
func _ready():
|
||||
$AddElif.button_up.connect(add_elif)
|
||||
$AddElse.button_up.connect(add_else)
|
||||
|
||||
func refresh():
|
||||
if parent_resource is DialogicConditionEvent:
|
||||
# hide add elif and add else button on ELSE event
|
||||
$AddElif.visible = parent_resource.condition_type != DialogicConditionEvent.ConditionTypes.Else
|
||||
$AddElse.visible = parent_resource.condition_type != DialogicConditionEvent.ConditionTypes.Else
|
||||
$Label.text = "End of "+["IF", "ELIF", "ELSE"][parent_resource.condition_type]+" ("+parent_resource.condition+")"
|
||||
|
||||
# hide add add else button if followed by ELIF or ELSE event
|
||||
var timeline_editor = find_parent('VisualEditor')
|
||||
if timeline_editor:
|
||||
var next_event = null
|
||||
if timeline_editor.get_block_below(get_parent()):
|
||||
next_event = timeline_editor.get_block_below(get_parent()).resource
|
||||
if next_event is DialogicConditionEvent:
|
||||
if next_event.condition_type != DialogicConditionEvent.ConditionTypes.If:
|
||||
$AddElse.hide()
|
||||
if parent_resource.condition_type == DialogicConditionEvent.ConditionTypes.Else:
|
||||
$Label.text = "End of ELSE"
|
||||
else:
|
||||
hide()
|
||||
|
||||
func add_elif():
|
||||
var timeline = find_parent('VisualEditor')
|
||||
if timeline:
|
||||
var resource = DialogicConditionEvent.new()
|
||||
resource.condition_type = DialogicConditionEvent.ConditionTypes.Elif
|
||||
timeline.add_event_with_end_branch(resource, get_parent().get_index()+1)
|
||||
timeline.indent_events()
|
||||
|
||||
func add_else():
|
||||
var timeline = find_parent('VisualEditor')
|
||||
if timeline:
|
||||
var resource = DialogicConditionEvent.new()
|
||||
resource.condition_type = DialogicConditionEvent.ConditionTypes.Else
|
||||
timeline.add_event_with_end_branch(resource, get_parent().get_index()+1)
|
||||
timeline.indent_events()
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Condition/ui_condition_end.gd" id="1_sh52m"]
|
||||
|
||||
[node name="Condition_End" type="HBoxContainer"]
|
||||
offset_right = 90.0
|
||||
offset_bottom = 23.0
|
||||
script = ExtResource("1_sh52m")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
offset_top = 2.0
|
||||
offset_right = 141.0
|
||||
offset_bottom = 28.0
|
||||
text = "End of condition X"
|
||||
|
||||
[node name="AddElif" type="Button" parent="."]
|
||||
offset_left = 145.0
|
||||
offset_right = 212.0
|
||||
offset_bottom = 31.0
|
||||
text = "Add Elif"
|
||||
|
||||
[node name="AddElse" type="Button" parent="."]
|
||||
offset_left = 216.0
|
||||
offset_right = 290.0
|
||||
offset_bottom = 31.0
|
||||
text = "Add Else"
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
extends Node
|
||||
class_name CustomEventConverter
|
||||
|
||||
# This is an extension to the Converter to allow for converting custom scenes
|
||||
# This will take a 1.x event node with a custom scene, and let you convert it to the new format
|
||||
# One type is provided for example. If you wish to convert them into a new format, add it here as another Match case
|
||||
# Otherwise, it will just create it as a commment with the parameters in it for you to do in-editor
|
||||
|
||||
|
||||
static func convertCustomEvent(event):
|
||||
var returnString = ""
|
||||
|
||||
match event['event_id']:
|
||||
"comment_001":
|
||||
# Example node, a custom event to simply store comments, as there wasn't a built-in one in 1.x
|
||||
returnString += "# " + event['comment_text']
|
||||
|
||||
return returnString
|
||||
34
Sprout/addons/dialogic/Events/Converter/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d0vwotbceutf6"
|
||||
path="res://.godot/imported/icon.png-1a336d53bfc8ce6e2b3ee694eb2fd062.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Converter/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-1a336d53bfc8ce6e2b3ee694eb2fd062.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
5
Sprout/addons/dialogic/Events/Converter/index.gd
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
func _get_settings_pages() -> Array:
|
||||
return [this_folder.path_join('settings_converter.tscn')]
|
||||
1022
Sprout/addons/dialogic/Events/Converter/settings_converter.gd
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Converter/settings_converter.gd" id="1_3feab"]
|
||||
[ext_resource type="StyleBox" uid="uid://dmsjhgv22dns8" path="res://addons/dialogic/Editor/Common/TitleBgStylebox.tres" id="2_lrwmv"]
|
||||
|
||||
[node name="Dialogic 1 Converter" type="HSplitContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_3feab")
|
||||
|
||||
[node name="LeftPanel" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_right = 727.0
|
||||
offset_bottom = 648.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 1.75
|
||||
|
||||
[node name="Label" type="Label" parent="LeftPanel"]
|
||||
layout_mode = 2
|
||||
offset_right = 727.0
|
||||
offset_bottom = 36.0
|
||||
theme_override_styles/normal = ExtResource("2_lrwmv")
|
||||
text = "Output"
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="LeftPanel"]
|
||||
layout_mode = 2
|
||||
offset_top = 40.0
|
||||
offset_right = 727.0
|
||||
offset_bottom = 648.0
|
||||
size_flags_vertical = 3
|
||||
vertical_scroll_mode = 0
|
||||
|
||||
[node name="OutputLog" type="RichTextLabel" parent="LeftPanel/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = false
|
||||
layout_mode = 2
|
||||
offset_right = 727.0
|
||||
offset_bottom = 608.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
bbcode_enabled = true
|
||||
text = "This is a converter script to convert Dialogic 1.4+ files into Dialogic 2.0 format.
|
||||
|
||||
Please copy your res://dialogic folder from a Dialogic 1.4 project into this project before proceeding.
|
||||
|
||||
Once you are ready, the Verify Files button will do a check to make sure all of the files are present, and prepare it for conversion."
|
||||
|
||||
[node name="RightPanel" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
offset_left = 739.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 648.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label2" type="Label" parent="RightPanel"]
|
||||
layout_mode = 2
|
||||
offset_right = 413.0
|
||||
offset_bottom = 36.0
|
||||
theme_override_styles/normal = ExtResource("2_lrwmv")
|
||||
text = "Settings"
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="RightPanel"]
|
||||
layout_mode = 2
|
||||
offset_top = 40.0
|
||||
offset_right = 413.0
|
||||
offset_bottom = 71.0
|
||||
text = "Add prefix to duplicate characters"
|
||||
|
||||
[node name="Verify" type="Button" parent="RightPanel"]
|
||||
layout_mode = 2
|
||||
offset_top = 75.0
|
||||
offset_right = 413.0
|
||||
offset_bottom = 106.0
|
||||
text = "Verify Files"
|
||||
|
||||
[node name="Begin" type="Button" parent="RightPanel"]
|
||||
layout_mode = 2
|
||||
offset_top = 110.0
|
||||
offset_right = 413.0
|
||||
offset_bottom = 141.0
|
||||
disabled = true
|
||||
text = "Begin Conversion"
|
||||
|
||||
[connection signal="toggled" from="RightPanel/CheckBox" to="." method="_on_check_box_toggled"]
|
||||
[connection signal="pressed" from="RightPanel/Verify" to="." method="_on_verify_pressed"]
|
||||
[connection signal="pressed" from="RightPanel/Begin" to="." method="_on_begin_pressed"]
|
||||
68
Sprout/addons/dialogic/Events/End Branch/event_end_branch.gd
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
@tool
|
||||
class_name DialogicEndBranchEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that indicates the end of a condition or choice (or custom branch).
|
||||
## In text this is not stored (only as a change in indentation).
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
dialogic.current_event_idx = find_next_index()-1
|
||||
finish()
|
||||
|
||||
|
||||
func find_next_index():
|
||||
var idx: int = dialogic.current_event_idx
|
||||
|
||||
var ignore: int = 1
|
||||
while true:
|
||||
idx += 1
|
||||
var event = dialogic.current_timeline.get_event(idx)
|
||||
if not event:
|
||||
return idx
|
||||
if event is DialogicEndBranchEvent:
|
||||
if ignore > 1: ignore -= 1
|
||||
elif event.can_contain_events and not event.should_execute_this_branch():
|
||||
ignore += 1
|
||||
elif ignore <= 1:
|
||||
return idx
|
||||
|
||||
return idx
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "End Branch"
|
||||
event_color = Color(1,1,1,1)
|
||||
event_category = Category.Logic
|
||||
event_sorting_index = 0
|
||||
disable_editor_button = true
|
||||
continue_at_end = true
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
## NOTE: This event is very special. It is rarely stored at all, as it is usually
|
||||
## just a placeholder for removing an indentation level.
|
||||
## When copying events however, some representation of this is necessary. That's why this is half-implemented.
|
||||
func to_text() -> String:
|
||||
return "<<END BRANCH>>"
|
||||
|
||||
|
||||
func from_text(string:String) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func is_valid_event(string:String) -> bool:
|
||||
if string.strip_edges().begins_with("<<END BRANCH>>"):
|
||||
return true
|
||||
return false
|
||||
BIN
Sprout/addons/dialogic/Events/End Branch/icon.png
Normal file
|
After Width: | Height: | Size: 558 B |
34
Sprout/addons/dialogic/Events/End Branch/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b0h5mknqmos7r"
|
||||
path="res://.godot/imported/icon.png-2770b68f95c6e9b6492f82019c7cb894.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/End Branch/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-2770b68f95c6e9b6492f82019c7cb894.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
6
Sprout/addons/dialogic/Events/End Branch/index.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_end_branch.gd')]
|
||||
34
Sprout/addons/dialogic/Events/End/event_end.gd
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
@tool
|
||||
class_name DialogicEndTimelineEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that ends a timeline (even if more events come after).
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
for character in dialogic.Portraits.get_joined_characters():
|
||||
dialogic.Portraits.remove_portrait(character)
|
||||
dialogic.end_timeline()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "End Timeline"
|
||||
set_default_color('Color4')
|
||||
event_category = Category.Timeline
|
||||
event_sorting_index = 10
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "end_timeline"
|
||||
BIN
Sprout/addons/dialogic/Events/End/icon.png
Normal file
|
After Width: | Height: | Size: 558 B |
34
Sprout/addons/dialogic/Events/End/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cstw2y41yacgc"
|
||||
path="res://.godot/imported/icon.png-a0a96e8aa3ccec20b5e27375ec04ccf1.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/End/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-a0a96e8aa3ccec20b5e27375ec04ccf1.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
6
Sprout/addons/dialogic/Events/End/index.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_end.gd')]
|
||||
3
Sprout/addons/dialogic/Events/Glossary/definition.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.9645 2.62927L12.8459 5.22444C12.6271 5.04546 12.3835 4.89134 12.1151 4.76208C11.8565 4.62287 11.5881 4.55327 11.3097 4.55327C11.0909 4.55327 10.8821 4.59802 10.6832 4.68751C10.4943 4.77699 10.3203 4.89631 10.1612 5.04546C10.0021 5.19461 9.86293 5.36364 9.74361 5.55256C9.6243 5.73154 9.52486 5.91052 9.44532 6.08949L9.14702 6.93964C9.35583 7.37714 9.55966 7.79475 9.75853 8.19248C9.92756 8.53055 10.1016 8.87359 10.2805 9.2216C10.4595 9.55966 10.6087 9.82813 10.728 10.027C10.9169 10.3153 11.1058 10.6136 11.2948 10.9219C11.4837 11.2202 11.6925 11.4936 11.9212 11.7422C12.0206 11.8516 12.1399 11.9261 12.2791 11.9659C12.4283 11.9957 12.5625 12.0107 12.6818 12.0107C12.8707 12.0107 13.0497 11.9858 13.2188 11.9361C13.3878 11.8864 13.5469 11.8217 13.696 11.7422L14.0689 12.2195C13.9297 12.4382 13.7607 12.657 13.5618 12.8757C13.3629 13.0945 13.1442 13.2933 12.9055 13.4723C12.6769 13.6513 12.4283 13.7955 12.1598 13.9048C11.9013 14.0242 11.6378 14.0838 11.3693 14.0838C11.1307 14.0838 10.9169 14.044 10.728 13.9645C10.549 13.8949 10.3849 13.8004 10.2358 13.6811C10.0867 13.5519 9.94745 13.4027 9.81819 13.2337C9.68893 13.0646 9.55469 12.8906 9.41549 12.7117C9.30611 12.5426 9.1868 12.3388 9.05753 12.1001C8.93822 11.8516 8.81393 11.598 8.68466 11.3395C8.5554 11.081 8.42614 10.8324 8.29688 10.5938C8.16762 10.3452 8.04333 10.1364 7.92401 9.96734C7.8743 10.1364 7.82955 10.3054 7.78978 10.4744C7.75001 10.6136 7.70526 10.7578 7.65555 10.907C7.60583 11.0561 7.56109 11.1705 7.52131 11.25C7.37216 11.5682 7.18324 11.8963 6.95455 12.2344C6.72586 12.5724 6.46236 12.8807 6.16407 13.1591C5.87572 13.4276 5.55753 13.6513 5.20952 13.8303C4.86151 13.9993 4.49361 14.0838 4.10583 14.0838C3.75782 14.0838 3.41975 14.0142 3.09162 13.875C2.7635 13.7457 2.46023 13.5717 2.18182 13.353L3.12145 10.8771C3.43964 11.076 3.78765 11.255 4.16549 11.4141C4.54333 11.5632 4.92117 11.6378 5.29901 11.6378C5.41833 11.6378 5.54262 11.6278 5.67188 11.608C5.80114 11.5781 5.92543 11.5384 6.04475 11.4886C6.17401 11.429 6.28836 11.3594 6.38779 11.2798C6.48722 11.1903 6.5618 11.0859 6.61151 10.9666C6.68111 10.8374 6.75569 10.6683 6.83523 10.4595C6.91478 10.2507 6.99432 10.0419 7.07387 9.8331C7.16336 9.59447 7.25285 9.34091 7.34234 9.07245L4.53836 4.5831C4.4091 4.43395 4.25001 4.31464 4.06109 4.22515C3.88211 4.12572 3.69319 4.076 3.49432 4.076C3.32529 4.076 3.1662 4.1108 3.01705 4.1804C2.8679 4.24006 2.72373 4.32458 2.58452 4.43395L2.18182 3.91194C2.32103 3.70313 2.48509 3.4993 2.67401 3.30043C2.87287 3.09162 3.08665 2.90768 3.31535 2.74859C3.54404 2.57955 3.78765 2.44532 4.04617 2.34589C4.30469 2.23651 4.56819 2.18182 4.83665 2.18182C5.16478 2.18182 5.46805 2.26634 5.74645 2.43537C6.02486 2.59447 6.28339 2.79333 6.52202 3.03197C6.76066 3.2706 6.97941 3.52912 7.17827 3.80753C7.37714 4.08594 7.55611 4.34447 7.7152 4.5831C7.79475 4.69248 7.87927 4.83168 7.96876 5.00072C8.06819 5.15981 8.16265 5.3189 8.25214 5.47799C8.36151 5.65697 8.46591 5.85086 8.56535 6.05966C8.66478 5.82103 8.76918 5.58239 8.87856 5.34376C8.96805 5.14489 9.05753 4.94106 9.14702 4.73225C9.24645 4.5135 9.33594 4.32458 9.41549 4.16549C9.56464 3.88708 9.73367 3.62856 9.92259 3.38992C10.1115 3.15128 10.3203 2.94248 10.549 2.7635C10.7876 2.58452 11.0462 2.44532 11.3246 2.34589C11.603 2.23651 11.9063 2.18182 12.2344 2.18182C12.5426 2.18182 12.8409 2.2216 13.1293 2.30114C13.4176 2.38069 13.696 2.49006 13.9645 2.62927Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
37
Sprout/addons/dialogic/Events/Glossary/definition.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://580n3xtnq3ru"
|
||||
path="res://.godot/imported/definition.svg-031b12c360f1060f573315dfb3d273d8.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Glossary/definition.svg"
|
||||
dest_files=["res://.godot/imported/definition.svg-031b12c360f1060f573315dfb3d273d8.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
43
Sprout/addons/dialogic/Events/Glossary/event_glossary.gd
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
@tool
|
||||
class_name DialogicGlossaryEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that does nothing right now.
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
pass
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Glossary"
|
||||
set_default_color('Color6')
|
||||
event_category = Category.AudioVisual
|
||||
event_sorting_index = 0
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
func get_shortcode() -> String:
|
||||
return "glossary"
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
}
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
pass
|
||||
11
Sprout/addons/dialogic/Events/Glossary/glossary_resource.gd
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class_name DialogicGlossary
|
||||
extends Resource
|
||||
|
||||
## Resource used to store glossary entries. Can be saved to disc and used as a glossary.
|
||||
## Add/create glossaries fom the settings
|
||||
|
||||
## Stores all entry information
|
||||
@export var entries :Dictionary = {}
|
||||
|
||||
## If false, no entries from this glossary will be shown
|
||||
@export var enabled :bool = true
|
||||
BIN
Sprout/addons/dialogic/Events/Glossary/icon.png
Normal file
|
After Width: | Height: | Size: 639 B |
34
Sprout/addons/dialogic/Events/Glossary/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b6wqvg2qcjxs"
|
||||
path="res://.godot/imported/icon.png-624eb6dbf7e3ab27845a397653fa2fbb.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/Glossary/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-624eb6dbf7e3ab27845a397653fa2fbb.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
13
Sprout/addons/dialogic/Events/Glossary/index.gd
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_glossary.gd')]
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
return [{'name':'Glossary', 'script':this_folder.path_join('subsystem_glossary.gd')}]
|
||||
|
||||
func _get_settings_pages() -> Array:
|
||||
return [this_folder.path_join('settings_glossary.tscn')]
|
||||
210
Sprout/addons/dialogic/Events/Glossary/settings_glossary.gd
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
var current_glossary :DialogicGlossary = null
|
||||
var current_entry_name := ""
|
||||
|
||||
################################################################################
|
||||
## BASICS
|
||||
################################################################################
|
||||
func _ready() -> void:
|
||||
%AddGlossaryFile.icon = get_theme_icon('Add', 'EditorIcons')
|
||||
%LoadGlossaryFile.icon = get_theme_icon('Folder', 'EditorIcons')
|
||||
%DeleteGlossaryFile.icon = get_theme_icon('Remove', 'EditorIcons')
|
||||
%DeleteGlossaryEntry.icon = get_theme_icon('Remove', 'EditorIcons')
|
||||
|
||||
%AddGlossaryEntry.icon = get_theme_icon('Add', 'EditorIcons')
|
||||
|
||||
%GlossaryList.item_selected.connect(_on_GlossaryList_item_selected)
|
||||
%EntryList.item_selected.connect(_on_EntryList_item_selected)
|
||||
|
||||
%DefaultColor.color_changed.connect(set_setting.bind('dialogic/glossary/default_color'))
|
||||
%DefaultCaseSensitive.toggled.connect(set_setting.bind('dialogic/glossary/default_case_sensitive'))
|
||||
|
||||
func set_setting(value, setting:String) -> void:
|
||||
ProjectSettings.set_setting(setting, value)
|
||||
ProjectSettings.save()
|
||||
|
||||
func refresh() -> void:
|
||||
%DefaultColor.color = DialogicUtil.get_project_setting('dialogic/glossary/default_color', Color.POWDER_BLUE)
|
||||
%DefaultCaseSensitive.button_pressed = DialogicUtil.get_project_setting('dialogic/glossary/default_case_sensitive', true)
|
||||
|
||||
%GlossaryList.clear()
|
||||
var idx := 0
|
||||
for file in DialogicUtil.get_project_setting('dialogic/glossary/glossary_files', []):
|
||||
if FileAccess.file_exists(file):
|
||||
%GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileList', 'EditorIcons'))
|
||||
else:
|
||||
%GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileDead', 'EditorIcons'))
|
||||
|
||||
%GlossaryList.set_item_tooltip(idx, file)
|
||||
idx += 1
|
||||
|
||||
%EntryList.clear()
|
||||
|
||||
if %GlossaryList.item_count != 0:
|
||||
%GlossaryList.select(0)
|
||||
_on_GlossaryList_item_selected(0)
|
||||
else:
|
||||
current_glossary = null
|
||||
hide_entry_editor()
|
||||
|
||||
################################################################################
|
||||
## GLOSSARY LIST
|
||||
################################################################################
|
||||
func _on_GlossaryList_item_selected(idx:int) -> void:
|
||||
%EntryList.clear()
|
||||
if FileAccess.file_exists(%GlossaryList.get_item_tooltip(idx)):
|
||||
current_glossary = load(%GlossaryList.get_item_tooltip(idx))
|
||||
if not current_glossary is DialogicGlossary:
|
||||
return
|
||||
|
||||
for entry in current_glossary.entries:
|
||||
%EntryList.add_item(entry)
|
||||
|
||||
if %EntryList.item_count != 0:
|
||||
%EntryList.select(0)
|
||||
_on_EntryList_item_selected(0)
|
||||
else:
|
||||
hide_entry_editor()
|
||||
|
||||
func _on_add_glossary_file_pressed() -> void:
|
||||
find_parent('EditorView').godot_file_dialog(create_new_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_SAVE_FILE, 'Create new glossary resource')
|
||||
|
||||
func create_new_glossary_file(path:String) -> void:
|
||||
var glossary := DialogicGlossary.new()
|
||||
glossary.resource_path = path
|
||||
ResourceSaver.save(glossary, path)
|
||||
load_glossary_file(path)
|
||||
|
||||
func _on_load_glossary_file_pressed() -> void:
|
||||
find_parent('EditorView').godot_file_dialog(load_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_OPEN_FILE, 'Select glossary resource')
|
||||
|
||||
func load_glossary_file(path:String) -> void:
|
||||
var list :Array= DialogicUtil.get_project_setting('dialogic/glossary/glossary_files', [])
|
||||
if not path in list:
|
||||
list.append(path)
|
||||
ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
|
||||
ProjectSettings.save()
|
||||
%GlossaryList.add_item(DialogicUtil.pretty_name(path), get_theme_icon('FileList', 'EditorIcons'))
|
||||
%GlossaryList.set_item_tooltip(%GlossaryList.item_count-1, path)
|
||||
%GlossaryList.select(%GlossaryList.item_count-1)
|
||||
_on_GlossaryList_item_selected(%GlossaryList.item_count-1)
|
||||
|
||||
func _on_delete_glossary_file_pressed() -> void:
|
||||
if len(%GlossaryList.get_selected_items()) != 0:
|
||||
var list :Array = DialogicUtil.get_project_setting('dialogic/glossary/glossary_files', [])
|
||||
list.erase(%GlossaryList.get_item_tooltip(
|
||||
%GlossaryList.get_selected_items()[0]))
|
||||
ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
|
||||
ProjectSettings.save()
|
||||
refresh()
|
||||
|
||||
################################################################################
|
||||
## ENTRY LIST
|
||||
################################################################################
|
||||
func _on_EntryList_item_selected(idx:int) -> void:
|
||||
current_entry_name = %EntryList.get_item_text(idx)
|
||||
var entry_info = current_glossary.entries[current_entry_name]
|
||||
%EntryEditorTitle.text = "Edit entry"
|
||||
%EntrySettings.show()
|
||||
%EntryName.text = current_entry_name
|
||||
%EntryCaseSensitive.button_pressed = entry_info.get('case_sensitive', %DefaultCaseSensitive.button_pressed)
|
||||
var alts = ""
|
||||
for i in entry_info.get('alternatives', []):
|
||||
alts += i+", "
|
||||
%EntryAlternatives.text = alts
|
||||
%EntryTitle.text = entry_info.get('title', '')
|
||||
%EntryText.text = entry_info.get('text', '')
|
||||
%EntryExtra.text = entry_info.get('extra', '')
|
||||
%EntryEnabled.button_pressed = entry_info.get('enabled', true)
|
||||
|
||||
%EntryColor.color = entry_info.get('color', %DefaultColor.color)
|
||||
%EntryCustomColor.button_pressed = entry_info.has('color')
|
||||
%EntryColor.disabled = !entry_info.has('color')
|
||||
|
||||
func _on_add_glossary_entry_pressed() -> void:
|
||||
if !current_glossary:
|
||||
return
|
||||
|
||||
var new_name := "New Entry"
|
||||
if new_name in current_glossary.entries:
|
||||
var count := 2
|
||||
while new_name + " " +str(count) in current_glossary.entries:
|
||||
count += 1
|
||||
new_name += " " + str(count)
|
||||
current_glossary.entries[new_name] = {}
|
||||
ResourceSaver.save(current_glossary)
|
||||
%EntryList.add_item(new_name)
|
||||
%EntryList.select(%EntryList.item_count-1)
|
||||
_on_EntryList_item_selected(%EntryList.item_count-1)
|
||||
|
||||
func _on_delete_glossary_entry_pressed() -> void:
|
||||
if len(%EntryList.get_selected_items()) != 0:
|
||||
if current_glossary:
|
||||
current_glossary.entries.erase(%EntryList.get_item_text(
|
||||
%EntryList.get_selected_items()[0]))
|
||||
%EntryList.remove_item(%EntryList.get_selected_items()[0])
|
||||
|
||||
################################################################################
|
||||
## ENTRY EDITOR
|
||||
################################################################################
|
||||
func hide_entry_editor() -> void:
|
||||
%EntrySettings.hide()
|
||||
%EntryEditorTitle.text = "No entry selected."
|
||||
|
||||
func _on_entry_name_text_changed(new_text:String) -> void:
|
||||
if current_entry_name != new_text.strip_edges():
|
||||
if new_text.strip_edges().is_empty() or new_text.strip_edges() in current_glossary.entries.keys():
|
||||
%EntryList.set_item_custom_bg_color(%EntryList.get_selected_items()[0],
|
||||
get_theme_color("warning_color", "Editor").darkened(0.8))
|
||||
%EntryList.set_item_text(%EntryList.get_selected_items()[0], new_text.strip_edges() + " (invalid name)")
|
||||
return
|
||||
else:
|
||||
%EntryList.set_item_custom_bg_color(%EntryList.get_selected_items()[0],
|
||||
Color.TRANSPARENT)
|
||||
var info :Dictionary = current_glossary.entries[current_entry_name]
|
||||
current_glossary.entries.erase(current_entry_name)
|
||||
current_glossary.entries[new_text.strip_edges()] = info
|
||||
%EntryList.set_item_text(%EntryList.get_selected_items()[0], new_text.strip_edges())
|
||||
current_entry_name = new_text.strip_edges()
|
||||
|
||||
ResourceSaver.save(current_glossary)
|
||||
|
||||
func _on_entry_case_sensitive_toggled(button_pressed:bool) -> void:
|
||||
current_glossary.entries[current_entry_name]['case_sensitive'] = button_pressed
|
||||
ResourceSaver.save(current_glossary)
|
||||
|
||||
func _on_entry_alternatives_text_changed(new_text:String) -> void:
|
||||
var alts := []
|
||||
for i in new_text.split(',', false):
|
||||
alts.append(i.strip_edges())
|
||||
current_glossary.entries[current_entry_name]['alternatives'] = alts
|
||||
ResourceSaver.save(current_glossary)
|
||||
|
||||
func _on_entry_title_text_changed(new_text:String) -> void:
|
||||
current_glossary.entries[current_entry_name]['title'] = new_text
|
||||
ResourceSaver.save(current_glossary)
|
||||
|
||||
func _on_entry_text_text_changed() -> void:
|
||||
current_glossary.entries[current_entry_name]['text'] = %EntryText.text
|
||||
ResourceSaver.save(current_glossary)
|
||||
|
||||
func _on_entry_extra_text_changed() -> void:
|
||||
current_glossary.entries[current_entry_name]['extra'] = %EntryExtra.text
|
||||
ResourceSaver.save(current_glossary)
|
||||
|
||||
func _on_entry_enabled_toggled(button_pressed:bool) -> void:
|
||||
current_glossary.entries[current_entry_name]['enabled'] = button_pressed
|
||||
ResourceSaver.save(current_glossary)
|
||||
|
||||
func _on_entry_custom_color_toggled(button_pressed:bool) -> void:
|
||||
%EntryColor.disabled = !button_pressed
|
||||
if !button_pressed:
|
||||
current_glossary.entries[current_entry_name].erase('color')
|
||||
else:
|
||||
current_glossary.entries[current_entry_name]['color'] = %EntryColor.color
|
||||
|
||||
func _on_entry_color_color_changed(color:Color) -> void:
|
||||
current_glossary.entries[current_entry_name]['color'] = color
|
||||
ResourceSaver.save(current_glossary)
|
||||
370
Sprout/addons/dialogic/Events/Glossary/settings_glossary.tscn
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
[gd_scene load_steps=5 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/dialogic/Events/Glossary/settings_glossary.gd" id="1_tf3p1"]
|
||||
[ext_resource type="StyleBox" uid="uid://dmsjhgv22dns8" path="res://addons/dialogic/Editor/Common/TitleBgStylebox.tres" id="2_kj6gm"]
|
||||
|
||||
[sub_resource type="Image" id="Image_ruhry"]
|
||||
data = {
|
||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_mi8la"]
|
||||
image = SubResource("Image_ruhry")
|
||||
|
||||
[node name="GlossaryEditor" type="VBoxContainer"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("1_tf3p1")
|
||||
|
||||
[node name="Entries" type="HBoxContainer" parent="."]
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Settings" type="VBoxContainer" parent="Entries"]
|
||||
offset_right = 371.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Title" type="Label" parent="Entries/Settings"]
|
||||
offset_right = 371.0
|
||||
offset_bottom = 36.0
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/normal = ExtResource("2_kj6gm")
|
||||
text = "Glossary Settings"
|
||||
|
||||
[node name="DefColor" type="HBoxContainer" parent="Entries/Settings"]
|
||||
offset_top = 40.0
|
||||
offset_right = 371.0
|
||||
offset_bottom = 66.0
|
||||
|
||||
[node name="Label" type="Label" parent="Entries/Settings/DefColor"]
|
||||
offset_right = 317.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Default color"
|
||||
|
||||
[node name="DefaultColor" type="ColorPickerButton" parent="Entries/Settings/DefColor"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
offset_left = 321.0
|
||||
offset_right = 371.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_horizontal = 8
|
||||
|
||||
[node name="DefCaseSensitive" type="HBoxContainer" parent="Entries/Settings"]
|
||||
offset_top = 70.0
|
||||
offset_right = 371.0
|
||||
offset_bottom = 96.0
|
||||
|
||||
[node name="Label" type="Label" parent="Entries/Settings/DefCaseSensitive"]
|
||||
offset_right = 343.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Default case sensitive"
|
||||
|
||||
[node name="DefaultCaseSensitive" type="CheckBox" parent="Entries/Settings/DefCaseSensitive"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 347.0
|
||||
offset_right = 371.0
|
||||
offset_bottom = 26.0
|
||||
|
||||
[node name="FileList" type="VBoxContainer" parent="Entries/Settings"]
|
||||
offset_top = 100.0
|
||||
offset_right = 371.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
size_flags_stretch_ratio = 0.69
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Entries/Settings/FileList"]
|
||||
offset_right = 371.0
|
||||
offset_bottom = 36.0
|
||||
|
||||
[node name="Title2" type="Label" parent="Entries/Settings/FileList/HBox"]
|
||||
offset_right = 218.0
|
||||
offset_bottom = 36.0
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/normal = ExtResource("2_kj6gm")
|
||||
text = "Active Files"
|
||||
|
||||
[node name="LoadGlossaryFile" type="Button" parent="Entries/Settings/FileList/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 222.0
|
||||
offset_right = 284.0
|
||||
offset_bottom = 36.0
|
||||
text = "Load"
|
||||
icon = SubResource("ImageTexture_mi8la")
|
||||
|
||||
[node name="AddGlossaryFile" type="Button" parent="Entries/Settings/FileList/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 288.0
|
||||
offset_right = 343.0
|
||||
offset_bottom = 36.0
|
||||
text = "Add"
|
||||
icon = SubResource("ImageTexture_mi8la")
|
||||
|
||||
[node name="DeleteGlossaryFile" type="Button" parent="Entries/Settings/FileList/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 347.0
|
||||
offset_right = 371.0
|
||||
offset_bottom = 36.0
|
||||
icon = SubResource("ImageTexture_mi8la")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="Entries/Settings/FileList"]
|
||||
offset_top = 40.0
|
||||
offset_right = 371.0
|
||||
offset_bottom = 500.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="GlossaryList" type="ItemList" parent="Entries/Settings/FileList/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 371.0
|
||||
offset_bottom = 460.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="Entries"]
|
||||
offset_left = 375.0
|
||||
offset_right = 379.0
|
||||
offset_bottom = 600.0
|
||||
|
||||
[node name="EntryList" type="VBoxContainer" parent="Entries"]
|
||||
offset_left = 383.0
|
||||
offset_right = 640.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.69
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Entries/EntryList"]
|
||||
offset_right = 257.0
|
||||
offset_bottom = 36.0
|
||||
|
||||
[node name="Title3" type="Label" parent="Entries/EntryList/HBox"]
|
||||
offset_right = 125.0
|
||||
offset_bottom = 36.0
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/normal = ExtResource("2_kj6gm")
|
||||
text = "Entries"
|
||||
|
||||
[node name="AddGlossaryEntry" type="Button" parent="Entries/EntryList/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 129.0
|
||||
offset_right = 229.0
|
||||
offset_bottom = 36.0
|
||||
text = "Add Entry"
|
||||
icon = SubResource("ImageTexture_mi8la")
|
||||
|
||||
[node name="DeleteGlossaryEntry" type="Button" parent="Entries/EntryList/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 233.0
|
||||
offset_right = 257.0
|
||||
offset_bottom = 36.0
|
||||
icon = SubResource("ImageTexture_mi8la")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="Entries/EntryList"]
|
||||
offset_top = 40.0
|
||||
offset_right = 257.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="EntryList" type="ItemList" parent="Entries/EntryList/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 257.0
|
||||
offset_bottom = 560.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="Entries"]
|
||||
offset_left = 644.0
|
||||
offset_right = 648.0
|
||||
offset_bottom = 600.0
|
||||
|
||||
[node name="EntryEditor" type="ScrollContainer" parent="Entries"]
|
||||
offset_left = 652.0
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
horizontal_scroll_mode = 0
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Entries/EntryEditor"]
|
||||
offset_right = 372.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="EntryEditorTitle" type="Label" parent="Entries/EntryEditor/VBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 372.0
|
||||
offset_bottom = 36.0
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/normal = ExtResource("2_kj6gm")
|
||||
text = "Entries"
|
||||
|
||||
[node name="EntrySettings" type="GridContainer" parent="Entries/EntryEditor/VBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_top = 40.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/h_separation = 13
|
||||
columns = 2
|
||||
|
||||
[node name="Label2" type="Label" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_right = 94.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_vertical = 0
|
||||
text = "Name"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_left = 107.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 31.0
|
||||
|
||||
[node name="EntryName" type="LineEdit" parent="Entries/EntryEditor/VBox/EntrySettings/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 87.7656
|
||||
offset_bottom = 31.0
|
||||
size_flags_horizontal = 0
|
||||
placeholder_text = "MyEntry"
|
||||
expand_to_text_length = true
|
||||
caret_blink = true
|
||||
caret_blink_speed = 0.5
|
||||
|
||||
[node name="EntryCaseSensitive" type="CheckBox" parent="Entries/EntryEditor/VBox/EntrySettings/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 91.0
|
||||
offset_right = 228.0
|
||||
offset_bottom = 31.0
|
||||
text = "Case sensitive"
|
||||
|
||||
[node name="Label3" type="Label" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_top = 35.0
|
||||
offset_right = 94.0
|
||||
offset_bottom = 61.0
|
||||
size_flags_vertical = 0
|
||||
text = "Alternatives"
|
||||
|
||||
[node name="EntryAlternatives" type="LineEdit" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 107.0
|
||||
offset_top = 35.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 66.0
|
||||
size_flags_horizontal = 3
|
||||
caret_blink = true
|
||||
caret_blink_speed = 0.5
|
||||
|
||||
[node name="Label4" type="Label" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_top = 70.0
|
||||
offset_right = 94.0
|
||||
offset_bottom = 96.0
|
||||
size_flags_vertical = 0
|
||||
text = "Title"
|
||||
|
||||
[node name="EntryTitle" type="LineEdit" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 107.0
|
||||
offset_top = 70.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 101.0
|
||||
size_flags_horizontal = 3
|
||||
caret_blink = true
|
||||
caret_blink_speed = 0.5
|
||||
|
||||
[node name="Label5" type="Label" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_top = 105.0
|
||||
offset_right = 94.0
|
||||
offset_bottom = 131.0
|
||||
size_flags_vertical = 0
|
||||
text = "Text"
|
||||
|
||||
[node name="EntryText" type="TextEdit" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
offset_left = 107.0
|
||||
offset_top = 105.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 205.0
|
||||
focus_next = NodePath("../EntryExtra")
|
||||
wrap_mode = 1
|
||||
|
||||
[node name="Label6" type="Label" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_top = 209.0
|
||||
offset_right = 94.0
|
||||
offset_bottom = 235.0
|
||||
size_flags_vertical = 0
|
||||
text = "Extra"
|
||||
|
||||
[node name="EntryExtra" type="TextEdit" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 50)
|
||||
offset_left = 107.0
|
||||
offset_top = 209.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 259.0
|
||||
wrap_mode = 1
|
||||
|
||||
[node name="Label8" type="Label" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_top = 263.0
|
||||
offset_right = 94.0
|
||||
offset_bottom = 289.0
|
||||
size_flags_vertical = 0
|
||||
text = "Enabled"
|
||||
|
||||
[node name="EntryEnabled" type="CheckBox" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 107.0
|
||||
offset_top = 263.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 289.0
|
||||
button_pressed = true
|
||||
|
||||
[node name="Label7" type="Label" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_top = 293.0
|
||||
offset_right = 94.0
|
||||
offset_bottom = 319.0
|
||||
size_flags_vertical = 0
|
||||
text = "Color"
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Entries/EntryEditor/VBox/EntrySettings"]
|
||||
offset_left = 107.0
|
||||
offset_top = 293.0
|
||||
offset_right = 372.0
|
||||
offset_bottom = 319.0
|
||||
|
||||
[node name="EntryCustomColor" type="CheckBox" parent="Entries/EntryEditor/VBox/EntrySettings/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 24.0
|
||||
offset_bottom = 26.0
|
||||
|
||||
[node name="EntryColor" type="ColorPickerButton" parent="Entries/EntryEditor/VBox/EntrySettings/HBox"]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 28.0
|
||||
offset_right = 265.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[connection signal="pressed" from="Entries/Settings/FileList/HBox/LoadGlossaryFile" to="." method="_on_load_glossary_file_pressed"]
|
||||
[connection signal="pressed" from="Entries/Settings/FileList/HBox/AddGlossaryFile" to="." method="_on_add_glossary_file_pressed"]
|
||||
[connection signal="pressed" from="Entries/Settings/FileList/HBox/DeleteGlossaryFile" to="." method="_on_delete_glossary_file_pressed"]
|
||||
[connection signal="pressed" from="Entries/EntryList/HBox/AddGlossaryEntry" to="." method="_on_add_glossary_entry_pressed"]
|
||||
[connection signal="pressed" from="Entries/EntryList/HBox/DeleteGlossaryEntry" to="." method="_on_delete_glossary_entry_pressed"]
|
||||
[connection signal="text_changed" from="Entries/EntryEditor/VBox/EntrySettings/HBoxContainer/EntryName" to="." method="_on_entry_name_text_changed"]
|
||||
[connection signal="toggled" from="Entries/EntryEditor/VBox/EntrySettings/HBoxContainer/EntryCaseSensitive" to="." method="_on_entry_case_sensitive_toggled"]
|
||||
[connection signal="text_changed" from="Entries/EntryEditor/VBox/EntrySettings/EntryAlternatives" to="." method="_on_entry_alternatives_text_changed"]
|
||||
[connection signal="text_changed" from="Entries/EntryEditor/VBox/EntrySettings/EntryTitle" to="." method="_on_entry_title_text_changed"]
|
||||
[connection signal="text_changed" from="Entries/EntryEditor/VBox/EntrySettings/EntryText" to="." method="_on_entry_text_text_changed"]
|
||||
[connection signal="text_changed" from="Entries/EntryEditor/VBox/EntrySettings/EntryExtra" to="." method="_on_entry_extra_text_changed"]
|
||||
[connection signal="toggled" from="Entries/EntryEditor/VBox/EntrySettings/EntryEnabled" to="." method="_on_entry_enabled_toggled"]
|
||||
[connection signal="toggled" from="Entries/EntryEditor/VBox/EntrySettings/HBox/EntryCustomColor" to="." method="_on_entry_custom_color_toggled"]
|
||||
[connection signal="color_changed" from="Entries/EntryEditor/VBox/EntrySettings/HBox/EntryColor" to="." method="_on_entry_color_color_changed"]
|
||||
76
Sprout/addons/dialogic/Events/Glossary/subsystem_glossary.gd
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
extends DialogicSubsystem
|
||||
|
||||
## Subsystem that handles glossaries.
|
||||
|
||||
## List of glossary resources that are used.
|
||||
var glossaries := []
|
||||
## If false, no parsing will be done.
|
||||
var enabled := true
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## STATE
|
||||
####################################################################################################
|
||||
|
||||
func clear_game_state() -> void:
|
||||
glossaries = []
|
||||
for path in DialogicUtil.get_project_setting('dialogic/glossary/glossary_files', []):
|
||||
add_glossary(path)
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## MAIN METHODS
|
||||
####################################################################################################
|
||||
|
||||
func parse_glossary(text:String) -> String:
|
||||
if !enabled: return text
|
||||
var def_case_sensitive :bool = DialogicUtil.get_project_setting('dialogic/glossary/default_case_sensitive', true)
|
||||
var def_color : Color= DialogicUtil.get_project_setting('dialogic/glossary/default_color', Color.POWDER_BLUE)
|
||||
var regex := RegEx.new()
|
||||
for glossary in glossaries:
|
||||
if !glossary.enabled:
|
||||
continue
|
||||
for entry in glossary.entries.keys():
|
||||
if !glossary.entries[entry].get('enabled', true):
|
||||
continue
|
||||
var pattern :String = '(?<=\\W|^)(?<word>'+glossary.entries[entry].get('regopts', entry)+')(?!])(?=\\W|$)'
|
||||
if glossary.entries[entry].get('case_sensitive', def_case_sensitive):
|
||||
regex.compile(pattern)
|
||||
else:
|
||||
regex.compile('(?i)'+pattern)
|
||||
text = regex.sub(text,
|
||||
'[url=' + entry + ']' +
|
||||
'[color=' + glossary.entries[entry].get('color', def_color).to_html() + ']${word}[/color]' +
|
||||
'[/url]', true
|
||||
)
|
||||
return text
|
||||
|
||||
|
||||
func add_glossary(path:String) -> void:
|
||||
if FileAccess.file_exists(path):
|
||||
var x = load(path)
|
||||
if x is DialogicGlossary:
|
||||
glossaries.append(x)
|
||||
for entry in x.entries.keys():
|
||||
var regopts :String = entry
|
||||
for i in x.entries[entry].get('alternatives', []):
|
||||
regopts += '|'+i
|
||||
x.entries[entry]['regopts'] = regopts
|
||||
else:
|
||||
printerr('[Dialogic] The glossary file "' + path + '" is missing. Make sure it exists.')
|
||||
|
||||
|
||||
func get_entry(name:String, parse_variables:bool = true) -> Dictionary:
|
||||
for glossary in glossaries:
|
||||
if name in glossary.entries:
|
||||
var info:Dictionary = glossary.entries[name].duplicate()
|
||||
if parse_variables and Dialogic.has_subsystem('VAR'):
|
||||
for key in info.keys():
|
||||
if typeof(info[key]) == TYPE_STRING:
|
||||
info[key] = Dialogic.VAR.parse_variables(info[key])
|
||||
return info
|
||||
return {}
|
||||
|
||||
|
||||
func set_entry(name: String, value: Dictionary) -> bool:
|
||||
return false
|
||||
3
Sprout/addons/dialogic/Events/History/definition.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.9645 2.62927L12.8459 5.22444C12.6271 5.04546 12.3835 4.89134 12.1151 4.76208C11.8565 4.62287 11.5881 4.55327 11.3097 4.55327C11.0909 4.55327 10.8821 4.59802 10.6832 4.68751C10.4943 4.77699 10.3203 4.89631 10.1612 5.04546C10.0021 5.19461 9.86293 5.36364 9.74361 5.55256C9.6243 5.73154 9.52486 5.91052 9.44532 6.08949L9.14702 6.93964C9.35583 7.37714 9.55966 7.79475 9.75853 8.19248C9.92756 8.53055 10.1016 8.87359 10.2805 9.2216C10.4595 9.55966 10.6087 9.82813 10.728 10.027C10.9169 10.3153 11.1058 10.6136 11.2948 10.9219C11.4837 11.2202 11.6925 11.4936 11.9212 11.7422C12.0206 11.8516 12.1399 11.9261 12.2791 11.9659C12.4283 11.9957 12.5625 12.0107 12.6818 12.0107C12.8707 12.0107 13.0497 11.9858 13.2188 11.9361C13.3878 11.8864 13.5469 11.8217 13.696 11.7422L14.0689 12.2195C13.9297 12.4382 13.7607 12.657 13.5618 12.8757C13.3629 13.0945 13.1442 13.2933 12.9055 13.4723C12.6769 13.6513 12.4283 13.7955 12.1598 13.9048C11.9013 14.0242 11.6378 14.0838 11.3693 14.0838C11.1307 14.0838 10.9169 14.044 10.728 13.9645C10.549 13.8949 10.3849 13.8004 10.2358 13.6811C10.0867 13.5519 9.94745 13.4027 9.81819 13.2337C9.68893 13.0646 9.55469 12.8906 9.41549 12.7117C9.30611 12.5426 9.1868 12.3388 9.05753 12.1001C8.93822 11.8516 8.81393 11.598 8.68466 11.3395C8.5554 11.081 8.42614 10.8324 8.29688 10.5938C8.16762 10.3452 8.04333 10.1364 7.92401 9.96734C7.8743 10.1364 7.82955 10.3054 7.78978 10.4744C7.75001 10.6136 7.70526 10.7578 7.65555 10.907C7.60583 11.0561 7.56109 11.1705 7.52131 11.25C7.37216 11.5682 7.18324 11.8963 6.95455 12.2344C6.72586 12.5724 6.46236 12.8807 6.16407 13.1591C5.87572 13.4276 5.55753 13.6513 5.20952 13.8303C4.86151 13.9993 4.49361 14.0838 4.10583 14.0838C3.75782 14.0838 3.41975 14.0142 3.09162 13.875C2.7635 13.7457 2.46023 13.5717 2.18182 13.353L3.12145 10.8771C3.43964 11.076 3.78765 11.255 4.16549 11.4141C4.54333 11.5632 4.92117 11.6378 5.29901 11.6378C5.41833 11.6378 5.54262 11.6278 5.67188 11.608C5.80114 11.5781 5.92543 11.5384 6.04475 11.4886C6.17401 11.429 6.28836 11.3594 6.38779 11.2798C6.48722 11.1903 6.5618 11.0859 6.61151 10.9666C6.68111 10.8374 6.75569 10.6683 6.83523 10.4595C6.91478 10.2507 6.99432 10.0419 7.07387 9.8331C7.16336 9.59447 7.25285 9.34091 7.34234 9.07245L4.53836 4.5831C4.4091 4.43395 4.25001 4.31464 4.06109 4.22515C3.88211 4.12572 3.69319 4.076 3.49432 4.076C3.32529 4.076 3.1662 4.1108 3.01705 4.1804C2.8679 4.24006 2.72373 4.32458 2.58452 4.43395L2.18182 3.91194C2.32103 3.70313 2.48509 3.4993 2.67401 3.30043C2.87287 3.09162 3.08665 2.90768 3.31535 2.74859C3.54404 2.57955 3.78765 2.44532 4.04617 2.34589C4.30469 2.23651 4.56819 2.18182 4.83665 2.18182C5.16478 2.18182 5.46805 2.26634 5.74645 2.43537C6.02486 2.59447 6.28339 2.79333 6.52202 3.03197C6.76066 3.2706 6.97941 3.52912 7.17827 3.80753C7.37714 4.08594 7.55611 4.34447 7.7152 4.5831C7.79475 4.69248 7.87927 4.83168 7.96876 5.00072C8.06819 5.15981 8.16265 5.3189 8.25214 5.47799C8.36151 5.65697 8.46591 5.85086 8.56535 6.05966C8.66478 5.82103 8.76918 5.58239 8.87856 5.34376C8.96805 5.14489 9.05753 4.94106 9.14702 4.73225C9.24645 4.5135 9.33594 4.32458 9.41549 4.16549C9.56464 3.88708 9.73367 3.62856 9.92259 3.38992C10.1115 3.15128 10.3203 2.94248 10.549 2.7635C10.7876 2.58452 11.0462 2.44532 11.3246 2.34589C11.603 2.23651 11.9063 2.18182 12.2344 2.18182C12.5426 2.18182 12.8409 2.2216 13.1293 2.30114C13.4176 2.38069 13.696 2.49006 13.9645 2.62927Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
37
Sprout/addons/dialogic/Events/History/definition.svg.import
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dlwtdexd63bxi"
|
||||
path="res://.godot/imported/definition.svg-22368503ac4b1eb0eb4b2dc0b2354e38.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/History/definition.svg"
|
||||
dest_files=["res://.godot/imported/definition.svg-22368503ac4b1eb0eb4b2dc0b2354e38.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
77
Sprout/addons/dialogic/Events/History/event_history.gd
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
@tool
|
||||
class_name DialogicHistoryEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that allows clearing, pausing and resuming of history functionality.
|
||||
|
||||
enum ActionTypes {Clear, Pause, Resume}
|
||||
|
||||
### Settings
|
||||
|
||||
## The type of action: Clear, Pause or Resume
|
||||
var action_type := ActionTypes.Pause
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTION
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
match action_type:
|
||||
ActionTypes.Clear:
|
||||
dialogic.History.full_history = []
|
||||
ActionTypes.Pause:
|
||||
dialogic.History.full_history_enabled = false
|
||||
ActionTypes.Resume:
|
||||
dialogic.History.full_history_enabled = true
|
||||
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "History"
|
||||
set_default_color('Color6')
|
||||
event_category = Category.AudioVisual
|
||||
event_sorting_index = 0
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "history"
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"action" : {"property": "action_type", "default": ActionTypes.Pause,
|
||||
"suggestions": func(): return {"Clear":{'value':'0'}, "Pause":{'value':'1'}, "Resume":{'value':'2'}}},
|
||||
}
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('action_type', ValueType.FixedOptionSelector, '', '', {
|
||||
'selector_options': [
|
||||
{
|
||||
'label': 'Pause History',
|
||||
'value': ActionTypes.Pause,
|
||||
},
|
||||
{
|
||||
'label': 'Resume History',
|
||||
'value': ActionTypes.Resume,
|
||||
},
|
||||
{
|
||||
'label': 'Clear History',
|
||||
'value': ActionTypes.Clear,
|
||||
},
|
||||
]
|
||||
})
|
||||
BIN
Sprout/addons/dialogic/Events/History/icon.png
Normal file
|
After Width: | Height: | Size: 639 B |
34
Sprout/addons/dialogic/Events/History/icon.png.import
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b2dft055rbthc"
|
||||
path="res://.godot/imported/icon.png-0f163f3ae73119c52d1c0fa16d2c9e6e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/dialogic/Events/History/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-0f163f3ae73119c52d1c0fa16d2c9e6e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
13
Sprout/addons/dialogic/Events/History/index.gd
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
@tool
|
||||
extends DialogicIndexer
|
||||
|
||||
|
||||
func _get_events() -> Array:
|
||||
return [this_folder.path_join('event_history.gd')]
|
||||
|
||||
|
||||
func _get_subsystems() -> Array:
|
||||
return [{'name':'History', 'script':this_folder.path_join('subsystem_history.gd')}]
|
||||
|
||||
func _get_settings_pages() -> Array:
|
||||
return [this_folder.path_join('settings_history.tscn')]
|
||||
35
Sprout/addons/dialogic/Events/History/settings_history.gd
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
func refresh():
|
||||
%HistoryToggle.button_pressed = DialogicUtil.get_project_setting('dialogic/history/history_system', true)
|
||||
%FullHistory.button_pressed = DialogicUtil.get_project_setting('dialogic/history/full_history', true)
|
||||
%HistoryLength.value = DialogicUtil.get_project_setting('dialogic/history/full_history_length', 50)
|
||||
%TextHistory.button_pressed = DialogicUtil.get_project_setting('dialogic/history/text_history', true)
|
||||
%FullText.button_pressed = DialogicUtil.get_project_setting('dialogic/history/full_history_option_save_text', false)
|
||||
|
||||
func _on_history_toggle_toggled(button_pressed):
|
||||
ProjectSettings.set_setting('dialogic/history/history_system', button_pressed)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_full_history_toggled(button_pressed):
|
||||
ProjectSettings.set_setting('dialogic/history/full_history', button_pressed)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_history_length_value_changed(value):
|
||||
ProjectSettings.set_setting('dialogic/history/full_history_length', value)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
|
||||
func _on_text_history_toggled(button_pressed):
|
||||
ProjectSettings.set_setting('dialogic/history/text_history', button_pressed)
|
||||
ProjectSettings.save()
|
||||
|
||||
|
||||
func _on_full_text_toggled(button_pressed):
|
||||
ProjectSettings.set_setting('dialogic/history/full_history_option_save_text', button_pressed)
|
||||
ProjectSettings.save()
|
||||
|
||||
1389
Sprout/addons/dialogic/Events/History/settings_history.tscn
Normal file
91
Sprout/addons/dialogic/Events/History/subsystem_history.gd
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
extends DialogicSubsystem
|
||||
|
||||
## Subsystem that manages history storing.
|
||||
|
||||
|
||||
signal history_text_already_read()
|
||||
|
||||
|
||||
## Set from the settings at runtime. If false, no history will be saved.
|
||||
var enabled: bool = true
|
||||
|
||||
## If true, all events will be stored with additional information.
|
||||
## Usefull if you want to be able to undo, or jump to certain points.
|
||||
## (Change from the settings).
|
||||
var full_history_enabled: bool = true
|
||||
var full_history_length: int = 50
|
||||
var full_history_option_save_text: bool = false
|
||||
## Stores the content of the full_history
|
||||
var full_history: Array = []
|
||||
|
||||
## If true, all text and choices text will be saved to the history. (Change from the settings).
|
||||
var text_read_history_enabled: bool = true
|
||||
## Stores the content of the text_read_history
|
||||
var text_read_history: Dictionary = {}
|
||||
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## INITIALIZE
|
||||
####################################################################################################
|
||||
|
||||
func _ready() -> void:
|
||||
enabled = DialogicUtil.get_project_setting('dialogic/history/history_system', true)
|
||||
full_history_enabled = DialogicUtil.get_project_setting('dialogic/history/full_history', true)
|
||||
full_history_length = DialogicUtil.get_project_setting('dialogic/history/full_history_length', 50)
|
||||
full_history_option_save_text = DialogicUtil.get_project_setting('dialogic/history/full_history_option_save_text', false)
|
||||
text_read_history_enabled = DialogicUtil.get_project_setting('dialogic/history/text_history', true)
|
||||
|
||||
|
||||
####################################################################################################
|
||||
## STATE
|
||||
####################################################################################################
|
||||
|
||||
# nothing implemented right now
|
||||
|
||||
####################################################################################################
|
||||
## MAIN METHODS
|
||||
####################################################################################################
|
||||
|
||||
func add_event_to_history(current_timeline:String, current_index:int, current_event:DialogicEvent) -> void:
|
||||
if full_history_enabled:
|
||||
var event_dict: Dictionary = {}
|
||||
event_dict['timeline'] = current_timeline
|
||||
event_dict['index'] = current_index
|
||||
event_dict['event_object'] = current_event
|
||||
event_dict['event_type'] = current_event.event_name
|
||||
|
||||
#A few more specific types of checks need to happen here to capture previous values
|
||||
|
||||
if current_event.event_name == "Text" and full_history_option_save_text:
|
||||
event_dict['text_event_character'] = current_event.character.get_character_name()
|
||||
event_dict['text_event_portrait'] = current_event.portrait
|
||||
event_dict['text_event_text'] = current_event.text
|
||||
|
||||
full_history.push_front(event_dict)
|
||||
if full_history.size() > full_history_length:
|
||||
var dropped = full_history.pop_back()
|
||||
|
||||
if text_read_history_enabled:
|
||||
if current_event.event_name == "Text" or current_event.event_name == "Choice":
|
||||
var line = str(current_index)+ "**" + current_timeline
|
||||
if line in text_read_history:
|
||||
emit_signal("history_text_already_read")
|
||||
else:
|
||||
text_read_history[line] = true
|
||||
|
||||
|
||||
func strip_events_from_full_history() -> void:
|
||||
for i in full_history.size():
|
||||
full_history[i].erase('event_object')
|
||||
|
||||
|
||||
func rebuild_all_history_events() -> void:
|
||||
var temp_timelines: Dictionary = {}
|
||||
for i in full_history.size():
|
||||
if full_history[i]['timeline'] in temp_timelines:
|
||||
full_history[i]['event_object'] = temp_timelines[full_history[i]['timeline']].events[full_history[i]['index']]
|
||||
else:
|
||||
var loaded_timeline = Dialogic.preload_timeline(full_history[i]['timeline'])
|
||||
temp_timelines[full_history[i]['timeline']] = loaded_timeline
|
||||
full_history[i]['event_object'] = loaded_timeline.events[full_history[i]['index']]
|
||||
108
Sprout/addons/dialogic/Events/Jump/event_jump.gd
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
@tool
|
||||
class_name DialogicJumpEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that allows starting another timeline. Also can jump to a label in that or the current timeline.
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## The timeline to jump to, if null then it's the current one. This setting should be a dialogic timeline resource.
|
||||
var timeline :DialogicTimeline = null
|
||||
## If not empty, the event will try to find a Label event with this set as name. Empty by default..
|
||||
var label_name : String = ""
|
||||
## If true when the timeline this event jumps to finishes, dialogic will continue this one from here.
|
||||
var return_after: bool = false
|
||||
|
||||
|
||||
### Helpers
|
||||
|
||||
## Path to the timeline. Mainly used by the editor.
|
||||
var _timeline_file: String = ""
|
||||
## Helper that indicates if [timeline] has been loaded from [_timeline_file].
|
||||
var _timeline_loaded: bool = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTION
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
if timeline: # TODO: Question, why is this always done, even if return_after is false?
|
||||
dialogic.push_to_jump_stack()
|
||||
if timeline and timeline != dialogic.current_timeline:
|
||||
dialogic.start_timeline(timeline, label_name)
|
||||
elif _timeline_file != "":
|
||||
if _timeline_file.contains("res://"):
|
||||
dialogic.start_timeline(_timeline_file, label_name)
|
||||
else:
|
||||
dialogic.start_timeline(Dialogic.find_timeline(_timeline_file), label_name)
|
||||
dialogic.jump_to_label(label_name)
|
||||
|
||||
|
||||
# TODO: Question what this is supposed to do. Can't see it being used anywhere. Is it for use from outside?
|
||||
func load_timeline() -> void:
|
||||
if timeline == null:
|
||||
if _timeline_file != "":
|
||||
timeline = Dialogic.preload_timeline(_timeline_file)
|
||||
_timeline_loaded = true
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Jump"
|
||||
set_default_color('Color2')
|
||||
event_category = Category.Timeline
|
||||
event_sorting_index = 0
|
||||
expand_by_default = false
|
||||
|
||||
|
||||
func _get_icon() -> Resource:
|
||||
return load(self.get_script().get_path().get_base_dir().path_join('icon_jump.png'))
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "jump"
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"timeline" : {"property": "_timeline_file", "default": null,
|
||||
"suggestions": get_timeline_suggestions},
|
||||
"label" : {"property": "label_name", "default": ""},
|
||||
"return_after" : {"property": "return_after", "default": false,
|
||||
"suggestions": func(): return {'True':{'value':'true'}, 'False':{'value':'false'}}}
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('_timeline_file', ValueType.ComplexPicker, 'to', '', {
|
||||
'file_extension': '.dtl',
|
||||
'suggestions_func': get_timeline_suggestions,
|
||||
'editor_icon': ["TripleBar", "EditorIcons"],
|
||||
'empty_text': '(this timeline)'
|
||||
})
|
||||
add_header_edit("label_name", ValueType.SinglelineText, "at", '', {'placeholder':'the beginning'})
|
||||
add_body_edit("return_after", ValueType.Bool, "Return to this spot after completed?")
|
||||
|
||||
|
||||
func get_timeline_suggestions(filter:String= "") -> Dictionary:
|
||||
var suggestions := {}
|
||||
var resources := DialogicUtil.list_resources_of_type('.dtl')
|
||||
|
||||
suggestions['(this timeline)'] = {'value':'', 'editor_icon':['GuiRadioUnchecked', 'EditorIcons']}
|
||||
|
||||
for resource in Engine.get_meta('dialogic_timeline_directory').keys():
|
||||
suggestions[resource] = {'value': resource, 'tooltip':Engine.get_meta('dialogic_timeline_directory')[resource], 'editor_icon': ["TripleBar", "EditorIcons"]}
|
||||
return suggestions
|
||||
59
Sprout/addons/dialogic/Events/Jump/event_label.gd
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
@tool
|
||||
class_name DialogicLabelEvent
|
||||
extends DialogicEvent
|
||||
|
||||
## Event that is used as an anchor. You can use the DialogicJumpEvent to jump to this point.
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
## Used to identify the label. Duplicate names in a timeline will mean it always chooses the first.
|
||||
var name: String = ""
|
||||
|
||||
|
||||
################################################################################
|
||||
## EXECUTE
|
||||
################################################################################
|
||||
|
||||
func _execute() -> void:
|
||||
# This event is mainly implemented in the DialogicGameHandlers jump_to_label() method.
|
||||
finish()
|
||||
|
||||
|
||||
################################################################################
|
||||
## INITIALIZE
|
||||
################################################################################
|
||||
|
||||
func _init() -> void:
|
||||
event_name = "Label"
|
||||
set_default_color('Color2')
|
||||
event_category = Category.Timeline
|
||||
event_sorting_index = 1
|
||||
continue_at_end = true
|
||||
|
||||
|
||||
func _get_icon() -> Resource:
|
||||
return load(self.get_script().get_path().get_base_dir().path_join('icon_label.png'))
|
||||
|
||||
|
||||
################################################################################
|
||||
## SAVING/LOADING
|
||||
################################################################################
|
||||
|
||||
func get_shortcode() -> String:
|
||||
return "label"
|
||||
|
||||
|
||||
func get_shortcode_parameters() -> Dictionary:
|
||||
return {
|
||||
#param_name : property_info
|
||||
"name" : {"property": "name", "default": ""},
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
## EDITOR REPRESENTATION
|
||||
################################################################################
|
||||
|
||||
func build_event_editor():
|
||||
add_header_edit('name', ValueType.SinglelineText, '')
|
||||