| @@ -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 | position: 1032.2125854492188,730.3896484375 | ||||
| --- | --- | ||||
| Huhuhu | Huhuhu | ||||
| <<if $truc is True>> | |||||
| OK ! | |||||
| <<if 1 > 2 >> | |||||
| OK !! | |||||
| [[zboub]] | |||||
| <<else>> | <<else>> | ||||
| Pas ok. | Pas ok. | ||||
| <<endif>> | <<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/name="Chepa" | ||||
| config/icon="res://icon.png" | config/icon="res://icon.png" | ||||
| [editor_plugins] | |||||
| enabled=PoolStringArray( "yarn_spinner" ) | |||||
| [importer_defaults] | |||||
| yarn.script={ | |||||
| "use_red_anyway": false | |||||
| } | |||||
| [rendering] | [rendering] | ||||
| quality/driver/driver_name="GLES2" | quality/driver/driver_name="GLES2" | ||||
| @@ -80,8 +80,21 @@ func new_yarn_thread(): | |||||
| thread['fibres'] = [] | thread['fibres'] = [] | ||||
| return thread | 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) | # Internally create a new fibre (during loading) | ||||
| func new_yarn_fibre(line): | |||||
| func new_yarn_fibre(line:String): | |||||
| # choice fibre | # choice fibre | ||||
| if line.substr(0,2) == '[[': | if line.substr(0,2) == '[[': | ||||
| if line.find('|') != -1: | if line.find('|') != -1: | ||||
| @@ -102,16 +115,22 @@ func new_yarn_fibre(line): | |||||
| return fibre | return fibre | ||||
| # logic instruction (not part of official Yarn standard) | # logic instruction (not part of official Yarn standard) | ||||
| elif line.substr(0,2) == '<<': | 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 | # text fibre | ||||
| var fibre = {} | var fibre = {} | ||||
| fibre['kind'] = 'text' | fibre['kind'] = 'text' | ||||