| @@ -0,0 +1,3 @@ | |||
| source_md5="10b14a4273e3c8eb3d990fd5ce59afed" | |||
| dest_md5="06821280489269c54a81298e6d9b3949" | |||
| @@ -0,0 +1,3 @@ | |||
| source_md5="52422b01794998083b364be682eb840a" | |||
| dest_md5="d41d8cd98f00b204e9800998ecf8427e" | |||
| @@ -0,0 +1,3 @@ | |||
| source_md5="52422b01794998083b364be682eb840a" | |||
| dest_md5="d41d8cd98f00b204e9800998ecf8427e" | |||
| @@ -0,0 +1,3 @@ | |||
| source_md5="9da48ae0e2c8858122bdf1a8b8dd698e" | |||
| dest_md5="348cbedcb0148126d176d7cbcee8e36c" | |||
| @@ -0,0 +1,34 @@ | |||
| [remap] | |||
| importer="texture" | |||
| type="StreamTexture" | |||
| path="res://.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.stex" | |||
| metadata={ | |||
| "vram_texture": false | |||
| } | |||
| [deps] | |||
| source_file="res://addons/yarn_spinner/icon.png" | |||
| dest_files=[ "res://.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.stex" ] | |||
| [params] | |||
| compress/mode=0 | |||
| compress/lossy_quality=0.7 | |||
| compress/hdr_mode=0 | |||
| compress/bptc_ldr=0 | |||
| compress/normal_map=0 | |||
| flags/repeat=0 | |||
| flags/filter=true | |||
| flags/mipmaps=false | |||
| flags/anisotropic=false | |||
| flags/srgb=2 | |||
| process/fix_alpha_border=true | |||
| process/premult_alpha=false | |||
| process/HDR_as_SRGB=false | |||
| process/invert_color=false | |||
| stream=false | |||
| size_limit=0 | |||
| detect_3d=true | |||
| svg/scale=1.0 | |||
| @@ -0,0 +1,7 @@ | |||
| [plugin] | |||
| name="Yarn Spinner" | |||
| description="A Yarn Spinner importer and runner for Godot" | |||
| author="Arnaud Cojez" | |||
| version="0.0.1" | |||
| script="yarn_spinner_plugin.gd" | |||
| @@ -0,0 +1,51 @@ | |||
| extends Node | |||
| var YarnScript = preload("yarn_script.gd") | |||
| export (Resource) var yarnScript | |||
| var say_func : FuncRef = null | |||
| func run_all(): | |||
| var next_node = yarnScript.nodes["Start"] | |||
| while next_node != null: | |||
| print("-> Jump to " + next_node["title"]) | |||
| var next_node_key = run_body(next_node["body"]) | |||
| if (yarnScript.nodes.has(next_node_key)): | |||
| next_node = yarnScript.nodes[next_node_key] | |||
| else: | |||
| next_node = null | |||
| func run_body(body): | |||
| for element in body: | |||
| if element["type"] == "jump": | |||
| return element["node"] | |||
| elif element["type"] == "choice_blocks": | |||
| var block = decide_choice_block(element["blocks"]) | |||
| return block["node"] # todo change later with "-> choices" | |||
| elif element["type"] == "condition_blocks": | |||
| var block = decide_condition_block(element["blocks"]) | |||
| var next_node_key = run_body(block["body"]) | |||
| if next_node_key != "": | |||
| return next_node_key | |||
| else: | |||
| if say_func == null or !say_func.is_valid(): | |||
| print(element) | |||
| else: | |||
| say_func.call_func(element) | |||
| return "" | |||
| export var choice = 0 | |||
| func decide_choice_block(blocks): | |||
| return blocks[choice] | |||
| func decide_condition_block(blocks): | |||
| for block in blocks: | |||
| var expr := Expression.new() | |||
| var err := expr.parse(block["condition"]) | |||
| if err == OK and expr.execute(): | |||
| return block | |||
| return null | |||
| @@ -0,0 +1,3 @@ | |||
| extends Resource | |||
| export var nodes = {} | |||
| @@ -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 | |||
| @@ -0,0 +1,16 @@ | |||
| tool | |||
| extends EditorPlugin | |||
| var yarn_spinner_importer | |||
| func _enter_tree(): | |||
| yarn_spinner_importer = preload("yarn_spinner_importer.gd").new() | |||
| add_custom_type("YarnScript", "Resource", preload("yarn_script.gd"), preload("icon.png")) | |||
| add_custom_type("YarnRunner", "Node", preload("yarn_runner.gd"), preload("icon.png")) | |||
| add_import_plugin(yarn_spinner_importer) | |||
| func _exit_tree(): | |||
| remove_custom_type("YarnScript") | |||
| remove_custom_type("YarnRunner") | |||
| remove_import_plugin(yarn_spinner_importer) | |||
| yarn_spinner_importer = null | |||
| @@ -12,8 +12,9 @@ tags: | |||
| position: 1032.2125854492188,730.3896484375 | |||
| --- | |||
| Huhuhu | |||
| <<if $truc is True>> | |||
| OK ! | |||
| <<if 1 > 2 >> | |||
| OK !! | |||
| [[zboub]] | |||
| <<else>> | |||
| Pas ok. | |||
| <<endif>> | |||
| @@ -0,0 +1,14 @@ | |||
| [remap] | |||
| importer="yarn.script" | |||
| type="Resource" | |||
| path="res://.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.res" | |||
| [deps] | |||
| source_file="res://main/dialogues/test.yarn" | |||
| dest_files=[ "res://.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.res" ] | |||
| [params] | |||
| use_red_anyway=false | |||
| @@ -0,0 +1,21 @@ | |||
| [gd_scene load_steps=4 format=2] | |||
| [ext_resource path="res://main/scripts/test_viteuf.gd" type="Script" id=1] | |||
| [ext_resource path="res://main/dialogues/test.yarn" type="Resource" id=2] | |||
| [ext_resource path="res://addons/yarn_spinner/yarn_runner.gd" type="Script" id=3] | |||
| [node name="Node" type="Control"] | |||
| anchor_right = 1.0 | |||
| anchor_bottom = 1.0 | |||
| script = ExtResource( 1 ) | |||
| __meta__ = { | |||
| "_edit_use_anchors_": false | |||
| } | |||
| [node name="YarnRunner" type="Node" parent="."] | |||
| script = ExtResource( 3 ) | |||
| yarnScript = ExtResource( 2 ) | |||
| [node name="RichTextLabel" type="RichTextLabel" parent="."] | |||
| anchor_right = 1.0 | |||
| anchor_bottom = 1.0 | |||
| @@ -0,0 +1,24 @@ | |||
| extends Control | |||
| func _ready(): | |||
| $YarnRunner.say_func = funcref(self, "say") | |||
| $YarnRunner.run_all() | |||
| func say(element): | |||
| $RichTextLabel.text += str(element["raw"]) + "\n" | |||
| func exec(stri): | |||
| var expr := Expression.new() | |||
| print("input : " + stri) | |||
| var err := expr.parse(stri) | |||
| if err == OK: | |||
| var res = expr.execute() | |||
| print("res : " + str(res)) | |||
| else: | |||
| print("err : " + str(err)) | |||
| func jajoujaj(): | |||
| print("jaaaja") | |||
| var current_node = "Start" | |||
| var current_line = 0 | |||
| @@ -35,6 +35,16 @@ _global_script_class_icons={ | |||
| config/name="Chepa" | |||
| config/icon="res://icon.png" | |||
| [editor_plugins] | |||
| enabled=PoolStringArray( "yarn_spinner" ) | |||
| [importer_defaults] | |||
| yarn.script={ | |||
| "use_red_anyway": false | |||
| } | |||
| [rendering] | |||
| quality/driver/driver_name="GLES2" | |||
| @@ -80,8 +80,21 @@ func new_yarn_thread(): | |||
| thread['fibres'] = [] | |||
| return thread | |||
| func exec(stri): | |||
| var expr := Expression.new() | |||
| print("input : " + stri) | |||
| var err := expr.parse(stri) | |||
| if err == OK: | |||
| var res = expr.execute() | |||
| print("res : " + str(res)) | |||
| else: | |||
| print("err : " + str(err)) | |||
| func jajoujaj(): | |||
| print("jaaaja") | |||
| # Internally create a new fibre (during loading) | |||
| func new_yarn_fibre(line): | |||
| func new_yarn_fibre(line:String): | |||
| # choice fibre | |||
| if line.substr(0,2) == '[[': | |||
| if line.find('|') != -1: | |||
| @@ -102,16 +115,22 @@ func new_yarn_fibre(line): | |||
| return fibre | |||
| # logic instruction (not part of official Yarn standard) | |||
| elif line.substr(0,2) == '<<': | |||
| if line.find(':') != -1: | |||
| var fibre = {} | |||
| fibre['kind'] = 'logic' | |||
| line = line.replace('<<', '') | |||
| line = line.replace('>>', '') | |||
| var split = line.split(':') | |||
| fibre['instruction'] = split[0] | |||
| fibre['command'] = split[1] | |||
| #print(line, split[0], split[1]) | |||
| return fibre | |||
| line = line.replace('<<', '') | |||
| line = line.replace('>>', '') | |||
| var line_split = line.split(' ') | |||
| print (line_split) | |||
| var command = line.substr(line.find(' ')) | |||
| exec(command) | |||
| # if line.find(':') != -1: | |||
| # fibre['kind'] = 'logic' | |||
| # line = line.replace('<<', '') | |||
| # line = line.replace('>>', '') | |||
| # var split = line.split(':') | |||
| # fibre['instruction'] = split[0] | |||
| # fibre['command'] = split[1] | |||
| # #print(line, split[0], split[1]) | |||
| # return fibre | |||
| # text fibre | |||
| var fibre = {} | |||
| fibre['kind'] = 'text' | |||