2026-09-28 22:19:13 +03:00
extends SceneTree
2026-09-30 08:08:27 +03:00
const E = preload ( "res://world/owniverse/math/exact.gd" )
2026-09-29 20:23:20 +03:00
const CivilizationState = preload ( "res://world/owniverse/civilization_state.gd" )
const CivilizationEvolution = preload ( "res://world/owniverse/civilization_evolution.gd" )
2026-09-28 22:19:13 +03:00
const Parameters = preload ( "res://world/owniverse/world_parameters.gd" )
const Descriptions = preload ( "res://world/owniverse/item_description.gd" )
var checks : int = 0
var failures : int = 0
func _initialize () -> void :
call_deferred ( "run" )
func check ( condition : bool , message : String ) -> void :
2026-09-30 08:08:27 +03:00
checks = E . add ( checks , 1 )
if ( not condition ):
failures = E . add ( failures , 1 )
2026-09-28 22:19:13 +03:00
push_error ( message )
func fixture ():
var world = load ( "res://world/owniverse/owniverse.gd" ) . new ()
for id in root . get_node ( "Global" ) . OwniverseEntities :
var entity = load ( "res://world/owniverse/owniverse_entity.gd" ) . new ( root . get_node ( "Global" ) . OwniverseEntities [ id ])
2026-09-29 20:23:20 +03:00
world . catalog . entities [ id ] = entity
2026-09-30 08:08:27 +03:00
world . state . amount [ id ] = 0
world . state . stats . total_amount [ id ] = 0
if E . ne ( entity . group_as , "" ):
world . state . amount [ entity . group_as ] = 0
world . state . stats . total_amount [ entity . group_as ] = 0
2026-09-28 22:19:13 +03:00
return world
func dispose ( world ) -> void :
2026-09-29 20:23:20 +03:00
for entity in world . catalog . entities . values (): entity . free ()
2026-09-28 22:19:13 +03:00
world . free ()
func run () -> void :
var global : Node = root . get_node ( "Global" )
var world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount . RD = 400
world . batches . append_batch ( world . state , "RD" , 400 , 0 , 0 )
var end : Variant = world . state . batches . RD [ 0 ] . end_year
world . environment . modify_world_parameter ( "Bur" , 9 )
check ( E . near ( world . state . batches . RD [ 0 ] . end_year , E . divide ( end , 10 )), "Burnability accelerates existing cohorts" )
var count : Variant = world . state . batches . RD [ 0 ] . amount
world . environment . modify_world_parameter ( "Bur" , E . neg ( 9 ))
check (( E . near ( world . state . batches . RD [ 0 ] . end_year , end ) and E . eq ( world . state . batches . RD [ 0 ] . amount , count )), "Changing burnability back preserves population and lifetime" )
world . environment . modify_world_parameter ( "Bur" , 9 )
world . batches . append_batch ( world . state , "YD" , 10 , 0 , 0 )
check ( E . near ( world . state . batches . YD [ 0 ] . start_year , E . divide ( world . catalog . entities . YD . lifespan , 10 )), "New stars use the same burnability" )
world . state . time = { "datetime" : E . subtract ( E . divide ( end , 10 ), E . of ( "0.1" )), "year" : E . subtract ( E . divide ( end , 10 ), E . of ( "0.1" ))}
2026-09-29 20:23:20 +03:00
world . batches . withdraw_batches ( world . state , "RD" , world . state . time . year )
2026-09-30 08:08:27 +03:00
world . environment . modify_world_parameter ( "Bur" , E . neg ( 18 ))
check ( E . ge ( world . state . batches . RD [ 0 ] . end_year , world . state . time . year ), "Slowing a partially evolved cohort keeps its remaining window in the future" )
world . environment . modify_world_parameter ( "Exp" , 9 )
check ( E . eq ( world . stellar . evolution_products ( world . state , world . catalog . entities . SN , 2 ) . blast , 180 ), "Explosion power includes world explosiveness" )
var products : Dictionary = world . stellar . evolution_products ( world . state , world . catalog . entities . HRG , 1000 )
check (( E . eq ( products . get ( "GRB" , 0 ), 100 ) and E . eq ( products . get ( "SN" , 0 ), 900 )), "Explosiveness raises rare-event probability without duplicating stars" )
world . catalog . entities . HRG . output_or . chance = 50
products = world . stellar . evolution_products ( world . state , world . catalog . entities . HRG , 100 )
check (( E . eq ( products . get ( "GRB" , 0 ), 100 ) and E . eq ( products . get ( "SN" , 0 ), 0 )), "Rare-event probability is bounded at one" )
2026-09-28 22:19:13 +03:00
dispose ( world )
world = fixture ()
2026-09-30 08:08:27 +03:00
var ordinary : Dictionary = world . life . life_products ( world . state , world . catalog . entities . HP , 1000000 )
2026-09-28 22:19:13 +03:00
world . state . life_remainders . clear ()
2026-09-30 08:08:27 +03:00
world . environment . modify_world_parameter ( "Agg" , 9 )
var aggressive : Dictionary = world . life . life_products ( world . state , world . catalog . entities . HP , 1000000 )
check ( E . gt ( aggressive . EON1 , ordinary . EON1 ), "Aggression accelerates primitive evolution" )
2026-09-28 22:19:13 +03:00
world . state . life_remainders . clear ()
2026-09-30 08:08:27 +03:00
world . state . world_parameters . Order = 10
var orderly : Dictionary = world . life . life_products ( world . state , world . catalog . entities . HP , 1000000 )
check ( E . lt ( orderly . RP , aggressive . RP ), "Order reduces degradation" )
2026-09-28 22:19:13 +03:00
world . state . life_remainders . clear ()
2026-09-30 08:08:27 +03:00
world . state . world_parameters . Enlightenment = 10
var enlightened : Dictionary = world . life . life_products ( world . state , world . catalog . entities . HP , 1000000 )
check ( E . gt ( enlightened . EON1 , orderly . EON1 ), "Enlightenment increases primitive progress" )
2026-09-28 22:19:13 +03:00
dispose ( world )
world = fixture ()
var hotter = fixture ()
for simulation in [ world , hotter ]:
2026-09-30 08:08:27 +03:00
simulation . state . amount . RD = 1000000
simulation . batches . append_batch ( simulation . state , "RD" , 1000000 , 0 , 0 )
hotter . environment . modify_world_parameter ( "Bur" , 9 )
world . evolute ( 1000 )
hotter . evolute ( 1000 )
check ( E . gt ( hotter . state . amount . S , world . state . amount . S ), "Burnability changes actual space production" )
check ( E . eq ( E . add ( hotter . state . energy , hotter . state . amount . S ), Parameters . INITIAL_ENERGY ), "Enhanced production still spends the GI energy budget" )
2026-09-28 22:19:13 +03:00
dispose ( world )
dispose ( hotter )
world = fixture ()
2026-09-30 08:08:27 +03:00
world . state . amount [ "C0.2" ] = 1000000
world . state . ethics [ "C0.2" ] = { "m" : 1000000 }
2026-09-28 22:19:13 +03:00
var baseline : Dictionary = world . state . duplicate ( true )
var advanced : Dictionary = baseline . duplicate ( true )
2026-09-30 08:08:27 +03:00
advanced . world_parameters . Agg = 19
2026-09-28 22:19:13 +03:00
var next_stage : String = global . CivilizationRules [ "C0.2" ] . next
var outcomes : Array = []
for target in [ baseline , advanced ]:
2026-09-30 08:08:27 +03:00
var expired : Dictionary = { "C0.2" : [{ "amount" : 1000000 , "from" : 0 , "to" : 1 }]}
2026-09-29 20:23:20 +03:00
var budget : Dictionary = CivilizationState . stellar_budget ( world , target , global . CivilizationRules )
2026-09-30 08:08:27 +03:00
CivilizationEvolution . prepare ( world , target , expired , 1 , budget , global . CivilizationRules )
outcomes . append ( expired [ "C0.2" ][ 0 ] . products . get ( next_stage , 0 ))
check ( E . gt ( outcomes [ 1 ], outcomes [ 0 ]), "Aggression changes actual ethical civilization transitions" )
2026-09-28 22:19:13 +03:00
dispose ( world )
# Trends use ethical proportions and stage weights, not population magnitude.
world = fixture ()
var rules : Dictionary = { "C2.0" : { "home" : "DwStar" }, "C2.1" : { "home" : "DwStar" }}
2026-09-30 08:08:27 +03:00
world . state . ethics = { "C2.0" : { "d" : 100 }, "C2.1" : { "d" : 100 }}
2026-09-28 22:19:13 +03:00
var source : Dictionary = world . state . duplicate ( true )
var single : Dictionary = source . duplicate ( true )
var split : Dictionary = source . duplicate ( true )
2026-09-30 08:08:27 +03:00
Parameters . advance ( source , single , rules , 1000000 )
for i in 10 : Parameters . advance ( source , split , rules , 100000 )
check ( E . near ( single . trends . Integration , E . of ( "0.018" )), "Trend integrates elapsed years and doubled stage weights" )
2026-09-28 22:19:13 +03:00
for key in Parameters . TRENDS :
2026-09-30 08:08:27 +03:00
check ( E . near ( single . trends [ key ], split . trends [ key ]), E . add ( "Trend is independent of frame partition: " , key ))
source . ethics [ "C2.0" ] . d = 1000000
2026-09-28 22:19:13 +03:00
var larger : Dictionary = world . state . duplicate ( true )
2026-09-30 08:08:27 +03:00
Parameters . advance ( source , larger , rules , 1000000 )
check ( E . near ( larger . trends . Integration , single . trends . Integration ), "Population proportions determine trend influence" )
world . state . trends . Integration = E . of ( "0.99" )
Parameters . advance ( source , world . state , rules , 100000000 )
check ( E . eq ( world . state . civilization_stats [ "trend:Integration" ], 2 ), "Long steps process every crossed threshold" )
check ( E . near ( world . state . trends . Integration , E . of ( "0.79" )), "Threshold crossing preserves the fractional remainder" )
check ((( E . gt ( world . state . world_parameters . Federation , 1 ) or E . gt ( world . state . world_parameters . Enlightenment , 1 )) or E . gt ( world . state . world_parameters . Order , 1 )), "Integration events change the world" )
var restored : Dictionary = E . decode ( bytes_to_var ( var_to_bytes ( E . encode ( world . state ))))
Parameters . advance ( source , world . state , rules , 100000000 )
Parameters . advance ( source , restored , rules , 100000000 )
check ( E . eq ( world . state , restored ), "Trend events resume identically after serialization" )
Parameters . advance ( source , world . state , rules , E . of ( "1.0e12" ))
check (( E . ge ( world . state . trends . Integration , 0 ) and E . lt ( world . state . trends . Integration , 1 )), "Very large intervals finish with a valid trend fraction" )
check ( E . ge ( world . state . world_parameters . Agg , 1 ), "Repeated integration events cannot reduce aggression below the scale" )
2026-10-01 07:48:52 +03:00
var payload : Dictionary = { "format_version" : 4 , "app_version" : "test" , "ui" : { "current_page" : 1 }, "state" : world . state }
2026-09-28 22:19:13 +03:00
var manager : Node = root . get_node ( "SaveManager" )
2026-09-30 08:08:27 +03:00
check ( E . eq ( manager . validate_data ( payload ), "" ), "New parameters and trends pass save validation" )
2026-09-28 22:19:13 +03:00
var old : Dictionary = payload . duplicate ( true )
old . state . erase ( "world_parameters" )
old . state . erase ( "trends" )
2026-09-30 08:08:27 +03:00
check ( E . eq ( manager . validate_data ( old ), "" ), "Existing current-format saves remain readable" )
payload . state . world_parameters . Agg = 20
check ( E . ne ( manager . validate_data ( payload ), "" ), "Out-of-range scales are rejected" )
2026-09-28 22:19:13 +03:00
dispose ( world )
2026-09-30 08:08:27 +03:00
for sample in [[ E . of ( "1e-7" ), 0 ], [ E . of ( "1e-6" ), E . of ( "0.5" )], [ E . of ( "1e-5" ), 1 ], [ E . of ( "0.9" ), 0 ], [ E . of ( "0.95" ), E . of ( "0.5" )], [ 1 , 1 ]]:
check ( E . near ( Parameters . density_display ( sample [ 0 ]) . position , sample [ 1 ]), E . add ( "Density boundary " , str ( sample [ 0 ])))
2026-09-28 22:19:13 +03:00
# The fixture overrides saving/loading; the player's save is never accessed.
var scene = load ( "res://world/world.tscn" ) . instantiate ()
var stage = scene . get_node ( "Stage" )
stage . owniverse . free ()
stage . set_script ( load ( "res://tests/fixtures/ui_stage.gd" ))
root . add_child ( scene )
await process_frame
await process_frame
2026-09-30 08:08:27 +03:00
stage . owniverse . state . energy = E . divide ( Parameters . INITIAL_ENERGY , 4 )
stage . owniverse . state . world_parameters . Bur = 19
stage . owniverse . state . trends . Integration = E . of ( "0.75" )
2026-09-28 22:19:13 +03:00
stage . world_indicators . update ( stage . owniverse )
2026-09-28 22:32:52 +03:00
var warning : TextureRect = scene . get_node ( "Background/WarningBorder" )
2026-09-30 08:08:27 +03:00
check ( E . eq ( warning . mouse_filter , Control . MOUSE_FILTER_IGNORE ), "Warning overlay does not block input" )
var environments : Array = [[ E . of ( "1.0e-7" ), "ff0000ff" ], [ E . of ( "1.0e-6" ), "df2020bf" ], [ E . of ( "1.0e-5" ), "c040407f" ], [ E . of ( "0.1" ), "00000000" ], [ E . of ( "0.9" ), "40a0c07f" ], [ E . of ( "0.97" ), "20b0dfbf" ], [ 1 , "00c0ffff" ], [ E . of ( "0.1" ), "00000000" ]]
2026-09-28 22:32:52 +03:00
for sample in environments :
2026-09-30 08:08:27 +03:00
check ( Parameters . density_warning_color ( sample [ 0 ]) . is_equal_approx ( Color . html ( sample [ 1 ])), E . add ( "Unity warning threshold: " , str ( sample [ 0 ])))
stage . owniverse . state . amount . H = E . of ( "1e14" )
2026-09-28 22:32:52 +03:00
stage . world_indicators . update ( stage . owniverse )
check ( warning . self_modulate . is_equal_approx ( Color . html ( "00c0ffff" )), "Compression warning uses current universe density" )
2026-09-30 08:08:27 +03:00
stage . owniverse . state . amount . H = 0
stage . owniverse . state . amount . S = E . of ( "1e24" )
2026-09-28 22:32:52 +03:00
stage . world_indicators . update ( stage . owniverse )
check ( warning . self_modulate . is_equal_approx ( Color . html ( "ff0000ff" )), "Rip warning replaces compression warning" )
2026-09-30 08:08:27 +03:00
stage . owniverse . state . amount . S = 0
2026-09-28 22:32:52 +03:00
stage . world_indicators . update ( stage . owniverse )
2026-09-30 08:08:27 +03:00
check ( E . eq ( warning . self_modulate . a , 0 ), "Warning clears when density returns to normal" )
2026-09-28 22:19:13 +03:00
for bar in stage . world_indicators . energy_bars :
2026-09-30 08:08:27 +03:00
check ( E . eq ( bar . scale . x , E . of ( "0.25" )), "Energy fill is synchronized on both pages" )
2026-09-28 22:19:13 +03:00
var pointer : Control = stage . world_indicators . pointers . Bur
2026-09-30 08:08:27 +03:00
check ( E . near ( E . add ( pointer . position . x , E . divide ( pointer . size . x , 2 )), 304 ), "Burnability pointer reaches the correct scale endpoint" )
check ( E . eq ( stage . world_indicators . trend_bars [ 1 ] . scale . y , E . of ( "0.75" )), "Trend bar displays its stored level" )
2026-09-28 22:19:13 +03:00
for locale in [ "en" , "ru" ]:
global . Locale = locale
for id in [ "Energy" , "Density" , "Burnability" , "Explosiveness" , "Aggression" , "Trends" ]:
var description : String = Descriptions . render ( stage . owniverse , global , id , "Indicator" )
2026-09-30 08:08:27 +03:00
check ((( not description . contains ( "—" )) and ( not description . contains ( "%value" ))), E . add ( E . add ( E . add ( "Resolved indicator description: " , locale ), "/" ), id ))
2026-09-28 22:19:13 +03:00
dispose ( stage . owniverse )
scene . free ()
2026-09-30 08:08:27 +03:00
print (( "World indicators: %d checks, %d failures" % [ checks , failures ]))
quit (( 0 if E . eq ( failures , 0 ) else 1 ))