Fix segfaults and a few other issues in wesnoth.find_path
Co-authored-by: mattsc
This commit is contained in:
parent
05b2ea2262
commit
56bdd42815
9 changed files with 158 additions and 113 deletions
|
@ -9,20 +9,17 @@ local M = wesnoth.map
|
|||
-- development releases, but it is of course easily possible to copy a function
|
||||
-- from a previous release directly into an add-on if it is needed there.
|
||||
--
|
||||
-- Invisible units ('viewing_side' parameter):
|
||||
-- Invisible units ('viewing_side' and 'ignore_visibility' parameters):
|
||||
-- With their default settings, the ai_helper functions use the vision a player of
|
||||
-- the respective side would see, that is, they assume no knowledge of invisible
|
||||
-- units. This can be influenced with the 'viewing_side' parameter, which works
|
||||
-- in the same way as it does in wesnoth.find_reach() and wesnoth.find_path():
|
||||
-- - If set to a valid side number, vision for that side is used
|
||||
-- - If set to an invalid side number (e.g. 0), all units on the map are seen
|
||||
-- - If omitted and a function takes a a parameter linked to a specific side,
|
||||
-- such as a side number or a unit, as input, vision of that side is used. In
|
||||
-- this case, viewing_side is passed as part of the optional @cfg configuration
|
||||
-- table and can be passed from function to function.
|
||||
-- - If omitted and the function takes no such input, viewing_side is made a
|
||||
-- required parameter in order to avoid mismatches between the default values
|
||||
-- of different functions.
|
||||
-- units. This can be influenced with the 'viewing_side' and 'ignore_visibility' parameters,
|
||||
-- which work in the same way as they do in wesnoth.find_reach() and wesnoth.find_path():
|
||||
-- - If 'viewing_side' is set, vision for that side is used. It must be set to a valid side number.
|
||||
-- - If 'ignore_visibility' is set to true, all units on the map are seen and shroud is ignored.
|
||||
-- This overrides 'viewing_side'.
|
||||
-- - If neither parameter is given and a function takes a parameter linked to a specific side,
|
||||
-- such as a side number or a unit, as input, vision of that side is used.
|
||||
-- - For some functions that take no other side-related input, 'viewing_side' is made a required parameter.
|
||||
--
|
||||
-- Path finding:
|
||||
-- All ai_helper functions disregard shroud for path finding (while still ignoring
|
||||
|
@ -1066,6 +1063,16 @@ end
|
|||
|
||||
--------- Unit related helper functions ----------
|
||||
|
||||
function ai_helper.check_viewing_side(viewing_side, function_str)
|
||||
-- Check that viewing_side is valid and set to an existing side
|
||||
if (not viewing_side) then
|
||||
error('ai_helper: missing required parameter viewing_side', 2)
|
||||
end
|
||||
if (type(viewing_side) ~= 'number') or (not wesnoth.sides[viewing_side]) then
|
||||
error('ai_helper: parameter viewing_side must be a valid side number', 2)
|
||||
end
|
||||
end
|
||||
|
||||
function ai_helper.is_passive_leader(aspect_value, id)
|
||||
if (type(aspect_value) == 'boolean') then return aspect_value end
|
||||
|
||||
|
@ -1110,25 +1117,19 @@ function ai_helper.get_visible_units(viewing_side, filter)
|
|||
-- Get units that are visible to side @viewing_side
|
||||
--
|
||||
-- Required parameters:
|
||||
-- @viewing_side: see comments at beginning of this file
|
||||
-- @viewing_side: must be set to a valid side number. If visibility is to be
|
||||
-- ignored, use wesnoth.get_units() instead.
|
||||
--
|
||||
-- Optional parameters:
|
||||
-- @filter: Standard unit filter WML table for the units
|
||||
-- Example 1: { type = 'Orcish Grunt' }
|
||||
-- Example 2: { { "filter_location", { x = 10, y = 12, radius = 5 } } }
|
||||
|
||||
if (not viewing_side) then
|
||||
error('ai_helper.get_visible_units() is missing required parameter viewing_side.', 2)
|
||||
end
|
||||
if (type(viewing_side) ~= 'number') then
|
||||
error('ai_helper.get_visible_units(): parameter viewing_side must be a number., 2')
|
||||
end
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
|
||||
local filter_plus_vision = {}
|
||||
if filter then filter_plus_vision = ai_helper.table_copy(filter) end
|
||||
if wesnoth.sides[viewing_side] then
|
||||
table.insert(filter_plus_vision, { "filter_vision", { side = viewing_side, visible = 'yes' } })
|
||||
end
|
||||
table.insert(filter_plus_vision, { "filter_vision", { side = viewing_side, visible = 'yes' } })
|
||||
|
||||
local units = {}
|
||||
local all_units = wesnoth.units.find_on_map()
|
||||
|
@ -1145,21 +1146,14 @@ function ai_helper.is_visible_unit(viewing_side, unit)
|
|||
-- Check whether @unit exists and is visible to side @viewing_side.
|
||||
--
|
||||
-- Required parameters:
|
||||
-- @viewing_side: see comments at beginning of this file.
|
||||
-- @viewing_side: must be set to a valid side number
|
||||
-- @unit: unit proxy table
|
||||
|
||||
if (not viewing_side) then
|
||||
error('ai_helper.is_visible_unit() is missing required parameter viewing_side.', 2)
|
||||
end
|
||||
if (type(viewing_side) ~= 'number') then
|
||||
error('ai_helper.is_visible_unit(): parameter viewing_side must be a number.', 2)
|
||||
end
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
|
||||
if (not unit) then return false end
|
||||
|
||||
if wesnoth.sides[viewing_side]
|
||||
and unit:matches({ { "filter_vision", { side = viewing_side, visible = 'no' } } })
|
||||
then
|
||||
if unit:matches({ { "filter_vision", { side = viewing_side, visible = 'no' } } }) then
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -1170,7 +1164,7 @@ function ai_helper.get_attackable_enemies(filter, side, cfg)
|
|||
-- Attackable enemies are defined as being being
|
||||
-- - enemies of the side defined in @side,
|
||||
-- - not petrified
|
||||
-- - and visible to the side defined in @cfg.viewing_side.
|
||||
-- - and visible to the side as defined in @cfg.viewing_side and @cfg.ignore_visibility.
|
||||
-- - have at least one adjacent hex that is not inside an area to avoid
|
||||
-- For speed reasons, this is done separately, rather than calling ai_helper.get_visible_units().
|
||||
--
|
||||
|
@ -1181,15 +1175,18 @@ function ai_helper.get_attackable_enemies(filter, side, cfg)
|
|||
-- @side: side number, if side other than current side is to be considered
|
||||
-- @cfg: table with optional configuration parameters:
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to @side.
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
-- avoid_map: if given, an enemy is included only if it does not have at least one
|
||||
-- adjacent hex outside of avoid_map
|
||||
|
||||
side = side or wesnoth.current.side
|
||||
local viewing_side = cfg and cfg.viewing_side or side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
local filter_plus_vision = {}
|
||||
if filter then filter_plus_vision = ai_helper.table_copy(filter) end
|
||||
if wesnoth.sides[viewing_side] then
|
||||
if (not ignore_visibility) then
|
||||
table.insert(filter_plus_vision, { "filter_vision", { side = viewing_side, visible = 'yes' } })
|
||||
end
|
||||
|
||||
|
@ -1220,21 +1217,24 @@ function ai_helper.get_attackable_enemies(filter, side, cfg)
|
|||
end
|
||||
|
||||
function ai_helper.is_attackable_enemy(unit, side, cfg)
|
||||
-- Check if @unit exists, is an enemy of @side, is visible to the side defined
|
||||
-- in @cfg.viewing_side and is not petrified.
|
||||
-- Check if @unit exists, is an enemy of @side, is visible to the side as defined
|
||||
-- by @cfg.viewing_side and @cfg.ignore_visibility and is not petrified.
|
||||
--
|
||||
-- Optional parameters:
|
||||
-- @side: side number, defaults to current side.
|
||||
-- @cfg: table with optional configuration parameters:
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to @side.
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
|
||||
side = side or wesnoth.current.side
|
||||
local viewing_side = cfg and cfg.viewing_side or side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
if (not unit)
|
||||
or (not wesnoth.sides.is_enemy(side, unit.side))
|
||||
or unit.status.petrified
|
||||
or (not ai_helper.is_visible_unit(viewing_side, unit))
|
||||
or ((not ignore_visibility) and (not ai_helper.is_visible_unit(viewing_side, unit)))
|
||||
then
|
||||
return false
|
||||
end
|
||||
|
@ -1251,6 +1251,7 @@ function ai_helper.get_closest_enemy(loc, side, cfg)
|
|||
-- @side: number of side for which to find enemy; defaults to current side
|
||||
-- @cfg: table with optional configuration parameters:
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to @side.
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
|
||||
side = side or wesnoth.current.side
|
||||
|
||||
|
@ -1443,6 +1444,7 @@ function ai_helper.next_hop(unit, x, y, cfg)
|
|||
-- @cfg: standard extra options for wesnoth.find_path()
|
||||
-- including:
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to side of @unit
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
-- plus:
|
||||
-- ignore_own_units: if set to true, then own units that can move out of the way are ignored
|
||||
-- path: if given, find the next hop along this path, rather than doing new path finding
|
||||
|
@ -1455,6 +1457,10 @@ function ai_helper.next_hop(unit, x, y, cfg)
|
|||
-- in the way) as possible. Setting 'fan_out=false' 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 viewing_side = cfg and cfg.viewing_side or unit.side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
local path, cost
|
||||
if cfg and cfg.path then
|
||||
path = cfg.path
|
||||
|
@ -1478,6 +1484,9 @@ function ai_helper.next_hop(unit, x, y, cfg)
|
|||
local unit_in_way
|
||||
if (not cfg) or (not cfg.ignore_units) then
|
||||
unit_in_way = wesnoth.units.get(path[i][1], path[i][2])
|
||||
if unit_in_way and (not ignore_visibility) and (not ai_helper.is_visible_unit(viewing_side, unit_in_way)) then
|
||||
unit_in_way = nil
|
||||
end
|
||||
|
||||
-- If ignore_own_units is set, ignore own side units that can move out of the way
|
||||
if cfg and cfg.ignore_own_units then
|
||||
|
@ -1528,10 +1537,12 @@ function ai_helper.next_hop(unit, x, y, cfg)
|
|||
inverse_reach_map:insert(r[1], r[2], inverse_cost)
|
||||
end
|
||||
|
||||
local units = ai_helper.get_visible_units(
|
||||
cfg and cfg.viewing_side or unit.side,
|
||||
{ { "not", { id = unit.id } }
|
||||
})
|
||||
local units
|
||||
if ignore_visibility then
|
||||
units = wesnoth.units.find_on_map({ { "not", { id = unit.id } } })
|
||||
else
|
||||
units = ai_helper.get_visible_units(viewing_side, { { "not", { id = unit.id } } })
|
||||
end
|
||||
local unit_map = LS.create()
|
||||
for _,u in ipairs(units) do unit_map:insert(u.x, u.y, u.id) end
|
||||
|
||||
|
@ -1564,15 +1575,20 @@ function ai_helper.can_reach(unit, x, y, cfg)
|
|||
-- ignore_units: if true, ignore both own and enemy units
|
||||
-- exclude_occupied: if true, exclude hex if there's a unit there, irrespective of value of 'ignore_units'
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to side of @unit
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
|
||||
cfg = cfg or {}
|
||||
local viewing_side = cfg.viewing_side or unit.side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
-- Is there a unit at the goal hex?
|
||||
-- Is there a visible unit at the goal hex?
|
||||
local unit_in_way = wesnoth.units.get(x, y)
|
||||
if (cfg.exclude_occupied)
|
||||
and unit_in_way and ai_helper.is_visible_unit(viewing_side, unit_in_way)
|
||||
then
|
||||
if unit_in_way and (not ignore_visibility) and (not ai_helper.is_visible_unit(viewing_side, unit_in_way)) then
|
||||
unit_in_way = nil
|
||||
end
|
||||
|
||||
if (cfg.exclude_occupied) and unit_in_way then
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -1580,10 +1596,7 @@ function ai_helper.can_reach(unit, x, y, cfg)
|
|||
-- or a unit of own side that cannot move away (this might be slow, don't know)
|
||||
if (not cfg.ignore_units) then
|
||||
-- If there's a unit at the goal that's not on own side (even ally), return false
|
||||
if unit_in_way
|
||||
and (unit_in_way.side ~= unit.side)
|
||||
and ai_helper.is_visible_unit(viewing_side, unit_in_way)
|
||||
then
|
||||
if unit_in_way and (unit_in_way.side ~= unit.side) then
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -1619,12 +1632,15 @@ function ai_helper.get_reachmap(unit, cfg)
|
|||
-- @cfg: table with optional configuration parameters:
|
||||
-- moves: if set to 'max', unit MP is set to max_moves before calculation
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to side of @unit
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
-- exclude_occupied: if true, exclude hexes that have units on them; defaults to
|
||||
-- false, in which case hexes with own units with moves > 0 are included
|
||||
-- avoid_map: location set of hexes to be excluded
|
||||
-- plus all other parameters to wesnoth.find_reach
|
||||
|
||||
local viewing_side = cfg and cfg.viewing_side or unit.side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
local old_moves = unit.moves
|
||||
if cfg and (cfg.moves == 'max') then unit.moves = unit.max_moves end
|
||||
|
@ -1637,7 +1653,14 @@ function ai_helper.get_reachmap(unit, cfg)
|
|||
is_available = false
|
||||
else
|
||||
local unit_in_way = wesnoth.units.get(loc[1], loc[2])
|
||||
if unit_in_way and (unit_in_way.id ~= unit.id) and ai_helper.is_visible_unit(viewing_side, unit_in_way) then
|
||||
if unit_in_way and (unit_in_way.id == unit.id) then
|
||||
unit_in_way = nil
|
||||
end
|
||||
if unit_in_way and (not ignore_visibility) and (not ai_helper.is_visible_unit(viewing_side, unit_in_way)) then
|
||||
unit_in_way = nil
|
||||
end
|
||||
|
||||
if unit_in_way then
|
||||
if cfg and cfg.exclude_occupied then
|
||||
is_available = false
|
||||
elseif (unit_in_way.side ~= unit.side) or (unit_in_way.moves == 0) then
|
||||
|
@ -1668,7 +1691,7 @@ end
|
|||
|
||||
function ai_helper.find_path_with_shroud(unit, x, y, cfg)
|
||||
-- Same as wesnoth.find_path, just that it works under shroud as well while still
|
||||
-- ignoring invisible units. It does this by using viewing_side=0 and taking
|
||||
-- ignoring invisible units. It does this by using ignore_visibility=true and taking
|
||||
-- invisible units off the map for the path finding process.
|
||||
--
|
||||
-- Notes on some of the optional parameters that can be passed in @cfg:
|
||||
|
@ -1676,19 +1699,23 @@ function ai_helper.find_path_with_shroud(unit, x, y, cfg)
|
|||
-- for determining which units are hidden and need to be extracted, as that
|
||||
-- is what the path_finder code uses. If set to an invalid side, we can use
|
||||
-- default path finding as shroud is ignored then anyway.
|
||||
-- - ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
-- This applies to the units only in this function, as it always ignores shroud.
|
||||
-- - ignore_units: if true, hidden units do not need to be extracted because
|
||||
-- all units are ignored anyway
|
||||
|
||||
local viewing_side = (cfg and cfg.viewing_side) or unit.side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
local path, cost
|
||||
if wesnoth.sides[viewing_side] and wesnoth.sides[viewing_side].shroud then
|
||||
if wesnoth.sides[viewing_side].shroud then
|
||||
local extracted_units = {}
|
||||
if (not cfg) or (not cfg.ignore_units) then
|
||||
local all_units = wesnoth.units.find_on_map()
|
||||
for _,u in ipairs(all_units) do
|
||||
if (u.side ~= viewing_side)
|
||||
and (not ai_helper.is_visible_unit(viewing_side, u))
|
||||
if (u.id ~= unit.id) and (u.side ~= viewing_side)
|
||||
and (not ignore_visibility) and (not ai_helper.is_visible_unit(viewing_side, u))
|
||||
then
|
||||
u:extract()
|
||||
table.insert(extracted_units, u)
|
||||
|
@ -1698,7 +1725,7 @@ function ai_helper.find_path_with_shroud(unit, x, y, cfg)
|
|||
|
||||
local cfg_copy = {}
|
||||
if cfg then cfg_copy = ai_helper.table_copy(cfg) end
|
||||
cfg_copy.viewing_side = 0
|
||||
cfg_copy.ignore_visibility = true
|
||||
path, cost = wesnoth.find_path(unit, x, y, cfg_copy)
|
||||
|
||||
for _,extracted_unit in ipairs(extracted_units) do
|
||||
|
@ -1943,10 +1970,13 @@ function ai_helper.move_unit_out_of_way(ai, unit, cfg)
|
|||
-- dx, dy: the direction in which moving out of the way is preferred
|
||||
-- labels: if set, display labels of the rating for each hex the unit can reach
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to side of @unit.
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
-- all other optional parameters to wesnoth.find_reach()
|
||||
|
||||
cfg = cfg or {}
|
||||
local viewing_side = cfg.viewing_side or unit.side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
local dx, dy
|
||||
if cfg.dx and cfg.dy then
|
||||
|
@ -1961,7 +1991,7 @@ function ai_helper.move_unit_out_of_way(ai, unit, cfg)
|
|||
for _,loc in ipairs(reach) do
|
||||
local unit_in_way = wesnoth.units.get(loc[1], loc[2])
|
||||
if (not unit_in_way) -- also excludes current hex
|
||||
or (not ai_helper.is_visible_unit(viewing_side, unit_in_way))
|
||||
or ((not ignore_visibility) and (not ai_helper.is_visible_unit(viewing_side, unit_in_way)))
|
||||
then
|
||||
local rating = loc[3] -- also disfavors hexes next to visible enemy units for which loc[3] = 0
|
||||
|
||||
|
@ -2009,9 +2039,12 @@ function ai_helper.movefull_outofway_stopunit(ai, unit, x, y, cfg)
|
|||
--
|
||||
-- @cfg: table with optional configuration parameters:
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to side of @unit
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to nil.
|
||||
-- all other optional parameters to ai_helper.move_unit_out_of_way() and wesnoth.find_path()
|
||||
|
||||
local viewing_side = cfg and cfg.viewing_side or unit.side
|
||||
ai_helper.check_viewing_side(viewing_side)
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
if (type(x) ~= 'number') then
|
||||
if x[1] then
|
||||
|
@ -2026,7 +2059,7 @@ function ai_helper.movefull_outofway_stopunit(ai, unit, x, y, cfg)
|
|||
if (cost <= unit.moves) then
|
||||
local unit_in_way = wesnoth.units.get(x, y)
|
||||
if unit_in_way and (unit_in_way ~= unit)
|
||||
and ai_helper.is_visible_unit(viewing_side, unit_in_way)
|
||||
and (ignore_visibility or ai_helper.is_visible_unit(viewing_side, unit_in_way))
|
||||
then
|
||||
ai_helper.move_unit_out_of_way(ai, unit_in_way, cfg)
|
||||
end
|
||||
|
@ -2043,15 +2076,15 @@ end
|
|||
---------- Attack related helper functions --------------
|
||||
|
||||
function ai_helper.get_attacks(units, cfg)
|
||||
-- Get all attacks the units stored in @units can do. Invisible enemies are
|
||||
-- excluded unless option @cfg.viewing_side=0 is used.
|
||||
-- Get all attacks the units stored in @units can do. Enemies invisible to the side
|
||||
-- of @units are excluded, unless option @cfg.ignore_visibility=true is used.
|
||||
--
|
||||
-- This includes a variety of configurable options, passed in the @cfg table
|
||||
-- @cfg: table with optional configuration parameters:
|
||||
-- moves: "current" (default for units on current side) or "max" (always used for units on other sides)
|
||||
-- include_occupied (false): if set, also include hexes occupied by own-side units that can move away
|
||||
-- simulate_combat (false): if set, also simulate the combat and return result (this is slow; only set if needed)
|
||||
-- viewing_side: see comments at beginning of this file. Defaults to side of @units
|
||||
-- ignore_visibility: see comments at beginning of this file. Defaults to side of @units
|
||||
-- all other optional parameters to wesnoth.find_reach()
|
||||
--
|
||||
-- Returns {} if no attacks can be done, otherwise table with fields:
|
||||
|
@ -2067,7 +2100,7 @@ function ai_helper.get_attacks(units, cfg)
|
|||
if (not units[1]) then return attacks end
|
||||
|
||||
local side = units[1].side -- all units need to be on same side
|
||||
local viewing_side = cfg and cfg.viewing_side or side
|
||||
local ignore_visibility = cfg and cfg.ignore_visibility
|
||||
|
||||
-- 'moves' can be either "current" or "max"
|
||||
-- For unit on current side: use "current" by default, or override by cfg.moves
|
||||
|
@ -2098,7 +2131,7 @@ function ai_helper.get_attacks(units, cfg)
|
|||
if (unit.side == side) then
|
||||
my_unit_map:insert(unit.x, unit.y, i)
|
||||
else
|
||||
if ai_helper.is_visible_unit(viewing_side, unit) then
|
||||
if ignore_visibility or ai_helper.is_visible_unit(side, unit) then
|
||||
other_unit_map:insert(unit.x, unit.y, i)
|
||||
end
|
||||
end
|
||||
|
@ -2157,7 +2190,9 @@ function ai_helper.get_attacks(units, cfg)
|
|||
for _,uiw_loc in ipairs(uiw_reach) do
|
||||
-- Unit in the way of the unit in the way
|
||||
local uiw_uiw = wesnoth.units.get(uiw_loc[1], uiw_loc[2])
|
||||
if (not uiw_uiw) or (not ai_helper.is_visible_unit(viewing_side, uiw_uiw)) then
|
||||
if (not uiw_uiw)
|
||||
or ((not ignore_visibility) and (not ai_helper.is_visible_unit(side, uiw_uiw)))
|
||||
then
|
||||
add_target = true
|
||||
break
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ local function get_reachable_enemy_leaders(unit, avoid_map)
|
|||
for _,e in ipairs(potential_enemy_leaders) do
|
||||
-- Cannot use AH.find_path_with_avoid() here as there might be enemies all around the enemy leader
|
||||
if (not avoid_map:get(e.x, e.y)) then
|
||||
local path, cost = wesnoth.find_path(unit, e.x, e.y, { ignore_units = true, viewing_side = 0 })
|
||||
local path, cost = wesnoth.find_path(unit, e.x, e.y, { ignore_units = true, ignore_visibility = true })
|
||||
if cost < AH.no_path then
|
||||
table.insert(enemy_leaders, e)
|
||||
end
|
||||
|
|
|
@ -587,7 +587,7 @@ return {
|
|||
-- If the recruited unit cannot reach the target hex, return it to the pool of targets
|
||||
if recruit_data.recruit.target_hex and recruit_data.recruit.target_hex[1] then
|
||||
local unit = wesnoth.units.get(recruit_data.recruit.best_hex[1], recruit_data.recruit.best_hex[2])
|
||||
local path, cost = wesnoth.find_path(unit, recruit_data.recruit.target_hex[1], recruit_data.recruit.target_hex[2], {viewing_side=0, max_cost=unit.max_moves+1})
|
||||
local path, cost = wesnoth.find_path(unit, recruit_data.recruit.target_hex[1], recruit_data.recruit.target_hex[2], {ignore_visibility=true, max_cost=unit.max_moves+1})
|
||||
if cost > unit.max_moves then
|
||||
-- The last village added to the list should be the one we tried to aim for, check anyway
|
||||
local last = #recruit_data.castle.assigned_villages_x
|
||||
|
@ -676,7 +676,7 @@ return {
|
|||
local target_hex = recruit_data.recruit.target_hex
|
||||
|
||||
local reference_hex = target_hex[1] and target_hex or best_hex
|
||||
local enemy_location, distance_to_enemy = AH.get_closest_enemy(reference_hex, wesnoth.current.side, { viewing_side = 0 })
|
||||
local enemy_location, distance_to_enemy = AH.get_closest_enemy(reference_hex, wesnoth.current.side, { ignore_visibility = true })
|
||||
|
||||
-- If no enemy is on the map, then we first use closest enemy start hex,
|
||||
-- and if that does not exist either, a location mirrored w.r.t the center of the map
|
||||
|
@ -731,7 +731,7 @@ return {
|
|||
random_gender = false
|
||||
}
|
||||
if target_hex[1] then
|
||||
local path, cost = wesnoth.find_path(recruit_unit, target_hex[1], target_hex[2], {viewing_side=0, max_cost=wesnoth.unit_types[recruit_id].max_moves+1})
|
||||
local path, cost = wesnoth.find_path(recruit_unit, target_hex[1], target_hex[2], {ignore_visibility=true, max_cost=wesnoth.unit_types[recruit_id].max_moves+1})
|
||||
if cost > wesnoth.unit_types[recruit_id].max_moves then
|
||||
-- Unit cost is effectively higher if cannot reach the village
|
||||
efficiency_index = 2
|
||||
|
@ -854,7 +854,7 @@ return {
|
|||
if target_hex[1] then
|
||||
recruitable_units[recruit_id].x = best_hex[1]
|
||||
recruitable_units[recruit_id].y = best_hex[2]
|
||||
local path, cost = wesnoth.find_path(recruitable_units[recruit_id], target_hex[1], target_hex[2], {viewing_side=0, max_cost=wesnoth.unit_types[recruit_id].max_moves+1})
|
||||
local path, cost = wesnoth.find_path(recruitable_units[recruit_id], target_hex[1], target_hex[2], {ignore_visibility=true, max_cost=wesnoth.unit_types[recruit_id].max_moves+1})
|
||||
if cost > wesnoth.unit_types[recruit_id].max_moves then
|
||||
-- penalty if the unit can't reach the target village
|
||||
bonus = bonus - 0.2
|
||||
|
@ -993,7 +993,7 @@ return {
|
|||
local key = unit.type .. '_' .. v[1] .. '-' .. v[2] .. '_' .. c[1] .. '-' .. c[2]
|
||||
local path, unit_distance
|
||||
if (not recruit_data.unit_distances[key]) then
|
||||
path, unit_distance = wesnoth.find_path(unit, c[1], c[2], {viewing_side=0, max_cost=fastest_unit_speed+1})
|
||||
path, unit_distance = wesnoth.find_path(unit, c[1], c[2], {ignore_visibility=true, max_cost=fastest_unit_speed+1})
|
||||
recruit_data.unit_distances[key] = unit_distance
|
||||
else
|
||||
unit_distance = recruit_data.unit_distances[key]
|
||||
|
|
|
@ -58,7 +58,7 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
-- Exclude hidden enemies, except if attack_hidden_enemies=yes is set in [micro_ai] tag
|
||||
local viewing_side = wesnoth.current.side
|
||||
local viewing_side, ignore_visibility = wesnoth.current.side, false
|
||||
if (not cfg.attack_hidden_enemies) then
|
||||
local hidden_enemies = AH.get_live_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
|
@ -69,7 +69,7 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
enemy_map:remove(e.x, e.y)
|
||||
end
|
||||
else
|
||||
viewing_side = 0
|
||||
ignore_visibility = true
|
||||
end
|
||||
|
||||
local aggression = ai.aspects.aggression
|
||||
|
@ -84,7 +84,7 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
if unit and unit.valid and (unit.attacks_left > 0) and (#unit.attacks > 0) then
|
||||
local unit_info = FAU.get_unit_info(unit, gamedata)
|
||||
local unit_copy = FAU.get_unit_copy(unit.id, gamedata)
|
||||
local attacks = AH.get_attacks({ unit }, { include_occupied = cfg.include_occupied_attack_hexes, viewing_side = viewing_side })
|
||||
local attacks = AH.get_attacks({ unit }, { include_occupied = cfg.include_occupied_attack_hexes, viewing_side = viewing_side, ignore_visibility = ignore_visibility })
|
||||
|
||||
if (#attacks > 0) then
|
||||
local max_rating, best_target, best_dst = - math.huge
|
||||
|
|
|
@ -51,7 +51,7 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
-- Exclude hidden enemies, except if attack_hidden_enemies=yes is set in [micro_ai] tag
|
||||
local viewing_side = wesnoth.current.side
|
||||
local viewing_side, ignore_visibility = wesnoth.current.side, false
|
||||
if (not cfg.attack_hidden_enemies) then
|
||||
local hidden_enemies = AH.get_live_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
|
@ -62,7 +62,7 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
enemy_map:remove(e.x, e.y)
|
||||
end
|
||||
else
|
||||
viewing_side = 0
|
||||
ignore_visibility = true
|
||||
end
|
||||
|
||||
local aggression = ai.aspects.aggression
|
||||
|
@ -119,7 +119,7 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
end
|
||||
end
|
||||
|
||||
local attacks = AH.get_attacks({ leader }, { include_occupied = cfg.include_occupied_attack_hexes, viewing_side = viewing_side })
|
||||
local attacks = AH.get_attacks({ leader }, { include_occupied = cfg.include_occupied_attack_hexes, viewing_side = viewing_side, ignore_visibility = ignore_visibility })
|
||||
|
||||
if (#attacks > 0) then
|
||||
local max_rating, best_target, best_dst = - math.huge
|
||||
|
|
|
@ -717,7 +717,7 @@
|
|||
local ai_helper = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local delf = wesnoth.units.find_on_map { id = 'Delfador' }[1]
|
||||
local sceptre_loc= wesnoth.special_locations.sceptre
|
||||
local path = wesnoth.find_path(delf, sceptre_loc[1], sceptre_loc[2], {ignore_units = true, viewing_side = 0}) -- # wmllint: noconvert
|
||||
local path = wesnoth.find_path(delf, sceptre_loc[1], sceptre_loc[2], {ignore_units = true, ignore_visibility = true}) -- # wmllint: noconvert
|
||||
_ = wesnoth.textdomain 'wesnoth-httt'
|
||||
|
||||
local dirs = { _"I sense the path to the Sceptre is to the east of me.",
|
||||
|
|
|
@ -437,7 +437,12 @@ function wml_actions.store_reachable_locations(cfg)
|
|||
local range = cfg.range or "movement"
|
||||
local moves = cfg.moves or "current"
|
||||
local variable = cfg.variable or wml.error "[store_reachable_locations] missing required variable= key"
|
||||
local reach_param = { viewing_side = cfg.viewing_side or 0 }
|
||||
local reach_param = { viewing_side = cfg.viewing_side }
|
||||
if cfg.viewing_side == 0 then
|
||||
wml.error "[store_reachable_locations] invalid viewing_side"
|
||||
elseif cfg.viewing_side == nil then
|
||||
reach_param.ignore_visibility = true
|
||||
end
|
||||
if range == "vision" then
|
||||
moves = "max"
|
||||
reach_param.ignore_units = true
|
||||
|
|
|
@ -23,7 +23,7 @@ function wesnoth.wml_actions.find_path(cfg)
|
|||
end
|
||||
|
||||
local allow_multiple_turns = cfg.allow_multiple_turns
|
||||
local viewing_side
|
||||
local ignore_visibility = not cfg.check_visibility
|
||||
|
||||
local nearest_by_cost = true
|
||||
local nearest_by_distance = false
|
||||
|
@ -38,8 +38,6 @@ function wesnoth.wml_actions.find_path(cfg)
|
|||
nearest_by_steps = true
|
||||
end
|
||||
|
||||
if not cfg.check_visibility then viewing_side = 0 end -- if check_visiblity then shroud is taken in account
|
||||
|
||||
-- only the first location with the lowest distance and lowest movement cost will match.
|
||||
local locations = wesnoth.get_locations(filter_location)
|
||||
|
||||
|
@ -57,7 +55,12 @@ function wesnoth.wml_actions.find_path(cfg)
|
|||
else
|
||||
local distance = wesnoth.map.distance_between ( unit.x, unit.y, location[1], location[2] )
|
||||
-- if we pass an unreachable location then an empty path and high value cost will be returned
|
||||
local path, cost = wesnoth.find_path( unit, location[1], location[2], { max_cost = max_cost, ignore_units = ignore_units, ignore_teleport = ignore_teleport, viewing_side = viewing_side } )
|
||||
local path, cost = wesnoth.find_path( unit, location[1], location[2], {
|
||||
max_cost = max_cost,
|
||||
ignore_units = ignore_units,
|
||||
ignore_teleport = ignore_teleport,
|
||||
ignore_visibility = ignore_visibility
|
||||
} )
|
||||
|
||||
if #path == 0 or cost >= 42424241 then
|
||||
-- it's not a reachable hex. 42424242 is the high value returned for unwalkable or busy terrains
|
||||
|
@ -104,7 +107,7 @@ function wesnoth.wml_actions.find_path(cfg)
|
|||
max_cost = max_cost,
|
||||
ignore_units = ignore_units,
|
||||
ignore_teleport = ignore_teleport,
|
||||
viewing_side = viewing_side
|
||||
ignore_visibility = ignore_visibility
|
||||
})
|
||||
local turns
|
||||
|
||||
|
@ -142,7 +145,7 @@ function wesnoth.wml_actions.find_path(cfg)
|
|||
max_cost = max_cost,
|
||||
ignore_units = ignore_units,
|
||||
ignore_teleport = ignore_teleport,
|
||||
viewing_side = viewing_side
|
||||
ignore_visibility = ignore_visibility
|
||||
} )
|
||||
local sub_turns
|
||||
|
||||
|
|
|
@ -1695,11 +1695,13 @@ int game_lua_kernel::intf_find_path(lua_State *L)
|
|||
int arg = 1;
|
||||
map_location src, dst;
|
||||
const unit* u = nullptr;
|
||||
int viewing_side = 0;
|
||||
|
||||
if (lua_isuserdata(L, arg))
|
||||
{
|
||||
u = &luaW_checkunit(L, arg);
|
||||
src = u->get_location();
|
||||
viewing_side = u->side();
|
||||
++arg;
|
||||
}
|
||||
else
|
||||
|
@ -1708,6 +1710,7 @@ int game_lua_kernel::intf_find_path(lua_State *L)
|
|||
unit_map::const_unit_iterator ui = units().find(src);
|
||||
if (ui.valid()) {
|
||||
u = ui.get_shared_ptr().get();
|
||||
viewing_side = u->side();
|
||||
}
|
||||
++arg;
|
||||
}
|
||||
|
@ -1721,7 +1724,6 @@ int game_lua_kernel::intf_find_path(lua_State *L)
|
|||
return luaL_argerror(L, arg - 2, "invalid location");
|
||||
|
||||
const gamemap &map = board().map();
|
||||
int viewing_side = 0;
|
||||
bool ignore_units = false, see_all = false, ignore_teleport = false;
|
||||
double stop_at = 10000;
|
||||
std::unique_ptr<pathfind::cost_calculator> calc;
|
||||
|
@ -1729,10 +1731,10 @@ int game_lua_kernel::intf_find_path(lua_State *L)
|
|||
if (lua_istable(L, arg))
|
||||
{
|
||||
ignore_units = luaW_table_get_def<bool>(L, arg, "ignore_units", false);
|
||||
|
||||
see_all = luaW_table_get_def<bool>(L, arg, "ignore_visibility", false);
|
||||
ignore_teleport = luaW_table_get_def<bool>(L, arg, "ignore_teleport", false);
|
||||
|
||||
stop_at = luaW_table_get_def<double>(L, arg, "stop_at", stop_at);
|
||||
stop_at = luaW_table_get_def<double>(L, arg, "max_cost", stop_at);
|
||||
|
||||
|
||||
lua_pushstring(L, "viewing_side");
|
||||
|
@ -1740,7 +1742,12 @@ int game_lua_kernel::intf_find_path(lua_State *L)
|
|||
if (!lua_isnil(L, -1)) {
|
||||
int i = luaL_checkinteger(L, -1);
|
||||
if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
|
||||
else see_all = true;
|
||||
else {
|
||||
// If there's a unit, we have a valid side, so fall back to legacy behaviour.
|
||||
// If we don't have a unit, legacy behaviour would be a crash, so let's not.
|
||||
if(u) see_all = true;
|
||||
deprecated_message("wesnoth.find_path with viewing_side=0 (or an invalid side)", DEP_LEVEL::FOR_REMOVAL, {1, 17, 0}, "To consider fogged and hidden units, use ignore_visibility=true instead.");
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
|
@ -1757,20 +1764,22 @@ int game_lua_kernel::intf_find_path(lua_State *L)
|
|||
calc.reset(new lua_pathfind_cost_calculator(L, arg));
|
||||
}
|
||||
|
||||
const team& viewing_team = viewing_side
|
||||
? board().get_team(viewing_side)
|
||||
: board().get_team(u->side());
|
||||
|
||||
pathfind::teleport_map teleport_locations;
|
||||
|
||||
if(!ignore_teleport) {
|
||||
teleport_locations = pathfind::get_teleport_locations(*u, viewing_team, see_all, ignore_units);
|
||||
if(viewing_side == 0) {
|
||||
return luaL_error(L, "wesnoth.find_path: ignore_teleport=false requires a valid viewing_side");
|
||||
} else {
|
||||
teleport_locations = pathfind::get_teleport_locations(*u, board().get_team(viewing_side), see_all, ignore_units);
|
||||
}
|
||||
}
|
||||
|
||||
if (!calc) {
|
||||
if (!u) return luaL_argerror(L, 1, "unit not found");
|
||||
if(!u) {
|
||||
return luaL_argerror(L, 1, "unit not found OR custom cost function not provided");
|
||||
}
|
||||
|
||||
calc.reset(new pathfind::shortest_path_calculator(*u, viewing_team,
|
||||
calc.reset(new pathfind::shortest_path_calculator(*u, board().get_team(viewing_side),
|
||||
teams(), map, ignore_units, false, see_all));
|
||||
}
|
||||
|
||||
|
@ -1819,40 +1828,33 @@ int game_lua_kernel::intf_find_reach(lua_State *L)
|
|||
++arg;
|
||||
}
|
||||
|
||||
int viewing_side = 0;
|
||||
int viewing_side = u->side();
|
||||
bool ignore_units = false, see_all = false, ignore_teleport = false;
|
||||
int additional_turns = 0;
|
||||
|
||||
if (lua_istable(L, arg))
|
||||
{
|
||||
lua_pushstring(L, "ignore_units");
|
||||
lua_rawget(L, arg);
|
||||
ignore_units = luaW_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushstring(L, "ignore_teleport");
|
||||
lua_rawget(L, arg);
|
||||
ignore_teleport = luaW_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushstring(L, "additional_turns");
|
||||
lua_rawget(L, arg);
|
||||
additional_turns = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
ignore_units = luaW_table_get_def<bool>(L, arg, "ignore_units", false);
|
||||
see_all = luaW_table_get_def<bool>(L, arg, "ignore_visibility", false);
|
||||
ignore_teleport = luaW_table_get_def<bool>(L, arg, "ignore_teleport", false);
|
||||
additional_turns = luaW_table_get_def<int>(L, arg, "max_cost", additional_turns);
|
||||
|
||||
lua_pushstring(L, "viewing_side");
|
||||
lua_rawget(L, arg);
|
||||
if (!lua_isnil(L, -1)) {
|
||||
int i = luaL_checkinteger(L, -1);
|
||||
if (i >= 1 && i <= static_cast<int>(teams().size())) viewing_side = i;
|
||||
else see_all = true;
|
||||
else {
|
||||
// If there's a unit, we have a valid side, so fall back to legacy behaviour.
|
||||
// If we don't have a unit, legacy behaviour would be a crash, so let's not.
|
||||
if(u) see_all = true;
|
||||
deprecated_message("wesnoth.find_reach with viewing_side=0 (or an invalid side)", DEP_LEVEL::FOR_REMOVAL, {1, 17, 0}, "To consider fogged and hidden units, use ignore_visibility=true instead.");
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
const team& viewing_team = viewing_side
|
||||
? board().get_team(viewing_side)
|
||||
: board().get_team(u->side());
|
||||
const team& viewing_team = board().get_team(viewing_side);
|
||||
|
||||
pathfind::paths res(*u, ignore_units, !ignore_teleport,
|
||||
viewing_team, additional_turns, see_all, ignore_units);
|
||||
|
|
Loading…
Add table
Reference in a new issue