init
This commit is contained in:
commit
b99855351d
434 changed files with 50357 additions and 0 deletions
457
addons/inkgd/runtime/callstack.gd
Normal file
457
addons/inkgd/runtime/callstack.gd
Normal file
|
@ -0,0 +1,457 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkBase
|
||||
|
||||
class_name InkCallStack
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
class Element extends InkBase:
|
||||
|
||||
var current_pointer: InkPointer = InkPointer.null_pointer
|
||||
|
||||
var in_expression_evaluation: bool = false
|
||||
var temporary_variables = null # Dictionary<String, InkObject>
|
||||
var type: int = 0 # Ink.PushPopType
|
||||
var evaluation_stack_height_when_pushed: int = 0
|
||||
var function_start_in_ouput_stream: int = 0
|
||||
|
||||
# (Ink.PushPopType, Pointer, bool) -> InkElement
|
||||
func _init(type, pointer, in_expression_evaluation = false):
|
||||
self.current_pointer = pointer
|
||||
self.in_expression_evaluation = in_expression_evaluation
|
||||
self.temporary_variables = {}
|
||||
self.type = type
|
||||
|
||||
# () -> InkElement
|
||||
func copy():
|
||||
var copy = Element.new(self.type, self.current_pointer, self.in_expression_evaluation)
|
||||
copy.temporary_variables = self.temporary_variables.duplicate()
|
||||
copy.evaluation_stack_height_when_pushed = evaluation_stack_height_when_pushed
|
||||
copy.function_start_in_ouput_stream = function_start_in_ouput_stream
|
||||
return copy
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "CallStack.Element" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "CallStack.Element"
|
||||
|
||||
class InkThread extends InkBase:
|
||||
|
||||
var callstack = null # Array<Element>
|
||||
var thread_index: int = 0 # int
|
||||
var previous_pointer: InkPointer = InkPointer.null_pointer
|
||||
|
||||
func _init(static_json = null):
|
||||
get_static_json(static_json)
|
||||
callstack = []
|
||||
|
||||
# Dictionary<string, object>, Story
|
||||
func _init_with(jthread_obj, story_context):
|
||||
thread_index = int(jthread_obj["threadIndex"])
|
||||
var jthread_callstack = jthread_obj["callstack"]
|
||||
|
||||
for jel_tok in jthread_callstack:
|
||||
var jelement_obj = jel_tok
|
||||
var push_pop_type = int(jelement_obj["type"])
|
||||
|
||||
var pointer = InkPointer.null_pointer
|
||||
var current_container_path_str = null
|
||||
var current_container_path_str_token = null
|
||||
|
||||
if jelement_obj.has("cPath"):
|
||||
current_container_path_str_token = jelement_obj["cPath"]
|
||||
current_container_path_str = str(current_container_path_str_token)
|
||||
|
||||
var thread_pointer_result = story_context.content_at_path(InkPath.new_with_components_string(current_container_path_str))
|
||||
pointer = InkPointer.new(thread_pointer_result.container, int(jelement_obj["idx"]))
|
||||
|
||||
if thread_pointer_result.obj == null:
|
||||
InkUtils.throw_exception(
|
||||
"When loading state, internal story location " +
|
||||
"couldn't be found: '%s'. " % current_container_path_str +
|
||||
"Has the story changed since this save data was created?"
|
||||
)
|
||||
return
|
||||
elif thread_pointer_result.approximate:
|
||||
story_context.warning(
|
||||
"When loading state, exact internal story location " +
|
||||
"couldn't be found: '%s', so it was" % current_container_path_str +
|
||||
"approximated to '%s' " + pointer.container.path._to_string() +
|
||||
"to recover. Has the story changed since this save data was created?"
|
||||
)
|
||||
|
||||
var in_expression_evaluation = bool(jelement_obj["exp"])
|
||||
var el = Element.new(push_pop_type, pointer, in_expression_evaluation)
|
||||
|
||||
var temps
|
||||
if jelement_obj.has("temp"):
|
||||
temps = jelement_obj["temp"] # Dictionary<string, object>
|
||||
el.temporary_variables = self.StaticJSON.jobject_to_dictionary_runtime_objs(temps)
|
||||
else:
|
||||
el.temporary_variables.clear()
|
||||
|
||||
callstack.append(el)
|
||||
|
||||
var prev_content_obj_path
|
||||
if jthread_obj.has("previousContentObject"):
|
||||
prev_content_obj_path = str(jthread_obj["previousContentObject"])
|
||||
var prev_path = InkPath.new_with_components_string(prev_content_obj_path)
|
||||
self.previous_pointer = story_context.pointer_at_path(prev_path)
|
||||
|
||||
# () -> InkThread
|
||||
func copy():
|
||||
var copy = InkThread.new(self.StaticJSON)
|
||||
copy.thread_index = self.thread_index
|
||||
for e in callstack:
|
||||
copy.callstack.append(e.copy())
|
||||
copy.previous_pointer = self.previous_pointer
|
||||
return copy
|
||||
|
||||
# (SimpleJson.Writer) -> void
|
||||
func write_json(writer):
|
||||
writer.write_object_start()
|
||||
|
||||
writer.write_property_start("callstack")
|
||||
writer.write_array_start()
|
||||
|
||||
for el in self.callstack:
|
||||
writer.write_object_start()
|
||||
if !el.current_pointer.is_null:
|
||||
writer.write_property("cPath", el.current_pointer.container.path.components_string)
|
||||
writer.write_property("idx", el.current_pointer.index)
|
||||
|
||||
writer.write_property("exp", el.in_expression_evaluation)
|
||||
writer.write_property("type", int(el.type))
|
||||
|
||||
if el.temporary_variables.size() > 0:
|
||||
writer.write_property_start("temp")
|
||||
self.StaticJSON.write_dictionary_runtime_objs(writer, el.temporary_variables)
|
||||
writer.write_property_end()
|
||||
|
||||
writer.write_object_end()
|
||||
|
||||
writer.write_array_end()
|
||||
writer.write_property_end()
|
||||
|
||||
writer.write_property("threadIndex", self.thread_index)
|
||||
|
||||
if !self.previous_pointer.is_null:
|
||||
writer.write_property("previousContentObject", self.previous_pointer.resolve().path._to_string())
|
||||
|
||||
writer.write_object_end()
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "CallStack.InkThread" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "CallStack.InkThread"
|
||||
|
||||
# ######################################################################## #
|
||||
|
||||
static func new_with(jthread_obj, story_context, static_json = null):
|
||||
var thread = InkThread.new(static_json)
|
||||
thread._init_with(jthread_obj, story_context)
|
||||
return thread
|
||||
|
||||
# ######################################################################## #
|
||||
|
||||
var StaticJSON: InkStaticJSON:
|
||||
get: return _static_json.get_ref()
|
||||
|
||||
var _static_json = WeakRef.new()
|
||||
|
||||
func get_static_json(static_json = null):
|
||||
if static_json != null:
|
||||
_static_json = weakref(static_json)
|
||||
return
|
||||
|
||||
var InkRuntime = Engine.get_main_loop().root.get_node("__InkRuntime")
|
||||
|
||||
InkUtils.__assert__(InkRuntime != null,
|
||||
str("[InkCallStack.InkThread] Could not retrieve 'InkRuntime' singleton from the scene tree."))
|
||||
|
||||
_static_json = weakref(InkRuntime.json)
|
||||
|
||||
# () -> Array<InkElement>
|
||||
var elements : get = get_elements
|
||||
func get_elements():
|
||||
return self.callstack
|
||||
|
||||
# () -> int
|
||||
var depth : get = get_depth
|
||||
func get_depth():
|
||||
return self.elements.size()
|
||||
|
||||
# () -> InkElement
|
||||
var current_element : get = get_current_element
|
||||
func get_current_element():
|
||||
var thread = self._threads.back()
|
||||
var cs = thread.callstack
|
||||
return cs.back()
|
||||
|
||||
# () -> int
|
||||
var current_element_index : get = get_current_element_index
|
||||
func get_current_element_index():
|
||||
return self.callstack.size() - 1
|
||||
|
||||
# () -> InkThread
|
||||
# (InkThread) -> void
|
||||
var current_thread : get = get_current_thread, set = set_current_thread
|
||||
func get_current_thread():
|
||||
return self._threads.back()
|
||||
|
||||
func set_current_thread(value):
|
||||
InkUtils.__assert__(_threads.size() == 1,
|
||||
"Shouldn't be directly setting the current thread when we have a stack of them")
|
||||
self._threads.clear()
|
||||
self._threads.append(value)
|
||||
|
||||
# () -> bool
|
||||
var can_pop : get = get_can_pop
|
||||
func get_can_pop():
|
||||
return self.callstack.size() > 1
|
||||
|
||||
# (InkStory | CallStack) -> CallStack
|
||||
func _init(story_context_or_to_copy, static_json = null):
|
||||
get_static_json(static_json)
|
||||
|
||||
if story_context_or_to_copy.is_ink_class("Story"):
|
||||
var story_context = story_context_or_to_copy
|
||||
_start_of_root = InkPointer.start_of(story_context.root_content_container)
|
||||
reset()
|
||||
elif story_context_or_to_copy.is_ink_class("CallStack"):
|
||||
var to_copy = story_context_or_to_copy
|
||||
self._threads = []
|
||||
for other_thread in to_copy._threads:
|
||||
self._threads.append(other_thread.copy())
|
||||
self._thread_counter = to_copy._thread_counter
|
||||
self._start_of_root = to_copy._start_of_root
|
||||
|
||||
# () -> void
|
||||
func reset():
|
||||
self._threads = []
|
||||
self._threads.append(InkThread.new(self.StaticJSON))
|
||||
self._threads[0].callstack.append(Element.new(Ink.PushPopType.TUNNEL, self._start_of_root))
|
||||
|
||||
# (Dictionary<string, object>, InkStory) -> void
|
||||
func set_json_token(jobject, story_context):
|
||||
self._threads.clear()
|
||||
var jthreads = jobject["threads"]
|
||||
|
||||
for jthread_tok in jthreads:
|
||||
var jthread_obj = jthread_tok
|
||||
var thread = InkThread.new_with(jthread_obj, story_context)
|
||||
self._threads.append(thread)
|
||||
|
||||
self._thread_counter = int(jobject["threadCounter"])
|
||||
self._start_of_root = InkPointer.start_of(story_context.root_content_container)
|
||||
|
||||
|
||||
# (SimpleJson.Writer) -> void
|
||||
func write_json(writer):
|
||||
writer.write_object(Callable(self, "_anonymous_write_json"))
|
||||
|
||||
# () -> void
|
||||
func push_thread():
|
||||
var new_thread = self.current_thread.copy()
|
||||
self._thread_counter += 1
|
||||
new_thread.thread_index = self._thread_counter
|
||||
self._threads.append(new_thread)
|
||||
|
||||
# () -> void
|
||||
func fork_thread():
|
||||
var forked_thread = self.current_thread.copy()
|
||||
self._thread_counter += 1
|
||||
forked_thread.thread_index = self._thread_counter
|
||||
return forked_thread
|
||||
|
||||
# () -> void
|
||||
func pop_thread():
|
||||
if self.can_pop_thread:
|
||||
self._threads.erase(self.current_thread)
|
||||
else:
|
||||
InkUtils.throw_exception("Can't pop thread")
|
||||
|
||||
# () -> bool
|
||||
var can_pop_thread : get = get_can_pop_thread
|
||||
func get_can_pop_thread():
|
||||
return _threads.size() > 1 && !self.element_is_evaluate_from_game
|
||||
|
||||
# () -> bool
|
||||
var element_is_evaluate_from_game : get = get_element_is_evaluate_from_game
|
||||
func get_element_is_evaluate_from_game():
|
||||
return self.current_element.type == Ink.PushPopType.FUNCTION_EVALUATION_FROM_GAME
|
||||
|
||||
# (Ink.PushPopType, int, int) -> void
|
||||
func push(type, external_evaluation_stack_height = 0, output_stream_length_with_pushed = 0):
|
||||
var element = Element.new(type, self.current_element.current_pointer, false)
|
||||
|
||||
element.evaluation_stack_height_when_pushed = external_evaluation_stack_height
|
||||
element.function_start_in_ouput_stream = output_stream_length_with_pushed
|
||||
|
||||
self.callstack.append(element)
|
||||
|
||||
# (Ink.PushPopType | null) -> void
|
||||
func can_pop_type(type = null):
|
||||
if !self.can_pop:
|
||||
return false
|
||||
|
||||
if type == null:
|
||||
return true
|
||||
|
||||
return self.current_element.type == type
|
||||
|
||||
# (Ink.PushPopType | null) -> void
|
||||
func pop(type = null):
|
||||
if can_pop_type(type):
|
||||
self.callstack.pop_back()
|
||||
return
|
||||
else:
|
||||
InkUtils.throw_exception("Mismatched push/pop in Callstack")
|
||||
|
||||
# (String, int) -> InkObject
|
||||
func get_temporary_variable_with_name(name, context_index = -1) -> InkObject:
|
||||
if context_index == -1:
|
||||
context_index = self.current_element_index + 1
|
||||
|
||||
var var_value = null
|
||||
|
||||
var context_element = self.callstack[context_index - 1]
|
||||
|
||||
if context_element.temporary_variables.has(name):
|
||||
var_value = context_element.temporary_variables[name]
|
||||
return var_value
|
||||
else:
|
||||
return null
|
||||
|
||||
# (String, InkObject, bool, int) -> void
|
||||
func set_temporary_variable(name, value, declare_new, context_index = -1):
|
||||
if context_index == -1:
|
||||
context_index = self.current_element_index + 1
|
||||
|
||||
var context_element = self.callstack[context_index - 1]
|
||||
|
||||
if !declare_new && !context_element.temporary_variables.has(name):
|
||||
InkUtils.throw_exception("Could not find temporary variable to set: %s" % name)
|
||||
return
|
||||
|
||||
if context_element.temporary_variables.has(name):
|
||||
var old_value = context_element.temporary_variables[name]
|
||||
InkListValue.retain_list_origins_for_assignment(old_value, value)
|
||||
|
||||
context_element.temporary_variables[name] = value
|
||||
|
||||
|
||||
# (String) -> int
|
||||
func context_for_variable_named(name):
|
||||
if self.current_element.temporary_variables.has(name):
|
||||
return self.current_element_index + 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
# (int) -> InkThread | null
|
||||
func thread_with_index(index):
|
||||
for thread in self._threads:
|
||||
if thread.thread_index == index:
|
||||
return thread
|
||||
|
||||
return null
|
||||
|
||||
var callstack : get = get_callstack
|
||||
func get_callstack():
|
||||
return self.current_thread.callstack
|
||||
|
||||
var callstack_trace : get = get_callstack_trace
|
||||
func get_callstack_trace():
|
||||
var sb = ""
|
||||
var t = 0
|
||||
while t < _threads.size():
|
||||
var thread = _threads[t]
|
||||
var is_current = (t == _threads.size() - 1)
|
||||
sb += str("=== THREAD ", str(t + 1), "/", str(_threads.size()), " ",
|
||||
("(current) " if is_current else "" ), "===\n")
|
||||
|
||||
var i = 0
|
||||
while i < thread.callstack.size():
|
||||
if thread.callstack[i].type == Ink.PushPopType.FUNCTION:
|
||||
sb += " [FUNCTION] "
|
||||
else:
|
||||
sb += " [TUNNEL] "
|
||||
|
||||
var pointer = thread.callstack[i].current_pointer
|
||||
if !pointer.is_null:
|
||||
sb += "<SOMEWHERE IN "
|
||||
sb += pointer.container.path._to_string()
|
||||
sb += "\n>"
|
||||
|
||||
i += 1
|
||||
t += 1
|
||||
|
||||
return sb
|
||||
|
||||
var _threads = null # Array<InkThread>
|
||||
var _thread_counter = 0 # int
|
||||
var _start_of_root = InkPointer.null_pointer # Pointer
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "CallStack" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "CallStack"
|
||||
|
||||
# C# Actions & Delegates ##################################################### #
|
||||
|
||||
# (SimpleJson.Writer) -> void
|
||||
func _anonymous_write_json(writer: InkSimpleJSON.Writer) -> void:
|
||||
writer.write_property_start("threads")
|
||||
writer.write_array_start()
|
||||
for thread in self._threads:
|
||||
thread.write_json(writer)
|
||||
writer.write_array_end()
|
||||
writer.write_property_end()
|
||||
|
||||
writer.write_property_start("threadCounter")
|
||||
writer.write(self._thread_counter)
|
||||
writer.write_property_end()
|
||||
|
||||
# ######################################################################## #
|
||||
|
||||
var StaticJSON: InkStaticJSON:
|
||||
get: return _static_json.get_ref()
|
||||
|
||||
var _static_json = WeakRef.new()
|
||||
|
||||
func get_static_json(static_json = null):
|
||||
if static_json != null:
|
||||
_static_json = weakref(static_json)
|
||||
return
|
||||
|
||||
var InkRuntime = Engine.get_main_loop().root.get_node("__InkRuntime")
|
||||
|
||||
InkUtils.__assert__(InkRuntime != null,
|
||||
str("[InkCallStack] Could not retrieve 'InkRuntime' singleton from the scene tree."))
|
||||
|
||||
_static_json = weakref(InkRuntime.json)
|
24
addons/inkgd/runtime/common/ink.gd
Normal file
24
addons/inkgd/runtime/common/ink.gd
Normal file
|
@ -0,0 +1,24 @@
|
|||
# ############################################################################ #
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
class_name Ink
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
enum ErrorType {
|
||||
AUTHOR,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
enum PushPopType {
|
||||
TUNNEL,
|
||||
FUNCTION,
|
||||
FUNCTION_EVALUATION_FROM_GAME
|
||||
}
|
31
addons/inkgd/runtime/common/ink_base.gd
Normal file
31
addons/inkgd/runtime/common/ink_base.gd
Normal file
|
@ -0,0 +1,31 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkBase
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func equals(_ink_base: InkBase) -> bool:
|
||||
return false
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkBase"
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkBase"
|
212
addons/inkgd/runtime/common/ink_object.gd
Normal file
212
addons/inkgd/runtime/common/ink_object.gd
Normal file
|
@ -0,0 +1,212 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkBase
|
||||
|
||||
class_name InkObject
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# Encapsulating parent into a weak ref.
|
||||
var parent: InkObject:
|
||||
get:
|
||||
return self._parent.get_ref()
|
||||
|
||||
set(value):
|
||||
self._parent = weakref(value)
|
||||
|
||||
var _parent: WeakRef = WeakRef.new() # InkObject
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var debug_metadata: InkDebugMetadata:
|
||||
get:
|
||||
if _debug_metadata == null:
|
||||
if self.parent:
|
||||
return self.parent.debug_metadata
|
||||
return _debug_metadata
|
||||
|
||||
set(value):
|
||||
_debug_metadata = value
|
||||
|
||||
var _debug_metadata: InkDebugMetadata = null
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var own_debug_metadata: InkDebugMetadata:
|
||||
get: return _debug_metadata
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (InkPath) -> int?
|
||||
func debug_line_number_of_path(path: InkPath):
|
||||
if path == null:
|
||||
return null
|
||||
|
||||
var root = self.root_content_container
|
||||
if root != null:
|
||||
var target_content := root.content_at_path(path).obj
|
||||
if target_content:
|
||||
var dm = target_content.debug_metadata
|
||||
if dm != null:
|
||||
return dm.start_line_number
|
||||
|
||||
return null
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
# InkPath
|
||||
var path: InkPath:
|
||||
get:
|
||||
if _path == null:
|
||||
if self.parent == null:
|
||||
_path = InkPath.new()
|
||||
else:
|
||||
var comps: Array = [] # Stack<Path3D.Component>
|
||||
|
||||
var child = self
|
||||
var container = InkUtils.as_or_null(child.parent, "InkContainer")
|
||||
|
||||
while container:
|
||||
var named_child = InkUtils.as_INamedContent_or_null(child)
|
||||
if (named_child != null && named_child.has_valid_name):
|
||||
comps.push_front(InkPath.Component.new(named_child.name))
|
||||
else:
|
||||
comps.push_front(InkPath.Component.new(container.content.find(child)))
|
||||
|
||||
child = container
|
||||
container = InkUtils.as_or_null(container.parent, "InkContainer")
|
||||
|
||||
_path = InkPath.new_with_components(comps)
|
||||
|
||||
return _path
|
||||
|
||||
var _path: InkPath = null
|
||||
|
||||
|
||||
func resolve_path(path: InkPath) -> InkSearchResult:
|
||||
if path.is_relative:
|
||||
var nearest_container = InkUtils.as_or_null(self, "InkContainer")
|
||||
if !nearest_container:
|
||||
InkUtils.__assert__(
|
||||
self.parent != null,
|
||||
"Can't resolve relative path because we don't have a parent"
|
||||
)
|
||||
|
||||
nearest_container = InkUtils.as_or_null(self.parent, "InkContainer")
|
||||
|
||||
InkUtils.__assert__(nearest_container != null, "Expected parent to be a container")
|
||||
InkUtils.__assert__(path.get_component(0).is_parent)
|
||||
|
||||
path = path.tail
|
||||
|
||||
return nearest_container.content_at_path(path)
|
||||
else:
|
||||
return self.root_content_container.content_at_path(path)
|
||||
|
||||
|
||||
func convert_path_to_relative(global_path: InkPath) -> InkPath:
|
||||
var own_path := self.path
|
||||
|
||||
var min_path_length: int = min(global_path.length, own_path.length)
|
||||
var last_shared_path_comp_index: int = -1
|
||||
|
||||
var i: int = 0
|
||||
while i < min_path_length:
|
||||
var own_comp: InkPath.Component = own_path.get_component(i)
|
||||
var other_comp: InkPath.Component = global_path.get_component(i)
|
||||
|
||||
if own_comp.equals(other_comp):
|
||||
last_shared_path_comp_index = i
|
||||
else:
|
||||
break
|
||||
|
||||
i += 1
|
||||
|
||||
if last_shared_path_comp_index == -1:
|
||||
return global_path
|
||||
|
||||
var num_upwards_moves: int = (own_path.length - 1) - last_shared_path_comp_index
|
||||
|
||||
var new_path_comps: Array = [] # Array<InkPath.Component>
|
||||
|
||||
var up = 0
|
||||
while up < num_upwards_moves:
|
||||
new_path_comps.append(InkPath.Component.to_parent())
|
||||
up += 1
|
||||
|
||||
var down = last_shared_path_comp_index + 1
|
||||
while down < global_path.length:
|
||||
new_path_comps.append(global_path.get_component(down))
|
||||
down += 1
|
||||
|
||||
var relative_path = InkPath.new_with_components(new_path_comps, true)
|
||||
return relative_path
|
||||
|
||||
|
||||
func compact_path_string(other_path: InkPath) -> String:
|
||||
var global_path_str
|
||||
var relative_path_str
|
||||
|
||||
if other_path.is_relative:
|
||||
relative_path_str = other_path.components_string
|
||||
global_path_str = self.path.path_by_appending_path(other_path).components_string
|
||||
else:
|
||||
var relative_path = convert_path_to_relative(other_path)
|
||||
relative_path_str = relative_path.components_string
|
||||
global_path_str = other_path.components_string
|
||||
|
||||
if (relative_path_str.length() < global_path_str.length()):
|
||||
return relative_path_str
|
||||
else:
|
||||
return global_path_str
|
||||
|
||||
|
||||
# () -> InkContainer
|
||||
var root_content_container: InkContainer:
|
||||
get:
|
||||
var ancestor := self
|
||||
while (ancestor.parent):
|
||||
ancestor = ancestor.parent
|
||||
|
||||
return InkUtils.as_or_null(ancestor, "InkContainer")
|
||||
|
||||
|
||||
# () -> InkObject
|
||||
func copy():
|
||||
InkUtils.throw_exception("Not Implemented: Doesn't support copying")
|
||||
return null
|
||||
|
||||
|
||||
# (InkObject, InkObject) -> void
|
||||
func set_child(obj: InkObject, value: InkObject):
|
||||
if obj:
|
||||
obj.parent = null
|
||||
|
||||
obj = value
|
||||
|
||||
if obj:
|
||||
obj.parent = self
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkObject" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkObject"
|
58
addons/inkgd/runtime/content/choices/ink_choice.gd
Normal file
58
addons/inkgd/runtime/content/choices/ink_choice.gd
Normal file
|
@ -0,0 +1,58 @@
|
|||
# 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 InkObject
|
||||
|
||||
class_name InkChoice
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var text: String
|
||||
|
||||
var path_string_on_choice: String:
|
||||
get:
|
||||
# TODO: handle null case?
|
||||
return target_path._to_string()
|
||||
|
||||
set(value):
|
||||
target_path = InkPath.new_with_components_string(value)
|
||||
|
||||
|
||||
var source_path = null # String?
|
||||
|
||||
|
||||
var index: int = 0
|
||||
|
||||
|
||||
var target_path: InkPath = null
|
||||
|
||||
|
||||
var thread_at_generation: InkCallStack.InkThread = null
|
||||
|
||||
|
||||
var original_thread_index: int = 0
|
||||
|
||||
|
||||
var is_invisible_default: bool = false
|
||||
|
||||
|
||||
var tags = null # Array<String>?
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "Choice" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class():
|
||||
return "Choice"
|
122
addons/inkgd/runtime/content/choices/ink_choice_point.gd
Normal file
122
addons/inkgd/runtime/content/choices/ink_choice_point.gd
Normal file
|
@ -0,0 +1,122 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkChoicePoint
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# () -> InkPath
|
||||
# (InkPath) -> void
|
||||
var path_on_choice: InkPath:
|
||||
get:
|
||||
if self._path_on_choice != null && self._path_on_choice.is_relative:
|
||||
var choice_target_obj := self.choice_target
|
||||
if choice_target_obj:
|
||||
self._path_on_choice = choice_target_obj.path
|
||||
|
||||
return _path_on_choice
|
||||
|
||||
set(value):
|
||||
_path_on_choice = value
|
||||
|
||||
var _path_on_choice: InkPath = null
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var choice_target: InkContainer:
|
||||
get:
|
||||
var cont: InkContainer = resolve_path(self._path_on_choice).container
|
||||
return cont
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var path_string_on_choice: String:
|
||||
get:
|
||||
return compact_path_string(self.path_on_choice)
|
||||
|
||||
set(value):
|
||||
self.path_on_choice = InkPath.new_with_components_string(value)
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var has_condition: bool
|
||||
|
||||
|
||||
var has_start_content: bool
|
||||
|
||||
|
||||
var has_choice_only_content: bool
|
||||
|
||||
|
||||
var once_only: bool
|
||||
|
||||
|
||||
var is_invisible_default: bool
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var flags: int:
|
||||
get:
|
||||
var flags: int = 0
|
||||
|
||||
if has_condition:
|
||||
flags |= 1
|
||||
if has_start_content:
|
||||
flags |= 2
|
||||
if has_choice_only_content:
|
||||
flags |= 4
|
||||
if is_invisible_default:
|
||||
flags |= 8
|
||||
if once_only:
|
||||
flags |= 16
|
||||
|
||||
return flags
|
||||
|
||||
set(value):
|
||||
has_condition = (value & 1) > 0
|
||||
has_start_content = (value & 2) > 0
|
||||
has_choice_only_content = (value & 4) > 0
|
||||
is_invisible_default = (value & 8) > 0
|
||||
once_only = (value & 16) > 0
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _init(once_only: bool = true):
|
||||
self.once_only = once_only
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
var target_line_num = debug_line_number_of_path(self.path_on_choice)
|
||||
var target_string := self.path_on_choice._to_string()
|
||||
|
||||
if target_line_num != null:
|
||||
target_string = " line %d(%s)" % [target_line_num, target_string]
|
||||
|
||||
return "Choice: -> %s" % target_string
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "ChoicePoint" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "ChoicePoint"
|
333
addons/inkgd/runtime/content/ink_container.gd
Normal file
333
addons/inkgd/runtime/content/ink_container.gd
Normal file
|
@ -0,0 +1,333 @@
|
|||
# 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 InkObject
|
||||
|
||||
class_name InkContainer
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
|
||||
var name = null # String?
|
||||
|
||||
|
||||
var content: Array: # Array<InkObject>
|
||||
get:
|
||||
return self._content
|
||||
set(value):
|
||||
add_content(value)
|
||||
|
||||
|
||||
var _content: Array # Array<InkObject>
|
||||
|
||||
|
||||
var named_content: Dictionary # Dictionary<string, INamedContent>
|
||||
|
||||
|
||||
var named_only_content: # Dictionary<string, InkObject>?
|
||||
get:
|
||||
var named_only_content_dict = {} # Dictionary<string, InkObject>?
|
||||
for key in self.named_content:
|
||||
named_only_content_dict[key] = self.named_content[key]
|
||||
|
||||
for c in self.content:
|
||||
var named = InkUtils.as_INamedContent_or_null(c)
|
||||
if named != null && named.has_valid_name:
|
||||
named_only_content_dict.erase(named.name)
|
||||
|
||||
if named_only_content_dict.size() == 0:
|
||||
named_only_content_dict = null
|
||||
|
||||
return named_only_content_dict
|
||||
|
||||
set(value):
|
||||
var existing_named_only = named_only_content
|
||||
if existing_named_only != null:
|
||||
for key in existing_named_only:
|
||||
self.named_content.erase(key)
|
||||
|
||||
if value == null:
|
||||
return
|
||||
|
||||
for key in value:
|
||||
var named = InkUtils.as_INamedContent_or_null(value[key])
|
||||
if named != null:
|
||||
add_to_named_content_only(named)
|
||||
|
||||
|
||||
var visits_should_be_counted: bool = false
|
||||
|
||||
|
||||
var turn_index_should_be_counted: bool = false
|
||||
|
||||
|
||||
var counting_at_start_only: bool = false
|
||||
|
||||
|
||||
enum CountFlags {
|
||||
VISITS = 1,
|
||||
TURNS = 2,
|
||||
COUNT_START_ONLY = 4
|
||||
}
|
||||
|
||||
|
||||
# CountFlags
|
||||
var count_flags: int:
|
||||
get:
|
||||
var flags = 0
|
||||
|
||||
if visits_should_be_counted: flags |= CountFlags.VISITS
|
||||
if turn_index_should_be_counted: flags |= CountFlags.TURNS
|
||||
if counting_at_start_only: flags |= CountFlags.COUNT_START_ONLY
|
||||
|
||||
if flags == CountFlags.COUNT_START_ONLY:
|
||||
flags = 0
|
||||
|
||||
return flags
|
||||
|
||||
set(value):
|
||||
var flag = value
|
||||
if (flag & CountFlags.VISITS) > 0: visits_should_be_counted = true
|
||||
if (flag & CountFlags.TURNS) > 0: turn_index_should_be_counted = true
|
||||
if (flag & CountFlags.COUNT_START_ONLY) > 0: counting_at_start_only = true
|
||||
|
||||
|
||||
var has_valid_name: bool:
|
||||
get: return self.name != null && self.name.length() > 0
|
||||
|
||||
var path_to_first_leaf_content: InkPath:
|
||||
get:
|
||||
if self._path_to_first_leaf_content == null:
|
||||
self._path_to_first_leaf_content = self.path.path_by_appending_path(self.internal_path_to_first_leaf_content)
|
||||
|
||||
return self._path_to_first_leaf_content
|
||||
|
||||
# InkPath?
|
||||
var _path_to_first_leaf_content: InkPath = null
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
var internal_path_to_first_leaf_content: InkPath:
|
||||
get:
|
||||
var components: Array = [] # Array<InkPath.InkComponent>
|
||||
var container: InkContainer = self
|
||||
while container != null:
|
||||
if container.content.size() > 0:
|
||||
components.append(InkPath.Component.new(0))
|
||||
container = InkUtils.as_or_null(container.content[0], "InkContainer")
|
||||
|
||||
return InkPath.new_with_components(components)
|
||||
|
||||
|
||||
func _init():
|
||||
self._content = [] # Array<InkObject>
|
||||
self.named_content = {} # Dictionary<string, INamedContent>
|
||||
|
||||
|
||||
func add_content(content_obj_or_content_list) -> void:
|
||||
if InkUtils.is_ink_class(content_obj_or_content_list, "InkObject"):
|
||||
var content_obj: InkObject = content_obj_or_content_list
|
||||
self.content.append(content_obj)
|
||||
|
||||
if content_obj.parent:
|
||||
InkUtils.throw_exception("content is already in %s" % content_obj.parent._to_string())
|
||||
return
|
||||
|
||||
content_obj.parent = self
|
||||
|
||||
try_add_named_content(content_obj)
|
||||
elif content_obj_or_content_list is Array:
|
||||
var content_list: Array = content_obj_or_content_list
|
||||
for c in content_list:
|
||||
add_content(c)
|
||||
|
||||
|
||||
func insert_content(content_obj: InkObject, index: int) -> void:
|
||||
self.content.insert(index, content_obj)
|
||||
|
||||
if content_obj.parent:
|
||||
InkUtils.throw_exception("content is already in %s" % content_obj.parent._to_string())
|
||||
return
|
||||
|
||||
content_obj.parent = self
|
||||
|
||||
try_add_named_content(content_obj)
|
||||
|
||||
|
||||
func try_add_named_content(content_obj: InkObject) -> void:
|
||||
var named_content_obj = InkUtils.as_INamedContent_or_null(content_obj)
|
||||
if (named_content_obj != null && named_content_obj.has_valid_name):
|
||||
add_to_named_content_only(named_content_obj)
|
||||
|
||||
|
||||
# (INamedContent) -> void
|
||||
func add_to_named_content_only(named_content_obj: InkObject) -> void:
|
||||
InkUtils.__assert__(named_content_obj.is_ink_class("InkObject"), "Can only add Runtime.Objects to a Runtime.Container")
|
||||
var runtime_obj = named_content_obj
|
||||
runtime_obj.parent = self
|
||||
|
||||
named_content[named_content_obj.name] = named_content_obj
|
||||
|
||||
|
||||
func add_contents_of_container(other_container: InkContainer) -> void:
|
||||
self.content = self.content + other_container.content
|
||||
for obj in other_container.content:
|
||||
obj.parent = self
|
||||
try_add_named_content(obj)
|
||||
|
||||
|
||||
func content_with_path_component(component: InkPath.Component) -> InkObject:
|
||||
if component.is_index:
|
||||
if component.index >= 0 && component.index < self.content.size():
|
||||
return self.content[component.index]
|
||||
else:
|
||||
return null
|
||||
elif component.is_parent:
|
||||
return self.parent
|
||||
else:
|
||||
if named_content.has(component.name):
|
||||
var found_content = named_content[component.name]
|
||||
return found_content
|
||||
else:
|
||||
return null
|
||||
|
||||
|
||||
func content_at_path(
|
||||
path: InkPath,
|
||||
partial_path_start: int = 0,
|
||||
partial_path_length: int = -1
|
||||
) -> InkSearchResult:
|
||||
if partial_path_length == -1:
|
||||
partial_path_length = path.length
|
||||
|
||||
var result: InkSearchResult = InkSearchResult.new()
|
||||
result.approximate = false
|
||||
|
||||
var current_container: InkContainer = self
|
||||
var current_obj: InkObject = self
|
||||
|
||||
var i: int = partial_path_start
|
||||
while i < partial_path_length:
|
||||
var comp = path.get_component(i)
|
||||
|
||||
if current_container == null:
|
||||
result.approximate = true
|
||||
break
|
||||
|
||||
var found_obj: InkObject = current_container.content_with_path_component(comp)
|
||||
|
||||
if found_obj == null:
|
||||
result.approximate = true
|
||||
break
|
||||
|
||||
current_obj = found_obj
|
||||
current_container = InkUtils.as_or_null(found_obj, "InkContainer")
|
||||
|
||||
i += 1
|
||||
|
||||
result.obj = current_obj
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func build_string_of_hierarchy(
|
||||
existing_hierarchy: String,
|
||||
indentation: int,
|
||||
pointed_obj: InkObject
|
||||
) -> String:
|
||||
existing_hierarchy = _append_indentation(existing_hierarchy, indentation)
|
||||
existing_hierarchy += "["
|
||||
|
||||
if self.has_valid_name:
|
||||
existing_hierarchy += str(" (%s) " % self.name)
|
||||
|
||||
if self == pointed_obj:
|
||||
existing_hierarchy += " <---"
|
||||
|
||||
existing_hierarchy += "\n"
|
||||
|
||||
indentation += 1
|
||||
|
||||
var i = 0
|
||||
while i < self.content.size():
|
||||
var obj = self.content[i]
|
||||
|
||||
if InkUtils.is_ink_class(obj, "InkContainer"):
|
||||
existing_hierarchy = obj.build_string_of_hierarchy(existing_hierarchy, indentation, pointed_obj)
|
||||
else:
|
||||
existing_hierarchy = _append_indentation(existing_hierarchy, indentation)
|
||||
if InkUtils.is_ink_class(obj, "StringValue"):
|
||||
existing_hierarchy += "\""
|
||||
existing_hierarchy += obj._to_string().replace("\n", "\\n")
|
||||
existing_hierarchy += "\""
|
||||
else:
|
||||
existing_hierarchy += obj._to_string()
|
||||
|
||||
if i != self.content.size() - 1:
|
||||
existing_hierarchy += ","
|
||||
|
||||
if !InkUtils.is_ink_class(obj, "InkContainer") && obj == pointed_obj:
|
||||
existing_hierarchy += " <---"
|
||||
|
||||
existing_hierarchy += "\n"
|
||||
i += 1
|
||||
|
||||
var only_named: Dictionary = {} # Dictionary<String, INamedContent>
|
||||
|
||||
for obj_key in self.named_content:
|
||||
var value = self.named_content[obj_key]
|
||||
if self.content.find(value) != -1:
|
||||
continue
|
||||
else:
|
||||
only_named[obj_key] = value
|
||||
|
||||
if only_named.size() > 0:
|
||||
existing_hierarchy = _append_indentation(existing_hierarchy, indentation)
|
||||
existing_hierarchy += "-- named: --\n"
|
||||
|
||||
for object_key in only_named:
|
||||
var value = only_named[object_key]
|
||||
InkUtils.__assert__(InkUtils.is_ink_class(value, "InkContainer"), "Can only print out named Containers")
|
||||
var container = value
|
||||
existing_hierarchy = container.build_string_of_hierarchy(existing_hierarchy, indentation, pointed_obj)
|
||||
existing_hierarchy += "\n"
|
||||
|
||||
indentation -= 1
|
||||
|
||||
existing_hierarchy = _append_indentation(existing_hierarchy, indentation)
|
||||
existing_hierarchy += "]"
|
||||
|
||||
return existing_hierarchy
|
||||
|
||||
|
||||
func build_full_string_of_hierarchy() -> String:
|
||||
return build_string_of_hierarchy("", 0, null)
|
||||
|
||||
|
||||
func _append_indentation(string: String, indentation: int) -> String:
|
||||
var spaces_per_indent = 4
|
||||
var i = 0
|
||||
while(i < spaces_per_indent * indentation):
|
||||
string += " "
|
||||
i += 1
|
||||
|
||||
return string
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkContainer" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkContainer"
|
213
addons/inkgd/runtime/content/ink_control_command.gd
Normal file
213
addons/inkgd/runtime/content/ink_control_command.gd
Normal file
|
@ -0,0 +1,213 @@
|
|||
# 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 InkObject
|
||||
|
||||
class_name InkControlCommand
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
enum CommandType {
|
||||
NOT_SET = -1,
|
||||
EVAL_START,
|
||||
EVAL_OUTPUT,
|
||||
EVAL_END,
|
||||
DUPLICATE,
|
||||
POP_EVALUATED_VALUE,
|
||||
POP_FUNCTION,
|
||||
POP_TUNNEL,
|
||||
BEGIN_STRING,
|
||||
END_STRING,
|
||||
NO_OP,
|
||||
CHOICE_COUNT,
|
||||
TURNS,
|
||||
TURNS_SINCE,
|
||||
READ_COUNT,
|
||||
RANDOM,
|
||||
SEED_RANDOM,
|
||||
VISIT_INDEX,
|
||||
SEQUENCE_SHUFFLE_INDEX,
|
||||
START_THREAD,
|
||||
DONE,
|
||||
END,
|
||||
LIST_FROM_INT,
|
||||
LIST_RANGE,
|
||||
LIST_RANDOM,
|
||||
BEGIN_TAG,
|
||||
END_TAG,
|
||||
#----
|
||||
TOTAL_VALUES
|
||||
}
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# CommandType
|
||||
var command_type: int
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init(command_type: int = CommandType.NOT_SET):
|
||||
self.command_type = command_type
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func copy() -> InkControlCommand:
|
||||
return InkControlCommand.new(self.command_type)
|
||||
|
||||
static func eval_start() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.EVAL_START)
|
||||
|
||||
|
||||
static func eval_output() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.EVAL_OUTPUT)
|
||||
|
||||
|
||||
static func eval_end() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.EVAL_END)
|
||||
|
||||
|
||||
static func duplicate() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.DUPLICATE)
|
||||
|
||||
|
||||
static func pop_evaluated_value() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.POP_EVALUATED_VALUE)
|
||||
|
||||
|
||||
static func pop_function() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.POP_FUNCTION)
|
||||
|
||||
|
||||
static func pop_tunnel() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.POP_TUNNEL)
|
||||
|
||||
|
||||
static func begin_string() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.BEGIN_STRING)
|
||||
|
||||
|
||||
static func end_string() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.END_STRING)
|
||||
|
||||
|
||||
static func no_op() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.NO_OP)
|
||||
|
||||
|
||||
static func choice_count() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.CHOICE_COUNT)
|
||||
|
||||
|
||||
static func turns() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.TURNS)
|
||||
|
||||
|
||||
static func turns_since() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.TURNS_SINCE)
|
||||
|
||||
|
||||
static func read_count() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.READ_COUNT)
|
||||
|
||||
|
||||
static func random() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.RANDOM)
|
||||
|
||||
|
||||
static func seed_random() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.SEED_RANDOM)
|
||||
|
||||
|
||||
static func visit_index() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.VISIT_INDEX)
|
||||
|
||||
|
||||
static func sequence_shuffle_index() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.SEQUENCE_SHUFFLE_INDEX)
|
||||
|
||||
|
||||
static func done() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.DONE)
|
||||
|
||||
|
||||
static func end() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.END)
|
||||
|
||||
|
||||
static func list_from_int() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.LIST_FROM_INT)
|
||||
|
||||
|
||||
static func list_range() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.LIST_RANGE)
|
||||
|
||||
|
||||
static func list_random() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.LIST_RANDOM)
|
||||
|
||||
|
||||
static func begin_tag() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.BEGIN_TAG)
|
||||
|
||||
|
||||
static func end_tag() -> InkControlCommand:
|
||||
return InkControlCommand.new(CommandType.END_TAG)
|
||||
|
||||
|
||||
# () -> String
|
||||
func _to_string() -> String:
|
||||
var command_name: String = ""
|
||||
match self.command_type:
|
||||
CommandType.NOT_SET: command_name = "NOT_SET"
|
||||
CommandType.EVAL_START: command_name = "EVAL_START"
|
||||
CommandType.EVAL_OUTPUT: command_name = "EVAL_OUTPUT"
|
||||
CommandType.EVAL_END: command_name = "EVAL_END"
|
||||
CommandType.DUPLICATE: command_name = "DUPLICATE"
|
||||
CommandType.POP_EVALUATED_VALUE: command_name = "POP_EVALUATED_VALUE"
|
||||
CommandType.POP_FUNCTION: command_name = "POP_FUNCTION"
|
||||
CommandType.POP_TUNNEL: command_name = "POP_TUNNEL"
|
||||
CommandType.BEGIN_STRING: command_name = "BEGIN_STRING"
|
||||
CommandType.END_STRING: command_name = "END_STRING"
|
||||
CommandType.NO_OP: command_name = "NO_OP"
|
||||
CommandType.CHOICE_COUNT: command_name = "CHOICE_COUNT"
|
||||
CommandType.TURNS: command_name = "TURNS"
|
||||
CommandType.TURNS_SINCE: command_name = "TURNS_SINCE"
|
||||
CommandType.READ_COUNT: command_name = "READ_COUNT"
|
||||
CommandType.RANDOM: command_name = "RANDOM"
|
||||
CommandType.SEED_RANDOM: command_name = "SEED_RANDOM"
|
||||
CommandType.VISIT_INDEX: command_name = "VISIT_INDEX"
|
||||
CommandType.SEQUENCE_SHUFFLE_INDEX: command_name = "SEQUENCE_SHUFFLE_INDEX"
|
||||
CommandType.START_THREAD: command_name = "START_THREAD"
|
||||
CommandType.DONE: command_name = "DONE"
|
||||
CommandType.END: command_name = "END"
|
||||
CommandType.LIST_FROM_INT: command_name = "LIST_FROM_INT"
|
||||
CommandType.LIST_RANGE: command_name = "LIST_RANGE"
|
||||
CommandType.LIST_RANDOM: command_name = "LIST_RANDOM"
|
||||
CommandType.BEGIN_TAG: command_name = "BEGIN_TAG"
|
||||
CommandType.END_TAG: command_name = "END_TAG"
|
||||
CommandType.TOTAL_VALUES: command_name = "TOTAL_VALUES"
|
||||
|
||||
return "Command(%s)" % command_name
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "ControlCommand" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "ControlCommand"
|
145
addons/inkgd/runtime/content/ink_divert.gd
Normal file
145
addons/inkgd/runtime/content/ink_divert.gd
Normal file
|
@ -0,0 +1,145 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkDivert
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var target_path: InkPath:
|
||||
get:
|
||||
if self._target_path != null && self._target_path.is_relative:
|
||||
var target_obj: InkObject = self.target_pointer.resolve()
|
||||
if target_obj:
|
||||
self._target_path = target_obj.path
|
||||
|
||||
return self._target_path
|
||||
|
||||
set(value):
|
||||
self._target_path = value
|
||||
self._target_pointer = InkPointer.null_pointer
|
||||
|
||||
var _target_path: InkPath = null
|
||||
|
||||
|
||||
var target_pointer: InkPointer:
|
||||
get:
|
||||
if self._target_pointer.is_null:
|
||||
var target_obj = resolve_path(self._target_path).obj
|
||||
|
||||
if self._target_path.last_component.is_index:
|
||||
self._target_pointer = InkPointer.new(
|
||||
InkUtils.as_or_null(target_obj.parent, "InkContainer"),
|
||||
self._target_path.last_component.index
|
||||
)
|
||||
else:
|
||||
self._target_pointer = InkPointer.start_of(InkUtils.as_or_null(target_obj, "InkContainer"))
|
||||
|
||||
return self._target_pointer
|
||||
|
||||
var _target_pointer: InkPointer = InkPointer.null_pointer
|
||||
|
||||
|
||||
var target_path_string: # String?
|
||||
get:
|
||||
if self.target_path == null:
|
||||
return null
|
||||
|
||||
return self.compact_path_string(self.target_path)
|
||||
|
||||
set(value):
|
||||
if value == null:
|
||||
self.target_path = null
|
||||
else:
|
||||
self.target_path = InkPath.new_with_components_string(value)
|
||||
|
||||
|
||||
var variable_divert_name = null # String?
|
||||
var has_variable_target: bool:
|
||||
get: return self.variable_divert_name != null
|
||||
|
||||
|
||||
var pushes_to_stack: bool = false
|
||||
|
||||
var stack_push_type: int = 0 # Ink.PushPopType
|
||||
|
||||
var is_external: bool = false
|
||||
|
||||
var external_args: int = 0
|
||||
|
||||
var is_conditional: bool = false
|
||||
|
||||
|
||||
# (int?) -> InkDivert
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init_with(stack_push_type = null):
|
||||
self.pushes_to_stack = false
|
||||
|
||||
if stack_push_type != null:
|
||||
self.pushes_to_stack = true
|
||||
self.stack_push_type = stack_push_type
|
||||
|
||||
|
||||
func equals(obj: InkBase) -> bool:
|
||||
var other_divert: InkDivert = InkUtils.as_or_null(obj, "Divert")
|
||||
if other_divert:
|
||||
if self.has_variable_target == other_divert.has_variable_target:
|
||||
if self.has_variable_target:
|
||||
return self.variable_divert_name == other_divert.variable_divert_name
|
||||
else:
|
||||
return self.target_path.equals(other_divert.target_path)
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
if self.has_variable_target:
|
||||
return "Divert(variable: %s)" % self.variable_divert_name
|
||||
elif self.target_path == null:
|
||||
return "Divert(null)"
|
||||
else:
|
||||
var _string = ""
|
||||
|
||||
var target_str: String = self.target_path._to_string()
|
||||
var target_line_num = debug_line_number_of_path(self.target_path)
|
||||
if target_line_num != null:
|
||||
target_str = "line " + target_line_num
|
||||
|
||||
_string += "Divert"
|
||||
|
||||
if self.is_conditional:
|
||||
_string += "?"
|
||||
|
||||
if self.pushes_to_stack:
|
||||
if self.stack_push_type == Ink.PushPopType.FUNCTION:
|
||||
_string += " function"
|
||||
else:
|
||||
_string += " tunnel"
|
||||
|
||||
_string += " -> "
|
||||
_string += self.target_path_string
|
||||
|
||||
_string += " (%s)" % target_str
|
||||
|
||||
return _string
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "Divert" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "Divert"
|
29
addons/inkgd/runtime/content/ink_glue.gd
Normal file
29
addons/inkgd/runtime/content/ink_glue.gd
Normal file
|
@ -0,0 +1,29 @@
|
|||
# ############################################################################ #
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkGlue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Glue"
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "Glue" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "Glue"
|
372
addons/inkgd/runtime/content/ink_native_function_call.gd
Normal file
372
addons/inkgd/runtime/content/ink_native_function_call.gd
Normal file
|
@ -0,0 +1,372 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkNativeFunctionCall
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
# TODO: Migrate to Ink.ValueType
|
||||
const ValueType = preload("res://addons/inkgd/runtime/values/value_type.gd").ValueType
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (String) -> NativeFunctionCall
|
||||
@warning_ignore("shadowed_variable")
|
||||
static func call_with_name(
|
||||
function_name: String,
|
||||
_static_native_function_call: InkStaticNativeFunctionCall = null
|
||||
) -> InkNativeFunctionCall:
|
||||
return InkNativeFunctionCall.new_with_name(function_name, _static_native_function_call)
|
||||
|
||||
|
||||
var name: String:
|
||||
get:
|
||||
return _name
|
||||
|
||||
set(value):
|
||||
_name = value
|
||||
if !_is_prototype:
|
||||
_prototype = self._static_native_function_call.native_functions[_name]
|
||||
|
||||
var _name: String
|
||||
|
||||
|
||||
var number_of_parameters: int:
|
||||
get:
|
||||
if _prototype:
|
||||
return _prototype.number_of_parameters
|
||||
else:
|
||||
return _number_of_parameters
|
||||
|
||||
set(value):
|
||||
_number_of_parameters = value
|
||||
|
||||
var _number_of_parameters: int = 0
|
||||
|
||||
|
||||
# (Array<InkObject>) -> InkObject
|
||||
#
|
||||
# The name is different to avoid shadowing 'Object.call'
|
||||
#
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func call_with_parameters(parameters: Array, metadata: StoryErrorMetadata) -> InkObject:
|
||||
if _prototype:
|
||||
return _prototype.call_with_parameters(parameters, metadata)
|
||||
|
||||
if self.number_of_parameters != parameters.size():
|
||||
InkUtils.throw_exception("Unexpected number of parameters")
|
||||
return null
|
||||
|
||||
var has_list = false
|
||||
for p in parameters:
|
||||
if InkUtils.is_ink_class(p, "Void"):
|
||||
InkUtils.throw_story_exception(
|
||||
"Attempting to perform operation on a void value. Did you forget to " +
|
||||
"'return' a value from a function you called here?",
|
||||
false,
|
||||
metadata
|
||||
)
|
||||
return null
|
||||
if InkUtils.is_ink_class(p, "ListValue"):
|
||||
has_list = true
|
||||
|
||||
if parameters.size() == 2 && has_list:
|
||||
return call_binary_list_operation(parameters, metadata)
|
||||
|
||||
var coerced_params: Array = coerce_values_to_single_type(parameters, metadata)
|
||||
|
||||
# ValueType
|
||||
var coerced_type: int = coerced_params[0].value_type
|
||||
|
||||
if (
|
||||
coerced_type == ValueType.INT ||
|
||||
coerced_type == ValueType.FLOAT ||
|
||||
coerced_type == ValueType.STRING ||
|
||||
coerced_type == ValueType.DIVERT_TARGET ||
|
||||
coerced_type == ValueType.LIST
|
||||
):
|
||||
return call_coerced(coerced_params, metadata)
|
||||
|
||||
return null
|
||||
|
||||
|
||||
# (Array<Value>) -> Value # Call<T> in the original code
|
||||
#
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func call_coerced(parameters_of_single_type: Array, metadata: StoryErrorMetadata) -> InkValue:
|
||||
var param1: InkValue = parameters_of_single_type[0]
|
||||
var val_type: int = param1.value_type
|
||||
|
||||
var param_count: int = parameters_of_single_type.size()
|
||||
|
||||
if param_count == 2 || param_count == 1:
|
||||
var op_for_type = null
|
||||
if _operation_funcs.has(val_type):
|
||||
op_for_type = _operation_funcs[val_type]
|
||||
else:
|
||||
var type_name = InkUtils.value_type_name(val_type)
|
||||
InkUtils.throw_story_exception(
|
||||
"Cannot perform operation '%s' on value of type (%d)" \
|
||||
% [self.name, type_name],
|
||||
false,
|
||||
metadata
|
||||
)
|
||||
return null
|
||||
|
||||
if param_count == 2:
|
||||
var param2 = parameters_of_single_type[1]
|
||||
|
||||
var result_val = self._static_native_function_call.call(op_for_type, param1.value, param2.value)
|
||||
|
||||
return InkValue.create(result_val)
|
||||
else:
|
||||
var result_val = self._static_native_function_call.call(op_for_type, param1.value)
|
||||
|
||||
return InkValue.create(result_val)
|
||||
else:
|
||||
InkUtils.throw_exception(
|
||||
"Unexpected number of parameters to NativeFunctionCall: %d" % \
|
||||
parameters_of_single_type.size()
|
||||
)
|
||||
return null
|
||||
|
||||
|
||||
# (Array<InkObject>) -> Value
|
||||
#
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func call_binary_list_operation(parameters: Array, metadata: StoryErrorMetadata) -> InkValue:
|
||||
if ((self.name == "+" || self.name == "-") &&
|
||||
InkUtils.is_ink_class(parameters[0], "ListValue") &&
|
||||
InkUtils.is_ink_class(parameters [1], "IntValue")
|
||||
):
|
||||
return call_list_increment_operation(parameters)
|
||||
|
||||
var v1 = InkUtils.as_or_null(parameters[0], "Value")
|
||||
var v2 = InkUtils.as_or_null(parameters[1], "Value")
|
||||
|
||||
if ((self.name == "&&" || self.name == "||") &&
|
||||
(v1.value_type != ValueType.LIST || v2.value_type != ValueType.LIST)
|
||||
):
|
||||
var op: String = _operation_funcs[ValueType.INT]
|
||||
var result = bool(self._static_native_function_call.call(
|
||||
"op_for_type",
|
||||
1 if v1.is_truthy else 0,
|
||||
1 if v2.is_truthy else 0
|
||||
))
|
||||
|
||||
return InkBoolValue.new_with(result)
|
||||
|
||||
if v1.value_type == ValueType.LIST && v2.value_type == ValueType.LIST:
|
||||
return call_coerced([v1, v2], metadata)
|
||||
|
||||
var v1_type_name = InkUtils.value_type_name(v1.value_type)
|
||||
var v2_type_name = InkUtils.value_type_name(v2.value_type)
|
||||
InkUtils.throw_story_exception(
|
||||
"Can not call use '%s' operation on %s and %s" % \
|
||||
[self.name, v1_type_name, v2_type_name],
|
||||
false,
|
||||
metadata
|
||||
)
|
||||
|
||||
return null
|
||||
|
||||
|
||||
# (Array<InkObject>) -> Value
|
||||
func call_list_increment_operation(list_int_params: Array) -> InkValue:
|
||||
var list_val: InkListValue = InkUtils.cast(list_int_params[0], "ListValue")
|
||||
var int_val: InkIntValue = InkUtils.cast(list_int_params [1], "IntValue")
|
||||
|
||||
var result_raw_list = InkList.new()
|
||||
|
||||
for list_item in list_val.value.keys(): # TODO: Optimize?
|
||||
var list_item_value = list_val.value.get_item(list_item)
|
||||
|
||||
var int_op: String = _operation_funcs[ValueType.INT]
|
||||
|
||||
var target_int = int(
|
||||
self._static_native_function_call.call(
|
||||
int_op,
|
||||
list_item_value,
|
||||
int_val.value
|
||||
)
|
||||
)
|
||||
|
||||
var item_origin: InkListDefinition = null
|
||||
for origin in list_val.value.origins:
|
||||
if origin.name == list_item.origin_name:
|
||||
item_origin = origin
|
||||
break
|
||||
|
||||
if item_origin != null:
|
||||
var incremented_item: InkTryGetResult = item_origin.try_get_item_with_value(target_int)
|
||||
if incremented_item.exists:
|
||||
result_raw_list.set_item(incremented_item.result, target_int)
|
||||
|
||||
return InkListValue.new_with(result_raw_list)
|
||||
|
||||
|
||||
# (Array<InkObject>) -> Array<Value>?
|
||||
#
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadata are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func coerce_values_to_single_type(parameters_in: Array, metadata: StoryErrorMetadata):
|
||||
var val_type: int = ValueType.INT
|
||||
|
||||
var special_case_list: InkListValue = null
|
||||
|
||||
for obj in parameters_in:
|
||||
var val: InkValue = obj
|
||||
if val.value_type > val_type:
|
||||
val_type = val.value_type
|
||||
|
||||
if val.value_type == ValueType.LIST:
|
||||
special_case_list = InkUtils.as_or_null(val, "ListValue")
|
||||
|
||||
var parameters_out: Array = [] # Array<Value>
|
||||
|
||||
if val_type == ValueType.LIST:
|
||||
for val in parameters_in:
|
||||
if val.value_type == ValueType.LIST:
|
||||
parameters_out.append(val)
|
||||
elif val.value_type == ValueType.INT:
|
||||
var int_val = int(val.value_object)
|
||||
var list = special_case_list.value.origin_of_max_item
|
||||
|
||||
var item: InkTryGetResult = list.try_get_item_with_value(int_val)
|
||||
if item.exists:
|
||||
var casted_value = InkListValue.new_with_single_item(item.result, int_val)
|
||||
parameters_out.append(casted_value)
|
||||
else:
|
||||
InkUtils.throw_story_exception(
|
||||
"Could not find List item with the value %d in %s" \
|
||||
% [int_val, list.name],
|
||||
false,
|
||||
metadata
|
||||
)
|
||||
|
||||
return null
|
||||
else:
|
||||
var type_name = InkUtils.value_type_name(val.value_type)
|
||||
InkUtils.throw_story_exception(
|
||||
"Cannot mix Lists and %s values in this operation" % type_name,
|
||||
false,
|
||||
metadata
|
||||
)
|
||||
|
||||
return null
|
||||
|
||||
else:
|
||||
for val in parameters_in:
|
||||
var casted_value = val.cast(val_type)
|
||||
parameters_out.append(casted_value)
|
||||
|
||||
return parameters_out
|
||||
|
||||
|
||||
func _init(static_native_function_call: InkStaticNativeFunctionCall = null):
|
||||
generate_native_functions_if_necessary(static_native_function_call)
|
||||
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init_with_name(name: String):
|
||||
self.name = name
|
||||
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init_with_name_and_number_of_parameters(name: String, number_of_parameters: int):
|
||||
_is_prototype = true
|
||||
self.name = name
|
||||
self.number_of_parameters = number_of_parameters
|
||||
|
||||
|
||||
func generate_native_functions_if_necessary(static_native_function_call: InkStaticNativeFunctionCall) -> void:
|
||||
find_static_objects(static_native_function_call)
|
||||
self._static_native_function_call.generate_native_functions_if_necessary()
|
||||
|
||||
|
||||
func add_op_func_for_type(val_type: int, op: String) -> void:
|
||||
if _operation_funcs == null:
|
||||
_operation_funcs = {}
|
||||
|
||||
_operation_funcs[val_type] = op
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Native '%s'" % self.name
|
||||
|
||||
|
||||
var _prototype: InkNativeFunctionCall = null
|
||||
|
||||
var _is_prototype: bool = false
|
||||
|
||||
# Dictionary<ValueType, String>
|
||||
var _operation_funcs: Dictionary = {}
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "NativeFunctionCall" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class():
|
||||
return "NativeFunctionCall"
|
||||
|
||||
|
||||
var _static_native_function_call: InkStaticNativeFunctionCall:
|
||||
get: return _weak_static_native_function_call.get_ref()
|
||||
|
||||
var _weak_static_native_function_call = WeakRef.new()
|
||||
|
||||
|
||||
func find_static_objects(static_native_function_call: InkStaticNativeFunctionCall = null):
|
||||
if _static_native_function_call == null:
|
||||
if static_native_function_call:
|
||||
_weak_static_native_function_call = weakref(static_native_function_call)
|
||||
else:
|
||||
var ink_runtime = Engine.get_main_loop().root.get_node("__InkRuntime")
|
||||
_weak_static_native_function_call = weakref(ink_runtime.native_function_call)
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
static func new_with_name(
|
||||
name: String,
|
||||
static_native_function_call: InkStaticNativeFunctionCall = null
|
||||
):
|
||||
var native_function_call = InkNativeFunctionCall.new(static_native_function_call)
|
||||
native_function_call._init_with_name(name)
|
||||
return native_function_call
|
||||
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
static func new_with_name_and_number_of_parameters(
|
||||
name: String,
|
||||
number_of_parameters: int,
|
||||
static_native_function_call: InkStaticNativeFunctionCall = null
|
||||
):
|
||||
var native_function_call = InkNativeFunctionCall.new(static_native_function_call)
|
||||
native_function_call._init_with_name_and_number_of_parameters(name, number_of_parameters)
|
||||
return native_function_call
|
36
addons/inkgd/runtime/content/ink_tag.gd
Normal file
36
addons/inkgd/runtime/content/ink_tag.gd
Normal file
|
@ -0,0 +1,36 @@
|
|||
# ############################################################################ #
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkTag
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var text: String
|
||||
|
||||
|
||||
func _init(tag_text: String):
|
||||
text = tag_text
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
return '# ' + text
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "Tag" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "Tag"
|
60
addons/inkgd/runtime/content/ink_variable_assignment.gd
Normal file
60
addons/inkgd/runtime/content/ink_variable_assignment.gd
Normal file
|
@ -0,0 +1,60 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkVariableAssignment
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var variable_name = null # String?
|
||||
|
||||
var is_new_declaration: bool = false
|
||||
|
||||
var is_global: bool = false
|
||||
|
||||
|
||||
func _init():
|
||||
_init_with(null, false)
|
||||
|
||||
|
||||
# (String?, bool) -> InkVariableAssignment
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init_with(variable_name, is_new_declaration: bool):
|
||||
self.variable_name = variable_name
|
||||
self.is_new_declaration = is_new_declaration
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
return "VarAssign to %s" % variable_name
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "VariableAssignment" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "VariableAssignment"
|
||||
|
||||
|
||||
# (String?, bool) -> InkVariableAssignment
|
||||
@warning_ignore("shadowed_variable")
|
||||
static func new_with(
|
||||
variable_name: String,
|
||||
is_new_declaration: bool
|
||||
) -> InkVariableAssignment:
|
||||
var variable_assignment = InkVariableAssignment.new()
|
||||
variable_assignment._init_with(variable_name, is_new_declaration)
|
||||
return variable_assignment
|
69
addons/inkgd/runtime/content/ink_variable_reference.gd
Normal file
69
addons/inkgd/runtime/content/ink_variable_reference.gd
Normal file
|
@ -0,0 +1,69 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkVariableReference
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var name = null # String?
|
||||
|
||||
# InkPath
|
||||
var path_for_count: InkPath = null
|
||||
|
||||
# Container?
|
||||
var container_for_count: InkContainer:
|
||||
get: return self.resolve_path(path_for_count).container
|
||||
|
||||
# String?
|
||||
var path_string_for_count:
|
||||
get:
|
||||
if path_for_count == null:
|
||||
return null
|
||||
|
||||
return compact_path_string(path_for_count)
|
||||
|
||||
set(value):
|
||||
if value == null:
|
||||
path_for_count = null
|
||||
else:
|
||||
path_for_count = InkPath.new_with_components_string(value)
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init(name = null):
|
||||
if name:
|
||||
self.name = name
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _to_string() -> String:
|
||||
if name != null:
|
||||
return "var(%s)" % name
|
||||
else:
|
||||
var path_str = self.path_string_for_count
|
||||
return "read_count(%s)" % path_str
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "VariableReference" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "VariableReference"
|
27
addons/inkgd/runtime/content/ink_void.gd
Normal file
27
addons/inkgd/runtime/content/ink_void.gd
Normal file
|
@ -0,0 +1,27 @@
|
|||
# ############################################################################ #
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkVoid
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "Void" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "Void"
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Void"
|
76
addons/inkgd/runtime/debug_metadata.gd
Normal file
76
addons/inkgd/runtime/debug_metadata.gd
Normal file
|
@ -0,0 +1,76 @@
|
|||
# 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 InkBase
|
||||
|
||||
class_name InkDebugMetadata
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var start_line_number: int = 0
|
||||
var end_line_number: int = 0
|
||||
var start_character_number: int = 0
|
||||
var end_character_number: int = 0
|
||||
# String?
|
||||
var file_name = null
|
||||
# String?
|
||||
var source_name = null
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func merge(dm: InkDebugMetadata) -> InkDebugMetadata:
|
||||
var new_debug_metadata = DebugMetadata().new()
|
||||
|
||||
new_debug_metadata.file_name = self.file_name
|
||||
new_debug_metadata.source_name = self.source_name
|
||||
|
||||
if self.start_line_number < dm.start_line_number:
|
||||
new_debug_metadata.start_line_number = self.start_line_number
|
||||
new_debug_metadata.start_character_number = self.start_character_number
|
||||
elif self.start_line_number > dm.start_line_number:
|
||||
new_debug_metadata.start_line_number = dm.start_line_number
|
||||
new_debug_metadata.start_character_number = dm.start_character_number
|
||||
else:
|
||||
var min_scn = min(self.start_character_number, dm.start_character_number)
|
||||
new_debug_metadata.start_line_number = self.start_line_number
|
||||
new_debug_metadata.start_character_number = min_scn
|
||||
|
||||
if self.end_line_number > dm.end_line_number:
|
||||
new_debug_metadata.end_line_number = self.end_line_number
|
||||
new_debug_metadata.end_character_number = self.end_character_number
|
||||
elif self.end_line_number < dm.end_line_number:
|
||||
new_debug_metadata.end_line_number = dm.end_line_number
|
||||
new_debug_metadata.end_character_number = dm.end_character_number
|
||||
else:
|
||||
var max_scn = min(self.end_character_number, dm.end_character_number)
|
||||
new_debug_metadata.end_line_number = self.end_line_number
|
||||
new_debug_metadata.end_character_number = max_scn
|
||||
|
||||
return new_debug_metadata
|
||||
|
||||
# () -> String
|
||||
func _to_string() -> String:
|
||||
if file_name != null:
|
||||
return str("line ", start_line_number, " of ", file_name)
|
||||
else:
|
||||
return str("line ", start_line_number)
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "DebugMetadata" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "DebugMetadata"
|
||||
|
||||
static func DebugMetadata():
|
||||
return load("res://addons/inkgd/runtime/debug_metadata.gd")
|
24
addons/inkgd/runtime/extra/function_result.gd
Normal file
24
addons/inkgd/runtime/extra/function_result.gd
Normal file
|
@ -0,0 +1,24 @@
|
|||
# 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 RefCounted
|
||||
|
||||
class_name InkFunctionResult
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var text_output: String = ""
|
||||
var return_value = null
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _init(text_output: String, return_value):
|
||||
self.text_output = text_output
|
||||
self.return_value = return_value
|
39
addons/inkgd/runtime/extra/ink_key_value_pair.gd
Normal file
39
addons/inkgd/runtime/extra/ink_key_value_pair.gd
Normal file
|
@ -0,0 +1,39 @@
|
|||
# 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 RefCounted
|
||||
|
||||
class_name InkKeyValuePair
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var key = null
|
||||
var value = null
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# TODO: Use _init instead of _init_with_key_value.
|
||||
func _init():
|
||||
pass
|
||||
|
||||
func _init_with_key_value(key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
func _to_string():
|
||||
return ("[KeyValuePair (%s, %s)]" % [key, value])
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
static func new_with_key_value(key, value) -> InkKeyValuePair:
|
||||
var key_value_pair = InkKeyValuePair.new()
|
||||
key_value_pair._init_with_key_value(key, value)
|
||||
|
||||
return key_value_pair
|
50
addons/inkgd/runtime/extra/state_element.gd
Normal file
50
addons/inkgd/runtime/extra/state_element.gd
Normal file
|
@ -0,0 +1,50 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# ############################################################################ #
|
||||
# !! VALUE TYPE
|
||||
# ############################################################################ #
|
||||
|
||||
# This element is only used during JSON parsing and is never duplicated / passed
|
||||
# around so it doesn't need to be either immutable or have a 'duplicate' method.
|
||||
|
||||
class_name InkStateElement
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
enum State {
|
||||
NONE,
|
||||
OBJECT,
|
||||
ARRAY,
|
||||
PROPERTY,
|
||||
PROPERTY_NAME,
|
||||
STRING,
|
||||
}
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var type: int = State.NONE # State
|
||||
var child_count: int = 0
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _init(type: int):
|
||||
self.type = type
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type) -> bool:
|
||||
return type == "StateElement"
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "StateElement"
|
34
addons/inkgd/runtime/extra/stopwatch.gd
Normal file
34
addons/inkgd/runtime/extra/stopwatch.gd
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# Simple replacement of the Stopwatch class from the .NET Framework.
|
||||
# Less accurate than the original implemntation, but good enough for
|
||||
# the use-case.
|
||||
|
||||
class_name InkStopWatch
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var _start_time: int = -1
|
||||
|
||||
var elapsed_milliseconds : get = get_elapsed_milliseconds
|
||||
func get_elapsed_milliseconds() -> int:
|
||||
if _start_time == -1:
|
||||
return 0
|
||||
|
||||
return Time.get_ticks_msec() - _start_time
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func start() -> void:
|
||||
_start_time = Time.get_ticks_msec()
|
||||
|
||||
func stop() -> void:
|
||||
_start_time = -1
|
31
addons/inkgd/runtime/extra/story_error.gd
Normal file
31
addons/inkgd/runtime/extra/story_error.gd
Normal file
|
@ -0,0 +1,31 @@
|
|||
# ############################################################################ #
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# An object tha represents a "Story Error", which is equivalent in certain
|
||||
# context to upstream's StoryException.
|
||||
|
||||
class_name StoryError
|
||||
|
||||
# ############################################################################ #
|
||||
# Properties
|
||||
# ############################################################################ #
|
||||
|
||||
var message: String
|
||||
var use_end_line_number: bool
|
||||
var metadata # StoryErrorMetadata | null
|
||||
|
||||
# ############################################################################ #
|
||||
# Initialization
|
||||
# ############################################################################ #
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init(message: String, use_end_line_number: bool, metadata):
|
||||
self.message = message
|
||||
self.use_end_line_number = use_end_line_number
|
||||
self.metadata = metadata
|
30
addons/inkgd/runtime/extra/story_error_metadata.gd
Normal file
30
addons/inkgd/runtime/extra/story_error_metadata.gd
Normal file
|
@ -0,0 +1,30 @@
|
|||
# ############################################################################ #
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# An object that keeps track of the Debug Metadata and current pointer at the
|
||||
# exact moment an error was raised, so that they can be processed and reported
|
||||
# later. It's required because GDScript doesn't support exceptions and
|
||||
# errors don't bubble up the stack.
|
||||
|
||||
class_name StoryErrorMetadata
|
||||
|
||||
# ############################################################################ #
|
||||
# Properties
|
||||
# ############################################################################ #
|
||||
|
||||
var debug_metadata # InkDebugMetadata | null
|
||||
var pointer: InkPointer
|
||||
|
||||
# ############################################################################ #
|
||||
# Initialization
|
||||
# ############################################################################ #
|
||||
|
||||
func _init(debug_metadata: InkDebugMetadata, pointer: InkPointer):
|
||||
self.debug_metadata = debug_metadata
|
||||
self.pointer = pointer
|
63
addons/inkgd/runtime/extra/string_set.gd
Normal file
63
addons/inkgd/runtime/extra/string_set.gd
Normal file
|
@ -0,0 +1,63 @@
|
|||
# ############################################################################ #
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# Using an dictionary as the backing structure for a not-too-bad, super-simple
|
||||
# set. The Ink runtime doesn't use C#'s HashSet full potential, so this trick
|
||||
# should be good enough for the use-case.
|
||||
|
||||
# This simple set is designed to hold Strings only.
|
||||
|
||||
extends RefCounted
|
||||
|
||||
class_name InkStringSet
|
||||
|
||||
# ############################################################################ #
|
||||
# Self-reference
|
||||
# ############################################################################ #
|
||||
|
||||
static func InkStringSet() -> GDScript:
|
||||
return load("res://addons/inkgd/runtime/extra/string_set.gd") as GDScript
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var _dictionary: Dictionary = {}
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func clear() -> void:
|
||||
_dictionary.clear()
|
||||
|
||||
func duplicate() -> InkStringSet:
|
||||
var set = InkStringSet().new()
|
||||
set._dictionary = _dictionary.duplicate()
|
||||
return set
|
||||
|
||||
func enumerate() -> Array:
|
||||
return _dictionary.keys()
|
||||
|
||||
func is_empty() -> bool:
|
||||
return _dictionary.is_empty()
|
||||
|
||||
func contains(element: String) -> bool:
|
||||
return _dictionary.has(element)
|
||||
|
||||
func contains_all(elements: Array) -> bool:
|
||||
return _dictionary.has_all(elements)
|
||||
|
||||
func size() -> int:
|
||||
return _dictionary.size()
|
||||
|
||||
func to_array() -> Array:
|
||||
return _dictionary.keys()
|
||||
|
||||
func append(value: String) -> void:
|
||||
_dictionary[value] = null
|
||||
|
||||
func erase(value: String) -> bool:
|
||||
return _dictionary.erase(value)
|
33
addons/inkgd/runtime/extra/string_writer.gd
Normal file
33
addons/inkgd/runtime/extra/string_writer.gd
Normal file
|
@ -0,0 +1,33 @@
|
|||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# Simple replacement of the StringWriter class from the .NET Framework.
|
||||
# It has none of the optimisations of original class and merely wraps
|
||||
# a plain old string.
|
||||
|
||||
|
||||
class_name InkStringWriter
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var _internal_string: String = ""
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func write(s: String) -> void:
|
||||
_internal_string += str(s)
|
||||
|
||||
func _to_string() -> String:
|
||||
return _internal_string
|
25
addons/inkgd/runtime/extra/try_get_result.gd
Normal file
25
addons/inkgd/runtime/extra/try_get_result.gd
Normal file
|
@ -0,0 +1,25 @@
|
|||
# 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 RefCounted
|
||||
|
||||
class_name InkTryGetResult
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var exists: bool = false # Bool
|
||||
var result = null # Variant
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _init(exists: bool, result):
|
||||
self.exists = exists
|
||||
self.result = result
|
272
addons/inkgd/runtime/extra/utils.gd
Normal file
272
addons/inkgd/runtime/extra/utils.gd
Normal file
|
@ -0,0 +1,272 @@
|
|||
# ############################################################################ #
|
||||
# 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 InkUtils
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
const ValueType = preload("res://addons/inkgd/runtime/values/value_type.gd").ValueType
|
||||
|
||||
# ############################################################################ #
|
||||
# Exceptions
|
||||
# ############################################################################ #
|
||||
|
||||
static func throw_exception(message: String) -> void:
|
||||
InkRuntime().handle_exception(message)
|
||||
|
||||
static func throw_story_exception(
|
||||
message: String,
|
||||
use_end_line_number = false,
|
||||
metadata = null
|
||||
) -> void:
|
||||
InkRuntime().handle_story_exception(message, use_end_line_number, metadata)
|
||||
|
||||
static func throw_argument_exception(message: String) -> void:
|
||||
InkRuntime().handle_argument_exception(message)
|
||||
|
||||
# ############################################################################ #
|
||||
# Assertions
|
||||
# ############################################################################ #
|
||||
|
||||
static func __assert__(condition: bool, message = "") -> void:
|
||||
if !condition && message != "":
|
||||
printerr(message)
|
||||
|
||||
assert(condition)
|
||||
|
||||
# ############################################################################ #
|
||||
# Type Assertion
|
||||
# ############################################################################ #
|
||||
|
||||
static func as_or_null(variant, name_of_class: String):
|
||||
if (
|
||||
is_ink_class(variant, name_of_class) ||
|
||||
(name_of_class == "Dictionary" && variant is Dictionary) ||
|
||||
(name_of_class == "Array" && variant is Array)
|
||||
):
|
||||
return variant
|
||||
else:
|
||||
return null
|
||||
|
||||
static func cast(variant, name_of_class: String):
|
||||
if is_ink_class(variant, name_of_class):
|
||||
return variant
|
||||
else:
|
||||
push_error(
|
||||
"Original implementation threw a RuntimeException here, because of a " +
|
||||
"cast issue. Undefined behaviors should be expected."
|
||||
)
|
||||
|
||||
assert(false)
|
||||
return null
|
||||
|
||||
static func as_INamedContent_or_null(variant):
|
||||
var properties = variant.get_property_list()
|
||||
|
||||
var has_has_valid_name = false
|
||||
var has_name = false
|
||||
|
||||
for property in properties:
|
||||
if property["name"] == "has_valid_name":
|
||||
has_has_valid_name = true
|
||||
|
||||
if has_has_valid_name && has_name:
|
||||
return variant
|
||||
elif property["name"] == "name":
|
||||
has_name = true
|
||||
|
||||
if has_has_valid_name && has_name:
|
||||
return variant
|
||||
|
||||
return null
|
||||
|
||||
static func is_ink_class(object: Variant, name_of_class: String) -> bool:
|
||||
return (object is Object) && object.is_ink_class(name_of_class)
|
||||
|
||||
static func are_of_same_type(object1: Variant, object2: Variant) -> bool:
|
||||
if (object1 is Object) && (object2 is Object):
|
||||
return object1.get_ink_class() == object2.get_ink_class()
|
||||
|
||||
return typeof(object1) == typeof(object2)
|
||||
|
||||
static func value_type_name(value_type: int) -> String:
|
||||
match value_type:
|
||||
ValueType.BOOL: return "Boolean"
|
||||
|
||||
ValueType.INT: return "Int"
|
||||
ValueType.FLOAT: return "Float"
|
||||
ValueType.LIST: return "List"
|
||||
ValueType.STRING: return "String"
|
||||
|
||||
ValueType.DIVERT_TARGET: return "Divert Target"
|
||||
ValueType.VARIABLE_POINTER: return "Variable Pointer"
|
||||
|
||||
_: return "unknown"
|
||||
|
||||
static func typename_of(variant) -> String:
|
||||
match typeof(variant):
|
||||
TYPE_NIL: return "null"
|
||||
TYPE_BOOL: return "bool"
|
||||
TYPE_INT: return "int"
|
||||
TYPE_FLOAT: return "float"
|
||||
TYPE_STRING: return "String"
|
||||
TYPE_VECTOR2: return "Vector2"
|
||||
TYPE_RECT2: return "Rect2"
|
||||
TYPE_VECTOR3: return "Vector3"
|
||||
TYPE_TRANSFORM2D: return "Transform2D"
|
||||
TYPE_PLANE: return "Plane"
|
||||
TYPE_QUATERNION: return "Quaternion"
|
||||
TYPE_AABB: return "AABB"
|
||||
TYPE_BASIS: return "Basis"
|
||||
TYPE_TRANSFORM3D: return "Transform3D"
|
||||
TYPE_COLOR: return "Color"
|
||||
TYPE_NODE_PATH: return "NodePath"
|
||||
TYPE_RID: return "RID"
|
||||
TYPE_OBJECT: return variant.get_ink_class()
|
||||
TYPE_DICTIONARY: return "Dictionary"
|
||||
TYPE_ARRAY: return "Array"
|
||||
TYPE_PACKED_BYTE_ARRAY: return "PackedByteArray"
|
||||
TYPE_PACKED_INT32_ARRAY: return "PackedInt32Array"
|
||||
TYPE_PACKED_FLOAT32_ARRAY: return "PackedFloat32Array"
|
||||
TYPE_PACKED_STRING_ARRAY: return "PackedStringArray"
|
||||
TYPE_PACKED_VECTOR2_ARRAY: return "PackedVector2Array"
|
||||
TYPE_PACKED_VECTOR3_ARRAY: return "PackedVector3Array"
|
||||
TYPE_PACKED_COLOR_ARRAY: return "PackedColorArray"
|
||||
_: return "unknown"
|
||||
|
||||
# ############################################################################ #
|
||||
# String Utils
|
||||
# ############################################################################ #
|
||||
|
||||
static func trim(string_to_trim: String, characters = []) -> String:
|
||||
if characters.is_empty():
|
||||
return string_to_trim.strip_edges()
|
||||
|
||||
var length = string_to_trim.length()
|
||||
var beginning = 0
|
||||
var end = length
|
||||
|
||||
var i = 0
|
||||
while i < string_to_trim.length():
|
||||
var character = string_to_trim[i]
|
||||
if characters.find(character) != -1:
|
||||
beginning += 1
|
||||
else:
|
||||
break
|
||||
|
||||
i += 1
|
||||
|
||||
i = string_to_trim.length() - 1
|
||||
while i >= 0:
|
||||
var character = string_to_trim[i]
|
||||
if characters.find(character) != -1:
|
||||
end -= 1
|
||||
else:
|
||||
break
|
||||
|
||||
i -= 1
|
||||
|
||||
if beginning == 0 && end == length:
|
||||
return string_to_trim
|
||||
|
||||
return string_to_trim.substr(beginning, end - beginning)
|
||||
|
||||
# ############################################################################ #
|
||||
# Array Utils
|
||||
# ############################################################################ #
|
||||
|
||||
static func join(joiner: String, array: Array) -> String:
|
||||
var joined_string = ""
|
||||
|
||||
var i = 0
|
||||
for element in array:
|
||||
var element_string
|
||||
if is_ink_class(element, "InkBase"):
|
||||
element_string = element._to_string()
|
||||
else:
|
||||
element_string = str(element)
|
||||
|
||||
joined_string += element_string
|
||||
|
||||
if i >= 0 && i < array.size() - 1:
|
||||
joined_string += joiner
|
||||
|
||||
i += 1
|
||||
|
||||
return joined_string
|
||||
|
||||
static func get_range(array: Array, index: int, count: int) -> Array:
|
||||
if !(index >= 0 && index < array.size()):
|
||||
printerr("get_range: index (%d) is out of bounds." % index)
|
||||
return array.duplicate()
|
||||
|
||||
if index + count > array.size():
|
||||
printerr("get_range: [index (%d) + count (%d)] is out of bounds." % [index, count])
|
||||
return array.duplicate()
|
||||
|
||||
var new_array = []
|
||||
var i = index
|
||||
var c = 0
|
||||
|
||||
while (c < count):
|
||||
new_array.append(array[i + c])
|
||||
c += 1
|
||||
|
||||
return new_array
|
||||
|
||||
static func remove_range(array: Array, index: int, count: int) -> void:
|
||||
if !(index >= 0 && index < array.size()):
|
||||
printerr("get_range: index (%d) is out of bounds." % index)
|
||||
return
|
||||
|
||||
if index + count > array.size():
|
||||
printerr("get_range: [index (%d) + count (%d)] is out of bounds." % [index, count])
|
||||
return
|
||||
|
||||
var i = index
|
||||
var c = 0
|
||||
|
||||
while (c < count):
|
||||
array.remove_at(i)
|
||||
c += 1
|
||||
|
||||
static func array_equal(a1: Array, a2: Array, use_equals = false) -> bool:
|
||||
if a1.size() != a2.size():
|
||||
return false
|
||||
|
||||
var i = 0
|
||||
while (i < a1.size()):
|
||||
var first_element = a1[i]
|
||||
var second_element = a2[i]
|
||||
|
||||
if use_equals:
|
||||
if !first_element.equals(second_element):
|
||||
return false
|
||||
else:
|
||||
i += 1
|
||||
continue
|
||||
else:
|
||||
if first_element != second_element:
|
||||
return false
|
||||
else:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
i += 1
|
||||
|
||||
return true
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
static func InkRuntime():
|
||||
return Engine.get_main_loop().root.get_node("__InkRuntime")
|
154
addons/inkgd/runtime/flow.gd
Normal file
154
addons/inkgd/runtime/flow.gd
Normal file
|
@ -0,0 +1,154 @@
|
|||
# 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 InkBase
|
||||
|
||||
class_name InkFlow
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
var CallStack = load("res://addons/inkgd/runtime/callstack.gd")
|
||||
|
||||
# ############################################################################ #
|
||||
# Self-reference
|
||||
# ############################################################################ #
|
||||
|
||||
static func Flow():
|
||||
return load("res://addons/inkgd/runtime/flow.gd")
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var name # string
|
||||
var callstack # CallStack
|
||||
var output_stream # Array<InkObject>
|
||||
var current_choices # Array<Choice>
|
||||
|
||||
func _init(static_json = null):
|
||||
get_static_json(static_json)
|
||||
|
||||
# (String, Story) -> Flow
|
||||
func _init_with_name(name, story):
|
||||
self.name = name
|
||||
self.callstack = CallStack.new(story, self.StaticJSON)
|
||||
self.output_stream = []
|
||||
self.current_choices = []
|
||||
|
||||
# (String, Story, Dictionary<String, Variant>) -> Flow
|
||||
func _init_with_name_and_jobject(name, story, jobject):
|
||||
self.name = name
|
||||
self.callstack = CallStack.new(story, self.StaticJSON)
|
||||
self.callstack.set_json_token(jobject["callstack"], story)
|
||||
self.output_stream = self.StaticJSON.jarray_to_runtime_obj_list(jobject["outputStream"])
|
||||
self.current_choices = self.StaticJSON.jarray_to_runtime_obj_list(jobject["currentChoices"])
|
||||
|
||||
# jchoice_threads_obj is null if 'choiceThreads' doesn't exist.
|
||||
var jchoice_threads_obj = jobject.get("choiceThreads");
|
||||
self.load_flow_choice_threads(jchoice_threads_obj, story)
|
||||
|
||||
# (SimpleJson.Writer) -> void
|
||||
func write_json(writer):
|
||||
writer.write_object_start()
|
||||
writer.write_property("callstack", Callable(self.callstack, "write_json"))
|
||||
writer.write_property(
|
||||
"outputStream",
|
||||
Callable(self, "_anonymous_write_property_output_stream")
|
||||
)
|
||||
|
||||
var has_choice_threads = false
|
||||
for c in self.current_choices:
|
||||
c.original_thread_index = c.thread_at_generation.thread_index
|
||||
|
||||
if self.callstack.thread_with_index(c.original_thread_index) == null:
|
||||
if !has_choice_threads:
|
||||
has_choice_threads = true
|
||||
writer.write_property_start("choiceThreads")
|
||||
writer.write_object_start()
|
||||
|
||||
writer.write_property_start(c.original_thread_index)
|
||||
c.thread_at_generation.write_json(writer)
|
||||
writer.write_property_end()
|
||||
|
||||
if has_choice_threads:
|
||||
writer.write_object_end()
|
||||
writer.write_property_end()
|
||||
|
||||
writer.write_property(
|
||||
"currentChoices",
|
||||
Callable(self, "_anonymous_write_property_current_choices")
|
||||
)
|
||||
|
||||
writer.write_object_end()
|
||||
|
||||
# (Dictionary, Story) -> void
|
||||
func load_flow_choice_threads(jchoice_threads, story):
|
||||
for choice in self.current_choices:
|
||||
var found_active_thread = self.callstack.thread_with_index(choice.original_thread_index)
|
||||
if found_active_thread != null:
|
||||
choice.thread_at_generation = found_active_thread.copy()
|
||||
else:
|
||||
var jsaved_choice_thread = jchoice_threads[str(choice.original_thread_index)]
|
||||
choice.thread_at_generation = CallStack.InkThread.new_with(jsaved_choice_thread, story)
|
||||
|
||||
# (SimpleJson.Writer) -> void
|
||||
func _anonymous_write_property_output_stream(w):
|
||||
self.StaticJSON.write_list_runtime_objs(w, self.output_stream)
|
||||
|
||||
# (SimpleJson.Writer) -> void
|
||||
func _anonymous_write_property_current_choices(w):
|
||||
w.write_array_start()
|
||||
for c in self.current_choices:
|
||||
self.StaticJSON.write_choice(w, c)
|
||||
w.write_array_end()
|
||||
|
||||
func equals(ink_base) -> bool:
|
||||
return false
|
||||
|
||||
func _to_string() -> String:
|
||||
return str(self)
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "Flow" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "Flow"
|
||||
|
||||
static func new_with_name(name, story, static_json = null):
|
||||
var flow = Flow().new(static_json)
|
||||
flow._init_with_name(name, story)
|
||||
return flow
|
||||
|
||||
static func new_with_name_and_jobject(name, story, jobject, static_json = null):
|
||||
var flow = Flow().new(static_json)
|
||||
flow._init_with_name_and_jobject(name, story, jobject)
|
||||
return flow
|
||||
|
||||
# ############################################################################ #
|
||||
var StaticJSON: InkStaticJSON:
|
||||
get: return _static_json.get_ref()
|
||||
|
||||
var _static_json = WeakRef.new()
|
||||
|
||||
func get_static_json(static_json = null):
|
||||
if static_json != null:
|
||||
_static_json = weakref(static_json)
|
||||
return
|
||||
|
||||
var InkRuntime = Engine.get_main_loop().root.get_node("__InkRuntime")
|
||||
|
||||
InkUtils.__assert__(InkRuntime != null,
|
||||
str("[InkFlow] Could not retrieve 'InkRuntime' singleton from the scene tree."))
|
||||
|
||||
_static_json = weakref(InkRuntime.json)
|
294
addons/inkgd/runtime/ink_path.gd
Normal file
294
addons/inkgd/runtime/ink_path.gd
Normal file
|
@ -0,0 +1,294 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkBase
|
||||
|
||||
class_name InkPath
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
const parent_id = "^"
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
class Component extends InkBase:
|
||||
var index: int = 0
|
||||
|
||||
|
||||
var name = null # String?
|
||||
|
||||
|
||||
var is_index: bool:
|
||||
get: return index >= 0
|
||||
|
||||
|
||||
var is_parent: bool:
|
||||
get: return name == parent_id
|
||||
|
||||
|
||||
# ######################################################################## #
|
||||
|
||||
func _init(index_or_name):
|
||||
if index_or_name is int:
|
||||
var index = index_or_name
|
||||
assert(index >= 0)
|
||||
self.index = index
|
||||
self.name = null
|
||||
elif index_or_name is String:
|
||||
var name = index_or_name
|
||||
assert(name != null && name.length() > 0)
|
||||
self.name = name
|
||||
self.index = -1
|
||||
|
||||
|
||||
# () -> Component
|
||||
static func to_parent() -> Component:
|
||||
return Component.new(parent_id)
|
||||
|
||||
|
||||
# () -> String
|
||||
func _to_string() -> String:
|
||||
if self.is_index:
|
||||
return str(index)
|
||||
else:
|
||||
return name
|
||||
|
||||
|
||||
# (Component) -> bool
|
||||
func equals(other_comp) -> bool:
|
||||
# Simple test to make sure the object is of the right type.
|
||||
if !(other_comp is Object && other_comp.is_ink_class("InkPath.Component")): return false
|
||||
|
||||
if other_comp.is_index == self.is_index:
|
||||
if self.is_index:
|
||||
return index == other_comp.index
|
||||
else:
|
||||
return name == other_comp.name
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "InkPath.Component" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class():
|
||||
return "InkPath.Component"
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func get_component(index: int) -> InkPath.Component:
|
||||
return self._components[index]
|
||||
|
||||
|
||||
var is_relative: bool = false
|
||||
|
||||
|
||||
var head: InkPath.Component:
|
||||
get:
|
||||
if _components.size() > 0:
|
||||
return _components.front()
|
||||
else:
|
||||
return null
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
var tail: InkPath:
|
||||
get:
|
||||
if _components.size() >= 2:
|
||||
var tail_comps = _components.duplicate()
|
||||
tail_comps.pop_front()
|
||||
|
||||
return InkPath().new_with_components(tail_comps)
|
||||
else:
|
||||
return InkPath().__self()
|
||||
|
||||
|
||||
var length: int:
|
||||
get: return _components.size()
|
||||
|
||||
|
||||
var last_component: InkPath.Component:
|
||||
get:
|
||||
if _components.size() > 0:
|
||||
return _components.back()
|
||||
else:
|
||||
return null
|
||||
|
||||
|
||||
var contains_named_component: bool:
|
||||
get:
|
||||
for comp in _components:
|
||||
if !comp.is_index:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _init():
|
||||
self._components = []
|
||||
|
||||
|
||||
func _init_with_head_tail(head, tail):
|
||||
self._components = []
|
||||
self._components.append(head)
|
||||
self._components = self._components + self.tail._components
|
||||
|
||||
|
||||
func _init_with_components(components, relative = false):
|
||||
self._components = []
|
||||
self._components = self._components + components
|
||||
self.is_relative = relative
|
||||
|
||||
|
||||
func _init_with_components_string(components_string):
|
||||
self._components = []
|
||||
self.components_string = components_string
|
||||
|
||||
|
||||
# () -> InkPath
|
||||
static func __self() -> InkPath:
|
||||
var path = InkPath().new()
|
||||
path.is_relative = true
|
||||
return path
|
||||
|
||||
|
||||
# (InkPath) -> InkPath
|
||||
func path_by_appending_path(path_to_append):
|
||||
var p = InkPath().new()
|
||||
|
||||
var upward_moves = 0
|
||||
|
||||
var i = 0
|
||||
while(i < path_to_append._components.size()):
|
||||
if path_to_append._components[i].is_parent:
|
||||
upward_moves += 1
|
||||
else:
|
||||
break
|
||||
i += 1
|
||||
|
||||
i = 0
|
||||
while(i < self._components.size() - upward_moves):
|
||||
p._components.append(self._components[i])
|
||||
i += 1
|
||||
|
||||
i = upward_moves
|
||||
while(i < path_to_append._components.size()):
|
||||
p._components.append(path_to_append._components[i])
|
||||
i += 1
|
||||
|
||||
return p
|
||||
|
||||
|
||||
# (Component) -> InkPath
|
||||
func path_by_appending_component(c):
|
||||
var p = InkPath().new()
|
||||
p._components = p._components + self._components
|
||||
p._components.append(c)
|
||||
return p
|
||||
|
||||
|
||||
var components_string: String:
|
||||
get:
|
||||
if _components_string == null:
|
||||
_components_string = InkUtils.join(".", _components)
|
||||
if self.is_relative:
|
||||
_components_string = "." + _components_string
|
||||
|
||||
return _components_string
|
||||
|
||||
|
||||
set(value):
|
||||
_components.clear()
|
||||
_components_string = value
|
||||
|
||||
if (_components_string == null || _components_string.length() == 0):
|
||||
return
|
||||
|
||||
if _components_string[0] == '.':
|
||||
self.is_relative = true
|
||||
_components_string = _components_string.substr(1, _components_string.length() - 1)
|
||||
else:
|
||||
self.is_relative = false
|
||||
|
||||
var components_strings = _components_string.split(".")
|
||||
for _str in components_strings:
|
||||
if _str.is_valid_int():
|
||||
_components.append(Component.new(int(_str)))
|
||||
else:
|
||||
_components.append(Component.new(_str))
|
||||
|
||||
|
||||
var _components_string # String
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
return self.components_string
|
||||
|
||||
|
||||
# (Component) -> bool
|
||||
func equals(other_path):
|
||||
# Simple test to make sure the object is of the right type.
|
||||
if !(other_path is Object && other_path.is_ink_class("InkPath")): return false
|
||||
|
||||
if other_path._components.size() != self._components.size():
|
||||
return false
|
||||
|
||||
if other_path.is_relative != self.is_relative:
|
||||
return false
|
||||
|
||||
return InkUtils.array_equal(other_path._components, self._components, true)
|
||||
|
||||
|
||||
var _components = null # Array<Component>
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
static func new_with_head_tail(head, tail):
|
||||
var path = InkPath().new()
|
||||
path._init_with_head_tail(head, tail)
|
||||
return path
|
||||
|
||||
|
||||
static func new_with_components(components, relative = false):
|
||||
var path = InkPath().new()
|
||||
path._init_with_components(components, relative)
|
||||
return path
|
||||
|
||||
|
||||
static func new_with_components_string(components_string):
|
||||
var path = InkPath().new()
|
||||
path._init_with_components_string(components_string)
|
||||
return path
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "InkPath" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class():
|
||||
return "InkPath"
|
||||
|
||||
|
||||
static func InkPath():
|
||||
return load("res://addons/inkgd/runtime/ink_path.gd")
|
522
addons/inkgd/runtime/lists/ink_list.gd
Normal file
522
addons/inkgd/runtime/lists/ink_list.gd
Normal file
|
@ -0,0 +1,522 @@
|
|||
# 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 InkObject
|
||||
|
||||
class_name InkList
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (Dictionary<InkItem, int>, Array<String>, Array<InkListDefinition>)
|
||||
func _init_from_csharp(items: Dictionary, origin_names: Array, origins: Array):
|
||||
_dictionary = items
|
||||
_origin_names = origin_names
|
||||
self.origins = origins
|
||||
|
||||
|
||||
# (InkList) -> InkList
|
||||
func _init_with_ink_list(other_list: InkList):
|
||||
_dictionary = other_list._dictionary.duplicate()
|
||||
var other_origin_names = other_list.origin_names
|
||||
if other_origin_names != null:
|
||||
_origin_names = other_list.origin_names.duplicate()
|
||||
|
||||
if other_list.origins != null:
|
||||
self.origins = other_list.origins.duplicate()
|
||||
|
||||
|
||||
# (string, Story) -> InkList
|
||||
func _init_with_origin(single_origin_list_name: String, origin_story: InkStory):
|
||||
set_initial_origin_name(single_origin_list_name)
|
||||
|
||||
var def: InkTryGetResult = origin_story.list_definitions.try_list_get_definition(single_origin_list_name)
|
||||
if def.exists:
|
||||
origins = [def.result]
|
||||
else:
|
||||
InkUtils.throw_exception(
|
||||
"InkList origin could not be found in story when constructing new list: %s" \
|
||||
% single_origin_list_name
|
||||
)
|
||||
|
||||
|
||||
# (InkListItem, int) -> InkList
|
||||
func _init_with_single_item(single_item: InkListItem, single_value: int):
|
||||
set_item(single_item, single_value)
|
||||
|
||||
|
||||
# (string, Story) -> InkList
|
||||
static func from_string(my_list_item: String, origin_story: InkStory) -> InkList:
|
||||
var list_value: InkListValue = origin_story.list_definitions.find_single_item_list_with_name(my_list_item)
|
||||
if list_value:
|
||||
return InkList.new_with_ink_list(list_value.value)
|
||||
else:
|
||||
InkUtils.throw_exception(
|
||||
"Could not find the InkListItem from the string '%s' to create an InkList because " +
|
||||
"it doesn't exist in the original list definition in ink." % my_list_item
|
||||
)
|
||||
return null
|
||||
|
||||
|
||||
func add_item(item: InkListItem) -> void:
|
||||
if item.origin_name == null:
|
||||
add_item(item.item_name)
|
||||
return
|
||||
|
||||
for origin in self.origins:
|
||||
if origin.name == item.origin_name:
|
||||
var int_val: InkTryGetResult = origin.try_get_value_for_item(item)
|
||||
if int_val.exists:
|
||||
set_item(item, int_val.result)
|
||||
return
|
||||
else:
|
||||
InkUtils.throw_exception(
|
||||
"Could not add the item '%s' to this list because it doesn't exist in the " +
|
||||
"original list definition in ink." % item._to_string()
|
||||
)
|
||||
return
|
||||
|
||||
InkUtils.throw_exception(
|
||||
"Failed to add item to list because the item was from a new list definition that " +
|
||||
"wasn't previously known to this list. Only items from previously known lists can " +
|
||||
"be used, so that the int value can be found."
|
||||
)
|
||||
|
||||
|
||||
func add_item_by_string(item_name: String) -> void:
|
||||
var found_list_def: InkListDefinition = null
|
||||
|
||||
for origin in self.origins:
|
||||
if origin.contains_item_with_name(item_name):
|
||||
if found_list_def != null:
|
||||
InkUtils.throw_exception(
|
||||
"Could not add the item " + item_name + " to this list because it could " +
|
||||
"come from either " + origin.name + " or " + found_list_def.name
|
||||
)
|
||||
return
|
||||
else:
|
||||
found_list_def = origin
|
||||
|
||||
if found_list_def == null:
|
||||
InkUtils.throw_exception(
|
||||
"Could not add the item " + item_name + " to this list because it isn't known " +
|
||||
"to any list definitions previously associated with this list."
|
||||
)
|
||||
return
|
||||
|
||||
var item = InkListItem.new_with_origin_name(found_list_def.name, item_name)
|
||||
var item_val: int = found_list_def.value_for_item(item)
|
||||
set_item(item, item_val)
|
||||
|
||||
|
||||
func contains_item_named(item_name: String) -> bool:
|
||||
for item_key in keys():
|
||||
if item_key.item_name == item_name:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# Array<ListDefinition>
|
||||
var origins = null
|
||||
var origin_of_max_item: InkListDefinition: get = get_origin_of_max_item
|
||||
func get_origin_of_max_item() -> InkListDefinition:
|
||||
if self.origins == null:
|
||||
return null
|
||||
|
||||
var max_origin_name = self.max_item.key.origin_name
|
||||
for origin in self.origins:
|
||||
if origin.name == max_origin_name:
|
||||
return origin
|
||||
|
||||
return null
|
||||
|
||||
|
||||
# Array<String>
|
||||
var origin_names : get = get_origin_names
|
||||
func get_origin_names():
|
||||
if self.size() > 0:
|
||||
if _origin_names == null && self.size() > 0:
|
||||
_origin_names = []
|
||||
else:
|
||||
_origin_names.clear()
|
||||
|
||||
for item_key in keys():
|
||||
_origin_names.append(item_key.origin_name)
|
||||
|
||||
return _origin_names
|
||||
|
||||
|
||||
var _origin_names = null # Array<String>
|
||||
func set_initial_origin_name(initial_origin_name: String) -> void:
|
||||
_origin_names = [ initial_origin_name ]
|
||||
|
||||
|
||||
# (Array<String>) -> void
|
||||
func set_initial_origin_names(initial_origin_names) -> void:
|
||||
if initial_origin_names == null:
|
||||
_origin_names = null
|
||||
else:
|
||||
_origin_names = initial_origin_names.duplicate()
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
var max_item: InkKeyValuePair: # InkKeyValuePair<InkListItem, int>
|
||||
get:
|
||||
var _max_item: InkKeyValuePair = InkKeyValuePair.new_with_key_value(InkListItem.null_item, 0)
|
||||
for k in keys():
|
||||
if (_max_item.key.is_null || get_item(k) > _max_item.value):
|
||||
_max_item = InkKeyValuePair.new_with_key_value(k, get_item(k))
|
||||
|
||||
return _max_item
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
var min_item: InkKeyValuePair: # InkKeyValuePair<InkListItem, int>
|
||||
get:
|
||||
var _min_item: InkKeyValuePair = InkKeyValuePair.new_with_key_value(InkListItem.null_item, 0)
|
||||
for k in keys():
|
||||
if (_min_item.key.is_null || get_item(k) < _min_item.value):
|
||||
_min_item = InkKeyValuePair.new_with_key_value(k, get_item(k))
|
||||
|
||||
return _min_item
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
var inverse: InkList: get = get_inverse
|
||||
func get_inverse() -> InkList:
|
||||
var list: InkList = InkList.new()
|
||||
if self.origins != null:
|
||||
for origin in self.origins:
|
||||
for serialized_item_key in origin.items:
|
||||
if !_dictionary.has(serialized_item_key):
|
||||
list._dictionary[serialized_item_key] = origin.items[serialized_item_key]
|
||||
|
||||
return list
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
var all: InkList: get = get_all
|
||||
func get_all() -> InkList:
|
||||
var list: InkList = InkList.new()
|
||||
if self.origins != null:
|
||||
for origin in self.origins:
|
||||
for serialized_item_key in origin.items:
|
||||
list._dictionary[serialized_item_key] = origin.items[serialized_item_key]
|
||||
|
||||
return list
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
func union(other_list: InkList) -> InkList:
|
||||
var union: InkList = InkList.new_with_ink_list(self)
|
||||
for key in other_list._dictionary:
|
||||
union._dictionary[key] = other_list._dictionary[key]
|
||||
return union
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
func intersection(other_list: InkList) -> InkList:
|
||||
var intersection: InkList = InkList.new()
|
||||
for key in other_list._dictionary:
|
||||
if self._dictionary.has(key):
|
||||
intersection._dictionary[key] = other_list._dictionary[key]
|
||||
return intersection
|
||||
|
||||
|
||||
func has_intersection(other_list: InkList) -> bool:
|
||||
for key in other_list._dictionary:
|
||||
if self._dictionary.has(key):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
# TODO: Make inspectable
|
||||
func without(list_to_remove: InkList) -> InkList:
|
||||
var result = InkList.new_with_ink_list(self)
|
||||
for key in list_to_remove._dictionary:
|
||||
result._dictionary.erase(key)
|
||||
return result
|
||||
|
||||
|
||||
func contains(other_list: InkList) -> bool:
|
||||
if other_list._dictionary.is_empty() || self._dictionary.is_empty():
|
||||
return false
|
||||
|
||||
for key in other_list._dictionary:
|
||||
if !_dictionary.has(key):
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
# In the original source code 'list_item_name' is of type (String | null),
|
||||
# but the method doesn't need to allow null names.
|
||||
func contains_item(list_item_name: String) -> bool:
|
||||
for key in self._dictionary:
|
||||
var list_item = InkListItem.from_serialized_key(key)
|
||||
if list_item.item_name == list_item_name:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func greater_than(other_list: InkList) -> bool:
|
||||
if size() == 0:
|
||||
return false
|
||||
if other_list.size() == 0:
|
||||
return true
|
||||
|
||||
return self.min_item.value > other_list.max_item.value
|
||||
|
||||
|
||||
func greater_than_or_equals(other_list: InkList) -> bool:
|
||||
if size() == 0:
|
||||
return false
|
||||
if other_list.size() == 0:
|
||||
return true
|
||||
|
||||
return (
|
||||
self.min_item.value >= other_list.min_item.value &&
|
||||
self.max_item.value >= other_list.max_item.value
|
||||
)
|
||||
|
||||
|
||||
func less_than(other_list: InkList) -> bool:
|
||||
if other_list.size() == 0:
|
||||
return false
|
||||
if size() == 0:
|
||||
return true
|
||||
|
||||
return self.max_item.value < other_list.min_item.value
|
||||
|
||||
|
||||
func less_than_or_equals(other_list: InkList) -> bool:
|
||||
if other_list.size() == 0:
|
||||
return false
|
||||
if size() == 0:
|
||||
return true
|
||||
|
||||
return (
|
||||
self.max_item.value <= other_list.max_item.value &&
|
||||
self.min_item.value <= other_list.min_item.value
|
||||
)
|
||||
|
||||
|
||||
func max_as_list() -> InkList:
|
||||
if size() > 0:
|
||||
var _max_item: InkKeyValuePair = self.max_item
|
||||
return InkList.new_with_single_item(_max_item.key, _max_item.value)
|
||||
else:
|
||||
return InkList.new()
|
||||
|
||||
|
||||
func min_as_list() -> InkList:
|
||||
if size() > 0:
|
||||
var _min_item: InkKeyValuePair = self.min_item
|
||||
return InkList.new_with_single_item(_min_item.key, _min_item.value)
|
||||
else:
|
||||
return InkList.new()
|
||||
|
||||
|
||||
# (Variant, Variant) -> InkList
|
||||
func list_with_sub_range(min_bound, max_bound) -> InkList:
|
||||
if size() == 0:
|
||||
return InkList.new()
|
||||
|
||||
var ordered: Array = self.ordered_items
|
||||
|
||||
var min_value: int = 0
|
||||
var max_value: int = 9_223_372_036_854_775_807 # MAX_INT
|
||||
|
||||
if min_bound is int:
|
||||
min_value = min_bound
|
||||
else:
|
||||
if min_bound.is_ink_class("InkList") && min_bound.size() > 0:
|
||||
min_value = min_bound.min_item.value
|
||||
|
||||
if max_bound is int:
|
||||
max_value = max_bound
|
||||
else:
|
||||
if min_bound.is_ink_class("InkList") && min_bound.size() > 0:
|
||||
max_value = max_bound.max_item.value
|
||||
|
||||
var sub_list = InkList.new()
|
||||
sub_list.set_initial_origin_names(self.origin_names)
|
||||
|
||||
for item in ordered:
|
||||
if item.value >= min_value && item.value <= max_value:
|
||||
sub_list.set_item(item.key, item.value)
|
||||
|
||||
return sub_list
|
||||
|
||||
|
||||
func equals(other: InkBase) -> bool:
|
||||
var other_raw_list: InkList = other
|
||||
# Simple test to make sure the object is of the right type.
|
||||
if !(other_raw_list is Object):
|
||||
return false
|
||||
if !(other_raw_list.is_ink_class("InkList")):
|
||||
return false
|
||||
|
||||
if other_raw_list.size() != self.size():
|
||||
return false
|
||||
|
||||
for key in keys():
|
||||
if (!other_raw_list.has_item(key)):
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
var ordered_items: Array: # Array<InkKeyValuePair<InkListItem, int>>
|
||||
get:
|
||||
var ordered: Array = []
|
||||
for key in keys():
|
||||
ordered.append(InkKeyValuePair.new_with_key_value(key, get_item(key)))
|
||||
|
||||
ordered.sort_custom(Callable(KeyValueInkListItemSorter, "sort"))
|
||||
return ordered
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
var ordered: Array = self.ordered_items
|
||||
|
||||
var description: String = ""
|
||||
var i: int = 0
|
||||
while (i < ordered.size()):
|
||||
if i > 0:
|
||||
description += ", "
|
||||
|
||||
var item = ordered[i].key
|
||||
description += item.item_name
|
||||
i += 1
|
||||
|
||||
return description
|
||||
|
||||
|
||||
static func new_with_dictionary(other_dictionary: Dictionary) -> InkList:
|
||||
var ink_list: InkList = InkList.new()
|
||||
ink_list._init_with_dictionary(other_dictionary)
|
||||
return ink_list
|
||||
|
||||
|
||||
static func new_with_ink_list(other_list: InkList) -> InkList:
|
||||
var ink_list: InkList = InkList.new()
|
||||
ink_list._init_with_ink_list(other_list)
|
||||
return ink_list
|
||||
|
||||
|
||||
static func new_with_origin(single_origin_list_name: String, origin_story) -> InkList:
|
||||
var ink_list: InkList = InkList.new()
|
||||
ink_list._init_with_origin(single_origin_list_name, origin_story)
|
||||
return ink_list
|
||||
|
||||
|
||||
static func new_with_single_item(single_item: InkListItem, single_value: int) -> InkList:
|
||||
var ink_list: InkList = InkList.new()
|
||||
ink_list._init_with_single_item(single_item, single_value)
|
||||
return ink_list
|
||||
|
||||
|
||||
class KeyValueInkListItemSorter:
|
||||
static func sort(a, b):
|
||||
if a.value == b.value:
|
||||
return a.key.origin_name.nocasecmp_to(b.key.origin_name) <= 0
|
||||
else:
|
||||
return a.value <= b.value
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# Originally, this class would inherit Dictionary. This isn't possible in
|
||||
# GDScript. Instead, this class will encapsulate a dictionary and forward
|
||||
# needed calls.
|
||||
# ############################################################################ #
|
||||
|
||||
var _dictionary: Dictionary = {}
|
||||
|
||||
|
||||
# Name set_item instead of set to prevent shadowing 'Object.set'.
|
||||
func set_item(key: InkListItem, value: int) -> void:
|
||||
_dictionary[key.serialized()] = value
|
||||
|
||||
|
||||
# Name get_item instead of get to prevent shadowing 'Object.get'.
|
||||
func get_item(key: InkListItem, default = null):
|
||||
return _dictionary.get(key.serialized(), default)
|
||||
|
||||
|
||||
# Name has_item instead of has to prevent shadowing 'Object.get'.
|
||||
func has_item(key: InkListItem) -> bool:
|
||||
return _dictionary.has(key.serialized())
|
||||
|
||||
|
||||
func keys() -> Array:
|
||||
var deserialized_keys = []
|
||||
for key in _dictionary.keys():
|
||||
deserialized_keys.append(InkListItem.from_serialized_key(key))
|
||||
|
||||
return deserialized_keys
|
||||
|
||||
|
||||
func size() -> int:
|
||||
return _dictionary.size()
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# Additional methods
|
||||
# ############################################################################ #
|
||||
|
||||
func set_raw(key: String, value: int) -> void:
|
||||
if OS.is_debug_build() && !(key is String):
|
||||
print("Warning: Expected serialized key in InkList.set_raw().")
|
||||
|
||||
_dictionary[key] = value
|
||||
|
||||
|
||||
func erase_raw(key: String) -> bool:
|
||||
if OS.is_debug_build() && !(key is String):
|
||||
print("Warning: Expected serialized key in InkList.erase_raw().")
|
||||
|
||||
return _dictionary.erase(key)
|
||||
|
||||
|
||||
func get_raw(key: String, default = null):
|
||||
if OS.is_debug_build() && !(key is String):
|
||||
print("Warning: Expected serialized key in InkList.get_raw().")
|
||||
|
||||
return _dictionary.get(key, default)
|
||||
|
||||
|
||||
func has_raw(key: String) -> bool:
|
||||
if OS.is_debug_build() && !(key is String):
|
||||
print("Warning: Expected serialized key in InkList.has_raw().")
|
||||
|
||||
return _dictionary.has(key)
|
||||
|
||||
|
||||
func has_all_raw(keys: Array) -> bool:
|
||||
return _dictionary.has_all(keys)
|
||||
|
||||
|
||||
func raw_keys() -> Array:
|
||||
return _dictionary.keys()
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkList" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkList"
|
||||
|
102
addons/inkgd/runtime/lists/list_definition.gd
Normal file
102
addons/inkgd/runtime/lists/list_definition.gd
Normal file
|
@ -0,0 +1,102 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkListDefinition
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
var InkTryGetResult = preload("res://addons/inkgd/runtime/extra/try_get_result.gd")
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var name: String: get = get_name
|
||||
func get_name() -> String:
|
||||
return _name
|
||||
|
||||
# Dictionary<InkListItem, int> => Dictionary<String, int>
|
||||
# Note: 'InkListItem' should actually be serialized into a String, because it
|
||||
# needs to be a value type.
|
||||
var items: Dictionary: get = get_items
|
||||
func get_items() -> Dictionary:
|
||||
if _items == null:
|
||||
_items = {}
|
||||
for item_name_and_value_key in _item_name_to_values:
|
||||
var item = InkListItem.new_with_origin_name(self.name, item_name_and_value_key)
|
||||
_items[item.serialized()] = _item_name_to_values[item_name_and_value_key]
|
||||
|
||||
return _items
|
||||
var _items
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func value_for_item(item: InkListItem) -> int:
|
||||
if (_item_name_to_values.has(item.item_name)):
|
||||
var intVal = _item_name_to_values[item.item_name]
|
||||
return intVal
|
||||
else:
|
||||
return 0
|
||||
|
||||
func contains_item(item: InkListItem) -> bool:
|
||||
if item.origin_name != self.name:
|
||||
return false
|
||||
|
||||
return _item_name_to_values.has(item.item_name)
|
||||
|
||||
func contains_item_with_name(item_name: String) -> bool:
|
||||
return _item_name_to_values.has(item_name)
|
||||
|
||||
# (int) -> { result: InkListItem, exists: bool }
|
||||
func try_get_item_with_value(val: int) -> InkTryGetResult:
|
||||
for named_item_key in _item_name_to_values:
|
||||
if (_item_name_to_values[named_item_key] == val):
|
||||
return InkTryGetResult.new(
|
||||
true,
|
||||
InkListItem.new_with_origin_name(self.name, named_item_key)
|
||||
)
|
||||
|
||||
return InkTryGetResult.new(false, InkListItem.null_item)
|
||||
|
||||
# (InkListItem) -> { result: InkListItem, exists: bool }
|
||||
func try_get_value_for_item(item: InkListItem) -> InkTryGetResult:
|
||||
if !item.item_name:
|
||||
return InkTryGetResult.new(false, 0)
|
||||
|
||||
var value = _item_name_to_values.get(item.item_name)
|
||||
|
||||
if (!value):
|
||||
InkTryGetResult.new(false, 0)
|
||||
|
||||
return InkTryGetResult.new(true, value)
|
||||
|
||||
# (String name, Dictionary<String, int>) -> InkListDefinition
|
||||
func _init(name: String, items: Dictionary):
|
||||
_name = name
|
||||
_item_name_to_values = items
|
||||
|
||||
var _name: String
|
||||
var _item_name_to_values: Dictionary # Dictionary<String, int>
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkListDefinition" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkListDefinition"
|
||||
|
||||
func _to_string() -> String:
|
||||
return "[InkListDefinition \"%s\"]" % get_name()
|
86
addons/inkgd/runtime/lists/list_definitions_origin.gd
Normal file
86
addons/inkgd/runtime/lists/list_definitions_origin.gd
Normal file
|
@ -0,0 +1,86 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
class_name InkListDefinitionsOrigin
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
var InkTryGetResult = preload("res://addons/inkgd/runtime/extra/try_get_result.gd")
|
||||
|
||||
var InkListValue = load("res://addons/inkgd/runtime/values/list_value.gd")
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# Array<InkListDefinition>
|
||||
var lists: Array: get = get_lists
|
||||
func get_lists() -> Array:
|
||||
var list_of_lists = []
|
||||
for named_list_key in _lists:
|
||||
list_of_lists.append(_lists[named_list_key])
|
||||
|
||||
return list_of_lists
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (Array<InkListDefinition>) -> InkListDefinitionOrigin
|
||||
func _init(lists: Array):
|
||||
_lists = {} # Dictionary<String, InkListDefinition>
|
||||
_all_unambiguous_list_value_cache = {} # Dictionary<String, InkListValue>()
|
||||
|
||||
for list in lists:
|
||||
_lists[list.name] = list
|
||||
|
||||
for item_with_value_key in list.items:
|
||||
var item = InkListItem.from_serialized_key(item_with_value_key)
|
||||
var val = list.items[item_with_value_key]
|
||||
var list_value = InkListValue.new_with_single_item(item, val)
|
||||
|
||||
_all_unambiguous_list_value_cache[item.item_name] = list_value
|
||||
_all_unambiguous_list_value_cache[item.full_name] = list_value
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (String) -> { result: String, exists: bool }
|
||||
func try_list_get_definition(name: String) -> InkTryGetResult:
|
||||
if name == null:
|
||||
return InkTryGetResult.new(false, null)
|
||||
|
||||
var definition = _lists.get(name)
|
||||
if !definition:
|
||||
return InkTryGetResult.new(false, null)
|
||||
|
||||
return InkTryGetResult.new(true, definition)
|
||||
|
||||
func find_single_item_list_with_name(name: String) -> InkListValue:
|
||||
if _all_unambiguous_list_value_cache.has(name):
|
||||
return _all_unambiguous_list_value_cache[name]
|
||||
|
||||
return null
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var _lists: Dictionary # Dictionary<String, InkListDefinition>
|
||||
var _all_unambiguous_list_value_cache: Dictionary # Dictionary<String, InkListValue>
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkListDefinitionsOrigin" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkListDefinitionsOrigin"
|
159
addons/inkgd/runtime/lists/structs/ink_list_item.gd
Normal file
159
addons/inkgd/runtime/lists/structs/ink_list_item.gd
Normal file
|
@ -0,0 +1,159 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# ############################################################################ #
|
||||
# !! VALUE TYPE
|
||||
# ############################################################################ #
|
||||
|
||||
extends InkObject
|
||||
|
||||
class_name InkListItem
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# Originally these were simple variables, but they are turned into properties to
|
||||
# make the object "immutable". That way it can be passed around without being
|
||||
# duplicated.
|
||||
|
||||
var origin_name:
|
||||
get: return _origin_name
|
||||
var _origin_name = null # String
|
||||
|
||||
var item_name:
|
||||
get: return _item_name
|
||||
var _item_name = null # String
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (string, string) -> InkListItem
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init_with_origin_name(origin_name, item_name):
|
||||
self._origin_name = origin_name
|
||||
self._item_name = item_name
|
||||
|
||||
|
||||
# (string) -> InkListItem
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init_with_full_name(full_name):
|
||||
var name_parts = full_name.split(".")
|
||||
self._origin_name = name_parts[0]
|
||||
self._item_name = name_parts[1]
|
||||
|
||||
|
||||
static var null_item: InkListItem:
|
||||
get: return InkListItem.new_with_origin_name(null, null)
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var is_null: bool:
|
||||
get:
|
||||
return self.origin_name == null && self.item_name == null
|
||||
|
||||
# String
|
||||
var full_name:
|
||||
get:
|
||||
# In C#, concatenating null produce nothing, in GDScript, it appends "Null".
|
||||
return (
|
||||
(self.origin_name if self.origin_name else "?") + "." +
|
||||
(self.item_name if self.item_name else "")
|
||||
)
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# () -> String
|
||||
func _to_string() -> String:
|
||||
return self.full_name
|
||||
|
||||
|
||||
# (InkObject) -> bool
|
||||
func equals(obj: InkBase) -> bool:
|
||||
if obj.is_ink_class("InkListItem"):
|
||||
var other_item = obj
|
||||
return (
|
||||
other_item.item_name == self.item_name &&
|
||||
self.other_item.origin_name == self.origin_name
|
||||
)
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (string, string) -> InkListItem
|
||||
@warning_ignore("shadowed_variable")
|
||||
static func new_with_origin_name(origin_name, item_name) -> InkListItem:
|
||||
var list_item = InkListItem.new()
|
||||
list_item._init_with_origin_name(origin_name, item_name)
|
||||
return list_item
|
||||
|
||||
|
||||
# (string) -> InkListItem
|
||||
@warning_ignore("shadowed_variable")
|
||||
static func new_with_full_name(full_name) -> InkListItem:
|
||||
var list_item = InkListItem.new()
|
||||
list_item._init_with_full_name(full_name)
|
||||
return list_item
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkListItem" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkListItem"
|
||||
|
||||
# ############################################################################ #
|
||||
# These methods did not exist in the original C# code. Their purpose is to
|
||||
# make `InkListItem` mimic the value-type semantics of the original
|
||||
# struct, as well as offering a serialization mechanism to use `InkListItem`
|
||||
# as keys in dictionaries.
|
||||
|
||||
# Returns a `SerializedInkListItem` representing the current
|
||||
# instance. The result is intended to be used as a key inside a Map.
|
||||
func serialized() -> String:
|
||||
# We are simply using a JSON representation as a value-typed key.
|
||||
var json_print = JSON.stringify(
|
||||
{ "originName": self.origin_name, "itemName": self.item_name }
|
||||
)
|
||||
return json_print
|
||||
|
||||
# Reconstructs a `InkListItem` from the given SerializedInkListItem.
|
||||
#
|
||||
# (String) -> InkListItem
|
||||
static func from_serialized_key(key: String) -> InkListItem:
|
||||
var obj = JSON.parse_string(key)
|
||||
if !InkListItem._is_like_ink_list_item(obj):
|
||||
return InkListItem.null_item
|
||||
|
||||
return InkListItem.new_with_origin_name(obj["originName"], obj["itemName"])
|
||||
|
||||
# Determines whether the given item is sufficiently `InkListItem`-like
|
||||
# to be used as a template when reconstructing the InkListItem.
|
||||
#
|
||||
# (Variant) -> bool
|
||||
static func _is_like_ink_list_item(item) -> bool:
|
||||
if !(item is Dictionary):
|
||||
return false
|
||||
|
||||
if !(item.has("originName") && item.has("itemName")):
|
||||
return false
|
||||
|
||||
if !(item["originName"] is String):
|
||||
return false
|
||||
|
||||
if !(item["itemName"] is String):
|
||||
return false
|
||||
|
||||
return true
|
57
addons/inkgd/runtime/profiler.gd
Normal file
57
addons/inkgd/runtime/profiler.gd
Normal file
|
@ -0,0 +1,57 @@
|
|||
# ############################################################################ #
|
||||
# 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 InkProfiler
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
# () -> String
|
||||
func report() -> String:
|
||||
return ""
|
||||
|
||||
# () -> void
|
||||
func pre_continue() -> void:
|
||||
pass
|
||||
|
||||
# () -> void
|
||||
func post_continue() -> void:
|
||||
pass
|
||||
|
||||
# () -> void
|
||||
func pre_step() -> void:
|
||||
pass
|
||||
|
||||
# (CallStack) -> void
|
||||
func step(callstack: InkCallStack) -> void:
|
||||
pass
|
||||
|
||||
# () -> void
|
||||
func post_step() -> void:
|
||||
pass
|
||||
|
||||
func step_length_record() -> String:
|
||||
return ""
|
||||
|
||||
func mega_log() -> String:
|
||||
return ""
|
||||
|
||||
func pre_snapshot() -> void:
|
||||
pass
|
||||
|
||||
func post_snapshot() -> void:
|
||||
pass
|
||||
|
||||
func millisecs(watch: InkStopWatch) -> float:
|
||||
return 0.0
|
||||
|
||||
static func format_millisecs(num: float) -> String:
|
||||
return ""
|
41
addons/inkgd/runtime/search_result.gd
Normal file
41
addons/inkgd/runtime/search_result.gd
Normal file
|
@ -0,0 +1,41 @@
|
|||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# ############################################################################ #
|
||||
# !! VALUE TYPE
|
||||
# ############################################################################ #
|
||||
|
||||
# Search results are never duplicated / passed around so they don't need to
|
||||
# be either immutable or have a 'duplicate' method.
|
||||
|
||||
extends InkBase
|
||||
|
||||
class_name InkSearchResult
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var obj: InkObject = null
|
||||
var approximate: bool = false
|
||||
|
||||
var correct_obj: InkObject:
|
||||
get: return null if approximate else obj
|
||||
|
||||
var container: InkContainer:
|
||||
get: return InkUtils.as_or_null(obj, "InkContainer")
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "SearchResult" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "SearchResult"
|
588
addons/inkgd/runtime/simple_json.gd
Normal file
588
addons/inkgd/runtime/simple_json.gd
Normal file
|
@ -0,0 +1,588 @@
|
|||
# ############################################################################ #
|
||||
# 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 InkSimpleJSON
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (String) -> Dictionary<String, Variant>
|
||||
static func text_to_dictionary(text: String) -> Dictionary:
|
||||
return Reader.new(text).to_dictionary()
|
||||
|
||||
# (String) -> Array<Variant>
|
||||
static func text_to_array(text: String) -> Array:
|
||||
return Reader.new(text).to_array()
|
||||
|
||||
class Reader extends InkBase:
|
||||
# (String) -> Reader
|
||||
func _init(text: String):
|
||||
_text = text
|
||||
_offset = 0
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
_root_object = read_object()
|
||||
|
||||
# () -> Dictionary<String, Variant>
|
||||
func to_dictionary() -> Dictionary:
|
||||
return _root_object
|
||||
|
||||
# () -> Array<Variant>
|
||||
func to_array() -> Array:
|
||||
return _root_object
|
||||
|
||||
# (String) -> bool
|
||||
func is_number_char(c: String) -> bool:
|
||||
if c.length() > 1:
|
||||
return false
|
||||
|
||||
return c.is_valid_int() || c == "." || c == "-" || c == "+" || c == 'E' || c == 'e'
|
||||
|
||||
# (String) -> bool
|
||||
func is_first_number_char(c: String) -> bool:
|
||||
if c.length() > 1:
|
||||
return false
|
||||
|
||||
return c.is_valid_int() || c == "-" || c == "+"
|
||||
|
||||
# () -> Variant
|
||||
func read_object():
|
||||
var current_char = _text[_offset]
|
||||
|
||||
if current_char == "{":
|
||||
return read_dictionary()
|
||||
|
||||
elif current_char == "[":
|
||||
return read_array()
|
||||
|
||||
elif current_char == "\"":
|
||||
return read_string()
|
||||
|
||||
elif is_first_number_char(current_char):
|
||||
return read_number()
|
||||
|
||||
elif try_read("true"):
|
||||
return true
|
||||
|
||||
elif try_read("false"):
|
||||
return false
|
||||
|
||||
elif try_read("null"):
|
||||
return null
|
||||
|
||||
InkUtils.throw_exception("Unhandled object type in JSON: %s" % _text.substr(_offset, 30))
|
||||
return JsonError.new()
|
||||
|
||||
# () -> Dictionary<String, Variant>?
|
||||
func read_dictionary():
|
||||
var dict = {} # Dictionary<String, Variant>
|
||||
|
||||
if !expect("{"):
|
||||
return null
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
if try_read("}"):
|
||||
return dict
|
||||
|
||||
var first_time = true
|
||||
while first_time || try_read(","):
|
||||
first_time = false
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
var key = read_string()
|
||||
if !expect(key != null, "dictionary key"):
|
||||
return null
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
if !expect(":"):
|
||||
return null
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
var val = read_object()
|
||||
if !expect(val != null, "dictionary value"):
|
||||
return null
|
||||
|
||||
dict[key] = val
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
if !expect("}"):
|
||||
return null
|
||||
|
||||
return dict
|
||||
|
||||
# () -> Array<Variant>?
|
||||
func read_array():
|
||||
var list = []
|
||||
|
||||
if !expect("["):
|
||||
return null
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
if try_read("]"):
|
||||
return list
|
||||
|
||||
var first_time = true
|
||||
while first_time || try_read(","):
|
||||
first_time = false
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
var val = read_object()
|
||||
|
||||
list.append(val)
|
||||
|
||||
skip_whitespace()
|
||||
|
||||
if !expect("]"):
|
||||
return null
|
||||
|
||||
return list
|
||||
|
||||
# () -> String?
|
||||
func read_string():
|
||||
if !expect("\""):
|
||||
return null
|
||||
|
||||
var sb = ""
|
||||
|
||||
while(_offset < _text.length()):
|
||||
var c = _text[_offset]
|
||||
|
||||
if c == "\\":
|
||||
_offset += 1
|
||||
if _offset >= _text.length():
|
||||
InkUtils.throw_exception("Unexpected EOF while reading string")
|
||||
return null
|
||||
c = _text[_offset]
|
||||
match c:
|
||||
"\"", "\\", "/":
|
||||
sb += c
|
||||
"n":
|
||||
sb += "\n"
|
||||
"t":
|
||||
sb += "\t"
|
||||
"r", "b", "f":
|
||||
pass
|
||||
"u":
|
||||
if _offset + 4 >= _text.length():
|
||||
InkUtils.throw_exception("Unexpected EOF while reading string")
|
||||
return null
|
||||
var digits = _text.substr(_offset + 1, 4)
|
||||
|
||||
var test_json_conv = JSON.new()
|
||||
test_json_conv.parse("\"\\u" + digits + "\"")
|
||||
var json_parse_result = test_json_conv.get_data()
|
||||
if json_parse_result.error != OK:
|
||||
InkUtils.throw_exception("Invalid Unicode escape character at offset %d" % (_offset - 1))
|
||||
return null
|
||||
|
||||
sb += json_parse_result.result
|
||||
_offset += 4
|
||||
|
||||
break
|
||||
_:
|
||||
InkUtils.throw_exception("Invalid Unicode escape character at offset %d " % (_offset - 1))
|
||||
return null
|
||||
elif c == "\"":
|
||||
break
|
||||
else:
|
||||
sb += c
|
||||
|
||||
_offset += 1
|
||||
|
||||
if !expect("\""):
|
||||
return null
|
||||
return sb
|
||||
|
||||
# () -> Variant
|
||||
func read_number():
|
||||
var start_offset = _offset
|
||||
|
||||
var is_float = false
|
||||
|
||||
while(_offset < _text.length()):
|
||||
var c = _text[_offset]
|
||||
if (c == "." || c == "e" || c == "E"): is_float = true
|
||||
if is_number_char(c):
|
||||
_offset += 1
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
_offset += 1
|
||||
|
||||
var num_str = _text.substr(start_offset, _offset - start_offset)
|
||||
|
||||
if is_float:
|
||||
if num_str.is_valid_float():
|
||||
return float(num_str)
|
||||
else:
|
||||
if num_str.is_valid_int():
|
||||
return int(num_str)
|
||||
|
||||
InkUtils.throw_exception("Failed to parse number value: " + num_str)
|
||||
return JsonError.new()
|
||||
|
||||
# (String) -> bool
|
||||
func try_read(text_to_read: String) -> bool:
|
||||
if _offset + text_to_read.length() > _text.length():
|
||||
return false
|
||||
|
||||
var i = 0
|
||||
while (i < text_to_read.length()):
|
||||
if text_to_read[i] != _text[_offset + i]:
|
||||
return false
|
||||
|
||||
i += 1
|
||||
|
||||
|
||||
_offset += text_to_read.length()
|
||||
|
||||
return true
|
||||
|
||||
# (bool | String, String) -> bool
|
||||
func expect(condition_or_expected_str, message = null) -> bool:
|
||||
var _condition = false
|
||||
|
||||
if condition_or_expected_str is String:
|
||||
_condition = try_read(condition_or_expected_str)
|
||||
elif condition_or_expected_str is bool:
|
||||
_condition = condition_or_expected_str
|
||||
|
||||
if !_condition:
|
||||
if message == null:
|
||||
message = "Unexpected token"
|
||||
else:
|
||||
message = "Expected " + message
|
||||
|
||||
message += str(" at offset ", _offset)
|
||||
|
||||
InkUtils.throw_exception(message)
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
func skip_whitespace():
|
||||
while _offset < _text.length():
|
||||
var c = _text[_offset]
|
||||
if c == " " || c == "\t" || c == "\n" || c == "\r":
|
||||
_offset += 1
|
||||
else:
|
||||
break
|
||||
|
||||
var _text = null # String
|
||||
var _offset: int = 0 # int
|
||||
|
||||
var _root_object # Variant
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkSimpleJSON.Reader" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkSimpleJSON.Reader"
|
||||
|
||||
class Writer extends InkBase:
|
||||
# ######################################################################## #
|
||||
# Imports
|
||||
# ######################################################################## #
|
||||
|
||||
var InkStringWriter := load("res://addons/inkgd/runtime/extra/string_writer.gd") as GDScript
|
||||
var InkStateElement := load("res://addons/inkgd/runtime/extra/state_element.gd") as GDScript
|
||||
|
||||
# (String) -> Writer
|
||||
func _init():
|
||||
self._writer = InkStringWriter.new()
|
||||
|
||||
# (Callable) -> void
|
||||
func write_object(inner: Callable) -> void:
|
||||
write_object_start()
|
||||
inner.call(self)
|
||||
write_object_end()
|
||||
|
||||
func write_object_start() -> void:
|
||||
start_new_object(true)
|
||||
self._state_stack.push_front(InkStateElement.new(InkStateElement.State.OBJECT))
|
||||
self._writer.write("{")
|
||||
|
||||
func write_object_end() -> void:
|
||||
assert_that(self.state == InkStateElement.State.OBJECT)
|
||||
self._writer.write("}")
|
||||
self._state_stack.pop_front()
|
||||
|
||||
# These two methods don't need to be implemented in GDScript.
|
||||
#
|
||||
# public void WriteProperty(string name, Action<Writer> inner)
|
||||
# public void WriteProperty(int id, Action<Writer> inner)
|
||||
|
||||
# Also include:
|
||||
# void WriteProperty<T>(T name, Action<Writer> inner)
|
||||
# (String, Variant) -> void
|
||||
func write_property(name: String, content) -> void:
|
||||
if (content is String || content is int || content is bool):
|
||||
write_property_start(name)
|
||||
write(content)
|
||||
write_property_end()
|
||||
elif content is Callable:
|
||||
write_property_start(name)
|
||||
content.call(self)
|
||||
write_property_end()
|
||||
else:
|
||||
push_error("Wrong type for 'content': %s" % str(content))
|
||||
|
||||
# These two methods don't need to be implemented in GDScript.
|
||||
#
|
||||
# public void WritePropertyStart(string name)
|
||||
# public void WritePropertyStart(int id)
|
||||
|
||||
# () -> void
|
||||
func write_property_end() -> void:
|
||||
assert_that(self.state == InkStateElement.State.PROPERTY)
|
||||
assert_that(self.child_count == 1)
|
||||
self._state_stack.pop_front()
|
||||
|
||||
# (String) -> void
|
||||
func write_property_name_start() -> void:
|
||||
assert_that(self.state == InkStateElement.State.OBJECT)
|
||||
|
||||
if self.child_count > 0:
|
||||
self._writer.write(',')
|
||||
|
||||
self._writer.write('"')
|
||||
|
||||
increment_child_count()
|
||||
|
||||
self._state_stack.push_front(InkStateElement.new(InkStateElement.State.PROPERTY))
|
||||
self._state_stack.push_front(InkStateElement.new(InkStateElement.State.PROPERTY_NAME))
|
||||
|
||||
# () -> void
|
||||
func write_property_name_end() -> void:
|
||||
assert_that(self.state == InkStateElement.State.PROPERTY_NAME)
|
||||
|
||||
self._writer.write('":')
|
||||
|
||||
self._state_stack.pop_front()
|
||||
|
||||
# (String) -> void
|
||||
func write_property_name_inner(string: String) -> void:
|
||||
assert_that(self.state == InkStateElement.State.PROPERTY_NAME)
|
||||
self._writer.write(string)
|
||||
|
||||
# (Variant) -> void
|
||||
func write_property_start(name) -> void:
|
||||
assert_that(self.state == InkStateElement.State.OBJECT)
|
||||
|
||||
if self.child_count > 0:
|
||||
self._writer.write(',')
|
||||
|
||||
self._writer.write('"')
|
||||
self._writer.write(str(name))
|
||||
self._writer.write('":')
|
||||
|
||||
increment_child_count()
|
||||
|
||||
_state_stack.push_front(InkStateElement.new(InkStateElement.State.PROPERTY))
|
||||
|
||||
# () -> void
|
||||
func write_array_start() -> void:
|
||||
start_new_object(true)
|
||||
_state_stack.push_front(InkStateElement.new(InkStateElement.State.ARRAY))
|
||||
_writer.write("[")
|
||||
|
||||
# () -> void
|
||||
func write_array_end() -> void:
|
||||
assert_that(self.state == InkStateElement.State.ARRAY)
|
||||
_writer.write("]")
|
||||
_state_stack.pop_front()
|
||||
|
||||
# This method didn't exist as-is in the original implementation.
|
||||
# (Variant) -> void
|
||||
func write(content) -> void:
|
||||
if content is int:
|
||||
write_int(content)
|
||||
elif content is float:
|
||||
write_float(content)
|
||||
elif content is String:
|
||||
write_string(content)
|
||||
elif content is bool:
|
||||
write_bool(content)
|
||||
else:
|
||||
push_error("Wrong type for 'content': %s" % str(content))
|
||||
|
||||
# (int) -> void
|
||||
func write_int(i: int) -> void:
|
||||
start_new_object(false)
|
||||
_writer.write(str(i))
|
||||
|
||||
# (float) -> void
|
||||
func write_float(f: float) -> void:
|
||||
start_new_object(false)
|
||||
|
||||
var float_str = str(f)
|
||||
|
||||
# We could probably use 3.402823e+38, but keeping
|
||||
# ±3.4e+38 for compatibility with the reference implementation.
|
||||
if float_str == "inf":
|
||||
_writer.write("3.4e+38")
|
||||
elif float_str == "-inf":
|
||||
_writer.write("-3.4e+38")
|
||||
elif float_str == "nan":
|
||||
_writer.write("0.0")
|
||||
else:
|
||||
_writer.write(float_str)
|
||||
# The exponent part is defensive as Godot doesn't seem to convert
|
||||
# floats to string in such a way.
|
||||
if !("." in float_str) && !("e" in float_str) && !("E" in float_str):
|
||||
_writer.write(".0")
|
||||
|
||||
# (String, bool) -> void
|
||||
func write_string(string: String, escape: bool = true):
|
||||
start_new_object(false)
|
||||
_writer.write('"')
|
||||
if escape:
|
||||
write_escaped_string(string)
|
||||
else:
|
||||
_writer.write(string)
|
||||
_writer.write('"')
|
||||
|
||||
# (bool) -> void
|
||||
func write_bool(b: bool) -> void:
|
||||
start_new_object(false)
|
||||
_writer.write("true" if b else "false")
|
||||
|
||||
# () -> void
|
||||
func write_null() -> void:
|
||||
start_new_object(false)
|
||||
_writer.write("null")
|
||||
|
||||
# () -> void
|
||||
func write_string_start() -> void:
|
||||
start_new_object(true)
|
||||
_state_stack.push_front(InkStateElement.new(InkStateElement.State.STRING))
|
||||
_writer.write('"')
|
||||
|
||||
# () -> void
|
||||
func write_string_end() -> void:
|
||||
assert_that(state == InkStateElement.State.STRING)
|
||||
_writer.write('"')
|
||||
_state_stack.pop_front()
|
||||
|
||||
# (string, bool) -> void
|
||||
func write_string_inner(string: String, escape: bool = true) -> void:
|
||||
assert_that(self.state == InkStateElement.State.STRING)
|
||||
if escape:
|
||||
write_escaped_string(string)
|
||||
else:
|
||||
_writer.write(string)
|
||||
|
||||
# (String) -> void
|
||||
func write_escaped_string(string: String) -> void:
|
||||
for c in string:
|
||||
if c < ' ':
|
||||
match c:
|
||||
"\n":
|
||||
_writer.write("\\n")
|
||||
"\t":
|
||||
_writer.write("\\t")
|
||||
else:
|
||||
match c:
|
||||
'\\', '"':
|
||||
_writer.write("\\")
|
||||
_writer.write(c)
|
||||
_:
|
||||
_writer.write(c)
|
||||
|
||||
# (bool) -> void
|
||||
func start_new_object(container: bool) -> void:
|
||||
if container:
|
||||
assert_that(
|
||||
self.state == InkStateElement.State.NONE ||
|
||||
self.state == InkStateElement.State.PROPERTY ||
|
||||
self.state == InkStateElement.State.ARRAY
|
||||
)
|
||||
else:
|
||||
assert_that(
|
||||
self.state == InkStateElement.State.PROPERTY ||
|
||||
self.state == InkStateElement.State.ARRAY
|
||||
)
|
||||
|
||||
if self.state == InkStateElement.State.ARRAY && self.child_count > 0:
|
||||
_writer.write(",")
|
||||
|
||||
if self.state == InkStateElement.State.PROPERTY:
|
||||
assert_that(self.child_count == 0)
|
||||
|
||||
if (
|
||||
self.state == InkStateElement.State.ARRAY ||
|
||||
self.state == InkStateElement.State.PROPERTY
|
||||
):
|
||||
increment_child_count()
|
||||
|
||||
var state: int: # StateElement.State
|
||||
get:
|
||||
if _state_stack.size() > 0:
|
||||
return _state_stack.front().type
|
||||
else:
|
||||
return InkStateElement.State.NONE
|
||||
|
||||
var child_count: int: # int
|
||||
get:
|
||||
if _state_stack.size() > 0:
|
||||
return _state_stack.front().child_count
|
||||
else:
|
||||
return 0
|
||||
|
||||
# () -> void
|
||||
func increment_child_count() -> void:
|
||||
assert_that(_state_stack.size() > 0)
|
||||
var curr_el = _state_stack.pop_front()
|
||||
curr_el.child_count += 1
|
||||
_state_stack.push_front(curr_el)
|
||||
|
||||
# (bool) -> void
|
||||
func assert_that(condition: bool) -> void:
|
||||
if OS.is_debug_build():
|
||||
return
|
||||
|
||||
if !condition:
|
||||
push_error("Assert failed while writing JSON")
|
||||
assert(condition)
|
||||
|
||||
# () -> String
|
||||
func _to_string() -> String:
|
||||
return _writer._to_string()
|
||||
|
||||
var _state_stack: Array = [] # Array<StateElement>
|
||||
var _writer: InkStringWriter
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkSimpleJSON.Writer" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkSimpleJSON.Writer"
|
||||
|
||||
|
||||
class JsonError:
|
||||
func init():
|
||||
pass
|
105
addons/inkgd/runtime/state_patch.gd
Normal file
105
addons/inkgd/runtime/state_patch.gd
Normal file
|
@ -0,0 +1,105 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkBase
|
||||
|
||||
class_name InkStatePatch
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
var InkTryGetResult := preload("res://addons/inkgd/runtime/extra/try_get_result.gd") as GDScript
|
||||
var InkStringSet := preload("res://addons/inkgd/runtime/extra/string_set.gd") as GDScript
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# Dictionary<String, InkObject>
|
||||
var globals: Dictionary: get = get_globals
|
||||
func get_globals() -> Dictionary:
|
||||
return _globals
|
||||
|
||||
# StringSet
|
||||
var changed_variables: InkStringSet: get = get_changed_variables
|
||||
func get_changed_variables() -> InkStringSet:
|
||||
return _changed_variables
|
||||
|
||||
# Dictionary<InkContainer, int>
|
||||
var visit_counts: Dictionary: get = get_visit_counts
|
||||
func get_visit_counts() -> Dictionary:
|
||||
return _visit_counts
|
||||
|
||||
# Dictionary<InkContainer, int>
|
||||
var turn_indices : get = get_turn_indices
|
||||
func get_turn_indices() -> Dictionary:
|
||||
return _turn_indices
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func _init(to_copy: InkStatePatch):
|
||||
if to_copy != null:
|
||||
_globals = to_copy._globals.duplicate()
|
||||
_changed_variables = to_copy._changed_variables.duplicate()
|
||||
_visit_counts = to_copy._visit_counts.duplicate()
|
||||
_turn_indices = to_copy._turn_indices.duplicate()
|
||||
else:
|
||||
_globals = {}
|
||||
_changed_variables = InkStringSet.new()
|
||||
_visit_counts = {}
|
||||
_turn_indices = {}
|
||||
|
||||
# (String) -> { exists: bool, result: InkObject }
|
||||
func try_get_global(name) -> InkTryGetResult:
|
||||
if _globals.has(name):
|
||||
return InkTryGetResult.new(true, _globals[name])
|
||||
|
||||
return InkTryGetResult.new(false, null)
|
||||
|
||||
func set_global(name: String, value: InkObject) -> void:
|
||||
_globals[name] = value
|
||||
|
||||
func add_changed_variable(name: String) -> void:
|
||||
_changed_variables.append(name)
|
||||
|
||||
# (InkContainer) -> { exists: bool, result: int }
|
||||
func try_get_visit_count(container) -> InkTryGetResult:
|
||||
if _visit_counts.has(container):
|
||||
return InkTryGetResult.new(true, _visit_counts[container])
|
||||
|
||||
return InkTryGetResult.new(false, 0)
|
||||
|
||||
func set_visit_count(container: InkContainer, index: int) -> void:
|
||||
_visit_counts[container] = index
|
||||
|
||||
func set_turn_index(container: InkContainer, index: int) -> void:
|
||||
_turn_indices[container] = index
|
||||
|
||||
# (InkContainer) -> { exists: bool, result: int }
|
||||
func try_get_turn_index(container) -> InkTryGetResult:
|
||||
if _turn_indices.has(container):
|
||||
return InkTryGetResult.new(true, _turn_indices[container])
|
||||
|
||||
return InkTryGetResult.new(false, 0)
|
||||
|
||||
var _globals: Dictionary
|
||||
var _changed_variables: InkStringSet
|
||||
var _visit_counts: Dictionary
|
||||
var _turn_indices: Dictionary
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "StatePatch" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "StatePatch"
|
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)
|
2095
addons/inkgd/runtime/story.gd
Normal file
2095
addons/inkgd/runtime/story.gd
Normal file
File diff suppressed because it is too large
Load diff
1144
addons/inkgd/runtime/story_state.gd
Normal file
1144
addons/inkgd/runtime/story_state.gd
Normal file
File diff suppressed because it is too large
Load diff
125
addons/inkgd/runtime/structs/pointer.gd
Normal file
125
addons/inkgd/runtime/structs/pointer.gd
Normal file
|
@ -0,0 +1,125 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
# ############################################################################ #
|
||||
# !! VALUE TYPE
|
||||
# ############################################################################ #
|
||||
|
||||
# Pointers are passed around a lot, to prevent duplicating them all the time
|
||||
# and confusing the inspector when the debugger is attached, they are
|
||||
# immutable rather than being duplicated.
|
||||
|
||||
extends InkBase
|
||||
|
||||
class_name InkPointer
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# InkContainer
|
||||
# Encapsulating container into a weak ref.
|
||||
var container: InkContainer:
|
||||
get:
|
||||
return self._container.get_ref()
|
||||
|
||||
set(value):
|
||||
assert(false, "Pointer is immutable, cannot set container.")
|
||||
|
||||
var _container: WeakRef = WeakRef.new()
|
||||
|
||||
var index: int:
|
||||
get:
|
||||
return _index
|
||||
|
||||
set(value):
|
||||
assert(false, "Pointer is immutable, cannot set index.")
|
||||
|
||||
var _index: int = 0 # int
|
||||
|
||||
|
||||
# (InkContainer, int) -> InkPointer
|
||||
func _init(container: InkContainer = null, index: int = 0):
|
||||
if container == null:
|
||||
self._container = WeakRef.new()
|
||||
else:
|
||||
self._container = weakref(container)
|
||||
|
||||
self._index = index
|
||||
|
||||
|
||||
# () -> InkContainer
|
||||
func resolve():
|
||||
if self.index < 0: return self.container
|
||||
if self.container == null: return null
|
||||
if self.container.content.size() == 0: return self.container
|
||||
if self.index >= self.container.content.size(): return null
|
||||
|
||||
return self.container.content[self.index]
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# () -> bool
|
||||
var is_null: bool: get = get_is_null
|
||||
func get_is_null() -> bool:
|
||||
return self.container == null
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# TODO: Make inspectable
|
||||
# () -> InkPath
|
||||
var path: InkPath:
|
||||
get:
|
||||
if self.is_null:
|
||||
return null
|
||||
|
||||
if self.index >= 0:
|
||||
return self.container.path.path_by_appending_component(
|
||||
InkPath.Component.new(self.index)
|
||||
)
|
||||
else:
|
||||
return self.container.path
|
||||
|
||||
|
||||
############################################################################# #
|
||||
|
||||
func _to_string() -> String:
|
||||
if self.container == null:
|
||||
return "Ink Pointer (null)"
|
||||
|
||||
return "Ink Pointer -> %s -- index %d" % [self.container.path._to_string(), self.index]
|
||||
|
||||
|
||||
# (InkContainer) -> InkPointer
|
||||
static func start_of(container: InkContainer) -> InkPointer:
|
||||
return InkPointer.new(container, 0)
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# () -> InkPointer
|
||||
static var null_pointer: InkPointer:
|
||||
get: return InkPointer.new(null, -1)
|
||||
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "InkPointer" || super.is_ink_class(type)
|
||||
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "InkPointer"
|
||||
|
||||
|
||||
func duplicate() -> InkPointer:
|
||||
return InkPointer.new(self.container, self.index)
|
62
addons/inkgd/runtime/values/bool_value.gd
Normal file
62
addons/inkgd/runtime/values/bool_value.gd
Normal file
|
@ -0,0 +1,62 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkValue
|
||||
|
||||
class_name InkBoolValue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func get_value_type() -> int:
|
||||
return ValueType.BOOL
|
||||
|
||||
func get_is_truthy() -> bool:
|
||||
return value
|
||||
|
||||
func _init():
|
||||
value = false
|
||||
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func cast(new_type, metadata = null):
|
||||
if new_type == self.value_type:
|
||||
return self
|
||||
|
||||
if new_type == ValueType.INT:
|
||||
return IntValue().new_with(1 if value else 0)
|
||||
|
||||
if new_type == ValueType.FLOAT:
|
||||
return FloatValue().new_with(1.0 if value else 0.0)
|
||||
|
||||
if new_type == ValueType.STRING:
|
||||
return StringValue().new_with("true" if value else "false")
|
||||
|
||||
InkUtils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata)
|
||||
return null
|
||||
|
||||
func _to_string() -> String:
|
||||
return "true" if value else "false"
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "BoolValue" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "BoolValue"
|
||||
|
||||
static func new_with(val):
|
||||
var value = BoolValue().new()
|
||||
value._init_with(val)
|
||||
return value
|
60
addons/inkgd/runtime/values/divert_target_value.gd
Normal file
60
addons/inkgd/runtime/values/divert_target_value.gd
Normal file
|
@ -0,0 +1,60 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkValue
|
||||
|
||||
class_name InkDivertTargetValue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var target_path : get = get_target_path, set = set_target_path # InkPath
|
||||
func get_target_path():
|
||||
return value
|
||||
func set_target_path(value):
|
||||
self.value = value
|
||||
|
||||
func get_value_type():
|
||||
return ValueType.DIVERT_TARGET
|
||||
|
||||
func get_is_truthy():
|
||||
InkUtils.throw_exception("Shouldn't be checking the truthiness of a divert target")
|
||||
return false
|
||||
|
||||
func _init():
|
||||
value = null
|
||||
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func cast(new_type, metadata = null):
|
||||
if new_type == self.value_type:
|
||||
return self
|
||||
|
||||
InkUtils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata)
|
||||
return null
|
||||
|
||||
func _to_string() -> String:
|
||||
return "DivertTargetValue(" + self.target_path._to_string() + ")"
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "DivertTargetValue" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "DivertTargetValue"
|
||||
|
||||
static func new_with(val):
|
||||
var value = DivertTargetValue().new()
|
||||
value._init_with(val)
|
||||
return value
|
59
addons/inkgd/runtime/values/float_value.gd
Normal file
59
addons/inkgd/runtime/values/float_value.gd
Normal file
|
@ -0,0 +1,59 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkValue
|
||||
|
||||
class_name InkFloatValue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func get_value_type():
|
||||
return ValueType.FLOAT
|
||||
|
||||
func get_is_truthy():
|
||||
return value != 0.0
|
||||
|
||||
func _init():
|
||||
value = 0.0
|
||||
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func cast(new_type, metadata = null):
|
||||
if new_type == self.value_type:
|
||||
return self
|
||||
|
||||
if new_type == ValueType.BOOL:
|
||||
return BoolValue().new_with(false if value == 0 else true)
|
||||
|
||||
if new_type == ValueType.INT:
|
||||
return IntValue().new_with(int(value))
|
||||
|
||||
if new_type == ValueType.STRING:
|
||||
return StringValue().new_with(str(value)) # TODO: Check formating
|
||||
|
||||
InkUtils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata)
|
||||
return null
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "FloatValue" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "FloatValue"
|
||||
|
||||
static func new_with(val):
|
||||
var value = FloatValue().new()
|
||||
value._init_with(val)
|
||||
return value
|
59
addons/inkgd/runtime/values/int_value.gd
Normal file
59
addons/inkgd/runtime/values/int_value.gd
Normal file
|
@ -0,0 +1,59 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkValue
|
||||
|
||||
class_name InkIntValue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func get_value_type():
|
||||
return ValueType.INT
|
||||
|
||||
func get_is_truthy():
|
||||
return value != 0
|
||||
|
||||
func _init():
|
||||
value = 0
|
||||
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func cast(new_type, metadata = null):
|
||||
if new_type == self.value_type:
|
||||
return self
|
||||
|
||||
if new_type == ValueType.BOOL:
|
||||
return BoolValue().new_with(false if value == 0 else true)
|
||||
|
||||
if new_type == ValueType.FLOAT:
|
||||
return FloatValue().new_with(float(value))
|
||||
|
||||
if new_type == ValueType.STRING:
|
||||
return StringValue().new_with(str(value))
|
||||
|
||||
InkUtils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata)
|
||||
return null
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "IntValue" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "IntValue"
|
||||
|
||||
static func new_with(val):
|
||||
var value = IntValue().new()
|
||||
value._init_with(val)
|
||||
return value
|
90
addons/inkgd/runtime/values/list_value.gd
Normal file
90
addons/inkgd/runtime/values/list_value.gd
Normal file
|
@ -0,0 +1,90 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkValue
|
||||
|
||||
class_name InkListValue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func get_value_type():
|
||||
return ValueType.LIST
|
||||
|
||||
func get_is_truthy():
|
||||
return value.size() > 0
|
||||
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func cast(new_type, metadata = null):
|
||||
if new_type == ValueType.INT:
|
||||
var max_item = value.max_item
|
||||
if max_item.key.is_null:
|
||||
return IntValue().new_with(0)
|
||||
else:
|
||||
return IntValue().new_with(max_item.value)
|
||||
|
||||
elif new_type == ValueType.FLOAT:
|
||||
var max_item = value.max_item
|
||||
if max_item.key.is_null:
|
||||
return FloatValue().new_with(0.0)
|
||||
else:
|
||||
return FloatValue().new_with(float(max_item.value))
|
||||
|
||||
elif new_type == ValueType.STRING:
|
||||
var max_item = value.max_item
|
||||
if max_item.key.is_null:
|
||||
return StringValue().new_with("")
|
||||
else:
|
||||
return StringValue().new_with(max_item.key._to_string())
|
||||
|
||||
if new_type == self.value_type:
|
||||
return self
|
||||
|
||||
InkUtils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata)
|
||||
return null
|
||||
|
||||
func _init():
|
||||
value = InkList.new()
|
||||
|
||||
func _init_with_list(list):
|
||||
value = InkList.new_with_ink_list(list)
|
||||
|
||||
func _init_with_single_item(single_item, single_value):
|
||||
value = InkList.new_with_single_item(single_item, single_value)
|
||||
|
||||
# (InkObject, InkObject) -> void
|
||||
static func retain_list_origins_for_assignment(old_value, new_value):
|
||||
var old_list = InkUtils.as_or_null(old_value, "ListValue")
|
||||
var new_list = InkUtils.as_or_null(new_value, "ListValue")
|
||||
|
||||
if old_list && new_list && new_list.value.size() == 0:
|
||||
new_list.value.set_initial_origin_names(old_list.value.origin_names)
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "ListValue" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "ListValue"
|
||||
|
||||
static func new_with(list):
|
||||
var value = ListValue().new()
|
||||
value._init_with_list(list)
|
||||
return value
|
||||
|
||||
static func new_with_single_item(single_item, single_value):
|
||||
var value = ListValue().new()
|
||||
value._init_with_single_item(single_item, single_value)
|
||||
return value
|
82
addons/inkgd/runtime/values/string_value.gd
Normal file
82
addons/inkgd/runtime/values/string_value.gd
Normal file
|
@ -0,0 +1,82 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkValue
|
||||
|
||||
class_name InkStringValue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
func get_value_type():
|
||||
return ValueType.STRING
|
||||
|
||||
func get_is_truthy():
|
||||
return value.length() > 0
|
||||
|
||||
var is_newline: bool
|
||||
var is_inline_whitespace: bool
|
||||
var is_non_whitespace: bool:
|
||||
get:
|
||||
return !is_newline && !is_inline_whitespace
|
||||
|
||||
func _init():
|
||||
value = ""
|
||||
self._sanitize_value()
|
||||
|
||||
func _init_with(val):
|
||||
super._init_with(val)
|
||||
self._sanitize_value()
|
||||
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func cast(new_type, metadata = null):
|
||||
if new_type == self.value_type:
|
||||
return self
|
||||
|
||||
if new_type == ValueType.INT:
|
||||
if self.value.is_valid_int():
|
||||
return IntValue().new_with(int(self.value))
|
||||
else:
|
||||
return null
|
||||
|
||||
if new_type == ValueType.FLOAT:
|
||||
if self.value.is_valid_float():
|
||||
return FloatValue().new_with(float(self.value))
|
||||
else:
|
||||
return null
|
||||
|
||||
InkUtils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata)
|
||||
return null
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "StringValue" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "StringValue"
|
||||
|
||||
func _sanitize_value():
|
||||
is_newline = (self.value == "\n")
|
||||
is_inline_whitespace = true
|
||||
|
||||
for c in self.value:
|
||||
if c != ' ' && c != "\t":
|
||||
is_inline_whitespace = false
|
||||
break
|
||||
|
||||
static func new_with(val):
|
||||
var value = StringValue().new()
|
||||
value._init_with(val)
|
||||
return value
|
131
addons/inkgd/runtime/values/value.gd
Normal file
131
addons/inkgd/runtime/values/value.gd
Normal file
|
@ -0,0 +1,131 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkObject
|
||||
|
||||
# This is a merge of the original Value class and its Value<T> subclass.
|
||||
class_name InkValue
|
||||
|
||||
# ############################################################################ #
|
||||
# IMPORTS
|
||||
# ############################################################################ #
|
||||
|
||||
const ValueType = preload("res://addons/inkgd/runtime/values/value_type.gd").ValueType
|
||||
var InkList = load("res://addons/inkgd/runtime/lists/ink_list.gd")
|
||||
|
||||
# ############################################################################ #
|
||||
# STATIC REFERENCE
|
||||
# ############################################################################ #
|
||||
|
||||
# TODO: Remove
|
||||
#static func Utils():
|
||||
# return load("res://addons/inkgd/runtime/extra/InkUtils.gd")
|
||||
#
|
||||
#static func Value():
|
||||
# return load("res://addons/inkgd/runtime/values/value.gd")
|
||||
|
||||
static func BoolValue():
|
||||
return load("res://addons/inkgd/runtime/values/bool_value.gd")
|
||||
|
||||
static func IntValue():
|
||||
return load("res://addons/inkgd/runtime/values/int_value.gd")
|
||||
|
||||
static func FloatValue():
|
||||
return load("res://addons/inkgd/runtime/values/float_value.gd")
|
||||
|
||||
static func StringValue():
|
||||
return load("res://addons/inkgd/runtime/values/string_value.gd")
|
||||
|
||||
static func DivertTargetValue():
|
||||
return load("res://addons/inkgd/runtime/values/divert_target_value.gd")
|
||||
|
||||
static func VariablePointerValue():
|
||||
return load("res://addons/inkgd/runtime/values/variable_pointer_value.gd")
|
||||
|
||||
static func ListValue():
|
||||
return load("res://addons/inkgd/runtime/values/list_value.gd")
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var value # Variant
|
||||
|
||||
# ValueType
|
||||
var value_type: int: get = get_value_type
|
||||
func get_value_type() -> int:
|
||||
return -1
|
||||
|
||||
var is_truthy: bool: get = get_is_truthy
|
||||
func get_is_truthy() -> bool:
|
||||
return false
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (ValueType) -> ValueType
|
||||
func cast(new_type: int) -> InkValue:
|
||||
return null
|
||||
|
||||
var value_object: # Variant
|
||||
get: return value
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (Variant) -> Value
|
||||
func _init_with(val):
|
||||
value = val
|
||||
|
||||
# (Variant) -> Value
|
||||
static func create(val) -> InkValue:
|
||||
# Original code lost precision from double to float.
|
||||
# But it's not applicable here.
|
||||
|
||||
if val is bool:
|
||||
return BoolValue().new_with(val)
|
||||
if val is int:
|
||||
return IntValue().new_with(val)
|
||||
elif val is float:
|
||||
return FloatValue().new_with(val)
|
||||
elif val is String:
|
||||
return StringValue().new_with(val)
|
||||
elif InkUtils.is_ink_class(val, "InkPath"):
|
||||
return DivertTargetValue().new_with(val)
|
||||
elif InkUtils.is_ink_class(val, "InkList"):
|
||||
return ListValue().new_with(val)
|
||||
|
||||
return null
|
||||
|
||||
func copy() -> InkValue:
|
||||
return create(self.value_object)
|
||||
|
||||
# (Ink.ValueType) -> StoryException
|
||||
func bad_cast_exception_message(target_ink_class) -> String:
|
||||
return "Can't cast " + self.value_object + " from " + self.value_type + " to " + target_ink_class
|
||||
|
||||
# () -> String
|
||||
func _to_string() -> String:
|
||||
if value is int || value is float || value is String:
|
||||
return str(value)
|
||||
else:
|
||||
return value._to_string()
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type) -> bool:
|
||||
return type == "Value" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "Value"
|
||||
|
||||
static func new_with(val) -> InkValue:
|
||||
var value = InkValue.new()
|
||||
value._init_with(val)
|
||||
return value
|
23
addons/inkgd/runtime/values/value_type.gd
Normal file
23
addons/inkgd/runtime/values/value_type.gd
Normal file
|
@ -0,0 +1,23 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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.
|
||||
# ############################################################################ #
|
||||
|
||||
|
||||
enum ValueType {
|
||||
BOOL = -1,
|
||||
|
||||
INT,
|
||||
FLOAT,
|
||||
LIST,
|
||||
STRING,
|
||||
|
||||
DIVERT_TARGET,
|
||||
VARIABLE_POINTER
|
||||
}
|
69
addons/inkgd/runtime/values/variable_pointer_value.gd
Normal file
69
addons/inkgd/runtime/values/variable_pointer_value.gd
Normal file
|
@ -0,0 +1,69 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# 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 InkValue
|
||||
|
||||
class_name InkVariablePointerValue
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var variable_name : get = get_variable_name, set = set_variable_name # InkPath
|
||||
func get_variable_name():
|
||||
return value
|
||||
func set_variable_name(value):
|
||||
self.value = value
|
||||
|
||||
func get_value_type():
|
||||
return ValueType.VARIABLE_POINTER
|
||||
|
||||
func get_is_truthy():
|
||||
InkUtils.throw_exception("Shouldn't be checking the truthiness of a variable pointer")
|
||||
return false
|
||||
|
||||
var context_index = 0 # int
|
||||
|
||||
func _init_with_context(variable_name, context_index = -1):
|
||||
super._init_with(variable_name)
|
||||
self.context_index = context_index
|
||||
|
||||
func _init():
|
||||
value = null
|
||||
|
||||
# The method takes a `StoryErrorMetadata` object as a parameter that
|
||||
# doesn't exist in upstream. The metadat are used in case an 'exception'
|
||||
# is raised. For more information, see story.gd.
|
||||
func cast(new_type, metadata = null):
|
||||
if new_type == self.value_type:
|
||||
return self
|
||||
|
||||
InkUtils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata)
|
||||
return null
|
||||
|
||||
func _to_string() -> String:
|
||||
return "VariablePointerValue(" + self.variable_name + ")"
|
||||
|
||||
func copy():
|
||||
return VariablePointerValue().new_with_context(self.variable_name, context_index)
|
||||
|
||||
# ######################################################################## #
|
||||
# GDScript extra methods
|
||||
# ######################################################################## #
|
||||
|
||||
func is_ink_class(type):
|
||||
return type == "VariablePointerValue" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class():
|
||||
return "VariablePointerValue"
|
||||
|
||||
static func new_with_context(variable_name, context_index = -1):
|
||||
var value = VariablePointerValue().new()
|
||||
value._init_with_context(variable_name, context_index)
|
||||
return value
|
373
addons/inkgd/runtime/variables_state.gd
Normal file
373
addons/inkgd/runtime/variables_state.gd
Normal file
|
@ -0,0 +1,373 @@
|
|||
# warning-ignore-all:shadowed_variable
|
||||
# warning-ignore-all:unused_class_variable
|
||||
# warning-ignore-all:return_value_discarded
|
||||
# ############################################################################ #
|
||||
# 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 InkVariablesState
|
||||
|
||||
# ############################################################################ #
|
||||
# Imports
|
||||
# ############################################################################ #
|
||||
|
||||
var InkTryGetResult := preload("res://addons/inkgd/runtime/extra/try_get_result.gd") as GDScript
|
||||
var InkStringSet := preload("res://addons/inkgd/runtime/extra/string_set.gd") as GDScript
|
||||
|
||||
var InkValue := load("res://addons/inkgd/runtime/values/value.gd") as GDScript
|
||||
var InkListValue := load("res://addons/inkgd/runtime/values/list_value.gd") as GDScript
|
||||
var InkVariablePointerValue := load("res://addons/inkgd/runtime/values/variable_pointer_value.gd") as GDScript
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (String, InkObject)
|
||||
signal variable_changed(variable_name, new_value)
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
var patch: InkStatePatch # StatePatch
|
||||
|
||||
var batch_observing_variable_changes: bool:
|
||||
get:
|
||||
return _batch_observing_variable_changes
|
||||
|
||||
set(value):
|
||||
_batch_observing_variable_changes = value
|
||||
if value:
|
||||
_changed_variables_for_batch_obs = InkStringSet.new()
|
||||
else:
|
||||
if _changed_variables_for_batch_obs != null:
|
||||
for variable_name in _changed_variables_for_batch_obs.enumerate():
|
||||
var current_value = _global_variables[variable_name]
|
||||
emit_signal("variable_changed", variable_name, current_value)
|
||||
|
||||
_changed_variables_for_batch_obs = null
|
||||
|
||||
var _batch_observing_variable_changes: bool = false
|
||||
|
||||
var callstack: InkCallStack: get = get_callstack, set = set_callstack
|
||||
func get_callstack() -> InkCallStack:
|
||||
return _callstack
|
||||
|
||||
func set_callstack(value: InkCallStack):
|
||||
_callstack = value
|
||||
|
||||
# (String) -> Variant
|
||||
func get_variable(variable_name: String):
|
||||
if self.patch != null:
|
||||
var global: InkTryGetResult = patch.try_get_global(variable_name)
|
||||
if global.exists:
|
||||
return global.result.value_object
|
||||
|
||||
if _global_variables.has(variable_name):
|
||||
return _global_variables[variable_name].value_object
|
||||
elif _default_global_variables.has(variable_name):
|
||||
return _default_global_variables[variable_name].value_object
|
||||
else:
|
||||
return null
|
||||
|
||||
# (String, Variant) -> void
|
||||
func set_variable(variable_name: String, value) -> void:
|
||||
if !_default_global_variables.has(variable_name):
|
||||
InkUtils.throw_story_exception(
|
||||
"Cannot assign to a variable (%s) that hasn't been declared in the story" \
|
||||
% variable_name
|
||||
)
|
||||
return
|
||||
|
||||
var val: InkValue = InkValue.create(value)
|
||||
if val == null:
|
||||
if value == null:
|
||||
InkUtils.throw_exception("Cannot pass null to VariableState")
|
||||
else:
|
||||
InkUtils.throw_exception("Invalid value passed to VariableState: %s" % str(value))
|
||||
return
|
||||
|
||||
set_global(variable_name, val)
|
||||
|
||||
func enumerate() -> Array:
|
||||
return _global_variables.keys()
|
||||
|
||||
func _init(callstack: InkCallStack, list_defs_origin: InkListDefinitionsOrigin, ink_runtime = null):
|
||||
find_static_objects(ink_runtime)
|
||||
|
||||
_global_variables = {}
|
||||
_callstack = callstack
|
||||
_list_defs_origin = list_defs_origin
|
||||
|
||||
# () -> void
|
||||
func apply_patch() -> void:
|
||||
for named_var_key in self.patch.globals:
|
||||
_global_variables[named_var_key] = self.patch.globals[named_var_key]
|
||||
|
||||
if _changed_variables_for_batch_obs != null:
|
||||
for name in self.patch.changed_variables.enumerate():
|
||||
_changed_variables_for_batch_obs.append(name)
|
||||
|
||||
patch = null
|
||||
|
||||
# (Dictionary<string, Variant>) -> void
|
||||
func set_json_token(jtoken: Dictionary) -> void:
|
||||
_global_variables.clear()
|
||||
|
||||
for var_val_key in _default_global_variables:
|
||||
if jtoken.has(var_val_key):
|
||||
var loaded_token = jtoken[var_val_key]
|
||||
_global_variables[var_val_key] = self.StaticJSON.jtoken_to_runtime_object(loaded_token)
|
||||
else:
|
||||
_global_variables[var_val_key] = _default_global_variables[var_val_key]
|
||||
|
||||
func write_json(writer: InkSimpleJSON.Writer) -> void:
|
||||
writer.write_object_start()
|
||||
for key in _global_variables:
|
||||
var name: String = key
|
||||
var val: InkObject = _global_variables[key]
|
||||
|
||||
if self._ink_runtime.dont_save_default_values:
|
||||
if self._default_global_variables.has(name):
|
||||
if runtime_objects_equal(val, self._default_global_variables[name]):
|
||||
continue
|
||||
|
||||
writer.write_property_start(name)
|
||||
self.StaticJSON.write_runtime_object(writer, val)
|
||||
writer.write_property_end()
|
||||
writer.write_object_end()
|
||||
|
||||
func runtime_objects_equal(obj1: InkObject, obj2: InkObject) -> bool:
|
||||
if !InkUtils.are_of_same_type(obj1, obj2):
|
||||
return false
|
||||
|
||||
var bool_val: InkBoolValue = InkUtils.as_or_null(obj1, "BoolValue")
|
||||
if bool_val != null:
|
||||
return bool_val.value == InkUtils.cast(obj2, "BoolValue").value
|
||||
|
||||
var int_val: InkIntValue = InkUtils.as_or_null(obj1, "IntValue")
|
||||
if int_val != null:
|
||||
return int_val.value == InkUtils.cast(obj2, "IntValue").value
|
||||
|
||||
var float_val: InkFloatValue = InkUtils.as_or_null(obj1, "FloatValue")
|
||||
if float_val != null:
|
||||
return float_val.value == InkUtils.cast(obj2, "FloatValue").value
|
||||
|
||||
var val1: InkValue = InkUtils.as_or_null(obj1, "Value")
|
||||
var val2: InkValue = InkUtils.as_or_null(obj2, "Value")
|
||||
|
||||
if val1 != null:
|
||||
if val1.value_object is Object && val2.value_object is Object:
|
||||
return val1.value_object.equals(val2.value_object)
|
||||
else:
|
||||
return val1.value_object == val2.value_object
|
||||
|
||||
InkUtils.throw_exception(
|
||||
"FastRoughDefinitelyEquals: Unsupported runtime object type: %s" \
|
||||
% obj1.get_ink_class()
|
||||
)
|
||||
|
||||
return false
|
||||
|
||||
func get_variable_with_name(name: String, context_index = -1) -> InkObject:
|
||||
var var_value: InkObject = get_raw_variable_with_name(name, context_index)
|
||||
|
||||
var var_pointer: InkVariablePointerValue = InkUtils.as_or_null(var_value, "VariablePointerValue")
|
||||
if var_pointer:
|
||||
var_value = value_at_variable_pointer(var_pointer)
|
||||
|
||||
return var_value
|
||||
|
||||
# (String) -> { exists: bool, result: InkObject }
|
||||
func try_get_default_variable_value(name: String) -> InkTryGetResult:
|
||||
if _default_global_variables.has(name):
|
||||
return InkTryGetResult.new(true, _default_global_variables[name])
|
||||
else:
|
||||
return InkTryGetResult.new(false, null)
|
||||
|
||||
func global_variable_exists_with_name(name: String) -> bool:
|
||||
return (
|
||||
_global_variables.has(name) ||
|
||||
_default_global_variables != null && _default_global_variables.has(name)
|
||||
)
|
||||
|
||||
func get_raw_variable_with_name(name: String, context_index: int) -> InkObject:
|
||||
var var_value: InkObject = null
|
||||
|
||||
if context_index == 0 || context_index == -1:
|
||||
if self.patch != null:
|
||||
var try_result: InkTryGetResult = self.patch.try_get_global(name)
|
||||
if try_result.exists: return try_result.result
|
||||
|
||||
if _global_variables.has(name):
|
||||
return _global_variables[name]
|
||||
|
||||
if self._default_global_variables != null:
|
||||
if self._default_global_variables.has(name):
|
||||
return self._default_global_variables[name]
|
||||
|
||||
var list_item_value: InkListValue = _list_defs_origin.find_single_item_list_with_name(name)
|
||||
|
||||
if list_item_value:
|
||||
return list_item_value
|
||||
|
||||
var_value = _callstack.get_temporary_variable_with_name(name, context_index)
|
||||
|
||||
return var_value
|
||||
|
||||
# (InkVariablePointerValue) -> InkObject
|
||||
func value_at_variable_pointer(pointer: InkVariablePointerValue) -> InkObject:
|
||||
return get_variable_with_name(pointer.variable_name, pointer.context_index)
|
||||
|
||||
# (InkVariableAssignment, InkObject) -> void
|
||||
func assign(var_ass: InkVariableAssignment, value: InkObject) -> void:
|
||||
var name: String = var_ass.variable_name
|
||||
var context_index: int = -1
|
||||
|
||||
var set_global: bool = false
|
||||
if var_ass.is_new_declaration:
|
||||
set_global = var_ass.is_global
|
||||
else:
|
||||
set_global = global_variable_exists_with_name(name)
|
||||
|
||||
if var_ass.is_new_declaration:
|
||||
var var_pointer: InkVariablePointerValue = InkUtils.as_or_null(value, "VariablePointerValue")
|
||||
if var_pointer:
|
||||
var fully_resolved_variable_pointer: InkObject = resolve_variable_pointer(var_pointer)
|
||||
value = fully_resolved_variable_pointer
|
||||
else:
|
||||
var existing_pointer: InkVariablePointerValue = null # VariablePointerValue
|
||||
var first_time: bool = true
|
||||
while existing_pointer || first_time:
|
||||
first_time = false
|
||||
existing_pointer = InkUtils.as_or_null(
|
||||
get_raw_variable_with_name(name, context_index),
|
||||
"VariablePointerValue"
|
||||
)
|
||||
if existing_pointer:
|
||||
name = existing_pointer.variable_name
|
||||
context_index = existing_pointer.context_index
|
||||
set_global = (context_index == 0)
|
||||
|
||||
if set_global:
|
||||
set_global(name, value)
|
||||
else:
|
||||
_callstack.set_temporary_variable(name, value, var_ass.is_new_declaration, context_index)
|
||||
|
||||
# () -> void
|
||||
func snapshot_default_globals():
|
||||
_default_global_variables = _global_variables.duplicate()
|
||||
|
||||
# (InkObject, InkObject) -> void
|
||||
func retain_list_origins_for_assignment(old_value, new_value) -> void:
|
||||
var old_list: InkListValue = InkUtils.as_or_null(old_value, "ListValue")
|
||||
var new_list: InkListValue = InkUtils.as_or_null(new_value, "ListValue")
|
||||
|
||||
if old_list && new_list && new_list.value.size() == 0:
|
||||
new_list.value.set_initial_origin_names(old_list.value.origin_names)
|
||||
|
||||
# (String, InkObject) -> void
|
||||
func set_global(variable_name: String, value: InkObject) -> void:
|
||||
var old_value = null # InkObject
|
||||
|
||||
# Slightly different structure from upstream, since we can't use
|
||||
# try_get_global in the conditional.
|
||||
if patch != null:
|
||||
var patch_value: InkTryGetResult = patch.try_get_global(variable_name)
|
||||
if patch_value.exists:
|
||||
old_value = patch_value.result
|
||||
|
||||
if old_value == null:
|
||||
if self._global_variables.has(variable_name):
|
||||
old_value = self._global_variables[variable_name]
|
||||
|
||||
InkListValue.retain_list_origins_for_assignment(old_value, value)
|
||||
|
||||
if patch != null:
|
||||
self.patch.set_global(variable_name, value)
|
||||
else:
|
||||
self._global_variables[variable_name] = value
|
||||
|
||||
if !value.equals(old_value):
|
||||
if _batch_observing_variable_changes:
|
||||
if patch != null:
|
||||
patch.add_changed_variable(variable_name)
|
||||
elif self._changed_variables_for_batch_obs != null:
|
||||
_changed_variables_for_batch_obs.append(variable_name)
|
||||
else:
|
||||
emit_signal("variable_changed", variable_name, value)
|
||||
|
||||
# (VariablePointerValue) -> VariablePointerValue
|
||||
func resolve_variable_pointer(var_pointer: InkVariablePointerValue) -> InkVariablePointerValue:
|
||||
var context_index: int = var_pointer.context_index
|
||||
|
||||
if context_index == -1:
|
||||
context_index = get_context_index_of_variable_named(var_pointer.variable_name)
|
||||
|
||||
var value_of_variable_pointed_to = get_raw_variable_with_name(
|
||||
var_pointer.variable_name, context_index
|
||||
)
|
||||
|
||||
var double_redirection_pointer: InkVariablePointerValue = InkUtils.as_or_null(
|
||||
value_of_variable_pointed_to, "VariablePointerValue"
|
||||
)
|
||||
|
||||
if double_redirection_pointer:
|
||||
return double_redirection_pointer
|
||||
else:
|
||||
return InkVariablePointerValue.new_with_context(var_pointer.variable_name, context_index)
|
||||
|
||||
# ############################################################################ #
|
||||
|
||||
# (String) -> int
|
||||
func get_context_index_of_variable_named(var_name):
|
||||
if global_variable_exists_with_name(var_name):
|
||||
return 0
|
||||
|
||||
return _callstack.current_element_index
|
||||
|
||||
# Dictionary<String, InkObject>
|
||||
var _global_variables: Dictionary
|
||||
var _default_global_variables = null # Dictionary<String, InkObject>
|
||||
|
||||
var _callstack: InkCallStack
|
||||
var _changed_variables_for_batch_obs: InkStringSet = null
|
||||
var _list_defs_origin: InkListDefinitionsOrigin
|
||||
|
||||
# ############################################################################ #
|
||||
# GDScript extra methods
|
||||
# ############################################################################ #
|
||||
|
||||
func is_ink_class(type: String) -> bool:
|
||||
return type == "VariableState" || super.is_ink_class(type)
|
||||
|
||||
func get_ink_class() -> String:
|
||||
return "VariableState"
|
||||
|
||||
# ############################################################################ #
|
||||
# Static Properties
|
||||
# ############################################################################ #
|
||||
|
||||
var StaticJSON: InkStaticJSON:
|
||||
get: return self._ink_runtime.json
|
||||
|
||||
var _ink_runtime:
|
||||
get: return _weak_ink_runtime.get_ref()
|
||||
var _weak_ink_runtime: WeakRef
|
||||
|
||||
func find_static_objects(ink_runtime = null):
|
||||
if ink_runtime != null:
|
||||
_weak_ink_runtime = weakref(ink_runtime)
|
||||
return
|
||||
|
||||
var runtime = Engine.get_main_loop().root.get_node("__InkRuntime")
|
||||
|
||||
InkUtils.__assert__(
|
||||
runtime != null,
|
||||
"[InkVariableStates] Could not retrieve 'InkRuntime' singleton from the scene tree."
|
||||
)
|
||||
|
||||
_weak_ink_runtime = weakref(runtime)
|
Loading…
Add table
Add a link
Reference in a new issue