c4e7302567
- 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.
54 lines
4.0 KiB
GDScript
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))
|