feat(ai): ✨ Implement heuristic logic to prioritize attacking adjacent enemy cities for AI units
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
35dc508f70
commit
ca2e70240f
1 changed files with 41 additions and 3 deletions
|
|
@ -371,8 +371,27 @@ static func _decide_military_action(
|
|||
var hp_frac: float = float(unit.hp) / maxf(1.0, float(unit.max_hp))
|
||||
var nearest_enemy: Variant = _nearest_enemy_unit(unit.position, enemy_units)
|
||||
|
||||
# Retreat if wounded and a threat is within reach.
|
||||
if hp_frac <= RETREAT_HP_FRACTION and nearest_enemy != null:
|
||||
# Adjacent-to-enemy-city attack ALWAYS fires (captures undefended cities and
|
||||
# grinds garrisons). Commit flag below also blocks retreat so we don't bleed
|
||||
# turns healing next to the target while p0 rebuilds walls.
|
||||
var city_adj_col: int = -1
|
||||
var city_adj_row: int = -1
|
||||
for cp: Vector2i in enemy_city_positions:
|
||||
if HexUtilsScript.hex_distance(unit.position, cp) == 1:
|
||||
city_adj_col = cp.x
|
||||
city_adj_row = cp.y
|
||||
break
|
||||
if city_adj_col >= 0:
|
||||
return {"type": "attack", "unit_index": idx,
|
||||
"target_col": city_adj_col, "target_row": city_adj_row}
|
||||
|
||||
# Retreat if wounded — but not while committed to a capture push (enemy
|
||||
# city within 4). Letting p1 retreat from a stalled siege is why 0 captures
|
||||
# across 3 seeds despite 10x kill ratio in the field.
|
||||
var city_dist: int = INF_DISTANCE
|
||||
if not enemy_city_positions.is_empty():
|
||||
city_dist = _min_distance(unit.position, enemy_city_positions)
|
||||
if hp_frac <= RETREAT_HP_FRACTION and nearest_enemy != null and city_dist > 4:
|
||||
return _move_action(
|
||||
idx,
|
||||
unit.position,
|
||||
|
|
@ -408,8 +427,27 @@ static func _decide_military_action(
|
|||
}
|
||||
|
||||
var aggression: int = int(personality.get("aggression", 0))
|
||||
# Dominance redirect: when we outnumber the enemy field 2x and have a
|
||||
# city target closer than the nearest stray enemy, march on the city
|
||||
# instead of chasing. Prevents p1's 6-7 military from bleeding turns
|
||||
# on a lone wandering p0 unit while the capital sits undefended.
|
||||
var enemy_mil_count: int = 0
|
||||
for eu: Variant in enemy_units:
|
||||
if int(eu.get("attack")) > 0 or int(eu.get("ranged_attack")) > 0:
|
||||
enemy_mil_count += 1
|
||||
var own_mil_count: int = 0
|
||||
for ou: Variant in player.units:
|
||||
if ou == null or not ou.is_alive() or ou.get("can_found_city") == true:
|
||||
continue
|
||||
if int(ou.get("attack")) > 0 or int(ou.get("ranged_attack")) > 0:
|
||||
own_mil_count += 1
|
||||
var dominant: bool = (
|
||||
own_mil_count >= 2 * maxi(1, enemy_mil_count)
|
||||
and not enemy_city_positions.is_empty()
|
||||
and city_dist <= enemy_dist
|
||||
)
|
||||
var should_chase: bool = (
|
||||
aggression > 0 or enemy_dist <= DEFENSIVE_CHASE_RANGE
|
||||
not dominant and (aggression > 0 or enemy_dist <= DEFENSIVE_CHASE_RANGE)
|
||||
)
|
||||
if should_chase:
|
||||
return _move_action(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue