Messenger Micro AI: add [avoid] tag functionality
This commit is contained in:
parent
0c4d8ebbe6
commit
a60736b6c7
4 changed files with 65 additions and 48 deletions
|
@ -4,13 +4,13 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local messenger_next_waypoint = wesnoth.require "ai/micro_ais/cas/ca_messenger_f_next_waypoint.lua"
|
||||
local best_attack
|
||||
|
||||
local function messenger_find_enemies_in_way(messenger, goal_x, goal_y)
|
||||
local function messenger_find_enemies_in_way(messenger, goal_x, goal_y, avoid_map)
|
||||
-- Returns the first unit on or next to the path of the messenger
|
||||
-- @messenger: proxy table for the messenger unit
|
||||
-- @goal_x,@goal_y: coordinates of the goal toward which the messenger moves
|
||||
-- Returns proxy table for the first unit found, or nil if none was found
|
||||
|
||||
local path, cost = AH.find_path_with_shroud(messenger, goal_x, goal_y, { ignore_units = true })
|
||||
local path, cost = AH.find_path_with_avoid(messenger, goal_x, goal_y, avoid_map, { ignore_enemies = true })
|
||||
if cost >= 42424242 then return end
|
||||
|
||||
-- The second path hex is the first that is important for the following analysis
|
||||
|
@ -23,7 +23,7 @@ local function messenger_find_enemies_in_way(messenger, goal_x, goal_y)
|
|||
|
||||
-- After that, go through adjacent hexes of all the other path hexes
|
||||
for i = 2,#path do
|
||||
local sub_path, sub_cost = AH.find_path_with_shroud(messenger, path[i][1], path[i][2], { ignore_units = true })
|
||||
local sub_path, sub_cost = AH.find_path_with_avoid(messenger, path[i][1], path[i][2], avoid_map, { ignore_enemies = true })
|
||||
if (sub_cost <= messenger.moves) then
|
||||
for xa,ya in H.adjacent_tiles(path[i][1], path[i][2]) do
|
||||
local enemy = wesnoth.units.get(xa, ya)
|
||||
|
@ -42,7 +42,9 @@ local function messenger_find_clearing_attack(messenger, goal_x, goal_y, cfg)
|
|||
-- @goal_x,@goal_y: coordinates of the goal toward which the messenger moves
|
||||
-- Returns proxy table containing the attack, or nil if none was found
|
||||
|
||||
local enemy_in_way = messenger_find_enemies_in_way(messenger, goal_x, goal_y)
|
||||
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
|
||||
|
||||
local enemy_in_way = messenger_find_enemies_in_way(messenger, goal_x, goal_y, avoid_map)
|
||||
if (not enemy_in_way) then return end
|
||||
|
||||
local filter = wml.get_child(cfg, "filter") or { id = cfg.id }
|
||||
|
@ -57,8 +59,9 @@ local function messenger_find_clearing_attack(messenger, goal_x, goal_y, cfg)
|
|||
|
||||
local max_rating = - math.huge
|
||||
for _,attack in ipairs(attacks) do
|
||||
if (attack.target.x == enemy_in_way.x) and (attack.target.y == enemy_in_way.y) then
|
||||
|
||||
if (attack.target.x == enemy_in_way.x) and (attack.target.y == enemy_in_way.y)
|
||||
and (not avoid_map:get(attack.dst.x, attack.dst.y))
|
||||
then
|
||||
-- Rating: expected HP of attacker and defender
|
||||
local rating = attack.att_stats.average_hp - 2 * attack.def_stats.average_hp
|
||||
|
||||
|
@ -74,16 +77,18 @@ local function messenger_find_clearing_attack(messenger, goal_x, goal_y, cfg)
|
|||
--> try to fight our way to that enemy
|
||||
for _,attack in ipairs(attacks) do
|
||||
-- Rating: expected HP of attacker and defender
|
||||
local rating = attack.att_stats.average_hp - 2 * attack.def_stats.average_hp
|
||||
if (not avoid_map:get(attack.dst.x, attack.dst.y)) then
|
||||
local rating = attack.att_stats.average_hp - 2 * attack.def_stats.average_hp
|
||||
|
||||
-- Give a huge bonus for closeness to enemy_in_way
|
||||
local tmp_defender = wesnoth.units.get(attack.target.x, attack.target.y)
|
||||
local dist = wesnoth.map.distance_between(enemy_in_way.x, enemy_in_way.y, tmp_defender.x, tmp_defender.y)
|
||||
-- Give a huge bonus for closeness to enemy_in_way
|
||||
local tmp_defender = wesnoth.units.get(attack.target.x, attack.target.y)
|
||||
local dist = wesnoth.map.distance_between(enemy_in_way.x, enemy_in_way.y, tmp_defender.x, tmp_defender.y)
|
||||
|
||||
rating = rating + 100. / dist
|
||||
rating = rating + 100. / dist
|
||||
|
||||
if (rating > max_rating) then
|
||||
max_rating, best_attack = rating, attack
|
||||
if (rating > max_rating) then
|
||||
max_rating, best_attack = rating, attack
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -108,7 +113,8 @@ function ca_messenger_attack:evaluation(cfg)
|
|||
end
|
||||
|
||||
function ca_messenger_attack:execution(cfg)
|
||||
AH.robust_move_and_attack(ai, best_attack.src, best_attack.dst, best_attack.target)
|
||||
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
|
||||
AH.robust_move_and_attack(ai, best_attack.src, best_attack.dst, best_attack.target, { avoid_map = avoid_map })
|
||||
best_attack = nil
|
||||
end
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ function ca_messenger_escort_move:execution(cfg)
|
|||
local escorts = get_escorts(cfg)
|
||||
local _, _, _, messengers = messenger_next_waypoint(cfg)
|
||||
|
||||
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
|
||||
|
||||
local enemies = AH.get_attackable_enemies()
|
||||
|
||||
local base_rating_map = LS.create()
|
||||
|
@ -44,40 +46,42 @@ function ca_messenger_escort_move:execution(cfg)
|
|||
local unit_rating = unit.max_moves / 100. + unit.hitpoints / 1000.
|
||||
|
||||
reach_map:iter( function(x, y, v)
|
||||
local base_rating = base_rating_map:get(x, y)
|
||||
if (not avoid_map:get(x, y)) or ((x == unit.x) and (y == unit.y)) then
|
||||
local base_rating = base_rating_map:get(x, y)
|
||||
|
||||
if (not base_rating) then
|
||||
base_rating = 0
|
||||
if (not base_rating) then
|
||||
base_rating = 0
|
||||
|
||||
-- Distance from messenger is most important; only closest messenger counts for this
|
||||
-- Give somewhat of a bonus for the messenger that has moved the farthest through the waypoints
|
||||
local max_messenger_rating = - math.huge
|
||||
for _,m in ipairs(messengers) do
|
||||
local messenger_rating = 1. / (M.distance_between(x, y, m.x, m.y) + 2.)
|
||||
local wp_rating = MAIUV.get_mai_unit_variables(m, cfg.ai_id, "wp_rating")
|
||||
messenger_rating = messenger_rating * 10. * (1. + wp_rating * 2.)
|
||||
-- Distance from messenger is most important; only closest messenger counts for this
|
||||
-- Give somewhat of a bonus for the messenger that has moved the farthest through the waypoints
|
||||
local max_messenger_rating = - math.huge
|
||||
for _,m in ipairs(messengers) do
|
||||
local messenger_rating = 1. / (M.distance_between(x, y, m.x, m.y) + 2.)
|
||||
local wp_rating = MAIUV.get_mai_unit_variables(m, cfg.ai_id, "wp_rating")
|
||||
messenger_rating = messenger_rating * 10. * (1. + wp_rating * 2.)
|
||||
|
||||
if (messenger_rating > max_messenger_rating) then
|
||||
max_messenger_rating = messenger_rating
|
||||
if (messenger_rating > max_messenger_rating) then
|
||||
max_messenger_rating = messenger_rating
|
||||
end
|
||||
end
|
||||
|
||||
base_rating = base_rating + max_messenger_rating
|
||||
|
||||
-- Distance from (sum of) enemies is important too
|
||||
-- This favors placing escort units between the messenger and close enemies
|
||||
for _,e in ipairs(enemies) do
|
||||
base_rating = base_rating + 1. / (M.distance_between(x, y, e.x, e.y) + 2.)
|
||||
end
|
||||
|
||||
base_rating_map:insert(x, y, base_rating)
|
||||
end
|
||||
|
||||
base_rating = base_rating + max_messenger_rating
|
||||
local rating = base_rating + unit_rating
|
||||
|
||||
-- Distance from (sum of) enemies is important too
|
||||
-- This favors placing escort units between the messenger and close enemies
|
||||
for _,e in ipairs(enemies) do
|
||||
base_rating = base_rating + 1. / (M.distance_between(x, y, e.x, e.y) + 2.)
|
||||
if (rating > max_rating) then
|
||||
max_rating = rating
|
||||
best_unit, best_hex = unit, { x, y }
|
||||
end
|
||||
|
||||
base_rating_map:insert(x, y, base_rating)
|
||||
end
|
||||
|
||||
local rating = base_rating + unit_rating
|
||||
|
||||
if (rating > max_rating) then
|
||||
max_rating = rating
|
||||
best_unit, best_hex = unit, { x, y }
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -16,23 +16,30 @@ end
|
|||
function ca_messenger_move:execution(cfg)
|
||||
local messenger, x, y = messenger_next_waypoint(cfg)
|
||||
|
||||
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
|
||||
|
||||
if (messenger.x ~= x) or (messenger.y ~= y) then
|
||||
local wp = AH.get_closest_location(
|
||||
{ x, y },
|
||||
{ { "not", { { "filter", { { "not", { side = wesnoth.current.side } } } } } } },
|
||||
messenger
|
||||
messenger, avoid_map
|
||||
)
|
||||
x, y = wp[1], wp[2]
|
||||
end
|
||||
|
||||
local next_hop = AH.next_hop(messenger, x, y, { ignore_own_units = true } )
|
||||
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
|
||||
|
||||
local path = AH.find_path_with_avoid(messenger, x, y, avoid_map)
|
||||
if (not path) then path = { { messenger.x, messenger.y } } end
|
||||
local next_hop = AH.next_hop(messenger, x, y, { path = path, avoid_map = avoid_map } )
|
||||
if (not next_hop) then next_hop = { messenger.x, messenger.y } end
|
||||
|
||||
-- Compare this to the "ideal path"
|
||||
local path = AH.find_path_with_shroud(messenger, x, y, { ignore_units = 'yes' })
|
||||
local path = AH.find_path_with_avoid(messenger, x, y, avoid_map, { ignore_enemies = true })
|
||||
if (not path) then path = { { messenger.x, messenger.y } } end
|
||||
local optimum_hop = { messenger.x, messenger.y }
|
||||
for _,step in ipairs(path) do
|
||||
local sub_path, sub_cost = AH.find_path_with_shroud(messenger, step[1], step[2])
|
||||
local sub_path, sub_cost = AH.find_path_with_avoid(messenger, step[1], step[2], avoid_map)
|
||||
if sub_cost > messenger.moves then
|
||||
break
|
||||
else
|
||||
|
@ -60,14 +67,14 @@ function ca_messenger_move:execution(cfg)
|
|||
if unit_in_way then unit_in_way:extract() end
|
||||
|
||||
messenger.loc = { next_hop[1], next_hop[2] }
|
||||
local _, cost1 = AH.find_path_with_shroud(messenger, x, y, { ignore_units = 'yes' })
|
||||
local _, cost1 = AH.find_path_with_avoid(messenger, x, y, avoid_map, { ignore_enemies = true })
|
||||
|
||||
local unit_in_way2 = wesnoth.units.get(optimum_hop[1], optimum_hop[2])
|
||||
if (unit_in_way2 == messenger) then unit_in_way2 = nil end
|
||||
if unit_in_way2 then unit_in_way2:extract() end
|
||||
|
||||
messenger.loc = { optimum_hop[1], optimum_hop[2] }
|
||||
local _, cost2 = AH.find_path_with_shroud(messenger, x, y, { ignore_units = 'yes' })
|
||||
local _, cost2 = AH.find_path_with_avoid(messenger, x, y, avoid_map, { ignore_enemies = true })
|
||||
|
||||
messenger.loc = { x_current, y_current }
|
||||
if unit_in_way then unit_in_way:to_map() end
|
||||
|
@ -78,7 +85,7 @@ function ca_messenger_move:execution(cfg)
|
|||
if (cost2 + messenger.max_moves/2 < cost1) then next_hop = optimum_hop end
|
||||
|
||||
if next_hop and ((next_hop[1] ~= messenger.x) or (next_hop[2] ~= messenger.y)) then
|
||||
AH.robust_move_and_attack(ai, messenger, next_hop)
|
||||
AH.robust_move_and_attack(ai, messenger, next_hop, nil, { avoid_map = avoid_map })
|
||||
else
|
||||
AH.checked_stopunit_moves(ai, messenger)
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ function wesnoth.micro_ais.messenger_escort(cfg)
|
|||
AH.get_multi_named_locs_xy('waypoint', cfg, 'Messenger [micro_ai] tag')
|
||||
end
|
||||
local required_keys = {}
|
||||
local optional_keys = { "id", "enemy_death_chance", "[filter]", "[filter_second]", "invert_order", "messenger_death_chance", "waypoint_loc", "waypoint_x", "waypoint_y" }
|
||||
local optional_keys = { "[avoid]", "id", "enemy_death_chance", "[filter]", "[filter_second]", "invert_order", "messenger_death_chance", "waypoint_loc", "waypoint_x", "waypoint_y" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_messenger',
|
||||
|
|
Loading…
Add table
Reference in a new issue