init
This commit is contained in:
commit
b99855351d
434 changed files with 50357 additions and 0 deletions
200
addons/inkgd/runtime/static/ink_runtime.gd
Normal file
200
addons/inkgd/runtime/static/ink_runtime.gd
Normal file
|
@ -0,0 +1,200 @@
|
|||
# warning-ignore-all:unused_class_variable
|
||||
# warning-ignore-all:shadowed_variable
|
||||
# ############################################################################ #
|
||||
# Copyright © 2015-2021 inkle Ltd.
|
||||
# Copyright © 2019-2022 Frédéric Maquin <fred@ephread.com>
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This file is part of inkgd.
|
||||
# inkgd is licensed under the terms of the MIT license.
|
||||
# ############################################################################ #
|
||||
|
||||
extends Node
|
||||
|
||||
# Hiding this type to prevent registration of "private" nodes.
|
||||
# See https://github.com/godotengine/godot-proposals/issues/1047
|
||||
# class_name InkRuntimeNode
|
||||
|
||||
# Expected to be added to the SceneTree as a singleton object.
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
var InkStaticJSON := load("res://addons/inkgd/runtime/static/json.gd") as GDScript
|
||||
var InkStaticNativeFunctionCall := load("res://addons/inkgd/runtime/static/native_function_call.gd") as GDScript
|
||||
|
||||
# ############################################################################ #
|
||||
# Signals
|
||||
# ############################################################################ #
|
||||
|
||||
## Emitted when the runtime encountered an exception. Exception are not
|
||||
## recoverable and may corrupt the state. They are the consequence of either
|
||||
## a programmer error or a bug in the runtime.
|
||||
signal exception_raised(message, stack_trace)
|
||||
|
||||
# ############################################################################ #
|
||||
# Properties
|
||||
# ############################################################################ #
|
||||
|
||||
# Skips saving global values that remain equal to the initial values that were
|
||||
# declared in Ink.
|
||||
var dont_save_default_values: bool = true
|
||||
|
||||
## Uses `assert` instead of `push_error` to report critical errors, thus
|
||||
## making them more explicit during development.
|
||||
var stop_execution_on_exception: bool = true
|
||||
|
||||
## Uses `assert` instead of `push_error` to report story errors, thus
|
||||
## making them more explicit during development.
|
||||
var stop_execution_on_error: bool = true
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var should_pause_execution_on_runtime_error: bool: get = get_speore, set = set_speore
|
||||
func get_speore() -> bool:
|
||||
printerr(
|
||||
"'should_pause_execution_on_runtime_error' is deprecated, " +
|
||||
"use 'stop_execution_on_exception' instead."
|
||||
)
|
||||
return stop_execution_on_exception
|
||||
func set_speore(value: bool):
|
||||
printerr(
|
||||
"'should_pause_execution_on_runtime_error' is deprecated, " +
|
||||
"use 'stop_execution_on_exception' instead."
|
||||
)
|
||||
stop_execution_on_exception = value
|
||||
|
||||
var should_pause_execution_on_story_error: bool: get = get_speose, set = set_speose
|
||||
func get_speose() -> bool:
|
||||
printerr(
|
||||
"'should_pause_execution_on_story_error' is deprecated, " +
|
||||
"use 'stop_execution_on_error' instead."
|
||||
)
|
||||
return stop_execution_on_error
|
||||
func set_speose(value: bool):
|
||||
printerr(
|
||||
"'should_pause_execution_on_story_error' is deprecated, " +
|
||||
"use 'stop_execution_on_error' instead."
|
||||
)
|
||||
stop_execution_on_error = value
|
||||
|
||||
# ############################################################################ #
|
||||
# "Static" Properties
|
||||
# ############################################################################ #
|
||||
|
||||
var native_function_call: InkStaticNativeFunctionCall = InkStaticNativeFunctionCall.new()
|
||||
var json: InkStaticJSON = InkStaticJSON.new(native_function_call)
|
||||
|
||||
# ############################################################################ #
|
||||
# Internal Properties
|
||||
# ############################################################################ #
|
||||
|
||||
# Recorded exceptions don't emit the 'exception' signal, since they are
|
||||
# expected to be processed by the story and emitted through 'on_error'.
|
||||
var record_story_exceptions: bool = false
|
||||
var current_story_exceptions: Array = []
|
||||
|
||||
var _argument_exception_raised: bool
|
||||
var _exception_raised: bool
|
||||
|
||||
# ############################################################################ #
|
||||
# Overrides
|
||||
# ############################################################################ #
|
||||
|
||||
func _init():
|
||||
name = "__InkRuntime"
|
||||
|
||||
# ############################################################################ #
|
||||
# Internal Methods
|
||||
# ############################################################################ #
|
||||
|
||||
func clear_raised_exceptions() -> bool:
|
||||
if _argument_exception_raised:
|
||||
_argument_exception_raised = false
|
||||
return true
|
||||
|
||||
if _argument_exception_raised:
|
||||
_argument_exception_raised = false
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func handle_exception(message: String) -> void:
|
||||
var exception_message = "EXCEPTION: %s" % message
|
||||
var stack_trace = _get_stack_trace()
|
||||
|
||||
_handle_generic_exception(
|
||||
exception_message,
|
||||
stop_execution_on_exception,
|
||||
stack_trace
|
||||
)
|
||||
|
||||
_exception_raised
|
||||
emit_signal("exception_raised", exception_message, stack_trace)
|
||||
|
||||
func handle_argument_exception(message: String) -> void:
|
||||
var exception_message = "ARGUMENT EXCEPTION: %s" % message
|
||||
var stack_trace = _get_stack_trace()
|
||||
|
||||
_handle_generic_exception(
|
||||
exception_message,
|
||||
stop_execution_on_error,
|
||||
stack_trace
|
||||
)
|
||||
|
||||
_argument_exception_raised = true
|
||||
emit_signal("exception_raised", exception_message, stack_trace)
|
||||
|
||||
func handle_story_exception(message: String, use_end_line_number: bool, metadata) -> void:
|
||||
# When exceptions are "recorded", they are not reported immediately.
|
||||
# 'Story' will take care of that at the end of the step.
|
||||
if record_story_exceptions:
|
||||
current_story_exceptions.append(StoryError.new(message, use_end_line_number, metadata))
|
||||
else:
|
||||
var exception_message = "STORY EXCEPTION: %s" % message
|
||||
var stack_trace = _get_stack_trace()
|
||||
|
||||
_handle_generic_exception(exception_message, stop_execution_on_error, stack_trace)
|
||||
|
||||
emit_signal("exception_raised", exception_message, stack_trace)
|
||||
|
||||
# ############################################################################ #
|
||||
# Private Methods
|
||||
# ############################################################################ #
|
||||
|
||||
func _handle_generic_exception(
|
||||
message: String,
|
||||
should_pause_execution: bool,
|
||||
stack_trace: PackedStringArray
|
||||
) -> void:
|
||||
if OS.is_debug_build():
|
||||
if should_pause_execution:
|
||||
assert(false, message)
|
||||
elif Engine.is_editor_hint():
|
||||
printerr(message)
|
||||
if stack_trace.size() > 0:
|
||||
printerr("Stack trace:")
|
||||
for line in stack_trace:
|
||||
printerr(line)
|
||||
else:
|
||||
push_error(message)
|
||||
|
||||
func _get_stack_trace() -> PackedStringArray:
|
||||
var trace := PackedStringArray()
|
||||
|
||||
var i = 1
|
||||
for stack_element in get_stack():
|
||||
if i <= 3:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
trace.append(str(
|
||||
" ", (i - 3), " - ", stack_element["source"], ":",
|
||||
stack_element["line"], " - at function: ", stack_element["function"]
|
||||
))
|
||||
|
||||
i += 1
|
||||
|
||||
return trace
|
623
addons/inkgd/runtime/static/json.gd
Normal file
623
addons/inkgd/runtime/static/json.gd
Normal file
|
@ -0,0 +1,623 @@
|
|||
# ############################################################################ #
|
||||
# Copyright © 2015-2021 inkle Ltd.
|
||||
# Copyright © 2019-2022 Frédéric Maquin <fred@ephread.com>
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This file is part of inkgd.
|
||||
# inkgd is licensed under the terms of the MIT license.
|
||||
# ############################################################################ #
|
||||
|
||||
extends InkBase
|
||||
|
||||
class_name InkStaticJSON
|
||||
|
||||
# In the C# code this class has only static methods. In the GDScript, it will rather
|
||||
# be a unique object, added to the InkRuntime singleton.
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var InkValue = load("res://addons/inkgd/runtime/values/value.gd")
|
||||
var InkStringValue = load("res://addons/inkgd/runtime/values/string_value.gd")
|
||||
var InkDivertTargetValue = load("res://addons/inkgd/runtime/values/divert_target_value.gd")
|
||||
var InkVariablePointerValue = load("res://addons/inkgd/runtime/values/variable_pointer_value.gd")
|
||||
var InkListValue = load("res://addons/inkgd/runtime/values/list_value.gd")
|
||||
|
||||
var InkList = load("res://addons/inkgd/runtime/lists/ink_list.gd")
|
||||
var InkListDefinition = load("res://addons/inkgd/runtime/lists/list_definition.gd")
|
||||
var InkListDefinitionsOrigin = load("res://addons/inkgd/runtime/lists/list_definitions_origin.gd")
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (Array<Variant>, bool) -> Array
|
||||
func jarray_to_runtime_obj_list(jarray: Array, skip_last = false) -> Array:
|
||||
var count = jarray.size()
|
||||
if skip_last:
|
||||
count -= 1
|
||||
|
||||
var list = []
|
||||
var i = 0
|
||||
while (i < count):
|
||||
var jtok = jarray[i]
|
||||
var runtime_obj = jtoken_to_runtime_object(jtok)
|
||||
list.append(runtime_obj)
|
||||
|
||||
i += 1
|
||||
|
||||
return list
|
||||
|
||||
# (self.Json.Writer, Dictionary<String, InkObject>) -> void
|
||||
func write_dictionary_runtime_objs(writer, dictionary: Dictionary) -> void:
|
||||
writer.write_object_start()
|
||||
for key in dictionary:
|
||||
writer.write_property_start(key)
|
||||
write_runtime_object(writer, dictionary[key])
|
||||
writer.write_property_end()
|
||||
writer.write_object_end()
|
||||
|
||||
# (self.Json.Writer, Array<InkObject>) -> void
|
||||
func write_list_runtime_objs(writer, list: Array) -> void:
|
||||
writer.write_array_start()
|
||||
for val in list:
|
||||
write_runtime_object(writer, val)
|
||||
writer.write_array_end()
|
||||
|
||||
# (self.Json.Writer, Array<Int>) -> void
|
||||
func write_int_dictionary(writer, dict: Dictionary) -> void:
|
||||
writer.write_object_start()
|
||||
for key in dict:
|
||||
writer.write_property(key, dict[key])
|
||||
writer.write_object_end()
|
||||
|
||||
# (self.Json.Writer, InkObject) -> void
|
||||
func write_runtime_object(writer, obj: InkObject) -> void:
|
||||
var container = InkUtils.as_or_null(obj, "InkContainer")
|
||||
if container:
|
||||
write_runtime_container(writer, container)
|
||||
return
|
||||
|
||||
var divert = InkUtils.as_or_null(obj, "Divert")
|
||||
if divert:
|
||||
var div_type_key = "->" # String
|
||||
if divert.is_external:
|
||||
div_type_key = "x()"
|
||||
elif divert.pushes_to_stack:
|
||||
if divert.stack_push_type == Ink.PushPopType.FUNCTION:
|
||||
div_type_key = "f()"
|
||||
elif divert.stackPushType == Ink.PushPopType.TUNNEL:
|
||||
div_type_key = "->t->"
|
||||
|
||||
var target_str = null # String
|
||||
if divert.has_variable_target:
|
||||
target_str = divert.variable_divert_name
|
||||
else:
|
||||
target_str = divert.target_path_string
|
||||
|
||||
writer.write_object_start()
|
||||
|
||||
writer.write_property(div_type_key, target_str)
|
||||
|
||||
if divert.has_variable_target:
|
||||
writer.write_property("var", true)
|
||||
|
||||
if divert.is_conditional:
|
||||
writer.write_property("c", true)
|
||||
|
||||
if divert.external_args > 0:
|
||||
writer.write_property("exArgs", divert.external_args)
|
||||
|
||||
writer.write_object_end()
|
||||
return
|
||||
|
||||
var choice_point = InkUtils.as_or_null(obj, "ChoicePoint")
|
||||
if choice_point:
|
||||
writer.write_object_start()
|
||||
writer.write_property("*", choice_point.path_string_on_choice)
|
||||
writer.write_property("flg", choice_point.flags)
|
||||
writer.write_object_end()
|
||||
return
|
||||
|
||||
var bool_val = InkUtils.as_or_null(obj, "BoolValue")
|
||||
if bool_val:
|
||||
writer.write(bool_val.value)
|
||||
return
|
||||
|
||||
var int_val = InkUtils.as_or_null(obj, "IntValue")
|
||||
if int_val:
|
||||
writer.write(int_val.value)
|
||||
return
|
||||
|
||||
var float_val = InkUtils.as_or_null(obj, "FloatValue")
|
||||
if float_val:
|
||||
writer.write(float_val.value)
|
||||
return
|
||||
|
||||
var str_val = InkUtils.as_or_null(obj, "StringValue")
|
||||
if str_val:
|
||||
if str_val.is_newline:
|
||||
writer.write_string("\\n", false)
|
||||
else:
|
||||
writer.write_string_start()
|
||||
writer.write_string_inner("^")
|
||||
writer.write_string_inner(str_val.value)
|
||||
writer.write_string_end()
|
||||
return
|
||||
|
||||
var list_val = InkUtils.as_or_null(obj, "ListValue")
|
||||
if list_val:
|
||||
write_ink_list(writer, list_val)
|
||||
return
|
||||
|
||||
var div_target_val = InkUtils.as_or_null(obj, "DivertTargetValue")
|
||||
if div_target_val:
|
||||
writer.write_object_start()
|
||||
writer.write_property("^->", div_target_val.value.components_string)
|
||||
writer.write_object_end()
|
||||
return
|
||||
|
||||
var var_ptr_val = InkUtils.as_or_null(obj, "VariablePointerValue")
|
||||
if var_ptr_val:
|
||||
writer.write_object_start()
|
||||
writer.write_property("^var", var_ptr_val.value)
|
||||
writer.write_property("ci", var_ptr_val.context_index)
|
||||
writer.write_object_end()
|
||||
return
|
||||
|
||||
var glue = InkUtils.as_or_null(obj, "Glue")
|
||||
if glue:
|
||||
writer.write("<>")
|
||||
return
|
||||
|
||||
var control_cmd = InkUtils.as_or_null(obj, "ControlCommand")
|
||||
if control_cmd:
|
||||
writer.write(self._control_command_names[control_cmd.command_type])
|
||||
return
|
||||
|
||||
var native_func = InkUtils.as_or_null(obj, "NativeFunctionCall")
|
||||
if native_func:
|
||||
var name = native_func.name
|
||||
|
||||
if name == "^": name = "L^"
|
||||
|
||||
writer.write(name)
|
||||
return
|
||||
|
||||
var var_ref = InkUtils.as_or_null(obj, "VariableReference")
|
||||
if var_ref:
|
||||
writer.write_object_start()
|
||||
|
||||
var read_count_path = var_ref.path_string_for_count
|
||||
if read_count_path != null:
|
||||
writer.write_property(["CNT?"], read_count_path)
|
||||
else:
|
||||
writer.write_property(["VAR?"], var_ref.name)
|
||||
|
||||
writer.write_object_end()
|
||||
return
|
||||
|
||||
var var_ass = InkUtils.as_or_null(obj, "VariableAssignment")
|
||||
if var_ass:
|
||||
writer.write_object_start()
|
||||
|
||||
var key = "VAR=" if var_ass.is_global else "temp="
|
||||
writer.write_property(key, var_ass.variable_name)
|
||||
|
||||
if !var_ass.is_new_declaration:
|
||||
writer.write_property("re", true)
|
||||
|
||||
writer.write_object_end()
|
||||
|
||||
return
|
||||
|
||||
var void_obj = InkUtils.as_or_null(obj, "Void")
|
||||
if void_obj:
|
||||
writer.write("void")
|
||||
return
|
||||
|
||||
# Legacy Tags (replaced in 1.1+)
|
||||
var tag = InkUtils.as_or_null(obj, "Tag")
|
||||
if tag:
|
||||
writer.write_object_start()
|
||||
writer.write_property("#", tag.text)
|
||||
writer.write_object_end()
|
||||
return
|
||||
|
||||
var choice = InkUtils.as_or_null(obj, "Choice")
|
||||
if choice:
|
||||
write_choice(writer, choice)
|
||||
return
|
||||
|
||||
InkUtils.throw_exception("Failed to convert runtime object to Json token: %s" % obj)
|
||||
return
|
||||
|
||||
# (Dictionary<String, Variant>) -> Dictionary<String, InkObject>
|
||||
func jobject_to_dictionary_runtime_objs(jobject: Dictionary) -> Dictionary:
|
||||
var dict = {}
|
||||
|
||||
for key in jobject:
|
||||
dict[key] = jtoken_to_runtime_object(jobject[key])
|
||||
|
||||
return dict
|
||||
|
||||
# (Dictionary<String, Variant>) -> Dictionary<String, int>
|
||||
func jobject_to_int_dictionary(jobject: Dictionary) -> Dictionary:
|
||||
var dict = {}
|
||||
for key in jobject:
|
||||
dict[key] = int(jobject[key])
|
||||
|
||||
return dict
|
||||
|
||||
# (Variant) -> InkObject
|
||||
func jtoken_to_runtime_object(token) -> InkObject:
|
||||
|
||||
if token is int || token is float || token is bool:
|
||||
return InkValue.create(token)
|
||||
|
||||
if token is String:
|
||||
var _str = token
|
||||
|
||||
var first_char = _str[0]
|
||||
if first_char == "^":
|
||||
return InkStringValue.new_with(_str.substr(1, _str.length() - 1))
|
||||
elif first_char == "\n" && _str.length() == 1:
|
||||
return InkStringValue.new_with("\n")
|
||||
|
||||
if _str == "<>": return InkGlue.new()
|
||||
|
||||
var i = 0
|
||||
while (i < _control_command_names.size()):
|
||||
var cmd_name = _control_command_names[i]
|
||||
if _str == cmd_name:
|
||||
return InkControlCommand.new(i)
|
||||
i += 1
|
||||
|
||||
if _str == "L^": _str = "^"
|
||||
if _static_native_function_call.call_exists_with_name(_str):
|
||||
return InkNativeFunctionCall.call_with_name(_str, _static_native_function_call)
|
||||
|
||||
if _str == "->->":
|
||||
return InkControlCommand.pop_tunnel()
|
||||
elif _str == "~ret":
|
||||
return InkControlCommand.pop_function()
|
||||
|
||||
if _str == "void":
|
||||
return InkVoid.new()
|
||||
|
||||
if token is Dictionary:
|
||||
var obj = token
|
||||
var prop_value
|
||||
|
||||
if obj.has("^->"):
|
||||
prop_value = obj["^->"]
|
||||
return InkDivertTargetValue.new_with(
|
||||
InkPath.new_with_components_string(str(prop_value))
|
||||
)
|
||||
|
||||
if obj.has("^var"):
|
||||
prop_value = obj["^var"]
|
||||
var var_ptr = InkVariablePointerValue.new_with_context(str(prop_value))
|
||||
if (obj.has("ci")):
|
||||
prop_value = obj["ci"]
|
||||
var_ptr.context_index = int(prop_value)
|
||||
return var_ptr
|
||||
|
||||
var is_divert = false
|
||||
var pushes_to_stack = false
|
||||
var div_push_type = Ink.PushPopType.FUNCTION
|
||||
var external = false
|
||||
|
||||
if obj.has("->"):
|
||||
prop_value = obj["->"]
|
||||
is_divert = true
|
||||
elif obj.has("f()"):
|
||||
prop_value = obj["f()"]
|
||||
is_divert = true
|
||||
pushes_to_stack = true
|
||||
div_push_type = Ink.PushPopType.FUNCTION
|
||||
elif obj.has("->t->"):
|
||||
prop_value = obj["->t->"]
|
||||
is_divert = true
|
||||
pushes_to_stack = true
|
||||
div_push_type = Ink.PushPopType.TUNNEL
|
||||
elif obj.has("x()"):
|
||||
prop_value = obj["x()"]
|
||||
is_divert = true
|
||||
external = true
|
||||
pushes_to_stack = false
|
||||
div_push_type = Ink.PushPopType.FUNCTION
|
||||
|
||||
if is_divert:
|
||||
var divert = InkDivert.new()
|
||||
divert.pushes_to_stack = pushes_to_stack
|
||||
divert.stack_push_type = div_push_type
|
||||
divert.is_external = external
|
||||
|
||||
var target = str(prop_value)
|
||||
|
||||
if obj.has("var"):
|
||||
prop_value = obj["var"]
|
||||
divert.variable_divert_name = target
|
||||
else:
|
||||
divert.target_path_string = target
|
||||
|
||||
divert.is_conditional = obj.has("c")
|
||||
#if divert.is_conditional: prop_value = obj["c"]
|
||||
|
||||
if external:
|
||||
if obj.has("exArgs"):
|
||||
prop_value = obj["exArgs"]
|
||||
divert.external_args = int(prop_value)
|
||||
|
||||
return divert
|
||||
|
||||
if obj.has("*"):
|
||||
prop_value = obj["*"]
|
||||
var choice = InkChoicePoint.new()
|
||||
choice.path_string_on_choice = str(prop_value)
|
||||
|
||||
if obj.has("flg"):
|
||||
prop_value = obj["flg"]
|
||||
choice.flags = int(prop_value)
|
||||
|
||||
return choice
|
||||
|
||||
if obj.has("VAR?"):
|
||||
prop_value = obj["VAR?"]
|
||||
return InkVariableReference.new(str(prop_value))
|
||||
elif obj.has("CNT?"):
|
||||
prop_value = obj["CNT?"]
|
||||
var read_count_var_ref = InkVariableReference.new()
|
||||
read_count_var_ref.path_string_for_count = str(prop_value)
|
||||
return read_count_var_ref
|
||||
|
||||
var is_var_ass = false
|
||||
var is_global_var = false
|
||||
if obj.has("VAR="):
|
||||
prop_value = obj["VAR="]
|
||||
is_var_ass = true
|
||||
is_global_var = true
|
||||
elif obj.has("temp="):
|
||||
prop_value = obj["temp="]
|
||||
is_var_ass = true
|
||||
is_global_var = false
|
||||
|
||||
if is_var_ass:
|
||||
var var_name = str(prop_value)
|
||||
var is_new_decl = !obj.has("re")
|
||||
var var_ass = InkVariableAssignment.new_with(var_name, is_new_decl)
|
||||
var_ass.is_global = is_global_var
|
||||
return var_ass
|
||||
|
||||
# Legacy Tags with texts (replaced in 1.1+)
|
||||
if obj.has("#"):
|
||||
prop_value = obj["#"]
|
||||
return InkTag.new(str(prop_value))
|
||||
|
||||
if obj.has("list"):
|
||||
prop_value = obj["list"]
|
||||
var list_content = prop_value
|
||||
var raw_list = InkList.new()
|
||||
if obj.has("origins"):
|
||||
prop_value = obj["origins"]
|
||||
var names_as_objs = prop_value
|
||||
raw_list.set_initial_origin_names(names_as_objs)
|
||||
|
||||
for name_to_val_key in list_content:
|
||||
var item = InkListItem.new_with_full_name(name_to_val_key)
|
||||
var val = list_content[name_to_val_key]
|
||||
raw_list.set_item(item, val)
|
||||
|
||||
return InkListValue.new_with(raw_list)
|
||||
|
||||
if obj.has("originalChoicePath"):
|
||||
return jobject_to_choice(obj)
|
||||
|
||||
if token is Array:
|
||||
var container = jarray_to_container(token)
|
||||
return container
|
||||
|
||||
if token == null:
|
||||
return null
|
||||
|
||||
InkUtils.throw_exception("Failed to convert token to runtime object: %s" % str(token))
|
||||
return null
|
||||
|
||||
# (self.Json.Writer, InkContainer, Bool) -> void
|
||||
func write_runtime_container(writer, container: InkContainer, without_name = false) -> void:
|
||||
writer.write_array_start()
|
||||
|
||||
for c in container.content:
|
||||
write_runtime_object(writer, c)
|
||||
|
||||
var named_only_content = container.named_only_content
|
||||
var count_flags = container.count_flags
|
||||
var has_name_property = (container.name != null) && !without_name
|
||||
|
||||
var has_terminator = named_only_content != null || count_flags > 0 || has_name_property
|
||||
|
||||
if has_terminator:
|
||||
writer.write_object_start()
|
||||
|
||||
if named_only_content != null:
|
||||
for named_content_key in named_only_content:
|
||||
var name = named_content_key
|
||||
var named_container = InkUtils.as_or_null(named_only_content[named_content_key], "InkContainer")
|
||||
writer.write_property_start(name)
|
||||
write_runtime_container(writer, named_container, true)
|
||||
writer.write_property_end()
|
||||
|
||||
if count_flags > 0:
|
||||
writer.write_property("#f", count_flags)
|
||||
|
||||
if has_name_property:
|
||||
writer.write_property("#n", container.name)
|
||||
|
||||
if has_terminator:
|
||||
writer.write_object_end()
|
||||
else:
|
||||
writer.write_null()
|
||||
|
||||
writer.write_array_end()
|
||||
|
||||
# (Array<Variant>) -> InkContainer
|
||||
func jarray_to_container(jarray: Array) -> InkContainer:
|
||||
var container = InkContainer.new()
|
||||
container.content = jarray_to_runtime_obj_list(jarray, true)
|
||||
|
||||
var terminating_obj = InkUtils.as_or_null(jarray.back(), "Dictionary") # Dictionary<string, Variant>
|
||||
if terminating_obj != null:
|
||||
var named_only_content = {} # new Dictionary<String, InkObject>
|
||||
|
||||
for key in terminating_obj:
|
||||
if key == "#f":
|
||||
container.count_flags = int(terminating_obj[key])
|
||||
elif key == "#n":
|
||||
container.name = str(terminating_obj[key])
|
||||
else:
|
||||
var named_content_item = jtoken_to_runtime_object(terminating_obj[key])
|
||||
var named_sub_container = InkUtils.as_or_null(named_content_item, "InkContainer")
|
||||
if named_sub_container:
|
||||
named_sub_container.name = key
|
||||
named_only_content[key] = named_content_item
|
||||
|
||||
container.named_only_content = named_only_content
|
||||
|
||||
return container
|
||||
|
||||
# (Dictionary<String, Variant>) -> Choice
|
||||
func jobject_to_choice(jobj: Dictionary) -> InkChoice:
|
||||
var choice = InkChoice.new()
|
||||
choice.text = str(jobj["text"])
|
||||
choice.index = int(jobj["index"])
|
||||
choice.source_path = str(jobj["originalChoicePath"])
|
||||
choice.original_thread_index = int(jobj["originalThreadIndex"])
|
||||
choice.path_string_on_choice = str(jobj["targetPath"])
|
||||
return choice
|
||||
|
||||
# (self.Json.Writer, Choice) -> Void
|
||||
func write_choice(writer, choice: InkChoice) -> void:
|
||||
writer.write_object_start()
|
||||
writer.write_property("text", choice.text)
|
||||
writer.write_property("index", choice.index)
|
||||
writer.write_property("originalChoicePath", choice.source_path)
|
||||
writer.write_property("originalThreadIndex", choice.original_thread_index)
|
||||
writer.write_property("targetPath", choice.path_string_on_choice)
|
||||
writer.write_object_end()
|
||||
|
||||
# (self.Json.Writer, ListValue) -> Void
|
||||
func write_ink_list(writer, list_val):
|
||||
var raw_list = list_val.value
|
||||
|
||||
writer.write_object_start()
|
||||
|
||||
writer.write_property_start("list")
|
||||
|
||||
writer.write_object_start()
|
||||
|
||||
for item_key in raw_list.raw_keys():
|
||||
var item = InkListItem.from_serialized_key(item_key)
|
||||
var item_val = raw_list.get_raw(item_key)
|
||||
|
||||
writer.write_property_name_start()
|
||||
writer.write_property_name_inner(item.origin_name if item.origin_name else "?")
|
||||
writer.write_property_name_inner(".")
|
||||
writer.write_property_name_inner(item.item_name)
|
||||
writer.write_property_name_end()
|
||||
|
||||
writer.write(item_val)
|
||||
|
||||
writer.write_property_end()
|
||||
|
||||
writer.write_object_end()
|
||||
|
||||
writer.write_property_end()
|
||||
|
||||
if raw_list.size() == 0 && raw_list.origin_names != null && raw_list.origin_names.size() > 0:
|
||||
writer.write_property_start("origins")
|
||||
writer.write_array_start()
|
||||
for name in raw_list.origin_names:
|
||||
writer.write(name)
|
||||
writer.write_array_end()
|
||||
writer.write_property_end()
|
||||
|
||||
writer.write_object_end()
|
||||
|
||||
# (ListDefinitionsOrigin) -> Dictionary<String, Variant>
|
||||
func list_definitions_to_jtoken (origin):
|
||||
var result = {} # Dictionary<String, Variant>
|
||||
for def in origin.lists:
|
||||
var list_def_json = {} # Dictionary<String, Variant>
|
||||
for item_to_val_key in def.items:
|
||||
var item = InkListItem.from_serialized_key(item_to_val_key)
|
||||
var val = def.items[item_to_val_key]
|
||||
list_def_json[item.item_name] = val
|
||||
|
||||
result[def.name] = list_def_json
|
||||
|
||||
return result
|
||||
|
||||
# (Variant) -> ListDefinitionsOrigin
|
||||
func jtoken_to_list_definitions(obj):
|
||||
var defs_obj = obj
|
||||
|
||||
var all_defs = [] # Array<ListDefinition>
|
||||
|
||||
for k in defs_obj:
|
||||
var name = str(k) # String
|
||||
var list_def_json = defs_obj[k] # Dictionary<String, Variant>
|
||||
|
||||
|
||||
var items = {} # Dictionary<String, int>
|
||||
for name_value_key in list_def_json:
|
||||
items[name_value_key] = int(list_def_json[name_value_key])
|
||||
|
||||
var def = InkListDefinition.new(name, items)
|
||||
all_defs.append(def)
|
||||
|
||||
return InkListDefinitionsOrigin.new(all_defs)
|
||||
|
||||
func _init(native_function_call):
|
||||
_static_native_function_call = native_function_call
|
||||
|
||||
_control_command_names = []
|
||||
|
||||
_control_command_names.append("ev") # EVAL_START
|
||||
_control_command_names.append("out") # EVAL_OUTPUT
|
||||
_control_command_names.append("/ev") # EVAL_END
|
||||
_control_command_names.append("du") # DUPLICATE
|
||||
_control_command_names.append("pop") # POP_EVALUATED_VALUE
|
||||
_control_command_names.append("~ret") # POP_FUNCTION
|
||||
_control_command_names.append("->->") # POP_TUNNEL
|
||||
_control_command_names.append("str") # BEGIN_STRING
|
||||
_control_command_names.append("/str") # END_STRING
|
||||
_control_command_names.append("nop") # NO_OP
|
||||
_control_command_names.append("choiceCnt") # CHOICE_COUNT
|
||||
_control_command_names.append("turn") # TURNS
|
||||
_control_command_names.append("turns") # TURNS_SINCE
|
||||
_control_command_names.append("readc") # READ_COUNT
|
||||
_control_command_names.append("rnd") # RANDOM
|
||||
_control_command_names.append("srnd") # SEED_RANDOM
|
||||
_control_command_names.append("visit") # VISIT_INDEX
|
||||
_control_command_names.append("seq") # SEQUENCE_SHUFFLE_INDEX
|
||||
_control_command_names.append("thread") # START_THREAD
|
||||
_control_command_names.append("done") # DONE
|
||||
_control_command_names.append("end") # END
|
||||
_control_command_names.append("listInt") # LIST_FROM_INT
|
||||
_control_command_names.append("range") # LIST_RANGE
|
||||
_control_command_names.append("lrnd") # LIST_RANDOM
|
||||
_control_command_names.append("#") # BEGIN_TAG
|
||||
_control_command_names.append("/#") # END_TAG
|
||||
|
||||
var i = 0
|
||||
while i < InkControlCommand.CommandType.TOTAL_VALUES:
|
||||
if _control_command_names[i] == null:
|
||||
InkUtils.throw_exception("Control command not accounted for in serialisation")
|
||||
i += 1
|
||||
|
||||
# Array<String>
|
||||
var _control_command_names = null
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# Eventually a pointer to InkRuntime.StaticJson
|
||||
var _static_native_function_call = null
|
284
addons/inkgd/runtime/static/native_function_call.gd
Normal file
284
addons/inkgd/runtime/static/native_function_call.gd
Normal file
|
@ -0,0 +1,284 @@
|
|||
# warning-ignore-all:unused_class_variable
|
||||
# ############################################################################ #
|
||||
# Copyright © 2015-2021 inkle Ltd.
|
||||
# Copyright © 2019-2022 Frédéric Maquin <fred@ephread.com>
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This file is part of inkgd.
|
||||
# inkgd is licensed under the terms of the MIT license.
|
||||
# ############################################################################ #
|
||||
|
||||
extends RefCounted
|
||||
|
||||
class_name InkStaticNativeFunctionCall
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
const ValueType = preload("res://addons/inkgd/runtime/values/value_type.gd").ValueType
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
const ADD = "+"
|
||||
const SUBTRACT = "-"
|
||||
const DIVIDE = "/"
|
||||
const MULTIPLY = "*"
|
||||
const MOD = "%"
|
||||
const NEGATE = "_"
|
||||
const EQUALS = "=="
|
||||
const GREATER = ">"
|
||||
const LESS = "<"
|
||||
const GREATER_THAN_OR_EQUALS = ">="
|
||||
const LESS_THAN_OR_EQUALS = "<="
|
||||
const NOT_EQUALS = "!="
|
||||
const NOT = "!"
|
||||
const AND = "&&"
|
||||
const OR = "||"
|
||||
const MIN = "MIN"
|
||||
const MAX = "MAX"
|
||||
const POW = "POW"
|
||||
const FLOOR = "FLOOR"
|
||||
const CEILING = "CEILING"
|
||||
const INT = "INT"
|
||||
const FLOAT = "FLOAT"
|
||||
const HAS = "?"
|
||||
const HASNT = "!?"
|
||||
const INTERSECT = "^"
|
||||
const LIST_MIN = "LIST_MIN"
|
||||
const LIST_MAX = "LIST_MAX"
|
||||
const ALL = "LIST_ALL"
|
||||
const COUNT = "LIST_COUNT"
|
||||
const VALUE_OF_LIST = "LIST_VALUE"
|
||||
const INVERT = "LIST_INVERT"
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var native_functions = null # Dictionary<String, String>
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (String) -> Bool
|
||||
func call_exists_with_name(function_name):
|
||||
generate_native_functions_if_necessary()
|
||||
return native_functions.has(function_name)
|
||||
|
||||
# () -> void
|
||||
func generate_native_functions_if_necessary():
|
||||
if native_functions == null:
|
||||
native_functions = {}
|
||||
|
||||
add_int_binary_op(ADD, "int_binary_op_add")
|
||||
add_int_binary_op(SUBTRACT, "int_binary_op_substract")
|
||||
add_int_binary_op(MULTIPLY, "int_binary_op_multiply")
|
||||
add_int_binary_op(DIVIDE, "int_binary_op_divide")
|
||||
add_int_binary_op(MOD, "int_binary_op_mod")
|
||||
add_int_unary_op (NEGATE, "int_unary_op_negate")
|
||||
|
||||
add_int_binary_op(EQUALS, "int_binary_op_equals")
|
||||
add_int_binary_op(GREATER, "int_binary_op_greater")
|
||||
add_int_binary_op(LESS, "int_binary_op_less")
|
||||
add_int_binary_op(GREATER_THAN_OR_EQUALS, "int_binary_op_greater_than_or_equals")
|
||||
add_int_binary_op(LESS_THAN_OR_EQUALS, "int_binary_op_less_than_or_equals")
|
||||
add_int_binary_op(NOT_EQUALS, "int_binary_op_not_equals")
|
||||
add_int_unary_op (NOT, "int_unary_op_not")
|
||||
|
||||
add_int_binary_op(AND, "int_binary_op_and")
|
||||
add_int_binary_op(OR, "int_binary_op_or")
|
||||
|
||||
add_int_binary_op(MAX, "int_binary_op_max")
|
||||
add_int_binary_op(MIN, "int_binary_op_min")
|
||||
|
||||
add_int_binary_op(POW, "int_binary_op_pow")
|
||||
add_int_unary_op (FLOOR, "int_unary_op_floor")
|
||||
add_int_unary_op (CEILING, "int_unary_op_ceiling")
|
||||
add_int_unary_op (INT, "int_unary_op_int")
|
||||
add_int_unary_op (FLOAT, "int_unary_op_float")
|
||||
|
||||
add_float_binary_op(ADD, "float_binary_op_add")
|
||||
add_float_binary_op(SUBTRACT, "float_binary_op_substract")
|
||||
add_float_binary_op(MULTIPLY, "float_binary_op_multiply")
|
||||
add_float_binary_op(DIVIDE, "float_binary_op_divide")
|
||||
add_float_binary_op(MOD, "float_binary_op_mod")
|
||||
add_float_unary_op (NEGATE, "float_unary_op_negate")
|
||||
|
||||
add_float_binary_op(EQUALS, "float_binary_op_equals")
|
||||
add_float_binary_op(GREATER, "float_binary_op_greater")
|
||||
add_float_binary_op(LESS, "float_binary_op_less")
|
||||
add_float_binary_op(GREATER_THAN_OR_EQUALS, "float_binary_op_greater_than_or_equals")
|
||||
add_float_binary_op(LESS_THAN_OR_EQUALS, "float_binary_op_less_than_or_equals")
|
||||
add_float_binary_op(NOT_EQUALS, "float_binary_op_not_equals")
|
||||
add_float_unary_op (NOT, "float_unary_op_not")
|
||||
|
||||
add_float_binary_op(AND, "float_binary_op_and")
|
||||
add_float_binary_op(OR, "float_binary_op_or")
|
||||
|
||||
add_float_binary_op(MAX, "float_binary_op_max")
|
||||
add_float_binary_op(MIN, "float_binary_op_min")
|
||||
|
||||
add_float_binary_op(POW, "float_binary_op_pow")
|
||||
add_float_unary_op (FLOOR, "float_unary_op_floor")
|
||||
add_float_unary_op (CEILING, "float_unary_op_ceiling")
|
||||
add_float_unary_op (INT, "float_unary_op_int")
|
||||
add_float_unary_op (FLOAT, "float_unary_op_float")
|
||||
|
||||
add_string_binary_op(ADD, "string_binary_op_add")
|
||||
add_string_binary_op(EQUALS, "string_binary_op_equals")
|
||||
add_string_binary_op(NOT_EQUALS, "string_binary_op_not_equals")
|
||||
add_string_binary_op(HAS, "string_binary_op_has")
|
||||
add_string_binary_op(HASNT, "string_binary_op_hasnt")
|
||||
|
||||
add_list_binary_op (ADD, "list_binary_op_add")
|
||||
add_list_binary_op (SUBTRACT, "list_binary_op_substract")
|
||||
add_list_binary_op (HAS, "list_binary_op_has")
|
||||
add_list_binary_op (HASNT, "list_binary_op_hasnt")
|
||||
add_list_binary_op (INTERSECT, "list_binary_op_intersect")
|
||||
|
||||
add_list_binary_op (EQUALS, "list_binary_op_equals")
|
||||
add_list_binary_op (GREATER, "list_binary_op_greater")
|
||||
add_list_binary_op (LESS, "list_binary_op_less")
|
||||
add_list_binary_op (GREATER_THAN_OR_EQUALS, "list_binary_op_greater_than_or_equals")
|
||||
add_list_binary_op (LESS_THAN_OR_EQUALS, "list_binary_op_less_than_or_equals")
|
||||
add_list_binary_op (NOT_EQUALS, "list_binary_op_not_equals")
|
||||
|
||||
add_list_binary_op (AND, "list_binary_op_and")
|
||||
add_list_binary_op (OR, "list_binary_op_or")
|
||||
|
||||
add_list_unary_op (NOT, "list_unary_op_not")
|
||||
|
||||
add_list_unary_op (INVERT, "list_unary_op_invert")
|
||||
add_list_unary_op (ALL, "list_unary_op_all")
|
||||
add_list_unary_op (LIST_MIN, "list_unary_op_list_min")
|
||||
add_list_unary_op (LIST_MAX, "list_unary_op_list_max")
|
||||
add_list_unary_op (COUNT, "list_unary_op_count")
|
||||
add_list_unary_op (VALUE_OF_LIST, "list_unary_op_value_of_list")
|
||||
|
||||
add_op_to_native_func(EQUALS, 2, ValueType.DIVERT_TARGET,
|
||||
"native_func_divert_targets_equal")
|
||||
add_op_to_native_func(NOT_EQUALS, 2, ValueType.DIVERT_TARGET,
|
||||
"native_func_divert_targets_not_equal")
|
||||
|
||||
# (String, int, ValueType, Variant)
|
||||
func add_op_to_native_func(name, args, val_type, op):
|
||||
var native_func = null # NativeFunctionCall
|
||||
if native_functions.has(name):
|
||||
native_func = native_functions[name]
|
||||
else:
|
||||
native_func = InkNativeFunctionCall.new_with_name_and_number_of_parameters(name, args, self)
|
||||
native_functions[name] = native_func
|
||||
|
||||
native_func.add_op_func_for_type(val_type, op)
|
||||
|
||||
func add_int_binary_op(name, op_function_name):
|
||||
add_op_to_native_func(name, 2, ValueType.INT, op_function_name)
|
||||
|
||||
func add_int_unary_op(name, op_function_name):
|
||||
add_op_to_native_func(name, 1, ValueType.INT, op_function_name)
|
||||
|
||||
func add_float_binary_op(name, op_function_name):
|
||||
add_op_to_native_func(name, 2, ValueType.FLOAT, op_function_name)
|
||||
|
||||
func add_float_unary_op(name, op_function_name):
|
||||
add_op_to_native_func(name, 1, ValueType.FLOAT, op_function_name)
|
||||
|
||||
func add_string_binary_op(name, op_function_name):
|
||||
add_op_to_native_func(name, 2, ValueType.STRING, op_function_name)
|
||||
|
||||
func add_list_binary_op(name, op_function_name):
|
||||
add_op_to_native_func(name, 2, ValueType.LIST, op_function_name)
|
||||
|
||||
func add_list_unary_op(name, op_function_name):
|
||||
add_op_to_native_func(name, 1, ValueType.LIST, op_function_name)
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func int_binary_op_add(x, y): return x + y
|
||||
func int_binary_op_substract(x, y): return x - y
|
||||
func int_binary_op_multiply(x, y): return x * y
|
||||
func int_binary_op_divide(x, y): return x / y
|
||||
func int_binary_op_mod(x, y): return x % y
|
||||
func int_unary_op_negate(x): return -x
|
||||
|
||||
func int_binary_op_equals(x, y): return x == y
|
||||
func int_binary_op_greater(x, y): return x > y
|
||||
func int_binary_op_less(x, y): return x < y
|
||||
func int_binary_op_greater_than_or_equals(x, y): return x >= y
|
||||
func int_binary_op_less_than_or_equals(x, y): return x <= y
|
||||
func int_binary_op_not_equals(x, y): return x != y
|
||||
func int_unary_op_not(x): return x == 0
|
||||
|
||||
func int_binary_op_and(x, y): return x != 0 && y != 0
|
||||
func int_binary_op_or(x, y): return x != 0 || y != 0
|
||||
|
||||
func int_binary_op_max(x, y): return max(x, y)
|
||||
func int_binary_op_min(x, y): return min(x, y)
|
||||
|
||||
func int_binary_op_pow(x, y): return pow(float(x), float(y))
|
||||
func int_unary_op_floor(x): return x
|
||||
func int_unary_op_ceiling(x): return x
|
||||
func int_unary_op_int(x): return x
|
||||
func int_unary_op_float(x): return float(x)
|
||||
|
||||
func float_binary_op_add(x, y): return x + y
|
||||
func float_binary_op_substract(x, y): return x - y
|
||||
func float_binary_op_multiply(x, y): return x * y
|
||||
func float_binary_op_divide(x, y): return x / y
|
||||
func float_binary_op_mod(x, y): return fmod(x, y)
|
||||
func float_unary_op_negate(x): return -x
|
||||
|
||||
func float_binary_op_equals(x, y): return x == y
|
||||
func float_binary_op_greater(x, y): return x > y
|
||||
func float_binary_op_less(x, y): return x < y
|
||||
func float_binary_op_greater_than_or_equals(x, y): return x >= y
|
||||
func float_binary_op_less_than_or_equals(x, y): return x <= y
|
||||
func float_binary_op_not_equals(x, y): return x != y
|
||||
func float_unary_op_not(x): return x == 0.0
|
||||
|
||||
func float_binary_op_and(x, y): return x != 0.0 && y != 0.0
|
||||
func float_binary_op_or(x, y): return x != 0.0 || y != 0.0
|
||||
|
||||
func float_binary_op_max(x, y): return max(x, y)
|
||||
func float_binary_op_min(x, y): return min(x, y)
|
||||
|
||||
func float_binary_op_pow(x, y): return pow(x, y)
|
||||
func float_unary_op_floor(x): return floor(x)
|
||||
func float_unary_op_ceiling(x): return ceil(x)
|
||||
func float_unary_op_int(x): return int(x)
|
||||
func float_unary_op_float(x): return x
|
||||
|
||||
func string_binary_op_add(x, y): return str(x, y)
|
||||
func string_binary_op_equals(x, y): return x == y
|
||||
func string_binary_op_not_equals(x, y): return x != y
|
||||
|
||||
# Note: The Content Test (in) operator does not returns true when testing
|
||||
# against the empty string, unlike the behaviour of the original C# runtime.
|
||||
func string_binary_op_has(x, y): return y == "" || (y in x)
|
||||
func string_binary_op_hasnt(x, y): return !(y in x) && y != ""
|
||||
|
||||
func list_binary_op_add(x, y): return x.union(y)
|
||||
func list_binary_op_substract(x, y): return x.without(y)
|
||||
func list_binary_op_has(x, y): return x.contains(y)
|
||||
func list_binary_op_hasnt(x, y): return !x.contains(y)
|
||||
func list_binary_op_intersect(x, y): return x.intersection(y)
|
||||
|
||||
func list_binary_op_equals(x, y): return x.equals(y)
|
||||
func list_binary_op_greater(x, y): return x.greater_than(y)
|
||||
func list_binary_op_less(x, y): return x.less_than(y)
|
||||
func list_binary_op_greater_than_or_equals(x, y): return x.greater_than_or_equals(y)
|
||||
func list_binary_op_less_than_or_equals(x, y): return x.less_than_or_equals(y)
|
||||
func list_binary_op_not_equals(x, y): return !x.equals(y)
|
||||
|
||||
func list_binary_op_and(x, y): return x.size() > 0 && y.size() > 0
|
||||
func list_binary_op_or(x, y): return x.size() > 0 || y.size() > 0
|
||||
|
||||
func list_unary_op_not(x): return 1 if x.size() == 0 else 0
|
||||
|
||||
func list_unary_op_invert(x): return x.inverse
|
||||
func list_unary_op_all(x): return x.all
|
||||
func list_unary_op_list_min(x): return x.min_as_list()
|
||||
func list_unary_op_list_max(x): return x.max_as_list()
|
||||
func list_unary_op_count(x): return x.size()
|
||||
func list_unary_op_value_of_list(x): return x.max_item.value
|
||||
|
||||
func native_func_divert_targets_equal(d1, d2): return d1.equals(d2)
|
||||
func native_func_divert_targets_not_equal(d1, d2): return !d1.equals(d2)
|
Loading…
Add table
Add a link
Reference in a new issue