diff options
Diffstat (limited to 'engines/sword2/router.cpp')
-rw-r--r-- | engines/sword2/router.cpp | 2506 |
1 files changed, 2506 insertions, 0 deletions
diff --git a/engines/sword2/router.cpp b/engines/sword2/router.cpp new file mode 100644 index 0000000000..bbca609e3c --- /dev/null +++ b/engines/sword2/router.cpp @@ -0,0 +1,2506 @@ +/* Copyright (C) 1994-1998 Revolution Software Ltd. + * Copyright (C) 2003-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + */ + +// --------------------------------------------------------------------------- +// ROUTER.CPP by James +// +// A rehash of Jeremy's original jrouter.c, containing low-level system +// routines for calculating routes between points inside a walk-grid, and +// constructing walk animations from mega-sets. +// +// jrouter.c underwent 2 major reworks from the original: +// (1) Restructured to allow more flexibility in the mega-sets, ie. more info +// taken from the walk-data +// - the new George & Nico mega-sets & walk-data were then tested & +// tweaked in the Sword1 system +// (2) Updated for the new Sword2 system, ie. new object structures +// - now compatible with Sword2, the essential code already having been +// tested +// +// --------------------------------------------------------------------------- + +/**************************************************************************** + * 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 ??? + * + **************************************************************************** + * + * TOTALLY REHASHED BY JAMES FOR NEW MEGAS USING OLD SYSTEM + * THEN REINCARNATED BY JAMES FOR NEW MEGAS USING NEW SYSTEM + * + ****************************************************************************/ + +#include "common/stdafx.h" +#include "common/stream.h" +#include "sword2/sword2.h" +#include "sword2/defs.h" +#include "sword2/logic.h" +#include "sword2/resman.h" +#include "sword2/router.h" + +namespace Sword2 { + +//---------------------------------------------------------- +// (4) WALK-GRID FILES +//---------------------------------------------------------- +// a walk-grid file consists of: +// +// standard file header +// walk-grid file header +// walk-grid data + +// Walk-Grid Header - taken directly from old "header.h" in STD_INC + +struct WalkGridHeader { + int32 numBars; // number of bars on the floor + int32 numNodes; // number of nodes +}; + +uint8 Router::returnSlotNo(uint32 megaId) { + if (_vm->_logic->readVar(ID) == CUR_PLAYER_ID) { + // George (8) + return 0; + } else { + // One of Nico's mega id's + return 1; + } +} + +void Router::allocateRouteMem() { + uint8 slotNo; + + // Player character always always slot 0, while the other mega + // (normally Nico) always uses slot 1 + // Better this way, so that if mega object removed from memory while + // in middle of route, the old route will be safely cleared from + // memory just before they create a new one + + slotNo = returnSlotNo(_vm->_logic->readVar(ID)); + + // if this slot is already used, then it can't be needed any more + // because this id is creating a new route! + + if (_routeSlots[slotNo]) + freeRouteMem(); + + _routeSlots[slotNo] = (WalkData *)malloc(sizeof(WalkData) * O_WALKANIM_SIZE); + + // 12000 bytes were used for this in Sword1 mega compacts, based on + // 20 bytes per 'WalkData' frame + // ie. allowing for 600 frames including end-marker + // Now 'WalkData' is 8 bytes, so 8*600 = 4800 bytes. + // Note that a 600 frame walk lasts about 48 seconds! + // (600fps / 12.5s = 48s) + + // mega keeps note of which slot contains the pointer to it's walk + // animation mem block + // +1 so that '0' can mean "not walking" + // megaObject->route_slot_id = slotNo + 1; +} + +WalkData *Router::getRouteMem() { + uint8 slotNo = returnSlotNo(_vm->_logic->readVar(ID)); + + return (WalkData *)_routeSlots[slotNo]; +} + +void Router::freeRouteMem() { + uint8 slotNo = returnSlotNo(_vm->_logic->readVar(ID)); + + free(_routeSlots[slotNo]); + _routeSlots[slotNo] = NULL; +} + +void Router::freeAllRouteMem() { + for (int i = 0; i < TOTAL_ROUTE_SLOTS; i++) { + free(_routeSlots[i]); + _routeSlots[i] = NULL; + } +} + +int32 Router::routeFinder(byte *ob_mega, byte *ob_walkdata, 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; + + setUpWalkGrid(ob_mega, x, y, dir); + loadWalkData(ob_walkdata); + + walkAnim = getRouteMem(); + + // 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 + +#ifndef FORCE_SLIDY + if (_targetDir == 8) { + // 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); + } +#endif + + 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 + // neighbour 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]; + 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 + + int32 SS = dS + dSS + 3; + int32 SD = dS + dDD; + int32 DS = dD + dSS; + int32 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 (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); + + i = 0; + steps = 0; + + do { + int32 opt = 1 << turns[i]; + if (options & opt) + steps = smoothCheck(turns[i], p, dirS, dirD); + i++; + } while (steps == 0 && i < 4); + + assert(steps); + + // 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; +} + +int32 Router::smoothCheck(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 + *********************************************************************/ + + static int32 k; + 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; + } + + return k; +} + +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; + slidy++; +} + +// SLOW IN + +bool Router::addSlowInFrames(WalkData *walkAnim) { + if (_walkData.usingSlowInFrames && _modularPath[1].num > 0) { + for (int slowInFrameNo = 0; slowInFrameNo < _walkData.nSlowInFrames[_currentDir]; slowInFrameNo++) { + walkAnim[_stepCount].frame = _firstSlowInFrame[_currentDir] + slowInFrameNo; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = _currentDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + return true; + } + + return false; +} + +void Router::earlySlowOut(byte *ob_mega, byte *ob_walkdata) { + int32 slowOutFrameNo; + int32 walk_pc; + WalkData *walkAnim; + + ObjectMega obMega(ob_mega); + + debug(5, "EARLY SLOW-OUT"); + + loadWalkData(ob_walkdata); + + debug(5, "********************************"); + debug(5, "_framesPerStep = %d", _framesPerStep); + debug(5, "_numberOfSlowOutFrames = %d", _numberOfSlowOutFrames); + debug(5, "_firstWalkingTurnLeftFrame = %d", _firstWalkingTurnLeftFrame); + debug(5, "_firstWalkingTurnRightFrame = %d", _firstWalkingTurnRightFrame); + debug(5, "_firstSlowOutFrame = %d", _firstSlowOutFrame); + debug(5, "********************************"); + + walk_pc = obMega.getWalkPc(); + + walkAnim = getRouteMem(); + + // if this mega does actually have slow-out frames + if (_walkData.usingSlowOutFrames) { + // overwrite the next step (half a cycle) of the walk + // (ie .step - 0..5) + + do { + debug(5, "STEP NUMBER: walkAnim[%d].step = %d", walk_pc, walkAnim[walk_pc].step); + debug(5, "ORIGINAL FRAME: walkAnim[%d].frame = %d", walk_pc, walkAnim[walk_pc].frame); + + // map from existing walk frame across to correct + // frame number of slow-out - remember, there may be + // more slow-out frames than walk-frames! + + if (walkAnim[walk_pc].frame >= _firstWalkingTurnRightFrame) { + // if it's a walking turn-right, rather than a + // normal step, then map it to a normal step + // frame first + + walkAnim[walk_pc].frame -= _firstWalkingTurnRightFrame; + debug(5, "MAPPED TO WALK: walkAnim[%d].frame = %d (walking turn-right frame --> walk frame)", walk_pc, walkAnim[walk_pc].frame); + } else if (walkAnim[walk_pc].frame >= _firstWalkingTurnLeftFrame) { + // if it's a walking turn-left, rather than a + // normal step, then map it to a normal step + // frame first + + walkAnim[walk_pc].frame -= _firstWalkingTurnLeftFrame; + debug(5, "MAPPED TO WALK: walkAnim[%d].frame = %d (walking turn-left frame --> walk frame)", walk_pc, walkAnim[walk_pc].frame); + } + + walkAnim[walk_pc].frame += _firstSlowOutFrame + ((walkAnim[walk_pc].frame / _framesPerStep) * (_numberOfSlowOutFrames - _framesPerStep)); + walkAnim[walk_pc].step = 0; + debug(5, "SLOW-OUT FRAME: walkAnim[%d].frame = %d",walk_pc, walkAnim[walk_pc].frame); + walk_pc++; + } while (walkAnim[walk_pc].step > 0); + + // add stationary frame(s) (OPTIONAL) + + for (slowOutFrameNo = _framesPerStep; slowOutFrameNo < _numberOfSlowOutFrames; slowOutFrameNo++) { + walkAnim[walk_pc].frame = walkAnim[walk_pc - 1].frame + 1; + debug(5, "EXTRA FRAME: walkAnim[%d].frame = %d", walk_pc, walkAnim[walk_pc].frame); + walkAnim[walk_pc].step = 0; + walkAnim[walk_pc].dir = walkAnim[walk_pc - 1].dir; + walkAnim[walk_pc].x = walkAnim[walk_pc - 1].x; + walkAnim[walk_pc].y = walkAnim[walk_pc - 1].y; + walk_pc++; + } + } else { + // this mega doesn't have slow-out frames + // stand in current direction + + walkAnim[walk_pc].frame = _firstStandFrame + walkAnim[walk_pc - 1].dir; + walkAnim[walk_pc].step = 0; + walkAnim[walk_pc].dir = walkAnim[walk_pc - 1].dir; + walkAnim[walk_pc].x = walkAnim[walk_pc - 1].x; + walkAnim[walk_pc].y = walkAnim[walk_pc - 1].y; + walk_pc++; + } + + // end of sequence + walkAnim[walk_pc].frame = 512; + + // so that this doesn't happen again while 'george_walking' is still + // '2' + walkAnim[walk_pc].step = 99; +} + +// SLOW OUT + +void Router::addSlowOutFrames(WalkData *walkAnim) { + int32 slowOutFrameNo; + + // if the mega did actually walk, we overwrite the last step (half a + // cycle) with slow-out frames + add any necessary stationary frames + + if (_walkData.usingSlowOutFrames && _lastCount >= _framesPerStep) { + // place stop frames here + // slowdown at the end of the last walk + + slowOutFrameNo = _lastCount - _framesPerStep; + + debug(5, "SLOW OUT: slowOutFrameNo(%d) = _lastCount(%d) - _framesPerStep(%d)", slowOutFrameNo, _lastCount, _framesPerStep); + + // overwrite the last step (half a cycle) of the walk + + do { + // map from existing walk frame across to correct + // frame number of slow-out - remember, there may be + // more slow-out frames than walk-frames! + + walkAnim[slowOutFrameNo].frame += _firstSlowOutFrame + ((walkAnim[slowOutFrameNo].frame / _framesPerStep) * (_numberOfSlowOutFrames - _framesPerStep)); + + // because no longer a normal walk-step + walkAnim[slowOutFrameNo].step = 0; + + debug(5, "walkAnim[%d].frame = %d",slowOutFrameNo,walkAnim[slowOutFrameNo].frame); + slowOutFrameNo++; + } while (slowOutFrameNo < _lastCount); + + // add stationary frame(s) (OPTIONAL) + + for (slowOutFrameNo = _framesPerStep; slowOutFrameNo < _numberOfSlowOutFrames; slowOutFrameNo++) { + walkAnim[_stepCount].frame = walkAnim[_stepCount - 1].frame + 1; + + debug(5, "EXTRA FRAMES: walkAnim[%d].frame = %d", _stepCount, walkAnim[_stepCount].frame); + + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = walkAnim[_stepCount - 1].dir; + walkAnim[_stepCount].x = walkAnim[_stepCount - 1].x; + walkAnim[_stepCount].y = walkAnim[_stepCount - 1].y; + _stepCount++; + } + } +} + +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 + *********************************************************************/ + + static int32 left = 0; + int32 p; + int32 lastDir; + int32 lastRealDir; + int32 turnDir; + int32 scale; + int32 step; + int32 module; + int32 moduleEnd; + int32 module16X; + int32 module16Y; + int32 stepX; + int32 stepY; + int32 errorX; + int32 errorY; + int32 lastErrorX; + int32 lastErrorY; + int32 frameCount; + int32 frames; + + 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; + + // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY + // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED + + debug(5, "SLIDY: STARTING THE WALK"); + + module = _framesPerChar + lastDir; + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + + // TURN TO START THE WALK + + debug(5, "SLIDY: TURNING 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 (_walkData.usingStandingTurnFrames) { + // new frames for turn frames 29oct95jps + if (turnDir < 0) + module = _firstStandingTurnLeftFrame + lastDir; + else + module = _firstStandingTurnRightFrame + lastDir; + + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + // rotate till were facing new dir then go back 45 degrees + while (lastDir != _currentDir) { + lastDir += turnDir; + + // new frames for turn frames 29oct95jps + if (turnDir < 0) { + if ( lastDir < 0) + lastDir += NO_DIRECTIONS; + module = _firstStandingTurnLeftFrame + lastDir; + } else { + if ( lastDir > 7) + lastDir -= NO_DIRECTIONS; + module = _firstStandingTurnRightFrame + lastDir; + } + + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + // the back 45 degrees bit + // step back one because new head turn for george takes us + // past the new dir + _stepCount--; + } + + // his head is in the right direction + lastRealDir = _currentDir; + + // SLIDY: THE SLOW IN + + addSlowInFrames(walkAnim); + + // THE WALK + + debug(5, "SLIDY: THE WALK"); + + // start the walk on the left or right leg, depending on how the + // slow-in frames were drawn + + // (0 = left; 1 = right) + + if (_walkData.leadingLeg[_currentDir] == 0) { + // start the walk on the left leg (ie. at beginning of the + // first step of the walk cycle) + left = 0; + } else { + // start the walk on the right leg (ie. at beginning of the + // second step of the walk cycle) + left = _framesPerStep; + } + + _lastCount = _stepCount; + + // this ensures that we don't put in turn frames for the start + lastDir = 99; + + // this ensures that we don't put in turn frames for the start + _currentDir = 99; + + do { + assert(_stepCount < O_WALKANIM_SIZE); + while (_modularPath[p].num == 0) { + p++; + 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 + left; + + if (left == 0) + left = _framesPerStep; + else + left = 0; + + moduleEnd = module + _framesPerStep; + step = 0; + scale = (_scaleA * _moduleY + _scaleB); + + do { + module16X += _walkData.dx[module] * scale; + module16Y += _walkData.dy[module] * scale; + _moduleX = module16X >> 16; + _moduleY = module16Y >> 16; + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = step; // normally 0,1,2,3,4,5,0,1,2,etc + walkAnim[_stepCount].dir = _currentDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + step++; + module++; + } 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; + if (left == 0) + left = _framesPerStep; + else + left = 0; + } + } else { + if (3 * ABS(lastErrorX) < ABS(errorX)) { + //the last stop was + // closest + _stepCount -= _framesPerStep; + if (left == 0) + left = _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++; + walkAnim[_lastCount + frameCount - 1].x += errorX * frameCount / frames; + } while (frameCount < frames); + } + + if (errorY != 0) { + frameCount = 0; + frames = _stepCount - _lastCount; + do { + frameCount++; + 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) { + // this ensures that we don't put in + // turn frames for this walk or the + // next + _currentDir = 99; + } + + if (_currentDir != 99) + lastRealDir = _currentDir; + + // check each turn condition in turn + + // only for george + if (lastDir != 99 && _currentDir != 99 && _walkData.usingWalkingTurnFrames) { + // 1 and -7 going right -1 and 7 going + // left + lastDir = _currentDir - lastDir; + + if (lastDir == -1 || lastDir == 7 || lastDir == -2 || lastDir == 6) { + // turn at the end of the last + // walk + + _frame = _lastCount - _framesPerStep; + do { + // turning left + walkAnim[_frame].frame += _firstWalkingTurnLeftFrame; + _frame++; + } while (_frame < _lastCount); + } else if (lastDir == 1 || lastDir == -7 || lastDir == 2 || lastDir == -6) { + // turn at the end of the + // current walk + + _frame = _lastCount - _framesPerStep; + do { + // turning right + walkAnim[_frame].frame += _firstWalkingTurnRightFrame; + _frame++; + } 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); + +#ifdef SWORD2_DEBUG + if (lastRealDir == 99) + error("slidyWalkAnimatorlast direction error"); +#endif + + // THE SLOW OUT + addSlowOutFrames(walkAnim); + + // TURNS TO END THE WALK ? + + // We've done the walk now put in any turns at the end + + if (_targetDir == 8) { + // ANY direction -> stand in the last direction + + module = _firstStandFrame + lastRealDir; + _targetDir = lastRealDir; + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastRealDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + if (_targetDir == 9) { + // 'stance' was non-zero + 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++; + } + } else if (_targetDir != lastRealDir) { + // 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 (_walkData.usingStandingTurnFrames) { + // new frames for turn frames 29oct95jps + if (turnDir < 0) + module = _firstStandingTurnLeftFrame + lastDir; + else + module = _firstStandingTurnRightFrame + lastDir; + + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastRealDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + // rotate if we need to + + while (lastRealDir != _targetDir) { + lastRealDir += turnDir; + + // new frames for turn frames 29oct95jps + if (turnDir < 0) { + if (lastRealDir < 0) + lastRealDir += NO_DIRECTIONS; + module = _firstStandingTurnLeftFrame + lastRealDir; + } else { + if (lastRealDir > 7) + lastRealDir -= NO_DIRECTIONS; + module = _firstStandingTurnRightFrame + lastRealDir; + } + + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastRealDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + module = _firstStandFrame + lastRealDir; + walkAnim[_stepCount - 1].frame = module; + } else { + // just stand at the end + module = _firstStandFrame + lastRealDir; + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastRealDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + walkAnim[_stepCount].frame = 512; + walkAnim[_stepCount].step = 99; + _stepCount++; + + walkAnim[_stepCount].frame = 512; + walkAnim[_stepCount].step = 99; + _stepCount++; + + walkAnim[_stepCount].frame = 512; + walkAnim[_stepCount].step = 99; + + // write all the frames to "debug.txt" + debug(5, "THE WALKDATA:"); + + for (_frame = 0; _frame <= _stepCount; _frame++) + debug(5, "walkAnim[%d].frame=%d", _frame, walkAnim[_frame].frame); + + debug(5, "routeFinder RouteSize is %d", _stepCount); + return; +} + +#ifndef FORCE_SLIDY + +// THE SOLID PATH ROUTINES + +int32 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; + + return 1; +} + +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 turnDir; + int32 scale; + int32 step; + int32 errorX; + int32 errorY; + int32 moduleEnd; + bool slowStart = false; + + // start at the beginning for a change + + int32 lastDir = _modularPath[0].dir; + int32 module = _framesPerChar + lastDir; + + _currentDir = _modularPath[1].dir; + _moduleX = _startX; + _moduleY = _startY; + _stepCount = 0; + + int32 module16X = _moduleX << 16; + int32 module16Y = _moduleY << 16; + + // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY + // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED + + debug(5, "SOLID: STARTING THE WALK"); + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + + // TURN TO START THE WALK + + debug(5, "SOLID: TURNING 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 (_walkData.usingStandingTurnFrames) { + // new frames for turn frames 29oct95jps + if (turnDir < 0) + module = _firstStandingTurnLeftFrame + lastDir; + else + module = _firstStandingTurnRightFrame + lastDir; + + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + // rotate till were facing new dir then go back 45 degrees + + while (lastDir != _currentDir) { + lastDir += turnDir; + + // new frames for turn frames + if (turnDir < 0) { + if (lastDir < 0) + lastDir += NO_DIRECTIONS; + module = _firstStandingTurnLeftFrame + lastDir; + } else { + if (lastDir > 7) + lastDir -= NO_DIRECTIONS; + module = _firstStandingTurnRightFrame + lastDir; + } + + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = 0; + walkAnim[_stepCount].dir = lastDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + } + + // the back 45 degrees bit + // step back one because new head turn for george takes us + // past the new dir + + _stepCount--; + } + + // THE SLOW IN + + slowStart = addSlowInFrames(walkAnim); + + // THE WALK + + debug(5, "SOLID: THE WALK"); + + // start the walk on the left or right leg, depending on how the + // slow-in frames were drawn + + // (0 = left; 1 = right) + if (_walkData.leadingLeg[_currentDir] == 0) { + // start the walk on the left leg (ie. at beginning of the + // first step of the walk cycle) + left = 0; + } else { + // start the walk on the right leg (ie. at beginning of the + // second step of the walk cycle) + left = _framesPerStep; + } + + _lastCount = _stepCount; + + // this ensures that we don't put in turn frames for the start + lastDir = 99; + + // this ensures that we don't put in turn frames for the start + _currentDir = 99; + + int32 p = 1; + + do { + while (_modularPath[p].num > 0) { + _currentDir = _modularPath[p].dir; + if (_currentDir < NO_DIRECTIONS) { + module = _currentDir * _framesPerStep * 2 + left; + + if (left == 0) + left = _framesPerStep; + else + left = 0; + + moduleEnd = module + _framesPerStep; + step = 0; + scale = (_scaleA * _moduleY + _scaleB); + + do { + module16X += _walkData.dx[module] * scale; + module16Y += _walkData.dy[module] * scale; + _moduleX = module16X >> 16; + _moduleY = module16Y >> 16; + walkAnim[_stepCount].frame = module; + walkAnim[_stepCount].step = step; // normally 0,1,2,3,4,5,0,1,2,etc + walkAnim[_stepCount].dir = _currentDir; + walkAnim[_stepCount].x = _moduleX; + walkAnim[_stepCount].y = _moduleY; + _stepCount++; + module++; + step++; + } 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; + + if (left == 0) + left = _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 < _framesPerStep) { + // no step taken + + // clean up if a slow in but no + // walk + + if (slowStart) { + _stepCount -= _walkData.nSlowInFrames[_currentDir]; + _lastCount -= _walkData.nSlowInFrames[_currentDir]; + slowStart = false; + } + + // this ensures that we don't + // put in turn frames for this + // walk or the next + + _currentDir = 99; + } + + // check each turn condition in turn + if (lastDir != 99 && _currentDir != 99 && _walkData.usingWalkingTurnFrames) { + // only for george + // 1 and -7 going right -1 and + // 7 going left + + lastDir = _currentDir - lastDir; + + if (lastDir == -1 || lastDir == 7 || lastDir == -2 || lastDir == 6) { + // turn at the end of + // the last walk + + _frame = _lastCount - _framesPerStep; + + do { + // turning left + walkAnim[_frame].frame += _firstWalkingTurnLeftFrame; + _frame++; + } while (_frame < _lastCount); + } else if (lastDir == 1 || lastDir == -7 || lastDir == 2 || lastDir == -6) { + // turn at the end of + // the current walk + + _frame = _lastCount - _framesPerStep; + do { + // turning right + walkAnim[_frame].frame += _firstWalkingTurnRightFrame; + _frame++; + } while (_frame < _lastCount); + } + } + + // all turns checked + _lastCount = _stepCount; + } + } + } + p++; + lastDir = _currentDir; + + // can only be valid first time round + slowStart = false; + } while (_modularPath[p].dir < NO_DIRECTIONS); + + // THE SLOW OUT + + addSlowOutFrames(walkAnim); + + 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++; + + walkAnim[_stepCount].frame = 512; + walkAnim[_stepCount].step = 99; + _stepCount++; + + walkAnim[_stepCount].frame = 512; + walkAnim[_stepCount].step = 99; + _stepCount++; + + walkAnim[_stepCount].frame = 512; + walkAnim[_stepCount].step = 99; + + debug(5, "THE WALKDATA:"); + + for (_frame = 0; _frame <= _stepCount; _frame++) + debug(5, "walkAnim[%d].frame=%d", _frame, walkAnim[_frame].frame); + + // NO END TURNS + + debug(5, "routeFinder RouteSize is %d", _stepCount); + // now check the route + + int i = 0; + + do { + if (!check(_modularPath[i].x, _modularPath[i].y, _modularPath[i + 1].x, _modularPath[i + 1].y)) + p = 0; + i++; + } while (i < p - 1); + + 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; +} +#endif + +// 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 + +void Router::loadWalkData(byte *ob_walkdata) { + uint16 firstFrameOfDirection; + uint16 walkFrameNo; + uint32 frameCounter = 0; // starts at frame 0 of mega set + int i; + + _walkData.read(ob_walkdata); + + // 0 = not using slow out frames; non-zero = using that many frames + // for each leading leg for each direction + + _numberOfSlowOutFrames = _walkData.usingSlowOutFrames; + + for (i = 0; i < NO_DIRECTIONS; i++) { + firstFrameOfDirection = i * _walkData.nWalkFrames; + + _modX[i] = 0; + _modY[i] = 0; + + for (walkFrameNo = firstFrameOfDirection; walkFrameNo < firstFrameOfDirection + _walkData.nWalkFrames / 2; walkFrameNo++) { + // eg. _modX[0] is the sum of the x-step sizes for the + // first half of the walk cycle for direction 0 + _modX[i] += _walkData.dx[walkFrameNo]; + _modY[i] += _walkData.dy[walkFrameNo]; + } + } + + _diagonalx = _modX[3]; + _diagonaly = _modY[3]; + + // interpret the walk data + + _framesPerStep = _walkData.nWalkFrames / 2; + _framesPerChar = _walkData.nWalkFrames * NO_DIRECTIONS; + + // offset pointers added Oct 30 95 JPS + // mega id references removed 16sep96 by JEL + + // WALK FRAMES + // start on frame 0 + + frameCounter += _framesPerChar; + + // STAND FRAMES + // stand frames come after the walk frames + // one stand frame for each direction + + _firstStandFrame = frameCounter; + frameCounter += NO_DIRECTIONS; + + // STANDING TURN FRAMES - OPTIONAL! + // standing turn-left frames come after the slow-out frames + // one for each direction + // standing turn-left frames come after the standing turn-right frames + // one for each direction + + if (_walkData.usingStandingTurnFrames) { + _firstStandingTurnLeftFrame = frameCounter; + frameCounter += NO_DIRECTIONS; + + _firstStandingTurnRightFrame = frameCounter; + frameCounter += NO_DIRECTIONS; + } else { + // refer instead to the normal stand frames + _firstStandingTurnLeftFrame = _firstStandFrame; + _firstStandingTurnRightFrame = _firstStandFrame; + } + + // WALKING TURN FRAMES - OPTIONAL! + // walking left-turn frames come after the stand frames + // walking right-turn frames come after the walking left-turn frames + + if (_walkData.usingWalkingTurnFrames) { + _firstWalkingTurnLeftFrame = frameCounter; + frameCounter += _framesPerChar; + + _firstWalkingTurnRightFrame = frameCounter; + frameCounter += _framesPerChar; + } else { + _firstWalkingTurnLeftFrame = 0; + _firstWalkingTurnRightFrame = 0; + } + + // SLOW-IN FRAMES - OPTIONAL! + // slow-in frames come after the walking right-turn frames + + if (_walkData.usingSlowInFrames) { + // Make note of frame number of first slow-in frame for each + // direction. There may be a different number of slow-in + // frames in each direction + + for (i = 0; i < NO_DIRECTIONS; i++) { + _firstSlowInFrame[i] = frameCounter; + frameCounter += _walkData.nSlowInFrames[i]; + } + } + + // SLOW-OUT FRAMES - OPTIONAL! + // slow-out frames come after the slow-in frames + + if (_walkData.usingSlowOutFrames) + _firstSlowOutFrame = frameCounter; +} + +// 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 p; + int32 dirx; + int32 diry; + int32 dir; + int32 ldx; + int32 ldy; + + // 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 + + p = 0; + + do { + 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; + } + p++; + } while (p < _routeLength); + + // set the last dir to continue previous route unless specified + + if (_targetDir == 8) { + // 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; +} + +void Router::setUpWalkGrid(byte *ob_mega, int32 x, int32 y, int32 dir) { + ObjectMega obMega(ob_mega); + + // get walk grid file + extra grid into 'bars' & 'node' arrays + loadWalkGrid(); + + // copy the mega structure into the local variables for use in all + // subroutines + + _startX = obMega.getFeetX(); + _startY = obMega.getFeetY(); + _startDir = obMega.getCurDir(); + _targetX = x; + _targetY = y; + _targetDir = dir; + + _scaleA = obMega.getScaleA(); + _scaleB = obMega.getScaleB(); + + // mega's current position goes into first node + + _node[0].x = _startX; + _node[0].y = _startY; + _node[0].level = 1; + _node[0].prev = 0; + _node[0].dist = 0; + + // reset other nodes + + for (int i = 1; i < _nNodes; i++) { + _node[i].level = 0; + _node[i].prev = 0; + _node[i].dist = 9999; + } + + // target position goes into final node + _node[_nNodes].x = _targetX; + _node[_nNodes].y = _targetY; + _node[_nNodes].level = 0; + _node[_nNodes].prev = 0; + _node[_nNodes].dist = 9999; +} + +void Router::plotWalkGrid() { + int32 i; + + // get walk grid file + extra grid into 'bars' & 'node' arrays + loadWalkGrid(); + + // lines + + for (i = 0; i < _nBars; i++) + _vm->_screen->drawLine(_bars[i].x1, _bars[i].y1, _bars[i].x2, _bars[i].y2, 254); + + // nodes + + // leave node 0 for start node + for (i = 1; i < _nNodes; i++) + plotCross(_node[i].x, _node[i].y, 184); +} + +void Router::plotCross(int16 x, int16 y, uint8 colour) { + _vm->_screen->drawLine(x - 1, y - 1, x + 1, y + 1, colour); + _vm->_screen->drawLine(x + 1, y - 1, x - 1, y + 1, colour); +} + +void Router::loadWalkGrid() { + WalkGridHeader floorHeader; + byte *fPolygrid; + uint16 fPolygridLen; + + _nBars = 0; // reset counts + _nNodes = 1; // leave node 0 for start-node + + // STATIC GRIDS (added/removed by object logics) + + // go through walkgrid list + for (int i = 0; i < MAX_WALKGRIDS; i++) { + if (_walkGridList[i]) { + int j; + + // open walk grid file + fPolygrid = _vm->_resman->openResource(_walkGridList[i]); + fPolygridLen = _vm->_resman->fetchLen(_walkGridList[i]); + + Common::MemoryReadStream readS(fPolygrid, fPolygridLen); + + readS.seek(ResHeader::size()); + + floorHeader.numBars = readS.readSint32LE(); + floorHeader.numNodes = readS.readSint32LE(); + + // check that we're not going to exceed the max + // allowed in the complete walkgrid arrays + + assert(_nBars + floorHeader.numBars < O_GRID_SIZE); + assert(_nNodes + floorHeader.numNodes < O_GRID_SIZE); + + // lines + + for (j = 0; j < floorHeader.numBars; j++) { + _bars[_nBars + j].x1 = readS.readSint16LE(); + _bars[_nBars + j].y1 = readS.readSint16LE(); + _bars[_nBars + j].x2 = readS.readSint16LE(); + _bars[_nBars + j].y2 = readS.readSint16LE(); + _bars[_nBars + j].xmin = readS.readSint16LE(); + _bars[_nBars + j].ymin = readS.readSint16LE(); + _bars[_nBars + j].xmax = readS.readSint16LE(); + _bars[_nBars + j].ymax = readS.readSint16LE(); + _bars[_nBars + j].dx = readS.readSint16LE(); + _bars[_nBars + j].dy = readS.readSint16LE(); + _bars[_nBars + j].co = readS.readSint32LE(); + } + + // nodes + + // leave node 0 for start node + for (j = 0; j < floorHeader.numNodes; j++) { + _node[_nNodes + j].x = readS.readSint16LE(); + _node[_nNodes + j].y = readS.readSint16LE(); + } + + // close walk grid file + _vm->_resman->closeResource(_walkGridList[i]); + + // increment counts of total bars & nodes in whole + // walkgrid + + _nBars += floorHeader.numBars; + _nNodes += floorHeader.numNodes; + } + } +} + +void Router::clearWalkGridList() { + memset(_walkGridList, 0, sizeof(_walkGridList)); +} + +// called from fnAddWalkGrid + +void Router::addWalkGrid(int32 gridResource) { + int i; + // First, scan the list to see if this grid is already included + + for (i = 0; i < MAX_WALKGRIDS; i++) { + if (_walkGridList[i] == gridResource) + return; + } + + // Scan the list for a free slot + + for (i = 0; i < MAX_WALKGRIDS; i++) { + if (_walkGridList[i] == 0) { + _walkGridList[i] = gridResource; + return; + } + } + + error("_walkGridList[] full"); +} + +// called from fnRemoveWalkGrid + +void Router::removeWalkGrid(int32 gridResource) { + for (int i = 0; i < MAX_WALKGRIDS; i++) { + if (_walkGridList[i] == gridResource) { + // If we've found it in the list, reset entry to zero. + // Otherwise just ignore the request. + _walkGridList[i] = 0; + break; + } + } +} + +} // End of namespace Sword2 |