Micro AIs: support named locations for [micro_ai] tag keys

This commit is contained in:
mattsc 2018-11-01 21:36:25 -07:00
parent 8676d3eaf1
commit dcf951434e
22 changed files with 224 additions and 100 deletions

View file

@ -633,6 +633,98 @@ function ai_helper.is_opposite_adjacent(hex1, hex2, center_hex)
return false
end
function ai_helper.get_named_loc_xy(param_core, cfg, required_for)
-- Get coordinates for either a named location or from x/y coordinates specified
-- in @cfg. The location can be provided:
-- - as name: cfg[param_core .. '_loc'] (string)
-- - or as coordinates: cfg[param_core .. '_x'] and cfg[param_core .. '_y'] (integers)
-- This is the syntax used by many Micro AIs.
-- Exception to this variable name syntax: if param_core = '', then the location
-- variables are 'location_id', 'x' and 'y'
--
-- Error messages are displayed if the named location does not exist, or if
-- the coordinates are not on the map. In addition, if @required_for (a string)
-- is provided, an error message is also displayed if neither a named location
-- nor both coordinates are provided. If @required_for is not passed and neither
-- input exists, nil is returned.
local param_loc = 'location_id'
if (param_core ~= '') then param_loc = param_core .. '_loc' end
local loc_id = cfg[param_loc]
if loc_id then
local loc = wesnoth.special_locations[loc_id]
if loc then
return loc
else
H.wml_error("Named location does not exist: " .. loc_id)
end
end
local param_x, param_y = 'x', 'y'
if (param_core ~= '') then param_x, param_y = param_core .. '_x', param_core .. '_y' end
local x, y = cfg[param_x], cfg[param_y]
if x and y then
local width, height = wesnoth.get_map_size()
if (x < 1) or (x > width) or (y < 1) or (y > height) then
H.wml_error("Location is not on map: " .. param_x .. ',' .. param_y .. ' = ' .. x .. ',' .. y)
end
return { x, y }
end
if required_for then
H.wml_error(required_for .. " requires either " .. param_loc .. "= or " .. param_x .. "/" .. param_y .. "= keys")
end
end
function ai_helper.get_multi_named_locs_xy(param_core, cfg, required_for)
-- Same as ai_helper.get_named_loc_xy, except that it takes comma separated
-- lists of locations.
-- The result is returned as an array of locations.
-- Empty table is returned if no locations are found.
local locs = {}
local param_loc = 'location_id'
if (param_core ~= '') then param_loc = param_core .. '_loc' end
local cfg_loc = cfg[param_loc]
if cfg_loc then
local loc_ids = ai_helper.split(cfg_loc, ",")
for _,loc_id in ipairs(loc_ids) do
local tmp_cfg = {}
tmp_cfg[param_loc] = loc_id
local loc = ai_helper.get_named_loc_xy(param_core, tmp_cfg)
table.insert(locs, loc)
end
return locs
end
local param_x, param_y = 'x', 'y'
if (param_core ~= '') then param_x, param_y = param_core .. '_x', param_core .. '_y' end
local cfg_x, cfg_y = cfg[param_x], cfg[param_y]
if cfg_x and cfg_y then
local xs = ai_helper.split(cfg_x, ",")
local ys = ai_helper.split(cfg_y, ",")
if (#xs ~= #ys) then
H.wml_error("Coordinate lists need to have same number of elements: " .. param_x .. ' and ' .. param_y)
end
for i,x in ipairs(xs) do
local tmp_cfg = {}
tmp_cfg[param_x] = tonumber(x)
tmp_cfg[param_y] = tonumber(ys[i])
local loc = ai_helper.get_named_loc_xy(param_core, tmp_cfg)
table.insert(locs, loc)
end
return locs
end
if required_for then
H.wml_error(required_for .. " requires either " .. param_loc .. "= or " .. param_x .. "/" .. param_y .. "= keys")
end
return locs
end
function ai_helper.get_locations_no_borders(location_filter)
-- Returns the same locations array as wesnoth.get_locations(location_filter),
-- but excluding hexes on the map border.

View file

@ -69,18 +69,12 @@ local function bottleneck_is_my_territory(map, enemy_map)
return territory_map
end
local function bottleneck_triple_from_keys(key_x, key_y, max_value)
local function bottleneck_triple_from_locs(locs, max_value)
-- Turn comma-separated lists of values in @key_x,@key_y into a location set.
-- Add a rating that has @max_value as its maximum, differentiated by order in the list.
local coords = {}
for x in string.gmatch(key_x, "%d+") do
table.insert(coords, { x })
end
local i = 1
for y in string.gmatch(key_y, "%d+") do
table.insert(coords[i], y)
table.insert(coords[i], max_value + 10 - i * 10)
i = i + 1
local coords = AH.table_copy(locs)
for i,coord in ipairs(coords) do
coord[3] = max_value + 10 - i * 10
end
return LS.of_triples(coords)
@ -236,20 +230,23 @@ function ca_bottleneck_move:evaluation(cfg, data)
if (not units[1]) then return 0 end
-- Set up the array that tells the AI where to defend the bottleneck
BD_def_map = bottleneck_triple_from_keys(cfg.x, cfg.y, 10000)
local locs = AH.get_multi_named_locs_xy('', cfg)
BD_def_map = bottleneck_triple_from_locs(locs, 10000)
-- Territory map, describing which hex is on AI's side of the bottleneck
-- This one is a bit expensive, esp. on large maps -> don't delete every move and reuse
-- However, after a reload, BD_is_my_territory is empty
-- -> need to recalculate in that case also
if (not BD_is_my_territory) or (type(BD_is_my_territory) == 'string') then
local enemy_map = bottleneck_triple_from_keys(cfg.enemy_x, cfg.enemy_y, 10000)
local enemy_locs = AH.get_multi_named_locs_xy('enemy', cfg)
local enemy_map = bottleneck_triple_from_locs(enemy_locs, 10000)
BD_is_my_territory = bottleneck_is_my_territory(BD_def_map, enemy_map)
end
-- Healer positioning map
if cfg.healer_x and cfg.healer_y then
BD_healer_map = bottleneck_triple_from_keys(cfg.healer_x, cfg.healer_y, 5000)
local healer_locs = AH.get_multi_named_locs_xy('healer', cfg)
if healer_locs[1] then
BD_healer_map = bottleneck_triple_from_locs(healer_locs, 5000)
else
BD_healer_map = bottleneck_create_positioning_map(5000, data)
end
@ -259,8 +256,9 @@ function ca_bottleneck_move:evaluation(cfg, data)
)
-- Leadership position map
if cfg.leadership_x and cfg.leadership_y then
BD_leadership_map = bottleneck_triple_from_keys(cfg.leadership_x, cfg.leadership_y, 4000)
local leadership_locs = AH.get_multi_named_locs_xy('leadership', cfg)
if leadership_locs[1] then
BD_leadership_map = bottleneck_triple_from_locs(leadership_locs, 4000)
else
BD_leadership_map = bottleneck_create_positioning_map(4000, data)
end

