Randoloot/tests/captain/lucky_explorer_test.gd
2026-10-16 22:04:36 +02:00

103 lines
4.1 KiB
GDScript

extends Node2D
const CATALOG := preload("res://scripts/captain/captain_skills.gd")
const GENERATOR := preload("res://scripts/items/item_generator.gd")
const EXPLORATION := preload("res://scripts/levels/exploration_generator.gd")
const PLAYER := preload("res://scenes/game/player_ship.tscn")
const DEFINITION := preload("res://resources/items/plasma_cannon.tres")
var ship: CharacterBody2D
func snapshot() -> Dictionary:
var container := Node2D.new()
add_child(container)
EXPLORATION.spawn(container, {"planets": []}, ship)
var result := {}
for poi in container.get_children():
result[poi.get("poi_id")] = {"kind": poi.get("kind"), "position": poi.position, "reward_seed": poi.get("reward_seed")}
container.free()
return result
func count_kind(data: Dictionary, kind: String) -> int:
var count := 0
for poi in data.values():
if poi["kind"] == kind:
count += 1
return count
func _ready() -> void:
GameState.start_new_run()
ship = PLAYER.instantiate() as CharacterBody2D
ship.name = "PlayerShip"
add_child(ship)
ship.set_physics_process(false)
ship.get_node("WeaponController").set_physics_process(false)
assert(CATALOG.IDS.has("lucky") and CATALOG.IDS.has("explorer"))
for id in ["lucky", "explorer"]:
assert(not CATALOG.DATA[id]["active"])
assert(CATALOG.icon(id) != null)
assert(not CATALOG.description(id, 3).is_empty())
# Every roll threshold is exact, with 0% baseline and a hard level cap.
for rank in range(4):
GameState.captain.skills["lucky"] = rank
var chance: float = [0.0, 0.05, 0.1, 0.25][rank]
assert(is_equal_approx(GameState.captain.get_skill_bonus("lucky"), chance))
for roll in 1000:
assert(GameState.captain.roll_loot_level(10, float(roll) / 1000.0) == (11 if float(roll) / 1000.0 < chance else 10))
assert(GameState.captain.roll_loot_level(1000, 0.0) == 1000)
assert(GameState.captain.roll_loot_level(0, 0.99) == 1)
# Exercise actual drops, including stat scaling and the normal level generator.
seed(888)
var upgrades := 0
for index in 2000:
var item := GENERATOR.generate_loot(10, 0, DEFINITION)
assert(item.item_level in [10, 11])
if item.item_level == 11:
upgrades += 1
assert(upgrades == 0 if rank == 0 else absf(float(upgrades) / 2000.0 - chance) < 0.04)
assert(GENERATOR.generate_for_definition(DEFINITION, 10).item_level == 10)
# Both new skills participate in the normal level-up offer and training path.
for id in ["lucky", "explorer"]:
var captain := CaptainProgress.new()
for skill in CATALOG.IDS:
captain.skills[skill] = 3
captain.skills[id] = 2
captain.learning_points = 1
captain.prepare_skill_offers()
assert(captain.skill_offers == [id])
assert(captain.train_skill(id))
assert(captain.get_skill_rank(id) == 3)
# Each rank adds exactly 1/3/6 wrecks and leaves every base POI intact.
var baseline := {}
for rank in range(4):
GameState.start_new_run()
GameState.system_seed = 222
GameState.captain.skills["explorer"] = rank
var current := snapshot()
if rank == 0:
baseline = current
var bonus: int = [0, 1, 3, 6][rank]
assert(count_kind(current, "wreck") == count_kind(baseline, "wreck") + bonus)
assert(count_kind(current, "asteroid") == count_kind(baseline, "asteroid"))
for id in baseline:
assert(current[id] == baseline[id])
GameState.start_new_run()
GameState.system_seed = 222
var before := snapshot()
GameState.captain.skills["explorer"] = 3
GameState.captain.emit_changed()
assert(snapshot() == before) # Reloading the current board does not add wrecks.
GameState.system_seed = 333
var next_board := snapshot()
var rng := RandomNumberGenerator.new()
rng.seed = 333 ^ 0x504f49
assert(count_kind(next_board, "wreck") == rng.randi_range(2, 4) + 6)
GameState.explored_pois["333:1000"] = true
var reloaded := snapshot()
assert(not reloaded.has("333:1000"))
assert(reloaded.size() == next_board.size() - 1)
assert(GameState.exploration_wreck_bonuses[333] == 6)
GameState.start_new_run()
assert(GameState.exploration_wreck_bonuses.is_empty())
print("LUCKY_EXPLORER_TEST: PASS (0/5/10/25% luck, thresholds, 8000 drops, level cap/scaling, skill offers/training, +1/3/6 wrecks, stable base POIs, next-board application, reload/claimed-wreck persistence)")
get_tree().quit()