/* ScummVM - Scumm Interpreter * Copyright (C) Revolution Software Ltd. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ * */ #include "stdafx.h" #include "router.h" #include "util.h" #include "objectman.h" #include "resman.h" #include "sworddefs.h" #define SLOW_IN 3 #define SLOW_OUT 7 SwordRouter::SwordRouter(ObjectMan *pObjMan, ResMan *pResMan) { _objMan = pObjMan; _resMan = pResMan; _numExtraBars = _numExtraNodes = 0; _nNodes = _nBars = 0; _playerTargetX = _playerTargetY = _playerTargetDir = _playerTargetStance = 0; } int SwordRouter::routeFinder(int32 id, BsObject *mega, int32 x, int32 y, int32 targetDir) { loadWalkResources(id, mega, x, y, targetDir); // init vars for subs // init offset pointers FrameInfos frameInfo; frameInfo.framesPerStep = _nWalkFrames / 2; frameInfo.framesPerChar = _nWalkFrames * NO_DIRECTIONS; frameInfo.standFrames = frameInfo.framesPerChar; frameInfo.turnFramesLeft = frameInfo.framesPerChar; frameInfo.turnFramesRight = frameInfo.framesPerChar; frameInfo.walkFramesLeft = 0; frameInfo.walkFramesRight = 0; frameInfo.slowInFrames = 0; frameInfo.slowOutFrames = 0; frameInfo.startX = mega->o_xcoord; frameInfo.startY = mega->o_ycoord; frameInfo.targetX = x; frameInfo.targetY = y; frameInfo.targetDir = targetDir; frameInfo.scaleA = mega->o_scale_a; frameInfo.scaleB = mega->o_scale_b; if (id == GEORGE) { frameInfo.turnFramesLeft = 3 * frameInfo.framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN + 4 * SLOW_OUT; frameInfo.turnFramesRight = 3 * frameInfo.framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN + 4 * SLOW_OUT + NO_DIRECTIONS; frameInfo.walkFramesLeft = frameInfo.framesPerChar + NO_DIRECTIONS; frameInfo.walkFramesRight = 2 * frameInfo.framesPerChar + NO_DIRECTIONS; frameInfo.slowInFrames = 3 * frameInfo.framesPerChar + NO_DIRECTIONS; frameInfo.slowOutFrames = 3 * frameInfo.framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN; } else if (id == NICO) { frameInfo.turnFramesLeft = frameInfo.framesPerChar + NO_DIRECTIONS; frameInfo.turnFramesRight = frameInfo.framesPerChar + 2 * NO_DIRECTIONS; frameInfo.walkFramesLeft = 0; frameInfo.walkFramesRight = 0; frameInfo.slowInFrames = 0; frameInfo.slowOutFrames = 0; } int32 routeFlag = getRoute(); int32 routeLength = 0; if (routeFlag == 1) { // extract the route as nodes and the directions to go between each node // route.X,route.Y and route.Dir now hold all the route infomation with // the target dir or route continuation routeLength = extractRoute(targetDir); } int32 solidFlag = 0; if (routeFlag == 2) //special case for zero length route { if (targetDir >7)// if target direction specified as any targetDir = mega->o_dir; // 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 = mega->o_dir; _modularPath[0].num = 0; _modularPath[0].x = mega->o_xcoord; _modularPath[0].y = mega->o_ycoord; _modularPath[1].dir = targetDir; _modularPath[1].num = 0; _modularPath[1].x = mega->o_xcoord; _modularPath[1].y = mega->o_ycoord; _modularPath[2].dir = 9; _modularPath[2].num = ROUTE_END_FLAG; slidyWalkAnimator(mega->o_route, &frameInfo, id); routeFlag = 2; } else if (routeFlag == 1) { // a normal route smoothestPath(mega->o_xcoord, mega->o_ycoord, mega->o_dir, routeLength);//Converts the route to an exact path // 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) { solidPath(mega->o_scale_a, mega->o_scale_b); solidFlag = solidWalkAnimator(mega->o_route, &frameInfo, id); } if (!solidFlag) { slidyPath(mega->o_scale_a, mega->o_scale_b, targetDir); slidyWalkAnimator(mega->o_route, &frameInfo, id); } } return routeFlag; } void SwordRouter::slidyPath(int32 scaleA, int32 scaleB, uint16 targetDir) { /**************************************************************************** * 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 * currently unused, but is intended for use when just clicking about. * * produce a module list from the line data * ****************************************************************************/ int32 smooth; int32 slidy; int32 scale; int32 stepX; int32 stepY; int32 deltaX; int32 deltaY; // strip out the short sections slidy = 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; while (_smoothPath[smooth].num < ROUTE_END_FLAG) { scale = scaleA * _smoothPath[smooth].y + scaleB; deltaX = _smoothPath[smooth].x - _modularPath[slidy-1].x; deltaY = _smoothPath[smooth].y - _modularPath[slidy-1].y; stepX = _modX[_smoothPath[smooth].dir]; stepY = _modY[_smoothPath[smooth].dir]; stepX = stepX * scale; stepY = stepY * scale; stepX = stepX >> 19;// quarter a step minimum stepY = stepY >> 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 += 1; } smooth += 1; } // 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 += 1; _modularPath[slidy].x = _smoothPath[smooth-1].x; _modularPath[slidy].y = _smoothPath[smooth-1].y; _modularPath[slidy].dir = 9; _modularPath[slidy].num = ROUTE_END_FLAG; } int32 SwordRouter::solidPath(int32 scaleA, int32 scaleB) { /**************************************************************************** * 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; 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 += 1; } smooth += 1; } 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; return 1; } int32 SwordRouter::solidWalkAnimator(WalkData *walkAnim, FrameInfos *frInfo, int32 megaId) { int32 p; int32 i; 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; p = 1; currentDir = _modularPath[1].dir; module = frInfo->framesPerChar + lastDir; moduleX = frInfo->startX; moduleY = frInfo->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 = frInfo->turnFramesLeft + lastDir; } else { module = frInfo->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 = frInfo->turnFramesLeft + lastDir; } else { if ( lastDir > 7) lastDir -= NO_DIRECTIONS; module = frInfo->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 = frInfo->framesPerStep; 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 do { while(_modularPath[p].num > 0) { currentDir = _modularPath[p].dir; if (currentDir< NO_DIRECTIONS) { module = currentDir * frInfo->framesPerStep * 2 + left; if (left == 0) left = frInfo->framesPerStep; else left = 0; moduleEnd = module + frInfo->framesPerStep; step = 0; scale = (frInfo->scaleA * moduleY + frInfo->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 -= frInfo->framesPerStep; if (left == 0) left = frInfo->framesPerStep; else left = 0; // 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) < frInfo->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 - frInfo->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 - frInfo->framesPerStep; do { walkAnim[frame].frame += 200; //was 60 now 116 frame += 1; } while(frame < lastCount ); } } // all turns checked lastCount = stepCount; } } } p = p + 1; lastDir = currentDir; slowStart = 0; //can only be valid first time round } while (_modularPath[p].dir < NO_DIRECTIONS); //**************************************************************************** // 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 - frInfo->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 - frInfo->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 = frInfo->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 //**************************************************************************** // Tdebug("RouteFinder RouteSize is %d", stepCount); // now check the route i = 0; do { if (!check(_modularPath[i].x, _modularPath[i].y, _modularPath[i+1].x, _modularPath[i+1].y)) p=0; i += 1; } while(itargetDir = _modularPath[p-1].dir; if (checkTarget(moduleX,moduleY) == 3)// new target on a line p = 0; } return p; } int32 SwordRouter::smoothestPath(uint16 startX, uint16 startY, uint16 startDir, int32 routeLength) { /* * 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 chosing 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 nuber 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 * */ int32 dirS; int32 dirD; int32 dS; int32 dD; int32 dSS; int32 dSD; int32 dDS; int32 dDD; int32 SS; int32 SD; int32 DS; int32 DD; int32 temp; int32 steps = 0; int32 option; int32 options; int32 lastDir; int32 nextDirS; int32 nextDirD; int32 tempturns[4]; int32 turns[4]; 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 (int32 p = 0; p < routeLength; p++) { dirS = _route[p].dirS; dirD = _route[p].dirD; nextDirS = _route[p+1].dirS; nextDirD = _route[p+1].dirD; // Check directions into and out of a pair of nodes // going in dS = dirS - lastDir; if ( dS < 0) dS = dS + NO_DIRECTIONS; dD = dirD - lastDir; if ( dD < 0) dD = dD + NO_DIRECTIONS; // coming out dSS = dirS - nextDirS; if ( dSS < 0) dSS = dSS + NO_DIRECTIONS; dDD = dirD - nextDirD; if ( dDD < 0) dDD = dDD + NO_DIRECTIONS; dSD = dirS - nextDirD; if ( dSD < 0) dSD = dSD + NO_DIRECTIONS; 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 SS = dS + dSS + 3; // Split routes look crap so weight against them SD = dS + dDD; DS = dD + dSS; DD = dD + dDD + 3; // set up turns as a sorted array of the turn values tempturns[0] = SS; turns[0] = 0; tempturns[1] = SD; turns[1] = 1; tempturns[2] = DS; turns[2] = 2; tempturns[3] = DD; turns[3] = 3; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (tempturns[j] > tempturns[j + 1]) { temp = turns[j]; turns[j] = turns[j+1]; turns[j+1] = temp; temp = tempturns[j]; tempturns[j] = tempturns[j+1]; tempturns[j+1] = temp; } } } // 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 options = newCheck(1, _route[p].x, _route[p].y, _route[p + 1].x, _route[p + 1].y); if (options == 0) error("BestTurns failed"); steps = 0; for (int i = 0; (i < 4) && (steps == 0); i++) { option = 1 << turns[i]; if (option & options) steps = smoothCheck(turns[i],p,dirS,dirD); } if (steps == 0) error("BestTurns failed"); // route.X route.Y route.dir and bestTurns start at far end } _smoothPath[steps].dir = 9; _smoothPath[steps].num = ROUTE_END_FLAG; return 1; } int32 SwordRouter::smoothCheck(int32 best, int32 p, int32 dirS, int32 dirD) { static int32 k; int32 tempK; int32 x; int32 y; int32 x2; int32 y2; int32 dx; int32 dy; int32 dsx; int32 dsy; int32 ddx; int32 ddy; int32 dirX; int32 dirY; int32 ss0; int32 ss1; int32 ss2; int32 sd0; int32 sd1; int32 sd2; if (p == 0) k = 1; tempK = 0; x = _route[p].x; y = _route[p].y; x2 = _route[p + 1].x; y2 = _route[p + 1].y; dx = x2 - x; dy = y2 - y; dirX = 1; dirY = 1; if (dx < 0) { dx = -dx; dirX = -1; } if (dy < 0) { dy = -dy; dirY = -1; } // set up sd0-ss2 to reflect possible movement in each direction if ((dirS == 0) || (dirS == 4)) { // vert and diag ddx = dx; ddy = (dx*_diagonaly)/_diagonalx; dsy = dy - 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 = dy; ddx = (dy*_diagonalx)/_diagonaly; dsx = dx - 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; } if (best == 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 = k + 1; _smoothPath[k].x = x+dsx/2+ddx; _smoothPath[k].y = y+dsy/2+ddy; _smoothPath[k].dir = dirD; _smoothPath[k].num = sd0; k = k + 1; _smoothPath[k].x = x+dsx+ddx; _smoothPath[k].y = y+dsy+ddy; _smoothPath[k].dir = dirS; _smoothPath[k].num = ss2; k = k + 1; tempK = k; } else if (best == 1) { //square, diagonal _smoothPath[k].x = x+dsx; _smoothPath[k].y = y+dsy; _smoothPath[k].dir = dirS; _smoothPath[k].num = ss0; k = k + 1; _smoothPath[k].x = x2; _smoothPath[k].y = y2; _smoothPath[k].dir = dirD; _smoothPath[k].num = sd0; k = k + 1; tempK = k; } else if (best == 2) { //diagonal square _smoothPath[k].x = x+ddx; _smoothPath[k].y = y+ddy; _smoothPath[k].dir = dirD; _smoothPath[k].num = sd0; k = k + 1; _smoothPath[k].x = x2; _smoothPath[k].y = y2; _smoothPath[k].dir = dirS; _smoothPath[k].num = ss0; k = k + 1; tempK = k; } else { //halfdiagonal, square, halfdiagonal _smoothPath[k].x = x+ddx/2; _smoothPath[k].y = y+ddy/2; _smoothPath[k].dir = dirD; _smoothPath[k].num = sd1; k = k + 1; _smoothPath[k].x = x+dsx+ddx/2; _smoothPath[k].y = y+dsy+ddy/2; _smoothPath[k].dir = dirS; _smoothPath[k].num = ss0; k = k + 1; _smoothPath[k].x = x2; _smoothPath[k].y = y2; _smoothPath[k].dir = dirD; _smoothPath[k].num = sd2; k = k + 1; tempK = k; } return tempK; } void SwordRouter::slidyWalkAnimator(WalkData *walkAnim, FrameInfos *frInfo, int32 megaId) { /**************************************************************************** * 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 * ****************************************************************************/ static int32 left = 0; 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; lastDir = _modularPath[0].dir; currentDir = _modularPath[1].dir; if (currentDir == NO_DIRECTIONS) currentDir = lastDir; moduleX = frInfo->startX; moduleY = frInfo->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 = frInfo->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 = frInfo->turnFramesLeft + lastDir; } else { module = frInfo->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 if ( lastDir < 0) lastDir += NO_DIRECTIONS; module = frInfo->turnFramesLeft + lastDir; } else { if ( lastDir > 7) lastDir -= NO_DIRECTIONS; module = frInfo->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 //**************************************************************************** if (left == 0) left = frInfo->framesPerStep; 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 p = 0; 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 * frInfo->framesPerStep * 2 + left; if (left == 0) left = frInfo->framesPerStep; else left = 0; moduleEnd = module + frInfo->framesPerStep; step = 0; scale = (frInfo->scaleA * moduleY + frInfo->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 > frInfo->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 -= frInfo->framesPerStep; if (left == 0) left = frInfo->framesPerStep; else left = 0; } } else { if (3*abs(lastErrorX) < abs(errorX)) { //the last stop was closest stepCount -= frInfo->framesPerStep; if (left == 0) left = frInfo->framesPerStep; else left = 0; } } } 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(frameCountframesPerStep) 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 - frInfo->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 - frInfo->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("SlidyWalkAnimator direction error"); if (frInfo->targetDir == NO_DIRECTIONS) { // stand in the last direction module = frInfo->standFrames + lastRealDir; frInfo->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 (frInfo->targetDir == 9) { if (stepCount == 0) { module = frInfo->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 (frInfo->targetDir != lastRealDir) { // rotate to targetDir // rotate to target direction turnDir = frInfo->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 = frInfo->turnFramesLeft + lastDir; else module = frInfo->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 != frInfo->targetDir) { lastRealDir += turnDir; if ( turnDir < 0) { // new frames for turn frames 29oct95jps if ( lastRealDir < 0) lastRealDir += NO_DIRECTIONS; module = frInfo->turnFramesLeft + lastRealDir; } else { if ( lastRealDir > 7) lastRealDir -= NO_DIRECTIONS; module = frInfo->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 = frInfo->standFrames + lastRealDir; walkAnim[stepCount-1].frame = module; } else { // just stand at the end module = frInfo->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; } /**************************************************************************** * 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 SwordRouter::extractRoute(int32 targetDir) { int32 prev; int32 prevx; int32 prevy; int32 last; int32 point; int32 dirx; int32 diry; int32 dir; // 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; while (prev > 0) { point = point - 1; prev = _node[last].prev; prevx = _node[prev].x; prevy = _node[prev].y; _route[point].x = prevx; _route[point].y = prevy; last = prev; } int32 routeLength = 0; do { _route[routeLength].x = _route[point].x; _route[routeLength].y = _route[point].y; point = point + 1; routeLength = routeLength + 1; } while (point < O_ROUTE_SIZE); for (int p = 0; p < routeLength; p++) { int32 dx = _route[p+1].x - _route[p].x; int32 dy = _route[p+1].y - _route[p].y; dirx = 1; diry = 1; if (dx < 0) { dx = -dx; dirx = -1; } if (dy < 0) { dy = -dy; diry = -1; } if ((_diagonaly * dx) > (_diagonalx * dy)) // dir = 1,2 or 2,3 or 5,6 or 6,7 { dir = 4 - 2 * dirx; // 2 or 6 _route[p].dirS = dir; dir = dir + diry * dirx; // 1,3,5 or 7 _route[p].dirD = dir; } else // dir = 7,0 or 0,1 or 3,4 or 4,5 { dir = 2 + 2 * diry; // 0 or 4 _route[p].dirS = dir; dir = 4 - 2 * dirx; // 2 or 6 dir = dir + diry * dirx; // 1,3,5 or 7 _route[p].dirD = dir; } } if (targetDir == 8) // ANY direction { _route[routeLength].dirS = _route[routeLength-1].dirS; _route[routeLength].dirD = _route[routeLength-1].dirD; } else { _route[routeLength].dirS = targetDir; _route[routeLength].dirD = targetDir; } return routeLength; } int SwordRouter::getRoute() { int32 routeRes; if ((_node[0].x == _node[_nNodes].x) && (_node[0].y == _node[_nNodes].y)) routeRes = 2; // start is the same as destination else routeRes = checkTarget(_node[_nNodes].x, _node[_nNodes].y); if (routeRes == 0) { // still looking for a route check if target is within a pixel of a line int32 level = 1; int changed; do { changed = scan(level); level++; } while(changed == 1); if (_node[_nNodes].dist < 9999) // did we reach the target? routeRes = 1; } return routeRes; } int SwordRouter::scan(int32 level) { int32 distance; int changed = 0; // 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 (int32 i = 0; i < _nNodes; i++) { if ((_node[i].dist < _node[_nNodes].dist) && (_node[i].level == level)) { int16 x1 = _node[i].x; int16 y1 = _node[i].y; for (int32 k = _nNodes; k > 0; k--) { if (_node[k].dist > _node[i].dist) { int16 x2 = _node[k].x; int16 y2 = _node[k].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[k].dist)) { //if (int temp = newCheck(0, x1, y1, x2, y2)) { if (newCheck(0, x1, y1, x2, y2)) { _node[k].level = level + 1; _node[k].dist = distance + _node[i].dist; _node[k].prev = i; changed = 1; } } } } } } return changed; } /******************************************************************************* * 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 *******************************************************************************/ int SwordRouter::newCheck(int32 status, int16 x1, int16 x2, int16 y1, int16 y2) { 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 = 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 = 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++; } } } } //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 = 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 = 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 = 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++; } } } } //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 = options + 8; } } } } } if (status == 0) status = steps; else status = options; return status; } int SwordRouter::check(int16 x1, int16 y1, int16 x2, int16 y2) { //call the fastest line check for the given line //returns 1 if line didn't cross any bars int steps; if ((x1 == x2) && (y1 == y2)) steps = 1; else if (x1 == x2) steps = vertCheck(x1, y1, y2); else if (y1 == y2) steps = horizCheck(x1, y1, x2); else steps = lineCheck(x1, y1, x2, y2); return steps; } int SwordRouter::horizCheck(int16 x1, int16 y, int16 x2) { int32 ldy; int32 i; int32 xc; int32 xmin; int32 xmax; int32 linesCrossed = 1; if (x1 > x2) { xmin = x2; xmax = x1; } else { xmin = x1; xmax = x2; } // line set to go one step in chosen direction // so ignore if it hits anything i = 0; do { // skip if not on module if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax) { // skip if not on module if (y >= _bars[i].ymin && y <= _bars[i].ymax) { // okay its a valid line calculate an intercept // wow but all this arithmetic we must have // loads of time if (_bars[i].dy == 0) linesCrossed = 0; else { ldy = y - _bars[i].y1; xc = _bars[i].x1 + (_bars[i].dx * ldy) / _bars[i].dy; // skip if not on module if (xc >= xmin - 1 && xc <= xmax + 1) linesCrossed = 0; } } } i++; } while (i < _nBars && linesCrossed); return linesCrossed; } int SwordRouter::vertCheck(int16 x, int16 y1, int16 y2) { int32 ldx; int32 i; int32 yc; int32 ymin; int32 ymax; int32 linesCrossed = 1; if (y1 > y2) { ymin = y2; ymax = y1; } else { ymin = y1; ymax = y2; } // line set to go one step in chosen direction // so ignore if it hits anything i = 0; do { if (x >= _bars[i].xmin && x <= _bars[i].xmax) { // overlapping // skip if not on module if (ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) { // okay its 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 = 0; else { ldx = x - _bars[i].x1; yc = _bars[i].y1 + (_bars[i].dy * ldx) / _bars[i].dx; // the intercept overlaps if (yc >= ymin - 1 && yc <= ymax + 1) linesCrossed = 0; } } } i++; } while (i < _nBars && linesCrossed); return linesCrossed; } int SwordRouter::lineCheck(int16 x1, int16 y1, int16 x2, int16 y2) { int32 dirx; int32 diry; int32 co; int32 slope; int32 i; int32 xc; int32 yc; int32 xmin; int32 ymin; int32 xmax; int32 ymax; int32 linesCrossed = 1; if (x1 > x2) { xmin = x2; xmax = x1; } else { xmin = x1; xmax = x2; } if (y1 > y2) { ymin = y2; ymax = y1; } else { ymin = y1; ymax = y2; } // line set to go one step in chosen direction // so ignore if it hits anything dirx = x2 - x1; diry = y2 - y1; co = (y1 * dirx)- (x1 * diry); // new line equation i = 0; do { // skip if not on module if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax) { // skip if not on module if (ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) { // okay its 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 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 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) { 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 = 0; } } } } } } } i++; } while (i < _nBars && linesCrossed); return linesCrossed; } int SwordRouter::checkTarget(int16 x, int16 y) { int32 dx, dy, xc, yc, xmin, xmax, ymin, ymax; int32 onLine = 0; xmin = x - 1; xmax = x + 1; ymin = y - 1; ymax = y + 1; for (int i = 0; (i < _nBars) && (onLine == 0); i++) { if ((xmax >= _bars[i].xmin) && (xmin <= _bars[i].xmax)) { //overlapping line if ((ymax >= _bars[i].ymin) && ( ymin <= _bars[i].ymax)) { //overlapping line // okay this line overlaps the target, calculate an y intersept for x if (_bars[i].dx == 0) // vertical line so we know it overlaps y yc = 0; else { dx = x - _bars[i].x1; yc = _bars[i].y1 + (_bars[i].dy * dx) / _bars[i].dx; } if ((yc >= ymin) && (yc <= ymax)) //overlapping point for y onLine = 3; // target on a line so drop out else { if (_bars[i].dy == 0) // vertical line so we know it overlaps y xc = 0; else { dy = y- _bars[i].y1; xc = _bars[i].x1 + (_bars[i].dx * dy) / _bars[i].dy; } if ((xc >= xmin) && (xc <= xmax)) //skip if not on module onLine = 3;// target on a line so drop out } } } } return onLine; } void SwordRouter::resetExtraData(void) { _numExtraBars = _numExtraNodes = 0; } void SwordRouter::setPlayerTarget(int32 x, int32 y, int32 dir, int32 stance) { _playerTargetX = x; _playerTargetY = y; _playerTargetDir = dir; _playerTargetStance = stance; } void SwordRouter::loadWalkResources(int32 megaId, BsObject *mega, int32 x, int32 y, int32 dir) { WalkGridHeader *floorHeader; int32 walkGridId = _objMan->fetchObject(mega->o_place)->o_resource; uint8 *fPolyGrid = (uint8*)_resMan->openFetchRes(walkGridId); floorHeader = (WalkGridHeader*)(fPolyGrid + sizeof(Header)); fPolyGrid += sizeof(WalkGridHeader) + sizeof(Header); _nBars = FROM_LE_32(floorHeader->numBars); _nNodes = FROM_LE_32(floorHeader->numNodes) + 1; if ((_nBars >= O_GRID_SIZE) || (_nNodes >= O_GRID_SIZE)) error("loadWalkResources: resource has %d bars and %d nodes", _nBars, _nNodes); for (int32 cnt = 0; cnt < _nBars; cnt++) { _bars[cnt].x1 = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].y1 = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].x2 = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].y2 = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].xmin = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].ymin = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].xmax = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].ymax = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].dx = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].dy = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _bars[cnt].co = READ_LE_UINT32(fPolyGrid); fPolyGrid += 4; } //_nBars = 0; // leave node 0 for start node for (int32 cnt = 1; cnt < _nNodes; cnt++) { _node[cnt].x = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; _node[cnt].y = READ_LE_UINT16(fPolyGrid); fPolyGrid += 2; } _resMan->resClose(walkGridId); // floor grid loaded. Copy george's extra bars and nodes. if (megaId == GEORGE) { memcpy(_bars + _nBars, _extraBars, _numExtraBars * sizeof(BarData)); _nBars += _numExtraBars; for (int32 cnt = 0; cnt < _numExtraNodes; cnt++) { _node[_nNodes + cnt].x = _extraNodes[cnt].x; _node[_nNodes + cnt].y = _extraNodes[cnt].y; } _nNodes += _numExtraNodes; } uint8 *walkData = (uint8*)_resMan->openFetchRes(mega->o_mega_resource); _nWalkFrames = walkData[0]; _nTurnFrames = walkData[1]; walkData += 2; for (int32 cnt = 0; cnt < NO_DIRECTIONS * (_nWalkFrames + 1 + _nTurnFrames); cnt++) { _dx[cnt] = READ_LE_UINT32(walkData); walkData += 4; } for (int32 cnt = 0; cnt < NO_DIRECTIONS * (_nWalkFrames + 1 + _nTurnFrames); cnt++) { _dy[cnt] = READ_LE_UINT32(walkData); walkData += 4; } for (int32 cnt = 0; cnt < NO_DIRECTIONS; cnt++) { _modX[cnt] = READ_LE_UINT32(walkData); walkData += 4; } for (int32 cnt = 0; cnt < NO_DIRECTIONS; cnt++) { _modY[cnt] = READ_LE_UINT32(walkData); walkData += 4; } _resMan->resClose(mega->o_mega_resource); _diagonalx = _modX[3]; _diagonaly = _modY[3]; if ((_diagonalx != 36) || (_diagonaly != 8)) warning("DiagX = %d, DiagY = %d", _diagonalx, _diagonaly); // mega data ready // finish setting grid by putting mega node at begining // and target node at end and reset current values _node[0].x = mega->o_xcoord; // the start _node[0].y = mega->o_ycoord; // _node[0].dist = 0; _node[0].prev = 0; _node[0].level = 1; for (int32 cnt = 1; cnt <= _nNodes; cnt++) { _node[cnt].dist = 9999; _node[cnt].prev = 0; _node[cnt].level = 0; } _node[_nNodes].x = x; // the destination _node[_nNodes].y = y; // } int SwordRouter::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; }