@@ -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' | |||