Initial commit
This commit is contained in:
459
engines/sword2/walker.cpp
Normal file
459
engines/sword2/walker.cpp
Normal file
@@ -0,0 +1,459 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* Additional copyright for this file:
|
||||
* Copyright (C) 1994-1998 Revolution Software Ltd.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WALKER.CPP by James (14nov96)
|
||||
|
||||
// Functions for moving megas about the place & also for keeping tabs on them
|
||||
|
||||
|
||||
|
||||
#include "sword2/sword2.h"
|
||||
#include "sword2/defs.h"
|
||||
#include "sword2/header.h"
|
||||
#include "sword2/interpreter.h"
|
||||
#include "sword2/logic.h"
|
||||
#include "sword2/resman.h"
|
||||
#include "sword2/router.h"
|
||||
#include "sword2/screen.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 embarrassing 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
|
||||
Reference in New Issue
Block a user