168 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
| ## Any data associated with inline dialogue BBCodes.
 | |
| class_name DMResolvedLineData extends RefCounted
 | |
| 
 | |
| ## The line's text
 | |
| var text: String = ""
 | |
| ## A map of pauses against where they are found in the text.
 | |
| var pauses: Dictionary = {}
 | |
| ## A map of speed changes against where they are found in the text.
 | |
| var speeds: Dictionary = {}
 | |
| ## A list of any mutations to run and where they are found in the text.
 | |
| var mutations: Array[Array] = []
 | |
| ## A duration reference for the line. Represented as "auto" or a stringified number.
 | |
| var time: String = ""
 | |
| 
 | |
| 
 | |
| func _init(line: String) -> void:
 | |
| 	text = line
 | |
| 	pauses = {}
 | |
| 	speeds = {}
 | |
| 	mutations = []
 | |
| 	time = ""
 | |
| 
 | |
| 	var bbcodes: Array = []
 | |
| 
 | |
| 	# Remove any escaped brackets (ie. "\[")
 | |
| 	var escaped_open_brackets: PackedInt32Array = []
 | |
| 	var escaped_close_brackets: PackedInt32Array = []
 | |
| 	for i in range(0, text.length() - 1):
 | |
| 		if text.substr(i, 2) == "\\[":
 | |
| 			text = text.substr(0, i) + "!" + text.substr(i + 2)
 | |
| 			escaped_open_brackets.append(i)
 | |
| 		elif text.substr(i, 2) == "\\]":
 | |
| 			text = text.substr(0, i) + "!" + text.substr(i + 2)
 | |
| 			escaped_close_brackets.append(i)
 | |
| 
 | |
| 	# Extract all of the BB codes so that we know the actual text (we could do this easier with
 | |
| 	# a RichTextLabel but then we'd need to await idle_frame which is annoying)
 | |
| 	var bbcode_positions = find_bbcode_positions_in_string(text)
 | |
| 	var accumulaive_length_offset = 0
 | |
| 	for position in bbcode_positions:
 | |
| 		# Ignore our own markers
 | |
| 		if position.code in ["wait", "speed", "/speed", "do", "do!", "set", "next", "if", "else", "/if"]:
 | |
| 			continue
 | |
| 
 | |
| 		bbcodes.append({
 | |
| 			bbcode = position.bbcode,
 | |
| 			start = position.start,
 | |
| 			offset_start = position.start - accumulaive_length_offset
 | |
| 		})
 | |
| 		accumulaive_length_offset += position.bbcode.length()
 | |
| 
 | |
| 	for bb in bbcodes:
 | |
| 		text = text.substr(0, bb.offset_start) + text.substr(bb.offset_start + bb.bbcode.length())
 | |
| 
 | |
| 	# Now find any dialogue markers
 | |
| 	var next_bbcode_position = find_bbcode_positions_in_string(text, false)
 | |
| 	var limit = 0
 | |
| 	while next_bbcode_position.size() > 0 and limit < 1000:
 | |
| 		limit += 1
 | |
| 
 | |
| 		var bbcode = next_bbcode_position[0]
 | |
| 
 | |
| 		var index = bbcode.start
 | |
| 		var code = bbcode.code
 | |
| 		var raw_args = bbcode.raw_args
 | |
| 		var args = {}
 | |
| 		if code in ["do", "do!", "set"]:
 | |
| 			var compilation: DMCompilation = DMCompilation.new()
 | |
| 			args["value"] = compilation.extract_mutation("%s %s" % [code, raw_args])
 | |
| 		else:
 | |
| 			# Could be something like:
 | |
| 			# 	"=1.0"
 | |
| 			# 	" rate=20 level=10"
 | |
| 			if raw_args and raw_args[0] == "=":
 | |
| 				raw_args = "value" + raw_args
 | |
| 			for pair in raw_args.strip_edges().split(" "):
 | |
| 				if "=" in pair:
 | |
| 					var bits = pair.split("=")
 | |
| 					args[bits[0]] = bits[1]
 | |
| 
 | |
