Randoloot/tests/levels/planet_reward_scaling_test.gd
2026-10-18 08:43:54 +02:00

111 lines
5.8 KiB
GDScript

extends Node
const REWARDS := preload("res://scripts/planets/planet_rewards.gd")
const EVENTS := preload("res://scripts/planets/surface_events.gd")
const PROFILES := preload("res://scripts/planets/planet_profiles.gd")
func offer(event: Dictionary, captain: Resource, state: Resource, id: String) -> Dictionary:
for option in EVENTS.options(event,captain,state):
if option.id == id:
return option
return {}
func _ready() -> void:
GameState.system_level = 32
var captain: Resource = preload("res://scripts/captain/captain_progress.gd").new()
captain.credits = 100000
captain.parts = 1000
for key in ["iron","sulfur","uranium","copper","nickel"]:
captain.minerals[key] = 1000
var state: Resource = captain.expedition
var samples: Array[Dictionary] = []
for index in 3:
var d := float(index)*0.5
var profile := PROFILES.generate(777,1,"Biom testowy")
profile.difficulty = d
profile.oxygen = roundi(80*(1.0-d))
profile.temperature = roundi(40+60*d)
profile.flora = roundi(50*d)
profile.landing_damage = 5
var expected_bounds: Vector2i = [Vector2i(0,2),Vector2i(1,6),Vector2i(2,10)][index]
assert(REWARDS.module_bonus_range(profile) == expected_bounds)
var rng := RandomNumberGenerator.new()
rng.seed = 348838
var histogram := {}
for sample_index in 10000:
var bonus := REWARDS.roll_module_bonus(rng,profile)
assert(bonus >= expected_bounds.x and bonus <= expected_bounds.y)
histogram[bonus] = int(histogram.get(bonus,0))+1
for bonus in range(expected_bounds.x,expected_bounds.y):
assert(int(histogram[bonus]) > int(histogram[bonus+1]))
assert(histogram.has(expected_bounds.y))
assert(REWARDS.material_multiplier(profile) == 1.0+index)
# Explicitly exclude all ammunition, rockets, credits and consumables.
var scaled := REWARDS.scale_materials({"parts":6,"copper":8,"silicon":8,"missile":4,"ammunition":10,"irradiated_ammunition":10,"energy_bank":1,"credits":50},profile)
assert(scaled.parts == 6*(1+index) and scaled.copper == 8*(1+index))
assert(scaled.missile == 4 and scaled.ammunition == 10 and scaled.irradiated_ammunition == 10 and scaled.energy_bank == 1 and scaled.credits == 50)
state.lander_hp = 100
assert(state.begin_visit(profile))
var trader := {"kind":"trader","seed":1234,"complete":false}
var goods := offer(trader,captain,state,"materials")
assert(goods.reward.copper == 8*(1+index) and goods.reward.silicon == 8*(1+index))
var missiles := offer(trader,captain,state,"missiles")
var barter := {"kind":"barter","seed":1234,"complete":false}
var barter_missiles := offer(barter,captain,state,"missiles")
assert(barter_missiles.reward.missile == 3)
assert(trader.module.item_level >= 32+expected_bounds.x and trader.module.item_level <= 32+expected_bounds.y)
var safe := {"kind":"safe_bet","seed":1234,"complete":false}
var guaranteed := offer(safe,captain,state,"safe")
assert(guaranteed.reward.parts == 6*(1+index))
assert(EVENTS.resolve(safe,"safe",captain,state).success)
assert(state.cargo_parts == 6*(1+index),"A reward must be scaled exactly once")
# Hidden outcomes retain their category and ammo counts for a shared seed.
var hidden := []
for seed_value in 200:
var chest_rng := RandomNumberGenerator.new()
chest_rng.seed = seed_value
var prize := EVENTS._mystery(chest_rng,32,profile)
hidden.append(prize)
# Every POI stage uses the biome range, without an additional stage/luck level.
var modules := 0
for attempt in 1200:
state.lander_hp = 100
var stage := attempt%3
var poi := {"kind":"mine","stage":stage,"complete":false}
var result: Dictionary = state.resolve_poi(captain,poi,rng)
if result.success:
for key in result.materials:
var minimum := roundi(5*(1+index)*(1.0+stage*0.65))
var maximum := roundi(12*(1+index)*(1.0+stage*0.65))
assert(int(result.materials[key]) >= minimum and int(result.materials[key]) <= maximum)
if result.module != null:
modules += 1
assert(result.module.item_level >= 32+expected_bounds.x and result.module.item_level <= 32+expected_bounds.y)
state.cargo_modules.clear()
assert(modules > 5)
samples.append({"profile":profile,"missile_count":missiles.reward.missile,"missile_price":missiles.cost.credits,"ammo_barter_cost":barter_missiles.cost.iron,"goods_cost":goods.cost.credits,"hidden":hidden})
state.finish_visit(captain,true)
print("BIOME ",d,": module range +",expected_bounds.x,"..+",expected_bounds.y,"; materials x",1+index,"; rockets=",missiles.reward.missile,"; price=",missiles.cost.credits,"; barter cost=",barter_missiles.cost.iron,"; tested POI modules=",modules)
for index in range(1,3):
assert(samples[index].missile_count == samples[0].missile_count)
assert(samples[index].missile_price < samples[index-1].missile_price)
assert(samples[index].ammo_barter_cost < samples[index-1].ammo_barter_cost)
assert(samples[index].goods_cost == samples[0].goods_cost)
for seed_value in 200:
var easy_prize: Dictionary = samples[0].hidden[seed_value]
var prize: Dictionary = samples[index].hidden[seed_value]
assert(prize.keys() == easy_prize.keys())
for key in prize:
if key in REWARDS.MATERIAL_KEYS:
assert(int(prize[key]) == int(easy_prize[key])*(1+index))
elif key == "missile":
assert(prize[key] == easy_prize[key])
elif key == "module":
var bounds := REWARDS.module_bonus_range(samples[index].profile)
assert(prize[key].item_level >= 32+bounds.x and prize[key].item_level <= 32+bounds.y)
var cap_rng := RandomNumberGenerator.new()
for level in [995,999,1000]:
for index in 50:
assert(REWARDS.generate_module(cap_rng,{"difficulty":1.0},level).item_level <= 1000)
print("BIOME REWARD SCALING PASS: 30000 bonus rolls, 3600 POI attempts, 600 mystery outcomes; materials grow, ammo counts stable, rocket offers cheaper, no duplicate scaling, level cap respected")
get_tree().quit()