extends SceneTree const E = preload("res://world/owniverse/math/exact.gd") const CivilizationState = preload("res://world/owniverse/civilization_state.gd") const CivilizationEvolution = preload("res://world/owniverse/civilization_evolution.gd") 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: checks = E.add(checks, 1) if (not condition): failures = E.add(failures, 1) 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]) world.catalog.entities[id] = entity 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 return world func dispose(world) -> void: for entity in world.catalog.entities.values(): entity.free() world.free() func run() -> void: var global: Node = root.get_node("Global") var world = fixture() 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"))} world.batches.withdraw_batches(world.state, "RD", world.state.time.year) 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") dispose(world) world = fixture() var ordinary: Dictionary = world.life.life_products(world.state, world.catalog.entities.HP, 1000000) world.state.life_remainders.clear() 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") world.state.life_remainders.clear() 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") world.state.life_remainders.clear() 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") dispose(world) world = fixture() var hotter = fixture() for simulation in [world, hotter]: 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") dispose(world) dispose(hotter) world = fixture() world.state.amount["C0.2"] = 1000000 world.state.ethics["C0.2"] = {"m": 1000000} var baseline: Dictionary = world.state.duplicate(true) var advanced: Dictionary = baseline.duplicate(true) advanced.world_parameters.Agg = 19 var next_stage: String = global.CivilizationRules["C0.2"].next var outcomes: Array = [] for target in [baseline, advanced]: var expired: Dictionary = {"C0.2": [{"amount": 1000000, "from": 0, "to": 1}]} var budget: Dictionary = CivilizationState.stellar_budget(world, target, global.CivilizationRules) 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") 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"}} world.state.ethics = {"C2.0": {"d": 100}, "C2.1": {"d": 100}} var source: Dictionary = world.state.duplicate(true) var single: Dictionary = source.duplicate(true) var split: Dictionary = source.duplicate(true) 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") for key in Parameters.TRENDS: check(E.near(single.trends[key], split.trends[key]), E.add("Trend is independent of frame partition: ", key)) source.ethics["C2.0"].d = 1000000 var larger: Dictionary = world.state.duplicate(true) 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") var payload: Dictionary = {"format_version": 5, "app_version": "test", "ui": {"current_page": 1}, "state": world.state} var manager: Node = root.get_node("SaveManager") check(E.eq(manager.validate_data(payload), ""), "New parameters and trends pass save validation") var old: Dictionary = payload.duplicate(true) old.state.erase("world_parameters") old.state.erase("trends") 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") dispose(world) 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]))) # 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 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") stage.world_indicators.update(stage.owniverse) var warning: TextureRect = scene.get_node("Background/WarningBorder") 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"]] for sample in environments: 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") stage.world_indicators.update(stage.owniverse) check(warning.self_modulate.is_equal_approx(Color.html("00c0ffff")), "Compression warning uses current universe density") stage.owniverse.state.amount.H = 0 stage.owniverse.state.amount.S = E.of("1e24") stage.world_indicators.update(stage.owniverse) check(warning.self_modulate.is_equal_approx(Color.html("ff0000ff")), "Rip warning replaces compression warning") stage.owniverse.state.amount.S = 0 stage.world_indicators.update(stage.owniverse) check(E.eq(warning.self_modulate.a, 0), "Warning clears when density returns to normal") for bar in stage.world_indicators.energy_bars: check(E.eq(bar.scale.x, E.of("0.25")), "Energy fill is synchronized on both pages") var pointer: Control = stage.world_indicators.pointers.Bur 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") 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") check(((not description.contains("—")) and (not description.contains("%value"))), E.add(E.add(E.add("Resolved indicator description: ", locale), "/"), id)) dispose(stage.owniverse) scene.free() print(("World indicators: %d checks, %d failures" % [checks, failures])) quit((0 if E.eq(failures, 0) else 1))