View file

@ -64,8 +64,15 @@ function ca_coward:execution(cfg)
-- Now take 'seek' and 'avoid' into account
for i,pos in ipairs(best_pos) do
-- Weighting based on distance from 'seek' and 'avoid'
local dist_seek = AH.generalized_distance(pos[1], pos[2], cfg.seek_x, cfg.seek_y)
local dist_avoid = AH.generalized_distance(pos[1], pos[2], cfg.avoid_x, cfg.avoid_y)
-- It is allowed to specify only x or y, thus the slightly more complex variable assignments
local seek_x, seek_y = cfg.seek_x, cfg.seek_y
local seek_loc = AH.get_named_loc_xy('seek', cfg)
if seek_loc then seek_x, seek_y = seek_loc[1], seek_loc[2] end
local avoid_x, avoid_y = cfg.avoid_x, cfg.avoid_y
local avoid_loc = AH.get_named_loc_xy('avoid', cfg)
if avoid_loc then avoid_x, avoid_y = avoid_loc[1], avoid_loc[2] end
local dist_seek = AH.generalized_distance(pos[1], pos[2], seek_x, seek_y)
local dist_avoid = AH.generalized_distance(pos[1], pos[2], avoid_x, avoid_y)
local rating = 1 / (dist_seek + 1) - 1 / (dist_avoid + 1)^2 * 0.75
best_pos[i][4] = rating

View file

