150 lines
4.5 KiB
C++
150 lines
4.5 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "ultima/nuvie/core/nuvie_defs.h"
|
|
#include "ultima/nuvie/actors/actor.h"
|
|
#include "ultima/nuvie/pathfinder/path.h"
|
|
#include "ultima/nuvie/pathfinder/actor_path_finder.h"
|
|
|
|
namespace Ultima {
|
|
namespace Nuvie {
|
|
|
|
ActorPathFinder::ActorPathFinder(Actor *a, MapCoord g)
|
|
: PathFinder(a->get_location(), g), actor(a) {
|
|
|
|
}
|
|
|
|
ActorPathFinder::~ActorPathFinder() {
|
|
|
|
}
|
|
|
|
bool ActorPathFinder::get_next_move(MapCoord &step) {
|
|
MapCoord rel_step;
|
|
if (have_path()) {
|
|
step = search->get_first_step();
|
|
return check_loc(step);
|
|
}
|
|
|
|
get_closest_dir(rel_step);
|
|
if (check_dir(loc, rel_step)) {
|
|
step = loc.abs_coords(rel_step.sx, rel_step.sy);
|
|
return true;
|
|
}
|
|
if (search_towards_target(goal, rel_step)) {
|
|
step = loc.abs_coords(rel_step.sx, rel_step.sy);
|
|
return true;
|
|
}
|
|
|
|
if (find_path()) {
|
|
step = search->get_first_step();
|
|
return check_loc(step);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Get relative direction from Loc to Goal and place in Rel_step. */
|
|
void ActorPathFinder::get_closest_dir(MapCoord &rel_step) {
|
|
rel_step.sx = clamp(goal.x - loc.x, -1, 1);
|
|
rel_step.sy = clamp(goal.y - loc.y, -1, 1);
|
|
rel_step.z = loc.z;
|
|
|
|
uint16 dx = loc.xdistance(goal), dy = loc.ydistance(goal);
|
|
if (dx > dy) rel_step.sy = 0;
|
|
else if (dx < dy) rel_step.sx = 0;
|
|
}
|
|
|
|
bool ActorPathFinder::check_loc(const MapCoord &mapLoc) {
|
|
return actor->check_move(mapLoc.x, mapLoc.y, mapLoc.z);
|
|
}
|
|
|
|
/* Find a move from actor to g, starting with rel_step. Replace
|
|
* rel_step with the result. */
|
|
bool ActorPathFinder::search_towards_target(const MapCoord &g, MapCoord &rel_step) {
|
|
MapCoord mapLoc = actor->get_location();
|
|
MapCoord ccw_rel_step = rel_step, cw_rel_step = rel_step;
|
|
if (check_dir(mapLoc, rel_step)) // check original direction
|
|
return true;
|
|
bool try_ccw = check_dir_and_distance(mapLoc, g, ccw_rel_step, -1); // check adjacent directions
|
|
bool try_cw = check_dir_and_distance(mapLoc, g, cw_rel_step, 1);
|
|
if (!try_ccw) try_ccw = check_dir_and_distance(mapLoc, g, ccw_rel_step, -2); // check perpendicular directions
|
|
if (!try_cw) try_cw = check_dir_and_distance(mapLoc, g, cw_rel_step, 2);
|
|
if (!try_ccw && !try_cw)
|
|
return false;
|
|
rel_step = ccw_rel_step;
|
|
if (!try_ccw) rel_step = cw_rel_step;
|
|
else if (!try_cw) rel_step = ccw_rel_step;
|
|
else { // both valid, use closest
|
|
MapCoord ccw_step = mapLoc.abs_coords(ccw_rel_step.sx, ccw_rel_step.sy);
|
|
MapCoord cw_step = mapLoc.abs_coords(cw_rel_step.sx, cw_rel_step.sy);
|
|
MapCoord target(g);
|
|
if (cw_step.distance(target) < ccw_step.distance(target))
|
|
rel_step = cw_rel_step;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// check rotated dir, and copy results to rel_step if neighbor is passable
|
|
bool ActorPathFinder::check_dir_and_distance(const MapCoord &mapLoc, const MapCoord &g, MapCoord &rel_step, sint8 rotate) {
|
|
MapCoord rel_step_2 = rel_step;
|
|
if (check_dir(mapLoc, rel_step_2, rotate)) {
|
|
MapCoord neighbor = mapLoc.abs_coords(rel_step_2.sx, rel_step_2.sy);
|
|
if (neighbor.distance(g) <= mapLoc.distance(g)) {
|
|
rel_step = rel_step_2;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// new direction is copied to rel if true
|
|
bool ActorPathFinder::check_dir(const MapCoord &mapLoc, MapCoord &rel, sint8 rot) {
|
|
sint8 xdir = rel.sx, ydir = rel.sy;
|
|
get_adjacent_dir(xdir, ydir, rot);
|
|
MapCoord new_loc = MapCoord(mapLoc).abs_coords(xdir, ydir);
|
|
if (check_loc(new_loc)) {
|
|
rel.sx = xdir;
|
|
rel.sy = ydir;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ActorPathFinder::actor_moved() {
|
|
update_location();
|
|
// pop step
|
|
if (have_path())
|
|
search->remove_first_step();
|
|
}
|
|
|
|
void ActorPathFinder::set_actor(Actor *a) {
|
|
actor = a;
|
|
}
|
|
|
|
bool ActorPathFinder::update_location() {
|
|
if (!actor)
|
|
return false;
|
|
actor->get_location(&loc.x, &loc.y, &loc.z);
|
|
return true;
|
|
}
|
|
|
|
} // End of namespace Nuvie
|
|
} // End of namespace Ultima
|