scummvm/engines/ags/engine/ac/route_finder_impl.cpp
Paul Gilbert b1649597d7 AGS: Replaced Math/std Min/Max with ScummVM macros
Inspired by 6dee5ee6f271c600e30879d892295ea6069e8cc9
2022-05-05 22:41:10 -07:00

257 lines
6.9 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//=============================================================================
//
// New jump point search (JPS) A* pathfinder by Martin Sedlak.
//
//=============================================================================
#include "ags/engine/ac/route_finder_impl.h"
#include "ags/lib/std/math.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 {
#define MAKE_INTCOORD(x,y) (((unsigned short)x << 16) | ((unsigned short)y))
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)++] = MAKE_INTCOORD(x, y);
}
return 1;
}
void set_route_move_speed(int speed_x, int speed_y) {
// negative move speeds like -2 get converted to 1/2
if (speed_x < 0) {
_G(move_speed_x) = itofix(1) / (-speed_x);
} else {
_G(move_speed_x) = itofix(speed_x);
}
if (speed_y < 0) {
_G(move_speed_y) = itofix(1) / (-speed_y);
} else {
_G(move_speed_y) = itofix(speed_y);
}
}
// Calculates the X and Y per game loop, for this stage of the
// movelist
void calculate_move_stage(MoveList *mlsp, int aaa) {
// 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] >> 16) & 0x000ffff;
short oury = (mlsp->pos[aaa] & 0x000ffff);
short destx = ((mlsp->pos[aaa + 1] >> 16) & 0x000ffff);
short desty = (mlsp->pos[aaa + 1] & 0x000ffff);
// Special case for vertical and horizontal movements
if (ourx == destx) {
mlsp->xpermove[aaa] = 0;
mlsp->ypermove[aaa] = _G(move_speed_y);
if (desty < oury)
mlsp->ypermove[aaa] = -mlsp->ypermove[aaa];
return;
}
if (oury == desty) {
mlsp->xpermove[aaa] = _G(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;
if (_G(move_speed_x) == _G(move_speed_y)) {
useMoveSpeed = _G(move_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 (_G(move_speed_x) > _G(move_speed_y)) {
// speed = y + ((1 - xproportion) * (x - y))
useMoveSpeed = _G(move_speed_y) + fixmul(xproportion, _G(move_speed_x) - _G(move_speed_y));
} else {
// speed = x + (xproportion * (y - x))
useMoveSpeed = _G(move_speed_x) + fixmul(itofix(1) - xproportion, _G(move_speed_y) - _G(move_speed_x));
}
}
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;
}
int find_route(short srcx, short srcy, short xx, short yy, Bitmap *onscreen, int movlst, int nocross, int ignore_walls) {
int i;
_G(wallscreen) = onscreen;
_G(num_navpoints) = 0;
if (ignore_walls || can_see_from(srcx, srcy, xx, yy)) {
_G(num_navpoints) = 2;
_G(navpoints)[0] = MAKE_INTCOORD(srcx, srcy);
_G(navpoints)[1] = MAKE_INTCOORD(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
int mlist = movlst;
_GP(mls)[mlist].numstage = _G(num_navpoints);
memcpy(&_GP(mls)[mlist].pos[0], &_G(navpoints)[0], sizeof(int) * _G(num_navpoints));
#ifdef DEBUG_PATHFINDER
AGS::Shared::Debug::Printf("stages: %d\n", _G(num_navpoints));
#endif
for (i = 0; i < _G(num_navpoints) - 1; i++)
calculate_move_stage(&_GP(mls)[mlist], i);
_GP(mls)[mlist].fromx = srcx;
_GP(mls)[mlist].fromy = srcy;
_GP(mls)[mlist].onstage = 0;
_GP(mls)[mlist].onpart = 0;
_GP(mls)[mlist].doneflag = 0;
_GP(mls)[mlist].lastx = -1;
_GP(mls)[mlist].lasty = -1;
return mlist;
}
} // namespace RouteFinder
} // namespace Engine
} // namespace AGS
} // namespace AGS3