CORE/src/misc.gd

304 lines
12 KiB
GDScript3
Raw Normal View History

# CORE FRAMEWORK SOURCE FILE
# Copyright (c) 2024 The StarOpenSource Project & Contributors
2024-03-03 18:53:09 +01:00
# Licensed under the GNU Affero General Public License v3
#
# This program is free software: you can redistribute it and/or modify
2024-03-03 18:53:09 +01:00
# 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
2024-03-03 18:53:09 +01:00
# GNU Affero General Public License for more details.
#
2024-03-03 18:53:09 +01:00
# 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/>.
2024-04-08 20:14:38 +02:00
## Contains various utility functions.
##
2024-04-08 20:14:38 +02:00
## This module contains many methods that don't fit into any other module
## and generally make your life as a developer easier.
2024-02-04 21:36:30 +01:00
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
2024-04-08 20:14:38 +02:00
# +++ 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 bytes/1048576
return bytes/float(1048576)
2024-04-08 20:14:38 +02:00
## 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.
2024-02-10 18:48:37 +01:00
func mib2byte(mib: float, flatten: bool = true) -> float:
if flatten: return int(mib*1048576)
return mib*1048576
2024-04-08 20:14:38 +02:00
## 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:
2024-02-10 18:48:37 +01:00
if flatten: return int(mib/1024)
return mib/1024
2024-04-08 20:14:38 +02:00
## 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.
2024-02-10 18:48:37 +01:00
func gib2mib(gib: float, flatten: bool = true) -> float:
if flatten: return int(gib*1024)
return gib*1024
2024-03-24 15:42:38 +01:00
2024-04-08 20:14:38 +02:00
# +++ 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]"))
2024-04-08 20:14:38 +02:00
## [/codeblock]
2024-03-24 15:42:38 +01:00
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:
2024-04-08 21:30:24 +02:00
loggeri.warn("Unable to format a string with a size of 0")
2024-03-24 15:42:38 +01:00
return ""
elif array.size() == 1:
2024-04-08 21:30:24 +02:00
loggeri.warn("Unable to format a string with a size of 1")
2024-03-24 15:42:38 +01:00
return array[0]
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
2024-03-24 15:56:45 +01:00
2024-04-08 20:14:38 +02:00
# +++ 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.
2024-03-24 15:56:45 +01:00
func array_to_stringarray(array: Array) -> Array[String]:
var output: Array[String] = []
for item in array:
if typeof(item) != TYPE_STRING:
logger.error("Cannot convert Array to Array[String]: Item '" + str(item) + "' is not of type String")
return []
output.append(item)
return output
2024-04-08 20:14:38 +02:00
## 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
2024-03-25 17:43:13 +01:00
2024-04-08 20:14:38 +02:00
# +++ 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)
2024-04-08 20:14:38 +02:00
## [/codeblock]
2024-03-25 21:10:59 +01:00
func get_center(parent_size: Vector2, child_size: Vector2) -> Vector2:
2024-03-25 17:43:13 +01:00
return Vector2(parent_size.x/2-child_size.x/2, parent_size.y/2-child_size.y/2)
2024-04-08 20:14:38 +02:00
## 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) -> 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:
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 calls shorter
func _sa(value) -> String:
return stringify_variables("%var%", { "var": value }, true)
2024-04-08 21:42:03 +02:00
## Moved to [method Core.quit_safely].
## @deprecated
func quit_safely(exitcode: int = 0) -> void: await core.quit_safely(exitcode)