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:
parent
b7bc394f95
commit
95212970b8
19 changed files with 56 additions and 26 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue