AI: ensure all CAs respect [avoid] tags
This is for the candidate actions that were merged into the default AI from the former Experimental AI.
This commit is contained in:
parent
620da22082
commit
34956ac15d
6 changed files with 142 additions and 86 deletions
|
@ -6,7 +6,7 @@ local M = wesnoth.map
|
||||||
local CS_leader_score
|
local CS_leader_score
|
||||||
-- Note that leader_target is also needed by the recruiting CA, so it must be stored in 'data'
|
-- Note that leader_target is also needed by the recruiting CA, so it must be stored in 'data'
|
||||||
|
|
||||||
local function get_reachable_enemy_leaders(unit)
|
local function get_reachable_enemy_leaders(unit, avoid_map)
|
||||||
-- We're cheating a little here and also find hidden enemy leaders. That's
|
-- 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
|
-- 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.
|
-- the enemy leaders are likely to be while the AI does not know how to do that.
|
||||||
|
@ -14,10 +14,13 @@ local function get_reachable_enemy_leaders(unit)
|
||||||
{ "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } }
|
{ "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } }
|
||||||
}
|
}
|
||||||
local enemy_leaders = {}
|
local enemy_leaders = {}
|
||||||
for j,e in ipairs(potential_enemy_leaders) do
|
for _,e in ipairs(potential_enemy_leaders) do
|
||||||
local path, cost = wesnoth.find_path(unit, e.x, e.y, { ignore_units = true, viewing_side = 0 })
|
-- Cannot use AH.find_path_with_avoid() here as there might be enemies all around the enemy leader
|
||||||
if cost < AH.no_path then
|
if (not avoid_map:get(e.x, e.y)) then
|
||||||
table.insert(enemy_leaders, e)
|
local path, cost = wesnoth.find_path(unit, e.x, e.y, { ignore_units = true, viewing_side = 0 })
|
||||||
|
if cost < AH.no_path then
|
||||||
|
table.insert(enemy_leaders, e)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -50,12 +53,17 @@ function ca_castle_switch:evaluation(cfg, data, filter_own)
|
||||||
|
|
||||||
local cheapest_unit_cost = AH.get_cheapest_recruit_cost()
|
local cheapest_unit_cost = AH.get_cheapest_recruit_cost()
|
||||||
|
|
||||||
|
local avoid_map = AH.get_avoid_map(ai, nil, true)
|
||||||
|
|
||||||
if data.leader_target and wesnoth.sides[wesnoth.current.side].gold >= cheapest_unit_cost then
|
if data.leader_target and wesnoth.sides[wesnoth.current.side].gold >= cheapest_unit_cost then
|
||||||
-- make sure move is still valid
|
-- make sure move is still valid
|
||||||
local next_hop = AH.next_hop(leader, data.leader_target[1], data.leader_target[2])
|
local path, cost = AH.find_path_with_avoid(leader, data.leader_target[1], data.leader_target[2], avoid_map)
|
||||||
|
local next_hop = AH.next_hop(leader, nil, nil, { path = path, avoid_map = avoid_map })
|
||||||
if next_hop and next_hop[1] == data.leader_target[1]
|
if next_hop and next_hop[1] == data.leader_target[1]
|
||||||
and next_hop[2] == data.leader_target[2] then
|
and next_hop[2] == data.leader_target[2] then
|
||||||
return CS_leader_score
|
return CS_leader_score
|
||||||
|
else
|
||||||
|
data.leader_target = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -81,13 +89,13 @@ function ca_castle_switch:evaluation(cfg, data, filter_own)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local enemy_leaders = get_reachable_enemy_leaders(leader)
|
local enemy_leaders = get_reachable_enemy_leaders(leader, avoid_map)
|
||||||
|
|
||||||
-- Look for the best keep
|
-- Look for the best keep
|
||||||
local best_score, best_loc, best_turns = 0, {}, 3
|
local best_score, best_loc, best_turns, best_path = 0, {}, 3
|
||||||
for i,loc in ipairs(keeps) do
|
for i,loc in ipairs(keeps) do
|
||||||
-- Only consider keeps within 2 turns movement
|
-- Only consider keeps within 2 turns movement
|
||||||
local path, cost = wesnoth.find_path(leader, loc[1], loc[2])
|
local path, cost = AH.find_path_with_avoid(leader, loc[1], loc[2], avoid_map)
|
||||||
local score = 0
|
local score = 0
|
||||||
-- Prefer closer keeps to enemy
|
-- Prefer closer keeps to enemy
|
||||||
local turns = math.ceil(cost/leader.max_moves)
|
local turns = math.ceil(cost/leader.max_moves)
|
||||||
|
@ -101,6 +109,7 @@ function ca_castle_switch:evaluation(cfg, data, filter_own)
|
||||||
best_score = score
|
best_score = score
|
||||||
best_loc = loc
|
best_loc = loc
|
||||||
best_turns = turns
|
best_turns = turns
|
||||||
|
best_path = path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -130,7 +139,7 @@ function ca_castle_switch:evaluation(cfg, data, filter_own)
|
||||||
end
|
end
|
||||||
|
|
||||||
if best_score > 0 then
|
if best_score > 0 then
|
||||||
local next_hop = AH.next_hop(leader, best_loc[1], best_loc[2])
|
local next_hop = AH.next_hop(leader, nil, nil, { path = best_path, avoid_map = avoid_map })
|
||||||
|
|
||||||
if next_hop and ((next_hop[1] ~= leader.x) or (next_hop[2] ~= leader.y)) then
|
if next_hop and ((next_hop[1] ~= leader.x) or (next_hop[2] ~= leader.y)) then
|
||||||
-- See if there is a nearby village that can be captured without delaying progress
|
-- See if there is a nearby village that can be captured without delaying progress
|
||||||
|
@ -138,12 +147,12 @@ function ca_castle_switch:evaluation(cfg, data, filter_own)
|
||||||
{ "and", { x = next_hop[1], y = next_hop[2], radius = leader.max_moves }},
|
{ "and", { x = next_hop[1], y = next_hop[2], radius = leader.max_moves }},
|
||||||
owner_side = 0 })
|
owner_side = 0 })
|
||||||
for i,loc in ipairs(close_villages) do
|
for i,loc in ipairs(close_villages) do
|
||||||
local path_village, cost_village = wesnoth.find_path(leader, loc[1], loc[2])
|
local path_village, cost_village = AH.find_path_with_avoid(leader, loc[1], loc[2], avoid_map)
|
||||||
if cost_village <= leader.moves then
|
if cost_village <= leader.moves then
|
||||||
local dummy_leader = leader:clone()
|
local dummy_leader = leader:clone()
|
||||||
dummy_leader.x = loc[1]
|
dummy_leader.x = loc[1]
|
||||||
dummy_leader.y = loc[2]
|
dummy_leader.y = loc[2]
|
||||||
local path_keep, cost_keep = wesnoth.find_path(dummy_leader, best_loc[1], best_loc[2])
|
local path_keep, cost_keep = wesnoth.find_path(dummy_leader, best_loc[1], best_loc[2], avoid_map)
|
||||||
local turns_from_keep = math.ceil(cost_keep/leader.max_moves)
|
local turns_from_keep = math.ceil(cost_keep/leader.max_moves)
|
||||||
if turns_from_keep < best_turns
|
if turns_from_keep < best_turns
|
||||||
or (turns_from_keep == 1 and wesnoth.sides[wesnoth.current.side].gold < cheapest_unit_cost)
|
or (turns_from_keep == 1 and wesnoth.sides[wesnoth.current.side].gold < cheapest_unit_cost)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||||
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
||||||
|
local LS = wesnoth.require "location_set"
|
||||||
local M = wesnoth.map
|
local M = wesnoth.map
|
||||||
|
|
||||||
local GV_unit, GV_village
|
local GV_unit, GV_village
|
||||||
|
@ -25,8 +26,15 @@ function ca_grab_villages:evaluation(cfg, data, filter_own)
|
||||||
|
|
||||||
local enemies = AH.get_attackable_enemies()
|
local enemies = AH.get_attackable_enemies()
|
||||||
|
|
||||||
local villages = wesnoth.get_villages()
|
local avoid_map = LS.of_pairs(ai.aspects.avoid)
|
||||||
-- Just in case:
|
|
||||||
|
local all_villages, villages = wesnoth.get_villages(), {}
|
||||||
|
for _,village in ipairs(all_villages) do
|
||||||
|
if (not avoid_map:get(village[1], village[2])) then
|
||||||
|
table.insert(villages, village)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if (not villages[1]) then
|
if (not villages[1]) then
|
||||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
-- only kicks in when the AI would do nothing else. It prevents the AI from
|
-- only kicks in when the AI would do nothing else. It prevents the AI from
|
||||||
-- being inactive on maps without enemy leaders and villages.
|
-- being inactive on maps without enemy leaders and villages.
|
||||||
|
|
||||||
|
local H = wesnoth.require "helper"
|
||||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||||
|
|
||||||
local MTAE_unit, MTAE_destination
|
local MTAE_unit, MTAE_destination
|
||||||
|
@ -25,41 +26,52 @@ function ca_move_to_any_enemy:evaluation(cfg, data, filter_own)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local avoid_map = AH.get_avoid_map(ai, nil, true)
|
||||||
|
|
||||||
|
-- In principle we don't even need to pass avoid_map here, as the loop below also
|
||||||
|
-- checks this, but we might as well eliminate unreachable enemies right away
|
||||||
|
local enemies = AH.get_attackable_enemies({}, wesnoth.current.sude, { avoid_map = avoid_map })
|
||||||
|
|
||||||
local unit, destination
|
local unit, destination
|
||||||
-- Find a unit that has a path to an space close to an enemy
|
-- Find first unit that can reach a hex adjacent to an enemy, and find closest enemy of those reachable.
|
||||||
|
-- This does not need to find the absolutely best combination, close to that is good enough.
|
||||||
for i,u in ipairs(units) do
|
for i,u in ipairs(units) do
|
||||||
local target = AH.get_closest_enemy({u.x, u.y})
|
local best_cost, best_path, best_enemy = AH.no_path
|
||||||
if target then
|
for i,e in ipairs(enemies) do
|
||||||
unit = u
|
-- We only need to look at adjacent hexes. And we don't worry whether they
|
||||||
|
-- are occupied by other enemies. If that is the case, no path will be found,
|
||||||
local x, y = wesnoth.find_vacant_tile(target.x, target.y)
|
-- but one of those enemies will later be found as potential target.
|
||||||
destination = AH.next_hop(unit, x, y)
|
for xa,ya in H.adjacent_tiles(e.x, e.y) do
|
||||||
if (destination[1] == unit.x) and (destination[2] == unit.y) then
|
if (not avoid_map:get(xa, ya)) then
|
||||||
destination = nil
|
local path, cost = AH.find_path_with_avoid(u, xa, ya, avoid_map)
|
||||||
|
if (cost < best_cost) then
|
||||||
|
best_cost = cost
|
||||||
|
best_path = path
|
||||||
|
best_enemy = e
|
||||||
|
-- We also don't care if this is the closest adjacent hex, just pick the first found
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if destination then
|
if best_enemy then
|
||||||
break
|
MTAE_destination = AH.next_hop(u, nil, nil, { path = best_path, avoid_map = avoid_map })
|
||||||
|
if (MTAE_destination[1] ~= u.x) or (MTAE_destination[2] ~= u.y) then
|
||||||
|
MTAE_unit = u
|
||||||
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
|
return 1000
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if (not destination) then
|
|
||||||
-- No path was found
|
|
||||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
MTAE_destination = destination
|
|
||||||
MTAE_unit = unit
|
|
||||||
|
|
||||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
return 1000
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function ca_move_to_any_enemy:execution(cfg, data)
|
function ca_move_to_any_enemy:execution(cfg, data)
|
||||||
if AH.print_exec() then AH.print_ts(' Executing move_to_any_enemy CA') end
|
if AH.print_exec() then AH.print_ts(' Executing move_to_any_enemy CA') end
|
||||||
AH.checked_move(ai, MTAE_unit, MTAE_destination[1], MTAE_destination[2])
|
AH.checked_move_full(ai, MTAE_unit, MTAE_destination[1], MTAE_destination[2])
|
||||||
MTAE_unit, MTAE_destination = nil,nil
|
MTAE_unit, MTAE_destination = nil,nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
------- Retreat CA --------------
|
------- Retreat CA --------------
|
||||||
|
|
||||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||||
|
local LS = wesnoth.require "location_set"
|
||||||
local R = wesnoth.require "ai/lua/retreat.lua"
|
local R = wesnoth.require "ai/lua/retreat.lua"
|
||||||
|
|
||||||
local retreat_unit, retreat_loc
|
local retreat_unit, retreat_loc
|
||||||
|
@ -15,7 +16,8 @@ function ca_retreat_injured:evaluation(cfg, data, filter_own)
|
||||||
side = wesnoth.current.side,
|
side = wesnoth.current.side,
|
||||||
{ "and", filter_own }
|
{ "and", filter_own }
|
||||||
}, true)
|
}, true)
|
||||||
local unit, loc = R.retreat_injured_units(units)
|
local avoid_map = LS.of_pairs(ai.aspects.avoid)
|
||||||
|
local unit, loc = R.retreat_injured_units(units, avoid_map)
|
||||||
if unit then
|
if unit then
|
||||||
retreat_unit = unit
|
retreat_unit = unit
|
||||||
retreat_loc = loc
|
retreat_loc = loc
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
------- Village Hunt CA --------------
|
------- Village Hunt CA --------------
|
||||||
-- Give extra priority to seeking villages if we have less than our share
|
-- Give extra priority to seeking villages if we have less than our share.
|
||||||
-- our share is defined as being slightly more than the total/the number of sides
|
-- Our share is defined as being slightly more than the total/the number of sides,
|
||||||
|
-- but only in the area not prohibited by an [avoid] directive.
|
||||||
|
|
||||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||||
|
local LS = wesnoth.require "location_set"
|
||||||
|
|
||||||
|
local VH_unit, VH_dst = {}, {}
|
||||||
|
|
||||||
local ca_village_hunt = {}
|
local ca_village_hunt = {}
|
||||||
|
|
||||||
|
@ -10,22 +14,37 @@ function ca_village_hunt:evaluation(cfg, data, filter_own)
|
||||||
local start_time, ca_name = wesnoth.get_time_stamp() / 1000., 'village_hunt'
|
local start_time, ca_name = wesnoth.get_time_stamp() / 1000., 'village_hunt'
|
||||||
if AH.print_eval() then AH.print_ts(' - Evaluating village_hunt CA:') end
|
if AH.print_eval() then AH.print_ts(' - Evaluating village_hunt CA:') end
|
||||||
|
|
||||||
local villages = wesnoth.get_villages()
|
local avoid_map = LS.of_pairs(ai.aspects.avoid)
|
||||||
|
|
||||||
if not villages[1] then
|
local all_villages, villages = wesnoth.get_villages(), {}
|
||||||
|
for _,village in ipairs(all_villages) do
|
||||||
|
if (not avoid_map:get(village[1], village[2])) then
|
||||||
|
table.insert(villages, village)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not villages[1]) then
|
||||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local my_villages = wesnoth.get_villages { owner_side = wesnoth.current.side }
|
local n_my_villages, n_allied_villages = 0, 0
|
||||||
|
for _,village in ipairs(villages) do
|
||||||
|
local owner = wesnoth.get_village_owner(village[1], village[2]) or -1
|
||||||
|
if (owner == wesnoth.current.side) then
|
||||||
|
n_my_villages = n_my_villages + 1
|
||||||
|
end
|
||||||
|
if (owner ~= -1) and (not wesnoth.sides.is_enemy(owner, wesnoth.current.side)) then
|
||||||
|
n_allied_villages = n_allied_villages + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if #my_villages > #villages / #wesnoth.sides then
|
if (n_my_villages > #villages / #wesnoth.sides) then
|
||||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local allied_villages = wesnoth.get_villages { {"filter_owner", { {"ally_of", { side = wesnoth.current.side }} }} }
|
if (n_allied_villages == #villages) then
|
||||||
if #allied_villages == #villages then
|
|
||||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
@ -36,7 +55,32 @@ function ca_village_hunt:evaluation(cfg, data, filter_own)
|
||||||
{ "and", filter_own }
|
{ "and", filter_own }
|
||||||
}, true)
|
}, true)
|
||||||
|
|
||||||
if not units[1] then
|
if (not units[1]) then
|
||||||
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local avoid_map = AH.get_avoid_map(ai, nil, true)
|
||||||
|
|
||||||
|
VH_unit = nil
|
||||||
|
for _,unit in ipairs(units) do
|
||||||
|
local best_cost = AH.no_path
|
||||||
|
for i,v in ipairs(villages) do
|
||||||
|
if not wesnoth.match_location(v[1], v[2], { {"filter_owner", { {"ally_of", { side = wesnoth.current.side }} }} }) then
|
||||||
|
local path, cost = AH.find_path_with_avoid(unit, v[1], v[2], avoid_map)
|
||||||
|
if (cost < best_cost) then
|
||||||
|
local dst = AH.next_hop(unit, nil, nil, { path = path, avoid_map = avoid_map })
|
||||||
|
if (dst[1] ~= unit.x) or (dst[2] ~= unit.y) then
|
||||||
|
best_cost = cost
|
||||||
|
VH_unit = unit
|
||||||
|
VH_dst = dst
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not VH_unit) then
|
||||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
@ -46,31 +90,10 @@ function ca_village_hunt:evaluation(cfg, data, filter_own)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ca_village_hunt:execution(cfg, data, filter_own)
|
function ca_village_hunt:execution(cfg, data, filter_own)
|
||||||
local unit = AH.get_units_with_moves({
|
|
||||||
side = wesnoth.current.side,
|
|
||||||
canrecruit = 'no',
|
|
||||||
{ "and", filter_own }
|
|
||||||
}, true)[1]
|
|
||||||
|
|
||||||
if AH.print_exec() then AH.print_ts(' Executing village_hunt CA') end
|
if AH.print_exec() then AH.print_ts(' Executing village_hunt CA') end
|
||||||
|
|
||||||
local villages = wesnoth.get_villages()
|
AH.checked_move_full(ai, VH_unit, VH_dst[1], VH_dst[2])
|
||||||
local best_cost, target = AH.no_path
|
VH_unit, VH_dst = nil, nil
|
||||||
for i,v in ipairs(villages) do
|
|
||||||
if not wesnoth.match_location(v[1], v[2], { {"filter_owner", { {"ally_of", { side = wesnoth.current.side }} }} }) then
|
|
||||||
local path, cost = wesnoth.find_path(unit, v[1], v[2], { ignore_units = true, max_cost = best_cost })
|
|
||||||
if cost < best_cost then
|
|
||||||
target = v
|
|
||||||
best_cost = cost
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if target then
|
|
||||||
local x, y = wesnoth.find_vacant_tile(target[1], target[2], unit)
|
|
||||||
local dest = AH.next_hop(unit, x, y)
|
|
||||||
AH.checked_move(ai, unit, dest[1], dest[2])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return ca_village_hunt
|
return ca_village_hunt
|
||||||
|
|
|
@ -33,7 +33,7 @@ end
|
||||||
|
|
||||||
-- Given a set of units, return one from the set that should retreat and the location to retreat to
|
-- Given a set of units, return one from the set that should retreat and the location to retreat to
|
||||||
-- Return nil if no unit needs to retreat
|
-- Return nil if no unit needs to retreat
|
||||||
function retreat_functions.retreat_injured_units(units)
|
function retreat_functions.retreat_injured_units(units, avoid_map)
|
||||||
-- Split units into those that regenerate and those that do not
|
-- Split units into those that regenerate and those that do not
|
||||||
local regen, regen_amounts, non_regen = {}, {}, {}
|
local regen, regen_amounts, non_regen = {}, {}, {}
|
||||||
for i,u in ipairs(units) do
|
for i,u in ipairs(units) do
|
||||||
|
@ -60,7 +60,7 @@ function retreat_functions.retreat_injured_units(units)
|
||||||
-- First we retreat non-regenerating units to healing terrain, if they can get to a safe location
|
-- First we retreat non-regenerating units to healing terrain, if they can get to a safe location
|
||||||
local unit_nr, loc_nr, threat_nr
|
local unit_nr, loc_nr, threat_nr
|
||||||
if non_regen[1] then
|
if non_regen[1] then
|
||||||
unit_nr, loc_nr, threat_nr = retreat_functions.get_retreat_injured_units(non_regen, {})
|
unit_nr, loc_nr, threat_nr = retreat_functions.get_retreat_injured_units(non_regen, {}, avoid_map)
|
||||||
if unit_nr and (threat_nr == 0) then
|
if unit_nr and (threat_nr == 0) then
|
||||||
return unit_nr, loc_nr, threat_nr
|
return unit_nr, loc_nr, threat_nr
|
||||||
end
|
end
|
||||||
|
@ -69,7 +69,7 @@ function retreat_functions.retreat_injured_units(units)
|
||||||
-- Then we retreat regenerating units to terrain with high defense, if they can get to a safe location
|
-- Then we retreat regenerating units to terrain with high defense, if they can get to a safe location
|
||||||
local unit_r, loc_r, threat_r
|
local unit_r, loc_r, threat_r
|
||||||
if regen[1] then
|
if regen[1] then
|
||||||
unit_r, loc_r, threat_r = retreat_functions.get_retreat_injured_units(regen, regen_amounts)
|
unit_r, loc_r, threat_r = retreat_functions.get_retreat_injured_units(regen, regen_amounts, avoid_map)
|
||||||
if unit_r and (threat_r == 0) then
|
if unit_r and (threat_r == 0) then
|
||||||
return unit_r, loc_r, threat_r
|
return unit_r, loc_r, threat_r
|
||||||
end
|
end
|
||||||
|
@ -118,7 +118,7 @@ function retreat_functions.get_healing_locations()
|
||||||
return healing_locs
|
return healing_locs
|
||||||
end
|
end
|
||||||
|
|
||||||
function retreat_functions.get_retreat_injured_units(healees, regen_amounts)
|
function retreat_functions.get_retreat_injured_units(healees, regen_amounts, avoid_map)
|
||||||
-- Only retreat to safe locations
|
-- Only retreat to safe locations
|
||||||
local enemies = AH.get_attackable_enemies()
|
local enemies = AH.get_attackable_enemies()
|
||||||
local enemy_attack_map = BC.get_attack_map(enemies)
|
local enemy_attack_map = BC.get_attack_map(enemies)
|
||||||
|
@ -134,20 +134,22 @@ function retreat_functions.get_retreat_injured_units(healees, regen_amounts)
|
||||||
-- Unit cannot self heal, make the terrain do it for us if possible
|
-- Unit cannot self heal, make the terrain do it for us if possible
|
||||||
local location_subset = {}
|
local location_subset = {}
|
||||||
for j,loc in ipairs(possible_locations) do
|
for j,loc in ipairs(possible_locations) do
|
||||||
local heal_amount = wesnoth.get_terrain_info(wesnoth.get_terrain(loc[1], loc[2])).healing or 0
|
if (not avoid_map) or (not avoid_map:get(loc[1], loc[2])) then
|
||||||
if heal_amount == true then
|
local heal_amount = wesnoth.get_terrain_info(wesnoth.get_terrain(loc[1], loc[2])).healing or 0
|
||||||
-- handle deprecated syntax
|
if heal_amount == true then
|
||||||
-- TODO: remove this when removed from game
|
-- handle deprecated syntax
|
||||||
heal_amount = 8
|
-- TODO: remove this when removed from game
|
||||||
|
heal_amount = 8
|
||||||
|
end
|
||||||
|
local curing = 0
|
||||||
|
if heal_amount > 0 then
|
||||||
|
curing = 2
|
||||||
|
end
|
||||||
|
local healer_values = healing_locs:get(loc[1], loc[2]) or {0, 0}
|
||||||
|
heal_amount = math.max(heal_amount, healer_values[1])
|
||||||
|
curing = math.max(curing, healer_values[2])
|
||||||
|
table.insert(location_subset, {loc[1], loc[2], heal_amount, curing})
|
||||||
end
|
end
|
||||||
local curing = 0
|
|
||||||
if heal_amount > 0 then
|
|
||||||
curing = 2
|
|
||||||
end
|
|
||||||
local healer_values = healing_locs:get(loc[1], loc[2]) or {0, 0}
|
|
||||||
heal_amount = math.max(heal_amount, healer_values[1])
|
|
||||||
curing = math.max(curing, healer_values[2])
|
|
||||||
table.insert(location_subset, {loc[1], loc[2], heal_amount, curing})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
possible_locations = location_subset
|
possible_locations = location_subset
|
||||||
|
|
Loading…
Add table
Reference in a new issue