Add guard behavior for opening defenders
This commit is contained in:
@@ -606,6 +606,16 @@ func _prepare_unit(source_unit: Dictionary) -> Dictionary:
|
||||
unit["controllable"] = bool(unit.get("controllable", true))
|
||||
unit["persist_progression"] = bool(unit.get("persist_progression", true))
|
||||
unit["ai_target_priority"] = max(0, int(unit.get("ai_target_priority", 0)))
|
||||
unit["ai_behavior"] = str(unit.get("ai_behavior", "")).strip_edges().to_lower()
|
||||
if unit["ai_behavior"] != "guard":
|
||||
unit["ai_behavior"] = ""
|
||||
if unit["ai_behavior"] == "guard" and not unit.has("ai_guard_radius"):
|
||||
unit["ai_guard_radius"] = 2
|
||||
unit["ai_guard_radius"] = max(0, int(unit.get("ai_guard_radius", 0)))
|
||||
var guard_anchor := _condition_cell(unit.get("ai_guard_anchor", [unit["pos"].x, unit["pos"].y]))
|
||||
if not is_inside(guard_anchor):
|
||||
guard_anchor = unit["pos"]
|
||||
unit["ai_guard_anchor"] = guard_anchor
|
||||
unit["status_effects"] = []
|
||||
unit["acted"] = false
|
||||
unit["moved"] = false
|
||||
@@ -3045,6 +3055,15 @@ func _run_enemy_turn() -> void:
|
||||
|
||||
|
||||
func _enemy_take_action(enemy: Dictionary) -> void:
|
||||
var movement_cells := _movement_range_for_unit(enemy)
|
||||
if _is_ai_guard(enemy):
|
||||
movement_cells = _ai_guard_movement_cells(enemy, movement_cells)
|
||||
if not _ai_guard_is_engaged(enemy, movement_cells):
|
||||
var return_cell := _best_ai_guard_return_cell(enemy, movement_cells)
|
||||
if return_cell != enemy["pos"]:
|
||||
_move_ai_unit(enemy, return_cell)
|
||||
return
|
||||
|
||||
if _try_enemy_skill_action(enemy):
|
||||
return
|
||||
|
||||
@@ -3054,16 +3073,16 @@ func _enemy_take_action(enemy: Dictionary) -> void:
|
||||
return
|
||||
|
||||
var best_cell: Vector2i = enemy["pos"]
|
||||
var move_attack_action := _best_ai_physical_attack_action(enemy, _movement_range_for_unit(enemy))
|
||||
var move_attack_action := _best_ai_physical_attack_action(enemy, movement_cells)
|
||||
if not move_attack_action.is_empty():
|
||||
best_cell = move_attack_action["origin"]
|
||||
else:
|
||||
var target := _find_nearest_target(enemy, TEAM_PLAYER)
|
||||
var target := _find_nearest_ai_guard_target(enemy, TEAM_PLAYER) if _is_ai_guard(enemy) else _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):
|
||||
for cell in movement_cells:
|
||||
var distance := _manhattan(cell, target["pos"])
|
||||
if distance < best_distance:
|
||||
best_distance = distance
|
||||
@@ -3088,6 +3107,97 @@ func _enemy_take_action(enemy: Dictionary) -> void:
|
||||
_resolve_combat(enemy, attack_action["target"])
|
||||
|
||||
|
||||
func _move_ai_unit(unit: Dictionary, target_cell: Vector2i) -> bool:
|
||||
if target_cell == unit["pos"]:
|
||||
return true
|
||||
var from_cell: Vector2i = unit["pos"]
|
||||
unit["pos"] = target_cell
|
||||
unit["moved"] = true
|
||||
_face_unit_from_to(unit, from_cell, target_cell)
|
||||
unit_motion_requested.emit(str(unit.get("id", "")), from_cell, target_cell)
|
||||
_emit_log("%s, %s?쇰줈 ?뺣컯." % [unit["name"], _format_cell(target_cell)])
|
||||
_run_events("unit_reaches_tile", str(unit.get("team", "")), turn_number, unit)
|
||||
return battle_status == STATUS_ACTIVE
|
||||
|
||||
|
||||
func _is_ai_guard(unit: Dictionary) -> bool:
|
||||
return str(unit.get("ai_behavior", "")).strip_edges().to_lower() == "guard"
|
||||
|
||||
|
||||
func _ai_guard_anchor(unit: Dictionary) -> Vector2i:
|
||||
var anchor: Vector2i = unit.get("ai_guard_anchor", unit.get("pos", Vector2i(-1, -1)))
|
||||
if not is_inside(anchor):
|
||||
return unit.get("pos", Vector2i(-1, -1))
|
||||
return anchor
|
||||
|
||||
|
||||
func _ai_guard_radius(unit: Dictionary) -> int:
|
||||
return max(0, int(unit.get("ai_guard_radius", 0)))
|
||||
|
||||
|
||||
func _ai_guard_contains_cell(unit: Dictionary, cell: Vector2i) -> bool:
|
||||
return _manhattan(_ai_guard_anchor(unit), cell) <= _ai_guard_radius(unit)
|
||||
|
||||
|
||||
func _ai_guard_movement_cells(unit: Dictionary, movement_cells: Array[Vector2i]) -> Array[Vector2i]:
|
||||
var filtered: Array[Vector2i] = []
|
||||
for cell in movement_cells:
|
||||
if _ai_guard_contains_cell(unit, cell):
|
||||
filtered.append(cell)
|
||||
if not filtered.is_empty():
|
||||
return filtered
|
||||
if not _ai_guard_contains_cell(unit, unit.get("pos", Vector2i(-1, -1))):
|
||||
return movement_cells
|
||||
return filtered
|
||||
|
||||
|
||||
func _ai_guard_attack_origins(unit: Dictionary, movement_cells: Array[Vector2i]) -> Array[Vector2i]:
|
||||
var origins: Array[Vector2i] = []
|
||||
var current: Vector2i = unit.get("pos", Vector2i(-1, -1))
|
||||
if is_inside(current):
|
||||
origins.append(current)
|
||||
for cell in movement_cells:
|
||||
if not origins.has(cell):
|
||||
origins.append(cell)
|
||||
return origins
|
||||
|
||||
|
||||
func _ai_guard_is_engaged(unit: Dictionary, movement_cells: Array[Vector2i]) -> bool:
|
||||
var target_team := _opposing_team(str(unit.get("team", "")))
|
||||
if target_team.is_empty():
|
||||
return false
|
||||
for target in get_living_units(target_team):
|
||||
if _ai_guard_contains_cell(unit, target.get("pos", Vector2i(-1, -1))):
|
||||
return true
|
||||
return not _best_ai_physical_attack_action(unit, _ai_guard_attack_origins(unit, movement_cells)).is_empty()
|
||||
|
||||
|
||||
func _best_ai_guard_return_cell(unit: Dictionary, movement_cells: Array[Vector2i]) -> Vector2i:
|
||||
var best_cell: Vector2i = unit.get("pos", Vector2i(-1, -1))
|
||||
var anchor := _ai_guard_anchor(unit)
|
||||
var best_distance := _manhattan(best_cell, anchor)
|
||||
for cell in movement_cells:
|
||||
var distance := _manhattan(cell, anchor)
|
||||
if distance < best_distance:
|
||||
best_distance = distance
|
||||
best_cell = cell
|
||||
return best_cell
|
||||
|
||||
|
||||
func _find_nearest_ai_guard_target(unit: Dictionary, target_team: String) -> Dictionary:
|
||||
var best_target := {}
|
||||
var best_score := 9999
|
||||
for target in get_living_units(target_team):
|
||||
if not _ai_guard_contains_cell(unit, target.get("pos", Vector2i(-1, -1))):
|
||||
continue
|
||||
var distance := _manhattan(unit["pos"], target["pos"])
|
||||
var score := distance - int(target.get("ai_target_priority", 0))
|
||||
if score < best_score:
|
||||
best_score = score
|
||||
best_target = target
|
||||
return best_target
|
||||
|
||||
|
||||
func _best_ai_physical_attack_action(attacker: Dictionary, origins: Array[Vector2i]) -> Dictionary:
|
||||
var best_action := {}
|
||||
var best_score := -999999
|
||||
|
||||
Reference in New Issue
Block a user