mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-27 03:26:04 +00:00

Also backtracked the removal of wesnoth.get_recall_units, which is now available as wesnoth.units.find_on_recall - it's just more convenient than passing x=recall to wesnoth.units.find
126 lines
4.9 KiB
Lua
126 lines
4.9 KiB
Lua
local H = wesnoth.require "helper"
|
|
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|
local LS = wesnoth.require "location_set"
|
|
local M = wesnoth.map
|
|
|
|
local function get_guardian(cfg)
|
|
local filter = wml.get_child(cfg, "filter") or { id = cfg.id }
|
|
local guardian = AH.get_units_with_moves {
|
|
side = wesnoth.current.side,
|
|
{ "and", filter }
|
|
}[1]
|
|
return guardian
|
|
end
|
|
|
|
local ca_zone_guardian = {}
|
|
|
|
function ca_zone_guardian:evaluation(cfg)
|
|
if get_guardian(cfg) then return cfg.ca_score end
|
|
return 0
|
|
end
|
|
|
|
function ca_zone_guardian:execution(cfg)
|
|
local guardian = get_guardian(cfg)
|
|
local reach = wesnoth.find_reach(guardian)
|
|
|
|
local zone = wml.get_child(cfg, "filter_location")
|
|
local zone_enemy = wml.get_child(cfg, "filter_location_enemy") or zone
|
|
local enemies = AH.get_attackable_enemies { { "filter_location", zone_enemy } }
|
|
if enemies[1] then
|
|
local min_dist, target = math.huge
|
|
for _,enemy in ipairs(enemies) do
|
|
local dist = M.distance_between(guardian.x, guardian.y, enemy.x, enemy.y)
|
|
if (dist < min_dist) then
|
|
target, min_dist = enemy, dist
|
|
end
|
|
end
|
|
|
|
-- If a valid target was found, guardian attacks this target, or moves toward it
|
|
if target then
|
|
-- Find tiles adjacent to the target
|
|
-- Save the one with the highest defense rating that guardian can reach
|
|
local best_defense, attack_loc = - math.huge
|
|
for xa,ya in H.adjacent_tiles(target.x, target.y) do
|
|
-- Only consider unoccupied hexes
|
|
local unit_in_way = wesnoth.units.get(xa, ya)
|
|
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way))
|
|
or (unit_in_way == guardian)
|
|
then
|
|
local defense = 100 - guardian:defense(wesnoth.get_terrain(xa, ya))
|
|
local nh = AH.next_hop(guardian, xa, ya)
|
|
if nh then
|
|
if (nh[1] == xa) and (nh[2] == ya) and (defense > best_defense) then
|
|
best_defense, attack_loc = defense, { xa, ya }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- If a valid hex was found: move there and attack
|
|
if attack_loc then
|
|
AH.robust_move_and_attack(ai, guardian, attack_loc, target)
|
|
else -- Otherwise move toward that enemy
|
|
local reach = wesnoth.find_reach(guardian)
|
|
|
|
-- Go through all hexes the guardian can reach, find closest to target
|
|
-- Cannot use next_hop here since target hex is occupied by enemy
|
|
local min_dist, nh = math.huge
|
|
for _,hex in ipairs(reach) do
|
|
-- Only consider unoccupied hexes
|
|
local unit_in_way = wesnoth.units.get(hex[1], hex[2])
|
|
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way))
|
|
or (unit_in_way == guardian)
|
|
then
|
|
local dist = M.distance_between(hex[1], hex[2], target.x, target.y)
|
|
if (dist < min_dist) then
|
|
min_dist, nh = dist, { hex[1], hex[2] }
|
|
end
|
|
end
|
|
end
|
|
|
|
AH.movefull_stopunit(ai, guardian, nh)
|
|
end
|
|
end
|
|
|
|
-- If no enemy around or within the zone, move toward station or zone
|
|
else
|
|
-- If cfg.station_loc or cfg.station_x/y are given, move toward that location
|
|
local newpos = AH.get_named_loc_xy('station', cfg)
|
|
-- Otherwise choose one randomly from those given in filter_location
|
|
if (not newpos) then
|
|
local locs_map = LS.of_pairs(AH.get_locations_no_borders(zone))
|
|
|
|
-- Check out which of those hexes the guardian can reach
|
|
local reach_map = LS.of_pairs(wesnoth.find_reach(guardian))
|
|
reach_map:inter(locs_map)
|
|
|
|
-- If it can reach some hexes, use only reachable locations,
|
|
-- otherwise move toward any (random) one from the entire set
|
|
if (reach_map:size() > 0) then
|
|
locs_map = reach_map
|
|
end
|
|
|
|
local locs = locs_map:to_pairs()
|
|
|
|
if (#locs > 0) then
|
|
local newind = math.random(#locs)
|
|
newpos = { locs[newind][1], locs[newind][2] }
|
|
else
|
|
newpos = { guardian.x, guardian.y }
|
|
end
|
|
end
|
|
|
|
-- Next hop toward that position
|
|
local nh = AH.next_hop(guardian, newpos[1], newpos[2])
|
|
if nh then
|
|
AH.movefull_stopunit(ai, guardian, nh)
|
|
end
|
|
end
|
|
if (not guardian) or (not guardian.valid) then return end
|
|
|
|
AH.checked_stopunit_moves(ai, guardian)
|
|
-- If there are attacks left and guardian ended up next to an enemy, we'll leave this to RCA AI
|
|
end
|
|
|
|
return ca_zone_guardian
|