scummvm/engines/sword1/router.cpp
2011-05-25 10:50:46 -04:00

2137 lines
60 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/debug.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "sword1/router.h"
#include "sword1/swordres.h"
#include "sword1/sworddefs.h"
#include "sword1/objectman.h"
#include "sword1/resman.h"
namespace Sword1 {
/****************************************************************************
* JROUTER.C polygon router with modular walks
* using a tree of modules
* 21 july 94
* 3 november 94
* System currently works by scanning grid data and coming up with a ROUTE
* as a series of way points(nodes), the smoothest eight directional PATH
* through these nodes is then found, and a WALK created to fit the PATH.
*
* Two funtions are called by the user, RouteFinder creates a route as a
* module list, HardWalk creates an animation list from the module list.
* The split is only provided to allow the possibility of turning the
* autorouter over two game cycles.
****************************************************************************
*
* Routine timings on osborne 486
*
* Read floor resource (file already loaded) 112 pixels
*
* Read mega resource (file already loaded) 112 pixels
*
*
*
****************************************************************************
*
* Modified 12 Oct 95
*
* Target Points within 1 pixel of a line are ignored ???
*
* Modules split into Points within 1 pixel of a line are ignored ???
*
****************************************************************************/
#define NO_DIRECTIONS 8
#define SLOW_IN 3
#define SLOW_OUT 7
#define ROUTE_END_FLAG 255
Router::Router(ObjectMan *pObjMan, ResMan *pResMan) {
_objMan = pObjMan;
_resMan = pResMan;
_nNodes = _nBars = 0;
_playerTargetX = _playerTargetY = _playerTargetDir = _playerTargetStance = 0;
_diagonalx = _diagonaly = 0;
_slidyWalkAnimatorState = false;
}
/*
* CODE
*/
int32 Router::routeFinder(int32 id, Object *megaObject, int32 x, int32 y, int32 dir) {
/*********************************************************************
* RouteFinder.C polygon router with modular walks
* 21 august 94
* 3 november 94
* routeFinder creates a list of modules that enables HardWalk to
* create an animation list.
*
* routeFinder currently works by scanning grid data and coming up
* with a ROUTE as a series of way points(nodes), the smoothest eight
* directional PATH through these nodes is then found, this
* information is made available to HardWalk for a WALK to be created
* to fit the PATH.
*
* 30 november 94 return values modified
*
* return 0 = failed to find a route
*
* 1 = found a route
*
* 2 = mega already at target
*
*********************************************************************/
int32 routeFlag = 0;
int32 solidFlag = 0;
WalkData *walkAnim;
megaId = id;
LoadWalkResources(megaObject, x, y, dir);
walkAnim = megaObject->o_route;
_framesPerStep = _nWalkFrames/2;
_framesPerChar = _nWalkFrames * NO_DIRECTIONS;
// offset pointers added Oct 30 95 JPS
standFrames = _framesPerChar;
turnFramesLeft = standFrames;
turnFramesRight = standFrames;
walkFramesLeft = 0;
walkFramesRight = 0;
slowInFrames = 0;
slowOutFrames = 0;
if (megaId == GEORGE) {
turnFramesLeft = 3 * _framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN + 4 * SLOW_OUT;
turnFramesRight = 3 * _framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN + 4 * SLOW_OUT + NO_DIRECTIONS;
walkFramesLeft = _framesPerChar + NO_DIRECTIONS;
walkFramesRight = 2 * _framesPerChar + NO_DIRECTIONS;
slowInFrames = 3 * _framesPerChar + NO_DIRECTIONS;
slowOutFrames = 3 * _framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN;
} else if (megaId == NICO) {
turnFramesLeft = _framesPerChar + NO_DIRECTIONS;
turnFramesRight = _framesPerChar + 2 * NO_DIRECTIONS;
walkFramesLeft = 0;
walkFramesRight = 0;
slowInFrames = 0;
slowOutFrames = 0;
}
// **************************************************************************
// All route data now loaded start finding a route
// **************************************************************************
// **************************************************************************
// check if we can get a route through the floor changed 12 Oct95 JPS
// **************************************************************************
routeFlag = getRoute();
switch (routeFlag) {
case 2:
// special case for zero length route
// if target direction specified as any
if (_targetDir > 7)
_targetDir = _startDir;
// just a turn on the spot is required set an end module for
// the route let the animator deal with it
// modularPath is normally set by extractRoute
_modularPath[0].dir = _startDir;
_modularPath[0].num = 0;
_modularPath[0].x = _startX;
_modularPath[0].y = _startY;
_modularPath[1].dir = _targetDir;
_modularPath[1].num = 0;
_modularPath[1].x = _startX;
_modularPath[1].y = _startY;
_modularPath[2].dir = 9;
_modularPath[2].num = ROUTE_END_FLAG;
slidyWalkAnimator(walkAnim);
routeFlag = 2;
break;
case 1:
// A normal route. Convert the route to an exact path
smoothestPath();
// The Route had waypoints and direction options
// The Path is an exact set of lines in 8 directions that
// reach the target.
// The path is in module format, but steps taken in each
// direction are not accurate
// if target dir = 8 then the walk isn't linked to an anim so
// we can create a route without sliding and miss the exact
// target
if (_targetDir == NO_DIRECTIONS) {
// can end facing ANY direction (ie. exact end
// position not vital) - so use SOLID walk to
// avoid sliding to exact position
solidPath();
solidFlag = solidWalkAnimator(walkAnim);
}
if (!solidFlag) {
// if we failed to create a SOLID route, do a SLIDY
// one instead
slidyPath();
slidyWalkAnimator(walkAnim);
}
break;
default:
// Route didn't reach target so assume point was off the floor
// routeFlag = 0;
break;
}
return routeFlag; // send back null route
}
int32 Router::getRoute() {
/*********************************************************************
* GetRoute.C extract a path from walk grid
* 12 october 94
*
* GetRoute currently works by scanning grid data and coming up with
* a ROUTE as a series of way points(nodes).
*
* static routeData _route[O_ROUTE_SIZE];
*
* return 0 = failed to find a route
*
* 1 = found a route
*
* 2 = mega already at target
*
* 3 = failed to find a route because target was on a line
*
*********************************************************************/
int32 routeGot = 0;
if (_startX == _targetX && _startY == _targetY)
routeGot = 2;
else {
// 'else' added by JEL (23jan96) otherwise 'routeGot' affected
// even when already set to '2' above - causing some 'turns'
// to walk downwards on the spot
// returns 3 if target on a line ( +- 1 pixel )
routeGot = checkTarget(_targetX, _targetY);
}
if (routeGot == 0) {
// still looking for a route check if target is within a pixel
// of a line
// scan through the nodes linking each node to its nearest
// neighbor until no more nodes change
// This is the routine that finds a route using scan()
int32 level = 1;
while (scan(level))
level++;
// Check to see if the route reached the target
if (_node[_nNodes].dist < 9999) {
// it did so extract the route as nodes and the
// directions to go between each node
routeGot = 1;
extractRoute();
// route.X,route.Y and route.Dir now hold all the
// route infomation with the target dir or route
// continuation
}
}
return routeGot;
}
// THE SLIDY PATH ROUTINES
int32 Router::smoothestPath() {
// This is the second big part of the route finder and the the only
// bit that tries to be clever (the other bits are clever).
//
// This part of the autorouter creates a list of modules from a set of
// lines running across the screen. The task is complicated by two
// things:
//
// Firstly in choosing a route through the maze of nodes the routine
// tries to minimise the amount of each individual turn avoiding 90
// degree and greater turns (where possible) and reduces the total
// number of turns (subject to two 45 degree turns being better than
// one 90 degree turn).
//
// Secondly when walking in a given direction the number of steps
// required to reach the end of that run is not calculated accurately.
// This is because I was unable to derive a function to relate number
// of steps taken between two points to the shrunken step size
int i;
int32 steps = 0;
int32 lastDir;
int32 tempturns[4];
int32 turns[4];
const int32 turntable[NO_DIRECTIONS] = { 0, 1, 3, 5, 7, 5, 3, 1 };
// route.X route.Y and route.Dir start at far end
_smoothPath[0].x = _startX;
_smoothPath[0].y = _startY;
_smoothPath[0].dir = _startDir;
_smoothPath[0].num = 0;
lastDir = _startDir;
// for each section of the route
for (int p = 0; p < _routeLength; p++) {
int32 dirS = _route[p].dirS;
int32 dirD = _route[p].dirD;
int32 nextDirS = _route[p + 1].dirS;
int32 nextDirD = _route[p + 1].dirD;
// Check directions into and out of a pair of nodes going in
int32 dS = dirS - lastDir;
if (dS < 0)
dS = dS + NO_DIRECTIONS;
int32 dD = dirD - lastDir;
if (dD < 0)
dD = dD + NO_DIRECTIONS;
// coming out
int32 dSS = dirS - nextDirS;
if (dSS < 0)
dSS = dSS + NO_DIRECTIONS;
int32 dDD = dirD - nextDirD;
if (dDD < 0)
dDD = dDD + NO_DIRECTIONS;
int32 dSD = dirS - nextDirD;
if (dSD < 0)
dSD = dSD + NO_DIRECTIONS;
int32 dDS = dirD - nextDirS;
if (dDS < 0)
dDS = dDS + NO_DIRECTIONS;
// Determine the amount of turning involved in each possible path
dS = turntable[dS];
dD = turntable[dD];
dSS = turntable[dSS];
dDD = turntable[dDD];
dSD = turntable[dSD];
dDS = turntable[dDS];
// get the best path out ie assume next section uses best direction
if (dSD < dSS)
dSS = dSD;
if (dDS < dDD)
dDD = dDS;
// Rate each option. Split routes look crap so weight against them
tempturns[0] = dS + dSS + 3;
turns[0] = 0;
tempturns[1] = dS + dDD;
turns[1] = 1;
tempturns[2] = dD + dSS;
turns[2] = 2;
tempturns[3] = dD + dDD + 3;
turns[3] = 3;
// set up turns as a sorted array of the turn values
for (i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (tempturns[j] > tempturns[j + 1]) {
SWAP(turns[j], turns[j + 1]);
SWAP(tempturns[j], tempturns[j + 1]);
}
}
}
// best option matched in order of the priority we would like
// to see on the screen but each option must be checked to see
// if it can be walked
int32 options = newCheck(1, _route[p].x, _route[p].y, _route[p + 1].x, _route[p + 1].y);
assert(options);
for (i = 0; i < 4; ++i) {
int32 opt = 1 << turns[i];
if (options & opt) {
smoothCheck(steps, turns[i], p, dirS, dirD);
break;
}
}
assert(i < 4);
// route.X route.Y route.dir and bestTurns start at far end
}
// best turns will end heading as near as possible to target dir rest
// is down to anim for now
_smoothPath[steps].dir = 9;
_smoothPath[steps].num = ROUTE_END_FLAG;
return 1;
}
void Router::smoothCheck(int32 &k, int32 best, int32 p, int32 dirS, int32 dirD) {
/*********************************************************************
* Slip sliding away
* This path checker checks to see if a walk that exactly follows the
* path would be valid. This should be inherently true for atleast one
* of the turn options.
* No longer checks the data it only creates the smoothPath array JPS
*********************************************************************/
int32 dsx, dsy;
int32 ddx, ddy;
int32 ss0, ss1, ss2;
int32 sd0, sd1, sd2;
if (p == 0)
k = 1;
int32 x = _route[p].x;
int32 y = _route[p].y;
int32 x2 = _route[p + 1].x;
int32 y2 = _route[p + 1].y;
int32 ldx = x2 - x;
int32 ldy = y2 - y;
int32 dirX = 1;
int32 dirY = 1;
if (ldx < 0) {
ldx = -ldx;
dirX = -1;
}
if (ldy < 0) {
ldy = -ldy;
dirY = -1;
}
// set up sd0-ss2 to reflect possible movement in each direction
if (dirS == 0 || dirS == 4) { // vert and diag
ddx = ldx;
ddy = (ldx * _diagonaly) / _diagonalx;
dsy = ldy - ddy;
ddx = ddx * dirX;
ddy = ddy * dirY;
dsy = dsy * dirY;
dsx = 0;
sd0 = (ddx + _modX[dirD] / 2) / _modX[dirD];
ss0 = (dsy + _modY[dirS] / 2) / _modY[dirS];
sd1 = sd0 / 2;
ss1 = ss0 / 2;
sd2 = sd0 - sd1;
ss2 = ss0 - ss1;
} else {
ddy = ldy;
ddx = (ldy * _diagonalx) / _diagonaly;
dsx = ldx - ddx;
ddy = ddy * dirY;
ddx = ddx * dirX;
dsx = dsx * dirX;
dsy = 0;
sd0 = (ddy + _modY[dirD] / 2) / _modY[dirD];
ss0 = (dsx + _modX[dirS] / 2) / _modX[dirS];
sd1 = sd0 / 2;
ss1 = ss0 / 2;
sd2 = sd0 - sd1;
ss2 = ss0 - ss1;
}
switch (best) {
case 0: // halfsquare, diagonal, halfsquare
_smoothPath[k].x = x + dsx / 2;
_smoothPath[k].y = y + dsy / 2;
_smoothPath[k].dir = dirS;
_smoothPath[k].num = ss1;
k++;
_smoothPath[k].x = x + dsx / 2 + ddx;
_smoothPath[k].y = y + dsy / 2 + ddy;
_smoothPath[k].dir = dirD;
_smoothPath[k].num = sd0;
k++;
_smoothPath[k].x = x + dsx + ddx;
_smoothPath[k].y = y + dsy + ddy;
_smoothPath[k].dir = dirS;
_smoothPath[k].num = ss2;
k++;
break;
case 1: // square, diagonal
_smoothPath[k].x = x + dsx;
_smoothPath[k].y = y + dsy;
_smoothPath[k].dir = dirS;
_smoothPath[k].num = ss0;
k++;
_smoothPath[k].x = x2;
_smoothPath[k].y = y2;
_smoothPath[k].dir = dirD;
_smoothPath[k].num = sd0;
k++;
break;
case 2: // diagonal square
_smoothPath[k].x = x + ddx;
_smoothPath[k].y = y + ddy;
_smoothPath[k].dir = dirD;
_smoothPath[k].num = sd0;
k++;
_smoothPath[k].x = x2;
_smoothPath[k].y = y2;
_smoothPath[k].dir = dirS;
_smoothPath[k].num = ss0;
k++;
break;
default: // halfdiagonal, square, halfdiagonal
_smoothPath[k].x = x + ddx / 2;
_smoothPath[k].y = y + ddy / 2;
_smoothPath[k].dir = dirD;
_smoothPath[k].num = sd1;
k++;
_smoothPath[k].x = x + dsx + ddx / 2;
_smoothPath[k].y = y + dsy + ddy / 2;
_smoothPath[k].dir = dirS;
_smoothPath[k].num = ss0;
k++;
_smoothPath[k].x = x2;
_smoothPath[k].y = y2;
_smoothPath[k].dir = dirD;
_smoothPath[k].num = sd2;
k++;
break;
}
}
void Router::slidyPath() {
/*********************************************************************
* slidyPath creates a path based on part steps with no sliding to get
* as near as possible to the target without any sliding this routine
* is intended for use when just clicking about.
*
* produce a module list from the line data
*********************************************************************/
int32 smooth = 1;
int32 slidy = 1;
// strip out the short sections
_modularPath[0].x = _smoothPath[0].x;
_modularPath[0].y = _smoothPath[0].y;
_modularPath[0].dir = _smoothPath[0].dir;
_modularPath[0].num = 0;
while (_smoothPath[smooth].num < ROUTE_END_FLAG) {
int32 scale = _scaleA * _smoothPath[smooth].y + _scaleB;
int32 deltaX = _smoothPath[smooth].x - _modularPath[slidy - 1].x;
int32 deltaY = _smoothPath[smooth].y - _modularPath[slidy - 1].y;
// quarter a step minimum
int32 stepX = (scale * _modX[_smoothPath[smooth].dir]) >> 19;
int32 stepY = (scale * _modY[_smoothPath[smooth].dir]) >> 19;
if (ABS(deltaX) >= ABS(stepX) && ABS(deltaY) >= ABS(stepY)) {
_modularPath[slidy].x = _smoothPath[smooth].x;
_modularPath[slidy].y = _smoothPath[smooth].y;
_modularPath[slidy].dir = _smoothPath[smooth].dir;
_modularPath[slidy].num = 1;
slidy++;
}
smooth++;
}
// in case the last bit had no steps
if (slidy > 1) {
_modularPath[slidy - 1].x = _smoothPath[smooth - 1].x;
_modularPath[slidy - 1].y = _smoothPath[smooth - 1].y;
}
// set up the end of the walk
_modularPath[slidy].x = _smoothPath[smooth - 1].x;
_modularPath[slidy].y = _smoothPath[smooth - 1].y;
_modularPath[slidy].dir = _targetDir;
_modularPath[slidy].num = 0;
slidy++;
_modularPath[slidy].x = _smoothPath[smooth - 1].x;
_modularPath[slidy].y = _smoothPath[smooth - 1].y;
_modularPath[slidy].dir = 9;
_modularPath[slidy].num = ROUTE_END_FLAG;
}
void Router::slidyWalkAnimator(WalkData *walkAnim) {
/*********************************************************************
* Skidding every where HardWalk creates an animation that exactly
* fits the smoothPath and uses foot slipping to fit whole steps into
* the route
*
* Parameters: georgeg, mouseg
* Returns: rout
*
* produce a module list from the line data
*********************************************************************/
int32 p;
int32 lastDir;
int32 lastRealDir;
int32 currentDir;
int32 turnDir;
int32 scale;
int32 step;
int32 module;
int32 moduleEnd;
int32 moduleX;
int32 moduleY;
int32 module16X = 0;
int32 module16Y = 0;
int32 stepX;
int32 stepY;
int32 errorX;
int32 errorY;
int32 lastErrorX;
int32 lastErrorY;
int32 lastCount;
int32 stepCount;
int32 frameCount;
int32 frames;
int32 frame;
// start at the begining for a change
p = 0;
lastDir = _modularPath[0].dir;
currentDir = _modularPath[1].dir;
if (currentDir == NO_DIRECTIONS)
currentDir = lastDir;
moduleX = _startX;
moduleY = _startY;
module16X = moduleX << 16;
module16Y = moduleY << 16;
stepCount = 0;
//****************************************************************************
// SLIDY
// START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY
// BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED
//****************************************************************************
module = _framesPerChar + lastDir;
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
//****************************************************************************
// SLIDY
// TURN TO START THE WALK
//****************************************************************************
// rotate if we need to
if (lastDir != currentDir) {
// get the direction to turn
turnDir = currentDir - lastDir;
if (turnDir < 0)
turnDir += NO_DIRECTIONS;
if (turnDir > 4)
turnDir = -1;
else if (turnDir > 0)
turnDir = 1;
// rotate to new walk direction
// for george and nico put in a head turn at the start
if ((megaId == GEORGE) || (megaId == NICO)) {
if (turnDir < 0) { // new frames for turn frames 29oct95jps
module = turnFramesLeft + lastDir;
} else {
module = turnFramesRight + lastDir;
}
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
// rotate till were facing new dir then go back 45 degrees
while (lastDir != currentDir) {
lastDir += turnDir;
if (turnDir < 0) { // new frames for turn frames 29oct95jps
if (lastDir < 0)
lastDir += NO_DIRECTIONS;
module = turnFramesLeft + lastDir;
} else {
if (lastDir > 7)
lastDir -= NO_DIRECTIONS;
module = turnFramesRight + lastDir;
}
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
// the back 45 degrees bit
stepCount -= 1;// step back one because new head turn for george takes us past the new dir
}
// his head is in the right direction
lastRealDir = currentDir;
//****************************************************************************
// SLIDY
// THE WALK
//****************************************************************************
_slidyWalkAnimatorState = !_slidyWalkAnimatorState;
lastCount = stepCount;
lastDir = 99;// this ensures that we don't put in turn frames for the start
currentDir = 99;// this ensures that we don't put in turn frames for the start
do {
while (_modularPath[p].num == 0) {
p = p + 1;
if (currentDir != 99)
lastRealDir = currentDir;
lastDir = currentDir;
lastCount = stepCount;
}
//calculate average amount to lose in each step on the way to the next _node
currentDir = _modularPath[p].dir;
if (currentDir < NO_DIRECTIONS) {
module = currentDir * _framesPerStep * 2 + _slidyWalkAnimatorState * _framesPerStep;
_slidyWalkAnimatorState = !_slidyWalkAnimatorState;
moduleEnd = module + _framesPerStep;
step = 0;
scale = (_scaleA * moduleY + _scaleB);
do {
module16X += _dx[module]*scale;
module16Y += _dy[module]*scale;
moduleX = module16X >> 16;
moduleY = module16Y >> 16;
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = step;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
step += 1;
module += 1;
} while ( module < moduleEnd);
stepX = _modX[_modularPath[p].dir];
stepY = _modY[_modularPath[p].dir];
errorX = _modularPath[p].x - moduleX;
errorX = errorX * stepX;
errorY = _modularPath[p].y - moduleY;
errorY = errorY * stepY;
if ((errorX < 0) || (errorY < 0)) {
_modularPath[p].num = 0; // the end of the path
// okay those last steps took us past our target but do we want to scoot or moonwalk
frames = stepCount - lastCount;
errorX = _modularPath[p].x - walkAnim[stepCount-1].x;
errorY = _modularPath[p].y - walkAnim[stepCount-1].y;
if (frames > _framesPerStep) {
lastErrorX = _modularPath[p].x - walkAnim[stepCount-7].x;
lastErrorY = _modularPath[p].y - walkAnim[stepCount-7].y;
if (stepX==0) {
if (3*ABS(lastErrorY) < ABS(errorY)) { //the last stop was closest
stepCount -= _framesPerStep;
_slidyWalkAnimatorState = !_slidyWalkAnimatorState;
}
} else {
if (3*ABS(lastErrorX) < ABS(errorX)) { //the last stop was closest
stepCount -= _framesPerStep;
_slidyWalkAnimatorState = !_slidyWalkAnimatorState;
}
}
}
errorX = _modularPath[p].x - walkAnim[stepCount-1].x;
errorY = _modularPath[p].y - walkAnim[stepCount-1].y;
// okay we've reached the end but we still have an error
if (errorX != 0) {
frameCount = 0;
frames = stepCount - lastCount;
do {
frameCount += 1;
walkAnim[lastCount + frameCount - 1].x += errorX*frameCount/frames;
} while (frameCount<frames);
}
if (errorY != 0) {
frameCount = 0;
frames = stepCount - lastCount;
do {
frameCount += 1;
walkAnim[lastCount + frameCount-1].y += errorY*frameCount/frames;
} while (frameCount<frames);
}
// Now is the time to put in the turn frames for the last turn
if (frames < _framesPerStep)
currentDir = 99;// this ensures that we don't put in turn frames for this walk or the next
if (currentDir != 99)
lastRealDir = currentDir;
// check each turn condition in turn
if (((lastDir != 99) && (currentDir != 99)) && (megaId == GEORGE)) { // only for george
lastDir = currentDir - lastDir;//1 and -7 going right -1 and 7 going left
if (((lastDir == -1) || (lastDir == 7)) || ((lastDir == -2) || (lastDir == 6))) {
// turn at the end of the last walk
frame = lastCount - _framesPerStep;
do {
walkAnim[frame].frame += 104;//turning left
frame += 1;
} while (frame < lastCount);
}
if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6))) {
// turn at the end of the current walk
frame = lastCount - _framesPerStep;
do {
walkAnim[frame].frame += 200; //was 60 now 116
frame += 1;
} while (frame < lastCount);
}
lastDir = currentDir;
}
// all turns checked
lastCount = stepCount;
moduleX = walkAnim[stepCount-1].x;
moduleY = walkAnim[stepCount-1].y;
module16X = moduleX << 16;
module16Y = moduleY << 16;
}
}
} while (_modularPath[p].dir < NO_DIRECTIONS);
if (lastRealDir == 99) {
error("SlidyWalkAnimatorlast direction error");
}
//****************************************************************************
// SLIDY
// TURNS TO END THE WALK ?
//****************************************************************************
// We've done the walk now put in any turns at the end
if (_targetDir == NO_DIRECTIONS) { // stand in the last direction
module = standFrames + lastRealDir;
_targetDir = lastRealDir;
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastRealDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
if (_targetDir == 9) {
if (stepCount == 0) {
module = _framesPerChar + lastRealDir;
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastRealDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
} else if (_targetDir != lastRealDir) { // rotate to _targetDir
// rotate to target direction
turnDir = _targetDir - lastRealDir;
if (turnDir < 0)
turnDir += NO_DIRECTIONS;
if (turnDir > 4)
turnDir = -1;
else if (turnDir > 0)
turnDir = 1;
// rotate to target direction
// for george and nico put in a head turn at the start
if ((megaId == GEORGE) || (megaId == NICO)) {
if (turnDir < 0) { // new frames for turn frames 29oct95jps
module = turnFramesLeft + lastDir;
} else {
module = turnFramesRight + lastDir;
}
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastRealDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
// rotate if we need to
while (lastRealDir != _targetDir) {
lastRealDir += turnDir;
if (turnDir < 0) { // new frames for turn frames 29oct95jps
if (lastRealDir < 0)
lastRealDir += NO_DIRECTIONS;
module = turnFramesLeft + lastRealDir;
} else {
if (lastRealDir > 7)
lastRealDir -= NO_DIRECTIONS;
module = turnFramesRight + lastRealDir;
}
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastRealDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
module = standFrames + lastRealDir;
walkAnim[stepCount-1].frame = module;
} else { // just stand at the end
module = standFrames + lastRealDir;
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastRealDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
walkAnim[stepCount].frame = 512;
stepCount += 1;
walkAnim[stepCount].frame = 512;
stepCount += 1;
walkAnim[stepCount].frame = 512;
// Tdebug("RouteFinder RouteSize is %d", stepCount);
return;
}
// ****************************************************************************
// * THE SOLID PATH ROUTINES
// ****************************************************************************
void Router::solidPath() {
/*********************************************************************
* SolidPath creates a path based on whole steps with no sliding to
* get as near as possible to the target without any sliding this
* routine is currently unused, but is intended for use when just
* clicking about.
*
* produce a module list from the line data
*********************************************************************/
int32 smooth;
int32 solid;
int32 scale;
int32 stepX;
int32 stepY;
int32 deltaX;
int32 deltaY;
// strip out the short sections
solid = 1;
smooth = 1;
_modularPath[0].x = _smoothPath[0].x;
_modularPath[0].y = _smoothPath[0].y;
_modularPath[0].dir = _smoothPath[0].dir;
_modularPath[0].num = 0;
do {
scale = _scaleA * _smoothPath[smooth].y + _scaleB;
deltaX = _smoothPath[smooth].x - _modularPath[solid - 1].x;
deltaY = _smoothPath[smooth].y - _modularPath[solid - 1].y;
stepX = _modX[_smoothPath[smooth].dir];
stepY = _modY[_smoothPath[smooth].dir];
stepX = stepX * scale;
stepY = stepY * scale;
stepX = stepX >> 16;
stepY = stepY >> 16;
if (ABS(deltaX) >= ABS(stepX) && ABS(deltaY) >= ABS(stepY)) {
_modularPath[solid].x = _smoothPath[smooth].x;
_modularPath[solid].y = _smoothPath[smooth].y;
_modularPath[solid].dir = _smoothPath[smooth].dir;
_modularPath[solid].num = 1;
solid++;
}
smooth++;
} while (_smoothPath[smooth].num < ROUTE_END_FLAG);
// in case the last bit had no steps
if (solid == 1) {
// there were no paths so put in a dummy end
solid = 2;
_modularPath[1].dir = _smoothPath[0].dir;
_modularPath[1].num = 0;
}
_modularPath[solid - 1].x = _smoothPath[smooth - 1].x;
_modularPath[solid - 1].y = _smoothPath[smooth - 1].y;
// set up the end of the walk
_modularPath[solid].x = _smoothPath[smooth - 1].x;
_modularPath[solid].y = _smoothPath[smooth - 1].y;
_modularPath[solid].dir = 9;
_modularPath[solid].num = ROUTE_END_FLAG;
}
int32 Router::solidWalkAnimator(WalkData *walkAnim) {
/*********************************************************************
* SolidWalk creates an animation based on whole steps with no sliding
* to get as near as possible to the target without any sliding. This
* routine is is intended for use when just clicking about.
*
* produce a module list from the line data
*
* returns 0 if solid route not found
*********************************************************************/
int32 left;
int32 lastDir;
int32 currentDir;
int32 turnDir;
int32 scale;
int32 step;
int32 module;
int32 moduleX;
int32 moduleY;
int32 module16X;
int32 module16Y;
int32 errorX;
int32 errorY;
int32 moduleEnd;
int32 slowStart;
int32 stepCount;
int32 lastCount;
int32 frame;
// start at the begining for a change
lastDir = _modularPath[0].dir;
currentDir = _modularPath[1].dir;
module = _framesPerChar + lastDir;
moduleX = _startX;
moduleY = _startY;
module16X = moduleX << 16;
module16Y = moduleY << 16;
slowStart = 0;
stepCount = 0;
//****************************************************************************
// SOLID
// START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY
// BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED
//****************************************************************************
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
//****************************************************************************
// SOLID
// TURN TO START THE WALK
//****************************************************************************
// rotate if we need to
if (lastDir != currentDir) {
// get the direction to turn
turnDir = currentDir - lastDir;
if (turnDir < 0)
turnDir += NO_DIRECTIONS;
if (turnDir > 4)
turnDir = -1;
else if (turnDir > 0)
turnDir = 1;
// rotate to new walk direction
// for george and nico put in a head turn at the start
if ((megaId == GEORGE) || (megaId == NICO)) {
if (turnDir < 0) { // new frames for turn frames 29oct95jps
module = turnFramesLeft + lastDir;
} else {
module = turnFramesRight + lastDir;
}
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
// rotate till were facing new dir then go back 45 degrees
while (lastDir != currentDir) {
lastDir += turnDir;
if (turnDir < 0) { // new frames for turn frames 29oct95jps
if (lastDir < 0)
lastDir += NO_DIRECTIONS;
module = turnFramesLeft + lastDir;
} else {
if (lastDir > 7)
lastDir -= NO_DIRECTIONS;
module = turnFramesRight + lastDir;
}
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = lastDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
// the back 45 degrees bit
stepCount -= 1;// step back one because new head turn for george takes us past the new dir
}
//****************************************************************************
// SOLID
// THE SLOW IN
//****************************************************************************
// do start frames if its george and left or right
if (megaId == GEORGE) {
if (_modularPath[1].num > 0) {
if (currentDir == 2) { // only for george
slowStart = 1;
walkAnim[stepCount].frame = 296;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
walkAnim[stepCount].frame = 297;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
walkAnim[stepCount].frame = 298;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
} else if (currentDir == 6) { // only for george
slowStart = 1;
walkAnim[stepCount].frame = 299;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
walkAnim[stepCount].frame = 300;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
walkAnim[stepCount].frame = 301;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
}
}
//****************************************************************************
// SOLID
// THE WALK
//****************************************************************************
if (currentDir > 4)
left = 1;
else
left = 0;
lastCount = stepCount;
lastDir = 99;// this ensures that we don't put in turn frames for the start
currentDir = 99;// this ensures that we don't put in turn frames for the start
int32 p;
for (p = 1; _modularPath[p].dir < NO_DIRECTIONS; ++p) {
while (_modularPath[p].num > 0) {
currentDir = _modularPath[p].dir;
if (currentDir < NO_DIRECTIONS) {
module = currentDir * _framesPerStep * 2 + left * _framesPerStep;
left = !left;
moduleEnd = module + _framesPerStep;
step = 0;
scale = (_scaleA * moduleY + _scaleB);
do {
module16X += _dx[module]*scale;
module16Y += _dy[module]*scale;
moduleX = module16X >> 16;
moduleY = module16Y >> 16;
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = step;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
module += 1;
step += 1;
} while ( module < moduleEnd);
errorX = _modularPath[p].x - moduleX;
errorX = errorX * _modX[_modularPath[p].dir];
errorY = _modularPath[p].y - moduleY;
errorY = errorY * _modY[_modularPath[p].dir];
if ((errorX < 0) || (errorY < 0)) {
_modularPath[p].num = 0;
stepCount -= _framesPerStep;
left = !left;
// Okay this is the end of a section
moduleX = walkAnim[stepCount-1].x;
moduleY = walkAnim[stepCount-1].y;
module16X = moduleX << 16;
module16Y = moduleY << 16;
_modularPath[p].x =moduleX;
_modularPath[p].y =moduleY;
// Now is the time to put in the turn frames for the last turn
if ((stepCount - lastCount) < _framesPerStep) { // no step taken
currentDir = 99;// this ensures that we don't put in turn frames for this walk or the next
if (slowStart == 1) { // clean up if a slow in but no walk
stepCount -= 3;
lastCount -= 3;
slowStart = 0;
}
}
// check each turn condition in turn
if (((lastDir != 99) && (currentDir != 99)) && (megaId == GEORGE)) { // only for george
lastDir = currentDir - lastDir;//1 and -7 going right -1 and 7 going left
if (((lastDir == -1) || (lastDir == 7)) || ((lastDir == -2) || (lastDir == 6))) {
// turn at the end of the last walk
frame = lastCount - _framesPerStep;
do {
walkAnim[frame].frame += 104;//turning left
frame += 1;
} while (frame < lastCount);
}
if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6))) {
// turn at the end of the current walk
frame = lastCount - _framesPerStep;
do {
walkAnim[frame].frame += 200; //was 60 now 116
frame += 1;
} while (frame < lastCount);
}
}
// all turns checked
lastCount = stepCount;
}
}
}
lastDir = currentDir;
slowStart = 0; //can only be valid first time round
}
//****************************************************************************
// SOLID
// THE SLOW OUT
//****************************************************************************
if ((currentDir == 2) && (megaId == GEORGE)) { // only for george
// place stop frames here
// slowdown at the end of the last walk
frame = lastCount - _framesPerStep;
if (walkAnim[frame].frame == 24) {
do {
walkAnim[frame].frame += 278;//stopping right
frame += 1;
} while (frame < lastCount);
walkAnim[stepCount].frame = 308;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
} else if (walkAnim[frame].frame == 30) {
do {
walkAnim[frame].frame += 279;//stopping right
frame += 1;
} while (frame < lastCount);
walkAnim[stepCount].frame = 315;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
} else if ((currentDir == 6) && (megaId == GEORGE)) { // only for george
// place stop frames here
// slowdown at the end of the last walk
frame = lastCount - _framesPerStep;
if (walkAnim[frame].frame == 72) {
do {
walkAnim[frame].frame += 244;//stopping left
frame += 1;
} while (frame < lastCount);
walkAnim[stepCount].frame = 322;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
} else if (walkAnim[frame].frame == 78) {
do {
walkAnim[frame].frame += 245;//stopping left
frame += 1;
} while (frame < lastCount);
walkAnim[stepCount].frame = 329;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
}
}
module = _framesPerChar + _modularPath[p-1].dir;
walkAnim[stepCount].frame = module;
walkAnim[stepCount].step = 0;
walkAnim[stepCount].dir = _modularPath[p-1].dir;
walkAnim[stepCount].x = moduleX;
walkAnim[stepCount].y = moduleY;
stepCount += 1;
walkAnim[stepCount].frame = 512;
stepCount += 1;
walkAnim[stepCount].frame = 512;
stepCount += 1;
walkAnim[stepCount].frame = 512;
//****************************************************************************
// SOLID
// NO END TURNS
//****************************************************************************
debug(5, "routeFinder RouteSize is %d", stepCount);
// now check the route
for (int i = 0; i < p - 1; ++i) {
if (!check(_modularPath[i].x, _modularPath[i].y, _modularPath[i + 1].x, _modularPath[i + 1].y))
p = 0;
}
if (p != 0) {
_targetDir = _modularPath[p - 1].dir;
if (checkTarget(moduleX, moduleY) == 3) {
// new target on a line
p = 0;
debug(5, "Solid walk target was on a line %d %d", moduleX, moduleY);
}
}
return p;
}
// ****************************************************************************
// * THE SCAN ROUTINES
// ****************************************************************************
bool Router::scan(int32 level) {
/*********************************************************************
* Called successively from routeFinder until no more changes take
* place in the grid array, ie he best path has been found
*
* Scans through every point in the node array and checks if there is
* a route between each point and if this route gives a new route.
*
* This routine could probably halve its processing time if it doubled
* up on the checks after each route check
*
*********************************************************************/
int32 x1, y1, x2, y2;
int32 distance;
bool changed = false;
// For all the nodes that have new values and a distance less than
// enddist, ie dont check for new routes from a point we checked
// before or from a point that is already further away than the best
// route so far.
for (int i = 0; i < _nNodes; i++) {
if (_node[i].dist < _node[_nNodes].dist && _node[i].level == level) {
x1 = _node[i].x;
y1 = _node[i].y;
for (int j = _nNodes; j > 0; j--) {
if (_node[j].dist > _node[i].dist) {
x2 = _node[j].x;
y2 = _node[j].y;
if (ABS(x2 - x1) > 4.5 * ABS(y2 - y1))
distance = (8 * ABS(x2 - x1) + 18 * ABS(y2 - y1)) / (54 * 8) + 1;
else
distance = (6 * ABS(x2 - x1) + 36 * ABS(y2 - y1)) / (36 * 14) + 1;
if (distance + _node[i].dist < _node[_nNodes].dist && distance + _node[i].dist < _node[j].dist) {
if (newCheck(0, x1, y1, x2, y2)) {
_node[j].level = level + 1;
_node[j].dist = distance + _node[i].dist;
_node[j].prev = i;
changed = true;
}
}
}
}
}
}
return changed;
}
int32 Router::newCheck(int32 status, int32 x1 , int32 y1 , int32 x2 ,int32 y2) {
/*********************************************************************
* newCheck routine checks if the route between two points can be
* achieved without crossing any of the bars in the Bars array.
*
* newCheck differs from check in that that 4 route options are
* considered corresponding to actual walked routes.
*
* Note distance doesnt take account of shrinking ???
*
* Note Bars array must be properly calculated ie min max dx dy co
*********************************************************************/
int32 ldx;
int32 ldy;
int32 dlx;
int32 dly;
int32 dirX;
int32 dirY;
int32 step1;
int32 step2;
int32 step3;
int32 steps;
int32 options;
steps = 0;
options = 0;
ldx = x2 - x1;
ldy = y2 - y1;
dirX = 1;
dirY = 1;
if (ldx < 0) {
ldx = -ldx;
dirX = -1;
}
if (ldy < 0) {
ldy = -ldy;
dirY = -1;
}
// make the route options
if (_diagonaly * ldx > _diagonalx * ldy) {
// dir = 1,2 or 2,3 or 5,6 or 6,7
dly = ldy;
dlx = (ldy * _diagonalx) / _diagonaly;
ldx = ldx - dlx;
dlx = dlx * dirX;
dly = dly * dirY;
ldx = ldx * dirX;
ldy = 0;
// options are square, diagonal a code 1 route
step1 = check(x1, y1, x1 + ldx, y1);
if (step1 != 0) {
step2 = check(x1 + ldx, y1, x2, y2);
if (step2 != 0) {
steps = step1 + step2;
options |= 2;
}
}
// diagonal, square a code 2 route
if (steps == 0 || status == 1) {
step1 = check(x1, y1, x1 + dlx, y1 + dly);
if (step1 != 0) {
step2 = check(x1 + dlx, y2, x2, y2);
if (step2 != 0) {
steps = step1 + step2;
options |= 4;
}
}
}
// halfsquare, diagonal, halfsquare a code 0 route
if (steps == 0 || status == 1) {
step1 = check(x1, y1, x1 + ldx / 2, y1);
if (step1 != 0) {
step2 = check(x1 + ldx / 2, y1, x1 + ldx / 2 + dlx, y2);
if (step2 != 0) {
step3 = check(x1 + ldx / 2 + dlx, y2, x2, y2);
if (step3 != 0) {
steps = step1 + step2 + step3;
options |= 1;
}
}
}
}
// halfdiagonal, square, halfdiagonal a code 3 route
if (steps == 0 || status == 1) {
step1 = check(x1, y1, x1 + dlx / 2, y1 + dly / 2);
if (step1 != 0) {
step2 = check(x1 + dlx / 2, y1 + dly / 2, x1 + ldx + dlx / 2, y1 + dly / 2);
if (step2 != 0) {
step3 = check(x1 + ldx + dlx / 2, y1 + dly / 2, x2, y2);
if (step3 != 0) {
steps = step1 + step2 + step3;
options |= 8;
}
}
}
}
} else {
// dir = 7,0 or 0,1 or 3,4 or 4,5
dlx = ldx;
dly = (ldx * _diagonaly) / _diagonalx;
ldy = ldy - dly;
dlx = dlx * dirX;
dly = dly * dirY;
ldy = ldy * dirY;
ldx = 0;
// options are square, diagonal a code 1 route
step1 = check(x1 ,y1, x1, y1 + ldy);
if (step1 != 0) {
step2 = check(x1, y1 + ldy, x2, y2);
if (step2 != 0) {
steps = step1 + step2;
options |= 2;
}
}
// diagonal, square a code 2 route
if (steps == 0 || status == 1) {
step1 = check(x1, y1, x2, y1 + dly);
if (step1 != 0) {
step2 = check(x2, y1 + dly, x2, y2);
if (step2 != 0) {
steps = step1 + step2;
options |= 4;
}
}
}
// halfsquare, diagonal, halfsquare a code 0 route
if (steps == 0 || status == 1) {
step1 = check(x1, y1, x1, y1 + ldy / 2);
if (step1 != 0) {
step2 = check(x1, y1 + ldy / 2, x2, y1 + ldy / 2 + dly);
if (step2 != 0) {
step3 = check(x2, y1 + ldy / 2 + dly, x2, y2);
if (step3 != 0) {
steps = step1 + step2 + step3;
options |= 1;
}
}
}
}
// halfdiagonal, square, halfdiagonal a code 3 route
if (steps == 0 || status == 1) {
step1 = check(x1, y1, x1 + dlx / 2, y1 + dly / 2);
if (step1 != 0) {
step2 = check(x1 + dlx / 2, y1 + dly / 2, x1 + dlx / 2, y1 + ldy + dly / 2);
if (step2 != 0) {
step3 = check(x1 + dlx / 2, y1 + ldy + dly / 2, x2, y2);
if (step3 != 0) {
steps = step1 + step2 + step3;
options |= 8;
}
}
}
}
}
if (status == 0)
status = steps;
else
status = options;
return status;
}
// ****************************************************************************
// * CHECK ROUTINES
// ****************************************************************************
bool Router::check(int32 x1, int32 y1, int32 x2, int32 y2) {
// call the fastest line check for the given line
// returns true if line didn't cross any bars
if (x1 == x2 && y1 == y2)
return true;
if (x1 == x2)
return vertCheck(x1, y1, y2);
if (y1 == y2)
return horizCheck(x1, y1, x2);
return lineCheck(x1, y1, x2, y2);
}
bool Router::lineCheck(int32 x1, int32 y1, int32 x2, int32 y2) {
bool linesCrossed = true;
int32 xmin = MIN(x1, x2);
int32 xmax = MAX(x1, x2);
int32 ymin = MIN(y1, y2);
int32 ymax = MAX(y1, y2);
// Line set to go one step in chosen direction so ignore if it hits
// anything
int32 dirx = x2 - x1;
int32 diry = y2 - y1;
int32 co = (y1 * dirx) - (x1 * diry); // new line equation
for (int i = 0; i < _nBars && linesCrossed; i++) {
// skip if not on module
if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax && ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) {
// Okay, it's a valid line. Calculate an intercept. Wow
// but all this arithmetic we must have loads of time
// slope it he slope between the two lines
int32 slope = (_bars[i].dx * diry) - (_bars[i].dy *dirx);
// assuming parallel lines don't cross
if (slope != 0) {
// calculate x intercept and check its on both
// lines
int32 xc = ((_bars[i].co * dirx) - (co * _bars[i].dx)) / slope;
// skip if not on module
if (xc >= xmin - 1 && xc <= xmax + 1) {
// skip if not on line
if (xc >= _bars[i].xmin - 1 && xc <= _bars[i].xmax + 1) {
int32 yc = ((_bars[i].co * diry) - (co * _bars[i].dy)) / slope;
// skip if not on module
if (yc >= ymin - 1 && yc <= ymax + 1) {
// skip if not on line
if (yc >= _bars[i].ymin - 1 && yc <= _bars[i].ymax + 1) {
linesCrossed = false;
}
}
}
}
}
}
}
return linesCrossed;
}
bool Router::horizCheck(int32 x1, int32 y, int32 x2) {
bool linesCrossed = true;
int32 xmin = MIN(x1, x2);
int32 xmax = MAX(x1, x2);
// line set to go one step in chosen direction so ignore if it hits
// anything
for (int i = 0; i < _nBars && linesCrossed; i++) {
// skip if not on module
if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax && y >= _bars[i].ymin && y <= _bars[i].ymax) {
// Okay, it's a valid line calculate an intercept. Wow
// but all this arithmetic we must have loads of time
if (_bars[i].dy == 0)
linesCrossed = false;
else {
int32 ldy = y - _bars[i].y1;
int32 xc = _bars[i].x1 + (_bars[i].dx * ldy) / _bars[i].dy;
// skip if not on module
if (xc >= xmin - 1 && xc <= xmax + 1)
linesCrossed = false;
}
}
}
return linesCrossed;
}
bool Router::vertCheck(int32 x, int32 y1, int32 y2) {
bool linesCrossed = true;
int32 ymin = MIN(y1, y2);
int32 ymax = MAX(y1, y2);
// Line set to go one step in chosen direction so ignore if it hits
// anything
for (int i = 0; i < _nBars && linesCrossed; i++) {
// skip if not on module
if (x >= _bars[i].xmin && x <= _bars[i].xmax && ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) {
// Okay, it's a valid line calculate an intercept. Wow
// but all this arithmetic we must have loads of time
// both lines vertical and overlap in x and y so they
// cross
if (_bars[i].dx == 0)
linesCrossed = false;
else {
int32 ldx = x - _bars[i].x1;
int32 yc = _bars[i].y1 + (_bars[i].dy * ldx) / _bars[i].dx;
// the intercept overlaps
if (yc >= ymin - 1 && yc <= ymax + 1)
linesCrossed = false;
}
}
}
return linesCrossed;
}
int32 Router::checkTarget(int32 x, int32 y) {
int32 onLine = 0;
int32 xmin = x - 1;
int32 xmax = x + 1;
int32 ymin = y - 1;
int32 ymax = y + 1;
// check if point +- 1 is on the line
// so ignore if it hits anything
for (int i = 0; i < _nBars && onLine == 0; i++) {
// overlapping line
if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax && ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) {
int32 xc, yc;
// okay this line overlaps the target calculate an y intercept for x
// vertical line so we know it overlaps y
if (_bars[i].dx == 0)
yc = 0;
else {
int ldx = x - _bars[i].x1;
yc = _bars[i].y1 + (_bars[i].dy * ldx) / _bars[i].dx;
}
// overlapping point for y
if (yc >= ymin && yc <= ymax) {
// target on a line so drop out
onLine = 3;
debug(5, "RouteFail due to target on a line %d %d", x, y);
} else {
// vertical line so we know it overlaps y
if (_bars[i].dy == 0)
xc = 0;
else {
int32 ldy = y - _bars[i].y1;
xc = _bars[i].x1 + (_bars[i].dx * ldy) / _bars[i].dy;
}
// skip if not on module
if (xc >= xmin && xc <= xmax) {
// target on a line so drop out
onLine = 3;
debug(5, "RouteFail due to target on a line %d %d", x, y);
}
}
}
}
return onLine;
}
// ****************************************************************************
// * THE SETUP ROUTINES
// ****************************************************************************
int32 Router::LoadWalkResources(Object *megaObject, int32 x, int32 y, int32 dir) {
WalkGridHeader floorHeader;
int32 i;
uint8 *fPolygrid;
uint8 *fMegaWalkData;
int32 floorId;
int32 walkGridResourceId;
Object *floorObject;
int32 cnt;
uint32 cntu;
// load in floor grid for current mega
floorId = megaObject->o_place;
//floorObject = (object *) Lock_object(floorId);
floorObject = _objMan->fetchObject(floorId);
walkGridResourceId = floorObject->o_resource;
//Unlock_object(floorId);
//ResOpen(walkGridResourceId); // mouse wiggle
//fPolygrid = ResLock(walkGridResourceId); // mouse wiggle
fPolygrid = (uint8*)_resMan->openFetchRes(walkGridResourceId);
fPolygrid += sizeof(Header);
memcpy(&floorHeader,fPolygrid,sizeof(WalkGridHeader));
fPolygrid += sizeof(WalkGridHeader);
_nBars = _resMan->getUint32(floorHeader.numBars);
if (_nBars >= O_GRID_SIZE) {
#ifdef DEBUG //check for id > number in file,
error("RouteFinder Error too many _bars %d", _nBars);
#endif
_nBars = 0;
}
_nNodes = _resMan->getUint32(floorHeader.numNodes)+1; //array starts at 0 begins at a start _node has nnodes nodes and a target _node
if (_nNodes >= O_GRID_SIZE) {
#ifdef DEBUG //check for id > number in file,
error("RouteFinder Error too many nodes %d", _nNodes);
#endif
_nNodes = 0;
}
/*memmove(&_bars[0],fPolygrid,_nBars*sizeof(BarData));
fPolygrid += _nBars*sizeof(BarData);//move pointer to start of _node data*/
for (cnt = 0; cnt < _nBars; cnt++) {
_bars[cnt].x1 = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].y1 = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].x2 = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].y2 = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].xmin = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].ymin = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].xmax = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].ymax = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].dx = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].dy = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_bars[cnt].co = _resMan->readUint32(fPolygrid); fPolygrid += 4;
}
/*j = 1;// leave _node 0 for start _node
do {
memmove(&_node[j].x,fPolygrid,2*sizeof(int16));
fPolygrid += 2*sizeof(int16);
j ++;
} while (j < _nNodes);//array starts at 0*/
for (cnt = 1; cnt < _nNodes; cnt++) {
_node[cnt].x = _resMan->readUint16(fPolygrid); fPolygrid += 2;
_node[cnt].y = _resMan->readUint16(fPolygrid); fPolygrid += 2;
}
//ResUnlock(walkGridResourceId); // mouse wiggle
//ResClose(walkGridResourceId); // mouse wiggle
_resMan->resClose(walkGridResourceId);
// floor grid loaded
// copy the mega structure into the local variables for use in all subroutines
_startX = megaObject->o_xcoord;
_startY = megaObject->o_ycoord;
_startDir = megaObject->o_dir;
_targetX = x;
_targetY= y;
_targetDir = dir;
_scaleA = megaObject->o_scale_a;
_scaleB = megaObject->o_scale_b;
//ResOpen(megaObject->o_mega_resource); // mouse wiggle
//fMegaWalkData = ResLock(megaObject->o_mega_resource); // mouse wiggle
fMegaWalkData = (uint8*)_resMan->openFetchRes(megaObject->o_mega_resource);
// Apparently this resource is in little endian in both the Mac and the PC version
_nWalkFrames = fMegaWalkData[0];
_nTurnFrames = fMegaWalkData[1];
fMegaWalkData += 2;
for (cnt = 0; cnt < NO_DIRECTIONS * (_nWalkFrames + 1 + _nTurnFrames); cnt++) {
_dx[cnt] = (int32)_resMan->readLEUint32(fMegaWalkData);
fMegaWalkData += 4;
}
for (cnt = 0; cnt < NO_DIRECTIONS * (_nWalkFrames + 1 + _nTurnFrames); cnt++) {
_dy[cnt] = (int32)_resMan->readLEUint32(fMegaWalkData);
fMegaWalkData += 4;
}
/*memmove(&_dx[0],fMegaWalkData,NO_DIRECTIONS*(_nWalkFrames+1+_nTurnFrames)*sizeof(int32));
fMegaWalkData += NO_DIRECTIONS*(_nWalkFrames+1+_nTurnFrames)*sizeof(int32);
memmove(&_dy[0],fMegaWalkData,NO_DIRECTIONS*(_nWalkFrames+1+_nTurnFrames)*sizeof(int32));
fMegaWalkData += NO_DIRECTIONS*(_nWalkFrames+1+_nTurnFrames)*sizeof(int32);*/
for (cntu = 0; cntu < NO_DIRECTIONS; cntu++) {
_modX[cntu] = (int32)_resMan->readLEUint32(fMegaWalkData);
fMegaWalkData += 4;
}
for (cntu = 0; cntu < NO_DIRECTIONS; cntu++) {
_modY[cntu] = (int32)_resMan->readLEUint32(fMegaWalkData);
fMegaWalkData += 4;
}
/*memmove(&_modX[0],fMegaWalkData,NO_DIRECTIONS*sizeof(int32));
fMegaWalkData += NO_DIRECTIONS*sizeof(int32);
memmove(&_modY[0],fMegaWalkData,NO_DIRECTIONS*sizeof(int32));
fMegaWalkData += NO_DIRECTIONS*sizeof(int32);*/
//ResUnlock(megaObject->o_mega_resource); // mouse wiggle
//ResClose(megaObject->o_mega_resource); // mouse wiggle
_resMan->resClose(megaObject->o_mega_resource);
_diagonalx = _modX[3]; //36
_diagonaly = _modY[3]; //8
// mega data ready
// finish setting grid by putting mega _node at begining
// and target _node at end and reset current values
_node[0].x = _startX;
_node[0].y = _startY;
_node[0].level = 1;
_node[0].prev = 0;
_node[0].dist = 0;
i=1;
do {
_node[i].level = 0;
_node[i].prev = 0;
_node[i].dist = 9999;
i=i+1;
} while (i < _nNodes);
_node[_nNodes].x = _targetX;
_node[_nNodes].y = _targetY;
_node[_nNodes].level = 0;
_node[_nNodes].prev = 0;
_node[_nNodes].dist = 9999;
return 1;
}
// ****************************************************************************
// * THE ROUTE EXTRACTOR
// ****************************************************************************
void Router::extractRoute() {
/*********************************************************************
* extractRoute gets route from the node data after a full scan, route
* is written with just the basic way points and direction options for
* heading to the next point.
*********************************************************************/
int32 prev;
int32 prevx;
int32 prevy;
int32 last;
int32 point;
int32 dirx;
int32 diry;
int32 dir;
int32 ldx;
int32 ldy;
int32 p;
// extract the route from the _node data
prev = _nNodes;
last = prev;
point = O_ROUTE_SIZE - 1;
_route[point].x = _node[last].x;
_route[point].y = _node[last].y;
do {
point--;
prev = _node[last].prev;
prevx = _node[prev].x;
prevy = _node[prev].y;
_route[point].x = prevx;
_route[point].y = prevy;
last = prev;
} while (prev > 0);
// now shuffle route down in the buffer
_routeLength = 0;
do {
_route[_routeLength].x = _route[point].x;
_route[_routeLength].y = _route[point].y;
point++;
_routeLength++;
} while (point < O_ROUTE_SIZE);
_routeLength--;
// okay the route exists as a series point now put in some directions
for (p = 0; p < _routeLength; ++p) {
ldx = _route[p + 1].x - _route[p].x;
ldy = _route[p + 1].y - _route[p].y;
dirx = 1;
diry = 1;
if (ldx < 0) {
ldx = -ldx;
dirx = -1;
}
if (ldy < 0) {
ldy = -ldy;
diry = -1;
}
if (_diagonaly * ldx > _diagonalx * ldy) {
// dir = 1,2 or 2,3 or 5,6 or 6,7
// 2 or 6
dir = 4 - 2 * dirx;
_route[p].dirS = dir;
// 1, 3, 5 or 7
dir = dir + diry * dirx;
_route[p].dirD = dir;
} else {
// dir = 7,0 or 0,1 or 3,4 or 4,5
// 0 or 4
dir = 2 + 2 * diry;
_route[p].dirS = dir;
// 2 or 6
dir = 4 - 2 * dirx;
// 1, 3, 5 or 7
dir = dir + diry * dirx;
_route[p].dirD = dir;
}
}
// set the last dir to continue previous route unless specified
if (_targetDir == NO_DIRECTIONS) {
// ANY direction
_route[p].dirS = _route[p - 1].dirS;
_route[p].dirD = _route[p - 1].dirD;
} else {
_route[p].dirS = _targetDir;
_route[p].dirD = _targetDir;
}
return;
}
#define DIAGONALX 36
#define DIAGONALY 8
int whatTarget(int32 startX, int32 startY, int32 destX, int32 destY) {
int tar_dir;
//setting up
int deltaX = destX-startX;
int deltaY = destY-startY;
int signX = (deltaX > 0);
int signY = (deltaY > 0);
int slope;
if ((ABS(deltaY) * DIAGONALX) < (ABS(deltaX) * DIAGONALY / 2))
slope = 0;// its flat
else if ((ABS(deltaY) * DIAGONALX / 2) > (ABS(deltaX) * DIAGONALY))
slope = 2;// its vertical
else
slope = 1;// its diagonal
if (slope == 0) { //flat
if (signX == 1) // going right
tar_dir = 2;
else
tar_dir = 6;
} else if (slope == 2) { //vertical
if (signY == 1) // going down
tar_dir = 4;
else
tar_dir = 0;
} else if (signX == 1) { //right diagonal
if (signY == 1) // going down
tar_dir = 3;
else
tar_dir = 1;
} else { //left diagonal
if (signY == 1) // going down
tar_dir = 5;
else
tar_dir = 7;
}
return tar_dir;
}
void Router::setPlayerTarget(int32 x, int32 y, int32 dir, int32 stance) {
_playerTargetX = x;
_playerTargetY = y;
_playerTargetDir = dir;
_playerTargetStance = stance;
}
} // End of namespace Sword1