|
|
@@ -0,0 +1,232 @@ |
|
|
|
tool |
|
|
|
extends EditorImportPlugin |
|
|
|
|
|
|
|
var YarnScript = preload("yarn_script.gd") |
|
|
|
|
|
|
|
enum Presets { DEFAULT } |
|
|
|
|
|
|
|
# types |
|
|
|
const EMPTY = "empty" |
|
|
|
const NODE_BODY_END = "node_body_end" |
|
|
|
const NODE_BODY_START = "node_body_start" |
|
|
|
const NODE_TAG = "node_tag" |
|
|
|
const JUMP = "jump" |
|
|
|
const TEXT = "text" |
|
|
|
const CHOICE = "choice" |
|
|
|
const CHOICE_BLOCKS = "choice_blocks" |
|
|
|
const CONDITION_IF = "if" |
|
|
|
const CONDITION_ELSE = "else" |
|
|
|
const CONDITION_ELSEIF = "elseif" |
|
|
|
const CONDITION_ENDIF = "endif" |
|
|
|
const CONDITION_BLOCKS = "condition_blocks" |
|
|
|
const COMMAND = "command" |
|
|
|
|
|
|
|
# Entry point |
|
|
|
|
|
|
|
func import(source_file, save_path, options, r_platform_variants, r_gen_files): |
|
|
|
var file : File = File.new() |
|
|
|
var err := file.open(source_file, File.READ) |
|
|
|
if err != OK: |
|
|
|
return err |
|
|
|
|
|
|
|
var yarn = (YarnScript).new() |
|
|
|
yarn.nodes = {} |
|
|
|
|
|
|
|
while not file.eof_reached(): |
|
|
|
var node = parse_header(file) |
|
|
|
node["body"] = parse_body(file) |
|
|
|
if node["title"] != "": |
|
|
|
yarn.nodes[node["title"]] = node |
|
|
|
|
|
|
|
file.close() |
|
|
|
|
|
|
|
return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], yarn) |
|
|
|
|
|
|
|
# Nodes parsing |
|
|
|
|
|
|
|
func parse_header(file : File): |
|
|
|
var new_node = {"title": "", "tags": "", "body":[]} |
|
|
|
|
|
|
|
while not file.eof_reached(): |
|
|
|
var parsed_line = parse_line_header(file.get_line()) |
|
|
|
var type = parsed_line["type"] |
|
|
|
|
|
|
|
# print("[" + type + "]\n\t" + str(parsed_line)) |
|
|
|
|
|
|
|
if type == EMPTY: |
|
|
|
pass |
|
|
|
elif type == NODE_BODY_START: |
|
|
|
break |
|
|
|
elif type == NODE_TAG: |
|
|
|
new_node[parsed_line["key"]] = parsed_line["value"] |
|
|
|
|
|
|
|
return new_node |
|
|
|
|
|
|
|
func parse_body(file): |
|
|
|
var body = [] |
|
|
|
|
|
|
|
while not file.eof_reached(): |
|
|
|
var parsed_line = parse_line_body(file.get_line()) |
|
|
|
var type = parsed_line["type"] |
|
|
|
|
|
|
|
# print("[" + type + "]\n\t" + str(parsed_line)) |
|
|
|
|
|
|
|
if type == EMPTY: |
|
|
|
pass |
|
|
|
elif type == NODE_BODY_END: |
|
|
|
break |
|
|
|
elif type == CONDITION_IF: |
|
|
|
body.push_back(parse_condition_blocks(file, parsed_line)) |
|
|
|
elif type == CHOICE: |
|
|
|
if body.size() == 0 or body[body.size() - 1]["type"] != CHOICE_BLOCKS: |
|
|
|
body.push_back({"type":CHOICE_BLOCKS, "blocks":[]}) |
|
|
|
body[body.size() -1]["blocks"].push_back(parsed_line) |
|
|
|
else: |
|
|
|
body.push_back(parsed_line) |
|
|
|
|
|
|
|
return body |
|
|
|
|
|
|
|
func parse_condition_blocks(file, parsed_line): |
|
|
|
var condition_blocks = {"type":CONDITION_BLOCKS, "blocks":[]} |
|
|
|
|
|
|
|
var current_block = {"condition":parsed_line["expression"], "body":[]} |
|
|
|
|
|
|
|
while not file.eof_reached(): |
|
|
|
parsed_line = parse_line_body(file.get_line()) |
|
|
|
var type = parsed_line["type"] |
|
|
|
|
|
|
|
# print("[" + type + "]\n\t" + str(parsed_line)) |
|
|
|
|
|
|
|
if type == EMPTY: |
|
|
|
pass |
|
|
|
elif type == CONDITION_IF: |
|
|
|
current_block["body"].push_back(parse_condition_blocks(file, parsed_line)) |
|
|
|
elif type == CONDITION_ELSEIF: |
|
|
|
condition_blocks["blocks"].push_back(current_block) |
|
|
|
current_block = {"condition":parsed_line["expression"], "body":[]} |
|
|
|
elif type == CONDITION_ELSE: |
|
|
|
condition_blocks["blocks"].push_back(current_block) |
|
|
|
current_block = {"condition":parsed_line["expression"], "body":[]} |
|
|
|
elif type == CONDITION_ENDIF: |
|
|
|
condition_blocks["blocks"].push_back(current_block) |
|
|
|
break |
|
|
|
else: |
|
|
|
current_block["body"].push_back(parsed_line) |
|
|
|
|
|
|
|
return condition_blocks |
|
|
|
|
|
|
|
# Lines Parsing |
|
|
|
|
|
|
|
func parse_line_header(raw_line : String): |
|
|
|
var parsed_line = {"type":"????", "raw":raw_line} |
|
|
|
|
|
|
|
if raw_line.strip_edges() == "": |
|
|
|
parsed_line["type"] = EMPTY |
|
|
|
elif raw_line.begins_with("---"): |
|
|
|
parsed_line["type"] = NODE_BODY_START |
|
|
|
else: |
|
|
|
var split = raw_line.split(':') |
|
|
|
if split.size() >= 2: |
|
|
|
parsed_line["type"] = NODE_TAG |
|
|
|
parsed_line["key"] = split[0] |
|
|
|
parsed_line["value"] = split[1].strip_edges() |
|
|
|
|
|
|
|
return parsed_line |
|
|
|
|
|
|
|
func parse_line_body(raw_line : String): |
|
|
|
#todo later, use indentation |
|
|
|
raw_line = raw_line.strip_edges() |
|
|
|
|
|
|
|
var parsed_line = {"type":"????", "raw":raw_line} |
|
|
|
|
|
|
|
if raw_line.strip_edges() == "": |
|
|
|
parsed_line["type"] = EMPTY |
|
|
|
elif raw_line.begins_with("==="): |
|
|
|
parsed_line["type"] = NODE_BODY_END |
|
|
|
elif raw_line.begins_with("[["): |
|
|
|
var split := raw_line.replace("[[", "").replace("]]", "").split("|") |
|
|
|
if split.size() > 1: |
|
|
|
parsed_line["type"] = CHOICE |
|
|
|
parsed_line["text"] = split[0] |
|
|
|
parsed_line["node"] = split[1] |
|
|
|
else: |
|
|
|
parsed_line["type"] = JUMP |
|
|
|
parsed_line["node"] = split[0] |
|
|
|
elif raw_line.begins_with("<<"): |
|
|
|
var split := raw_line.replace("<<", "").replace(">>", "").strip_edges().split(' ') |
|
|
|
if split.size() > 0: |
|
|
|
if split[0] == "if": |
|
|
|
parsed_line["type"] = CONDITION_IF |
|
|
|
var expr := "" |
|
|
|
for token in Array(split).slice(1, split.size() -1): |
|
|
|
expr += token + " " |
|
|
|
parsed_line["expression"] = expr |
|
|
|
elif split[0] == "elseif": |
|
|
|
parsed_line["type"] = CONDITION_ELSEIF |
|
|
|
var expr := "" |
|
|
|
for token in Array(split).slice(1, split.size() -1): |
|
|
|
expr += token + " " |
|
|
|
parsed_line["expression"] = expr |
|
|
|
elif split[0] == "else": |
|
|
|
parsed_line["type"] = CONDITION_ELSE |
|
|
|
parsed_line["expression"] = "true" # add default expression always true |
|
|
|
elif split[0] == "endif": |
|
|
|
parsed_line["type"] = CONDITION_ENDIF |
|
|
|
else: |
|
|
|
parsed_line["type"] = COMMAND |
|
|
|
parsed_line["command"] = split[0] |
|
|
|
parsed_line["args"] = Array(split).slice(1, split.size() - 1) |
|
|
|
else: |
|
|
|
parsed_line["type"] = TEXT |
|
|
|
# use regex ? |
|
|
|
var separator_idx = raw_line.find(':') |
|
|
|
if separator_idx != -1: |
|
|
|
var chara = raw_line.substr(0, separator_idx).strip_edges() |
|
|
|
var text = raw_line.substr(separator_idx + 1 , -1).strip_edges() |
|
|
|
parsed_line["chara"] = chara |
|
|
|
parsed_line["text"] = text |
|
|
|
else: |
|
|
|
parsed_line["chara"] = "" |
|
|
|
parsed_line["text"] = raw_line.strip_edges() |
|
|
|
|
|
|
|
return parsed_line |
|
|
|
|
|
|
|
# Required editor functions |
|
|
|
|
|
|
|
func get_importer_name(): |
|
|
|
return "yarn.script" |
|
|
|
|
|
|
|
func get_visible_name(): |
|
|
|
return "Yarn Script" |
|
|
|
|
|
|
|
func get_recognized_extensions(): |
|
|
|
return ["yarn", "txt"] |
|
|
|
|
|
|
|
func get_save_extension(): |
|
|
|
return "res" |
|
|
|
|
|
|
|
func get_resource_type(): |
|
|
|
return "Resource" |
|
|
|
|
|
|
|
func get_preset_count(): |
|
|
|
return Presets.size() |
|
|
|
|
|
|
|
func get_preset_name(preset): |
|
|
|
match preset: |
|
|
|
Presets.DEFAULT: |
|
|
|
return "Default" |
|
|
|
_: |
|
|
|
return "Unknown" |
|
|
|
|
|
|
|
func get_import_options(preset): |
|
|
|
match preset: |
|
|
|
Presets.DEFAULT: |
|
|
|
return [{ |
|
|
|
"name": "use_red_anyway", |
|
|
|
"default_value": false |
|
|
|
}] |
|
|
|
_: |
|
|
|
return [] |
|
|
|
|
|
|
|
func get_option_visibility(option, options): |
|
|
|
return true |