| 		match code:
 | |
| 			"wait":
 | |
| 				if pauses.has(index):
 | |
| 					pauses[index] += args.get("value").to_float()
 | |
| 				else:
 | |
| 					pauses[index] = args.get("value").to_float()
 | |
| 			"speed":
 | |
| 				speeds[index] = args.get("value").to_float()
 | |
| 			"/speed":
 | |
| 				speeds[index] = 1.0
 | |
| 			"do", "do!", "set":
 | |
| 				mutations.append([index, args.get("value")])
 | |
| 			"next":
 | |
| 				time = args.get("value") if args.has("value") else "0"
 | |
| 
 | |
| 		# Find any BB codes that are after this index and remove the length from their start
 | |
| 		var length = bbcode.bbcode.length()
 | |
| 		for bb in bbcodes:
 | |
| 			if bb.offset_start > bbcode.start:
 | |
| 				bb.offset_start -= length
 | |
| 				bb.start -= length
 | |
| 
 | |
| 		# Find any escaped brackets after this that need moving
 | |
| 		for i in range(0, escaped_open_brackets.size()):
 | |
| 			if escaped_open_brackets[i] > bbcode.start:
 | |
| 				escaped_open_brackets[i] -= length
 | |
| 		for i in range(0, escaped_close_brackets.size()):
 | |
| 			if escaped_close_brackets[i] > bbcode.start:
 | |
| 				escaped_close_brackets[i] -= length
 | |
| 
 | |
| 		text = text.substr(0, index) + text.substr(index + length)
 | |
| 		next_bbcode_position = find_bbcode_positions_in_string(text, false)
 | |
| 
 | |
| 	# Put the BB Codes back in
 | |
| 	for bb in bbcodes:
 | |
| 		text = text.insert(bb.start, bb.bbcode)
 | |
| 
 | |
| 	# Put the escaped brackets back in
 | |
| 	for index in escaped_open_brackets:
 | |
| 		text = text.left(index) + "[" + text.right(text.length() - index - 1)
 | |
| 	for index in escaped_close_brackets:
 | |
| 		text = text.left(index) + "]" + text.right(text.length() - index - 1)
 | |
| 
 | |
| 
 | |
| func find_bbcode_positions_in_string(string: String, find_all: bool = true, include_conditions: bool = false) -> Array[Dictionary]:
 | |
| 	if not "[" in string: return []
 | |
| 
 | |
| 	var positions: Array[Dictionary] = []
 | |
| 
 | |
| 	var open_brace_count: int = 0
 | |
| 	var start: int = 0
 | |
| 	var bbcode: String = ""
 | |
| 	var code: String = ""
 | |
| 	var is_finished_code: bool = false
 | |
| 	for i in range(0, string.length()):
 | |
| 		if string[i] == "[":
 | |
| 			if open_brace_count == 0:
 | |
| 				start = i
 | |
| 				bbcode = ""
 | |
| 				code = ""
 | |
| 				is_finished_code = false
 | |
| 			open_brace_count += 1
 | |
| 
 | |
| 		else:
 | |
| 			if not is_finished_code and (string[i].to_upper() != string[i] or string[i] == "/" or string[i] == "!"):
 | |
| 				code += string[i]
 | |
| 			else:
 | |
| 				is_finished_code = true
 | |
| 
 | |
| 		if open_brace_count > 0:
 | |
| 			bbcode += string[i]
 | |
| 
 | |
| 		if string[i] == "]":
 | |
| 			open_brace_count -= 1
 | |
| 			if open_brace_count == 0 and (include_conditions or not code in ["if", "else", "/if"]):
 | |
| 				positions.append({
 | |
| 					bbcode = bbcode,
 | |
| 					code = code,
 | |
| 					start = start,
 | |
| 					end = i,
 | |
| 					raw_args = bbcode.substr(code.length() + 1, bbcode.length() - code.length() - 2).strip_edges()
 | |
| 				})
 | |
| 
 | |
| 				if not find_all:
 | |
| 					return positions
 | |
| 
 | |
| 	return positions
 | 
