Initial commit
This commit is contained in:
306
engines/ags/engine/ac/route_finder_impl.cpp
Normal file
306
engines/ags/engine/ac/route_finder_impl.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// New jump point search (JPS) A* pathfinder by Martin Sedlak.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "ags/engine/ac/route_finder_impl.h"
|
||||
#include "ags/shared/ac/common.h" // quit()
|
||||
#include "ags/engine/ac/move_list.h" // MoveList
|
||||
#include "ags/shared/ac/common_defines.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/engine/ac/route_finder_jps.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using AGS::Shared::Bitmap;
|
||||
|
||||
namespace AGS {
|
||||
namespace Engine {
|
||||
namespace RouteFinder {
|
||||
|
||||
static const int MAXNAVPOINTS = MAXNEEDSTAGES;
|
||||
|
||||
void init_pathfinder() {
|
||||
}
|
||||
|
||||
void shutdown_pathfinder() {
|
||||
}
|
||||
|
||||
void set_wallscreen(Bitmap *wallscreen_) {
|
||||
_G(wallscreen) = wallscreen_;
|
||||
}
|
||||
|
||||
static void sync_nav_wallscreen() {
|
||||
// FIXME: this is dumb, but...
|
||||
_GP(nav).Resize(_G(wallscreen)->GetWidth(), _G(wallscreen)->GetHeight());
|
||||
|
||||
for (int y = 0; y < _G(wallscreen)->GetHeight(); y++)
|
||||
_GP(nav).SetMapRow(y, _G(wallscreen)->GetScanLine(y));
|
||||
}
|
||||
|
||||
int can_see_from(int x1, int y1, int x2, int y2) {
|
||||
_G(lastcx) = x1;
|
||||
_G(lastcy) = y1;
|
||||
|
||||
if ((x1 == x2) && (y1 == y2))
|
||||
return 1;
|
||||
|
||||
sync_nav_wallscreen();
|
||||
|
||||
return !_GP(nav).TraceLine(x1, y1, x2, y2, _G(lastcx), _G(lastcy));
|
||||
}
|
||||
|
||||
void get_lastcpos(int &lastcx_, int &lastcy_) {
|
||||
lastcx_ = _G(lastcx);
|
||||
lastcy_ = _G(lastcy);
|
||||
}
|
||||
|
||||
// new routing using JPS
|
||||
static int find_route_jps(int fromx, int fromy, int destx, int desty) {
|
||||
sync_nav_wallscreen();
|
||||
|
||||
std::vector<int> path, cpath;
|
||||
path.clear();
|
||||
cpath.clear();
|
||||
|
||||
if (_GP(nav).NavigateRefined(fromx, fromy, destx, desty, path, cpath) == Navigation::NAV_UNREACHABLE)
|
||||
return 0;
|
||||
|
||||
_G(num_navpoints) = 0;
|
||||
|
||||
// new behavior: cut path if too complex rather than abort with error message
|
||||
int count = MIN<int>((int)cpath.size(), MAXNAVPOINTS);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int x, y;
|
||||
_GP(nav).UnpackSquare(cpath[i], x, y);
|
||||
|
||||
_G(navpoints)[_G(num_navpoints)++] = {x, y};
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline fixed input_speed_to_fixed(int speed_val) {
|
||||
// negative move speeds like -2 get converted to 1/2
|
||||
if (speed_val < 0) {
|
||||
return itofix(1) / (-speed_val);
|
||||
} else {
|
||||
return itofix(speed_val);
|
||||
}
|
||||
}
|
||||
|
||||
void set_route_move_speed(int speed_x, int speed_y) {
|
||||
_G(move_speed_x) = input_speed_to_fixed(speed_x);
|
||||
_G(move_speed_y) = input_speed_to_fixed(speed_y);
|
||||
}
|
||||
|
||||
inline fixed calc_move_speed_at_angle(fixed speed_x, fixed speed_y, fixed xdist, fixed ydist) {
|
||||
fixed useMoveSpeed;
|
||||
if (speed_x == speed_y) {
|
||||
useMoveSpeed = speed_x;
|
||||
} else {
|
||||
// different X and Y move speeds
|
||||
// the X proportion of the movement is (x / (x + y))
|
||||
fixed xproportion = fixdiv(xdist, (xdist + ydist));
|
||||
|
||||
if (speed_x > speed_y) {
|
||||
// speed = y + ((1 - xproportion) * (x - y))
|
||||
useMoveSpeed = speed_y + fixmul(xproportion, speed_x - speed_y);
|
||||
} else {
|
||||
// speed = x + (xproportion * (y - x))
|
||||
useMoveSpeed = speed_x + fixmul(itofix(1) - xproportion, speed_y - speed_x);
|
||||
}
|
||||
}
|
||||
return useMoveSpeed;
|
||||
}
|
||||
|
||||
// Calculates the X and Y per game loop, for this stage of the movelist
|
||||
void calculate_move_stage(MoveList *mlsp, int aaa, fixed move_speed_x, fixed move_speed_y) {
|
||||
// work out the x & y per move. First, opp/adj=tan, so work out the angle
|
||||
if (mlsp->pos[aaa] == mlsp->pos[aaa + 1]) {
|
||||
mlsp->xpermove[aaa] = 0;
|
||||
mlsp->ypermove[aaa] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
short ourx = mlsp->pos[aaa].X;
|
||||
short oury = mlsp->pos[aaa].Y;
|
||||
short destx = mlsp->pos[aaa + 1].X;
|
||||
short desty = mlsp->pos[aaa + 1].Y;
|
||||
|
||||
// Special case for vertical and horizontal movements
|
||||
if (ourx == destx) {
|
||||
mlsp->xpermove[aaa] = 0;
|
||||
mlsp->ypermove[aaa] = move_speed_y;
|
||||
if (desty < oury)
|
||||
mlsp->ypermove[aaa] = -mlsp->ypermove[aaa];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (oury == desty) {
|
||||
mlsp->xpermove[aaa] = move_speed_x;
|
||||
mlsp->ypermove[aaa] = 0;
|
||||
if (destx < ourx)
|
||||
mlsp->xpermove[aaa] = -mlsp->xpermove[aaa];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fixed xdist = itofix(abs(ourx - destx));
|
||||
fixed ydist = itofix(abs(oury - desty));
|
||||
|
||||
fixed useMoveSpeed = calc_move_speed_at_angle(move_speed_x, move_speed_y, xdist, ydist);
|
||||
|
||||
fixed angl = fixatan(fixdiv(ydist, xdist));
|
||||
|
||||
// now, since new opp=hyp*sin, work out the Y step size
|
||||
//fixed newymove = useMoveSpeed * fsin(angl);
|
||||
fixed newymove = fixmul(useMoveSpeed, fixsin(angl));
|
||||
|
||||
// since adj=hyp*cos, work out X step size
|
||||
//fixed newxmove = useMoveSpeed * fcos(angl);
|
||||
fixed newxmove = fixmul(useMoveSpeed, fixcos(angl));
|
||||
|
||||
if (destx < ourx)
|
||||
newxmove = -newxmove;
|
||||
if (desty < oury)
|
||||
newymove = -newymove;
|
||||
|
||||
mlsp->xpermove[aaa] = newxmove;
|
||||
mlsp->ypermove[aaa] = newymove;
|
||||
}
|
||||
|
||||
void recalculate_move_speeds(MoveList *mlsp, int old_speed_x, int old_speed_y, int new_speed_x, int new_speed_y) {
|
||||
const fixed old_movspeed_x = input_speed_to_fixed(old_speed_x);
|
||||
const fixed old_movspeed_y = input_speed_to_fixed(old_speed_y);
|
||||
const fixed new_movspeed_x = input_speed_to_fixed(new_speed_x);
|
||||
const fixed new_movspeed_y = input_speed_to_fixed(new_speed_y);
|
||||
// save current stage's step lengths, for later onpart's update
|
||||
const fixed old_stage_xpermove = mlsp->xpermove[mlsp->onstage];
|
||||
const fixed old_stage_ypermove = mlsp->ypermove[mlsp->onstage];
|
||||
|
||||
for (int i = 0; (i < mlsp->numstage) && ((mlsp->xpermove[i] != 0) || (mlsp->ypermove[i] != 0)); ++i) {
|
||||
// First three cases where the speed is a plain factor, therefore
|
||||
// we may simply divide on old one and multiple on a new one
|
||||
if (((old_movspeed_x == old_movspeed_y) && (new_movspeed_x == new_movspeed_y)) // diagonal move at straight 45 degrees
|
||||
|| (mlsp->xpermove[i] == 0) // straight vertical move
|
||||
|| (mlsp->ypermove[i] == 0)) // straight horizontal move
|
||||
{
|
||||
mlsp->xpermove[i] = fixdiv(fixmul(mlsp->xpermove[i], new_movspeed_x), old_movspeed_x);
|
||||
mlsp->ypermove[i] = fixdiv(fixmul(mlsp->ypermove[i], new_movspeed_y), old_movspeed_y);
|
||||
} else {
|
||||
// Move at angle has adjusted speed factor, which we must recalculate first
|
||||
short ourx = mlsp->pos[i].X;
|
||||
short oury = mlsp->pos[i].Y;
|
||||
short destx = mlsp->pos[i + 1].X;
|
||||
short desty = mlsp->pos[i + 1].Y;
|
||||
|
||||
fixed xdist = itofix(abs(ourx - destx));
|
||||
fixed ydist = itofix(abs(oury - desty));
|
||||
fixed old_speed_at_angle = calc_move_speed_at_angle(old_movspeed_x, old_movspeed_y, xdist, ydist);
|
||||
fixed new_speed_at_angle = calc_move_speed_at_angle(new_movspeed_x, new_movspeed_y, xdist, ydist);
|
||||
|
||||
mlsp->xpermove[i] = fixdiv(fixmul(mlsp->xpermove[i], new_speed_at_angle), old_speed_at_angle);
|
||||
mlsp->ypermove[i] = fixdiv(fixmul(mlsp->ypermove[i], new_speed_at_angle), old_speed_at_angle);
|
||||
}
|
||||
}
|
||||
|
||||
// now adjust current passed stage fraction
|
||||
if (mlsp->onpart >= 0.f) {
|
||||
if (old_stage_xpermove != 0)
|
||||
mlsp->onpart = (mlsp->onpart * fixtof(old_stage_xpermove)) / fixtof(mlsp->xpermove[mlsp->onstage]);
|
||||
else
|
||||
mlsp->onpart = (mlsp->onpart * fixtof(old_stage_ypermove)) / fixtof(mlsp->ypermove[mlsp->onstage]);
|
||||
}
|
||||
}
|
||||
|
||||
int find_route(short srcx, short srcy, short xx, short yy, int move_speed_x, int move_speed_y, Bitmap *onscreen, int move_id, int nocross, int ignore_walls) {
|
||||
|
||||
_G(wallscreen) = onscreen;
|
||||
|
||||
_G(num_navpoints) = 0;
|
||||
|
||||
if (ignore_walls || can_see_from(srcx, srcy, xx, yy)) {
|
||||
_G(num_navpoints) = 2;
|
||||
_G(navpoints)[0] = {srcx, srcy};
|
||||
_G(navpoints)[1] = {xx, yy};
|
||||
} else {
|
||||
if ((nocross == 0) && (_G(wallscreen)->GetPixel(xx, yy) == 0))
|
||||
return 0; // clicked on a wall
|
||||
|
||||
find_route_jps(srcx, srcy, xx, yy);
|
||||
}
|
||||
|
||||
if (!_G(num_navpoints))
|
||||
return 0;
|
||||
|
||||
// FIXME: really necessary?
|
||||
if (_G(num_navpoints) == 1)
|
||||
_G(navpoints)[_G(num_navpoints)++] = _G(navpoints)[0];
|
||||
|
||||
assert(_G(num_navpoints) <= MAXNAVPOINTS);
|
||||
|
||||
#ifdef DEBUG_PATHFINDER
|
||||
AGS::Shared::Debug::Printf("Route from %d,%d to %d,%d - %d stages", srcx, srcy, xx, yy, _G(num_navpoints));
|
||||
#endif
|
||||
|
||||
MoveList mlist;
|
||||
mlist.numstage = _G(num_navpoints);
|
||||
memcpy(&mlist.pos[0], &_G(navpoints)[0], sizeof(Point) * _G(num_navpoints));
|
||||
#ifdef DEBUG_PATHFINDER
|
||||
AGS::Shared::Debug::Printf("stages: %d\n", _G(num_navpoints));
|
||||
#endif
|
||||
|
||||
const fixed fix_speed_x = input_speed_to_fixed(move_speed_x);
|
||||
const fixed fix_speed_y = input_speed_to_fixed(move_speed_y);
|
||||
for (int i = 0; i < _G(num_navpoints) - 1; i++) {
|
||||
calculate_move_stage(&mlist, i, fix_speed_x, fix_speed_y);
|
||||
}
|
||||
|
||||
mlist.from = {srcx, srcy};
|
||||
_GP(mls)[move_id] = mlist;
|
||||
return move_id;
|
||||
}
|
||||
|
||||
bool add_waypoint_direct(MoveList *mlsp, short x, short y, int move_speed_x, int move_speed_y) {
|
||||
if (mlsp->numstage >= MAXNEEDSTAGES)
|
||||
return false;
|
||||
|
||||
const fixed fix_speed_x = input_speed_to_fixed(move_speed_x);
|
||||
const fixed fix_speed_y = input_speed_to_fixed(move_speed_y);
|
||||
mlsp->pos[mlsp->numstage] = {x, y};
|
||||
calculate_move_stage(mlsp, mlsp->numstage - 1, fix_speed_x, fix_speed_y);
|
||||
mlsp->numstage++;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace RouteFinder
|
||||
} // namespace Engine
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
Reference in New Issue
Block a user