feat(combat): Introduce capture_city() and mark_player_eliminated() functions and update Player class to track elimination state

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-04-30 07:35:06 -07:00
parent 6ade8dbdff
commit 1433d4adb3
3 changed files with 17 additions and 2 deletions

View file

@ -117,6 +117,7 @@ static func capture_city(
city.owner = attacker.owner
city.is_capital = false
city.captured_turn = GameState.turn_number
for tile_pos: Vector2i in city.owned_tiles:
var layer: Dictionary = GameState.get_primary_layer()
@ -134,7 +135,10 @@ static func capture_city(
EventBus.city_captured.emit(city, old_owner, attacker.owner)
if old_player != null and old_player.cities.is_empty():
if old_player != null and old_player.cities.is_empty() and not old_player.is_eliminated:
# Latch dedupes against victory_manager._reconcile_eliminations,
# which sweeps the same condition each turn.
old_player.is_eliminated = true
EventBus.player_eliminated.emit(old_owner)

View file

@ -30,6 +30,12 @@ var race_id: String = ""
var gender_preset: String = "male"
## True for the local human player; AI otherwise.
var is_human: bool = true
## True after this player has been eliminated (no cities AND no living
## founder unit). Set by `victory_manager._reconcile_eliminations` on the
## first turn the transition is observed; latches forever (eliminated
## players cannot recover under current rules). Used to ensure
## `EventBus.player_eliminated` fires exactly once per player per game.
var is_eliminated: bool = false
## Assigned UI color; populated by `GameState.add_player`.
var color: Color = Color.WHITE

View file

@ -136,7 +136,12 @@ static func capture_city(
EventBus.city_captured.emit(city, old_owner, attacker.owner)
if old_player != null and old_player.cities.is_empty():
if old_player != null and old_player.cities.is_empty() and not old_player.is_eliminated:
# Latch dedupes against victory_manager._reconcile_eliminations,
# which sweeps the same condition each turn. Either path may fire
# first (combat for instant-kill, reconciliation for end-of-turn
# starvation etc.); whichever wins, the other is silent.
old_player.is_eliminated = true
EventBus.player_eliminated.emit(old_owner)