Teach enemy AI weapon-aware attacks

This commit is contained in:
2026-06-18 11:30:37 +09:00
parent 6ca276082e
commit a4330481a6
8 changed files with 135 additions and 77 deletions

View File

@@ -2302,21 +2302,26 @@ func _enemy_take_action(enemy: Dictionary) -> void:
if _try_enemy_skill_action(enemy):
return
var target := _find_nearest_target(enemy, TEAM_PLAYER)
if target.is_empty():
return
if _is_in_attack_range(enemy, target["pos"]):
_resolve_combat(enemy, target)
var attack_action := _best_ai_physical_attack_action(enemy, [enemy["pos"]])
if not attack_action.is_empty():
_resolve_combat(enemy, attack_action["target"])
return
var best_cell: Vector2i = enemy["pos"]
var best_distance := _manhattan(best_cell, target["pos"])
for cell in _movement_range_for_unit(enemy):
var distance := _manhattan(cell, target["pos"])
if distance < best_distance:
best_distance = distance
best_cell = cell
var move_attack_action := _best_ai_physical_attack_action(enemy, _movement_range_for_unit(enemy))
if not move_attack_action.is_empty():
best_cell = move_attack_action["origin"]
else:
var target := _find_nearest_target(enemy, TEAM_PLAYER)
if target.is_empty():
return
var best_distance := _manhattan(best_cell, target["pos"])
for cell in _movement_range_for_unit(enemy):
var distance := _manhattan(cell, target["pos"])
if distance < best_distance:
best_distance = distance
best_cell = cell
if best_cell != enemy["pos"]:
var from_cell: Vector2i = enemy["pos"]
@@ -2331,9 +2336,45 @@ func _enemy_take_action(enemy: Dictionary) -> void:
if _try_enemy_skill_action(enemy):
return
target = _find_nearest_target(enemy, TEAM_PLAYER)
if not target.is_empty() and _is_in_attack_range(enemy, target["pos"]):
_resolve_combat(enemy, target)
attack_action = _best_ai_physical_attack_action(enemy, [enemy["pos"]])
if not attack_action.is_empty():
_resolve_combat(enemy, attack_action["target"])
func _best_ai_physical_attack_action(attacker: Dictionary, origins: Array[Vector2i]) -> Dictionary:
var best_action := {}
var best_score := -999999
var target_team := _opposing_team(str(attacker.get("team", "")))
if target_team.is_empty() or _unit_has_action_lock(attacker, "attack"):
return {}
for origin in origins:
if not is_inside(origin):
continue
for target in get_living_units(target_team):
if not _attack_cells_from(attacker, origin).has(target["pos"]):
continue
var damage := calculate_damage(attacker, target)
var score := _physical_attack_ai_score(attacker, target, damage)
score -= _manhattan(attacker["pos"], origin) * 2
if score > best_score:
best_score = score
best_action = {
"origin": origin,
"target": target,
"damage": damage,
"score": score
}
return best_action
func _physical_attack_ai_score(attacker: Dictionary, target: Dictionary, damage: int) -> int:
var score := damage * 10
score += _weapon_effectiveness_bonus(attacker, target) * 6
score += _ai_target_priority_score(target)
if int(target.get("hp", 0)) - damage <= 0:
score += 1000
return score
func _try_enemy_skill_action(enemy: Dictionary) -> bool: