diff options
Diffstat (limited to 'engines/sherlock/scene.cpp')
-rw-r--r-- | engines/sherlock/scene.cpp | 1439 |
1 files changed, 1439 insertions, 0 deletions
diff --git a/engines/sherlock/scene.cpp b/engines/sherlock/scene.cpp new file mode 100644 index 0000000000..3edac3c27d --- /dev/null +++ b/engines/sherlock/scene.cpp @@ -0,0 +1,1439 @@ +/* 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/scene.h" +#include "sherlock/sherlock.h" +#include "sherlock/screen.h" +#include "sherlock/scalpel/scalpel.h" +#include "sherlock/scalpel/scalpel_people.h" +#include "sherlock/scalpel/scalpel_scene.h" +#include "sherlock/tattoo/tattoo.h" +#include "sherlock/tattoo/tattoo_scene.h" +#include "sherlock/tattoo/tattoo_user_interface.h" + +namespace Sherlock { + +static const int FS_TRANS[8] = { + Scalpel::STOP_UP, Scalpel::STOP_UPRIGHT, Scalpel::STOP_RIGHT, Scalpel::STOP_DOWNRIGHT, + Scalpel::STOP_DOWN, Scalpel::STOP_DOWNLEFT, Scalpel::STOP_LEFT, Scalpel::STOP_UPLEFT +}; + +/*----------------------------------------------------------------*/ + +BgFileHeader::BgFileHeader() { + _numStructs = -1; + _numImages = -1; + _numcAnimations = -1; + _descSize = -1; + _seqSize = -1; + + // Serrated Scalpel + _fill = -1; + + // Rose Tattoo + _scrollSize = -1; + _bytesWritten = -1; + _fadeStyle = -1; + Common::fill(&_palette[0], &_palette[PALETTE_SIZE], 0); +} + +void BgFileHeader::load(Common::SeekableReadStream &s, bool isRoseTattoo) { + _numStructs = s.readUint16LE(); + _numImages = s.readUint16LE(); + _numcAnimations = s.readUint16LE(); + _descSize = s.readUint16LE(); + _seqSize = s.readUint16LE(); + + if (isRoseTattoo) { + _scrollSize = s.readUint16LE(); + _bytesWritten = s.readUint32LE(); + _fadeStyle = s.readByte(); + } else { + _fill = s.readUint16LE(); + + } +} + +/*----------------------------------------------------------------*/ + +void BgFileHeaderInfo::load(Common::SeekableReadStream &s) { + _filesize = s.readUint32LE(); + _maxFrames = s.readByte(); + + char buffer[9]; + s.read(buffer, 9); + _filename = Common::String(buffer); +} + +void BgFileHeaderInfo::load3DO(Common::SeekableReadStream &s) { + _filesize = s.readUint32BE(); + _maxFrames = s.readByte(); + + char buffer[9]; + s.read(buffer, 9); + _filename = Common::String(buffer); + s.skip(2); // only on 3DO! +} + +/*----------------------------------------------------------------*/ + +void Exit::load(Common::SeekableReadStream &s, bool isRoseTattoo) { + if (isRoseTattoo) { + char buffer[41]; + s.read(buffer, 41); + _dest = Common::String(buffer); + } + + left = s.readSint16LE(); + top = s.readSint16LE(); + setWidth(s.readUint16LE()); + setHeight(s.readUint16LE()); + + _image = isRoseTattoo ? s.readByte() : 0; + _scene = s.readSint16LE(); + + if (!isRoseTattoo) + _allow = s.readSint16LE(); + + _newPosition.x = s.readSint16LE(); + _newPosition.y = s.readSint16LE(); + _newFacing = s.readUint16LE(); + + if (isRoseTattoo) + _allow = s.readSint16LE(); +} + +void Exit::load3DO(Common::SeekableReadStream &s) { + left = s.readSint16BE(); + top = s.readSint16BE(); + setWidth(s.readUint16BE()); + setHeight(s.readUint16BE()); + + _image = 0; + _scene = s.readSint16BE(); + + _allow = s.readSint16BE(); + + _newPosition.x = s.readSint16BE(); + _newPosition.y = s.readSint16BE(); + _newFacing = s.readUint16BE(); + s.skip(2); // Filler +} + +/*----------------------------------------------------------------*/ + +void SceneEntry::load(Common::SeekableReadStream &s) { + _startPosition.x = s.readSint16LE(); + _startPosition.y = s.readSint16LE(); + _startDir = s.readByte(); + _allow = s.readByte(); +} + +void SceneEntry::load3DO(Common::SeekableReadStream &s) { + _startPosition.x = s.readSint16BE(); + _startPosition.y = s.readSint16BE(); + _startDir = s.readByte(); + _allow = s.readByte(); +} + +void SceneSound::load(Common::SeekableReadStream &s) { + char buffer[9]; + s.read(buffer, 8); + buffer[8] = '\0'; + + _name = Common::String(buffer); + _priority = s.readByte(); +} + +void SceneSound::load3DO(Common::SeekableReadStream &s) { + load(s); +} + +/*----------------------------------------------------------------*/ + +int ObjectArray::indexOf(const Object &obj) const { + for (uint idx = 0; idx < size(); ++idx) { + if (&(*this)[idx] == &obj) + return idx; + } + + return -1; +} + +/*----------------------------------------------------------------*/ + +void ScaleZone::load(Common::SeekableReadStream &s) { + left = s.readSint16LE(); + top = s.readSint16LE(); + setWidth(s.readUint16LE()); + setHeight(s.readUint16LE()); + + _topNumber = s.readByte(); + _bottomNumber = s.readByte(); +} + +/*----------------------------------------------------------------*/ + +void WalkArray::load(Common::SeekableReadStream &s, bool isRoseTattoo) { + _pointsCount = (int8)s.readByte(); + + for (int idx = 0; idx < _pointsCount; ++idx) { + int x = s.readSint16LE(); + int y = isRoseTattoo ? s.readSint16LE() : s.readByte(); + push_back(Common::Point(x, y)); + } +} + +/*----------------------------------------------------------------*/ + +Scene *Scene::init(SherlockEngine *vm) { + if (vm->getGameID() == GType_SerratedScalpel) + return new Scalpel::ScalpelScene(vm); + else + return new Tattoo::TattooScene(vm); +} + +Scene::Scene(SherlockEngine *vm): _vm(vm) { + _sceneStats = new bool *[SCENES_COUNT]; + _sceneStats[0] = new bool[SCENES_COUNT * 65]; + Common::fill(&_sceneStats[0][0], &_sceneStats[0][SCENES_COUNT * 65], false); + for (int idx = 1; idx < SCENES_COUNT; ++idx) { + _sceneStats[idx] = _sceneStats[idx - 1] + 65; + } + _currentScene = -1; + _goToScene = -1; + _loadingSavedGame = false; + _walkedInScene = false; + _version = 0; + _lzwMode = false; + _invGraphicItems = 0; + _cAnimFramePause = 0; + _restoreFlag = false; + _animating = 0; + _doBgAnimDone = true; + _tempFadeStyle = 0; + _exitZone = -1; + _doBgAnimDone = false; +} + +Scene::~Scene() { + freeScene(); + delete[] _sceneStats[0]; + delete[] _sceneStats; +} + +void Scene::selectScene() { + Events &events = *_vm->_events; + People &people = *_vm->_people; + Screen &screen = *_vm->_screen; + Talk &talk = *_vm->_talk; + UserInterface &ui = *_vm->_ui; + + // Reset fields + ui._windowOpen = ui._infoFlag = false; + ui._menuMode = STD_MODE; + + // Free any previous scene + freeScene(); + + // Load the scene + Common::String sceneFile = Common::String::format("res%02d", _goToScene); + // _rrmName gets set during loadScene() + // _rrmName is for ScalpelScene::startCAnim + _currentScene = _goToScene; + _goToScene = -1; + + loadScene(sceneFile); + + // If the fade style was changed from running a movie, then reset it + if (_tempFadeStyle) { + screen._fadeStyle = _tempFadeStyle; + _tempFadeStyle = 0; + } + + people[HOLMES]._walkDest = Common::Point(people[HOLMES]._position.x / FIXED_INT_MULTIPLIER, + people[HOLMES]._position.y / FIXED_INT_MULTIPLIER); + + _restoreFlag = true; + events.clearEvents(); + + // If there were any scripts waiting to be run, but were interrupt by a running + // canimation (probably the last scene's exit canim), clear the _scriptMoreFlag + if (talk._scriptMoreFlag == 3) + talk._scriptMoreFlag = 0; +} + +void Scene::freeScene() { + if (_currentScene == -1) + return; + + _vm->_talk->freeTalkVars(); + _vm->_inventory->freeInv(); + _vm->_music->freeSong(); + _vm->_sound->freeLoadedSounds(); + + if (!_loadingSavedGame) + saveSceneStatus(); + else + _loadingSavedGame = false; + + _sequenceBuffer.clear(); + _descText.clear(); + _walkPoints.clear(); + _cAnim.clear(); + _bgShapes.clear(); + _zones.clear(); + _canimShapes.clear(); + + for (uint idx = 0; idx < _images.size(); ++idx) + delete _images[idx]._images; + _images.clear(); + + _currentScene = -1; +} + +bool Scene::loadScene(const Common::String &filename) { + Events &events = *_vm->_events; + Music &music = *_vm->_music; + People &people = *_vm->_people; + Resources &res = *_vm->_res; + SaveManager &saves = *_vm->_saves; + Screen &screen = *_vm->_screen; + UserInterface &ui = *_vm->_ui; + bool flag; + + _walkedInScene = false; + + // Reset the list of walkable areas + _zones.clear(); + _zones.push_back(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); + + _descText.clear(); + _comments = ""; + _bgShapes.clear(); + _cAnim.clear(); + _sequenceBuffer.clear(); + + // + // Load the room resource file for the scene + // + + if (!IS_3DO) { + // PC version + Common::String roomFilename = filename + ".rrm"; + _roomFilename = roomFilename; + + flag = _vm->_res->exists(roomFilename); + if (flag) { + Common::SeekableReadStream *rrmStream = _vm->_res->load(roomFilename); + + rrmStream->seek(39); + if (IS_SERRATED_SCALPEL) { + _version = rrmStream->readByte(); + _lzwMode = _version == 10; + } else { + _lzwMode = rrmStream->readByte() > 0; + } + + // Go to header and read it in + rrmStream->seek(rrmStream->readUint32LE()); + + BgFileHeader bgHeader; + bgHeader.load(*rrmStream, IS_ROSE_TATTOO); + _invGraphicItems = bgHeader._numImages + 1; + + if (IS_ROSE_TATTOO) { + screen.initPaletteFade(bgHeader._bytesWritten); + rrmStream->read(screen._cMap, PALETTE_SIZE); + screen.translatePalette(screen._cMap); + + paletteLoaded(); + + // Read in background + if (_lzwMode) { + res.decompress(*rrmStream, (byte *)screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT); + } else { + rrmStream->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT); + } + } + + // Read in the shapes header info + Common::Array<BgFileHeaderInfo> bgInfo; + bgInfo.resize(bgHeader._numStructs); + + for (uint idx = 0; idx < bgInfo.size(); ++idx) + bgInfo[idx].load(*rrmStream); + + // Read information + if (IS_ROSE_TATTOO) { + // Load shapes + Common::SeekableReadStream *infoStream = !_lzwMode ? rrmStream : res.decompress(*rrmStream, bgHeader._numStructs * 625); + + _bgShapes.resize(bgHeader._numStructs); + for (int idx = 0; idx < bgHeader._numStructs; ++idx) + _bgShapes[idx].load(*infoStream, _vm->getGameID() == GType_RoseTattoo); + + if (_lzwMode) + delete infoStream; + + // Load description text + _descText.resize(bgHeader._descSize); + if (_lzwMode) + res.decompress(*rrmStream, (byte *)&_descText[0], bgHeader._descSize); + else + rrmStream->read(&_descText[0], bgHeader._descSize); + + // Load sequences + _sequenceBuffer.resize(bgHeader._seqSize); + if (_lzwMode) + res.decompress(*rrmStream, &_sequenceBuffer[0], bgHeader._seqSize); + else + rrmStream->read(&_sequenceBuffer[0], bgHeader._seqSize); + } else if (!_lzwMode) { + // Serrated Scalpel uncompressed info + _bgShapes.resize(bgHeader._numStructs); + for (int idx = 0; idx < bgHeader._numStructs; ++idx) + _bgShapes[idx].load(*rrmStream, false); + + if (bgHeader._descSize) { + _descText.resize(bgHeader._descSize); + rrmStream->read(&_descText[0], bgHeader._descSize); + } + + if (bgHeader._seqSize) { + _sequenceBuffer.resize(bgHeader._seqSize); + rrmStream->read(&_sequenceBuffer[0], bgHeader._seqSize); + } + } else { + // Serrated Scalpel compressed info + Common::SeekableReadStream *infoStream; + + // Read shapes + infoStream = Resources::decompressLZ(*rrmStream, bgHeader._numStructs * 569); + + _bgShapes.resize(bgHeader._numStructs); + for (int idx = 0; idx < bgHeader._numStructs; ++idx) + _bgShapes[idx].load(*infoStream, false); + + delete infoStream; + + // Read description texts + if (bgHeader._descSize) { + infoStream = Resources::decompressLZ(*rrmStream, bgHeader._descSize); + + _descText.resize(bgHeader._descSize); + infoStream->read(&_descText[0], bgHeader._descSize); + + delete infoStream; + } + + // Read sequences + if (bgHeader._seqSize) { + infoStream = Resources::decompressLZ(*rrmStream, bgHeader._seqSize); + + _sequenceBuffer.resize(bgHeader._seqSize); + infoStream->read(&_sequenceBuffer[0], bgHeader._seqSize); + + delete infoStream; + } + } + + // Set up the list of images used by the scene + _images.resize(bgHeader._numImages + 1); + for (int idx = 0; idx < bgHeader._numImages; ++idx) { + _images[idx + 1]._filesize = bgInfo[idx]._filesize; + _images[idx + 1]._maxFrames = bgInfo[idx]._maxFrames; + + // Read in the image data + Common::SeekableReadStream *imageStream = _lzwMode ? + res.decompress(*rrmStream, bgInfo[idx]._filesize) : + rrmStream->readStream(bgInfo[idx]._filesize); + + _images[idx + 1]._images = new ImageFile(*imageStream); + + delete imageStream; + } + + // Set up the bgShapes + for (int idx = 0; idx < bgHeader._numStructs; ++idx) { + _bgShapes[idx]._images = _images[_bgShapes[idx]._misc]._images; + _bgShapes[idx]._imageFrame = !_bgShapes[idx]._images ? (ImageFrame *)nullptr : + &(*_bgShapes[idx]._images)[0]; + + _bgShapes[idx]._examine = Common::String(&_descText[_bgShapes[idx]._descOffset]); + _bgShapes[idx]._sequences = &_sequenceBuffer[_bgShapes[idx]._sequenceOffset]; + _bgShapes[idx]._misc = 0; + _bgShapes[idx]._seqCounter = 0; + _bgShapes[idx]._seqCounter2 = 0; + _bgShapes[idx]._seqStack = 0; + _bgShapes[idx]._frameNumber = -1; + _bgShapes[idx]._oldPosition = Common::Point(0, 0); + _bgShapes[idx]._oldSize = Common::Point(1, 1); + } + + // Load in cAnim list + _cAnim.clear(); + if (bgHeader._numcAnimations) { + int animSize = IS_SERRATED_SCALPEL ? 65 : 47; + Common::SeekableReadStream *cAnimStream = _lzwMode ? + res.decompress(*rrmStream, animSize * bgHeader._numcAnimations) : + rrmStream->readStream(animSize * bgHeader._numcAnimations); + + // Load cAnim offset table as well + uint32 *cAnimOffsetTablePtr = new uint32[bgHeader._numcAnimations]; + uint32 *cAnimOffsetPtr = cAnimOffsetTablePtr; + memset(cAnimOffsetTablePtr, 0, bgHeader._numcAnimations * sizeof(uint32)); + if (IS_SERRATED_SCALPEL) { + // Save current stream offset + int32 curOffset = rrmStream->pos(); + rrmStream->seek(44); // Seek to cAnim-Offset-Table + for (uint16 curCAnim = 0; curCAnim < bgHeader._numcAnimations; curCAnim++) { + *cAnimOffsetPtr = rrmStream->readUint32LE(); + cAnimOffsetPtr++; + } + // Seek back to original stream offset + rrmStream->seek(curOffset); + } + // TODO: load offset table for Rose Tattoo as well + + // Go to the start of the cAnimOffsetTable + cAnimOffsetPtr = cAnimOffsetTablePtr; + + _cAnim.resize(bgHeader._numcAnimations); + for (uint idx = 0; idx < _cAnim.size(); ++idx) { + _cAnim[idx].load(*cAnimStream, IS_ROSE_TATTOO, *cAnimOffsetPtr); + cAnimOffsetPtr++; + } + + delete cAnimStream; + delete[] cAnimOffsetTablePtr; + } + + + + // Read in the room bounding areas + int size = rrmStream->readUint16LE(); + Common::SeekableReadStream *boundsStream = !_lzwMode ? rrmStream : + res.decompress(*rrmStream, size); + + _zones.resize(size / 10); + for (uint idx = 0; idx < _zones.size(); ++idx) { + _zones[idx].left = boundsStream->readSint16LE(); + _zones[idx].top = boundsStream->readSint16LE(); + _zones[idx].setWidth(boundsStream->readSint16LE() + 1); + _zones[idx].setHeight(boundsStream->readSint16LE() + 1); + boundsStream->skip(2); // Skip unused scene number field + } + + if (_lzwMode) + delete boundsStream; + + // Ensure we've reached the path version byte + if (rrmStream->readByte() != (IS_SERRATED_SCALPEL ? 254 : 251)) + error("Invalid scene path data"); + + // Load the walk directory and walk data + assert(_zones.size() < MAX_ZONES); + + + for (uint idx1 = 0; idx1 < _zones.size(); ++idx1) { + Common::fill(&_walkDirectory[idx1][0], &_walkDirectory[idx1][MAX_ZONES], 0); + for (uint idx2 = 0; idx2 < _zones.size(); ++idx2) + _walkDirectory[idx1][idx2] = rrmStream->readSint16LE(); + } + + // Read in the walk data + size = rrmStream->readUint16LE(); + Common::SeekableReadStream *walkStream = !_lzwMode ? rrmStream : + res.decompress(*rrmStream, size); + + int startPos = walkStream->pos(); + while ((walkStream->pos() - startPos) < size) { + _walkPoints.push_back(WalkArray()); + _walkPoints[_walkPoints.size() - 1]._fileOffset = walkStream->pos() - startPos; + _walkPoints[_walkPoints.size() - 1].load(*walkStream, IS_ROSE_TATTOO); + } + + if (_lzwMode) + delete walkStream; + + // Translate the file offsets of the walk directory to indexes in the loaded walk data + for (uint idx1 = 0; idx1 < _zones.size(); ++idx1) { + for (uint idx2 = 0; idx2 < _zones.size(); ++idx2) { + int fileOffset = _walkDirectory[idx1][idx2]; + if (fileOffset == -1) + continue; + + uint dataIndex = 0; + while (dataIndex < _walkPoints.size() && _walkPoints[dataIndex]._fileOffset != fileOffset) + ++dataIndex; + assert(dataIndex < _walkPoints.size()); + _walkDirectory[idx1][idx2] = dataIndex; + } + } + + if (IS_ROSE_TATTOO) { + // Read in the entrance + _entrance.load(*rrmStream); + + // Load scale zones + _scaleZones.resize(rrmStream->readByte()); + for (uint idx = 0; idx < _scaleZones.size(); ++idx) + _scaleZones[idx].load(*rrmStream); + } + + // Read in the exits + _exitZone = -1; + int numExits = rrmStream->readByte(); + _exits.resize(numExits); + + for (int idx = 0; idx < numExits; ++idx) + _exits[idx].load(*rrmStream, IS_ROSE_TATTOO); + + if (IS_SERRATED_SCALPEL) + // Read in the entrance + _entrance.load(*rrmStream); + + // Initialize sound list + int numSounds = rrmStream->readByte(); + _sounds.resize(numSounds); + + for (int idx = 0; idx < numSounds; ++idx) + _sounds[idx].load(*rrmStream); + + loadSceneSounds(); + + if (IS_ROSE_TATTOO) { + // Load the object sound list + char buffer[27]; + + _objSoundList.resize(rrmStream->readUint16LE()); + for (uint idx = 0; idx < _objSoundList.size(); ++idx) { + rrmStream->read(buffer, 27); + _objSoundList[idx] = Common::String(buffer); + } + } else { + // Read in palette + rrmStream->read(screen._cMap, PALETTE_SIZE); + screen.translatePalette(screen._cMap); + Common::copy(screen._cMap, screen._cMap + PALETTE_SIZE, screen._sMap); + + // Read in the background + Common::SeekableReadStream *bgStream = !_lzwMode ? rrmStream : + res.decompress(*rrmStream, SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT); + + bgStream->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT); + + if (_lzwMode) + delete bgStream; + } + + // Backup the image and set the palette + screen._backBuffer2.blitFrom(screen._backBuffer1); + screen.setPalette(screen._cMap); + + delete rrmStream; + } + + } else { + // === 3DO version === + _roomFilename = "rooms/" + filename + ".rrm"; + flag = _vm->_res->exists(_roomFilename); + if (!flag) + error("loadScene: 3DO room data file not found"); + + Common::SeekableReadStream *roomStream = _vm->_res->load(_roomFilename); + uint32 roomStreamSize = roomStream->size(); + + // there should be at least all bytes of the header data + if (roomStreamSize < 128) + error("loadScene: 3DO room data file is too small"); + + // Read 3DO header + roomStream->skip(4); // UINT32: offset graphic data? + uint16 header3DO_numStructs = roomStream->readUint16BE(); + uint16 header3DO_numImages = roomStream->readUint16BE(); + uint16 header3DO_numAnimations = roomStream->readUint16BE(); + roomStream->skip(6); + + uint32 header3DO_bgInfo_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_bgInfo_size = roomStream->readUint32BE(); + uint32 header3DO_bgShapes_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_bgShapes_size = roomStream->readUint32BE(); + uint32 header3DO_descriptions_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_descriptions_size = roomStream->readUint32BE(); + uint32 header3DO_sequence_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_sequence_size = roomStream->readUint32BE(); + uint32 header3DO_cAnim_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_cAnim_size = roomStream->readUint32BE(); + uint32 header3DO_roomBounding_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_roomBounding_size = roomStream->readUint32BE(); + uint32 header3DO_walkDirectory_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_walkDirectory_size = roomStream->readUint32BE(); + uint32 header3DO_walkData_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_walkData_size = roomStream->readUint32BE(); + uint32 header3DO_exits_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_exits_size = roomStream->readUint32BE(); + uint32 header3DO_entranceData_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_entranceData_size = roomStream->readUint32BE(); + uint32 header3DO_soundList_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_soundList_size = roomStream->readUint32BE(); + //uint32 header3DO_unknown_offset = roomStream->readUint32BE() + 0x80; + //uint32 header3DO_unknown_size = roomStream->readUint32BE(); + roomStream->skip(8); // Skip over unknown offset+size + uint32 header3DO_bgGraphicData_offset = roomStream->readUint32BE() + 0x80; + uint32 header3DO_bgGraphicData_size = roomStream->readUint32BE(); + + // Calculate amount of entries + int32 header3DO_soundList_count = header3DO_soundList_size / 9; + + _invGraphicItems = header3DO_numImages + 1; + + // Verify all offsets + if (header3DO_bgInfo_offset >= roomStreamSize) + error("loadScene: 3DO bgInfo offset points outside of room file"); + if (header3DO_bgInfo_size > (roomStreamSize - header3DO_bgInfo_offset)) + error("loadScene: 3DO bgInfo size goes beyond room file"); + if (header3DO_bgShapes_offset >= roomStreamSize) + error("loadScene: 3DO bgShapes offset points outside of room file"); + if (header3DO_bgShapes_size > (roomStreamSize - header3DO_bgShapes_offset)) + error("loadScene: 3DO bgShapes size goes beyond room file"); + if (header3DO_descriptions_offset >= roomStreamSize) + error("loadScene: 3DO descriptions offset points outside of room file"); + if (header3DO_descriptions_size > (roomStreamSize - header3DO_descriptions_offset)) + error("loadScene: 3DO descriptions size goes beyond room file"); + if (header3DO_sequence_offset >= roomStreamSize) + error("loadScene: 3DO sequence offset points outside of room file"); + if (header3DO_sequence_size > (roomStreamSize - header3DO_sequence_offset)) + error("loadScene: 3DO sequence size goes beyond room file"); + if (header3DO_cAnim_offset >= roomStreamSize) + error("loadScene: 3DO cAnim offset points outside of room file"); + if (header3DO_cAnim_size > (roomStreamSize - header3DO_cAnim_offset)) + error("loadScene: 3DO cAnim size goes beyond room file"); + if (header3DO_roomBounding_offset >= roomStreamSize) + error("loadScene: 3DO roomBounding offset points outside of room file"); + if (header3DO_roomBounding_size > (roomStreamSize - header3DO_roomBounding_offset)) + error("loadScene: 3DO roomBounding size goes beyond room file"); + if (header3DO_walkDirectory_offset >= roomStreamSize) + error("loadScene: 3DO walkDirectory offset points outside of room file"); + if (header3DO_walkDirectory_size > (roomStreamSize - header3DO_walkDirectory_offset)) + error("loadScene: 3DO walkDirectory size goes beyond room file"); + if (header3DO_walkData_offset >= roomStreamSize) + error("loadScene: 3DO walkData offset points outside of room file"); + if (header3DO_walkData_size > (roomStreamSize - header3DO_walkData_offset)) + error("loadScene: 3DO walkData size goes beyond room file"); + if (header3DO_exits_offset >= roomStreamSize) + error("loadScene: 3DO exits offset points outside of room file"); + if (header3DO_exits_size > (roomStreamSize - header3DO_exits_offset)) + error("loadScene: 3DO exits size goes beyond room file"); + if (header3DO_entranceData_offset >= roomStreamSize) + error("loadScene: 3DO entranceData offset points outside of room file"); + if (header3DO_entranceData_size > (roomStreamSize - header3DO_entranceData_offset)) + error("loadScene: 3DO entranceData size goes beyond room file"); + if (header3DO_soundList_offset >= roomStreamSize) + error("loadScene: 3DO soundList offset points outside of room file"); + if (header3DO_soundList_size > (roomStreamSize - header3DO_soundList_offset)) + error("loadScene: 3DO soundList size goes beyond room file"); + if (header3DO_bgGraphicData_offset >= roomStreamSize) + error("loadScene: 3DO bgGraphicData offset points outside of room file"); + if (header3DO_bgGraphicData_size > (roomStreamSize - header3DO_bgGraphicData_offset)) + error("loadScene: 3DO bgGraphicData size goes beyond room file"); + + // === BGINFO === read in the shapes header info + Common::Array<BgFileHeaderInfo> bgInfo; + + uint32 expected3DO_bgInfo_size = header3DO_numStructs * 16; + if (expected3DO_bgInfo_size != header3DO_bgInfo_size) // Security check + error("loadScene: 3DO bgInfo size mismatch"); + + roomStream->seek(header3DO_bgInfo_offset); + bgInfo.resize(header3DO_numStructs); + for (uint idx = 0; idx < bgInfo.size(); ++idx) + bgInfo[idx].load3DO(*roomStream); + + // === BGSHAPES === read in the shapes info + uint32 expected3DO_bgShapes_size = header3DO_numStructs * 588; + if (expected3DO_bgShapes_size != header3DO_bgShapes_size) // Security check + error("loadScene: 3DO bgShapes size mismatch"); + + roomStream->seek(header3DO_bgShapes_offset); + _bgShapes.resize(header3DO_numStructs); + for (int idx = 0; idx < header3DO_numStructs; ++idx) + _bgShapes[idx].load3DO(*roomStream); + + // === DESCRIPTION === read description text + if (header3DO_descriptions_size) { + roomStream->seek(header3DO_descriptions_offset); + _descText.resize(header3DO_descriptions_size); + roomStream->read(&_descText[0], header3DO_descriptions_size); + } + + // === SEQUENCE === read sequence buffer + if (header3DO_sequence_size) { + roomStream->seek(header3DO_sequence_offset); + _sequenceBuffer.resize(header3DO_sequence_size); + roomStream->read(&_sequenceBuffer[0], header3DO_sequence_size); + } + + // === IMAGES === set up the list of images used by the scene + roomStream->seek(header3DO_bgGraphicData_offset); + _images.resize(header3DO_numImages + 1); + for (int idx = 0; idx < header3DO_numImages; ++idx) { + _images[idx + 1]._filesize = bgInfo[idx]._filesize; + _images[idx + 1]._maxFrames = bgInfo[idx]._maxFrames; + + // Read image data into memory + Common::SeekableReadStream *imageStream = roomStream->readStream(bgInfo[idx]._filesize); + + // Load image data into an ImageFile array as room file data + // which is basically a fixed header, followed by a raw cel header, followed by regular cel data + _images[idx + 1]._images = new ImageFile3DO(*imageStream, true); + + delete imageStream; + } + + // === BGSHAPES === Set up the bgShapes + for (int idx = 0; idx < header3DO_numStructs; ++idx) { + _bgShapes[idx]._images = _images[_bgShapes[idx]._misc]._images; + _bgShapes[idx]._imageFrame = !_bgShapes[idx]._images ? (ImageFrame *)nullptr : + &(*_bgShapes[idx]._images)[0]; + + _bgShapes[idx]._examine = Common::String(&_descText[_bgShapes[idx]._descOffset]); + _bgShapes[idx]._sequences = &_sequenceBuffer[_bgShapes[idx]._sequenceOffset]; + _bgShapes[idx]._misc = 0; + _bgShapes[idx]._seqCounter = 0; + _bgShapes[idx]._seqCounter2 = 0; + _bgShapes[idx]._seqStack = 0; + _bgShapes[idx]._frameNumber = -1; + _bgShapes[idx]._oldPosition = Common::Point(0, 0); + _bgShapes[idx]._oldSize = Common::Point(1, 1); + } + + // === CANIM === read cAnim list + _cAnim.clear(); + if (header3DO_numAnimations) { + roomStream->seek(header3DO_cAnim_offset); + Common::SeekableReadStream *cAnimStream = roomStream->readStream(header3DO_cAnim_size); + + uint32 *cAnimOffsetTablePtr = new uint32[header3DO_numAnimations]; + uint32 *cAnimOffsetPtr = cAnimOffsetTablePtr; + uint32 cAnimOffset = 0; + memset(cAnimOffsetTablePtr, 0, header3DO_numAnimations * sizeof(uint32)); + + // Seek to end of graphics data and load cAnim offset table from there + roomStream->seek(header3DO_bgGraphicData_offset + header3DO_bgGraphicData_size); + for (uint16 curCAnim = 0; curCAnim < header3DO_numAnimations; curCAnim++) { + cAnimOffset = roomStream->readUint32BE(); + if (cAnimOffset >= roomStreamSize) + error("loadScene: 3DO cAnim entry offset points outside of room file"); + + *cAnimOffsetPtr = cAnimOffset; + cAnimOffsetPtr++; + } + + // Go to the start of the cAnimOffsetTable + cAnimOffsetPtr = cAnimOffsetTablePtr; + + _cAnim.resize(header3DO_numAnimations); + for (uint idx = 0; idx < _cAnim.size(); ++idx) { + _cAnim[idx].load3DO(*cAnimStream, *cAnimOffsetPtr); + cAnimOffsetPtr++; + } + + delete cAnimStream; + delete[] cAnimOffsetTablePtr; + } + + // === BOUNDING AREAS === Read in the room bounding areas + int roomBoundingCount = header3DO_roomBounding_size / 12; + uint32 expected3DO_roomBounding_size = roomBoundingCount * 12; + if (expected3DO_roomBounding_size != header3DO_roomBounding_size) + error("loadScene: 3DO roomBounding size mismatch"); + + roomStream->seek(header3DO_roomBounding_offset); + _zones.resize(roomBoundingCount); + for (uint idx = 0; idx < _zones.size(); ++idx) { + _zones[idx].left = roomStream->readSint16BE(); + _zones[idx].top = roomStream->readSint16BE(); + _zones[idx].setWidth(roomStream->readSint16BE() + 1); + _zones[idx].setHeight(roomStream->readSint16BE() + 1); + roomStream->skip(4); // skip UINT32 + } + + // === WALK DIRECTORY === Load the walk directory + uint32 expected3DO_walkDirectory_size = _zones.size() * _zones.size() * 2; + if (expected3DO_walkDirectory_size != header3DO_walkDirectory_size) + error("loadScene: 3DO walkDirectory size mismatch"); + + roomStream->seek(header3DO_walkDirectory_offset); + assert(_zones.size() < MAX_ZONES); + for (uint idx1 = 0; idx1 < _zones.size(); ++idx1) { + for (uint idx2 = 0; idx2 < _zones.size(); ++idx2) + _walkDirectory[idx1][idx2] = roomStream->readSint16BE(); + } + + // === WALK DATA === Read in the walk data + roomStream->seek(header3DO_walkData_offset); + + int startPos = roomStream->pos(); + while ((roomStream->pos() - startPos) < (int)header3DO_walkData_size) { + _walkPoints.push_back(WalkArray()); + _walkPoints[_walkPoints.size() - 1]._fileOffset = roomStream->pos() - startPos; + _walkPoints[_walkPoints.size() - 1].load(*roomStream, false); + } + + // Translate the file offsets of the walk directory to indexes in the loaded walk data + for (uint idx1 = 0; idx1 < _zones.size(); ++idx1) { + for (uint idx2 = 0; idx2 < _zones.size(); ++idx2) { + int fileOffset = _walkDirectory[idx1][idx2]; + if (fileOffset == -1) + continue; + + uint dataIndex = 0; + while (dataIndex < _walkPoints.size() && _walkPoints[dataIndex]._fileOffset != fileOffset) + ++dataIndex; + assert(dataIndex < _walkPoints.size()); + _walkDirectory[idx1][idx2] = dataIndex; + } + } + + // === EXITS === Read in the exits + roomStream->seek(header3DO_exits_offset); + + int exitsCount = header3DO_exits_size / 20; + + _exitZone = -1; + _exits.resize(exitsCount); + for (int idx = 0; idx < exitsCount; ++idx) + _exits[idx].load3DO(*roomStream); + + // === ENTRANCE === Read in the entrance + if (header3DO_entranceData_size != 8) + error("loadScene: 3DO entranceData size mismatch"); + + roomStream->seek(header3DO_entranceData_offset); + _entrance.load3DO(*roomStream); + + // === SOUND LIST === Initialize sound list + roomStream->seek(header3DO_soundList_offset); + _sounds.resize(header3DO_soundList_count); + for (int idx = 0; idx < header3DO_soundList_count; ++idx) + _sounds[idx].load3DO(*roomStream); + + delete roomStream; + + // === BACKGROUND PICTURE === + // load from file rooms\[filename].bg + // it's uncompressed 15-bit RGB555 data + + Common::String roomBackgroundFilename = "rooms/" + filename + ".bg"; + flag = _vm->_res->exists(roomBackgroundFilename); + if (!flag) + error("loadScene: 3DO room background file not found (%s)", roomBackgroundFilename.c_str()); + + Common::File roomBackgroundStream; + if (!roomBackgroundStream.open(roomBackgroundFilename)) + error("Could not open file - %s", roomBackgroundFilename.c_str()); + + int totalPixelCount = SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT; + uint16 *roomBackgroundDataPtr = NULL; + uint16 *pixelSourcePtr = NULL; + uint16 *pixelDestPtr = (uint16 *)screen._backBuffer1.getPixels(); + uint16 curPixel = 0; + uint32 roomBackgroundStreamSize = roomBackgroundStream.size(); + uint32 expectedBackgroundSize = totalPixelCount * 2; + + // Verify file size of background file + if (expectedBackgroundSize != roomBackgroundStreamSize) + error("loadScene: 3DO room background file not expected size"); + + roomBackgroundDataPtr = new uint16[totalPixelCount]; + roomBackgroundStream.read(roomBackgroundDataPtr, roomBackgroundStreamSize); + roomBackgroundStream.close(); + + // Convert data from RGB555 to RGB565 + pixelSourcePtr = roomBackgroundDataPtr; + for (int pixels = 0; pixels < totalPixelCount; pixels++) { + curPixel = READ_BE_UINT16(pixelSourcePtr++); + + byte curPixelRed = (curPixel >> 10) & 0x1F; + byte curPixelGreen = (curPixel >> 5) & 0x1F; + byte curPixelBlue = curPixel & 0x1F; + *pixelDestPtr = ((curPixelRed << 11) | (curPixelGreen << 6) | (curPixelBlue)); + pixelDestPtr++; + } + + delete[] roomBackgroundDataPtr; + +#if 0 + // code to show the background + screen.blitFrom(screen._backBuffer1); + _vm->_events->wait(10000); +#endif + + // Backup the image + screen._backBuffer2.blitFrom(screen._backBuffer1); + } + + // Handle drawing any on-screen interface + ui.drawInterface(); + + checkSceneStatus(); + + if (!saves._justLoaded) { + for (uint idx = 0; idx < _bgShapes.size(); ++idx) { + if (_bgShapes[idx]._type == HIDDEN && _bgShapes[idx]._aType == TALK_EVERY) + _bgShapes[idx].toggleHidden(); + } + + // Check for TURNON objects + for (uint idx = 0; idx < _bgShapes.size(); ++idx) { + if (_bgShapes[idx]._type == HIDDEN && (_bgShapes[idx]._flags & TURNON_OBJ)) + _bgShapes[idx].toggleHidden(); + } + + // Check for TURNOFF objects + for (uint idx = 0; idx < _bgShapes.size(); ++idx) { + if (_bgShapes[idx]._type != HIDDEN && (_bgShapes[idx]._flags & TURNOFF_OBJ) && + _bgShapes[idx]._type != INVALID) + _bgShapes[idx].toggleHidden(); + if (_bgShapes[idx]._type == HIDE_SHAPE) + // Hiding isn't needed, since objects aren't drawn yet + _bgShapes[idx]._type = HIDDEN; + } + } + + checkSceneFlags(false); + checkInventory(); + + // Handle starting any music for the scene + if (IS_SERRATED_SCALPEL && music._musicOn && music.loadSong(_currentScene)) + music.startSong(); + + // Load walking images if not already loaded + people.loadWalk(); + + // Transition to the scene and setup entrance co-ordinates and animations + transitionToScene(); + + // Player has not yet walked in this scene + _walkedInScene = false; + saves._justLoaded = false; + + events.clearEvents(); + return flag; +} + +void Scene::loadSceneSounds() { + Sound &sound = *_vm->_sound; + + for (uint idx = 0; idx < _sounds.size(); ++idx) + sound.loadSound(_sounds[idx]._name, _sounds[idx]._priority); +} + +void Scene::checkSceneStatus() { + if (_sceneStats[_currentScene][64]) { + for (uint idx = 0; idx < 64; ++idx) { + bool flag = _sceneStats[_currentScene][idx]; + + if (idx < _bgShapes.size()) { + Object &obj = _bgShapes[idx]; + + if (flag) { + // No shape to erase, so flag as hidden + obj._type = HIDDEN; + } else if (obj._images == nullptr || obj._images->size() == 0) { + // No shape + obj._type = NO_SHAPE; + } else { + obj._type = ACTIVE_BG_SHAPE; + } + } else { + // Finished checks + return; + } + } + } +} + +void Scene::saveSceneStatus() { + // Flag any objects for the scene that have been altered + int count = MIN((int)_bgShapes.size(), 64); + for (int idx = 0; idx < count; ++idx) { + Object &obj = _bgShapes[idx]; + _sceneStats[_currentScene][idx] = obj._type == HIDDEN || obj._type == REMOVE + || obj._type == HIDE_SHAPE || obj._type == INVALID; + } + + // Flag scene as having been visited + _sceneStats[_currentScene][64] = true; +} + +void Scene::checkSceneFlags(bool flag) { + SpriteType mode = flag ? HIDE_SHAPE : HIDDEN; + + for (uint idx = 0; idx < _bgShapes.size(); ++idx) { + Object &o = _bgShapes[idx]; + + if (o._requiredFlag) { + if (!_vm->readFlags(_bgShapes[idx]._requiredFlag)) { + // Kill object + if (o._type != HIDDEN && o._type != INVALID) { + if (o._images == nullptr || o._images->size() == 0) + // No shape to erase, so flag as hidden + o._type = HIDDEN; + else + // Flag it as needing to be hidden after first erasing it + o._type = mode; + } + } else if (_bgShapes[idx]._requiredFlag > 0) { + // Restore object + if (o._images == nullptr || o._images->size() == 0) + o._type = NO_SHAPE; + else + o._type = ACTIVE_BG_SHAPE; + } + } + } + + // Check inventory for items to remove based on flag changes + for (int idx = 0; idx < _vm->_inventory->_holdings; ++idx) { + InventoryItem &ii = (*_vm->_inventory)[idx]; + if (ii._requiredFlag && !_vm->readFlags(ii._requiredFlag)) { + // Kill object: move it after the active holdings + InventoryItem tempItem = (*_vm->_inventory)[idx]; + _vm->_inventory->insert_at(_vm->_inventory->_holdings, tempItem); + _vm->_inventory->remove_at(idx); + _vm->_inventory->_holdings--; + } + } + + // Check inactive inventory items for ones to reactivate based on flag changes + for (uint idx = _vm->_inventory->_holdings; idx < _vm->_inventory->size(); ++idx) { + InventoryItem &ii = (*_vm->_inventory)[idx]; + if (ii._requiredFlag && _vm->readFlags(ii._requiredFlag)) { + // Restore object: move it after the active holdings + InventoryItem tempItem = (*_vm->_inventory)[idx]; + _vm->_inventory->remove_at(idx); + _vm->_inventory->insert_at(_vm->_inventory->_holdings, tempItem); + _vm->_inventory->_holdings++; + } + } +} + +void Scene::checkInventory() { + for (uint shapeIdx = 0; shapeIdx < _bgShapes.size(); ++shapeIdx) { + for (int invIdx = 0; invIdx < _vm->_inventory->_holdings; ++invIdx) { + if (_bgShapes[shapeIdx]._name.equalsIgnoreCase((*_vm->_inventory)[invIdx]._name)) { + _bgShapes[shapeIdx]._type = INVALID; + break; + } + } + } +} + +void Scene::transitionToScene() { + People &people = *_vm->_people; + SaveManager &saves = *_vm->_saves; + Screen &screen = *_vm->_screen; + Talk &talk = *_vm->_talk; + Point32 &hSavedPos = people._hSavedPos; + int &hSavedFacing = people._hSavedFacing; + + if (hSavedPos.x < 1) { + // No exit information from last scene-check entrance info + if (_entrance._startPosition.x < 1) { + // No entrance info either, so use defaults + if (IS_SERRATED_SCALPEL) { + hSavedPos = Point32(160 * FIXED_INT_MULTIPLIER, 100 * FIXED_INT_MULTIPLIER); + hSavedFacing = 4; + } else { + hSavedPos = people[HOLMES]._position; + hSavedFacing = people[HOLMES]._sequenceNumber; + } + } else { + // setup entrance info + hSavedPos.x = _entrance._startPosition.x * FIXED_INT_MULTIPLIER; + hSavedPos.y = _entrance._startPosition.y * FIXED_INT_MULTIPLIER; + if (IS_SERRATED_SCALPEL) { + hSavedPos.x /= 100; + hSavedPos.y /= 100; + } + + hSavedFacing = _entrance._startDir; + } + } else { + // Exit information exists, translate it to real sequence info + // Note: If a savegame was just loaded, then the data is already correct. + // Otherwise, this is a linked scene or entrance info, and must be translated + if (hSavedFacing < 8 && !saves._justLoaded) { + hSavedFacing = FS_TRANS[hSavedFacing]; + hSavedPos.x *= FIXED_INT_MULTIPLIER; + hSavedPos.y *= FIXED_INT_MULTIPLIER; + } + } + + int cAnimNum = -1; + + if (hSavedFacing < 101) { + // Standard info, so set it + people[HOLMES]._position = hSavedPos; + people[HOLMES]._sequenceNumber = hSavedFacing; + } else { + // It's canimation information + cAnimNum = hSavedFacing - 101; + } + + // Reset positioning for next load + hSavedPos = Common::Point(-1, -1); + hSavedFacing = -1; + + if (cAnimNum != -1) { + // Prevent Holmes from being drawn + people[HOLMES]._position = Common::Point(0, 0); + } + + for (uint objIdx = 0; objIdx < _bgShapes.size(); ++objIdx) { + Object &obj = _bgShapes[objIdx]; + + if (obj._aType > 1 && obj._type != INVALID && obj._type != HIDDEN) { + Common::Point topLeft = obj._position; + Common::Point bottomRight; + + if (obj._type != NO_SHAPE) { + topLeft += obj._imageFrame->_offset; + bottomRight.x = topLeft.x + obj._imageFrame->_frame.w; + bottomRight.y = topLeft.y + obj._imageFrame->_frame.h; + } else { + bottomRight = topLeft + obj._noShapeSize; + } + + if (Common::Rect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y).contains( + Common::Point(people[HOLMES]._position.x / FIXED_INT_MULTIPLIER, + people[HOLMES]._position.y / FIXED_INT_MULTIPLIER))) { + // Current point is already inside box - impact occurred on + // a previous call. So simply do nothing except talk until the + // player is clear of the box + switch (obj._aType) { + case FLAG_SET: + for (int useNum = 0; useNum < USE_COUNT; ++useNum) { + if (obj._use[useNum]._useFlag) { + if (!_vm->readFlags(obj._use[useNum]._useFlag)) + _vm->setFlags(obj._use[useNum]._useFlag); + } + + if (!talk._talkToAbort) { + for (int nameIdx = 0; nameIdx < NAMES_COUNT; ++nameIdx) { + toggleObject(obj._use[useNum]._names[nameIdx]); + } + } + } + + obj._type = HIDDEN; + break; + + default: + break; + } + } + } + } + + updateBackground(); + + // Actually do the transition + if (screen._fadeStyle) { + if (!IS_3DO) { + // do pixel-transition for PC + screen.randomTransition(); + } else { + // fade in for 3DO + screen.clear(); + screen.fadeIntoScreen3DO(3); + } + } else { + screen.blitFrom(screen._backBuffer1); + } + screen.update(); + + // Start any initial animation for the scene + if (cAnimNum != -1) { + CAnim &c = _cAnim[cAnimNum]; + PositionFacing pt = c._goto[0]; + + c._goto[0].x = c._goto[0].y = -1; + people[HOLMES]._position = Common::Point(0, 0); + + startCAnim(cAnimNum, 1); + c._goto[0] = pt; + } +} + +int Scene::toggleObject(const Common::String &name) { + int count = 0; + + for (uint idx = 0; idx < _bgShapes.size(); ++idx) { + if (name.equalsIgnoreCase(_bgShapes[idx]._name)) { + ++count; + _bgShapes[idx].toggleHidden(); + } + } + + return count; +} + +void Scene::updateBackground() { + People &people = *_vm->_people; + + // Update Holmes if he's turned on + for (int idx = 0; idx < MAX_CHARACTERS; ++idx) { + if (people[idx]._type == CHARACTER) + people[idx].adjustSprite(); + } + + // Flag the bg shapes which need to be redrawn + checkBgShapes(); + + // Draw the shapes for the scene + drawAllShapes(); +} + +Exit *Scene::checkForExit(const Common::Rect &r) { + for (uint idx = 0; idx < _exits.size(); ++idx) { + if (_exits[idx].intersects(r)) + return &_exits[idx]; + } + + return nullptr; +} + +int Scene::findBgShape(const Common::Rect &r) { + if (!_doBgAnimDone) + // New frame hasn't been drawn yet + return -1; + + for (int idx = (int)_bgShapes.size() - 1; idx >= 0; --idx) { + Object &o = _bgShapes[idx]; + if (o._type != INVALID && o._type != NO_SHAPE && o._type != HIDDEN + && o._aType <= PERSON) { + if (r.intersects(o.getNewBounds())) + return idx; + } else if (o._type == NO_SHAPE) { + if (r.intersects(o.getNoShapeBounds())) + return idx; + } + } + + return -1; +} + +int Scene::findBgShape(const Common::Point &pt) { + return findBgShape(Common::Rect(pt.x, pt.y, pt.x + 1, pt.y + 1)); +} + +int Scene::checkForZones(const Common::Point &pt, int zoneType) { + int matches = 0; + + for (uint idx = 0; idx < _bgShapes.size(); ++idx) { + Object &o = _bgShapes[idx]; + if ((o._aType == zoneType && o._type != INVALID) && o._type != HIDDEN) { + Common::Rect r = o._type == NO_SHAPE ? o.getNoShapeBounds() : o.getNewBounds(); + + if (r.contains(pt)) { + ++matches; + o.setFlagsAndToggles(); + _vm->_talk->talkTo(o._use[0]._target); + } + } + } + + return matches; +} + +int Scene::whichZone(const Common::Point &pt) { + for (uint idx = 0; idx < _zones.size(); ++idx) { + if (_zones[idx].contains(pt)) + return idx; + } + + return -1; +} + +int Scene::closestZone(const Common::Point &pt) { + int dist = 1000; + int zone = -1; + + for (uint idx = 0; idx < _zones.size(); ++idx) { + Common::Point zc((_zones[idx].left + _zones[idx].right) / 2, + (_zones[idx].top + _zones[idx].bottom) / 2); + int d = ABS(zc.x - pt.x) + ABS(zc.y - pt.y); + + if (d < dist) { + // Found a closer zone + dist = d; + zone = idx; + } + } + + return zone; +} + +void Scene::synchronize(Serializer &s) { + if (s.isSaving()) + saveSceneStatus(); + + if (s.isSaving()) { + s.syncAsSint16LE(_currentScene); + } else { + s.syncAsSint16LE(_goToScene); + _loadingSavedGame = true; + } + + for (int sceneNum = 0; sceneNum < SCENES_COUNT; ++sceneNum) { + for (int flag = 0; flag < 65; ++flag) { + s.syncAsByte(_sceneStats[sceneNum][flag]); + } + } +} + +void Scene::checkBgShapes() { + People &people = *_vm->_people; + Person &holmes = people[HOLMES]; + Common::Point pt(holmes._position.x / FIXED_INT_MULTIPLIER, holmes._position.y / FIXED_INT_MULTIPLIER); + + // Iterate through the shapes + for (uint idx = 0; idx < _bgShapes.size(); ++idx) { + Object &obj = _bgShapes[idx]; + if (obj._type == ACTIVE_BG_SHAPE || (IS_SERRATED_SCALPEL && obj._type == STATIC_BG_SHAPE)) { + if ((obj._flags & 5) == 1) { + obj._misc = (pt.y < (obj._position.y + obj.frameHeight() - 1)) ? + NORMAL_FORWARD : NORMAL_BEHIND; + } else if (!(obj._flags & OBJ_BEHIND)) { + obj._misc = BEHIND; + } else if (obj._flags & OBJ_FORWARD) { + obj._misc = FORWARD; + } + } + } +} + +} // End of namespace Sherlock |