Skip to content

Commit

Permalink
Micro AIs: fix errors caused when WML events interfere with AI actions
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mattsc committed Feb 28, 2014
1 parent b7bc394 commit 9521297
Show file tree
Hide file tree
Showing 19 changed files with 56 additions and 26 deletions.
3 changes: 2 additions & 1 deletion data/ai/micro_ais/cas/ca_big_animals.lua
Expand Up @@ -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
Expand All @@ -108,7 +110,6 @@ function ca_big_animals:execution(ai, cfg)
if target.id then
AH.checked_attack(ai, unit, target)
end

end
end

Expand Down
1 change: 1 addition & 0 deletions data/ai/micro_ais/cas/ca_bottleneck_move.lua
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions data/ai/micro_ais/cas/ca_coward.lua
Expand Up @@ -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
12 changes: 8 additions & 4 deletions data/ai/micro_ais/cas/ca_forest_animals_move.lua
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions data/ai/micro_ais/cas/ca_forest_animals_tusker_attack.lua
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion data/ai/micro_ais/cas/ca_forest_animals_tusklet_move.lua
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions data/ai/micro_ais/cas/ca_goto.lua
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions data/ai/micro_ais/cas/ca_hang_out.lua
Expand Up @@ -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

Expand Down
11 changes: 9 additions & 2 deletions data/ai/micro_ais/cas/ca_hunter.lua
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion data/ai/micro_ais/cas/ca_lurkers.lua
Expand Up @@ -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
Expand All @@ -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
3 changes: 3 additions & 0 deletions data/ai/micro_ais/cas/ca_messenger_attack.lua
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions data/ai/micro_ais/cas/ca_messenger_move.lua
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions data/ai/micro_ais/cas/ca_patrol.lua
Expand Up @@ -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
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions data/ai/micro_ais/cas/ca_protect_unit_attack.lua
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions data/ai/micro_ais/cas/ca_simple_attack.lua
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions data/ai/micro_ais/cas/ca_stationed_guardian.lua
Expand Up @@ -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")
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion data/ai/micro_ais/cas/ca_wolves_multipacks_attack.lua
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion data/ai/micro_ais/cas/ca_wolves_multipacks_wander.lua
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions data/ai/micro_ais/cas/ca_zone_guardian.lua
Expand Up @@ -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")
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit 9521297

Please sign in to comment.