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
This commit is contained in:
parent
78836882af
commit
6e13b6397c
42 changed files with 540 additions and 749 deletions
143
tests/test_planet_garrison.gd
Normal file
143
tests/test_planet_garrison.gd
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
extends SceneTree
|
||||
|
||||
# Headless gameplay test: run with
|
||||
# godot --headless -s res://tests/test_planet_garrison.gd
|
||||
# Forgejo #211: taking a planet is killing what defends it. The capture
|
||||
# points this replaced - stand in a ring, wait out a timer, once per ring -
|
||||
# are gone, so what has to be proven is that the FIGHT reaches the conquest
|
||||
# state, through the enemies' own deaths rather than through a bookkeeping
|
||||
# call a test makes on their behalf.
|
||||
#
|
||||
# test_conquered_planet_clear.gd covers the counting rules around landing and
|
||||
# re-landing. This one covers the wiring: a real Enemy, destroyed the way
|
||||
# anything destroys one, moves the planet toward being held.
|
||||
#
|
||||
# Covers:
|
||||
# A. an authored garrison registers itself when the surface is composed
|
||||
# B. killing one defender is progress, not conquest
|
||||
# C. killing the last one conquers the planet, exactly once, and the
|
||||
# research award rides along with the signal
|
||||
# D. an enemy that dies after the planet has already fallen changes nothing
|
||||
|
||||
# Resolved via load() inside _initialize(), NOT a top-level `const ...
|
||||
# preload(...)`. A top-level preload in a script-mode ENTRY point (-s
|
||||
# this_file.gd) evaluates during this file's own early compilation, which can
|
||||
# happen before autoloads are registered - and these scripts reference
|
||||
# GameState in function bodies, which GDScript resolves at compile time.
|
||||
var PLANET_SETUP_SCRIPT: Script
|
||||
var SPAWN_SCRIPT: Script
|
||||
|
||||
var game_state: Node
|
||||
var host: Node
|
||||
var planet_id: String = "test_garrison_planet"
|
||||
var conquered_count: int = 0
|
||||
var conquered_points: int = -1
|
||||
var stage: int = 0
|
||||
var elapsed: float = 0.0
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
PLANET_SETUP_SCRIPT = load("res://surface/PlanetSetup.gd")
|
||||
SPAWN_SCRIPT = load("res://surface/PlanetSetupSpawn.gd")
|
||||
game_state = root.get_node("GameState")
|
||||
game_state.planet_capture.clear()
|
||||
game_state.planet_id = planet_id
|
||||
game_state.planet_definition_id = "rocky"
|
||||
game_state.planet_conquered.connect(func(_id, points):
|
||||
conquered_count += 1
|
||||
conquered_points = points)
|
||||
|
||||
var setup: Resource = PLANET_SETUP_SCRIPT.new()
|
||||
setup.setup_id = "test_garrison"
|
||||
setup.surface_profile_id = "rocky"
|
||||
# Worth something, so the award travelling with the signal is checked on a
|
||||
# planet that actually pays.
|
||||
setup.research_point_reward = 3
|
||||
for i in range(3):
|
||||
var spawn: Resource = SPAWN_SCRIPT.new()
|
||||
spawn.enemy_id = "chaser"
|
||||
spawn.position = Vector2(0.0, 300.0 * float(i + 1))
|
||||
setup.enemy_spawns.append(spawn)
|
||||
|
||||
host = load("res://surface/PlanetSetupHost.tscn").instantiate()
|
||||
root.add_child(host)
|
||||
host.apply_setup(setup)
|
||||
|
||||
|
||||
func _defenders() -> Array:
|
||||
var out: Array = []
|
||||
for child in host.get_children():
|
||||
if is_instance_valid(child) and child.is_in_group("enemies") \
|
||||
and not child.is_queued_for_deletion():
|
||||
out.append(child)
|
||||
return out
|
||||
|
||||
|
||||
func _process(delta: float) -> bool:
|
||||
elapsed += delta
|
||||
if elapsed < 0.2:
|
||||
return false
|
||||
match stage:
|
||||
0:
|
||||
# --- A. the garrison registered itself -----------------------
|
||||
var defenders: Array = _defenders()
|
||||
if defenders.size() != 3:
|
||||
return _fail("the surface placed %d defenders for 3 authored" % defenders.size())
|
||||
if game_state.garrison_total(planet_id) != 3 \
|
||||
or game_state.garrison_remaining(planet_id) != 3:
|
||||
return _fail("a 3-strong garrison registered as %d of %d" % [
|
||||
game_state.garrison_remaining(planet_id),
|
||||
game_state.garrison_total(planet_id)])
|
||||
print("3 defenders placed, and the planet knows it")
|
||||
|
||||
# --- B. one down is not conquest -----------------------------
|
||||
var points_before: int = game_state.research_points
|
||||
defenders[0].take_damage(defenders[0].hp)
|
||||
if game_state.garrison_remaining(planet_id) != 2:
|
||||
return _fail("one kill left %d of the garrison standing, expected 2" \
|
||||
% game_state.garrison_remaining(planet_id))
|
||||
if game_state.is_planet_conquered(planet_id):
|
||||
return _fail("the planet fell to the first kill")
|
||||
if game_state.research_points != points_before:
|
||||
return _fail("an unfinished conquest paid out")
|
||||
print("one defender down: 2 left, planet still theirs")
|
||||
|
||||
# --- C. the last one takes it --------------------------------
|
||||
defenders[1].take_damage(defenders[1].hp)
|
||||
defenders[2].take_damage(defenders[2].hp)
|
||||
if not game_state.is_planet_conquered(planet_id):
|
||||
return _fail("the whole garrison is dead and the planet is not ours")
|
||||
if conquered_count != 1:
|
||||
return _fail("planet_conquered fired %d times, expected once" % conquered_count)
|
||||
if conquered_points != 3:
|
||||
return _fail("the conquest reported %d research points, expected the authored 3" \
|
||||
% conquered_points)
|
||||
if game_state.research_points != points_before + 3:
|
||||
return _fail("the reported award was not the one banked")
|
||||
print("last defender down: planet taken, 3 research points paid once")
|
||||
|
||||
elapsed = 0.0
|
||||
stage = 1
|
||||
return false
|
||||
1:
|
||||
# --- D. a straggler after the fact changes nothing -----------
|
||||
# A shot already in flight, or a bomb still blooming, can kill
|
||||
# something a frame after the planet has fallen.
|
||||
var points_after: int = game_state.research_points
|
||||
game_state.mark_garrison_enemy_destroyed(planet_id)
|
||||
if conquered_count != 1:
|
||||
return _fail("a kill after conquest fired planet_conquered again")
|
||||
if game_state.research_points != points_after:
|
||||
return _fail("a kill after conquest paid the reward a second time")
|
||||
print("a late kill is absorbed: no second conquest, no second payment")
|
||||
|
||||
print("TEST_PLANET_GARRISON: PASS")
|
||||
quit(0)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _fail(message: String) -> bool:
|
||||
print("TEST_PLANET_GARRISON: FAIL (%s)" % message)
|
||||
quit(1)
|
||||
return true
|
||||
Loading…
Reference in a new issue