diff options
author | Max Horn | 2006-02-11 22:45:04 +0000 |
---|---|---|
committer | Max Horn | 2006-02-11 22:45:04 +0000 |
commit | 26ee630756ebdd7c96bccede0881a8c8b98e8f2b (patch) | |
tree | 26e378d5cf990a2b81c2c96e9e683a7f333b62e8 /engines/sword2/walker.cpp | |
parent | 2a9a0d4211b1ea5723f1409d91cb95de8984429e (diff) | |
download | scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.gz scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.bz2 scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.zip |
Moved engines to the new engines/ directory
svn-id: r20582
Diffstat (limited to 'engines/sword2/walker.cpp')
-rw-r--r-- | engines/sword2/walker.cpp | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/engines/sword2/walker.cpp b/engines/sword2/walker.cpp new file mode 100644 index 0000000000..82ef80f65f --- /dev/null +++ b/engines/sword2/walker.cpp @@ -0,0 +1,454 @@ +/* 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$ + */ + +// WALKER.CPP by James (14nov96) + +// Functions for moving megas about the place & also for keeping tabs on them + +#include "common/stdafx.h" +#include "sword2/sword2.h" +#include "sword2/defs.h" +#include "sword2/interpreter.h" +#include "sword2/logic.h" +#include "sword2/resman.h" +#include "sword2/router.h" + +namespace Sword2 { + +void Router::setStandbyCoords(int16 x, int16 y, uint8 dir) { + assert(dir <= 7); + + _standbyX = x; + _standbyY = y; + _standbyDir = dir; +} + +/** + * Work out direction from start to dest. + */ + +// Used in whatTarget(); not valid for all megas +#define diagonalx 36 +#define diagonaly 8 + +int Router::whatTarget(int startX, int startY, int destX, int destY) { + int deltaX = destX - startX; + int deltaY = destY - startY; + + // 7 0 1 + // 6 2 + // 5 4 3 + + // Flat route + + if (ABS(deltaY) * diagonalx < ABS(deltaX) * diagonaly / 2) + return (deltaX > 0) ? 2 : 6; + + // Vertical route + + if (ABS(deltaY) * diagonalx / 2 > ABS(deltaX) * diagonaly) + return (deltaY > 0) ? 4 : 0; + + // Diagonal route + + if (deltaX > 0) + return (deltaY > 0) ? 3 : 1; + + return (deltaY > 0) ? 5 : 7; +} + +/** + * Walk meta to (x,y,dir). Set RESULT to 0 if it succeeded. Otherwise, set + * RESULT to 1. Return true if the mega has finished walking. + */ + +int Router::doWalk(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y, uint8 target_dir) { + ObjectLogic obLogic(ob_logic); + ObjectGraphic obGraph(ob_graph); + ObjectMega obMega(ob_mega); + + // If this is the start of the walk, calculate the route. + + if (obLogic.getLooping() == 0) { + // If we're already there, don't even bother allocating + // memory and calling the router, just quit back & continue + // the script! This avoids an embarassing mega stand frame + // appearing for one cycle when we're already in position for + // an anim eg. repeatedly clicking on same object to repeat + // an anim - no mega frame will appear in between runs of the + // anim. + + if (obMega.getFeetX() == target_x && obMega.getFeetY() == target_y && obMega.getCurDir() == target_dir) { + _vm->_logic->writeVar(RESULT, 0); + return IR_CONT; + } + + assert(target_dir <= 8); + + obMega.setWalkPc(0); + + // Set up mem for _walkData in route_slots[] & set mega's + // 'route_slot_id' accordingly + allocateRouteMem(); + + int32 route = routeFinder(ob_mega, ob_walkdata, target_x, target_y, target_dir); + + // 0 = can't make route to target + // 1 = created route + // 2 = zero route but may need to turn + + if (route != 1 && route != 2) { + freeRouteMem(); + _vm->_logic->writeVar(RESULT, 1); + return IR_CONT; + } + + // Walk is about to start + + obMega.setIsWalking(1); + obLogic.setLooping(1); + obGraph.setAnimResource(obMega.getMegasetRes()); + } else if (_vm->_logic->readVar(EXIT_FADING) && _vm->_screen->getFadeStatus() == RDFADE_BLACK) { + // Double clicked an exit, and the screen has faded down to + // black. Ok, that's it. Back to script and change screen. + + // We have to clear te EXIT_CLICK_ID variable in case there's a + // walk instruction on the new screen, or it'd be cut short. + + freeRouteMem(); + + obLogic.setLooping(0); + obMega.setIsWalking(0); + _vm->_logic->writeVar(EXIT_CLICK_ID, 0); + _vm->_logic->writeVar(RESULT, 0); + + return IR_CONT; + } + + // Get pointer to walkanim & current frame position + + WalkData *walkAnim = getRouteMem(); + int32 walk_pc = obMega.getWalkPc(); + + // If stopping the walk early, overwrite the next step with a + // slow-out, then finish + + if (_vm->_logic->checkEventWaiting() && walkAnim[walk_pc].step == 0 && walkAnim[walk_pc + 1].step == 1) { + // At the beginning of a step + earlySlowOut(ob_mega, ob_walkdata); + } + + // Get new frame of walk + + obGraph.setAnimPc(walkAnim[walk_pc].frame); + obMega.setCurDir(walkAnim[walk_pc].dir); + obMega.setFeetX(walkAnim[walk_pc].x); + obMega.setFeetY(walkAnim[walk_pc].y); + + // Is the NEXT frame is the end-marker (512) of the walk sequence? + + if (walkAnim[walk_pc + 1].frame != 512) { + // No, it wasn't. Increment the walk-anim frame number and + // come back next cycle. + obMega.setWalkPc(obMega.getWalkPc() + 1); + return IR_REPEAT; + } + + // We have reached the end-marker, which means we can return to the + // script just as the final (stand) frame of the walk is set. + + freeRouteMem(); + obLogic.setLooping(0); + obMega.setIsWalking(0); + + // If George's walk has been interrupted to run a new action script for + // instance or Nico's walk has been interrupted by player clicking on + // her to talk + + // There used to be code here for checking if two megas were colliding, + // but it had been commented out, and it was only run if a function + // that always returned zero returned non-zero. + + if (_vm->_logic->checkEventWaiting()) { + _vm->_logic->startEvent(); + _vm->_logic->writeVar(RESULT, 1); + return IR_TERMINATE; + } + + _vm->_logic->writeVar(RESULT, 0); + + // CONTINUE the script so that RESULT can be checked! Also, if an anim + // command follows the fnWalk command, the 1st frame of the anim (which + // is always a stand frame itself) can replace the final stand frame of + // the walk, to hide the slight difference between the shrinking on the + // mega frames and the pre-shrunk anim start-frame. + + return IR_CONT; +} + +/** + * Walk mega to start position of anim + */ + +int Router::walkToAnim(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 animRes) { + int16 target_x = 0; + int16 target_y = 0; + uint8 target_dir = 0; + + // Walkdata is needed for earlySlowOut if player clicks elsewhere + // during the walk. + + // If this is the start of the walk, read anim file to get start coords + + ObjectLogic obLogic(ob_logic); + + if (obLogic.getLooping() == 0) { + byte *anim_file = _vm->_resman->openResource(animRes); + AnimHeader anim_head; + + anim_head.read(_vm->fetchAnimHeader(anim_file)); + + target_x = anim_head.feetStartX; + target_y = anim_head.feetStartY; + target_dir = anim_head.feetStartDir; + + _vm->_resman->closeResource(animRes); + + // If start coords not yet set in anim header, use the standby + // coords (which should be set beforehand in the script). + + if (target_x == 0 && target_y == 0) { + target_x = _standbyX; + target_y = _standbyY; + target_dir = _standbyDir; + } + + assert(target_dir <= 7); + } + + return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir); +} + +/** + * Route to the left or right hand side of target id, if possible. + */ + +int Router::walkToTalkToMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId, uint32 separation) { + ObjectMega obMega(ob_mega); + + int16 target_x = 0; + int16 target_y = 0; + uint8 target_dir = 0; + + // If this is the start of the walk, calculate the route. + + ObjectLogic obLogic(ob_logic); + + if (obLogic.getLooping() == 0) { + assert(_vm->_resman->fetchType(megaId) == GAME_OBJECT); + + // Call the base script. This is the graphic/mouse service + // call, and will set _engineMega to the ObjectMega of mega we + // want to route to. + + _vm->_logic->runResScript(megaId, 3); + + ObjectMega targetMega(_vm->_logic->getEngineMega()); + + // Stand exactly beside the mega, ie. at same y-coord + target_y = targetMega.getFeetY(); + + int scale = obMega.calcScale(); + int mega_separation = (separation * scale) / 256; + + debug(4, "Target is at (%d, %d), separation %d", targetMega.getFeetX(), targetMega.getFeetY(), mega_separation); + + if (targetMega.getFeetX() < obMega.getFeetX()) { + // Target is left of us, so aim to stand to their + // right. Face down_left + + target_x = targetMega.getFeetX() + mega_separation; + target_dir = 5; + } else { + // Ok, must be right of us so aim to stand to their + // left. Face down_right. + + target_x = targetMega.getFeetX() - mega_separation; + target_dir = 3; + } + } + + return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir); +} +/** + * Turn mega to the specified direction. Just needs to call doWalk() with + * current feet coords, so router can produce anim of turn frames. + */ + +int Router::doFace(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint8 target_dir) { + int16 target_x = 0; + int16 target_y = 0; + + // If this is the start of the turn, get the mega's current feet + // coords + the required direction + + ObjectLogic obLogic(ob_logic); + + if (obLogic.getLooping() == 0) { + assert(target_dir <= 7); + + ObjectMega obMega(ob_mega); + + target_x = obMega.getFeetX(); + target_y = obMega.getFeetY(); + } + + return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir); +} + +/** + * Turn mega to face point (x,y) on the floor + */ + +int Router::faceXY(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y) { + uint8 target_dir = 0; + + // If this is the start of the turn, get the mega's current feet + // coords + the required direction + + ObjectLogic obLogic(ob_logic); + + if (obLogic.getLooping() == 0) { + ObjectMega obMega(ob_mega); + + target_dir = whatTarget(obMega.getFeetX(), obMega.getFeetY(), target_x, target_y); + } + + return doFace(ob_logic, ob_graph, ob_mega, ob_walkdata, target_dir); +} + +/** + * Turn mega to face another mega. + */ + +int Router::faceMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId) { + uint8 target_dir = 0; + + // If this is the start of the walk, decide where to walk to. + + ObjectLogic obLogic(ob_logic); + + if (obLogic.getLooping() == 0) { + assert(_vm->_resman->fetchType(megaId) == GAME_OBJECT); + + // Call the base script. This is the graphic/mouse service + // call, and will set _engineMega to the ObjectMega of mega we + // want to turn to face. + + _vm->_logic->runResScript(megaId, 3); + + ObjectMega obMega(ob_mega); + ObjectMega targetMega(_vm->_logic->getEngineMega()); + + target_dir = whatTarget(obMega.getFeetX(), obMega.getFeetY(), targetMega.getFeetX(), targetMega.getFeetY()); + } + + return doFace(ob_logic, ob_graph, ob_mega, ob_walkdata, target_dir); +} + +/** + * Stand mega at (x,y,dir) + * Sets up the graphic object, but also needs to set the new 'current_dir' in + * the mega object, so the router knows in future + */ + +void Router::standAt(byte *ob_graph, byte *ob_mega, int32 x, int32 y, int32 dir) { + assert(dir >= 0 && dir <= 7); + + ObjectGraphic obGraph(ob_graph); + ObjectMega obMega(ob_mega); + + // Set up the stand frame & set the mega's new direction + + obMega.setFeetX(x); + obMega.setFeetY(y); + obMega.setCurDir(dir); + + // Mega-set animation file + obGraph.setAnimResource(obMega.getMegasetRes()); + + // Dir + first stand frame (always frame 96) + obGraph.setAnimPc(dir + 96); +} + +/** + * Stand mega at end position of anim + */ + +void Router::standAfterAnim(byte *ob_graph, byte *ob_mega, uint32 animRes) { + byte *anim_file = _vm->_resman->openResource(animRes); + AnimHeader anim_head; + + anim_head.read(_vm->fetchAnimHeader(anim_file)); + + int32 x = anim_head.feetEndX; + int32 y = anim_head.feetEndY; + int32 dir = anim_head.feetEndDir; + + _vm->_resman->closeResource(animRes); + + // If start coords not available either use the standby coords (which + // should be set beforehand in the script) + + if (x == 0 && y == 0) { + x = _standbyX; + y = _standbyY; + dir = _standbyDir; + } + + standAt(ob_graph, ob_mega, x, y, dir); +} + +void Router::standAtAnim(byte *ob_graph, byte *ob_mega, uint32 animRes) { + byte *anim_file = _vm->_resman->openResource(animRes); + AnimHeader anim_head; + + anim_head.read(_vm->fetchAnimHeader(anim_file)); + + int32 x = anim_head.feetStartX; + int32 y = anim_head.feetStartY; + int32 dir = anim_head.feetStartDir; + + _vm->_resman->closeResource(animRes); + + // If start coords not available use the standby coords (which should + // be set beforehand in the script) + + if (x == 0 && y == 0) { + x = _standbyX; + y = _standbyY; + dir = _standbyDir; + } + + standAt(ob_graph, ob_mega, x, y, dir); +} + +} // End of namespace Sword2 |