scummvm/engines/sherlock/scene.cpp
Paul Gilbert 8ee889beb4 SHERLOCK: RT: Refactor out incorrectly working _loadingSavedGame
This likely fixes just about every remaining outstanding bug report.
The variable, for a savegame being loaded, was only reset when a
previously loaded scene was freed. But if you loaded a game directly
from the launcher, there was no previous scene to free, and the
variable remained set. Which meant that you could do things in the
scene and then either leave the scene or make another save without
the _sceneStats update method being called to reflect the changes
made in the change in the global flags.
2015-10-13 20:16:32 -04:00

1423 lines
45 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "sherlock/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/scalpel/scalpel_screen.h"
#include "sherlock/tattoo/tattoo.h"
#include "sherlock/tattoo/tattoo_scene.h"
#include "sherlock/tattoo/tattoo_user_interface.h"
namespace Sherlock {
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();
_newPosition._facing = 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();
_newPosition._facing = 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 * (MAX_BGSHAPES + 1)];
Common::fill(&_sceneStats[0][0], &_sceneStats[0][SCENES_COUNT * (MAX_BGSHAPES + 1)], false);
for (int idx = 1; idx < SCENES_COUNT; ++idx) {
_sceneStats[idx] = _sceneStats[idx - 1] + (MAX_BGSHAPES + 1);
}
_currentScene = -1;
_goToScene = -1;
_walkedInScene = false;
_version = 0;
_compressed = false;
_invGraphicItems = 0;
_cAnimFramePause = 0;
_restoreFlag = false;
_animating = 0;
_doBgAnimDone = true;
_tempFadeStyle = 0;
_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;
// 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() {
SaveManager &saves = *_vm->_saves;
if (_currentScene == -1)
return;
_vm->_ui->clearWindow();
_vm->_talk->freeTalkVars();
_vm->_talk->clearSequences();
_vm->_inventory->freeInv();
_vm->_music->freeSong();
_vm->_sound->freeLoadedSounds();
if (!saves._justLoaded)
saveSceneStatus();
_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();
_compressed = _version == 10;
} else {
_compressed = 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) {
// Resize the screen if necessary
int fullWidth = SHERLOCK_SCREEN_WIDTH + bgHeader._scrollSize;
if (screen._backBuffer1.w() != fullWidth) {
screen._backBuffer1.create(fullWidth, SHERLOCK_SCREEN_HEIGHT);
screen._backBuffer2.create(fullWidth, SHERLOCK_SCREEN_HEIGHT);
}
// Handle initializing the palette
screen.initPaletteFade(bgHeader._bytesWritten);
rrmStream->read(screen._cMap, PALETTE_SIZE);
paletteLoaded();
screen.translatePalette(screen._cMap);
// Read in background
if (_compressed) {
res.decompress(*rrmStream, (byte *)screen._backBuffer1.getPixels(), fullWidth * SHERLOCK_SCREEN_HEIGHT);
} else {
rrmStream->read(screen._backBuffer1.getPixels(), fullWidth * 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 = !_compressed ? rrmStream : res.decompress(*rrmStream, bgHeader._numStructs * 625);
_bgShapes.resize(bgHeader._numStructs);
for (int idx = 0; idx < bgHeader._numStructs; ++idx)
_bgShapes[idx].load(*infoStream, true);
if (_compressed)
delete infoStream;
// Load description text
_descText.resize(bgHeader._descSize);
if (_compressed)
res.decompress(*rrmStream, (byte *)&_descText[0], bgHeader._descSize);
else
rrmStream->read(&_descText[0], bgHeader._descSize);
// Load sequences
_sequenceBuffer.resize(bgHeader._seqSize);
if (_compressed)
res.decompress(*rrmStream, &_sequenceBuffer[0], bgHeader._seqSize);
else
rrmStream->read(&_sequenceBuffer[0], bgHeader._seqSize);
} else if (!_compressed) {
// 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 = _compressed ?
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 = _compressed ?
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 = !_compressed ? 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 (_compressed)
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 = !_compressed ? rrmStream->readStream(size) :
res.decompress(*rrmStream, size);
// 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 dataOffset = _walkDirectory[idx1][idx2];
if (dataOffset == -1)
continue;
// Check to see if we've already loaded the walk set for the given data offset
uint dataIndex = 0;
while (dataIndex < _walkPoints.size() && _walkPoints[dataIndex]._fileOffset != dataOffset)
++dataIndex;
if (dataIndex == _walkPoints.size()) {
// Walk data for that offset hasn't been loaded yet, so load it now
_walkPoints.push_back(WalkArray());
walkStream->seek(dataOffset);
_walkPoints[_walkPoints.size() - 1]._fileOffset = dataOffset;
_walkPoints[_walkPoints.size() - 1].load(*walkStream, IS_ROSE_TATTOO);
dataIndex = _walkPoints.size() - 1;
}
_walkDirectory[idx1][idx2] = dataIndex;
}
}
delete walkStream;
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
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 = !_compressed ? rrmStream :
res.decompress(*rrmStream, SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT);
bgStream->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT);
if (_compressed)
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);
// Read in the walk data
Common::SeekableReadStream *walkStream = !_compressed ? roomStream->readStream(header3DO_walkData_size) :
res.decompress(*roomStream, header3DO_walkData_size);
// 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 dataOffset = _walkDirectory[idx1][idx2];
if (dataOffset == -1)
continue;
// Check to see if we've already loaded the walk set for the given data offset
uint dataIndex = 0;
while (dataIndex < _walkPoints.size() && _walkPoints[dataIndex]._fileOffset != dataOffset)
++dataIndex;
if (dataIndex == _walkPoints.size()) {
// Walk data for that offset hasn't been loaded yet, so load it now
_walkPoints.push_back(WalkArray());
walkStream->seek(dataOffset);
_walkPoints[_walkPoints.size() - 1]._fileOffset = dataOffset;
_walkPoints[_walkPoints.size() - 1].load(*walkStream, IS_ROSE_TATTOO);
dataIndex = _walkPoints.size() - 1;
}
_walkDirectory[idx1][idx2] = dataIndex;
}
}
delete walkStream;
// === EXITS === Read in the exits
roomStream->seek(header3DO_exits_offset);
int exitsCount = header3DO_exits_size / 20;
_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][MAX_BGSHAPES]) {
for (int idx = 0; idx < MAX_BGSHAPES; ++idx) {
bool flag = _sceneStats[_currentScene][idx];
if (idx < (int)_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(), MAX_BGSHAPES);
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][MAX_BGSHAPES] = 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[0] || o._requiredFlag[1]) {
bool objectFlag = true;
if (o._requiredFlag[0] != 0)
objectFlag = _vm->readFlags(o._requiredFlag[0]);
if (o._requiredFlag[1] != 0)
objectFlag &= _vm->readFlags(o._requiredFlag[1]);
if (!objectFlag) {
// 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 (IS_ROSE_TATTOO || o._requiredFlag[0] > 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._savedPos;
int &hSavedFacing = people._savedPos._facing;
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) {
if (IS_ROSE_TATTOO)
hSavedFacing = Tattoo::FS_TRANS[hSavedFacing];
else
hSavedFacing = Scalpel::FS_TRANS[hSavedFacing];
hSavedPos.x *= FIXED_INT_MULTIPLIER;
hSavedPos.y *= FIXED_INT_MULTIPLIER;
}
}
int cAnimNum = -1;
if (!saves._justLoaded) {
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);
}
// If the scene is capable of scrolling, set the current scroll so that whoever has control
// of the scroll code is in the middle of the screen
if (screen._backBuffer1.w() > SHERLOCK_SCREEN_WIDTH)
people[people._walkControl].centerScreenOnPerson();
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();
static_cast<Scalpel::Scalpel3DOScreen *>(_vm->_screen)->fadeIntoScreen3DO(3);
}
} else {
screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
}
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::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;
}
void Scene::synchronize(Serializer &s) {
if (s.isSaving())
saveSceneStatus();
if (s.isSaving()) {
s.syncAsSint16LE(_currentScene);
} else {
s.syncAsSint16LE(_goToScene);
}
for (int sceneNum = 1; sceneNum < SCENES_COUNT; ++sceneNum) {
for (int flag = 0; flag <= MAX_BGSHAPES; ++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