mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 06:39:17 +00:00
1442 lines
41 KiB
C++
1442 lines
41 KiB
C++
/* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include "sherlock/tattoo/tattoo_people.h"
|
|
#include "sherlock/tattoo/tattoo_scene.h"
|
|
#include "sherlock/tattoo/tattoo_talk.h"
|
|
#include "sherlock/tattoo/tattoo_user_interface.h"
|
|
#include "sherlock/tattoo/tattoo.h"
|
|
|
|
namespace Sherlock {
|
|
|
|
namespace Tattoo {
|
|
|
|
#define FACING_PLAYER 16
|
|
#define NUM_ADJUSTED_WALKS 21
|
|
|
|
struct AdjustWalk {
|
|
char _vgsName[9];
|
|
int _xAdjust;
|
|
int _flipXAdjust;
|
|
int _yAdjust;
|
|
} ;
|
|
|
|
static const AdjustWalk ADJUST_WALKS[NUM_ADJUSTED_WALKS] = {
|
|
{ "TUPRIGHT", -7, -19, 6 },
|
|
{ "TRIGHT", 8, -14, 0 },
|
|
{ "TDOWNRG", 14, -12, 0 },
|
|
{ "TWUPRIGH", 12, 4, 2 },
|
|
{ "TWRIGHT", 31, -14, 0 },
|
|
{ "TWDOWNRG", 6, -24, 0 },
|
|
{ "HTUPRIGH", 2, -20, 0 },
|
|
{ "HTRIGHT", 28, -20, 0 },
|
|
{ "HTDOWNRG", 8, -2, 0 },
|
|
{ "GTUPRIGH", 4, -12, 0 },
|
|
{ "GTRIGHT", 12, -16, 0 },
|
|
{ "GTDOWNRG", 10, -18, 0 },
|
|
{ "JTUPRIGH", 8, -10, 0 },
|
|
{ "JTRIGHT", 22, -6, 0 },
|
|
{ "JTDOWNRG", 4, -20, 0 },
|
|
{ "CTUPRIGH", 10, 0, 0 },
|
|
{ "CTRIGHT", 26, -22, 0 },
|
|
{ "CTDOWNRI", 16, 4, 0 },
|
|
{ "ITUPRIGH", 0, 0, 0 },
|
|
{ "ITRIGHT", 20, 0, 0 },
|
|
{ "ITDOWNRG", 8, 0, 0 }
|
|
};
|
|
|
|
static const int WALK_SPEED_X[99] = {
|
|
90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 98, 90, 90, 90, 90, 90, 91, 90, 90,
|
|
90, 90,100, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,100, 90,
|
|
90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,103, 90, 90, 90, 90, 90, 90, 90,
|
|
90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
|
90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90
|
|
};
|
|
|
|
static const int WALK_SPEED_Y[99] = {
|
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 32, 32, 32, 28, 28, 28, 28, 28, 26, 28, 28,
|
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
|
32, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 31, 28, 28, 28, 28, 28, 28, 28,
|
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
|
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28
|
|
};
|
|
|
|
static const int WALK_SPEED_DIAG_X[99] = {
|
|
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
|
|
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
|
|
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
|
|
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 90, 50, 50, 50, 50, 50, 50, 50, 50, 50,
|
|
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50
|
|
};
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
SavedNPCPath::SavedNPCPath() {
|
|
Common::fill(&_path[0], &_path[MAX_NPC_PATH], 0);
|
|
_npcIndex = 0;
|
|
_npcPause = 0;
|
|
_npcFacing = 0;
|
|
_lookHolmes = false;
|
|
}
|
|
|
|
SavedNPCPath::SavedNPCPath(byte path[MAX_NPC_PATH], int npcIndex, int npcPause, const Common::Point &walkDest,
|
|
int npcFacing, bool lookHolmes) : _npcIndex(npcIndex), _npcPause(npcPause), _walkDest(walkDest),
|
|
_npcFacing(npcFacing), _lookHolmes(lookHolmes) {
|
|
Common::copy(&path[0], &path[MAX_NPC_PATH], &_path[0]);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
TattooPerson::TattooPerson() : Person() {
|
|
Common::fill(&_npcPath[0], &_npcPath[MAX_NPC_PATH], 0);
|
|
_tempX = _tempScaleVal = 0;
|
|
_npcIndex = 0;
|
|
_npcMoved = false;
|
|
_npcFacing = -1;
|
|
_resetNPCPath = true;
|
|
_savedNpcSequence = 0;
|
|
_savedNpcFrame = 0;
|
|
_updateNPCPath = true;
|
|
_npcPause = 0;
|
|
_lookHolmes = false;
|
|
}
|
|
|
|
void TattooPerson::freeAltGraphics() {
|
|
if (_altImages != nullptr) {
|
|
delete _altImages;
|
|
_altImages = nullptr;
|
|
}
|
|
|
|
_altSeq = 0;
|
|
}
|
|
|
|
void TattooPerson::adjustSprite() {
|
|
People &people = *_vm->_people;
|
|
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
|
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
|
|
|
if (_type == INVALID)
|
|
return;
|
|
|
|
if (_type == CHARACTER && _status) {
|
|
// Sprite waiting to move, so restart walk
|
|
_walkCount = _status;
|
|
_status = 0;
|
|
|
|
_walkDest = _walkTo.front();
|
|
setWalking();
|
|
} else if (_type == CHARACTER && _walkCount) {
|
|
if (_walkCount > 10) {
|
|
_walkDest = _nextDest;
|
|
setWalking();
|
|
}
|
|
|
|
_position += _delta;
|
|
if (_walkCount)
|
|
--_walkCount;
|
|
|
|
if (!_walkCount) {
|
|
// If there are remaining points to walk, move to the next one
|
|
if (!_walkTo.empty()) {
|
|
_walkDest = _walkTo.pop();
|
|
setWalking();
|
|
} else {
|
|
gotoStand();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_type != CHARACTER) {
|
|
if (_position.y > SHERLOCK_SCREEN_HEIGHT)
|
|
_position.y = SHERLOCK_SCREEN_HEIGHT;
|
|
|
|
if (_position.y < UPPER_LIMIT)
|
|
_position.y = UPPER_LIMIT;
|
|
|
|
if (_position.x < LEFT_LIMIT)
|
|
_position.x = LEFT_LIMIT;
|
|
|
|
if (_position.x > RIGHT_LIMIT)
|
|
_position.x = RIGHT_LIMIT;
|
|
}
|
|
|
|
int frameNum = _frameNumber;
|
|
if (frameNum == -1)
|
|
frameNum = 0;
|
|
int idx = _walkSequences[_sequenceNumber][frameNum];
|
|
if (idx > _maxFrames)
|
|
idx = 1;
|
|
|
|
// Set the image frame
|
|
if (_altSeq)
|
|
_imageFrame = &(*_altImages)[idx - 1];
|
|
else
|
|
_imageFrame = &(*_images)[idx - 1];
|
|
|
|
// See if the player has come to a stop after clicking on an Arrow zone to leave the scene.
|
|
// If so, this will set up the exit information for the scene transition
|
|
if (!_walkCount && ui._exitZone != -1 && scene._walkedInScene && scene._goToScene == -1 &&
|
|
!_description.compareToIgnoreCase(people[HOLMES]._description)) {
|
|
Exit &exit = scene._exits[ui._exitZone];
|
|
scene._goToScene = exit._scene;
|
|
|
|
if (exit._newPosition.x != 0) {
|
|
people._savedPos = exit._newPosition;
|
|
|
|
if (people._savedPos._facing > 100 && people._savedPos.x < 1)
|
|
people._savedPos.x = 100;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TattooPerson::gotoStand() {
|
|
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
|
|
|
// If the misc field is set, then we're running a special talk sequence, so don't interrupt it.
|
|
if (_misc)
|
|
return;
|
|
|
|
_walkTo.clear();
|
|
_walkCount = 0;
|
|
int oldFacing = _sequenceNumber;
|
|
|
|
// If the person was talking or listening, just return it to the standing sequence
|
|
// in the direction they were pointing
|
|
if (_sequenceNumber >= TALK_UPRIGHT && _sequenceNumber <= LISTEN_UPLEFT) {
|
|
switch (_sequenceNumber) {
|
|
case TALK_UPRIGHT:
|
|
case LISTEN_UPRIGHT:
|
|
_sequenceNumber = STOP_UPRIGHT;
|
|
break;
|
|
case TALK_RIGHT:
|
|
case LISTEN_RIGHT:
|
|
_sequenceNumber = STOP_RIGHT;
|
|
break;
|
|
case TALK_DOWNRIGHT:
|
|
case LISTEN_DOWNRIGHT:
|
|
_sequenceNumber = STOP_DOWNRIGHT;
|
|
break;
|
|
case TALK_DOWNLEFT:
|
|
case LISTEN_DOWNLEFT:
|
|
_sequenceNumber = STOP_DOWNLEFT;
|
|
break;
|
|
case TALK_LEFT:
|
|
case LISTEN_LEFT:
|
|
_sequenceNumber = STOP_LEFT;
|
|
break;
|
|
case TALK_UPLEFT:
|
|
case LISTEN_UPLEFT:
|
|
_sequenceNumber = STOP_UPLEFT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (_seqTo) {
|
|
// Reset to previous value
|
|
_walkSequences[oldFacing]._sequences[_frameNumber] = _seqTo;
|
|
_seqTo = 0;
|
|
}
|
|
|
|
// Set the Frame number to the last frame so we don't move
|
|
_frameNumber = 0;
|
|
|
|
checkWalkGraphics();
|
|
|
|
_oldWalkSequence = -1;
|
|
people._allowWalkAbort = true;
|
|
return;
|
|
}
|
|
|
|
// If the sprite that is stopping is an NPC and he is supposed to face a certain direction
|
|
// when he stops, set that direction here
|
|
int npc = -1;
|
|
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
|
if (_imageFrame == people[idx]._imageFrame)
|
|
npc = idx;
|
|
}
|
|
|
|
if (npc != -1 && people[npc]._npcFacing != -1) {
|
|
if (people[npc]._npcFacing == FACING_PLAYER) {
|
|
// See where Holmes is with respect to the NPC (x coords)
|
|
if (people[HOLMES]._position.x < people[npc]._position.x)
|
|
people[npc]._npcFacing = STOP_LEFT;
|
|
else
|
|
people[npc]._npcFacing = STOP_RIGHT;
|
|
|
|
// See where Holmes is with respect to the NPC (y coords)
|
|
if (people[HOLMES]._position.y < people[npc]._position.y - (10 * FIXED_INT_MULTIPLIER)) {
|
|
// Holmes is above the NPC so reset the facing to the diagonal ups
|
|
if (people[npc]._npcFacing == STOP_RIGHT)
|
|
people[npc]._npcFacing = STOP_UPRIGHT;
|
|
else
|
|
people[npc]._npcFacing = STOP_UPLEFT;
|
|
} else {
|
|
if (people[HOLMES]._position.y > people[npc]._position.y + (10 * FIXED_INT_MULTIPLIER)) {
|
|
// Holmes is below the NPC so reset the facing to the diagonal downs
|
|
if (people[npc]._npcFacing == STOP_RIGHT)
|
|
people[npc]._npcFacing = STOP_DOWNRIGHT;
|
|
else
|
|
people[npc]._npcFacing = STOP_DOWNLEFT;
|
|
}
|
|
}
|
|
}
|
|
|
|
_sequenceNumber = people[npc]._npcFacing;
|
|
} else {
|
|
switch (_sequenceNumber) {
|
|
case WALK_UP: _sequenceNumber = STOP_UP; break;
|
|
case WALK_UPRIGHT: _sequenceNumber = STOP_UPRIGHT; break;
|
|
case WALK_RIGHT: _sequenceNumber = STOP_RIGHT; break;
|
|
case WALK_DOWNRIGHT: _sequenceNumber = STOP_DOWNRIGHT; break;
|
|
case WALK_DOWN: _sequenceNumber = STOP_DOWN; break;
|
|
case WALK_DOWNLEFT: _sequenceNumber = STOP_DOWNLEFT;break;
|
|
case WALK_LEFT: _sequenceNumber = STOP_LEFT; break;
|
|
case WALK_UPLEFT: _sequenceNumber = STOP_UPLEFT; break;
|
|
}
|
|
}
|
|
|
|
// Only restart the frame number at 0 if the new sequence is different from the last sequence
|
|
// so we don't let Holmes repeat standing.
|
|
if (_oldWalkSequence != -1) {
|
|
if (_seqTo) {
|
|
// Reset to previous value
|
|
_walkSequences[oldFacing]._sequences[_frameNumber] = _seqTo;
|
|
_seqTo = 0;
|
|
}
|
|
|
|
_frameNumber = 0;
|
|
}
|
|
|
|
checkWalkGraphics();
|
|
|
|
_oldWalkSequence = -1;
|
|
people._allowWalkAbort = true;
|
|
}
|
|
|
|
void TattooPerson::setWalking() {
|
|
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
|
int oldDirection, oldFrame;
|
|
Common::Point delta;
|
|
_nextDest = _walkDest;
|
|
|
|
// Flag that player has now walked in the scene
|
|
scene._walkedInScene = true;
|
|
|
|
// Stop any previous walking, since a new dest is being set
|
|
_walkCount = 0;
|
|
oldDirection = _sequenceNumber;
|
|
oldFrame = _frameNumber;
|
|
|
|
// Set speed to use horizontal and vertical movement
|
|
int scaleVal = scene.getScaleVal(_position);
|
|
Common::Point speed(MAX(WALK_SPEED_X[scene._currentScene - 1] * SCALE_THRESHOLD / scaleVal, 2),
|
|
MAX(WALK_SPEED_Y[scene._currentScene - 1] * SCALE_THRESHOLD / scaleVal, 2));
|
|
Common::Point diagSpeed(MAX(WALK_SPEED_DIAG_X[scene._currentScene - 1] * SCALE_THRESHOLD / scaleVal, 2),
|
|
MAX((WALK_SPEED_Y[scene._currentScene - 1] - 2) * SCALE_THRESHOLD / scaleVal, 2));
|
|
|
|
// If the player is already close to the given destination that no walking is needed,
|
|
// move to the next straight line segment in the overall walking route, if there is one
|
|
for (;;) {
|
|
if (_centerWalk || !_walkTo.empty()) {
|
|
// Since we want the player to be centered on the ultimate destination, and the player
|
|
// is drawn from the left side, move the cursor half the width of the player to center it
|
|
delta = Common::Point(_position.x / FIXED_INT_MULTIPLIER - _walkDest.x,
|
|
_position.y / FIXED_INT_MULTIPLIER - _walkDest.y);
|
|
|
|
int dir;
|
|
if (ABS(delta.x) > ABS(delta.y))
|
|
dir = (delta.x < 0) ? WALK_LEFT : WALK_RIGHT;
|
|
else
|
|
dir = (delta.y < 0) ? WALK_UP : WALK_DOWN;
|
|
|
|
scaleVal = scene.getScaleVal(Point32(_walkDest.x * FIXED_INT_MULTIPLIER,
|
|
_walkDest.y * FIXED_INT_MULTIPLIER));
|
|
_walkDest.x -= _stopFrames[dir]->sDrawXSize(scaleVal) / 2;
|
|
}
|
|
|
|
delta = Common::Point(
|
|
ABS(_position.x / FIXED_INT_MULTIPLIER - _walkDest.x),
|
|
ABS(_position.y / FIXED_INT_MULTIPLIER - _walkDest.y)
|
|
);
|
|
|
|
// If we're ready to move a sufficient distance, that's it. Otherwise,
|
|
// move onto the next portion of the walk path, if there is one
|
|
if ((delta.x > 3 || delta.y > 0) || _walkTo.empty())
|
|
break;
|
|
|
|
// Pop next walk segment off the walk route stack
|
|
_walkDest = _walkTo.pop();
|
|
}
|
|
|
|
// If a sufficient move is being done, then start the move
|
|
if (delta.x > 3 || delta.y) {
|
|
// See whether the major movement is horizontal or vertical
|
|
if (delta.x >= delta.y) {
|
|
// Set the initial frame sequence for the left and right, as well
|
|
// as setting the delta x depending on direction
|
|
if (_walkDest.x < (_position.x / FIXED_INT_MULTIPLIER)) {
|
|
_sequenceNumber = WALK_LEFT;
|
|
_delta.x = speed.x * -(FIXED_INT_MULTIPLIER / 10);
|
|
} else {
|
|
_sequenceNumber = WALK_RIGHT;
|
|
_delta.x = speed.x * (FIXED_INT_MULTIPLIER / 10);
|
|
}
|
|
|
|
// See if the x delta is too small to be divided by the speed, since
|
|
// this would cause a divide by zero error
|
|
if ((delta.x * 10) >= speed.x) {
|
|
// Det the delta y
|
|
_delta.y = (delta.y * FIXED_INT_MULTIPLIER) / ((delta.x * 10) / speed.x);
|
|
if (_walkDest.y < (_position.y / FIXED_INT_MULTIPLIER))
|
|
_delta.y = -_delta.y;
|
|
|
|
// Set how many times we should add the delta to the player's position
|
|
_walkCount = (delta.x * 10) / speed.x;
|
|
} else {
|
|
// The delta x was less than the speed (ie. we're really close to
|
|
// the destination). So set delta to 0 so the player won't move
|
|
_delta = Point32(0, 0);
|
|
_position = Point32(_walkDest.x * FIXED_INT_MULTIPLIER, _walkDest.y * FIXED_INT_MULTIPLIER);
|
|
|
|
_walkCount = 1;
|
|
}
|
|
|
|
// See if the sequence needs to be changed for diagonal walking
|
|
if (_delta.y > 1500) {
|
|
if (_sequenceNumber == WALK_LEFT || _sequenceNumber == WALK_RIGHT) {
|
|
_delta.x = _delta.x / speed.x * diagSpeed.x;
|
|
_delta.y = (delta.y * FIXED_INT_MULTIPLIER) / (delta.x * 10 / diagSpeed.x);
|
|
|
|
_walkCount = delta.x * 10 / diagSpeed.x;
|
|
}
|
|
|
|
switch (_sequenceNumber) {
|
|
case WALK_LEFT:
|
|
_sequenceNumber = WALK_DOWNLEFT;
|
|
break;
|
|
case WALK_RIGHT:
|
|
_sequenceNumber = WALK_DOWNRIGHT;
|
|
break;
|
|
}
|
|
} else if (_delta.y < -1500) {
|
|
if (_sequenceNumber == WALK_LEFT || _sequenceNumber == WALK_RIGHT) {
|
|
_delta.x = _delta.x / speed.x * diagSpeed.x;
|
|
_delta.y = -1 * (delta.y * FIXED_INT_MULTIPLIER) / (delta.x * 10 / diagSpeed.x);
|
|
|
|
_walkCount = (delta.x * 10) / diagSpeed.x;
|
|
}
|
|
|
|
switch (_sequenceNumber) {
|
|
case WALK_LEFT:
|
|
_sequenceNumber = WALK_UPLEFT;
|
|
break;
|
|
case WALK_RIGHT:
|
|
_sequenceNumber = WALK_UPRIGHT;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// Major movement is vertical, so set the sequence for up and down,
|
|
// and set the delta Y depending on the direction
|
|
if (_walkDest.y < (_position.y / FIXED_INT_MULTIPLIER)) {
|
|
_sequenceNumber = WALK_UP;
|
|
_delta.y = speed.y * -(FIXED_INT_MULTIPLIER / 10);
|
|
} else {
|
|
speed.y = diagSpeed.y;
|
|
_sequenceNumber = WALK_DOWN;
|
|
_delta.y = speed.y * (FIXED_INT_MULTIPLIER / 10);
|
|
}
|
|
|
|
// Set the delta x
|
|
if (delta.y * 10 / speed.y)
|
|
_delta.x = (delta.x * FIXED_INT_MULTIPLIER) / (delta.y * 10 / speed.y);
|
|
else
|
|
_delta.x = (delta.x * FIXED_INT_MULTIPLIER) / delta.y;
|
|
|
|
if (_walkDest.x < _position.y / FIXED_INT_MULTIPLIER)
|
|
_delta.x = -_delta.x;
|
|
|
|
// Set how many times we should add the delta's to the players position
|
|
if (delta.y * 10 / speed.y)
|
|
_walkCount = delta.y * 10 / speed.y;
|
|
else
|
|
_walkCount = delta.y;
|
|
}
|
|
}
|
|
|
|
// See if the new walk sequence is the same as the old. If it's a new one,
|
|
// we need to reset the frame number to zero so it's animation starts at
|
|
// it's beginning. Otherwise, if it's the same sequence, we can leave it
|
|
// as is, so it keeps the animation going at wherever it was up to
|
|
if (_sequenceNumber != _oldWalkSequence) {
|
|
if (_seqTo) {
|
|
// Reset to previous value
|
|
_walkSequences[oldDirection]._sequences[_frameNumber] = _seqTo;
|
|
_seqTo = 0;
|
|
}
|
|
_frameNumber = 0;
|
|
}
|
|
|
|
checkWalkGraphics();
|
|
_oldWalkSequence = _sequenceNumber;
|
|
|
|
if (!_walkCount && _walkTo.empty())
|
|
gotoStand();
|
|
|
|
// If the sequence is the same as when we started, then Holmes was standing still and we're trying
|
|
// to re-stand him, so reset Holmes' rame to the old frame number from before it was reset to 0
|
|
if (_sequenceNumber == oldDirection)
|
|
_frameNumber = oldFrame;
|
|
}
|
|
|
|
void TattooPerson::walkToCoords(const Point32 &destPos, int destDir) {
|
|
TattooEngine &vm = *(TattooEngine *)_vm;
|
|
Events &events = *_vm->_events;
|
|
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
|
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
|
Talk &talk = *_vm->_talk;
|
|
|
|
CursorId oldCursor = events.getCursor();
|
|
events.setCursor(WAIT);
|
|
|
|
_walkDest = Common::Point(destPos.x / FIXED_INT_MULTIPLIER, destPos.y / FIXED_INT_MULTIPLIER);
|
|
|
|
bool isHolmes = this == &people[HOLMES];
|
|
if (isHolmes) {
|
|
people._allowWalkAbort = true;
|
|
} else {
|
|
// Clear the path Variables
|
|
_npcIndex = _npcPause;
|
|
Common::fill(_npcPath, _npcPath + 100, 0);
|
|
_npcFacing = destDir;
|
|
}
|
|
|
|
_centerWalk = false;
|
|
|
|
// Only move the person if they're going an appreciable distance
|
|
if (ABS(_walkDest.x - (_position.x / FIXED_INT_MULTIPLIER)) > 8 ||
|
|
ABS(_walkDest.y - (_position.y / FIXED_INT_MULTIPLIER)) > 4) {
|
|
goAllTheWay();
|
|
|
|
do {
|
|
// Keep doing animations whilst walk is in progress
|
|
events.wait(1);
|
|
scene.doBgAnim();
|
|
|
|
if (events.kbHit()) {
|
|
Common::KeyState keyState = events.getKey();
|
|
|
|
if (keyState.keycode == Common::KEYCODE_ESCAPE && vm._runningProlog) {
|
|
vm.setFlags(-76);
|
|
vm.setFlags(396);
|
|
scene._goToScene = 1;
|
|
talk._talkToAbort = true;
|
|
}
|
|
}
|
|
} while (!_vm->shouldQuit() && _walkCount && !talk._talkToAbort);
|
|
}
|
|
|
|
_centerWalk = true;
|
|
if (!isHolmes)
|
|
_updateNPCPath = true;
|
|
|
|
if (!talk._talkToAbort) {
|
|
// put character exactly on right spot
|
|
_position = destPos;
|
|
|
|
if (_sequenceNumber != destDir) {
|
|
// Facing character to correct ending direction
|
|
_sequenceNumber = destDir;
|
|
gotoStand();
|
|
}
|
|
|
|
if (!isHolmes)
|
|
_updateNPCPath = false;
|
|
|
|
// Secondary walking wait loop
|
|
bool done = false;
|
|
while (!done && !_vm->shouldQuit()) {
|
|
events.wait(1);
|
|
scene.doBgAnim();
|
|
|
|
// See if we're past the initial goto stand sequence
|
|
for (int idx = 0; idx < _frameNumber; ++idx) {
|
|
if (_walkSequences[_sequenceNumber][idx] == 0) {
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (events.kbHit()) {
|
|
Common::KeyState keyState = events.getKey();
|
|
|
|
if (keyState.keycode == Common::KEYCODE_ESCAPE && vm._runningProlog) {
|
|
vm.setFlags(-76);
|
|
vm.setFlags(396);
|
|
scene._goToScene = 1;
|
|
talk._talkToAbort = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isHolmes)
|
|
_updateNPCPath = true;
|
|
|
|
if (!talk._talkToAbort)
|
|
events.setCursor(oldCursor);
|
|
}
|
|
}
|
|
|
|
void TattooPerson::clearNPC() {
|
|
Common::fill(&_npcPath[0], &_npcPath[MAX_NPC_PATH], 0);
|
|
_npcIndex = 0;
|
|
_pathStack.clear();
|
|
_npcName = "";
|
|
}
|
|
|
|
void TattooPerson::updateNPC() {
|
|
People &people = *_vm->_people;
|
|
Talk &talk = *_vm->_talk;
|
|
|
|
// If the NPC isn't on, or it's in Talk or Listen Mode, then return without doing anything
|
|
if (_type != CHARACTER || _sequenceNumber >= TALK_UPRIGHT)
|
|
return;
|
|
|
|
// If the NPC is paused, just decrement his pause counter and exit
|
|
if (_npcPause) {
|
|
// Decrement counter
|
|
--_npcPause;
|
|
|
|
// Now see if we need to update the NPC's frame sequence so that he faces Holmes
|
|
if (_lookHolmes) {
|
|
// See where Holmes is with respect to the NPC (x coordinate)
|
|
_npcFacing = (people[HOLMES]._position.x < _position.x) ? STOP_LEFT : STOP_RIGHT;
|
|
|
|
// See where Holmes is with respect to the NPC (y coordinate)
|
|
if (people[HOLMES]._position.y < (_position.y - 10 * FIXED_INT_MULTIPLIER)) {
|
|
// Holmes is above the NPC so reset the facing to a diagonal up
|
|
_npcFacing = (_npcFacing == STOP_RIGHT) ? STOP_UPRIGHT : STOP_UPLEFT;
|
|
} else if (people[HOLMES]._position.y > (_position.y + 10 * FIXED_INT_MULTIPLIER)) {
|
|
// Holmes is below the NPC so reset the facing to a diagonal down
|
|
_npcFacing = (_npcFacing == STOP_RIGHT) ? STOP_DOWNRIGHT : STOP_DOWNLEFT;
|
|
}
|
|
|
|
// See if we need to set the old_walk_sequence so the NPC will put his arms
|
|
// up if he turns another way
|
|
if (_sequenceNumber != _npcFacing)
|
|
_oldWalkSequence = _sequenceNumber;
|
|
|
|
gotoStand();
|
|
}
|
|
} else {
|
|
// Reset the look flag so the NPC won't face Holmes anymore
|
|
_lookHolmes = false;
|
|
|
|
// See if the NPC is stopped or not. Don't do anything if he's moving
|
|
if (!_walkCount) {
|
|
// If there is no new command, reset the path back to the beginning
|
|
if (!_npcPath[_npcIndex])
|
|
_npcIndex = 0;
|
|
|
|
// The NPC is stopped and any pause he was doing is done. We can now see what
|
|
// the next command in the NPC path is.
|
|
|
|
// Scan Past any NPC Path Labels since they do nothing except mark places for If's and Goto's
|
|
while (_npcPath[_npcIndex] == NPCPATH_PATH_LABEL)
|
|
_npcIndex += 2;
|
|
|
|
if (_npcPath[_npcIndex]) {
|
|
_npcFacing = -1;
|
|
|
|
switch (_npcPath[_npcIndex]) {
|
|
case NPCPATH_SET_DEST: {
|
|
// Set the NPC's new destination
|
|
int xp = (_npcPath[_npcIndex + 1] - 1) * 256 + _npcPath[_npcIndex + 2] - 1;
|
|
if (xp > 16384)
|
|
xp = -1 * (xp - 16384);
|
|
_walkDest.x = xp;
|
|
_walkDest.y = (_npcPath[_npcIndex + 3] - 1) * 256 + _npcPath[_npcIndex + 4] - 1;
|
|
_npcFacing = _npcPath[_npcIndex + 5] - 1;
|
|
|
|
goAllTheWay();
|
|
_npcIndex += 6;
|
|
break;
|
|
}
|
|
|
|
case NPCPATH_PAUSE:
|
|
// Set the NPC to pause where he is
|
|
_npcPause = (_npcPath[_npcIndex + 1] - 1) * 256 + _npcPath[_npcIndex + 2] - 1;
|
|
_npcIndex += 3;
|
|
break;
|
|
|
|
case NPCPATH_SET_TALK_FILE: {
|
|
// Set the NPC's Talk File to use if Holmes talks to them
|
|
++_npcIndex;
|
|
|
|
_npcName = "";
|
|
for (int idx = 0; idx < 8; ++idx) {
|
|
if (_npcPath[_npcIndex + idx] != '~')
|
|
_npcName += _npcPath[_npcIndex + idx];
|
|
else
|
|
break;
|
|
}
|
|
|
|
_npcIndex += 8;
|
|
break;
|
|
}
|
|
|
|
case NPCPATH_CALL_TALK_FILE: {
|
|
// Call a Talk File
|
|
++_npcIndex;
|
|
|
|
Common::String name;
|
|
for (int idx = 0; idx < 8; ++idx) {
|
|
if (_npcPath[_npcIndex + idx] != '~')
|
|
name += _npcPath[_npcIndex + idx];
|
|
else
|
|
break;
|
|
}
|
|
|
|
_npcIndex += 8;
|
|
talk.talkTo(name);
|
|
break;
|
|
}
|
|
|
|
case NPCPATH_TAKE_NOTES:
|
|
// Set the NPC to Pause where he is and set his frame sequences
|
|
// so he takes notes while he's paused
|
|
_npcPause = (_npcPath[_npcIndex + 1] - 1) * 256 + _npcPath[_npcIndex + 2] - 1;
|
|
_npcIndex += 3;
|
|
break;
|
|
|
|
case NPCPATH_FACE_HOLMES:
|
|
// Set the NPC to Pause where he is and set his look flag so he will always face Holmes
|
|
// while he is paused
|
|
_npcPause = (_npcPath[_npcIndex + 1] - 1) * 256 + _npcPath[_npcIndex + 2] - 1;
|
|
_lookHolmes = true;
|
|
_npcIndex += 3;
|
|
break;
|
|
|
|
//case NPCPATH_PATH_LABEL: // No implementation needed here
|
|
|
|
case NPCPATH_GOTO_LABEL: {
|
|
// Goto NPC Path Label
|
|
int label = _npcPath[_npcIndex + 1];
|
|
_npcIndex = 0;
|
|
|
|
// Scan through NPC path data to find the label
|
|
bool found = false;
|
|
while (!found) {
|
|
switch (_npcPath[_npcIndex]) {
|
|
case NPCPATH_SET_DEST:
|
|
_npcIndex += 6;
|
|
break;
|
|
case NPCPATH_PAUSE:
|
|
case NPCPATH_TAKE_NOTES:
|
|
case NPCPATH_FACE_HOLMES:
|
|
_npcIndex += 3;
|
|
break;
|
|
case NPCPATH_SET_TALK_FILE:
|
|
case NPCPATH_CALL_TALK_FILE:
|
|
_npcIndex += 8;
|
|
break;
|
|
case NPCPATH_PATH_LABEL:
|
|
if (_npcPath[_npcIndex + 1] == label)
|
|
found = true;
|
|
_npcIndex += 2;
|
|
break;
|
|
case NPCPATH_GOTO_LABEL:
|
|
_npcIndex += 2;
|
|
break;
|
|
case NPCPATH_IFFLAG_GOTO_LABEL:
|
|
_npcIndex += 4;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NPCPATH_IFFLAG_GOTO_LABEL: {
|
|
// If FLAG then Goto Label
|
|
int flag = (_npcPath[_npcIndex + 1] - 1) * 256 + _npcPath[_npcIndex + 2] - 1 - (_npcPath[_npcIndex + 2] == 1 ? 1 : 0);
|
|
|
|
// Set the value the flag should be for the if statement to succeed
|
|
bool flagVal = flag < 16384;
|
|
|
|
int label = _npcPath[_npcIndex + 3];
|
|
_npcIndex += 4;
|
|
|
|
// If the flag is set Correctly, move the NPC Index to the given label
|
|
if (_vm->readFlags(flag & 16383) == flagVal) {
|
|
_npcIndex = 0;
|
|
bool found = false;
|
|
while (!found)
|
|
{
|
|
switch (_npcPath[_npcIndex])
|
|
{
|
|
case NPCPATH_SET_DEST:
|
|
_npcIndex += 6;
|
|
break;
|
|
case NPCPATH_PAUSE:
|
|
case NPCPATH_TAKE_NOTES:
|
|
case NPCPATH_FACE_HOLMES:
|
|
_npcIndex += 3;
|
|
break;
|
|
case NPCPATH_SET_TALK_FILE:
|
|
case NPCPATH_CALL_TALK_FILE:
|
|
_npcIndex += 8;
|
|
break;
|
|
case NPCPATH_PATH_LABEL:
|
|
if (_npcPath[_npcIndex + 1] == label)
|
|
found = true;
|
|
_npcIndex += 2;
|
|
break;
|
|
case NPCPATH_GOTO_LABEL:
|
|
_npcIndex += 2;
|
|
break;
|
|
case NPCPATH_IFFLAG_GOTO_LABEL:
|
|
_npcIndex += 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TattooPerson::pushNPCPath() {
|
|
assert(_pathStack.size() < 2);
|
|
SavedNPCPath savedPath(_npcPath, _npcIndex, _npcPause, _position, _sequenceNumber, _lookHolmes);
|
|
_pathStack.push(savedPath);
|
|
}
|
|
|
|
void TattooPerson::pullNPCPath() {
|
|
// Pop the stack entry and restore the fields
|
|
SavedNPCPath path = _pathStack.pop();
|
|
Common::copy(&path._path[0], &path._path[MAX_NPC_PATH], &_npcPath[0]);
|
|
_npcIndex = path._npcIndex;
|
|
_npcPause = path._npcPause;
|
|
|
|
// Handle the first case if the NPC was paused
|
|
if (_npcPause) {
|
|
_walkDest = Common::Point(path._walkDest.x / FIXED_INT_MULTIPLIER, path._walkDest.y / FIXED_INT_MULTIPLIER);
|
|
_npcFacing = path._npcFacing;
|
|
_lookHolmes = path._lookHolmes;
|
|
|
|
// See if the NPC was moved
|
|
if (_walkDest.x != (_position.x / FIXED_INT_MULTIPLIER) ||
|
|
_walkDest.y != (_position.y / FIXED_INT_MULTIPLIER)) {
|
|
goAllTheWay();
|
|
_npcPause = 0;
|
|
_npcIndex -= 3;
|
|
} else {
|
|
// See if we need to set the old walk sequence so the NPC will put his arms up if he turns another way
|
|
if (_npcFacing != _sequenceNumber)
|
|
_oldWalkSequence = _sequenceNumber;
|
|
|
|
gotoStand();
|
|
}
|
|
} else {
|
|
// Handle the second case if the NPC was in motion
|
|
_npcIndex -= 6;
|
|
}
|
|
}
|
|
|
|
Common::Point TattooPerson::getSourcePoint() const {
|
|
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
|
int scaleVal = scene.getScaleVal(_position);
|
|
|
|
return Common::Point(_position.x / FIXED_INT_MULTIPLIER + _imageFrame->sDrawXSize(scaleVal) / 2,
|
|
_position.y / FIXED_INT_MULTIPLIER);
|
|
}
|
|
|
|
void TattooPerson::setObjTalkSequence(int seq) {
|
|
assert(seq != -1 && _type == CHARACTER);
|
|
|
|
if (_seqTo) {
|
|
// reset to previous value
|
|
_walkSequences[_sequenceNumber]._sequences[_frameNumber] = _seqTo;
|
|
_seqTo = 0;
|
|
}
|
|
|
|
_sequenceNumber = _gotoSeq;
|
|
_frameNumber = 0;
|
|
checkWalkGraphics();
|
|
}
|
|
|
|
void TattooPerson::checkWalkGraphics() {
|
|
People &people = *_vm->_people;
|
|
|
|
if (_images == nullptr) {
|
|
freeAltGraphics();
|
|
return;
|
|
}
|
|
|
|
Common::String filename = Common::String::format("%s.vgs", _walkSequences[_sequenceNumber]._vgsName.c_str());
|
|
|
|
// Set the adjust depending on if we have to fine tune the x position of this particular graphic
|
|
_adjust.x = _adjust.y = 0;
|
|
|
|
for (int idx = 0; idx < NUM_ADJUSTED_WALKS; ++idx) {
|
|
if (!scumm_strnicmp(_walkSequences[_sequenceNumber]._vgsName.c_str(), ADJUST_WALKS[idx]._vgsName,
|
|
strlen(ADJUST_WALKS[idx]._vgsName))) {
|
|
if (_walkSequences[_sequenceNumber]._horizFlip)
|
|
_adjust.x = ADJUST_WALKS[idx]._flipXAdjust;
|
|
else
|
|
_adjust.x = ADJUST_WALKS[idx]._xAdjust;
|
|
|
|
_adjust.y = ADJUST_WALKS[idx]._yAdjust;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// See if we're already using Alternate Graphics
|
|
if (_altSeq) {
|
|
// See if the VGS file called for is different than the alternate graphics already loaded
|
|
if (!_walkSequences[_sequenceNumber]._vgsName.compareToIgnoreCase(_walkSequences[_altSeq - 1]._vgsName)) {
|
|
// Different AltGraphics, Free the old ones
|
|
freeAltGraphics();
|
|
}
|
|
}
|
|
|
|
// If there is no Alternate Sequence set, see if we need to load a new one
|
|
if (!_altSeq) {
|
|
int npcNum = -1;
|
|
// Find which NPC this is so we can check the name of the graphics loaded
|
|
for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
|
|
if (this == &people[idx]) {
|
|
npcNum = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (npcNum != -1) {
|
|
// See if the VGS file called for is different than the main graphics which are already loaded
|
|
if (filename.compareToIgnoreCase(people[npcNum]._walkVGSName) != 0) {
|
|
// See if this is one of the more used Walk Graphics stored in WALK.LIB
|
|
for (int idx = 0; idx < NUM_IN_WALK_LIB; ++idx) {
|
|
if (!scumm_stricmp(filename.c_str(), WALK_LIB_NAMES[idx])) {
|
|
people._useWalkLib = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_altImages = new ImageFile(filename);
|
|
people._useWalkLib = false;
|
|
|
|
_altSeq = _sequenceNumber + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this is a different seqeunce from the current sequence, reset the appropriate variables
|
|
if (_sequences != &_walkSequences[_sequenceNumber]._sequences[0]) {
|
|
_seqTo = _seqCounter = _seqCounter2 = _seqStack = _startSeq = 0;
|
|
_sequences = &_walkSequences[_sequenceNumber]._sequences[0];
|
|
_seqSize = _walkSequences[_sequenceNumber]._sequences.size();
|
|
}
|
|
|
|
setImageFrame();
|
|
}
|
|
|
|
void TattooPerson::synchronize(Serializer &s) {
|
|
s.syncAsSint32LE(_position.x);
|
|
s.syncAsSint32LE(_position.y);
|
|
s.syncAsSint16LE(_sequenceNumber);
|
|
s.syncAsSint16LE(_type);
|
|
s.syncString(_walkVGSName);
|
|
s.syncString(_description);
|
|
s.syncString(_examine);
|
|
|
|
// NPC specific properties
|
|
s.syncBytes(&_npcPath[0], MAX_NPC_PATH);
|
|
s.syncString(_npcName);
|
|
s.syncAsSint32LE(_npcPause);
|
|
s.syncAsByte(_lookHolmes);
|
|
s.syncAsByte(_updateNPCPath);
|
|
|
|
// Walk to list
|
|
uint count = _walkTo.size();
|
|
s.syncAsUint16LE(count);
|
|
if (s.isLoading()) {
|
|
// Load path
|
|
for (uint idx = 0; idx < count; ++idx) {
|
|
int xp = 0, yp = 0;
|
|
s.syncAsSint16LE(xp);
|
|
s.syncAsSint16LE(yp);
|
|
_walkTo.push(Common::Point(xp, yp));
|
|
}
|
|
} else {
|
|
// Save path
|
|
Common::Array<Common::Point> path;
|
|
|
|
// Save the points of the path
|
|
for (uint idx = 0; idx < count; ++idx) {
|
|
Common::Point pt = _walkTo.pop();
|
|
s.syncAsSint16LE(pt.x);
|
|
s.syncAsSint16LE(pt.y);
|
|
path.push_back(pt);
|
|
}
|
|
|
|
// Re-add the pending points back to the _walkTo queue
|
|
for (uint idx = 0; idx < count; ++idx)
|
|
_walkTo.push(path[idx]);
|
|
}
|
|
|
|
// Verbs
|
|
for (int idx = 0; idx < 2; ++idx)
|
|
_use[idx].synchronize(s);
|
|
}
|
|
|
|
void TattooPerson::walkHolmesToNPC() {
|
|
Events &events = *_vm->_events;
|
|
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
|
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
|
Screen &screen = *_vm->_screen;
|
|
Talk &talk = *_vm->_talk;
|
|
TattooPerson &holmes = people[HOLMES];
|
|
int facing;
|
|
|
|
// If the NPC is moving, stop him at his current position
|
|
if (_walkCount) {
|
|
// Reset the facing so the NPC will stop facing the direction he was going,
|
|
// rather than the direction he was supposed to when he finished wlaking
|
|
_npcFacing = -1;
|
|
gotoStand();
|
|
}
|
|
|
|
int scaleVal = scene.getScaleVal(_position);
|
|
ImageFrame &imgFrame = (*holmes._images)[0];
|
|
|
|
// Clear the path variables
|
|
memset(_npcPath, 0, 100);
|
|
|
|
// Set the NPC path so he pauses for 250 while looking at Holmes
|
|
_npcPath[0] = 6;
|
|
_npcPath[1] = 1;
|
|
_npcPath[2] = 251;
|
|
_npcIndex = 0;
|
|
_npcPause = 250;
|
|
_lookHolmes = true;
|
|
|
|
// See where Holmes is with respect to the NPC (x coords)
|
|
if (holmes._position.x < _position.x) {
|
|
holmes._walkDest.x = MAX(_position.x / FIXED_INT_MULTIPLIER - imgFrame.sDrawXSize(scaleVal), 0);
|
|
} else {
|
|
holmes._walkDest.x = MIN(_position.x / FIXED_INT_MULTIPLIER + imgFrame.sDrawXSize(scaleVal) * 2,
|
|
screen._backBuffer1.w() - 1);
|
|
}
|
|
|
|
// See where Holmes is with respect to the NPC (y coords)
|
|
if (holmes._position.y < (_position.y - imgFrame.sDrawXSize(scaleVal) * 500)) {
|
|
holmes._walkDest.y = MAX(_position.y / FIXED_INT_MULTIPLIER - imgFrame.sDrawXSize(scaleVal) / 2, 0);
|
|
} else {
|
|
if (holmes._position.y > (_position.y + imgFrame.sDrawXSize(scaleVal) * 500)) {
|
|
// Holmes is below the NPC
|
|
holmes._walkDest.y = MIN(_position.y / FIXED_INT_MULTIPLIER + imgFrame.sDrawXSize(scaleVal) / 2,
|
|
SHERLOCK_SCREEN_HEIGHT - 1);
|
|
} else {
|
|
// Holmes is roughly on the same Y as the NPC
|
|
holmes._walkDest.y = _position.y / FIXED_INT_MULTIPLIER;
|
|
}
|
|
}
|
|
|
|
events.setCursor(WAIT);
|
|
|
|
_walkDest.x += 10;
|
|
people._allowWalkAbort = true;
|
|
holmes.goAllTheWay();
|
|
|
|
// Do doBgAnim should be called over and over until walk is done
|
|
do {
|
|
events.wait(1);
|
|
scene.doBgAnim();
|
|
} while (holmes._walkCount);
|
|
|
|
if (!talk._talkToAbort) {
|
|
// Setup correct direction for Holmes to face
|
|
|
|
// See where Holmes is with respect to the NPC (x coords)
|
|
facing = (holmes._position.x < _position.x) ? STOP_RIGHT : STOP_LEFT;
|
|
|
|
// See where Holmes is with respect to the NPC (y coords)
|
|
if (holmes._position.y < (_position.y - (10 * FIXED_INT_MULTIPLIER))) {
|
|
// Holmes is above the NPC. Reset the facing to the diagonal downs
|
|
facing = (facing == STOP_RIGHT) ? STOP_DOWNRIGHT : STOP_DOWNLEFT;
|
|
} else {
|
|
if (holmes._position.y > (_position.y + 10 * FIXED_INT_MULTIPLIER)) {
|
|
// Holmes is below the NPC. Reset the facing to the diagonal ups
|
|
facing = (facing == STOP_RIGHT) ? STOP_UPRIGHT : STOP_UPLEFT;
|
|
}
|
|
}
|
|
|
|
holmes._sequenceNumber = facing;
|
|
holmes.gotoStand();
|
|
|
|
events.setCursor(ARROW);
|
|
}
|
|
}
|
|
|
|
void TattooPerson::centerScreenOnPerson() {
|
|
Screen &screen = *_vm->_screen;
|
|
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
|
|
|
ui._targetScroll.x = CLIP(_position.x / FIXED_INT_MULTIPLIER - SHERLOCK_SCREEN_WIDTH / 2,
|
|
0, screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH);
|
|
screen._currentScroll = ui._targetScroll;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
TattooPeople::TattooPeople(SherlockEngine *vm) : People(vm) {
|
|
for (int idx = 0; idx < 6; ++idx)
|
|
_data.push_back(new TattooPerson());
|
|
}
|
|
|
|
void TattooPeople::setListenSequence(int speaker, int sequenceNum) {
|
|
Scene &scene = *_vm->_scene;
|
|
|
|
// If no speaker is specified, then nothing needs to be done
|
|
if (speaker == -1)
|
|
return;
|
|
|
|
int objNum = findSpeaker(speaker);
|
|
if (objNum < 256 && objNum != -1) {
|
|
// See if the Object has to wait for an Abort Talk Code
|
|
Object &obj = scene._bgShapes[objNum];
|
|
if (obj.hasAborts())
|
|
obj._gotoSeq = sequenceNum;
|
|
else
|
|
obj.setObjTalkSequence(sequenceNum);
|
|
} else if (objNum != -1) {
|
|
objNum -= 256;
|
|
TattooPerson &person = (*this)[objNum];
|
|
|
|
int newDir = person._sequenceNumber;
|
|
switch (person._sequenceNumber) {
|
|
case WALK_UP:
|
|
case STOP_UP:
|
|
case WALK_UPRIGHT:
|
|
case STOP_UPRIGHT:
|
|
case TALK_UPRIGHT:
|
|
case LISTEN_UPRIGHT:
|
|
newDir = LISTEN_UPRIGHT;
|
|
break;
|
|
case WALK_RIGHT:
|
|
case STOP_RIGHT:
|
|
case TALK_RIGHT:
|
|
case LISTEN_RIGHT:
|
|
newDir = LISTEN_RIGHT;
|
|
break;
|
|
case WALK_DOWNRIGHT:
|
|
case STOP_DOWNRIGHT:
|
|
case TALK_DOWNRIGHT:
|
|
case LISTEN_DOWNRIGHT:
|
|
newDir = LISTEN_DOWNRIGHT;
|
|
break;
|
|
case WALK_DOWN:
|
|
case STOP_DOWN:
|
|
case WALK_DOWNLEFT:
|
|
case STOP_DOWNLEFT:
|
|
case TALK_DOWNLEFT:
|
|
case LISTEN_DOWNLEFT:
|
|
newDir = LISTEN_DOWNLEFT;
|
|
break;
|
|
case WALK_LEFT:
|
|
case STOP_LEFT:
|
|
case TALK_LEFT:
|
|
case LISTEN_LEFT:
|
|
newDir = LISTEN_LEFT;
|
|
break;
|
|
case WALK_UPLEFT:
|
|
case STOP_UPLEFT:
|
|
case TALK_UPLEFT:
|
|
case LISTEN_UPLEFT:
|
|
newDir = LISTEN_UPLEFT;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// See if the NPC's Seq has to wait for an Abort Talk Code
|
|
if (person.hasAborts()) {
|
|
person._gotoSeq = newDir;
|
|
} else {
|
|
if (person._seqTo) {
|
|
// Reset to previous value
|
|
person._walkSequences[person._sequenceNumber]._sequences[person._frameNumber] = person._seqTo;
|
|
person._seqTo = 0;
|
|
}
|
|
|
|
person._sequenceNumber = newDir;
|
|
person._frameNumber = 0;
|
|
person.checkWalkGraphics();
|
|
}
|
|
}
|
|
}
|
|
|
|
void TattooPeople::setTalkSequence(int speaker, int sequenceNum) {
|
|
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
|
Scene &scene = *_vm->_scene;
|
|
TattooTalk &talk = *(TattooTalk *)_vm->_talk;
|
|
|
|
// If no speaker is specified, then nothing needs to be done
|
|
if (speaker == -1)
|
|
return;
|
|
|
|
int objNum = people.findSpeaker(speaker);
|
|
if (objNum != -1 && objNum < 256) {
|
|
Object &obj = scene._bgShapes[objNum];
|
|
|
|
// See if the Object has to wait for an Abort Talk Code
|
|
if (obj.hasAborts()) {
|
|
talk.pushTalkSequence(&obj);
|
|
obj._gotoSeq = sequenceNum;
|
|
}
|
|
else {
|
|
obj.setObjTalkSequence(sequenceNum);
|
|
}
|
|
}
|
|
else if (objNum != -1) {
|
|
objNum -= 256;
|
|
TattooPerson &person = people[objNum];
|
|
int newDir = person._sequenceNumber;
|
|
|
|
switch (newDir) {
|
|
case WALK_UP:
|
|
case STOP_UP:
|
|
case WALK_UPRIGHT:
|
|
case STOP_UPRIGHT:
|
|
case TALK_UPRIGHT:
|
|
case LISTEN_UPRIGHT:
|
|
newDir = TALK_UPRIGHT;
|
|
break;
|
|
case WALK_RIGHT:
|
|
case STOP_RIGHT:
|
|
case TALK_RIGHT:
|
|
case LISTEN_RIGHT:
|
|
newDir = TALK_RIGHT;
|
|
break;
|
|
case WALK_DOWNRIGHT:
|
|
case STOP_DOWNRIGHT:
|
|
case TALK_DOWNRIGHT:
|
|
case LISTEN_DOWNRIGHT:
|
|
newDir = TALK_DOWNRIGHT;
|
|
break;
|
|
case WALK_DOWN:
|
|
case STOP_DOWN:
|
|
case WALK_DOWNLEFT:
|
|
case STOP_DOWNLEFT:
|
|
case TALK_DOWNLEFT:
|
|
case LISTEN_DOWNLEFT:
|
|
newDir = TALK_DOWNLEFT;
|
|
break;
|
|
case WALK_LEFT:
|
|
case STOP_LEFT:
|
|
case TALK_LEFT:
|
|
case LISTEN_LEFT:
|
|
newDir = TALK_LEFT;
|
|
break;
|
|
case WALK_UPLEFT:
|
|
case STOP_UPLEFT:
|
|
case TALK_UPLEFT:
|
|
case LISTEN_UPLEFT:
|
|
newDir = TALK_UPLEFT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// See if the NPC's sequence has to wait for an Abort Talk Code
|
|
if (person.hasAborts()) {
|
|
person._gotoSeq = newDir;
|
|
} else {
|
|
if (person._seqTo) {
|
|
// Reset to previous value
|
|
person._walkSequences[person._sequenceNumber]._sequences[person._frameNumber] = person._seqTo;
|
|
person._seqTo = 0;
|
|
}
|
|
|
|
person._sequenceNumber = newDir;
|
|
person._frameNumber = 0;
|
|
person.checkWalkGraphics();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int TattooPeople::findSpeaker(int speaker) {
|
|
int result = People::findSpeaker(speaker);
|
|
const char *portrait = _characters[speaker]._portrait;
|
|
|
|
// Fallback that Rose Tattoo uses if no speaker was found
|
|
if (result == -1) {
|
|
bool flag = _vm->readFlags(FLAG_PLAYER_IS_HOLMES);
|
|
|
|
if (_data[HOLMES]->_type == CHARACTER && ((speaker == HOLMES && flag) || (speaker == WATSON && !flag)))
|
|
return -1;
|
|
|
|
for (uint idx = 1; idx < _data.size(); ++idx) {
|
|
TattooPerson &p = (*this)[idx];
|
|
|
|
if (p._type == CHARACTER) {
|
|
Common::String name(p._name.c_str(), p._name.c_str() + 4);
|
|
|
|
if (name.equalsIgnoreCase(portrait) && p._npcName[4] >= '0' && p._npcName[4] <= '9')
|
|
return idx + 256;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void TattooPeople::synchronize(Serializer &s) {
|
|
s.syncAsByte(_holmesOn);
|
|
|
|
for (uint idx = 0; idx < _data.size(); ++idx)
|
|
(*this)[idx].synchronize(s);
|
|
|
|
s.syncAsSint16LE(_holmesQuotient);
|
|
|
|
if (s.isLoading()) {
|
|
_savedPos.x = _data[HOLMES]->_position.x;
|
|
_savedPos.y = _data[HOLMES]->_position.y;
|
|
_savedPos._facing = _data[HOLMES]->_sequenceNumber;
|
|
}
|
|
}
|
|
|
|
bool TattooPeople::loadWalk() {
|
|
Resources &res = *_vm->_res;
|
|
bool result = false;
|
|
|
|
for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
|
|
Person &person = *_data[idx];
|
|
|
|
if (!person._walkLoaded && (person._type == CHARACTER || person._type == HIDDEN_CHARACTER)) {
|
|
if (person._type == HIDDEN_CHARACTER)
|
|
person._type = INVALID;
|
|
|
|
// See if this is one of the more used Walk Graphics stored in WALK.LIB
|
|
for (int libNum = 0; libNum < NUM_IN_WALK_LIB; ++libNum) {
|
|
if (!person._walkVGSName.compareToIgnoreCase(WALK_LIB_NAMES[libNum])) {
|
|
_useWalkLib = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Load the images for the character
|
|
person._images = new ImageFile(person._walkVGSName, false);
|
|
person._maxFrames = person._images->size();
|
|
|
|
// Load walk sequence data
|
|
Common::String fname = Common::String(person._walkVGSName.c_str(), strchr(person._walkVGSName.c_str(), '.'));
|
|
fname += ".SEQ";
|
|
|
|
// Load the walk sequence data
|
|
Common::SeekableReadStream *stream = res.load(fname, _useWalkLib ? "walk.lib" : "vgs.lib");
|
|
|
|
person._walkSequences.resize(stream->readByte());
|
|
|
|
for (uint seqNum = 0; seqNum < person._walkSequences.size(); ++seqNum)
|
|
person._walkSequences[seqNum].load(*stream);
|
|
|
|
// Close the sequences resource
|
|
delete stream;
|
|
_useWalkLib = false;
|
|
|
|
person._sequences = &person._walkSequences[person._sequenceNumber]._sequences[0];
|
|
person._seqSize = person._walkSequences[person._sequenceNumber]._sequences.size();
|
|
person._frameNumber = 0;
|
|
person.setImageFrame();
|
|
|
|
// Set the stop Frames pointers
|
|
for (int dirNum = 0; dirNum < 8; ++dirNum) {
|
|
int count = 0;
|
|
while (person._walkSequences[dirNum + 8][count] != 0)
|
|
++count;
|
|
count += 2;
|
|
count = person._walkSequences[dirNum + 8][count] - 1;
|
|
person._stopFrames[dirNum] = &(*person._images)[count];
|
|
}
|
|
|
|
result = true;
|
|
person._walkLoaded = true;
|
|
} else if (person._type != CHARACTER) {
|
|
person._walkLoaded = false;
|
|
}
|
|
}
|
|
|
|
_forceWalkReload = false;
|
|
return result;
|
|
}
|
|
|
|
|
|
void TattooPeople::pullNPCPaths() {
|
|
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
|
TattooPerson &p = (*this)[idx];
|
|
if (p._npcMoved) {
|
|
while (!p._pathStack.empty())
|
|
p.pullNPCPath();
|
|
}
|
|
}
|
|
}
|
|
|
|
const Common::Point TattooPeople::restrictToZone(int zoneId, const Common::Point &destPos) {
|
|
Scene &scene = *_vm->_scene;
|
|
Screen &screen = *_vm->_screen;
|
|
Common::Rect &r = scene._zones[zoneId];
|
|
|
|
if (destPos.x < 0 || destPos.x > screen._backBuffer1.w())
|
|
return destPos;
|
|
else if (destPos.y < r.top && r.left < destPos.x && destPos.x < r.right)
|
|
return Common::Point(destPos.x, r.top);
|
|
else if (destPos.y > r.bottom && r.left < destPos.x && destPos.x < r.right)
|
|
return Common::Point(destPos.x, r.bottom);
|
|
else if (destPos.x < r.left && r.top < destPos.y && destPos.y < r.bottom)
|
|
return Common::Point(r.left, destPos.y);
|
|
else if (destPos.x > r.right && r.top < destPos.y && destPos.y < r.bottom)
|
|
return Common::Point(r.bottom, destPos.y);
|
|
|
|
// Find which corner of the zone the point is closet to
|
|
if (destPos.x <= r.left) {
|
|
return Common::Point(r.left, (destPos.y <= r.top) ? r.top : r.bottom);
|
|
} else {
|
|
return Common::Point(r.right, (destPos.y <= r.top) ? r.top : r.bottom);
|
|
}
|
|
}
|
|
|
|
|
|
} // End of namespace Tattoo
|
|
|
|
} // End of namespace Sherlock
|