MicroAIs: Refactor code to make it easy for add-ons to add new ones
This commit is contained in:
parent
6ed34069fe
commit
c19ddba30a
12 changed files with 585 additions and 511 deletions
|
@ -138,6 +138,9 @@ Version 1.13.4+dev:
|
|||
The table is read-only and raises an error if you attempt to write to it.
|
||||
* The way to create Lua candidate actions has changed a little. Old code
|
||||
will require minor changes.
|
||||
* New wesnoth.micro_ais table contains the loaders for all Micro AIs.
|
||||
New loaders can easily be installed by add-ons. See any built-in
|
||||
micro AI (in ai/micro_ais/mai-defs/) for an example of how to do this.
|
||||
* Wesnoth formula engine:
|
||||
* Formulas in unit filters can now access nearly all unit attributes
|
||||
* New syntax features:
|
||||
|
|
133
data/ai/micro_ais/mai-defs/animals.lua
Normal file
133
data/ai/micro_ais/mai-defs/animals.lua
Normal file
|
@ -0,0 +1,133 @@
|
|||
local H = wesnoth.require "lua/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 CA_parms = {
|
||||
ai_id = 'mai_big_animals',
|
||||
{ ca_id = "move", location = 'ca_big_animals.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.wolves(cfg)
|
||||
local required_keys = { "filter", "filter_second" }
|
||||
local optional_keys = { "attack_only_prey", "avoid_type" }
|
||||
local score = cfg.ca_score or 90000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_wolves',
|
||||
{ ca_id = "move", location = 'ca_wolves_move.lua', score = score },
|
||||
{ ca_id = "wander", location = 'ca_wolves_wander.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
if cfg.attack_only_prey then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
elseif cfg.avoid_type then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "not", {
|
||||
type=cfg.avoid_type
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
end
|
||||
return required_keys, optional_keys, CA_parms
|
||||
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" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_herding',
|
||||
{ ca_id = "attack_close_enemy", location = 'ca_herding_attack_close_enemy.lua', score = score },
|
||||
{ ca_id = "sheep_runs_enemy", location = 'ca_herding_sheep_runs_enemy.lua', score = score - 1 },
|
||||
{ ca_id = "sheep_runs_dog", location = 'ca_herding_sheep_runs_dog.lua', score = score - 2 },
|
||||
{ ca_id = "herd_sheep", location = 'ca_herding_herd_sheep.lua', score = score - 3 },
|
||||
{ ca_id = "sheep_move", location = 'ca_herding_sheep_move.lua', score = score - 4 },
|
||||
{ ca_id = "dog_move", location = 'ca_herding_dog_move.lua', score = score - 5 },
|
||||
{ ca_id = "dog_stopmove", location = 'ca_herding_dog_stopmove.lua', score = score - 6 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
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 score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_forest_animals',
|
||||
{ ca_id = "new_rabbit", location = 'ca_forest_animals_new_rabbit.lua', score = score },
|
||||
{ ca_id = "tusker_attack", location = 'ca_forest_animals_tusker_attack.lua', score = score - 1 },
|
||||
{ ca_id = "move", location = 'ca_forest_animals_move.lua', score = score - 2 },
|
||||
{ ca_id = "tusklet_move", location = 'ca_forest_animals_tusklet_move.lua', score = score - 3 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.swarm(cfg)
|
||||
local optional_keys = { "scatter_distance", "vision_distance", "enemy_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_swarm',
|
||||
{ ca_id = "scatter", location = 'ca_swarm_scatter.lua', score = score },
|
||||
{ ca_id = "move", location = 'ca_swarm_move.lua', score = score - 1 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.wolves_multipacks(cfg)
|
||||
local optional_keys = { "type", "pack_size", "show_pack_number" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_wolves_multipacks',
|
||||
{ ca_id = "attack", location = 'ca_wolves_multipacks_attack.lua', score = score },
|
||||
{ ca_id = "wander", location = 'ca_wolves_multipacks_wander.lua', score = score - 1 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.hunter(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Hunter [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "home_x", "home_y" }
|
||||
local optional_keys = { "id", "filter", "filter_location", "rest_turns", "show_messages" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_hunter',
|
||||
{ ca_id = "move", location = 'ca_hunter.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
12
data/ai/micro_ais/mai-defs/bottleneck.lua
Normal file
12
data/ai/micro_ais/mai-defs/bottleneck.lua
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
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" }
|
||||
local score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_bottleneck',
|
||||
{ ca_id = 'move', location = 'ca_bottleneck_move.lua', score = score },
|
||||
{ ca_id = 'attack', location = 'ca_bottleneck_attack.lua', score = score - 1 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
17
data/ai/micro_ais/mai-defs/escort.lua
Normal file
17
data/ai/micro_ais/mai-defs/escort.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
||||
function wesnoth.micro_ais.messenger_escort(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Messenger [micro_ai] tag requires either id= key or [filter] 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 score = cfg.ca_score or 300000
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_messenger',
|
||||
{ ca_id = 'attack', location = 'ca_messenger_attack.lua', score = score },
|
||||
{ ca_id = 'move', location = 'ca_messenger_move.lua', score = score - 1 },
|
||||
{ ca_id = 'escort_move', location = 'ca_messenger_escort_move.lua', score = score - 2 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
115
data/ai/micro_ais/mai-defs/fast.lua
Normal file
115
data/ai/micro_ais/mai-defs/fast.lua
Normal file
|
@ -0,0 +1,115 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
|
||||
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 CA_parms = {
|
||||
ai_id = 'mai_fast',
|
||||
{ ca_id = 'combat', location = 'ca_fast_combat.lua', score = 100000 },
|
||||
{ ca_id = 'move', location = 'ca_fast_move.lua', score = 20000 },
|
||||
{ ca_id = 'combat_leader', location = 'ca_fast_combat_leader.lua', score = 19900 }
|
||||
}
|
||||
|
||||
-- Also need to delete/add some default CAs
|
||||
if (cfg.action == 'delete') then
|
||||
-- This can be done independently of whether these were removed earlier
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="combat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::combat_phase",
|
||||
max_score=100000,
|
||||
score=100000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="villages",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::get_villages_phase",
|
||||
max_score=60000,
|
||||
score=60000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="retreat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::retreat_phase",
|
||||
max_score=40000,
|
||||
score=40000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_to_targets",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_to_targets_phase",
|
||||
max_score=20000,
|
||||
score=20000
|
||||
} }
|
||||
}
|
||||
else
|
||||
if (not cfg.skip_combat_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[combat]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'combat') or (parm.ca_id == 'combat_leader') then
|
||||
table.remove(CA_parms, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not cfg.skip_move_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[villages]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[retreat]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_to_targets]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'move') then
|
||||
table.remove(CA_parms, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
53
data/ai/micro_ais/mai-defs/guardian.lua
Normal file
53
data/ai/micro_ais/mai-defs/guardian.lua
Normal file
|
@ -0,0 +1,53 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
||||
function wesnoth.micro_ais.stationed_guardian(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Stationed Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "distance", "station_x", "station_y" }
|
||||
local optional_keys = { "id", "filter", "guard_x", "guard_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
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.zone_guardian(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
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 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
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.return_guardian(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Return Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "return_x", "return_y" }
|
||||
local optional_keys = { "id", "filter" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_return_guardian',
|
||||
{ ca_id = 'move', location = 'ca_return_guardian.lua', score = cfg.ca_score or 100010 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.coward(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
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 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
|
||||
end
|
17
data/ai/micro_ais/mai-defs/healers.lua
Normal file
17
data/ai/micro_ais/mai-defs/healers.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
function wesnoth.micro_ais.healer_support(cfg)
|
||||
local optional_keys = { "aggression", "injured_units_only", "max_threats", "filter", "filter_second" }
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_healer',
|
||||
{ ca_id = 'initialize', location = 'ca_healer_initialize.lua', score = 999990 },
|
||||
{ ca_id = 'move', location = 'ca_healer_move.lua', score = 105000 },
|
||||
}
|
||||
|
||||
-- The healers_can_attack CA is only added to the table if aggression ~= 0
|
||||
-- But: make sure we always try removal
|
||||
if (cfg.action == 'delete') or (tonumber(cfg.aggression) ~= 0) then
|
||||
table.insert(CA_parms, { ca_id = 'may_attack', location = 'ca_healer_may_attack.lua', score = 99990 })
|
||||
end
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
42
data/ai/micro_ais/mai-defs/misc.lua
Normal file
42
data/ai/micro_ais/mai-defs/misc.lua
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
function wesnoth.micro_ais.lurkers(cfg)
|
||||
local required_keys = { "filter", "filter_location" }
|
||||
local optional_keys = { "stationary", "filter_location_wander" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_lurkers',
|
||||
{ ca_id = 'move', location = 'ca_lurkers.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
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_enemies", "filter", "ignore_units", "ignore_enemy_at_goal",
|
||||
"release_all_units_at_goal", "release_unit_at_goal", "unique_goals", "use_straight_line"
|
||||
}
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_goto',
|
||||
{ ca_id = 'move', location = 'ca_goto.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.hang_out(cfg)
|
||||
local optional_keys = { "filter", "filter_location", "avoid", "mobilize_condition", "mobilize_on_gold_less_than" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_hang_out',
|
||||
{ ca_id = 'move', location = 'ca_hang_out.lua', score = cfg.ca_score or 170000 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.simple_attack(cfg)
|
||||
local optional_keys = { "filter", "filter_second", "weapon" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_simple_attack',
|
||||
{ ca_id = 'move', location = 'ca_simple_attack.lua', score = cfg.ca_score or 110000 }
|
||||
}
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
14
data/ai/micro_ais/mai-defs/patrol.lua
Normal file
14
data/ai/micro_ais/mai-defs/patrol.lua
Normal file
|
@ -0,0 +1,14 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
||||
function wesnoth.micro_ais.patrol(cfg)
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Patrol [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
local required_keys = { "waypoint_x", "waypoint_y" }
|
||||
local optional_keys = { "id", "filter", "attack", "one_time_only", "out_and_back" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_patrol',
|
||||
{ ca_id = "move", location = 'ca_patrol.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
return required_keys, optional_keys, CA_parms
|
||||
end
|
89
data/ai/micro_ais/mai-defs/protect.lua
Normal file
89
data/ai/micro_ais/mai-defs/protect.lua
Normal file
|
@ -0,0 +1,89 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
|
||||
|
||||
function wesnoth.micro_ais.protect_unit(cfg)
|
||||
local required_keys = { "id", "goal_x", "goal_y" }
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_protect_unit',
|
||||
{ ca_id = 'finish', location = 'ca_protect_unit_finish.lua', score = 300000 },
|
||||
{ ca_id = 'attack', location = 'ca_protect_unit_attack.lua', score = 95000 },
|
||||
{ ca_id = 'move', location = 'ca_protect_unit_move.lua', score = 94999 }
|
||||
}
|
||||
|
||||
-- [unit] tags need to be dealt with separately
|
||||
cfg.id, cfg.goal_x, cfg.goal_y = {}, {}, {}
|
||||
if (cfg.action ~= 'delete') then
|
||||
for unit in H.child_range(cfg, "unit") do
|
||||
if (not unit.id) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required id= key")
|
||||
end
|
||||
if (not unit.goal_x) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required goal_x= key")
|
||||
end
|
||||
if (not unit.goal_y) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required goal_y= key")
|
||||
end
|
||||
table.insert(cfg.id, unit.id)
|
||||
table.insert(cfg.goal_x, unit.goal_x)
|
||||
table.insert(cfg.goal_y, unit.goal_y)
|
||||
end
|
||||
|
||||
if (not cfg.id[1]) then
|
||||
H.wml_error("Protect Unit Micro AI is missing required [unit] tag")
|
||||
end
|
||||
end
|
||||
|
||||
-- Optional key disable_move_leader_to_keep: needs to be dealt with
|
||||
-- separately as it affects a default CA
|
||||
if cfg.disable_move_leader_to_keep then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_leader_to_keep]"
|
||||
}
|
||||
end
|
||||
|
||||
-- attacks aspects also needs to be set separately
|
||||
local unit_ids_str = 'dummy'
|
||||
for _,id in ipairs(cfg.id) do
|
||||
unit_ids_str = unit_ids_str .. ',' .. id
|
||||
end
|
||||
local aspect_parms = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
ca_id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_own", {
|
||||
{ "not", {
|
||||
id = unit_ids_str
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, aspect_parms)
|
||||
-- We also need to add the move_leader_to_keep CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_leader_to_keep",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_leader_to_keep_phase",
|
||||
max_score=160000,
|
||||
score=160000
|
||||
} }
|
||||
}
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, aspect_parms)
|
||||
end
|
||||
return required_keys, {}, CA_parms
|
||||
end
|
65
data/ai/micro_ais/mai-defs/recruiting.lua
Normal file
65
data/ai/micro_ais/mai-defs/recruiting.lua
Normal file
|
@ -0,0 +1,65 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
|
||||
local function handle_default_recruitment(cfg)
|
||||
-- Also need to delete/add the default recruitment CA
|
||||
if cfg.action == 'add' then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[recruitment]"
|
||||
}
|
||||
elseif cfg.action == 'delete' then
|
||||
-- We need to add the recruitment CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="recruitment",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::aspect_recruitment_phase",
|
||||
max_score=180000,
|
||||
score=180000
|
||||
} }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.recruit_rushers(cfg)
|
||||
local optional_keys = { "randomness" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_rusher_recruit',
|
||||
{ ca_id = "move", location = 'ca_recruit_rushers.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
handle_default_recruitment(cfg)
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
||||
|
||||
function wesnoth.micro_ais.recruit_random(cfg)
|
||||
local optional_keys = { "skip_low_gold_recruiting", "type", "prob" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_random_recruit',
|
||||
{ ca_id = "move", location = 'ca_recruit_random.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
if (cfg.action ~= 'delete') then
|
||||
-- The 'probability' tags need to be handled separately here
|
||||
cfg.type, cfg.prob = {}, {}
|
||||
for probability in H.child_range(cfg, "probability") do
|
||||
if (not probability.type) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required type= key")
|
||||
end
|
||||
if (not probability.probability) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required probability= key")
|
||||
end
|
||||
table.insert(cfg.type, probability.type)
|
||||
table.insert(cfg.prob, probability.probability)
|
||||
end
|
||||
end
|
||||
|
||||
handle_default_recruitment(cfg)
|
||||
return {}, optional_keys, CA_parms
|
||||
end
|
|
@ -2,6 +2,20 @@ local H = wesnoth.require "lua/helper.lua"
|
|||
local W = H.set_wml_action_metatable {}
|
||||
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
|
||||
|
||||
wesnoth.micro_ais = {}
|
||||
|
||||
-- Load all default MicroAIs
|
||||
wesnoth.require("ai/micro_ais/mai-defs/animals.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/bottleneck.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/escort.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/fast.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/guardian.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/healers.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/misc.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/patrol.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/protect.lua")
|
||||
wesnoth.require("ai/micro_ais/mai-defs/recruiting.lua")
|
||||
|
||||
function wesnoth.wml_actions.micro_ai(cfg)
|
||||
local CA_path = 'ai/micro_ais/cas/'
|
||||
|
||||
|
@ -21,518 +35,18 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
end
|
||||
|
||||
-- Set up the configuration tables for the different Micro AIs
|
||||
local required_keys, optional_keys, CA_parms = {}, {}, {}
|
||||
|
||||
--------- Healer Support Micro AI ------------------------------------
|
||||
if (cfg.ai_type == 'healer_support') then
|
||||
optional_keys = { "aggression", "injured_units_only", "max_threats", "filter", "filter_second" }
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
CA_parms = {
|
||||
ai_id = 'mai_healer',
|
||||
{ ca_id = 'initialize', location = CA_path .. 'ca_healer_initialize.lua', score = 999990 },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_healer_move.lua', score = 105000 },
|
||||
}
|
||||
|
||||
-- The healers_can_attack CA is only added to the table if aggression ~= 0
|
||||
-- But: make sure we always try removal
|
||||
if (cfg.action == 'delete') or (tonumber(cfg.aggression) ~= 0) then
|
||||
table.insert(CA_parms, { ca_id = 'may_attack', location = CA_path .. 'ca_healer_may_attack.lua', score = 99990 })
|
||||
end
|
||||
|
||||
--------- Bottleneck Defense Micro AI -----------------------------------
|
||||
elseif (cfg.ai_type == 'bottleneck_defense') then
|
||||
required_keys = { "x", "y", "enemy_x", "enemy_y" }
|
||||
optional_keys = { "healer_x", "healer_y", "leadership_x", "leadership_y", "active_side_leader" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_bottleneck',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_bottleneck_move.lua', score = score },
|
||||
{ ca_id = 'attack', location = CA_path .. 'ca_bottleneck_attack.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
--------- Messenger Escort Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'messenger_escort') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Messenger [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "waypoint_x", "waypoint_y" }
|
||||
optional_keys = { "id", "enemy_death_chance", "filter", "filter_second", "invert_order", "messenger_death_chance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_messenger',
|
||||
{ ca_id = 'attack', location = CA_path .. 'ca_messenger_attack.lua', score = score },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_messenger_move.lua', score = score - 1 },
|
||||
{ ca_id = 'escort_move', location = CA_path .. 'ca_messenger_escort_move.lua', score = score - 2 }
|
||||
}
|
||||
|
||||
--------- Lurkers Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'lurkers') then
|
||||
required_keys = { "filter", "filter_location" }
|
||||
optional_keys = { "stationary", "filter_location_wander" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_lurkers',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_lurkers.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Protect Unit Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'protect_unit') then
|
||||
required_keys = { "id", "goal_x", "goal_y" }
|
||||
-- Scores for this AI need to be hard-coded, it does not work otherwise
|
||||
CA_parms = {
|
||||
ai_id = 'mai_protect_unit',
|
||||
{ ca_id = 'finish', location = CA_path .. 'ca_protect_unit_finish.lua', score = 300000 },
|
||||
{ ca_id = 'attack', location = CA_path .. 'ca_protect_unit_attack.lua', score = 95000 },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_protect_unit_move.lua', score = 94999 }
|
||||
}
|
||||
|
||||
-- [unit] tags need to be dealt with separately
|
||||
cfg.id, cfg.goal_x, cfg.goal_y = {}, {}, {}
|
||||
if (cfg.action ~= 'delete') then
|
||||
for unit in H.child_range(cfg, "unit") do
|
||||
if (not unit.id) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required id= key")
|
||||
end
|
||||
if (not unit.goal_x) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required goal_x= key")
|
||||
end
|
||||
if (not unit.goal_y) then
|
||||
H.wml_error("Protect Unit Micro AI [unit] tag is missing required goal_y= key")
|
||||
end
|
||||
table.insert(cfg.id, unit.id)
|
||||
table.insert(cfg.goal_x, unit.goal_x)
|
||||
table.insert(cfg.goal_y, unit.goal_y)
|
||||
end
|
||||
|
||||
if (not cfg.id[1]) then
|
||||
H.wml_error("Protect Unit Micro AI is missing required [unit] tag")
|
||||
end
|
||||
end
|
||||
|
||||
-- Optional key disable_move_leader_to_keep: needs to be dealt with
|
||||
-- separately as it affects a default CA
|
||||
if cfg.disable_move_leader_to_keep then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_leader_to_keep]"
|
||||
}
|
||||
end
|
||||
|
||||
-- attacks aspects also needs to be set separately
|
||||
local unit_ids_str = 'dummy'
|
||||
for _,id in ipairs(cfg.id) do
|
||||
unit_ids_str = unit_ids_str .. ',' .. id
|
||||
end
|
||||
local aspect_parms = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
ca_id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_own", {
|
||||
{ "not", {
|
||||
id = unit_ids_str
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, aspect_parms)
|
||||
-- We also need to add the move_leader_to_keep CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_leader_to_keep",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_leader_to_keep_phase",
|
||||
max_score=160000,
|
||||
score=160000
|
||||
} }
|
||||
}
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, aspect_parms)
|
||||
end
|
||||
|
||||
--------- Micro AI Guardian -----------------------------------
|
||||
elseif (cfg.ai_type == 'stationed_guardian') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Stationed Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "distance", "station_x", "station_y" }
|
||||
optional_keys = { "id", "filter", "guard_x", "guard_y" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_stationed_guardian',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_stationed_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'zone_guardian') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Zone Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "filter_location" }
|
||||
optional_keys = { "id", "filter", "filter_location_enemy", "station_x", "station_y" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_zone_guardian',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_zone_guardian.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'return_guardian') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Return Guardian [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "return_x", "return_y" }
|
||||
optional_keys = { "id", "filter" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_return_guardian',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_return_guardian.lua', score = cfg.ca_score or 100010 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'coward') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Coward [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "distance" }
|
||||
optional_keys = { "attack_if_trapped", "id", "filter", "filter_second", "seek_x", "seek_y","avoid_x","avoid_y" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_coward',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_coward.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Micro AI Animals ------------------------------------
|
||||
elseif (cfg.ai_type == 'big_animals') then
|
||||
required_keys = { "filter"}
|
||||
optional_keys = { "avoid_unit", "filter_location", "filter_location_wander" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_big_animals',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_big_animals.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'wolves') then
|
||||
required_keys = { "filter", "filter_second" }
|
||||
optional_keys = { "attack_only_prey", "avoid_type" }
|
||||
local score = cfg.ca_score or 90000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_wolves',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_wolves_move.lua', score = score },
|
||||
{ ca_id = "wander", location = CA_path .. 'ca_wolves_wander.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
if cfg.attack_only_prey then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "and", H.get_child(cfg, "filter_second") }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
elseif cfg.avoid_type then
|
||||
local wolves_aspects = {
|
||||
{
|
||||
aspect = "attacks",
|
||||
facet = {
|
||||
name = "ai_default_rca::aspect_attacks",
|
||||
id = "dont_attack",
|
||||
invalidate_on_gamestate_change = "yes",
|
||||
{ "filter_enemy", {
|
||||
{ "not", {
|
||||
type=cfg.avoid_type
|
||||
} }
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfg.action == "delete") then
|
||||
MAIH.delete_aspects(cfg.side, wolves_aspects)
|
||||
else
|
||||
MAIH.add_aspects(cfg.side, wolves_aspects)
|
||||
end
|
||||
end
|
||||
|
||||
elseif (cfg.ai_type == 'herding') then
|
||||
required_keys = { "filter_location", "filter", "filter_second", "herd_x", "herd_y" }
|
||||
optional_keys = { "attention_distance", "attack_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_herding',
|
||||
{ ca_id = "attack_close_enemy", location = CA_path .. 'ca_herding_attack_close_enemy.lua', score = score },
|
||||
{ ca_id = "sheep_runs_enemy", location = CA_path .. 'ca_herding_sheep_runs_enemy.lua', score = score - 1 },
|
||||
{ ca_id = "sheep_runs_dog", location = CA_path .. 'ca_herding_sheep_runs_dog.lua', score = score - 2 },
|
||||
{ ca_id = "herd_sheep", location = CA_path .. 'ca_herding_herd_sheep.lua', score = score - 3 },
|
||||
{ ca_id = "sheep_move", location = CA_path .. 'ca_herding_sheep_move.lua', score = score - 4 },
|
||||
{ ca_id = "dog_move", location = CA_path .. 'ca_herding_dog_move.lua', score = score - 5 },
|
||||
{ ca_id = "dog_stopmove", location = CA_path .. 'ca_herding_dog_stopmove.lua', score = score - 6 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'forest_animals') then
|
||||
optional_keys = { "rabbit_type", "rabbit_number", "rabbit_enemy_distance", "rabbit_hole_img",
|
||||
"tusker_type", "tusklet_type", "deer_type", "filter_location"
|
||||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_forest_animals',
|
||||
{ ca_id = "new_rabbit", location = CA_path .. 'ca_forest_animals_new_rabbit.lua', score = score },
|
||||
{ ca_id = "tusker_attack", location = CA_path .. 'ca_forest_animals_tusker_attack.lua', score = score - 1 },
|
||||
{ ca_id = "move", location = CA_path .. 'ca_forest_animals_move.lua', score = score - 2 },
|
||||
{ ca_id = "tusklet_move", location = CA_path .. 'ca_forest_animals_tusklet_move.lua', score = score - 3 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'swarm') then
|
||||
optional_keys = { "scatter_distance", "vision_distance", "enemy_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_swarm',
|
||||
{ ca_id = "scatter", location = CA_path .. 'ca_swarm_scatter.lua', score = score },
|
||||
{ ca_id = "move", location = CA_path .. 'ca_swarm_move.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'wolves_multipacks') then
|
||||
optional_keys = { "type", "pack_size", "show_pack_number" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
ai_id = 'mai_wolves_multipacks',
|
||||
{ ca_id = "attack", location = CA_path .. 'ca_wolves_multipacks_attack.lua', score = score },
|
||||
{ ca_id = "wander", location = CA_path .. 'ca_wolves_multipacks_wander.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'hunter') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Hunter [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "home_x", "home_y" }
|
||||
optional_keys = { "id", "filter", "filter_location", "rest_turns", "show_messages" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_hunter',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_hunter.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Patrol Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'patrol') then
|
||||
if (cfg.action ~= 'delete') and (not cfg.id) and (not H.get_child(cfg, "filter")) then
|
||||
H.wml_error("Patrol [micro_ai] tag requires either id= key or [filter] tag")
|
||||
end
|
||||
required_keys = { "waypoint_x", "waypoint_y" }
|
||||
optional_keys = { "id", "filter", "attack", "one_time_only", "out_and_back" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_patrol',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_patrol.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Recruiting Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'recruit_rushers') or (cfg.ai_type == 'recruit_random')then
|
||||
if (cfg.ai_type == 'recruit_rushers') then
|
||||
optional_keys = { "randomness" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_rusher_recruit',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_recruit_rushers.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
else
|
||||
optional_keys = { "skip_low_gold_recruiting", "type", "prob" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_random_recruit',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_recruit_random.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
if (cfg.action ~= 'delete') then
|
||||
-- The 'probability' tags need to be handled separately here
|
||||
cfg.type, cfg.prob = {}, {}
|
||||
for probability in H.child_range(cfg, "probability") do
|
||||
if (not probability.type) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required type= key")
|
||||
end
|
||||
if (not probability.probability) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required probability= key")
|
||||
end
|
||||
table.insert(cfg.type, probability.type)
|
||||
table.insert(cfg.prob, probability.probability)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Also need to delete/add the default recruitment CA
|
||||
if cfg.action == 'add' then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[recruitment]"
|
||||
}
|
||||
elseif cfg.action == 'delete' then
|
||||
-- We need to add the recruitment CA back in
|
||||
-- This works even if it was not removed, it simply overwrites the existing CA
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="recruitment",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::aspect_recruitment_phase",
|
||||
max_score=180000,
|
||||
score=180000
|
||||
} }
|
||||
}
|
||||
end
|
||||
|
||||
--------- Goto Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'goto') then
|
||||
required_keys = { "filter_location" }
|
||||
optional_keys = {
|
||||
"avoid_enemies", "filter", "ignore_units", "ignore_enemy_at_goal",
|
||||
"release_all_units_at_goal", "release_unit_at_goal", "unique_goals", "use_straight_line"
|
||||
}
|
||||
CA_parms = {
|
||||
ai_id = 'mai_goto',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_goto.lua', score = cfg.ca_score or 300000 }
|
||||
}
|
||||
|
||||
--------- Hang Out Micro AI ------------------------------------
|
||||
elseif (cfg.ai_type == 'hang_out') then
|
||||
optional_keys = { "filter", "filter_location", "avoid", "mobilize_condition", "mobilize_on_gold_less_than" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_hang_out',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_hang_out.lua', score = cfg.ca_score or 170000 }
|
||||
}
|
||||
|
||||
--------- Simple Attack Micro AI ---------------------------
|
||||
elseif (cfg.ai_type == 'simple_attack') then
|
||||
optional_keys = { "filter", "filter_second", "weapon" }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_simple_attack',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_simple_attack.lua', score = cfg.ca_score or 110000 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'fast_ai') then
|
||||
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"
|
||||
}
|
||||
CA_parms = {
|
||||
ai_id = 'mai_fast',
|
||||
{ ca_id = 'combat', location = CA_path .. 'ca_fast_combat.lua', score = 100000 },
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_fast_move.lua', score = 20000 },
|
||||
{ ca_id = 'combat_leader', location = CA_path .. 'ca_fast_combat_leader.lua', score = 19900 }
|
||||
}
|
||||
|
||||
-- Also need to delete/add some default CAs
|
||||
if (cfg.action == 'delete') then
|
||||
-- This can be done independently of whether these were removed earlier
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="combat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::combat_phase",
|
||||
max_score=100000,
|
||||
score=100000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="villages",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::get_villages_phase",
|
||||
max_score=60000,
|
||||
score=60000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="retreat",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::retreat_phase",
|
||||
max_score=40000,
|
||||
score=40000
|
||||
} }
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "add",
|
||||
path = "stage[main_loop].candidate_action",
|
||||
{ "candidate_action", {
|
||||
id="move_to_targets",
|
||||
engine="cpp",
|
||||
name="ai_default_rca::move_to_targets_phase",
|
||||
max_score=20000,
|
||||
score=20000
|
||||
} }
|
||||
}
|
||||
else
|
||||
if (not cfg.skip_combat_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[combat]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'combat') or (parm.ca_id == 'combat_leader') then
|
||||
table.remove(CA_parms, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not cfg.skip_move_ca) then
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[villages]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[retreat]"
|
||||
}
|
||||
|
||||
W.modify_ai {
|
||||
side = cfg.side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[move_to_targets]"
|
||||
}
|
||||
else
|
||||
for i,parm in ipairs(CA_parms) do
|
||||
if (parm.ca_id == 'move') then
|
||||
table.remove(CA_parms, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If we got here, none of the valid ai_types was specified
|
||||
else
|
||||
if wesnoth.micro_ais[cfg.ai_type] == nil then
|
||||
H.wml_error("unknown value for ai_type= in [micro_ai]")
|
||||
end
|
||||
|
||||
|
||||
local required_keys, optional_keys, CA_parms = wesnoth.micro_ais[cfg.ai_type](cfg)
|
||||
|
||||
-- Fixup any relative CA paths
|
||||
for i,v in ipairs(CA_parms) do
|
||||
if v.location and v.location:find('~') ~= 1 then
|
||||
v.location = CA_path .. v.location
|
||||
end
|
||||
end
|
||||
|
||||
MAIH.micro_ai_setup(cfg, CA_parms, required_keys, optional_keys)
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue