local H = wesnoth.require "lua/helper.lua" local AH = wesnoth.require "ai/lua/ai_helper.lua" local function get_sheep(cfg) local sheep = wesnoth.get_units { side = wesnoth.current.side, { "and", cfg.filter_second } } return sheep end local function get_dogs(cfg) local dogs = AH.get_units_with_moves { side = wesnoth.current.side, { "and", cfg.filter } } return dogs end local function get_enemies(cfg, radius) local enemies = wesnoth.get_units { { "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } }, { "filter_location", { radius = radius, { "filter", { side = wesnoth.current.side, { "and", cfg.filter_second } } } } } } return enemies end local ca_herding_attack_close_enemy = {} function ca_herding_attack_close_enemy:evaluation(ai, cfg) -- Any enemy within attention_distance (default = 8) hexes of a sheep will get the dogs' attention -- with enemies within attack_distance (default: 4) being attacked if not get_sheep(cfg)[1] then return 0 end if not get_dogs(cfg)[1] then return 0 end local radius = cfg.attention_distance or 8 if get_enemies(cfg, radius)[1] then return cfg.ca_score end return 0 end function ca_herding_attack_close_enemy:execution(ai, cfg) local sheep = get_sheep(cfg) local dogs = get_dogs(cfg) -- We start with enemies within attack_distance (default: 4) hexes, which will be attacked local radius = cfg.attack_distance or 4 local enemies = get_enemies(cfg, radius) max_rating, best_dog, best_enemy, best_hex = -9e99 for _,enemy in ipairs(enemies) do for _,dog in ipairs(dogs) do local reach_map = AH.get_reachable_unocc(dog) reach_map:iter( function(x, y, v) -- Most important: distance from enemy local rating = - H.distance_between(x, y, enemy.x, enemy.y) * 100. -- 2nd: distance from any sheep for _,single_sheep in ipairs(sheep) do rating = rating - H.distance_between(x, y, single_sheep.x, single_sheep.y) end -- 3rd: most distant dog goes first rating = rating + H.distance_between(enemy.x, enemy.y, dog.x, dog.y) / 100. reach_map:insert(x, y, rating) if (rating > max_rating) then max_rating = rating best_dog, best_enemy, best_hex = dog, enemy, { x, y } end end) end end -- If we found a move, we do it, and attack if possible if best_dog then AH.movefull_stopunit(ai, best_dog, best_hex) if (not best_dog) or (not best_dog.valid) then return end if (not best_enemy) or (not best_enemy.valid) then return end if H.distance_between(best_dog.x, best_dog.y, best_enemy.x, best_enemy.y) == 1 then AH.checked_attack(ai, best_dog, best_enemy) end return end -- If we got here, no enemies to attack where found, so we go on to block other enemies local radius = cfg.attention_distance or 8 local enemies = get_enemies(cfg, radius) -- Find closest sheep/enemy pair first local min_dist, closest_sheep, closest_enemy = 9e99 for _,enemy in ipairs(enemies) do for _,single_sheep in ipairs(sheep) do local dist = H.distance_between(enemy.x, enemy.y, single_sheep.x, single_sheep.y) if dist < min_dist then min_dist = dist closest_sheep, closest_enemy = single_sheep, enemy end end end -- Move dogs in between enemies and sheep max_rating, best_dog, best_hex = -9e99 for _,dog in ipairs(dogs) do local reach_map = AH.get_reachable_unocc(dog) reach_map:iter( function(x, y, v) -- We want equal distance between enemy and closest sheep local rating = - math.abs( H.distance_between(x, y, closest_sheep.x, closest_sheep.y) - H.distance_between(x, y, closest_enemy.x, closest_enemy.y) ) * 100 -- 2nd: closeness to sheep rating = rating - H.distance_between(x, y, closest_sheep.x, closest_sheep.y) reach_map:insert(x, y, rating) -- 3rd: most distant dog goes first rating = rating + H.distance_between(closest_enemy.x, closest_enemy.y, dog.x, dog.y) / 100. reach_map:insert(x, y, rating) if (rating > max_rating) then max_rating, best_hex, best_dog = rating, { x, y }, dog end end) end AH.movefull_stopunit(ai, best_dog, best_hex) end return ca_herding_attack_close_enemy