Initial commit
This commit is contained in:
149
engines/ultima/nuvie/pathfinder/actor_path_finder.cpp
Normal file
149
engines/ultima/nuvie/pathfinder/actor_path_finder.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* 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
|
||||
60
engines/ultima/nuvie/pathfinder/actor_path_finder.h
Normal file
60
engines/ultima/nuvie/pathfinder/actor_path_finder.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_ACTOR_PATH_FINDER_H
|
||||
#define NUVIE_PATHFINDER_ACTOR_PATH_FINDER_H
|
||||
|
||||
#include "ultima/nuvie/pathfinder/path_finder.h"
|
||||
#include "ultima/nuvie/pathfinder/dir_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Actor;
|
||||
|
||||
class ActorPathFinder: public PathFinder, public DirFinder {
|
||||
protected:
|
||||
Actor *actor;
|
||||
|
||||
public:
|
||||
ActorPathFinder(Actor *a, MapCoord g);
|
||||
~ActorPathFinder() override;
|
||||
void set_actor(Actor *a);
|
||||
|
||||
virtual bool update_location(); /* get location from actor (use any time) */
|
||||
virtual void actor_moved(); /* the actor moved ON PATH...
|
||||
(use after get_next_move()) */
|
||||
|
||||
bool check_loc(const MapCoord &loc) override;
|
||||
|
||||
void get_closest_dir(MapCoord &rel_step); // relative dir loc->goal
|
||||
bool get_next_move(MapCoord &step) override;
|
||||
|
||||
protected:
|
||||
bool search_towards_target(const MapCoord &g, MapCoord &rel_step);
|
||||
bool check_dir(const MapCoord &loc, MapCoord &rel, sint8 rot = 0) override;
|
||||
bool check_dir_and_distance(const MapCoord &loc, const MapCoord &g, MapCoord &rel_step, sint8 rotate);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
218
engines/ultima/nuvie/pathfinder/astar_path.cpp
Normal file
218
engines/ultima/nuvie/pathfinder/astar_path.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/* 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/shared/std/containers.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/pathfinder/dir_finder.h"
|
||||
#include "ultima/nuvie/pathfinder/astar_path.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
AStarPath::AStarPath() : final_node(0) {
|
||||
}
|
||||
|
||||
void AStarPath::create_path() {
|
||||
astar_node *i = final_node; // iterator through steps, from back
|
||||
delete_path();
|
||||
Std::vector<astar_node *> reverse_list;
|
||||
while (i) {
|
||||
reverse_list.push_back(i);
|
||||
i = i->parent;
|
||||
}
|
||||
while (!reverse_list.empty()) {
|
||||
i = reverse_list.back();
|
||||
add_step(i->loc);
|
||||
reverse_list.pop_back();
|
||||
}
|
||||
set_path_size(step_count);
|
||||
}/* Get a new neighbor to nnode and score it, returning true if it's usable. */
|
||||
bool AStarPath::score_to_neighbor(sint8 dir, astar_node *nnode, astar_node *neighbor,
|
||||
sint32 &nnode_to_neighbor) {
|
||||
sint8 sx = -1, sy = -1;
|
||||
DirFinder::get_adjacent_dir(sx, sy, dir); // sx,sy = neighbor -1,-1 + dir
|
||||
// get neighbor of nnode towards sx,sy, and cost to that neighbor
|
||||
neighbor->loc = nnode->loc.abs_coords(sx, sy);
|
||||
nnode_to_neighbor = step_cost(nnode->loc, neighbor->loc);
|
||||
if (nnode_to_neighbor == -1) {
|
||||
delete neighbor; // this neighbor is blocked
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}/* Compare a node's score to the start node to already scored neighbors. */
|
||||
bool AStarPath::compare_neighbors(astar_node *nnode, astar_node *neighbor,
|
||||
sint32 nnode_to_neighbor, astar_node *in_open,
|
||||
astar_node *in_closed) {
|
||||
neighbor->to_start = nnode->to_start + nnode_to_neighbor;
|
||||
// ignore this neighbor if already checked and closer to start
|
||||
if ((in_open && in_open->to_start <= neighbor->to_start)
|
||||
|| (in_closed && in_closed->to_start <= neighbor->to_start)) {
|
||||
delete neighbor;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}/* Check all neighbors of a node (location) and save them to the "seen" list. */
|
||||
bool AStarPath::search_node_neighbors(astar_node *nnode, const MapCoord &goal,
|
||||
const uint32 max_score) {
|
||||
for (uint32 dir = 1; dir < 8; dir += 2) {
|
||||
astar_node *neighbor = new astar_node;
|
||||
sint32 nnode_to_neighbor = -1;
|
||||
if (!score_to_neighbor(dir, nnode, neighbor, nnode_to_neighbor))
|
||||
continue; // this neighbor is blocked
|
||||
astar_node *in_open = find_open_node(neighbor),
|
||||
*in_closed = find_closed_node(neighbor);
|
||||
if (!compare_neighbors(nnode, neighbor, nnode_to_neighbor, in_open, in_closed))
|
||||
continue;
|
||||
neighbor->parent = nnode;
|
||||
neighbor->to_goal = path_cost_est(neighbor->loc, goal);
|
||||
neighbor->score = neighbor->to_start + neighbor->to_goal;
|
||||
neighbor->len = nnode->len + 1;
|
||||
if (neighbor->score > max_score) {
|
||||
delete neighbor; // too far away
|
||||
continue;
|
||||
}
|
||||
// take neighbor out of closed list and put into open list
|
||||
if (in_closed)
|
||||
remove_closed_node(in_closed);
|
||||
if (!in_open)
|
||||
push_open_node(neighbor);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Do A* search of tiles to create a path from `start' to `goal'.
|
||||
* Don't search past nodes with a score over the max. score.
|
||||
* Create a partial path to low-score nodes with a distance-to-start over the
|
||||
* max_steps count, defined here. Actor may perform another search when needed.
|
||||
* Returns true if a path is created
|
||||
*/
|
||||
bool AStarPath::path_search(const MapCoord &start, const MapCoord &goal) {
|
||||
//DEBUG(0,LEVEL_DEBUGGING,"SEARCH: %d: %d,%d -> %d,%d\n",actor->get_actor_num(),start.x,start.y,goal.x,goal.y);
|
||||
astar_node *start_node = new astar_node;
|
||||
start_node->loc = start;
|
||||
start_node->to_start = 0;
|
||||
start_node->to_goal = path_cost_est(start, goal);
|
||||
start_node->score = start_node->to_start + start_node->to_goal;
|
||||
start_node->len = 0;
|
||||
push_open_node(start_node);
|
||||
const uint32 max_score = get_max_score(start_node->to_goal);
|
||||
const uint32 max_steps = 8 * 2 * 4; // walk up to four screen lengths before searching again
|
||||
while (!open_nodes.empty()) {
|
||||
astar_node *nnode = pop_open_node(); // next closest
|
||||
if (nnode->loc == goal || nnode->len >= max_steps) {
|
||||
if (nnode->loc != goal)
|
||||
DEBUG(0, LEVEL_DEBUGGING, "out of steps, making partial path (nnode->len=%d)\n", nnode->len);
|
||||
//DEBUG(0,LEVEL_DEBUGGING,"GOAL\n");
|
||||
final_node = nnode;
|
||||
create_path();
|
||||
delete_nodes();
|
||||
return true; // reached goal - success
|
||||
}
|
||||
// check cardinal neighbors (starting at top going clockwise)
|
||||
search_node_neighbors(nnode, goal, max_score);
|
||||
// node and neighbors checked, put into closed
|
||||
closed_nodes.push_back(nnode);
|
||||
}
|
||||
//DEBUG(0,LEVEL_DEBUGGING,"FAIL\n");
|
||||
delete_nodes();
|
||||
return false; // out of open nodes - failure
|
||||
}
|
||||
|
||||
/* Return the cost of moving one step from `c1' to `c2', which is always 1. This
|
||||
* isn't very helpful, so subclasses should provide their own function.
|
||||
* Returns -1 if c2 is blocked. */
|
||||
sint32 AStarPath::step_cost(const MapCoord &c1, const MapCoord &c2) {
|
||||
if (!pf->check_loc(c2.x, c2.y, c2.z)
|
||||
|| c2.distance(c1) > 1)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return an item in the list of closed nodes whose location matches `ncmp'.
|
||||
*/
|
||||
astar_node *AStarPath::find_closed_node(astar_node *ncmp) {
|
||||
for (astar_node *n : closed_nodes)
|
||||
if (n->loc == ncmp->loc)
|
||||
return n;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Return an item in the list of open nodes whose location matches `ncmp'.
|
||||
*/
|
||||
astar_node *AStarPath::find_open_node(astar_node *ncmp) {
|
||||
for (astar_node *n : open_nodes)
|
||||
if (n->loc == ncmp->loc)
|
||||
return n;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Add new node pointer to the list of open nodes (sorting by score).
|
||||
*/
|
||||
void AStarPath::push_open_node(astar_node *node) {
|
||||
if (open_nodes.empty()) {
|
||||
open_nodes.push_front(node);
|
||||
return;
|
||||
}
|
||||
|
||||
Std::list<astar_node *>::iterator n = open_nodes.begin();
|
||||
// get to end of list or to a node with equal or greater score
|
||||
while (n != open_nodes.end() && (*n++)->score < node->score);
|
||||
open_nodes.insert(n, node); // and add before that location
|
||||
}
|
||||
|
||||
/* Return pointer to the highest priority node from the list of open nodes, and
|
||||
* remove it.
|
||||
*/
|
||||
astar_node *AStarPath::pop_open_node() {
|
||||
astar_node *best = open_nodes.front();
|
||||
open_nodes.pop_front(); // remove it
|
||||
return best;
|
||||
}
|
||||
|
||||
/* Find item in the list of closed nodes whose location matched `ncmp', and
|
||||
* remove it from the list.
|
||||
*/
|
||||
void AStarPath::remove_closed_node(astar_node *ncmp) {
|
||||
Std::list<astar_node *>::iterator n;
|
||||
for (n = closed_nodes.begin(); n != closed_nodes.end(); n++)
|
||||
if ((*n)->loc == ncmp->loc) {
|
||||
closed_nodes.erase(n);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete nodes dereferenced from pointers in the lists.
|
||||
*/
|
||||
void AStarPath::delete_nodes() {
|
||||
while (!open_nodes.empty()) {
|
||||
astar_node *delnode = open_nodes.front();
|
||||
open_nodes.pop_front();
|
||||
delete delnode;
|
||||
}
|
||||
while (!closed_nodes.empty()) {
|
||||
astar_node *delnode = closed_nodes.front();
|
||||
closed_nodes.pop_front();
|
||||
delete delnode;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
82
engines/ultima/nuvie/pathfinder/astar_path.h
Normal file
82
engines/ultima/nuvie/pathfinder/astar_path.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_ASTAR_PATH_H
|
||||
#define NUVIE_PATHFINDER_ASTAR_PATH_H
|
||||
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/pathfinder/path.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
typedef struct astar_node_s {
|
||||
MapCoord loc; // location
|
||||
uint32 to_start; // costs from this node to start and to goal
|
||||
uint32 to_goal;
|
||||
uint32 score; // node score
|
||||
uint32 len; // number of nodes before this one, regardless of score
|
||||
struct astar_node_s *parent;
|
||||
astar_node_s() : loc(0, 0, 0), to_start(0), to_goal(0), score(0), len(0),
|
||||
parent(nullptr) { }
|
||||
} astar_node;
|
||||
/* Provides A* search and cost methods for PathFinder and subclasses.
|
||||
*/class AStarPath: public Path {
|
||||
protected:
|
||||
Std::list<astar_node *> open_nodes, closed_nodes; // nodes seen
|
||||
astar_node *final_node; // last node in path search, used by create_path()
|
||||
/* Forms a usable path from results of a search. */
|
||||
void create_path();
|
||||
/* Search routine. */
|
||||
bool search_node_neighbors(astar_node *nnode, const MapCoord &goal, const uint32 max_score);
|
||||
bool compare_neighbors(astar_node *nnode, astar_node *neighbor,
|
||||
sint32 nnode_to_neighbor, astar_node *in_open,
|
||||
astar_node *in_closed);
|
||||
bool score_to_neighbor(sint8 dir, astar_node *nnode, astar_node *neighbor,
|
||||
sint32 &nnode_to_neighbor);
|
||||
public:
|
||||
AStarPath();
|
||||
~AStarPath() override { }
|
||||
bool path_search(const MapCoord &start, const MapCoord &goal) override;
|
||||
uint32 path_cost_est(const MapCoord &s, const MapCoord &g) override {
|
||||
return Path::path_cost_est(s, g);
|
||||
}
|
||||
uint32 get_max_score(uint32 cost) override {
|
||||
return Path::get_max_score(cost);
|
||||
}
|
||||
uint32 path_cost_est(const astar_node &n1, const astar_node &n2) {
|
||||
return Path::path_cost_est(n1.loc, n2.loc);
|
||||
}
|
||||
sint32 step_cost(const MapCoord &c1, const MapCoord &c2) override;
|
||||
protected:
|
||||
/* FIXME: These node functions can be replaced with a priority_queue and a list. */
|
||||
astar_node *find_open_node(astar_node *ncmp);
|
||||
void push_open_node(astar_node *node);
|
||||
astar_node *pop_open_node();
|
||||
astar_node *find_closed_node(astar_node *ncmp);
|
||||
void remove_closed_node(astar_node *ncmp);
|
||||
void delete_nodes();
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
95
engines/ultima/nuvie/pathfinder/combat_path_finder.cpp
Normal file
95
engines/ultima/nuvie/pathfinder/combat_path_finder.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/* 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/actors/actor.h"
|
||||
#include "ultima/nuvie/pathfinder/combat_path_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
CombatPathFinder::CombatPathFinder(Actor *a)
|
||||
: ActorPathFinder(a, a->get_location()), target_mode(PATHFINDER_NONE),
|
||||
max_dist(0), target(nullptr) {
|
||||
}
|
||||
|
||||
/* Without a mode set, CombatPathFinder is identical to ActorPathFinder. */
|
||||
CombatPathFinder::CombatPathFinder(Actor *a, Actor *t)
|
||||
: ActorPathFinder(a, t->get_location()), target_mode(PATHFINDER_CHASE),
|
||||
target(t), max_dist(0) {
|
||||
}
|
||||
|
||||
CombatPathFinder::~CombatPathFinder() {
|
||||
|
||||
}
|
||||
|
||||
bool CombatPathFinder::reached_goal() {
|
||||
if (target_mode == PATHFINDER_CHASE)
|
||||
return (loc.distance(goal) <= 1);
|
||||
if (target_mode == PATHFINDER_FLEE)
|
||||
return (max_dist != 0 && loc.distance(goal) > max_dist);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CombatPathFinder::set_flee_mode(Actor *targetActor) {
|
||||
target_mode = PATHFINDER_FLEE;
|
||||
target = targetActor;
|
||||
update_location();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CombatPathFinder::set_chase_mode(Actor *targetActor) {
|
||||
target_mode = PATHFINDER_CHASE;
|
||||
target = targetActor;
|
||||
update_location();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CombatPathFinder::set_mode(CombatPathFinderMode mode, Actor *targetActor) {
|
||||
target_mode = mode;
|
||||
target = targetActor;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CombatPathFinder::update_location() {
|
||||
ActorPathFinder::update_location();
|
||||
set_goal(target->get_location());
|
||||
if (max_dist != 0 && loc.distance(goal) > max_dist)
|
||||
target_mode = PATHFINDER_NONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CombatPathFinder::get_next_move(MapCoord &step) {
|
||||
if (target_mode == PATHFINDER_CHASE)
|
||||
return ActorPathFinder::get_next_move(step);
|
||||
if (target_mode == PATHFINDER_FLEE) {
|
||||
get_closest_dir(step);
|
||||
step.sx = -step.sx;
|
||||
step.sy = -step.sy;
|
||||
if (check_dir(loc, step)) {
|
||||
step = loc.abs_coords(step.sx, step.sy);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
63
engines/ultima/nuvie/pathfinder/combat_path_finder.h
Normal file
63
engines/ultima/nuvie/pathfinder/combat_path_finder.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_COMBAT_PATH_FINDER_H
|
||||
#define NUVIE_PATHFINDER_COMBAT_PATH_FINDER_H
|
||||
|
||||
#include "ultima/nuvie/pathfinder/actor_path_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
typedef enum {
|
||||
PATHFINDER_NONE,
|
||||
PATHFINDER_CHASE,
|
||||
PATHFINDER_FLEE
|
||||
} CombatPathFinderMode;
|
||||
|
||||
class CombatPathFinder: public ActorPathFinder {
|
||||
protected:
|
||||
Actor *target;
|
||||
CombatPathFinderMode target_mode;
|
||||
|
||||
bool update_location() override;
|
||||
|
||||
uint8 max_dist;
|
||||
|
||||
public:
|
||||
CombatPathFinder(Actor *a);
|
||||
CombatPathFinder(Actor *a, Actor *t);
|
||||
~CombatPathFinder() override;
|
||||
bool set_flee_mode(Actor *actor);
|
||||
bool set_chase_mode(Actor *actor);
|
||||
bool set_mode(CombatPathFinderMode mode, Actor *actor);
|
||||
void set_distance(uint8 dist) {
|
||||
max_dist = dist;
|
||||
}
|
||||
|
||||
bool get_next_move(MapCoord &step) override;
|
||||
bool reached_goal() override;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
115
engines/ultima/nuvie/pathfinder/dir_finder.cpp
Normal file
115
engines/ultima/nuvie/pathfinder/dir_finder.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/* 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/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/pathfinder/dir_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/** STATIC FUNCTIONS **/
|
||||
|
||||
/* From a normalized direction xdir,ydir as base, get the normalized direction
|
||||
* towards one of the seven nearby tiles to the left or right of base.
|
||||
* For rotate: -n = left n tiles, n = right n tiles
|
||||
*/
|
||||
void DirFinder::get_adjacent_dir(sint8 &xdir, sint8 &ydir, sint8 rotate) {
|
||||
struct {
|
||||
sint8 x, y;
|
||||
} neighbors[8] = { { -1, -1}, { +0, -1}, { +1, -1},
|
||||
{ +1, +0},/*ACTOR*/ { +1, +1},
|
||||
{ +0, +1}, { -1, +1}, { -1, +0}
|
||||
};
|
||||
|
||||
for (uint32 start = 0; start < 8; start++)
|
||||
if (neighbors[start].x == xdir && neighbors[start].y == ydir) {
|
||||
sint32 dest = start + rotate;
|
||||
while (dest < 0 || dest > 7)
|
||||
dest += (dest < 0) ? 8 : -8;
|
||||
xdir = neighbors[dest].x;
|
||||
ydir = neighbors[dest].y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NuvieDir DirFinder::get_nuvie_dir(sint16 xrel, sint16 yrel) {
|
||||
NuvieDir direction = NUVIE_DIR_N; // default
|
||||
|
||||
if (xrel == 0 && yrel == 0) // nowhere
|
||||
return direction;
|
||||
if (xrel == 0) // up or down
|
||||
direction = (yrel < 0) ? NUVIE_DIR_N : NUVIE_DIR_S;
|
||||
else if (yrel == 0) // left or right
|
||||
direction = (xrel < 0) ? NUVIE_DIR_W : NUVIE_DIR_E;
|
||||
else if (xrel < 0 && yrel < 0)
|
||||
direction = NUVIE_DIR_NW;
|
||||
else if (xrel > 0 && yrel < 0)
|
||||
direction = NUVIE_DIR_NE;
|
||||
else if (xrel < 0 && yrel > 0)
|
||||
direction = NUVIE_DIR_SW;
|
||||
else if (xrel > 0 && yrel > 0)
|
||||
direction = NUVIE_DIR_SE;
|
||||
return direction;
|
||||
}
|
||||
|
||||
NuvieDir DirFinder::get_nuvie_dir(uint16 sx, uint16 sy, uint16 tx, uint16 ty, uint8 z) {
|
||||
return DirFinder::get_nuvie_dir(get_wrapped_rel_dir(tx, sx, z), get_wrapped_rel_dir(ty, sy, z));
|
||||
}
|
||||
|
||||
// oxdir = original xdir, txdir = to xdir
|
||||
sint8 DirFinder::get_turn_towards_dir(sint16 oxdir, sint16 oydir, sint8 txdir, sint8 tydir) {
|
||||
oxdir = clamp(oxdir, -1, 1);
|
||||
oydir = clamp(oydir, -1, 1);
|
||||
txdir = clamp(txdir, -1, 1);
|
||||
tydir = clamp(tydir, -1, 1);
|
||||
struct {
|
||||
sint8 x, y;
|
||||
} dirs[8] = {{ -1, -1}, { +0, -1}, { +1, -1}, { +1, +0}, { +1, +1}, { +0, +1}, { -1, +1}, { -1, +0}};
|
||||
uint8 t = 0, o = 0;
|
||||
for (uint8 d = 0; d < 8; d++) {
|
||||
if (dirs[d].x == oxdir && dirs[d].y == oydir)
|
||||
o = d;
|
||||
if (dirs[d].x == txdir && dirs[d].y == tydir)
|
||||
t = d;
|
||||
}
|
||||
sint8 turn = t - o;
|
||||
if (turn > 4)
|
||||
turn = -(8 - turn);
|
||||
return clamp(turn, -1, 1);
|
||||
}
|
||||
|
||||
// xdir,ydir = normal direction from->to (simple method)
|
||||
void DirFinder::get_normalized_dir(const MapCoord &from, const MapCoord &to, sint8 &xdir, sint8 &ydir) {
|
||||
xdir = clamp(to.x - from.x, -1, 1);
|
||||
ydir = clamp(to.y - from.y, -1, 1);
|
||||
|
||||
/* uint16 dx = from.xdistance(to), dy = from.ydistance(to);
|
||||
if(dx > 0 && dy > 0)
|
||||
{
|
||||
if(dx > dy) xdir = 0;
|
||||
else if(dx < dy) ydir = 0;
|
||||
}*/
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
44
engines/ultima/nuvie/pathfinder/dir_finder.h
Normal file
44
engines/ultima/nuvie/pathfinder/dir_finder.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_DIR_FINDER_H
|
||||
#define NUVIE_PATHFINDER_DIR_FINDER_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class MapCoord;
|
||||
|
||||
class DirFinder {
|
||||
public:
|
||||
DirFinder() { }
|
||||
|
||||
static void get_adjacent_dir(sint8 &xdir, sint8 &ydir, sint8 rotate);
|
||||
static NuvieDir get_nuvie_dir(sint16 xrel, sint16 yrel);
|
||||
static NuvieDir get_nuvie_dir(uint16 sx, uint16 sy, uint16 tx, uint16 ty, uint8 z);
|
||||
static sint8 get_turn_towards_dir(sint16 oxdir, sint16 oydir, sint8 txdir, sint8 tydir);
|
||||
static void get_normalized_dir(const MapCoord &from, const MapCoord &to, sint8 &xdir, sint8 &ydir);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
474
engines/ultima/nuvie/pathfinder/party_path_finder.cpp
Normal file
474
engines/ultima/nuvie/pathfinder/party_path_finder.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
/* 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/shared/std/containers.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/pathfinder/seek_path.h"
|
||||
#include "ultima/nuvie/pathfinder/actor_path_finder.h"
|
||||
#include "ultima/nuvie/pathfinder/party_path_finder.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/u6_objects.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::vector;
|
||||
|
||||
PartyPathFinder::PartyPathFinder(Party *p) : party(p) {
|
||||
assert(p);
|
||||
}
|
||||
|
||||
PartyPathFinder::~PartyPathFinder() {
|
||||
|
||||
}
|
||||
|
||||
/* True if a member's target and leader are in roughly the same direction. */
|
||||
bool PartyPathFinder::is_behind_target(uint32 member_num) {
|
||||
if (get_leader() < 0)
|
||||
return false;
|
||||
uint8 ldir = get_member(get_leader()).actor->get_direction(); // leader direciton
|
||||
MapCoord from = party->get_location(member_num);
|
||||
MapCoord to = party->get_formation_coords(member_num); // target
|
||||
sint8 to_x = to.x - from.x, to_y = to.y - from.y;
|
||||
return (((ldir == NUVIE_DIR_N && to_y < 0)
|
||||
|| (ldir == NUVIE_DIR_S && to_y > 0)
|
||||
|| (ldir == NUVIE_DIR_E && to_x > 0)
|
||||
|| (ldir == NUVIE_DIR_W && to_x < 0)));
|
||||
}
|
||||
|
||||
bool PartyPathFinder::is_at_target(uint32 p) {
|
||||
MapCoord target_loc = party->get_formation_coords(p);
|
||||
MapCoord member_loc = party->get_location(p);
|
||||
return (target_loc == member_loc);
|
||||
}
|
||||
|
||||
/* Is anyone in front of `member_num' adjacent to `from'?
|
||||
* (is_contiguous(member, member_loc) == "is member adjacent to another member
|
||||
* whose following position is lower-numbered?") */
|
||||
bool PartyPathFinder::is_contiguous(uint32 member_num, const MapCoord &from) {
|
||||
const bool isU6 = Game::get_game()->get_game_type() == NUVIE_GAME_U6;
|
||||
const Actor *inActor = get_member(member_num).actor;
|
||||
const bool isMouse = isU6 && inActor->get_obj_n() == OBJ_U6_MOUSE;
|
||||
bool retVal = false;
|
||||
|
||||
for (uint32 q = 0; q < member_num; q++) { // check lower-numbered members
|
||||
const Actor *actor = get_member(q).actor;
|
||||
const bool otherIsMouse = isU6 && actor->get_obj_n() == OBJ_U6_MOUSE;
|
||||
if (actor && actor->is_immobile() == true) continue;
|
||||
|
||||
MapCoord loc = party->get_location(q);
|
||||
if (!isMouse && !otherIsMouse && from.distance(loc) == 0)
|
||||
return false; // Do not allow stacking (except for Sherry)
|
||||
if (from.distance(loc) <= 1) {
|
||||
retVal = true;
|
||||
continue; // Stay in the loop to check if other party members are on the same tile
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/* Is member adjacent to another member whose following position is lower-numbered? */
|
||||
bool PartyPathFinder::is_contiguous(uint32 member_num) {
|
||||
MapCoord member_loc = party->get_location(member_num);
|
||||
return (is_contiguous(member_num, member_loc));
|
||||
}
|
||||
|
||||
/* Returns in rel_x and rel_y the direction a character needs to move to get
|
||||
* closer to their target. */
|
||||
void PartyPathFinder::get_target_dir(uint32 p, sint8 &rel_x, sint8 &rel_y) {
|
||||
//MapCoord leader_loc = party->get_leader_location();
|
||||
MapCoord target_loc = party->get_formation_coords(p);
|
||||
MapCoord member_loc = party->get_location(p);
|
||||
|
||||
rel_x = get_wrapped_rel_dir(target_loc.x, member_loc.x, target_loc.z);
|
||||
rel_y = get_wrapped_rel_dir(target_loc.y, member_loc.y, target_loc.z);
|
||||
}
|
||||
|
||||
/* Returns in vec_x and vec_y the last direction the leader moved in. It's
|
||||
* derived from his facing direction so it's not as precise as get_last_move(). */
|
||||
void PartyPathFinder::get_forward_dir(sint8 &vec_x, sint8 &vec_y) {
|
||||
// get_last_move(vec_x, vec_y);
|
||||
vec_x = 0;
|
||||
vec_y = 0;
|
||||
NuvieDir dir = (get_leader() >= 0) ? get_member(get_leader()).actor->get_direction() : NUVIE_DIR_N;
|
||||
if (dir == NUVIE_DIR_N) {
|
||||
vec_x = 0;
|
||||
vec_y = -1;
|
||||
} else if (dir == NUVIE_DIR_S) {
|
||||
vec_x = 0;
|
||||
vec_y = 1;
|
||||
} else if (dir == NUVIE_DIR_E) {
|
||||
vec_x = 1;
|
||||
vec_y = 0;
|
||||
} else if (dir == NUVIE_DIR_W) {
|
||||
vec_x = -1;
|
||||
vec_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns in vec_x and vec_y the last direction the leader moved in. */
|
||||
void PartyPathFinder::get_last_move(sint8 &vec_x, sint8 &vec_y) {
|
||||
MapCoord leader_loc = party->get_leader_location();
|
||||
|
||||
vec_x = get_wrapped_rel_dir(leader_loc.x, party->prev_leader_x, leader_loc.z);
|
||||
vec_y = get_wrapped_rel_dir(leader_loc.y, party->prev_leader_y, leader_loc.z);
|
||||
}
|
||||
|
||||
/* Returns true if the leader moved before the last call to follow(). */
|
||||
bool PartyPathFinder::leader_moved() {
|
||||
MapCoord leader_loc = party->get_leader_location();
|
||||
return ((leader_loc.x - party->prev_leader_x)
|
||||
|| (leader_loc.y - party->prev_leader_y));
|
||||
}
|
||||
|
||||
/* Returns true if the leader moved far enough away from follower to pull him. */
|
||||
bool PartyPathFinder::leader_moved_away(uint32 p) {
|
||||
MapCoord leader_loc = party->get_leader_location();
|
||||
MapCoord target_loc = party->get_formation_coords(p);
|
||||
MapCoord member_loc = party->get_location(p);
|
||||
return (leader_loc.distance(member_loc) > leader_loc.distance(target_loc));
|
||||
}
|
||||
|
||||
/* Compares leader position with last known position. */
|
||||
bool PartyPathFinder::leader_moved_diagonally() {
|
||||
MapCoord leader_loc = party->get_leader_location();
|
||||
return (party->prev_leader_x != leader_loc.x
|
||||
&& party->prev_leader_y != leader_loc.y);
|
||||
}
|
||||
|
||||
bool PartyPathFinder::follow_passA(uint32 p) {
|
||||
bool contiguous = is_contiguous(p);
|
||||
bool try_again = false;
|
||||
sint8 vec_x = 0, vec_y = 0; // previous direction of party leader's movement
|
||||
sint8 rel_x = 0, rel_y = 0; // direction to target
|
||||
|
||||
get_target_dir(p, rel_x, rel_y);
|
||||
if (contiguous) {
|
||||
if (is_at_target(p))
|
||||
return true;
|
||||
|
||||
// Move towards target, and see if we get to try again.
|
||||
// If you always get an extra move, distant followers move towards the
|
||||
// leader instead of forward. Only get an extra move if we stopped, or
|
||||
// blocked square is in the same direction the leader moved.
|
||||
get_last_move(vec_x, vec_y);
|
||||
if (!leader_moved() && !try_moving_to_target(p))
|
||||
try_again = true;
|
||||
else if (leader_moved() && leader_moved_away(p) && !try_moving_to_target(p))
|
||||
if (is_behind_target(p))
|
||||
try_again = true;
|
||||
} else {
|
||||
if (!move_member(p, rel_x, rel_y))
|
||||
try_again = true;
|
||||
}
|
||||
|
||||
// get another move chance here
|
||||
if (try_again) {
|
||||
MapCoord target_loc = party->get_formation_coords(p);
|
||||
if (!try_all_directions(p, target_loc)) { // turn towards target
|
||||
if (contiguous)
|
||||
return false;
|
||||
if (!move_member(p, rel_x, rel_y, true)) // allow non-contiguous moves
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PartyPathFinder::follow_passB(uint32 p) {
|
||||
if (is_contiguous(p)) {
|
||||
if (is_at_target(p))
|
||||
return true;
|
||||
|
||||
if (leader_moved_away(p)) { // only move if leader walked away from us
|
||||
// move forward (direction leader moved) if target is ahead of us
|
||||
if (leader_moved() && is_behind_target(p))
|
||||
try_moving_forward(p);
|
||||
if (leader_moved_diagonally()) // extra move
|
||||
try_moving_sideways(p);
|
||||
}
|
||||
} else {
|
||||
if (!try_moving_forward(p)) {
|
||||
sint8 vec_x, vec_y;
|
||||
get_forward_dir(vec_x, vec_y);
|
||||
MapCoord member_loc = party->get_location(p);
|
||||
MapCoord forward_loc = member_loc.abs_coords(vec_x, vec_y);
|
||||
try_all_directions(p, forward_loc); // turn towards forward
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_contiguous(p)) // critical; still not contiguous
|
||||
if (!try_moving_to_leader(p, true))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Follower moves up, down, left, or right. (to intercept a target who moved
|
||||
* diagonally) Returns true if the character moved. */
|
||||
bool PartyPathFinder::try_moving_sideways(uint32 p) {
|
||||
// prefer movement towards target direction
|
||||
sint8 rel_x, rel_y;
|
||||
get_target_dir(p, rel_x, rel_y);
|
||||
if (!move_member(p, rel_x, 0)) // try two directions
|
||||
if (!move_member(p, 0, rel_y))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Follower tries moving towards the leader, and then towards each adjacent
|
||||
* direction if necessary. Returns true if the character moved. */
|
||||
bool PartyPathFinder::try_moving_to_leader(uint32 p, bool ignore_position) {
|
||||
// move towards leader (allow non-contiguous moves)
|
||||
sint8 rel_x, rel_y;
|
||||
get_target_dir(p, rel_x, rel_y);
|
||||
if (move_member(p, rel_x, rel_y, ignore_position, true, false))
|
||||
return true;
|
||||
DirFinder::get_adjacent_dir(rel_x, rel_y, -1);
|
||||
if (move_member(p, rel_x, rel_y, ignore_position, true, false))
|
||||
return true;
|
||||
DirFinder::get_adjacent_dir(rel_x, rel_y, 2);
|
||||
if (move_member(p, rel_x, rel_y, ignore_position, true, false))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try moving in a forward direction. (direction leader moved) */
|
||||
bool PartyPathFinder::try_moving_forward(uint32 p) {
|
||||
sint8 vec_x = 0, vec_y = 0;
|
||||
get_forward_dir(vec_x, vec_y);
|
||||
if (!move_member(p, vec_x, vec_y))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Follower moves in the direction of their target, trying both adjacent
|
||||
* directions if necessary.
|
||||
* Returns true if character moved, or doesn't need to. Returns false if he stil
|
||||
* needs to try to move. */
|
||||
bool PartyPathFinder::try_moving_to_target(uint32 p, bool avoid_damage_tiles) {
|
||||
sint8 rel_x, rel_y;
|
||||
get_target_dir(p, rel_x, rel_y);
|
||||
if (!move_member(p, rel_x, rel_y, false, false)) { // don't ignore position, don't bump other followers
|
||||
sint8 leader = get_leader();
|
||||
if (leader >= 0) {
|
||||
// try both adjacent directions, first the one which is
|
||||
// perpendicular to the leader's facing direction
|
||||
uint8 ldir = get_member(leader).actor->get_direction();
|
||||
sint8 dx = (ldir == NUVIE_DIR_W) ? -1 : (ldir == NUVIE_DIR_E) ? 1 : 0;
|
||||
sint8 dy = (ldir == NUVIE_DIR_N) ? -1 : (ldir == NUVIE_DIR_S) ? 1 : 0;
|
||||
sint8 relx2 = rel_x, rely2 = rel_y; // adjacent directions, counter-clockwise
|
||||
sint8 relx3 = rel_x, rely3 = rel_y; // clockwise
|
||||
DirFinder::get_adjacent_dir(relx2, rely2, -1);
|
||||
DirFinder::get_adjacent_dir(relx3, rely3, 1);
|
||||
if (!(abs(relx2) == abs(dy) && abs(rely2) == abs(dx))) {
|
||||
// first isn't perpendicular; swap directions
|
||||
DirFinder::get_adjacent_dir(relx2, rely2, 2); // becomes clockwise
|
||||
DirFinder::get_adjacent_dir(relx3, rely3, -2); // counter-clockwise
|
||||
}
|
||||
if (!move_member(p, relx2, rely2))
|
||||
if (!move_member(p, relx3, rely3)) {
|
||||
// this makes Iolo (follower 3) try to move around other party
|
||||
// members when leader changes direction and they
|
||||
// block him
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Follower p will try moving in every direction, first towards the leader, and
|
||||
* then in a circular order starting with the direction closest to target_loc.
|
||||
* Returns true if character moved. */
|
||||
bool PartyPathFinder::try_all_directions(uint32 p, MapCoord target_loc) {
|
||||
MapCoord leader_loc = party->get_leader_location();
|
||||
MapCoord member_loc = party->get_location(p);
|
||||
sint8 to_leader_x = get_wrapped_rel_dir(leader_loc.x, member_loc.x, leader_loc.z);
|
||||
sint8 to_leader_y = get_wrapped_rel_dir(leader_loc.y, member_loc.y, leader_loc.z);
|
||||
// rotate direction, towards target
|
||||
sint8 rot = DirFinder::get_turn_towards_dir(to_leader_x, to_leader_y,
|
||||
sint8(target_loc.x - member_loc.x),
|
||||
sint8(target_loc.y - member_loc.y));
|
||||
if (rot == 0) rot = 1; // default clockwise
|
||||
|
||||
// check all directions, first only those adjacent to the real target
|
||||
MapCoord real_target = party->get_formation_coords(p);
|
||||
for (uint32 dir = 0; dir < 8; dir++) {
|
||||
MapCoord dest = member_loc.abs_coords(to_leader_x, to_leader_y);
|
||||
if (dest.distance(real_target) == 1 && move_member(p, to_leader_x, to_leader_y))
|
||||
return true;
|
||||
DirFinder::get_adjacent_dir(to_leader_x, to_leader_y, rot);
|
||||
}
|
||||
// this time, don't allow any moves that take us further from the leader
|
||||
// than our target position is (unless we're already that far away)
|
||||
for (uint32 dir = 0; dir < 8; dir++) {
|
||||
MapCoord dest = member_loc.abs_coords(to_leader_x, to_leader_y);
|
||||
if ((dest.distance(leader_loc) <= real_target.distance(leader_loc)
|
||||
|| dest.distance(leader_loc) <= member_loc.distance(leader_loc))
|
||||
&& move_member(p, to_leader_x, to_leader_y))
|
||||
return true;
|
||||
DirFinder::get_adjacent_dir(to_leader_x, to_leader_y, rot);
|
||||
}
|
||||
// now try any move possible (don't bother if already contiguous)
|
||||
if (!is_contiguous(p))
|
||||
for (uint32 dir = 0; dir < 8; dir++) {
|
||||
//MapCoord dest = member_loc.abs_coords(to_leader_x, to_leader_y);
|
||||
if (move_member(p, to_leader_x, to_leader_y))
|
||||
return true;
|
||||
DirFinder::get_adjacent_dir(to_leader_x, to_leader_y, rot);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns a list(vector) of all locations adjacent to 'center', sorted by their
|
||||
* distance to 'target'. (near to far)
|
||||
*/
|
||||
vector<MapCoord>
|
||||
PartyPathFinder::get_neighbor_tiles(const MapCoord ¢er, const MapCoord &target) {
|
||||
sint8 rel_x = get_wrapped_rel_dir(target.x, center.x, target.z);
|
||||
sint8 rel_y = get_wrapped_rel_dir(target.y, center.y, target.z);
|
||||
vector<MapCoord> neighbors;
|
||||
for (uint32 dir = 0; dir < 8; dir++) {
|
||||
MapCoord this_square = center.abs_coords(rel_x, rel_y); // initial square in first iteration
|
||||
vector<MapCoord>::iterator i = neighbors.begin();
|
||||
uint32 sorted = 0;
|
||||
for (; sorted < neighbors.size(); sorted++, i++) {
|
||||
MapCoord check_square = neighbors[sorted];
|
||||
if (target.distance(this_square) < target.distance(check_square)
|
||||
&& !party->is_anyone_at(check_square)) { // exclude squares with any other party member from being at the front of the list
|
||||
neighbors.insert(i, this_square);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sorted == neighbors.size()) // place before end of the list
|
||||
neighbors.insert(neighbors.end(), this_square);
|
||||
DirFinder::get_adjacent_dir(rel_x, rel_y, 1);
|
||||
}
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
/* This is like Actor::push_actor and exchanges positions with an actor, or
|
||||
* moves them to a neighboring location. This method doesn't move the original
|
||||
* "bumper" actor. When exchanging positions, the bumped character loses their
|
||||
* turn.
|
||||
* Characters can only be "bumped" within one move of their ORIGINAL square. It
|
||||
* is illegal to bump someone into a CRITICAL or IMPOSSIBLE state.
|
||||
* Returns true if the party member moved successfully.
|
||||
*/
|
||||
bool PartyPathFinder::bump_member(uint32 bumped_member_num, uint32 member_num) {
|
||||
if (member_num >= party->get_party_size())
|
||||
return false;
|
||||
Actor *actor = get_member(bumped_member_num).actor;
|
||||
if (actor->is_immobile())
|
||||
return false;
|
||||
Actor *push_actor = get_member(member_num).actor;
|
||||
MapCoord bump_from = party->get_location(bumped_member_num);
|
||||
MapCoord bump_target = party->get_formation_coords(bumped_member_num); // initial direction
|
||||
MapCoord member_loc = party->get_location(member_num);
|
||||
sint8 to_member_x = get_wrapped_rel_dir(member_loc.x, bump_from.x, member_loc.z); // to push_actor
|
||||
sint8 to_member_y = get_wrapped_rel_dir(member_loc.y, bump_from.y, member_loc.z);
|
||||
|
||||
// sort neighboring squares by distance to target (closest first)
|
||||
vector<MapCoord> neighbors;
|
||||
if (bump_target != bump_from)
|
||||
neighbors = get_neighbor_tiles(bump_from, bump_target);
|
||||
else { // sort by distance to leader
|
||||
MapCoord leader_loc = party->get_leader_location();
|
||||
neighbors = get_neighbor_tiles(bump_from, leader_loc);
|
||||
}
|
||||
|
||||
for (uint32 dir = 0; dir < 8; dir++) {
|
||||
sint8 rel_x = get_wrapped_rel_dir(neighbors[dir].x, bump_from.x, bump_from.z);
|
||||
sint8 rel_y = get_wrapped_rel_dir(neighbors[dir].y, bump_from.y, bump_from.z);
|
||||
// Since this direction is blocked, it will only be at the end of the
|
||||
// sorted list.
|
||||
if (rel_x == to_member_x && rel_y == to_member_y) {
|
||||
// Use special push() that ignores actors, and reduces moves left.
|
||||
actor->push(push_actor, ACTOR_PUSH_HERE);
|
||||
return true;
|
||||
} else if (move_member(bumped_member_num, rel_x, rel_y)) {
|
||||
// Reduce moves left so actor can't move (or get pushed) again.
|
||||
actor->set_moves_left(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* "Try a move", only if target is contiguous. */
|
||||
bool PartyPathFinder::move_member(uint32 member_num, sint16 relx, sint16 rely, bool ignore_position, bool can_bump, bool avoid_danger_tiles) {
|
||||
/**Do not call with relx and rely set to 0.**/
|
||||
if (relx == 0 && rely == 0)
|
||||
return true;
|
||||
MapCoord member_loc = party->get_location(member_num);
|
||||
MapCoord target(member_loc);
|
||||
target = member_loc.abs_coords(relx, rely);
|
||||
Actor *actor = get_member(member_num).actor;
|
||||
ActorMoveFlags flags = ACTOR_IGNORE_MOVES | ACTOR_IGNORE_PARTY_MEMBERS;
|
||||
if (!avoid_danger_tiles)
|
||||
flags = flags | ACTOR_IGNORE_DANGER;
|
||||
|
||||
if (is_contiguous(member_num, target) || ignore_position) {
|
||||
if (actor->move(target.x, target.y, target.z, flags)) {
|
||||
actor->set_direction(relx, rely);
|
||||
return true;
|
||||
}
|
||||
// Block commented out: It seems that, at least in Ultima 6, party members do not
|
||||
// bump each other, so we now ignore them via ACTOR_IGNORE_PARTY_MEMBERS.
|
||||
// Instead, is_contiguous() prevents them from stacking.
|
||||
// if (actor->get_error()->err == ACTOR_BLOCKED_BY_ACTOR) {
|
||||
// const Actor *blocking_actor = actor->get_error()->blocking_actor;
|
||||
// sint8 blocking_member_num = -1;
|
||||
// if (blocking_actor)
|
||||
// blocking_member_num = party->get_member_num(blocking_actor);
|
||||
// if (blocking_member_num < sint32(member_num))
|
||||
// return false; // blocked by an actor not in the party
|
||||
// if (bump_member(uint32(blocking_member_num), member_num)
|
||||
// && actor->move(target.x, target.y, target.z, flags | ACTOR_IGNORE_MOVES)) {
|
||||
// actor->set_direction(relx, rely);
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return false; // target is not contiguous, or move is blocked
|
||||
}
|
||||
|
||||
/* Use a better pathfinder to search for the leader. */
|
||||
void PartyPathFinder::seek_leader(uint32 p) {
|
||||
Actor *actor = get_member(p).actor;
|
||||
MapCoord leader_loc = party->get_leader_location();
|
||||
ActorPathFinder *df = actor->get_pathfinder();
|
||||
if (!df) {
|
||||
df = new ActorPathFinder(actor, leader_loc);
|
||||
actor->set_pathfinder(df, new SeekPath);
|
||||
} else if (leader_moved()) // update target
|
||||
df->set_goal(leader_loc);
|
||||
}
|
||||
|
||||
void PartyPathFinder::end_seek(uint32 p) {
|
||||
get_member(p).actor->delete_pathfinder();
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
101
engines/ultima/nuvie/pathfinder/party_path_finder.h
Normal file
101
engines/ultima/nuvie/pathfinder/party_path_finder.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_PARTY_PATH_FINDER_H
|
||||
#define NUVIE_PATHFINDER_PARTY_PATH_FINDER_H
|
||||
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* PartyPathFinder moves the entire party at once.
|
||||
* FIXME: Move to target if one square away and unblocked. Shamino isn't in the
|
||||
* correct square after moving through a doorway.
|
||||
* FIXME: Perhaps is_contiguous() should require everyone in front of a follower
|
||||
* to also be contiguous. If more than one followers are lost, they stay
|
||||
* together more than look for the leader.
|
||||
* FIXME: When walking along the wall, the last follower doesn't move up when
|
||||
* there is a closer free square. (it sometimes behaves the same way in U6)
|
||||
* FIXME: If a higher-number follower is closer to the leader than a lower-numbered
|
||||
* one, the one with higher priority bumps him out of the way, and he loses a
|
||||
* move. This causes him to become non-contiguous. Followers should NEVER be
|
||||
* moved to non-contiguous squares. Strangely, this only happens when moving in
|
||||
* certain directions. (disable SEEK mode to check)
|
||||
* FIXME: When changing directions, followers on opposite sides of the party
|
||||
* (perpendicular to the forward direction) shouldn't exchange positions until
|
||||
* the leader stops.
|
||||
*/
|
||||
|
||||
#define AVOID_DAMAGE_TILES true
|
||||
|
||||
class PartyPathFinder {
|
||||
Party *party; // friend
|
||||
public:
|
||||
PartyPathFinder(Party *p);
|
||||
~PartyPathFinder();
|
||||
|
||||
bool follow_passA(uint32 p); // returns true if party member p moved
|
||||
bool follow_passB(uint32 p);
|
||||
void seek_leader(uint32 p);
|
||||
void end_seek(uint32 p);
|
||||
|
||||
bool move_member(uint32 member_num, sint16 relx, sint16 rely, bool ignore_position = false, bool can_bump = true, bool avoid_danger_tiles = true);
|
||||
bool bump_member(uint32 bumped_member_num, uint32 member_num);
|
||||
|
||||
bool is_seeking(uint32 member_num) {
|
||||
return (get_member(member_num).actor->get_pathfinder() != 0);
|
||||
}
|
||||
bool is_contiguous(uint32 member_num, const MapCoord &from);
|
||||
bool is_contiguous(uint32 member_num);
|
||||
bool is_behind_target(uint32 member_num);
|
||||
bool is_at_target(uint32 p);
|
||||
void get_target_dir(uint32 p, sint8 &rel_x, sint8 &rel_y);
|
||||
void get_forward_dir(sint8 &vec_x, sint8 &vec_y);
|
||||
void get_last_move(sint8 &vec_x, sint8 &vec_y);
|
||||
|
||||
protected:
|
||||
bool try_moving_to_leader(uint32 p, bool ignore_position);
|
||||
bool try_moving_forward(uint32 p);
|
||||
bool try_moving_to_target(uint32 p, bool avoid_damage_tiles = false);
|
||||
bool try_all_directions(uint32 p, MapCoord target_loc);
|
||||
bool try_moving_sideways(uint32 p);
|
||||
|
||||
bool leader_moved_away(uint32 p);
|
||||
bool leader_moved_diagonally();
|
||||
bool leader_moved();
|
||||
|
||||
Std::vector<MapCoord> get_neighbor_tiles(const MapCoord ¢er, const MapCoord &target);
|
||||
|
||||
// use party
|
||||
struct PartyMember get_member(uint32 p) {
|
||||
return (party->member[p]);
|
||||
}
|
||||
sint8 get_leader() {
|
||||
return (party->get_leader());
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
128
engines/ultima/nuvie/pathfinder/path.cpp
Normal file
128
engines/ultima/nuvie/pathfinder/path.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/* 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/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/pathfinder/path.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Path::Path()
|
||||
: path(0), step_count(0), path_size(0), pf(0) {
|
||||
}
|
||||
|
||||
Path::~Path() {
|
||||
delete_path();
|
||||
}
|
||||
|
||||
void Path::set_path_size(int alloc_size) {
|
||||
path_size = alloc_size;
|
||||
path = (MapCoord *)nuvie_realloc(path, path_size * sizeof(MapCoord));
|
||||
}
|
||||
|
||||
/* Take estimate of a path, and return the highest allowed score of any nodes
|
||||
* in the search of that path.
|
||||
*/
|
||||
uint32 Path::get_max_score(uint32 cost) {
|
||||
uint32 max_score = cost * 2;
|
||||
// search at least this far (else short paths will have too
|
||||
// low of a maximum score to move around walls)
|
||||
if (max_score < 8 * 2 * 3)
|
||||
max_score = 8 * 2 * 3;
|
||||
return max_score;
|
||||
}
|
||||
|
||||
/* Return a weighted estimate of the highest cost from location `s' to `g'.
|
||||
*/
|
||||
uint32 Path::path_cost_est(const MapCoord &s, const MapCoord &g) {
|
||||
uint32 major = (s.xdistance(g) >= s.ydistance(g))
|
||||
? s.xdistance(g) : s.ydistance(g);
|
||||
uint32 minor = (s.xdistance(g) >= s.ydistance(g))
|
||||
? s.ydistance(g) : s.xdistance(g);
|
||||
return (2 * major + minor);
|
||||
}
|
||||
|
||||
/* Free and zero path.
|
||||
*/
|
||||
void Path::delete_path() {
|
||||
if (path)
|
||||
free(path);
|
||||
path = nullptr;
|
||||
step_count = 0;
|
||||
path_size = 0;
|
||||
}
|
||||
|
||||
const MapCoord &Path::get_first_step() {
|
||||
return Path::get_step(0);
|
||||
}
|
||||
|
||||
const MapCoord &Path::get_last_step() {
|
||||
return Path::get_step(step_count - 1);
|
||||
}
|
||||
|
||||
const MapCoord &Path::get_step(uint32 step_index) {
|
||||
return path[step_index];
|
||||
}
|
||||
|
||||
bool Path::have_path() {
|
||||
return (path && step_count > 0);
|
||||
}
|
||||
|
||||
void Path::get_path(MapCoord **path_start, uint32 &pathSize) {
|
||||
if (path_start)
|
||||
*path_start = path;
|
||||
pathSize = step_count;
|
||||
}
|
||||
|
||||
/* Increases path size in blocks and adds a step to the end of the path. */
|
||||
void Path::add_step(const MapCoord &loc) {
|
||||
const int path_block_size = 8;
|
||||
if (step_count >= path_size) {
|
||||
path_size += path_block_size;
|
||||
path = (MapCoord *)nuvie_realloc(path, path_size * sizeof(MapCoord));
|
||||
}
|
||||
path[step_count++] = loc;
|
||||
}
|
||||
|
||||
bool Path::remove_first_step() {
|
||||
if (have_path()) {
|
||||
step_count -= 1;
|
||||
path_size = step_count;
|
||||
MapCoord *new_path = (MapCoord *)malloc(path_size * sizeof(MapCoord));
|
||||
memcpy(new_path, &(path[1]), step_count * sizeof(MapCoord));
|
||||
free(path);
|
||||
path = new_path;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Path::check_dir(const MapCoord &loc, MapCoord &rel) {
|
||||
return pf->check_dir(loc, rel);
|
||||
}
|
||||
|
||||
bool Path::check_loc(const MapCoord &loc) {
|
||||
return pf->check_loc(loc);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
84
engines/ultima/nuvie/pathfinder/path.h
Normal file
84
engines/ultima/nuvie/pathfinder/path.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_PATH_H
|
||||
#define NUVIE_PATHFINDER_PATH_H
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/pathfinder/path_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class MapCoord;
|
||||
|
||||
/* Abstract for a class that provides the path-search routines and cost methods
|
||||
* to a PathFinder. This includes useful default methods, and path handling.
|
||||
*/
|
||||
class Path {
|
||||
protected:
|
||||
MapCoord *path; // list of tiles in the path, set by create_path()
|
||||
uint32 step_count; // number of locations in the path
|
||||
uint32 path_size; // allocated elements in list
|
||||
PathFinder *pf;
|
||||
|
||||
void add_step(const MapCoord &loc);
|
||||
bool check_dir(const MapCoord &loc, MapCoord &rel);
|
||||
bool check_loc(const MapCoord &loc);
|
||||
void set_path_size(int alloc_size);
|
||||
|
||||
public:
|
||||
Path();
|
||||
virtual ~Path();
|
||||
void set_pathfinder(PathFinder *pathfinder) {
|
||||
pf = pathfinder;
|
||||
}
|
||||
|
||||
/* The pathfinding routine. Can return success or failure of a search. */
|
||||
virtual bool path_search(const MapCoord &start, const MapCoord &goal) = 0;
|
||||
void delete_path();
|
||||
virtual bool have_path();
|
||||
|
||||
/* Returns the real cost of moving from a node (or location) to a
|
||||
neighboring node. (a single step) */
|
||||
virtual sint32 step_cost(const MapCoord &c1, const MapCoord &c2) = 0;
|
||||
|
||||
/* Estimate highest possible cost from s to g */
|
||||
virtual uint32 path_cost_est(const MapCoord &s, const MapCoord &g);
|
||||
/* Returns maximum score of any single node in the search of a path with
|
||||
a certain estimated cost.*/
|
||||
virtual uint32 get_max_score(uint32 cost);
|
||||
|
||||
virtual const MapCoord &get_first_step();
|
||||
virtual const MapCoord &get_last_step();
|
||||
virtual const MapCoord &get_step(uint32 step_index);
|
||||
virtual void get_path(MapCoord **path_start, uint32 &path_size);
|
||||
uint32 get_num_steps() {
|
||||
return step_count;
|
||||
}
|
||||
|
||||
virtual bool remove_first_step();
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
91
engines/ultima/nuvie/pathfinder/path_finder.cpp
Normal file
91
engines/ultima/nuvie/pathfinder/path_finder.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/* 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/pathfinder/path.h"
|
||||
#include "ultima/nuvie/pathfinder/path_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
PathFinder::PathFinder() : start(0, 0, 0), goal(0, 0, 0), loc(0, 0, 0), search(0) {
|
||||
|
||||
}
|
||||
|
||||
PathFinder::PathFinder(const MapCoord &s, const MapCoord &g)
|
||||
: start(s), goal(g), loc(0, 0, 0), search(0) {
|
||||
|
||||
}
|
||||
|
||||
PathFinder::~PathFinder() {
|
||||
delete search;
|
||||
}
|
||||
|
||||
bool PathFinder::check_dir(const MapCoord &from, MapCoord &rel, sint8) {
|
||||
return check_loc(MapCoord(from.x + rel.sx, from.y + rel.sy, from.z));
|
||||
}
|
||||
|
||||
bool PathFinder::check_loc(uint16 x, uint16 y, uint8 z) {
|
||||
return check_loc(MapCoord(x, y, z));
|
||||
}
|
||||
|
||||
void PathFinder::new_search(Path *new_path) {
|
||||
delete search;
|
||||
search = new_path;
|
||||
search->set_pathfinder(this);
|
||||
}
|
||||
|
||||
bool PathFinder::find_path() {
|
||||
if (search) {
|
||||
if (search->have_path())
|
||||
search->delete_path();
|
||||
return (search->path_search(loc, goal));
|
||||
}
|
||||
return false; // no path-search object
|
||||
}
|
||||
|
||||
bool PathFinder::have_path() {
|
||||
return (search && search->have_path());
|
||||
}
|
||||
|
||||
void PathFinder::set_goal(const MapCoord &g) {
|
||||
goal = g;
|
||||
if (have_path())
|
||||
search->delete_path();
|
||||
}
|
||||
|
||||
void PathFinder::set_start(const MapCoord &s) {
|
||||
start = s;
|
||||
if (have_path())
|
||||
search->delete_path();
|
||||
}
|
||||
|
||||
bool PathFinder::is_path_clear() {
|
||||
uint32 num_steps = search->get_num_steps();
|
||||
for (unsigned int n = 0; n < num_steps; n++) {
|
||||
const MapCoord &pos = search->get_step(n);
|
||||
if (!check_loc(pos))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
80
engines/ultima/nuvie/pathfinder/path_finder.h
Normal file
80
engines/ultima/nuvie/pathfinder/path_finder.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_PATH_FINDER_H
|
||||
#define NUVIE_PATHFINDER_PATH_FINDER_H
|
||||
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Path;
|
||||
|
||||
class PathFinder {
|
||||
protected:
|
||||
MapCoord start, goal, loc; /* source, destination, current location */
|
||||
|
||||
Path *search; /* contains path-search algorithms, and
|
||||
game-specific step costs */
|
||||
|
||||
void new_search(Path *new_path);
|
||||
// bool is_hazardous(MapCoord &loc); will have to check objects and tiles
|
||||
|
||||
public:
|
||||
PathFinder();
|
||||
PathFinder(const MapCoord &s, const MapCoord &g);
|
||||
virtual ~PathFinder();
|
||||
void set_search(Path *new_path) {
|
||||
new_search(new_path);
|
||||
}
|
||||
|
||||
virtual void set_start(const MapCoord &s);
|
||||
virtual void set_goal(const MapCoord &g);
|
||||
virtual void set_location(const MapCoord &l) {
|
||||
loc = l;
|
||||
}
|
||||
|
||||
virtual MapCoord get_location() {
|
||||
return loc;
|
||||
}
|
||||
virtual MapCoord get_goal() {
|
||||
return goal;
|
||||
}
|
||||
virtual bool reached_goal() {
|
||||
return (loc.x == goal.x && loc.y == goal.y
|
||||
&& loc.z == goal.z);
|
||||
}
|
||||
|
||||
virtual bool check_dir(const MapCoord &from, MapCoord &rel, sint8 unused = 0);
|
||||
virtual bool check_loc(const MapCoord &loc) = 0;
|
||||
bool check_loc(uint16 x, uint16 y, uint8 z);
|
||||
|
||||
virtual bool find_path(); /* get path to goal if one doesn't already exist */
|
||||
virtual bool have_path(); /* a working path exists */
|
||||
virtual bool is_path_clear(); /* recheck each location in path */
|
||||
virtual bool get_next_move(MapCoord &step) = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
102
engines/ultima/nuvie/pathfinder/sched_path_finder.cpp
Normal file
102
engines/ultima/nuvie/pathfinder/sched_path_finder.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/* 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/core/map.h"
|
||||
#include "ultima/nuvie/pathfinder/path.h"
|
||||
#include "ultima/nuvie/pathfinder/sched_path_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* NOTE: Path_type must always be valid. */
|
||||
SchedPathFinder::SchedPathFinder(Actor *a, MapCoord g, Path *path_type)
|
||||
: ActorPathFinder(a, g), prev_step_i(0), next_step_i(0) {
|
||||
new_search(path_type);
|
||||
assert(search && actor);
|
||||
}
|
||||
|
||||
SchedPathFinder::~SchedPathFinder() {
|
||||
|
||||
}
|
||||
|
||||
bool SchedPathFinder::get_next_move(MapCoord &step) {
|
||||
// jump to goal if both locations are off-screen
|
||||
if (!goal.is_visible() && !loc.is_visible()) {
|
||||
if (check_loc(goal)) {
|
||||
search->delete_path();
|
||||
step = goal;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!search->have_path())
|
||||
if (!find_path())
|
||||
return false;
|
||||
step = search->get_step(next_step_i); // have a path, take a step
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SchedPathFinder::find_path() {
|
||||
if (search->have_path())
|
||||
search->delete_path();
|
||||
if (!search->path_search(loc, goal)) {
|
||||
DEBUG(0, LEVEL_WARNING, "actor %d failed to find a path to %x,%x\n", actor->get_actor_num(), goal.x, goal.y);
|
||||
return false;
|
||||
}
|
||||
prev_step_i = next_step_i = 0;
|
||||
incr_step(); // the first step is the start location, so skip it
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns true if actor location is correct. */
|
||||
bool SchedPathFinder::is_location_in_path() {
|
||||
const MapCoord &prev_step = search->get_step(prev_step_i);
|
||||
return (loc == prev_step);
|
||||
}
|
||||
|
||||
/* Update previous and next steps in path. */
|
||||
void SchedPathFinder::incr_step() {
|
||||
const MapCoord &prev_loc = search->get_step(prev_step_i);
|
||||
const MapCoord &next_loc = search->get_step(next_step_i);
|
||||
const MapCoord &last_loc = search->get_last_step();
|
||||
if (prev_loc != last_loc) {
|
||||
if (prev_loc != next_loc) // prev_step is going to stay behind next_step
|
||||
++prev_step_i;
|
||||
if (next_loc != last_loc)
|
||||
++next_step_i;
|
||||
}
|
||||
}
|
||||
|
||||
void SchedPathFinder::actor_moved() {
|
||||
update_location();
|
||||
if (search->have_path())
|
||||
incr_step();
|
||||
}
|
||||
|
||||
/* Don't bother moving around other actors. They will probably move soon. */
|
||||
bool SchedPathFinder::check_loc(const MapCoord &locPos) {
|
||||
return actor->check_move(locPos.x, locPos.y, locPos.z, ACTOR_IGNORE_OTHERS);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
55
engines/ultima/nuvie/pathfinder/sched_path_finder.h
Normal file
55
engines/ultima/nuvie/pathfinder/sched_path_finder.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_SCHED_PATH_FINDER_H
|
||||
#define NUVIE_PATHFINDER_SCHED_PATH_FINDER_H
|
||||
|
||||
#include "ultima/nuvie/pathfinder/actor_path_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* Long-range pathfinder for NPCs.
|
||||
*/
|
||||
class SchedPathFinder: public ActorPathFinder {
|
||||
protected:
|
||||
uint32 prev_step_i, next_step_i; /* step counters */
|
||||
|
||||
public:
|
||||
/* Pass 'path_type' to define search rules and methods to be used. The
|
||||
PathFinder is responsible for deleting it when finished. */
|
||||
SchedPathFinder(Actor *a, MapCoord g, Path *path_type);
|
||||
~SchedPathFinder() override;
|
||||
|
||||
bool get_next_move(MapCoord &step) override; /* returns the next step in the path */
|
||||
bool find_path() override; /* gets a NEW path from location->goal */
|
||||
void actor_moved() override; /* update location and step counters */
|
||||
|
||||
bool check_loc(const MapCoord &loc) override; // ignores other actors
|
||||
protected:
|
||||
bool is_location_in_path();
|
||||
void incr_step();
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
195
engines/ultima/nuvie/pathfinder/seek_path.cpp
Normal file
195
engines/ultima/nuvie/pathfinder/seek_path.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/* 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/map.h"
|
||||
#include "ultima/nuvie/pathfinder/dir_finder.h"
|
||||
#include "ultima/nuvie/pathfinder/seek_path.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::vector;
|
||||
|
||||
SeekPath::SeekPath() {
|
||||
|
||||
}
|
||||
|
||||
SeekPath::~SeekPath() {
|
||||
|
||||
}
|
||||
|
||||
/* Get two relative directions that a line can travel to trace around an
|
||||
obstacle towards `xdir',`ydir'. */
|
||||
bool SeekPath::get_obstacle_tracer(const MapCoord &start, sint32 xdir, sint32 ydir,
|
||||
sint32 &Axdir, sint32 &Aydir,
|
||||
sint32 &Bxdir, sint32 &Bydir) {
|
||||
if (xdir && ydir) { // original direction is diagonal
|
||||
MapCoord checkA(start.x + xdir, start.y, start.z);
|
||||
MapCoord checkB(start.x, start.y + ydir, start.z);
|
||||
if (check_loc(checkA)) { // can go in X
|
||||
Axdir = xdir;
|
||||
Aydir = 0; // Horizontal; in X direction
|
||||
} else { // X is blocked, must go in Y
|
||||
Axdir = 0;
|
||||
Aydir = -ydir; // Vertical; opposite Y direction
|
||||
}
|
||||
if (check_loc(checkB)) { // can go in Y
|
||||
Bxdir = 0;
|
||||
Bydir = ydir; // Vertical; in Y direction
|
||||
} else { // Y is blocked, must go in X
|
||||
Bxdir = -xdir;
|
||||
Bydir = 0; // Horizontal; opposite X direction
|
||||
}
|
||||
} else { // orthagonal
|
||||
// scan in perpendicular straight line
|
||||
Axdir = ydir;
|
||||
Aydir = xdir;
|
||||
Bxdir = -Axdir;
|
||||
Bydir = -Aydir;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns true if an opening is found along the original line. */
|
||||
bool SeekPath::trace_check_obstacle(bool &turned, MapCoord &line, sint32 &deltax, sint32 &deltay, sint32 &xdir, sint32 &ydir, Std::vector<MapCoord> *scan) {
|
||||
MapCoord obstacle(line.x + xdir, line.y + ydir, line.z);
|
||||
if (check_loc(obstacle)) { // no obstacle here; able to move closer
|
||||
if (scan->empty() || scan->back() != line)
|
||||
scan->push_back(line); // *ADD TRACE NODE*
|
||||
if (!turned) {
|
||||
scan->push_back(obstacle); // *ADD TRACE NODE*
|
||||
return true;
|
||||
}
|
||||
// bend line TOWARDS obstacle
|
||||
line.x += xdir;
|
||||
line.y += ydir; // step forward
|
||||
sint32 old_deltax = deltax, old_deltay = deltay;
|
||||
deltax = xdir;
|
||||
deltay = ydir; // now moving in that direction
|
||||
xdir = -old_deltax;
|
||||
ydir = -old_deltay; // and looking away from old delta
|
||||
turned = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SeekPath::trace_around_corner(MapCoord &line, sint32 &deltax, sint32 &deltay, sint32 &xdir, sint32 &ydir, Std::vector<MapCoord> *scan) {
|
||||
line.x -= deltax;
|
||||
line.y -= deltay; // step back
|
||||
if (scan->empty() || scan->back() != line)
|
||||
scan->push_back(line); // *ADD TRACE NODE*
|
||||
sint8 old_xdir = xdir, old_ydir = ydir;
|
||||
xdir = deltax;
|
||||
ydir = deltay; // now looking in that direction
|
||||
deltax = -old_xdir;
|
||||
deltay = -old_ydir; // and moving scan away from old obstacle
|
||||
}
|
||||
|
||||
/* Trace an obstacle from 'start' towards the direction 'deltax' and 'deltay',
|
||||
looking for openings towards 'xdir' and 'ydir'. The scan can bend 90 degrees
|
||||
to get around walls. Returns true if something that looks like an opening
|
||||
has been found. Trace nodes are placed at turns and where the scan ends. */
|
||||
bool SeekPath::trace_obstacle(MapCoord line, sint32 deltax, sint32 deltay, sint32 xdir, sint32 ydir, Std::vector<MapCoord> *scan) {
|
||||
const uint32 scan_max = 8; // number of squares to check before giving up
|
||||
bool bend = false; // true if the scanning line is rotated 90 degrees
|
||||
uint32 s = 0;
|
||||
do {
|
||||
line.x += deltax;
|
||||
line.y += deltay;
|
||||
if (!check_loc(line)) {
|
||||
if (!bend) { // bend line AWAY from obstacle
|
||||
trace_around_corner(line, deltax, deltay, xdir, ydir, scan);
|
||||
bend = true;
|
||||
} else // blocked (we only allow one turn)
|
||||
break;
|
||||
} else if (trace_check_obstacle(bend, line, deltax, deltay, xdir, ydir, scan))
|
||||
return true;
|
||||
} while (++s < scan_max);
|
||||
scan->resize(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// choose which set of nodes traced around an obstacle should be used for a path
|
||||
Std::vector<MapCoord> *SeekPath::get_best_scan(const MapCoord &start, const MapCoord &goal) {
|
||||
if (A_scan.empty() && B_scan.empty())
|
||||
return 0;
|
||||
if (A_scan.empty())
|
||||
return &B_scan;
|
||||
if (B_scan.empty())
|
||||
return &A_scan;
|
||||
if (B_scan.back().distance(goal) < A_scan.back().distance(goal))
|
||||
return &B_scan;
|
||||
return &A_scan;
|
||||
}
|
||||
|
||||
// copy A or B nodes to the path
|
||||
void SeekPath::create_path(const MapCoord &start, const MapCoord &goal) {
|
||||
vector<MapCoord> *nodes = get_best_scan(start, goal); // points to line A or B
|
||||
MapCoord prev_node(start);
|
||||
|
||||
// these nodes are only at certain locations in the path, so all steps in
|
||||
// between have to be added
|
||||
while (nodes && !nodes->empty()) {
|
||||
// create steps from prev_node to this_node
|
||||
MapCoord this_node = nodes->front();
|
||||
nodes->erase(nodes->begin());
|
||||
if (this_node == start) // start is the first prev_node, which results in duplicate steps
|
||||
continue;
|
||||
sint16 dx = clamp(this_node.x - prev_node.x, -1, 1), dy = clamp(this_node.y - prev_node.y, -1, 1);
|
||||
do {
|
||||
prev_node = prev_node.abs_coords(dx, dy); // add dx & dy
|
||||
add_step(prev_node);
|
||||
} while (prev_node != this_node);
|
||||
|
||||
prev_node = this_node;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if a path is found around the obstacle between locations. */
|
||||
bool SeekPath::path_search(const MapCoord &start, const MapCoord &goal) {
|
||||
sint8 xdir = 0, ydir = 0; // direction start->goal
|
||||
DirFinder::get_normalized_dir(start, goal, xdir, ydir); // init xdir & ydir
|
||||
|
||||
// confirm that goal is more than one square away
|
||||
if ((start.x + xdir) == goal.x && (start.y + ydir) == goal.y)
|
||||
return false;
|
||||
|
||||
// determine if each line (A and B) will be vertical or horizontal
|
||||
sint32 Ax = 0, Ay = 0, Bx = 0, By = 0; // vector of line segments to scan
|
||||
get_obstacle_tracer(start, xdir, ydir, Ax, Ay, Bx, By);
|
||||
|
||||
// direction from line to scan is perpendicular to line, towards obstacle
|
||||
delete_nodes();
|
||||
bool successA = trace_obstacle(start, Ax, Ay, (Ay) ? xdir : 0, (Ax) ? ydir : 0, &A_scan);
|
||||
bool successB = trace_obstacle(start, Bx, By, (By) ? xdir : 0, (Bx) ? ydir : 0, &B_scan);
|
||||
if (successA || successB)
|
||||
create_path(start, goal); // create path from available nodes
|
||||
delete_nodes();
|
||||
return (successA || successB);
|
||||
}
|
||||
|
||||
void SeekPath::delete_nodes() {
|
||||
A_scan.clear();
|
||||
B_scan.clear();
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
69
engines/ultima/nuvie/pathfinder/seek_path.h
Normal file
69
engines/ultima/nuvie/pathfinder/seek_path.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_SEEK_PATH_H
|
||||
#define NUVIE_PATHFINDER_SEEK_PATH_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/nuvie/pathfinder/path.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* Provides routines for building short paths around obstacles and seeking a
|
||||
* target. Much of the work doesn't involve finding a path at all, but instead
|
||||
* finding the direction closest to the target.
|
||||
*/
|
||||
class SeekPath: public Path {
|
||||
protected:
|
||||
Std::vector<MapCoord> A_scan, B_scan; // nodes of a line scanned by trace_obstacle()
|
||||
|
||||
void create_path(const MapCoord &start, const MapCoord &goal);
|
||||
Std::vector<MapCoord> *get_best_scan(const MapCoord &start, const MapCoord &goal);
|
||||
void delete_nodes();
|
||||
bool trace_check_obstacle(bool &turned, MapCoord &line, sint32 &deltax, sint32 &deltay, sint32 &xdir, sint32 &ydir, Std::vector<MapCoord> *scan);
|
||||
void trace_around_corner(MapCoord &line, sint32 &deltax, sint32 &deltay, sint32 &xdir, sint32 &ydir, Std::vector<MapCoord> *scan);
|
||||
|
||||
public:
|
||||
SeekPath();
|
||||
~SeekPath() override;
|
||||
sint32 step_cost(const MapCoord &c1, const MapCoord &c2) override {
|
||||
return -1;
|
||||
}
|
||||
bool path_search(const MapCoord &start, const MapCoord &goal) override;
|
||||
void delete_path() {
|
||||
Path::delete_path();
|
||||
delete_nodes();
|
||||
}
|
||||
|
||||
/* Trace obstacle towards xdir,ydir for a possible opening. */
|
||||
bool trace_obstacle(MapCoord line, sint32 deltax, sint32 deltay, sint32 xdir, sint32 ydir, Std::vector<MapCoord> *scan);
|
||||
/* Get two relative directions that a line can travel to trace around an
|
||||
obstacle towards `xdir',`ydir'. */
|
||||
bool get_obstacle_tracer(const MapCoord &start, sint32 xdir, sint32 ydir,
|
||||
sint32 &Axdir, sint32 &Aydir,
|
||||
sint32 &Bxdir, sint32 &Bydir);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
66
engines/ultima/nuvie/pathfinder/u6_astar_path.cpp
Normal file
66
engines/ultima/nuvie/pathfinder/u6_astar_path.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/* 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/core/game.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/usecode/usecode.h"
|
||||
#include "ultima/nuvie/pathfinder/u6_astar_path.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* Return the cost of moving one step from `c1' to `c2'.
|
||||
* Blocking objects are checked for, and doors may be passable
|
||||
* Returns -1 if c2 is blocked.
|
||||
*/
|
||||
sint32 U6AStarPath::step_cost(const MapCoord &c1, const MapCoord &c2) {
|
||||
Game *game = Game::get_game();
|
||||
sint32 c = 1; // final cost is not necessarily the actual move cost
|
||||
|
||||
// FIXME: need an actor->check_move(loc2, loc1) to check one step only
|
||||
if (c2.distance(c1) > 1)
|
||||
return -1;
|
||||
if (!pf->check_loc(c2.x, c2.y, c2.z)) {
|
||||
// check for door
|
||||
// Door objects consist of a wall and the actual door tile.
|
||||
// We use get_objBasedAt() here since we are only interested in the latter.
|
||||
Obj *block = game->get_obj_manager()->get_objBasedAt(c2.x, c2.y, c2.z, true, false);
|
||||
if (block && game->get_usecode()->is_unlocked_door(block))
|
||||
c += 2; // cost for opening door
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
// add cost of *original* step
|
||||
// c += game->get_game_map()->get_impedance(c1.x, c1.y, c1.z);
|
||||
|
||||
if (c1.x != c2.x && c1.y != c2.y) // prefer non-diagonal
|
||||
c *= 2;
|
||||
return c;
|
||||
}
|
||||
|
||||
// Possible step cost is 1 to 16.
|
||||
uint32 U6AStarPath::path_cost_est(const MapCoord &s, const MapCoord &g) {
|
||||
return Path::path_cost_est(s, g);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
40
engines/ultima/nuvie/pathfinder/u6_astar_path.h
Normal file
40
engines/ultima/nuvie/pathfinder/u6_astar_path.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_PATHFINDER_U6_ASTAR_PATH_H
|
||||
#define NUVIE_PATHFINDER_U6_ASTAR_PATH_H
|
||||
|
||||
#include "ultima/nuvie/pathfinder/astar_path.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* This provides a U6-specific step_cost() method. */
|
||||
class U6AStarPath: public AStarPath {
|
||||
public:
|
||||
sint32 step_cost(const MapCoord &c1, const MapCoord &c2) override;
|
||||
uint32 path_cost_est(const MapCoord &s, const MapCoord &g) override;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user