Files
owniverse/world/owniverse/universe_environment.gd
T
Kirill c4e7302567 Add new tests and implement frame budget management
- Introduced `civilization_benchmark.gd` and `legacy_quantity_math.gd` for benchmarking and legacy quantity calculations.
- Created `test_dynamic_math.gd`, `test_frame_evolution.gd`, and `test_sparse_batches.gd` to validate mathematical operations and frame evolution logic.
- Implemented `frame_budget.gd` to manage cooperative work on the main thread, ensuring no partially calculated state is published.
- Added UID files for all new scripts to maintain unique identifiers.
2026-10-01 23:43:34 +03:00

54 lines
4.0 KiB
GDScript

extends "res://world/owniverse/simulation_module.gd"
const E = preload("res://world/owniverse/math/exact.gd")
const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
func stellar_environment(source: Dictionary) -> Dictionary:
var quantities: Dictionary = world.ledger.physical_amounts(source)
# SMBH is a self-group in GI; its quantity stores mass, not object count.
var mass: Variant = source.amount.get("SMBH", 0)
# Geometric volume includes a 1e22 scale per void and can exceed E27.
# It only feeds approximate density/blast coefficients, never stock counts.
var volume: float = E.approx(quantities.get("S", 0))
for id in quantities:
var entity: OwniverseEntity = world.catalog.entities[id]
if E.contains(["H", "He", "Met", "SMBH"], id):
mass = E.add(mass, quantities[id])
else:
mass = E.add(mass, E.multiply(quantities[id], E.maximum(0, entity.mass)))
volume += E.approx(quantities[id]) * maxf(0.0, E.approx(entity.space))
volume += E.approx(quantities[id]) * maxf(0.0, E.approx(entity.voids)) * 1e22
var metallicity: Variant = 0
if E.gt(mass, 0):
metallicity = E.maximum(0, E.add(E.multiply(E.of("0.4"), E.logarithm(E.add(E.mul_div(100, source.amount.get("Met", 0), mass), E.of("0.11")))), E.of("0.88")))
var base_mass: Variant = E.multiply(65536, ((E.power(mass, E.of("0.1")) if (E.gt(mass, 0) and E.lt(mass, E.of("1.0e10"))) else ((10 if E.ge(mass, E.of("1.0e10")) else 1)))))
if not is_finite(volume) or not is_finite(E.approx(E.add(base_mass, mass))):
# Dynamic counts may exceed the float exponent range. Evaluate the
# same geometry using logarithms, avoiding infinity / infinity.
var exact_volume: Variant = quantities.get("S", 0)
for id in quantities:
var entity: OwniverseEntity = world.catalog.entities[id]
var size: Variant = E.add(E.maximum(0, entity.space), E.multiply(E.maximum(0, entity.voids), E.of("1e22")))
exact_volume = E.add(exact_volume, E.multiply(quantities[id], size))
var density_log: float = log(1.23456e-7) + E.approx(E.logarithm(E.add(base_mass, mass))) / 2.0 - E.approx(E.logarithm(E.maximum(1, exact_volume))) / 6.0
var density: Variant = E.of("1e%d" % int(floor(density_log / log(10.0))))
density = E.multiply(density, E.coefficient(exp(density_log - floor(density_log / log(10.0)) * log(10.0))))
return {"metallicity": metallicity, "density": density, "blast": E.clamp_value(E.divide(source.amount.get("S", 0), exact_volume), 0, 1) if E.gt(exact_volume, 0) else 1}
return {"metallicity": metallicity, "density": E.coefficient(1.23456e-7 * sqrt(E.approx(E.add(base_mass, mass))) / pow(maxf(1.0, volume), 1.0 / 6.0)), "blast": E.coefficient(clampf(E.approx(source.amount.get("S", 0)) / volume, 0.0, 1.0)) if volume > 0.0 else 1}
func modify_world_parameter(key: String, change: Variant) -> void:
if (not E.contains(["Bur", "Exp", "Agg"], key) or (not E.finite(change))): return
var old_multiplier: Variant = WorldParameters.effective(world.state, key)
if (not world.state.has("world_parameters")): world.state.world_parameters = WorldParameters.defaults()
world.state.world_parameters[key] = E.clamp_value(E.add(WorldParameters.value(world.state, key), change), 1, 19)
if E.eq(key, "Bur"):
var ratio: Variant = E.divide(old_multiplier, WorldParameters.effective(world.state, key))
var now: Variant = world.state.time.year
for id in world.state.batches:
if ((not world.catalog.entities.has(id)) or E.ne(world.catalog.entities[id].group_as, "Star")): continue
for batch : Dictionary in world.state.batches[id]:
# Only future survival time changes. Birth dates and the fractional
# withdrawal carry remain intact; already elapsed time is not replayed.
for field in ["start_year", "end_year", "processed_until"]:
if batch.has(field): batch[field] = E.add(now, E.multiply(E.maximum(0, E.subtract(batch[field], now)), ratio))