Merge pull request #6802 from mattsc/mai_parms_tests
Add Micro AI parameter checking
This commit is contained in:
commit
07d8a55d17
16 changed files with 258 additions and 65 deletions
|
@ -667,7 +667,7 @@ function ai_helper.get_named_loc_xy(param_core, cfg, required_for)
|
|||
if loc then
|
||||
return loc
|
||||
else
|
||||
wml.error("Named location does not exist: " .. loc_id)
|
||||
wml.error("Named location does not exist: " .. loc_id .. " " .. (required_for or ''))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -676,7 +676,7 @@ function ai_helper.get_named_loc_xy(param_core, cfg, required_for)
|
|||
local x, y = cfg[param_x], cfg[param_y]
|
||||
if x and y then
|
||||
if not wesnoth.current.map:on_board(x, y) then
|
||||
wml.error("Location is not on map: " .. param_x .. ',' .. param_y .. ' = ' .. x .. ',' .. y)
|
||||
wml.error("Location is not on map: " .. param_x .. ',' .. param_y .. ' = ' .. x .. ',' .. y .. " " .. (required_for or ''))
|
||||
end
|
||||
|
||||
return { x, y }
|
||||
|
@ -702,7 +702,7 @@ function ai_helper.get_multi_named_locs_xy(param_core, cfg, required_for)
|
|||
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)
|
||||
local loc = ai_helper.get_named_loc_xy(param_core, tmp_cfg, required_for)
|
||||
table.insert(locs, loc)
|
||||
end
|
||||
return locs
|
||||
|
@ -722,7 +722,7 @@ function ai_helper.get_multi_named_locs_xy(param_core, cfg, required_for)
|
|||
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)
|
||||
local loc = ai_helper.get_named_loc_xy(param_core, tmp_cfg, required_for)
|
||||
table.insert(locs, loc)
|
||||
end
|
||||
return locs
|
||||
|
|
|
@ -2,8 +2,8 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
|
||||
|
||||
function wesnoth.micro_ais.big_animals(cfg)
|
||||
local required_keys = { "[filter]"}
|
||||
local optional_keys = { "[avoid_unit]", "[filter_location]", "[filter_location_wander]" }
|
||||
local required_keys = { filter = 'tag' }
|
||||
local optional_keys = { avoid_unit = 'tag', filter_location = 'tag', filter_location_wander = 'tag' }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_big_animals',
|
||||
{ ca_id = "move", location = 'ca_big_animals.lua', score = cfg.ca_score or 300000 }
|
||||
|
@ -12,8 +12,8 @@ function wesnoth.micro_ais.big_animals(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.wolves(cfg)
|
||||
local required_keys = { "[filter]", "[filter_second]" }
|
||||
local optional_keys = { "attack_only_prey", "avoid_type" }
|
||||
local required_keys = { filter = 'tag', filter_second = 'tag' }
|
||||
local optional_keys = { attack_only_prey = 'boolean', avoid_type = 'string' }
|
||||
local score = cfg.ca_score or 90000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_wolves',
|
||||
|
@ -69,8 +69,10 @@ function wesnoth.micro_ais.herding(cfg)
|
|||
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 required_keys = { filter_location = 'tag', filter = 'tag', filter_second = 'tag' }
|
||||
local optional_keys = { attention_distance = 'integer', attack_distance = 'integer',
|
||||
herd_loc = 'string', herd_x = 'integer', herd_y = 'integer'
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_herding',
|
||||
|
@ -112,8 +114,9 @@ function wesnoth.persistent_tags.micro_ai_rabbits.write(add)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.forest_animals(cfg)
|
||||
local optional_keys = { "rabbit_type", "rabbit_number", "rabbit_enemy_distance", "rabbit_hole_img",
|
||||
"tusker_type", "tusklet_type", "deer_type", "[filter_location]"
|
||||
local optional_keys = { rabbit_type = 'string', rabbit_number = 'integer',
|
||||
rabbit_enemy_distance = 'integer', rabbit_hole_img = 'string', tusker_type = 'string',
|
||||
tusklet_type = 'string', deer_type = 'string', filter_location = 'tag'
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
|
@ -146,7 +149,9 @@ function wesnoth.micro_ais.forest_animals(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.swarm(cfg)
|
||||
local optional_keys = { "[avoid]", "[filter]", "scatter_distance", "vision_distance", "enemy_distance" }
|
||||
local optional_keys = { avoid = 'tag', filter = 'tag', scatter_distance = 'integer',
|
||||
vision_distance = 'integer', enemy_distance = 'integer'
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_swarm',
|
||||
|
@ -157,7 +162,7 @@ function wesnoth.micro_ais.swarm(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.wolves_multipacks(cfg)
|
||||
local optional_keys = { "[avoid]", "type", "pack_size", "show_pack_number" }
|
||||
local optional_keys = { avoid = 'tag', type = 'string', pack_size = 'integer', show_pack_number = 'boolean' }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_wolves_multipacks',
|
||||
|
@ -175,7 +180,9 @@ function wesnoth.micro_ais.hunter(cfg)
|
|||
AH.get_named_loc_xy('home', cfg, 'Hunter [micro_ai] tag')
|
||||
end
|
||||
local required_keys = {}
|
||||
local optional_keys = { "id", "[filter]", "[filter_location]", "home_loc", "home_x", "home_y", "rest_turns", "show_messages" }
|
||||
local optional_keys = { id = 'string', filter = 'tag', filter_location = 'tag', home_loc = 'string',
|
||||
home_x = 'integer', home_y = 'integer', rest_turns = 'integer', show_messages = 'boolean'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_hunter',
|
||||
{ ca_id = "move", location = 'ca_hunter.lua', score = cfg.ca_score or 300000 }
|
||||
|
|
|
@ -7,8 +7,11 @@ function wesnoth.micro_ais.bottleneck_defense(cfg)
|
|||
end
|
||||
|
||||
local required_keys = {}
|
||||
local optional_keys = { "location_id", "x", "y", "enemy_loc", "enemy_x", "enemy_y", "[filter]",
|
||||
"healer_loc", "healer_x", "healer_y", "leadership_loc","leadership_x", "leadership_y", "active_side_leader"
|
||||
local optional_keys = { location_id = 'string', x = 'integer_list', y = 'integer_list',
|
||||
enemy_loc = 'string', enemy_x = 'integer_list', enemy_y = 'integer_list',
|
||||
healer_loc = 'string', healer_x = 'integer_list', healer_y = 'integer_list',
|
||||
leadership_loc = 'string', leadership_x = 'integer_list', leadership_y = 'integer_list',
|
||||
filter = 'tag', active_side_leader = 'boolean'
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
|
|
|
@ -8,7 +8,9 @@ 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 = { "[avoid]", "id", "enemy_death_chance", "[filter]", "[filter_second]", "invert_order", "messenger_death_chance", "waypoint_loc", "waypoint_x", "waypoint_y" }
|
||||
local optional_keys = { avoid = 'tag', id = 'string', enemy_death_chance = 'float', filter = 'tag', filter_second = 'tag',
|
||||
invert_order = 'boolean', messenger_death_chance = 'float', waypoint_loc = 'string', waypoint_x = 'integer_list', waypoint_y = 'integer_list'
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_messenger',
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
function wesnoth.micro_ais.fast_ai(cfg)
|
||||
local optional_keys = {
|
||||
"attack_hidden_enemies", "[avoid]", "dungeon_mode",
|
||||
"[filter]", "[filter_second]", "include_occupied_attack_hexes",
|
||||
"leader_additional_threat", "leader_attack_max_units", "leader_weight", "move_cost_factor",
|
||||
"weak_units_first", "skip_combat_ca", "skip_move_ca", "threatened_leader_fights"
|
||||
local optional_keys = { attack_hidden_enemies = 'boolean', avoid = 'tag', dungeon_mode = 'boolean',
|
||||
filter = 'tag', filter_second = 'tag', include_occupied_attack_hexes = 'boolean',
|
||||
leader_additional_threat = 'float', leader_attack_max_units = 'integer', leader_weight = 'float',
|
||||
move_cost_factor = 'float', weak_units_first = 'boolean', skip_combat_ca = 'boolean',
|
||||
skip_move_ca = 'boolean', threatened_leader_fights = 'boolean'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_fast',
|
||||
|
|
|
@ -7,8 +7,11 @@ function wesnoth.micro_ais.stationed_guardian(cfg)
|
|||
end
|
||||
AH.get_named_loc_xy('station', cfg, 'Stationed guardian [micro_ai] tag')
|
||||
end
|
||||
local required_keys = { "distance" }
|
||||
local optional_keys = { "id", "[filter]", "guard_loc", "guard_x", "guard_y", "station_loc", "station_x", "station_y" }
|
||||
local required_keys = { distance = 'integer' }
|
||||
local optional_keys = { id = 'string', filter = 'tag',
|
||||
guard_loc = 'string', guard_x = 'integer', guard_y = 'integer',
|
||||
station_loc = 'string', station_x = 'integer', station_y = 'integer'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_stationed_guardian',
|
||||
{ ca_id = 'move', location = 'ca_stationed_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
|
@ -20,8 +23,10 @@ function wesnoth.micro_ais.zone_guardian(cfg)
|
|||
if (cfg.action ~= 'delete') and (not cfg.id) and (not wml.get_child(cfg, "filter")) then
|
||||
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_loc", "station_x", "station_y" }
|
||||
local required_keys = { filter_location = 'tag' }
|
||||
local optional_keys = { id = 'string', filter = 'tag', filter_location_enemy = 'tag',
|
||||
station_loc = 'string', station_x = 'integer', station_y = 'integer'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_zone_guardian',
|
||||
{ ca_id = 'move', location = 'ca_zone_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
|
@ -37,7 +42,9 @@ function wesnoth.micro_ais.return_guardian(cfg)
|
|||
AH.get_named_loc_xy('return', cfg, 'Return guardian [micro_ai] tag')
|
||||
end
|
||||
local required_keys = {}
|
||||
local optional_keys = { "id", "[filter]", "return_loc", "return_x", "return_y" }
|
||||
local optional_keys = { id = 'string', filter = 'tag',
|
||||
return_loc = 'string', return_x = 'integer', return_y = 'integer'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_return_guardian',
|
||||
{ ca_id = 'move', location = 'ca_return_guardian.lua', score = cfg.ca_score or 100100 }
|
||||
|
@ -49,8 +56,11 @@ function wesnoth.micro_ais.coward(cfg)
|
|||
if (cfg.action ~= 'delete') and (not cfg.id) and (not wml.get_child(cfg, "filter")) then
|
||||
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_loc", "seek_x", "seek_y", "avoid_loc", "avoid_x", "avoid_y" }
|
||||
local required_keys = { distance = 'integer' }
|
||||
local optional_keys = { attack_if_trapped = 'boolean', id = 'string', filter = 'tag',
|
||||
filter_second = 'tag', seek_loc = 'string', seek_x = 'integer', seek_y = 'integer',
|
||||
avoid_loc = 'string', avoid_x = 'integer', avoid_y = 'integer'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_coward',
|
||||
{ ca_id = 'move', location = 'ca_coward.lua', score = cfg.ca_score or 300000 }
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
function wesnoth.micro_ais.healer_support(cfg)
|
||||
local optional_keys = { "aggression", "injured_units_only", "max_threats", "[filter]", "[filter_second]" }
|
||||
local optional_keys = { aggression = 'float', injured_units_only = 'boolean',
|
||||
max_threats = 'integer', filter = 'tag', filter_second = 'tag'
|
||||
}
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_healer',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
function wesnoth.micro_ais.assassin(cfg)
|
||||
local required_keys = { "[filter]", "[filter_second]" }
|
||||
local optional_keys = { "[prefer]" }
|
||||
local required_keys = { filter = 'tag', filter_second = 'tag' }
|
||||
local optional_keys = { prefer = 'tag' }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_assassin',
|
||||
{ ca_id = 'attack', location = 'ca_simple_attack.lua', score = 110001 },
|
||||
|
@ -10,8 +10,8 @@ function wesnoth.micro_ais.assassin(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.lurkers(cfg)
|
||||
local required_keys = { "[filter]", "[filter_location]" }
|
||||
local optional_keys = { "stationary", "[filter_location_wander]" }
|
||||
local required_keys = { filter = 'tag', filter_location = 'tag' }
|
||||
local optional_keys = { stationary = 'boolean', filter_location_wander = 'tag' }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_lurkers',
|
||||
{ ca_id = 'move', location = 'ca_lurkers.lua', score = cfg.ca_score or 300000 }
|
||||
|
@ -21,10 +21,10 @@ end
|
|||
|
||||
-- goto is a keyword, so need to use index operator directly
|
||||
wesnoth.micro_ais["goto"] = function(cfg)
|
||||
local required_keys = { "[filter_location]" }
|
||||
local optional_keys = {
|
||||
"[avoid]", "avoid_enemies", "[filter]", "ignore_units", "ignore_enemy_at_goal",
|
||||
"release_all_units_at_goal", "release_unit_at_goal", "remove_movement", "unique_goals", "use_straight_line"
|
||||
local required_keys = { filter_location = 'tag' }
|
||||
local optional_keys = { avoid = 'tag', avoid_enemies = 'float', filter = 'tag', ignore_units = 'boolean',
|
||||
ignore_enemy_at_goal = 'boolean', release_all_units_at_goal = 'boolean', release_unit_at_goal = 'boolean',
|
||||
remove_movement = 'boolean', unique_goals = 'boolean', use_straight_line = 'boolean'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_goto',
|
||||
|
@ -34,7 +34,8 @@ wesnoth.micro_ais["goto"] = function(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.hang_out(cfg)
|
||||
local optional_keys = { "[filter]", "[filter_location]", "[avoid]", "[mobilize_condition]", "mobilize_on_gold_less_than" }
|
||||
local optional_keys = { filter = 'tag', filter_location = 'tag', avoid = 'tag',
|
||||
mobilize_condition = 'tag', mobilize_on_gold_less_than = 'integer' }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_hang_out',
|
||||
{ ca_id = 'move', location = 'ca_hang_out.lua', score = cfg.ca_score or 170000 }
|
||||
|
@ -43,7 +44,7 @@ function wesnoth.micro_ais.hang_out(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.simple_attack(cfg)
|
||||
local optional_keys = { "[filter]", "[filter_second]", "weapon" }
|
||||
local optional_keys = { filter = 'tag', filter_second = 'tag', weapon = 'integer' }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_simple_attack',
|
||||
{ ca_id = 'move', location = 'ca_simple_attack.lua', score = cfg.ca_score or 110000 }
|
||||
|
|
|
@ -8,7 +8,10 @@ function wesnoth.micro_ais.patrol(cfg)
|
|||
AH.get_multi_named_locs_xy('waypoint', cfg, 'Patrol [micro_ai] tag')
|
||||
end
|
||||
local required_keys = {}
|
||||
local optional_keys = { "id", "[filter]", "attack", "attack_range", "attack_invisible_enemies", "one_time_only", "out_and_back", "waypoint_loc", "waypoint_x", "waypoint_y" }
|
||||
local optional_keys = { id = 'string', filter = 'tag', attack = 'string',
|
||||
attack_range = 'integer', attack_invisible_enemies = 'boolean', one_time_only = 'boolean',
|
||||
out_and_back = 'boolean', waypoint_loc = 'string', waypoint_x = 'integer_list', waypoint_y = 'integer_list'
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_patrol',
|
||||
{ ca_id = "move", location = 'ca_patrol.lua', score = cfg.ca_score or 300000 }
|
||||
|
|
|
@ -58,5 +58,6 @@ function wesnoth.micro_ais.protect_unit(cfg)
|
|||
else
|
||||
MAIH.add_aspects(cfg.side, aspect_parms)
|
||||
end
|
||||
return {"[unit]"}, {}, CA_parms
|
||||
|
||||
return { unit = 'tag' }, { disable_move_leader_to_keep = 'boolean' }, CA_parms
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ local function handle_default_recruitment(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.recruit_rushers(cfg)
|
||||
local optional_keys = { "high_level_fraction", "randomness" }
|
||||
local optional_keys = { high_level_fraction = 'float', randomness = 'float' }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_rusher_recruit',
|
||||
{ ca_id = "move", location = '../../lua/ca_recruit_rushers.lua', score = cfg.ca_score or 180000 }
|
||||
|
@ -29,7 +29,7 @@ function wesnoth.micro_ais.recruit_rushers(cfg)
|
|||
end
|
||||
|
||||
function wesnoth.micro_ais.recruit_random(cfg)
|
||||
local optional_keys = { "skip_low_gold_recruiting", "[probability]" }
|
||||
local optional_keys = { skip_low_gold_recruiting = 'boolean', probability = 'tag' }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_random_recruit',
|
||||
{ ca_id = "move", location = 'ca_recruit_random.lua', score = cfg.ca_score or 180000 }
|
||||
|
|
|
@ -1,6 +1,74 @@
|
|||
local T = wml.tag
|
||||
local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local showed_deprecation_message = {}
|
||||
|
||||
local function show_deprecation_message(ai_type)
|
||||
if (not showed_deprecation_message[ai_type]) then
|
||||
wesnoth.deprecated_message('[micro_ai] tag (' .. ai_type .. '): The old syntax for required and optional parameters when creating a Micro AIs', 3, '1.19', "instead of { 'parameter' } it is now { parameter = 'parameter_type' }. See data/ai/micro_ai/mai-defs/ for examples." )
|
||||
showed_deprecation_message[ai_type] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function is_number(value, check_int, ai_type, name)
|
||||
-- Allow any number for 'float' type.
|
||||
-- Allow anything that has an integer representation for 'integer', but
|
||||
-- issue warning if it is given in 'float' notation.
|
||||
local number = tonumber(value)
|
||||
if (not number) then
|
||||
return false
|
||||
elseif check_int then
|
||||
if (number ~= math.floor(number)) then
|
||||
return false
|
||||
elseif (math.type(number) ~= 'integer') then
|
||||
str = "[micro_ai] tag (" .. ai_type .. ") parameter '" .. name .. "' must be an integer. It has an integer representation, but is provided in floating-point format."
|
||||
warn(str)
|
||||
std_print(str .. ' (see Lua console for stack trace)')
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function check_key_type(k_name, k_value, k_type, ai_type)
|
||||
if k_value then
|
||||
local is_wrong_type = false
|
||||
if (k_type == 'float') then
|
||||
is_wrong_type = not is_number(k_value, false, ai_type, k_name)
|
||||
elseif (k_type == 'integer') then
|
||||
is_wrong_type = not is_number(k_value, true, ai_type, k_name)
|
||||
elseif (k_type == 'float_list') then
|
||||
local str = tostring(k_value)
|
||||
for _,s in ipairs(str:split()) do
|
||||
if (not is_number(s, false, ai_type, k_name)) then
|
||||
is_wrong_type = true
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif (k_type == 'integer_list') then
|
||||
local str = tostring(k_value)
|
||||
for _,s in ipairs(str:split()) do
|
||||
if (not is_number(s, true, ai_type, k_name)) then
|
||||
is_wrong_type = true
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif (k_type == 'boolean') then
|
||||
if (type(k_value) ~= k_type) then
|
||||
is_wrong_type = true
|
||||
end
|
||||
-- Anything can be a string
|
||||
elseif (k_type ~= 'string') then
|
||||
-- This is just here to make sure we're not forgettign something
|
||||
wml.error("[micro_ai] tag (" .. ai_type .. ") parameter '" .. k_name .. "' unknown type " .. k_type)
|
||||
end
|
||||
|
||||
if is_wrong_type then
|
||||
wml.error("[micro_ai] tag (" .. ai_type .. ") parameter '" .. k_name .. "' must be of type " .. k_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local micro_ai_helper = {}
|
||||
|
||||
function micro_ai_helper.add_CAs(side, ca_id_core, CA_parms, CA_cfg)
|
||||
|
@ -164,32 +232,125 @@ function micro_ai_helper.micro_ai_setup(cfg, CA_parms, required_keys, optional_k
|
|||
local CA_cfg = {}
|
||||
|
||||
-- Required keys
|
||||
for _,v in pairs(required_keys) do
|
||||
if v:match('%[[a-zA-Z0-9_]+%]') then
|
||||
v = v:sub(2,-2)
|
||||
if not wml.get_child(cfg, v) then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: [" .. v .. "]")
|
||||
for k_name,k_type in pairs(required_keys) do
|
||||
if (type(k_name) == 'number') then
|
||||
-- Old syntax is supported for Wesnoth 1.17/1.18
|
||||
if k_type:match('%[[a-zA-Z0-9_]+%]') then
|
||||
k_type = k_type:sub(2,-2)
|
||||
if not wml.get_child(cfg, k_type) then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: [" .. k_type .. "]")
|
||||
end
|
||||
for child in wml.child_range(cfg, k_type) do
|
||||
table.insert(CA_cfg, T[k_type](child))
|
||||
end
|
||||
else
|
||||
if not cfg[k_type] then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: " .. k_type .."=")
|
||||
end
|
||||
CA_cfg[k_type] = cfg[k_type]
|
||||
end
|
||||
show_deprecation_message(cfg.ai_type)
|
||||
elseif (k_type == 'tag') then
|
||||
-- Check that this is not a scalar parameter
|
||||
if cfg[k_name] then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") parameter '[" .. k_name .. "]' must be a WML tag")
|
||||
end
|
||||
for child in wml.child_range(cfg, v) do
|
||||
table.insert(CA_cfg, T[v](child))
|
||||
if (not wml.get_child(cfg, k_name)) then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: [" .. k_name .. "]")
|
||||
end
|
||||
for child in wml.child_range(cfg, k_name) do
|
||||
table.insert(CA_cfg, T[k_name](child))
|
||||
end
|
||||
else
|
||||
if not cfg[v] then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: " .. v .."=")
|
||||
check_key_type(k_name, cfg[k_name], k_type, cfg.ai_type)
|
||||
if not cfg[k_name] then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") is missing required parameter: " .. k_name .."=")
|
||||
end
|
||||
CA_cfg[v] = cfg[v]
|
||||
CA_cfg[k_name] = cfg[k_name]
|
||||
end
|
||||
end
|
||||
|
||||
-- Optional keys
|
||||
for _,v in pairs(optional_keys) do
|
||||
if v:match('%[[a-zA-Z0-9_]+%]') then
|
||||
v = v:sub(2,-2)
|
||||
for child in wml.child_range(cfg, v) do
|
||||
table.insert(CA_cfg, T[v](child))
|
||||
for k_name,k_type in pairs(optional_keys) do
|
||||
if (type(k_name) == 'number') then
|
||||
-- Old syntax is supported for Wesnoth 1.17/1.18
|
||||
if k_type:match('%[[a-zA-Z0-9_]+%]') then
|
||||
k_type = k_type:sub(2,-2)
|
||||
for child in wml.child_range(cfg, k_type) do
|
||||
table.insert(CA_cfg, T[k_type](child))
|
||||
end
|
||||
else
|
||||
CA_cfg[k_type] = cfg[k_type]
|
||||
end
|
||||
show_deprecation_message(cfg.ai_type)
|
||||
elseif (k_type == 'tag') then
|
||||
-- Check that this is not a scalar parameter
|
||||
if cfg[k_name] then
|
||||
wml.error("[micro_ai] tag (" .. cfg.ai_type .. ") parameter '[" .. k_name .. "]' must be a WML tag")
|
||||
end
|
||||
for child in wml.child_range(cfg, k_name) do
|
||||
table.insert(CA_cfg, T[k_name](child))
|
||||
end
|
||||
else
|
||||
CA_cfg[v] = cfg[v]
|
||||
check_key_type(k_name, cfg[k_name], k_type, cfg.ai_type)
|
||||
CA_cfg[k_name] = cfg[k_name]
|
||||
end
|
||||
end
|
||||
|
||||
-- Check whether there are invalid keys in the [micro_ai] tag
|
||||
for k in pairs(cfg) do
|
||||
if (not showed_deprecation_message[cfg.ai_type]) -- otherwise this produces false positives
|
||||
and (k ~= 'side') and (k ~= 'ai_type') and (k ~= 'action')
|
||||
and (k ~= 'ca_id') and (k ~= 'ca_score') and (type(k) ~= 'number')
|
||||
then
|
||||
local is_invalid = true
|
||||
for k_name in pairs(required_keys) do
|
||||
if (k == k_name) then
|
||||
is_invalid = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if is_invalid then
|
||||
for k_name in pairs(optional_keys) do
|
||||
if (k == k_name) then
|
||||
is_invalid = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if is_invalid then
|
||||
local str = "[micro_ai] tag (" .. cfg.ai_type .. ") contains invalid parameter: " .. k
|
||||
warn(str)
|
||||
std_print(str .. ' (see Lua console for stack trace)')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check whether there are invalid tags in the [micro_ai] tag
|
||||
for _,t in ipairs(cfg) do
|
||||
-- [filter] is always added to [micro_ai] tags inside a unit's [ai] tag,
|
||||
-- whether the specific MAI supports it or not
|
||||
if (not showed_deprecation_message[cfg.ai_type]) and (t[1] ~= 'filter') then
|
||||
local is_invalid = true
|
||||
for k_name in pairs(required_keys) do
|
||||
if (t[1] == k_name) then
|
||||
is_invalid = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if is_invalid then
|
||||
for k_name in pairs(optional_keys) do
|
||||
if (t[1] == k_name) then
|
||||
is_invalid = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if is_invalid then
|
||||
str = "[micro_ai] tag (" .. cfg.ai_type .. ") contains invalid parameter: [" .. t[1] .. "]"
|
||||
warn(str)
|
||||
std_print(str .. ' (see Lua console for stack trace)')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -468,7 +468,7 @@ separate attack Zone"
|
|||
[set_menu_item]
|
||||
id=m06_zone
|
||||
description= _ "Zone Guardian"
|
||||
image=units/nagas/fighter.png~CROP(25,19,24,24)
|
||||
image=units/nagas/fighter/fighter.png~CROP(25,19,24,24)
|
||||
[show_if]
|
||||
{VARIABLE_CONDITIONAL scenario_name equals guardians}
|
||||
[/show_if]
|
||||
|
|
|
@ -372,7 +372,7 @@
|
|||
[set_menu_item]
|
||||
id=m01_menu_lurker4
|
||||
description= _ "Place a Side 4 lurker"
|
||||
image=units/nagas/fighter.png~CROP(25,19,24,24)
|
||||
image=units/nagas/fighter/fighter.png~CROP(25,19,24,24)
|
||||
[show_if]
|
||||
{VARIABLE_CONDITIONAL scenario_name equals lurkers}
|
||||
[/show_if]
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
[set_menu_item]
|
||||
id=m03_new_vampire_bat
|
||||
description= _ "Place Side 2 Vampire Bat"
|
||||
image=units/undead/bat-se-4.png~CROP(24,16,24,24)
|
||||
image=units/bats/bat-se-4.png~CROP(24,16,24,24)
|
||||
[show_if]
|
||||
{VARIABLE_CONDITIONAL scenario_name equals swarm}
|
||||
[/show_if]
|
||||
|
@ -116,7 +116,7 @@
|
|||
[set_menu_item]
|
||||
id=m04_new_blood_bat
|
||||
description= _ "Place Side 2 Blood Bat"
|
||||
image=units/undead/bloodbat-se-4.png~CROP(24,16,24,24)
|
||||
image=units/bats/bloodbat-se-4.png~CROP(24,16,24,24)
|
||||
[show_if]
|
||||
{VARIABLE_CONDITIONAL scenario_name equals swarm}
|
||||
[/show_if]
|
||||
|
|
|
@ -198,6 +198,9 @@ void team_builder::handle_leader(const config& leader)
|
|||
stored.remove_attribute(attr);
|
||||
}
|
||||
|
||||
// Remove [ai] tag as it is already added for the side
|
||||
stored.remove_children("ai");
|
||||
|
||||
// Provide some default values, if not specified.
|
||||
config::attribute_value& a1 = stored["canrecruit"];
|
||||
if(a1.blank()) {
|
||||
|
|
Loading…
Add table
Reference in a new issue