Micro AIs: fix errors caused when WML events interfere with AI actions

If a WML event takes a unit off the map, many of the AIs would
previously have run into problems by trying to access a unit that is
then not there any more.  Thus, the existence of the unit needs to be
verified before continuing in cases when this would cause errors.
This commit is contained in:
mattsc 2014-02-27 16:25:16 -08:00
parent b7bc394f95
commit 95212970b8
19 changed files with 56 additions and 26 deletions

View file

@ -83,8 +83,10 @@ function ca_big_animals:execution(ai, cfg)
if (best_hex[1] ~= unit.x) or (best_hex[2] ~= unit.y) then
AH.checked_move(ai, unit, best_hex[1], best_hex[2]) -- partial move only
if (not unit) or (not unit.valid) then return end
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
end
@ -108,7 +110,6 @@ function ca_big_animals:execution(ai, cfg)
if target.id then
AH.checked_attack(ai, unit, target)
end
end
end

View file

@ -499,6 +499,7 @@ function ca_bottleneck_move:execution(ai, cfg, self)
if (self.data.unit.x ~= self.data.hex[1]) or (self.data.unit.y ~= self.data.hex[2]) then -- test needed for level-up move
AH.checked_move(ai, self.data.unit, self.data.hex[1], self.data.hex[2]) -- don't want full move, as this might be stepping out of the way
end
if (not self.data.unit) or (not self.data.unit.valid) then return end
-- If this is a move for a level-up attack, do the attack also
if self.data.lu_defender then

View file

@ -116,10 +116,9 @@ function ca_coward:execution(ai, cfg)
if (mx ~= unit.x or my ~= unit.y) then
AH.movefull_stopunit(ai, unit, mx, my)
end
if (not unit) or (not unit.valid) then return end
-- Get unit again, just in case it was killed by a moveto event
local unit = wesnoth.get_units{ id = cfg.id }[1]
if unit then AH.checked_stopunit_all(ai, unit) end
AH.checked_stopunit_all(ai, unit)
end
return ca_coward

View file

@ -122,9 +122,14 @@ function ca_forest_animals_move:execution(ai, cfg)
-- Now we check for close enemies again, as we might just have moved within reach of some
local close_enemies = {}
for j,e in ipairs(enemies) do
if (H.distance_between(unit.x, unit.y, e.x, e.y) <= unit.max_moves+1) then
table.insert(close_enemies, e)
-- We use a trick here to exclude the case when the unit might have been
-- removed in an event above
if unit and unit.valid then
for j,e in ipairs(enemies) do
if (H.distance_between(unit.x, unit.y, e.x, e.y) <= unit.max_moves+1) then
table.insert(close_enemies, e)
end
end
end
--print(' #close_enemies after move', #close_enemies, #enemies, unit.id)
@ -163,7 +168,6 @@ function ca_forest_animals_move:execution(ai, cfg)
-- Finally, take moves away, as only partial move might have been done
-- Also attacks, as these units never attack
if unit and unit.valid then AH.checked_stopunit_all(ai, unit) end
-- Need this ^ test here because bunnies might have disappeared
end
end

View file

@ -55,6 +55,8 @@ function ca_forest_animals_tusker_attack:execution(ai, cfg)
end)
--print('attacker', attacker.x, attacker.y, ' -> ', best_hex[1], best_hex[2])
AH.movefull_stopunit(ai, attacker, best_hex)
if (not attacker) or (not attacker.valid) then return end
if (not target) or (not target.valid) then return end
-- If adjacent, attack
local dist = H.distance_between(attacker.x, attacker.y, target.x, target.y)

View file

@ -41,7 +41,7 @@ function ca_forest_animals_tusklet_move:execution(ai, cfg)
AH.movefull_stopunit(ai, tusklet, best_hex)
-- Also make sure tusklets never attack
AH.checked_stopunit_all(ai, tusklet)
if tusklet and tusklet.valid then AH.checked_stopunit_all(ai, tusklet) end
end
end

View file

@ -206,6 +206,7 @@ function ca_goto:execution(ai, cfg, self)
else
AH.checked_stopunit_moves(ai, best_unit)
end
if (not best_unit) or (not best_unit.valid) then return end
-- If release_unit_at_goal= or release_all_units_at_goal= key is set:
-- Check if the unit made it to one of the goal hexes

