Randoloot/tests/levels/planet_exploration_balance_test.gd
2026-10-27 22:20:01 +01:00

105 lines
6.0 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Node
const PROFILES := preload("res://scripts/planets/planet_profiles.gd")
const RULES := preload("res://scripts/planets/poi_rules.gd")
const CAPTAIN := preload("res://scripts/captain/captain_progress.gd")
var failures: Array[String] = []
func check(value: bool, message: String) -> void:
if not value:
failures.append(message)
push_error(message)
func equip(captain: Resource, rank: int) -> void:
for item in captain.gear_items: item.level = rank
func _ready() -> void:
var captain := CAPTAIN.new()
var state: Resource = captain.expedition
var statistics: Array[Dictionary] = []
for level in [1,10,30,60,120]:
equip(captain,level)
captain.level = level
var ore := 0.0
var damage := 0.0
var recovery := 0.0
var difficulty := 0.0
for seed_value in 200:
var profile := PROFILES.generate(seed_value+711,0,"Test",level)
var poi := {"id":seed_value,"kind":"deposit","complete":false,"bonus_available":false}
var plan := RULES.preview(profile,captain,poi,1234)
for amount in plan.materials.values(): ore += float(amount)
damage += (plan.damage.x+plan.damage.y)*0.5
recovery += float(RULES.report("deposit",profile,captain).multiplier)
difficulty += float(profile.difficulty)
if level == 1:
check(profile.easy and profile.landing_damage == 5,"first-level planets are gentle")
check(is_equal_approx(RULES.report("deposit",profile,captain).multiplier,1.0),"starter equipment recovers 100 percent")
check(plan.damage == Vector2i(2,4),"first-level ordinary damage stays 2–4 HP")
statistics.append({"level":level,"mean_materials":ore/200,"mean_damage":damage/200,"mean_recovery":recovery/200,"mean_difficulty":difficulty/200})
for index in range(1,statistics.size()):
check(statistics[index].mean_materials > statistics[index-1].mean_materials,"rewards grow with planet level")
check(statistics[index].mean_damage > statistics[index-1].mean_damage,"matched-equipment wear grows gradually")
check(statistics[index].mean_difficulty > statistics[index-1].mean_difficulty,"planet difficulty grows gradually")
equip(captain,1)
captain.level = 1
var profile := PROFILES.generate(123,0,"Pierwsza planeta",1)
state.lander_hp = 100
check(state.begin_visit(profile),"first expedition begins")
var permanent := captain.minerals.duplicate()
for index in 10:
var poi := {"id":index,"kind":"deposit","complete":false,"bonus_available":false}
var hp: float = state.lander_hp
var plan := RULES.preview(profile,captain,poi,state.visit_seed)
check(plan == RULES.preview(profile,captain,poi,state.visit_seed),"reopening forecast never rerolls")
check(state.lander_hp == hp and not poi.complete,"forecast is read-only")
var result: Dictionary = state.resolve_poi(captain,poi)
check(result.materials == plan.materials,"actual resources match forecast exactly")
check(result.damage >= plan.damage.x and result.damage <= plan.damage.y,"damage stays within forecast")
check(is_equal_approx(state.lander_hp,hp-result.damage),"ordinary POI damages lander")
var cargo: Dictionary = state.cargo.duplicate()
check(state.resolve_poi(captain,poi).is_empty() and state.cargo == cargo,"ordinary rewards cannot repeat")
check(state.lander_hp >= 55 and state.lander_hp <= 75,"ten starter POIs leave 55–75 HP")
check(captain.minerals == permanent,"loot remains pending until return")
state.finish_visit(captain,true)
check(captain.minerals != permanent,"safe return delivers cargo")
var advanced := PROFILES.generate(123,0,"Trudna planeta",120)
equip(captain,50)
check(RULES.report("cave",advanced,captain).multiplier < 0.9,"level 50 gear does not neutralize endgame planets")
check(RULES.damage_range("cave",advanced,captain).x > 8,"outdated gear increases damage")
equip(captain,20)
var middle := PROFILES.generate(123,0,"Test sprzętu",20)
var cave_before := RULES.damage_range("cave",middle,captain).x
var warehouse_before := RULES.damage_range("warehouse",middle,captain).x
captain.gear_items[0].level = 1
check(RULES.damage_range("cave",middle,captain).x-cave_before > RULES.damage_range("warehouse",middle,captain).x-warehouse_before,"breathing gear matters most in caves")
equip(captain,1)
state.lander_hp = 100
check(state.begin_visit(profile),"bonus expedition begins")
var bonus_poi := {"id":100,"kind":"deposit","complete":false,"bonus_available":true}
check(state.resolve_poi(captain,bonus_poi,null,true).is_empty(),"bonus requires initial exploration")
state.resolve_poi(captain,bonus_poi)
var bonus_plan := RULES.preview(profile,captain,bonus_poi,state.visit_seed,true)
check(bonus_plan.damage == Vector2i(8,18),"starter bonus damage is 8–18 HP")
var cargo_before: Dictionary = state.cargo.duplicate()
state.resolve_poi(captain,bonus_poi,null,true)
check(bonus_poi.bonus_complete and state.cargo != cargo_before,"optional bonus awards extra loot once")
check(state.resolve_poi(captain,bonus_poi,null,true).is_empty(),"bonus cannot repeat")
var lethal := {"id":101,"kind":"village","complete":false,"bonus_available":true}
state.resolve_poi(captain,lethal)
state.lander_hp = 1
var credits: int = captain.credits
var minerals: Dictionary = captain.minerals.duplicate()
state.resolve_poi(captain,lethal,null,true)
check(state.lander_hp == 0,"greedy exploration can destroy the lander")
var returned: Dictionary = state.finish_visit(captain,true)
check(not returned.success and captain.credits == credits and captain.minerals == minerals,"destruction loses expedition loot without touching owned resources")
var bonus_count := 0
for index in 2000:
if RULES.has_bonus({"id":index,"kind":"deposit"},profile): bonus_count += 1
check(bonus_count >= 400 and bonus_count <= 600,"roughly one quarter of POIs offer bonus exploration")
var file := FileAccess.open("res://artifacts/planet_exploration_balance.json",FileAccess.WRITE)
file.store_string(JSON.stringify({"statistics":statistics,"bonus_fraction":bonus_count/2000.0,"failures":failures},"\t"))
file.close()
print("PLANET_EXPLORATION_BALANCE: ","PASS" if failures.is_empty() else "FAIL"," ",JSON.stringify(statistics))
get_tree().quit(0 if failures.is_empty() else 1)