GodotProjects/Sprout/addons/dialogic/Events/Text/node_dialog_text.gd
2023-01-03 16:54:53 -07:00

147 lines
4.3 KiB
GDScript

extends RichTextLabel
class_name DialogicNode_DialogText
enum ALIGNMENT {LEFT, CENTER, RIGHT}
@export var Align : ALIGNMENT = ALIGNMENT.LEFT
@onready var timer :Timer = null
var effect_regex = RegEx.new()
var modifier_words_select_regex = RegEx.new()
var effects:Array = []
var speed:float = 0.01
signal started_revealing_text()
signal continued_revealing_text(new_character)
signal finished_revealing_text()
func _ready() -> void:
# add to necessary
add_to_group('dialogic_dialog_text')
bbcode_enabled = true
text = ""
# setup my timer
timer = Timer.new()
add_child(timer)
timer.wait_time = 0.01
timer.one_shot = true
DialogicUtil.update_timer_process_callback(timer)
timer.timeout.connect(continue_reveal)
# compile effects regex
effect_regex.compile("(?<!\\\\)\\[\\s*(?<command>mood|portrait|speed|signal|pause)\\s*(=\\s*(?<value>.+?)\\s*)?\\]")
# compule modifier regexs
modifier_words_select_regex.compile("(?<!\\\\)\\[[^\\[\\]]+(,[^\\]]*)\\]")
# this is called by the DialogicGameHandler to set text
func reveal_text(_text:String) -> void:
speed = DialogicUtil.get_project_setting('dialogic/text/speed', 0.01)
text = parse_effects(parse_modifiers(_text))
if Align == ALIGNMENT.CENTER:
text = '[center]'+text
elif Align == ALIGNMENT.RIGHT:
text = '[right]'+text
visible_characters = 0
if speed <= 0:
timer.start(0.01)
else:
timer.start(speed)
emit_signal('started_revealing_text')
# called by the timer -> reveals more text
func continue_reveal() -> void:
if visible_characters < get_total_character_count():
visible_characters += 1
emit_signal("continued_revealing_text", text[visible_characters-1])
execute_effects()
if timer.is_stopped():
if speed <= 0:
continue_reveal()
else:
timer.start(speed)
else:
finish_text()
# shows all the text imidiatly
# called by this thing itself or the DialogicGameHandler
func finish_text() -> void:
visible_ratio = 1
execute_effects(true)
timer.stop()
Dialogic.current_state = Dialogic.states.IDLE
emit_signal("finished_revealing_text")
func parse_effects(_text:String) -> String:
effects.clear()
var position_correction = 0
for effect_match in effect_regex.search_all(_text):
# append [index, command, value] to effects array
effects.append([effect_match.get_start()-position_correction, effect_match.get_string('command'), effect_match.get_string('value').strip_edges()])
## TODO MIGHT BE BROKEN, because I had to replace string.erase for godot 4
_text = _text.substr(0,effect_match.get_start()-position_correction)+_text.substr(effect_match.get_start()-position_correction+len(effect_match.get_string()))
position_correction += len(effect_match.get_string())
_text = _text.replace('\\[', '[')
return _text
func execute_effects(skip :bool= false) -> void:
# might have to execute multiple effects
while effects and (visible_characters >= effects.front()[0] or visible_characters== -1):
var effect = effects.pop_front()
match effect[1]:
'pause':
if skip:
continue
if effect[2].is_valid_float():
timer.start(effect[2].to_float())
else:
timer.start(1)
'speed':
if skip:
continue
if effect[2].is_valid_float():
speed = effect[2].to_float()
else:
speed = DialogicUtil.get_project_setting('dialogic/text/speed', 0.01)
'signal':
Dialogic.emit_signal("text_signal", effect[2])
'portrait':
if effect[2]:
if Dialogic.current_state_info.get('character', null):
Dialogic.Portraits.change_portrait(load(Dialogic.current_state_info.character), effect[2])
'mood':
if effect[2]:
if Dialogic.current_state_info.get('character', null):
var Character = load(Dialogic.current_state_info.character)
Dialogic.Text.update_typing_sound_mood(Character.custom_info.get('sound_moods', {}).get(effect[2], {}))
func parse_modifiers(_text:String) -> String:
# [word, select] effect
for replace_mod_match in modifier_words_select_regex.search_all(_text):
var string = replace_mod_match.get_string().trim_prefix("[").trim_suffix("]")
string = string.replace('\\,', '<comma>')
var list = string.split(',')
var item = list[randi()%len(list)]
item = item.replace('<comma>', ',')
_text = _text.replace(replace_mod_match.get_string(), item.strip_edges())
# [br] effect
_text = _text.replace('[br]', '\n')
return _text
func pause() -> void:
timer.stop()
func resume() -> void:
continue_reveal()