feat(ai): ✨ Add AI turn bridge class to coordinate AI components and game state transitions
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
6c1847a210
commit
5980926b84
1 changed files with 61 additions and 2 deletions
|
|
@ -13,6 +13,12 @@ extends RefCounted
|
|||
const CityScript: GDScript = preload("res://engine/src/entities/city.gd")
|
||||
const PlayerScript: GDScript = preload("res://engine/src/entities/player.gd")
|
||||
const UnitScript: GDScript = preload("res://engine/src/entities/unit.gd")
|
||||
const CombatResolverScript: GDScript = preload(
|
||||
"res://engine/src/modules/combat/combat_resolver.gd"
|
||||
)
|
||||
const CombatUtilsScript: GDScript = preload(
|
||||
"res://engine/src/modules/combat/combat_utils.gd"
|
||||
)
|
||||
|
||||
|
||||
## Run all AI decisions for one player, apply them, then return the count
|
||||
|
|
@ -198,8 +204,7 @@ static func _apply_action(action: Dictionary, player: RefCounted) -> bool:
|
|||
"set_production":
|
||||
return _apply_set_production(action, player)
|
||||
"attack":
|
||||
push_warning("AiTurnBridge: 'attack' action not yet wired — skipping")
|
||||
return false
|
||||
return _apply_attack(action, player)
|
||||
push_warning("AiTurnBridge: unknown action type '%s'" % action_type)
|
||||
return false
|
||||
|
||||
|
|
@ -267,6 +272,60 @@ static func _apply_set_production(action: Dictionary, player: RefCounted) -> boo
|
|||
return true
|
||||
|
||||
|
||||
static func _apply_attack(action: Dictionary, player: RefCounted) -> bool:
|
||||
## Resolve an AI-initiated attack via the same combat path the human UI uses.
|
||||
## CombatResolver.resolve() handles HP, death, XP, signals, and capture; we
|
||||
## only spend the attacker's movement and do basic input validation here.
|
||||
var idx: int = int(action.get("unit_index", -1))
|
||||
if idx < 0 or idx >= player.units.size():
|
||||
return false
|
||||
var attacker: Unit = player.units[idx] as Unit
|
||||
if attacker == null or not attacker.is_alive():
|
||||
return false
|
||||
if attacker.movement_remaining <= 0:
|
||||
return false
|
||||
|
||||
var target: Vector2i = Vector2i(
|
||||
int(action.get("target_col", 0)),
|
||||
int(action.get("target_row", 0)),
|
||||
)
|
||||
|
||||
var primary: Dictionary = GameState.get_primary_layer()
|
||||
var all_units: Array = primary.get("units", [])
|
||||
var defender: RefCounted = _find_enemy_at(target, player.index, all_units)
|
||||
if defender == null:
|
||||
defender = CombatUtilsScript.get_city_at(target)
|
||||
if defender == null or defender.owner == player.index:
|
||||
return false
|
||||
|
||||
var game_map: RefCounted = GameState.get_game_map()
|
||||
if game_map == null:
|
||||
return false
|
||||
|
||||
var resolver: RefCounted = CombatResolverScript.new()
|
||||
resolver.resolve(attacker, defender, game_map, all_units)
|
||||
attacker.movement_remaining = 0
|
||||
return true
|
||||
|
||||
|
||||
static func _find_enemy_at(
|
||||
pos: Vector2i, attacker_owner: int, all_units: Array
|
||||
) -> RefCounted:
|
||||
## Return the first enemy unit standing on `pos`, or null. Mirrors
|
||||
## world_map_combat.get_enemy_at so AI and human paths agree on targeting.
|
||||
for unit_ref: Variant in all_units:
|
||||
if not unit_ref is UnitScript:
|
||||
continue
|
||||
if unit_ref.position != pos:
|
||||
continue
|
||||
if unit_ref.owner == attacker_owner:
|
||||
continue
|
||||
if not unit_ref.is_alive():
|
||||
continue
|
||||
return unit_ref
|
||||
return null
|
||||
|
||||
|
||||
static func _generate_city_name(player: RefCounted) -> String:
|
||||
var race_data: Dictionary = DataLoader.get_race(player.race_id)
|
||||
var city_names: Array = race_data.get("city_names", [])
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue