237 lines
8.9 KiB
GDScript3
237 lines
8.9 KiB
GDScript3
|
extends Node
|
||
|
class_name DialogicParser
|
||
|
|
||
|
|
||
|
# adds name coloring to the dialog texts
|
||
|
static func parse_characters(dialog_script):
|
||
|
var characters = DialogicUtil.get_character_list()
|
||
|
var event_index := 0
|
||
|
for event in dialog_script['events']:
|
||
|
# if this is a text or question event
|
||
|
if event.get('event_id') in ['dialogic_001', 'dialogic_010']:
|
||
|
var text :String = event.get({'dialogic_001':'text', 'dialogic_010':'question'}[event.get('event_id')], '')
|
||
|
for character in characters:
|
||
|
# check whether to use the name or the display name
|
||
|
var char_names = [character.get('name')]
|
||
|
if character.get('data', {}).get('display_name_bool', false):
|
||
|
if character.get('display_name'): char_names.append(character.get('display_name'))
|
||
|
if character.get('data', {}).get('nickname_bool', false):
|
||
|
for nickname in character.get('data').get('nickname', '').split(',', true, 0):
|
||
|
if nickname.strip_edges():
|
||
|
char_names.append(nickname.strip_edges())
|
||
|
|
||
|
#Regex purposefully excludes [] as replacing those interferes with the second regex
|
||
|
var escapeRegExp = "(?=[+&|!(){}^\"~*.?:\\\\-])"
|
||
|
|
||
|
var regex = RegEx.new()
|
||
|
regex.compile(escapeRegExp)
|
||
|
char_names = regex.sub(str(char_names), "\\", true)
|
||
|
|
||
|
var regex_thing = "((\\]|^)[^\\[]*)(?<name>"+str(char_names).replace("[", "(").replace("]", ")").replace(", ", "|")+")"
|
||
|
regex.compile(regex_thing)
|
||
|
|
||
|
var counter = 0
|
||
|
for result in regex.search_all(text):
|
||
|
text = text.insert(result.get_start("name")+((9+8+8)*counter), '[color=#' + character['color'].to_html() + ']')
|
||
|
text = text.insert(result.get_end("name")+9+8+((9+8+8)*counter), '[/color]')
|
||
|
result = regex.search(text)
|
||
|
counter += 1
|
||
|
|
||
|
dialog_script['events'][event_index][{'dialogic_001':'text', 'dialogic_010':'question'}[event.get('event_id')]] = text
|
||
|
|
||
|
event_index += 1
|
||
|
|
||
|
return dialog_script
|
||
|
|
||
|
|
||
|
# removes empty lines, splits message at new lines
|
||
|
static func parse_text_lines(unparsed_dialog_script: Dictionary, preview:bool = false) -> Dictionary:
|
||
|
var parsed_dialog: Dictionary = unparsed_dialog_script
|
||
|
var new_events: Array = []
|
||
|
var settings = DialogicResources.get_settings_config()
|
||
|
var split_new_lines = settings.get_value('dialog', 'new_lines', true)
|
||
|
var remove_empty_messages = settings.get_value('dialog', 'remove_empty_messages', true)
|
||
|
|
||
|
# Return the same thing if it doesn't have events
|
||
|
if unparsed_dialog_script.has('events') == false:
|
||
|
return unparsed_dialog_script
|
||
|
|
||
|
# Parsing
|
||
|
for event in unparsed_dialog_script['events']:
|
||
|
if event.has('text') and event.has('character') and event.has('portrait'):
|
||
|
if event['text'].empty() and remove_empty_messages:
|
||
|
pass
|
||
|
elif '\n' in event['text'] and preview == false and split_new_lines:
|
||
|
var lines = event['text'].split('\n')
|
||
|
var counter = 0
|
||
|
for line in lines:
|
||
|
if not line.empty():
|
||
|
var n_event = {
|
||
|
'event_id':'dialogic_001',
|
||
|
'text': line,
|
||
|
'character': event['character'],
|
||
|
'portrait': event['portrait'],
|
||
|
}
|
||
|
#assigning voices to the new events
|
||
|
if event.has('voice_data'):
|
||
|
if event['voice_data'].has(str(counter)):
|
||
|
n_event['voice_data'] = {'0':event['voice_data'][str(counter)]}
|
||
|
new_events.append(n_event)
|
||
|
counter += 1
|
||
|
else:
|
||
|
new_events.append(event)
|
||
|
else:
|
||
|
new_events.append(event)
|
||
|
|
||
|
parsed_dialog['events'] = new_events
|
||
|
|
||
|
return parsed_dialog
|
||
|
|
||
|
|
||
|
# returns the text but with BBcode for glossary and the values of the variables
|
||
|
static func parse_definitions(current_dialog, text: String, variables: bool = true, glossary: bool = true):
|
||
|
var final_text: String = text
|
||
|
if not current_dialog.preview:
|
||
|
current_dialog.definitions = Dialogic._get_definitions()
|
||
|
if variables:
|
||
|
final_text = _insert_variable_definitions(current_dialog, text)
|
||
|
if glossary and current_dialog._should_show_glossary():
|
||
|
final_text = _insert_glossary_definitions(current_dialog, final_text)
|
||
|
return final_text
|
||
|
|
||
|
|
||
|
# creates a list of questions to be used at the end of choices
|
||
|
static func parse_branches(current_dialog, dialog_script: Dictionary) -> Dictionary:
|
||
|
current_dialog.questions = [] # Resetting the questions
|
||
|
|
||
|
# Return the same thing if it doesn't have events
|
||
|
if dialog_script.has('events') == false:
|
||
|
return dialog_script
|
||
|
|
||
|
var parser_queue = [] # This saves the last question opened, and it gets removed once it was consumed by a endbranch event
|
||
|
var event_idx: int = 0 # The current id for jumping later on
|
||
|
var question_idx: int = 0 # identifying the questions to assign options to it
|
||
|
for event in dialog_script['events']:
|
||
|
if event['event_id'] == 'dialogic_011':
|
||
|
var opened_branch = parser_queue.back()
|
||
|
var option = {
|
||
|
'question_idx': opened_branch['question_idx'],
|
||
|
'label': parse_definitions(current_dialog, event['choice'], true, false),
|
||
|
'event_idx': event_idx,
|
||
|
}
|
||
|
if event.has('condition') and event.has('definition') and event.has('value'):
|
||
|
option = {
|
||
|
'question_idx': opened_branch['question_idx'],
|
||
|
'label': parse_definitions(current_dialog, event['choice'], true, false),
|
||
|
'event_idx': event_idx,
|
||
|
'condition': event['condition'],
|
||
|
'definition': event['definition'],
|
||
|
'value': event['value'],
|
||
|
}
|
||
|
else:
|
||
|
option = {
|
||
|
'question_idx': opened_branch['question_idx'],
|
||
|
'label': parse_definitions(current_dialog, event['choice'], true, false),
|
||
|
'event_idx': event_idx,
|
||
|
'condition': '',
|
||
|
'definition': '',
|
||
|
'value': '',
|
||
|
}
|
||
|
dialog_script['events'][opened_branch['event_idx']]['options'].append(option)
|
||
|
event['question_idx'] = opened_branch['question_idx']
|
||
|
elif event['event_id'] == 'dialogic_010':
|
||
|
event['event_idx'] = event_idx
|
||
|
event['question_idx'] = question_idx
|
||
|
event['answered'] = false
|
||
|
question_idx += 1
|
||
|
current_dialog.questions.append(event)
|
||
|
parser_queue.append(event)
|
||
|
elif event['event_id'] == 'dialogic_012':
|
||
|
event['event_idx'] = event_idx
|
||
|
event['question_idx'] = question_idx
|
||
|
event['answered'] = false
|
||
|
question_idx += 1
|
||
|
current_dialog.questions.append(event)
|
||
|
parser_queue.append(event)
|
||
|
elif event['event_id'] == 'dialogic_013' and parser_queue:
|
||
|
event['event_idx'] = event_idx
|
||
|
var opened_branch = parser_queue.pop_back()
|
||
|
event['end_branch_of'] = opened_branch['question_idx']
|
||
|
dialog_script['events'][opened_branch['event_idx']]['end_idx'] = event_idx
|
||
|
event_idx += 1
|
||
|
|
||
|
return dialog_script
|
||
|
|
||
|
|
||
|
static func parse_anchors(current_dialog):
|
||
|
current_dialog.anchors = {}
|
||
|
var idx = 0
|
||
|
for event in current_dialog.dialog_script['events']:
|
||
|
if event['event_id'] == 'dialogic_015':
|
||
|
current_dialog.anchors[event['id']] = idx
|
||
|
idx += 1
|
||
|
|
||
|
|
||
|
# adds the alignment BBCode to text events
|
||
|
static func parse_alignment(current_dialog, text):
|
||
|
var alignment = current_dialog.current_theme.get_value('text', 'alignment', 0)
|
||
|
var fname = current_dialog.current_theme.get_value('settings', 'name', 'none')
|
||
|
if alignment in [1,4,7]:
|
||
|
text = '[center]' + text + '[/center]'
|
||
|
elif alignment in [2,5,8]:
|
||
|
text = '[right]' + text + '[/right]'
|
||
|
return text
|
||
|
|
||
|
|
||
|
# adds the values of the variables
|
||
|
static func _insert_variable_definitions(current_dialog, text: String):
|
||
|
var final_text := text;
|
||
|
|
||
|
# Regex for searching text inside brackets []
|
||
|
var regex = RegEx.new()
|
||
|
regex.compile('\\[(.*?)\\]')
|
||
|
var result = regex.search_all(final_text)
|
||
|
if result:
|
||
|
for res in result:
|
||
|
var r_string = res.get_string()
|
||
|
# Choosing a random word if there is a list like [word1,word2,word3,word4]
|
||
|
if ',' in r_string:
|
||
|
var r_string_array = r_string.replace('[', '').replace(']', '').split(',')
|
||
|
var new_word = r_string_array[randi() % r_string_array.size()]
|
||
|
# Check if the random selected word is a variable that exists and get the value
|
||
|
for d in current_dialog.definitions['variables']:
|
||
|
var name : String = d['name']
|
||
|
if new_word == d['name']:
|
||
|
new_word = str(d['value'])
|
||
|
# Replace the old string with the new word
|
||
|
final_text = final_text.replace(r_string, new_word)
|
||
|
else:
|
||
|
# Replace the name of a value [whatever] with the result
|
||
|
var r_string_array = r_string.replace('[', '').replace(']', '')
|
||
|
|
||
|
# Find the ID if it's got an absolute path
|
||
|
if '/' in r_string_array:
|
||
|
var variable_id=Dialogic._get_variable_from_file_name(r_string_array)
|
||
|
for d in current_dialog.definitions['variables']:
|
||
|
if d['id'] == variable_id:
|
||
|
final_text = final_text.replace(r_string, d['value'])
|
||
|
else:
|
||
|
for d in current_dialog.definitions['variables']:
|
||
|
if d['name'] == r_string_array:
|
||
|
final_text = final_text.replace(r_string, d['value'])
|
||
|
|
||
|
return final_text
|
||
|
|
||
|
# adds the BBCode for the glossary words
|
||
|
static func _insert_glossary_definitions(current_dialog, text: String):
|
||
|
var color = current_dialog.current_theme.get_value('definitions', 'color', '#ffbebebe')
|
||
|
var final_text := text
|
||
|
# I should use regex here, but this is way easier :)
|
||
|
for d in current_dialog.definitions['glossary']:
|
||
|
final_text = final_text.replace(d['name'],
|
||
|
'[url=' + d['id'] + ']' +
|
||
|
'[color=' + color + ']' + d['name'] + '[/color]' +
|
||
|
'[/url]'
|
||
|
)
|
||
|
return final_text
|