333 lines
8.7 KiB
GDScript
333 lines
8.7 KiB
GDScript
# 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"
|