remove(entities): 🔥 Remove combat utility functions from GDScript after migration to Rust-based resolver

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-06-03 06:03:32 -07:00
parent dc834d7363
commit 7aec731e80
2 changed files with 0 additions and 172 deletions

View file

@ -1,171 +0,0 @@
class_name CombatUtils
extends RefCounted
## Static utility functions for combat: unit/city lookup, death handling, siege capture.
## No damage computation lives here — all combat math is in Rust (GdCombatResolver).
const UnitScript = preload("res://engine/src/entities/unit.gd")
const CityScript = preload("res://engine/src/entities/city.gd")
const HexUtilsScript = preload("res://engine/src/map/hex_utils.gd")
const ItemSystemScript = preload("res://engine/src/modules/management/item_system.gd")
## Get garrison combat unit at a city tile.
static func get_garrison(pos: Vector2i, all_units: Array) -> RefCounted:
for unit: RefCounted in all_units:
if unit is UnitScript and unit.position == pos and unit.is_military():
return unit
return null
## Get all units at a position.
static func get_units_at(pos: Vector2i, all_units: Array) -> Array:
var result: Array = []
for unit: RefCounted in all_units:
if unit is UnitScript and unit.position == pos:
result.append(unit)
return result
## Get all specialist units at a position owned by non-attacker.
static func get_specialists_at(pos: Vector2i, all_units: Array, attacker_owner: int) -> Array:
var result: Array = []
for unit: RefCounted in all_units:
if unit is UnitScript and unit.position == pos and unit.owner != attacker_owner:
if unit.is_specialist():
result.append(unit)
return result
## Get all units adjacent to a position.
static func get_adjacent_units(pos: Vector2i, all_units: Array) -> Array:
var neighbors: Array[Vector2i] = HexUtilsScript.get_neighbors(pos)
var result: Array = []
for neighbor_pos: Vector2i in neighbors:
for unit: RefCounted in all_units:
if unit is UnitScript and unit.position == neighbor_pos:
result.append(unit)
return result
## Get the city at a position (if any) from GameState.
static func get_city_at(pos: Vector2i) -> RefCounted:
for player: RefCounted in GameState.players:
for city: RefCounted in player.cities:
if city is CityScript and city.position == pos:
return city
return null
## Whether this unit is a siege unit (gets bonus vs cities).
static func is_siege_unit(unit: RefCounted) -> bool:
if unit is UnitScript:
return unit.get_combat_type() == "siege"
return false
## Handle a unit's death: check soul gem, drop loot, remove from game state, emit signal.
static func handle_unit_death(unit: RefCounted, killer: RefCounted, all_units: Array) -> void:
if not unit is UnitScript:
return
# Soul Gem check: resurrect at 30% HP instead of dying.
if ItemSystemScript.has_active_item(unit, "soul_gem"):
ItemSystemScript.consume_charge(unit, "soul_gem")
unit.hp = maxi(int(float(unit.get_max_hp()) * 0.3), 1)
return
# Drop equipped items as ground loot before removing the unit.
# ItemSystem.drop_all_loot wants the dying unit's tile, not the whole
# map — passing the map used to Array-type-error the FFI and abort the
# death handler before it could emit unit_destroyed.
var primary_layer: Dictionary = GameState.get_primary_layer()
if not primary_layer.is_empty():
var game_map: RefCounted = primary_layer.get("map")
if game_map != null:
var tile: Resource = game_map.get_tile(unit.position) as Resource
if tile != null:
ItemSystemScript.drop_all_loot(unit, tile)
if unit.owner == -1 and killer != null and killer is UnitScript and killer.owner >= 0:
_roll_wild_creature_loot(unit, killer)
all_units.erase(unit)
if unit.owner >= 0 and unit.owner < GameState.players.size():
var player: RefCounted = GameState.players[unit.owner]
player.units.erase(unit)
if not primary_layer.is_empty():
primary_layer.get("units", []).erase(unit)
EventBus.unit_destroyed.emit(unit, killer)
## Handle city capture: transfer ownership, emit signals, destroy High Archon if capital.
static func capture_city(
city: RefCounted,
attacker: RefCounted,
old_owner: int,
all_units: Array,
) -> void:
var old_player: RefCounted = GameState.get_player(old_owner)
var new_player: RefCounted = GameState.get_player(attacker.owner)
if old_player != null:
old_player.cities.erase(city)
# p1-29a — increment cumulative city-loss counter on the dispossessed
# player. Drives last-stand defense multiplier when they reach 1 city
# remaining. Never decremented (persists even on recapture).
old_player.cities_lost_total += 1
if new_player != null and city not in new_player.cities:
new_player.cities.append(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()
if layer.is_empty():
continue
var map_ref: RefCounted = layer.get("map")
if map_ref == null:
continue
var t: RefCounted = map_ref.get_tile(tile_pos)
if t != null:
t.owner = attacker.owner
if old_player != null:
_destroy_high_archon(old_player, all_units)
EventBus.city_captured.emit(city, old_owner, attacker.owner)
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)
static func _roll_wild_creature_loot(victim: RefCounted, killer: RefCounted) -> void:
if killer.owner < 0 or killer.owner >= GameState.players.size():
return
var killer_player: RefCounted = GameState.players[killer.owner]
var creature_type: String = victim.type_id if victim.type_id != "" else victim.unit_id
var turn_seed: int = GameState.game_rng.seed ^ GameState.turn_number
var killer_id: int = hash(killer.id)
var victim_id: int = hash(victim.id)
ItemSystemScript.roll_fauna_drops(creature_type, killer_player, turn_seed, killer_id, victim_id)
## Destroy the High Archon of a player (capital capture penalty).
static func _destroy_high_archon(player: RefCounted, all_units: Array) -> void:
for unit: RefCounted in player.units:
if unit is UnitScript and unit.type_id == "high_archon":
unit.hp = 0
all_units.erase(unit)
player.units.erase(unit)
var primary_layer: Dictionary = GameState.get_primary_layer()
if not primary_layer.is_empty():
primary_layer.get("units", []).erase(unit)
EventBus.unit_destroyed.emit(unit, null)
break

View file

@ -1 +0,0 @@
uid://e7rxe1g4o8hn