Merge pull request #5630 from mattsc/remove_fai_uses
Remove Formula AI uses
This commit is contained in:
commit
f1c37f64ac
9 changed files with 191 additions and 276 deletions
|
@ -1,33 +0,0 @@
|
|||
#textdomain wesnoth-lib
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai.cfg}
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=formula_ai # id is needed to uniquely identify a MP AI, it is not needed in the scenario AI
|
||||
description=_"Multiplayer_AI^Dev AI: Default + Experimental Recruitment (Formula AI)" # wmllint: no spellcheck
|
||||
mp_rank=100000
|
||||
# this description is, again, needed for MP AI (it shows in AI list under this description
|
||||
|
||||
[stage]
|
||||
engine=fai
|
||||
name=side_formulas
|
||||
move="{ai/formula/new_recruitment.fai}"
|
||||
[/stage]
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
#{AI_CA_RECRUITMENT}
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_HIGH_XP_ATTACK}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
[/stage]
|
||||
[/ai]
|
|
@ -1,37 +0,0 @@
|
|||
#textdomain wesnoth-lib
|
||||
|
||||
#ifndef AI_CA_GOTO
|
||||
{core/macros/ai_candidate_actions.cfg}
|
||||
#endif
|
||||
|
||||
[ai]
|
||||
id=formula_ai_poisoning
|
||||
description=_"Multiplayer_AI^Dev AI: Default + Poisoning (Formula AI)" # wmllint: no spellcheck
|
||||
mp_rank=100000
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
{AI_CA_GOTO}
|
||||
{AI_CA_RECRUITMENT}
|
||||
{AI_CA_MOVE_LEADER_TO_GOALS}
|
||||
{AI_CA_MOVE_LEADER_TO_KEEP}
|
||||
{AI_CA_HIGH_XP_ATTACK}
|
||||
{AI_CA_COMBAT}
|
||||
{AI_CA_HEALING}
|
||||
{AI_CA_VILLAGES}
|
||||
{AI_CA_RETREAT}
|
||||
{AI_CA_MOVE_TO_TARGETS}
|
||||
{AI_CA_LEADER_SHARES_KEEP}
|
||||
[candidate_action] #it consists of several candidate actions
|
||||
engine=fai # fai engine is temporary disabled, it will be enabled real soon. Note that the rest of parameters are parsed by formula ai engine
|
||||
name=poisoner #this paramerer (and the rest of them), are specific to fai engine
|
||||
type=attack
|
||||
[filter]
|
||||
me="filter( input, 'me', filter(me.attacks,'att',filter(att.special,'spe',contains_string(spe,'poison'))))"
|
||||
target="filter( input, 'target', target.undead = 0 and target.hitpoints > 5 and index_of('poisoned',keys(target.states)) = -1)"
|
||||
[/filter]
|
||||
evaluation="{ai/formula/poisoner_eval.fai}"
|
||||
action="{ai/formula/poisoner_attack.fai}"
|
||||
[/candidate_action]
|
||||
[/stage]
|
||||
[/ai]
|
|
@ -1,4 +1,5 @@
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local BC = wesnoth.dofile "ai/lua/battle_calcs.lua"
|
||||
local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
|
||||
|
||||
local function get_patrol(cfg)
|
||||
|
@ -10,6 +11,78 @@ local function get_patrol(cfg)
|
|||
return patrol
|
||||
end
|
||||
|
||||
local function get_best_attack(unit, loc, last_waypoint, cfg)
|
||||
local attack_range = cfg.attack_range or 1
|
||||
|
||||
-- The attack calculation can be somewhat expensive, check first if there are enemies within the specified range
|
||||
local enemies = AH.get_attackable_enemies(
|
||||
{ id = cfg.attack, { "filter_location", { x = loc[1], y = loc[2], radius = attack_range } } },
|
||||
wesnoth.current.side,
|
||||
{ ignore_visibility = cfg.attack_invisible_enemies }
|
||||
)
|
||||
|
||||
-- An enemy on the last waypoint gets attacked preferentially and independent of
|
||||
-- whether its id is given in cfg.attack; but it still needs to be within attack range
|
||||
local enemy_last_waypoint
|
||||
if last_waypoint then
|
||||
enemy_last_waypoint = AH.get_attackable_enemies(
|
||||
{ x = last_waypoint[1], y = last_waypoint[2] },
|
||||
wesnoth.current.side,
|
||||
{ ignore_visibility = cfg.attack_invisible_enemies }
|
||||
)[1]
|
||||
if enemy_last_waypoint and (wesnoth.map.distance_between(enemy_last_waypoint, loc) <= attack_range) then
|
||||
local already_included = false
|
||||
for _,enemy in ipairs(enemies) do
|
||||
if (enemy.id == enemy_last_waypoint.id) then
|
||||
already_included = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if (not already_included) then
|
||||
table.insert(enemies, enemy_last_waypoint)
|
||||
end
|
||||
end
|
||||
end
|
||||
if (#enemies == 0) then return end
|
||||
|
||||
local old_moves, old_loc
|
||||
if ((loc[1] ~= unit.x) or (loc[2] ~= unit.y)) then
|
||||
old_moves, old_loc = unit.moves, unit.loc
|
||||
local _,sub_cost = AH.find_path_with_shroud(unit, loc)
|
||||
unit.moves = unit.moves - sub_cost
|
||||
unit.loc = loc
|
||||
end
|
||||
local attacks = AH.get_attacks({ unit }, { ignore_visibility = cfg.attack_invisible_enemies })
|
||||
if old_moves then
|
||||
unit.moves, unit.loc = old_moves, old_loc
|
||||
end
|
||||
|
||||
local max_rating, best_enemy, best_dst = -math.huge
|
||||
for _,attack in ipairs(attacks) do
|
||||
for _,enemy in ipairs(enemies) do
|
||||
if (attack.target.x == enemy.x) and (attack.target.y == enemy.y) then
|
||||
local dst = { attack.dst.x, attack.dst.y }
|
||||
local rating = BC.attack_rating(unit, enemy, dst)
|
||||
|
||||
-- Prioritize any enemy on the last waypoint
|
||||
if enemy_last_waypoint and (enemy_last_waypoint.id == enemy.id) then
|
||||
rating = rating + 1000
|
||||
end
|
||||
|
||||
if (rating > max_rating) then
|
||||
max_rating = rating
|
||||
best_enemy = enemy
|
||||
best_dst = dst
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return best_enemy, best_dst
|
||||
end
|
||||
|
||||
local ca_patrol = {}
|
||||
|
||||
function ca_patrol:evaluation(cfg)
|
||||
|
@ -42,15 +115,16 @@ function ca_patrol:execution(cfg)
|
|||
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
|
||||
end
|
||||
|
||||
while patrol.moves > 0 do
|
||||
-- Check whether one of the enemies to be attacked is next to the patroller
|
||||
-- If so, don't move, but attack that enemy
|
||||
local adjacent_enemy = AH.get_attackable_enemies {
|
||||
id = cfg.attack,
|
||||
{ "filter_adjacent", { id = patrol.id } }
|
||||
}[1]
|
||||
if adjacent_enemy then break end
|
||||
-- Check for a possible attack from the patrol's current position first, that
|
||||
-- way we can skip the other evaluation if one is found
|
||||
local last_waypoint
|
||||
if cfg.one_time_only then last_waypoint = waypoints[n_wp] end
|
||||
local enemy, dst
|
||||
if (patrol.attacks_left > 0) and (#patrol.attacks > 0) then
|
||||
enemy, dst = get_best_attack(patrol, patrol.loc, last_waypoint, cfg)
|
||||
end
|
||||
|
||||
while (not enemy) and (patrol.moves > 0) do
|
||||
-- Also check whether we're next to any unit (enemy or ally) which is on the next waypoint
|
||||
local unit_on_wp = AH.get_visible_units(wesnoth.current.side, {
|
||||
x = patrol_vars.patrol_x,
|
||||
|
@ -97,7 +171,7 @@ function ca_patrol:execution(cfg)
|
|||
end
|
||||
end
|
||||
|
||||
-- If we're on the last waypoint on one_time_only is set, stop here
|
||||
-- If we're on the last waypoint and one_time_only is set, stop here
|
||||
if cfg.one_time_only and
|
||||
(patrol.x == waypoints[n_wp][1]) and (patrol.y == waypoints[n_wp][2])
|
||||
then
|
||||
|
@ -106,6 +180,16 @@ function ca_patrol:execution(cfg)
|
|||
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
|
||||
-- Check whether an attackable enemy comes into attack range at any hex along the way
|
||||
local path = AH.find_path_with_shroud(patrol, nh[1], nh[2])
|
||||
for i = 2,#path do -- The patrol's current position is already checked above
|
||||
local loc = path[i]
|
||||
enemy, dst = get_best_attack(patrol, loc, last_waypoint, cfg)
|
||||
if enemy then
|
||||
nh = loc
|
||||
break
|
||||
end
|
||||
end
|
||||
AH.checked_move(ai, patrol, nh[1], nh[2])
|
||||
else
|
||||
AH.checked_stopunit_moves(ai, patrol)
|
||||
|
@ -114,28 +198,26 @@ function ca_patrol:execution(cfg)
|
|||
if (not patrol) or (not patrol.valid) then return end
|
||||
end
|
||||
|
||||
-- Attack unit on the last waypoint under all circumstances if cfg.one_time_only is set
|
||||
local adjacent_enemy
|
||||
if cfg.one_time_only then
|
||||
adjacent_enemy = AH.get_attackable_enemies {
|
||||
x = waypoints[n_wp][1],
|
||||
y = waypoints[n_wp][2],
|
||||
{ "filter_adjacent", { id = patrol.id } }
|
||||
}[1]
|
||||
-- It is possible that the patrol unexpectedly ends up next to an enemy, e.g. because of an ambush
|
||||
if not (enemy) then
|
||||
enemy, dst = get_best_attack(patrol, patrol.loc, last_waypoint, cfg)
|
||||
end
|
||||
|
||||
-- Otherwise attack adjacent enemy (if specified)
|
||||
if (not adjacent_enemy) then
|
||||
adjacent_enemy = AH.get_attackable_enemies {
|
||||
id = cfg.attack,
|
||||
{ "filter_adjacent", { id = patrol.id } }
|
||||
}[1]
|
||||
-- It is also possible that the patrol cannot make it to 'dst' because of an ambush,
|
||||
-- in which case we can check whether the ambusher can/should be attacked.
|
||||
-- So we need to execute the move and the attack separately.
|
||||
if enemy then
|
||||
AH.robust_move_and_attack(ai, patrol, dst)
|
||||
if (not patrol) or (not patrol.valid) then return end
|
||||
|
||||
if (patrol.x ~= dst[1]) or (patrol.y ~= dst[2]) then
|
||||
enemy, dst = get_best_attack(patrol, patrol.loc, last_waypoint, cfg)
|
||||
end
|
||||
end
|
||||
|
||||
if adjacent_enemy then AH.checked_attack(ai, patrol, adjacent_enemy) end
|
||||
if (not patrol) or (not patrol.valid) then return end
|
||||
|
||||
AH.checked_stopunit_all(ai, patrol)
|
||||
if enemy then
|
||||
AH.robust_move_and_attack(ai, patrol, dst, enemy)
|
||||
end
|
||||
end
|
||||
|
||||
return ca_patrol
|
||||
|
|
|
@ -8,7 +8,7 @@ function wesnoth.micro_ais.patrol(cfg)
|
|||
AH.get_multi_named_locs_xy('waypoint', cfg, 'Patrol [micro_ai] tag')
|
||||
end
|
||||
local required_keys = {}
|
||||
local optional_keys = { "id", "[filter]", "attack", "one_time_only", "out_and_back", "waypoint_loc", "waypoint_x", "waypoint_y" }
|
||||
local optional_keys = { "id", "[filter]", "attack", "attack_range", "attack_invisible_enemies", "one_time_only", "out_and_back", "waypoint_loc", "waypoint_x", "waypoint_y" }
|
||||
local CA_parms = {
|
||||
ai_id = 'mai_patrol',
|
||||
{ ca_id = "move", location = 'ca_patrol.lua', score = cfg.ca_score or 300000 }
|
||||
|
|
|
@ -1,17 +1,5 @@
|
|||
#textdomain wesnoth-ai
|
||||
|
||||
#define HOME_GUARDIAN X Y
|
||||
# Meant to be used as a suffix to a unit-generating macro call.
|
||||
# The previously-generated unit will treat (X,Y) as its home.
|
||||
[+unit]
|
||||
[ai]
|
||||
[vars]
|
||||
home_loc="loc({X},{Y})"
|
||||
[/vars]
|
||||
[/ai]
|
||||
[/unit]
|
||||
#enddef
|
||||
|
||||
[test]
|
||||
id=guardians
|
||||
name= _ "Guardians"
|
||||
|
@ -54,26 +42,6 @@
|
|||
recruit=Orcish Archer,Orcish Grunt
|
||||
persistent=no
|
||||
gold=30
|
||||
|
||||
[ai]
|
||||
[modify_ai]
|
||||
side=1
|
||||
action=add
|
||||
# wmllint: unbalanced-on
|
||||
path=stage[main_loop].candidate_action[]
|
||||
# wmllint: unbalanced-off
|
||||
[candidate_action]
|
||||
engine=fai
|
||||
name=go_home
|
||||
id=go_home
|
||||
type=movement
|
||||
# wmlindent: start ignoring
|
||||
evaluation="if( (null != me.vars.home_loc), {AI_CA_MOVE_TO_TARGETS_SCORE}+10, 0)"
|
||||
action="if( (me.loc != me.vars.home_loc), move(me.loc, next_hop(me.loc, me.vars.home_loc)), move(me.loc, me.loc)#do not move this turn#)"
|
||||
# wmlindent: stop ignoring
|
||||
[/candidate_action]
|
||||
[/modify_ai]
|
||||
[/ai]
|
||||
[/side]
|
||||
|
||||
# Put all the units and markers out there
|
||||
|
@ -264,31 +232,6 @@
|
|||
return_x,return_y=21,9
|
||||
[/micro_ai]
|
||||
|
||||
# The home guards
|
||||
[unit]
|
||||
type=Troll Whelp
|
||||
side=2
|
||||
id=home1
|
||||
name= _ "Home Guard 1"
|
||||
x,y=19,2
|
||||
[variables]
|
||||
label="home 19,2"
|
||||
[/variables]
|
||||
[/unit]
|
||||
{HOME_GUARDIAN 19 2}
|
||||
|
||||
[unit]
|
||||
type=Troll
|
||||
side=2
|
||||
id=home 2
|
||||
name= _ "Home Guard 2"
|
||||
x,y=21,10
|
||||
[variables]
|
||||
label="home 21,10"
|
||||
[/variables]
|
||||
[/unit]
|
||||
{HOME_GUARDIAN 21 10}
|
||||
|
||||
# The stationed guardians
|
||||
[unit]
|
||||
type=Skeleton Archer
|
||||
|
@ -480,23 +423,6 @@ separate attack Zone"
|
|||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
[set_menu_item]
|
||||
id=m03_home
|
||||
description= _ "Home Guard"
|
||||
image=units/trolls/grunt.png~CROP(31,7,24,24)
|
||||
[show_if]
|
||||
{VARIABLE_CONDITIONAL scenario_name equals guardians}
|
||||
[/show_if]
|
||||
[command]
|
||||
[message]
|
||||
speaker=narrator
|
||||
image=portraits/trolls/troll.png
|
||||
caption= _ "Home Guard"
|
||||
message= _ "A 'home guard' is a variant on the 'guardian' AI special. With this variant, the unit has an assigned 'home' location, and will return there if not involved in combat and if not going to a village, whether for healing or to capture it this turn. (By contrast, the standard guardian AI will cause the unit to stay where it last attacked.) This differs from 'return guardian' in that a home guard will press the attack, possibly getting drawn quite far from 'home', rather than returning after each attack. (It can also be lured away by a string of closely-placed villages, but that is something a map builder can control.)
|
||||
This also demonstrates how to combine candidate actions from Formula AI and Lua AI in one side. The home guard is written in Formula AI, while the return and stationed guardians and the cowards are written in Lua AI. In addition the non-guardian units of the side follow the default AI behavior."
|
||||
[/message]
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
[set_menu_item]
|
||||
id=m04_stationed
|
||||
description= _ "Stationed Guardian"
|
||||
|
|
|
@ -271,49 +271,13 @@
|
|||
income=-2
|
||||
[/side]
|
||||
|
||||
# Formula AI Lurkers
|
||||
[side]
|
||||
side=6
|
||||
controller=ai
|
||||
type=Saurian Oracle
|
||||
x,y=12,17
|
||||
max_moves,max_attacks=0,0
|
||||
persistent=no
|
||||
|
||||
team_name=lurkers_fai
|
||||
user_team_name= _ "Formula AI Lurkers (saurians)"
|
||||
|
||||
gold=0
|
||||
income=-2
|
||||
|
||||
[ai]
|
||||
[stage]
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
|
||||
[candidate_action]
|
||||
engine=fai
|
||||
name=lurker_moves_fai
|
||||
id=lurker_moves_fai
|
||||
max_score=300000
|
||||
type=movement
|
||||
[filter]
|
||||
me="filter(input, (self.type = 'Saurian Skirmisher'))"
|
||||
[/filter]
|
||||
evaluation=300000
|
||||
action="{ai/micro_ais/engines/lurker_moves.fai}"
|
||||
[/candidate_action]
|
||||
[/stage]
|
||||
[/ai]
|
||||
[/side]
|
||||
|
||||
[event]
|
||||
name=preload
|
||||
first_time_only=no
|
||||
|
||||
# Hide the other sides' leaders; they are only there so that side color shows up in Status menu
|
||||
[hide_unit]
|
||||
side=2,3,4,5,6
|
||||
side=2,3,4,5
|
||||
canrecruit=yes
|
||||
[/hide_unit]
|
||||
[/event]
|
||||
|
@ -329,7 +293,6 @@
|
|||
{SCATTER_UNITS 3 "Saurian Skirmisher" 1 (x,terrain=12-19,S*) (side=3)}
|
||||
{SCATTER_UNITS 3 "Naga Fighter" 1 (y,terrain=18,W*) (side=4)}
|
||||
{SCATTER_UNITS 3 "Saurian Skirmisher" 1 (x,terrain=21-29,S*) (side=5)}
|
||||
{SCATTER_UNITS 3 "Saurian Skirmisher" 1 (x,terrain=32-99,S*) (side=6)}
|
||||
|
||||
# The Micro AI lurkers
|
||||
[micro_ai]
|
||||
|
@ -376,7 +339,7 @@
|
|||
[/micro_ai]
|
||||
|
||||
# The WML lurkers
|
||||
{LURKER_MOVES 5 (1,2,3,4,6)}
|
||||
{LURKER_MOVES 5 (1,2,3,4)}
|
||||
|
||||
{PLACE_IMAGE "scenery/signpost.png" 27 3}
|
||||
{SET_LABEL 27 3 _"End Scenario"}
|
||||
|
@ -429,18 +392,6 @@
|
|||
{UNIT 5 (Saurian Skirmisher) $x1 $y1 ()}
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
|
||||
[set_menu_item]
|
||||
id=m01_menu_lurker6
|
||||
description= _ "Place a Side 6 lurker"
|
||||
image=units/saurians/skirmisher/skirmisher.png~CROP(28,25,24,24)
|
||||
[show_if]
|
||||
{VARIABLE_CONDITIONAL scenario_name equals lurkers}
|
||||
[/show_if]
|
||||
[command]
|
||||
{UNIT 6 (Saurian Skirmisher) $x1 $y1 ()}
|
||||
[/command]
|
||||
[/set_menu_item]
|
||||
[/event]
|
||||
|
||||
# Start
|
||||
|
@ -465,7 +416,7 @@ Side 3 (green): saurians attacking only from swamp. If no enemy is in range, the
|
|||
|
||||
Side 4 (purple): nagas wandering only on water terrain, but attacking from both water and swamp.
|
||||
|
||||
We also added two other sides, which demonstrate lurker behavior coded in WML (Side 5, gray) and Formula AI (Side 6, brown)."
|
||||
We also added another side, which demonstrates lurker behavior coded in WML (Side 5, gray)."
|
||||
[/message]
|
||||
|
||||
[message]
|
||||
|
@ -510,7 +461,7 @@ The Lua Lurker AI is coded as a Micro AI. A Micro AI can be added and adapted to
|
|||
[if]
|
||||
[not]
|
||||
[have_unit]
|
||||
side=2,3,4,5,6
|
||||
side=2,3,4,5
|
||||
[/have_unit]
|
||||
[/not]
|
||||
[then]
|
||||
|
|
|
@ -247,8 +247,8 @@
|
|||
faction=Custom
|
||||
[ai]
|
||||
[stage]
|
||||
engine=fai
|
||||
name=unit_formulas
|
||||
id=main_loop
|
||||
name=ai_default_rca::candidate_action_evaluation_loop
|
||||
[/stage]
|
||||
[/ai]
|
||||
[/side]
|
||||
|
@ -279,15 +279,19 @@
|
|||
name=_ "Odrun"
|
||||
side=7
|
||||
x,y=25,4
|
||||
[ai]
|
||||
loop_formula="{ai/formula/patrol.fai}"
|
||||
[vars]
|
||||
guard_radius=3
|
||||
waypoints=[ loc(25,4) -> loc(14,2), loc(14,2) -> loc(9,12), loc(9,12) -> loc(6,19), loc(6,19) -> loc(25,4) ]
|
||||
next_step="loc(25,4)"
|
||||
[/vars]
|
||||
[/ai]
|
||||
[/unit]
|
||||
[micro_ai]
|
||||
side=7
|
||||
ai_type=patrol
|
||||
action=add
|
||||
[filter]
|
||||
id=Odrun
|
||||
[/filter]
|
||||
waypoint_x=14,9,6,25
|
||||
waypoint_y=2,12,19,4
|
||||
attack_range=4
|
||||
attack_invisible_enemies=yes
|
||||
[/micro_ai]
|
||||
|
||||
[unit]
|
||||
type=Goblin Pillager
|
||||
|
@ -295,15 +299,19 @@
|
|||
name=_ "Kardur"
|
||||
side=7
|
||||
x,y=10,20
|
||||
[ai]
|
||||
loop_formula="{ai/formula/patrol.fai}"
|
||||
[vars]
|
||||
guard_radius=3
|
||||
waypoints=[ loc(10,20) -> loc(18,24), loc(18,24) -> loc(21,19), loc(21,19) -> loc(18,11), loc(18,11) -> loc(10,20) ]
|
||||
next_step="loc(10,20)"
|
||||
[/vars]
|
||||
[/ai]
|
||||
[/unit]
|
||||
[micro_ai]
|
||||
side=7
|
||||
ai_type=patrol
|
||||
action=add
|
||||
[filter]
|
||||
id=Kardur
|
||||
[/filter]
|
||||
waypoint_x=18,21,18,10
|
||||
waypoint_y=24,19,11,20
|
||||
attack_range=4
|
||||
attack_invisible_enemies=yes
|
||||
[/micro_ai]
|
||||
|
||||
[unit]
|
||||
type=Goblin Pillager
|
||||
|
@ -311,15 +319,19 @@
|
|||
name=_ "Kartrog"
|
||||
side=7
|
||||
x,y=22,27
|
||||
[ai]
|
||||
loop_formula="{ai/formula/patrol.fai}"
|
||||
[vars]
|
||||
guard_radius=3
|
||||
waypoints=[ loc(22,27) -> loc(27,24), loc(27,24) -> loc(21,16),loc(21,16) -> loc(22,27), loc(22,27) -> loc(6,24), loc(6,24)-> loc(22,27)]
|
||||
next_step="loc(22,27)"
|
||||
[/vars]
|
||||
[/ai]
|
||||
[/unit]
|
||||
[micro_ai]
|
||||
side=7
|
||||
ai_type=patrol
|
||||
action=add
|
||||
[filter]
|
||||
id=Kartrog
|
||||
[/filter]
|
||||
waypoint_x=27,21,22,6,22
|
||||
waypoint_y=24,16,27,24,27
|
||||
attack_range=4
|
||||
attack_invisible_enemies=yes
|
||||
[/micro_ai]
|
||||
|
||||
[unit]
|
||||
type=Goblin Knight
|
||||
|
@ -327,15 +339,19 @@
|
|||
name=_ "Sdrul"
|
||||
side=7
|
||||
x,y=13,19
|
||||
[ai]
|
||||
loop_formula="{ai/formula/patrol.fai}"
|
||||
[vars]
|
||||
guard_radius=3
|
||||
waypoints=[ loc(13,19) -> loc(16,15), loc(16,15) -> loc(25,4), loc(25,4) -> loc(13,19) ]
|
||||
next_step="loc(13,19)"
|
||||
[/vars]
|
||||
[/ai]
|
||||
[/unit]
|
||||
[micro_ai]
|
||||
side=7
|
||||
ai_type=patrol
|
||||
action=add
|
||||
[filter]
|
||||
id=Sdrul
|
||||
[/filter]
|
||||
waypoint_x=16,25,13
|
||||
waypoint_y=15,4,19
|
||||
attack_range=4
|
||||
attack_invisible_enemies=yes
|
||||
[/micro_ai]
|
||||
|
||||
[unit]
|
||||
type=Direwolf Rider
|
||||
|
@ -343,15 +359,19 @@
|
|||
name=_ "Utrub"
|
||||
side=7
|
||||
x,y=29,24
|
||||
[ai]
|
||||
loop_formula="{ai/formula/patrol.fai}"
|
||||
[vars]
|
||||
guard_radius=3
|
||||
waypoints=[ loc(29,24) -> loc(16,15), loc(16,15) -> loc(29,24) ]
|
||||
next_step="loc(29,24)"
|
||||
[/vars]
|
||||
[/ai]
|
||||
[/unit]
|
||||
[micro_ai]
|
||||
side=7
|
||||
ai_type=patrol
|
||||
action=add
|
||||
[filter]
|
||||
id=Utrub
|
||||
[/filter]
|
||||
waypoint_x=16,29
|
||||
waypoint_y=15,24
|
||||
attack_range=4
|
||||
attack_invisible_enemies=yes
|
||||
[/micro_ai]
|
||||
|
||||
[unit]
|
||||
type=Goblin Knight
|
||||
|
@ -359,15 +379,19 @@
|
|||
name=_ "Uhmit"
|
||||
side=7
|
||||
x,y=17,8
|
||||
[ai]
|
||||
loop_formula="{ai/formula/patrol.fai}"
|
||||
[vars]
|
||||
guard_radius=3
|
||||
waypoints=[ loc(25,4) -> loc(16,15), loc(16,15) -> loc(25,4) ]
|
||||
next_step="loc(17,8)"
|
||||
[/vars]
|
||||
[/ai]
|
||||
[/unit]
|
||||
[micro_ai]
|
||||
side=7
|
||||
ai_type=patrol
|
||||
action=add
|
||||
[filter]
|
||||
id=Uhmit
|
||||
[/filter]
|
||||
waypoint_x=25,16,17
|
||||
waypoint_y=4,15,8
|
||||
attack_range=4
|
||||
attack_invisible_enemies=yes
|
||||
[/micro_ai]
|
||||
|
||||
#### end of wolves ####
|
||||
|
||||
|
|
|
@ -1461,6 +1461,8 @@
|
|||
{DEPRECATED_KEY id string}
|
||||
{REQUIRED_KEYS_LOC_OR_XY waypoint string_list s_range_list}
|
||||
{SIMPLE_KEY attack string_list}
|
||||
{DEFAULT_KEY attack_range s_int 1}
|
||||
{DEFAULT_KEY attack_invisible_enemies s_bool no}
|
||||
{DEFAULT_KEY ca_score s_unsigned 300000}
|
||||
{DEFAULT_KEY one_time_only s_bool no}
|
||||
{DEFAULT_KEY out_and_back s_bool no}
|
||||
|
|
|
@ -1151,7 +1151,7 @@
|
|||
("y" "x" "terrain"))
|
||||
("micro_ai"
|
||||
("avoid_unit" "filter_location_enemy" "mobilize_condition" "avoid" "filter_location_wander" "filter_location" "unit" "probability" "filter_second" "filter")
|
||||
("weapon" "disable_move_leader_to_keep" "avoid_type" "tusklet_type" "tusker_type" "rabbit_type" "deer_type" "active_side_leader" "leadership_y" "leadership_x" "healer_y" "healer_x" "enemy_y" "enemy_x" "y" "x" "show_messages" "rest_turns" "use_straight_line" "avoid_enemies" "unique_goals" "release_all_units_at_goal" "release_unit_at_goal" "ca_id" "seek_x" "distance" "aggression" "injured_units_only" "stationary" "out_and_back" "attack" "one_time_only" "waypoint_y" "waypoint_x" "id" "skip_low_gold_recruiting" "ca_score" "pack_size" "show_pack_number" "action" "ai_type" "side"))
|
||||
("weapon" "disable_move_leader_to_keep" "avoid_type" "tusklet_type" "tusker_type" "rabbit_type" "deer_type" "active_side_leader" "leadership_y" "leadership_x" "healer_y" "healer_x" "enemy_y" "enemy_x" "y" "x" "show_messages" "rest_turns" "use_straight_line" "avoid_enemies" "unique_goals" "release_all_units_at_goal" "release_unit_at_goal" "ca_id" "seek_x" "distance" "aggression" "injured_units_only" "stationary" "out_and_back" "attack" "attack_range" "attack_invisible_enemies" "one_time_only" "waypoint_y" "waypoint_x" "id" "skip_low_gold_recruiting" "ca_score" "pack_size" "show_pack_number" "action" "ai_type" "side"))
|
||||
("petrify" nil
|
||||
("id"))
|
||||
("background_layer" nil
|
||||
|
|
Loading…
Add table
Reference in a new issue