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:
Loïc Wisniewski 2026-09-05 13:31:25 +02:00
commit 6e13b6397c
42 changed files with 540 additions and 749 deletions

View file

@ -4,6 +4,9 @@ const SURFACE_PROFILES = preload("res://surface/PlanetSurfaceProfiles.gd")
const PLANET_DEFINITIONS = preload("res://planets/PlanetDefinitions.gd")
const PLANET_SETUPS = preload("res://surface/PlanetSetups.gd")
const SETUP_HOST_SCENE: PackedScene = preload("res://surface/PlanetSetupHost.tscn")
# The crew's own log, the same voice the prologue uses for the things that
# happen TO them rather than the things they are told (#212).
const CONQUEST_SPEAKER: String = "KESTREL - LOG"
# Bootstraps the surviving fleet, including each vessel type, on this scene's
# own startup (from GameState, since this is a fresh scene tree - nothing here
@ -81,9 +84,16 @@ func _compose_surface(profile: Resource) -> void:
# capture_state()'s fallback for an unregistered planet_id defaults
# to conquered=false (see test_planet_conquest_gate.gd's own "a
# fresh planet reported conquered" check), which would otherwise
# permanently block extractor placement here - register_capture_
# points() treats a total of 0 as vacuously already conquered.
GameState.register_capture_points(GameState.planet_id, 0)
# permanently block extractor placement here - a garrison of zero is
# vacuously already beaten.
GameState.register_planet_garrison(GameState.planet_id, 0)
# #212: conquest is otherwise announced only by a caption changing and a
# HUD number the player has no reason to be watching at that moment. The
# surface already carries a narration banner, so the moment gets the same
# treatment every other turn of the story does.
if not GameState.planet_conquered.is_connected(_on_planet_conquered):
GameState.planet_conquered.connect(_on_planet_conquered)
if profile.encounter_scene != null:
var encounter: Node = profile.encounter_scene.instantiate()
@ -105,3 +115,19 @@ func _on_exit_pressed() -> void:
func _return_to_space() -> void:
get_tree().change_scene_to_file("res://world/Main.tscn")
# The banner is a sibling under UI and may not exist in a stripped-down test
# harness, so this is fetched rather than held and is allowed to be missing.
func _on_planet_conquered(planet_id: String, research_points_awarded: int) -> void:
if planet_id != GameState.planet_id:
return
var banner: Node = get_node_or_null("../UI/NarrationBanner")
if banner == null:
return
banner.say("The last of the garrison is down. This world is ours.",
CONQUEST_SPEAKER)
# Said even when it is none, because "this one paid nothing" is the
# information a player deciding where to go next actually needs.
banner.say("Research points recovered: %d." % research_points_awarded,
CONQUEST_SPEAKER)