View file

@ -128,12 +128,12 @@ 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
u.variables.mai_hangout_moved = nil
if u and u.valid then u.variables.mai_hangout_moved = nil end
end
else
-- Otherwise move unit and mark as having been used
AH.checked_move(ai, best_unit, best_hex[1], best_hex[2])
best_unit.variables.mai_hangout_moved = true
if best_unit and best_unit.valid then best_unit.variables.mai_hangout_moved = true end
end
end

View file

@ -114,8 +114,10 @@ function ca_hunter:execution(ai, cfg)
if (best_hex[1] ~= unit.x) or (best_hex[2] ~= unit.y) then
AH.checked_move(ai, unit, best_hex[1], best_hex[2]) -- partial move only
if (not unit) or (not unit.valid) then return end
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
end
@ -149,6 +151,7 @@ function ca_hunter:execution(ai, cfg)
if next_hop then
--print(next_hop[1], next_hop[2])
AH.movefull_stopunit(ai, unit, next_hop)
if (not unit) or (not unit.valid) then return end
-- If there's an enemy on the 'home' hex and we got right next to it, attack that enemy
if (H.distance_between(cfg.home_x, cfg.home_y, next_hop[1], next_hop[2]) == 1) then
@ -158,15 +161,17 @@ function ca_hunter:execution(ai, cfg)
W.message { speaker = unit.id, message = 'Get out of my home!' }
end
AH.checked_attack(ai, unit, enemy)
if (not unit) or (not unit.valid) then return end
end
end
end
-- We also attack the weakest adjacent enemy, if still possible
hunter_attack_weakest_adj_enemy(ai, unit)
if (not unit) or (not unit.valid) then return end
-- If the unit got home, start the resting counter
if unit.valid and (unit.x == cfg.home_x) and (unit.y == cfg.home_y) then
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)
if cfg.show_messages then
@ -182,12 +187,14 @@ function ca_hunter:execution(ai, cfg)
if (unit.variables.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
-- However, we do also attack the weakest adjacent enemy, if still possible
hunter_attack_weakest_adj_enemy(ai, unit)
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.valid and (unit.hitpoints >= unit.max_hitpoints) and (unit.variables.resting_until <= wesnoth.current.turn) then
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 cfg.show_messages then

View file

@ -63,11 +63,13 @@ function ca_lurkers:execution(ai, cfg)
local rand = math.random(1, rattack_nt_target:size())
local dst = rattack_nt_target:to_stable_pairs()
AH.movefull_stopunit(ai, me, dst[rand])
if (not me) or (not me.valid) then return end
AH.checked_attack(ai, me, target)
attacked = true
break
end
end
if (not me) or (not me.valid) then return end
-- If unit has moves left (that is, it didn't attack), go to random wander terrain hex
-- Check first that unit wasn't killed in the attack
@ -91,9 +93,10 @@ function ca_lurkers:execution(ai, cfg)
end
AH.movefull_stopunit(ai, me, dst)
end
if (not me) or (not me.valid) then return end
-- If the unit has moves or attacks left at this point, take them away
if me and me.valid then AH.checked_stopunit_all(ai, me) end
AH.checked_stopunit_all(ai, me)
end
return ca_lurkers

View file

@ -156,6 +156,9 @@ function ca_messenger_attack:execution(ai, cfg, self)
local defender = wesnoth.get_unit(self.data.best_attack.target.x, self.data.best_attack.target.y)
AH.movefull_stopunit(ai, attacker, self.data.best_attack.dst.x, self.data.best_attack.dst.y)
if (not attacker) or (not attacker.valid) then return end
if (not defender) or (not defender.valid) then return end
AH.checked_attack(ai, attacker, defender)
self.data.best_attack = nil
end

View file

@ -62,6 +62,7 @@ function ca_messenger_move:execution(ai, cfg, self)
else
AH.checked_stopunit_moves(ai, messenger)
end
if (not messenger) or (not messenger.valid) then return end
-- We also test whether an attack without retaliation or with little damage is possible
if (not H.get_child(messenger.__cfg, 'attack')) then return end
@ -116,6 +117,7 @@ function ca_messenger_move:execution(ai, cfg, self)
AH.checked_attack(ai, messenger, target)
end
end
if (not messenger) or (not messenger.valid) then return end
-- Finally, make sure unit is really done after this
AH.checked_stopunit_attacks(ai, messenger)

View file

@ -124,6 +124,7 @@ function ca_patrol:execution(ai, cfg, self)
AH.checked_stopunit_moves(ai, patrol)
end
end
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
@ -152,9 +153,9 @@ function ca_patrol:execution(ai, cfg, self)
break
end
end
if (not patrol) or (not patrol.valid) then return end
-- Check that patrol is not killed
if patrol and patrol.valid then AH.checked_stopunit_all(ai, patrol) end
AH.checked_stopunit_all(ai, patrol)
end
return ca_patrol

View file

@ -127,6 +127,9 @@ function ca_protect_unit_attack:execution(ai, cfg, self)
local defender = wesnoth.get_unit(self.data.best_attack.target.x, self.data.best_attack.target.y)
AH.movefull_stopunit(ai, attacker, self.data.best_attack.dst.x, self.data.best_attack.dst.y)
if (not attacker) or (not attacker.valid) then return end
if (not defender) or (not defender.valid) then return end
AH.checked_attack(ai, attacker, defender)
self.data.best_attack = nil
end

View file

@ -79,6 +79,9 @@ function ca_simple_attack:execution(ai, cfg, self)
local defender = wesnoth.get_unit(self.data.attack.target.x, self.data.attack.target.y)
AH.movefull_outofway_stopunit(ai, attacker, self.data.attack.dst.x, self.data.attack.dst.y)
if (not attacker) or (not attacker.valid) then return end
if (not defender) or (not defender.valid) then return end
AH.checked_attack(ai, attacker, defender, (cfg.weapon or -1))
self.data.attack = nil
end

View file

@ -92,8 +92,8 @@ function ca_stationed_guardian:execution(ai, cfg)
if (best_defense ~= -9e99) then
--print("Attack at:",attack_loc[1],attack_loc[2],best_defense)
AH.movefull_stopunit(ai, unit, attack_loc)
-- There should be an ai.check_attack_action() here in case something weird is
-- done in a 'moveto' event.
if (not unit) or (not unit.valid) then return end
if (not target) or (not target.valid) then return end
AH.checked_attack(ai, unit, target)
else -- otherwise move toward that enemy
--print("Cannot reach target, moving toward it")
@ -125,9 +125,9 @@ function ca_stationed_guardian:execution(ai, cfg)
AH.movefull_stopunit(ai, unit, nh)
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 AH.checked_stopunit_moves(ai, unit) end
if (not unit) or (not unit.valid) then return end
AH.checked_stopunit_moves(ai, unit)
-- If there are attacks left and unit ended up next to an enemy, we'll leave this to RCA AI
end

View file

@ -182,7 +182,7 @@ function ca_wolves_multipacks_attack:execution(ai, cfg)
W.label { x = w.x, y = w.y, text = "" }
end
AH.movefull_stopunit(ai, w, best_hex)
if cfg.show_pack_number then
if cfg.show_pack_number and w and w.valid then
WMPF.color_label(w.x, w.y, pack_number)
end
end

View file

@ -132,7 +132,7 @@ function ca_wolves_multipacks_wander:execution(ai, cfg)
W.label { x = w.x, y = w.y, text = "" }
end
AH.movefull_stopunit(ai, w, best_hex)
if cfg.show_pack_number then
if cfg.show_pack_number and w and w.valid then
WMPF.color_label(w.x, w.y, k)
end
end

View file

@ -84,6 +84,7 @@ function ca_zone_guardian:execution(ai, cfg)
if (best_defense ~= -9e99) then
--print("Attack at:",attack_loc[1],attack_loc[2],best_defense)
AH.movefull_stopunit(ai, unit, attack_loc)
if (not unit) or (not unit.valid) then return end
AH.checked_attack(ai, unit, target)
else -- otherwise move toward that enemy
--print("Cannot reach target, moving toward it")
@ -153,10 +154,9 @@ function ca_zone_guardian:execution(ai, cfg)
AH.movefull_stopunit(ai, unit, nh)
end
end
if (not unit) or (not unit.valid) then return 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 AH.checked_stopunit_moves(ai, unit) end
AH.checked_stopunit_moves(ai, unit)
-- If there are attacks left and unit ended up next to an enemy, we'll leave this to RCA AI
end