@ -25,9 +25,10 @@ function ca_herding_dog_move:execution(cfg)
local herding_perimeter = LS.of_pairs(wesnoth.get_locations(wml.get_child(cfg, "filter_location")))
-- Find average distance of herding_perimeter from center
local herd_loc = AH.get_named_loc_xy('herd', cfg)
local av_dist = 0
herding_perimeter:iter( function(x, y, v)
av_dist = av_dist + M.distance_between(x, y, cfg.herd_x, cfg.herd_y)
av_dist = av_dist + M.distance_between(x, y, herd_loc[1], herd_loc[2])
end)
av_dist = av_dist / herding_perimeter:size()
@ -39,7 +40,7 @@ function ca_herding_dog_move:execution(cfg)
rating = rating + 1000 + math.random(99) / 100.
else
rating = rating
- math.abs(M.distance_between(x, y, cfg.herd_x, cfg.herd_y) - av_dist)
- math.abs(M.distance_between(x, y, herd_loc[1], herd_loc[2]) - av_dist)
+ math.random(99) / 100.
end

View file

@ -1,3 +1,4 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local H = wesnoth.require "helper"
local LS = wesnoth.require "location_set"
@ -5,9 +6,10 @@ return function(cfg)
-- Find the area that the sheep can occupy
-- First, find all contiguous hexes around center hex that are inside herding_perimeter
local location_filter = wml.get_child(cfg, "filter_location")
local herd_loc = AH.get_named_loc_xy('herd', cfg)
local herding_area = LS.of_pairs(wesnoth.get_locations {
x = cfg.herd_x,
y = cfg.herd_y,
x = herd_loc[1],
y = herd_loc[2],
radius = 999,
{ "filter_radius", { { "not", location_filter } } }
})

View file

@ -43,7 +43,8 @@ function ca_herding_herd_sheep:execution(cfg)
local sheep_to_herd = get_sheep_to_herd(cfg)
local max_rating, best_dog, best_hex = - math.huge
local c_x, c_y = cfg.herd_x, cfg.herd_y
local herd_loc = AH.get_named_loc_xy('herd', cfg)
local c_x, c_y = herd_loc[1], herd_loc[2]
for _,single_sheep in ipairs(sheep_to_herd) do
-- Farthest sheep goes first
local sheep_rating = M.distance_between(c_x, c_y, single_sheep.x, single_sheep.y) / 10.

View file

@ -27,7 +27,8 @@ function ca_herding_sheep_runs_dog:execution(cfg)
{ "filter_adjacent", { x = sheep.x, y = sheep.y } }
}[1]
local c_x, c_y = cfg.herd_x, cfg.herd_y
local herd_loc = AH.get_named_loc_xy('herd', cfg)
local c_x, c_y = herd_loc[1], herd_loc[2]
-- If dog is farther from center, sheep moves in, otherwise it moves out
local sign = 1
if (M.distance_between(dog.x, dog.y, c_x, c_y) >= M.distance_between(sheep.x, sheep.y, c_x, c_y)) then

View file

@ -52,7 +52,7 @@ end
function ca_hunter:execution(cfg)
-- Hunter does a random wander in area given by @cfg.hunting_ground until it finds
-- and kills an enemy unit, then retreats to position given by @cfg.home_x/y
-- and kills an enemy unit, then retreats to position given by @cfg.home_loc or @cfg.home_x/y
-- for @cfg.rest_turns turns, or until fully healed
local hunter = get_hunter(cfg)
@ -129,7 +129,8 @@ function ca_hunter:execution(cfg)
-- If we got here, this means the hunter is either returning, or resting
if (hunter_vars.hunting_status == 'returning') then
goto_x, goto_y = wesnoth.find_vacant_tile(cfg.home_x, cfg.home_y, hunter)
local home_loc = AH.get_named_loc_xy('home', cfg)
goto_x, goto_y = wesnoth.find_vacant_tile(home_loc[1], home_loc[2], hunter)
local next_hop = AH.next_hop(hunter, goto_x, goto_y)
if next_hop then
@ -137,8 +138,8 @@ function ca_hunter:execution(cfg)
if (not hunter) or (not hunter.valid) then return end
-- If there's an enemy on the 'home' hex and we got right next to it, attack that enemy
if (M.distance_between(cfg.home_x, cfg.home_y, hunter.x, hunter.y) == 1) then
local enemy = wesnoth.get_unit(cfg.home_x, cfg.home_y)
if (M.distance_between(home_loc[1], home_loc[2], hunter.x, hunter.y) == 1) then
local enemy = wesnoth.get_unit(home_loc[1], home_loc[2])
if AH.is_attackable_enemy(enemy) then
if cfg.show_messages then
wesnoth.wml_actions.message { speaker = hunter.id, message = 'Get out of my home!' }
@ -155,7 +156,7 @@ function ca_hunter:execution(cfg)
if (not hunter) or (not hunter.valid) then return end
-- If the hunter got home, start the resting counter
if (hunter.x == cfg.home_x) and (hunter.y == cfg.home_y) then
if (hunter.x == home_loc[1]) and (hunter.y == home_loc[2]) then
hunter_vars.hunting_status = 'resting'
hunter_vars.resting_until = wesnoth.current.turn + (cfg.rest_turns or 3)
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)

