Merge branch 'master' of github.com:wesnoth/wesnoth
This commit is contained in:
commit
116c6a6e97
13 changed files with 362 additions and 180 deletions
|
@ -1,6 +1,7 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local ca_big_animals = {}
|
||||
|
||||
|
@ -33,15 +34,18 @@ function ca_big_animals:execution(ai, cfg)
|
|||
--AH.put_labels(avoid)
|
||||
|
||||
for i,unit in ipairs(units) do
|
||||
local goal = MAIUV.get_mai_unit_variables(unit, cfg.ai_id)
|
||||
|
||||
-- Unit gets a new goal if none exist or on any move with 10% random chance
|
||||
local r = math.random(10)
|
||||
if (not unit.variables.goal_x) or (r == 1) then
|
||||
if (not goal.goal_x) or (r == 1) then
|
||||
local locs = AH.get_passable_locations(cfg.filter_location or {})
|
||||
local rand = math.random(#locs)
|
||||
--print(type, ': #locs', #locs, rand)
|
||||
unit.variables.goal_x, unit.variables.goal_y = locs[rand][1], locs[rand][2]
|
||||
goal.goal_x, goal.goal_y = locs[rand][1], locs[rand][2]
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, goal)
|
||||
end
|
||||
--print('Big animal goto: ', type, unit.variables.goal_x, unit.variables.goal_y, r)
|
||||
--print('Big animal goto: ', unit.id, goal.goal_x, goal.goal_y, r)
|
||||
|
||||
-- hexes the unit can reach
|
||||
local reach_map = AH.get_reachable_unocc(unit)
|
||||
|
@ -57,7 +61,7 @@ function ca_big_animals:execution(ai, cfg)
|
|||
local max_rating, best_hex = -9e99, {}
|
||||
reach_map:iter( function(x, y, v)
|
||||
-- Distance from goal is first rating
|
||||
local rating = - H.distance_between(x, y, unit.variables.goal_x, unit.variables.goal_y)
|
||||
local rating = - H.distance_between(x, y, goal.goal_x, goal.goal_y)
|
||||
|
||||
-- Proximity to an enemy unit is a plus
|
||||
local enemy_hp = 500
|
||||
|
@ -87,14 +91,12 @@ function ca_big_animals:execution(ai, cfg)
|
|||
else -- If animal did not move, we need to stop it (also delete the goal)
|
||||
AH.checked_stopunit_moves(ai, unit)
|
||||
if (not unit) or (not unit.valid) then return end
|
||||
unit.variables.goal_x = nil
|
||||
unit.variables.goal_y = nil
|
||||
MAIUV.delete_mai_unit_variables(unit, cfg.ai_id)
|
||||
end
|
||||
|
||||
-- Or if this gets the unit to the goal, we also delete the goal
|
||||
if (unit.x == unit.variables.goal_x) and (unit.y == unit.variables.goal_y) then
|
||||
unit.variables.goal_x = nil
|
||||
unit.variables.goal_y = nil
|
||||
if (unit.x == goal.goal_x) and (unit.y == goal.goal_y) then
|
||||
MAIUV.delete_mai_unit_variables(unit, cfg.ai_id)
|
||||
end
|
||||
|
||||
-- Finally, if the unit ended up next to enemies, attack the weakest of those
|
||||
|
|
|
@ -22,7 +22,7 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
-- which case we do not do anything
|
||||
if cfg.release_all_units_at_goal then
|
||||
for rel in H.child_range(self.data, "goto_release_all") do
|
||||
if (rel.id == cfg.ca_id) then
|
||||
if (rel.id == cfg.ai_id) then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
@ -65,7 +65,7 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
if cfg.release_unit_at_goal then
|
||||
for i_unit=#units,1,-1 do
|
||||
for rel in H.child_range(self.data, "goto_release_unit") do
|
||||
if (rel.id == cfg.ca_id .. '_' .. units[i_unit].id) then
|
||||
if (rel.id == cfg.ai_id .. '_' .. units[i_unit].id) then
|
||||
table.remove(units, i_unit)
|
||||
break
|
||||
end
|
||||
|
@ -227,12 +227,12 @@ function ca_goto:execution(ai, cfg, self)
|
|||
-- 2. Keys cannot contain certain characters -> everything potentially user-defined needs to be in values
|
||||
if unit_at_goal then
|
||||
if cfg.release_unit_at_goal then
|
||||
table.insert(self.data, { "goto_release_unit" , { id = cfg.ca_id .. '_' .. best_unit.id } } )
|
||||
table.insert(self.data, { "goto_release_unit" , { id = cfg.ai_id .. '_' .. best_unit.id } } )
|
||||
end
|
||||
|
||||
if cfg.release_all_units_at_goal then
|
||||
--print("Releasing all units")
|
||||
table.insert(self.data, { "goto_release_all", { id = cfg.ca_id } } )
|
||||
table.insert(self.data, { "goto_release_all", { id = cfg.ai_id } } )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local ca_hang_out = {}
|
||||
|
||||
|
@ -8,8 +9,8 @@ function ca_hang_out:evaluation(ai, cfg, self)
|
|||
cfg = cfg or {}
|
||||
|
||||
-- Return 0 if the mobilize condition has previously been met
|
||||
for mobilze in H.child_range(self.data, "hangout_mobilize_units") do
|
||||
if (mobilze.id == cfg.ca_id) then
|
||||
for mobilize in H.child_range(self.data, "hangout_mobilize_units") do
|
||||
if (mobilize.id == cfg.ai_id) then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
@ -18,12 +19,12 @@ function ca_hang_out:evaluation(ai, cfg, self)
|
|||
if (cfg.mobilize_condition and wesnoth.eval_conditional(cfg.mobilize_condition))
|
||||
or (cfg.mobilize_on_gold_less_than and (wesnoth.sides[wesnoth.current.side].gold < cfg.mobilize_on_gold_less_than))
|
||||
then
|
||||
table.insert(self.data, { "hangout_mobilize_units" , { id = cfg.ca_id } } )
|
||||
table.insert(self.data, { "hangout_mobilize_units" , { id = cfg.ai_id } } )
|
||||
|
||||
-- Need to unmark all units also
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side, { "and", cfg.filter } }
|
||||
for i,u in ipairs(units) do
|
||||
u.variables.mai_hangout_moved = nil
|
||||
MAIUV.delete_mai_unit_variables(u, cfg.ai_id)
|
||||
end
|
||||
|
||||
return 0
|
||||
|
@ -85,7 +86,7 @@ function ca_hang_out:execution(ai, cfg, self)
|
|||
local best_hex, best_unit, max_rating = {}, {}, -9e99
|
||||
for i,u in ipairs(units) do
|
||||
-- Only consider units that have not been marked yet
|
||||
if (not u.variables.mai_hangout_moved) then
|
||||
if (not MAIUV.get_mai_unit_variables(u, cfg.ai_id, "moved")) then
|
||||
local best_hex_unit, max_rating_unit = {}, -9e99
|
||||
|
||||
-- Check out all unoccupied hexes the unit can reach
|
||||
|
@ -128,12 +129,14 @@ function ca_hang_out:execution(ai, cfg, self)
|
|||
for i,u in ipairs(units) do
|
||||
AH.checked_stopunit_moves(ai, u)
|
||||
-- Also remove the markers
|
||||
if u and u.valid then u.variables.mai_hangout_moved = nil end
|
||||
if u and u.valid then MAIUV.delete_mai_unit_variables(u, cfg.ai_id) end
|
||||
end
|
||||
else
|
||||
-- Otherwise move unit and mark as having been used
|
||||
AH.checked_move(ai, best_unit, best_hex[1], best_hex[2])
|
||||
if best_unit and best_unit.valid then best_unit.variables.mai_hangout_moved = true end
|
||||
if best_unit and best_unit.valid then
|
||||
MAIUV.set_mai_unit_variables(best_unit, cfg.ai_id, { moved = true })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local ca_hunter = {}
|
||||
|
||||
|
@ -64,17 +65,19 @@ function ca_hunter:execution(ai, cfg)
|
|||
)[1]
|
||||
|
||||
-- If hunting_status is not set for the unit -> default behavior -> hunting
|
||||
if (not unit.variables.hunting_status) then
|
||||
local hunter_vars = MAIUV.get_mai_unit_variables(unit, cfg.ai_id)
|
||||
if (not hunter_vars.hunting_status) then
|
||||
-- Unit gets a new goal if none exist or on any move with 10% random chance
|
||||
local r = math.random(10)
|
||||
if (not unit.variables.goal_x) or (r <= 1) then
|
||||
if (not hunter_vars.goal_x) or (r <= 1) then
|
||||
-- 'locs' includes border hexes, but that does not matter here
|
||||
locs = AH.get_passable_locations((cfg.filter_location or {}), unit)
|
||||
local rand = math.random(#locs)
|
||||
--print('#locs', #locs, rand)
|
||||
unit.variables.goal_x, unit.variables.goal_y = locs[rand][1], locs[rand][2]
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = locs[rand][1], locs[rand][2]
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, hunter_vars)
|
||||
end
|
||||
--print('Hunter goto: ', unit.variables.goal_x, unit.variables.goal_y, r)
|
||||
--print('Hunter goto: ', hunter_vars.goal_x, hunter_vars.goal_y, r)
|
||||
|
||||
-- Hexes the unit can reach
|
||||
local reach_map = AH.get_reachable_unocc(unit)
|
||||
|
@ -83,7 +86,7 @@ function ca_hunter:execution(ai, cfg)
|
|||
local max_rating, best_hex = -9e99, {}
|
||||
reach_map:iter( function(x, y, v)
|
||||
-- Distance from goal is first rating
|
||||
local rating = - H.distance_between(x, y, unit.variables.goal_x, unit.variables.goal_y)
|
||||
local rating = - H.distance_between(x, y, hunter_vars.goal_x, hunter_vars.goal_y)
|
||||
|
||||
-- Proximity to an enemy unit is a plus
|
||||
local enemy_hp = 500
|
||||
|
@ -109,12 +112,14 @@ function ca_hunter:execution(ai, cfg)
|
|||
else -- If hunter did not move, we need to stop it (also delete the goal)
|
||||
AH.checked_stopunit_moves(ai, unit)
|
||||
if (not unit) or (not unit.valid) then return end
|
||||
unit.variables.goal_x, unit.variables.goal_y = nil, nil
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = nil, nil
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, hunter_vars)
|
||||
end
|
||||
|
||||
-- Or if this gets the unit to the goal, we also delete the goal
|
||||
if (unit.x == unit.variables.goal_x) and (unit.y == unit.variables.goal_y) then
|
||||
unit.variables.goal_x, unit.variables.goal_y = nil, nil
|
||||
if (unit.x == hunter_vars.goal_x) and (unit.y == hunter_vars.goal_y) then
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = nil, nil
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, hunter_vars)
|
||||
end
|
||||
|
||||
-- Finally, if the unit ended up next to enemies, attack the weakest of those
|
||||
|
@ -122,8 +127,9 @@ function ca_hunter:execution(ai, cfg)
|
|||
|
||||
-- If the enemy was killed, hunter returns home
|
||||
if unit.valid and (attack_status == 'killed') then
|
||||
unit.variables.goal_x, unit.variables.goal_y = nil, nil
|
||||
unit.variables.hunting_status = 'return'
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = nil, nil
|
||||
hunter_vars.hunting_status = 'return'
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, hunter_vars)
|
||||
if cfg.show_messages then
|
||||
W.message { speaker = unit.id, message = 'Now that I have eaten, I will go back home.' }
|
||||
end
|
||||
|
@ -134,7 +140,7 @@ function ca_hunter:execution(ai, cfg)
|
|||
end
|
||||
|
||||
-- If we got here, this means the unit is either returning, or resting
|
||||
if (unit.variables.hunting_status == 'return') then
|
||||
if (hunter_vars.hunting_status == 'return') then
|
||||
goto_x, goto_y = wesnoth.find_vacant_tile(cfg.home_x, cfg.home_y)
|
||||
--print('Go home:', home_x, home_y, goto_x, goto_y)
|
||||
|
||||
|
@ -163,10 +169,11 @@ function ca_hunter:execution(ai, cfg)
|
|||
|
||||
-- If the unit got home, start the resting counter
|
||||
if (unit.x == cfg.home_x) and (unit.y == cfg.home_y) then
|
||||
unit.variables.hunting_status = 'resting'
|
||||
unit.variables.resting_until = wesnoth.current.turn + (cfg.rest_turns or 3)
|
||||
hunter_vars.hunting_status = 'resting'
|
||||
hunter_vars.resting_until = wesnoth.current.turn + (cfg.rest_turns or 3)
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, hunter_vars)
|
||||
if cfg.show_messages then
|
||||
W.message { speaker = unit.id, message = 'I made it home - resting now until the end of Turn ' .. unit.variables.resting_until .. ' or until fully healed.' }
|
||||
W.message { speaker = unit.id, message = 'I made it home - resting now until the end of Turn ' .. hunter_vars.resting_until .. ' or until fully healed.' }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -175,7 +182,7 @@ function ca_hunter:execution(ai, cfg)
|
|||
end
|
||||
|
||||
-- If we got here, the only remaining action is resting
|
||||
if (unit.variables.hunting_status == 'resting') then
|
||||
if (hunter_vars.hunting_status == 'resting') then
|
||||
-- So all we need to do is take moves away from the unit
|
||||
AH.checked_stopunit_moves(ai, unit)
|
||||
if (not unit) or (not unit.valid) then return end
|
||||
|
@ -185,9 +192,10 @@ function ca_hunter:execution(ai, cfg)
|
|||
if (not unit) or (not unit.valid) then return end
|
||||
|
||||
-- If this is the last turn of resting, we also remove the status and turn variable
|
||||
if (unit.hitpoints >= unit.max_hitpoints) and (unit.variables.resting_until <= wesnoth.current.turn) then
|
||||
unit.variables.hunting_status = nil
|
||||
unit.variables.resting_until = nil
|
||||
if (unit.hitpoints >= unit.max_hitpoints) and (hunter_vars.resting_until <= wesnoth.current.turn) then
|
||||
hunter_vars.hunting_status = nil
|
||||
hunter_vars.resting_until = nil
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, hunter_vars)
|
||||
if cfg.show_messages then
|
||||
W.message { speaker = unit.id, message = 'I am done resting. It is time to go hunting again next turn.' }
|
||||
end
|
||||
|
@ -196,7 +204,8 @@ function ca_hunter:execution(ai, cfg)
|
|||
end
|
||||
|
||||
-- In principle we should never get here, but just in case: reset variable, so that unit goes hunting on next turn
|
||||
unit.variables.hunting_status = nil
|
||||
hunter_vars.hunting_status = nil
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, hunter_vars)
|
||||
end
|
||||
|
||||
return ca_hunter
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local ca_messenger_escort_move = {}
|
||||
|
||||
|
@ -58,7 +59,8 @@ function ca_messenger_escort_move:execution(ai, cfg)
|
|||
local max_messenger_rating = -9e99
|
||||
for _,m in ipairs(messengers) do
|
||||
local messenger_rating = 1. / (H.distance_between(x, y, m.x, m.y) + 2.)
|
||||
messenger_rating = messenger_rating * 10. * (1. + m.variables.wp_rating * 2.)
|
||||
local wp_rating = MAIUV.get_mai_unit_variables(m, cfg.ai_id, "wp_rating")
|
||||
messenger_rating = messenger_rating * 10. * (1. + wp_rating * 2.)
|
||||
|
||||
if (messenger_rating > max_messenger_rating) then
|
||||
max_messenger_rating = messenger_rating
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
return function(cfg)
|
||||
-- Calculate next waypoint and rating for all messengers
|
||||
|
@ -26,7 +27,7 @@ return function(cfg)
|
|||
for i, m in ipairs(messengers) do
|
||||
-- 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 = m.variables.wp_i or 1
|
||||
local wp_i = MAIUV.get_mai_unit_variables(m, cfg.ai_id, "wp_i") or 1
|
||||
local wp_x, wp_y = waypoint_x[wp_i], waypoint_y[wp_i]
|
||||
|
||||
-- If this messenger is within 3 hexes of the next waypoint, we go on to the one after that
|
||||
|
@ -34,8 +35,6 @@ return function(cfg)
|
|||
local dist_wp = H.distance_between(m.x, m.y, wp_x, wp_y)
|
||||
if (dist_wp <= 3) and (wp_i < #waypoint_x) then wp_i = wp_i + 1 end
|
||||
|
||||
m.variables.wp_i, m.variables.wp_x, m.variables.wp_y = wp_i, wp_x, wp_y
|
||||
|
||||
-- Also store the rating for each messenger
|
||||
-- For now, this is simply a "forward rating"
|
||||
local rating = wp_i - dist_wp / 1000.
|
||||
|
@ -47,7 +46,7 @@ return function(cfg)
|
|||
rating = #waypoint_x - rating
|
||||
end
|
||||
|
||||
m.variables.wp_rating = rating
|
||||
MAIUV.set_mai_unit_variables(m, cfg.ai_id, { wp_i = wp_i, wp_x = wp_x, wp_y = wp_y, wp_rating = rating })
|
||||
|
||||
-- Find the messenger with the highest rating that has MP left
|
||||
if (m.moves > 0) and (rating > max_rating) then
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local ca_patrol = {}
|
||||
|
||||
|
@ -28,10 +29,12 @@ function ca_patrol:execution(ai, cfg)
|
|||
|
||||
local n_wp = #cfg.waypoint_x -- just for convenience
|
||||
|
||||
local patrol_vars = MAIUV.get_mai_unit_variables(patrol, cfg.ai_id)
|
||||
|
||||
-- Set up waypoints, taking into account whether 'reverse' is set
|
||||
-- This works even the first time, when patrol.variables.patrol_reverse is not set yet
|
||||
-- This works even the first time, when patrol_vars.patrol_reverse is not set yet
|
||||
local waypoints = {}
|
||||
if patrol.variables.patrol_reverse then
|
||||
if patrol_vars.patrol_reverse then
|
||||
for i = 1,n_wp do
|
||||
waypoints[i] = { tonumber(cfg.waypoint_x[n_wp-i+1]), tonumber(cfg.waypoint_y[n_wp-i+1]) }
|
||||
end
|
||||
|
@ -43,10 +46,11 @@ function ca_patrol:execution(ai, cfg)
|
|||
|
||||
-- If not set, set next location (first move)
|
||||
-- This needs to be in WML format, so that it persists over save/load cycles
|
||||
if (not patrol.variables.patrol_x) then
|
||||
patrol.variables.patrol_x = waypoints[1][1]
|
||||
patrol.variables.patrol_y = waypoints[1][2]
|
||||
patrol.variables.patrol_reverse = false
|
||||
if (not patrol_vars.patrol_x) then
|
||||
patrol_vars.patrol_x = waypoints[1][1]
|
||||
patrol_vars.patrol_y = waypoints[1][2]
|
||||
patrol_vars.patrol_reverse = false
|
||||
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
|
||||
end
|
||||
|
||||
while patrol.moves > 0 do
|
||||
|
@ -61,8 +65,8 @@ function ca_patrol:execution(ai, cfg)
|
|||
|
||||
-- Also check whether we're next to any unit (enemy or ally) which is on the next waypoint
|
||||
local unit_on_wp = wesnoth.get_units {
|
||||
x = patrol.variables.patrol_x,
|
||||
y = patrol.variables.patrol_y,
|
||||
x = patrol_vars.patrol_x,
|
||||
y = patrol_vars.patrol_y,
|
||||
{ "filter_adjacent", { id = patrol.id } }
|
||||
}[1]
|
||||
|
||||
|
@ -75,28 +79,32 @@ function ca_patrol:execution(ai, cfg)
|
|||
-- Move him to the first one (or reverse route), if he's on the last waypoint
|
||||
-- Unless cfg.one_time_only is set
|
||||
if cfg.one_time_only then
|
||||
patrol.variables.patrol_x = waypoints[n_wp][1]
|
||||
patrol.variables.patrol_y = waypoints[n_wp][2]
|
||||
patrol_vars.patrol_x = waypoints[n_wp][1]
|
||||
patrol_vars.patrol_y = waypoints[n_wp][2]
|
||||
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
|
||||
else
|
||||
-- Go back to first WP or reverse direction
|
||||
if cfg.out_and_back then
|
||||
patrol.variables.patrol_x = waypoints[n_wp-1][1]
|
||||
patrol.variables.patrol_y = waypoints[n_wp-1][2]
|
||||
|
||||
patrol_vars.patrol_x = waypoints[n_wp-1][1]
|
||||
patrol_vars.patrol_y = waypoints[n_wp-1][2]
|
||||
-- We also need to reverse the waypoints right here, as this might not be the end of the move
|
||||
patrol.variables.patrol_reverse = not patrol.variables.patrol_reverse
|
||||
patrol_vars.patrol_reverse = not patrol_vars.patrol_reverse
|
||||
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
|
||||
|
||||
local tmp_wp = {}
|
||||
for i,wp in ipairs(waypoints) do tmp_wp[n_wp-i+1] = wp end
|
||||
waypoints = tmp_wp
|
||||
else
|
||||
patrol.variables.patrol_x = waypoints[1][1]
|
||||
patrol.variables.patrol_y = waypoints[1][2]
|
||||
patrol_vars.patrol_x = waypoints[1][1]
|
||||
patrol_vars.patrol_y = waypoints[1][2]
|
||||
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- ... else move him on the next waypoint
|
||||
patrol.variables.patrol_x = waypoints[i+1][1]
|
||||
patrol.variables.patrol_y = waypoints[i+1][2]
|
||||
patrol_vars.patrol_x = waypoints[i+1][1]
|
||||
patrol_vars.patrol_y = waypoints[i+1][2]
|
||||
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -107,7 +115,7 @@ function ca_patrol:execution(ai, cfg)
|
|||
then
|
||||
AH.checked_stopunit_moves(ai, patrol)
|
||||
else -- otherwise move toward next WP
|
||||
local x, y = wesnoth.find_vacant_tile(patrol.variables.patrol_x, patrol.variables.patrol_y, patrol)
|
||||
local x, y = wesnoth.find_vacant_tile(patrol_vars.patrol_x, patrol_vars.patrol_y, patrol)
|
||||
local nh = AH.next_hop(patrol, x, y)
|
||||
if nh and ((nh[1] ~= patrol.x) or (nh[2] ~= patrol.y)) then
|
||||
AH.checked_move(ai, patrol, nh[1], nh[2])
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
local WMPF = wesnoth.require "ai/micro_ais/cas/ca_wolves_multipacks_functions.lua"
|
||||
|
||||
|
@ -8,7 +9,6 @@ local ca_wolves_multipacks_attack = {}
|
|||
|
||||
function ca_wolves_multipacks_attack:evaluation(ai, cfg)
|
||||
local unit_type = cfg.type or "Wolf"
|
||||
|
||||
-- If wolves have attacks left, call this CA
|
||||
-- It will generally be disabled by being black-listed, so as to avoid
|
||||
-- having to do the full attack evaluation for every single move
|
||||
|
@ -26,7 +26,6 @@ function ca_wolves_multipacks_attack:execution(ai, cfg)
|
|||
-- and I want all wolves in a pack to move first, before going on to the next pack
|
||||
-- which makes this slightly more complicated than it would be otherwise
|
||||
for pack_number,pack in pairs(packs) do
|
||||
|
||||
local keep_attacking_this_pack = true -- whether there might be attacks left
|
||||
local pack_attacked = false -- whether an attack by the pack has happened
|
||||
|
||||
|
@ -97,10 +96,13 @@ function ca_wolves_multipacks_attack:execution(ai, cfg)
|
|||
-- the same target for all wolves of the pack)
|
||||
for x, y in H.adjacent_tiles(target.x, target.y) do
|
||||
local adj_unit = wesnoth.get_unit(x, y)
|
||||
if adj_unit and (adj_unit.variables.pack == pack_number)
|
||||
and (adj_unit.side == wesnoth.current.side) and (adj_unit.attacks_left == 0)
|
||||
then
|
||||
rating = rating + 10 -- very strongly favors this target
|
||||
if adj_unit then
|
||||
local pack = MAIUV.get_mai_unit_variables(adj_unit, cfg.ai_id, "pack")
|
||||
if (pack == pack_number) and (adj_unit.side == wesnoth.current.side)
|
||||
and (adj_unit.attacks_left == 0)
|
||||
then
|
||||
rating = rating + 10 -- very strongly favors this target
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local wolves_multipacks_functions = {}
|
||||
|
||||
|
@ -25,9 +26,10 @@ function wolves_multipacks_functions.assign_packs(cfg)
|
|||
local packs = {}
|
||||
-- Find wolves that already have a pack number assigned
|
||||
for i,w in ipairs(wolves) do
|
||||
if w.variables.pack then
|
||||
if (not packs[w.variables.pack]) then packs[w.variables.pack] = {} end
|
||||
table.insert(packs[w.variables.pack], { x = w.x, y = w.y, id = w.id })
|
||||
local pack = MAIUV.get_mai_unit_variables(w, cfg.ai_id, "pack")
|
||||
if pack then
|
||||
if (not packs[pack]) then packs[pack] = {} end
|
||||
table.insert(packs[pack], { x = w.x, y = w.y, id = w.id })
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -37,7 +39,7 @@ function wolves_multipacks_functions.assign_packs(cfg)
|
|||
--print(' have pack:', k, ' #members:', #p)
|
||||
if (#p == 1) then
|
||||
local wolf = wesnoth.get_unit(p[1].x, p[1].y)
|
||||
wolf.variables.pack, wolf.variables.goal_x, wolf.variables.goal_y = nil, nil, nil
|
||||
MAIUV.delete_mai_unit_variables(wolf, cfg.ai_id)
|
||||
packs[k] = nil
|
||||
end
|
||||
end
|
||||
|
@ -47,10 +49,11 @@ function wolves_multipacks_functions.assign_packs(cfg)
|
|||
-- Wolves that are not in a pack (new ones or those removed above)
|
||||
local nopack_wolves = {}
|
||||
for i,w in ipairs(wolves) do
|
||||
if (not w.variables.pack) then
|
||||
local pack = MAIUV.get_mai_unit_variables(w, cfg.ai_id, "pack")
|
||||
if (not pack) then
|
||||
table.insert(nopack_wolves, w)
|
||||
-- Also erase any goal one of these might have
|
||||
w.variables.pack, w.variables.goal_x, w.variables.goal_y = nil, nil, nil
|
||||
MAIUV.delete_mai_unit_variables(w, cfg.ai_id)
|
||||
end
|
||||
end
|
||||
--print('#nopack_wolves:', #nopack_wolves)
|
||||
|
@ -70,7 +73,7 @@ function wolves_multipacks_functions.assign_packs(cfg)
|
|||
end
|
||||
if (min_dist < 9e99) then
|
||||
table.insert(packs[k], { x = best_wolf.x, y = best_wolf.y, id = best_wolf.id })
|
||||
best_wolf.variables.pack = k
|
||||
MAIUV.set_mai_unit_variables(best_wolf, cfg.ai_id, { pack = k })
|
||||
table.remove(nopack_wolves, best_ind)
|
||||
end
|
||||
end
|
||||
|
@ -93,7 +96,7 @@ function wolves_multipacks_functions.assign_packs(cfg)
|
|||
packs[new_pack] = {}
|
||||
for i,w in ipairs(nopack_wolves) do
|
||||
table.insert(packs[new_pack], { x = w.x, y = w.y, id = w.id })
|
||||
w.variables.pack = new_pack
|
||||
MAIUV.set_mai_unit_variables(w, cfg.ai_id, { pack = new_pack })
|
||||
end
|
||||
break
|
||||
end
|
||||
|
@ -121,7 +124,7 @@ function wolves_multipacks_functions.assign_packs(cfg)
|
|||
-- Need to count down for table.remove to work correctly
|
||||
for i = pack_size,1,-1 do
|
||||
table.insert(packs[new_pack], { x = best_wolves[i].x, y = best_wolves[i].y, id = best_wolves[i].id })
|
||||
best_wolves[i].variables.pack = new_pack
|
||||
MAIUV.set_mai_unit_variables(best_wolves[i], cfg.ai_id, { pack = new_pack })
|
||||
end
|
||||
end
|
||||
--print('After grouping remaining single wolves')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
local WMPF = wesnoth.require "ai/micro_ais/cas/ca_wolves_multipacks_functions.lua"
|
||||
|
||||
|
@ -27,9 +28,11 @@ function ca_wolves_multipacks_wander:execution(ai, cfg)
|
|||
local wolf = wesnoth.get_unit(loc.x, loc.y)
|
||||
--print(k, i, wolf.id)
|
||||
table.insert(wolves, wolf)
|
||||
|
||||
-- If any of the wolves in the pack has a goal set, we use that one
|
||||
if wolf.variables.goal_x then
|
||||
goal = { wolf.variables.goal_x, wolf.variables.goal_y }
|
||||
local wolf_goal = MAIUV.get_mai_unit_variables(wolf, cfg.ai_id)
|
||||
if wolf_goal.goal_x then
|
||||
goal = { wolf_goal.goal_x, wolf_goal.goal_y }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -61,7 +64,7 @@ function ca_wolves_multipacks_wander:execution(ai, cfg)
|
|||
|
||||
-- This goal is saved with every wolf of the pack
|
||||
for i,w in ipairs(wolves) do
|
||||
w.variables.goal_x, w.variables.goal_y = goal[1], goal[2]
|
||||
MAIUV.insert_mai_unit_variables(w, cfg.ai_id, { goal_x = goal[1], goal_y = goal[2] })
|
||||
end
|
||||
|
||||
-- The pack wanders with only 2 considerations
|
||||
|
|
|
@ -1,44 +1,53 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local W = H.set_wml_action_metatable {}
|
||||
local AH = wesnoth.require("ai/lua/ai_helper.lua")
|
||||
local MAIUV = wesnoth.dofile "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local micro_ai_helper = {}
|
||||
|
||||
function micro_ai_helper.add_CAs(side, CA_parms, CA_cfg)
|
||||
-- Add the candidate actions defined in 'CA_parms' to the AI of 'side'
|
||||
-- CA_parms is an array of tables, one for each CA to be added (CA setup parameters)
|
||||
-- CA_cfg is a table with the parameters passed to the eval/exec functions
|
||||
-- Add the candidate actions defined in @CA_parms to the AI of @side
|
||||
-- @CA_parms is an array of tables, one for each CA to be added (CA setup parameters)
|
||||
-- and also contains one key: ai_id
|
||||
-- @CA_cfg is a table with the parameters passed to the eval/exec functions
|
||||
--
|
||||
-- Required keys for CA_parms:
|
||||
-- - ca_id: is used for CA id/name and the eval/exec function names
|
||||
-- Required keys for each table of @CA_parms:
|
||||
-- - ca_id: is used for CA id/name
|
||||
-- - location: the path+file name for the external CA file
|
||||
-- - score: the evaluation score
|
||||
|
||||
for i,parms in ipairs(CA_parms) do
|
||||
-- Make sure the id/name of each CA are unique.
|
||||
-- We do this by seeing if a CA by that name exists already.
|
||||
-- If not, we use the passed id in parms.ca_id
|
||||
-- If yes, we add a number to the end of parms.ca_id until we find an id that does not exist yet
|
||||
local ca_id, id_found = parms.ca_id, true
|
||||
-- We need to make sure that the id/name of each CA are unique.
|
||||
-- We do this by checking if CAs starting with ai_id exist already
|
||||
-- If yes, we add numbers to the end of ai_id until we find an id that does not exist yet
|
||||
|
||||
local n = 1
|
||||
while id_found do -- This is really just a precaution
|
||||
id_found = false
|
||||
local ai_id, id_found = CA_parms.ai_id, true
|
||||
|
||||
for ai_tag in H.child_range(wesnoth.sides[side].__cfg, 'ai') do
|
||||
for stage in H.child_range(ai_tag, 'stage') do
|
||||
for ca in H.child_range(stage, 'candidate_action') do
|
||||
if (ca.name == ca_id) then id_found = true end
|
||||
--print('---> found CA:', ca.name, id_found)
|
||||
local n = 1
|
||||
while id_found do -- This is really just a precaution
|
||||
id_found = false
|
||||
|
||||
for ai_tag in H.child_range(wesnoth.sides[side].__cfg, 'ai') do
|
||||
for stage in H.child_range(ai_tag, 'stage') do
|
||||
for ca in H.child_range(stage, 'candidate_action') do
|
||||
if string.find(ca.name, ai_id .. '_') then
|
||||
id_found = true
|
||||
--print('---> found CA:', ca.name, ai_id, id_found, string.find(ca.name, ai_id))
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (id_found) then ca_id = parms.ca_id .. n end
|
||||
n = n+1
|
||||
end
|
||||
|
||||
-- Always pass the ca_id and ca_score to the eval/exec functions
|
||||
CA_cfg.ca_id = ca_id
|
||||
if (id_found) then ai_id = CA_parms.ai_id .. n end
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
-- Now add the CAs
|
||||
for i,parms in ipairs(CA_parms) do
|
||||
local ca_id = ai_id .. '_' .. parms.ca_id
|
||||
|
||||
-- Always pass the ai_id and ca_score to the eval/exec functions
|
||||
CA_cfg.ai_id = ai_id
|
||||
CA_cfg.ca_score = parms.score
|
||||
|
||||
local CA = {
|
||||
|
@ -63,27 +72,33 @@ function micro_ai_helper.add_CAs(side, CA_parms, CA_cfg)
|
|||
end
|
||||
|
||||
function micro_ai_helper.delete_CAs(side, CA_parms)
|
||||
-- Delete the candidate actions defined in 'CA_parms' from the AI of 'side'
|
||||
-- CA_parms is an array of tables, one for each CA to be removed
|
||||
-- Delete the candidate actions defined in @CA_parms from the AI of @side
|
||||
-- @CA_parms is an array of tables, one for each CA to be removed
|
||||
-- We can simply pass the one used for add_CAs(), although only the
|
||||
-- CA_parms.ca_id field is needed
|
||||
|
||||
for i,parms in ipairs(CA_parms) do
|
||||
local ca_id = parms.ca_id
|
||||
local ca_id = CA_parms.ai_id .. '_' .. parms.ca_id
|
||||
|
||||
W.modify_ai {
|
||||
side = side,
|
||||
action = "try_delete",
|
||||
path = "stage[main_loop].candidate_action[" .. ca_id .. "]"
|
||||
}
|
||||
|
||||
-- Also need to delete variable stored in all units of the side, so that later MAIs can use these units
|
||||
local units = wesnoth.get_units { side = side }
|
||||
for i,u in ipairs(units) do
|
||||
MAIUV.delete_mai_unit_variables(u, CA_parms.ai_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function micro_ai_helper.add_aspects(side, aspect_parms)
|
||||
-- Add the aspects defined in 'aspect_parms' to the AI of 'side'
|
||||
-- aspect_parms is an array of tables, one for each aspect to be added
|
||||
-- Add the aspects defined in @aspect_parms to the AI of @side
|
||||
-- @aspect_parms is an array of tables, one for each aspect to be added
|
||||
--
|
||||
-- Required keys for aspect_parms:
|
||||
-- Required keys for @aspect_parms:
|
||||
-- - aspect: the aspect name (e.g. 'attacks' or 'aggression')
|
||||
-- - facet: A table describing the facet to be added
|
||||
--
|
||||
|
@ -111,8 +126,8 @@ function micro_ai_helper.add_aspects(side, aspect_parms)
|
|||
end
|
||||
|
||||
function micro_ai_helper.delete_aspects(side, aspect_parms)
|
||||
-- Delete the aspects defined in 'aspect_parms' from the AI of 'side'
|
||||
-- aspect_parms is an array of tables, one for each CA to be removed
|
||||
-- Delete the aspects defined in @aspect_parms from the AI of @side
|
||||
-- @aspect_parms is an array of tables, one for each aspect to be removed
|
||||
-- We can simply pass the one used for add_aspects(), although only the
|
||||
-- aspect_parms.aspect_id field is needed
|
||||
|
||||
|
@ -126,15 +141,12 @@ function micro_ai_helper.delete_aspects(side, aspect_parms)
|
|||
end
|
||||
|
||||
function micro_ai_helper.micro_ai_setup(cfg, CA_parms, required_keys, optional_keys)
|
||||
-- If cfg.ca_id is set, it gets added to the ca_id= key of all CAs
|
||||
-- If cfg.ca_id is set, it gets used as the ai_id= key
|
||||
-- This allows for selective removal of CAs
|
||||
if cfg.ca_id then
|
||||
for i,parms in ipairs(CA_parms) do
|
||||
-- Need to save eval_id first though
|
||||
parms.eval_id = parms.ca_id
|
||||
parms.ca_id = parms.ca_id .. '_' .. cfg.ca_id
|
||||
end
|
||||
end
|
||||
-- Note: the ca_id key of the [micro_ai] tag should really be renamed to ai_id,
|
||||
-- but that would mean breaking backward compatibility, so we'll just deal with it internally instead
|
||||
|
||||
CA_parms.ai_id = cfg.ca_id or CA_parms.ai_id
|
||||
|
||||
-- If action=delete, we do that and are done
|
||||
if (cfg.action == 'delete') then
|
||||
|
|
89
data/ai/micro_ais/micro_ai_unit_variables.lua
Normal file
89
data/ai/micro_ais/micro_ai_unit_variables.lua
Normal file
|
@ -0,0 +1,89 @@
|
|||
-- This is a set of functions provides a consistent way of storing Micro AI
|
||||
-- variables in units. They need to be stored inside a [micro_ai] tag in a
|
||||
-- unit's [variables] tag together with an ai_id= key, so that they can be
|
||||
-- removed when the Micro AI gets deleted. Otherwise subsequent Micro AIs used
|
||||
-- in the same scenario (or using the same units in later scenarios) might work
|
||||
-- incorrectly or not at all.
|
||||
-- Note that with this method, there can only ever be one of these tags for each
|
||||
-- ai_ca in each unit (but of course several when there are several Micro AIs
|
||||
-- with different ai_CA values affecting the same unit)
|
||||
-- For the time being, we only allow key=value style variables.
|
||||
|
||||
local H = wesnoth.require "lua/helper.lua"
|
||||
|
||||
local micro_ai_unit_variables = {}
|
||||
|
||||
function micro_ai_unit_variables.modify_mai_unit_variables(unit, ai_id, action, vars_table)
|
||||
-- Modify [unit][variables][micro_ai] tags
|
||||
-- @ai_id (string): the id of the Micro AI
|
||||
-- @action (string): "delete", "set" or "insert"
|
||||
-- @vars_table: table of key=value pairs with the variables to be set or inserted (not needed for @action="delete")
|
||||
|
||||
local unit_cfg = unit.__cfg
|
||||
local variables, ind = H.get_child(unit_cfg, "variables")
|
||||
|
||||
-- Always delete the respective [variables][micro_ai] tag, if it exists
|
||||
local existing_table
|
||||
for i,mai in ipairs(variables, "micro_ai") do
|
||||
if (mai[1] == "micro_ai") and (mai[2].ai_id == ai_id) then
|
||||
existing_table = mai[2]
|
||||
table.remove(variables, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Then replace it, if the "set" action is selected
|
||||
-- or add the new keys to it, overwriting old ones with the same name, if action == "insert"
|
||||
if (action == "set") or (action == "insert") then
|
||||
local tag = { "micro_ai" }
|
||||
|
||||
if (not existing_table) or (action == "set") then
|
||||
tag[2] = vars_table
|
||||
tag[2].ai_id = ai_id
|
||||
else
|
||||
for k,v in pairs(vars_table) do existing_table[k] = v end
|
||||
tag[2] = existing_table
|
||||
end
|
||||
|
||||
table.insert(unit_cfg[ind][2], tag)
|
||||
end
|
||||
|
||||
-- All of this so far was only on the table dump -> apply to unit
|
||||
wesnoth.put_unit(unit_cfg)
|
||||
end
|
||||
|
||||
function micro_ai_unit_variables.delete_mai_unit_variables(unit, ai_id)
|
||||
micro_ai_unit_variables.modify_mai_unit_variables(unit, ai_id, "delete")
|
||||
end
|
||||
|
||||
function micro_ai_unit_variables.insert_mai_unit_variables(unit, ai_id, vars_table)
|
||||
micro_ai_unit_variables.modify_mai_unit_variables(unit, ai_id, "insert", vars_table)
|
||||
end
|
||||
|
||||
function micro_ai_unit_variables.set_mai_unit_variables(unit, ai_id, vars_table)
|
||||
micro_ai_unit_variables.modify_mai_unit_variables(unit, ai_id, "set", vars_table)
|
||||
end
|
||||
|
||||
function micro_ai_unit_variables.get_mai_unit_variables(unit, ai_id, key)
|
||||
-- Get the content of [unit][variables][micro_ai] tag for the given @ai_id
|
||||
-- Return value:
|
||||
-- - If tag is found: value of key if @key parameter is given, otherwise
|
||||
-- table of key=value pairs (including the ai_id key)
|
||||
-- - If no such tag is found: nil (if @key is set), otherwise empty table
|
||||
|
||||
for mai in H.child_range(unit.variables.__cfg, "micro_ai") do
|
||||
if (mai.ai_id == ai_id) then
|
||||
if key then
|
||||
return mai[key]
|
||||
else
|
||||
return mai
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If we got here, no corresponding tag was found
|
||||
-- Return empty table; or nil if @key was set
|
||||
if (not key) then return {} end
|
||||
end
|
||||
|
||||
return micro_ai_unit_variables
|
|
@ -3,7 +3,8 @@ local W = H.set_wml_action_metatable {}
|
|||
local MAIH = wesnoth.require("ai/micro_ais/micro_ai_helper.lua")
|
||||
|
||||
function wesnoth.wml_actions.micro_ai(cfg)
|
||||
-- Set up the [micro_ai] tag functionality for each Micro AI
|
||||
local CA_path = 'ai/micro_ais/cas/'
|
||||
|
||||
cfg = cfg.__parsed
|
||||
|
||||
-- Add translation for old-syntax animal MAIs to new syntax plus deprecation message
|
||||
|
@ -57,21 +58,21 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
|
||||
-- Set up the configuration tables for the different Micro AIs
|
||||
local required_keys, optional_keys, CA_parms = {}, {}, {}
|
||||
local CA_path = 'ai/micro_ais/cas/'
|
||||
|
||||
--------- 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 = {
|
||||
{ ca_id = 'mai_healer_initialize', location = CA_path .. 'ca_healer_initialize.lua', score = 999990 },
|
||||
{ ca_id = 'mai_healer_move', location = CA_path .. 'ca_healer_move.lua', score = 105000 },
|
||||
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 = 'mai_healer_may_attack', location = CA_path .. 'ca_healer_may_attack.lua', score = 99990 })
|
||||
table.insert(CA_parms, { ca_id = 'may_attack', location = CA_path .. 'ca_healer_may_attack.lua', score = 99990 })
|
||||
end
|
||||
|
||||
--------- Bottleneck Defense Micro AI -----------------------------------
|
||||
|
@ -80,38 +81,44 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
optional_keys = { "healer_x", "healer_y", "leadership_x", "leadership_y", "active_side_leader" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
{ ca_id = 'mai_bottleneck_move', location = CA_path .. 'ca_bottleneck_move.lua', score = score },
|
||||
{ ca_id = 'mai_bottleneck_attack', location = CA_path .. 'ca_bottleneck_attack.lua', score = score - 1 }
|
||||
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 (not cfg.id) and (not H.get_child(cfg, "filter")) 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 = {
|
||||
{ ca_id = 'mai_messenger_attack', location = CA_path .. 'ca_messenger_attack.lua', score = score },
|
||||
{ ca_id = 'mai_messenger_move', location = CA_path .. 'ca_messenger_move.lua', score = score - 1 },
|
||||
{ ca_id = 'mai_messenger_escort_move', location = CA_path .. 'ca_messenger_escort_move.lua', score = score - 2 }
|
||||
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 = { { ca_id = 'mai_lurkers', location = CA_path .. 'ca_lurkers.lua', score = cfg.ca_score or 300000 } }
|
||||
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 = {
|
||||
{ ca_id = 'mai_protect_unit_finish', location = CA_path .. 'ca_protect_unit_finish.lua', score = 300000 },
|
||||
{ ca_id = 'mai_protect_unit_attack', location = CA_path .. 'ca_protect_unit_attack.lua', score = 95000 },
|
||||
{ ca_id = 'mai_protect_unit_move', location = CA_path .. 'ca_protect_unit_move.lua', score = 94999 }
|
||||
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
|
||||
|
@ -190,50 +197,66 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
|
||||
--------- Micro AI Guardian -----------------------------------
|
||||
elseif (cfg.ai_type == 'stationed_guardian') then
|
||||
if (not cfg.id) and (not H.get_child(cfg, "filter")) 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", "guard_x", "guard_y" }
|
||||
optional_keys = { "id", "filter" }
|
||||
CA_parms = { { ca_id = 'mai_stationed_guardian', location = CA_path .. 'ca_stationed_guardian.lua', score = cfg.ca_score or 300000 } }
|
||||
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 (not cfg.id) and (not H.get_child(cfg, "filter")) 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 = { { ca_id = 'mai_zone_guardian', location = CA_path .. 'ca_zone_guardian.lua', score = cfg.ca_score or 300000 } }
|
||||
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 (not cfg.id) and (not H.get_child(cfg, "filter")) 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 = { { ca_id = 'mai_return_guardian', location = CA_path .. 'ca_return_guardian.lua', score = cfg.ca_score or 100010 } }
|
||||
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 (not cfg.id) and (not H.get_child(cfg, "filter")) 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 = { "id", "filter", "filter_second", "seek_x", "seek_y","avoid_x","avoid_y" }
|
||||
CA_parms = { { ca_id = 'mai_coward', location = CA_path .. 'ca_coward.lua', score = cfg.ca_score or 300000 } }
|
||||
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 = { { ca_id = "mai_big_animals", location = CA_path .. 'ca_big_animals.lua', score = cfg.ca_score or 300000 } }
|
||||
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 = {
|
||||
{ ca_id = "mai_wolves_move", location = CA_path .. 'ca_wolves_move.lua', score = score },
|
||||
{ ca_id = "mai_wolves_wander", location = CA_path .. 'ca_wolves_wander.lua', score = score - 1 }
|
||||
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
|
||||
|
@ -283,12 +306,13 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
optional_keys = { "attention_distance", "attack_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
{ ca_id = "mai_herding_attack_close_enemy", location = CA_path .. 'ca_herding_attack_close_enemy.lua', score = score },
|
||||
{ ca_id = "mai_herding_sheep_runs_enemy", location = CA_path .. 'ca_herding_sheep_runs_enemy.lua', score = score - 1 },
|
||||
{ ca_id = "mai_herding_sheep_runs_dog", location = CA_path .. 'ca_herding_sheep_runs_dog.lua', score = score - 2 },
|
||||
{ ca_id = "mai_herding_herd_sheep", location = CA_path .. 'ca_herding_herd_sheep.lua', score = score - 3 },
|
||||
{ ca_id = "mai_herding_sheep_move", location = CA_path .. 'ca_herding_sheep_move.lua', score = score - 4 },
|
||||
{ ca_id = "mai_herding_dog_move", location = CA_path .. 'ca_herding_dog_move.lua', score = score - 5 }
|
||||
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 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'forest_animals') then
|
||||
|
@ -297,66 +321,83 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
}
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
{ ca_id = "mai_forest_animals_new_rabbit", location = CA_path .. 'ca_forest_animals_new_rabbit.lua', score = score },
|
||||
{ ca_id = "mai_forest_animals_tusker_attack", location = CA_path .. 'ca_forest_animals_tusker_attack.lua', score = score - 1 },
|
||||
{ ca_id = "mai_forest_animals_move", location = CA_path .. 'ca_forest_animals_move.lua', score = score - 2 },
|
||||
{ ca_id = "mai_forest_animals_tusklet_move", location = CA_path .. 'ca_forest_animals_tusklet_move.lua', score = score - 3 }
|
||||
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 = {
|
||||
{ ca_id = "mai_swarm_scatter", location = CA_path .. 'ca_swarm_scatter.lua', score = score },
|
||||
{ ca_id = "mai_swarm_move", location = CA_path .. 'ca_swarm_move.lua', score = score - 1 }
|
||||
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 = {
|
||||
{ ca_id = "mai_wolves_multipacks_attack", location = CA_path .. 'ca_wolves_multipacks_attack.lua', score = score },
|
||||
{ ca_id = "mai_wolves_multipacks_wander", location = CA_path .. 'ca_wolves_multipacks_wander.lua', score = score - 1 }
|
||||
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 (not cfg.id) and (not H.get_child(cfg, "filter")) 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 = { { ca_id = "mai_hunter", location = CA_path .. 'ca_hunter.lua', score = cfg.ca_score or 300000 } }
|
||||
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 (not cfg.id) and (not H.get_child(cfg, "filter")) 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 = { { ca_id = "mai_patrol", location = CA_path .. 'ca_patrol.lua', score = cfg.ca_score or 300000 } }
|
||||
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 = { { ca_id = "mai_rusher_recruit", location = CA_path .. 'ca_recruit_rushers.lua', score = cfg.ca_score or 180000 } }
|
||||
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 = { { ca_id = "mai_random_recruit", location = CA_path .. 'ca_recruit_random.lua', score = cfg.ca_score or 180000 } }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_random_recruit',
|
||||
{ ca_id = "move", location = CA_path .. 'ca_recruit_random.lua', score = cfg.ca_score or 180000 }
|
||||
}
|
||||
|
||||
-- The 'probability' tags need to be handled separately here
|
||||
cfg.type, cfg.prob = {}, {}
|
||||
for p in H.child_range(cfg, "probability") do
|
||||
if (not p.type) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required type= key")
|
||||
if (cfg.action ~= 'delete') then
|
||||
-- The 'probability' tags need to be handled separately here
|
||||
cfg.type, cfg.prob = {}, {}
|
||||
for p in H.child_range(cfg, "probability") do
|
||||
if (not p.type) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required type= key")
|
||||
end
|
||||
if (not p.probability) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required probability= key")
|
||||
end
|
||||
table.insert(cfg.type, p.type)
|
||||
table.insert(cfg.prob, p.probability)
|
||||
end
|
||||
if (not p.probability) then
|
||||
H.wml_error("Random Recruiting Micro AI [probability] tag is missing required probability= key")
|
||||
end
|
||||
table.insert(cfg.type, p.type)
|
||||
table.insert(cfg.prob, p.probability)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -391,17 +432,26 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
"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 = { { ca_id = 'mai_goto', location = CA_path .. 'ca_goto.lua', score = cfg.ca_score or 300000 } }
|
||||
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 = { { ca_id = 'mai_hang_out', location = CA_path .. 'ca_hang_out.lua', score = cfg.ca_score or 170000 } }
|
||||
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 = { { ca_id = 'mai_simple_attack', location = CA_path .. 'ca_simple_attack.lua', score = cfg.ca_score or 110000 } }
|
||||
CA_parms = {
|
||||
ai_id = 'mai_simple_attack',
|
||||
{ ca_id = 'move', location = CA_path .. 'ca_simple_attack.lua', score = cfg.ca_score or 110000 }
|
||||
}
|
||||
|
||||
-- If we got here, none of the valid ai_types was specified
|
||||
else
|
||||
|
|
Loading…
Add table
Reference in a new issue