diff --git a/data/ai/lua/generic_recruit_engine.lua b/data/ai/lua/generic_recruit_engine.lua index ac5748d2ee9..a47e40c5aca 100644 --- a/data/ai/lua/generic_recruit_engine.lua +++ b/data/ai/lua/generic_recruit_engine.lua @@ -14,6 +14,9 @@ return { -- (default always returns false) -- leader_takes_village: function that returns true if and only if the leader is going to move to capture a village this turn -- (default always returns true) + -- Note: the recruiting code assumes full knowledge of units on the map and the recruit lists of other sides for the purpose of + -- finding the best unit types to recruit. It does not work otherwise. It assumes normal vision of the AI side (that is, it disregards + -- hidden enemy units) for determining from which keep hex the leader should recruit and on which castle hexes to recruit new units init = function(ai_cas, params) if not params then params = {} @@ -354,7 +357,7 @@ return { local no_space = true for i,c in ipairs(data.castle.locs) do local unit = wesnoth.get_unit(c[1], c[2]) - if (not unit) then + if (not AH.is_visible_unit(wesnoth.current.side, unit)) then no_space = false break end @@ -612,19 +615,19 @@ return { -- and also the closest enemy local max_rating = -1 - local enemy_leaders = AH.get_live_units { canrecruit = 'yes', - { "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } } - } + local enemy_leaders = AH.get_attackable_enemies { canrecruit = 'yes' } local closest_enemy_distance, closest_enemy_location = AH.get_closest_enemy() for i,c in ipairs(data.castle.locs) do local rating = 0 local unit = wesnoth.get_unit(c[1], c[2]) - if (not unit) then + if (not AH.is_visible_unit(wesnoth.current.side, unit)) then for j,e in ipairs(enemy_leaders) do rating = rating + 1 / H.distance_between(c[1], c[2], e.x, e.y) ^ 2. end - rating = rating + 1 / H.distance_between(c[1], c[2], closest_enemy_location.x, closest_enemy_location.y) ^ 2. + if closest_enemy_location then + rating = rating + 1 / H.distance_between(c[1], c[2], closest_enemy_location.x, closest_enemy_location.y) ^ 2. + end if (rating > max_rating) then max_rating, best_hex = rating, { c[1], c[2] } end @@ -650,9 +653,9 @@ return { local target_hex = recruit_data.recruit.target_hex local distance_to_enemy, enemy_location if target_hex[1] then - distance_to_enemy, enemy_location = AH.get_closest_enemy(target_hex) + distance_to_enemy, enemy_location = AH.get_closest_enemy(target_hex, wesnoth.current.side, { viewing_side = 0 }) else - distance_to_enemy, enemy_location = AH.get_closest_enemy(best_hex) + distance_to_enemy, enemy_location = AH.get_closest_enemy(best_hex, wesnoth.current.side, { viewing_side = 0 }) end local gold_limit = 9e99 diff --git a/data/ai/lua/generic_rush_engine.lua b/data/ai/lua/generic_rush_engine.lua index 16a31049d22..a298fadcb4d 100644 --- a/data/ai/lua/generic_rush_engine.lua +++ b/data/ai/lua/generic_rush_engine.lua @@ -67,6 +67,9 @@ return { -------- Castle Switch CA -------------- local function get_reachable_enemy_leaders(unit) + -- We're cheating a little here and also find hidden enemy leaders. That's + -- because a human player could make a pretty good educated guess as to where + -- the enemy leaders are likely to be while the AI does not know how to do that. local potential_enemy_leaders = AH.get_live_units { canrecruit = 'yes', { "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } } } @@ -228,7 +231,7 @@ return { local should_wait = false for i,loc in ipairs(castle) do local unit = wesnoth.get_unit(loc[1], loc[2]) - if not unit then + if (not AH.is_visible_unit(wesnoth.current.side, unit)) then should_wait = false break elseif unit.moves > 0 then @@ -273,9 +276,7 @@ return { return 0 end - local enemies = AH.get_live_units { - { "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} } - } + local enemies = AH.get_attackable_enemies() local villages = wesnoth.get_villages() -- Just in case: @@ -331,7 +332,7 @@ return { for i,u in ipairs(units) do -- Skip villages that have units other than 'u' itself on them local village_occupied = false - if unit_in_way and ((unit_in_way.x ~= u.x) or (unit_in_way.y ~= u.y)) then + if AH.is_visible_unit(wesnoth.current.side, unit_in_way) and ((unit_in_way ~= u)) then village_occupied = true end @@ -486,21 +487,13 @@ return { function generic_rush:spread_poison_exec() local attacker = wesnoth.get_unit(self.data.attack.src.x, self.data.attack.src.y) + -- If several attacks have poison, this will always find the last one + local is_poisoner, poison_weapon = AH.has_weapon_special(attacker, "poison") if AH.print_exec() then print_time(' Executing spread_poison CA') end if AH.show_messages() then W.message { speaker = attacker.id, message = 'Poison attack' } end - local defender = wesnoth.get_unit(self.data.attack.target.x, self.data.attack.target.y) - - AH.movefull_stopunit(ai, attacker, self.data.attack.dst.x, self.data.attack.dst.y) - if (not attacker) or (not attacker.valid) then return end - if (not defender) or (not defender.valid) then return end - - -- Find the poison weapon - -- If several attacks have poison, this will always find the last one - local is_poisoner, poison_weapon = AH.has_weapon_special(attacker, "poison") - - AH.checked_attack(ai, attacker, defender, poison_weapon) + AH.robust_move_and_attack(ai, attacker, self.data.attack.dst, self.data.attack.target, { weapon = poison_weapon }) self.data.attack = nil end @@ -544,7 +537,7 @@ return { end function generic_rush:retreat_injured_units_exec() - AH.movefull_outofway_stopunit(ai, self.data.retreat_unit, self.data.retreat_loc) + AH.robust_move_and_attack(ai, self.data.retreat_unit, self.data.retreat_loc) self.data.retreat_unit = nil self.data.retreat_loc = nil end diff --git a/data/ai/lua/retreat.lua b/data/ai/lua/retreat.lua index f394d39030e..67cececd2da 100644 --- a/data/ai/lua/retreat.lua +++ b/data/ai/lua/retreat.lua @@ -109,9 +109,7 @@ end function retreat_functions.get_retreat_injured_units(healees, regenerates) -- Only retreat to safe locations - local enemies = AH.get_live_units { - { "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} } - } + local enemies = AH.get_attackable_enemies() local enemy_attack_map = BC.get_attack_map(enemies) local healing_locs = retreat_functions.get_healing_locations() @@ -151,7 +149,9 @@ function retreat_functions.get_retreat_injured_units(healees, regenerates) for j,loc in ipairs(possible_locations) do local unit_in_way = wesnoth.get_unit(loc[1], loc[2]) - if (not unit_in_way) or ((unit_in_way.moves > 0) and (unit_in_way.side == wesnoth.current.side)) then + if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way)) + or ((unit_in_way.moves > 0) and (unit_in_way.side == wesnoth.current.side)) + then local rating = base_rating local heal_score = 0 if regenerates then