303 lines
12 KiB
GDScript
303 lines
12 KiB
GDScript
# CORE FRAMEWORK SOURCE FILE
|
|
# Copyright (c) 2024 The StarOpenSource Project & Contributors
|
|
# Licensed under the GNU Affero General Public License v3
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
## Contains various utility functions.
|
|
##
|
|
## This module contains many methods that don't fit into any other module
|
|
## and generally make your life as a developer easier.
|
|
extends CoreBaseModule
|
|
|
|
# Configuration
|
|
var config_stringify_show_type: bool
|
|
var config_stringify_color_range8: bool
|
|
var config_stringify_array: bool
|
|
var config_stringify_dictionary: bool
|
|
|
|
# +++ module +++
|
|
func _pull_config() -> void:
|
|
config_stringify_show_type = core.config.misc_stringify_show_type
|
|
config_stringify_color_range8 = core.config.misc_stringify_color_range8
|
|
config_stringify_array = core.config.misc_stringify_array
|
|
config_stringify_dictionary = core.config.misc_stringify_dictionary
|
|
|
|
# +++ data type conversion +++
|
|
## Converts a number of bytes into mebibytes.[br]
|
|
## [br]
|
|
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
|
|
@warning_ignore("integer_division")
|
|
func byte2mib(bytes: int, flatten: bool = true) -> float:
|
|
if flatten: return float(int(float(bytes)/1048576.0))
|
|
return float(bytes)/1048576.0
|
|
|
|
## Converts a number of mebibytes into bytes.[br]
|
|
## [br]
|
|
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
|
|
func mib2byte(mib: float, flatten: bool = true) -> float:
|
|
if flatten: return float(int(mib*1048576.0))
|
|
return mib*1048576.0
|
|
|
|
## Converts a number of mebibytes into gibibytes.[br]
|
|
## [br]
|
|
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
|
|
func mib2gib(mib: float, flatten: bool = true) -> float:
|
|
if flatten: return float(int(mib/1024.0))
|
|
return mib/1024.0
|
|
|
|
## Converts a number of gebibytes into mebibytes.[br]
|
|
## [br]
|
|
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
|
|
func gib2mib(gib: float, flatten: bool = true) -> float:
|
|
if flatten: return float(int(gib*1024.0))
|
|
return gib*1024.0
|
|
|
|
# +++ type formatting +++
|
|
## Converts a string array into a normal, nicely formatted string.[br]
|
|
## [br]
|
|
## With [code]item_before[/code] and [code]item_after[/code] you can customize the lists apperance. Here's an example on how to format every item bold:
|
|
## [codeblock]
|
|
## extends Node
|
|
##
|
|
## var core: Core = get_node("/root/CORE")
|
|
## var misc: CoreBaseModule = core.misc
|
|
## var logger: CoreLoggerInstance = core.logger.get_instance("some/script.gd")
|
|
##
|
|
## func _ready() -> void:
|
|
## var array: Array[String] = ["Apples", "Bananas", "Oranges"]
|
|
##
|
|
## logger.info(misc.format_stringarray(array))
|
|
## logger.info(misc.format_stringarray(array, "[b]", "[/b]"))
|
|
## [/codeblock]
|
|
func format_stringarray(array: Array[String], item_before: String = "", item_after: String = "", separator_list: String = ", ", separator_final: String = " & ") -> String:
|
|
var output: String = ""
|
|
|
|
if array.size() == 0:
|
|
logger.warn("Unable to format a string with a size of 0")
|
|
return ""
|
|
elif array.size() == 1:
|
|
logger.warn("Unable to format a string with a size of 1")
|
|
return item_before + array[0] + item_after
|
|
|
|
for item in array:
|
|
if output == "": output = item_before + item + item_after
|
|
else: output = output.replace("If you somehow see this text report this at https://git.staropensource.de/StarOpenSource/CORE/issues, thank you!", separator_list) + "If you somehow see this text report this at https://git.staropensource.de/StarOpenSource/CORE/issues, thank you!" + item_before + item + item_after
|
|
output = output.replace("If you somehow see this text report this at https://git.staropensource.de/StarOpenSource/CORE/issues, thank you!", separator_final)
|
|
|
|
return output
|
|
|
|
# +++ array conversion +++
|
|
## Converts an array into a string array.[br]
|
|
## [br]
|
|
## If an item is found that is not of type [code]String[/code], an empty array is returned.
|
|
func array_to_stringarray(array: Array) -> Array[String]:
|
|
var output: Array[String] = []
|
|
|
|
for item in array:
|
|
if typeof(item) != TYPE_STRING:
|
|
logger.error(core.stringify_string("Cannot convert Array to Array[String]: Item %item% is not of type String", { "item": item }))
|
|
return []
|
|
output.append(item)
|
|
|
|
return output
|
|
|
|
## Converts a string array into an array.
|
|
func stringarray_to_array(array: Array[String]) -> Array:
|
|
var output: Array = []
|
|
|
|
for item in array:
|
|
output.append(item)
|
|
|
|
return output
|
|
|
|
# +++ etc +++
|
|
## Calculates the center of the child in the area of the parent.[br]
|
|
## [br]
|
|
## Example:
|
|
## [codeblock]
|
|
## extends Control
|
|
##
|
|
## var core: Core = get_node("/root/CORE")
|
|
## var misc: CoreBaseModule = core.misc
|
|
##
|
|
## func _ready() -> void:
|
|
## position = misc.center_object(get_parent().size, size)
|
|
## [/codeblock]
|
|
func get_center(parent_size: Vector2, child_size: Vector2) -> Vector2:
|
|
return Vector2(parent_size.x/2-child_size.x/2, parent_size.y/2-child_size.y/2)
|
|
|
|
## Makes variables as look correct inside strings.[br]
|
|
## Short examples:[br]
|
|
## [code]true[/code] -> [code]'true'[/code][br]
|
|
## [code]Vector2(69.064, PI)[/code] -> [code]'x=69.064 y=3.14159265358979'[/code][br]
|
|
## [code]"This is a test string"[/code] -> [code]'"This is a test string"'[/code][br]
|
|
## Full example:[br]
|
|
## [codeblock]
|
|
## Code:
|
|
## logger.diag(stringify_variables("Triggered %trigger% (pos=%position%, successful=%success%)", { "trigger": "shoot", "position": Vector2(5156.149, 581.69), "success": true }))
|
|
##
|
|
## Output:
|
|
## [16:44:35] [DIAG Test.gd] Triggered '"shoot"' (pos='x=5156.149 y=581.69', successful='true')
|
|
## [/codeblock]
|
|
func stringify_variables(template: String, variables: Dictionary, no_quotes: bool = false, force_no_type: bool = false) -> String:
|
|
# To decrease allocations
|
|
var value
|
|
var type: String = ""
|
|
var replacement: String = ""
|
|
for placeholder in variables:
|
|
# Check key type
|
|
if typeof(placeholder) != TYPE_STRING:
|
|
logger.error("Invalid placeholder type '\"" + type_string(typeof(placeholder)) + "\", skipping")
|
|
continue
|
|
|
|
# Check for correct type
|
|
value = variables[placeholder]
|
|
|
|
match(typeof(value)):
|
|
# Primitives
|
|
Variant.Type.TYPE_NIL:
|
|
replacement = "null"
|
|
type = ""
|
|
Variant.Type.TYPE_BOOL:
|
|
replacement = str(value)
|
|
type = "bool"
|
|
Variant.Type.TYPE_INT:
|
|
replacement = str(value)
|
|
type = "int"
|
|
Variant.Type.TYPE_FLOAT:
|
|
replacement = str(value)
|
|
type = "float"
|
|
Variant.Type.TYPE_STRING:
|
|
replacement = "\"" + value + "\""
|
|
type = "String"
|
|
Variant.Type.TYPE_STRING_NAME:
|
|
replacement = "\"" + value + "\""
|
|
type = "StringName"
|
|
# Non-primitives
|
|
Variant.Type.TYPE_OBJECT:
|
|
replacement = str(value)
|
|
type = "Object"
|
|
Variant.Type.TYPE_COLOR:
|
|
if config_stringify_color_range8: replacement = "r=" + _sa(value.r8) + " g=" + _sa(value.g8) + " b=" + _sa(value.b8) + " a=" + _sa(value.a8)
|
|
else: replacement = "r=" + _sa(value.r) + " g=" + _sa(value.g) + " b=" + _sa(value.b) + " a=" + _sa(value.a)
|
|
type = "Color"
|
|
Variant.Type.TYPE_RID:
|
|
replacement = "id=" + _sa(value.get_id()) + " valid=" + _sa(value.is_valid())
|
|
type = "RID"
|
|
Variant.Type.TYPE_ARRAY:
|
|
if config_stringify_array:
|
|
if value.size() == 0:
|
|
replacement = "[]"
|
|
else:
|
|
replacement = "[ "
|
|
for item in value:
|
|
if replacement == "[ ": replacement += _sa(item)
|
|
else: replacement += ", " + _sa(item)
|
|
replacement += " ]"
|
|
else: replacement = str(value)
|
|
if value.get_typed_builtin() != TYPE_NIL:
|
|
type = "Array[" + type_string(typeof(value.get_typed_builtin())) + "]"
|
|
else:
|
|
type = "Array"
|
|
Variant.Type.TYPE_DICTIONARY:
|
|
if config_stringify_dictionary:
|
|
if value.size() == 0: replacement = "{}"
|
|
else:
|
|
replacement = "{ "
|
|
for key in value:
|
|
if replacement == "{ ": replacement += _sa(key) + ": " + _sa(value[key])
|
|
else: replacement += ", " + _sa(key)
|
|
replacement += " }"
|
|
else: replacement = str(value)
|
|
type = "Dictionary"
|
|
# TODO: Packed Arrays
|
|
# Nodes & scripting
|
|
Variant.Type.TYPE_NODE_PATH:
|
|
replacement = str(value)
|
|
type = "NodePath"
|
|
Variant.Type.TYPE_CALLABLE:
|
|
replacement = "valid=" + _sa(value.is_valid()) + " standard=" + _sa(value.is_standard()) + " object=" + _sa(value.get_object() ) + " method=" + value.get_method() + " args=" + _sa(value.get_bound_arguments())
|
|
type = "Callable"
|
|
Variant.Type.TYPE_SIGNAL:
|
|
replacement = "name=" + _sa(value.get_name()) + " object=" + _sa(value.get_object())
|
|
type = "Signal"
|
|
# 2D
|
|
Variant.Type.TYPE_VECTOR2:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y)
|
|
type = "Vector2"
|
|
Variant.Type.TYPE_VECTOR2I:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y)
|
|
type = "Vector2i"
|
|
Variant.Type.TYPE_RECT2:
|
|
replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end)
|
|
type = "Rect2"
|
|
Variant.Type.TYPE_RECT2I:
|
|
replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end)
|
|
type = "Rect2i"
|
|
Variant.Type.TYPE_TRANSFORM2D:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " origin=" + _sa(value.origin)
|
|
type = "Transform2D"
|
|
# 3D
|
|
Variant.Type.TYPE_VECTOR3:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z)
|
|
type = "Vector3"
|
|
Variant.Type.TYPE_VECTOR3I:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z)
|
|
type = "Vector3i"
|
|
Variant.Type.TYPE_PLANE:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " d=" + _sa(value.d) + " normal=" + _sa(value.normal)
|
|
type = "Plane"
|
|
Variant.Type.TYPE_QUATERNION:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
|
type = "Quaternion"
|
|
Variant.Type.TYPE_AABB:
|
|
replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end)
|
|
type = "AABB"
|
|
Variant.Type.TYPE_TRANSFORM3D:
|
|
replacement = "basis=" + _sa(value.basis) + " origin=" + _sa(value.origin)
|
|
type = "Transform3D"
|
|
Variant.Type.TYPE_BASIS:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z)
|
|
type = "Basis"
|
|
Variant.Type.TYPE_PROJECTION:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
|
type = "Projection"
|
|
# 4D
|
|
Variant.Type.TYPE_VECTOR4:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
|
type = "Vector4"
|
|
Variant.Type.TYPE_VECTOR4I:
|
|
replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w)
|
|
type = "Vector4i"
|
|
_:
|
|
replacement = str(value)
|
|
type = "unknown"
|
|
|
|
# Replace
|
|
if config_stringify_show_type and !force_no_type:
|
|
if type != "": type = "(" + type.to_lower() + ") "
|
|
else:
|
|
type = ""
|
|
var quote: String = "'" if !no_quotes else ""
|
|
template = template.replace("%" + placeholder + "%", quote + type + replacement + quote)
|
|
return template
|
|
|
|
# Makes internal calls shorter
|
|
func _sa(value) -> String:
|
|
return stringify_variables("%var%", { "var": value }, true)
|
|
|
|
## Moved to [method Core.quit_safely].
|
|
## @deprecated
|
|
func quit_safely(exitcode: int = 0) -> void: await core.quit_safely(exitcode)
|