local LS = wesnoth.require "lua/location_set.lua" local AH = wesnoth.require "ai/lua/ai_helper.lua" local H = wesnoth.require "lua/helper.lua" local function get_lurker(cfg) -- We simply pick the first of the lurkers, they have no strategy local lurker = AH.get_units_with_moves { side = wesnoth.current.side, { "and", cfg.filter } }[1] return lurker end local ca_lurkers = {} function ca_lurkers:evaluation(ai, cfg) if get_lurker(cfg) then return cfg.ca_score end return 0 end function ca_lurkers:execution(ai, cfg) local lurker = get_lurker(cfg) local targets = AH.get_live_units { { "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } } } -- Sort targets by hitpoints (lurkers choose lowest HP target) table.sort(targets, function(a, b) return (a.hitpoints < b.hitpoints) end) local reach = LS.of_pairs(wesnoth.find_reach(lurker.x, lurker.y)) local reachable_attack_terrain = LS.of_pairs(wesnoth.get_locations { { "and", { x = lurker.x, y = lurker.y, radius = lurker.moves } }, { "and", cfg.filter_location } }) reachable_attack_terrain:inter(reach) -- Need to restrict that to reachable and not occupied by an ally (except own position) local reachable_attack_terrain = reachable_attack_terrain:filter(function(x, y, v) local occ_hex = wesnoth.get_units { x = x, y = y, { "not", { x = lurker.x, y = lurker.y } } }[1] return not occ_hex end) -- Attack the weakest reachable enemy for _,target in ipairs(targets) do -- Get reachable attack terrain next to target unit local reachable_attack_terrrain_adj_target = LS.of_pairs( wesnoth.get_locations { x = target.x, y = target.y, radius = 1 } ) reachable_attack_terrrain_adj_target:inter(reachable_attack_terrain) -- Since enemies are sorted by hitpoints, we can simply attack the first enemy found if reachable_attack_terrrain_adj_target:size() > 0 then local rand = math.random(1, reachable_attack_terrrain_adj_target:size()) local dst = reachable_attack_terrrain_adj_target:to_stable_pairs() AH.movefull_stopunit(ai, lurker, dst[rand]) if (not lurker) or (not lurker.valid) then return end if (not target) or (not target.valid) then return end if (H.distance_between(lurker.x, lurker.y, target.x, target.y) ~= 1) then return end AH.checked_attack(ai, lurker, target) return end end -- If we got here, unit did not attack: go to random wander terrain hex if (lurker.moves > 0) and (not cfg.stationary) then local reachable_wander_terrain = LS.of_pairs( wesnoth.get_locations { { "and", { x = lurker.x, y = lurker.y, radius = lurker.moves } }, { "and", (cfg.filter_location_wander or cfg.filter_location) } }) reachable_wander_terrain:inter(reach) -- Need to restrict that to reachable and not occupied by an ally (except own position) local reachable_wander_terrain = reachable_wander_terrain:filter(function(x, y, v) local occ_hex = wesnoth.get_units { x = x, y = y, { "not", { x = lurker.x, y = lurker.y } } }[1] return not occ_hex end) if (reachable_wander_terrain:size() > 0) then local dst = reachable_wander_terrain:to_stable_pairs() local rand = math.random(1, reachable_wander_terrain:size()) AH.movefull_stopunit(ai, lurker, dst[rand]) return end end -- If the unit has moves or attacks left at this point, take them away AH.checked_stopunit_all(ai, lurker) end return ca_lurkers