Micro AIs: add zone guardian by Zazweda
This commit is contained in:
parent
98084d86a9
commit
c63258da84
3 changed files with 219 additions and 1 deletions
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Add table
Reference in a new issue