mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-29 21:24:53 +00:00
1424 lines
45 KiB
C++
1424 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/scalpel/3do/scalpel_3do_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.width() != 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.SHblitFrom(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.SHblitFrom(screen._backBuffer1);
|
|
_vm->_events->wait(10000);
|
|
#endif
|
|
|
|
// Backup the image
|
|
screen._backBuffer2.SHblitFrom(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.width() > 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
|