Multipack Wolves MAI: add [avoid] tag functionality
This includes both adding an [avoid] tag to the MAI itself, and taking the default AI [avoid] tag into account. If both are given, the former takes precedence over the latter.
This commit is contained in:
parent
d7418ab8e2
commit
873bf2f390
3 changed files with 56 additions and 24 deletions
|
@ -23,6 +23,8 @@ end
|
|||
function ca_wolves_multipacks_attack:execution(cfg)
|
||||
local packs = WMPF.assign_packs(cfg)
|
||||
|
||||
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
|
||||
|
||||
-- Attacks are dealt with on a pack by pack basis
|
||||
-- and we want all wolves in a pack to move first, before going on to the next pack
|
||||
for pack_number,pack in pairs(packs) do
|
||||
|
@ -41,20 +43,23 @@ function ca_wolves_multipacks_attack:execution(cfg)
|
|||
local all_attacks = {}
|
||||
if wolves[1] then all_attacks = AH.get_attacks(wolves, { simulate_combat = true }) end
|
||||
|
||||
-- Eliminate targets that would split up the wolves by more than 3 hexes
|
||||
-- Eliminate targets that would split up the wolves by more than 3 hexes.
|
||||
-- Also eliminate attacks in areas specified by [avoid] tags.
|
||||
local attacks = {}
|
||||
for _,attack in ipairs(all_attacks) do
|
||||
local attack_splits_pack = false
|
||||
for _,wolf in ipairs(wolves) do
|
||||
local nh = AH.next_hop(wolf, attack.dst.x, attack.dst.y)
|
||||
local dist = M.distance_between(nh[1], nh[2], attack.dst.x, attack.dst.y)
|
||||
if (dist > 3) then
|
||||
attack_splits_pack = true
|
||||
break
|
||||
if (not avoid_map:get(attack.dst.x, attack.dst.y)) then
|
||||
local attack_splits_pack = false
|
||||
for _,wolf in ipairs(wolves) do
|
||||
local nh = AH.next_hop(wolf, attack.dst.x, attack.dst.y)
|
||||
local dist = M.distance_between(nh[1], nh[2], attack.dst.x, attack.dst.y)
|
||||
if (dist > 3) then
|
||||
attack_splits_pack = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if (not attack_splits_pack) then
|
||||
table.insert(attacks, attack)
|
||||
end
|
||||
end
|
||||
if (not attack_splits_pack) then
|
||||
table.insert(attacks, attack)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -169,13 +174,13 @@ function ca_wolves_multipacks_attack:execution(cfg)
|
|||
rating = rating - M.distance_between(x, y, wolf_no_moves.x, wolf_no_moves.y)
|
||||
end
|
||||
return rating
|
||||
end)
|
||||
end, { avoid_map = avoid_map })
|
||||
|
||||
if cfg.show_pack_number then
|
||||
WMPF.clear_label(wolf_moves.x, wolf_moves.y)
|
||||
end
|
||||
|
||||
AH.movefull_stopunit(ai, wolf_moves, best_hex)
|
||||
AH.movefull_stopunit(ai, wolf_moves, best_hex or { wolf_moves.x, wolf_moves.y })
|
||||
|
||||
if cfg.show_pack_number and wolf_moves and wolf_moves.valid then
|
||||
WMPF.put_label(wolf_moves.x, wolf_moves.y, pack_number)
|
||||
|
|
|
@ -21,6 +21,8 @@ end
|
|||
function ca_wolves_multipacks_wander:execution(cfg)
|
||||
local packs = WMPF.assign_packs(cfg)
|
||||
|
||||
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
|
||||
|
||||
for pack_number,pack in pairs(packs) do
|
||||
-- If any of the wolves has a goal set, this is used for the entire pack
|
||||
local wolves, goal = {}, {}
|
||||
|
@ -43,21 +45,38 @@ function ca_wolves_multipacks_wander:execution(cfg)
|
|||
-- Pack gets a new goal if none exist or on any move with 10% random chance
|
||||
local rand = math.random(10)
|
||||
if (not goal[1]) or (rand == 1) then
|
||||
local locs = AH.get_locations_no_borders {}
|
||||
local all_locs = AH.get_locations_no_borders {}
|
||||
local locs = {}
|
||||
for _,loc in ipairs(all_locs) do
|
||||
if (not avoid_map:get(loc[1], loc[2])) then
|
||||
table.insert(locs, loc)
|
||||
end
|
||||
end
|
||||
|
||||
-- Need to find reachable terrain for this to be a viable goal
|
||||
-- We only check whether the first wolf can get there
|
||||
local unreachable = true
|
||||
while unreachable do
|
||||
while locs[1] and unreachable do
|
||||
local rand = math.random(#locs)
|
||||
local next_hop = AH.next_hop(wolves[1], locs[rand][1], locs[rand][2])
|
||||
if next_hop then
|
||||
goal = { locs[rand][1], locs[rand][2] }
|
||||
unreachable = nil
|
||||
else
|
||||
table.remove(locs, rand)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- It is possible that no goal is found here due to the combination of
|
||||
-- impassable terrain and [avoid] instructions
|
||||
if (not goal[1]) then
|
||||
for _,wolf in ipairs(wolves) do
|
||||
AH.checked_stopunit_moves(ai, wolf)
|
||||
end
|
||||
wolves = {}
|
||||
end
|
||||
|
||||
-- This goal is saved with every wolf of the pack
|
||||
for _,wolf in ipairs(wolves) do
|
||||
MAIUV.insert_mai_unit_variables(wolf, cfg.ai_id, { goal_x = goal[1], goal_y = goal[2] })
|
||||
|
@ -72,10 +91,10 @@ function ca_wolves_multipacks_wander:execution(cfg)
|
|||
-- Number of wolves that can reach each hex,
|
||||
local reach_map = LS.create()
|
||||
for _,wolf in ipairs(wolves) do
|
||||
local reach = wesnoth.find_reach(wolf)
|
||||
for _,loc in ipairs(reach) do
|
||||
reach_map:insert(loc[1], loc[2], (reach_map:get(loc[1], loc[2]) or 0) + 100)
|
||||
end
|
||||
local single_reach_map = AH.get_reachmap(wolf, { avoid_map = avoid_map })
|
||||
single_reach_map:iter( function(x, y, v)
|
||||
reach_map:insert(x, y, (reach_map:get(x, y) or 0) + 100)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Keep only those hexes that can be reached by all wolves in the pack
|
||||
|
@ -100,7 +119,7 @@ function ca_wolves_multipacks_wander:execution(cfg)
|
|||
-- If there's no hex that all units can reach, use the 'center of gravity' between them
|
||||
-- Then we move the first wolf (fewest MP) toward that hex, and the position of that wolf
|
||||
-- becomes the goto coordinates for the others
|
||||
if (not goto_hex) then
|
||||
if wolves[1] and (not goto_hex) then
|
||||
local cg = { 0, 0 } -- Center of gravity hex
|
||||
for _,wolf in ipairs(wolves) do
|
||||
cg = { cg[1] + wolf.x, cg[2] + wolf.y }
|
||||
|
@ -111,11 +130,19 @@ function ca_wolves_multipacks_wander:execution(cfg)
|
|||
-- Find closest move for Wolf #1 to that position, which then becomes the goto hex
|
||||
goto_hex = AH.find_best_move(wolves[1], function(x, y)
|
||||
return -M.distance_between(x, y, cg[1], cg[2])
|
||||
end)
|
||||
end, { avoid_map = avoid_map })
|
||||
-- We could move this wolf right here, but for convenience all the actual moves
|
||||
-- are done together below. This should be a small extra calculation cost
|
||||
end
|
||||
|
||||
-- It is possible here also that no goto_hex is found
|
||||
if (not goto_hex) then
|
||||
for _,wolf in ipairs(wolves) do
|
||||
AH.checked_stopunit_moves(ai, wolf)
|
||||
end
|
||||
wolves = {}
|
||||
end
|
||||
|
||||
-- Now all wolves in the pack are moved toward goto_hex, starting with the one with fewest MP
|
||||
-- Distance to goal hex is taken into account as secondary criterion
|
||||
for _,wolf in ipairs(wolves) do
|
||||
|
@ -123,13 +150,13 @@ function ca_wolves_multipacks_wander:execution(cfg)
|
|||
local rating = -M.distance_between(x, y, goto_hex[1], goto_hex[2])
|
||||
rating = rating -M.distance_between(x, y, goal[1], goal[2]) / 100.
|
||||
return rating
|
||||
end)
|
||||
end, { avoid_map = avoid_map })
|
||||
|
||||
if cfg.show_pack_number then
|
||||
WMPF.clear_label(wolf.x, wolf.y)
|
||||
end
|
||||
|
||||
local move_result = AH.movefull_stopunit(ai, wolf, best_hex)
|
||||
local move_result = AH.movefull_stopunit(ai, wolf, best_hex or { wolf.x, wolf.y })
|
||||
|
||||
if cfg.show_pack_number and wolf and wolf.valid then
|
||||
WMPF.put_label(wolf.x, wolf.y, pack_number)
|
||||
|
|
|
@ -154,7 +154,7 @@ function wesnoth.micro_ais.swarm(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.wolves_multipacks(cfg)
|
||||
local optional_keys = { "type", "pack_size", "show_pack_number" }
|
||||
local optional_keys = { "[avoid]", "type", "pack_size", "show_pack_number" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_wolves_multipacks',
|
||||
|
|
Loading…
Add table
Reference in a new issue