Change project organization

This commit is contained in:
2026-03-19 22:18:34 +01:00
parent 278b073501
commit 3c8709c834
39 changed files with 74 additions and 75 deletions

240
core/big_number.gd Normal file
View File

@@ -0,0 +1,240 @@
class_name BigNumber
extends RefCounted
## The core value representation.
## For example, 1.5e24 is represented as mantissa = 1.5, exponent = 24.
var mantissa: float
var exponent: int
## Pre-calculate this to avoid dividing logs constantly
const LOG10_E: float = 0.4342944819032518
func _init(m: float = 0.0, e: int = 0) -> void:
mantissa = m
exponent = e
_normalize()
## Adjusts the internal representation so the mantissa is always between 1.0 and 9.99...
## or exactly 0.0.
func _normalize() -> void:
if mantissa == 0.0:
exponent = 0
return
var is_negative: bool = mantissa < 0.0
var abs_m: float = abs(mantissa)
if abs_m >= 10.0 or abs_m < 1.0:
# Calculate how many powers of 10 we need to shift
# Godot's log() is base 'e', so we multiply by log10(e) to get log10(x)
var exp_diff: int = floori(log(abs_m) * LOG10_E)
abs_m /= pow(10.0, float(exp_diff))
exponent += exp_diff
mantissa = abs_m if not is_negative else -abs_m
# ==========================================
# MATH OPERATIONS
# ==========================================
func add(other: BigNumber) -> BigNumber:
if mantissa == 0: return BigNumber.new(other.mantissa, other.exponent)
if other.mantissa == 0: return BigNumber.new(mantissa, exponent)
var exp_diff: int = exponent - other.exponent
# If the difference in magnitude is massive, the smaller number is insignificant
# (15 is a safe threshold for 64-bit float precision).
if exp_diff >= 15:
return BigNumber.new(mantissa, exponent)
elif exp_diff <= -15:
return BigNumber.new(other.mantissa, other.exponent)
var new_m: float = mantissa
var new_e: int = exponent
# Scale the smaller number down to match the larger number's exponent
if exp_diff > 0:
new_m += other.mantissa / pow(10.0, float(exp_diff))
elif exp_diff < 0:
new_m = (mantissa / pow(10.0, float(-exp_diff))) + other.mantissa
new_e = other.exponent
else:
new_m += other.mantissa
return BigNumber.new(new_m, new_e)
## High-performance addition for the _process() loop.
## Modifies THIS instance instead of creating a new RefCounted object.
func add_in_place(other: BigNumber) -> void:
if other.mantissa == 0.0: return
if mantissa == 0.0:
mantissa = other.mantissa
exponent = other.exponent
return
var exp_diff: int = exponent - other.exponent
if exp_diff >= 15:
return # Other number is too small to matter
elif exp_diff <= -15:
# This number is effectively replaced by the larger other number
mantissa = other.mantissa
exponent = other.exponent
return
if exp_diff > 0:
mantissa += other.mantissa / pow(10.0, float(exp_diff))
elif exp_diff < 0:
mantissa = (mantissa / pow(10.0, float(-exp_diff))) + other.mantissa
exponent = other.exponent
else:
mantissa += other.mantissa
# Prevent floating-point drift near zero
if abs(mantissa) < 0.0000000001:
mantissa = 0.0
exponent = 0
else:
_normalize()
func subtract(other: BigNumber) -> BigNumber:
# Subtraction is just adding a negative number
var negative_other = BigNumber.new(-other.mantissa, other.exponent)
return add(negative_other)
func multiply(other: BigNumber) -> BigNumber:
var new_m: float = mantissa * other.mantissa
var new_e: int = exponent + other.exponent
return BigNumber.new(new_m, new_e)
func divide(other: BigNumber) -> BigNumber:
if other.mantissa == 0.0:
push_error("BigNumber: Division by zero!")
return BigNumber.new(0.0, 0)
var new_m: float = mantissa / other.mantissa
var new_e: int = exponent - other.exponent
return BigNumber.new(new_m, new_e)
# ==========================================
# COMPARISONS
# ==========================================
## Returns 1 if this > other, -1 if this < other, 0 if equal
func compare_to(other: BigNumber) -> int:
if mantissa == 0.0 and other.mantissa == 0.0:
return 0
# Handle zero explicitly before sign/exponent checks.
if mantissa == 0.0:
return -1 if other.mantissa > 0.0 else 1
if other.mantissa == 0.0:
return 1 if mantissa > 0.0 else -1
# Handle signs
if mantissa > 0 and other.mantissa < 0:
return 1
if mantissa < 0 and other.mantissa > 0:
return -1
# Both are same sign. Compare exponents first.
var sign_mult: int = 1 if mantissa > 0 else -1
if exponent > other.exponent:
return sign_mult
if exponent < other.exponent:
return -sign_mult
# Exponents are equal, compare mantissas
if mantissa > other.mantissa:
return 1
if mantissa < other.mantissa:
return -1
return 0
func is_greater_than(other: BigNumber) -> bool:
return compare_to(other) == 1
func is_less_than(other: BigNumber) -> bool:
return compare_to(other) == -1
func is_equal_to(other: BigNumber) -> bool:
return compare_to(other) == 0
## Calculates the progress ratio between this number and a target number.
## Returns a standard float clamped between 0.0 and 1.0 for UI progress bars.
func get_ratio(target: BigNumber) -> float:
if target.mantissa == 0.0:
return 1.0 # If the goal is 0, you've already beaten it!
if mantissa == 0.0:
return 0.0
var exp_diff: int = exponent - target.exponent
# If the target is massively larger, progress is practically 0%
if exp_diff <= -15:
return 0.0
# If current is equal or greater, progress is 100%
if exp_diff >= 15 or is_greater_than(target) or is_equal_to(target):
return 1.0
# Calculate the actual float ratio
var ratio: float = (mantissa / target.mantissa) * pow(10.0, float(exp_diff))
# Clamp it just to be perfectly safe for UI elements
return clampf(ratio, 0.0, 1.0)
# ==========================================
# UTILITIES
# ==========================================
## Creates a BigNumber from a standard float or int
static func from_float(val: float) -> BigNumber:
return BigNumber.new(val, 0)
## Outputs a UI-friendly string (e.g., "1.50e12")
func to_string_sci(decimals: int = 2) -> String:
if exponent < 3:
# For small numbers, just show the regular number
var val: float = mantissa * pow(10.0, float(exponent))
return ("%." + str(decimals) + "f") % val
var format_str: String = "%." + str(decimals) + "f"
return (format_str % mantissa) + "e" + str(exponent)
## Optional: Standard idle game suffix formatting (K, M, B, T, Qa, etc.)
func to_string_suffix(decimals: int = 2) -> String:
if exponent < 3:
return to_string_sci(decimals)
var suffixes = ["", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc"]
var suffix_index: int = floori(exponent / 3.0)
if suffix_index < suffixes.size():
var display_mantissa = mantissa * pow(10.0, float(exponent % 3))
var format_str: String = "%." + str(decimals) + "f"
return (format_str % display_mantissa) + suffixes[suffix_index]
else:
# Fall back to scientific if we run out of suffixes
return to_string_sci(decimals)
# ==========================================
# SAVE & LOAD (SERIALIZATION)
# ==========================================
## Converts the BigNumber into a basic Dictionary for easy JSON saving.
func serialize() -> Dictionary:
return {
"m": mantissa,
"e": exponent
}
## A static factory method that creates a new BigNumber from loaded Dictionary data.
static func deserialize(data: Dictionary) -> BigNumber:
# Provide fallbacks (0.0 and 0) just in case the save file is corrupted
var loaded_m: float = data.get("m", 0.0)
var loaded_e: int = data.get("e", 0)
return BigNumber.new(loaded_m, loaded_e)