ai_helper.robust_move_and_attack: return action result to calling function
This function is supposed to be able to deal with all kinds of rare and weird events without throwing errors, but it should still return a status table describing what happened. The vast majority of situations will simply return the move_result tables. For all the strange stuff that could also happen due to WML events etc., we set up dummy tables with the same structure and (somewhat) descriptive error messages instead.
This commit is contained in:
parent
bb43622081
commit
cc6679159b
1 changed files with 70 additions and 36 deletions
|
@ -167,6 +167,15 @@ function ai_helper.is_incomplete_or_empty_move(check)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ai_helper.dummy_check_action(gamestate_changed, ok, result, status)
|
||||||
|
return {
|
||||||
|
gamestate_changed = gamestate_changed or false,
|
||||||
|
ok = ok or false,
|
||||||
|
result = result or 'ai_helper::DUMMY_FAILED_ACTION',
|
||||||
|
status = status or 99999
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
function ai_helper.checked_action_error(action, error_code)
|
function ai_helper.checked_action_error(action, error_code)
|
||||||
if wesnoth.game_config.debug then
|
if wesnoth.game_config.debug then
|
||||||
error(action .. ' could not be executed. Error code: ' .. error_code)
|
error(action .. ' could not be executed. Error code: ' .. error_code)
|
||||||
|
@ -295,7 +304,9 @@ function ai_helper.robust_move_and_attack(ai, src, dst, target_loc, cfg)
|
||||||
local dst_x, dst_y = dst.x or dst[1], dst.y or dst[2]
|
local dst_x, dst_y = dst.x or dst[1], dst.y or dst[2]
|
||||||
|
|
||||||
local unit = wesnoth.get_unit(src_x, src_y)
|
local unit = wesnoth.get_unit(src_x, src_y)
|
||||||
if (not unit) then return end
|
if (not unit) then
|
||||||
|
return ai_helper.dummy_check_action(false, false, 'robust_move_and_attack::NO_UNIT')
|
||||||
|
end
|
||||||
|
|
||||||
-- Getting target at beginning also, in case events mess up things along the way
|
-- Getting target at beginning also, in case events mess up things along the way
|
||||||
local target, target_x, target_y
|
local target, target_x, target_y
|
||||||
|
@ -303,18 +314,23 @@ function ai_helper.robust_move_and_attack(ai, src, dst, target_loc, cfg)
|
||||||
target_x, target_y = target_loc.x or target_loc[1], target_loc.y or target_loc[2]
|
target_x, target_y = target_loc.x or target_loc[1], target_loc.y or target_loc[2]
|
||||||
target = wesnoth.get_unit(target_x, target_y)
|
target = wesnoth.get_unit(target_x, target_y)
|
||||||
|
|
||||||
if (not target) then return end
|
if (not target) then
|
||||||
|
return ai_helper.dummy_check_action(false, false, 'robust_move_and_attack::NO_TARGET')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local gamestate_changed = false
|
local gamestate_changed = false
|
||||||
|
local move_result
|
||||||
if (unit.moves > 0) then
|
if (unit.moves > 0) then
|
||||||
if (src_x == dst_x) and (src_y == dst_y) then
|
if (src_x == dst_x) and (src_y == dst_y) then
|
||||||
local check = ai.stopunit_moves(unit)
|
move_result = ai.stopunit_moves(unit)
|
||||||
|
|
||||||
-- The only possible failure modes are non-recoverable (such as E_NOT_OWN_UNIT)
|
-- The only possible failure modes are non-recoverable (such as E_NOT_OWN_UNIT)
|
||||||
if (not check.ok) then return end
|
if (not move_result.ok) then return move_result end
|
||||||
if (not unit) or (not unit.valid) then return end
|
|
||||||
|
if (not unit) or (not unit.valid) then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_DISAPPEARED')
|
||||||
|
end
|
||||||
|
|
||||||
gamestate_changed = true
|
gamestate_changed = true
|
||||||
else
|
else
|
||||||
|
@ -325,7 +341,9 @@ function ai_helper.robust_move_and_attack(ai, src, dst, target_loc, cfg)
|
||||||
local uiw_old_moves = unit_in_way.moves
|
local uiw_old_moves = unit_in_way.moves
|
||||||
ai_helper.move_unit_out_of_way(ai, unit_in_way, cfg)
|
ai_helper.move_unit_out_of_way(ai, unit_in_way, cfg)
|
||||||
|
|
||||||
if (not unit_in_way) or (not unit_in_way.valid) then return end
|
if (not unit_in_way) or (not unit_in_way.valid) then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_IN_WAY_DISAPPEARED')
|
||||||
|
end
|
||||||
|
|
||||||
-- Failed move out of way: abandon remaining actions
|
-- Failed move out of way: abandon remaining actions
|
||||||
if (unit_in_way.x == dst_x) and (unit_in_way.y == dst_y) then
|
if (unit_in_way.x == dst_x) and (unit_in_way.y == dst_y) then
|
||||||
|
@ -333,37 +351,42 @@ function ai_helper.robust_move_and_attack(ai, src, dst, target_loc, cfg)
|
||||||
-- Forcing a gamestate change, if necessary
|
-- Forcing a gamestate change, if necessary
|
||||||
ai.stopunit_moves(unit_in_way)
|
ai.stopunit_moves(unit_in_way)
|
||||||
end
|
end
|
||||||
return
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_IN_WAY_EMPTY_MOVE')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check whether dst hex is free now (an event could have done something funny)
|
-- Check whether dst hex is free now (an event could have done something funny)
|
||||||
local unit_in_way = wesnoth.get_unit(dst_x, dst_y)
|
local unit_in_way = wesnoth.get_unit(dst_x, dst_y)
|
||||||
if unit_in_way then return end
|
if unit_in_way then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::ANOTHER_UNIT_IN_WAY')
|
||||||
|
end
|
||||||
|
|
||||||
gamestate_changed = true
|
gamestate_changed = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if (not unit) or (not unit.valid) or (unit.x ~= src_x) or (unit.y ~= src_y) then
|
if (not unit) or (not unit.valid) or (unit.x ~= src_x) or (unit.y ~= src_y) then
|
||||||
return
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_DISAPPEARED')
|
||||||
end
|
end
|
||||||
|
|
||||||
local check = ai.check_move(unit, dst_x, dst_y)
|
local check_result = ai.check_move(unit, dst_x, dst_y)
|
||||||
if (not check.ok) then
|
if (not check_result.ok) then
|
||||||
if (not ai_helper.is_incomplete_or_empty_move(check)) then
|
if (not ai_helper.is_incomplete_or_empty_move(check_result)) then
|
||||||
if (not gamestate_changed) then
|
if (not gamestate_changed) then
|
||||||
ai.stopunit_moves(unit)
|
ai.stopunit_moves(unit)
|
||||||
end
|
end
|
||||||
return
|
return check_result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfg and cfg.partial_move then
|
if cfg and cfg.partial_move then
|
||||||
ai.move(unit, dst_x, dst_y)
|
move_result = ai.move(unit, dst_x, dst_y)
|
||||||
else
|
else
|
||||||
ai.move_full(unit, dst_x, dst_y)
|
move_result = ai.move_full(unit, dst_x, dst_y)
|
||||||
end
|
end
|
||||||
|
if (not move_result.ok) then return move_result end
|
||||||
|
|
||||||
if (not unit) or (not unit.valid) then return end
|
if (not unit) or (not unit.valid) then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_DISAPPEARED')
|
||||||
|
end
|
||||||
|
|
||||||
-- Failed move: abandon rest of actions
|
-- Failed move: abandon rest of actions
|
||||||
if (unit.x == src_x) and (unit.y == src_y) then
|
if (unit.x == src_x) and (unit.y == src_y) then
|
||||||
|
@ -371,46 +394,57 @@ function ai_helper.robust_move_and_attack(ai, src, dst, target_loc, cfg)
|
||||||
-- Forcing a gamestate change, if necessary
|
-- Forcing a gamestate change, if necessary
|
||||||
ai.stopunit_moves(unit)
|
ai.stopunit_moves(unit)
|
||||||
end
|
end
|
||||||
return
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNPLANNED_EMPTY_MOVE')
|
||||||
end
|
end
|
||||||
|
|
||||||
gamestate_changed = true
|
gamestate_changed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Tests after the move, before continuing to attack
|
-- Tests after the move, before continuing to attack, to ensure WML events
|
||||||
if (not unit) or (not unit.valid) then return end
|
-- did not do something funny
|
||||||
if (unit.x ~= dst_x) or (unit.y ~= dst_y) then return end
|
if (not unit) or (not unit.valid) then
|
||||||
if (not target) or (not target.valid) then return end
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_DISAPPEARED')
|
||||||
if (target.x ~= target_x) or (target.y ~= target_y) then return end
|
end
|
||||||
|
if (unit.x ~= dst_x) or (unit.y ~= dst_y) then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_NOT_AT_DESTINATION')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- In case all went well and there's no attack to be done
|
||||||
|
if (not target_x) then return move_result end
|
||||||
|
|
||||||
|
if (not target) or (not target.valid) then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::TARGET_DISAPPEARED')
|
||||||
|
end
|
||||||
|
if (target.x ~= target_x) or (target.y ~= target_y) then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::TARGET_MOVED')
|
||||||
|
end
|
||||||
|
|
||||||
local weapon = cfg and cfg.weapon
|
local weapon = cfg and cfg.weapon
|
||||||
local old_attacks_left = unit.attacks_left
|
local old_attacks_left = unit.attacks_left
|
||||||
|
|
||||||
local check = ai.check_attack(unit, target, weapon)
|
local check_result = ai.check_attack(unit, target, weapon)
|
||||||
if (not check.ok) then
|
if (not check_result.ok) then
|
||||||
if (not gamestate_changed) then
|
if (not gamestate_changed) then
|
||||||
ai.stopunit_all(unit)
|
ai.stopunit_all(unit)
|
||||||
end
|
end
|
||||||
return
|
return check_result
|
||||||
end
|
end
|
||||||
|
|
||||||
ai.attack(unit, target, weapon)
|
move_result = ai.attack(unit, target, weapon)
|
||||||
|
-- This should not happen, given that we just checked, but just in case
|
||||||
|
if (not move_result.ok) then return move_result end
|
||||||
|
|
||||||
if (not unit) or (not unit.valid) then return end
|
if (not unit) or (not unit.valid) then
|
||||||
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::UNIT_DISAPPEARED')
|
||||||
|
end
|
||||||
|
|
||||||
if (unit.attacks_left == old_attacks_left) and (not gamestate_changed) then
|
if (unit.attacks_left == old_attacks_left) and (not gamestate_changed) then
|
||||||
ai.stopunit_all(unit)
|
ai.stopunit_all(unit)
|
||||||
return
|
return ai_helper.dummy_check_action(true, false, 'robust_move_and_attack::NO_ATTACK')
|
||||||
else
|
|
||||||
gamestate_changed = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This cannot possibly be reached with gamestate_changed=false at the moment,
|
return move_result
|
||||||
-- but keeping it in case more is added later.
|
|
||||||
if (not gamestate_changed) then
|
|
||||||
ai.stopunit_all(unit)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
----- General functionality and maths helper functions ------
|
----- General functionality and maths helper functions ------
|
||||||
|
|
Loading…
Add table
Reference in a new issue