local AH = wesnoth.require "ai/lua/ai_helper.lua" local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua" local function get_patrol(cfg) local filter = cfg.filter or { id = cfg.id } local patrol = AH.get_units_with_moves { side = wesnoth.current.side, { "and", filter } }[1] return patrol end local ca_patrol = {} function ca_patrol:evaluation(ai, cfg) if get_patrol(cfg) then return cfg.ca_score end return 0 end function ca_patrol:execution(ai, cfg) local patrol = get_patrol(cfg) local patrol_vars = MAIUV.get_mai_unit_variables(patrol, cfg.ai_id) -- Set up waypoints, taking into account whether 'reverse' is set -- This works even the first time, when patrol_vars.patrol_reverse is not set yet cfg.waypoint_x = AH.split(cfg.waypoint_x, ",") cfg.waypoint_y = AH.split(cfg.waypoint_y, ",") local n_wp = #cfg.waypoint_x local waypoints = {} for i = 1,n_wp do if patrol_vars.patrol_reverse then waypoints[i] = { tonumber(cfg.waypoint_x[n_wp-i+1]), tonumber(cfg.waypoint_y[n_wp-i+1]) } else waypoints[i] = { tonumber(cfg.waypoint_x[i]), tonumber(cfg.waypoint_y[i]) } end end -- If not set, set next location (first move) -- This needs to be in WML format, so that it persists over save/load cycles if (not patrol_vars.patrol_x) then patrol_vars.patrol_x = waypoints[1][1] patrol_vars.patrol_y = waypoints[1][2] patrol_vars.patrol_reverse = false MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars) end while patrol.moves > 0 do -- Check whether one of the enemies to be attacked is next to the patroller -- If so, don't move, but attack that enemy local adjacent_enemy = wesnoth.get_units { id = cfg.attack, { "filter_adjacent", { id = patrol.id } }, { "filter_side", {{ "enemy_of", { side = wesnoth.current.side } }} } }[1] if adjacent_enemy then break end -- Also check whether we're next to any unit (enemy or ally) which is on the next waypoint local unit_on_wp = wesnoth.get_units { x = patrol_vars.patrol_x, y = patrol_vars.patrol_y, { "filter_adjacent", { id = patrol.id } } }[1] for i,wp in ipairs(waypoints) do -- If the patrol is on a waypoint or adjacent to one that is occupied by any unit if ((patrol.x == wp[1]) and (patrol.y == wp[2])) or (unit_on_wp and ((unit_on_wp.x == wp[1]) and (unit_on_wp.y == wp[2]))) then if (i == n_wp) then -- Move him to the first one (or reverse route), if he's on the last waypoint -- Unless cfg.one_time_only is set if cfg.one_time_only then patrol_vars.patrol_x = waypoints[n_wp][1] patrol_vars.patrol_y = waypoints[n_wp][2] MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars) else -- Go back to first WP or reverse direction if cfg.out_and_back then patrol_vars.patrol_x = waypoints[n_wp-1][1] patrol_vars.patrol_y = waypoints[n_wp-1][2] -- We also need to reverse the waypoints right here, as this might not be the end of the move patrol_vars.patrol_reverse = not patrol_vars.patrol_reverse MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars) local tmp_wp = {} for j,wp2 in ipairs(waypoints) do tmp_wp[n_wp-j+1] = wp2 end waypoints = tmp_wp else patrol_vars.patrol_x = waypoints[1][1] patrol_vars.patrol_y = waypoints[1][2] MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars) end end else -- ... else move him on toward the next waypoint patrol_vars.patrol_x = waypoints[i+1][1] patrol_vars.patrol_y = waypoints[i+1][2] MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars) end end end -- If we're on the last waypoint on one_time_only is set, stop here if cfg.one_time_only and (patrol.x == waypoints[n_wp][1]) and (patrol.y == waypoints[n_wp][2]) then AH.checked_stopunit_moves(ai, patrol) else -- Otherwise move toward next WP local x, y = wesnoth.find_vacant_tile(patrol_vars.patrol_x, patrol_vars.patrol_y, patrol) local nh = AH.next_hop(patrol, x, y) if nh and ((nh[1] ~= patrol.x) or (nh[2] ~= patrol.y)) then AH.checked_move(ai, patrol, nh[1], nh[2]) else 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 local adjacent_enemy if cfg.one_time_only then adjacent_enemy = wesnoth.get_units{ x = waypoints[n_wp][1], y = waypoints[n_wp][2], { "filter_adjacent", { id = patrol.id } }, { "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } } }[1] end -- Otherwise attack adjacent enemy (if specified) if (not adjacent_enemy) then adjacent_enemy = wesnoth.get_units{ id = cfg.attack, { "filter_adjacent", { id = patrol.id } }, { "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } } }[1] end if adjacent_enemy then AH.checked_attack(ai, patrol, adjacent_enemy) end if (not patrol) or (not patrol.valid) then return end AH.checked_stopunit_all(ai, patrol) end return ca_patrol