# ############################################################################ # # Copyright © 2015-2021 inkle Ltd. # Copyright © 2019-2022 Frédéric Maquin # 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, 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) -> 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) -> 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) -> 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) -> Dictionary 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) -> Dictionary 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) -> 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 if terminating_obj != null: var named_only_content = {} # new Dictionary 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) -> 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 func list_definitions_to_jtoken (origin): var result = {} # Dictionary for def in origin.lists: var list_def_json = {} # Dictionary 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 for k in defs_obj: var name = str(k) # String var list_def_json = defs_obj[k] # Dictionary var items = {} # Dictionary 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 var _control_command_names = null # ############################################################################ # # Eventually a pointer to InkRuntime.StaticJson var _static_native_function_call = null