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