|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- 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:
- _:
- return []
-
- func get_option_visibility(option, options):
- return true
|