diff options
author | Torbjörn Andersson | 2005-05-03 09:00:06 +0000 |
---|---|---|
committer | Torbjörn Andersson | 2005-05-03 09:00:06 +0000 |
commit | f5f9da940b6071df1e921faa42fa56a501e2ab62 (patch) | |
tree | f4ce163f7ed645ed75cae8a98307d8e7a5059101 /sword2/walker.cpp | |
parent | 5f7b3d8cf2bd69b01258b8facdabc9b9de736555 (diff) | |
download | scummvm-rg350-f5f9da940b6071df1e921faa42fa56a501e2ab62.tar.gz scummvm-rg350-f5f9da940b6071df1e921faa42fa56a501e2ab62.tar.bz2 scummvm-rg350-f5f9da940b6071df1e921faa42fa56a501e2ab62.zip |
More cleanup/restructuring: Moved walk-related code from Logic to Router.
(I may have to think up some better name for that class later.)
svn-id: r17901
Diffstat (limited to 'sword2/walker.cpp')
-rw-r--r-- | sword2/walker.cpp | 376 |
1 files changed, 373 insertions, 3 deletions
diff --git a/sword2/walker.cpp b/sword2/walker.cpp index 3492abab8f..102cc7950f 100644 --- a/sword2/walker.cpp +++ b/sword2/walker.cpp @@ -20,18 +20,26 @@ // WALKER.CPP by James (14nov96) -// Script functions for moving megas about the place & also for keeping tabs -// on them +// 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. */ @@ -40,7 +48,7 @@ namespace Sword2 { #define diagonalx 36 #define diagonaly 8 -int Logic::whatTarget(int startX, int startY, int destX, int destY) { +int Router::whatTarget(int startX, int startY, int destX, int destY) { int deltaX = destX - startX; int deltaY = destY - startY; @@ -66,4 +74,366 @@ int Logic::whatTarget(int startX, int startY, int destX, int destY) { 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(ObjectLogic *ob_logic, ObjectGraphic *ob_graph, ObjectMega *ob_mega, ObjectWalkdata *ob_walkdata, int16 target_x, int16 target_y, uint8 target_dir) { + // If this is the start of the walk, calculate the route. + + if (!ob_logic->looping) { + // 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 (ob_mega->feet_x == target_x && ob_mega->feet_y == target_y && ob_mega->current_dir == target_dir) { + Logic::_scriptVars[RESULT] = 0; + return IR_CONT; + } + + assert(target_dir <= 8); + + ob_mega->walk_pc = 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(); + Logic::_scriptVars[RESULT] = 1; + return IR_CONT; + } + + // Walk is about to start + + ob_mega->currently_walking = 1; + ob_logic->looping = 1; + ob_graph->anim_resource = ob_mega->megaset_res; + } else if (Logic::_scriptVars[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(); + + ob_logic->looping = 0; + ob_mega->currently_walking = 0; + Logic::_scriptVars[EXIT_CLICK_ID] = 0; + Logic::_scriptVars[RESULT] = 0; + + return IR_CONT; + } + + // Get pointer to walkanim & current frame position + + WalkData *walkAnim = getRouteMem(); + int32 walk_pc = ob_mega->walk_pc; + + // 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 + + ob_graph->anim_pc = walkAnim[walk_pc].frame; + ob_mega->current_dir = walkAnim[walk_pc].dir; + ob_mega->feet_x = walkAnim[walk_pc].x; + ob_mega->feet_y = 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. + ob_mega->walk_pc++; + 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(); + ob_logic->looping = 0; + ob_mega->currently_walking = 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(); + Logic::_scriptVars[RESULT] = 1; + return IR_TERMINATE; + } + + Logic::_scriptVars[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(ObjectLogic *ob_logic, ObjectGraphic *ob_graph, ObjectMega *ob_mega, ObjectWalkdata *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 + + if (!ob_logic->looping) { + byte *anim_file = _vm->_resman->openResource(animRes); + AnimHeader *anim_head = _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(ObjectLogic *ob_logic, ObjectGraphic *ob_graph, ObjectMega *ob_mega, ObjectWalkdata *ob_walkdata, uint32 megaId, uint32 separation) { + int16 target_x = 0; + int16 target_y = 0; + uint8 target_dir = 0; + + // If this is the start of the walk, calculate the route. + + if (!ob_logic->looping) { + StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(megaId); + + assert(head->fileType == 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. + + char *raw_script_ad = (char *) head; + uint32 null_pc = 3; + + _vm->_logic->runScript(raw_script_ad, raw_script_ad, &null_pc); + _vm->_resman->closeResource(megaId); + + ObjectMega *targetMega = _vm->_logic->getEngineMega(); + + // Stand exactly beside the mega, ie. at same y-coord + target_y = targetMega->feet_y; + + // Apply scale factor to walk distance. Ay+B gives 256 * scale + // ie. 256 * 256 * true_scale for even better accuracy, ie. + // scale = (Ay + B) / 256 + + int scale = (ob_mega->scale_a * ob_mega->feet_y + ob_mega->scale_b) / 256; + int mega_separation = (separation * scale) / 256; + + debug(4, "Target is at (%d, %d), separation %d", targetMega->feet_x, targetMega->feet_y, mega_separation); + + if (targetMega->feet_x < ob_mega->feet_x) { + // Target is left of us, so aim to stand to their + // right. Face down_left + + target_x = targetMega->feet_x + 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->feet_x - 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(ObjectLogic *ob_logic, ObjectGraphic *ob_graph, ObjectMega *ob_mega, ObjectWalkdata *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 + + if (!ob_logic->looping) { + assert(target_dir <= 7); + + target_x = ob_mega->feet_x; + target_y = ob_mega->feet_y; + } + + 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(ObjectLogic *ob_logic, ObjectGraphic *ob_graph, ObjectMega *ob_mega, ObjectWalkdata *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 + + if (!ob_logic->looping) { + target_dir = whatTarget(ob_mega->feet_x, ob_mega->feet_y, 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(ObjectLogic *ob_logic, ObjectGraphic *ob_graph, ObjectMega *ob_mega, ObjectWalkdata *ob_walkdata, uint32 megaId) { + uint8 target_dir = 0; + + // If this is the start of the walk, decide where to walk to. + + if (!ob_logic->looping) { + StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(megaId); + + assert(head->fileType == 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. + + char *raw_script_ad = (char *) head; + uint32 null_pc = 3; + + _vm->_logic->runScript(raw_script_ad, raw_script_ad, &null_pc); + _vm->_resman->closeResource(megaId); + + ObjectMega *targetMega = _vm->_logic->getEngineMega(); + + target_dir = whatTarget(ob_mega->feet_x, ob_mega->feet_y, targetMega->feet_x, targetMega->feet_y); + } + + 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(ObjectGraphic *ob_graph, ObjectMega *ob_mega, int32 x, int32 y, int32 dir) { + assert(dir >= 0 && dir <= 7); + + // Set up the stand frame & set the mega's new direction + + ob_mega->feet_x = x; + ob_mega->feet_y = y; + ob_mega->current_dir = dir; + + // Mega-set animation file + ob_graph->anim_resource = ob_mega->megaset_res; + + // Dir + first stand frame (always frame 96) + ob_graph->anim_pc = dir + 96; +} + +/** + * stand mega at end position of anim + */ + +void Router::standAfterAnim(ObjectGraphic *ob_graph, ObjectMega *ob_mega, uint32 animRes) { + byte *anim_file = _vm->_resman->openResource(animRes); + AnimHeader *anim_head = _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(ObjectGraphic *ob_graph, ObjectMega *ob_mega, uint32 animRes) { + byte *anim_file = _vm->_resman->openResource(animRes); + AnimHeader *anim_head = _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 |