mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-01 06:58:34 +00:00
8ee889beb4
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.
1423 lines
45 KiB
C++
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
|