Skip to content

Commit

Permalink
ai_helper.next_hop: let units fan out
Browse files Browse the repository at this point in the history
... as opposed to lining up if there are allied units in the way.

This changes (fixes) the default behavior. An optional 'no_fan_out' parameter is provided in order to restore the old behavior.
  • Loading branch information
mattsc committed Nov 29, 2019
1 parent a4b2af0 commit 4e27134
Showing 1 changed file with 50 additions and 0 deletions.
50 changes: 50 additions & 0 deletions data/ai/lua/ai_helper.lua
Expand Up @@ -1410,6 +1410,12 @@ function ai_helper.next_hop(unit, x, y, cfg)
-- path: if given, find the next hop along this path, rather than doing new path finding
-- In this case, it is assumed that the path is possible, in other words, that cost has been checked
-- avoid_map: a location set with the hexes the unit is not allowed to step on
-- no_fan_out: prior to Wesnoth 1.16, the unit strictly followed the path, which can lead to
-- a line-up of units if there are allied units in the way (e.g. when multiple units are
-- moved by the same candidate action. Now they fan out instead, trying to get as close to
-- the ideal next_hop goal (defined as where the unit could get if there were no allied units
-- in the way) as possible. Setting 'no_fan_out=true' restores the old behavior. The main
-- disadvantage of the new method is that it needs to do more path finding and therefore takes longer.

local path, cost
if cfg and cfg.path then
Expand All @@ -1421,6 +1427,7 @@ function ai_helper.next_hop(unit, x, y, cfg)

-- If none of the hexes are unoccupied, use current position as default
local next_hop, nh_cost = { unit.x, unit.y }, 0
local next_hop_ideal = { unit.x, unit.y }

-- Go through loop to find reachable, unoccupied hex along the path
-- Start at second index, as first is just the unit position itself
Expand All @@ -1446,12 +1453,55 @@ function ai_helper.next_hop(unit, x, y, cfg)
if not unit_in_way then
next_hop, nh_cost = path[i], sub_cost
end
next_hop_ideal = path[i]
else
break
end
end
end

if ((not cfg) or (not cfg.no_fan_out))
and ((next_hop[1] ~= next_hop_ideal[1]) or (next_hop[2] ~= next_hop_ideal[2]))
then
-- If we cannot get to the ideal next hop, try fanning out instead
local reach = wesnoth.find_reach(unit, cfg)

-- Need the reach map of the unit from the ideal next hop hex
-- There will always be another unit there, otherwise we would not have gotten here
local unit_in_way = wesnoth.get_unit(next_hop_ideal[1], next_hop_ideal[2])
unit_in_way:extract()
local old_x, old_y = unit.x, unit.y
unit:extract()
unit:to_map(next_hop_ideal[1], next_hop_ideal[2])
local inverse_reach = wesnoth.find_reach(unit, { ignore_units = true }) -- no ZoC
unit:extract()
unit:to_map(old_x, old_y)
unit_in_way:to_map()

local inverse_reach_map = LS.create()
for _,r in pairs(inverse_reach) do inverse_reach_map:insert(r[1], r[2], r[3]) end

local units = ai_helper.get_visible_units(
cfg and cfg.viewing_side or unit.side,
{ { "not", { id = unit.id } }
})
local unit_map = LS.create()
for _,u in ipairs(units) do unit_map:insert(u.x, u.y, u.id) end

local max_rating = - math.huge
for _,loc in ipairs(reach) do
if (not unit_map:get(loc[1], loc[2]))
and ((not cfg) or (not cfg.avoid_map) or (not cfg.avoid_map:get(loc[1], loc[2])))
then
local rating = inverse_reach_map:get(loc[1], loc[2]) or - math.huge
if (rating > max_rating) then
max_rating = rating
next_hop = { loc[1], loc[2] } -- eliminating the third argument
end
end
end
end

return next_hop, nh_cost
end

Expand Down

0 comments on commit 4e27134

Please sign in to comment.