diff --git a/.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.md5 b/.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.md5 new file mode 100644 index 0000000..725781a --- /dev/null +++ b/.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.md5 @@ -0,0 +1,3 @@ +source_md5="10b14a4273e3c8eb3d990fd5ce59afed" +dest_md5="06821280489269c54a81298e6d9b3949" + diff --git a/.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.stex b/.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.stex new file mode 100644 index 0000000..56ae2be Binary files /dev/null and b/.import/icon.png-6587814821ca75f730cf7dfe8a5167cb.stex differ diff --git a/.import/test - Copie.txt-05f23fb6780d7fb8747b6c25d01e926c.md5 b/.import/test - Copie.txt-05f23fb6780d7fb8747b6c25d01e926c.md5 new file mode 100644 index 0000000..8f138d0 --- /dev/null +++ b/.import/test - Copie.txt-05f23fb6780d7fb8747b6c25d01e926c.md5 @@ -0,0 +1,3 @@ +source_md5="52422b01794998083b364be682eb840a" +dest_md5="d41d8cd98f00b204e9800998ecf8427e" + diff --git a/.import/test - Copie.yarn-bfaf9553976186f49f9681f51669695e.md5 b/.import/test - Copie.yarn-bfaf9553976186f49f9681f51669695e.md5 new file mode 100644 index 0000000..8f138d0 --- /dev/null +++ b/.import/test - Copie.yarn-bfaf9553976186f49f9681f51669695e.md5 @@ -0,0 +1,3 @@ +source_md5="52422b01794998083b364be682eb840a" +dest_md5="d41d8cd98f00b204e9800998ecf8427e" + diff --git a/.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.md5 b/.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.md5 new file mode 100644 index 0000000..edec5b6 --- /dev/null +++ b/.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.md5 @@ -0,0 +1,3 @@ +source_md5="9da48ae0e2c8858122bdf1a8b8dd698e" +dest_md5="348cbedcb0148126d176d7cbcee8e36c" + diff --git a/.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.res b/.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.res new file mode 100644 index 0000000..96e23c5 Binary files /dev/null and b/.import/test.yarn-642d0590cee80e9b0bd554c3bdc33727.res differ diff --git a/addons/yarn_spinner/icon.png b/addons/yarn_spinner/icon.png new file mode 100644 index 0000000..4f3b4ff Binary files /dev/null and b/addons/yarn_spinner/icon.png differ diff --git a/addons/yarn_spinner/icon.png.import b/addons/yarn_spinner/icon.png.import new file mode 100644 index 0000000..69841d2 --- /dev/null +++ b/addons/yarn_spinner/icon.png.import @@ -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 diff --git a/addons/yarn_spinner/plugin.cfg b/addons/yarn_spinner/plugin.cfg new file mode 100644 index 0000000..4a32b2e --- /dev/null +++ b/addons/yarn_spinner/plugin.cfg @@ -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" diff --git a/addons/yarn_spinner/yarn_runner.gd b/addons/yarn_spinner/yarn_runner.gd new file mode 100644 index 0000000..a40a198 --- /dev/null +++ b/addons/yarn_spinner/yarn_runner.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 diff --git a/addons/yarn_spinner/yarn_script.gd b/addons/yarn_spinner/yarn_script.gd new file mode 100644 index 0000000..9b5965f --- /dev/null +++ b/addons/yarn_spinner/yarn_script.gd @@ -0,0 +1,3 @@ +extends Resource + +export var nodes = {} diff --git a/addons/yarn_spinner/yarn_spinner_importer.gd b/addons/yarn_spinner/yarn_spinner_importer.gd new file mode 100644 index 0000000..8f32876 --- /dev/null +++ b/addons/yarn_spinner/yarn_spinner_importer.gd @@ -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 diff --git a/addons/yarn_spinner/yarn_spinner_plugin.gd b/addons/yarn_spinner/yarn_spinner_plugin.gd new file mode 100644 index 0000000..4cf15a3 --- /dev/null +++ b/addons/yarn_spinner/yarn_spinner_plugin.gd @@ -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 diff --git a/main/dialogues/test.yarn b/main/dialogues/test.yarn index aacda39..3ed5583 100644 --- a/main/dialogues/test.yarn +++ b/main/dialogues/test.yarn @@ -12,8 +12,9 @@ tags: position: 1032.2125854492188,730.3896484375 --- Huhuhu -<> - OK ! +< 2 >> + OK !! + [[zboub]] <> Pas ok. <> diff --git a/main/dialogues/test.yarn.import b/main/dialogues/test.yarn.import new file mode 100644 index 0000000..3712340 --- /dev/null +++ b/main/dialogues/test.yarn.import @@ -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 diff --git a/main/scenes/test_viteuf.tscn b/main/scenes/test_viteuf.tscn new file mode 100644 index 0000000..299333b --- /dev/null +++ b/main/scenes/test_viteuf.tscn @@ -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 diff --git a/main/scripts/test_viteuf.gd b/main/scripts/test_viteuf.gd new file mode 100644 index 0000000..97570b9 --- /dev/null +++ b/main/scripts/test_viteuf.gd @@ -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 diff --git a/project.godot b/project.godot index 564cf45..d7b1c9a 100644 --- a/project.godot +++ b/project.godot @@ -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" diff --git a/yarn/scripts/yarn-importer.gd b/yarn/scripts/yarn-importer.gd index 9e95ca5..7d8b313 100644 --- a/yarn/scripts/yarn-importer.gd +++ b/yarn/scripts/yarn-importer.gd @@ -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'