aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock/objects.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sherlock/objects.cpp')
-rw-r--r--engines/sherlock/objects.cpp1533
1 files changed, 1533 insertions, 0 deletions
diff --git a/engines/sherlock/objects.cpp b/engines/sherlock/objects.cpp
new file mode 100644
index 0000000000..6ef08c28cc
--- /dev/null
+++ b/engines/sherlock/objects.cpp
@@ -0,0 +1,1533 @@
+/* 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;
+ _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 it's 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))
+ return false;
+
+ // 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)
+ return false;
+
+ 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 true;
+}
+
+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);
+ } 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 (_sequences[_frameNumber] == 0 || _frameNumber >= checkFrame) {
+ result = true;
+
+ if (_frameNumber >= (checkFrame - 1)) {
+ _frameNumber = START_FRAME;
+ } else {
+ // Determine next sequence to use
+ int seq = _sequences[_frameNumber + 1];
+
+ 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 (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;
+
+ _seqCounter = 0;
+ if (_sequences[_frameNumber] == 0)
+ seq = _sequences[_frameNumber + 1];
+ else
+ return;
+ } else {
+ // Find beginning of sequence
+ 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) {
+ ++idx;
+ if (_sequences[idx] == 0) {
+ ++seqCc;
+ idx += 2;
+ }
+ }
+
+ if (idx >= checkFrame)
+ idx = 0;
+ _frameNumber = 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() + 6, name.c_str() + 9);
+
+ 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;
+ _allow = 0;
+ _frameNumber = _sequenceNumber = 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;
+ 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() {
+ _sequenceNumber = 0;
+ _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);
+
+ _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) {
+ TalkSequence &ts = talk._talkSequenceStack[_restoreSlot];
+ if (_seqTo != 0)
+ _sequences[_frameNumber] = _seqTo;
+ _frameNumber = ts._frameNumber;
+ _sequenceNumber = ts._sequenceNumber;
+ _seqStack = ts._seqStack;
+ _seqTo = ts._seqTo;
+ _seqCounter = ts._seqCounter;
+ _seqCounter2 = ts._seqCounter2;
+ _talkSeq = 0;
+
+ // Flag this slot as free again
+ ts._obj = nullptr;
+
+ return;
+ }
+
+ assert(_type != CHARACTER);
+
+ talk.pushTalkSequence(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 = 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;
+ itemName.setChar(tolower(itemName[0]), 0);
+ screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, "Picked up %s", 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