spaceship/tests/test_lab_research_reward.gd
Loïc Wisniewski 6e13b6397c Take planets by killing what defends them
#211 conquest is the fight now: a planet registers the garrison its setup
authors, each defender's own death counts against it, and the last one takes
the world. The capture points - stand in a ring, wait out a timer, once per
ring - are gone, along with the editor palette that placed them.
#212 conquest opens a dialog on the surface naming the research points
recovered; the award travels with the signal, after it is banked.
#209 the prologue's charted sectors go with the breach, so the map the
survivors keep is of where they now are - and "chart 30 sectors" is no
longer complete before they have flown anywhere.
#218 enemy variety is gated on the FLEET, not on wave strength: a starting
fleet meets chasers only at any wave number, and each vessel past three
opens one more kind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
2026-09-05 13:31:25 +02:00

119 lines
5.2 KiB
GDScript

extends SceneTree
# Headless test: run with
# godot --headless -s res://tests/test_lab_research_reward.gd
# A harder enemy research lab should be worth more research points, and how
# hard a lab is depends on the layout authored for it - how many capture
# points, how many defenders - which the planet TYPE cannot know. So the
# reward is a per-map value set in the map editor, falling back to the
# planet type's flat rate when a setup does not express an opinion.
#
# Covers:
# A. a setup carries its own reward and it survives a save/load round trip
# B. a map's reward overrides the planet type's flat rate on conquest
# C. a setup that says nothing (0) still gets the type's rate, so every
# map authored before this existed keeps its old value
# D. the editor's control cycles the value and reports it
var game_state: Node
var editor: Node
var stage: int = 0
var elapsed: float = 0.0
func _initialize() -> void:
game_state = root.get_node("GameState")
game_state.reset_new_game()
# #108: reset_new_game() arms the prologue, and the prologue opens with a
# convoy rather than the game's own starting fleet. This test is about
# what the GAME starts from, which is where the prologue hands over -
# see test_intro_scene.gd for the prologue itself.
game_state.intro_active = false
func _process(delta: float) -> bool:
elapsed += delta
match stage:
0:
# --- A. the field exists and round-trips ---------------------
var setup: Resource = load("res://surface/PlanetSetup.gd").new()
setup.setup_id = "reward_probe"
setup.display_name = "Reward Probe"
setup.surface_profile_id = "rocky"
setup.research_point_reward = 5
var path: String = "user://test_reward_probe.tres"
if ResourceSaver.save(setup, path) != OK:
return _fail("could not save a setup carrying a research reward")
var reloaded: Resource = ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE)
if reloaded == null or int(reloaded.research_point_reward) != 5:
return _fail("the reward did not survive a save/load round trip")
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
print("a setup carries its own reward (%d) across a save/load" % reloaded.research_point_reward)
# --- B. the map's reward beats the planet type's rate --------
# A research_lab planet's own definition pays a flat 1.
var flat_rate: int = int(load("res://planets/ResearchLabPlanetDefinition.tres").conquest_research_points)
game_state.research_points = 0
game_state.register_planet_garrison("hard_lab", 2, "research_lab", 6)
game_state.mark_garrison_enemy_destroyed("hard_lab")
game_state.mark_garrison_enemy_destroyed("hard_lab")
if game_state.research_points != 6:
return _fail("a hard lab authored at 6 points awarded %d" % game_state.research_points)
if flat_rate >= 6:
return _fail("the planet type's flat rate (%d) is not below the authored one - this test proves nothing" % flat_rate)
print("a hard lab authored at 6 pays 6, over the planet type's flat %d" % flat_rate)
# --- C. silence still means the type's rate ------------------
game_state.research_points = 0
game_state.register_planet_garrison("plain_lab", 1, "research_lab", 0)
game_state.mark_garrison_enemy_destroyed("plain_lab")
if game_state.research_points != flat_rate:
return _fail("a setup with no authored reward paid %d, expected the type's %d" \
% [game_state.research_points, flat_rate])
print("a setup with no opinion still pays the planet type's %d" % flat_rate)
# A harder map must never be worth LESS than an easier one.
if 6 <= flat_rate:
return _fail("authoring a harder lab did not raise its worth")
editor = load("res://surface/PlanetSetupEditor.tscn").instantiate()
root.add_child(editor)
current_scene = editor
elapsed = 0.0
stage = 1
return false
1:
# --- D. the editor can set it --------------------------------
if elapsed < 0.2:
return false
editor.new_setup("reward_edit", "Reward Edit", "rocky")
var seen: Array = []
for i in range(editor.REWARD_OPTIONS.size()):
seen.append(int(editor.current_setup.research_point_reward))
editor._on_reward_pressed()
# Cycling the whole list must come back round to where it began.
if int(editor.current_setup.research_point_reward) != seen[0]:
return _fail("cycling every reward option did not return to the start")
if seen.size() != editor.REWARD_OPTIONS.size() or seen.max() <= 0:
return _fail("the reward control did not offer a range of values: %s" % [seen])
editor.current_setup.research_point_reward = 3
editor._refresh_reward_label()
if not "3" in editor.reward_value_label.text:
return _fail("the editor does not report the chosen reward: '%s'" % editor.reward_value_label.text)
editor.current_setup.research_point_reward = 0
editor._refresh_reward_label()
if not "DEFAULT" in editor.reward_value_label.text:
return _fail("the editor does not distinguish 'use the planet type's rate': '%s'" \
% editor.reward_value_label.text)
print("the map editor cycles %s and reports the choice" % [seen])
print("TEST_LAB_RESEARCH_REWARD: PASS")
quit(0)
return true
return false
func _fail(message: String) -> bool:
print("TEST_LAB_RESEARCH_REWARD: FAIL (%s)" % message)
quit(1)
return true