View file

@ -13,12 +13,7 @@ return function(cfg)
local messengers = wesnoth.get_units { side = wesnoth.current.side, { "and", filter } }
if (not messengers[1]) then return end
local waypoint_x = AH.split(cfg.waypoint_x, ",")
local waypoint_y = AH.split(cfg.waypoint_y, ",")
for i,_ in ipairs(waypoint_x) do
waypoint_x[i] = tonumber(waypoint_x[i])
waypoint_y[i] = tonumber(waypoint_y[i])
end
local waypoints = AH.get_multi_named_locs_xy('waypoint', cfg)
-- Set the next waypoint for all messengers
-- Also find those with MP left and return the one to next move, together with the WP to move toward
@ -27,12 +22,12 @@ return function(cfg)
-- To avoid code duplication and ensure consistency, we store some pieces of
-- information in the messenger units, even though it could be calculated each time it is needed
local wp_i = MAIUV.get_mai_unit_variables(messenger, cfg.ai_id, "wp_i") or 1
local wp_x, wp_y = waypoint_x[wp_i], waypoint_y[wp_i]
local wp_x, wp_y = waypoints[wp_i][1], waypoints[wp_i][2]
-- If this messenger is within 3 hexes of the next waypoint, we go on to the one after that
-- except if it's the last one
local dist_wp = wesnoth.map.distance_between(messenger.x, messenger.y, wp_x, wp_y)
if (dist_wp <= 3) and (wp_i < #waypoint_x) then wp_i = wp_i + 1 end
if (dist_wp <= 3) and (wp_i < #waypoints) then wp_i = wp_i + 1 end
-- Also store the rating for each messenger
-- For now, this is simply a "forward rating"

View file

@ -119,11 +119,10 @@ function ca_messenger_move:execution(cfg)
AH.checked_attack(ai, messenger, best_target, best_weapon)
else
-- Always attack enemy on last waypoint
local waypoint_x = AH.split(cfg.waypoint_x, ",")
local waypoint_y = AH.split(cfg.waypoint_y, ",")
local waypoints = AH.get_multi_named_locs_xy('waypoint', cfg)
local target = AH.get_attackable_enemies {
x = tonumber(waypoint_x[#waypoint_x]),
y = tonumber(waypoint_y[#waypoint_y]),
x = waypoints[#waypoints][1],
y = waypoints[#waypoints][2],
{ "filter_adjacent", { id = messenger.id } }
}[1]

View file

@ -23,16 +23,14 @@ function ca_patrol:execution(cfg)
-- Set up waypoints, taking into account whether 'reverse' is set
-- This works even the first time, when patrol_vars.patrol_reverse is not set yet
cfg.waypoint_x = AH.split(cfg.waypoint_x, ",")
cfg.waypoint_y = AH.split(cfg.waypoint_y, ",")
local n_wp = #cfg.waypoint_x
local waypoints = {}
for i = 1,n_wp do
if patrol_vars.patrol_reverse then
waypoints[i] = { tonumber(cfg.waypoint_x[n_wp-i+1]), tonumber(cfg.waypoint_y[n_wp-i+1]) }
else
waypoints[i] = { tonumber(cfg.waypoint_x[i]), tonumber(cfg.waypoint_y[i]) }
local waypoints = AH.get_multi_named_locs_xy('waypoint', cfg)
local n_wp = #waypoints
if patrol_vars.patrol_reverse then
local tmp = {}
for i = 1,n_wp do
tmp[i] = { waypoints[n_wp-i+1][1], waypoints[n_wp-i+1][2] }
end
waypoints = tmp
end
-- If not set, set next location (first move)

View file

@ -7,10 +7,11 @@ function ca_protect_unit_finish:evaluation(cfg)
for u in wml.child_range(cfg, "unit") do
local unit = AH.get_units_with_moves { id = u.id }[1]
if unit then
local path, cost = AH.find_path_with_shroud(unit, u.goal_x, u.goal_y)
if (cost <= unit.moves) and ((unit.x ~= u.goal_x) or (unit.y ~= u.goal_y)) then
local goal = AH.get_named_loc_xy('goal', u)
local path, cost = AH.find_path_with_shroud(unit, goal[1], goal[2])
if (cost <= unit.moves) and ((unit.x ~= goal[1]) or (unit.y ~= goal[2])) then
PU_unit = unit
PU_goal = { u.goal_x, u.goal_y }
PU_goal = { goal[1], goal[2] }
return 300000
end
end

View file

@ -39,7 +39,7 @@ function ca_protect_unit_move:execution(cfg, data)
local unit = F.choose(protected_units, function(u) return - u.hitpoints end)
local goal = {}
for u in wml.child_range(cfg, "unit") do
if (unit.id == u.id) then goal = { u.goal_x, u.goal_y } end
if (unit.id == u.id) then goal = AH.get_named_loc_xy('goal', u) end
end
local reach_map = AH.get_reachable_unocc(unit)

View file

@ -15,7 +15,8 @@ local ca_return_guardian = {}
function ca_return_guardian:evaluation(cfg)
local guardian = get_guardian(cfg)
if guardian then
if (guardian.x == cfg.return_x) and (guardian.y == cfg.return_y) then
local return_loc = AH.get_named_loc_xy('return', cfg)
if (guardian.x == return_loc[1]) and (guardian.y == return_loc[2]) then
return cfg.ca_score - 200
else
return cfg.ca_score
@ -27,9 +28,10 @@ end
function ca_return_guardian:execution(cfg)
local guardian = get_guardian(cfg)
local return_loc = AH.get_named_loc_xy('return', cfg)
-- In case the return hex is occupied:
local x, y = cfg.return_x, cfg.return_y
local x, y = return_loc[1], return_loc[2]
if (guardian.x ~= x) or (guardian.y ~= y) then
x, y = wesnoth.find_vacant_tile(x, y, guardian)
end

View file

@ -37,10 +37,12 @@ function ca_stationed_guardian:execution(cfg)
-- Otherwise, guardian will either attack or move toward station
-- Enemies must be within cfg.distance of guardian, (s_x, s_y) *and* (g_x, g_y)
-- simultaneously for guardian to attack
local station_loc = AH.get_named_loc_xy('station', cfg)
local guard_loc = AH.get_named_loc_xy('guard', cfg) or station_loc
local min_dist, target = math.huge
for _,enemy in ipairs(enemies) do
local dist_s = M.distance_between(cfg.station_x, cfg.station_y, enemy.x, enemy.y)
local dist_g = M.distance_between(cfg.guard_x or cfg.station_x, cfg.guard_y or cfg.station_y, enemy.x, enemy.y)
local dist_s = M.distance_between(station_loc[1], station_loc[2], enemy.x, enemy.y)
local dist_g = M.distance_between(guard_loc[1], guard_loc[2], enemy.x, enemy.y)
-- If valid target found, save the one with the shortest distance from (g_x, g_y)
if (dist_s <= cfg.distance) and (dist_g <= cfg.distance) and (dist_g < min_dist) then
@ -96,7 +98,7 @@ function ca_stationed_guardian:execution(cfg)
-- If no enemy is within the target zone, move toward station position
else
local nh = AH.next_hop(guardian, cfg.station_x, cfg.station_y)
local nh = AH.next_hop(guardian, station_loc[1], station_loc[2])
if not nh then
nh = { guardian.x, guardian.y }
end

View file

@ -84,12 +84,10 @@ function ca_zone_guardian:execution(cfg)
-- If no enemy around or within the zone, move toward station or zone
else
local newpos
-- If cfg.station_x/y are given, move toward that location
if cfg.station_x and cfg.station_y then
newpos = { cfg.station_x, cfg.station_y }
-- If cfg.station_loc or cfg.station_x/y are given, move toward that location
local newpos = AH.get_named_loc_xy('station', cfg)
-- Otherwise choose one randomly from those given in filter_location
else
if (not newpos) then
local locs_map = LS.of_pairs(AH.get_locations_no_borders(zone))
-- Check out which of those hexes the guardian can reach

View file

@ -1,3 +1,4 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local H = wesnoth.require "helper"
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
@ -66,8 +67,11 @@ function wesnoth.micro_ais.wolves(cfg)
end
function wesnoth.micro_ais.herding(cfg)
local required_keys = { "[filter_location]", "[filter]", "[filter_second]", "herd_x", "herd_y" }
local optional_keys = { "attention_distance", "attack_distance" }
if (cfg.action ~= 'delete') then
AH.get_named_loc_xy('herd', cfg, 'Herding [micro_ai] tag')
end
local required_keys = { "[filter_location]", "[filter]", "[filter_second]" }
local optional_keys = { "attention_distance", "attack_distance", "herd_loc", "herd_x", "herd_y" }
local score = cfg.ca_score or 300000
local CA_parms = {
ai_id = 'mai_herding',
@ -165,11 +169,14 @@ function wesnoth.micro_ais.wolves_multipacks(cfg)
end
function wesnoth.micro_ais.hunter(cfg)
if (cfg.action ~= 'delete') and (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Hunter [micro_ai] tag requires either id= key or [filter] tag")
if (cfg.action ~= 'delete') then
if (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Hunter [micro_ai] tag requires either id= key or [filter] tag")
end
AH.get_named_loc_xy('home', cfg, 'Hunter [micro_ai] tag')
end
local required_keys = { "home_x", "home_y" }
local optional_keys = { "id", "[filter]", "[filter_location]", "rest_turns", "show_messages" }
local required_keys = {}
local optional_keys = { "id", "[filter]", "[filter_location]", "home_loc", "home_x", "home_y", "rest_turns", "show_messages" }
local CA_parms = {
ai_id = 'mai_hunter',
{ ca_id = "move", location = 'ca_hunter.lua', score = cfg.ca_score or 300000 }

View file

@ -1,7 +1,15 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
function wesnoth.micro_ais.bottleneck_defense(cfg)
local required_keys = { "x", "y", "enemy_x", "enemy_y" }
local optional_keys = { "healer_x", "healer_y", "leadership_x", "leadership_y", "active_side_leader" }
if (cfg.action ~= 'delete') then
AH.get_multi_named_locs_xy('', cfg, 'Bottleneck [micro_ai] tag')
AH.get_multi_named_locs_xy('enemy', cfg, 'Bottleneck [micro_ai] tag')
end
local required_keys = {}
local optional_keys = { "location_id", "x", "y", "enemy_loc", "enemy_x", "enemy_y",
"healer_loc", "healer_x", "healer_y", "leadership_loc","leadership_x", "leadership_y", "active_side_leader"
}
local score = cfg.ca_score or 300000
local CA_parms = {
ai_id = 'mai_bottleneck',

View file

@ -1,11 +1,15 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local H = wesnoth.require "helper"
function wesnoth.micro_ais.messenger_escort(cfg)
if (cfg.action ~= 'delete') and (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Messenger [micro_ai] tag requires either id= key or [filter] tag")
if (cfg.action ~= 'delete') then
if (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Messenger [micro_ai] tag requires either id= key or [filter] tag")
end
AH.get_multi_named_locs_xy('waypoint', cfg, 'Messenger [micro_ai] tag')
end
local required_keys = { "waypoint_x", "waypoint_y" }
local optional_keys = { "id", "enemy_death_chance", "[filter]", "[filter_second]", "invert_order", "messenger_death_chance" }
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 score = cfg.ca_score or 300000
local CA_parms = {
ai_id = 'mai_messenger',

View file

@ -1,16 +1,20 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local H = wesnoth.require "helper"
function wesnoth.micro_ais.stationed_guardian(cfg)
if (cfg.action ~= 'delete') and (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Stationed Guardian [micro_ai] tag requires either id= key or [filter] tag")
if (cfg.action ~= 'delete') then
if (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Stationed Guardian [micro_ai] tag requires either id= key or [filter] tag")
end
AH.get_named_loc_xy('station', cfg, 'Stationed guardian [micro_ai] tag')
end
local required_keys = { "distance", "station_x", "station_y" }
local optional_keys = { "id", "[filter]", "guard_x", "guard_y" }
local required_keys = { "distance" }
local optional_keys = { "id", "[filter]", "guard_loc", "guard_x", "guard_y", "station_loc", "station_x", "station_y" }
local CA_parms = {
ai_id = 'mai_stationed_guardian',
{ ca_id = 'move', location = 'ca_stationed_guardian.lua', score = cfg.ca_score or 300000 }
}
return required_keys, optional_keys, CA_parms
return required_keys, optional_keys, CA_parms
end
function wesnoth.micro_ais.zone_guardian(cfg)
@ -18,25 +22,28 @@ function wesnoth.micro_ais.zone_guardian(cfg)
H.wml_error("Zone Guardian [micro_ai] tag requires either id= key or [filter] tag")
end
local required_keys = { "[filter_location]" }
local optional_keys = { "id", "[filter]", "[filter_location_enemy]", "station_x", "station_y" }
local optional_keys = { "id", "[filter]", "[filter_location_enemy]", "station_loc", "station_x", "station_y" }
local CA_parms = {
ai_id = 'mai_zone_guardian',
{ ca_id = 'move', location = 'ca_zone_guardian.lua', score = cfg.ca_score or 300000 }
}
return required_keys, optional_keys, CA_parms
return required_keys, optional_keys, CA_parms
end
function wesnoth.micro_ais.return_guardian(cfg)
if (cfg.action ~= 'delete') and (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Return Guardian [micro_ai] tag requires either id= key or [filter] tag")
if (cfg.action ~= 'delete') then
if (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Return Guardian [micro_ai] tag requires either id= key or [filter] tag")
end
AH.get_named_loc_xy('return', cfg, 'Return guardian [micro_ai] tag')
end
local required_keys = { "return_x", "return_y" }
local optional_keys = { "id", "[filter]" }
local required_keys = {}
local optional_keys = { "id", "[filter]", "return_loc", "return_x", "return_y" }
local CA_parms = {
ai_id = 'mai_return_guardian',
{ ca_id = 'move', location = 'ca_return_guardian.lua', score = cfg.ca_score or 100100 }
}
return required_keys, optional_keys, CA_parms
return required_keys, optional_keys, CA_parms
end
function wesnoth.micro_ais.coward(cfg)
@ -44,10 +51,10 @@ function wesnoth.micro_ais.coward(cfg)
H.wml_error("Coward [micro_ai] tag requires either id= key or [filter] tag")
end
local required_keys = { "distance" }
local optional_keys = { "attack_if_trapped", "id", "[filter]", "[filter_second]", "seek_x", "seek_y","avoid_x","avoid_y" }
local optional_keys = { "attack_if_trapped", "id", "[filter]", "[filter_second]", "seek_loc", "seek_x", "seek_y", "avoid_loc", "avoid_x", "avoid_y" }
local CA_parms = {
ai_id = 'mai_coward',
{ ca_id = 'move', location = 'ca_coward.lua', score = cfg.ca_score or 300000 }
}
return required_keys, optional_keys, CA_parms
return required_keys, optional_keys, CA_parms
end

View file

@ -1,11 +1,15 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local H = wesnoth.require "helper"
function wesnoth.micro_ais.patrol(cfg)
if (cfg.action ~= 'delete') and (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Patrol [micro_ai] tag requires either id= key or [filter] tag")
if (cfg.action ~= 'delete') then
if (not cfg.id) and (not wml.get_child(cfg, "filter")) then
H.wml_error("Patrol [micro_ai] tag requires either id= key or [filter] tag")
end
AH.get_multi_named_locs_xy('waypoint', cfg, 'Patrol [micro_ai] tag')
end
local required_keys = { "waypoint_x", "waypoint_y" }
local optional_keys = { "id", "[filter]", "attack", "one_time_only", "out_and_back" }
local required_keys = {}
local optional_keys = { "id", "[filter]", "attack", "one_time_only", "out_and_back", "waypoint_loc", "waypoint_x", "waypoint_y" }
local CA_parms = {
ai_id = 'mai_patrol',
{ ca_id = "move", location = 'ca_patrol.lua', score = cfg.ca_score or 300000 }

View file

@ -1,3 +1,4 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local H = wesnoth.require "helper"
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
@ -15,12 +16,7 @@ function wesnoth.micro_ais.protect_unit(cfg)
if not u.id then
H.wml_error("Protect Unit Micro AI missing id key in [unit] tag")
end
if not u.goal_x then
H.wml_error("Protect Unit Micro AI missing goal_x key in [unit] tag")
end
if not u.goal_y then
H.wml_error("Protect Unit Micro AI missing goal_y key in [unit] tag")
end
AH.get_multi_named_locs_xy('goal', u, 'Protect Unit Micro AI [unit] tag')
table.insert(unit_ids, u.id)
end