Micro AIs: add zone guardian by Zazweda

This commit is contained in:
mattsc 2013-05-01 14:09:03 -07:00
parent 98084d86a9
commit c63258da84
3 changed files with 219 additions and 1 deletions

View file

@ -108,6 +108,7 @@ return {
if unit then ai.stopunit_all(unit) end
end
----- The return guardian AI -----
function guardians:return_guardian_eval(cfg)
local unit = wesnoth.get_units { id = cfg.id }[1]
@ -130,6 +131,126 @@ return {
end
end
----- The zone guardian AI -----
-- Required params : id, filter_location (SFL)
-- Optional params : filter_location_enemy (SLF)
-- The zone guardian randomly moves accross the specified filter_location until
-- it "detects" an enemy in filter_location_enemy (if specified) or in the filter_location (otherwise).
-- It then attacks this enemy until it goes out of the "attack zone"
function guardians:zone_guardian_eval(cfg)
local unit = wesnoth.get_units { id = cfg.id }[1]
-- Don't need to check if unit exists as this is a sticky CA
if (unit.moves > 0) then
return 100010
end
end
--Check if an enemy is detected in filter_location_enemy (or filter_location) and attack it or start the "move" randomly function
function guardians:zone_guardian_exec(cfg)
local unit = wesnoth.get_units { id = cfg.id }[1]
local reach = wesnoth.find_reach(unit)
local zone_enemy = cfg.filter_location_enemy or cfg.filter_location
-- enemy units within reach
local enemies = wesnoth.get_units {
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
{ "filter_location", zone_enemy }
}
if enemies[1] then
local target = {}
local min_dist = 9999
for i,e in ipairs(enemies) do
local dg = H.distance_between(unit.x, unit.y, e.x, e.y)
-- If valid target found, save the one with the shortest distance from unit
if (dg < min_dist) then
--print("target:", e.id, ds, dg)
target = e
min_dist = dg
end
end
-- If a valid target was found, unit attacks this target, or moves toward it
if (min_dist ~= 9999) then
--print ("Go for enemy unit:", target.id)
-- Find tiles adjacent to the target, and save the one that our unit
-- can reach with the highest defense rating
local best_defense, attack_loc = -9e99, {}
for x,y in H.adjacent_tiles(target.x, target.y) do
-- only consider unoccupied hexes
local occ_hex = wesnoth.get_units { x=x, y=y, { "not", { id = unit.id } } }[1]
if not occ_hex then
-- defense rating of the hex
local defense = 100 - wesnoth.unit_defense(unit, wesnoth.get_terrain(x, y))
--print(x,y,defense)
local nh = AH.next_hop(unit, x, y)
-- if this is best defense rating and unit can reach it, save this location
if nh then
if (nh[1] == x) and (nh[2] == y) and (defense > best_defense) then
best_defense, attack_loc = defense, {x, y}
end
end
end
end
-- If a valid hex was found: move there and attack
if (best_defense ~= -9e99) then
--print("Attack at:",attack_loc[1],attack_loc[2],best_defense)
AH.movefull_stopunit(ai, unit, attack_loc)
ai.attack(unit, target)
else -- otherwise move toward that enemy
--print("Cannot reach target, moving toward it")
local reach = wesnoth.find_reach(unit)
-- Go through all hexes the unit can reach, find closest to target
local nh = {} -- cannot use next_hop here since target hex is occupied by enemy
local min_dist = 9999
for i,r in ipairs(reach) do
-- only consider unoccupied hexes
local occ_hex = wesnoth.get_units { x=r[1], y=r[2], { "not", { id = unit.id } } }[1]
if not occ_hex then
local d = H.distance_between(r[1], r[2], target.x, target.y)
if d < min_dist then
min_dist = d
nh = {r[1], r[2]}
end
end
end
-- Finally, execute the move toward the target
AH.movefull_stopunit(ai, unit, nh)
end
end
-- If no enemy around or within the zone, move toward "random" position which are mainy the borders
else
--print "Move toward newpos"
local width, height = wesnoth.get_map_size()
local locs = wesnoth.get_locations {
x = '1-' .. width,
y = '1-' .. height,
{ "and", cfg.filter_location }
}
local newpos = math.random(#locs)
local nh = AH.next_hop(unit, locs[newpos][1], locs[newpos][2])
if nh then
AH.movefull_stopunit(ai, unit, nh)
end
end
-- Get unit again, just in case something was done to it in a 'moveto' or 'attack' event
local unit = wesnoth.get_units{ id = cfg.id }[1]
if unit then ai.stopunit_moves(unit) end
-- If there are attacks left and unit ended up next to an enemy, we'll leave this to RCA AI
end
----- The stationed guardian AI -----
function guardians:stationed_guardian_eval(cfg)
local unit = wesnoth.get_units { id = cfg.id }[1]

View file

@ -421,6 +421,9 @@ function wesnoth.wml_actions.micro_ai(cfg)
required_keys["stationed_guardian"] = { "id", "distance", "station_x", "station_y", "guard_x", "guard_y" }
optional_keys["stationed_guardian"] = {}
required_keys["zone_guardian"] = { "id", "filter_location" }
optional_keys["zone_guardian"] = { "filter_location_enemy" }
required_keys["coward"] = { "id", "distance" }
optional_keys["coward"] = { "seek_x", "seek_y","avoid_x","avoid_y" }
@ -460,6 +463,7 @@ function wesnoth.wml_actions.micro_ai(cfg)
local max_scores = {}
max_scores["stationed_guardian"] = 100010
max_scores["zone_guardian"] = 100010
max_scores["coward"] = 300000
local unit = wesnoth.get_units { id=cfg.id }[1]

View file

@ -71,7 +71,7 @@
[/modify_ai]
[/ai]
{MICRO_AI_GUARDIAN}
{MICRO_AI_GUARDIAN}
[/side]
# Put all the units and markers out there
@ -328,6 +328,86 @@ s=4,14 g=7,13"
guard_x,guard_y=3,13
[/micro_ai]
# Zone guardians
[unit]
type=Troll
side=2
id=zone1
name=_"Gate Keeper"
x,y=5,25
[modifications]
{TRAIT_STRONG}
[/modifications]
[variables]
label=_"Zone Guard"
[/variables]
[/unit]
[micro_ai]
side=2
ai_type=guardian_unit
action=add
guardian_type=zone_guardian
id=zone1
[filter_location]
x=4,5,6,7,5
y=24,24,24,24,25
[/filter_location]
[/micro_ai]
[unit]
type=Dwarvish Guardsman
side=2
id=zone2
name=_"Home Keeper"
x,y=2,27
[modifications]
{TRAIT_STRONG}
[/modifications]
[variables]
label=_"Zone Guard with
separate attack Zone"
[/variables]
[/unit]
[micro_ai]
side=2
ai_type=guardian_unit
action=add
guardian_type=zone_guardian
id=zone2
[filter_location]
x=1,2,3,2
y=27,27,27,26
[/filter_location]
[filter_location_enemy]
x,y=1-4,24-27
[/filter_location_enemy]
[/micro_ai]
[unit]
type=Naga Fighter
side=2
id=zone3
name=_"Water Guardian"
x,y=29,8
[variables]
label=_"Zone Guard"
[/variables]
[/unit]
[micro_ai]
side=2
ai_type=guardian_unit
action=add
guardian_type=zone_guardian
id=zone3
[filter_location]
x,y=22-31,4-11 # This is intentionally chosen to extend past the lake
terrain=W*
[/filter_location]
[/micro_ai]
{SET_LABEL 3 13 _"Guarded Location"}
{SET_LABEL 2 14 _"Station 1"}
{SET_LABEL 4 14 _"Station 2"}
@ -417,6 +497,19 @@ This also demonstrates how to combine candidate actions from Formula AI and Lua
- Both 'seek' and 'avoid' may consist of only one coordinate ('x' or 'y'), in which case not a single hex, but a line of hexes is sought or avoided."}
[/command]
[/set_menu_item]
[set_menu_item]
id=m06_zone
description=_"Zone Guardian"
image=units/nagas/fighter.png~CROP(25,19,24,24)
[show_if]
{VARIABLE_CONDITIONAL scenario_name equals guardians}
[/show_if]
[command]
{MESSAGE narrator "units/monsters/water-serpent.png" _"Zone Guardian" _"A zone guardian is a unit that, as the name says, guards a zone. It moves randomly inside this zone until an enemy enters it (or a separately defined enemy zone, see below). Applications might be the defense of a castle or a nesting area. The zone macro can be called with an optional enemy zone:
- If not specified, the zone guard attacks any enemy coming inside its guard zone.
- Otherwise, it attacks any enemy entering the enemy zone and once there are no more enemies, it goes back to patrol in its basic zone."}
[/command]
[/set_menu_item]
[/event]
[event]