mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-07 10:21:31 +00:00
ad7372b0c2
In Pratt's Loft, the player can probe the pillow with either the needle or the hatpin. However, the German version accidentally set up use actions for the hatpin twice, resulting in a crash if you tried to use it
1581 lines
40 KiB
C++
1581 lines
40 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 "common/util.h"
|
|
#include "sherlock/objects.h"
|
|
#include "sherlock/people.h"
|
|
#include "sherlock/scene.h"
|
|
#include "sherlock/scalpel/scalpel.h"
|
|
#include "sherlock/scalpel/scalpel_map.h"
|
|
#include "sherlock/scalpel/scalpel_people.h"
|
|
#include "sherlock/tattoo/tattoo.h"
|
|
|
|
namespace Sherlock {
|
|
|
|
#define START_FRAME 0
|
|
|
|
#define NUM_ADJUSTED_WALKS 21
|
|
|
|
// Distance to walk around WALK_AROUND boxes
|
|
#define CLEAR_DIST_X 5
|
|
#define CLEAR_DIST_Y 0
|
|
|
|
#define ADJUST_COORD(COORD) \
|
|
if (COORD.x != -1) \
|
|
COORD.x *= FIXED_INT_MULTIPLIER; \
|
|
if (COORD.y != -1) \
|
|
COORD.y *= FIXED_INT_MULTIPLIER
|
|
|
|
SherlockEngine *BaseObject::_vm;
|
|
bool BaseObject::_countCAnimFrames;
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
void BaseObject::setVm(SherlockEngine *vm) {
|
|
_vm = vm;
|
|
_countCAnimFrames = false;
|
|
}
|
|
|
|
BaseObject::BaseObject() {
|
|
_type = INVALID;
|
|
_sequences = nullptr;
|
|
_images = nullptr;
|
|
_imageFrame = nullptr;
|
|
_sequenceNumber = 0;
|
|
_startSeq = 0;
|
|
_walkCount = 0;
|
|
_allow = 0;
|
|
_frameNumber = 0;
|
|
_lookFlag = 0;
|
|
_requiredFlag[0] = _requiredFlag[1] = 0;
|
|
_status = 0;
|
|
_misc = 0;
|
|
_maxFrames = 0;
|
|
_flags = 0;
|
|
_aType = OBJECT;
|
|
_lookFrames = 0;
|
|
_seqCounter = 0;
|
|
_lookcAnim = 0;
|
|
_seqStack = 0;
|
|
_seqTo = 0;
|
|
_descOffset = 0;
|
|
_seqCounter2 = 0;
|
|
_seqSize = 0;
|
|
_quickDraw = 0;
|
|
_scaleVal = 0;
|
|
_gotoSeq = 0;
|
|
_talkSeq = 0;
|
|
_restoreSlot = 0;
|
|
}
|
|
|
|
bool BaseObject::hasAborts() const {
|
|
int seqNum = _talkSeq;
|
|
|
|
// See if the object is in its regular sequence
|
|
bool startChecking = !seqNum || _type == CHARACTER;
|
|
|
|
uint idx = 0;
|
|
do
|
|
{
|
|
// Get the Frame value
|
|
int v = _sequences[idx++];
|
|
|
|
// See if we found an Allow Talk Interrupt Code
|
|
if (startChecking && v == ALLOW_TALK_CODE)
|
|
return true;
|
|
|
|
// If we've started checking and we've encountered another Talk or Listen Sequence Code,
|
|
// then we're done checking this sequence because this is where it would repeat
|
|
if (startChecking && (v == TALK_SEQ_CODE || v == TALK_LISTEN_CODE))
|
|
break;
|
|
|
|
// See if we've found the beginning of a Talk Sequence
|
|
if ((v == TALK_SEQ_CODE && seqNum < 128) || (v == TALK_LISTEN_CODE && seqNum >= 128)) {
|
|
// If checking was already on and we came across one of these codes, then there couldn't
|
|
// have been an Allow Talk Interrupt code in the sequence we were checking, so we're done.
|
|
if (startChecking)
|
|
break;
|
|
|
|
seqNum--;
|
|
// See if we're at the correct Talk Sequence Number
|
|
if (!(seqNum & 127))
|
|
{
|
|
// Correct Sequence, Start Checking Now
|
|
startChecking = true;
|
|
}
|
|
} else {
|
|
// Move ahead any extra because of special control codes
|
|
switch (v) {
|
|
case 0: idx++; break;
|
|
case MOVE_CODE:
|
|
case TELEPORT_CODE: idx += 4; break;
|
|
case CALL_TALK_CODE:idx += 8; break;
|
|
case HIDE_CODE: idx += 2; break;
|
|
}
|
|
}
|
|
} while (idx < _seqSize);
|
|
|
|
return false;
|
|
}
|
|
|
|
void BaseObject::checkObject() {
|
|
Scene &scene = *_vm->_scene;
|
|
Sound &sound = *_vm->_sound;
|
|
Talk &talk = *_vm->_talk;
|
|
int checkFrame = _allow ? MAX_FRAME : FRAMES_END;
|
|
bool codeFound;
|
|
|
|
if (_seqTo) {
|
|
byte *ptr = &_sequences[_frameNumber];
|
|
if (*ptr == _seqTo) {
|
|
// The sequence is completed. Reset to normal
|
|
*ptr = _seqTo + (IS_ROSE_TATTOO ? 0 : SEQ_TO_CODE + 128);
|
|
_seqTo = 0;
|
|
} else {
|
|
// Continue doing sequence
|
|
if (*ptr > _seqTo)
|
|
*ptr -= 1;
|
|
else
|
|
*ptr += 1;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
++_frameNumber;
|
|
|
|
do {
|
|
if (!_sequences) {
|
|
warning("checkObject: _sequences is not set");
|
|
break;
|
|
}
|
|
|
|
// Check for end of sequence
|
|
codeFound = checkEndOfSequence();
|
|
|
|
if (_sequences[_frameNumber] >= 128 && _frameNumber < checkFrame) {
|
|
codeFound = true;
|
|
int v = _sequences[_frameNumber];
|
|
|
|
// Check for a Talk or Listen Sequence
|
|
if (IS_ROSE_TATTOO && v == ALLOW_TALK_CODE) {
|
|
if (_gotoSeq) {
|
|
setObjTalkSequence(_gotoSeq);
|
|
_gotoSeq = 0;
|
|
} else {
|
|
++_frameNumber;
|
|
}
|
|
} else if (IS_ROSE_TATTOO && (v == TALK_SEQ_CODE || v == TALK_LISTEN_CODE)) {
|
|
if (_talkSeq)
|
|
setObjTalkSequence(_talkSeq);
|
|
else
|
|
setObjSequence(0, false);
|
|
} else if (v >= GOTO_CODE) {
|
|
// Goto code found
|
|
v -= GOTO_CODE;
|
|
_seqCounter2 = _seqCounter;
|
|
_seqStack = _frameNumber + 1;
|
|
setObjSequence(v, false);
|
|
} else if (v >= SOUND_CODE && (v < (SOUND_CODE + 30))) {
|
|
codeFound = true;
|
|
++_frameNumber;
|
|
v -= SOUND_CODE + (IS_SERRATED_SCALPEL ? 1 : 0);
|
|
|
|
if (sound._soundOn && !_countCAnimFrames) {
|
|
if (!scene._sounds[v]._name.empty() && sound._digitized)
|
|
sound.playLoadedSound(v, WAIT_RETURN_IMMEDIATELY);
|
|
}
|
|
} else if (v >= FLIP_CODE && v < (FLIP_CODE + 3)) {
|
|
// Flip code
|
|
codeFound = true;
|
|
++_frameNumber;
|
|
v -= FLIP_CODE;
|
|
|
|
// Alter the flipped status
|
|
switch (v) {
|
|
case 0:
|
|
// Clear the flag
|
|
_flags &= ~OBJ_FLIPPED;
|
|
break;
|
|
case 1:
|
|
// Set the flag
|
|
_flags |= OBJ_FLIPPED;
|
|
break;
|
|
case 2:
|
|
// Toggle the flag
|
|
_flags ^= OBJ_FLIPPED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (IS_ROSE_TATTOO && v == TELEPORT_CODE) {
|
|
_position.x = READ_LE_UINT16(&_sequences[_frameNumber + 1]);
|
|
_position.y = READ_LE_UINT16(&_sequences[_frameNumber + 3]);
|
|
|
|
_frameNumber += 5;
|
|
} else if (IS_ROSE_TATTOO && v == CALL_TALK_CODE) {
|
|
Common::String filename;
|
|
for (int idx = 0; idx < 8; ++idx) {
|
|
if (_sequences[_frameNumber + 1 + idx] != 1)
|
|
filename += (char)_sequences[_frameNumber + 1 + idx];
|
|
else
|
|
break;
|
|
}
|
|
|
|
_frameNumber += 8;
|
|
talk.talkTo(filename);
|
|
|
|
} else if (IS_ROSE_TATTOO && v == HIDE_CODE) {
|
|
switch (_sequences[_frameNumber + 2]) {
|
|
case 1:
|
|
// Hide Object
|
|
if (scene._bgShapes[_sequences[_frameNumber + 1] - 1]._type != HIDDEN)
|
|
scene._bgShapes[_sequences[_frameNumber + 1] - 1].toggleHidden();
|
|
break;
|
|
|
|
case 2:
|
|
// Activate Object
|
|
if (scene._bgShapes[_sequences[_frameNumber + 1] - 1]._type == HIDDEN)
|
|
scene._bgShapes[_sequences[_frameNumber + 1] - 1].toggleHidden();
|
|
break;
|
|
|
|
case 3:
|
|
// Toggle Object
|
|
scene._bgShapes[_sequences[_frameNumber + 1] - 1].toggleHidden();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
_frameNumber += 3;
|
|
|
|
} else {
|
|
v -= 128;
|
|
|
|
// 68-99 is a sequence code
|
|
if (v > SEQ_TO_CODE) {
|
|
if (IS_ROSE_TATTOO) {
|
|
++_frameNumber;
|
|
byte *p = &_sequences[_frameNumber];
|
|
_seqTo = *p;
|
|
*p = *(p - 2);
|
|
|
|
if (*p > _seqTo)
|
|
*p -= 1;
|
|
else
|
|
*p += 1;
|
|
|
|
--_frameNumber;
|
|
} else {
|
|
byte *p = &_sequences[_frameNumber];
|
|
v -= SEQ_TO_CODE; // # from 1-32
|
|
_seqTo = v;
|
|
*p = *(p - 1);
|
|
|
|
if (*p > 128)
|
|
// If the high bit is set, convert to a real frame
|
|
*p -= (byte)(SEQ_TO_CODE - 128);
|
|
|
|
if (*p > _seqTo)
|
|
*p -= 1;
|
|
else
|
|
*p += 1;
|
|
|
|
// Will be incremented below to return back to original value
|
|
--_frameNumber;
|
|
v = 0;
|
|
}
|
|
} else if (IS_ROSE_TATTOO && v == 10) {
|
|
// Set delta for objects
|
|
_delta = Common::Point(READ_LE_UINT16(&_sequences[_frameNumber + 1]),
|
|
READ_LE_UINT16(&_sequences[_frameNumber + 3]));
|
|
_noShapeSize = Common::Point(0, 0);
|
|
_frameNumber += 4;
|
|
|
|
} else if (v == 10) {
|
|
// Set delta for objects
|
|
Common::Point pt(_sequences[_frameNumber + 1], _sequences[_frameNumber + 2]);
|
|
if (pt.x > 128)
|
|
pt.x = (pt.x - 128) * -1;
|
|
else
|
|
pt.x--;
|
|
|
|
if (pt.y > 128)
|
|
pt.y = (pt.y - 128) * -1;
|
|
else
|
|
pt.y--;
|
|
|
|
_delta = pt;
|
|
_frameNumber += 2;
|
|
|
|
} else if (v < USE_COUNT) {
|
|
for (int idx = 0; idx < NAMES_COUNT; ++idx) {
|
|
checkNameForCodes(_use[v]._names[idx]);
|
|
}
|
|
|
|
if (_use[v]._useFlag)
|
|
_vm->setFlags(_use[v]._useFlag);
|
|
}
|
|
|
|
++_frameNumber;
|
|
}
|
|
}
|
|
} while (codeFound);
|
|
}
|
|
|
|
bool BaseObject::checkEndOfSequence() {
|
|
Screen &screen = *_vm->_screen;
|
|
int checkFrame = _allow ? MAX_FRAME : FRAMES_END;
|
|
bool result = false;
|
|
|
|
if (_type == REMOVE || _type == INVALID)
|
|
return false;
|
|
|
|
if (_frameNumber < 0 || _frameNumber >= checkFrame || _sequences[_frameNumber] == 0) {
|
|
result = true;
|
|
|
|
if (_frameNumber < 0 || _frameNumber >= (checkFrame - 1)) {
|
|
_frameNumber = START_FRAME;
|
|
} else {
|
|
// Determine next sequence to use
|
|
int seq = _sequences[_frameNumber + 1];
|
|
|
|
// If the object has been turned off, we're going nowhere
|
|
if (IS_ROSE_TATTOO && (_type == HIDE_SHAPE || _type == HIDDEN || _type == REMOVE))
|
|
return false;
|
|
|
|
if (seq == 99) {
|
|
--_frameNumber;
|
|
screen._backBuffer1.transBlitFrom(*_imageFrame, _position);
|
|
screen._backBuffer2.transBlitFrom(*_imageFrame, _position);
|
|
_type = INVALID;
|
|
} else if (IS_ROSE_TATTOO && _talkSeq && seq == 0) {
|
|
setObjTalkSequence(_talkSeq);
|
|
} else {
|
|
setObjSequence(seq, false);
|
|
}
|
|
}
|
|
|
|
if (_allow && _frameNumber == 0) {
|
|
// canimation just ended
|
|
if (_type != NO_SHAPE && _type != REMOVE) {
|
|
_type = REMOVE;
|
|
|
|
if (!_countCAnimFrames) {
|
|
// Save details before shape is removed
|
|
_delta.x = _imageFrame->_frame.w;
|
|
_delta.y = _imageFrame->_frame.h;
|
|
_position += _imageFrame->_offset;
|
|
|
|
// Free the images
|
|
delete _images;
|
|
_images = nullptr;
|
|
_imageFrame = nullptr;
|
|
}
|
|
} else {
|
|
_type = INVALID;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void BaseObject::setObjSequence(int seq, bool wait) {
|
|
Scene &scene = *_vm->_scene;
|
|
int checkFrame = _allow ? MAX_FRAME : FRAMES_END;
|
|
|
|
if (IS_ROSE_TATTOO && (seq == -1 || seq == 255))
|
|
// This means goto beginning
|
|
seq = 0;
|
|
|
|
if (seq >= 128) {
|
|
// Loop the sequence until the count exceeded
|
|
seq -= 128;
|
|
|
|
++_seqCounter;
|
|
if (_seqCounter >= seq) {
|
|
// Go to next sequence
|
|
if (_seqStack) {
|
|
_frameNumber = _seqStack;
|
|
_seqStack = 0;
|
|
_seqCounter = _seqCounter2;
|
|
_seqCounter2 = 0;
|
|
if (_frameNumber >= checkFrame)
|
|
_frameNumber = START_FRAME;
|
|
|
|
return;
|
|
}
|
|
|
|
_frameNumber += 2;
|
|
if (_frameNumber >= checkFrame)
|
|
_frameNumber = 0;
|
|
|
|
// For Rose Tattoo, save the starting frame for new sequences
|
|
if (IS_ROSE_TATTOO)
|
|
_startSeq = _frameNumber;
|
|
|
|
_seqCounter = 0;
|
|
if (_sequences[_frameNumber] == 0)
|
|
seq = _sequences[_frameNumber + 1];
|
|
else
|
|
return;
|
|
} else {
|
|
// Find beginning of sequence
|
|
if (IS_ROSE_TATTOO) {
|
|
// Use the saved start of the sequence to reset the frame
|
|
_frameNumber = _startSeq;
|
|
} else {
|
|
// For Scalpel, scan backwards from the end of the sequence to find its start
|
|
do {
|
|
--_frameNumber;
|
|
} while (_frameNumber > 0 && _sequences[_frameNumber] != 0);
|
|
|
|
if (_frameNumber != 0)
|
|
_frameNumber += 2;
|
|
}
|
|
|
|
return;
|
|
}
|
|
} else {
|
|
// Reset sequence counter
|
|
_seqCounter = 0;
|
|
}
|
|
|
|
int idx = 0;
|
|
int seqCc = 0;
|
|
|
|
while (seqCc < seq && idx < checkFrame) {
|
|
if (IS_SERRATED_SCALPEL) {
|
|
++idx;
|
|
|
|
if (_sequences[idx] == 0) {
|
|
++seqCc;
|
|
idx += 2;
|
|
}
|
|
} else {
|
|
byte s = _sequences[idx];
|
|
|
|
if (s == 0) {
|
|
++seqCc;
|
|
++idx;
|
|
} else if (s == MOVE_CODE || s == TELEPORT_CODE) {
|
|
idx += 4;
|
|
} else if (s == CALL_TALK_CODE) {
|
|
idx += 8;
|
|
} else if (s == HIDE_CODE) {
|
|
idx += 2;
|
|
}
|
|
|
|
++idx;
|
|
}
|
|
}
|
|
|
|
if (idx >= checkFrame)
|
|
idx = 0;
|
|
_frameNumber = idx;
|
|
_startSeq = idx;
|
|
|
|
if (wait) {
|
|
seqCc = idx;
|
|
while (_sequences[idx] != 0)
|
|
++idx;
|
|
|
|
idx = idx - seqCc + 2;
|
|
for (; idx > 0; --idx)
|
|
scene.doBgAnim();
|
|
}
|
|
}
|
|
|
|
int BaseObject::checkNameForCodes(const Common::String &name, FixedTextActionId fixedTextActionId) {
|
|
FixedText &fixedText = *_vm->_fixedText;
|
|
People &people = *_vm->_people;
|
|
Scene &scene = *_vm->_scene;
|
|
Screen &screen = *_vm->_screen;
|
|
Talk &talk = *_vm->_talk;
|
|
UserInterface &ui = *_vm->_ui;
|
|
bool printed = false;
|
|
|
|
scene.toggleObject(name);
|
|
|
|
if (name.hasPrefix("*")) {
|
|
// A code was found
|
|
printed = true;
|
|
char ch = (name == "*") ? 0 : toupper(name[1]);
|
|
|
|
switch (ch) {
|
|
case 'C':
|
|
talk.talkTo(name.c_str() + 2);
|
|
break;
|
|
|
|
case 'T':
|
|
case 'B':
|
|
case 'F':
|
|
case 'W':
|
|
// Nothing: action was already done before canimation
|
|
break;
|
|
|
|
case 'G':
|
|
case 'A': {
|
|
// G: Have object go somewhere
|
|
// A: Add onto existing co-ordinates
|
|
Common::String sx(name.c_str() + 2, name.c_str() + 5);
|
|
Common::String sy(name.c_str() + 5, name.c_str() + 8);
|
|
|
|
if (ch == 'G')
|
|
_position = Common::Point(atoi(sx.c_str()), atoi(sy.c_str()));
|
|
else
|
|
_position += Common::Point(atoi(sx.c_str()), atoi(sy.c_str()));
|
|
break;
|
|
}
|
|
|
|
case 'V':
|
|
// Do nothing for Verb codes. This is only a flag for Inventory syntax
|
|
break;
|
|
|
|
default:
|
|
if (ch >= '0' && ch <= '9') {
|
|
scene._goToScene = atoi(name.c_str() + 1);
|
|
|
|
if (IS_SERRATED_SCALPEL && scene._goToScene < 97) {
|
|
Scalpel::ScalpelMap &map = *(Scalpel::ScalpelMap *)_vm->_map;
|
|
if (map[scene._goToScene].x) {
|
|
map._overPos.x = (map[scene._goToScene].x - 6) * FIXED_INT_MULTIPLIER;
|
|
map._overPos.y = (map[scene._goToScene].y + 9) * FIXED_INT_MULTIPLIER;
|
|
}
|
|
}
|
|
|
|
const char *p;
|
|
if ((p = strchr(name.c_str(), ',')) != nullptr) {
|
|
++p;
|
|
|
|
Common::String s(p, p + 3);
|
|
people._savedPos.x = atoi(s.c_str());
|
|
|
|
s = Common::String(p + 3, p + 6);
|
|
people._savedPos.y = atoi(s.c_str());
|
|
|
|
s = Common::String(p + 6, p + 9);
|
|
people._savedPos._facing = atoi(s.c_str());
|
|
if (people._savedPos._facing == 0)
|
|
people._savedPos._facing = 10;
|
|
} else if ((p = strchr(name.c_str(), '/')) != nullptr) {
|
|
people._savedPos = PositionFacing(1, 0, 100 + atoi(p + 1));
|
|
}
|
|
} else {
|
|
scene._goToScene = 100;
|
|
}
|
|
|
|
people[HOLMES]._position = Point32(0, 0);
|
|
break;
|
|
}
|
|
} else if (name.hasPrefix("!")) {
|
|
// Message attached to canimation
|
|
int messageNum = atoi(name.c_str() + 1);
|
|
ui._infoFlag = true;
|
|
ui.clearInfo();
|
|
Common::String errorMessage = fixedText.getActionMessage(fixedTextActionId, messageNum);
|
|
screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, "%s", errorMessage.c_str());
|
|
ui._menuCounter = 25;
|
|
} else if (name.hasPrefix("@")) {
|
|
// Message attached to canimation
|
|
ui._infoFlag = true;
|
|
ui.clearInfo();
|
|
screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, "%s", name.c_str() + 1);
|
|
printed = true;
|
|
ui._menuCounter = 25;
|
|
}
|
|
|
|
return printed;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
void Sprite::clear() {
|
|
_name = "";
|
|
_description = "";
|
|
_examine.clear();
|
|
_pickUp = "";
|
|
_walkSequences.clear();
|
|
_sequences = nullptr;
|
|
_images = nullptr;
|
|
_imageFrame = nullptr;
|
|
_walkCount = 0;
|
|
_oldWalkSequence = 0;
|
|
_allow = 0;
|
|
_frameNumber = 0;
|
|
_position.x = _position.y = 0;
|
|
_delta.x = _delta.y = 0;
|
|
_oldPosition.x = _oldPosition.y = 0;
|
|
_oldSize.x = _oldSize.y = 0;
|
|
_goto.x = _goto.y = 0;
|
|
_type = INVALID;
|
|
_pickUp.clear();
|
|
_noShapeSize.x = _noShapeSize.y = 0;
|
|
_status = 0;
|
|
_misc = 0;
|
|
_altImages = nullptr;
|
|
_altSeq = 0;
|
|
_centerWalk = 0;
|
|
Common::fill(&_stopFrames[0], &_stopFrames[8], (ImageFrame *)nullptr);
|
|
}
|
|
|
|
void Sprite::setImageFrame() {
|
|
int frameNum = MAX(_frameNumber, 0);
|
|
int imageNumber = _walkSequences[_sequenceNumber][frameNum];
|
|
|
|
if (IS_SERRATED_SCALPEL)
|
|
imageNumber = imageNumber + _walkSequences[_sequenceNumber][0] - 2;
|
|
else if (imageNumber > _maxFrames)
|
|
imageNumber = 1;
|
|
|
|
// Get the images to use
|
|
ImageFile *images = _altSeq ? _altImages : _images;
|
|
assert(images);
|
|
|
|
if (IS_3DO) {
|
|
// only do this to the image-array with 110 entries
|
|
// map uses another image-array and this code
|
|
if (images->size() == 110) {
|
|
// 3DO has 110 animation frames inside walk.anim
|
|
// PC has 55
|
|
// this adjusts the framenumber accordingly
|
|
// sort of HACK
|
|
imageNumber *= 2;
|
|
}
|
|
} else if (IS_ROSE_TATTOO) {
|
|
--imageNumber;
|
|
}
|
|
|
|
// Set the frame pointer
|
|
_imageFrame = &(*images)[imageNumber];
|
|
}
|
|
|
|
void Sprite::checkSprite() {
|
|
Events &events = *_vm->_events;
|
|
People &people = *_vm->_people;
|
|
Scene &scene = *_vm->_scene;
|
|
Screen &screen = *_vm->_screen;
|
|
Talk &talk = *_vm->_talk;
|
|
Point32 pt;
|
|
Common::Rect objBounds;
|
|
Common::Point spritePt(_position.x / FIXED_INT_MULTIPLIER, _position.y / FIXED_INT_MULTIPLIER);
|
|
|
|
if (_type != CHARACTER || (IS_SERRATED_SCALPEL && talk._talkCounter))
|
|
return;
|
|
|
|
pt = _walkCount ? _position + _delta : _position;
|
|
pt.x /= FIXED_INT_MULTIPLIER;
|
|
pt.y /= FIXED_INT_MULTIPLIER;
|
|
|
|
if (IS_ROSE_TATTOO) {
|
|
checkObject();
|
|
|
|
// For Rose Tattoo, we only do the further processing for Sherlock
|
|
if (this != &people[HOLMES])
|
|
return;
|
|
}
|
|
|
|
for (uint idx = 0; idx < scene._bgShapes.size() && !talk._talkToAbort; ++idx) {
|
|
Object &obj = scene._bgShapes[idx];
|
|
if (obj._aType <= PERSON || obj._type == INVALID || obj._type == HIDDEN)
|
|
continue;
|
|
|
|
if (obj._type == NO_SHAPE) {
|
|
objBounds = Common::Rect(obj._position.x, obj._position.y,
|
|
obj._position.x + obj._noShapeSize.x + 1, obj._position.y + obj._noShapeSize.y + 1);
|
|
} else {
|
|
int xp = obj._position.x + obj._imageFrame->_offset.x;
|
|
int yp = obj._position.y + obj._imageFrame->_offset.y;
|
|
objBounds = Common::Rect(xp, yp,
|
|
xp + obj._imageFrame->_frame.w + 1, yp + obj._imageFrame->_frame.h + 1);
|
|
}
|
|
|
|
if (objBounds.contains(pt)) {
|
|
if (objBounds.contains(spritePt)) {
|
|
// Current point is already inside the the bounds, so impact occurred
|
|
// on a previous call. So simply do nothing until we're clear of the box
|
|
switch (obj._aType) {
|
|
case TALK_MOVE:
|
|
if (_walkCount) {
|
|
// Holmes is moving
|
|
obj._type = HIDDEN;
|
|
obj.setFlagsAndToggles();
|
|
talk.talkTo(obj._use[0]._target);
|
|
}
|
|
break;
|
|
|
|
case PAL_CHANGE:
|
|
case PAL_CHANGE2:
|
|
if (_walkCount) {
|
|
int palStart = atoi(obj._use[0]._names[0].c_str()) * 3;
|
|
int palLength = atoi(obj._use[0]._names[1].c_str()) * 3;
|
|
int templ = atoi(obj._use[0]._names[2].c_str()) * 3;
|
|
if (templ == 0)
|
|
templ = 100;
|
|
|
|
// Ensure only valid palette change data found
|
|
if (palLength > 0) {
|
|
// Figure out how far into the shape Holmes is so that we
|
|
// can figure out what percentage of the original palette
|
|
// to set the current palette to
|
|
int palPercent = (pt.x - objBounds.left) * 100 / objBounds.width();
|
|
palPercent = palPercent * templ / 100;
|
|
if (obj._aType == PAL_CHANGE)
|
|
// Invert percentage
|
|
palPercent = 100 - palPercent;
|
|
|
|
for (int i = palStart; i < (palStart + palLength); ++i)
|
|
screen._sMap[i] = screen._cMap[i] * palPercent / 100;
|
|
|
|
events.pollEvents();
|
|
screen.setPalette(screen._sMap);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TALK:
|
|
case TALK_EVERY:
|
|
obj._type = HIDDEN;
|
|
obj.setFlagsAndToggles();
|
|
talk.talkTo(obj._use[0]._target);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
// New impact just occurred
|
|
switch (obj._aType) {
|
|
case BLANK_ZONE:
|
|
// A blank zone masks out all other remaining zones underneath it.
|
|
// If this zone is hit, exit the outer loop so we do not check anymore
|
|
return;
|
|
|
|
case SOLID:
|
|
case TALK:
|
|
// Stop walking
|
|
if (obj._aType == TALK) {
|
|
obj.setFlagsAndToggles();
|
|
talk.talkTo(obj._use[0]._target);
|
|
} else {
|
|
gotoStand();
|
|
}
|
|
break;
|
|
|
|
case TALK_EVERY:
|
|
if (obj._aType == TALK_EVERY) {
|
|
obj._type = HIDDEN;
|
|
obj.setFlagsAndToggles();
|
|
talk.talkTo(obj._use[0]._target);
|
|
} else {
|
|
gotoStand();
|
|
}
|
|
break;
|
|
|
|
case FLAG_SET:
|
|
obj.setFlagsAndToggles();
|
|
obj._type = HIDDEN;
|
|
break;
|
|
|
|
case WALK_AROUND:
|
|
if (objBounds.contains(people[HOLMES]._walkTo.front())) {
|
|
// Reached zone
|
|
gotoStand();
|
|
} else {
|
|
// Destination not within box, walk to best corner
|
|
Common::Point walkPos;
|
|
|
|
if (spritePt.x >= objBounds.left && spritePt.x < objBounds.right) {
|
|
// Impact occurred due to vertical movement. Determine whether to
|
|
// travel to the left or right side
|
|
if (_delta.x > 0)
|
|
// Go to right side
|
|
walkPos.x = objBounds.right + CLEAR_DIST_X;
|
|
else if (_delta.x < 0) {
|
|
// Go to left side
|
|
walkPos.x = objBounds.left - CLEAR_DIST_X;
|
|
} else {
|
|
// Going straight up or down. So choose best side
|
|
if (spritePt.x >= (objBounds.left + objBounds.width() / 2))
|
|
walkPos.x = objBounds.right + CLEAR_DIST_X;
|
|
else
|
|
walkPos.x = objBounds.left - CLEAR_DIST_X;
|
|
}
|
|
|
|
walkPos.y = (_delta.y >= 0) ? objBounds.top - CLEAR_DIST_Y :
|
|
objBounds.bottom + CLEAR_DIST_Y;
|
|
} else {
|
|
// Impact occurred due to horizontal movement
|
|
if (_delta.y > 0)
|
|
// Go to bottom of box
|
|
walkPos.y = objBounds.bottom + CLEAR_DIST_Y;
|
|
else if (_delta.y < 0)
|
|
// Go to top of box
|
|
walkPos.y = objBounds.top - CLEAR_DIST_Y;
|
|
else {
|
|
// Going straight horizontal, so choose best side
|
|
if (spritePt.y >= (objBounds.top + objBounds.height() / 2))
|
|
walkPos.y = objBounds.bottom + CLEAR_DIST_Y;
|
|
else
|
|
walkPos.y = objBounds.top - CLEAR_DIST_Y;
|
|
}
|
|
|
|
walkPos.x = (_delta.x >= 0) ? objBounds.left - CLEAR_DIST_X :
|
|
objBounds.right + CLEAR_DIST_X;
|
|
}
|
|
|
|
walkPos.x += people[HOLMES]._imageFrame->_frame.w / 2;
|
|
people[HOLMES]._walkDest = walkPos;
|
|
people[HOLMES]._walkTo.push(walkPos);
|
|
people[HOLMES].setWalking();
|
|
}
|
|
break;
|
|
|
|
case DELTA:
|
|
_position.x += 200;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const Common::Rect Sprite::getOldBounds() const {
|
|
return Common::Rect(_oldPosition.x, _oldPosition.y, _oldPosition.x + _oldSize.x, _oldPosition.y + _oldSize.y);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
void WalkSequence::load(Common::SeekableReadStream &s) {
|
|
char buffer[9];
|
|
s.read(buffer, 9);
|
|
_vgsName = Common::String(buffer);
|
|
_horizFlip = s.readByte() != 0;
|
|
|
|
_sequences.resize(s.readUint16LE());
|
|
s.skip(4); // Skip over pointer field of structure
|
|
|
|
s.read(&_sequences[0], _sequences.size());
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
WalkSequences &WalkSequences::operator=(const WalkSequences &src) {
|
|
resize(src.size());
|
|
for (uint idx = 0; idx < size(); ++idx) {
|
|
const WalkSequence &wSrc = src[idx];
|
|
WalkSequence &wDest = (*this)[idx];
|
|
wDest._horizFlip = wSrc._horizFlip;
|
|
|
|
wDest._sequences.resize(wSrc._sequences.size());
|
|
Common::copy(&wSrc._sequences[0], &wSrc._sequences[0] + wSrc._sequences.size(), &wDest._sequences[0]);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
ActionType::ActionType() {
|
|
_cAnimNum = _cAnimSpeed = 0;
|
|
_useFlag = 0;
|
|
}
|
|
|
|
void ActionType::load(Common::SeekableReadStream &s) {
|
|
char buffer[12];
|
|
|
|
_cAnimNum = s.readByte();
|
|
_cAnimSpeed = s.readByte();
|
|
if (_cAnimSpeed & 0x80)
|
|
_cAnimSpeed = -(_cAnimSpeed & 0x7f);
|
|
|
|
for (int idx = 0; idx < NAMES_COUNT; ++idx) {
|
|
s.read(buffer, 12);
|
|
_names[idx] = Common::String(buffer);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
UseType::UseType(): ActionType() {
|
|
}
|
|
|
|
void UseType::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
|
|
char buffer[12];
|
|
|
|
if (isRoseTattoo) {
|
|
s.read(buffer, 12);
|
|
_verb = Common::String(buffer);
|
|
}
|
|
|
|
ActionType::load(s);
|
|
|
|
_useFlag = s.readSint16LE();
|
|
|
|
if (!isRoseTattoo)
|
|
s.skip(6);
|
|
|
|
s.read(buffer, 12);
|
|
_target = Common::String(buffer);
|
|
}
|
|
|
|
void UseType::load3DO(Common::SeekableReadStream &s) {
|
|
char buffer[12];
|
|
|
|
_cAnimNum = s.readByte();
|
|
_cAnimSpeed = s.readByte();
|
|
if (_cAnimSpeed & 0x80)
|
|
_cAnimSpeed = -(_cAnimSpeed & 0x7f);
|
|
|
|
for (int idx = 0; idx < NAMES_COUNT; ++idx) {
|
|
s.read(buffer, 12);
|
|
_names[idx] = Common::String(buffer);
|
|
}
|
|
|
|
_useFlag = s.readSint16BE();
|
|
|
|
s.skip(6);
|
|
|
|
s.read(buffer, 12);
|
|
_target = Common::String(buffer);
|
|
}
|
|
|
|
void UseType::synchronize(Serializer &s) {
|
|
s.syncString(_verb);
|
|
s.syncAsSint16LE(_cAnimNum);
|
|
s.syncAsSint16LE(_cAnimSpeed);
|
|
s.syncAsSint16LE(_useFlag);
|
|
|
|
for (int idx = 0; idx < 4; ++idx)
|
|
s.syncString(_names[idx]);
|
|
s.syncString(_target);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
Object::Object(): BaseObject() {
|
|
_sequenceOffset = 0;
|
|
_pickup = 0;
|
|
_defaultCommand = 0;
|
|
_pickupFlag = 0;
|
|
}
|
|
|
|
void Object::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
|
|
char buffer[41];
|
|
s.read(buffer, 12);
|
|
_name = Common::String(buffer);
|
|
s.read(buffer, 41);
|
|
_description = Common::String(buffer);
|
|
|
|
_examine.clear();
|
|
_sequences = nullptr;
|
|
_images = nullptr;
|
|
_imageFrame = nullptr;
|
|
|
|
s.skip(4);
|
|
_sequenceOffset = s.readUint16LE();
|
|
s.seek(10, SEEK_CUR);
|
|
|
|
_walkCount = s.readByte();
|
|
_allow = s.readByte();
|
|
_frameNumber = s.readSint16LE();
|
|
_sequenceNumber = s.readSint16LE();
|
|
_position.x = s.readSint16LE();
|
|
_position.y = s.readSint16LE();
|
|
_delta.x = s.readSint16LE();
|
|
_delta.y = s.readSint16LE();
|
|
_type = (SpriteType)s.readUint16LE();
|
|
_oldPosition.x = s.readSint16LE();
|
|
_oldPosition.y = s.readSint16LE();
|
|
_oldSize.x = s.readUint16LE();
|
|
_oldSize.y = s.readUint16LE();
|
|
|
|
_goto.x = s.readSint16LE();
|
|
_goto.y = s.readSint16LE();
|
|
if (!isRoseTattoo) {
|
|
_goto.x = _goto.x * FIXED_INT_MULTIPLIER / 100;
|
|
_goto.y = _goto.y * FIXED_INT_MULTIPLIER / 100;
|
|
}
|
|
|
|
_pickup = isRoseTattoo ? 0 : s.readByte();
|
|
_defaultCommand = isRoseTattoo ? 0 : s.readByte();
|
|
_lookFlag = s.readSint16LE();
|
|
_pickupFlag = isRoseTattoo ? 0 : s.readSint16LE();
|
|
_requiredFlag[0] = s.readSint16LE();
|
|
_noShapeSize.x = s.readUint16LE();
|
|
_noShapeSize.y = s.readUint16LE();
|
|
_status = s.readUint16LE();
|
|
_misc = s.readByte();
|
|
_maxFrames = s.readUint16LE();
|
|
_flags = s.readByte();
|
|
|
|
if (!isRoseTattoo)
|
|
_aOpen.load(s);
|
|
|
|
_aType = (AType)s.readByte();
|
|
_lookFrames = s.readByte();
|
|
_seqCounter = s.readByte();
|
|
if (isRoseTattoo) {
|
|
_lookPosition.x = s.readUint16LE() * FIXED_INT_MULTIPLIER;
|
|
_lookPosition.y = s.readSint16LE() * FIXED_INT_MULTIPLIER;
|
|
} else {
|
|
_lookPosition.x = s.readUint16LE() * FIXED_INT_MULTIPLIER / 100;
|
|
_lookPosition.y = s.readByte() * FIXED_INT_MULTIPLIER;
|
|
}
|
|
_lookPosition._facing = s.readByte();
|
|
_lookcAnim = s.readByte();
|
|
|
|
if (!isRoseTattoo)
|
|
_aClose.load(s);
|
|
|
|
_seqStack = s.readByte();
|
|
_seqTo = s.readByte();
|
|
_descOffset = s.readUint16LE();
|
|
_seqCounter2 = s.readByte();
|
|
_seqSize = s.readUint16LE();
|
|
|
|
if (isRoseTattoo) {
|
|
for (int idx = 0; idx < 6; ++idx)
|
|
_use[idx].load(s, true);
|
|
|
|
// WORKAROUND: Fix German version using hatpin/pin in pillow in Pratt's loft
|
|
if (_use[1]._target == "Nadel" && _use[1]._verb == "Untersuche"
|
|
&& _use[2]._target == "Nadel" && _use[2]._verb == "Untersuche")
|
|
_use[1]._target = "Alte Nadel";
|
|
|
|
_quickDraw = s.readByte();
|
|
_scaleVal = s.readUint16LE();
|
|
_requiredFlag[1] = s.readSint16LE();
|
|
_gotoSeq = s.readByte();
|
|
_talkSeq = s.readByte();
|
|
_restoreSlot = s.readByte();
|
|
} else {
|
|
s.skip(1);
|
|
_aMove.load(s);
|
|
s.skip(8);
|
|
|
|
for (int idx = 0; idx < 4; ++idx)
|
|
_use[idx].load(s, false);
|
|
}
|
|
//warning("object %s, useAnim %d", _name.c_str(), _use[0]._cAnimNum);
|
|
}
|
|
|
|
void Object::load3DO(Common::SeekableReadStream &s) {
|
|
int32 streamStartPos = s.pos();
|
|
char buffer[41];
|
|
|
|
_examine.clear();
|
|
_sequences = nullptr;
|
|
_images = nullptr;
|
|
_imageFrame = nullptr;
|
|
|
|
// on 3DO all of this data is reordered!!!
|
|
// it seems that possibly the 3DO compiler reordered the global struct
|
|
// 3DO size for 1 object is 588 bytes
|
|
s.skip(4);
|
|
_sequenceOffset = s.readUint16LE(); // weird that this seems to be LE
|
|
s.seek(10, SEEK_CUR);
|
|
|
|
// Offset 16
|
|
_frameNumber = s.readSint16BE();
|
|
_sequenceNumber = s.readSint16BE();
|
|
_position.x = s.readSint16BE();
|
|
_position.y = s.readSint16BE();
|
|
_delta.x = s.readSint16BE();
|
|
_delta.y = s.readSint16BE();
|
|
_type = (SpriteType)s.readUint16BE();
|
|
_oldPosition.x = s.readSint16BE();
|
|
_oldPosition.y = s.readSint16BE();
|
|
_oldSize.x = s.readUint16BE();
|
|
_oldSize.y = s.readUint16BE();
|
|
|
|
_goto.x = s.readSint16BE();
|
|
_goto.y = s.readSint16BE();
|
|
_goto.x = _goto.x * FIXED_INT_MULTIPLIER / 100;
|
|
_goto.y = _goto.y * FIXED_INT_MULTIPLIER / 100;
|
|
|
|
// Offset 42
|
|
warning("pos %d", s.pos());
|
|
|
|
// Unverified
|
|
_lookFlag = s.readSint16BE();
|
|
_pickupFlag = s.readSint16BE();
|
|
_requiredFlag[0] = s.readSint16BE();
|
|
_noShapeSize.x = s.readUint16BE();
|
|
_noShapeSize.y = s.readUint16BE();
|
|
_status = s.readUint16BE();
|
|
// Unverified END
|
|
|
|
_maxFrames = s.readUint16BE();
|
|
// offset 56
|
|
_lookPosition.x = s.readUint16BE() * FIXED_INT_MULTIPLIER / 100;
|
|
// offset 58
|
|
_descOffset = s.readUint16BE();
|
|
_seqSize = s.readUint16BE();
|
|
|
|
s.skip(2); // boundary filler
|
|
|
|
// 288 bytes
|
|
for (int idx = 0; idx < 4; ++idx) {
|
|
_use[idx].load3DO(s);
|
|
s.skip(2); // Filler
|
|
}
|
|
|
|
// 158 bytes
|
|
_aOpen.load(s); // 2 + 12*4 bytes = 50 bytes
|
|
s.skip(2); // Boundary filler
|
|
_aClose.load(s);
|
|
s.skip(2); // Filler
|
|
_aMove.load(s);
|
|
s.skip(2); // Filler
|
|
|
|
// offset 508
|
|
// 3DO: name is at the end
|
|
s.read(buffer, 12);
|
|
_name = Common::String(buffer);
|
|
s.read(buffer, 41);
|
|
_description = Common::String(buffer);
|
|
|
|
// Unverified
|
|
_walkCount = s.readByte();
|
|
_allow = s.readByte();
|
|
_pickup = s.readByte();
|
|
_defaultCommand = s.readByte();
|
|
// Unverified END
|
|
|
|
// Probably those here?!?!
|
|
_misc = s.readByte();
|
|
_flags = s.readByte();
|
|
|
|
// Unverified
|
|
_aType = (AType)s.readByte();
|
|
_lookFrames = s.readByte();
|
|
_seqCounter = s.readByte();
|
|
// Unverified END
|
|
|
|
_lookPosition.y = s.readByte() * FIXED_INT_MULTIPLIER;
|
|
_lookPosition._facing = s.readByte();
|
|
|
|
// Unverified
|
|
_lookcAnim = s.readByte();
|
|
_seqStack = s.readByte();
|
|
_seqTo = s.readByte();
|
|
_seqCounter2 = s.readByte();
|
|
// Unverified END
|
|
|
|
s.skip(12); // Unknown
|
|
|
|
//warning("object %s, offset %d", _name.c_str(), streamPos);
|
|
//warning("object %s, lookPosX %d, lookPosY %d", _name.c_str(), _lookPosition.x, _lookPosition.y);
|
|
//warning("object %s, defCmd %d", _name.c_str(), _defaultCommand);
|
|
int32 dataSize = s.pos() - streamStartPos;
|
|
assert(dataSize == 588);
|
|
}
|
|
|
|
void Object::toggleHidden() {
|
|
if (_type != HIDDEN && _type != HIDE_SHAPE && _type != INVALID) {
|
|
if (_seqTo != 0)
|
|
_sequences[_frameNumber] = _seqTo + SEQ_TO_CODE + 128;
|
|
_seqTo = 0;
|
|
|
|
if (_images == nullptr || _images->size() == 0)
|
|
// No shape to erase, so flag as hidden
|
|
_type = HIDDEN;
|
|
else
|
|
// Otherwise, flag it to be hidden after it gets erased
|
|
_type = HIDE_SHAPE;
|
|
} else if (_type != INVALID) {
|
|
if (_seqTo != 0)
|
|
_sequences[_frameNumber] = _seqTo + SEQ_TO_CODE + 128;
|
|
_seqTo = 0;
|
|
|
|
_seqCounter = _seqCounter2 = 0;
|
|
_seqStack = 0;
|
|
_frameNumber = -1;
|
|
|
|
if (_images == nullptr || _images->size() == 0) {
|
|
_type = NO_SHAPE;
|
|
} else {
|
|
_type = ACTIVE_BG_SHAPE;
|
|
int idx = _sequences[0];
|
|
if (idx >= _maxFrames)
|
|
// Turn on: set up first frame
|
|
idx = 0;
|
|
|
|
_imageFrame = &(*_images)[idx];
|
|
}
|
|
}
|
|
}
|
|
|
|
void Object::setObjTalkSequence(int seq) {
|
|
Talk &talk = *_vm->_talk;
|
|
|
|
// See if we're supposed to restore the object's sequence from the talk sequence stack
|
|
if (seq == -1) {
|
|
if (_seqTo != 0)
|
|
_sequences[_frameNumber] = _seqTo;
|
|
|
|
talk.pullSequence(_restoreSlot);
|
|
return;
|
|
}
|
|
|
|
assert(_type != CHARACTER);
|
|
|
|
talk.pushSequenceEntry(this);
|
|
int talkSeqNum = seq;
|
|
|
|
// Find where the talk sequence data begins in the object
|
|
int idx = 0;
|
|
for (;;) {
|
|
// Get the Frame value
|
|
byte f = _sequences[idx++];
|
|
|
|
// See if we've found the beginning of a Talk Sequence
|
|
if ((f == TALK_SEQ_CODE && seq < 128) || (f == TALK_LISTEN_CODE && seq > 128)) {
|
|
--seq;
|
|
|
|
// See if we're at the correct Talk Sequence Number
|
|
if (!(seq & 127))
|
|
{
|
|
// Correct Sequence, Start Talking Here
|
|
if (_seqTo != 0)
|
|
_sequences[_frameNumber] = _seqTo;
|
|
_frameNumber = idx;
|
|
_seqTo = 0;
|
|
_seqStack = 0;
|
|
_seqCounter = 0;
|
|
_seqCounter2 = 0;
|
|
_talkSeq = talkSeqNum;
|
|
break;
|
|
}
|
|
} else {
|
|
// Move ahead any extra because of special control codes
|
|
switch (f) {
|
|
case 0: idx++; break;
|
|
case MOVE_CODE:
|
|
case TELEPORT_CODE: idx += 4; break;
|
|
case CALL_TALK_CODE: idx += 8; break;
|
|
case HIDE_CODE: idx += 2; break;
|
|
}
|
|
}
|
|
|
|
// See if we're out of sequence data
|
|
if (idx >= (int)_seqSize)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Object::setFlagsAndToggles() {
|
|
Scene &scene = *_vm->_scene;
|
|
Talk &talk = *_vm->_talk;
|
|
|
|
for (int useIdx = 0; useIdx < USE_COUNT; ++useIdx) {
|
|
if (_use[useIdx]._useFlag) {
|
|
if (!_vm->readFlags(_use[useIdx]._useFlag))
|
|
_vm->setFlags(_use[useIdx]._useFlag);
|
|
}
|
|
|
|
if (_use[useIdx]._cAnimSpeed) {
|
|
if (_use[useIdx]._cAnimNum == 0)
|
|
// 0 is really a 10
|
|
scene.startCAnim(9, _use[useIdx]._cAnimSpeed);
|
|
else
|
|
scene.startCAnim(_use[useIdx]._cAnimNum - 1, _use[useIdx]._cAnimSpeed);
|
|
}
|
|
|
|
if (!talk._talkToAbort) {
|
|
for (int idx = 0; idx < NAMES_COUNT; ++idx)
|
|
scene.toggleObject(_use[useIdx]._names[idx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Object::adjustObject() {
|
|
if (_type == REMOVE)
|
|
return;
|
|
|
|
if (IS_ROSE_TATTOO && (_delta.x || _delta.y)) {
|
|
// The shape position is in pixels, and the delta is in fixed integer amounts
|
|
int t;
|
|
_noShapeSize.x += _delta.x;
|
|
t = _noShapeSize.x / (FIXED_INT_MULTIPLIER / 10);
|
|
_noShapeSize.x -= t * (FIXED_INT_MULTIPLIER / 10);
|
|
_position.x += t;
|
|
|
|
_noShapeSize.y += _delta.y;
|
|
t = _noShapeSize.y / (FIXED_INT_MULTIPLIER / 10);
|
|
_noShapeSize.y -= t * (FIXED_INT_MULTIPLIER / 10);
|
|
_position.y += t;
|
|
} else if (IS_SERRATED_SCALPEL) {
|
|
// The delta is in whole pixels, so simply adjust the position with it
|
|
_position += _delta;
|
|
}
|
|
|
|
if (_position.y > LOWER_LIMIT)
|
|
_position.y = LOWER_LIMIT;
|
|
|
|
if (_type != NO_SHAPE) {
|
|
int frame = _frameNumber;
|
|
if (frame == -1)
|
|
frame = 0;
|
|
|
|
int imgNum = _sequences[frame];
|
|
if (imgNum > _maxFrames || imgNum == 0)
|
|
imgNum = 1;
|
|
|
|
_imageFrame = &(*_images)[imgNum - 1];
|
|
}
|
|
}
|
|
|
|
int Object::pickUpObject(FixedTextActionId fixedTextActionId) {
|
|
FixedText &fixedText = *_vm->_fixedText;
|
|
Inventory &inv = *_vm->_inventory;
|
|
People &people = *_vm->_people;
|
|
Scene &scene = *_vm->_scene;
|
|
Screen &screen = *_vm->_screen;
|
|
Talk &talk = *_vm->_talk;
|
|
UserInterface &ui = *_vm->_ui;
|
|
int pickup = _pickup & 0x7f;
|
|
bool printed = false;
|
|
int numObjects = 0;
|
|
|
|
if (pickup == 99) {
|
|
for (int idx = 0; idx < NAMES_COUNT && !talk._talkToAbort; ++idx) {
|
|
if (checkNameForCodes(_use[0]._names[idx], kFixedTextAction_Invalid)) {
|
|
if (!talk._talkToAbort)
|
|
printed = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (!pickup || (pickup > 50 && pickup <= 80)) {
|
|
int message = _pickup;
|
|
if (message > 50)
|
|
message -= 50;
|
|
|
|
ui._infoFlag = true;
|
|
ui.clearInfo();
|
|
Common::String errorMessage = fixedText.getActionMessage(fixedTextActionId, message);
|
|
screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, "%s", errorMessage.c_str());
|
|
ui._menuCounter = 30;
|
|
} else {
|
|
// Pick it up
|
|
bool takeFlag = true;
|
|
if ((_pickup & 0x80) == 0) {
|
|
// Play an animation
|
|
if (pickup > 80) {
|
|
takeFlag = false; // Don't pick it up
|
|
scene.startCAnim(pickup - 81, 1);
|
|
if (_pickupFlag)
|
|
_vm->setFlags(_pickupFlag);
|
|
} else {
|
|
scene.startCAnim(pickup - 1, 1);
|
|
if (!talk._talkToAbort) {
|
|
// Erase the shape
|
|
_type = _type == NO_SHAPE ? INVALID : REMOVE;
|
|
}
|
|
}
|
|
|
|
if (talk._talkToAbort)
|
|
return 0;
|
|
} else {
|
|
// Play generic pickup sequence
|
|
// Original moved cursor position here
|
|
people[HOLMES].goAllTheWay();
|
|
ui._menuCounter = 25;
|
|
ui._temp1 = 1;
|
|
}
|
|
|
|
for (int idx = 0; idx < NAMES_COUNT && !talk._talkToAbort; ++idx) {
|
|
if (checkNameForCodes(_use[0]._names[idx], kFixedTextAction_Invalid)) {
|
|
if (!talk._talkToAbort)
|
|
printed = true;
|
|
}
|
|
}
|
|
if (talk._talkToAbort)
|
|
return 0;
|
|
|
|
// Add the item to the player's inventory
|
|
if (takeFlag)
|
|
numObjects = inv.putItemInInventory(*this);
|
|
|
|
if (!printed) {
|
|
ui._infoFlag = true;
|
|
ui.clearInfo();
|
|
|
|
Common::String itemName = _description;
|
|
|
|
// It's an item, make it lowercase
|
|
switch (_vm->getLanguage()) {
|
|
case Common::DE_DEU:
|
|
// don't do this for German version
|
|
break;
|
|
default:
|
|
// do it for English + Spanish version
|
|
itemName.setChar(tolower(itemName[0]), 0);
|
|
break;
|
|
}
|
|
|
|
screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, fixedText.getObjectPickedUpText(), itemName.c_str());
|
|
ui._menuCounter = 25;
|
|
}
|
|
}
|
|
|
|
return numObjects;
|
|
}
|
|
|
|
const Common::Rect Object::getNewBounds() const {
|
|
Point32 pt = _position;
|
|
if (_imageFrame)
|
|
pt += _imageFrame->_offset;
|
|
|
|
return Common::Rect(pt.x, pt.y, pt.x + frameWidth(), pt.y + frameHeight());
|
|
}
|
|
|
|
const Common::Rect Object::getNoShapeBounds() const {
|
|
return Common::Rect(_position.x, _position.y,
|
|
_position.x + _noShapeSize.x, _position.y + _noShapeSize.y);
|
|
}
|
|
|
|
const Common::Rect Object::getOldBounds() const {
|
|
return Common::Rect(_oldPosition.x, _oldPosition.y,
|
|
_oldPosition.x + _oldSize.x, _oldPosition.y + _oldSize.y);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
void CAnim::load(Common::SeekableReadStream &s, bool isRoseTattoo, uint32 dataOffset) {
|
|
char buffer[12];
|
|
s.read(buffer, 12);
|
|
_name = Common::String(buffer);
|
|
|
|
if (isRoseTattoo) {
|
|
Common::fill(&_sequences[0], &_sequences[30], 0);
|
|
_dataSize = s.readUint32LE();
|
|
} else {
|
|
s.read(_sequences, 30);
|
|
}
|
|
|
|
_position.x = s.readSint16LE();
|
|
_position.y = s.readSint16LE();
|
|
|
|
if (isRoseTattoo) {
|
|
_flags = s.readByte();
|
|
_scaleVal = s.readSint16LE();
|
|
} else {
|
|
_dataSize = s.readUint32LE();
|
|
_type = (SpriteType)s.readUint16LE();
|
|
_flags = s.readByte();
|
|
}
|
|
|
|
_goto[0].x = s.readSint16LE();
|
|
_goto[0].y = s.readSint16LE();
|
|
_goto[0]._facing = s.readSint16LE();
|
|
ADJUST_COORD(_goto[0]);
|
|
|
|
if (isRoseTattoo) {
|
|
// Get Goto position and facing for second NPC
|
|
_goto[1].x = s.readSint16LE();
|
|
_goto[1].y = s.readSint16LE();
|
|
_goto[1]._facing = s.readSint16LE();
|
|
ADJUST_COORD(_goto[1]);
|
|
} else if (_goto[0].x != -1) {
|
|
// For Serrated Scalpel, adjust the loaded co-ordinates
|
|
_goto[0].x = _goto[0].x / 100;
|
|
_goto[0].y = _goto[0].y / 100;
|
|
}
|
|
|
|
_teleport[0].x = s.readSint16LE();
|
|
_teleport[0].y = s.readSint16LE();
|
|
_teleport[0]._facing = s.readSint16LE();
|
|
ADJUST_COORD(_teleport[0]);
|
|
|
|
if (isRoseTattoo) {
|
|
// Get Teleport position and facing for second NPC
|
|
_teleport[1].x = s.readSint16LE();
|
|
_teleport[1].y = s.readSint16LE();
|
|
_teleport[1]._facing = s.readSint16LE();
|
|
ADJUST_COORD(_teleport[1]);
|
|
} else if (_teleport[0].x != -1) {
|
|
// For Serrated Scalpel, adjust the loaded co-ordinates
|
|
_teleport[0].x = _teleport[0].x / 100;
|
|
_teleport[0].y = _teleport[0].y / 100;
|
|
}
|
|
|
|
// Save offset of data, which is actually inside another table inside the room data file
|
|
// This table is at offset 44 for Serrated Scalpel
|
|
// TODO: find it for the other game
|
|
_dataOffset = dataOffset;
|
|
}
|
|
|
|
void CAnim::load3DO(Common::SeekableReadStream &s, uint32 dataOffset) {
|
|
// this got reordered on 3DO
|
|
// maybe it was the 3DO compiler
|
|
|
|
_dataSize = s.readUint32BE();
|
|
// Save offset of data, which is inside another table inside the room data file
|
|
_dataOffset = dataOffset;
|
|
|
|
_position.x = s.readSint16BE();
|
|
_position.y = s.readSint16BE();
|
|
|
|
_type = (SpriteType)s.readUint16BE();
|
|
|
|
_goto[0].x = s.readSint16BE();
|
|
_goto[0].y = s.readSint16BE();
|
|
_goto[0]._facing = s.readSint16BE();
|
|
|
|
_teleport[0].x = s.readSint16BE();
|
|
_teleport[0].y = s.readSint16BE();
|
|
_teleport[0]._facing = s.readSint16BE();
|
|
|
|
char buffer[12];
|
|
s.read(buffer, 12);
|
|
_name = Common::String(buffer);
|
|
|
|
s.read(_sequences, 30);
|
|
_flags = s.readByte();
|
|
|
|
s.skip(3); // Filler
|
|
|
|
_goto[0].x = _goto[0].x * FIXED_INT_MULTIPLIER / 100;
|
|
_goto[0].y = _goto[0].y * FIXED_INT_MULTIPLIER / 100;
|
|
_teleport[0].x = _teleport[0].x * FIXED_INT_MULTIPLIER / 100;
|
|
_teleport[0].y = _teleport[0].y * FIXED_INT_MULTIPLIER / 100;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
SceneImage::SceneImage() {
|
|
_images = nullptr;
|
|
_maxFrames = 0;
|
|
_filesize = 0;
|
|
}
|
|
|
|
} // End of namespace Sherlock
|