diff options
Diffstat (limited to 'engines')
222 files changed, 3863 insertions, 2048 deletions
diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp index 62f07ad639..63b0937354 100644 --- a/engines/adl/adl.cpp +++ b/engines/adl/adl.cpp @@ -58,6 +58,7 @@ AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) : _graphics(nullptr), _isRestarting(false), _isRestoring(false), + _isQuitting(false), _skipOneCommand(false), _gameDescription(gd), _saveVerb(0), @@ -553,7 +554,7 @@ Common::Error AdlEngine::run() { _display->setMode(DISPLAY_MODE_MIXED); - while (1) { + while (!_isQuitting) { uint verb = 0, noun = 0; _isRestarting = false; @@ -1101,7 +1102,13 @@ int AdlEngine::o1_quit(ScriptEnv &e) { OP_DEBUG_0("\tQUIT_GAME()"); printMessage(_messageIds.thanksForPlaying); - quitGame(); + // Wait for a key here to ensure that the user gets a chance + // to read the thank-you message + _display->printAsciiString("PRESS ANY KEY TO QUIT"); + inputKey(); + + // We use _isRestarting to abort the current game loop iteration + _isQuitting = _isRestarting = true; return -1; } diff --git a/engines/adl/adl.h b/engines/adl/adl.h index 971336ef50..fc696f074f 100644 --- a/engines/adl/adl.h +++ b/engines/adl/adl.h @@ -359,7 +359,7 @@ protected: // Game state State _state; - bool _isRestarting, _isRestoring; + bool _isRestarting, _isRestoring, _isQuitting; bool _skipOneCommand; private: diff --git a/engines/adl/adl_v2.cpp b/engines/adl/adl_v2.cpp index 979d794146..45810d64ca 100644 --- a/engines/adl/adl_v2.cpp +++ b/engines/adl/adl_v2.cpp @@ -182,8 +182,8 @@ Common::String AdlEngine_v2::loadMessage(uint idx) const { void AdlEngine_v2::printString(const Common::String &str) { Common::String s(str); - byte endPos = TEXT_WIDTH - 1; - byte pos = 0; + uint endPos = TEXT_WIDTH - 1; + uint pos = 0; while (true) { while (pos <= endPos && pos != s.size()) { diff --git a/engines/bladerunner/actor_clues.cpp b/engines/bladerunner/actor_clues.cpp index a84d54906b..f9a4389b71 100644 --- a/engines/bladerunner/actor_clues.cpp +++ b/engines/bladerunner/actor_clues.cpp @@ -67,9 +67,7 @@ ActorClues::ActorClues(BladeRunnerEngine *vm, int cluesType) { } ActorClues::~ActorClues() { - if (_clues) { - delete[] _clues; - } + delete[] _clues; _maxCount = 0; _count = 0; @@ -78,7 +76,7 @@ ActorClues::~ActorClues() { void ActorClues::acquire(int clueId, char flag2, int fromActorId) { int clueIndex = findClueIndex(clueId); _clues[clueIndex]._flags |= 0x01; - _clues[_count]._flags = (_clues[_count]._flags & ~0x02) | ((flag2 << 1) & 0x02); + _clues[clueIndex]._flags = (_clues[clueIndex]._flags & ~0x02) | ((flag2 << 1) & 0x02); _clues[clueIndex]._fromActorId = fromActorId; debug("Actor acquired clue: \"%s\" from %d", _vm->_crimesDatabase->getClueText(clueId), fromActorId); diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index 33110c07de..e60fe77d26 100644 --- a/engines/bladerunner/bladerunner.cpp +++ b/engines/bladerunner/bladerunner.cpp @@ -457,6 +457,8 @@ void BladeRunnerEngine::shutdown() { // TODO: Delete sine and cosine lookup tables // TODO: Unload AI dll + delete _aiScripts; + _aiScripts = nullptr; delete[] _gameVars; _gameVars = nullptr; @@ -481,7 +483,12 @@ void BladeRunnerEngine::shutdown() { // TODO: Delete datetime - not used - // TODO: Delete actors + int actorCount = (int)_gameInfo->getActorCount(); + for (int i = 0; i != actorCount; ++i) { + delete _actors[i]; + _actors[i] = nullptr; + } + _playerActor = nullptr; // TODO: Delete proper ZBuf class delete[] _zBuffer1; diff --git a/engines/bladerunner/light.cpp b/engines/bladerunner/light.cpp index 08eb8ab5be..8be8d0112b 100644 --- a/engines/bladerunner/light.cpp +++ b/engines/bladerunner/light.cpp @@ -27,9 +27,13 @@ namespace BladeRunner { Light::Light() { + _animationData = nullptr; } Light::~Light() { + if (_animationData != nullptr) { + delete[] _animationData; + } } void Light::read(Common::ReadStream *stream, int framesCount, int frame, int animated) { @@ -43,6 +47,9 @@ void Light::read(Common::ReadStream *stream, int framesCount, int frame, int ani _animatedParameters = stream->readUint32LE(); + if (_animationData != nullptr) { + delete[] _animationData; + } int floatsCount = size / 4; _animationData = new float[floatsCount]; for (int i = 0; i < floatsCount; i++) { @@ -80,6 +87,10 @@ void Light::readVqa(Common::ReadStream *stream, int framesCount, int frame, int int size = stream->readUint32LE(); + if(_animationData != nullptr) { + delete[] _animationData; + } + int floatsCount = size / 4; _animationData = new float[floatsCount]; for (int i = 0; i < floatsCount; i++) { diff --git a/engines/bladerunner/scene.cpp b/engines/bladerunner/scene.cpp index a629b4263b..c29a6345cc 100644 --- a/engines/bladerunner/scene.cpp +++ b/engines/bladerunner/scene.cpp @@ -29,6 +29,7 @@ #include "bladerunner/chapters.h" #include "bladerunner/gameinfo.h" #include "bladerunner/items.h" +#include "bladerunner/settings.h" #include "bladerunner/scene_objects.h" #include "bladerunner/script/script.h" #include "bladerunner/slice_renderer.h" @@ -56,8 +57,8 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { // TODO: Clear regions // TODO: Destroy all overlays _defaultLoop = 0; - _defaultLoopSet = 0; - _field_20_loop_stuff = 0; + _defaultLoopSet = false; + _specialLoopAtEnd = false; _specialLoopMode = -1; _specialLoop = -1; _frame = -1; @@ -76,9 +77,6 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { _vqaPlayer = new VQAPlayer(_vm); - if (!_vqaPlayer->open(vqaName)) - return false; - Common::String sceneName = _vm->_gameInfo->getSceneName(sceneId); if (!_vm->_script->open(sceneName)) return false; @@ -99,8 +97,15 @@ bool Scene::open(int setId, int sceneId, bool isLoadingGame) { return true; } - // TODO: set VQADecoder parameters - //_vm->_scene->advanceFrame(0, 0); + if (!_vqaPlayer->open(vqaName)) + return false; + + if (_specialLoop == -1) { + _vqaPlayer->setLoop(_defaultLoop, -1, 2, nullptr, nullptr); + _defaultLoopSet = true; + _specialLoopAtEnd = false; + } + _vm->_scene->advanceFrame(_vm->_surface1, _vm->_zBuffer1); _vm->_playerActor->setAtXYZ(_actorStartPosition, _actorStartFacing); //_vm->_playerActor->setSetId(setId); @@ -148,6 +153,7 @@ bool Scene::close(bool isLoadingGame) { if (isLoadingGame) { _vm->_script->PlayerWalkedOut(); } + // if (SceneScript_isLoaded() && !SceneScript_unload()) { // result = false; // } @@ -171,18 +177,26 @@ int Scene::advanceFrame(Graphics::Surface &surface, uint16 *&zBuffer) { _vqaPlayer->updateLights(_vm->_lights); } - if (frame < 0) { - return frame; + if (_specialLoopMode && _specialLoopMode != 2 && _specialLoopMode != 3) { + if (_specialLoopMode == 1) { + if (frame == -3) { // TODO: when will this happen? bad data in/eof of vqa + _vm->_settings->setNewSetAndScene(_nextSetId, _nextSceneId); + _vm->playerGainsControl(); + } + } else if (!_specialLoopAtEnd) { + _vqaPlayer->setLoop(_defaultLoop + 1, -1, 0, &Scene::loopEndedStatic, this); + _specialLoopAtEnd = true; + } + } else if (!this->_defaultLoopSet) { + _vqaPlayer->setLoop(_defaultLoop, -1, 1, &Scene::loopEndedStatic, this); + _defaultLoopSet = true; + if (_specialLoopMode == 0) { + _vm->playerLosesControl(); + } } - _frame = frame; - if (_specialLoopMode == 0 && frame == _vqaPlayer->getLoopEndFrame(_specialLoop)) { - _playerWalkedIn = true; - _specialLoopMode = -1; - } - if (_specialLoopMode == 0 && !_defaultLoopSet) { - _vqaPlayer->setLoop(_defaultLoop + 1); - _defaultLoopSet = true; + if (frame >= 0) { + _frame = frame; } return frame; @@ -193,25 +207,32 @@ void Scene::setActorStart(Vector3 position, int facing) { _actorStartFacing = facing; } -void Scene::loopSetDefault(int a) { - // warning("\t\t\tScene::loopSetDefault(%d)", a); - _defaultLoop = a; +void Scene::loopSetDefault(int loopId) { + _defaultLoop = loopId; } -void Scene::loopStartSpecial(int a, int b, int c) { - // warning("\t\t\tScene::loopStartSpecial(%d, %d, %d)", a, b, c); - _specialLoopMode = a; - _specialLoop = b; +void Scene::loopStartSpecial(int specialLoopMode, int loopId, int flags) { + _specialLoopMode = specialLoopMode; + _specialLoop = loopId; - if (_specialLoop == 1) { - // a1->on_loop_end_switch_to_set_id = sub_42BE08_options_get_set_enter_arg_1(&unk_48E910_options); - // a1->on_loop_end_switch_to_scene_id = sub_42BE00_options_get_set_enter_arg_2(&unk_48E910_options); + int unknown = -1; + if (_specialLoopMode == 1) { + unknown = 0; } - if (c) { - // _field_20_loop_stuff = 1; - // v6 = a1->_field_28_loop_special_loop_number; - // sub_453434_scene_method_loop(a1); + int loopMode = 1; + if (flags) { + loopMode = 2; + } + + _vqaPlayer->setLoop(_specialLoop, unknown, loopMode, &Scene::loopEndedStatic, this); + if (_specialLoopMode == 1) { + this->_nextSetId = _vm->_settings->getNewSet(); + this->_nextSceneId = _vm->_settings->getNewScene(); + } + if (flags) { + this->_specialLoopAtEnd = true; + loopEnded(0, _specialLoop); } } @@ -265,4 +286,37 @@ const char *Scene::objectGetName(int objectId) { return _set->objectGetName(objectId); } +void Scene::loopEnded(int frame, int loopId) { + if (_specialLoopMode && _specialLoopMode != 2 && _specialLoopMode != 3) { + if (_specialLoopMode == 1) { + _defaultLoopSet = true; + _specialLoopAtEnd = false; + _vm->playerLosesControl(); + } + } else if (_specialLoopAtEnd) { + _vqaPlayer->setLoop(_defaultLoop, -1, 1, &Scene::loopEndedStatic, this); + _defaultLoopSet = true; + _specialLoopAtEnd = false; + if (_specialLoopMode == 0) { + _vm->playerLosesControl(); + } + } else { + if (_specialLoopMode == 0) { + _vm->playerGainsControl(); + _playerWalkedIn = true; + } + if (_specialLoopMode == 3) { + //TODO: + //spinner::open(Spinner); + } + _specialLoopMode = -1; + _specialLoop = -1; + _vqaPlayer->setLoop(_defaultLoop + 1, -1, 0, nullptr, nullptr); + _specialLoopAtEnd = true; + } +} + +void Scene::loopEndedStatic(void *data, int frame, int loopId) { + ((Scene*)data)->loopEnded(frame, loopId); +} } // End of namespace BladeRunner diff --git a/engines/bladerunner/scene.h b/engines/bladerunner/scene.h index 6a34fcd249..5bc25fc6fc 100644 --- a/engines/bladerunner/scene.h +++ b/engines/bladerunner/scene.h @@ -44,10 +44,10 @@ public: VQAPlayer *_vqaPlayer; int _defaultLoop; - int _defaultLoopSet; - int _field_20_loop_stuff; + bool _defaultLoopSet; int _specialLoopMode; int _specialLoop; + bool _specialLoopAtEnd; int _introFinished; int _nextSetId; int _nextSceneId; @@ -83,9 +83,7 @@ public: delete _set; delete _regions; delete _exits; - if (_vqaPlayer != nullptr) { - delete _vqaPlayer; - } + delete _vqaPlayer; } bool open(int setId, int sceneId, bool isLoadingGame); @@ -109,6 +107,10 @@ public: void objectSetIsObstacleAll(bool isObstacle, bool sceneLoaded); void objectSetIsTarget(int objectId, bool isTarget, bool sceneLoaded); const char *objectGetName(int objectId); + +private: + void loopEnded(int frame, int loopId); + static void loopEndedStatic(void* data, int frame, int loopId); }; } // End of namespace BladeRunner diff --git a/engines/bladerunner/scene_objects.cpp b/engines/bladerunner/scene_objects.cpp index 6d10edd83f..c55097358d 100644 --- a/engines/bladerunner/scene_objects.cpp +++ b/engines/bladerunner/scene_objects.cpp @@ -65,6 +65,7 @@ void SceneObjects::clear() { _sceneObjects[i]._isMoving = 0; _sceneObjects[i]._isRetired = 0; } + _count = 0; } bool SceneObjects::addActor(int sceneObjectId, BoundingBox *boundingBox, Common::Rect *screenRectangle, uint8 isClickable, uint8 isMoving, uint8 isTarget, uint8 isRetired) { @@ -207,7 +208,7 @@ bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObject break; } } - for (int j = _count - 1; j >= i; --j) { + for (int j = _count - 2; j >= i; --j) { _sceneObjectsSortedByDistance[j + 1] = _sceneObjectsSortedByDistance[j]; } diff --git a/engines/bladerunner/script/script.cpp b/engines/bladerunner/script/script.cpp index b0bb638440..45b23120d5 100644 --- a/engines/bladerunner/script/script.cpp +++ b/engines/bladerunner/script/script.cpp @@ -55,14 +55,12 @@ namespace BladeRunner { bool Script::open(const Common::String &name) { delete _currentScript; - if (name == "RC01") { _currentScript = new ScriptRC01(_vm); return true; } if (name == "RC02") { _currentScript = new ScriptRC02(_vm); return true; } if (name == "RC03") { _currentScript = new ScriptRC03(_vm); return true; } if (name == "RC04") { _currentScript = new ScriptRC04(_vm); return true; } if (name == "RC51") { _currentScript = new ScriptRC51(_vm); return true; } - return false; } @@ -916,18 +914,18 @@ void ScriptBase::Overlay_Remove(const char *overlay) { warning("Overlay_Remove(%s)", overlay); } -void ScriptBase::Scene_Loop_Set_Default(int a) { - // debug("Scene_Loop_Set_Default(%d)", a); - - _vm->_scene->loopSetDefault(a); - // _vm->_scene->_defaultLoop = a; +void ScriptBase::Scene_Loop_Set_Default(int loopId) { + _vm->_scene->loopSetDefault(loopId); } -void ScriptBase::Scene_Loop_Start_Special(int a, int b, int c) { - // debug("Scene_Loop_Start_Special(%d, %d, %d)", a, b, c); - - _vm->_scene->loopStartSpecial(a, b, c); - // _vm->_scene->_field_24_loop_start_special_param_1 = a; +void ScriptBase::Scene_Loop_Start_Special(int sceneLoopMode, int loopId, int c) { + if (sceneLoopMode == 1) { + c = 1; + } + _vm->_scene->loopStartSpecial(sceneLoopMode, loopId, c); + if (sceneLoopMode == 1) { + _vm->_settings->clearNewSetAndScene(); + } } void ScriptBase::Outtake_Play(int id, int noLocalization, int container) { @@ -1427,12 +1425,19 @@ void ScriptBase::VK_Play_Speech_Line(int actorIndex, int a2, float a3) { AIScripts::AIScripts(BladeRunnerEngine *vm) : _vm(vm), _inScriptCounter(0) { for (int i = 0; i != 100; ++i) - _AIScripts[i] = 0; + _AIScripts[i] = nullptr; _AIScripts[0] = new AIScript_McCoy(_vm); _AIScripts[23] = new AIScript_Officer_Leroy(_vm); } +AIScripts::~AIScripts() { + for (int i = 0; i != 100; ++i) { + delete _AIScripts[i]; + _AIScripts[i] = nullptr; + } +} + void AIScripts::Initialize(int actor) { if (_AIScripts[actor]) _AIScripts[actor]->Initialize(); diff --git a/engines/bladerunner/set_effects.cpp b/engines/bladerunner/set_effects.cpp index 54894e2a1c..c4038f6726 100644 --- a/engines/bladerunner/set_effects.cpp +++ b/engines/bladerunner/set_effects.cpp @@ -38,7 +38,7 @@ SetEffects::SetEffects(BladeRunnerEngine *vm) { _fadeDensity = 0.0f; _fogsCount = 0; - _fogs = NULL; + _fogs = nullptr; } SetEffects::~SetEffects() { @@ -55,7 +55,7 @@ void SetEffects::read(Common::ReadStream *stream, int framesCount) { int i; for (i = 0; i < _fogsCount; i++) { int type = stream->readUint32LE(); - Fog *fog = NULL; + Fog *fog = nullptr; switch (type) { case 0: fog = new FogCone(); diff --git a/engines/bladerunner/vqa_decoder.cpp b/engines/bladerunner/vqa_decoder.cpp index 940f53ee25..fe94012c51 100644 --- a/engines/bladerunner/vqa_decoder.cpp +++ b/engines/bladerunner/vqa_decoder.cpp @@ -34,8 +34,6 @@ #include "common/util.h" #include "common/memstream.h" - - namespace BladeRunner { #define kAESC 0x41455343 @@ -488,7 +486,7 @@ bool VQADecoder::readLNIN(Common::SeekableReadStream *s, uint32 size) { if (chd.id != kLNIO || chd.size != 4u * loopNamesCount) return false; - uint32 *loopNameOffsets = (uint32*)alloca(loopNamesCount * sizeof(uint32)); + uint32 *loopNameOffsets = (uint32*)malloc(loopNamesCount * sizeof(uint32)); for (int i = 0; i != loopNamesCount; ++i) { loopNameOffsets[i] = s->readUint32LE(); } @@ -497,7 +495,7 @@ bool VQADecoder::readLNIN(Common::SeekableReadStream *s, uint32 size) { if (chd.id != kLNID) return false; - char *names = (char*)alloca(roundup(chd.size)); + char *names = (char*)malloc(roundup(chd.size)); s->read(names, roundup(chd.size)); for (int i = 0; i != loopNamesCount; ++i) { @@ -509,6 +507,8 @@ bool VQADecoder::readLNIN(Common::SeekableReadStream *s, uint32 size) { // debug("%2d: %s", i, _loopInfo.loops[i].name.c_str()); } + free(loopNameOffsets); + free(names); return true; } diff --git a/engines/bladerunner/vqa_player.cpp b/engines/bladerunner/vqa_player.cpp index f875b84cce..4fb19cf561 100644 --- a/engines/bladerunner/vqa_player.cpp +++ b/engines/bladerunner/vqa_player.cpp @@ -121,7 +121,7 @@ void VQAPlayer::updateLights(Lights *lights) { _decoder.decodeLights(lights); } -bool VQAPlayer::setLoop(int loop) { +bool VQAPlayer::setLoop(int loop, int unknown, int loopMode, void(*callback)(void*, int, int), void *callbackData) { int begin, end; if (!_decoder.getLoopBeginAndEndFrame(loop, &begin, &end)) { return false; @@ -131,6 +131,9 @@ bool VQAPlayer::setLoop(int loop) { _loopBegin = begin; _loopEnd = end; + _callbackLoopEnded = callback; + _callbackData = callbackData; + // warning("\t\t\tActive Loop: %d - %d\n", begin, end); return true; @@ -158,6 +161,9 @@ int VQAPlayer::calcNextFrame(int frame) const { if (_curLoop != -1 && frame >= _loopEnd) { frame = _loopBegin; + if (_callbackLoopEnded != nullptr) { + _callbackLoopEnded(_callbackData, 0, _curLoop); + } } else { frame++; } diff --git a/engines/bladerunner/vqa_player.h b/engines/bladerunner/vqa_player.h index f5769944cf..3a821a8323 100644 --- a/engines/bladerunner/vqa_player.h +++ b/engines/bladerunner/vqa_player.h @@ -55,6 +55,9 @@ class VQAPlayer { bool _audioStarted; Audio::SoundHandle _soundHandle; + void (*_callbackLoopEnded)(void*, int frame, int loopId); + void *_callbackData; + public: VQAPlayer(BladeRunnerEngine *vm) @@ -69,7 +72,8 @@ public: _loopEnd(-1), _nextFrameTime(0), _hasAudio(false), - _audioStarted(false) { + _audioStarted(false), + _callbackLoopEnded(nullptr) { } ~VQAPlayer() { @@ -85,9 +89,7 @@ public: void updateView(View *view); void updateLights(Lights *lights); - bool setLoop(int loop); - // void setLoopSpecial(int loop, bool wait); - // void setLoopDefault(int loop); + bool setLoop(int loop, int unknown, int loopMode, void(*callback)(void*, int, int), void* callbackData); int getLoopBeginFrame(int loop); int getLoopEndFrame(int loop); diff --git a/engines/chewy/detection.cpp b/engines/chewy/detection.cpp index a59355f166..f6f66efba0 100644 --- a/engines/chewy/detection.cpp +++ b/engines/chewy/detection.cpp @@ -73,8 +73,9 @@ static const ChewyGameDescription gameDescriptions[] = { { // Chewy - ESC von F5 - German + // Master version 1.1 (CHEWY.EXE - offset 0x8AB28) // The source CD-ROM has the Matrix code SONOPRESS R-7885 B - // Most likely a newer re-release, it contains several demos and files from 1996 + // The disc contains several demos and files from 1996 // Provided by rootfather { "chewy", @@ -89,6 +90,7 @@ static const ChewyGameDescription gameDescriptions[] = { { // Chewy - ESC von F5 - German + // Master version 1.0 (CHEWY.EXE - offset 0x8AB10) // The source CD-ROM has the Matrix code SONOPRESS M-2742 A // CD-ROM has the label "CHEWY_V1_0" // Provided by rootfather diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp index 73d97e100d..13ba76191b 100644 --- a/engines/composer/composer.cpp +++ b/engines/composer/composer.cpp @@ -21,6 +21,7 @@ */ #include "common/scummsys.h" +#include "common/config-manager.h" #include "common/events.h" #include "common/random.h" #include "common/keyboard.h" @@ -120,6 +121,9 @@ Common::Error ComposerEngine::run() { else warning("FPS in book.ini is zero. Defaulting to 8..."); uint32 lastDrawTime = 0; + _lastSaveTime = _system->getMillis(); + + bool loadFromLauncher = ConfMan.hasKey("save_slot"); while (!shouldQuit()) { for (uint i = 0; i < _pendingPageChanges.size(); i++) { @@ -169,7 +173,12 @@ Common::Error ComposerEngine::run() { } else if (_needsUpdate) { redraw(); } - + if (loadFromLauncher) { + loadGameState(ConfMan.getInt("save_slot")); + loadFromLauncher = false; + } + if (shouldPerformAutoSave(_lastSaveTime)) + saveGameState(0, "Autosave"); while (_eventMan->pollEvent(event)) { switch (event.type) { case Common::EVENT_LBUTTONDOWN: @@ -378,7 +387,7 @@ void ComposerEngine::loadLibrary(uint id) { } Common::String filename; - + Common::String oldGroup = _bookGroup; if (getGameType() == GType_ComposerV1) { if (!id || _bookGroup.empty()) filename = getStringFromConfig("Common", "StartPage"); @@ -412,6 +421,7 @@ void ComposerEngine::loadLibrary(uint id) { Library library; library._id = id; + library._group = oldGroup; library._archive = new ComposerArchive(); if (!library._archive->openFile(filename)) error("failed to open '%s'", filename.c_str()); diff --git a/engines/composer/composer.h b/engines/composer/composer.h index d1a85e975a..a4b421bfa0 100644 --- a/engines/composer/composer.h +++ b/engines/composer/composer.h @@ -29,6 +29,7 @@ #include "common/debug.h" #include "common/debug-channels.h" #include "common/error.h" +#include "common/serializer.h" #include "common/textconsole.h" #include "common/rect.h" @@ -114,6 +115,7 @@ struct Library { uint _id; Archive *_archive; + Common::String _group; Common::List<Button> _buttons; Common::List<KeyboardHandler> _keyboardHandlers; }; @@ -150,6 +152,19 @@ class ComposerEngine : public Engine { protected: Common::Error run(); + template <typename T> + void syncArray(Common::Serializer &ser, Common::Array<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion); + template <typename T> + void syncList(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion); + template <typename T> + void syncListReverse(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion); + template <typename T> + void sync(Common::Serializer &ser, T &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion); + bool canLoadGameStateCurrently() { return true; } + Common::Error loadGameState(int slot); + bool canSaveGameStateCurrently() { return true; } + Common::Error saveGameState(int slot, const Common::String &desc); + public: ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc); virtual ~ComposerEngine(); @@ -173,7 +188,7 @@ private: Audio::QueuingAudioStream *_audioStream; uint16 _currSoundPriority; - uint32 _currentTime, _lastTime; + uint32 _currentTime, _lastTime, _timeDelta, _lastSaveTime; bool _needsUpdate; Common::Array<Common::Rect> _dirtyRects; @@ -210,6 +225,7 @@ private: uint16 _mouseSpriteId; Common::Point _mouseOffset; + Common::String makeSaveGameName(int slot); Common::String getStringFromConfig(const Common::String §ion, const Common::String &key); Common::String getFilename(const Common::String §ion, uint id); Common::String mangleFilename(Common::String filename); @@ -231,6 +247,7 @@ private: void tickOldScripts(); bool tickOldScript(OldScript *script); + void loadAnimation(Animation *&anim, uint16 animId, int16 x, int16 y, int16 eventParam, int32 size = 0); void playAnimation(uint16 animId, int16 param1, int16 param2, int16 param3); void stopAnimation(Animation *anim, bool localOnly = false, bool pipesOnly = false); void playWaveForAnim(uint16 id, uint16 priority, bool bufferingOnly); diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp index 689a72a743..8de3b33134 100644 --- a/engines/composer/detection.cpp +++ b/engines/composer/detection.cpp @@ -21,6 +21,9 @@ */ #include "base/plugins.h" +#include "common/savefile.h" +#include "common/serializer.h" +#include "common/str-array.h" #include "engines/advancedDetector.h" #include "composer/composer.h" @@ -448,6 +451,8 @@ public: virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual bool hasFeature(MetaEngineFeature f) const; + virtual int getMaximumSaveSlot() const; + virtual SaveStateList listSaves(const char* target) const; }; bool ComposerMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { @@ -459,11 +464,52 @@ bool ComposerMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD } bool ComposerMetaEngine::hasFeature(MetaEngineFeature f) const { - return false; + return ((f == kSupportsListSaves) || (f == kSupportsLoadingDuringStartup)); +} + +Common::String getSaveName(Common::InSaveFile *in) { + Common::Serializer ser(in, NULL); + Common::String name; + uint32 tmp; + ser.syncAsUint32LE(tmp); + ser.syncAsUint32LE(tmp); + ser.syncString(name); + return name; +} +int ComposerMetaEngine::getMaximumSaveSlot() const { + return 99; +} +SaveStateList ComposerMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String saveDesc; + Common::String pattern = Common::String::format("%s.??", target); + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(file->c_str() + file->size() - 2); + + if (slotNum >= 0 && slotNum <= 99) { + Common::InSaveFile *in = saveFileMan->openForLoading(*file); + if (in) { + saveDesc = getSaveName(in); + saveList.push_back(SaveStateDescriptor(slotNum, saveDesc)); + delete in; + } + } + } + + return saveList; } bool Composer::ComposerEngine::hasFeature(EngineFeature f) const { - return (f == kSupportsRTL); + return (f == kSupportsRTL + || f == kSupportsSavingDuringRuntime + || f == kSupportsLoadingDuringRuntime); } #if PLUGIN_ENABLED_DYNAMIC(COMPOSER) diff --git a/engines/composer/graphics.cpp b/engines/composer/graphics.cpp index 87694636d8..32b9812f32 100644 --- a/engines/composer/graphics.cpp +++ b/engines/composer/graphics.cpp @@ -44,9 +44,9 @@ bool Sprite::contains(const Common::Point &pos) const { } enum { - kAnimOpEvent = 1, - kAnimOpPlayWave = 2, - kAnimOpPlayAnim = 3, + kAnimOpEvent = 1, + kAnimOpPlayWave = 2, + kAnimOpPlayAnim = 3, kAnimOpDrawSprite = 4 }; @@ -57,6 +57,7 @@ Animation::Animation(Common::SeekableReadStream *stream, uint16 id, Common::Poin // probably total size? uint32 unknown = _stream->readUint32LE(); + _size = unknown; debug(8, "anim: size %d, state %08x, unknown %08x", size, _state, unknown); @@ -82,17 +83,7 @@ void Animation::seekToCurrPos() { _stream->seek(_offset, SEEK_SET); } -void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventParam) { - // First, we check if this animation is already playing, - // and if it is, we sabotage that running one first. - for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) { - Animation *anim = *i; - if (anim->_id != animId) - continue; - - stopAnimation(*i); - } - +void ComposerEngine::loadAnimation(Animation *&anim, uint16 animId, int16 x, int16 y, int16 eventParam, int32 size) { Common::SeekableReadStream *stream = NULL; Pipe *newPipe = NULL; @@ -102,7 +93,10 @@ void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventP if (!pipe->hasResource(ID_ANIM, animId)) continue; stream = pipe->getResource(ID_ANIM, animId, false); - break; + + // When loading from savegame, make sure we have the correct stream + if ((!size) || (stream->size() >= size)) break; + stream = NULL; } // If we didn't find it, try the libraries. @@ -111,33 +105,50 @@ void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventP warning("ignoring attempt to play invalid anim %d", animId); return; } - stream = getResource(ID_ANIM, animId); + Common::List<Library>::iterator j; + for (j = _libraries.begin(); j != _libraries.end(); j++) { + stream = j->_archive->getResource(ID_ANIM, animId); - uint32 type = 0; - for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) - if (i->_archive->hasResource(ID_ANIM, animId)) { - type = i->_archive->getResourceFlags(ID_ANIM, animId); - break; - } + // When loading from savegame, make sure we have the correct stream + if ((!size) || (stream->size() >= size)) break; + stream = NULL; + } + + uint32 type = j->_archive->getResourceFlags(ID_ANIM, animId); // If the resource is a pipe itself, then load the pipe // and then fish the requested animation out of it. if (type != 1) { _pipeStreams.push_back(stream); - newPipe = new Pipe(stream); + newPipe = new Pipe(stream, animId); _pipes.push_front(newPipe); newPipe->nextFrame(); stream = newPipe->getResource(ID_ANIM, animId, false); } } - Animation *anim = new Animation(stream, animId, Common::Point(x, y), eventParam); - _anims.push_back(anim); - runEvent(kEventAnimStarted, animId, eventParam, 0); + anim = new Animation(stream, animId, Common::Point(x, y), eventParam); if (newPipe) newPipe->_anim = anim; } +void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventParam) { + // First, we check if this animation is already playing, + // and if it is, we sabotage that running one first. + for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) { + Animation *anim = *i; + if (anim->_id != animId) + continue; + + stopAnimation(*i); + } + + Animation *anim = NULL; + loadAnimation(anim, animId, x, y, eventParam); + _anims.push_back(anim); + runEvent(kEventAnimStarted, animId, eventParam, 0); +} + void ComposerEngine::stopAnimation(Animation *anim, bool localOnly, bool pipesOnly) { // disable the animation anim->_state = 0; @@ -376,7 +387,7 @@ void ComposerEngine::playPipe(uint16 id) { } Common::SeekableReadStream *stream = getResource(ID_PIPE, id); - OldPipe *pipe = new OldPipe(stream); + OldPipe *pipe = new OldPipe(stream, id); _pipes.push_front(pipe); //pipe->nextFrame(); diff --git a/engines/composer/graphics.h b/engines/composer/graphics.h index a8f37ddf60..4805e5017d 100644 --- a/engines/composer/graphics.h +++ b/engines/composer/graphics.h @@ -59,6 +59,7 @@ struct Animation { uint32 _eventParam; uint32 _state; + uint32 _size; Common::Array<AnimationEntry> _entries; diff --git a/engines/composer/module.mk b/engines/composer/module.mk index c879d53630..74465cf156 100644 --- a/engines/composer/module.mk +++ b/engines/composer/module.mk @@ -6,6 +6,7 @@ MODULE_OBJS = \ detection.o \ graphics.o \ resource.o \ + saveload.o \ scripting.o # This module can be built as a plugin diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp index d867f734a9..fa1811c05a 100644 --- a/engines/composer/resource.cpp +++ b/engines/composer/resource.cpp @@ -248,10 +248,11 @@ bool ComposerArchive::openStream(Common::SeekableReadStream *stream) { return true; } -Pipe::Pipe(Common::SeekableReadStream *stream) { +Pipe::Pipe(Common::SeekableReadStream *stream, uint16 id) { _offset = 0; _stream = stream; _anim = NULL; + _pipeId = id; } Pipe::~Pipe() { @@ -312,8 +313,14 @@ Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffer if (res.entries.size() == 1) { Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(_stream, res.entries[0].offset, res.entries[0].offset + res.entries[0].size); - if (buffering) + if (buffering) { _types[tag].erase(id); + bool found = false; + for (Common::List<uint16>::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++) + if ((*i) == id) found = true; + if (!found) + _bufferedResources[tag].push_back(id); + } return stream; } @@ -330,12 +337,18 @@ Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffer _stream->read(buffer + offset, res.entries[i].size); offset += res.entries[i].size; } - if (buffering) + if (buffering) { _types[tag].erase(id); + bool found = false; + for (Common::List<uint16>::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++) + if ((*i) == id) found = true; + if (!found) + _bufferedResources[tag].push_back(id); + } return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES); } -OldPipe::OldPipe(Common::SeekableReadStream *stream) : Pipe(stream), _currFrame(0) { +OldPipe::OldPipe(Common::SeekableReadStream *stream, uint16 pipeId) : Pipe(stream, pipeId), _currFrame(0) { uint32 tag = _stream->readUint32BE(); if (tag != ID_PIPE) error("invalid tag for pipe (%08x)", tag); diff --git a/engines/composer/resource.h b/engines/composer/resource.h index b624da1776..fc4e20a2cd 100644 --- a/engines/composer/resource.h +++ b/engines/composer/resource.h @@ -106,7 +106,7 @@ struct PipeResource { class Pipe { public: - Pipe(Common::SeekableReadStream *stream); + Pipe(Common::SeekableReadStream *stream, uint16 id); virtual ~Pipe(); virtual void nextFrame(); @@ -116,6 +116,11 @@ public: Common::SeekableReadStream *getResource(uint32 tag, uint16 id, bool buffering); virtual const Common::Array<uint16> *getScripts() { return NULL; } + uint16 getPipeId() const { return _pipeId; } + virtual uint32 getOffset() const { return _offset; } + virtual void setOffset(uint32 offset) { while (_offset < offset) nextFrame(); } + typedef Common::HashMap<uint32, Common::List<uint16> > DelMap; + DelMap _bufferedResources; protected: Common::SeekableReadStream *_stream; @@ -123,16 +128,19 @@ protected: typedef Common::HashMap<uint16, PipeResource> ResourceMap; typedef Common::HashMap<uint32, ResourceMap> TypeMap; TypeMap _types; + uint16 _pipeId; uint32 _offset; }; class OldPipe : public Pipe { public: - OldPipe(Common::SeekableReadStream *stream); + OldPipe(Common::SeekableReadStream *stream, uint16 pipeId); void nextFrame(); const Common::Array<uint16> *getScripts() { return &_scripts; } + uint32 getOffset() const { return _currFrame; } + void setOffset(uint32 offset) { while (_currFrame < offset) nextFrame(); } protected: uint32 _currFrame, _numFrames; diff --git a/engines/composer/saveload.cpp b/engines/composer/saveload.cpp new file mode 100644 index 0000000000..ea657a9dd4 --- /dev/null +++ b/engines/composer/saveload.cpp @@ -0,0 +1,428 @@ +/* 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 "audio/audiostream.h" +#include "audio/decoders/raw.h" +#include "common/config-manager.h" +#include "common/memstream.h" +#include "common/savefile.h" +#include "common/serializer.h" +#include "common/system.h" +#include "common/zlib.h" +#include "graphics/palette.h" + +#include "composer/composer.h" +#include "composer/graphics.h" + +namespace Composer { + +template<class T> +void ComposerEngine::syncArray(Common::Serializer &ser, Common::Array<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + if (ser.isSaving()) { + uint32 size = data.size(); + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (typename Common::Array<T>::iterator i = data.begin(); i != data.end(); i++) { + sync<T>(ser, *i, minVersion, maxVersion); + } + } else { + uint32 size; + data.clear(); + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (uint32 i = 0; i < size; i++) { + T item; + sync<T>(ser, item, minVersion, maxVersion); + data.push_back(item); + } + } +} +template<class T> +void ComposerEngine::syncList(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + if (ser.isSaving()) { + uint32 size = data.size(); + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (typename Common::List<T>::iterator i = data.begin(); i != data.end(); i++) { + sync<T>(ser, *i, minVersion, maxVersion); + } + } else { + uint32 size; + data.clear(); + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (uint32 i = 0; i < size; i++) { + T item; + sync<T>(ser, item, minVersion, maxVersion); + data.push_back(item); + } + } +} +template<class T> +void ComposerEngine::syncListReverse(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + if (ser.isSaving()) { + uint32 size = data.size(); + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (typename Common::List<T>::iterator i = data.reverse_begin(); i != data.end(); i--) { + sync<T>(ser, *i, minVersion, maxVersion); + } + } else { + uint32 size; + data.clear(); + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (uint32 i = 0; i < size; i++) { + T item; + sync<T>(ser, item, minVersion, maxVersion); + data.push_front(item); + } + } +} +template<> +void ComposerEngine::sync<uint16>(Common::Serializer &ser, uint16 &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + ser.syncAsUint16LE(data, minVersion, maxVersion); +} +template<> +void ComposerEngine::sync<uint32>(Common::Serializer &ser, uint32 &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + ser.syncAsUint32LE(data, minVersion, maxVersion); +} +template<> +void ComposerEngine::sync<Library>(Common::Serializer &ser, Library &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + if (ser.isSaving()) { + ser.syncAsUint16LE(data._id, minVersion, maxVersion); + ser.syncString(data._group, minVersion, maxVersion); + } else { + uint16 id; + ser.syncAsUint16LE(id, minVersion, maxVersion); + ser.syncString(_bookGroup, minVersion, maxVersion); + loadLibrary(id); + } +} +template<> +void ComposerEngine::syncListReverse<Library>(Common::Serializer &ser, Common::List<Library> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + if (ser.isSaving()) { + uint32 size = data.size(); + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (Common::List<Library>::iterator i = data.reverse_begin(); i != data.end(); i--) { + sync<Library>(ser, *i, minVersion, maxVersion); + } + } else { + uint32 size; + ser.syncAsUint32LE(size, minVersion, maxVersion); + for (uint32 i = 0; i < size; i++) { + Library item; + sync<Library>(ser, item, minVersion, maxVersion); + } + } +} +template<> +void ComposerEngine::sync<PendingPageChange>(Common::Serializer &ser, PendingPageChange &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + ser.syncAsUint16LE(data._pageId, minVersion, maxVersion); + ser.syncAsByte(data._remove, minVersion, maxVersion); +} +template<> +void ComposerEngine::sync<OldScript *>(Common::Serializer &ser, OldScript *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + uint16 id; + uint32 pos, delay; + if (ser.isSaving()) { + pos = data->_stream->pos(); + id = data->_id; + delay = data->_currDelay; + } + ser.syncAsUint32LE(pos); + ser.syncAsUint16LE(id); + ser.syncAsUint32LE(delay); + if (ser.isLoading()) { + data = new OldScript(id, getResource(ID_SCRP, id)); + data->_currDelay = delay; + data->_stream->seek(pos, SEEK_SET); + } +} +template<> +void ComposerEngine::sync<QueuedScript>(Common::Serializer &ser, QueuedScript &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + ser.syncAsUint32LE(data._baseTime); + ser.syncAsUint32LE(data._duration); + ser.syncAsUint32LE(data._count); + ser.syncAsUint16LE(data._scriptId); + if (ser.isLoading()) data._baseTime += _timeDelta; +} +template<> +void ComposerEngine::sync<Pipe *>(Common::Serializer &ser, Pipe *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + uint16 id; + uint32 offset, tmp; + if (ser.isSaving()) { + id = data->getPipeId(); + offset = data->getOffset(); + tmp = data->_bufferedResources.size(); + } + ser.syncAsUint16LE(id); + ser.syncAsUint32LE(offset); + + if (ser.isLoading()) { + // On load, get and initialize streams + Common::SeekableReadStream *stream; + if (getGameType() == GType_ComposerV1) { + stream = getResource(ID_PIPE, id); + data = new OldPipe(stream, id); + } else { + stream = getResource(ID_ANIM, id); + data = new Pipe(stream, id); + } + _pipeStreams.push_back(stream); + data->setOffset(offset); + ser.syncAsUint32LE(tmp); + for (uint32 j = tmp; j > 0; j--) { + uint32 tag; + ser.syncAsUint32LE(tag); + ser.syncAsUint32LE(tmp); + for (uint32 k = tmp; k > 0; k--) { + ser.syncAsUint16LE(id); + if (data->hasResource(tag, id)) + data->getResource(tag, id, true); + } + } + } else { + ser.syncAsUint32LE(tmp); + for (Pipe::DelMap::iterator i = data->_bufferedResources.begin(); i != data->_bufferedResources.end(); i++) { + uint32 key = (*i)._key; + ser.syncAsUint32LE(key); + syncList<uint16>(ser, (*i)._value, minVersion, maxVersion); + } + } +} +template<> +void ComposerEngine::sync<AnimationEntry>(Common::Serializer &ser, AnimationEntry &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + ser.syncAsUint32LE(data.state); + ser.syncAsUint16LE(data.counter); + ser.syncAsUint16LE(data.prevValue); +} +template<> +void ComposerEngine::sync<Animation *>(Common::Serializer &ser, Animation *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + uint16 animId, x, y; + uint32 offset, state, param; + int32 size; + if (ser.isSaving()) { + animId = data->_id; + offset = data->_offset; + x = data->_basePos.x; + y = data->_basePos.x; + state = data->_state; + param = data->_eventParam; + size = data->_size; + } + ser.syncAsUint16LE(animId); + ser.syncAsUint32LE(offset); + ser.syncAsUint16LE(x); + ser.syncAsUint16LE(y); + ser.syncAsUint32LE(state); + ser.syncAsUint32LE(param); + ser.syncAsUint32LE(size); + if (ser.isLoading()) { + // On load, get and initialize streams + loadAnimation(data, animId, x, y, param, size); + data->_offset = offset; + data->_state = state; + uint32 tmp; + ser.syncAsUint32LE(tmp); + for (uint32 i = 0; i < tmp; i++) { + sync<AnimationEntry>(ser, data->_entries[i], minVersion, maxVersion); + } + } else { + syncArray<AnimationEntry>(ser, data->_entries, minVersion, maxVersion); + } +} +template<> +void ComposerEngine::sync<Sprite>(Common::Serializer &ser, Sprite &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) { + ser.syncAsUint16LE(data._id); + ser.syncAsUint16LE(data._animId); + ser.syncAsSint16LE(data._pos.x); + ser.syncAsSint16LE(data._pos.y); + ser.syncAsUint16LE(data._surface.w); + ser.syncAsUint16LE(data._surface.h); + ser.syncAsUint16LE(data._surface.pitch); + ser.syncAsUint16LE(data._zorder); + if (ser.isLoading()) + data._surface.setPixels(malloc(data._surface.h * data._surface.pitch)); + byte *pix = static_cast<byte *>(data._surface.getPixels()); + for (uint16 y = 0; y < data._surface.h; y++) { + for (uint16 x = 0; x < data._surface.w; x++) { + ser.syncAsByte(pix[x]); + } + pix += data._surface.pitch; + } + +} +Common::String ComposerEngine::makeSaveGameName(int slot) { + return (_targetName + Common::String::format(".%02d", slot)); +} + +Common::Error ComposerEngine::loadGameState(int slot) { + Common::String filename = makeSaveGameName(slot); + Common::InSaveFile *in; + if (!(in = _saveFileMan->openForLoading(filename))) + return Common::kPathNotFile; + + Common::Serializer ser(in, NULL); + byte magic[4]; + ser.syncBytes(magic, 4); + if (magic[0] != 'C' || magic[1] != 'M' || magic[2] != 'P' || magic[3] != 'S') + return Common::kUnknownError; + + ser.syncVersion(0); + Common::String desc; + ser.syncString(desc); + uint32 tmp; + ser.syncAsUint32LE(tmp); + _rnd->setSeed(tmp); + ser.syncAsUint32LE(_currentTime); + _timeDelta = _system->getMillis() - _currentTime; + _currentTime += _timeDelta; + ser.syncAsUint32LE(_lastTime); + _lastTime += _timeDelta; + + // Unload all Libraries + Common::Array<uint16> libIds; + for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) + libIds.push_back((*i)._id); + for (uint32 i = 0; i < libIds.size(); i++) + unloadLibrary(libIds[i]); + + syncListReverse<Library>(ser, _libraries); + ser.syncString(_bookGroup); + + syncArray<PendingPageChange>(ser, _pendingPageChanges); + syncArray<uint16>(ser, _stack); + syncArray<uint16>(ser, _vars); + + // Free outdated pointers + for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) { + delete *i; + } + + syncList<OldScript *>(ser, _oldScripts); + syncArray<QueuedScript>(ser, _queuedScripts); + + ser.syncAsSint16LE(_lastMousePos.x); + ser.syncAsSint16LE(_lastMousePos.y); + g_system->warpMouse(_lastMousePos.x, _lastMousePos.y); + ser.syncAsByte(_mouseEnabled); + ser.syncAsByte(_mouseVisible); + ser.syncAsUint16LE(_mouseSpriteId); + + // Free outdated pointers + for (Common::List<Pipe *>::iterator i = _pipes.begin(); i != _pipes.end(); i++) { + delete *i; + } + for (Common::Array<Common::SeekableReadStream *>::iterator i = _pipeStreams.begin(); i != _pipeStreams.end(); i++) { + delete *i; + } + + _pipeStreams.clear(); + syncListReverse<Pipe *>(ser, _pipes); + + // Free outdated pointers + for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) { + delete *i; + } + + syncList<Animation *>(ser, _anims); + syncList<Sprite>(ser, _sprites); + + _dirtyRects.clear(); + + // Redraw the whole screen + _dirtyRects.push_back(Common::Rect(0, 0, 640, 480)); + byte palbuf[256 * 3]; + ser.syncBytes(palbuf, 256 * 3); + _system->getPaletteManager()->setPalette(palbuf, 0, 256); + _needsUpdate = true; + + _mixer->stopAll(); + _audioStream = NULL; + + // Restore the buffered audio + ser.syncAsSint16LE(_currSoundPriority); + int32 numSamples; + ser.syncAsSint32LE(numSamples); + int16 *audioBuffer = (int16 *)malloc(numSamples * 2); + for (int32 i = 0; i < numSamples; i++) + ser.syncAsSint16LE(audioBuffer[i]); + _audioStream = Audio::makeQueuingAudioStream(22050, false); + _audioStream->queueBuffer((byte *)audioBuffer, numSamples * 2, DisposeAfterUse::YES, Audio::FLAG_16BITS); + if (!_mixer->isSoundHandleActive(_soundHandle)) + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream); + + + // Reset autosave duration on load + _lastSaveTime = _system->getMillis(); + + return Common::kNoError; +} + +Common::Error ComposerEngine::saveGameState(int slot, const Common::String &desc) { + Common::String filename = makeSaveGameName(slot); + Common::OutSaveFile *out; + _lastSaveTime = _system->getMillis(); + if (!(out = _saveFileMan->openForSaving(filename))) + return Common::kWritingFailed; + + Common::Serializer ser(NULL, out); + byte magic[4] = {'C', 'M', 'P', 'S'}; + ser.syncBytes(magic, 4); + ser.syncVersion(0); + Common::String desctmp = desc; + ser.syncString(desctmp); + uint32 tmp = _rnd->getSeed(); + ser.syncAsUint32LE(tmp); + ser.syncAsUint32LE(_currentTime); + ser.syncAsUint32LE(_lastTime); + + syncListReverse<Library>(ser, _libraries); + ser.syncString(_bookGroup); + + syncArray<PendingPageChange>(ser, _pendingPageChanges); + syncArray<uint16>(ser, _stack); + syncArray<uint16>(ser, _vars); + syncList<OldScript *>(ser, _oldScripts); + syncArray<QueuedScript>(ser, _queuedScripts); + + ser.syncAsSint16LE(_lastMousePos.x); + ser.syncAsSint16LE(_lastMousePos.y); + ser.syncAsByte(_mouseEnabled); + ser.syncAsByte(_mouseVisible); + ser.syncAsUint16LE(_mouseSpriteId); + + syncListReverse<Pipe *>(ser, _pipes); + syncList<Animation *>(ser, _anims); + syncList<Sprite>(ser, _sprites); + + byte paletteBuffer[256 * 3]; + _system->getPaletteManager()->grabPalette(paletteBuffer, 0, 256); + ser.syncBytes(paletteBuffer, 256 * 3); + + ser.syncAsSint16LE(_currSoundPriority); + int16 audioBuffer[22050]; + int32 numSamples = _audioStream->readBuffer(audioBuffer, 22050); + if (numSamples == -1) numSamples = 0; + ser.syncAsSint32LE(numSamples); + for (int32 i = 0; i < numSamples; i++) + ser.syncAsSint16LE(audioBuffer[i]); + + out->finalize(); + return Common::kNoError; +} +} // End of namespace Composer diff --git a/engines/director/archive.cpp b/engines/director/archive.cpp index 1af63a81b7..ff97d3fce1 100644 --- a/engines/director/archive.cpp +++ b/engines/director/archive.cpp @@ -20,229 +20,455 @@ * */ -#include "common/macresman.h" - +#include "director/archive.h" #include "director/director.h" -#include "director/resource.h" -#include "director/lingo/lingo.h" + +#include "common/debug.h" +#include "common/macresman.h" namespace Director { -Archive *DirectorEngine::createArchive() { - if (getPlatform() == Common::kPlatformMacintosh) { - if (getVersion() < 4) - return new MacArchive(); - else - return new RIFXArchive(); - } else { - return new RIFFArchive(); - } +// Base Archive code + +Archive::Archive() { + _stream = 0; + _isBigEndian = true; } -void DirectorEngine::loadMainArchive() { - if (getPlatform() == Common::kPlatformWindows) - loadEXE(); - else - loadMac(); -} - -void DirectorEngine::cleanupMainArchive() { - delete _mainArchive; - delete _macBinary; -} - -void DirectorEngine::loadEXE() { - Common::SeekableReadStream *exeStream = SearchMan.createReadStreamForMember(getEXEName()); - if (!exeStream) - error("Failed to open EXE '%s'", getEXEName().c_str()); - - _lingo->processEvent(kEventStart, 0); - - exeStream->seek(-4, SEEK_END); - exeStream->seek(exeStream->readUint32LE()); - - switch (getVersion()) { - case 3: - loadEXEv3(exeStream); - break; - case 4: - loadEXEv4(exeStream); - break; - case 5: - loadEXEv5(exeStream); - break; - case 7: - loadEXEv7(exeStream); - break; - default: - error("Unhandled Windows EXE version %d", getVersion()); +Archive::~Archive() { + close(); +} + +bool Archive::openFile(const Common::String &fileName) { + Common::File *file = new Common::File(); + + if (!file->open(fileName)) { + delete file; + return false; + } + + if (!openStream(file)) { + close(); + return false; } + + _fileName = fileName; + + return true; } -void DirectorEngine::loadEXEv3(Common::SeekableReadStream *stream) { - uint16 entryCount = stream->readUint16LE(); - if (entryCount != 1) - error("Unhandled multiple entry v3 EXE"); +void Archive::close() { + _types.clear(); + + if (_stream) + delete _stream; - stream->skip(5); // unknown + _stream = 0; +} + +bool Archive::hasResource(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + return false; - stream->readUint32LE(); // Main MMM size - Common::String mmmFileName = readPascalString(*stream); - Common::String directoryName = readPascalString(*stream); + return _types[tag].contains(id); +} - debugC(1, kDebugLoading, "Main MMM: '%s'", mmmFileName.c_str()); - debugC(1, kDebugLoading, "Directory Name: '%s'", directoryName.c_str()); +bool Archive::hasResource(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) + return false; - _mainArchive = new RIFFArchive(); + const ResourceMap &resMap = _types[tag]; - if (!_mainArchive->openFile(mmmFileName)) - error("Could not open '%s'", mmmFileName.c_str()); + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.matchString(resName)) + return true; - delete stream; + return false; } -void DirectorEngine::loadEXEv4(Common::SeekableReadStream *stream) { - if (stream->readUint32BE() != MKTAG('P', 'J', '9', '3')) - error("Invalid projector tag found in v4 EXE"); +Common::SeekableSubReadStreamEndian *Archive::getResource(uint32 tag, uint16 id) { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; - uint32 rifxOffset = stream->readUint32LE(); - /* uint32 fontMapOffset = */ stream->readUint32LE(); - /* uint32 resourceForkOffset1 = */ stream->readUint32LE(); - /* uint32 resourceForkOffset2 = */ stream->readUint32LE(); - stream->readUint32LE(); // graphics DLL offset - stream->readUint32LE(); // sound DLL offset - /* uint32 rifxOffsetAlt = */ stream->readUint32LE(); // equivalent to rifxOffset + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - loadEXERIFX(stream, rifxOffset); + const Resource &res = resMap[id]; + + return new Common::SeekableSubReadStreamEndian(_stream, res.offset, res.offset + res.size, _isBigEndian, DisposeAfterUse::NO); } -void DirectorEngine::loadEXEv5(Common::SeekableReadStream *stream) { - if (stream->readUint32LE() != MKTAG('P', 'J', '9', '5')) - error("Invalid projector tag found in v5 EXE"); +uint32 Archive::getOffset(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; - uint32 rifxOffset = stream->readUint32LE(); - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - /* uint16 screenWidth = */ stream->readUint16LE(); - /* uint16 screenHeight = */ stream->readUint16LE(); - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - /* uint32 fontMapOffset = */ stream->readUint32LE(); + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); - loadEXERIFX(stream, rifxOffset); + return resMap[id].offset; } -void DirectorEngine::loadEXEv7(Common::SeekableReadStream *stream) { - if (stream->readUint32LE() != MKTAG('P', 'J', '0', '0')) - error("Invalid projector tag found in v7 EXE"); +uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const { + if (!_types.contains(tag) || resName.empty()) + return 0xFFFF; - uint32 rifxOffset = stream->readUint32LE(); - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // unknown - stream->readUint32LE(); // some DLL offset + const ResourceMap &resMap = _types[tag]; - loadEXERIFX(stream, rifxOffset); + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + if (it->_value.name.matchString(resName)) + return it->_key; + + return 0xFFFF; } -void DirectorEngine::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) { - _mainArchive = new RIFXArchive(); +Common::String Archive::getName(uint32 tag, uint16 id) const { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; - if (!_mainArchive->openStream(stream, offset)) - error("Failed to load RIFX from EXE"); + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + return resMap[id].name; } -void DirectorEngine::loadMac() { - if (getVersion() < 4) { - // The data is part of the resource fork of the executable - _mainArchive = new MacArchive(); +Common::Array<uint32> Archive::getResourceTypeList() const { + Common::Array<uint32> typeList; - if (!_mainArchive->openFile(getEXEName())) - error("Failed to open Mac binary '%s'", getEXEName().c_str()); - } else { - // The RIFX is located in the data fork of the executable - _macBinary = new Common::MacResManager(); + for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++) + typeList.push_back(it->_key); - if (!_macBinary->open(getEXEName()) || !_macBinary->hasDataFork()) - error("Failed to open Mac binary '%s'", getEXEName().c_str()); + return typeList; +} - Common::SeekableReadStream *dataFork = _macBinary->getDataFork(); - _mainArchive = new RIFXArchive(); +Common::Array<uint16> Archive::getResourceIDList(uint32 type) const { + Common::Array<uint16> idList; - // First we need to detect PPC vs. 68k + if (!_types.contains(type)) + return idList; - uint32 tag = dataFork->readUint32BE(); - uint32 startOffset; + const ResourceMap &resMap = _types[type]; - if (SWAP_BYTES_32(tag) == MKTAG('P', 'J', '9', '3') || tag == MKTAG('P', 'J', '9', '5') || tag == MKTAG('P', 'J', '0', '0')) { - // PPC: The RIFX shares the data fork with the binary - startOffset = dataFork->readUint32BE(); - } else { - // 68k: The RIFX is the only thing in the data fork - startOffset = 0; - } + for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) + idList.push_back(it->_key); - if (!_mainArchive->openStream(dataFork, startOffset)) - error("Failed to load RIFX from Mac binary"); - } + return idList; } -void DirectorEngine::loadSharedCastsFrom(Common::String filename) { - Archive *shardcst = createArchive(); +uint32 Archive::convertTagToUppercase(uint32 tag) { + uint32 newTag = toupper(tag >> 24) << 24; + newTag |= toupper((tag >> 16) & 0xFF) << 16; + newTag |= toupper((tag >> 8) & 0xFF) << 8; - debugC(1, kDebugLoading, "Loading Shared cast '%s'", filename.c_str()); + return newTag | toupper(tag & 0xFF); +} - shardcst->openFile(filename); +// Mac Archive code - _sharedDIB = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; - _sharedSTXT = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; - _sharedSound = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; - _sharedBMP = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; +MacArchive::MacArchive() : Archive(), _resFork(0) { +} - Score *castScore = new Score(this, shardcst); +MacArchive::~MacArchive() { + delete _resFork; +} - castScore->loadConfig(*shardcst->getResource(MKTAG('V','W','C','F'), 1024)); - castScore->loadCastData(*shardcst->getResource(MKTAG('V','W','C','R'), 1024)); +void MacArchive::close() { + Archive::close(); + delete _resFork; + _resFork = 0; +} - _sharedCasts = &castScore->_casts; +bool MacArchive::openFile(const Common::String &fileName) { + close(); - Common::Array<uint16> dib = shardcst->getResourceIDList(MKTAG('D','I','B',' ')); - if (dib.size() != 0) { - debugC(3, kDebugLoading, "Loading %d DIBs", dib.size()); + _resFork = new Common::MacResManager(); - for (Common::Array<uint16>::iterator iterator = dib.begin(); iterator != dib.end(); ++iterator) { - debugC(3, kDebugLoading, "Shared DIB %d", *iterator); - _sharedDIB->setVal(*iterator, shardcst->getResource(MKTAG('D','I','B',' '), *iterator)); - } + if (!_resFork->open(fileName) || !_resFork->hasResFork()) { + close(); + return false; + } + + _fileName = _resFork->getBaseFileName(); + if (_fileName.hasSuffix(".bin")) { + for (int i = 0; i < 4; i++) + _fileName.deleteLastChar(); } - Common::Array<uint16> stxt = shardcst->getResourceIDList(MKTAG('S','T','X','T')); - if (stxt.size() != 0) { - debugC(3, kDebugLoading, "Loading %d STXTs", stxt.size()); + Common::MacResTagArray tagArray = _resFork->getResTagArray(); - for (Common::Array<uint16>::iterator iterator = stxt.begin(); iterator != stxt.end(); ++iterator) { - debugC(3, kDebugLoading, "Shared STXT %d", *iterator); - _sharedSTXT->setVal(*iterator, shardcst->getResource(MKTAG('S','T','X','T'), *iterator)); + for (uint32 i = 0; i < tagArray.size(); i++) { + ResourceMap &resMap = _types[tagArray[i]]; + Common::MacResIDArray idArray = _resFork->getResIDArray(tagArray[i]); + + for (uint32 j = 0; j < idArray.size(); j++) { + Resource &res = resMap[idArray[j]]; + + res.offset = res.size = 0; // unused + res.name = _resFork->getResName(tagArray[i], idArray[j]); + debug(3, "Found MacArchive resource '%s' %d: %s", tag2str(tagArray[i]), idArray[j], res.name.c_str()); } } - Common::Array<uint16> bmp = shardcst->getResourceIDList(MKTAG('B','I','T','D')); - if (bmp.size() != 0) { - debugC(3, kDebugLoading, "Loading %d BITDs", bmp.size()); - for (Common::Array<uint16>::iterator iterator = bmp.begin(); iterator != bmp.end(); ++iterator) { - _sharedBMP->setVal(*iterator, shardcst->getResource(MKTAG('B','I','T','D'), *iterator)); + return true; +} + +bool MacArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { + // TODO: Add support for this (v4 Windows games) + return false; +} + +Common::SeekableSubReadStreamEndian *MacArchive::getResource(uint32 tag, uint16 id) { + assert(_resFork); + Common::SeekableReadStream *stream = _resFork->getResource(tag, id); + return new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), true, DisposeAfterUse::NO); +} + +// RIFF Archive code + +bool RIFFArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { + close(); + + stream->seek(startOffset); + + if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'I', 'F', 'F')) + return false; + + stream->readUint32LE(); // size + + if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'M', 'M', 'P')) + return false; + + if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('C', 'F', 'T', 'C')) + return false; + + uint32 cftcSize = stream->readUint32LE(); + uint32 startPos = stream->pos(); + stream->readUint32LE(); // unknown (always 0?) + + while ((uint32)stream->pos() < startPos + cftcSize) { + uint32 tag = convertTagToUppercase(stream->readUint32BE()); + + uint32 size = stream->readUint32LE(); + uint32 id = stream->readUint32LE(); + uint32 offset = stream->readUint32LE(); + + if (tag == 0) + break; + + uint16 startResPos = stream->pos(); + stream->seek(offset + 12); + + Common::String name = ""; + byte nameSize = stream->readByte(); + + if (nameSize) { + for (uint8 i = 0; i < nameSize; i++) { + name += stream->readByte(); + } } + + stream->seek(startResPos); + + debug(3, "Found RIFF resource '%s' %d: %d @ 0x%08x", tag2str(tag), id, size, offset); + + ResourceMap &resMap = _types[tag]; + Resource &res = resMap[id]; + res.offset = offset; + res.size = size; + res.name = name; } - Common::Array<uint16> sound = shardcst->getResourceIDList(MKTAG('S','N','D',' ')); - if (stxt.size() != 0) { - debugC(3, kDebugLoading, "Loading %d SNDs", sound.size()); - for (Common::Array<uint16>::iterator iterator = sound.begin(); iterator != sound.end(); ++iterator) { - _sharedSound->setVal(*iterator, shardcst->getResource(MKTAG('S','N','D',' '), *iterator)); + _stream = stream; + return true; +} + +Common::SeekableSubReadStreamEndian *RIFFArchive::getResource(uint32 tag, uint16 id) { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const Resource &res = resMap[id]; + + // Adjust to skip the resource header + uint32 offset = res.offset + 12; + uint32 size = res.size - 4; + // Skip the Pascal string + _stream->seek(offset); + byte stringSize = _stream->readByte(); // 1 for this byte + + offset += stringSize + 1; + size -= stringSize + 1; + + // Align to nearest word boundary + if (offset & 1) { + offset++; + size--; + } + + return new Common::SeekableSubReadStreamEndian(_stream, offset, offset + size, true, DisposeAfterUse::NO); +} + +// RIFX Archive code + +bool RIFXArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { + close(); + + stream->seek(startOffset); + + uint32 headerTag = stream->readUint32BE(); + + if (headerTag == MKTAG('R', 'I', 'F', 'X')) + _isBigEndian = true; + else if (SWAP_BYTES_32(headerTag) == MKTAG('R', 'I', 'F', 'X')) + _isBigEndian = false; + else + return false; + + Common::SeekableSubReadStreamEndian subStream(stream, startOffset + 4, stream->size(), _isBigEndian, DisposeAfterUse::NO); + + subStream.readUint32(); // size + + uint32 rifxType = subStream.readUint32(); + + if (rifxType != MKTAG('M', 'V', '9', '3') && rifxType != MKTAG('A', 'P', 'P', 'L')) + return false; + + if (subStream.readUint32() != MKTAG('i', 'm', 'a', 'p')) + return false; + + subStream.readUint32(); // imap length + subStream.readUint32(); // unknown + uint32 mmapOffset = subStream.readUint32() - startOffset - 4; + uint32 version = subStream.readUint32(); // 0 for 4.0, 0x4c1 for 5.0, 0x4c7 for 6.0, 0x708 for 8.5, 0x742 for 10.0 + warning("RIFX: version: %x", version); + + subStream.seek(mmapOffset); + + if (subStream.readUint32() != MKTAG('m', 'm', 'a', 'p')) + return false; + + subStream.readUint32(); // mmap length + subStream.readUint16(); // unknown + subStream.readUint16(); // unknown + subStream.readUint32(); // resCount + empty entries + uint32 resCount = subStream.readUint32(); + subStream.skip(8); // all 0xFF + subStream.readUint32(); // unknown + + Common::Array<Resource> resources; + + // Need to look for these two resources + const Resource *keyRes = 0; + const Resource *casRes = 0; + + for (uint32 i = 0; i < resCount; i++) { + uint32 tag = subStream.readUint32(); + uint32 size = subStream.readUint32(); + uint32 offset = subStream.readUint32(); + uint16 flags = subStream.readUint16(); + uint16 unk1 = subStream.readUint16(); + uint32 unk2 = subStream.readUint32(); + + debug(3, "Found RIFX resource index %d: '%s', %d @ 0x%08x (%d), flags: %x unk1: %x unk2: %x", + i, tag2str(tag), size, offset, offset, flags, unk1, unk2); + + Resource res; + res.offset = offset; + res.size = size; + resources.push_back(res); + + // APPL is a special case; it has an embedded "normal" archive + if (rifxType == MKTAG('A', 'P', 'P', 'L') && tag == MKTAG('F', 'i', 'l', 'e')) + return openStream(stream, offset); + + // Looking for two types here + if (tag == MKTAG('K', 'E', 'Y', '*')) + keyRes = &resources[resources.size() - 1]; + else if (tag == MKTAG('C', 'A', 'S', '*')) + casRes = &resources[resources.size() - 1]; + } + + // We need to have found the 'File' resource already + if (rifxType == MKTAG('A', 'P', 'P', 'L')) { + warning("No 'File' resource present in APPL archive"); + return false; + } + + // A KEY* must be present + if (!keyRes) { + warning("No 'KEY*' resource present"); + return false; + } + + // Parse the CAS*, if present + if (casRes) { + Common::SeekableSubReadStreamEndian casStream(stream, casRes->offset + 8, casRes->offset + 8 + casRes->size, _isBigEndian, DisposeAfterUse::NO); + + uint casSize = casRes->size / 4; + + debugCN(2, kDebugLoading, "CAS*: %d [", casSize); + + for (uint i = 0; i < casSize; i++) { + uint32 index = casStream.readUint32(); + + const Resource &res = resources[index]; + _types[MKTAG('C', 'A', 'S', 't')][i + 1] = res; + + debugCN(2, kDebugLoading, "%d ", index); } + debugC(2, kDebugLoading, "]"); } + + // Parse the KEY* + Common::SeekableSubReadStreamEndian keyStream(stream, keyRes->offset + 8, keyRes->offset + 8 + keyRes->size, _isBigEndian, DisposeAfterUse::NO); + uint16 unk1 = keyStream.readUint16(); + uint16 unk2 = keyStream.readUint16(); + uint32 unk3 = keyStream.readUint32(); + uint32 keyCount = keyStream.readUint32(); + + debugC(2, kDebugLoading, "KEY*: unk1: %d unk2: %d unk3: %d keyCount: %d", unk1, unk2, unk3, keyCount); + + for (uint32 i = 0; i < keyCount; i++) { + uint32 index = keyStream.readUint32(); + uint32 id = keyStream.readUint32(); + uint32 resTag = keyStream.readUint32(); + + debugC(2, kDebugLoading, "KEY*: index: %d id: %d resTag: %s", index, id, tag2str(resTag)); + + const Resource &res = resources[index]; + debug(3, "Found RIFX resource: '%s' id: 0x%04x, %d @ 0x%08x (%d)", tag2str(resTag), id, res.size, res.offset, res.offset); + _types[resTag][id] = res; + } + + _stream = stream; + return true; +} + +Common::SeekableSubReadStreamEndian *RIFXArchive::getResource(uint32 tag, uint16 id) { + if (!_types.contains(tag)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const ResourceMap &resMap = _types[tag]; + + if (!resMap.contains(id)) + error("Archive does not contain '%s' %04x", tag2str(tag), id); + + const Resource &res = resMap[id]; + + uint32 offset = res.offset + 8; + uint32 size = res.size; + + return new Common::SeekableSubReadStreamEndian(_stream, offset, offset + size, true, DisposeAfterUse::NO); } + } // End of namespace Director diff --git a/engines/director/resource.h b/engines/director/archive.h index 1265908cba..93f4ce7ee9 100644 --- a/engines/director/resource.h +++ b/engines/director/archive.h @@ -20,8 +20,8 @@ * */ -#ifndef DIRECTOR_RESOURCE_H -#define DIRECTOR_RESOURCE_H +#ifndef DIRECTOR_ARCHIVE_H +#define DIRECTOR_ARCHIVE_H #include "common/file.h" #include "common/substream.h" @@ -102,6 +102,7 @@ public: ~RIFXArchive() {} bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0); + Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id); }; } // End of namespace Director diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp new file mode 100644 index 0000000000..667fc248ea --- /dev/null +++ b/engines/director/cast.cpp @@ -0,0 +1,136 @@ +/* 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 "director/director.h" +#include "director/cast.h" +#include "director/score.h" + +namespace Director { + +BitmapCast::BitmapCast(Common::ReadStreamEndian &stream, uint16 version) { + if (version < 4) { + flags = stream.readByte(); + someFlaggyThing = stream.readUint16(); + initialRect = Score::readRect(stream); + boundingRect = Score::readRect(stream); + regY = stream.readUint16(); + regX = stream.readUint16(); + unk1 = unk2 = 0; + + if (someFlaggyThing & 0x8000) { + unk1 = stream.readUint16(); + unk2 = stream.readUint16(); + } + } else { + initialRect = Score::readRect(stream); + boundingRect = Score::readRect(stream); + regX = stream.readUint16(); + regY = stream.readUint16(); + } + modified = 0; +} + +TextCast::TextCast(Common::ReadStreamEndian &stream, uint16 version) { + if (version < 4) { + flags1 = stream.readByte(); + borderSize = static_cast<SizeType>(stream.readByte()); + gutterSize = static_cast<SizeType>(stream.readByte()); + boxShadow = static_cast<SizeType>(stream.readByte()); + textType = static_cast<TextType>(stream.readByte()); + textAlign = static_cast<TextAlignType>(stream.readUint16()); + palinfo1 = stream.readUint16(); + palinfo2 = stream.readUint16(); + palinfo3 = stream.readUint16(); + + int t = stream.readUint32(); + assert(t == 0); // So far we saw only 0 here + + initialRect = Score::readRect(stream); + textShadow = static_cast<SizeType>(stream.readByte()); + byte flags = stream.readByte(); + if (flags & 0x1) + textFlags.push_back(kTextFlagEditable); + if (flags & 0x2) + textFlags.push_back(kTextFlagAutoTab); + if (flags & 0x4) + textFlags.push_back(kTextFlagDoNotWrap); + if (flags & 0xf8) + warning("Unproxessed text cast flags: %x", flags & 0xf8); + + // TODO: FIXME: guesswork + fontId = stream.readByte(); + fontSize = stream.readByte(); + } else { + initialRect = Score::readRect(stream); + boundingRect = Score::readRect(stream); + } + + modified = 0; +} + +ShapeCast::ShapeCast(Common::ReadStreamEndian &stream, uint16 version) { + if (version < 4) { + /*byte flags = */ stream.readByte(); + /*unk1 = */ stream.readByte(); + shapeType = static_cast<ShapeType>(stream.readByte()); + initialRect = Score::readRect(stream); + pattern = stream.readUint16BE(); + fgCol = stream.readByte(); + bgCol = stream.readByte(); + fillType = stream.readByte(); + lineThickness = stream.readByte(); + lineDirection = stream.readByte(); + } else { + initialRect = Score::readRect(stream); + boundingRect = Score::readRect(stream); + } + modified = 0; +} + +ButtonCast::ButtonCast(Common::ReadStreamEndian &stream, uint16 version) : TextCast(stream, version) { + if (version < 4) { + buttonType = static_cast<ButtonType>(stream.readUint16BE()); + } else { + initialRect = Score::readRect(stream); + boundingRect = Score::readRect(stream); + } + modified = 0; +} + +ScriptCast::ScriptCast(Common::ReadStreamEndian &stream, uint16 version) { + if (version < 4) { + error("Unhandled Script cast"); + } else { + initialRect = Score::readRect(stream); + boundingRect = Score::readRect(stream); + + id = stream.readUint32(); + + debugC(4, kDebugLoading, "CASt: Script id: %d", id); + + stream.readByte(); // There should be no more data + assert(stream.eos()); + } + modified = 0; +} + +} // End of namespace Director diff --git a/engines/director/cast.h b/engines/director/cast.h new file mode 100644 index 0000000000..69edf8ce89 --- /dev/null +++ b/engines/director/cast.h @@ -0,0 +1,162 @@ +/* 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. + * + */ + +#ifndef DIRECTOR_CAST_H +#define DIRECTOR_CAST_H + +#include "common/rect.h" +#include "common/substream.h" + +namespace Director { + +enum CastType { + kCastBitmap = 1, + kCastFilmLoop = 2, + kCastText = 3, + kCastPalette = 4, + kCastPicture = 5, + kCastSound = 6, + kCastButton = 7, + kCastShape = 8, + kCastMovie = 9, + kCastDigitalVideo = 10, + kCastScript = 11 +}; + +struct Cast { + CastType type; + Common::Rect initialRect; + Common::Rect boundingRect; + + byte modified; +}; + +struct BitmapCast : Cast { + BitmapCast(Common::ReadStreamEndian &stream, uint16 version = 2); + + uint16 regX; + uint16 regY; + uint8 flags; + uint16 someFlaggyThing; + uint16 unk1, unk2; +}; + +enum ShapeType { + kShapeRectangle, + kShapeRoundRect, + kShapeOval, + kShapeLine +}; + +struct ShapeCast : Cast { + ShapeCast(Common::ReadStreamEndian &stream, uint16 version = 2); + + ShapeType shapeType; + uint16 pattern; + byte fgCol; + byte bgCol; + byte fillType; + byte lineThickness; + byte lineDirection; +}; + +enum TextType { + kTextTypeAdjustToFit, + kTextTypeScrolling, + kTextTypeFixed +}; + +enum TextAlignType { + kTextAlignRight = -1, + kTextAlignLeft, + kTextAlignCenter +}; + +enum TextFlag { + kTextFlagEditable, + kTextFlagAutoTab, + kTextFlagDoNotWrap +}; + +enum SizeType { + kSizeNone, + kSizeSmallest, + kSizeSmall, + kSizeMedium, + kSizeLarge, + kSizeLargest +}; + +struct TextCast : Cast { + TextCast(Common::ReadStreamEndian &stream, uint16 version = 2); + + SizeType borderSize; + SizeType gutterSize; + SizeType boxShadow; + + byte flags1; + uint32 fontId; + uint16 fontSize; + TextType textType; + TextAlignType textAlign; + SizeType textShadow; + Common::Array<TextFlag> textFlags; + int16 palinfo1, palinfo2, palinfo3; +}; + +enum ButtonType { + kTypeButton, + kTypeCheckBox, + kTypeRadio +}; + +struct ButtonCast : TextCast { + ButtonCast(Common::ReadStreamEndian &stream, uint16 version = 2); + + ButtonType buttonType; +}; + +struct ScriptCast : Cast { + ScriptCast(Common::ReadStreamEndian &stream, uint16 version = 2); + + uint32 id; +}; + + + +struct CastInfo { + Common::String script; + Common::String name; + Common::String directory; + Common::String fileName; + Common::String type; +}; + +struct Label { + Common::String name; + uint16 number; + Label(Common::String name1, uint16 number1) { name = name1; number = number1; } +}; + +} // End of namespace Director + +#endif diff --git a/engines/director/director.cpp b/engines/director/director.cpp index 8c5e456781..32358e4b6e 100644 --- a/engines/director/director.cpp +++ b/engines/director/director.cpp @@ -27,7 +27,7 @@ #include "graphics/macgui/macwindowmanager.h" #include "director/director.h" -#include "director/resource.h" +#include "director/archive.h" #include "director/sound.h" #include "director/lingo/lingo.h" @@ -120,10 +120,13 @@ Common::Error DirectorEngine::run() { //testFont(); - _movies = scanMovies(ConfMan.get("path")); + if (getPlatform() == Common::kPlatformWindows) + _sharedCastFile = "SHARDCST.MMM"; + else + _sharedCastFile = "Shared Cast*"; + + loadSharedCastsFrom(_sharedCastFile); - if (!_sharedCastFile.empty()) - loadSharedCastsFrom(_sharedCastFile); loadMainArchive(); _currentScore = new Score(this, _mainArchive); diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp index 0ce7f2be9b..6464943d99 100644 --- a/engines/director/frame.cpp +++ b/engines/director/frame.cpp @@ -27,9 +27,10 @@ #include "image/bmp.h" #include "director/director.h" +#include "director/cast.h" #include "director/frame.h" #include "director/images.h" -#include "director/resource.h" +#include "director/archive.h" #include "director/score.h" #include "director/sprite.h" @@ -54,7 +55,7 @@ Frame::Frame(DirectorEngine *vm) { _palette = NULL; - _sprites.resize(CHANNEL_COUNT); + _sprites.resize(CHANNEL_COUNT + 1); for (uint16 i = 0; i < _sprites.size(); i++) { Sprite *sp = new Sprite(); @@ -80,9 +81,9 @@ Frame::Frame(const Frame &frame) { debugC(1, kDebugLoading, "Frame. action: %d transType: %d transDuration: %d", _actionId, _transType, _transDuration); - _sprites.resize(CHANNEL_COUNT); + _sprites.resize(CHANNEL_COUNT + 1); - for (uint16 i = 0; i < CHANNEL_COUNT; i++) { + for (uint16 i = 0; i < CHANNEL_COUNT + 1; i++) { _sprites[i] = new Sprite(*frame._sprites[i]); } } @@ -112,6 +113,105 @@ void Frame::readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offs } } +void Frame::readChannels(Common::ReadStreamEndian *stream) { + _actionId = stream->readByte(); + _soundType1 = stream->readByte(); // type: 0x17 for sounds (sound is cast id), 0x16 for MIDI (sound is cmd id) + uint8 transFlags = stream->readByte(); // 0x80 is whole stage (vs changed area), rest is duration in 1/4ths of a second + + if (transFlags & 0x80) + _transArea = 1; + else + _transArea = 0; + _transDuration = transFlags & 0x7f; + + _transChunkSize = stream->readByte(); + _tempo = stream->readByte(); + _transType = static_cast<TransitionType>(stream->readByte()); + _sound1 = stream->readUint16(); + if (_vm->getPlatform() == Common::kPlatformMacintosh) { + _sound2 = stream->readUint16(); + _soundType2 = stream->readByte(); + } else { + byte unk[3]; + stream->read(unk, 3); + warning("unk1: %x unk2: %x unk3: %x", unk[0], unk[1], unk[2]); + } + _skipFrameFlag = stream->readByte(); + _blend = stream->readByte(); + + if (_vm->getPlatform() != Common::kPlatformMacintosh) { + _sound2 = stream->readUint16(); + _soundType2 = stream->readByte(); + } + + uint16 palette = stream->readUint16(); + + if (palette) { + warning("STUB: Palette info"); + } + + debugC(kDebugLoading, 8, "%d %d %d %d %d %d %d %d %d %d %d", _actionId, _soundType1, _transDuration, _transChunkSize, _tempo, _transType, _sound1, _skipFrameFlag, _blend, _sound2, _soundType2); + + _palette = new PaletteInfo(); + _palette->firstColor = stream->readByte(); // for cycles. note: these start at 0x80 (for pal entry 0)! + _palette->lastColor = stream->readByte(); + _palette->flags = stream->readByte(); + _palette->speed = stream->readByte(); + _palette->frameCount = stream->readUint16(); + + _palette->cycleCount = stream->readUint16(); + + byte unk[11]; + stream->read(unk, 6); + + if (_vm->getPlatform() == Common::kPlatformMacintosh) { + if (_vm->getVersion() < 4) { + stream->read(unk, 3); + } else { + stream->read(unk, 11); + //Common::hexdump(unk, 11); + + if (_vm->getVersion() >= 5) { + stream->read(unk, 7); + //Common::hexdump(unk, 7); + } + } + } + + for (int i = 0; i < CHANNEL_COUNT; i++) { + Sprite &sprite = *_sprites[i + 1]; + + sprite._x1 = stream->readByte(); + sprite._enabled = (stream->readByte() != 0); + sprite._x2 = stream->readUint16(); + + sprite._flags = stream->readUint16(); + sprite._ink = static_cast<InkType>(sprite._flags & 0x3f); + + if (sprite._flags & 0x40) + sprite._trails = 1; + else + sprite._trails = 0; + + sprite._castId = stream->readUint16(); + sprite._startPoint.y = stream->readUint16(); + sprite._startPoint.x = stream->readUint16(); + sprite._height = stream->readUint16(); + sprite._width = stream->readUint16(); + + debugC(kDebugLoading, 8, "%03d(%d)[%x,%x,%04x,%d/%d/%d/%d]", sprite._castId, sprite._enabled, sprite._x1, sprite._x2, sprite._flags, sprite._startPoint.x, sprite._startPoint.y, sprite._width, sprite._height); + + if (_vm->getPlatform() == Common::kPlatformMacintosh && _vm->getVersion() >= 4) { + sprite._scriptId = stream->readUint16(); + sprite._flags2 = stream->readByte(); // 0x40 editable, 0x80 moveable + sprite._unk2 = stream->readByte(); + + if (_vm->getVersion() >= 5) + sprite._unk3 = stream->readUint32(); + } + } +} + void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 finishPosition = offset + size; @@ -179,6 +279,8 @@ void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 break; } } + + warning("%d %d %d %d %d %d %d %d %d %d %d", _actionId, _soundType1, _transDuration, _transChunkSize, _tempo, _transType, _sound1, _skipFrameFlag, _blend, _sound2, _soundType2); } void Frame::readPaletteInfo(Common::SeekableSubReadStreamEndian &stream) { @@ -198,11 +300,13 @@ void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offse uint16 finishPosition = fieldPosition + size; Sprite &sprite = *_sprites[spritePosition]; + int x1 = 0; + int x2 = 0; while (fieldPosition < finishPosition) { switch (fieldPosition) { case kSpritePositionUnk1: - /*byte x1 = */ stream.readByte(); + x1 = stream.readByte(); fieldPosition++; break; case kSpritePositionEnabled: @@ -210,7 +314,7 @@ void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offse fieldPosition++; break; case kSpritePositionUnk2: - /*byte x2 = */ stream.readUint16(); + x2 = stream.readUint16(); fieldPosition += 2; break; case kSpritePositionFlags: @@ -251,6 +355,8 @@ void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offse break; } } + warning("%03d(%d)[%x,%x,%04x,%d/%d/%d/%d]", sprite._castId, sprite._enabled, x1, x2, sprite._flags, sprite._startPoint.x, sprite._startPoint.y, sprite._width, sprite._height); + } void Frame::prepareFrame(Score *score) { diff --git a/engines/director/frame.h b/engines/director/frame.h index 68eb1e33bb..ce82fa527c 100644 --- a/engines/director/frame.h +++ b/engines/director/frame.h @@ -35,6 +35,10 @@ class Sprite; #define CHANNEL_COUNT 24 +enum { + kChannelDataSize = (25 * 50) +}; + enum TransitionType { kTransNone, kTransWipeRight, @@ -97,6 +101,7 @@ struct PaletteInfo { uint8 flags; uint8 speed; uint16 frameCount; + uint16 cycleCount; }; @@ -105,6 +110,7 @@ public: Frame(DirectorEngine *vm); Frame(const Frame &frame); ~Frame(); + void readChannels(Common::ReadStreamEndian *stream); void readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size); void prepareFrame(Score *score); uint16 getSpriteIDFromPos(Common::Point pos); @@ -123,7 +129,9 @@ private: void drawMatteSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect); void drawGhostSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect); void drawReverseSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect); + public: + byte _channelData[kChannelDataSize]; uint8 _actionId; uint8 _transDuration; uint8 _transArea; //1 - Whole Stage, 0 - Changing Area @@ -144,6 +152,6 @@ public: DirectorEngine *_vm; }; -} //End of namespace Director +} // End of namespace Director #endif diff --git a/engines/director/images.cpp b/engines/director/images.cpp index cd8223ae8e..0ec84af0d3 100644 --- a/engines/director/images.cpp +++ b/engines/director/images.cpp @@ -41,6 +41,7 @@ DIBDecoder::~DIBDecoder() { } void DIBDecoder::destroy() { + delete _surface; _surface = 0; delete[] _palette; diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp index 37a333ba10..dd5977673e 100644 --- a/engines/director/lingo/lingo-code.cpp +++ b/engines/director/lingo/lingo-code.cpp @@ -729,6 +729,8 @@ void Lingo::c_repeatwithcode(void) { if (!g_lingo->_returning) g_lingo->_pc = end; /* next stmt */ + + delete counter; } void Lingo::c_exitRepeat(void) { @@ -972,6 +974,8 @@ void Lingo::c_global() { s->global = true; g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str()); + + delete s; } void Lingo::c_instance() { diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp index a8633f1765..270746678b 100644 --- a/engines/director/lingo/lingo-the.cpp +++ b/engines/director/lingo/lingo-the.cpp @@ -21,6 +21,7 @@ */ #include "director/lingo/lingo.h" +#include "director/cast.h" #include "director/sprite.h" namespace Director { diff --git a/engines/director/module.mk b/engines/director/module.mk index 1ea361590a..38e3cfea4a 100644 --- a/engines/director/module.mk +++ b/engines/director/module.mk @@ -2,6 +2,7 @@ MODULE := engines/director MODULE_OBJS = \ archive.o \ + cast.o \ detection.o \ director.o \ frame.o \ diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp index f952346d16..9799510c6b 100644 --- a/engines/director/movie.cpp +++ b/engines/director/movie.cpp @@ -79,4 +79,4 @@ Movie::~Movie() { delete _currentVideo; } -} //End of namespace Director +} // End of namespace Director diff --git a/engines/director/movie.h b/engines/director/movie.h index 888262a2d1..e4729a62c3 100644 --- a/engines/director/movie.h +++ b/engines/director/movie.h @@ -60,6 +60,6 @@ private: Video::VideoDecoder *_currentVideo; DirectorEngine *_vm; }; -} //End of namespace Director +} // End of namespace Director #endif diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp index 8efec141d1..54ab2198e1 100644 --- a/engines/director/resource.cpp +++ b/engines/director/resource.cpp @@ -20,430 +20,243 @@ * */ -#include "director/resource.h" - -#include "common/debug.h" #include "common/macresman.h" -namespace Director { - -// Base Archive code - -Archive::Archive() { - _stream = 0; - _isBigEndian = true; -} - -Archive::~Archive() { - close(); -} - -bool Archive::openFile(const Common::String &fileName) { - Common::File *file = new Common::File(); +#include "director/director.h" +#include "director/archive.h" +#include "director/lingo/lingo.h" - if (!file->open(fileName)) { - delete file; - return false; - } +namespace Director { - if (!openStream(file)) { - close(); - return false; +Archive *DirectorEngine::createArchive() { + if (getPlatform() == Common::kPlatformMacintosh) { + if (getVersion() < 4) + return new MacArchive(); + else + return new RIFXArchive(); + } else { + return new RIFFArchive(); } - - _fileName = fileName; - - return true; -} - -void Archive::close() { - _types.clear(); - - if (_stream) - delete _stream; - - _stream = 0; -} - -bool Archive::hasResource(uint32 tag, uint16 id) const { - if (!_types.contains(tag)) - return false; - - return _types[tag].contains(id); -} - -bool Archive::hasResource(uint32 tag, const Common::String &resName) const { - if (!_types.contains(tag) || resName.empty()) - return false; - - const ResourceMap &resMap = _types[tag]; - - for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) - if (it->_value.name.matchString(resName)) - return true; - - return false; } -Common::SeekableSubReadStreamEndian *Archive::getResource(uint32 tag, uint16 id) { - if (!_types.contains(tag)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); - - const ResourceMap &resMap = _types[tag]; - - if (!resMap.contains(id)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); - - const Resource &res = resMap[id]; - - return new Common::SeekableSubReadStreamEndian(_stream, res.offset, res.offset + res.size, _isBigEndian, DisposeAfterUse::NO); -} - -uint32 Archive::getOffset(uint32 tag, uint16 id) const { - if (!_types.contains(tag)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); - - const ResourceMap &resMap = _types[tag]; - - if (!resMap.contains(id)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); - - return resMap[id].offset; -} - -uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const { - if (!_types.contains(tag) || resName.empty()) - return 0xFFFF; - - const ResourceMap &resMap = _types[tag]; - - for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) - if (it->_value.name.matchString(resName)) - return it->_key; - - return 0xFFFF; +void DirectorEngine::loadMainArchive() { + if (getPlatform() == Common::kPlatformWindows) + loadEXE(); + else + loadMac(); } -Common::String Archive::getName(uint32 tag, uint16 id) const { - if (!_types.contains(tag)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); - - const ResourceMap &resMap = _types[tag]; - - if (!resMap.contains(id)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); - - return resMap[id].name; +void DirectorEngine::cleanupMainArchive() { + delete _mainArchive; + delete _macBinary; } -Common::Array<uint32> Archive::getResourceTypeList() const { - Common::Array<uint32> typeList; - - for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++) - typeList.push_back(it->_key); - - return typeList; +void DirectorEngine::loadEXE() { + Common::SeekableReadStream *exeStream = SearchMan.createReadStreamForMember(getEXEName()); + if (!exeStream) + error("Failed to open EXE '%s'", getEXEName().c_str()); + + _lingo->processEvent(kEventStart, 0); + + exeStream->seek(-4, SEEK_END); + exeStream->seek(exeStream->readUint32LE()); + + switch (getVersion()) { + case 3: + loadEXEv3(exeStream); + break; + case 4: + loadEXEv4(exeStream); + break; + case 5: + loadEXEv5(exeStream); + break; + case 7: + loadEXEv7(exeStream); + break; + default: + error("Unhandled Windows EXE version %d", getVersion()); + } } -Common::Array<uint16> Archive::getResourceIDList(uint32 type) const { - Common::Array<uint16> idList; +void DirectorEngine::loadEXEv3(Common::SeekableReadStream *stream) { + uint16 entryCount = stream->readUint16LE(); + if (entryCount != 1) + error("Unhandled multiple entry v3 EXE"); - if (!_types.contains(type)) - return idList; + stream->skip(5); // unknown - const ResourceMap &resMap = _types[type]; + stream->readUint32LE(); // Main MMM size + Common::String mmmFileName = readPascalString(*stream); + Common::String directoryName = readPascalString(*stream); - for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++) - idList.push_back(it->_key); + debugC(1, kDebugLoading, "Main MMM: '%s'", mmmFileName.c_str()); + debugC(1, kDebugLoading, "Directory Name: '%s'", directoryName.c_str()); - return idList; -} + _mainArchive = new RIFFArchive(); -uint32 Archive::convertTagToUppercase(uint32 tag) { - uint32 newTag = toupper(tag >> 24) << 24; - newTag |= toupper((tag >> 16) & 0xFF) << 16; - newTag |= toupper((tag >> 8) & 0xFF) << 8; + if (!_mainArchive->openFile(mmmFileName)) + error("Could not open '%s'", mmmFileName.c_str()); - return newTag | toupper(tag & 0xFF); + delete stream; } -// Mac Archive code +void DirectorEngine::loadEXEv4(Common::SeekableReadStream *stream) { + if (stream->readUint32BE() != MKTAG('P', 'J', '9', '3')) + error("Invalid projector tag found in v4 EXE"); -MacArchive::MacArchive() : Archive(), _resFork(0) { -} + uint32 rifxOffset = stream->readUint32LE(); + /* uint32 fontMapOffset = */ stream->readUint32LE(); + /* uint32 resourceForkOffset1 = */ stream->readUint32LE(); + /* uint32 resourceForkOffset2 = */ stream->readUint32LE(); + stream->readUint32LE(); // graphics DLL offset + stream->readUint32LE(); // sound DLL offset + /* uint32 rifxOffsetAlt = */ stream->readUint32LE(); // equivalent to rifxOffset -MacArchive::~MacArchive() { - delete _resFork; + loadEXERIFX(stream, rifxOffset); } -void MacArchive::close() { - Archive::close(); - delete _resFork; - _resFork = 0; +void DirectorEngine::loadEXEv5(Common::SeekableReadStream *stream) { + if (stream->readUint32LE() != MKTAG('P', 'J', '9', '5')) + error("Invalid projector tag found in v5 EXE"); + + uint32 rifxOffset = stream->readUint32LE(); + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + /* uint16 screenWidth = */ stream->readUint16LE(); + /* uint16 screenHeight = */ stream->readUint16LE(); + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + /* uint32 fontMapOffset = */ stream->readUint32LE(); + + loadEXERIFX(stream, rifxOffset); } -bool MacArchive::openFile(const Common::String &fileName) { - close(); +void DirectorEngine::loadEXEv7(Common::SeekableReadStream *stream) { + if (stream->readUint32LE() != MKTAG('P', 'J', '0', '0')) + error("Invalid projector tag found in v7 EXE"); - _resFork = new Common::MacResManager(); + uint32 rifxOffset = stream->readUint32LE(); + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // some DLL offset - if (!_resFork->open(fileName) || !_resFork->hasResFork()) { - close(); - return false; - } - - _fileName = _resFork->getBaseFileName(); - if (_fileName.hasSuffix(".bin")) { - for (int i = 0; i < 4; i++) - _fileName.deleteLastChar(); - } - - Common::MacResTagArray tagArray = _resFork->getResTagArray(); - - for (uint32 i = 0; i < tagArray.size(); i++) { - ResourceMap &resMap = _types[tagArray[i]]; - Common::MacResIDArray idArray = _resFork->getResIDArray(tagArray[i]); - - for (uint32 j = 0; j < idArray.size(); j++) { - Resource &res = resMap[idArray[j]]; - - res.offset = res.size = 0; // unused - res.name = _resFork->getResName(tagArray[i], idArray[j]); - debug(3, "Found MacArchive resource '%s' %d: %s", tag2str(tagArray[i]), idArray[j], res.name.c_str()); - } - } - - return true; + loadEXERIFX(stream, rifxOffset); } -bool MacArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { - // TODO: Add support for this (v4 Windows games) - return false; -} +void DirectorEngine::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) { + _mainArchive = new RIFXArchive(); -Common::SeekableSubReadStreamEndian *MacArchive::getResource(uint32 tag, uint16 id) { - assert(_resFork); - Common::SeekableReadStream *stream = _resFork->getResource(tag, id); - return new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), true, DisposeAfterUse::NO); + if (!_mainArchive->openStream(stream, offset)) + error("Failed to load RIFX from EXE"); } -// RIFF Archive code - -bool RIFFArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { - close(); +void DirectorEngine::loadMac() { + if (getVersion() < 4) { + // The data is part of the resource fork of the executable + _mainArchive = new MacArchive(); - stream->seek(startOffset); + if (!_mainArchive->openFile(getEXEName())) + error("Failed to open Mac binary '%s'", getEXEName().c_str()); + } else { + // The RIFX is located in the data fork of the executable + _macBinary = new Common::MacResManager(); - if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'I', 'F', 'F')) - return false; + if (!_macBinary->open(getEXEName()) || !_macBinary->hasDataFork()) + error("Failed to open Mac binary '%s'", getEXEName().c_str()); - stream->readUint32LE(); // size + Common::SeekableReadStream *dataFork = _macBinary->getDataFork(); + _mainArchive = new RIFXArchive(); - if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'M', 'M', 'P')) - return false; + // First we need to detect PPC vs. 68k - if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('C', 'F', 'T', 'C')) - return false; + uint32 tag = dataFork->readUint32BE(); + uint32 startOffset; - uint32 cftcSize = stream->readUint32LE(); - uint32 startPos = stream->pos(); - stream->readUint32LE(); // unknown (always 0?) - - while ((uint32)stream->pos() < startPos + cftcSize) { - uint32 tag = convertTagToUppercase(stream->readUint32BE()); - - uint32 size = stream->readUint32LE(); - uint32 id = stream->readUint32LE(); - uint32 offset = stream->readUint32LE(); - - if (tag == 0) - break; - - uint16 startResPos = stream->pos(); - stream->seek(offset + 12); - - Common::String name = ""; - byte nameSize = stream->readByte(); - - if (nameSize) { - for (uint8 i = 0; i < nameSize; i++) { - name += stream->readByte(); - } + if (SWAP_BYTES_32(tag) == MKTAG('P', 'J', '9', '3') || tag == MKTAG('P', 'J', '9', '5') || tag == MKTAG('P', 'J', '0', '0')) { + // PPC: The RIFX shares the data fork with the binary + startOffset = dataFork->readUint32BE(); + } else { + // 68k: The RIFX is the only thing in the data fork + startOffset = 0; } - stream->seek(startResPos); - - debug(3, "Found RIFF resource '%s' %d: %d @ 0x%08x", tag2str(tag), id, size, offset); - - ResourceMap &resMap = _types[tag]; - Resource &res = resMap[id]; - res.offset = offset; - res.size = size; - res.name = name; + if (!_mainArchive->openStream(dataFork, startOffset)) + error("Failed to load RIFX from Mac binary"); } - - _stream = stream; - return true; } -Common::SeekableSubReadStreamEndian *RIFFArchive::getResource(uint32 tag, uint16 id) { - if (!_types.contains(tag)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); +void DirectorEngine::loadSharedCastsFrom(Common::String filename) { + Archive *shardcst = createArchive(); - const ResourceMap &resMap = _types[tag]; + debugC(1, kDebugLoading, "Loading Shared cast '%s'", filename.c_str()); - if (!resMap.contains(id)) - error("Archive does not contain '%s' %04x", tag2str(tag), id); + if (!shardcst->openFile(filename)) { + warning("No shared cast %s", filename.c_str()); - const Resource &res = resMap[id]; + _sharedCasts = new Common::HashMap<int, Cast *>; - // Adjust to skip the resource header - uint32 offset = res.offset + 12; - uint32 size = res.size - 4; - // Skip the Pascal string - _stream->seek(offset); - byte stringSize = _stream->readByte(); // 1 for this byte - - offset += stringSize + 1; - size -= stringSize + 1; - - // Align to nearest word boundary - if (offset & 1) { - offset++; - size--; + return; } - return new Common::SeekableSubReadStreamEndian(_stream, offset, offset + size, true, DisposeAfterUse::NO); -} + _sharedDIB = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; + _sharedSTXT = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; + _sharedSound = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; + _sharedBMP = new Common::HashMap<int, Common::SeekableSubReadStreamEndian *>; -// RIFX Archive code + Score *castScore = new Score(this, shardcst); -bool RIFXArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) { - close(); + castScore->loadConfig(*shardcst->getResource(MKTAG('V','W','C','F'), 1024)); - stream->seek(startOffset); + if (getVersion() < 4) + castScore->loadCastDataVWCR(*shardcst->getResource(MKTAG('V','W','C','R'), 1024)); - uint32 headerTag = stream->readUint32BE(); - - if (headerTag == MKTAG('R', 'I', 'F', 'X')) - _isBigEndian = true; - else if (SWAP_BYTES_32(headerTag) == MKTAG('R', 'I', 'F', 'X')) - _isBigEndian = false; - else - return false; - - Common::SeekableSubReadStreamEndian subStream(stream, startOffset + 4, stream->size(), _isBigEndian, DisposeAfterUse::NO); - - subStream.readUint32(); // size - - uint32 rifxType = subStream.readUint32(); - - if (rifxType != MKTAG('M', 'V', '9', '3') && rifxType != MKTAG('A', 'P', 'P', 'L')) - return false; - - if (subStream.readUint32() != MKTAG('i', 'm', 'a', 'p')) - return false; - - subStream.readUint32(); // imap length - subStream.readUint32(); // unknown - uint32 mmapOffset = subStream.readUint32() - startOffset - 4; - - subStream.seek(mmapOffset); - - if (subStream.readUint32() != MKTAG('m', 'm', 'a', 'p')) - return false; - - subStream.readUint32(); // mmap length - subStream.readUint16(); // unknown - subStream.readUint16(); // unknown - subStream.readUint32(); // resCount + empty entries - uint32 resCount = subStream.readUint32(); - subStream.skip(8); // all 0xFF - subStream.readUint32(); // unknown - - Common::Array<Resource> resources; - - // Need to look for these two resources - const Resource *keyRes = 0; - const Resource *casRes = 0; - - for (uint32 i = 0; i < resCount; i++) { - uint32 tag = subStream.readUint32(); - uint32 size = subStream.readUint32(); - uint32 offset = subStream.readUint32(); - /*uint16 flags = */ subStream.readUint16(); - /*uint16 unk1 = */ subStream.readUint16(); - /*uint32 unk2 = */ subStream.readUint32(); - - debug(3, "Found RIFX resource index %d: '%s', %d @ 0x%08x", i, tag2str(tag), size, offset); + Common::Array<uint16> cast = shardcst->getResourceIDList(MKTAG('C','A','S','t')); + if (cast.size() > 0) { + for (Common::Array<uint16>::iterator iterator = cast.begin(); iterator != cast.end(); ++iterator) + castScore->loadCastData(*shardcst->getResource(MKTAG('C','A','S','t'), *iterator), *iterator); + } - Resource res; - res.offset = offset; - res.size = size; - resources.push_back(res); + _sharedCasts = &castScore->_casts; - // APPL is a special case; it has an embedded "normal" archive - if (rifxType == MKTAG('A', 'P', 'P', 'L') && tag == MKTAG('F', 'i', 'l', 'e')) - return openStream(stream, offset); + Common::Array<uint16> dib = shardcst->getResourceIDList(MKTAG('D','I','B',' ')); + if (dib.size() != 0) { + debugC(3, kDebugLoading, "Loading %d DIBs", dib.size()); - // Looking for two types here - if (tag == MKTAG('K', 'E', 'Y', '*')) - keyRes = &resources[resources.size() - 1]; - else if (tag == MKTAG('C', 'A', 'S', '*')) - casRes = &resources[resources.size() - 1]; + for (Common::Array<uint16>::iterator iterator = dib.begin(); iterator != dib.end(); ++iterator) { + debugC(3, kDebugLoading, "Shared DIB %d", *iterator); + _sharedDIB->setVal(*iterator, shardcst->getResource(MKTAG('D','I','B',' '), *iterator)); + } } - // We need to have found the 'File' resource already - if (rifxType == MKTAG('A', 'P', 'P', 'L')) { - warning("No 'File' resource present in APPL archive"); - return false; - } + Common::Array<uint16> stxt = shardcst->getResourceIDList(MKTAG('S','T','X','T')); + if (stxt.size() != 0) { + debugC(3, kDebugLoading, "Loading %d STXTs", stxt.size()); - // A KEY* must be present - if (!keyRes) { - warning("No 'KEY*' resource present"); - return false; + for (Common::Array<uint16>::iterator iterator = stxt.begin(); iterator != stxt.end(); ++iterator) { + debugC(3, kDebugLoading, "Shared STXT %d", *iterator); + _sharedSTXT->setVal(*iterator, shardcst->getResource(MKTAG('S','T','X','T'), *iterator)); + } } - // Parse the CAS*, if present - Common::Array<uint32> casEntries; - if (casRes) { - Common::SeekableSubReadStreamEndian casStream(stream, casRes->offset + 8, casRes->offset + 8 + casRes->size, _isBigEndian, DisposeAfterUse::NO); - casEntries.resize(casRes->size / 4); - - for (uint32 i = 0; i < casEntries.size(); i++) - casEntries[i] = casStream.readUint32(); + Common::Array<uint16> bmp = shardcst->getResourceIDList(MKTAG('B','I','T','D')); + if (bmp.size() != 0) { + debugC(3, kDebugLoading, "Loading %d BITDs", bmp.size()); + for (Common::Array<uint16>::iterator iterator = bmp.begin(); iterator != bmp.end(); ++iterator) { + _sharedBMP->setVal(*iterator, shardcst->getResource(MKTAG('B','I','T','D'), *iterator)); + } } - // Parse the KEY* - Common::SeekableSubReadStreamEndian keyStream(stream, keyRes->offset + 8, keyRes->offset + 8 + keyRes->size, _isBigEndian, DisposeAfterUse::NO); - /*uint16 unk1 = */ keyStream.readUint16(); - /*uint16 unk2 = */ keyStream.readUint16(); - /*uint32 unk3 = */ keyStream.readUint32(); - uint32 keyCount = keyStream.readUint32(); - - for (uint32 i = 0; i < keyCount; i++) { - uint32 index = keyStream.readUint32(); - uint32 id = keyStream.readUint32(); - uint32 resTag = keyStream.readUint32(); - - // Handle CAS*/CASt nonsense - if (resTag == MKTAG('C', 'A', 'S', 't')) { - for (uint32 j = 0; j < casEntries.size(); j++) { - if (casEntries[j] == index) { - id += j + 1; - break; - } - } + Common::Array<uint16> sound = shardcst->getResourceIDList(MKTAG('S','N','D',' ')); + if (stxt.size() != 0) { + debugC(3, kDebugLoading, "Loading %d SNDs", sound.size()); + for (Common::Array<uint16>::iterator iterator = sound.begin(); iterator != sound.end(); ++iterator) { + _sharedSound->setVal(*iterator, shardcst->getResource(MKTAG('S','N','D',' '), *iterator)); } - - const Resource &res = resources[index]; - debug(3, "Found RIFX resource: '%s' 0x%04x, %d @ 0x%08x", tag2str(resTag), id, res.size, res.offset); - _types[resTag][id] = res; } - - _stream = stream; - return true; } } // End of namespace Director diff --git a/engines/director/score.cpp b/engines/director/score.cpp index 5c594c20fc..1c78745330 100644 --- a/engines/director/score.cpp +++ b/engines/director/score.cpp @@ -23,6 +23,7 @@ #include "common/system.h" #include "common/config-manager.h" #include "common/events.h" +#include "common/memstream.h" #include "engines/util.h" #include "graphics/font.h" @@ -30,9 +31,10 @@ #include "graphics/macgui/macfontmanager.h" #include "graphics/macgui/macwindowmanager.h" +#include "director/cast.h" #include "director/score.h" #include "director/frame.h" -#include "director/resource.h" +#include "director/archive.h" #include "director/sound.h" #include "director/sprite.h" #include "director/lingo/lingo.h" @@ -191,7 +193,7 @@ void Score::loadArchive() { if (_vm->getVersion() < 4) { assert(_movieArchive->hasResource(MKTAG('V','W','C','R'), 1024)); - loadCastData(*_movieArchive->getResource(MKTAG('V','W','C','R'), 1024)); + loadCastDataVWCR(*_movieArchive->getResource(MKTAG('V','W','C','R'), 1024)); } if (_movieArchive->hasResource(MKTAG('V','W','A','C'), 1024)) { @@ -208,12 +210,16 @@ void Score::loadArchive() { Common::Array<uint16> vwci = _movieArchive->getResourceIDList(MKTAG('V','W','C','I')); if (vwci.size() > 0) { - Common::Array<uint16>::iterator iterator; - - for (iterator = vwci.begin(); iterator != vwci.end(); ++iterator) + for (Common::Array<uint16>::iterator iterator = vwci.begin(); iterator != vwci.end(); ++iterator) loadCastInfo(*_movieArchive->getResource(MKTAG('V','W','C','I'), *iterator), *iterator); } + Common::Array<uint16> cast = _movieArchive->getResourceIDList(MKTAG('C','A','S','t')); + if (cast.size() > 0) { + for (Common::Array<uint16>::iterator iterator = cast.begin(); iterator != cast.end(); ++iterator) + loadCastData(*_movieArchive->getResource(MKTAG('C','A','S','t'), *iterator), *iterator); + } + Common::Array<uint16> stxt = _movieArchive->getResourceIDList(MKTAG('S','T','X','T')); if (stxt.size() > 0) { loadScriptText(*_movieArchive->getResource(MKTAG('S','T','X','T'), *stxt.begin())); @@ -262,10 +268,16 @@ void Score::loadFrames(Common::SeekableSubReadStreamEndian &stream) { size -= 4; if (_vm->getVersion() > 3) { - stream.skip(16); + uint32 unk1 = stream.readUint32(); + uint32 unk2 = stream.readUint32(); + uint16 unk3 = stream.readUint16(); + uint16 unk4 = stream.readUint16(); + uint16 unk5 = stream.readUint16(); + uint16 unk6 = stream.readUint16(); + size -= 16; - warning("STUB: Score::loadFrames. Skipping initial bytes"); - //Unknown, some bytes - constant (refer to contuinity). + warning("STUB: Score::loadFrames. unk1: %x unk2: %x unk3: %x unk4: %x unk5: %x unk6: %x", unk1, unk2, unk3, unk4, unk5, unk6); + // Unknown, some bytes - constant (refer to contuinity). } uint16 channelSize; @@ -274,11 +286,20 @@ void Score::loadFrames(Common::SeekableSubReadStreamEndian &stream) { Frame *initial = new Frame(_vm); _frames.push_back(initial); + // This is a representation of the channelData. It gets overridden + // partically by channels, hence we keep it and read the score from left to right + // + // TODO Merge it with shared cast + byte channelData[kChannelDataSize]; + memset(channelData, 0, kChannelDataSize); + while (size != 0) { uint16 frameSize = stream.readUint16(); + debugC(kDebugLoading, 8, "++++ score frame %d (frameSize %d) size %d", _frames.size(), frameSize, size); size -= frameSize; frameSize -= 2; - Frame *frame = new Frame(*_frames.back()); + + Frame *frame = new Frame(_vm); while (frameSize != 0) { if (_vm->getVersion() < 4) { @@ -286,17 +307,25 @@ void Score::loadFrames(Common::SeekableSubReadStreamEndian &stream) { channelOffset = stream.readByte() * 2; frameSize -= channelSize + 2; } else { - channelSize = stream.readByte(); - channelOffset = stream.readByte(); + channelSize = stream.readUint16(); + channelOffset = stream.readUint16(); frameSize -= channelSize + 4; } - frame->readChannel(stream, channelOffset, channelSize); + + assert(channelOffset + channelSize < kChannelDataSize); + + stream.read(&channelData[channelOffset], channelSize); } + Common::MemoryReadStreamEndian *str = new Common::MemoryReadStreamEndian(channelData, ARRAYSIZE(channelData), stream.isBE()); + frame->readChannels(str); + + delete str; + _frames.push_back(frame); } - //remove initial frame + // Remove initial frame _frames.remove_at(0); } @@ -319,8 +348,8 @@ void Score::readVersion(uint32 rid) { debug("Version: %d.%d", _versionMajor, _versionMinor); } -void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream) { - debugC(1, kDebugLoading, "Score::loadCastData(). start: %d, end: %d", _castArrayStart, _castArrayEnd); +void Score::loadCastDataVWCR(Common::SeekableSubReadStreamEndian &stream) { + debugC(1, kDebugLoading, "Score::loadCastDataVWCR(). start: %d, end: %d", _castArrayStart, _castArrayEnd); for (uint16 id = _castArrayStart; id <= _castArrayEnd; id++) { byte size = stream.readByte(); @@ -347,13 +376,13 @@ void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream) { _casts[id]->type = kCastButton; break; default: - warning("Unhandled cast type: %d", castType); + warning("Score::loadCastDataVWCR(): Unhandled cast type: %d", castType); stream.skip(size - 1); break; } } - //Set cast pointers to sprites + // Set cast pointers to sprites for (uint16 i = 0; i < _frames.size(); i++) { for (uint16 j = 0; j < _frames[i]->_sprites.size(); j++) { byte castId = _frames[i]->_sprites[j]->_castId; @@ -364,6 +393,116 @@ void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream) { } } +void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream, uint16 id) { + // d4+ variant + if (stream.size() == 0) + return; + + if (stream.size() < 26) { + warning("CAST data id %d is too small", id); + return; + } + + debugC(3, kDebugLoading, "CASt: id: %d", id); + + if (debugChannelSet(5, kDebugLoading)) + stream.hexdump(stream.size()); + + uint32 size1, size2, size3, castType; + byte unk1 = 0, unk2 = 0, unk3 = 0; + + if (_vm->getVersion() < 5) { + size1 = stream.readUint16(); + size2 = stream.readUint32(); + size3 = 0; + castType = stream.readByte(); + unk1 = stream.readByte(); + unk2 = stream.readByte(); + unk3 = stream.readByte(); + } else { + // FIXME: only the cast type and the strings are good + castType = stream.readUint32(); + size2 = stream.readUint32(); + size3 = stream.readUint32(); + size1 = stream.readUint32(); + assert(size1 == 0x14); + size1 = 0; + } + + debugC(3, kDebugLoading, "CASt: id: %d type: %x size1: %d size2: %d (%x) size3: %d unk1: %d unk2: %d unk3: %d", + id, castType, size1, size2, size2, size3, unk1, unk2, unk3); + + byte *data = (byte *)calloc(size1 + 16, 1); + stream.read(data, size1 + 16); + + Common::MemoryReadStreamEndian castStream(data, size1 + 16, stream.isBE()); + + switch (castType) { + case kCastBitmap: + warning("CASt: Bitmap"); + Common::hexdump(data, size1 + 16); + _casts[id] = new BitmapCast(castStream, _vm->getVersion()); + _casts[id]->type = kCastBitmap; + break; + case kCastText: + warning("CASt: Text"); + Common::hexdump(data, size1 + 16); + _casts[id] = new TextCast(castStream, _vm->getVersion()); + _casts[id]->type = kCastText; + break; + case kCastShape: + warning("CASt: Shape"); + Common::hexdump(data, size1 + 16); + + _casts[id] = new ShapeCast(castStream, _vm->getVersion()); + _casts[id]->type = kCastShape; + break; + case kCastButton: + warning("CASt: Button"); + Common::hexdump(data, size1 + 16); + + _casts[id] = new ButtonCast(castStream, _vm->getVersion()); + _casts[id]->type = kCastButton; + break; + case kCastScript: + _casts[id] = new ScriptCast(castStream, _vm->getVersion()); + _casts[id]->type = kCastScript; + break; + default: + warning("Score::loadCastData(): Unhandled cast type: %d", castType); + break; + } + + free(data); + + if (size2) { + uint32 entryType = 0; + Common::Array<Common::String> castStrings = loadStrings(stream, entryType, false); + + debugCN(4, kDebugLoading, "str(%d): '", castStrings.size()); + + for (uint i = 0; i < castStrings.size(); i++) { + debugCN(4, kDebugLoading, "%s'", castStrings[i].c_str()); + if (i != castStrings.size() - 1) + debugCN(4, kDebugLoading, ", '"); + } + debugC(4, kDebugLoading, "'"); + + CastInfo *ci = new CastInfo(); + + ci->script = castStrings[0]; + ci->name = castStrings[1]; + ci->directory = castStrings[2]; + ci->fileName = castStrings[3]; + ci->type = castStrings[4]; + + _castsInfo[id] = ci; + } + + if (size3) + warning("size3: %x", size3); +} + void Score::loadLabels(Common::SeekableSubReadStreamEndian &stream) { _labels = new Common::SortedArray<Label *>(compareLabels); uint16 count = stream.readUint16() + 1; @@ -379,9 +518,11 @@ void Score::loadLabels(Common::SeekableSubReadStreamEndian &stream) { stream.seek(stringPos); Common::String label; + for (uint16 j = stringPos; j < nextStringPos; j++) { label += stream.readByte(); } + _labels->insert(new Label(label, frame)); stream.seek(streamPos); @@ -405,7 +546,7 @@ void Score::loadActions(Common::SeekableSubReadStreamEndian &stream) { uint16 offset = count * 4 + 2; byte id = stream.readByte(); - /*byte subId = */ stream.readByte(); //I couldn't find how it used in continuity (except print). Frame actionId = 1 byte. + /*byte subId = */ stream.readByte(); // I couldn't find how it used in continuity (except print). Frame actionId = 1 byte. uint16 stringPos = stream.readUint16() + offset; for (uint16 i = 0; i < count; i++) { @@ -647,28 +788,31 @@ Common::Array<Common::String> Score::loadStrings(Common::SeekableSubReadStreamEn stream.seek(offset); } - uint16 count = stream.readUint16(); - offset += (count + 1) * 4 + 2; // positions info + uint16 count - uint32 startPos = stream.readUint32() + offset; + uint16 count = stream.readUint16() + 1; - for (uint16 i = 0; i < count; i++) { - Common::String entryString; - uint32 nextPos = stream.readUint32() + offset; - uint32 streamPos = stream.pos(); + uint32 *entries = (uint32 *)calloc(count, sizeof(uint32)); - stream.seek(startPos); + for (uint i = 0; i < count; i++) + entries[i] = stream.readUint32(); - while (startPos != nextPos) { - entryString += stream.readByte(); - ++startPos; - } + byte *data = (byte *)malloc(entries[count - 1]); + stream.read(data, entries[count - 1]); - strings.push_back(entryString); + for (uint i = 0; i < count - 1; i++) { + Common::String entryString; - stream.seek(streamPos); - startPos = nextPos; + for (uint j = entries[i]; j < entries[i + 1]; j++) + if (data[j] == '\r') + entryString += '\n'; + else + entryString += data[j]; + + strings.push_back(entryString); } + free(data); + free(entries); + return strings; } @@ -697,77 +841,14 @@ void Score::loadFontMap(Common::SeekableSubReadStreamEndian &stream) { } } -BitmapCast::BitmapCast(Common::SeekableSubReadStreamEndian &stream) { - flags = stream.readByte(); - someFlaggyThing = stream.readUint16(); - initialRect = Score::readRect(stream); - boundingRect = Score::readRect(stream); - regY = stream.readUint16(); - regX = stream.readUint16(); - unk1 = unk2 = 0; - - if (someFlaggyThing & 0x8000) { - unk1 = stream.readUint16(); - unk2 = stream.readUint16(); - } - modified = 0; -} - -TextCast::TextCast(Common::SeekableSubReadStreamEndian &stream) { - flags1 = stream.readByte(); - borderSize = static_cast<SizeType>(stream.readByte()); - gutterSize = static_cast<SizeType>(stream.readByte()); - boxShadow = static_cast<SizeType>(stream.readByte()); - textType = static_cast<TextType>(stream.readByte()); - textAlign = static_cast<TextAlignType>(stream.readUint16()); - palinfo1 = stream.readUint16(); - palinfo2 = stream.readUint16(); - palinfo3 = stream.readUint16(); - - int t = stream.readUint32(); - assert(t == 0); // So far we saw only 0 here - - initialRect = Score::readRect(stream); - textShadow = static_cast<SizeType>(stream.readByte()); - byte flags = stream.readByte(); - if (flags & 0x1) - textFlags.push_back(kTextFlagEditable); - if (flags & 0x2) - textFlags.push_back(kTextFlagAutoTab); - if (flags & 0x4) - textFlags.push_back(kTextFlagDoNotWrap); - if (flags & 0xf8) - warning("Unproxessed text cast flags: %x", flags & 0xf8); - - // TODO: FIXME: guesswork - fontId = stream.readByte(); - fontSize = stream.readByte(); - - modified = 0; -} - -ShapeCast::ShapeCast(Common::SeekableSubReadStreamEndian &stream) { - /*byte flags = */ stream.readByte(); - /*unk1 = */ stream.readByte(); - shapeType = static_cast<ShapeType>(stream.readByte()); - initialRect = Score::readRect(stream); - pattern = stream.readUint16BE(); - fgCol = stream.readByte(); - bgCol = stream.readByte(); - fillType = stream.readByte(); - lineThickness = stream.readByte(); - lineDirection = stream.readByte(); - modified = 0; -} - -Common::Rect Score::readRect(Common::SeekableSubReadStreamEndian &stream) { - Common::Rect *rect = new Common::Rect(); - rect->top = stream.readUint16(); - rect->left = stream.readUint16(); - rect->bottom = stream.readUint16(); - rect->right = stream.readUint16(); +Common::Rect Score::readRect(Common::ReadStreamEndian &stream) { + Common::Rect rect; + rect.top = stream.readUint16(); + rect.left = stream.readUint16(); + rect.bottom = stream.readUint16(); + rect.right = stream.readUint16(); - return *rect; + return rect; } void Score::startLoop() { @@ -930,4 +1011,4 @@ Sprite *Score::getSpriteById(uint16 id) { } } -} //End of namespace Director +} // End of namespace Director diff --git a/engines/director/score.h b/engines/director/score.h index 83f25284b1..9f6088307b 100644 --- a/engines/director/score.h +++ b/engines/director/score.h @@ -34,26 +34,14 @@ namespace Graphics { namespace Director { class Archive; +struct CastInfo; class DirectorEngine; class DirectorSound; class Frame; +struct Label; class Lingo; class Sprite; -enum CastType { - kCastBitmap = 1, - kCastFilmLoop, - kCastText, - kCastPalette, - kCastPicture, - kCastSound, - kCastButton, - kCastShape, - kCastMovie, - kCastDigitalVideo, - kCastScript -}; - enum ScriptType { kMovieScript = 0, kSpriteScript = 1, @@ -62,120 +50,12 @@ enum ScriptType { kMaxScriptType = 2 }; -struct Cast { - CastType type; - Common::Rect initialRect; - byte modified; -}; - -struct BitmapCast : Cast { - BitmapCast(Common::SeekableSubReadStreamEndian &stream); - - Common::Rect boundingRect; - uint16 regX; - uint16 regY; - uint8 flags; - uint16 someFlaggyThing; - uint16 unk1, unk2; -}; - -enum ShapeType { - kShapeRectangle, - kShapeRoundRect, - kShapeOval, - kShapeLine -}; - -struct ShapeCast : Cast { - ShapeCast(Common::SeekableSubReadStreamEndian &stream); - - ShapeType shapeType; - uint16 pattern; - byte fgCol; - byte bgCol; - byte fillType; - byte lineThickness; - byte lineDirection; -}; - -enum TextType { - kTextTypeAdjustToFit, - kTextTypeScrolling, - kTextTypeFixed -}; - -enum TextAlignType { - kTextAlignRight = -1, - kTextAlignLeft, - kTextAlignCenter -}; - -enum TextFlag { - kTextFlagEditable, - kTextFlagAutoTab, - kTextFlagDoNotWrap -}; - -enum SizeType { - kSizeNone, - kSizeSmallest, - kSizeSmall, - kSizeMedium, - kSizeLarge, - kSizeLargest -}; - -struct TextCast : Cast { - TextCast(Common::SeekableSubReadStreamEndian &stream); - - SizeType borderSize; - SizeType gutterSize; - SizeType boxShadow; - - byte flags1; - uint32 fontId; - uint16 fontSize; - TextType textType; - TextAlignType textAlign; - SizeType textShadow; - Common::Array<TextFlag> textFlags; - int16 palinfo1, palinfo2, palinfo3; -}; - -enum ButtonType { - kTypeButton, - kTypeCheckBox, - kTypeRadio -}; - -struct ButtonCast : TextCast { - ButtonCast(Common::SeekableSubReadStreamEndian &stream) : TextCast(stream) { - buttonType = static_cast<ButtonType>(stream.readUint16BE()); - } - - ButtonType buttonType; -}; - -struct CastInfo { - Common::String script; - Common::String name; - Common::String directory; - Common::String fileName; - Common::String type; -}; - -struct Label { - Common::String name; - uint16 number; - Label(Common::String name1, uint16 number1) { name = name1; number = number1; } -}; - class Score { public: Score(DirectorEngine *vm, Archive *); ~Score(); - static Common::Rect readRect(Common::SeekableSubReadStreamEndian &stream); + static Common::Rect readRect(Common::ReadStreamEndian &stream); static int compareLabels(const void *a, const void *b); void loadArchive(); void setStartToLabel(Common::String label); @@ -186,7 +66,8 @@ public: void processEvents(); Archive *getArchive() const { return _movieArchive; }; void loadConfig(Common::SeekableSubReadStreamEndian &stream); - void loadCastData(Common::SeekableSubReadStreamEndian &stream); + void loadCastDataVWCR(Common::SeekableSubReadStreamEndian &stream); + void loadCastData(Common::SeekableSubReadStreamEndian &stream, uint16 id); void setCurrentFrame(uint16 frameId) { _currentFrame = frameId; } int getCurrentFrame() { return _currentFrame; } Common::String getMacName() const { return _macName; } @@ -243,6 +124,6 @@ private: DirectorEngine *_vm; }; -} //End of namespace Director +} // End of namespace Director #endif diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp index af08094821..1bd70d257d 100644 --- a/engines/director/sound.cpp +++ b/engines/director/sound.cpp @@ -102,4 +102,4 @@ void DirectorSound::stopSound() { _mixer->stopHandle(*_sound2); } -} //End of namespace Director +} // End of namespace Director diff --git a/engines/director/sprite.cpp b/engines/director/sprite.cpp index 24db236563..a59313ad33 100644 --- a/engines/director/sprite.cpp +++ b/engines/director/sprite.cpp @@ -21,6 +21,7 @@ */ #include "director/director.h" +#include "director/cast.h" #include "director/score.h" #include "director/sprite.h" @@ -93,4 +94,4 @@ Sprite::~Sprite() { delete _cast; } -} //End of namespace Director +} // End of namespace Director diff --git a/engines/director/sprite.h b/engines/director/sprite.h index e879f83599..d248036693 100644 --- a/engines/director/sprite.h +++ b/engines/director/sprite.h @@ -51,18 +51,18 @@ enum InkType { //Director v4 enum SpriteType { - kInactiveSprite, //turns the sprite off + kInactiveSprite, // turns the sprite off kBitmapSprite, kRectangleSprite, kRoundedRectangleSprite, kOvalSprite, - kLineTopBottomSprite, //line from top left to bottom right - kLineBottomTopSprite, //line from bottom left to top right + kLineTopBottomSprite, // line from top left to bottom right + kLineBottomTopSprite, // line from bottom left to top right kTextSprite, kButtonSprite, kCheckboxSprite, kRadioButtonSprite, - kUndeterminedSprite = 16 //use castType property to examine the type of cast member associated with sprite + kUndeterminedSprite = 16 // use castType property to examine the type of cast member associated with sprite }; enum SpritePosition { @@ -97,6 +97,14 @@ public: Sprite(); Sprite(const Sprite &sprite); ~Sprite(); + + byte _x1; + uint16 _x2; + uint16 _scriptId; + byte _flags2; // x40 editable, 0x80 moveable + byte _unk2; + uint32 _unk3; + bool _enabled; byte _castId; InkType _ink; @@ -106,8 +114,8 @@ public: Common::Point _startPoint; uint16 _width; uint16 _height; - //TODO: default constraint = 0, if turned on, sprite is constrainted to the bounding rect - //As i know, constrainted != 0 only if sprite moveable + // TODO: default constraint = 0, if turned on, sprite is constrainted to the bounding rect + // As i know, constrainted != 0 only if sprite moveable byte _constraint; byte _moveable; byte _backColor; @@ -119,19 +127,19 @@ public: byte _blend; bool _visible; SpriteType _type; - //Using in digital movie sprites + // Using in digital movie sprites byte _movieRate; uint16 _movieTime; uint16 _startTime; uint16 _stopTime; byte _volume; byte _stretch; - //Using in shape sprites + // Using in shape sprites byte _lineSize; - //Using in text sprites + // Using in text sprites Common::String _editableText; }; -} //End of namespace Director +} // End of namespace Director #endif diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp index cf748ffa41..a72342da3d 100644 --- a/engines/dm/group.cpp +++ b/engines/dm/group.cpp @@ -1672,7 +1672,10 @@ int16 GroupMan::getChampionDamage(Group *group, uint16 champIndex) { uint16 poisonAttack = creatureInfo._poisonAttack; if (poisonAttack && _vm->getRandomNumber(2)) { poisonAttack = championMan.getStatisticAdjustedAttack(curChampion, kDMStatVitality, poisonAttack); - if (poisonAttack >= 0) + + // Strangerke: In the original, the check was on >= 0, which is pretty useless for a unsigned variable. + // I changed the check to > 0 because, considering the code of championPoison, it avoids a potential bug. + if (poisonAttack > 0) championMan.championPoison(champIndex, poisonAttack); } return damage; diff --git a/engines/fullpipe/statics.cpp b/engines/fullpipe/statics.cpp index 404a8edecc..bf769e2f61 100644 --- a/engines/fullpipe/statics.cpp +++ b/engines/fullpipe/statics.cpp @@ -547,7 +547,7 @@ void Movement::draw(bool flipFlag, int angle) { if (flipFlag) { bmp->flipVertical()->drawShaded(1, x, y + 30 + _currDynamicPhase->_rect->bottom, _currDynamicPhase->_paletteData, _currDynamicPhase->_alpha); - } if (angle) { + } else if (angle) { bmp->drawRotated(x, y, angle, _currDynamicPhase->_paletteData, _currDynamicPhase->_alpha); } else { bmp->putDib(x, y, (int32 *)_currDynamicPhase->_paletteData, _currDynamicPhase->_alpha); diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h index af58397200..9f952e5df6 100644 --- a/engines/kyra/lol.h +++ b/engines/kyra/lol.h @@ -987,8 +987,7 @@ private: uint16 _specialGuiShapeY; uint16 _specialGuiShapeMirrorFlag; - char _lastOverridePalFile[12]; - char *_lastOverridePalFilePtr; + Common::String _lastOverridePalFile; int _lastSpecialColor; int _lastSpecialColorWeight; diff --git a/engines/kyra/scene_lol.cpp b/engines/kyra/scene_lol.cpp index 391de5e49c..a746080190 100644 --- a/engines/kyra/scene_lol.cpp +++ b/engines/kyra/scene_lol.cpp @@ -303,12 +303,10 @@ void LoLEngine::loadLevelGraphics(const char *file, int specialColor, int weight _lastSpecialColor = specialColor; _lastSpecialColorWeight = weight; strcpy(_lastBlockDataFile, file); - if (palFile) { - strcpy(_lastOverridePalFile, palFile); - _lastOverridePalFilePtr = _lastOverridePalFile; - } else { - _lastOverridePalFilePtr = 0; - } + if (palFile) + _lastOverridePalFile = palFile; + else + _lastOverridePalFile.clear(); } if (_flags.use16ColorMode) { @@ -361,8 +359,8 @@ void LoLEngine::loadLevelGraphics(const char *file, int specialColor, int weight memcpy(_vcnColTable, v, 128); v += 128; - if (_lastOverridePalFilePtr) { - _res->loadFileToBuf(_lastOverridePalFilePtr, _screen->getPalette(0).getData(), 384); + if (!_lastOverridePalFile.empty()) { + _res->loadFileToBuf(_lastOverridePalFile.c_str(), _screen->getPalette(0).getData(), 384); } else { _screen->getPalette(0).copy(v, 0, 128); } diff --git a/engines/kyra/timer.cpp b/engines/kyra/timer.cpp index edca3ef5bf..97e989ea38 100644 --- a/engines/kyra/timer.cpp +++ b/engines/kyra/timer.cpp @@ -90,7 +90,7 @@ void TimerManager::reset() { void TimerManager::addTimer(uint8 id, TimerFunc *func, int countdown, bool enabled) { Iterator timer = Common::find_if(_timers.begin(), _timers.end(), TimerEqual(id)); if (timer != _timers.end()) { - warning("Adding allready existing timer %d", id); + warning("Adding already existing timer %d", id); return; } diff --git a/engines/lab/console.cpp b/engines/lab/console.cpp index 20b1f5645b..a90c2605a7 100644 --- a/engines/lab/console.cpp +++ b/engines/lab/console.cpp @@ -81,7 +81,7 @@ bool Console::Cmd_DumpSceneResources(int argc, const char **argv) { "ResetBuffer", "SpecialCmd", "CShowMessage", "PlaySoundNoWait" }; - debugPrintf("Room mesage: %s\n", roomData->_roomMsg.c_str()); + debugPrintf("Room message: %s\n", roomData->_roomMsg.c_str()); debugPrintf("Transition: %s (%d)\n", transitions[roomData->_transitionType], roomData->_transitionType); debugPrintf("Script:\n"); diff --git a/engines/mads/palette.cpp b/engines/mads/palette.cpp index 7651fe8e65..de870295a5 100644 --- a/engines/mads/palette.cpp +++ b/engines/mads/palette.cpp @@ -665,15 +665,15 @@ void Fader::insertionSort(int size, byte *id, byte *value) { int moveCount = size - arrIndex - 1; if (moveCount > 0) { - Common::copy(idP + 1, idP + moveCount + 2, idP); - Common::copy(valueP + 1, valueP + moveCount + 2, valueP); + Common::copy(idP + 1, idP + moveCount + 1, idP); + Common::copy(valueP + 1, valueP + moveCount + 1, valueP); } // Scan for insert spot int idx = 0; if (endIndex > 0) { bool breakFlag = false; - for (; idx <= endIndex && !breakFlag; ++idx) { + for (; idx <= endIndex - 1 && !breakFlag; ++idx) { breakFlag = savedId < id[idx]; } } diff --git a/engines/mads/staticres.cpp b/engines/mads/staticres.cpp index b659d9a27c..6cb76a1d89 100644 --- a/engines/mads/staticres.cpp +++ b/engines/mads/staticres.cpp @@ -43,7 +43,7 @@ const char *const kFenceStr = "fence"; const char *const kOverStr = "over"; const char *const kGameReleaseInfoStr = "ScummVM rev: 8.43 14-Sept-92"; -const char *const kGameReleaseTitleStr = "GAME RELASE VERSION INFO"; +const char *const kGameReleaseTitleStr = "GAME RELEASE VERSION INFO"; const uint32 DEFAULT_VGA_LOW_PALETTE[16] = { 0x000000, 0x0000a8, 0x00a800, 0x00a8a8, 0xa80000, 0xa800a8, 0xa85400, 0xa8a8a8, diff --git a/engines/mohawk/POTFILES b/engines/mohawk/POTFILES index ced02735c7..42d1d084c2 100644 --- a/engines/mohawk/POTFILES +++ b/engines/mohawk/POTFILES @@ -2,4 +2,5 @@ engines/mohawk/detection.cpp engines/mohawk/dialogs.cpp engines/mohawk/myst.cpp engines/mohawk/riven.cpp +engines/mohawk/riven_external.cpp engines/mohawk/mohawk.cpp diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index a1eef85a69..12b4851a9c 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -147,8 +147,8 @@ Common::Error MohawkEngine_Riven::run() { // We need to have a cursor source, or the game won't work if (!_cursor->hasSource()) { - Common::String message = "You're missing a Riven executable. The Windows executable is 'riven.exe' or 'rivendmo.exe'. "; - message += "Using the 'arcriven.z' installer file also works. In addition, you can use the Mac 'Riven' executable."; + Common::String message = _("You're missing a Riven executable. The Windows executable is 'riven.exe' or 'rivendmo.exe'. "); + message += _("Using the 'arcriven.z' installer file also works. In addition, you can use the Mac 'Riven' executable."); GUIErrorMessage(message); warning("%s", message.c_str()); return Common::kNoGameDataFoundError; @@ -159,7 +159,7 @@ Common::Error MohawkEngine_Riven::run() { // We need extras.mhk for inventory images, marble images, and credits images if (!_extrasFile->openFile("extras.mhk")) { - Common::String message = "You're missing 'extras.mhk'. Using the 'arcriven.z' installer file also works."; + Common::String message = _("You're missing 'extras.mhk'. Using the 'arcriven.z' installer file also works."); GUIErrorMessage(message); warning("%s", message.c_str()); return Common::kNoGameDataFoundError; diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index 125630445e..fb98145b44 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -30,6 +30,7 @@ #include "gui/message.h" #include "common/events.h" #include "common/system.h" +#include "common/translation.h" namespace Mohawk { @@ -209,8 +210,8 @@ void RivenExternal::runCommand(uint16 argc, uint16 *argv) { } void RivenExternal::runDemoBoundaryDialog() { - GUI::MessageDialog dialog("Exploration beyond this point available only within the full version of\n" - "the game."); + GUI::MessageDialog dialog(_("Exploration beyond this point available only within the full version of\n" + "the game.")); dialog.runModal(); } @@ -651,11 +652,11 @@ void RivenExternal::xalaunchbrowser(uint16 argc, uint16 *argv) { // // [YES] [NO] - GUI::MessageDialog dialog("At this point, the Riven Demo would\n" + GUI::MessageDialog dialog(_("At this point, the Riven Demo would\n" "ask if you would like to open a web browser\n" "to bring you to the Red Orb store to buy\n" "the game. ScummVM cannot do that and\n" - "the site no longer exists."); + "the site no longer exists.")); dialog.runModal(); } diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 8737c8b954..83f1271252 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -53,6 +53,7 @@ #ifdef ENABLE_SCI32 #include "sci/graphics/frameout.h" #include "sci/graphics/paint32.h" +#include "sci/graphics/palette32.h" #include "video/coktel_decoder.h" #endif @@ -228,6 +229,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), registerCmd("view_listnode", WRAP_METHOD(Console, cmdViewListNode)); registerCmd("view_reference", WRAP_METHOD(Console, cmdViewReference)); registerCmd("vr", WRAP_METHOD(Console, cmdViewReference)); // alias + registerCmd("dump_reference", WRAP_METHOD(Console, cmdDumpReference)); + registerCmd("dr", WRAP_METHOD(Console, cmdDumpReference)); // alias registerCmd("view_object", WRAP_METHOD(Console, cmdViewObject)); registerCmd("vo", WRAP_METHOD(Console, cmdViewObject)); // alias registerCmd("active_object", WRAP_METHOD(Console, cmdViewActiveObject)); @@ -446,6 +449,7 @@ bool Console::cmdHelp(int argc, const char **argv) { debugPrintf(" value_type - Determines the type of a value\n"); debugPrintf(" view_listnode - Examines the list node at the given address\n"); debugPrintf(" view_reference / vr - Examines an arbitrary reference\n"); + debugPrintf(" dump_reference / dr - Dumps an arbitrary reference to disk\n"); debugPrintf(" view_object / vo - Examines the object at the given address\n"); debugPrintf(" active_object - Shows information on the currently active object or class\n"); debugPrintf(" acc_object - Shows information on the object or class at the address indexed by the accumulator\n"); @@ -2843,6 +2847,125 @@ bool Console::cmdViewReference(int argc, const char **argv) { return true; } +bool Console::cmdDumpReference(int argc, const char **argv) { + if (argc < 2) { + debugPrintf("Dumps an arbitrary reference to disk.\n"); + debugPrintf("Usage: %s <start address> [<end address>]\n", argv[0]); + debugPrintf("Where <start address> is the starting address to dump\n"); + debugPrintf("<end address>, if provided, is the address where the dump ends\n"); + debugPrintf("Check the \"addresses\" command on how to use addresses\n"); + return true; + } + + reg_t reg = NULL_REG; + reg_t reg_end = NULL_REG; + + if (parse_reg_t(_engine->_gamestate, argv[1], ®, false)) { + debugPrintf("Invalid address passed.\n"); + debugPrintf("Check the \"addresses\" command on how to use addresses\n"); + return true; + } + + if (argc > 2) { + if (parse_reg_t(_engine->_gamestate, argv[2], ®_end, false)) { + debugPrintf("Invalid address passed.\n"); + debugPrintf("Check the \"addresses\" command on how to use addresses\n"); + return true; + } + } + + if (reg.getSegment() == 0 && reg.getOffset() == 0) { + debugPrintf("Register is null.\n"); + return true; + } + + if (g_sci->getKernel()->findRegType(reg) != SIG_TYPE_REFERENCE) { + debugPrintf("%04x:%04x is not a reference\n", PRINT_REG(reg)); + return true; + } + + if (reg_end.getSegment() != reg.getSegment() && reg_end != NULL_REG) { + debugPrintf("Ending segment different from starting segment. Assuming no bound on dump.\n"); + reg_end = NULL_REG; + } + + Common::DumpFile out; + Common::String outFileName; + uint32 bytesWritten; + + switch (_engine->_gamestate->_segMan->getSegmentType(reg.getSegment())) { +#ifdef ENABLE_SCI32 + case SEG_TYPE_BITMAP: { + outFileName = Common::String::format("%04x_%04x.tga", PRINT_REG(reg)); + out.open(outFileName); + SciBitmap &bitmap = *_engine->_gamestate->_segMan->lookupBitmap(reg); + const Color *color = g_sci->_gfxPalette32->getCurrentPalette().colors; + const uint16 numColors = ARRAYSIZE(g_sci->_gfxPalette32->getCurrentPalette().colors); + + out.writeByte(0); // image id length + out.writeByte(1); // color map type (present) + out.writeByte(1); // image type (uncompressed color-mapped) + out.writeSint16LE(0); // index of first color map entry + out.writeSint16LE(numColors); // number of color map entries + out.writeByte(24); // number of bits per color entry (RGB24) + out.writeSint16LE(0); // bottom-left x-origin + out.writeSint16LE(bitmap.getHeight() - 1); // bottom-left y-origin + out.writeSint16LE(bitmap.getWidth()); // width + out.writeSint16LE(bitmap.getHeight()); // height + out.writeByte(8); // bits per pixel + out.writeByte(1 << 5); // origin of pixel data (top-left) + + bytesWritten = 18; + + for (int i = 0; i < numColors; ++i) { + out.writeByte(color->b); + out.writeByte(color->g); + out.writeByte(color->r); + ++color; + } + + bytesWritten += numColors * 3; + bytesWritten += out.write(bitmap.getPixels(), bitmap.getWidth() * bitmap.getHeight()); + break; + } +#endif + + default: { + const SegmentRef block = _engine->_gamestate->_segMan->dereference(reg); + uint32 size = block.maxSize; + + if (size == 0) { + debugPrintf("Size of reference is zero.\n"); + return true; + } + + if (reg_end.getSegment() != 0 && (size < reg_end.getOffset() - reg.getOffset())) { + debugPrintf("Block end out of bounds (size %d). Resetting.\n", size); + reg_end = NULL_REG; + } + + if (reg_end.getSegment() != 0 && (size >= reg_end.getOffset() - reg.getOffset())) { + size = reg_end.getOffset() - reg.getOffset(); + } + + if (reg_end.getSegment() != 0) { + debugPrintf("Block size less than or equal to %d\n", size); + } + + outFileName = Common::String::format("%04x_%04x.dmp", PRINT_REG(reg)); + out.open(outFileName); + bytesWritten = out.write(block.raw, size); + break; + } + } + + out.finalize(); + out.close(); + + debugPrintf("Wrote %u bytes to %s\n", bytesWritten, outFileName.c_str()); + return true; +} + bool Console::cmdViewObject(int argc, const char **argv) { if (argc != 2) { debugPrintf("Examines the object at the given address.\n"); @@ -4530,7 +4653,7 @@ int Console::printObject(reg_t pos) { uint i; if (!obj) { - debugPrintf("[%04x:%04x]: Not an object.", PRINT_REG(pos)); + debugPrintf("[%04x:%04x]: Not an object.\n", PRINT_REG(pos)); return 1; } diff --git a/engines/sci/console.h b/engines/sci/console.h index 0b87a4408b..d4b17ee802 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -163,6 +163,7 @@ private: bool cmdValueType(int argc, const char **argv); bool cmdViewListNode(int argc, const char **argv); bool cmdViewReference(int argc, const char **argv); + bool cmdDumpReference(int argc, const char **argv); bool cmdViewObject(int argc, const char **argv); bool cmdViewActiveObject(int argc, const char **argv); bool cmdViewAccumulatorObject(int argc, const char **argv); diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 83f1d30916..a00da7c5fd 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -556,7 +556,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, s_fallbackDesc.guiOptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); if (allFiles.contains("resource.map") || allFiles.contains("Data1") - || allFiles.contains("resmap.001") || allFiles.contains("resmap.001")) { + || allFiles.contains("resmap.000") || allFiles.contains("resmap.001")) { foundResMap = true; } diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 22d2b6f308..4a3d13cbed 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -3398,7 +3398,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by alonzotg in bug report #3206006) + // Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy v1.001 Int#0.000.566 (supplied by alonzotg in bug report #3206006) {"qfg1", "", { {"resource.map", 0, "85512508ed4e4ef1e3b309adabceeda9", 6486}, {"resource.000", 0, "481b034132106390cb5160fe61dd5f58", 80334}, diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h index 15c80a7277..36b0d06360 100644 --- a/engines/sci/engine/features.h +++ b/engines/sci/engine/features.h @@ -117,6 +117,17 @@ public: inline bool hasNewPaletteCode() const { return getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7; } + + inline bool VMDOpenStopsAudio() const { + // Of the games that use VMDs: + // Yes: Phant1, Shivers, Torin + // No: SQ6 + // TODO: Optional extra flag to kPlayVMD which defaults to Yes: PQ:SWAT + // TODO: SCI3, GK2 (GK2's VMD code is closer to SCI3 than SCI21) + return getSciVersion() == SCI_VERSION_2_1_MIDDLE && + g_sci->getGameId() != GID_SQ6 && + g_sci->getGameId() != GID_GK2; + } #endif /** diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index cce9a223d3..03c8a673a8 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -466,6 +466,7 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDOpen(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDInit(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv); +reg_t kPlayVMDIgnorePalettes(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 6e88112992..8093147cb4 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -466,6 +466,7 @@ static const SciKernelMapSubEntry kPlayVMD_subops[] = { { SIG_SINCE_SCI21, 0, MAP_CALL(PlayVMDOpen), "r(i)(i)", NULL }, { SIG_SINCE_SCI21, 1, MAP_CALL(PlayVMDInit), "ii(i)(i)(ii)", NULL }, { SIG_SINCE_SCI21, 6, MAP_CALL(PlayVMDClose), "", NULL }, + { SIG_SINCE_SCI21, 7, MAP_CALL(PlayVMDIgnorePalettes), "", NULL }, { SIG_SINCE_SCI21, 10, MAP_CALL(PlayVMDGetStatus), "", NULL }, { SIG_SINCE_SCI21, 14, MAP_CALL(PlayVMDPlayUntilEvent), "i(i)(i)", NULL }, { SIG_SINCE_SCI21, 16, MAP_CALL(PlayVMDShowCursor), "i", NULL }, diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 6ec61343b0..74c1f99778 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -733,11 +733,12 @@ namespace { } bool isSignedType(const char type) { - return type == 'd' || type == 'i'; + // For whatever reason, %d ends up being treated as unsigned in SSCI + return type == 'i'; } bool isUnsignedType(const char type) { - return strchr("uxXoc", type); + return strchr("duxXoc", type); } bool isStringType(const char type) { @@ -805,8 +806,11 @@ Common::String format(const Common::String &source, int argc, const reg_t *argv) continue; } - assert(argIndex < argc); - out += readPlaceholder(in, argv[argIndex++]); + if (argIndex < argc) { + out += readPlaceholder(in, argv[argIndex++]); + } else { + out += readPlaceholder(in, NULL_REG); + } } else { out += *in++; } diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 83a02883af..11378d7647 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -428,6 +428,11 @@ reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv) { return make_reg(0, g_sci->_video32->getVMDPlayer().close()); } +reg_t kPlayVMDIgnorePalettes(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getVMDPlayer().ignorePalettes(); + return s->r_acc; +} + reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv) { return make_reg(0, g_sci->_video32->getVMDPlayer().getStatus()); } diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp index b0615b4213..26ab9b47a5 100644 --- a/engines/sci/engine/message.cpp +++ b/engines/sci/engine/message.cpp @@ -333,12 +333,14 @@ void MessageState::popCursorStack() { error("Message: attempt to pop from empty stack"); } -int MessageState::hexDigitToInt(char h) { +int MessageState::hexDigitToWrongInt(char h) { + // Hex digits above 9 are incorrectly interpreted by SSCI as 11-16 instead + // of 10-15 because of a never-fixed typo if ((h >= 'A') && (h <= 'F')) - return h - 'A' + 10; + return h - 'A' + 11; if ((h >= 'a') && (h <= 'f')) - return h - 'a' + 10; + return h - 'a' + 11; if ((h >= '0') && (h <= '9')) return h - '0'; @@ -355,8 +357,8 @@ bool MessageState::stringHex(Common::String &outStr, const Common::String &inStr if (index + 2 >= inStr.size()) return false; - int digit1 = hexDigitToInt(inStr[index + 1]); - int digit2 = hexDigitToInt(inStr[index + 2]); + int digit1 = hexDigitToWrongInt(inStr[index + 1]); + int digit2 = hexDigitToWrongInt(inStr[index + 2]); // Check for hex if ((digit1 == -1) || (digit2 == -1)) diff --git a/engines/sci/engine/message.h b/engines/sci/engine/message.h index ff76534d2d..5847e4767e 100644 --- a/engines/sci/engine/message.h +++ b/engines/sci/engine/message.h @@ -73,7 +73,7 @@ private: bool getRecord(CursorStack &stack, bool recurse, MessageRecord &record); void outputString(reg_t buf, const Common::String &str); Common::String processString(const char *s); - int hexDigitToInt(char h); + int hexDigitToWrongInt(char h); bool stringHex(Common::String &outStr, const Common::String &inStr, uint &index); bool stringLit(Common::String &outStr, const Common::String &inStr, uint &index); bool stringStage(Common::String &outStr, const Common::String &inStr, uint &index); diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 2e4da46b70..720f6783ee 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -643,20 +643,33 @@ void SoundCommandParser::reconstructPlayList() { _music->_mutex.unlock(); for (MusicList::iterator i = songs.begin(); i != songs.end(); ++i) { - initSoundResource(*i); + MusicEntry *entry = *i; + initSoundResource(entry); - if ((*i)->status == kSoundPlaying) { +#ifdef ENABLE_SCI32 + if (_soundVersion >= SCI_VERSION_2_1_EARLY && entry->isSample) { + const reg_t &soundObj = entry->soundObj; + + if ((int)readSelectorValue(_segMan, soundObj, SELECTOR(loop)) != -1 && + readSelector(_segMan, soundObj, SELECTOR(handle)) != NULL_REG) { + + writeSelector(_segMan, soundObj, SELECTOR(handle), NULL_REG); + processPlaySound(soundObj, entry->playBed); + } + } else +#endif + if (entry->status == kSoundPlaying) { // WORKAROUND: PQ3 (German?) scripts can set volume negative in the // sound object directly without going through DoSound. // Since we re-read this selector when re-playing the sound after loading, // this will lead to unexpected behaviour. As a workaround we // sync the sound object's selectors here. (See bug #5501) - writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(loop), (*i)->loop); - writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(priority), (*i)->priority); + writeSelectorValue(_segMan, entry->soundObj, SELECTOR(loop), entry->loop); + writeSelectorValue(_segMan, entry->soundObj, SELECTOR(priority), entry->priority); if (_soundVersion >= SCI_VERSION_1_EARLY) - writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol), (*i)->volume); + writeSelectorValue(_segMan, entry->soundObj, SELECTOR(vol), entry->volume); - processPlaySound((*i)->soundObj, (*i)->playBed); + processPlaySound(entry->soundObj, entry->playBed); } } } diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 5ac4b758ba..5f3370bad5 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -2190,8 +2190,27 @@ static const SciScriptPatcherEntry larry6Signatures[] = { #pragma mark - #pragma mark Leisure Suit Larry 6 Hires +// When entering room 270 (diving board) from room 230, a typo in the game +// script means that `setScale` is called accidentally instead of `setScaler`. +// In SSCI this did not do much because the first argument happened to be +// smaller than the y-position of `ego`, but in ScummVM the first argument is +// larger and so a debug message "y value less than vanishingY" is displayed. +static const uint16 larry6HiresSignatureSetScale[] = { + SIG_MAGICDWORD, + 0x38, SIG_UINT16(0x14b), // pushi 014b (setScale) + 0x38, SIG_UINT16(0x05), // pushi 0005 + 0x51, 0x2c, // class 2c (Scaler) + SIG_END +}; + +static const uint16 larry6HiresPatchSetScale[] = { + 0x38, SIG_UINT16(0x14f), // pushi 014f (setScaler) + PATCH_END +}; + // script, description, signature patch static const SciScriptPatcherEntry larry6HiresSignatures[] = { + { true, 270, "fix incorrect setScale call", 1, larry6HiresSignatureSetScale, larry6HiresPatchSetScale }, { true, 64990, "increase number of save games", 1, sci2NumSavesSignature1, sci2NumSavesPatch1 }, { true, 64990, "increase number of save games", 1, sci2NumSavesSignature2, sci2NumSavesPatch2 }, { true, 64990, "disable change directory button", 1, sci2ChangeDirSignature, sci2ChangeDirPatch }, diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index 361c1cb895..e8f0be3a79 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -552,8 +552,17 @@ public: case kArrayTypeID: return ((reg_t *)_data)[index]; case kArrayTypeByte: - case kArrayTypeString: - return make_reg(0, ((byte *)_data)[index]); + case kArrayTypeString: { + int16 value; + + if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { + value = ((int8 *)_data)[index]; + } else { + value = ((uint8 *)_data)[index]; + } + + return make_reg(0, value); + } default: error("Invalid array type %d", _type); } diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index dabf79a54b..4e9c59b225 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -399,6 +399,7 @@ const SciWorkaroundEntry kAbs_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kArraySetElements_workarounds[] = { + { GID_GK1, 302, 64918, 0, "Str", "callKernel", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when erasing a letter on the wall in St Louis Cemetery { GID_PHANTASMAGORIA,902, 64918, 0, "Str", "callKernel", NULL, 0, { WORKAROUND_FAKE, 0 } }, // tries to set an element of a string array to the ego object when starting a new game and selecting a chapter above 1 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -684,21 +685,6 @@ const SciWorkaroundEntry kNewWindow_workarounds[] = { }; // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround -const SciWorkaroundEntry kNumCels_workarounds[] = { - { GID_GK1, 460, 64998, -1, "GKEgo", "lastCel", NULL, -1, { WORKAROUND_FAKE, 5 } }, // when Gabriel clicks "ask" on the bartender from a distance - { GID_GK1, 808, 64998, -1, "sDJEnters", "changeState", NULL, -1, { WORKAROUND_FAKE, 6 } }, // - { GID_GK2, 470, 64998, -1, "pLookieLoos", "lastCel", NULL, -1, { WORKAROUND_FAKE, 50 } }, // random background movement in the crime scene in Munich city centre - { GID_GK2, 470, 64998, -1, "pNewsCrew", "lastCel", NULL, -1, { WORKAROUND_FAKE, 18 } }, // random background movement in the crime scene in Munich city centre - { GID_GK2, 470, 64998, -1, "pLeberGroup", "lastCel", NULL, -1, { WORKAROUND_FAKE, 22 } }, // random background movement in the crime scene in Munich city centre - { GID_SQ6, 270, 64998, -1, "offWorld", "lastCel", NULL, -1, { WORKAROUND_FAKE, 3 } }, // when exiting the kidnapping room - { GID_SQ6, 320, 64998, -1, "wandererB", "lastCel", NULL, -1, { WORKAROUND_FAKE, 8 } }, // random background movement on Polysorbate LX street 3 - { GID_SQ6, 340, 64998, -1, "wandererB", "lastCel", NULL, -1, { WORKAROUND_FAKE, 8 } }, // random background movement on Polysorbate LX street 1 - { GID_SQ6, 530, 64998, -1, "monitors", "lastCel", NULL, -1, { WORKAROUND_FAKE, 5 } }, // random background movement during cutscene - { GID_SQ6, 680, 64998, -1, "ego", "lastCel", NULL, -1, { WORKAROUND_FAKE, 44 } }, // when double-clicking hand icon on blockage - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kPalVarySetPercent_workarounds[] = { { GID_GK1, 370, 370, 0, "graceComeOut", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // there's an extra parameter in GK1, when changing chapters. This extra parameter seems to be a bug or just unimplemented functionality, as there's no visible change from the original in the chapter change room SCI_WORKAROUNDENTRY_TERMINATOR diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index c50a7fb04e..2d72ae5811 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -85,7 +85,6 @@ extern const SciWorkaroundEntry kIsObject_workarounds[]; extern const SciWorkaroundEntry kMemory_workarounds[]; extern const SciWorkaroundEntry kMoveCursor_workarounds[]; extern const SciWorkaroundEntry kNewWindow_workarounds[]; -extern const SciWorkaroundEntry kNumCels_workarounds[]; extern const SciWorkaroundEntry kPalVarySetPercent_workarounds[]; extern const SciWorkaroundEntry kRandom_workarounds[]; extern const SciWorkaroundEntry kReadNumber_workarounds[]; diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index 430500ce1e..09ea05bd59 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -800,7 +800,7 @@ int16 CelObjView::getNumLoops(const GuiResourceId viewId) { return resource->data[2]; } -int16 CelObjView::getNumCels(const GuiResourceId viewId, const int16 loopNo) { +int16 CelObjView::getNumCels(const GuiResourceId viewId, int16 loopNo) { const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); if (!resource) { @@ -813,25 +813,15 @@ int16 CelObjView::getNumCels(const GuiResourceId viewId, const int16 loopNo) { // Every version of SCI32 has a logic error in this function that causes // random memory to be read if a script requests the cel count for one - // past the maximum loop index. At least GK1 room 800 does this, and gets - // stuck in an infinite loop because the game script expects this method - // to return a non-zero value. - // The scope of this bug means it is likely to pop up in other games, so we - // explicitly trap the bad condition here and report it so that any other - // game scripts relying on this broken behavior can be fixed as well + // past the maximum loop index. For example, GK1 room 808 does this, and + // gets stuck in an infinite loop because the game script expects this + // method to return a non-zero value. + // This bug is triggered in basically every SCI32 game and appears to be + // universally fixable simply by always using the next lowest loop instead. if (loopNo == loopCount) { - SciCallOrigin origin; - SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kNumCels_workarounds, &origin); - switch (solution.type) { - case WORKAROUND_NONE: - error("[CelObjView::getNumCels]: loop number %d is equal to loop count in view %u, %s", loopNo, viewId, origin.toString().c_str()); - case WORKAROUND_FAKE: - return (int16)solution.value; - case WORKAROUND_IGNORE: - return 0; - case WORKAROUND_STILLCALL: - break; - } + const SciCallOrigin origin = g_sci->getEngineState()->getCurrentCallOrigin(); + debugC(kDebugLevelWorkarounds, "Workaround: kNumCels loop %d -> loop %d in view %u, %s", loopNo, loopNo - 1, viewId, origin.toString().c_str()); + --loopNo; } if (loopNo > loopCount || loopNo < 0) { diff --git a/engines/sci/graphics/lists32.h b/engines/sci/graphics/lists32.h index 4f74c77325..ff0bc6ceae 100644 --- a/engines/sci/graphics/lists32.h +++ b/engines/sci/graphics/lists32.h @@ -170,6 +170,13 @@ public: } /** + * The maximum number of elements the container is able to hold. + */ + size_type max_size() const { + return N; + } + + /** * The number of populated slots in the array. The size * of the array will only go down once `pack` is called. */ diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index 81e9bbbfd3..c4cfb35096 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -23,7 +23,6 @@ #ifndef SCI_GRAPHICS_PALETTE32_H #define SCI_GRAPHICS_PALETTE32_H -#include "sci/graphics/palette.h" namespace Sci { #pragma mark HunkPalette diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 23e92ef6a9..de6df39bb9 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -84,11 +84,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { } } - if (_resMan->detectHires()) { - _scriptWidth = 640; - _scriptHeight = 480; - } - // if not yet set, set those to script-width/height if (!_width) _width = _scriptWidth; diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index d142ff75c3..7375fdeffd 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -527,7 +527,7 @@ int16 GfxText32::getTextWidth(const uint index, uint length) const { --length; } } else { - width += font->getCharWidth(currentChar); + width += font->getCharWidth((unsigned char)currentChar); } if (length > 0) { diff --git a/engines/sci/graphics/transitions32.cpp b/engines/sci/graphics/transitions32.cpp index ddcb50b140..bad0185179 100644 --- a/engines/sci/graphics/transitions32.cpp +++ b/engines/sci/graphics/transitions32.cpp @@ -778,7 +778,7 @@ bool GfxTransitions32::processPixelDissolve21Mid(const PlaneShowStyle &showStyle int seq = 1; uint iteration = 0; - const uint numIterationsPerTick = ARRAYSIZE(g_sci->_gfxFrameout->_showList); + const uint numIterationsPerTick = g_sci->_gfxFrameout->_showList.max_size(); clearShowRects(); diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index 1db66644c8..bf0c990015 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -28,6 +28,7 @@ #include "engine.h" // for Engine, g_engine #include "engines/util.h" // for initGraphics #include "sci/console.h" // for Console +#include "sci/engine/features.h" // for GameFeatures #include "sci/engine/state.h" // for EngineState #include "sci/engine/vm_types.h" // for reg_t #include "sci/event.h" // for SciEvent, EventManager, SCI_... @@ -39,10 +40,12 @@ #include "sci/graphics/plane32.h" // for Plane, PlanePictureCodes::kP... #include "sci/graphics/screen_item32.h" // for ScaleInfo, ScreenItem, Scale... #include "sci/sci.h" // for SciEngine, g_sci, getSciVersion -#include "sci/graphics/video32.h" +#include "sci/sound/audio32.h" // for Audio32 #include "sci/video/seq_decoder.h" // for SEQDecoder #include "video/avi_decoder.h" // for AVIDecoder #include "video/coktel_decoder.h" // for AdvancedVMDDecoder +#include "sci/graphics/video32.h" + namespace Graphics { struct Surface; } namespace Sci { @@ -68,7 +71,8 @@ void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const // mechanism that is very similar to that used by the VMD player, which // allows the SEQ to be drawn into a bitmap ScreenItem and displayed using // the normal graphics system. - _segMan->allocateBitmap(&_bitmap, _decoder->getWidth(), _decoder->getHeight(), kDefaultSkipColor, 0, 0, kLowResX, kLowResY, 0, false, false); + SciBitmap &bitmap = *_segMan->allocateBitmap(&_bitmap, _decoder->getWidth(), _decoder->getHeight(), kDefaultSkipColor, 0, 0, kLowResX, kLowResY, 0, false, false); + bitmap.getBuffer().fillRect(Common::Rect(_decoder->getWidth(), _decoder->getHeight()), 0); CelInfo32 celInfo; celInfo.type = kCelTypeMem; @@ -163,49 +167,37 @@ AVIPlayer::IOStatus AVIPlayer::init1x(const int16 x, const int16 y, int16 width, _pixelDouble = false; if (!width || !height) { - width = _decoder->getWidth(); - height = _decoder->getHeight(); - } else if (getSciVersion() == SCI_VERSION_2_1_EARLY && g_sci->getGameId() == GID_KQ7) { - // KQ7 1.51 provides an explicit width and height when it wants scaling, - // though the width and height it provides are not scaled - _pixelDouble = true; - width *= 2; - height *= 2; - } - - // QFG4CD gives non-multiple-of-2 values for width and height, - // which would normally be OK except the source video is a pixel bigger - // in each dimension + const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const Ratio screenToScriptX(scriptWidth, screenWidth); + const Ratio screenToScriptY(scriptHeight, screenHeight); + width = (_decoder->getWidth() * screenToScriptX).toInt(); + height = (_decoder->getHeight() * screenToScriptY).toInt(); + } + + // QFG4CD gives non-multiple-of-2 values for width and height of the intro + // video, which would normally be OK except the source video is a pixel + // bigger in each dimension so it just causes part of the video to get cut + // off width = (width + 1) & ~1; height = (height + 1) & ~1; - _drawRect.left = x; - _drawRect.top = y; - _drawRect.right = x + width; - _drawRect.bottom = y + height; - - // SCI2.1mid uses init2x to draw a pixel-doubled AVI, but SCI2 has only the - // one play routine which automatically pixel-doubles in hi-res mode - if (getSciVersion() == SCI_VERSION_2) { - // This is somewhat of a hack; credits.avi from GK1 is not - // rendered correctly in SSCI because it is a 640x480 video, but the - // game script gives the wrong dimensions. Since this is the only - // high-resolution AVI ever used, just set the draw rectangle to draw - // the entire screen - if (_decoder->getWidth() > 320) { - _drawRect.left = 0; - _drawRect.top = 0; - _drawRect.right = 320; - _drawRect.bottom = 200; - } - - // In hi-res mode, video will be pixel doubled, so the origin (which - // corresponds to the correct position without pixel doubling) needs to - // be corrected - if (g_sci->_gfxFrameout->_isHiRes && _decoder->getWidth() <= 320) { - _drawRect.left /= 2; - _drawRect.top /= 2; - } + // GK1 CREDITS.AVI is not rendered correctly in SSCI because it is a 640x480 + // video and the game script gives the wrong dimensions. + // Since this is the only high-resolution AVI ever used by any SCI game, + // just set the draw rectangle to draw across the entire screen + if (g_sci->getGameId() == GID_GK1 && _decoder->getWidth() > 320) { + _drawRect.left = 0; + _drawRect.top = 0; + _drawRect.right = 320; + _drawRect.bottom = 200; + } else { + _drawRect.left = x; + _drawRect.top = y; + _drawRect.right = x + width; + _drawRect.bottom = y + height; } init(); @@ -222,8 +214,8 @@ AVIPlayer::IOStatus AVIPlayer::init2x(const int16 x, const int16 y) { _drawRect.top = y; _drawRect.right = x + _decoder->getWidth() * 2; _drawRect.bottom = y + _decoder->getHeight() * 2; - _pixelDouble = true; + init(); return kIOSuccess; @@ -233,42 +225,38 @@ void AVIPlayer::init() { int16 xRes; int16 yRes; - bool useScreenDimensions = false; - if (g_sci->_gfxFrameout->_isHiRes && _decoder->getWidth() > 320) { - useScreenDimensions = true; - } - - // KQ7 1.51 gives video position in screen coordinates, not game - // coordinates, because in SSCI they are passed to Video for Windows, which - // renders as an overlay on the game video. Because we put the video into a - // ScreenItem instead of rendering directly to the hardware surface, the - // coordinates need to be converted to game script coordinates - if (g_sci->getGameId() == GID_KQ7 && getSciVersion() == SCI_VERSION_2_1_EARLY) { - useScreenDimensions = !_pixelDouble; - // This y-translation is arbitrary, based on what roughly centers the - // videos in the game window - _drawRect.translate(-_drawRect.left / 2, -_drawRect.top * 2 / 3); - } - - if (useScreenDimensions) { + // GK1 CREDITS.AVI or KQ7 1.51 half-size videos + if ((g_sci->_gfxFrameout->_isHiRes && _decoder->getWidth() > 320) || + (g_sci->getGameId() == GID_KQ7 && getSciVersion() == SCI_VERSION_2_1_EARLY && _drawRect.width() <= 160)) { xRes = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; yRes = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; } else { xRes = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - yRes = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + const Ratio videoRatio(_decoder->getWidth(), _decoder->getHeight()); + const Ratio screenRatio(4, 3); + + // Videos that already have a 4:3 aspect ratio should not receive any + // aspect ratio correction + if (videoRatio == screenRatio) { + yRes = 240; + } else { + yRes = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + } } _plane = new Plane(_drawRect); g_sci->_gfxFrameout->addPlane(*_plane); if (_decoder->getPixelFormat().bytesPerPixel == 1) { - _segMan->allocateBitmap(&_bitmap, _decoder->getWidth(), _decoder->getHeight(), kDefaultSkipColor, 0, 0, xRes, yRes, 0, false, false); + SciBitmap &bitmap = *_segMan->allocateBitmap(&_bitmap, _decoder->getWidth(), _decoder->getHeight(), kDefaultSkipColor, 0, 0, xRes, yRes, 0, false, false); + bitmap.getBuffer().fillRect(Common::Rect(_decoder->getWidth(), _decoder->getHeight()), 0); CelInfo32 celInfo; celInfo.type = kCelTypeMem; celInfo.bitmap = _bitmap; - _screenItem = new ScreenItem(_plane->_object, celInfo, Common::Point(_drawRect.left, _drawRect.top), ScaleInfo()); + _screenItem = new ScreenItem(_plane->_object, celInfo, Common::Point(), ScaleInfo()); g_sci->_gfxFrameout->addScreenItem(*_screenItem); g_sci->_gfxFrameout->frameOut(true); } else { @@ -384,7 +372,7 @@ void AVIPlayer::renderFrame() const { const uint8 *end = (const uint8 *)surface->getPixels() + surface->w * surface->h; while (source != end) { - uint8 value = *source++; + const uint8 value = *source++; *target++ = value == 0 ? 255 : value; } } else { @@ -414,12 +402,19 @@ void AVIPlayer::renderFrame() const { } else { assert(surface->format.bytesPerPixel == 4); + const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + Common::Rect drawRect(_drawRect); + mulru(drawRect, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight), 1); if (_pixelDouble) { const uint32 *source = (const uint32 *)surface->getPixels(); uint32 *target = (uint32 *)_scaleBuffer; - // target pitch here is in uint32s, not bytes + // target pitch here is in uint32s, not bytes, because the surface + // bpp is 4 const uint16 pitch = surface->pitch / 2; for (int y = 0; y < surface->h; ++y) { for (int x = 0; x < surface->w; ++x) { @@ -434,17 +429,12 @@ void AVIPlayer::renderFrame() const { target += pitch; } - g_system->copyRectToScreen(_scaleBuffer, surface->pitch * 2, _drawRect.left, _drawRect.top, _drawRect.width(), _drawRect.height()); + g_system->copyRectToScreen(_scaleBuffer, surface->pitch * 2, drawRect.left, drawRect.top, _drawRect.width(), _drawRect.height()); } else { - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - - mulinc(drawRect, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight)); - g_system->copyRectToScreen(surface->getPixels(), surface->pitch, drawRect.left, drawRect.top, surface->w, surface->h); } + + g_system->updateScreen(); } } @@ -500,6 +490,7 @@ VMDPlayer::VMDPlayer(SegManager *segMan, EventManager *eventMan) : _isOpen(false), _isInitialized(false), + _yieldFrame(0), _yieldInterval(0), _lastYieldedFrameNo(0), @@ -512,6 +503,7 @@ VMDPlayer::VMDPlayer(SegManager *segMan, EventManager *eventMan) : _blackLines(false), _leaveScreenBlack(false), _leaveLastFrame(false), + _ignorePalettes(false), _blackoutPlane(nullptr), @@ -538,6 +530,10 @@ VMDPlayer::IOStatus VMDPlayer::open(const Common::String &fileName, const OpenFl error("Attempted to play %s, but another VMD was loaded", fileName.c_str()); } + if (g_sci->_features->VMDOpenStopsAudio()) { + g_sci->_audio32->stop(kAllChannels); + } + if (_decoder->loadFile(fileName)) { if (flags & kOpenFlagMute) { _decoder->setVolume(0); @@ -571,6 +567,7 @@ VMDPlayer::IOStatus VMDPlayer::close() { _decoder->close(); _isOpen = false; _isInitialized = false; + _ignorePalettes = false; if (!_planeIsOwned && _screenItem != nullptr) { g_sci->_gfxFrameout->deleteScreenItem(*_screenItem); @@ -625,12 +622,12 @@ VMDPlayer::VMDStatus VMDPlayer::getStatus() const { VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval) { assert(lastFrameNo >= -1); - const int32 maxFrameNo = (int32)(_decoder->getFrameCount() - 1); + const int32 maxFrameNo = _decoder->getFrameCount() - 1; - if ((flags & kEventFlagToFrame) && lastFrameNo > 0) { - _decoder->setEndFrame(MIN<int32>(lastFrameNo, maxFrameNo)); + if (flags & kEventFlagToFrame) { + _yieldFrame = MIN<int32>(lastFrameNo, maxFrameNo); } else { - _decoder->setEndFrame(maxFrameNo); + _yieldFrame = maxFrameNo; } if (flags & kEventFlagYieldToVM) { @@ -705,6 +702,7 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { reg_t bitmapId; SciBitmap &vmdBitmap = *_segMan->allocateBitmap(&bitmapId, vmdRect.width(), vmdRect.height(), 255, 0, 0, screenWidth, screenHeight, 0, false, false); + vmdBitmap.getBuffer().fillRect(Common::Rect(vmdRect.width(), vmdRect.height()), 0); if (screenWidth != scriptWidth || screenHeight != scriptHeight) { mulru(vmdRect, Ratio(scriptWidth, screenWidth), Ratio(scriptHeight, screenHeight), 1); @@ -767,6 +765,11 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { const int currentFrameNo = _decoder->getCurFrame(); + if (currentFrameNo == _yieldFrame) { + stopFlag = kEventFlagEnd; + break; + } + if (_yieldInterval > 0 && currentFrameNo != _lastYieldedFrameNo && (currentFrameNo % _yieldInterval) == 0 @@ -826,7 +829,7 @@ void VMDPlayer::renderFrame() const { // we are just submitting it directly here because the decoder exposes // this information a little bit differently than the one in SSCI const bool dirtyPalette = _decoder->hasDirtyPalette(); - if (dirtyPalette) { + if (dirtyPalette && !_ignorePalettes) { Palette palette; palette.timestamp = g_sci->getTickCount(); for (uint16 i = 0; i < _startColor; ++i) { diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index 75b8fb2d21..4fc627e674 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -326,6 +326,12 @@ private: bool _isInitialized; /** + * For VMDs played with the `kEventFlagToFrame` flag, + * the target frame for yielding back to the SCI VM. + */ + int32 _yieldFrame; + + /** * For VMDs played with the `kEventFlagYieldToVM` flag, * the number of frames that should be rendered until * yielding back to the SCI VM. @@ -347,6 +353,13 @@ private: #pragma mark - #pragma mark VMDPlayer - Rendering +public: + /** + * Causes the VMD player to ignore all palettes in + * the currently playing video. + */ + void ignorePalettes() { _ignorePalettes = true; } + private: /** * The location of the VMD plane, in game script @@ -408,6 +421,11 @@ private: bool _leaveLastFrame; /** + * Whether or not palettes from the VMD should be ignored. + */ + bool _ignorePalettes; + + /** * Renders a frame of video to the output bitmap. */ void renderFrame() const; diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp index a09ba8f3ce..a0f958167d 100644 --- a/engines/sci/parser/vocabulary.cpp +++ b/engines/sci/parser/vocabulary.cpp @@ -365,7 +365,7 @@ bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) { } } } - } while (changed && loopCount < 10); + } while (changed && loopCount++ < 10); return ret; } diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 8826b0625a..5b57eed123 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -2459,38 +2459,6 @@ void ResourceManager::detectSciVersion() { } } -bool ResourceManager::detectHires() { - // SCI 1.1 and prior is never hires - if (getSciVersion() <= SCI_VERSION_1_1) - return false; - -#ifdef ENABLE_SCI32 - for (int i = 0; i < 32768; i++) { - Resource *res = findResource(ResourceId(kResourceTypePic, i), 0); - - if (res) { - if (READ_SCI11ENDIAN_UINT16(res->data) == 0x0e) { - // SCI32 picture - uint16 width = READ_SCI11ENDIAN_UINT16(res->data + 10); - uint16 height = READ_SCI11ENDIAN_UINT16(res->data + 12); - // Surely lowres (e.g. QFG4CD) - if ((width == 320) && ((height == 190) || (height == 200))) - return false; - // Surely hires - if ((width >= 600) || (height >= 400)) - return true; - } - } - } - - // We haven't been able to find hires content - - return false; -#else - error("no sci32 support"); -#endif -} - bool ResourceManager::detectFontExtended() { Resource *res = findResource(ResourceId(kResourceTypeFont, 0), 0); diff --git a/engines/sci/resource.h b/engines/sci/resource.h index 70db5909b7..928d571dbc 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -424,7 +424,6 @@ private: public: #endif - bool detectHires(); // Detects, if standard font of current game includes extended characters (>0x80) bool detectFontExtended(); // Detects, if SCI1.1 game uses palette merging diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 246c031c06..e89d05217c 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -1166,7 +1166,9 @@ void SciEngine::syncIngameAudioOptions() { void SciEngine::updateScummVMAudioOptions() { // Update ScummVM's speech/subtitles settings for SCI1.1 CD games, // depending on the in-game settings - if (isCD() && getSciVersion() == SCI_VERSION_1_1) { + if ((isCD() && getSciVersion() == SCI_VERSION_1_1) || + getSciVersion() >= SCI_VERSION_2) { + uint16 ingameSetting = _gamestate->variables[VAR_GLOBAL][kGlobalVarMessageType].getOffset(); switch (ingameSetting) { diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index a62092f493..98958a3050 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -359,7 +359,7 @@ void Sound::playSound(int soundID) { _currentCDSound = soundID; } else { // All other sound types are ignored - warning("Scumm::Sound::playSound: encountered audio resoure with chunk type 'SOUN' and sound type %d", type); + warning("Scumm::Sound::playSound: encountered audio resource with chunk type 'SOUN' and sound type %d", type); } } else if ((_vm->_game.platform == Common::kPlatformMacintosh) && (_vm->_game.id == GID_INDY3) && READ_BE_UINT16(ptr + 8) == 0x1C) { diff --git a/engines/titanic/carry/arm.cpp b/engines/titanic/carry/arm.cpp index cbc14da477..1f77247e93 100644 --- a/engines/titanic/carry/arm.cpp +++ b/engines/titanic/carry/arm.cpp @@ -108,7 +108,7 @@ bool CArm::TranslateObjectMsg(CTranslateObjectMsg *msg) { bool CArm::UseWithOtherMsg(CUseWithOtherMsg *msg) { if (_string6 != "None") { - CShowTextMsg textMsg("The arm is already holding something."); + CShowTextMsg textMsg(ARM_ALREADY_HOLDING); textMsg.execute("PET"); return false; } else if (msg->_other->getName() == "GondolierLeftLever") { @@ -138,9 +138,10 @@ bool CArm::UseWithOtherMsg(CUseWithOtherMsg *msg) { bool CArm::MouseDragStartMsg(CMouseDragStartMsg *msg) { if (!_fieldE0) { - CShowTextMsg textMsg("You can't get this."); + CShowTextMsg textMsg(YOU_CANT_GET_THIS); textMsg.execute("PET"); } else if (checkStartDragging(msg)) { + hideMouse(); _tempPos = msg->_mousePos - _bounds; setPosition(msg->_mousePos - _tempPos); diff --git a/engines/titanic/carry/carry.cpp b/engines/titanic/carry/carry.cpp index 03798e8713..4f8df11260 100644 --- a/engines/titanic/carry/carry.cpp +++ b/engines/titanic/carry/carry.cpp @@ -25,6 +25,7 @@ #include "titanic/npcs/character.h" #include "titanic/npcs/succubus.h" #include "titanic/pet_control/pet_control.h" +#include "titanic/titanic.h" namespace Titanic { @@ -48,8 +49,8 @@ CCarry::CCarry() : CGameObject(), _fieldDC(0), _fieldE0(1), _itemFrame(0), _enterFrame(0), _enterFrameSet(false), _visibleFrame(0), _string1("None"), _fullViewName("NULL"), - _string3("That doesn't seem to do anything."), - _string4("It doesn't seem to want this.") { + _string3(g_vm->_strings[DOESNT_DO_ANYTHING]), + _string4(g_vm->_strings[DOESNT_WANT_THIS]) { } void CCarry::save(SimpleFile *file, int indent) { @@ -98,18 +99,19 @@ void CCarry::load(SimpleFile *file) { bool CCarry::MouseDragStartMsg(CMouseDragStartMsg *msg) { CString name = getName(); + debugC(ERROR_BASIC, kDebugScripts, "MosueDragStartMsg - %s", name.c_str()); if (_fieldE0) { - if (_visible) { - CShowTextMsg textMsg("You can't get this."); - textMsg.execute("PET"); - } - } else { if (checkStartDragging(msg)) { CPassOnDragStartMsg startMsg(msg->_mousePos); startMsg.execute(this); return true; } + } else { + if (_visible) { + CShowTextMsg textMsg(YOU_CANT_GET_THIS); + textMsg.execute("PET"); + } } return false; @@ -121,6 +123,9 @@ bool CCarry::MouseDragMoveMsg(CMouseDragMoveMsg *msg) { } bool CCarry::MouseDragEndMsg(CMouseDragEndMsg *msg) { + debugC(ERROR_BASIC, kDebugScripts, "MosueDragEndMsg"); + showMouse(); + if (msg->_dropTarget) { if (msg->_dropTarget->isPet()) { petAddToInventory(); @@ -224,6 +229,8 @@ bool CCarry::EnterViewMsg(CEnterViewMsg *msg) { } bool CCarry::PassOnDragStartMsg(CPassOnDragStartMsg *msg) { + hideMouse(); + if (_visibleFrame != -1) loadFrame(_visibleFrame); @@ -234,7 +241,7 @@ bool CCarry::PassOnDragStartMsg(CPassOnDragStartMsg *msg) { _tempPos = msg->_mousePos - _bounds; } - setPosition(_tempPos - getMousePos()); + setPosition(getMousePos() - _tempPos); return true; } diff --git a/engines/titanic/carry/carry_parrot.cpp b/engines/titanic/carry/carry_parrot.cpp index 57d82af78a..ed86384147 100644 --- a/engines/titanic/carry/carry_parrot.cpp +++ b/engines/titanic/carry/carry_parrot.cpp @@ -145,6 +145,7 @@ bool CCarryParrot::MouseDragEndMsg(CMouseDragEndMsg *msg) { } } + showMouse(); return true; } @@ -153,8 +154,8 @@ bool CCarryParrot::PassOnDragStartMsg(CPassOnDragStartMsg *msg) { moveToView(); setPosition(Point(0, 0)); setVisible(true); - playClip("Pick Up", 2); - playClip("Flapping", 1); + playClip("Pick Up", MOVIE_STOP_PREVIOUS); + playClip("Flapping", MOVIE_REPEAT); stopTimer(_timerId); _timerId = addTimer(1000, 1000); diff --git a/engines/titanic/carry/central_core.cpp b/engines/titanic/carry/central_core.cpp index e210b34cbe..afc3b85bf0 100644 --- a/engines/titanic/carry/central_core.cpp +++ b/engines/titanic/carry/central_core.cpp @@ -47,7 +47,7 @@ bool CCentralCore::UseWithOtherMsg(CUseWithOtherMsg *msg) { CPuzzleSolvedMsg solvedMsg; solvedMsg.execute("BigHammer"); } else if (name == "SpeechCentre") { - CShowTextMsg textMsg("This does not reach."); + CShowTextMsg textMsg(DOES_NOT_REACH); textMsg.execute("PET"); } diff --git a/engines/titanic/carry/chicken.cpp b/engines/titanic/carry/chicken.cpp index 0e8f6b3653..b80096b718 100644 --- a/engines/titanic/carry/chicken.cpp +++ b/engines/titanic/carry/chicken.cpp @@ -74,7 +74,7 @@ bool CChicken::UseWithOtherMsg(CUseWithOtherMsg *msg) { actMsg.execute(this); petAddToInventory(); } else { - CShowTextMsg textMsg("The chicken is already clean."); + CShowTextMsg textMsg(CHICKEN_ALREADY_CLEAN); textMsg.execute("PET"); } @@ -200,10 +200,12 @@ bool CChicken::ParrotTriesChickenMsg(CParrotTriesChickenMsg *msg) { } bool CChicken::MouseDragEndMsg(CMouseDragEndMsg *msg) { - if (_field13C) + if (_field13C) { + showMouse(); return true; - else + } else { return CCarry::MouseDragEndMsg(msg); + } } bool CChicken::PETObjectStateMsg(CPETObjectStateMsg *msg) { diff --git a/engines/titanic/carry/hose.cpp b/engines/titanic/carry/hose.cpp index e90119138a..3bfb4eae47 100644 --- a/engines/titanic/carry/hose.cpp +++ b/engines/titanic/carry/hose.cpp @@ -22,6 +22,7 @@ #include "titanic/carry/hose.h" #include "titanic/npcs/succubus.h" +#include "titanic/titanic.h" namespace Titanic { @@ -44,7 +45,7 @@ void CHose::deinit() { } CHose::CHose() : CCarry(), - _string6("Succ-U-Bus auxiliary hose attachment incompatible with sliding glass cover.") { + _string6(g_vm->_strings[HOSE_INCOMPATIBLE]) { } void CHose::save(SimpleFile *file, int indent) { diff --git a/engines/titanic/carry/perch.cpp b/engines/titanic/carry/perch.cpp index 4f0e76bdb0..f432dafa85 100644 --- a/engines/titanic/carry/perch.cpp +++ b/engines/titanic/carry/perch.cpp @@ -40,7 +40,7 @@ void CPerch::load(SimpleFile *file) { bool CPerch::UseWithOtherMsg(CUseWithOtherMsg *msg) { if (msg->_other->isEquals("SpeechCentre")) { - CShowTextMsg textMsg("This does not reach."); + CShowTextMsg textMsg(DOES_NOT_REACH); textMsg.execute("PET"); } diff --git a/engines/titanic/carry/plug_in.cpp b/engines/titanic/carry/plug_in.cpp index 438b9a5883..55767ce9a8 100644 --- a/engines/titanic/carry/plug_in.cpp +++ b/engines/titanic/carry/plug_in.cpp @@ -50,7 +50,7 @@ bool CPlugIn::UseWithOtherMsg(CUseWithOtherMsg *msg) { if (otherName == "PET") { return CCarry::UseWithOtherMsg(msg); } else if (isEquals("DatasideTransporter")) { - CShowTextMsg textMsg("This item is incorrectly calibrated."); + CShowTextMsg textMsg(INCORRECTLY_CALIBRATED); textMsg.execute("PET"); } else if (isEquals("DatasideTransporter")) { error("TODO: Set msg->_other->fieldC4 = 2"); diff --git a/engines/titanic/continue_save_dialog.cpp b/engines/titanic/continue_save_dialog.cpp index 39b7d1942a..0982cab408 100644 --- a/engines/titanic/continue_save_dialog.cpp +++ b/engines/titanic/continue_save_dialog.cpp @@ -54,9 +54,10 @@ CContinueSaveDialog::~CContinueSaveDialog() { } void CContinueSaveDialog::addSavegame(int slot, const CString &name) { - assert(_saves.size() < SAVEGAME_SLOTS_COUNT); - _slotNames[_saves.size()].setText(name); - _saves.push_back(SaveEntry(slot, name)); + if (_saves.size() < SAVEGAME_SLOTS_COUNT) { + _slotNames[_saves.size()].setText(name); + _saves.push_back(SaveEntry(slot, name)); + } } Rect CContinueSaveDialog::getSlotBounds(int index) { diff --git a/engines/titanic/core/background.cpp b/engines/titanic/core/background.cpp index 733dfc1cf3..792cd28346 100644 --- a/engines/titanic/core/background.cpp +++ b/engines/titanic/core/background.cpp @@ -58,7 +58,7 @@ void CBackground::load(SimpleFile *file) { bool CBackground::StatusChangeMsg(CStatusChangeMsg *msg) { setVisible(true); if (_fieldDC) { - playMovie(_startFrame, _endFrame, 16); + playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE); } else { playMovie(_startFrame, _endFrame, 0); } diff --git a/engines/titanic/core/game_object.cpp b/engines/titanic/core/game_object.cpp index 8f71b3d995..76a2f2d5c0 100644 --- a/engines/titanic/core/game_object.cpp +++ b/engines/titanic/core/game_object.cpp @@ -403,8 +403,11 @@ void CGameObject::loadImage(const CString &name, bool pendingFlag) { void CGameObject::loadFrame(int frameNumber) { _frameNumber = -1; - if (!_resource.empty()) + + if (!_surface && !_resource.empty()) { loadResource(_resource); + _resource.clear(); + } if (_surface) _surface->setMovieFrame(frameNumber); @@ -612,9 +615,8 @@ void CGameObject::playMovie(uint flags) { void CGameObject::playMovie(int startFrame, int endFrame, uint flags) { _frameNumber = -1; - if (!_surface) { - if (!_resource.empty()) - loadResource(_resource); + if (!_surface && !_resource.empty()) { + loadResource(_resource); _resource.clear(); } @@ -630,9 +632,8 @@ void CGameObject::playMovie(int startFrame, int endFrame, uint flags) { void CGameObject::playMovie(int startFrame, int endFrame, int initialFrame, uint flags) { _frameNumber = -1; - if (!_surface) { - if (!_resource.empty()) - loadResource(_resource); + if (!_surface && !_resource.empty()) { + loadResource(_resource); _resource.clear(); } @@ -645,6 +646,8 @@ void CGameObject::playMovie(int startFrame, int endFrame, int initialFrame, uint } void CGameObject::playClip(const CString &name, uint flags) { + debugC(ERROR_DETAILED, kDebugScripts, "playClip - %s", name.c_str()); + _frameNumber = -1; CMovieClip *clip = _movieClips.findByName(name); if (clip) @@ -652,6 +655,8 @@ void CGameObject::playClip(const CString &name, uint flags) { } void CGameObject::playClip(uint startFrame, uint endFrame) { + debugC(ERROR_DETAILED, kDebugScripts, "playClip - %d to %d", startFrame, endFrame); + CMovieClip *clip = new CMovieClip("", startFrame, endFrame); CGameManager *gameManager = getGameManager(); CRoomItem *room = gameManager->getRoom(); @@ -753,7 +758,7 @@ int CGameObject::playSound(const CString &name, CProximity &prox) { if (gameManager && !name.empty()) { g_vm->_filesManager->preload(name); - gameManager->_sound.playSound(name, prox); + return gameManager->_sound.playSound(name, prox); } return -1; @@ -864,7 +869,8 @@ CViewItem *CGameObject::parseView(const CString &viewString) { if (project) { if (room->getName() != roomName) { // Scan for the correct room - for (room = project->findFirstRoom(); room && room->getName() != roomName; + for (room = project->findFirstRoom(); + room && room->getName() != roomName; room = project->findNextRoom(room)) ; } } @@ -912,12 +918,12 @@ Point CGameObject::getMousePos() const { } bool CGameObject::compareViewNameTo(const CString &name) const { - return getViewFullName().compareToIgnoreCase(name); + return !getViewFullName().compareToIgnoreCase(name); } int CGameObject::compareRoomNameTo(const CString &name) { CRoomItem *room = getGameManager()->getRoom(); - return room->getName().compareToIgnoreCase(name); + return !room->getName().compareToIgnoreCase(name); } CString CGameObject::getRoomName() const { @@ -1142,11 +1148,11 @@ void CGameObject::lockMouse() { } void CGameObject::hideMouse() { - CScreenManager::_screenManagerPtr->_mouseCursor->hide(); + CScreenManager::_screenManagerPtr->_mouseCursor->incHideCounter(); } void CGameObject::showMouse() { - CScreenManager::_screenManagerPtr->_mouseCursor->show(); + CScreenManager::_screenManagerPtr->_mouseCursor->decHideCounter(); } void CGameObject::disableMouse() { @@ -1159,12 +1165,12 @@ void CGameObject::enableMouse() { showMouse(); } -void CGameObject::mouseLockE4() { - CScreenManager::_screenManagerPtr->_mouseCursor->lockE4(); +void CGameObject::mouseDisableControl() { + CScreenManager::_screenManagerPtr->_mouseCursor->disableControl(); } -void CGameObject::mouseUnlockE4() { - CScreenManager::_screenManagerPtr->_mouseCursor->unlockE4(); +void CGameObject::mouseEnableControl() { + CScreenManager::_screenManagerPtr->_mouseCursor->enableControl(); } void CGameObject::mouseSetPosition(const Point &pt, double rate) { @@ -1330,17 +1336,17 @@ CMusicRoom *CGameObject::getMusicRoom() const { return gameManager ? &gameManager->_musicRoom : nullptr; } -int CGameObject::getPassengerClass() const { +PassengerClass CGameObject::getPassengerClass() const { CGameManager *gameManager = getGameManager(); - return gameManager ? gameManager->_gameState._passengerClass : 3; + return gameManager ? gameManager->_gameState._passengerClass : THIRD_CLASS; } -int CGameObject::getPriorClass() const { +PassengerClass CGameObject::getPriorClass() const { CGameManager *gameManager = getGameManager(); - return gameManager ? gameManager->_gameState._priorClass : 3; + return gameManager ? gameManager->_gameState._priorClass : THIRD_CLASS; } -void CGameObject::setPassengerClass(int newClass) { +void CGameObject::setPassengerClass(PassengerClass newClass) { if (newClass >= 1 && newClass <= 4) { // Change the passenger class CGameManager *gameMan = getGameManager(); @@ -1575,7 +1581,7 @@ void CGameObject::petMoveToHiddenRoom() { } } -void CGameObject::petReassignRoom(int passClassNum) { +void CGameObject::petReassignRoom(PassengerClass passClassNum) { CPetControl *petControl = getPetControl(); if (petControl) petControl->reassignRoom(passClassNum); @@ -1607,7 +1613,7 @@ void CGameObject::petSetRooms1D4(int v) { void CGameObject::petOnSummonBot(const CString &name, int val) { CPetControl *pet = getPetControl(); if (pet) - pet->summonBot(name, val); + pet->onSummonBot(name, val); } void CGameObject::petUnlockInput() { @@ -1654,7 +1660,7 @@ void CGameObject::startTalking(CTrueTalkNPC *npc, uint id, CViewItem *view) { } } -void CGameObject::endTalking(CTrueTalkNPC *npc, bool viewFlag, CViewItem *view) { +void CGameObject::setTalking(CTrueTalkNPC *npc, bool viewFlag, CViewItem *view) { CPetControl *pet = getPetControl(); if (pet) pet->setActiveNPC(npc); diff --git a/engines/titanic/core/game_object.h b/engines/titanic/core/game_object.h index b50278cf21..ae35e2ba5b 100644 --- a/engines/titanic/core/game_object.h +++ b/engines/titanic/core/game_object.h @@ -165,8 +165,15 @@ protected: */ void enableMouse(); - void mouseLockE4(); - void mouseUnlockE4(); + /** + * Disables user control of the mouse + */ + void mouseDisableControl(); + + /** + * Re-enables user control of the mouse + */ + void mouseEnableControl(); /** * Sets the mouse to a new position @@ -445,7 +452,7 @@ protected: /** * Set's the player's passenger class */ - void setPassengerClass(int newClass); + void setPassengerClass(PassengerClass newClass); /** * Overrides whether the object's movie has audio timing @@ -705,12 +712,12 @@ public: /** * Return the player's passenger class */ - int getPassengerClass() const; + PassengerClass getPassengerClass() const; /** * Return the player's previous passenger class */ - int getPriorClass() const; + PassengerClass getPriorClass() const; /** * Sets the mail identifier for an object @@ -877,7 +884,7 @@ public: /** * Gives the player a new assigned room in the specified passenger class */ - void petReassignRoom(int passClassNum); + void petReassignRoom(PassengerClass passClassNum); /** * Sets a new area in the PET @@ -925,19 +932,20 @@ public: /*--- CTrueTalkManager Methods ---*/ /** - * Stop a conversation with the NPC + * Start a conversation with the NPC */ - void endTalking(CTrueTalkNPC *npc, bool viewFlag, CViewItem *view = nullptr); + void startTalking(CTrueTalkNPC *npc, uint id, CViewItem *view = nullptr); /** * Start a conversation with the NPC */ - void startTalking(CTrueTalkNPC *npc, uint id, CViewItem *view = nullptr); + void startTalking(const CString &name, uint id, CViewItem *view = nullptr); /** * Start a conversation with the NPC */ - void startTalking(const CString &name, uint id, CViewItem *view = nullptr); + void setTalking(CTrueTalkNPC *npc, bool viewFlag, CViewItem *view = nullptr); + /** * Sets a dial region for a given NPC diff --git a/engines/titanic/core/project_item.cpp b/engines/titanic/core/project_item.cpp index 65e8645baa..af67f69580 100644 --- a/engines/titanic/core/project_item.cpp +++ b/engines/titanic/core/project_item.cpp @@ -372,15 +372,20 @@ CTreeItem *CProjectItem::findChildInstance(ClassDef *classDef) const { } CRoomItem *CProjectItem::findNextRoom(CRoomItem *priorRoom) const { - return dynamic_cast<CRoomItem *>(findSiblingInstanceOf(CRoomItem::_type, priorRoom)); + return dynamic_cast<CRoomItem *>(findSiblingChildInstanceOf(CRoomItem::_type, priorRoom)); } -CTreeItem *CProjectItem::findSiblingInstanceOf(ClassDef *classDef, CTreeItem *startItem) const { - CTreeItem *treeItem = startItem->getParent()->getNextSibling(); - if (treeItem == nullptr) - return nullptr; +CTreeItem *CProjectItem::findSiblingChildInstanceOf(ClassDef *classDef, CTreeItem *startItem) const { + for (CTreeItem *treeItem = startItem->getParent()->getNextSibling(); + treeItem; treeItem = treeItem->getNextSibling()) { + for (CTreeItem *childItem = treeItem->getFirstChild(); + childItem; childItem = childItem->getNextSibling()) { + if (childItem->isInstanceOf(classDef)) + return childItem; + } + } - return findChildInstance(classDef); + return nullptr; } CDontSaveFileItem *CProjectItem::getDontSaveFileItem() const { diff --git a/engines/titanic/core/project_item.h b/engines/titanic/core/project_item.h index 4d009dd50b..20c4a4377a 100644 --- a/engines/titanic/core/project_item.h +++ b/engines/titanic/core/project_item.h @@ -118,7 +118,7 @@ private: /** * Finds the next sibling occurance of a given class type */ - CTreeItem *findSiblingInstanceOf(ClassDef *classDef, CTreeItem *startItem) const; + CTreeItem *findSiblingChildInstanceOf(ClassDef *classDef, CTreeItem *startItem) const; private: /** * Load project data from the passed file diff --git a/engines/titanic/core/saveable_object.cpp b/engines/titanic/core/saveable_object.cpp index 7522a34737..73b4bf861f 100644 --- a/engines/titanic/core/saveable_object.cpp +++ b/engines/titanic/core/saveable_object.cpp @@ -421,14 +421,14 @@ CSaveableObject *ClassDef::create() { /*------------------------------------------------------------------------*/ -Common::HashMap<Common::String, CSaveableObject::CreateFunction> * - CSaveableObject::_classList = nullptr; -Common::List<ClassDef *> *CSaveableObject::_classDefs; +CSaveableObject::ClassListMap *CSaveableObject::_classList; +CSaveableObject::ClassDefList *CSaveableObject::_classDefs; #define DEFFN(T) CSaveableObject *Function##T() { return new T(); } \ ClassDef *T::_type #define ADDFN(CHILD, PARENT) \ CHILD::_type = new TypeTemplate<CHILD>(#CHILD, PARENT::_type); \ + _classDefs->push_back(CHILD::_type); \ (*_classList)[#CHILD] = Function##CHILD DEFFN(CArm); @@ -835,6 +835,7 @@ DEFFN(CMouseDragMsg); DEFFN(CMouseDragStartMsg); DEFFN(CMouseDragMoveMsg); DEFFN(CMouseDragEndMsg); +DEFFN(CMouseWheelMsg); DEFFN(CMoveToStartPosMsg); DEFFN(CMovieEndMsg); DEFFN(CMovieFrameMsg); @@ -1019,8 +1020,8 @@ DEFFN(CStarControl); DEFFN(CTimeEventInfo); void CSaveableObject::initClassList() { - _classDefs = new Common::List<ClassDef *>(); - _classList = new Common::HashMap<Common::String, CreateFunction>(); + _classDefs = new ClassDefList(); + _classList = new ClassListMap(); ADDFN(CArm, CCarry); ADDFN(CAuditoryCentre, CBrain); ADDFN(CBowlEar, CEar); @@ -1426,6 +1427,7 @@ void CSaveableObject::initClassList() { ADDFN(CMouseDragStartMsg, CMouseDragMsg); ADDFN(CMouseDragMoveMsg, CMouseDragMsg); ADDFN(CMouseDragEndMsg, CMouseDragMsg); + ADDFN(CMouseWheelMsg, CMouseMsg); ADDFN(CMoveToStartPosMsg, CMessage); ADDFN(CMovieEndMsg, CMessage); ADDFN(CMovieFrameMsg, CMessage); @@ -1615,7 +1617,7 @@ void CSaveableObject::initClassList() { } void CSaveableObject::freeClassList() { - Common::List<ClassDef *>::iterator i; + ClassDefList::iterator i; for (i = _classDefs->begin(); i != _classDefs->end(); ++i) delete *i; diff --git a/engines/titanic/core/saveable_object.h b/engines/titanic/core/saveable_object.h index 4c7c1a7737..80525f1156 100644 --- a/engines/titanic/core/saveable_object.h +++ b/engines/titanic/core/saveable_object.h @@ -59,8 +59,10 @@ public: class CSaveableObject { typedef CSaveableObject *(*CreateFunction)(); private: - static Common::List<ClassDef *> *_classDefs; - static Common::HashMap<Common::String, CreateFunction> *_classList; + typedef Common::List<ClassDef *> ClassDefList; + typedef Common::HashMap<Common::String, CreateFunction> ClassListMap; + static ClassDefList *_classDefs; + static ClassListMap *_classList; public: /** * Sets up the list of saveable object classes diff --git a/engines/titanic/core/view_item.cpp b/engines/titanic/core/view_item.cpp index 9b20860a46..aa1ff7108e 100644 --- a/engines/titanic/core/view_item.cpp +++ b/engines/titanic/core/view_item.cpp @@ -270,7 +270,7 @@ bool CViewItem::handleMouseMsg(CMouseMsg *msg, bool flag) { if (gameObjects.size() == 0) return false; - for (int idx = (int)gameObjects.size() - 1; idx >= 0; ++idx) { + for (int idx = (int)gameObjects.size() - 1; idx >= 0; --idx) { if (gameObjects[idx]->_cursorId != CURSOR_IGNORE) { CScreenManager::_screenManagerPtr->_mouseCursor->setCursor(gameObjects[idx]->_cursorId); break; diff --git a/engines/titanic/events.cpp b/engines/titanic/events.cpp index fa057de432..97f9a86eb3 100644 --- a/engines/titanic/events.cpp +++ b/engines/titanic/events.cpp @@ -31,7 +31,7 @@ namespace Titanic { Events::Events(TitanicEngine *vm): _vm(vm), _frameCounter(1), - _priorFrameTime(0) { + _priorFrameTime(0), _specialButtons(0) { } void Events::pollEvents() { @@ -47,33 +47,46 @@ void Events::pollEvents() { eventTarget()->mouseMove(_mousePos); break; case Common::EVENT_LBUTTONDOWN: + _specialButtons |= MK_LBUTTON; _mousePos = event.mouse; eventTarget()->leftButtonDown(_mousePos); break; case Common::EVENT_LBUTTONUP: + _specialButtons &= ~MK_LBUTTON; _mousePos = event.mouse; eventTarget()->leftButtonUp(_mousePos); break; case Common::EVENT_MBUTTONDOWN: + _specialButtons |= MK_MBUTTON; _mousePos = event.mouse; eventTarget()->middleButtonDown(_mousePos); break; case Common::EVENT_MBUTTONUP: + _specialButtons &= ~MK_MBUTTON; _mousePos = event.mouse; eventTarget()->middleButtonUp(_mousePos); break; case Common::EVENT_RBUTTONDOWN: + _specialButtons |= MK_RBUTTON; _mousePos = event.mouse; eventTarget()->rightButtonDown(_mousePos); break; case Common::EVENT_RBUTTONUP: + _specialButtons &= ~MK_RBUTTON; _mousePos = event.mouse; eventTarget()->rightButtonUp(_mousePos); break; + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + _mousePos = event.mouse; + eventTarget()->mouseWheel(_mousePos, event.type == Common::EVENT_WHEELUP); + break; case Common::EVENT_KEYDOWN: + handleKbdSpecial(event.kbd); eventTarget()->keyDown(event.kbd); break; case Common::EVENT_KEYUP: + handleKbdSpecial(event.kbd); eventTarget()->keyUp(event.kbd); break; default: @@ -125,31 +138,34 @@ void Events::sleep(uint time) { } bool Events::waitForPress(uint expiry) { - CGameManager *gameManager = g_vm->_window->_gameManager; uint32 delayEnd = g_system->getMillis() + expiry; + CPressTarget pressTarget; + addTarget(&pressTarget); - while (!_vm->shouldQuit() && g_system->getMillis() < delayEnd) { - g_system->delayMillis(10); - checkForNextFrameCounter(); - - // Regularly update the sound mixer - if (gameManager) - gameManager->_sound.updateMixer(); - - Common::Event event; - if (g_system->getEventManager()->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_LBUTTONDOWN: - case Common::EVENT_MBUTTONDOWN: - case Common::EVENT_KEYDOWN: - return true; - default: - break; - } - } + while (!_vm->shouldQuit() && g_system->getMillis() < delayEnd && !pressTarget._pressed) { + pollEventsAndWait(); } - return false; + removeTarget(); + return pressTarget._pressed; +} + +void Events::setMousePos(const Common::Point &pt) { + g_system->warpMouse(pt.x, pt.y); + _mousePos = pt; + eventTarget()->mouseMove(_mousePos); +} + +void Events::handleKbdSpecial(Common::KeyState keyState) { + if (keyState.flags & Common::KBD_CTRL) + _specialButtons |= MK_CONTROL; + else + _specialButtons &= ~MK_CONTROL; + + if (keyState.flags & Common::KBD_SHIFT) + _specialButtons |= MK_SHIFT; + else + _specialButtons &= ~MK_SHIFT; } } // End of namespace Titanic diff --git a/engines/titanic/events.h b/engines/titanic/events.h index 3ea9b63217..52e900c2ef 100644 --- a/engines/titanic/events.h +++ b/engines/titanic/events.h @@ -65,10 +65,26 @@ public: virtual void middleButtonDoubleClick(const Point &mousePos) {} virtual void rightButtonDown(const Point &mousePos) {} virtual void rightButtonUp(const Point &mousePos) {} + virtual void mouseWheel(const Point &mousePos, bool wheelUp) {} virtual void keyDown(Common::KeyState keyState) {} virtual void keyUp(Common::KeyState keyState) {} }; +/** + * An eent target used for waiting for a mouse or keypress + */ +class CPressTarget : public CEventTarget { +public: + bool _pressed; +public: + CPressTarget() : _pressed(false) {} + virtual ~CPressTarget() {} + virtual void leftButtonDown(const Point &mousePos) { _pressed = true; } + virtual void middleButtonDown(const Point &mousePos) { _pressed = true; } + virtual void rightButtonDown(const Point &mousePos) { _pressed = true; } + virtual void keyDown(Common::KeyState keyState) { _pressed = true; } +}; + class Events { private: TitanicEngine *_vm; @@ -76,6 +92,7 @@ private: uint32 _frameCounter; uint32 _priorFrameTime; Common::Point _mousePos; + uint _specialButtons; /** * Check whether it's time to display the next screen frame @@ -88,6 +105,11 @@ private: CEventTarget *eventTarget() const { return _eventTargets.top(); } + + /** + * Handles setting/resettings special buttons on key up/down + */ + void handleKbdSpecial(Common::KeyState keyState); public: Events(TitanicEngine *vm); ~Events() {} @@ -138,6 +160,28 @@ public: * Wait for a mouse or keypress */ bool waitForPress(uint expiry); + + /** + * Get the mouse position + */ + Common::Point getMousePos() const { return _mousePos; } + + /** + * Sets the mouse position + */ + void setMousePos(const Common::Point &pt); + + /* + * Return whether a given special key is currently pressed + */ + bool isSpecialPressed(SpecialButtons btn) const { + return (_specialButtons & btn) != 0; + } + + /** + * Returns the bitset of the currently pressed special buttons + */ + uint getSpecialButtons() const { return _specialButtons; } }; } // End of namespace Titanic diff --git a/engines/titanic/game/cdrom.cpp b/engines/titanic/game/cdrom.cpp index cd913d05f7..0d1cd3a6f2 100644 --- a/engines/titanic/game/cdrom.cpp +++ b/engines/titanic/game/cdrom.cpp @@ -50,6 +50,7 @@ void CCDROM::load(SimpleFile *file) { bool CCDROM::MouseDragStartMsg(CMouseDragStartMsg *msg) { if (checkStartDragging(msg)) { + hideMouse(); _tempPos = msg->_mousePos - _bounds; setPosition(msg->_mousePos - _tempPos); return true; @@ -59,6 +60,8 @@ bool CCDROM::MouseDragStartMsg(CMouseDragStartMsg *msg) { } bool CCDROM::MouseDragEndMsg(CMouseDragEndMsg *msg) { + showMouse(); + if (msg->_dropTarget && msg->_dropTarget->getName() == "newComputer") { CCDROMTray *newTray = dynamic_cast<CCDROMTray *>(getRoom()->findByName("newTray")); diff --git a/engines/titanic/game/cdrom_tray.cpp b/engines/titanic/game/cdrom_tray.cpp index 505bdad319..d6aa32c702 100644 --- a/engines/titanic/game/cdrom_tray.cpp +++ b/engines/titanic/game/cdrom_tray.cpp @@ -95,6 +95,7 @@ bool CCDROMTray::ActMsg(CActMsg *msg) { playSound("a#35.wav", 50, 0, 0); } else if (msg->_action == "newSTCD") { // Starship Titanic CD dropped on CDROM Tray + disableMouse(); playMovie(11, 21, MOVIE_NOTIFY_OBJECT); playSound("a#35.wav", 50, 0, 0); } else { diff --git a/engines/titanic/game/computer.cpp b/engines/titanic/game/computer.cpp index 3077b46178..9aa5db252c 100644 --- a/engines/titanic/game/computer.cpp +++ b/engines/titanic/game/computer.cpp @@ -54,7 +54,7 @@ bool CComputer::ActMsg(CActMsg *msg) { else if (msg->_action == "CD2") playMovie(50, 79, 0); else if (msg->_action == "STCD") - playMovie(80, 90, 4); + playMovie(80, 90, MOVIE_NOTIFY_OBJECT); _currentCD = msg->_action; _state = 0; diff --git a/engines/titanic/game/maitred/maitred_prod_receptor.cpp b/engines/titanic/game/maitred/maitred_prod_receptor.cpp index 95e029af44..6f64a76d7f 100644 --- a/engines/titanic/game/maitred/maitred_prod_receptor.cpp +++ b/engines/titanic/game/maitred/maitred_prod_receptor.cpp @@ -81,7 +81,7 @@ bool CMaitreDProdReceptor::MouseMoveMsg(CMouseMoveMsg *msg) { prodMsg._value = 125; CMaitreD *maitreD = dynamic_cast<CMaitreD *>(findRoomObject("MaitreD")); - if (maitreD && maitreD->_field100 <= 0) + if (maitreD && maitreD->_speechCounter == 0) prodMsg.execute(this); return true; @@ -90,7 +90,7 @@ bool CMaitreDProdReceptor::MouseMoveMsg(CMouseMoveMsg *msg) { bool CMaitreDProdReceptor::ProdMaitreDMsg(CProdMaitreDMsg *msg) { if (_fieldC4) { CMaitreD *maitreD = static_cast<CMaitreD *>(findRoomObject("MaitreD")); - if (maitreD->_field100 <= 0) { + if (maitreD->_speechCounter == 0) { CViewItem *view = findView(); startTalking(maitreD, msg->_value, view); diff --git a/engines/titanic/game/pet/pet.cpp b/engines/titanic/game/pet/pet.cpp index 99c9e01eb3..235d68f242 100644 --- a/engines/titanic/game/pet/pet.cpp +++ b/engines/titanic/game/pet/pet.cpp @@ -62,7 +62,7 @@ void CPET::load(SimpleFile *file) { bool CPET::ShowTextMsg(CShowTextMsg *msg) { CPetControl *pet = getPetControl(); if (pet) - pet->petDisplayMessage(1, msg->_value); + pet->petDisplayMessage(1, msg->_message); return true; } diff --git a/engines/titanic/game/pet/pet_lift.cpp b/engines/titanic/game/pet/pet_lift.cpp index a7b48853e6..75a48a6a6c 100644 --- a/engines/titanic/game/pet/pet_lift.cpp +++ b/engines/titanic/game/pet/pet_lift.cpp @@ -50,8 +50,8 @@ bool CPETLift::TransportMsg(CTransportMsg *msg) { } else if (msg->_roomName == "BottomOfWell") { floorNum = 39; } else if (msg->_roomName == "PlayersRoom" && pet) { - int assignedFloor = pet->getAssignedFloorNum(); - if (assignedFloor < 1 || assignedFloor > 39) { + floorNum = pet->getAssignedFloorNum(); + if (floorNum < 1 || floorNum > 39) { pet->petDisplayMessage(NO_ROOM_ASSIGNED); floorNum = -1; } diff --git a/engines/titanic/game/pet/pet_position.cpp b/engines/titanic/game/pet/pet_position.cpp index d3d030eb16..db6f9997c9 100644 --- a/engines/titanic/game/pet/pet_position.cpp +++ b/engines/titanic/game/pet/pet_position.cpp @@ -171,8 +171,8 @@ bool CPETPosition::EnterViewMsg(CEnterViewMsg *msg) { bool CPETPosition::LeaveViewMsg(CLeaveViewMsg *msg) { CPetControl *pet = getPetControl(); - CString oldView = msg->_oldView->getName(); - CString newView = msg->_newView->getName(); + CString oldView = msg->_oldView->getFullViewName(); + CString newView = msg->_newView->getFullViewName(); if (pet && newView == "Lift.Node 1.N") { int elevatorNum = pet->getRoomsElevatorNum(); diff --git a/engines/titanic/game/pet/pet_transport.cpp b/engines/titanic/game/pet/pet_transport.cpp index a48e70ed01..29dce6c1b6 100644 --- a/engines/titanic/game/pet/pet_transport.cpp +++ b/engines/titanic/game/pet/pet_transport.cpp @@ -26,6 +26,7 @@ namespace Titanic { BEGIN_MESSAGE_MAP(CPETTransport, CGameObject) ON_MESSAGE(EnterRoomMsg) + ON_MESSAGE(LeaveRoomMsg) END_MESSAGE_MAP() void CPETTransport::save(SimpleFile *file, int indent) { @@ -39,6 +40,11 @@ void CPETTransport::load(SimpleFile *file) { } bool CPETTransport::EnterRoomMsg(CEnterRoomMsg *msg) { + petSetRemoteTarget(); + return true; +} + +bool CPETTransport::LeaveRoomMsg(CLeaveRoomMsg *msg) { petClear(); return true; } diff --git a/engines/titanic/game/pet/pet_transport.h b/engines/titanic/game/pet/pet_transport.h index 58aefe6743..6a08c4bdf3 100644 --- a/engines/titanic/game/pet/pet_transport.h +++ b/engines/titanic/game/pet/pet_transport.h @@ -31,6 +31,7 @@ namespace Titanic { class CPETTransport : public CGameObject { DECLARE_MESSAGE_MAP; virtual bool EnterRoomMsg(CEnterRoomMsg *msg); + virtual bool LeaveRoomMsg(CLeaveRoomMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/game/service_elevator_window.cpp b/engines/titanic/game/service_elevator_window.cpp index 13db7a26f8..d548bbf37e 100644 --- a/engines/titanic/game/service_elevator_window.cpp +++ b/engines/titanic/game/service_elevator_window.cpp @@ -35,25 +35,25 @@ END_MESSAGE_MAP() static const int FACTORS[4] = { 0, 20, 100, 0 }; CServiceElevatorWindow::CServiceElevatorWindow() : CBackground(), - _fieldE0(0), _fieldE4(0), _fieldE8(0), _fieldEC(0) { + _destFloor(0), _notifyFlag(false), _isIndicator(false), _intoSpace(false) { } void CServiceElevatorWindow::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_fieldE0, indent); - file->writeNumberLine(_fieldE4, indent); - file->writeNumberLine(_fieldE8, indent); - file->writeNumberLine(_fieldEC, indent); + file->writeNumberLine(_destFloor, indent); + file->writeNumberLine(_notifyFlag, indent); + file->writeNumberLine(_isIndicator, indent); + file->writeNumberLine(_intoSpace, indent); CBackground::save(file, indent); } void CServiceElevatorWindow::load(SimpleFile *file) { file->readNumber(); - _fieldE0 = file->readNumber(); - _fieldE4 = file->readNumber(); - _fieldE8 = file->readNumber(); - _fieldEC = file->readNumber(); + _destFloor = file->readNumber(); + _notifyFlag = file->readNumber(); + _isIndicator = file->readNumber(); + _intoSpace = file->readNumber(); CBackground::load(file); } @@ -61,39 +61,42 @@ void CServiceElevatorWindow::load(SimpleFile *file) { bool CServiceElevatorWindow::ServiceElevatorFloorChangeMsg(CServiceElevatorFloorChangeMsg *msg) { if (getView() == findView()) { CDoorbot *doorbot = dynamic_cast<CDoorbot *>(findRoom()->findByName("Doorbot")); - int val = (_fieldE8 && doorbot) ? 65 : 15; + int fps = (_isIndicator && doorbot) ? 65 : 15; CMovieClip *clip = _movieClips.findByName("Going Up"); if (!clip) return true; - int count = _endFrame - _startFrame; - setMovieFrameRate(1.0 * count / val); + int count = clip->_endFrame - clip->_startFrame; + setMovieFrameRate(1.0 * count / fps); - int startFrame = clip->_startFrame + count * FACTORS[msg->_value1] / 100; - int endFrame = clip->_startFrame + count * FACTORS[msg->_value2] / 100; + int startFrame = clip->_startFrame + count * FACTORS[msg->_startFloor] / 100; + int endFrame = clip->_startFrame + count * FACTORS[msg->_endFloor] / 100; - if (_fieldE4) { + if (_notifyFlag) { + // Service elevator indicator playMovie(startFrame, endFrame, MOVIE_NOTIFY_OBJECT); } else { + // Background outside elevator playMovie(startFrame, endFrame, 0); - if (_fieldEC) + if (_intoSpace) playClip("Into Space"); } } - _fieldE0 = msg->_value2; + _destFloor = msg->_endFloor; return true; } bool CServiceElevatorWindow::MovieEndMsg(CMovieEndMsg *msg) { + // Called when indicator reaches desired destination floor CServiceElevatorMsg elevMsg(5); elevMsg.execute(findRoom()->findByName("Service Elevator Entity")); return true; } bool CServiceElevatorWindow::EnterViewMsg(CEnterViewMsg *msg) { - if (_fieldEC) { + if (_intoSpace) { playClip("Fade Up"); playMovie(1, 2, 0); } else { @@ -101,7 +104,7 @@ bool CServiceElevatorWindow::EnterViewMsg(CEnterViewMsg *msg) { if (clip) { int frameNum = clip->_startFrame + (clip->_endFrame - clip->_startFrame) - * FACTORS[_fieldE0] / 100; + * FACTORS[_destFloor] / 100; loadFrame(frameNum); } else { loadFrame(0); diff --git a/engines/titanic/game/service_elevator_window.h b/engines/titanic/game/service_elevator_window.h index 88e1663aba..baeef5aeb8 100644 --- a/engines/titanic/game/service_elevator_window.h +++ b/engines/titanic/game/service_elevator_window.h @@ -33,10 +33,10 @@ class CServiceElevatorWindow : public CBackground { bool MovieEndMsg(CMovieEndMsg *msg); bool EnterViewMsg(CEnterViewMsg *msg); public: - int _fieldE0; - int _fieldE4; - int _fieldE8; - int _fieldEC; + int _destFloor; + bool _notifyFlag; + bool _isIndicator; + bool _intoSpace; public: CLASSDEF; CServiceElevatorWindow(); diff --git a/engines/titanic/game/television.cpp b/engines/titanic/game/television.cpp index 571ebcd6fa..2d5a09790b 100644 --- a/engines/titanic/game/television.cpp +++ b/engines/titanic/game/television.cpp @@ -257,11 +257,11 @@ bool CTelevision::MovieEndMsg(CMovieEndMsg *msg) { if (_turnOn) loadFrame(502); else - warning("There is currently nothing available for your viewing pleasure on this channel."); + petDisplayMessage(NOTHING_ON_CHANNEL); } else if (_fieldE0 == 5 && *CGetLiftEye2::_destObject != "NULL") { loadFrame(393 + _v4); } else { - warning("There is currently nothing available for your viewing pleasure on this channel."); + petDisplayMessage(NOTHING_ON_CHANNEL); } return true; diff --git a/engines/titanic/game/transport/gondolier.cpp b/engines/titanic/game/transport/gondolier.cpp index 8c28ff9b66..85f3b365b5 100644 --- a/engines/titanic/game/transport/gondolier.cpp +++ b/engines/titanic/game/transport/gondolier.cpp @@ -46,7 +46,7 @@ void CGondolier::load(SimpleFile *file) { } bool CGondolier::StatusChangeMsg(CStatusChangeMsg *msg) { - CShowTextMsg textMsg("Only First Class passengers are allowed to use the Gondoliers."); + CShowTextMsg textMsg(GONDOLIERS_FIRST_CLASS_ONLY); textMsg.execute("PET"); return true; } diff --git a/engines/titanic/game/transport/lift_indicator.cpp b/engines/titanic/game/transport/lift_indicator.cpp index a642451fe0..df0ff397da 100644 --- a/engines/titanic/game/transport/lift_indicator.cpp +++ b/engines/titanic/game/transport/lift_indicator.cpp @@ -38,31 +38,31 @@ BEGIN_MESSAGE_MAP(CLiftindicator, CLift) END_MESSAGE_MAP() CLiftindicator::CLiftindicator() : CLift(), - _fieldFC(0), _start(0), _end(0) { + _multiplier(0), _startY(0), _endY(0) { } void CLiftindicator::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_fieldFC, indent); + file->writeNumberLine(_multiplier, indent); file->writePoint(_indicatorPos, indent); - file->writeNumberLine(_start, indent); - file->writeNumberLine(_end, indent); + file->writeNumberLine(_startY, indent); + file->writeNumberLine(_endY, indent); CLift::save(file, indent); } void CLiftindicator::load(SimpleFile *file) { file->readNumber(); - _fieldFC = file->readNumber(); + _multiplier = file->readNumber(); _indicatorPos = file->readPoint(); - _start = file->readNumber(); - _end = file->readNumber(); + _startY = file->readNumber(); + _endY = file->readNumber(); CLift::load(file); } bool CLiftindicator::EnterViewMsg(CEnterViewMsg *msg) { - double multiplier = _fieldFC * 0.037037037; + double multiplier = _multiplier * 0.037037037; CPetControl *pet = getPetControl(); int floorNum = pet->getRoomsFloorNum(); debugC(kDebugScripts, "Lifts = %d,%d,%d,%d, %d", @@ -70,7 +70,7 @@ bool CLiftindicator::EnterViewMsg(CEnterViewMsg *msg) { CLift::_elevator3Floor, CLift::_elevator4Floor, floorNum); - if ((pet->petGetRoomsWellEntry() & 1) == (_fieldFC & 1)) { + if ((pet->petGetRoomsWellEntry() & 1) == (_liftNum & 1)) { petSetRemoteTarget(); petSetArea(PET_REMOTE); @@ -142,7 +142,7 @@ bool CLiftindicator::LeaveViewMsg(CLeaveViewMsg *msg) { } bool CLiftindicator::PETActivateMsg(CPETActivateMsg *msg) { - double multiplier = _fieldFC * 0.037037037; + double multiplier = _multiplier * 0.037037037; CPetControl *pet = getPetControl(); if (msg->_name == "Lift") { @@ -155,8 +155,8 @@ bool CLiftindicator::PETActivateMsg(CPETActivateMsg *msg) { && pet->getRoomsFloorNum() != CLift::_elevator4Floor) { petDisplayMessage(1, ELEVATOR_NON_FUNCTIONAL); } else { - _start = _indicatorPos.y + (int)(_startFrame * multiplier); - _end = _indicatorPos.y + (int)(_endFrame * multiplier); + _startY = _indicatorPos.y + (int)(_startFrame * multiplier); + _endY = _indicatorPos.y + (int)(_endFrame * multiplier); lockMouse(); addTimer(100); @@ -213,15 +213,15 @@ bool CLiftindicator::LeaveRoomMsg(CLeaveRoomMsg *msg) { } bool CLiftindicator::TimerMsg(CTimerMsg *msg) { - debugC(kDebugScripts, "Start %d, End %d", _start, _end); + debugC(kDebugScripts, "Start %d, End %d", _startY, _endY); - if (_start > _end) { + if (_startY > _endY) { setPosition(Point(_bounds.left, _bounds.top - 1)); - --_start; + --_startY; addTimer(20); - } else if (_start < _end) { + } else if (_startY < _endY) { setPosition(Point(_bounds.left, _bounds.top + 1)); - ++_start; + ++_startY; addTimer(20); } else { CMovieEndMsg endMsg(0, 0); diff --git a/engines/titanic/game/transport/lift_indicator.h b/engines/titanic/game/transport/lift_indicator.h index 5d0bc45d7b..5ff9dc6d36 100644 --- a/engines/titanic/game/transport/lift_indicator.h +++ b/engines/titanic/game/transport/lift_indicator.h @@ -39,10 +39,10 @@ class CLiftindicator : public CLift { bool LeaveRoomMsg(CLeaveRoomMsg *msg); bool TimerMsg(CTimerMsg *msg); private: - int _fieldFC; + int _multiplier; Point _indicatorPos; - int _start; - int _end; + int _startY; + int _endY; public: CLASSDEF; CLiftindicator(); diff --git a/engines/titanic/game/transport/service_elevator.cpp b/engines/titanic/game/transport/service_elevator.cpp index 066a418dbb..dbd4e5d74f 100644 --- a/engines/titanic/game/transport/service_elevator.cpp +++ b/engines/titanic/game/transport/service_elevator.cpp @@ -116,6 +116,7 @@ bool CServiceElevator::ServiceElevatorMsg(CServiceElevatorMsg *msg) { break; case 5: + // Reaching destination floor _fieldF8 = false; _fieldDC = _v3; loadSound("z#423.wav"); @@ -164,9 +165,10 @@ bool CServiceElevator::TimerMsg(CTimerMsg *msg) { if (!isSoundActive(_soundHandle1)) { stopAnimTimer(_timerId); if (msg->_actionVal == 0) { + // Elevator in motion after pressing button _fieldF8 = true; CServiceElevatorFloorChangeMsg changeMsg(_fieldDC, _v3); - changeMsg.execute(getRoom()); + changeMsg.execute(getRoom(), nullptr, MSGFLAG_SCAN); _soundHandle2 = playSound("z#424.wav"); if (doorbot) { @@ -174,6 +176,7 @@ bool CServiceElevator::TimerMsg(CTimerMsg *msg) { actMsg.execute(doorbot); } } else { + // Finished playing message for bottom/middle floor disabled enableMouse(); if (doorbot) { CActMsg actMsg; @@ -207,10 +210,10 @@ bool CServiceElevator::ServiceElevatorFloorRequestMsg(CServiceElevatorFloorReque if (doorbot && _v3 == 0) { _soundHandle1 = playSound("z#415.wav", 50); - addTimer(1, 1000, 500); + _timerId = addTimer(1, 1000, 500); } else if (doorbot && _v3 == 1) { _soundHandle1 = playSound("z#417.wav", 50); - addTimer(1, 1000, 500); + _timerId = addTimer(1, 1000, 500); } else if (_fieldDC == _v3) { switch (_v3) { case 0: @@ -226,7 +229,7 @@ bool CServiceElevator::ServiceElevatorFloorRequestMsg(CServiceElevatorFloorReque break; } - addTimer(1, 1000, 500); + _timerId = addTimer(1, 1000, 500); } else { switch (_v3) { case 0: @@ -236,13 +239,13 @@ bool CServiceElevator::ServiceElevatorFloorRequestMsg(CServiceElevatorFloorReque _soundHandle1 = playSound(_fieldDC ? "z#419.wav" : "z#418.wav", 50); break; case 2: - _soundHandle1 = playSound("z#414.wav", 50); + _soundHandle1 = playSound("z#409.wav", 50); break; default: break; } - addTimer(0, 1000, 500); + _timerId = addTimer(0, 1000, 500); } return true; diff --git a/engines/titanic/game_location.h b/engines/titanic/game_location.h index f145d36340..87416f6cd7 100644 --- a/engines/titanic/game_location.h +++ b/engines/titanic/game_location.h @@ -30,6 +30,11 @@ namespace Titanic { +enum PassengerClass { + UNCHECKED = 4, THIRD_CLASS = 3, SECOND_CLASS = 2, FIRST_CLASS = 1, + NO_CLASS = 0 +}; + class CGameState; class CGameLocation { diff --git a/engines/titanic/game_manager.cpp b/engines/titanic/game_manager.cpp index 6023244325..015296fb14 100644 --- a/engines/titanic/game_manager.cpp +++ b/engines/titanic/game_manager.cpp @@ -150,11 +150,12 @@ void CGameManager::playClip(CMovieClip *clip, CRoomItem *oldRoom, CRoomItem *new if (clip && clip->_startFrame != clip->_endFrame && _movie) { // Clip details specifying a sub-section of movie to play Rect tempRect(20, 10, SCREEN_WIDTH - 20, 350); + CMouseCursor &mouseCursor = *CScreenManager::_screenManagerPtr->_mouseCursor; lockInputHandler(); - CScreenManager::_screenManagerPtr->_mouseCursor->hide(); + mouseCursor.incHideCounter(); _movie->playCutscene(tempRect, clip->_startFrame, clip->_endFrame); - CScreenManager::_screenManagerPtr->_mouseCursor->show(); + mouseCursor.decHideCounter(); unlockInputHandler(); } } diff --git a/engines/titanic/game_state.cpp b/engines/titanic/game_state.cpp index ea94deec35..5b6d34ad76 100644 --- a/engines/titanic/game_state.cpp +++ b/engines/titanic/game_state.cpp @@ -35,7 +35,6 @@ bool CGameStateMovieList::clear() { ++i; } else { i = erase(i); - delete movieItem; } } @@ -45,10 +44,10 @@ bool CGameStateMovieList::clear() { /*------------------------------------------------------------------------*/ CGameState::CGameState(CGameManager *gameManager) : - _gameManager(gameManager), _gameLocation(this), - _passengerClass(0), _priorClass(0), _mode(GSMODE_NONE), - _seasonNum(SEASON_SUMMER), _petActive(false), _field1C(false), _quitGame(false), - _field24(0), _nodeChangeCtr(0), _nodeEnterTicks(0), _field38(0) { + _gameManager(gameManager), _gameLocation(this), _passengerClass(NO_CLASS), + _priorClass(NO_CLASS), _mode(GSMODE_NONE), _seasonNum(SEASON_SUMMER), + _petActive(false), _field1C(false), _quitGame(false), _field24(0), + _nodeChangeCtr(0), _nodeEnterTicks(0), _field38(0) { } void CGameState::save(SimpleFile *file) const { @@ -64,8 +63,8 @@ void CGameState::save(SimpleFile *file) const { void CGameState::load(SimpleFile *file) { _petActive = file->readNumber() != 0; - _passengerClass = file->readNumber(); - _priorClass = file->readNumber(); + _passengerClass = (PassengerClass)file->readNumber(); + _priorClass = (PassengerClass)file->readNumber(); _seasonNum = (Season)file->readNumber(); _field24 = file->readNumber(); _field38 = file->readNumber(); @@ -79,14 +78,14 @@ void CGameState::load(SimpleFile *file) { void CGameState::setMode(GameStateMode newMode) { CScreenManager *sm = CScreenManager::_screenManagerPtr; - if (newMode == GSMODE_CUTSCENE && newMode != _mode) { + if (newMode == GSMODE_CUTSCENE && _mode != GSMODE_CUTSCENE) { if (_gameManager) _gameManager->lockInputHandler(); if (sm && sm->_mouseCursor) sm->_mouseCursor->hide(); - } else if (newMode != GSMODE_CUTSCENE && newMode != _mode) { + } else if (newMode != GSMODE_CUTSCENE && _mode == GSMODE_CUTSCENE) { if (sm && sm->_mouseCursor) sm->_mouseCursor->show(); @@ -130,7 +129,7 @@ void CGameState::changeView(CViewItem *newView, CMovieClip *clip) { oldView->leaveView(newView); // If Shift key is pressed, skip showing the transition clip - if (g_vm->_window->isSpecialPressed(MK_SHIFT)) + if (g_vm->_events->isSpecialPressed(MK_SHIFT)) clip = nullptr; if (_mode == GSMODE_CUTSCENE) { @@ -139,7 +138,7 @@ void CGameState::changeView(CViewItem *newView, CMovieClip *clip) { } else { oldView->preEnterView(newView); _gameManager->_gameView->setView(newView); - CRoomItem *oldRoom = newView->findNode()->findRoom(); + CRoomItem *oldRoom = oldView->findNode()->findRoom(); CRoomItem *newRoom = newView->findNode()->findRoom(); // If a transition clip is defined, play it diff --git a/engines/titanic/game_state.h b/engines/titanic/game_state.h index 77b4ded79f..ba1dff2a45 100644 --- a/engines/titanic/game_state.h +++ b/engines/titanic/game_state.h @@ -64,8 +64,8 @@ public: CGameManager *_gameManager; CGameLocation _gameLocation; CGameStateMovieList _movieList; - int _passengerClass; - int _priorClass; + PassengerClass _passengerClass; + PassengerClass _priorClass; GameStateMode _mode; Season _seasonNum; bool _petActive; diff --git a/engines/titanic/input_handler.cpp b/engines/titanic/input_handler.cpp index 9fa2b0073c..e2d1bd7a32 100644 --- a/engines/titanic/input_handler.cpp +++ b/engines/titanic/input_handler.cpp @@ -49,7 +49,10 @@ void CInputHandler::incLockCount() { } void CInputHandler::decLockCount() { - if (--_lockCount == 0 && _inputTranslator) { + --_lockCount; + assert(_lockCount >= 0); + + if (_lockCount == 0 && _inputTranslator) { if (_dragging && !_inputTranslator->isMousePressed()) { CMouseButtonUpMsg upMsg(_mousePos, MK_LBUTTON); handleMessage(upMsg); @@ -107,6 +110,7 @@ void CInputHandler::processMessage(CMessage *msg) { _dragging = false; _dragItem = nullptr; + _gameManager->_dragItem = nullptr; } } else if (_buttonDown) { if (!mouseMsg->isMouseMoveMsg()) { diff --git a/engines/titanic/input_translator.cpp b/engines/titanic/input_translator.cpp index ce272d152c..0f717de37f 100644 --- a/engines/titanic/input_translator.cpp +++ b/engines/titanic/input_translator.cpp @@ -90,6 +90,11 @@ void CInputTranslator::rightButtonUp(int special, const Point &pt) { _inputHandler->handleMessage(msg); } +void CInputTranslator::mouseWheel(bool wheelUp, const Point &pt) { + CMouseWheelMsg msg(pt, wheelUp); + _inputHandler->handleMessage(msg); +} + void CInputTranslator::rightButtonDoubleClick(int special, const Point &pt) { CMouseDoubleClickMsg msg(pt, MB_RIGHT); _inputHandler->handleMessage(msg); @@ -108,7 +113,7 @@ void CInputTranslator::keyDown(const Common::KeyState &keyState) { } bool CInputTranslator::isMousePressed() const { - return g_vm->_window->getSpecialButtons() & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON); + return g_vm->_events->getSpecialButtons() & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON); } } // End of namespace Titanic diff --git a/engines/titanic/input_translator.h b/engines/titanic/input_translator.h index d92157bccc..66dcaa1cbe 100644 --- a/engines/titanic/input_translator.h +++ b/engines/titanic/input_translator.h @@ -50,6 +50,7 @@ public: void middleButtonDoubleClick(int special, const Point &pt); void rightButtonDown(int special, const Point &pt); void rightButtonUp(int special, const Point &pt); + void mouseWheel(bool wheelUp, const Point &pt); void rightButtonDoubleClick(int special, const Point &pt); void keyDown(const Common::KeyState &keyState); diff --git a/engines/titanic/main_game_window.cpp b/engines/titanic/main_game_window.cpp index 80da792e4a..de0ac715ba 100644 --- a/engines/titanic/main_game_window.cpp +++ b/engines/titanic/main_game_window.cpp @@ -32,8 +32,7 @@ namespace Titanic { CMainGameWindow::CMainGameWindow(TitanicEngine *vm): _vm(vm), - _specialButtons(0), _priorLeftDownTime(0), - _priorMiddleDownTime(0), _priorRightDownTime(0) { + _priorLeftDownTime(0), _priorMiddleDownTime(0), _priorRightDownTime(0) { _gameView = nullptr; _gameManager = nullptr; _project = nullptr; @@ -150,6 +149,14 @@ void CMainGameWindow::draw() { scrManager->clearSurface(SURFACE_BACKBUFFER, &_gameManager->_bounds); switch (_gameManager->_gameState._mode) { + case GSMODE_PENDING_LOAD: + // Pending savegame to load + _gameManager->_gameState.setMode(GSMODE_INTERACTIVE); + _project->loadGame(_pendingLoadSlot); + _pendingLoadSlot = -1; + + // Deliberate fall-through to draw loaded game + case GSMODE_INTERACTIVE: case GSMODE_CUTSCENE: if (_gameManager->_gameState._petActive) @@ -165,12 +172,6 @@ void CMainGameWindow::draw() { _vm->_filesManager->insertCD(scrManager); break; - case GSMODE_PENDING_LOAD: - // Pending savegame to load - _gameManager->_gameState.setMode(GSMODE_INTERACTIVE); - _vm->_window->_project->loadGame(_pendingLoadSlot); - break; - default: break; } @@ -246,17 +247,21 @@ void CMainGameWindow::onIdle() { } #define HANDLE_MESSAGE(METHOD) if (_inputAllowed) { \ - _gameManager->_inputTranslator.METHOD(_specialButtons, mousePos); \ + _gameManager->_inputTranslator.METHOD(g_vm->_events->getSpecialButtons(), mousePos); \ mouseChanged(); \ } void CMainGameWindow::mouseMove(const Point &mousePos) { + if (!isMouseControlEnabled()) + return; + HANDLE_MESSAGE(mouseMove) } void CMainGameWindow::leftButtonDown(const Point &mousePos) { - _specialButtons |= MK_LBUTTON; + if (!isMouseControlEnabled()) + return; if ((_vm->_events->getTicksCount() - _priorLeftDownTime) < DOUBLE_CLICK_TIME) { _priorLeftDownTime = 0; @@ -268,16 +273,22 @@ void CMainGameWindow::leftButtonDown(const Point &mousePos) { } void CMainGameWindow::leftButtonUp(const Point &mousePos) { - _specialButtons &= ~MK_LBUTTON; + if (!isMouseControlEnabled()) + return; + HANDLE_MESSAGE(leftButtonUp) } void CMainGameWindow::leftButtonDoubleClick(const Point &mousePos) { + if (!isMouseControlEnabled()) + return; + HANDLE_MESSAGE(leftButtonDoubleClick) } void CMainGameWindow::middleButtonDown(const Point &mousePos) { - _specialButtons |= MK_MBUTTON; + if (!isMouseControlEnabled()) + return; if ((_vm->_events->getTicksCount() - _priorMiddleDownTime) < DOUBLE_CLICK_TIME) { _priorMiddleDownTime = 0; @@ -289,16 +300,22 @@ void CMainGameWindow::middleButtonDown(const Point &mousePos) { } void CMainGameWindow::middleButtonUp(const Point &mousePos) { - _specialButtons &= ~MK_MBUTTON; + if (!isMouseControlEnabled()) + return; + HANDLE_MESSAGE(middleButtonUp) } void CMainGameWindow::middleButtonDoubleClick(const Point &mousePos) { + if (!isMouseControlEnabled()) + return; + HANDLE_MESSAGE(middleButtonDoubleClick) } void CMainGameWindow::rightButtonDown(const Point &mousePos) { - _specialButtons |= MK_RBUTTON; + if (!isMouseControlEnabled()) + return; if ((_vm->_events->getTicksCount() - _priorRightDownTime) < DOUBLE_CLICK_TIME) { _priorRightDownTime = 0; @@ -310,21 +327,28 @@ void CMainGameWindow::rightButtonDown(const Point &mousePos) { } void CMainGameWindow::rightButtonUp(const Point &mousePos) { - _specialButtons &= ~MK_RBUTTON; + if (!isMouseControlEnabled()) + return; + HANDLE_MESSAGE(rightButtonUp) } -void CMainGameWindow::rightButtonDoubleClick(const Point &mousePos) { - HANDLE_MESSAGE(rightButtonDoubleClick) +void CMainGameWindow::mouseWheel(const Point &mousePos, bool wheelUp) { + if (!isMouseControlEnabled()) + return; + + _gameManager->_inputTranslator.mouseWheel(wheelUp, mousePos); + mouseChanged(); } -void CMainGameWindow::charPress(char c) { +void CMainGameWindow::rightButtonDoubleClick(const Point &mousePos) { + if (!isMouseControlEnabled()) + return; + HANDLE_MESSAGE(rightButtonDoubleClick) } void CMainGameWindow::keyDown(Common::KeyState keyState) { - handleKbdSpecial(keyState); - if (keyState.keycode == Common::KEYCODE_d && (keyState.flags & Common::KBD_CTRL)) { // Attach to the debugger _vm->_debugger->attach(); @@ -335,20 +359,12 @@ void CMainGameWindow::keyDown(Common::KeyState keyState) { _gameManager->_inputTranslator.keyDown(keyState); } -void CMainGameWindow::keyUp(Common::KeyState keyState) { - handleKbdSpecial(keyState); -} - -void CMainGameWindow::handleKbdSpecial(Common::KeyState keyState) { - if (keyState.flags & Common::KBD_CTRL) - _specialButtons |= MK_CONTROL; - else - _specialButtons &= ~MK_CONTROL; +bool CMainGameWindow::isMouseControlEnabled() const { + CScreenManager *screenMan = CScreenManager::_screenManagerPtr; + if (!screenMan || !screenMan->_mouseCursor) + return true; - if (keyState.flags & Common::KBD_SHIFT) - _specialButtons |= MK_SHIFT; - else - _specialButtons &= ~MK_SHIFT; + return screenMan->_mouseCursor->_inputEnabled; } } // End of namespace Titanic diff --git a/engines/titanic/main_game_window.h b/engines/titanic/main_game_window.h index 7bd918df04..52d4267c83 100644 --- a/engines/titanic/main_game_window.h +++ b/engines/titanic/main_game_window.h @@ -39,7 +39,6 @@ class CMainGameWindow : public CEventTarget { private: TitanicEngine *_vm; int _pendingLoadSlot; - uint _specialButtons; uint32 _priorLeftDownTime; uint32 _priorMiddleDownTime; uint32 _priorRightDownTime; @@ -74,8 +73,11 @@ private: void leftButtonDoubleClick(const Point &mousePos); void middleButtonDoubleClick(const Point &mousePos); void rightButtonDoubleClick(const Point &mousePos); - void charPress(char c); - void handleKbdSpecial(Common::KeyState keyState); + + /** + * Returns true if the player can control the mouse + */ + bool isMouseControlEnabled() const; public: CGameView *_gameView; CGameManager *_gameManager; @@ -99,8 +101,8 @@ public: virtual void middleButtonUp(const Point &mousePos); virtual void rightButtonDown(const Point &mousePos); virtual void rightButtonUp(const Point &mousePos); + virtual void mouseWheel(const Point &mousePos, bool wheelUp); virtual void keyDown(Common::KeyState keyState); - virtual void keyUp(Common::KeyState keyState); /** * Called when the application starts @@ -126,18 +128,6 @@ public: * Schedules a savegame to be loaded */ void loadGame(int slotId); - - /* - * Return whether a given special key is currently pressed - */ - bool isSpecialPressed(SpecialButtons btn) const { - return (_specialButtons & btn) != 0; - } - - /** - * Returns the bitset of the currently pressed special buttons - */ - uint getSpecialButtons() const { return _specialButtons; } }; } // End of namespace Titanic diff --git a/engines/titanic/messages/messages.cpp b/engines/titanic/messages/messages.cpp index a8507063ff..db89c3de3d 100644 --- a/engines/titanic/messages/messages.cpp +++ b/engines/titanic/messages/messages.cpp @@ -25,6 +25,7 @@ #include "titanic/core/game_object.h" #include "titanic/core/message_target.h" #include "titanic/core/tree_item.h" +#include "titanic/pet_control/pet_control.h" #include "titanic/titanic.h" namespace Titanic { @@ -163,4 +164,16 @@ bool CMessage::isLeaveViewMsg() const { return dynamic_cast<const CLeaveViewMsg *>(this) != nullptr; } +/*------------------------------------------------------------------------*/ + +CShowTextMsg::CShowTextMsg() : CMessage(), _message("NO TEXT INCLUDED!!!") { +} + +CShowTextMsg::CShowTextMsg(const CString &msg) : CMessage(), _message(msg) { +} + +CShowTextMsg::CShowTextMsg(StringId stringId) : CMessage() { + _message = g_vm->_strings[stringId]; +} + } // End of namespace Titanic diff --git a/engines/titanic/messages/messages.h b/engines/titanic/messages/messages.h index b421e8ecb8..fbe504700b 100644 --- a/engines/titanic/messages/messages.h +++ b/engines/titanic/messages/messages.h @@ -26,6 +26,7 @@ #include "common/keyboard.h" #include "titanic/core/saveable_object.h" #include "titanic/core/tree_item.h" +#include "titanic/support/strings.h" namespace Titanic { @@ -182,6 +183,20 @@ public: } }; +class CShowTextMsg : public CMessage { +public: + CString _message; +public: + CLASSDEF; + CShowTextMsg(); + CShowTextMsg(const CString &msg); + CShowTextMsg(StringId stringId); + + static bool isSupportedBy(const CTreeItem *item) { + return supports(item, _type); + } +}; + MESSAGE1(CActMsg, CString, action, ""); MESSAGE1(CActivationmsg, CString, value, ""); MESSAGE1(CAddHeadPieceMsg, CString, value, "NULL"); @@ -286,7 +301,7 @@ MESSAGE0(CReplaceBowlAndNutsMsg); MESSAGE1(CRestaurantMusicChanged, CString, value, ""); MESSAGE2(CSendCCarryMsg, CString, strValue, "", int, numValue, 0); MESSAGE1(CSenseWorkingMsg, CString, value, "Not Working"); -MESSAGE2(CServiceElevatorFloorChangeMsg, int, value1, 0, int, value2, 0); +MESSAGE2(CServiceElevatorFloorChangeMsg, int, startFloor, 0, int, endFloor, 0); MESSAGE0(CServiceElevatorFloorRequestMsg); MESSAGE1(CServiceElevatorMsg, int, value, 4); MESSAGE2(CSetChevButtonImageMsg, int, value1, 0, int, value2, 0); @@ -301,7 +316,6 @@ MESSAGE0(CSetMusicControlsMsg); MESSAGE2(CSetVarMsg, CString, varName, "", int, value, 0); MESSAGE2(CSetVolumeMsg, int, volume, 70, int, secondsTransition, 0); MESSAGE2(CShipSettingMsg, int, value, 0, CString, name, ""); -MESSAGE1(CShowTextMsg, CString, value, "NO TEXT INCLUDED!!!"); MESSAGE2(CSignalObject, CString, strValue, "", int, numValue, 0); MESSAGE1(CSpeechFallsFromTreeMsg, Point, pos, Point()); MESSAGE1(CStartMusicMsg, CMusicPlayer *, musicPlayer, (CMusicPlayer *)nullptr); diff --git a/engines/titanic/messages/mouse_messages.h b/engines/titanic/messages/mouse_messages.h index a10f3b42a8..05f9685c04 100644 --- a/engines/titanic/messages/mouse_messages.h +++ b/engines/titanic/messages/mouse_messages.h @@ -101,6 +101,20 @@ public: static void generate(); }; +class CMouseWheelMsg : public CMouseMsg { +public: + bool _wheelUp; +public: + CLASSDEF; + CMouseWheelMsg() : CMouseMsg(), _wheelUp(false) {} + CMouseWheelMsg(const Point &pt, bool wheelUp) : + CMouseMsg(pt, 0), _wheelUp(wheelUp) {} + + static bool isSupportedBy(const CTreeItem *item) { + return supports(item, _type); + } +}; + class CMouseDoubleClickMsg : public CMouseButtonMsg { public: CLASSDEF; diff --git a/engines/titanic/moves/exit_lift.cpp b/engines/titanic/moves/exit_lift.cpp index de9a3117af..a85af7c7a2 100644 --- a/engines/titanic/moves/exit_lift.cpp +++ b/engines/titanic/moves/exit_lift.cpp @@ -46,8 +46,8 @@ void CExitLift::load(SimpleFile *file) { bool CExitLift::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { CPetControl *pet = getPetControl(); - int floorNum = pet->getRoomsFloorNum();//ebx - int elevNum = pet->getRoomsElevatorNum(); //eax + int floorNum = pet->getRoomsFloorNum(); + int elevNum = pet->getRoomsElevatorNum(); if (floorNum == 39) { switch (elevNum) { diff --git a/engines/titanic/moves/restricted_move.cpp b/engines/titanic/moves/restricted_move.cpp index b1040a3554..92804d473d 100644 --- a/engines/titanic/moves/restricted_move.cpp +++ b/engines/titanic/moves/restricted_move.cpp @@ -51,7 +51,7 @@ bool CRestrictedMove::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { if (classNum <= _classNum) { // Okay to change to the given destination changeView(_destination); - } else if (classNum != 4) { + } else if (classNum != UNCHECKED) { petDisplayMessage(1, CLASS_NOT_ALLOWED_AT_DEST); } else if (compareRoomNameTo("EmbLobby")) { playSound("a#17.wav"); @@ -66,9 +66,9 @@ bool CRestrictedMove::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { bool CRestrictedMove::EnterViewMsg(CEnterViewMsg *msg) { int classNum = getPassengerClass(); - bool flag = classNum > _classNum; + bool flag = classNum <= _classNum; - if (classNum == 4) { + if (classNum == UNCHECKED) { if (compareRoomNameTo("EmbLobby")) flag = false; else if (compareViewNameTo("Titania.Node 1.S")) diff --git a/engines/titanic/npcs/barbot.cpp b/engines/titanic/npcs/barbot.cpp index 791adaa05c..87aaea611f 100644 --- a/engines/titanic/npcs/barbot.cpp +++ b/engines/titanic/npcs/barbot.cpp @@ -336,7 +336,7 @@ bool CBarbot::TurnOn(CTurnOn *msg) { _fieldC4 = 1; ++_v0; petSetArea(PET_CONVERSATION); - endTalking(this, true); + setTalking(this, true); } return true; diff --git a/engines/titanic/npcs/bellbot.cpp b/engines/titanic/npcs/bellbot.cpp index 7aa32eeef3..1326655299 100644 --- a/engines/titanic/npcs/bellbot.cpp +++ b/engines/titanic/npcs/bellbot.cpp @@ -79,7 +79,7 @@ bool CBellBot::OnSummonBotMsg(COnSummonBotMsg *msg) { for (idx = 0; idx < 8; ++idx) { if (compareRoomNameTo(ROOM_WAVES[idx][0])) { playSound(ROOM_WAVES[idx][1]); - + break; } } if (idx == 8) @@ -99,7 +99,7 @@ bool CBellBot::OnSummonBotMsg(COnSummonBotMsg *msg) { bool CBellBot::LeaveViewMsg(CLeaveViewMsg *msg) { if (_npcFlags & NPCFLAG_10000) { performAction(1); - _npcFlags &= ~NPCFLAG_4; + _npcFlags &= ~NPCFLAG_START_IDLING; CDismissBotMsg dismissMsg; dismissMsg.execute(this); } @@ -113,13 +113,13 @@ bool CBellBot::MovieEndMsg(CMovieEndMsg *msg) { } else if (clipExistsByEnd("Walk On", msg->_endFrame)) { setPosition(Point(80, 10)); loadFrame(543); - _npcFlags |= NPCFLAG_4; + _npcFlags |= NPCFLAG_START_IDLING; if (_npcFlags & NPCFLAG_40000) { startTalking(this, 157); _npcFlags &= ~NPCFLAG_40000; } - endTalking(this, true); + setTalking(this, true); petSetArea(PET_CONVERSATION); } else if (clipExistsByEnd("Walk Off", msg->_endFrame)) { CPutBotBackInHisBoxMsg boxMsg; @@ -142,8 +142,8 @@ bool CBellBot::Use(CUse *msg) { bool CBellBot::DismissBotMsg(CDismissBotMsg *msg) { if (_npcFlags & NPCFLAG_10000) { playClip("Walk Off", MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); - if (_npcFlags & NPCFLAG_4) { - _npcFlags &= ~NPCFLAG_4; + if (_npcFlags & NPCFLAG_START_IDLING) { + _npcFlags &= ~NPCFLAG_START_IDLING; performAction(true); } else { performAction(false); @@ -160,7 +160,7 @@ bool CBellBot::TrueTalkTriggerActionMsg(CTrueTalkTriggerActionMsg *msg) { switch (msg->_action) { case 1: case 28: { - _npcFlags &= ~NPCFLAG_2; + _npcFlags &= ~NPCFLAG_IDLING; CDismissBotMsg dismissMsg; dismissMsg.execute(this); break; @@ -203,7 +203,7 @@ bool CBellBot::MovieFrameMsg(CMovieFrameMsg *msg) { bool CBellBot::PutBotBackInHisBoxMsg(CPutBotBackInHisBoxMsg *msg) { petMoveToHiddenRoom(); - _npcFlags &= ~NPCFLAG_4; + _npcFlags &= ~NPCFLAG_START_IDLING; return true; } @@ -229,7 +229,7 @@ bool CBellBot::NPCPlayTalkingAnimationMsg(CNPCPlayTalkingAnimationMsg *msg) { }; if (msg->_value2 == 2) - playClip("Mother Frame", 0); + playClip("Mother Frame"); else msg->_names = NAMES; @@ -238,8 +238,6 @@ bool CBellBot::NPCPlayTalkingAnimationMsg(CNPCPlayTalkingAnimationMsg *msg) { bool CBellBot::TimerMsg(CTimerMsg *msg) { if (msg->_action == "SummonDoorbot") { - CTrueTalkNPC::TimerMsg(msg); - } else { CRoomItem *room = getRoom(); if (room) { CSummonBotMsg botMsg; @@ -249,6 +247,8 @@ bool CBellBot::TimerMsg(CTimerMsg *msg) { } _npcFlags &= ~NPCFLAG_20000; + } else { + CTrueTalkNPC::TimerMsg(msg); } return true; diff --git a/engines/titanic/npcs/bilge_succubus.cpp b/engines/titanic/npcs/bilge_succubus.cpp index d1b89e58b4..006f532a31 100644 --- a/engines/titanic/npcs/bilge_succubus.cpp +++ b/engines/titanic/npcs/bilge_succubus.cpp @@ -431,7 +431,7 @@ bool CBilgeSuccUBus::TurnOn(CTurnOn *msg) { CSUBTransition transMsg; transMsg.execute(this); - endTalking(this, true); + setTalking(this, true); petSetArea(PET_REMOTE); petHighlightGlyph(16); } diff --git a/engines/titanic/npcs/deskbot.cpp b/engines/titanic/npcs/deskbot.cpp index c5032f1674..3a65b6f5bb 100644 --- a/engines/titanic/npcs/deskbot.cpp +++ b/engines/titanic/npcs/deskbot.cpp @@ -52,7 +52,8 @@ END_MESSAGE_MAP() int CDeskbot::_v1; int CDeskbot::_v2; -CDeskbot::CDeskbot() : CTrueTalkNPC(), _deskbotActive(false), _classNum(0) { +CDeskbot::CDeskbot() : CTrueTalkNPC(), _deskbotActive(false), + _classNum(NO_CLASS) { } void CDeskbot::save(SimpleFile *file, int indent) { @@ -70,7 +71,7 @@ void CDeskbot::load(SimpleFile *file) { _v1 = file->readNumber(); _v2 = file->readNumber(); _deskbotActive = file->readNumber(); - _classNum = file->readNumber(); + _classNum = (PassengerClass)file->readNumber(); CTrueTalkNPC::load(file); } @@ -78,7 +79,9 @@ void CDeskbot::load(SimpleFile *file) { bool CDeskbot::TurnOn(CTurnOn *msg) { if (!_deskbotActive) { setVisible(true); - playClip("BellRinging", 4); + playClip("BellRinging"); + playClip("Opening", MOVIE_NOTIFY_OBJECT); + playSound("b#69.wav"); petSetArea(PET_CONVERSATION); @@ -114,7 +117,7 @@ bool CDeskbot::MovieEndMsg(CMovieEndMsg *msg) { dec54(); unlockMouse(); playSound("z#47.wav"); - _classNum = false; + _classNum = NO_CLASS; } _npcFlags &= ~NPCFLAG_10000; @@ -143,9 +146,9 @@ bool CDeskbot::MovieEndMsg(CMovieEndMsg *msg) { if (_npcFlags & NPCFLAG_20000) { _npcFlags &= ~(NPCFLAG_40000 | NPCFLAG_20000); - endTalking(this, 1, findView()); + setTalking(this, true, findView()); - _npcFlags |= NPCFLAG_4; + _npcFlags |= NPCFLAG_START_IDLING; flag = true; } @@ -170,23 +173,23 @@ bool CDeskbot::TrueTalkTriggerActionMsg(CTrueTalkTriggerActionMsg *msg) { inc54(); lockMouse(); petSetArea(PET_CONVERSATION); - playClip("ReprogramPETInHand", 4); + playClip("ReprogramPETInHand", MOVIE_NOTIFY_OBJECT); _npcFlags |= NPCFLAG_10000; - _classNum = msg->_param1; + _classNum = (PassengerClass)msg->_param1; switch (_classNum) { - case 1: + case FIRST_CLASS: petDisplayMessage(UPGRADED_TO_FIRST_CLASS); setPassengerClass(_classNum); petReassignRoom(_classNum); break; - case 2: + case SECOND_CLASS: petDisplayMessage(UPGRADED_TO_SECOND_CLASS); setPassengerClass(_classNum); petReassignRoom(_classNum); break; - case 3: - setPassengerClass(3); + case THIRD_CLASS: + setPassengerClass(THIRD_CLASS); petReassignRoom(_classNum); break; default: @@ -197,31 +200,31 @@ bool CDeskbot::TrueTalkTriggerActionMsg(CTrueTalkTriggerActionMsg *msg) { if (getPassengerClass() == 1) { CPetControl *petControl = getPetControl(); if (petControl) - petControl->changeLocationClass(4); + petControl->changeLocationClass(UNCHECKED); } break; case 21: - if (getPassengerClass() == 1) { + if (getPassengerClass() == FIRST_CLASS) { CPetControl *petControl = getPetControl(); if (petControl) - petControl->changeLocationClass(3); + petControl->changeLocationClass(THIRD_CLASS); } break; case 22: - if (getPassengerClass() == 1) { + if (getPassengerClass() == FIRST_CLASS) { CPetControl *petControl = getPetControl(); if (petControl) - petControl->changeLocationClass(2); + petControl->changeLocationClass(SECOND_CLASS); } break; case 23: - if (getPassengerClass() == 1) { + if (getPassengerClass() == FIRST_CLASS) { CPetControl *petControl = getPetControl(); if (petControl) - petControl->changeLocationClass(1); + petControl->changeLocationClass(FIRST_CLASS); } break; @@ -300,8 +303,8 @@ bool CDeskbot::TurnOff(CTurnOff *msg) { stopMovie(); performAction(1, findView()); - _npcFlags = (_npcFlags & ~(NPCFLAG_SPEAKING | NPCFLAG_2 | NPCFLAG_4)) | NPCFLAG_40000; - playClip("Closing", 0x14); + _npcFlags = (_npcFlags & ~(NPCFLAG_SPEAKING | NPCFLAG_IDLING | NPCFLAG_START_IDLING)) | NPCFLAG_40000; + playClip("Closing", MOVIE_GAMESTATE | MOVIE_NOTIFY_OBJECT); } return true; diff --git a/engines/titanic/npcs/deskbot.h b/engines/titanic/npcs/deskbot.h index ab48d63546..1b462f6584 100644 --- a/engines/titanic/npcs/deskbot.h +++ b/engines/titanic/npcs/deskbot.h @@ -45,7 +45,7 @@ private: static int _v2; public: bool _deskbotActive; - int _classNum; + PassengerClass _classNum; public: CLASSDEF; CDeskbot(); diff --git a/engines/titanic/npcs/doorbot.cpp b/engines/titanic/npcs/doorbot.cpp index d26f4bb828..ebab6508c2 100644 --- a/engines/titanic/npcs/doorbot.cpp +++ b/engines/titanic/npcs/doorbot.cpp @@ -22,6 +22,7 @@ #include "titanic/npcs/doorbot.h" #include "titanic/core/room_item.h" +#include "titanic/titanic.h" namespace Titanic { @@ -48,7 +49,7 @@ int CDoorbot::_v1; int CDoorbot::_v2; CDoorbot::CDoorbot() : CTrueTalkNPC() { - _field108 = 0; + _introMovieNum = 0; _timerId = 0; _field110 = 0; _field114 = 0; @@ -59,7 +60,7 @@ void CDoorbot::save(SimpleFile *file, int indent) { file->writeNumberLine(_v1, indent); file->writeNumberLine(_v2, indent); - file->writeNumberLine(_field108, indent); + file->writeNumberLine(_introMovieNum, indent); file->writeNumberLine(_timerId, indent); file->writeNumberLine(_field110, indent); file->writeNumberLine(_field114, indent); @@ -72,7 +73,7 @@ void CDoorbot::load(SimpleFile *file) { _v1 = file->readNumber(); _v2 = file->readNumber(); - _field108 = file->readNumber(); + _introMovieNum = file->readNumber(); _timerId = file->readNumber(); _field110 = file->readNumber(); _field114 = file->readNumber(); @@ -81,11 +82,14 @@ void CDoorbot::load(SimpleFile *file) { } bool CDoorbot::MovieEndMsg(CMovieEndMsg *msg) { + debugC(ERROR_DETAILED, kDebugScripts, "CDoorbot MovieEndMsg flags=%x v=%d, start=%d, end=%d", + _npcFlags, _introMovieNum, msg->_startFrame, msg->_endFrame); + if (_npcFlags & NPCFLAG_DOORBOT_INTRO) { - switch (_field108) { + switch (_introMovieNum) { case 3: startTalking(this, 221482); - _field108 = 4; + _introMovieNum = 4; break; case 6: @@ -99,7 +103,7 @@ bool CDoorbot::MovieEndMsg(CMovieEndMsg *msg) { case 7: startTalking(this, 221467); - _field108 = 8; + _introMovieNum = 8; break; case 9: @@ -118,11 +122,11 @@ bool CDoorbot::MovieEndMsg(CMovieEndMsg *msg) { CTrueTalkNPC::MovieEndMsg(msg); } else if (_npcFlags & NPCFLAG_100000) { if (clipExistsByEnd("Cloak Off", msg->_endFrame)) { - _npcFlags = (_npcFlags & ~NPCFLAG_8) | NPCFLAG_4; - endTalking(this, false); + _npcFlags = (_npcFlags & ~NPCFLAG_8) | NPCFLAG_START_IDLING; + setTalking(this, false); startTalking(this, 221474); _npcFlags |= NPCFLAG_DOORBOT_INTRO; - _field108 = 0; + _introMovieNum = 0; } else if (clipExistsByEnd("Cloak On", msg->_endFrame)) { petShow(); setState1C(true); @@ -135,8 +139,8 @@ bool CDoorbot::MovieEndMsg(CMovieEndMsg *msg) { || clipExistsByEnd("Whizz On Right", msg->_endFrame)) { setPosition(Point((600 - _bounds.width()) / 2 + 18, 42)); loadFrame(0); - endTalking(this, true); - _npcFlags |= NPCFLAG_4; + setTalking(this, true); + _npcFlags |= NPCFLAG_START_IDLING; petSetArea(PET_CONVERSATION); } else if (clipExistsByEnd("Whizz Off Left", msg->_endFrame) || clipExistsByEnd("Whizz Off Right", msg->_endFrame)) { @@ -195,12 +199,12 @@ bool CDoorbot::TrueTalkTriggerActionMsg(CTrueTalkTriggerActionMsg *msg) { break; case 4: - _npcFlags = (_npcFlags & ~NPCFLAG_2) | NPCFLAG_4000000; + _npcFlags = (_npcFlags & ~NPCFLAG_IDLING) | NPCFLAG_4000000; playClip("Whizz Off Left", MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); break; case 28: { - _npcFlags &= ~(NPCFLAG_2 | NPCFLAG_4); + _npcFlags &= ~(NPCFLAG_IDLING | NPCFLAG_START_IDLING); CDismissBotMsg dismissMsg; dismissMsg.execute(this); break; @@ -227,13 +231,13 @@ bool CDoorbot::DoorbotNeededInElevatorMsg(CDoorbotNeededInElevatorMsg *msg) { setPosition(Point(100, 42)); if (_npcFlags & NPCFLAG_DOORBOT_INTRO) { - _field108 = 7; + _introMovieNum = 7; _npcFlags |= NPCFLAG_200000; loadFrame(797); } else { _npcFlags = 0; if (msg->_value) - endTalking(this, true); + setTalking(this, true); } return true; @@ -242,7 +246,7 @@ bool CDoorbot::DoorbotNeededInElevatorMsg(CDoorbotNeededInElevatorMsg *msg) { bool CDoorbot::LeaveViewMsg(CLeaveViewMsg *msg) { if (!(_npcFlags & NPCFLAG_DOORBOT_INTRO) && (_npcFlags & NPCFLAG_400000)) { performAction(true); - _npcFlags &= ~NPCFLAG_4; + _npcFlags &= ~NPCFLAG_START_IDLING; } return true; @@ -252,6 +256,8 @@ bool CDoorbot::TimerMsg(CTimerMsg *msg) { if (msg->_action == "NPCIdleAnim") { return CTrueTalkNPC::TimerMsg(msg); } else if (_npcFlags & NPCFLAG_DOORBOT_INTRO) { + _timerId = 0; + switch (msg->_actionVal) { case 0: startTalking(this, 221475); @@ -266,11 +272,11 @@ bool CDoorbot::TimerMsg(CTimerMsg *msg) { break; case 3: - playClip("DoubleTake Start", 0); - playClip("DoubleTake End", 0); - playClip("DoubleTake Start", 0); + playClip("DoubleTake Start"); + playClip("DoubleTake End"); + playClip("DoubleTake Start"); playClip("DoubleTake End", MOVIE_NOTIFY_OBJECT); - _field108 = 3; + _introMovieNum = 3; break; case 4: @@ -281,21 +287,23 @@ bool CDoorbot::TimerMsg(CTimerMsg *msg) { case 5: lockInputHandler(); - mouseLockE4(); + mouseDisableControl(); _field114 = true; startTalking(this, 221485); break; case 6: + // Start dragging photograph to PET CMouseButtonDownMsg::generate(); mouseSetPosition(Point(200, 430), 2500); _timerId = addTimer(7, 2500, 0); break; case 7: - CMouseButtonDownMsg::generate(); + // Drop photograph in PET + CMouseButtonUpMsg::generate(); startTalking(this, 221486); - mouseUnlockE4(); + mouseEnableControl(); unlockInputHandler(); _field114 = false; disableMouse(); @@ -334,11 +342,17 @@ bool CDoorbot::NPCPlayTalkingAnimationMsg(CNPCPlayTalkingAnimationMsg *msg) { if (msg->_value2 != 2) { if (_npcFlags & NPCFLAG_200000) { - if (_field108 == 8 || _field110) { + switch (_introMovieNum) { + case 8: + case 10: msg->_names = NAMES2; - } else if (_field108 == 9) { + break; + case 9: msg->_names = NAMES3; - _field108 = 10; + _introMovieNum = 10; + break; + default: + break; } } else if (_npcFlags & (NPCFLAG_100000 | NPCFLAG_400000)) { msg->_names = NAMES1; @@ -362,7 +376,7 @@ bool CDoorbot::NPCPlayIdleAnimationMsg(CNPCPlayIdleAnimationMsg *msg) { bool CDoorbot::PutBotBackInHisBoxMsg(CPutBotBackInHisBoxMsg *msg) { petMoveToHiddenRoom(); - _npcFlags &= ~(NPCFLAG_4 | NPCFLAG_100000 | NPCFLAG_200000 | NPCFLAG_DOORBOT_INTRO); + _npcFlags &= ~(NPCFLAG_START_IDLING | NPCFLAG_100000 | NPCFLAG_200000 | NPCFLAG_DOORBOT_INTRO); if (msg->_value) performAction(true); @@ -375,8 +389,8 @@ bool CDoorbot::DismissBotMsg(CDismissBotMsg *msg) { MOVIE_STOP_PREVIOUS | MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); movieEvent(); - if (_npcFlags & NPCFLAG_4) { - _npcFlags &= ~NPCFLAG_4; + if (_npcFlags & NPCFLAG_START_IDLING) { + _npcFlags &= ~NPCFLAG_START_IDLING; performAction(true); } else { performAction(false); @@ -409,7 +423,7 @@ bool CDoorbot::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) switch (msg->_dialogueId) { case 10552: playClip("SE Try Buttons", MOVIE_NOTIFY_OBJECT); - _field108 = 9; + _introMovieNum = 9; break; case 10553: @@ -418,7 +432,7 @@ bool CDoorbot::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) case 10557: playClip("SE Move To Right", MOVIE_NOTIFY_OBJECT); - _field108 = 11; + _introMovieNum = 11; break; case 10559: @@ -435,13 +449,13 @@ bool CDoorbot::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) case 10561: enableMouse(); - _field108 = 1; + _introMovieNum = 1; stopAnimTimer(_timerId); _timerId = addTimer(2, 10000, 0); break; case 10562: - if (_field108 == 1) { + if (_introMovieNum == 1) { stopAnimTimer(_timerId); _timerId = addTimer(2, getRandomNumber(5000), 0); } @@ -462,7 +476,7 @@ bool CDoorbot::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) _timerId = 0; if (_field110 == 2) { playClip("Cloak On", MOVIE_NOTIFY_OBJECT); - _field108 = 6; + _introMovieNum = 6; } else { _timerId = addTimer(3, 2000, 0); } @@ -478,7 +492,8 @@ bool CDoorbot::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) } case 10568: - mouseLockE4(); + // Start moving cursor to photograph + mouseDisableControl(); mouseSetPosition(Point(600, 250), 2500); _timerId = addTimer(6, 2500, 0); break; @@ -497,7 +512,7 @@ bool CDoorbot::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) case 10571: playClip("Cloak On", MOVIE_NOTIFY_OBJECT); - _field108 = 6; + _introMovieNum = 6; break; default: @@ -512,9 +527,9 @@ bool CDoorbot::TextInputMsg(CTextInputMsg *msg) { if (!(_npcFlags & NPCFLAG_DOORBOT_INTRO)) return CTrueTalkNPC::TextInputMsg(msg); - if (_field108 == 1) { + if (_introMovieNum == 1) { stopAnimTimer(_timerId); - _field108 = 2; + _introMovieNum = 2; _timerId = 0; if (msg->_input == "yes" || msg->_input == "yeah" @@ -532,34 +547,36 @@ bool CDoorbot::TextInputMsg(CTextInputMsg *msg) { } bool CDoorbot::EnterViewMsg(CEnterViewMsg *msg) { - if ((_npcFlags & NPCFLAG_DOORBOT_INTRO) && _field108 == 7) + if ((_npcFlags & NPCFLAG_DOORBOT_INTRO) && _introMovieNum == 7) playClip("SE Move And Turn", MOVIE_NOTIFY_OBJECT); return true; } bool CDoorbot::ActMsg(CActMsg *msg) { + debugC(ERROR_DETAILED, kDebugScripts, "CDoorbot ActMsg action=%s v108=%d v110=%d v114=%d", + msg->_action.c_str(), _introMovieNum, _field110, _field114); + if (msg->_action == "DoorbotPlayerPressedTopButton") { disableMouse(); startTalking(this, 221471); } else if (msg->_action == "DoorbotPlayerPressedMiddleButton") { startTalking(this, 221470); - } - else if (msg->_action == "DoorbotPlayerPressedBottomButton") { + } else if (msg->_action == "DoorbotPlayerPressedBottomButton") { startTalking(this, 221469); } else if (msg->_action == "DoorbotReachedEmbLobby") { startTalking(this, 221472); } else if (msg->_action == "PlayerPicksUpPhoto") { _field110 = 1; - if (!_field114 && _field108 == 4) { + if (!_field114 && _introMovieNum == 4) { stopAnimTimer(_timerId); _timerId = 0; - _field108 = 5; + _introMovieNum = 5; startTalking(this, 221484); } } else if (msg->_action == "PlayerPutsPhotoInPet") { _field110 = 2; - if (!_field114 && _field108 == 5) { + if (!_field114 && _introMovieNum == 5) { stopAnimTimer(_timerId); _timerId = 0; startTalking(this, 221486); diff --git a/engines/titanic/npcs/doorbot.h b/engines/titanic/npcs/doorbot.h index 9095ebc7e7..1cb7ec3f27 100644 --- a/engines/titanic/npcs/doorbot.h +++ b/engines/titanic/npcs/doorbot.h @@ -49,7 +49,7 @@ private: static int _v1; static int _v2; private: - int _field108; + int _introMovieNum; int _timerId; int _field110; int _field114; diff --git a/engines/titanic/npcs/liftbot.cpp b/engines/titanic/npcs/liftbot.cpp index 64bc7c4989..bb554a104f 100644 --- a/engines/titanic/npcs/liftbot.cpp +++ b/engines/titanic/npcs/liftbot.cpp @@ -66,7 +66,7 @@ void CLiftBot::load(SimpleFile *file) { bool CLiftBot::TextInputMsg(CTextInputMsg *msg) { CPetControl *pet = getPetControl(); if (_enabled || pet->getRoomsElevatorNum() != 4) { - if (getName() != "LiftBot") { + if (getName() == "LiftBot") { CViewItem *view = findView(); processInput(msg, view); } @@ -79,13 +79,12 @@ bool CLiftBot::EnterViewMsg(CEnterViewMsg *msg) { CPetControl *pet = getPetControl(); if (!_enabled && pet->getRoomsElevatorNum() == 4) { loadFrame(700); - } else if (!_flag) { - if (getName() != "LiftBot") { - CViewItem *view = findView(); - endTalking(this, true, view); - petSetArea(PET_CONVERSATION); - _flag = 1; - } + } else if (!_flag && getName() == "LiftBot") { + // First time meeting the LiftBot + CViewItem *view = findView(); + setTalking(this, true, view); + petSetArea(PET_CONVERSATION); + _flag = 1; } return true; @@ -109,7 +108,7 @@ bool CLiftBot::TrueTalkTriggerActionMsg(CTrueTalkTriggerActionMsg *msg) { } bool CLiftBot::LeaveRoomMsg(CLeaveRoomMsg *msg) { - if (getName() != "LiftBot") + if (getName() == "LiftBot") performAction(false); return true; @@ -124,7 +123,7 @@ bool CLiftBot::TurnOn(CTurnOn *msg) { _enabled = true; if (!_flag) { if (isEquals("LiftBotTalking")) { - endTalking(this, MOVIE_REPEAT, findView()); + setTalking(this, MOVIE_REPEAT, findView()); petSetArea(PET_CONVERSATION); _flag = true; } @@ -164,7 +163,7 @@ bool CLiftBot::NPCPlayTalkingAnimationMsg(CNPCPlayTalkingAnimationMsg *msg) { }; if (msg->_value2 == 2) - playClip("At Rest", 0); + playClip("At Rest"); else msg->_names = NAMES; return true; @@ -174,7 +173,7 @@ bool CLiftBot::ActMsg(CActMsg *msg) { if (msg->_action == "ActivateLift") { _enabled = true; CViewItem *view = findView(); - endTalking(this, true, view); + setTalking(this, true, view); startTalking(this, 155, view); } else if (msg->_action == "LiftArrive") { CViewItem *view = findView(); diff --git a/engines/titanic/npcs/maitre_d.cpp b/engines/titanic/npcs/maitre_d.cpp index 658b7c7123..cbb406fffa 100644 --- a/engines/titanic/npcs/maitre_d.cpp +++ b/engines/titanic/npcs/maitre_d.cpp @@ -114,7 +114,7 @@ bool CMaitreD::TrueTalkTriggerActionMsg(CTrueTalkTriggerActionMsg *msg) { } bool CMaitreD::EnterViewMsg(CEnterViewMsg *msg) { - endTalking(this, true, findView()); + setTalking(this, true, findView()); _field12C = _field134; if (_string3 == "STMusic" && (!_field11C || _string2 == _string3)) diff --git a/engines/titanic/npcs/parrot.cpp b/engines/titanic/npcs/parrot.cpp index 97a1944fef..c3702e17d4 100644 --- a/engines/titanic/npcs/parrot.cpp +++ b/engines/titanic/npcs/parrot.cpp @@ -202,7 +202,7 @@ bool CParrot::ActMsg(CActMsg *msg) { } bool CParrot::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { - if (!(_npcFlags & NPCFLAG_2000000) && _field100 <= 0) { + if (!(_npcFlags & NPCFLAG_2000000) && _speechCounter == 0) { CTrueTalkTriggerActionMsg triggerMsg(280250, 280250, 1); triggerMsg.execute(this); } @@ -291,7 +291,7 @@ bool CParrot::MovieEndMsg(CMovieEndMsg *msg) { return true; } else if (clipExistsByEnd("Lean Over To Chicken", msg->_endFrame)) { - playClip("Eat Chicken", 0); + playClip("Eat Chicken"); playClip("Eat Chicken 2", MOVIE_NOTIFY_OBJECT); _v1 = 1; @@ -352,9 +352,9 @@ bool CParrot::EnterViewMsg(CEnterViewMsg *msg) { _field118 = 1; _npcFlags &= ~(NPCFLAG_10000 | NPCFLAG_20000 | NPCFLAG_40000 | NPCFLAG_80000 | NPCFLAG_100000 | NPCFLAG_200000 | NPCFLAG_400000); loadFrame(0); - endTalking(this, true, findView()); + setTalking(this, true, findView()); - if (_field100 > 0) { + if (_speechCounter > 0) { playRandomClip(NAMES, MOVIE_NOTIFY_OBJECT); } else { startTalking(this, 280258, findView()); @@ -362,7 +362,7 @@ bool CParrot::EnterViewMsg(CEnterViewMsg *msg) { petSetArea(PET_CONVERSATION); _field12C = 0; - _npcFlags |= NPCFLAG_4; + _npcFlags |= NPCFLAG_START_IDLING; } return true; @@ -404,7 +404,7 @@ bool CParrot::MouseDragStartMsg(CMouseDragStartMsg *msg) { bool CParrot::LeaveViewMsg(CLeaveViewMsg *msg) { performAction(true); - _npcFlags &= ~NPCFLAG_4; + _npcFlags &= ~NPCFLAG_START_IDLING; return true; } @@ -425,7 +425,7 @@ bool CParrot::ParrotSpeakMsg(CParrotSpeakMsg *msg) { } // Don't have the parrot speak too often - if ((getTicksCount() - _field120) < 20000 || _field100) + if ((getTicksCount() - _field120) < 20000 || _speechCounter) return true; playSound("z#475.wav", 50); diff --git a/engines/titanic/npcs/succubus.cpp b/engines/titanic/npcs/succubus.cpp index 6db8c8d798..bdde682c03 100644 --- a/engines/titanic/npcs/succubus.cpp +++ b/engines/titanic/npcs/succubus.cpp @@ -671,7 +671,7 @@ bool CSuccUBus::TurnOn(CTurnOn *msg) { CSUBTransition transMsg; transMsg.execute(this); - endTalking(this, true, findView()); + setTalking(this, true, findView()); petSetArea(PET_REMOTE); petHighlightGlyph(16); } diff --git a/engines/titanic/npcs/titania.cpp b/engines/titanic/npcs/titania.cpp index aa92ddd166..db0c85d5f5 100644 --- a/engines/titanic/npcs/titania.cpp +++ b/engines/titanic/npcs/titania.cpp @@ -180,7 +180,7 @@ bool CTitania::ActMsg(CActMsg *msg) { CActMsg actMsg("Woken"); actMsg.execute("MouthSlot"); actMsg.execute("VisionCentreSlot"); - setPassengerClass(4); + setPassengerClass(UNCHECKED); addTimer(1000); } else { diff --git a/engines/titanic/npcs/true_talk_npc.cpp b/engines/titanic/npcs/true_talk_npc.cpp index 55666a21a9..8d08600c9b 100644 --- a/engines/titanic/npcs/true_talk_npc.cpp +++ b/engines/titanic/npcs/true_talk_npc.cpp @@ -24,6 +24,7 @@ #include "titanic/core/view_item.h" #include "titanic/pet_control/pet_control.h" #include "titanic/game_manager.h" +#include "titanic/titanic.h" namespace Titanic { @@ -41,7 +42,7 @@ END_MESSAGE_MAP() CTrueTalkNPC::CTrueTalkNPC() : _assetName("z451.dlg"), _assetNumber(0x11170), _fieldE4(0), _npcFlags(0), _speechDuration(0), _startTicks(0), - _fieldF4(0), _fieldF8(0), _speechTimerId(0), _field100(0), _field104(0) { + _fieldF4(0), _fieldF8(0), _speechTimerId(0), _speechCounter(0), _field104(0) { } void CTrueTalkNPC::save(SimpleFile *file, int indent) { @@ -55,7 +56,7 @@ void CTrueTalkNPC::save(SimpleFile *file, int indent) { file->writeNumberLine(_fieldF4, indent); file->writeNumberLine(_fieldF8, indent); file->writeNumberLine(_speechTimerId, indent); - file->writeNumberLine(_field100, indent); + file->writeNumberLine(_speechCounter, indent); file->writeNumberLine(_field104, indent); CCharacter::save(file, indent); @@ -72,7 +73,7 @@ void CTrueTalkNPC::load(SimpleFile *file) { _fieldF4 = file->readNumber(); _fieldF8 = file->readNumber(); _speechTimerId = file->readNumber(); - _field100 = file->readNumber(); + _speechCounter = file->readNumber(); _field104 = file->readNumber(); CCharacter::load(file); @@ -95,8 +96,11 @@ bool CTrueTalkNPC::DismissBotMsg(CDismissBotMsg *msg) { } bool CTrueTalkNPC::TrueTalkNotifySpeechStartedMsg(CTrueTalkNotifySpeechStartedMsg *msg) { + debugC(ERROR_DETAILED, kDebugScripts, "%s TrueTalkNotifySpeechStartedMsg flags=%x dialogueId=%d", + getName().c_str(), _npcFlags, msg->_dialogueId); + _npcFlags |= NPCFLAG_SPEAKING; - ++_field100; + ++_speechCounter; if (!(_npcFlags & NPCFLAG_8)) { // Stop any previous animation @@ -107,8 +111,8 @@ bool CTrueTalkNPC::TrueTalkNotifySpeechStartedMsg(CTrueTalkNotifySpeechStartedMs _speechDuration = msg->_speechDuration; _startTicks = getTicksCount(); - if (!hasActiveMovie() || (_npcFlags & NPCFLAG_2)) { - _npcFlags &= ~NPCFLAG_2; + if (!hasActiveMovie() || (_npcFlags & NPCFLAG_IDLING)) { + _npcFlags &= ~NPCFLAG_IDLING; stopMovie(); CNPCPlayTalkingAnimationMsg msg1(_speechDuration, 0, nullptr); @@ -125,12 +129,13 @@ bool CTrueTalkNPC::TrueTalkNotifySpeechStartedMsg(CTrueTalkNotifySpeechStartedMs } bool CTrueTalkNPC::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *msg) { + debugC(ERROR_DETAILED, kDebugScripts, "%s TrueTalkNotifySpeechEndedMsg flags=%x dialogueId=%d", getName().c_str(), _npcFlags, msg->_dialogueId); _npcFlags &= ~NPCFLAG_SPEAKING; - --_field100; + --_speechCounter; _speechDuration = 0; if (!(_npcFlags & NPCFLAG_8)) { - CNPCPlayTalkingAnimationMsg msg1(0, 2, 0); + CNPCPlayTalkingAnimationMsg msg1(0, 2, nullptr); msg1.execute(this); CNPCQueueIdleAnimMsg msg2; msg2.execute(this); @@ -140,8 +145,8 @@ bool CTrueTalkNPC::TrueTalkNotifySpeechEndedMsg(CTrueTalkNotifySpeechEndedMsg *m } bool CTrueTalkNPC::MovieEndMsg(CMovieEndMsg *msg) { - if (_npcFlags & NPCFLAG_2) { - _npcFlags &= ~NPCFLAG_2; + if (_npcFlags & NPCFLAG_IDLING) { + _npcFlags &= ~NPCFLAG_IDLING; CNPCQueueIdleAnimMsg idleMsg; idleMsg.execute(this); return true; @@ -170,8 +175,8 @@ bool CTrueTalkNPC::NPCQueueIdleAnimMsg(CNPCQueueIdleAnimMsg *msg) { } bool CTrueTalkNPC::TimerMsg(CTimerMsg *msg) { - if (_npcFlags & NPCFLAG_4) { - if (_field100 > 0) + if (_npcFlags & NPCFLAG_START_IDLING) { + if (_speechCounter > 0) return false; CNPCPlayIdleAnimationMsg idleMsg; @@ -181,7 +186,7 @@ bool CTrueTalkNPC::TimerMsg(CTimerMsg *msg) { animMsg.execute(this); } - _npcFlags &= ~NPCFLAG_2; + _npcFlags &= ~NPCFLAG_IDLING; } } diff --git a/engines/titanic/npcs/true_talk_npc.h b/engines/titanic/npcs/true_talk_npc.h index 3adf055449..d9ea037a72 100644 --- a/engines/titanic/npcs/true_talk_npc.h +++ b/engines/titanic/npcs/true_talk_npc.h @@ -30,8 +30,8 @@ namespace Titanic { enum NpcFlag { - NPCFLAG_SPEAKING = 1, NPCFLAG_2 = 2, NPCFLAG_4 = 4, NPCFLAG_8 = 8, - NPCFLAG_10000 = 0x10000, NPCFLAG_20000 = 0x20000, + NPCFLAG_SPEAKING = 1, NPCFLAG_IDLING = 2, NPCFLAG_START_IDLING = 4, + NPCFLAG_8 = 8, NPCFLAG_10000 = 0x10000, NPCFLAG_20000 = 0x20000, NPCFLAG_40000 = 0x40000, NPCFLAG_80000 = 0x80000, NPCFLAG_100000 = 0x100000, NPCFLAG_200000 = 0x200000, NPCFLAG_400000 = 0x400000, NPCFLAG_800000 = 0x800000, @@ -66,7 +66,7 @@ protected: protected: void processInput(CTextInputMsg *msg, CViewItem *view); public: - int _field100; + int _speechCounter; public: CLASSDEF; CTrueTalkNPC(); diff --git a/engines/titanic/pet_control/pet_control.cpp b/engines/titanic/pet_control/pet_control.cpp index 4cdab24f6d..415d1e9821 100644 --- a/engines/titanic/pet_control/pet_control.cpp +++ b/engines/titanic/pet_control/pet_control.cpp @@ -27,6 +27,7 @@ #include "titanic/messages/pet_messages.h" #include "titanic/game_manager.h" #include "titanic/game_state.h" +#include "titanic/titanic.h" namespace Titanic { @@ -37,6 +38,7 @@ BEGIN_MESSAGE_MAP(CPetControl, CGameObject) ON_MESSAGE(MouseDragEndMsg) ON_MESSAGE(MouseButtonUpMsg) ON_MESSAGE(MouseDoubleClickMsg) + ON_MESSAGE(MouseWheelMsg) ON_MESSAGE(KeyCharMsg) ON_MESSAGE(VirtualKeyCharMsg) ON_MESSAGE(TimerMsg) @@ -154,7 +156,7 @@ void CPetControl::postLoad() { if (!_remoteTargetName.empty() && root) _remoteTarget = dynamic_cast<CGameObject *>(root->findByName(_remoteTargetName)); - setArea(_currentArea); + setArea(_currentArea, true); loaded(); } @@ -184,7 +186,9 @@ void CPetControl::resetRemoteTarget() { } void CPetControl::setActiveNPC(CTrueTalkNPC *npc) { - if (_activeNPC == npc) { + if (_activeNPC != npc) { + _activeNPC = npc; + if (_activeNPC) { _activeNPCName = npc->getName(); _conversations.displayNPCName(npc); @@ -207,8 +211,8 @@ void CPetControl::resetActiveNPC() { _activeNPCName = ""; } -PetArea CPetControl::setArea(PetArea newArea) { - if (newArea == _currentArea || !isAreaActive()) +PetArea CPetControl::setArea(PetArea newArea, bool forceChange) { + if ((!forceChange && newArea == _currentArea) || !isAreaActive()) return _currentArea; // Signal the currently active area that it's being left @@ -315,6 +319,13 @@ bool CPetControl::MouseDoubleClickMsg(CMouseDoubleClickMsg *msg) { return _sections[_currentArea]->MouseDoubleClickMsg(msg); } +bool CPetControl::MouseWheelMsg(CMouseWheelMsg *msg) { + if (!containsPt(msg->_mousePos) || isInputLocked()) + return false; + + return _sections[_currentArea]->MouseWheelMsg(msg); +} + bool CPetControl::KeyCharMsg(CKeyCharMsg *msg) { if (isInputLocked()) return false; @@ -377,7 +388,7 @@ bool CPetControl::checkDragEnd(CGameObject *item) const { } void CPetControl::displayMessage(StringId stringId, int param) const { - CString msg = CString::format(_strings[stringId].c_str(), param); + CString msg = CString::format(g_vm->_strings[stringId].c_str(), param); _sections[_currentArea]->displayMessage(msg); } @@ -388,7 +399,7 @@ void CPetControl::displayMessage(const CString &str, int param) const { void CPetControl::addTranslation(StringId id1, StringId id2) { setArea(PET_TRANSLATION); - _translation.addTranslation(_strings[id1], _strings[id2]); + _translation.addTranslation(g_vm->_strings[id1], g_vm->_strings[id2]); } void CPetControl::clearTranslation() { @@ -461,7 +472,7 @@ void CPetControl::moveToHiddenRoom(CTreeItem *item) { CRoomItem *room = getHiddenRoom(); if (room) { item->detach(); - room->addUnder(item); + item->addUnder(room); } } @@ -594,6 +605,7 @@ void CPetControl::onSummonBot(const CString &name, int val) { COnSummonBotMsg summonMsg(val); summonMsg.execute(bot); + makeDirty(); } } @@ -609,10 +621,13 @@ bool CPetControl::dismissBot(const CString &name) { CDismissBotMsg dismissMsg; for (CTreeItem *treeItem = view->getFirstChild(); treeItem; treeItem = treeItem->scan(view)) { - if (!treeItem->getName().compareToIgnoreCase(name)) - dismissMsg.execute(treeItem); - else - result = true; + CGameObject *obj = dynamic_cast<CGameObject *>(treeItem); + if (obj) { + if (!obj->getName().compareToIgnoreCase(name)) + result = true; + else + dismissMsg.execute(treeItem); + } } return result; @@ -686,7 +701,7 @@ void CPetControl::resetDials0() { int CPetControl::getMailDest(const CRoomFlags &roomFlags) const { if (!roomFlags.isSuccUBusRoomFlags()) - return roomFlags.getPassengerClassNum(); + return (int)roomFlags.getPassengerClassNum(); return roomFlags.getSuccUBusNum(roomFlags.getSuccUBusRoomName()); } diff --git a/engines/titanic/pet_control/pet_control.h b/engines/titanic/pet_control/pet_control.h index d42dff598c..34d1330b52 100644 --- a/engines/titanic/pet_control/pet_control.h +++ b/engines/titanic/pet_control/pet_control.h @@ -68,7 +68,6 @@ private: CRoomItem *_hiddenRoom; Rect _drawBounds; PetEventInfo _timers[2]; - Strings _strings; private: /** * Returns true if the control is in a valid state @@ -116,6 +115,7 @@ protected: bool MouseDragEndMsg(CMouseDragEndMsg *msg); bool MouseButtonUpMsg(CMouseButtonUpMsg *msg); bool MouseDoubleClickMsg(CMouseDoubleClickMsg *msg); + bool MouseWheelMsg(CMouseWheelMsg *msg); bool KeyCharMsg(CKeyCharMsg *msg); bool VirtualKeyCharMsg(CVirtualKeyCharMsg *msg); bool TimerMsg(CTimerMsg *msg); @@ -180,7 +180,7 @@ public: /** * Sets the currently viewed area within the PET */ - PetArea setArea(PetArea newSection); + PetArea setArea(PetArea newSection, bool forceChange = false); /** * Hides the text cursor in the current section, if applicable @@ -421,14 +421,14 @@ public: /** * Gives the player a new assigned room in the specified passenger class */ - void reassignRoom(int passClassNum) { + void reassignRoom(PassengerClass passClassNum) { _rooms.reassignRoom(passClassNum); } /** * Change the current location passenger class */ - bool changeLocationClass(int newClassNum) { + bool changeLocationClass(PassengerClass newClassNum) { return _rooms.changeLocationClass(newClassNum); } diff --git a/engines/titanic/pet_control/pet_conversations.cpp b/engines/titanic/pet_control/pet_conversations.cpp index 58dcd57384..c2cddd9598 100644 --- a/engines/titanic/pet_control/pet_conversations.cpp +++ b/engines/titanic/pet_control/pet_conversations.cpp @@ -23,6 +23,7 @@ #include "titanic/pet_control/pet_conversations.h" #include "titanic/pet_control/pet_control.h" #include "titanic/game_manager.h" +#include "titanic/titanic.h" namespace Titanic { @@ -116,11 +117,11 @@ void CPetConversations::draw(CScreenManager *screenManager) { _textInput.draw(screenManager); if (_logChanged) { - int startIndex = _log.getLinesStart(); - if (startIndex >= 0) { - int npcNum = _log.getNPCNum(1, startIndex); + int endIndex = _log.displayEndIndex(); + if (endIndex >= 0) { + int npcNum = _log.getNPCNum(1, endIndex); if (npcNum > 0 && npcNum < 10) - _npcNum = npcNum; + _npcNum = npcNum - 1; } _logChanged = false; @@ -164,7 +165,7 @@ bool CPetConversations::MouseButtonUpMsg(CMouseButtonUpMsg *msg) { if (_doorBot.MouseButtonUpMsg(msg->_mousePos)) { switch (canSummonBot("DoorBot")) { case SUMMON_CANT: - _log.addLine("Sadly, it is not possible to summon the DoorBot from this location.", getColor(1)); + _log.addLine(g_vm->_strings[CANT_SUMMON_DOORBOT], getColor(1)); break; case SUMMON_CAN: summonBot("DoorBot"); @@ -181,7 +182,7 @@ bool CPetConversations::MouseButtonUpMsg(CMouseButtonUpMsg *msg) { if (_bellBot.MouseButtonUpMsg(msg->_mousePos)) { switch (canSummonBot("BellBot")) { case SUMMON_CANT: - _log.addLine("Sadly, it is not possible to summon the BellBot from this location.", getColor(1)); + _log.addLine(g_vm->_strings[CANT_SUMMON_BELLBOT], getColor(1)); break; case SUMMON_CAN: summonBot("BellBot"); @@ -203,6 +204,15 @@ bool CPetConversations::MouseDoubleClickMsg(CMouseDoubleClickMsg *msg) { || _scrollUp.MouseDoubleClickMsg(msg->_mousePos); } +bool CPetConversations::MouseWheelMsg(CMouseWheelMsg *msg) { + if (msg->_wheelUp) + scrollUp(); + else + scrollDown(); + + return true; +} + bool CPetConversations::KeyCharMsg(CKeyCharMsg *msg) { Common::KeyState keyState; keyState.ascii = msg->_key; @@ -268,40 +278,42 @@ void CPetConversations::timerExpired(int val) { } void CPetConversations::displayNPCName(CGameObject *npc) { + const Strings &strings = g_vm->_strings; + if (npc) { displayMessage(CString()); - CString msg = "Talking to "; + CString msg = strings[TALKING_TO]; CString name = npc->getName(); int id = 1; if (name.contains("Doorbot")) { - msg += "the DoorBot"; - } else if (name.contains("DeskBot")) { + msg += strings[DOORBOT_NAME]; + } else if (name.contains("Deskbot")) { id = 2; - msg += "the DeskBot"; + msg += strings[DESKBOT_NAME]; } else if (name.contains("LiftBot")) { id = 3; - msg += "a LiftBot"; + msg += strings[LIFTBOT_NAME]; } else if (name.contains("Parrot")) { id = 4; - msg += "the Parrot"; + msg += strings[PARROT_NAME]; } else if (name.contains("BarBot")) { id = 5; - msg += "the BarBot"; + msg += strings[BARBOT_NAME]; } else if (name.contains("ChatterBot")) { id = 6; - msg += "a ChatterBot"; + msg += strings[CHATTERBOT_NAME]; } else if (name.contains("BellBot")) { id = 7; - msg += "the BellBot"; + msg += strings[BELLBOT_NAME]; } else if (name.contains("Maitre")) { id = 8; - msg += "the Maitre d'Bot"; + msg += strings[MAITRED_NAME]; } else if (name.contains("Succubus") || name.contains("Sub")) { id = 9; - msg += "a Succ-U-Bus"; + msg += strings[SUCCUBUS_NAME]; } else { - msg += "Unknown"; + msg += strings[UNKNOWN_NAME]; } _log.setNPC(1, id); @@ -418,7 +430,7 @@ int CPetConversations::canSummonBot(const CString &name) { void CPetConversations::summonBot(const CString &name) { if (_petControl) { - if (_petControl->getPassengerClass() >= 4) { + if (_petControl->getPassengerClass() >= UNCHECKED) { _petControl->displayMessage(AT_LEAST_3RD_CLASS_FOR_HELP); } else { _petControl->summonBot(name, 0); @@ -498,7 +510,7 @@ void CPetConversations::textLineEntered(const CString &textLine) { if (!inputMsg._response.empty()) _log.addLine(inputMsg._response); } else { - _log.addLine("There is no one here to talk to", getColor(1)); + _log.addLine(g_vm->_strings[NO_ONE_TO_TALK_TO], getColor(1)); } // Clear input line and scroll log down to end to show response diff --git a/engines/titanic/pet_control/pet_conversations.h b/engines/titanic/pet_control/pet_conversations.h index efb7db4277..1837e5df2a 100644 --- a/engines/titanic/pet_control/pet_conversations.h +++ b/engines/titanic/pet_control/pet_conversations.h @@ -164,6 +164,7 @@ public: virtual bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); virtual bool MouseButtonUpMsg(CMouseButtonUpMsg *msg); virtual bool MouseDoubleClickMsg(CMouseDoubleClickMsg *msg); + virtual bool MouseWheelMsg(CMouseWheelMsg *msg); virtual bool KeyCharMsg(CKeyCharMsg *msg); virtual bool VirtualKeyCharMsg(CVirtualKeyCharMsg *msg); diff --git a/engines/titanic/pet_control/pet_drag_chev.cpp b/engines/titanic/pet_control/pet_drag_chev.cpp index 7816570a23..4fe64283ff 100644 --- a/engines/titanic/pet_control/pet_drag_chev.cpp +++ b/engines/titanic/pet_control/pet_drag_chev.cpp @@ -54,20 +54,16 @@ bool CPetDragChev::MouseDragMoveMsg(CMouseDragMoveMsg *msg) { } bool CPetDragChev::MouseDragEndMsg(CMouseDragEndMsg *msg) { - if (msg->_dropTarget) { - CSuccUBus *succubus = dynamic_cast<CSuccUBus *>(msg->_dropTarget); + CSuccUBus *succubus = dynamic_cast<CSuccUBus *>(msg->_dropTarget); - if (succubus) { - CSetChevRoomBits chevMsg(_id); - chevMsg.execute(succubus); - } else { - CPetControl *petControl = getPetControl(); - if (petControl && petControl->contains(msg->_mousePos) - && msg->_mousePos.x < 528) { - if (petControl->checkDragEnd(this)) - petMoveToHiddenRoom(); - } - } + if (succubus) { + CSetChevRoomBits chevMsg(_id); + chevMsg.execute(succubus); + } else { + CPetControl *petControl = getPetControl(); + if (!petControl || !petControl->contains(msg->_mousePos) + || msg->_mousePos.x >= 528 || !petControl->checkDragEnd(this)) + petMoveToHiddenRoom(); } return true; diff --git a/engines/titanic/pet_control/pet_gfx_element.cpp b/engines/titanic/pet_control/pet_gfx_element.cpp index e43c847bb1..3fdfd02f3a 100644 --- a/engines/titanic/pet_control/pet_gfx_element.cpp +++ b/engines/titanic/pet_control/pet_gfx_element.cpp @@ -49,11 +49,11 @@ void CPetGfxElement::reset(const CString &name, CPetControl *petControl, PetElem return; CString numString(3); - int classNum = petControl->getPassengerClass(); + PassengerClass classNum = petControl->getPassengerClass(); - if (classNum >= 1 && classNum <= 3) { + if (classNum >= FIRST_CLASS && classNum <= THIRD_CLASS) { numString = CString(classNum); - } else if (classNum == 4) { + } else if (classNum == UNCHECKED) { int stateC = petControl->getPriorClass(); if (stateC == 1) numString = CString(stateC); diff --git a/engines/titanic/pet_control/pet_glyphs.cpp b/engines/titanic/pet_control/pet_glyphs.cpp index bd05342095..5cc428b104 100644 --- a/engines/titanic/pet_control/pet_glyphs.cpp +++ b/engines/titanic/pet_control/pet_glyphs.cpp @@ -101,9 +101,9 @@ void CPetGlyphs::setup(int numVisible, CPetSection *owner) { int buttonsLeft = numVisible * 70 + 21; _scrollLeft.setBounds(Rect(0, 0, 31, 15)); - _scrollLeft.translate(buttonsLeft, 373); + _scrollLeft.translate(buttonsLeft + 7, 373); _scrollRight.setBounds(Rect(0, 0, 31, 15)); - _scrollRight.translate(buttonsLeft, 413); + _scrollRight.translate(buttonsLeft + 7, 413); } void CPetGlyphs::reset() { @@ -155,7 +155,7 @@ void CPetGlyphs::draw(CScreenManager *screenManager) { int itemIndex = getItemIndex(index); if (itemIndex >= 0 && itemIndex < listSize) { - Point pt = getPosition(itemIndex); + Point pt = getPosition(index); CPetGlyph *glyph = getGlyph(itemIndex); if (glyph) diff --git a/engines/titanic/pet_control/pet_inventory.cpp b/engines/titanic/pet_control/pet_inventory.cpp index be01146398..c1eb0754a2 100644 --- a/engines/titanic/pet_control/pet_inventory.cpp +++ b/engines/titanic/pet_control/pet_inventory.cpp @@ -242,7 +242,7 @@ void CPetInventory::playMovie(CGameObject *movie, int flag) { if (_movie) { if (flag) - _movie->playMovie(0, 14, 1); + _movie->playMovie(0, 14, MOVIE_REPEAT); else _movie->playMovie(0); } diff --git a/engines/titanic/pet_control/pet_inventory_glyphs.cpp b/engines/titanic/pet_control/pet_inventory_glyphs.cpp index 03293eb453..e6087b5c02 100644 --- a/engines/titanic/pet_control/pet_inventory_glyphs.cpp +++ b/engines/titanic/pet_control/pet_inventory_glyphs.cpp @@ -295,7 +295,7 @@ void CPetInventoryGlyph::startBackgroundMovie() { if (_owner) { CPetInventory *section = dynamic_cast<CPetInventory *>(_owner->getOwner()); if (section) - section->playMovie(_background, 1); + section->playMovie(_background, MOVIE_REPEAT); } } @@ -303,7 +303,7 @@ void CPetInventoryGlyph::startForegroundMovie() { if (_owner) { CPetInventory *section = dynamic_cast<CPetInventory *>(_owner->getOwner()); if (section) - section->playMovie(_image, 1); + section->playMovie(_image, MOVIE_REPEAT); } } @@ -311,7 +311,7 @@ void CPetInventoryGlyph::stopMovie() { if (_owner) { CPetInventory *section = dynamic_cast<CPetInventory *>(_owner->getOwner()); if (section) - section->playMovie(nullptr, 1); + section->playMovie(nullptr, MOVIE_REPEAT); } } diff --git a/engines/titanic/pet_control/pet_load_save.cpp b/engines/titanic/pet_control/pet_load_save.cpp index 136953cab7..aa438c06d3 100644 --- a/engines/titanic/pet_control/pet_load_save.cpp +++ b/engines/titanic/pet_control/pet_load_save.cpp @@ -159,7 +159,7 @@ void CPetLoadSave::highlightChange() { uint col = section ? section->getColor(3) : 0; for (int idx = 0; idx < SAVEGAME_SLOTS_COUNT; ++idx) - _slotNames[idx].setColor(col); + _slotNames[idx].setLineColor(0, col); if (_savegameSlotNum != -1) { col = section ? section->getColor(4) : 0; diff --git a/engines/titanic/pet_control/pet_real_life.h b/engines/titanic/pet_control/pet_real_life.h index b359d285c8..7c7101a84b 100644 --- a/engines/titanic/pet_control/pet_real_life.h +++ b/engines/titanic/pet_control/pet_real_life.h @@ -87,11 +87,6 @@ public: virtual CGameObject *dragEnd(const Point &pt) const { return nullptr; } /** - * Display a message - */ - virtual void displayMessage(const CString &msg) {} - - /** * Returns true if the object is in a valid state */ virtual bool isValid(CPetControl *petControl); diff --git a/engines/titanic/pet_control/pet_remote.cpp b/engines/titanic/pet_control/pet_remote.cpp index 7e98308114..46af55c4d3 100644 --- a/engines/titanic/pet_control/pet_remote.cpp +++ b/engines/titanic/pet_control/pet_remote.cpp @@ -118,10 +118,10 @@ bool CPetRemote::reset() { _right.reset("PetRight", _petControl, MODE_UNSELECTED); _top.reset("PetTopUp", _petControl, MODE_SELECTED); _top.reset("PetTop", _petControl, MODE_UNSELECTED); - _bottom.reset("PetBottomUp", _petControl, MODE_SELECTED); - _bottom.reset("PetBottom", _petControl, MODE_UNSELECTED); - _action.reset("PetActionUp", _petControl, MODE_SELECTED); - _action.reset("PetAction", _petControl, MODE_UNSELECTED); + _bottom.reset("PetBottomUp", _petControl, MODE_UNSELECTED); + _bottom.reset("PetBottom", _petControl, MODE_SELECTED); + _action.reset("PetActionUp", _petControl, MODE_UNSELECTED); + _action.reset("PetAction", _petControl, MODE_SELECTED); _send.reset("PetActSend0", _petControl, MODE_UNSELECTED); _send.reset("PetActSend1", _petControl, MODE_SELECTED); @@ -314,8 +314,8 @@ bool CPetRemote::getRemoteData(int roomIndex, Common::Array<uint> &indexes) { const byte *p = &REMOTE_DATA[0]; for (int idx = 0; idx < TOTAL_ROOMS; ++idx) { if (*p == roomIndex) { - for (int ctr = 0; ctr < *p; ++ctr) - indexes.push_back(p[ctr + 1]); + for (int ctr = 0; ctr < p[1]; ++ctr) + indexes.push_back(p[ctr + 2]); return true; } diff --git a/engines/titanic/pet_control/pet_remote_glyphs.cpp b/engines/titanic/pet_control/pet_remote_glyphs.cpp index aa756bc941..2e69ad6bce 100644 --- a/engines/titanic/pet_control/pet_remote_glyphs.cpp +++ b/engines/titanic/pet_control/pet_remote_glyphs.cpp @@ -24,6 +24,7 @@ #include "titanic/pet_control/pet_remote.h" #include "titanic/pet_control/pet_control.h" #include "titanic/messages/pet_messages.h" +#include "titanic/support/strings.h" #include "titanic/titanic.h" namespace Titanic { @@ -58,22 +59,22 @@ bool CBasicRemoteGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) { CPetRemoteGlyph::setup(petControl, owner); setDefaults(_gfxName, petControl); if (owner) - _gfxElement = getElement(18); + _callButton = getElement(18); return true; } void CBasicRemoteGlyph::draw2(CScreenManager *screenManager) { - if (_gfxElement) - _gfxElement->draw(screenManager); + if (_callButton) + _callButton->draw(screenManager); } bool CBasicRemoteGlyph::MouseButtonDownMsg(const Point &pt) { - return _gfxElement && _gfxElement->MouseButtonDownMsg(pt); + return _callButton && _callButton->MouseButtonDownMsg(pt); } bool CBasicRemoteGlyph::MouseButtonUpMsg(const Point &pt) { - if (_gfxElement && _gfxElement->MouseButtonUpMsg(pt)) { - getOwner()->generateMessage(RMSG_ACTIVATE, "Lift"); + if (_callButton && _callButton->MouseButtonUpMsg(pt)) { + getOwner()->generateMessage(RMSG_ACTIVATE, _msgString); return true; } @@ -124,22 +125,22 @@ bool CRemoteGotoGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) { setDefaults(_gfxName, petControl); if (owner) - _gfxElement = getElement(7); + _goButton = getElement(7); return true; } void CRemoteGotoGlyph::draw2(CScreenManager *screenManager) { - if (_gfxElement) - _gfxElement->draw(screenManager); + if (_goButton) + _goButton->draw(screenManager); } bool CRemoteGotoGlyph::MouseButtonDownMsg(const Point &pt) { - return _gfxElement && _gfxElement->MouseButtonDownMsg(pt); + return _goButton && _goButton->MouseButtonDownMsg(pt); } bool CRemoteGotoGlyph::MouseButtonUpMsg(const Point &pt) { - if (!_gfxElement || !_gfxElement->MouseButtonUpMsg(pt)) + if (!_goButton || !_goButton->MouseButtonUpMsg(pt)) return false; CPetControl *petControl = getPetControl(); @@ -216,7 +217,7 @@ bool CTelevisionControlGlyph::MouseButtonUpMsg(const Point &pt) { } void CTelevisionControlGlyph::getTooltip(CPetText *text) { - text->setText("Television control"); + text->setText(TELEVISION_CONTROL); } /*------------------------------------------------------------------------*/ @@ -280,7 +281,7 @@ bool CEntertainmentDeviceGlyph::MouseButtonUpMsg(const Point &pt) { } void CEntertainmentDeviceGlyph::getTooltip(CPetText *text) { - text->setText("Operate visual entertainment device"); + text->setText(OPERATE_ENTERTAINMENT); } /*------------------------------------------------------------------------*/ @@ -334,7 +335,7 @@ bool COperateLightsGlyph::MouseButtonUpMsg(const Point &pt) { } void COperateLightsGlyph::getTooltip(CPetText *text) { - text->setText("Operate the lights"); + text->setText(OPERATE_LIGHTS); } /*------------------------------------------------------------------------*/ @@ -346,7 +347,7 @@ bool CDeployFloralGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) { } void CDeployFloralGlyph::getTooltip(CPetText *text) { - text->setText("Deploy floral enhancement"); + text->setText(DEPLOY_FLORAL_ENHANCEMENT); } @@ -359,7 +360,7 @@ bool CDeployFullyRelaxationGlyph::setup(CPetControl *petControl, CPetGlyphs *own } void CDeployFullyRelaxationGlyph::getTooltip(CPetText *text) { - text->setText("Deploy fully recumbent relaxation device"); + text->setText(DEPLOY_FULLY_RELAXATION); } /*------------------------------------------------------------------------*/ @@ -371,7 +372,7 @@ bool CDeployComfortGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) { } void CDeployComfortGlyph::getTooltip(CPetText *text) { - text->setText("Deploy comfort workstation"); + text->setText(DEPLOY_COMFORT_WORKSTATION); } /*------------------------------------------------------------------------*/ @@ -383,7 +384,7 @@ bool CDeployMinorStorageGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) } void CDeployMinorStorageGlyph::getTooltip(CPetText *text) { - text->setText("Deploy minor horizontally mobile storage compartment"); + text->setText(DEPLOY_MINOR_STORAGE); } /*------------------------------------------------------------------------*/ @@ -395,7 +396,7 @@ bool CDeployMajorRelaxationGlyph::setup(CPetControl *petControl, CPetGlyphs *own } void CDeployMajorRelaxationGlyph::getTooltip(CPetText *text) { - text->setText("Deploy major semi-recumbent relaxation device"); + text->setText(DEPLOY_MAJOR_RELAXATION); } /*------------------------------------------------------------------------*/ @@ -407,7 +408,7 @@ bool CInflateRelaxationGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) } void CInflateRelaxationGlyph::getTooltip(CPetText *text) { - text->setText("Inflate fully recumbent relaxation device "); + text->setText(INFLATE_RELAXATION_DEVICE); } /*------------------------------------------------------------------------*/ @@ -419,7 +420,7 @@ bool CDeployMaintenanceGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) } void CDeployMaintenanceGlyph::getTooltip(CPetText *text) { - text->setText("Deploy personal maintenance hub"); + text->setText(DEPLOY_MAINTENANCE_HUB); } /*------------------------------------------------------------------------*/ @@ -431,7 +432,7 @@ bool CDeployWorkSurfaceGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) } void CDeployWorkSurfaceGlyph::getTooltip(CPetText *text) { - text->setText("Deploy executive horizontal worksurface"); + text->setText(DEPLOY_EXECUTIVE_SURFACE); } /*------------------------------------------------------------------------*/ @@ -443,7 +444,7 @@ bool CDeployMinorRelaxationGlyph::setup(CPetControl *petControl, CPetGlyphs *own } void CDeployMinorRelaxationGlyph::getTooltip(CPetText *text) { - text->setText("Deploy minor semi-recumbent relaxation device"); + text->setText(DEPLOY_MINOR_RELAXATION); } /*------------------------------------------------------------------------*/ @@ -455,7 +456,7 @@ bool CDeploySinkGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) { } void CDeploySinkGlyph::getTooltip(CPetText *text) { - text->setText("Deploy aqueous cleansing receptacle"); + text->setText(DEPLOY_SINK); } /*------------------------------------------------------------------------*/ @@ -467,7 +468,7 @@ bool CDeployMajorStorageGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) } void CDeployMajorStorageGlyph::getTooltip(CPetText *text) { - text->setText("Deploy major horizontally mobile storage compartment"); + text->setText(DEPLOY_MAJOR_STORAGE); } /*------------------------------------------------------------------------*/ @@ -515,7 +516,7 @@ bool CSuccubusDeliveryGlyph::MouseButtonUpMsg(const Point &pt) { } void CSuccubusDeliveryGlyph::getTooltip(CPetText *text) { - text->setText("Succ-U-Bus delivery system control"); + text->setText(SUCCUBUS_DELIVERY_SYSTEM); } /*------------------------------------------------------------------------*/ @@ -554,9 +555,67 @@ bool CNavigationControllerGlyph::MouseButtonUpMsg(const Point &pt) { } void CNavigationControllerGlyph::getTooltip(CPetText *text) { - text->setText("Navigation controller"); + text->setText(NAVIGATION_CONTROLLER); +} + +/*------------------------------------------------------------------------*/ + +CSummonElevatorGlyph::CSummonElevatorGlyph() : CBasicRemoteGlyph( + "3PetLift", g_vm->_strings[SUMMON_ELEVATOR], "Lift") { +} + +/*------------------------------------------------------------------------*/ + +CSummonPelleratorGlyph::CSummonPelleratorGlyph() : CBasicRemoteGlyph( + "3PetPellerator", g_vm->_strings[SUMMON_PELLERATOR], "Pellerator") { +} + +/*------------------------------------------------------------------------*/ + +CGotoBottomOfWellGlyph::CGotoBottomOfWellGlyph() : CRemoteGotoGlyph("3PetBotOfWell", + g_vm->_strings[GO_TO_BOTTOM_OF_WELL], 10) { +} + +/*------------------------------------------------------------------------*/ + +CGotoTopOfWellGlyph::CGotoTopOfWellGlyph() : CRemoteGotoGlyph("3PetTopOfWell", + g_vm->_strings[GO_TO_TOP_OF_WELL], 32) { +} + +/*------------------------------------------------------------------------*/ + +CGotoStateroomGlyph::CGotoStateroomGlyph() : CRemoteGotoGlyph("3PetRoom", + g_vm->_strings[GO_TO_STATEROOM], 33) { +} + +/*------------------------------------------------------------------------*/ + +CGotoBarGlyph::CGotoBarGlyph() : CRemoteGotoGlyph("3PetBar", + g_vm->_strings[GO_TO_BAR], 7) { +} + +/*------------------------------------------------------------------------*/ + +CGotoPromenadeDeckGlyph::CGotoPromenadeDeckGlyph() : CRemoteGotoGlyph("3PetPromDeck", + g_vm->_strings[GO_TO_PROMENADE_DECK], 23) { } /*------------------------------------------------------------------------*/ +CGotoArboretumGlyph::CGotoArboretumGlyph() : CRemoteGotoGlyph("3PetArboretum", + g_vm->_strings[GO_TO_ARBORETUM], 5) { +} + +/*------------------------------------------------------------------------*/ + +CGotoMusicRoomGlyph::CGotoMusicRoomGlyph() : CRemoteGotoGlyph("3PetMusicRoom", + g_vm->_strings[GO_TO_MUSIC_ROOM], 20) { +} + +/*------------------------------------------------------------------------*/ + +CGotoRestaurantGlyph::CGotoRestaurantGlyph() : CRemoteGotoGlyph("3Pet1stClassRest", + g_vm->_strings[GO_TO_1ST_CLASS_RESTAURANT], 1) { +} + } // End of namespace Titanic diff --git a/engines/titanic/pet_control/pet_remote_glyphs.h b/engines/titanic/pet_control/pet_remote_glyphs.h index 6114c81a9f..c661a1e2af 100644 --- a/engines/titanic/pet_control/pet_remote_glyphs.h +++ b/engines/titanic/pet_control/pet_remote_glyphs.h @@ -65,9 +65,9 @@ public: class CPetRemoteGlyph : public CPetGlyph { protected: - CPetGfxElement *_gfxElement; + CPetGfxElement *_callButton; protected: - CPetRemoteGlyph() : CPetGlyph(), _gfxElement(nullptr) {} + CPetRemoteGlyph() : CPetGlyph(), _callButton(nullptr) {} /** * Set defaults for the glyph @@ -150,13 +150,13 @@ public: class CRemoteGotoGlyph : public CPetRemoteGlyph { protected: int _roomIndex; - CPetGfxElement *_gfxElement; + CPetGfxElement *_goButton; CString _gfxName, _tooltip; public: - CRemoteGotoGlyph() : CPetRemoteGlyph(), _gfxElement(nullptr), _roomIndex(21) {} - CRemoteGotoGlyph(const CString &gfxName, const CString &tooltip) : - CPetRemoteGlyph(), _gfxElement(nullptr), _roomIndex(21), - _gfxName(gfxName), _tooltip(tooltip) {} + CRemoteGotoGlyph() : CPetRemoteGlyph(), _goButton(nullptr), _roomIndex(21) {} + CRemoteGotoGlyph(const CString &gfxName, const CString &tooltip, int roomIndex) : + CPetRemoteGlyph(), _gfxName(gfxName), _tooltip(tooltip), _roomIndex(roomIndex), + _goButton(nullptr) {} /** * Setup the glyph @@ -186,14 +186,12 @@ public: class CSummonElevatorGlyph : public CBasicRemoteGlyph { public: - CSummonElevatorGlyph() : CBasicRemoteGlyph( - "3PetLift", "Summon Elevator", "Lift") {} + CSummonElevatorGlyph(); }; class CSummonPelleratorGlyph : public CBasicRemoteGlyph { public: - CSummonPelleratorGlyph() : CBasicRemoteGlyph( - "3PetPellerator", "Summon Pellerator", "Pellerator") {} + CSummonPelleratorGlyph(); }; class CTelevisionControlGlyph : public CPetRemoteGlyph { @@ -664,50 +662,42 @@ public: class CGotoBottomOfWellGlyph : public CRemoteGotoGlyph { public: - CGotoBottomOfWellGlyph() : CRemoteGotoGlyph("3PetBotOfWell", - "Go to the Bottom of the Well") {} + CGotoBottomOfWellGlyph(); }; class CGotoTopOfWellGlyph : public CRemoteGotoGlyph { public: - CGotoTopOfWellGlyph() : CRemoteGotoGlyph("3PetTopOfWell", - "Go to the Top of the Well") {} + CGotoTopOfWellGlyph(); }; class CGotoStateroomGlyph : public CRemoteGotoGlyph { public: - CGotoStateroomGlyph() : CRemoteGotoGlyph("3PetRoom", - "Go to your stateroom") {} + CGotoStateroomGlyph(); }; class CGotoBarGlyph : public CRemoteGotoGlyph { public: - CGotoBarGlyph() : CRemoteGotoGlyph("3PetBar", - "Go to the Bar") {} + CGotoBarGlyph(); }; class CGotoPromenadeDeckGlyph : public CRemoteGotoGlyph { public: - CGotoPromenadeDeckGlyph() : CRemoteGotoGlyph("3PetPromDeck", - "Go to the Promenade Deck") {} + CGotoPromenadeDeckGlyph(); }; class CGotoArboretumGlyph : public CRemoteGotoGlyph { public: - CGotoArboretumGlyph() : CRemoteGotoGlyph("3PetArboretum", - "Go to the Arboretum") {} + CGotoArboretumGlyph(); }; class CGotoMusicRoomGlyph : public CRemoteGotoGlyph { public: - CGotoMusicRoomGlyph() : CRemoteGotoGlyph("3PetMusicRoom", - "Go to the Music Room") {} + CGotoMusicRoomGlyph(); }; class CGotoRestaurantGlyph : public CRemoteGotoGlyph { public: - CGotoRestaurantGlyph() : CRemoteGotoGlyph("3Pet1stClassRest", - "Go to the First Class Restaurant") {} + CGotoRestaurantGlyph(); }; } // End of namespace Titanic diff --git a/engines/titanic/pet_control/pet_rooms.cpp b/engines/titanic/pet_control/pet_rooms.cpp index fb92f2692d..d32667e5f7 100644 --- a/engines/titanic/pet_control/pet_rooms.cpp +++ b/engines/titanic/pet_control/pet_rooms.cpp @@ -56,6 +56,7 @@ bool CPetRooms::reset() { void CPetRooms::draw(CScreenManager *screenManager) { _petControl->drawSquares(screenManager, 6); _plinth.draw(screenManager); + _glyphs.draw(screenManager); _glyphItem.drawAt(screenManager, getGlyphPos(), false); _text.draw(screenManager); } @@ -67,7 +68,7 @@ bool CPetRooms::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { if (!_glyphItem.contains(getGlyphPos(), msg->_mousePos)) return false; - _glyphItem.MouseButtonDownMsg(msg->_mousePos); + _glyphItem.selectGlyph(getGlyphPos(), msg->_mousePos); return true; } @@ -165,7 +166,8 @@ void CPetRooms::enter(PetArea oldArea) { } void CPetRooms::enterRoom(CRoomItem *room) { - + if (room) + resetHighlight(); } CPetText *CPetRooms::getText() { @@ -221,7 +223,7 @@ bool CPetRooms::setupControl(CPetControl *petControl) { _glyphs.setup(6, this); _glyphs.setFlags(GFLAG_16); _glyphItem.setup(petControl, &_glyphs); - _glyphItem.set38(1); + _glyphItem.setFlag(1); return true; } @@ -240,17 +242,17 @@ uint CPetRooms::getRoomFlags() const { if (flags) return flags; - int classNum = roomFlags.whatPassengerClass(_floorNum); + PassengerClass classNum = roomFlags.whatPassengerClass(_floorNum); roomFlags.setPassengerClassBits(classNum); roomFlags.setFloorNum(_floorNum); switch (classNum) { - case 1: + case FIRST_CLASS: roomFlags.setElevatorNum(_elevatorNum); roomFlags.setRoomBits(_roomNum); break; - case 2: + case SECOND_CLASS: if (_roomNum > 0) { if (_roomNum >= 3) { roomFlags.setElevatorNum(_elevatorNum == 1 || _elevatorNum == 2 ? 1 : 3); @@ -264,7 +266,7 @@ uint CPetRooms::getRoomFlags() const { } break; - case 3: + case THIRD_CLASS: roomFlags.setElevatorNum(_elevatorNum); roomFlags.setRoomBits(_roomNum + _field1CC * 6 - 6); break; @@ -276,7 +278,7 @@ uint CPetRooms::getRoomFlags() const { return roomFlags.get(); } -void CPetRooms::reassignRoom(int passClassNum) { +void CPetRooms::reassignRoom(PassengerClass passClassNum) { CPetRoomsGlyph *glyph = _glyphs.findAssignedRoom(); if (glyph) // Flag the old assigned room as no longer assigned @@ -329,12 +331,12 @@ CPetRoomsGlyph *CPetRooms::addGlyph(uint roomFlags, bool highlight_) { } } -bool CPetRooms::changeLocationClass(int newClassNum) { +bool CPetRooms::changeLocationClass(PassengerClass newClassNum) { CPetRoomsGlyph *glyph = _glyphs.findAssignedRoom(); if (!glyph) return 0; - glyph->changeLocation(newClassNum); + glyph->changeClass(newClassNum); return true; } diff --git a/engines/titanic/pet_control/pet_rooms.h b/engines/titanic/pet_control/pet_rooms.h index 28d6553a33..efc7e3c969 100644 --- a/engines/titanic/pet_control/pet_rooms.h +++ b/engines/titanic/pet_control/pet_rooms.h @@ -26,6 +26,7 @@ #include "titanic/pet_control/pet_section.h" #include "titanic/pet_control/pet_text.h" #include "titanic/pet_control/pet_rooms_glyphs.h" +#include "titanic/game_location.h" namespace Titanic { @@ -155,12 +156,12 @@ public: /** * Gives the player a new assigned room in the specified passenger class */ - void reassignRoom(int passClassNum); + void reassignRoom(PassengerClass passClassNum); /** * Change the current location passenger class */ - bool changeLocationClass(int newClassNum); + bool changeLocationClass(PassengerClass newClassNum); /** * Returns true if a room glyph exists with the given flags diff --git a/engines/titanic/pet_control/pet_rooms_glyphs.cpp b/engines/titanic/pet_control/pet_rooms_glyphs.cpp index e89e8072dc..f38ed28a80 100644 --- a/engines/titanic/pet_control/pet_rooms_glyphs.cpp +++ b/engines/titanic/pet_control/pet_rooms_glyphs.cpp @@ -30,15 +30,15 @@ namespace Titanic { CPetRoomsGlyph::CPetRoomsGlyph() : CPetGlyph(), - _roomFlags(0), _field38(0), _mode(RGM_UNASSIGNED), - _object0(nullptr), _object1(nullptr), _object2(nullptr), _object3(nullptr), - _object4(nullptr), _object5(nullptr), _object6(nullptr), _object7(nullptr) { + _roomFlags(0), _mailFlag(0), _mode(RGM_UNASSIGNED), + _chevLeftOnDim(nullptr), _chevLeftOffDim(nullptr), _chevLeftOnLit(nullptr), _chevLeftOffLit(nullptr), + _chevRightOnDim(nullptr), _chevRightOffDim(nullptr), _chevRightOnLit(nullptr), _chevRightOffLit(nullptr) { } CPetRoomsGlyph::CPetRoomsGlyph(uint flags) : CPetGlyph(), - _roomFlags(flags), _field38(0), _mode(RGM_UNASSIGNED), - _object0(nullptr), _object1(nullptr), _object2(nullptr), _object3(nullptr), - _object4(nullptr), _object5(nullptr), _object6(nullptr), _object7(nullptr) { + _roomFlags(flags), _mailFlag(0), _mode(RGM_UNASSIGNED), + _chevLeftOnDim(nullptr), _chevLeftOffDim(nullptr), _chevLeftOnLit(nullptr), _chevLeftOffLit(nullptr), + _chevRightOnDim(nullptr), _chevRightOffDim(nullptr), _chevRightOnLit(nullptr), _chevRightOffLit(nullptr) { } bool CPetRoomsGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) { @@ -46,14 +46,14 @@ bool CPetRoomsGlyph::setup(CPetControl *petControl, CPetGlyphs *owner) { return false; CPetSection *section = owner->getOwner(); - _object0 = section->getBackground(9); - _object1 = section->getBackground(12); - _object4 = section->getBackground(13); - _object5 = section->getBackground(10); - _object2 = section->getBackground(11); - _object3 = section->getBackground(14); - _object6 = section->getBackground(15); - _object7 = _object6; + _chevLeftOnDim = section->getBackground(8); + _chevLeftOffDim = section->getBackground(9); + _chevRightOnDim = section->getBackground(12); + _chevRightOffDim = section->getBackground(13); + _chevLeftOnLit = section->getBackground(10); + _chevLeftOffLit = section->getBackground(11); + _chevRightOnLit = section->getBackground(14); + _chevRightOffLit = section->getBackground(15); return true; } @@ -69,19 +69,19 @@ void CPetRoomsGlyph::drawAt(CScreenManager *screenManager, const Point &pt, bool uint roomBits = roomFlags.getRoomBits(); // Save a copy of object pointers that may be modified - CGameObject *obj0 = _object0; - CGameObject *obj1 = _object1; - CGameObject *obj4 = _object4; - CGameObject *obj5 = _object5; - - if (_field38 == 1 || isHighlighted_) { - _object0 = _object2; - _object1 = _object3; - _object4 = _object6; - _object5 = _object7; + CGameObject *leftOnDim = _chevLeftOnDim; + CGameObject *leftOffDim = _chevLeftOffDim; + CGameObject *rightOnDim = _chevRightOnDim; + CGameObject *rightOffDim = _chevRightOffDim; + + if (_mailFlag || isHighlighted_) { + _chevLeftOnDim = _chevLeftOnLit; + _chevLeftOffDim = _chevLeftOffLit; + _chevRightOnDim = _chevRightOnLit; + _chevRightOffDim = _chevRightOffLit; } - // Draw the images + // Draw the chevron fragments for each line Point destPt = pt; drawObjects(classBits + elevBits * 4, destPt, screenManager); destPt.y += 10; @@ -94,21 +94,21 @@ void CPetRoomsGlyph::drawAt(CScreenManager *screenManager, const Point &pt, bool drawObjects(((roomBits & 7) << 1) + (roomFlags.getBit0() ? 1 : 0), destPt, screenManager); - // Restore original object pointers - _object0 = obj0; - _object1 = obj1; - _object4 = obj4; - _object5 = obj5; + // Restore original image pointers + _chevLeftOnDim = leftOnDim; + _chevLeftOffDim = leftOffDim; + _chevRightOnDim = rightOnDim; + _chevRightOffDim = rightOffDim; } void CPetRoomsGlyph::selectGlyph(const Point &topLeft, const Point &pt) { - if (isAssigned()) { - bool isShiftPressed = g_vm->_window->getSpecialButtons() & MK_SHIFT; + if (!isAssigned()) { + bool isShiftPressed = g_vm->_events->getSpecialButtons() & MK_SHIFT; if (isShiftPressed) { int selection = getSelection(topLeft, pt); if (selection >= 0) - _roomFlags |= 1 << selection; + _roomFlags ^= 1 << selection; } updateTooltip(); @@ -116,7 +116,7 @@ void CPetRoomsGlyph::selectGlyph(const Point &topLeft, const Point &pt) { } bool CPetRoomsGlyph::dragGlyph(const Point &topLeft, CMouseDragStartMsg *msg) { - bool isShiftPressed = g_vm->_window->getSpecialButtons() & MK_SHIFT; + bool isShiftPressed = g_vm->_events->getSpecialButtons() & MK_SHIFT; CPetControl *petControl = getPetControl(); if (!isShiftPressed && petControl) { @@ -124,7 +124,7 @@ bool CPetRoomsGlyph::dragGlyph(const Point &topLeft, CMouseDragStartMsg *msg) { if (chevron) { chevron->_id = _roomFlags; - chevron->_isMail = _field38; + chevron->_isMail = _mailFlag != 0; petControl->removeFromInventory(chevron, false, false); chevron->loadSurface(); @@ -133,6 +133,8 @@ bool CPetRoomsGlyph::dragGlyph(const Point &topLeft, CMouseDragStartMsg *msg) { if (msg->execute(chevron)) return true; + + petControl->moveToHiddenRoom(chevron); } } @@ -148,9 +150,9 @@ void CPetRoomsGlyph::getTooltip(CPetText *text) { msg = "Your assigned room: "; } else if (isPreviouslyAssigned()) { msg = "A previously assigned room: "; - } else if (!_field38) { + } else if (!_mailFlag) { msg = "Saved Chevron: "; - } else if (_field38 == 1 && owner->getRoomFlags() == _roomFlags) { + } else if (_mailFlag == 1 && owner->getRoomFlags() == _roomFlags) { msg = "Current location: "; } @@ -183,9 +185,9 @@ void CPetRoomsGlyph::loadFlags(SimpleFile *file, int val) { } } -void CPetRoomsGlyph::changeLocation(int newClassNum) { +void CPetRoomsGlyph::changeClass(PassengerClass newClassNum) { CRoomFlags roomFlags(_roomFlags); - roomFlags.changeLocation(newClassNum); + roomFlags.changeClass(newClassNum); _roomFlags = roomFlags.get(); } @@ -212,15 +214,15 @@ int CPetRoomsGlyph::getSelection(const Point &topLeft, const Point &pt) { } void CPetRoomsGlyph::drawObjects(uint flags, const Point &pt, CScreenManager *screenManager) { - if (_object0 && _object1 && _object4 && _object5) { + if (_chevLeftOnDim && _chevLeftOffDim && _chevRightOnDim && _chevRightOffDim) { Point destPos = pt; - ((flags & 8) ? _object0 : _object5)->draw(screenManager, destPos); + ((flags & 8) ? _chevLeftOnDim : _chevLeftOffDim)->draw(screenManager, destPos); destPos.x += 13; - ((flags & 4) ? _object4 : _object5)->draw(screenManager, destPos); + ((flags & 4) ? _chevRightOnDim : _chevRightOffDim)->draw(screenManager, destPos); destPos.x += 13; - ((flags & 2) ? _object0 : _object1)->draw(screenManager, destPos); + ((flags & 2) ? _chevLeftOnDim : _chevLeftOffDim)->draw(screenManager, destPos); destPos.x += 13; - ((flags & 1) ? _object4 : _object5)->draw(screenManager, destPos); + ((flags & 1) ? _chevRightOnDim : _chevRightOffDim)->draw(screenManager, destPos); } } diff --git a/engines/titanic/pet_control/pet_rooms_glyphs.h b/engines/titanic/pet_control/pet_rooms_glyphs.h index 11de6c4a8e..6c51b6f875 100644 --- a/engines/titanic/pet_control/pet_rooms_glyphs.h +++ b/engines/titanic/pet_control/pet_rooms_glyphs.h @@ -25,6 +25,7 @@ #include "titanic/pet_control/pet_glyphs.h" #include "titanic/support/simple_file.h" +#include "titanic/game_location.h" namespace Titanic { @@ -35,16 +36,16 @@ enum RoomGlyphMode { class CPetRoomsGlyph : public CPetGlyph { private: uint _roomFlags; - int _field38; + uint _mailFlag; RoomGlyphMode _mode; - CGameObject *_object0; - CGameObject *_object1; - CGameObject *_object2; - CGameObject *_object3; - CGameObject *_object4; - CGameObject *_object5; - CGameObject *_object6; - CGameObject *_object7; + CGameObject *_chevLeftOnDim; + CGameObject *_chevLeftOffDim; + CGameObject *_chevLeftOnLit; + CGameObject *_chevLeftOffLit; + CGameObject *_chevRightOnDim; + CGameObject *_chevRightOffDim; + CGameObject *_chevRightOnLit; + CGameObject *_chevRightOffLit; private: /** * Find the selected button under the given point, based on the buttons @@ -112,14 +113,20 @@ public: */ uint getRoomFlags() const { return _roomFlags; } - void set38(int val) { _field38 = val; } + /** + * Set mail status flag + */ + void setFlag(uint val) { _mailFlag = val; } /** * Sets the mode of the glyph */ void setMode(RoomGlyphMode mode) { _mode = mode; } - void changeLocation(int newClassNum); + /** + * Change the current class + */ + void changeClass(PassengerClass newClassNum); /** * Returns true if the room is either currently or previously assigned diff --git a/engines/titanic/pet_control/pet_save.cpp b/engines/titanic/pet_control/pet_save.cpp index d344c8cd14..60afa11ab4 100644 --- a/engines/titanic/pet_control/pet_save.cpp +++ b/engines/titanic/pet_control/pet_save.cpp @@ -49,6 +49,16 @@ bool CPetSave::MouseButtonUpMsg(const Point &pt) { } } +bool CPetSave::KeyCharMsg(int key) { + if (CPetLoadSave::KeyCharMsg(key)) + return true; + + if (_savegameSlotNum != -1) + _slotNames[_savegameSlotNum].handleKey(key); + + return true; +} + void CPetSave::highlightCurrent(const Point &pt) { resetSlots(); highlightSave(_savegameSlotNum); @@ -71,11 +81,12 @@ void CPetSave::unhighlightSave(int index) { void CPetSave::execute() { CPetControl *pet = getPetControl(); if (_savegameSlotNum >= 0) { + int slotNumber = _savegameSlotNum; highlightSlot(-1); CProjectItem *project = pet ? pet->getRoot() : nullptr; if (project) { - project->saveGame(_savegameSlotNum, _slotNames[_savegameSlotNum].getText()); + project->saveGame(slotNumber, _slotNames[slotNumber].getText()); pet->displayMessage(BLANK); } } else if (pet) { diff --git a/engines/titanic/pet_control/pet_save.h b/engines/titanic/pet_control/pet_save.h index e5e956403f..39a25eb087 100644 --- a/engines/titanic/pet_control/pet_save.h +++ b/engines/titanic/pet_control/pet_save.h @@ -40,6 +40,11 @@ public: virtual bool MouseButtonUpMsg(const Point &pt); /** + * Handles keypresses when the glyph is focused + */ + virtual bool KeyCharMsg(int key); + + /** * Unhighlight any currently highlighted element */ virtual void unhighlightCurrent() { unhighlightSave(_savegameSlotNum); } diff --git a/engines/titanic/pet_control/pet_section.cpp b/engines/titanic/pet_control/pet_section.cpp index 50d6c7615c..aced697705 100644 --- a/engines/titanic/pet_control/pet_section.cpp +++ b/engines/titanic/pet_control/pet_section.cpp @@ -43,6 +43,7 @@ void CPetSection::displayMessage(const CString &msg) { if (text) { text->setColor(getColor(1)); + text->setText(msg); _petControl->makeDirty(); removeText(5000); } diff --git a/engines/titanic/pet_control/pet_section.h b/engines/titanic/pet_control/pet_section.h index 9e9afe6c21..c68aa90411 100644 --- a/engines/titanic/pet_control/pet_section.h +++ b/engines/titanic/pet_control/pet_section.h @@ -106,6 +106,7 @@ public: virtual bool MouseDragEndMsg(CMouseDragEndMsg *msg) { return false; } virtual bool MouseButtonUpMsg(CMouseButtonUpMsg *msg) { return false; } virtual bool MouseDoubleClickMsg(CMouseDoubleClickMsg *msg) { return false; } + virtual bool MouseWheelMsg(CMouseWheelMsg *msg) { return false; } virtual bool KeyCharMsg(CKeyCharMsg *msg) { return false; } virtual bool VirtualKeyCharMsg(CVirtualKeyCharMsg *msg) { return false; } diff --git a/engines/titanic/pet_control/pet_text.cpp b/engines/titanic/pet_control/pet_text.cpp index 1aa9b804ff..7bb0bad16e 100644 --- a/engines/titanic/pet_control/pet_text.cpp +++ b/engines/titanic/pet_control/pet_text.cpp @@ -21,12 +21,13 @@ */ #include "titanic/pet_control/pet_text.h" +#include "titanic/titanic.h" namespace Titanic { CPetText::CPetText(uint count) : _stringsMerged(false), _maxCharsPerLine(-1), _lineCount(0), - _linesStart(-1), _unused1(0), _unused2(0), _unused3(0), + _displayEndCharIndex(-1), _unused1(0), _unused2(0), _unused3(0), _backR(0xff), _backG(0xff), _backB(0xff), _textR(0), _textG(0), _textB(200), _fontNumber(0), _npcFlag(0), _npcId(0), _hasBorder(true), @@ -57,7 +58,7 @@ void CPetText::setup() { } void CPetText::setLineColor(uint lineNum, uint col) { - setLineColor(lineNum, col & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff); + setLineColor(lineNum, col & 0xff, (col >> 8) & 0xff, (col >> 16) & 0xff); } void CPetText::setLineColor(uint lineNum, byte r, byte g, byte b) { @@ -87,7 +88,7 @@ CString CPetText::getColorText(byte r, byte g, byte b) { void CPetText::load(SimpleFile *file, int param) { if (!param) { uint numLines = file->readNumber(); - uint charsPerLine = file->readNumber(); + int charsPerLine = file->readNumber(); uint count = file->readNumber(); _bounds = file->readRect(); _unused1 = file->readNumber(); @@ -102,8 +103,9 @@ void CPetText::load(SimpleFile *file, int param) { _hasBorder = file->readNumber() != 0; _scrollTop = file->readNumber(); - resize(numLines); setMaxCharsPerLine(charsPerLine); + resize(numLines); + _lineCount = (count == 0) ? 0 : count - 1; assert(_array.size() >= count); for (uint idx = 0; idx < count; ++idx) { @@ -172,7 +174,7 @@ void CPetText::draw(CScreenManager *screenManager) { tempRect.grow(-2); int oldFontNumber = screenManager->setFontNumber(_fontNumber); - screenManager->writeString(SURFACE_BACKBUFFER, tempRect, _scrollTop, _lines, _textCursor); + _displayEndCharIndex = screenManager->writeString(SURFACE_BACKBUFFER, tempRect, _scrollTop, _lines, _textCursor); screenManager->setFontNumber(oldFontNumber); } @@ -211,6 +213,10 @@ void CPetText::setText(const CString &str) { appendText(str); } +void CPetText::setText(StringId stringId) { + setText(g_vm->_strings[stringId]); +} + void CPetText::appendText(const CString &str) { int lineSize = _array[_lineCount]._line.size(); int strSize = str.size(); @@ -295,7 +301,7 @@ int CPetText::getTextWidth(CScreenManager *screenManager) { int CPetText::getTextHeight(CScreenManager *screenManager) { mergeStrings(); int oldFontNumber = screenManager->setFontNumber(_fontNumber); - int textHeight = screenManager->getTextBounds(_lines, _bounds.width()); + int textHeight = screenManager->getTextBounds(_lines, _bounds.width() - 4); screenManager->setFontNumber(oldFontNumber); return textHeight; @@ -449,7 +455,7 @@ void CPetText::hideCursor() { } } -int CPetText::getNPCNum(uint npcId, uint startIndex) { +int CPetText::getNPCNum(uint ident, uint startIndex) { if (!_stringsMerged) { mergeStrings(); if (!_stringsMerged) @@ -460,18 +466,20 @@ int CPetText::getNPCNum(uint npcId, uint startIndex) { if (startIndex < 5 || startIndex >= size) return -1; - // Loop through string - for (const char *strP = _lines.c_str(); size >= 5; ++strP, --size) { + // Loop backwards from the starting index to find an NPC ident sequence + for (const char *strP = _lines.c_str() + startIndex; + strP >= (_lines.c_str() + 5); --strP) { if (*strP == 26) { byte id = *(strP - 2); - if (id == npcId) + if (id == ident) return *(strP - 1); + strP -= 3; } else if (*strP == 27) { - strP += 4; + strP -= 4; } } - return - 1; + return -1; } void CPetText::setFontNumber(int fontNumber) { diff --git a/engines/titanic/pet_control/pet_text.h b/engines/titanic/pet_control/pet_text.h index 0a6bb2d03d..9b2f47274c 100644 --- a/engines/titanic/pet_control/pet_text.h +++ b/engines/titanic/pet_control/pet_text.h @@ -43,7 +43,7 @@ private: Rect _bounds; int _maxCharsPerLine; int _lineCount; - int _linesStart; + int _displayEndCharIndex; int _unused1; int _unused2; int _unused3; @@ -136,6 +136,11 @@ public: void setText(const CString &str); /** + * Set the text + */ + void setText(StringId stringId); + + /** * Set text color */ void setColor(uint col); @@ -176,9 +181,10 @@ public: void setNPC(int npcFlag, int npcId); /** - * Get the index into _lines where on-screen text starts + * Returns the character index into _lines of the last + * character to be displayed on-screen */ - int getLinesStart() const { return _linesStart; } + int displayEndIndex() const { return _displayEndCharIndex; } /** * Scroll the text up @@ -246,8 +252,11 @@ public: /** * Get an NPC Number embedded within on-screen text. * Used by the PET log to encode which NPC spoke + * @param ident Npc Type. Always passed as 1 + * @param startIndex Starting index to scan backwards + * through the log text to find an NPC ident sequence */ - int getNPCNum(uint npcId, uint startIndex); + int getNPCNum(uint ident, uint startIndex); /** * Replaces any occurances of line colors that appear in the diff --git a/engines/titanic/room_flags.cpp b/engines/titanic/room_flags.cpp index 9be8ea3d33..66519aa091 100644 --- a/engines/titanic/room_flags.cpp +++ b/engines/titanic/room_flags.cpp @@ -112,7 +112,7 @@ int CRoomFlags::getRoomArea() const { uint v6 = getElevatorNum(); if (v6 >= 1 && v6 <= 4) { - uint v7 = getPassengerClassNum() - 1; + uint v7 = (int)getPassengerClassNum() - 1; if (v7) { uint v8 = v7 - 1; if (v8) { @@ -148,41 +148,11 @@ CString CRoomFlags::getRoomDesc() const { result += ", "; result += getElevatorDesc(); result += ", "; - result += getRoomDesc(); + result += getRoomNumDesc(); return result; } case 4: - if (isTransportRoom()) { - switch (_data) { - case 0x68797: - return "The Service Elevator"; - case 0x5D3AD: - return "The Super Galactic Leisure Lounge"; - case 0x96E45: - return "The Elevator"; - case 0xAD171: - return "The Dome"; - case 0xC95E9: - return "The Pellerator"; - case 0xDF4D1: - return "The Top of the Well"; - default: - break; - } - } - - if (getRoomCategory() == 0) { - return "Nowhere you're likely to want to go."; - } else { - CString result = getPassengerClassDesc(); - result += ", "; - result += getFloorDesc(); - return result; - } - break; - - case 5: switch (_data) { case 0x1D0D9: return "The Parrot Lobby"; @@ -215,6 +185,36 @@ CString CRoomFlags::getRoomDesc() const { } return "Unknown Room"; + case 5: + if (isTransportRoom()) { + switch (_data) { + case 0x68797: + return "The Service Elevator"; + case 0x5D3AD: + return "The Super Galactic Leisure Lounge"; + case 0x96E45: + return "The Elevator"; + case 0xAD171: + return "The Dome"; + case 0xC95E9: + return "The Pellerator"; + case 0xDF4D1: + return "The Top of the Well"; + default: + break; + } + } + + if (getRoomCategory() == 0) { + return "Nowhere you're likely to want to go."; + } else { + CString result = getPassengerClassDesc(); + result += ", "; + result += getFloorDesc(); + return result; + } + break; + default: break; } @@ -241,14 +241,14 @@ uint CRoomFlags::getPassengerClassBits() const { } CString CRoomFlags::getPassengerClassDesc() const { - int classNum = getPassengerClassNum(); + PassengerClass classNum = getPassengerClassNum(); switch (classNum) { - case 1: + case FIRST_CLASS: return "1st class"; - case 2: + case SECOND_CLASS: return "2nd class"; - case 3: + case THIRD_CLASS: return "SGT class"; default: return "no class"; @@ -269,21 +269,20 @@ uint CRoomFlags::decodeFloorBits(uint bits) const { int offset = bits & 0xF; switch ((bits >> 4) & 0xF) { - case 1: - case 2: - case 3: - base = 40; + case 9: + base = 0; break; - case 4: + case 0xD: base = 10; break; - case 5: + case 0xE: base = 20; break; - case 6: + case 0xF: base = 30; break; default: + base = 40; break; } @@ -369,29 +368,29 @@ CString CRoomFlags::getSuccUBusRoomName() const { return CString(); } -void CRoomFlags::changeLocation(int action) { +void CRoomFlags::changeClass(PassengerClass newClassNum) { uint floorNum = getFloorNum(); uint roomNum = getRoomNum(); uint elevatorNum = getElevatorNum(); - uint classNum = getPassengerClassNum(); + PassengerClass classNum = getPassengerClassNum(); uint v10, v11, v12, v13; switch (classNum) { - case 1: + case FIRST_CLASS: v10 = 2; v11 = 19; v12 = 1; v13 = 3; break; - case 2: + case SECOND_CLASS: v10 = 20; v11 = 27; v12 = 1; v13 = (elevatorNum & 1) ? 3 : 4; break; - case 3: + case THIRD_CLASS: v10 = 28; v11 = 38; v12 = 1; @@ -407,25 +406,28 @@ void CRoomFlags::changeLocation(int action) { } // Perform action to change room or floor - switch (action) { - case 1: + switch (newClassNum) { + case FIRST_CLASS: if (--roomNum < v12) roomNum = v12; break; - case 2: + case SECOND_CLASS: if (++roomNum > v13) roomNum = v13; break; - case 3: + case THIRD_CLASS: if (--floorNum < v10) floorNum = v10; break; - case 4: + case UNCHECKED: if (++floorNum > v11) floorNum = v11; + + default: + break; } // Set new floor and room @@ -442,16 +444,16 @@ bool CRoomFlags::compareClassElevator(uint flags1, uint flags2) { uint elev1 = f1.getElevatorNum(); uint elev2 = f2.getElevatorNum(); - uint class1 = f1.getPassengerClassNum(); - uint class2 = f2.getPassengerClassNum(); + PassengerClass class1 = f1.getPassengerClassNum(); + PassengerClass class2 = f2.getPassengerClassNum(); - if (class1 > 0 && class1 < 3) { + if (class1 == FIRST_CLASS || class1 == SECOND_CLASS) { if (elev1 == 2) elev1 = 1; else if (elev1 == 4) elev1 = 3; } - if (class2 > 0 && class2 < 3) { + if (class2 == FIRST_CLASS || class2 == SECOND_CLASS) { if (elev2 == 2) elev2 = 1; else if (elev2 == 4) @@ -474,12 +476,12 @@ bool CRoomFlags::isTitania(uint flags1, uint flags2) { return flags2 == 0x8A397; } -void CRoomFlags::setRandomLocation(int classNum, bool flag) { +void CRoomFlags::setRandomLocation(PassengerClass classNum, bool flag) { uint minRoom, elevNum, maxRoom, maxFloor, minFloor; do { switch (classNum) { - case 1: + case FIRST_CLASS: minFloor = 2; maxFloor = 19; minRoom = 1; @@ -487,7 +489,7 @@ void CRoomFlags::setRandomLocation(int classNum, bool flag) { elevNum = g_vm->getRandomNumber(flag ? 2 : 3); break; - case 2: + case SECOND_CLASS: minFloor = 20; maxFloor = 27; elevNum = g_vm->getRandomNumber(flag ? 2 : 3); @@ -495,14 +497,12 @@ void CRoomFlags::setRandomLocation(int classNum, bool flag) { maxRoom = ((elevNum - 1) & 1) ? 3 : 4; break; - case 3: + case THIRD_CLASS: minRoom = 1; minFloor = 28; maxFloor = 38; maxRoom = 18; - elevNum = g_vm->getRandomNumber(1); - if (elevNum == 1) - elevNum = 2; + elevNum = g_vm->getRandomNumber(1) ? 2 : 0; break; default: @@ -514,14 +514,15 @@ void CRoomFlags::setRandomLocation(int classNum, bool flag) { setElevatorBits(elevNum); setRoomBits(roomNum); setFloorNum(floorNum); + setPassengerClassBits(classNum); } while (_data == 0x59706); } -int CRoomFlags::whatPassengerClass(int floorNum) { +PassengerClass CRoomFlags::whatPassengerClass(int floorNum) { if (is2To19(floorNum)) - return 1; + return FIRST_CLASS; - return is20To27(floorNum) ? 2 : 3; + return is20To27(floorNum) ? SECOND_CLASS : THIRD_CLASS; } } // End of namespace Titanic diff --git a/engines/titanic/room_flags.h b/engines/titanic/room_flags.h index f0f90f80d1..8e86bf707e 100644 --- a/engines/titanic/room_flags.h +++ b/engines/titanic/room_flags.h @@ -24,6 +24,7 @@ #define TITANIC_ROOM_FLAGS_H #include "titanic/support/string.h" +#include "titanic/game_location.h" namespace Titanic { @@ -142,7 +143,9 @@ public: /** * Gets the passenger class number */ - uint getPassengerClassNum() const { return getPassengerClassBits(); } + PassengerClass getPassengerClassNum() const { + return (PassengerClass)getPassengerClassBits(); + } /** * Get a description for the passenger class @@ -198,12 +201,12 @@ public: /** * Change the passenger class */ - void changeLocation(int action); + void changeClass(PassengerClass newClassNum); /** * Sets a random destination in the flags */ - void setRandomLocation(int classNum, bool flag); + void setRandomLocation(PassengerClass classNum, bool flag); /** * Gets the succubus number associated with a given room @@ -218,7 +221,7 @@ public: /** * Returns what passenger class a particular floor number belongs to */ - static int whatPassengerClass(int floorNum); + static PassengerClass whatPassengerClass(int floorNum); bool not5() const { return getConditionally() != 5; } diff --git a/engines/titanic/sound/qmixer.cpp b/engines/titanic/sound/qmixer.cpp index 733164947e..b3910f846a 100644 --- a/engines/titanic/sound/qmixer.cpp +++ b/engines/titanic/sound/qmixer.cpp @@ -60,7 +60,19 @@ void QMixer::qsWaveMixFreeWave(Audio::SoundHandle &handle) { } void QMixer::qsWaveMixFlushChannel(int iChannel, uint flags) { - // Not currently implemented in ScummVM + if (flags & QMIX_OPENALL) { + // Ignore channel, and flush all the channels + for (uint idx = 0; idx < _channels.size(); ++idx) + qsWaveMixFlushChannel(idx, 0); + } else { + // Flush the specified channel + Common::List<SoundEntry>::iterator i; + Common::List<SoundEntry> &sounds = _channels[iChannel]._sounds; + for (i = sounds.begin(); i != sounds.end(); ++i) + _mixer->stopHandle((*i)._soundHandle); + + sounds.clear(); + } } void QMixer::qsWaveMixSetPanRate(int iChannel, uint flags, uint rate) { @@ -83,11 +95,28 @@ void QMixer::qsWaveMixSetVolume(int iChannel, uint flags, uint volume) { } void QMixer::qsWaveMixSetSourcePosition(int iChannel, uint flags, const QSVECTOR &position) { - // Not currently implemented in ScummVM + ChannelEntry &channel = _channels[iChannel]; + + // Flag whether distance should reset when a new sound is started + channel._resetDistance = (flags & QMIX_USEONCE) != 0; + + // Currently, we only do a basic simulation of spatial positioning by + // getting the distance, and proportionately reducing the volume the + // further away the source is + channel._distance = sqrt(position.x * position.x + position.y * position.y + + position.z * position.z); } void QMixer::qsWaveMixSetPolarPosition(int iChannel, uint flags, const QSPOLAR &position) { - // Not currently implemented in ScummVM + ChannelEntry &channel = _channels[iChannel]; + + // Flag whether distance should reset when a new sound is started + channel._resetDistance = (flags & QMIX_USEONCE) != 0; + + // Currently, we only do a basic simulation of spatial positioning by + // getting the distance, and proportionately reducing the volume the + // further away the source is + channel._distance = position.range; } void QMixer::qsWaveMixSetListenerPosition(const QSVECTOR &position, uint flags) { @@ -163,7 +192,8 @@ void QMixer::qsWaveMixPump() { if (channel._volume != oldVolume && !channel._sounds.empty() && channel._sounds.front()._started) { - _mixer->setChannelVolume(channel._sounds.front()._soundHandle, channel._volume); + _mixer->setChannelVolume(channel._sounds.front()._soundHandle, + channel.getRawVolume()); } } @@ -177,7 +207,7 @@ void QMixer::qsWaveMixPump() { sound._waveFile->_stream->rewind(); _mixer->playStream(sound._waveFile->_soundType, &sound._soundHandle, sound._waveFile->_stream, - -1, channel._volume, 0, DisposeAfterUse::NO); + -1, channel.getRawVolume(), 0, DisposeAfterUse::NO); } else { // Sound is finished if (sound._callback) @@ -195,13 +225,30 @@ void QMixer::qsWaveMixPump() { if (!channel._sounds.empty()) { SoundEntry &sound = channel._sounds.front(); if (!sound._started) { + if (channel._resetDistance) + channel._distance = 0.0; + + // Calculate an effective volume based on distance of source _mixer->playStream(sound._waveFile->_soundType, &sound._soundHandle, sound._waveFile->_stream, - -1, channel._volume, 0, DisposeAfterUse::NO); + -1, channel.getRawVolume(), 0, DisposeAfterUse::NO); sound._started = true; } } } } +/*------------------------------------------------------------------------*/ + +byte QMixer::ChannelEntry::getRawVolume() const { + // Emperically decided adjustment divisor for distances + const double ADJUSTMENT_FACTOR = 5.0; + + double r = 1.0 + (_distance / ADJUSTMENT_FACTOR); + double percent = 1.0 / (r * r); + + double newVolume = _volume * percent; + return (byte)newVolume; +} + } // End of namespace Titanic diff --git a/engines/titanic/sound/qmixer.h b/engines/titanic/sound/qmixer.h index 9a0ea85ede..b8c7f6dae2 100644 --- a/engines/titanic/sound/qmixer.h +++ b/engines/titanic/sound/qmixer.h @@ -197,9 +197,19 @@ class QMixer { uint _volumeChangeEnd; byte _volumeStart; byte _volumeEnd; + // Distance of source + double _distance; + bool _resetDistance; ChannelEntry() : _volume(0), _panRate(0), _volumeChangeStart(0), - _volumeChangeEnd(0), _volumeStart(0), _volumeEnd(0) {} + _volumeChangeEnd(0), _volumeStart(0), _volumeEnd(0), + _distance(0.0), _resetDistance(true) {} + + /** + * Calculates the raw volume level to pass to ScummVM playStream, taking + * into the sound's volume level and distance from origin + */ + byte getRawVolume() const; }; private: Audio::Mixer *_mixer; @@ -290,7 +300,7 @@ public: void qsWaveMixSetDistanceMapping(int iChannel, uint flags, const QMIX_DISTANCES &distances); /** - * + * Sets the frequency/rate of sound playback */ void qsWaveMixSetFrequency(int iChannel, uint flags, uint frequency); diff --git a/engines/titanic/sound/sound.cpp b/engines/titanic/sound/sound.cpp index e48c8760c5..c28823148e 100644 --- a/engines/titanic/sound/sound.cpp +++ b/engines/titanic/sound/sound.cpp @@ -57,7 +57,7 @@ void CSound::preEnterView(CViewItem *newView, bool isNewRoom) { _soundManager.setListenerPosition(xp, yp, zp, cosVal, sinVal, 0, isNewRoom); } -bool CSound::isActive(int handle) const { +bool CSound::isActive(int handle) { if (handle != 0 && handle != -1) return _soundManager.isActive(handle); @@ -129,6 +129,7 @@ CWaveFile *CSound::loadSound(const CString &name) { // Found it, so move it to the front of the list and return _sounds.remove(soundItem); _sounds.push_front(soundItem); + soundItem->_waveFile->reset(); return soundItem->_waveFile; } } diff --git a/engines/titanic/sound/sound.h b/engines/titanic/sound/sound.h index 21f2a93f24..c767a3d75f 100644 --- a/engines/titanic/sound/sound.h +++ b/engines/titanic/sound/sound.h @@ -115,7 +115,7 @@ public: /** * Returns true if a sound with the specified handle is active */ - bool isActive(int handle) const; + bool isActive(int handle); /** * Sets the volume for a sound diff --git a/engines/titanic/sound/sound_manager.cpp b/engines/titanic/sound/sound_manager.cpp index c1a46e5103..5f8e53caf3 100644 --- a/engines/titanic/sound/sound_manager.cpp +++ b/engines/titanic/sound/sound_manager.cpp @@ -311,7 +311,9 @@ void QSoundManager::setPolarPosition(int handle, double range, double azimuth, d } } -bool QSoundManager::isActive(int handle) const { +bool QSoundManager::isActive(int handle) { + resetChannel(10); + for (uint idx = 0; idx < _slots.size(); ++idx) { if (_slots[idx]._handle == handle) return true; @@ -320,7 +322,7 @@ bool QSoundManager::isActive(int handle) const { return false; } -bool QSoundManager::isActive(const CWaveFile *waveFile) const { +bool QSoundManager::isActive(const CWaveFile *waveFile) { return _sounds.contains(waveFile); } diff --git a/engines/titanic/sound/sound_manager.h b/engines/titanic/sound/sound_manager.h index a3074a8e57..ff556346de 100644 --- a/engines/titanic/sound/sound_manager.h +++ b/engines/titanic/sound/sound_manager.h @@ -123,12 +123,12 @@ public: /** * Returns true if the given sound is currently active */ - virtual bool isActive(int handle) const = 0; + virtual bool isActive(int handle) = 0; /** * Returns true if the given sound is currently active */ - virtual bool isActive(const CWaveFile *waveFile) const { return false; } + virtual bool isActive(const CWaveFile *waveFile) { return false; } /** * Handles regularly updating the mixer @@ -395,12 +395,12 @@ public: /** * Returns true if the given sound is currently active */ - virtual bool isActive(int handle) const; + virtual bool isActive(int handle); /** * Returns true if the given sound is currently active */ - virtual bool isActive(const CWaveFile *waveFile) const; + virtual bool isActive(const CWaveFile *waveFile); /** * Handles regularly updating the mixer diff --git a/engines/titanic/sound/wave_file.cpp b/engines/titanic/sound/wave_file.cpp index ade94aad50..f2366d1dd5 100644 --- a/engines/titanic/sound/wave_file.cpp +++ b/engines/titanic/sound/wave_file.cpp @@ -51,9 +51,9 @@ uint CWaveFile::getDurationTicks() const { // a desired size. Since I have no idea how the system API // method works, for now I'm using a simple ratio of a // sample output to input value - uint size = _size - 0x46; - double newSize = (double)size * (1475712.0 / 199836.0); - return newSize * 1000.0 / _stream->getRate(); + uint dataSize = _size - 0x46; + double newSize = (double)dataSize * (1475712.0 / 199836.0); + return (uint)(newSize * 1000.0 / _stream->getRate()); } bool CWaveFile::loadSound(const CString &name) { @@ -106,4 +106,8 @@ uint CWaveFile::getFrequency() const { return _stream->getRate(); } +void CWaveFile::reset() { + _stream->rewind(); +} + } // End of namespace Titanic z diff --git a/engines/titanic/sound/wave_file.h b/engines/titanic/sound/wave_file.h index 19d367936f..e4bb71a0b6 100644 --- a/engines/titanic/sound/wave_file.h +++ b/engines/titanic/sound/wave_file.h @@ -81,6 +81,11 @@ public: * Return the frequency of the loaded wave file */ uint getFrequency() const; + + /** + * Resets the music stream + */ + void reset(); }; } // End of namespace Titanic diff --git a/engines/titanic/star_control/star_control_sub13.cpp b/engines/titanic/star_control/star_control_sub13.cpp index 490e6d0630..5da6118a74 100644 --- a/engines/titanic/star_control/star_control_sub13.cpp +++ b/engines/titanic/star_control/star_control_sub13.cpp @@ -225,7 +225,7 @@ void CStarControlSub13::fn19(double *v1, double *v2, double *v3, double *v4) { } void CStarControlSub13::reset() { - const double FACTOR = 3.1415927 * 0.0055555557; + //const double FACTOR = 3.1415927 * 0.0055555557; error("TODO: CStarControlSub13::reset"); } diff --git a/engines/titanic/support/avi_surface.cpp b/engines/titanic/support/avi_surface.cpp index 07458812b9..525c6513dd 100644 --- a/engines/titanic/support/avi_surface.cpp +++ b/engines/titanic/support/avi_surface.cpp @@ -41,6 +41,7 @@ AVISurface::AVISurface(const CResourceKey &key) { _streamCount = 0; _movieFrameSurface[0] = _movieFrameSurface[1] = nullptr; _framePixels = nullptr; + _priorFrameTime = 0; // Reset current frame. We need to keep track of frames separately from the decoder, // since it needs to be able to go beyond the frame count or to negative to allow @@ -164,8 +165,8 @@ bool AVISurface::handleEvents(CMovieEventList &events) { _currentFrame += _isReversed ? -1 : 1; int newFrame = _currentFrame; - if ((info->_isReversed && newFrame <= info->_endFrame) || - (!info->_isReversed && newFrame >= info->_endFrame)) { + if ((info->_isReversed && newFrame < info->_endFrame) || + (!info->_isReversed && newFrame > info->_endFrame)) { if (info->_isRepeat) { newFrame = info->_startFrame; } else { @@ -180,6 +181,7 @@ bool AVISurface::handleEvents(CMovieEventList &events) { // Not empty, so move onto new first one info = _movieRangeInfo.front(); newFrame = info->_startFrame; + setReversed(info->_isReversed); } } } @@ -286,8 +288,20 @@ void AVISurface::setFrame(int frameNumber) { renderFrame(); } -bool AVISurface::isNextFrame() const { - return _decoder->getTimeToNextFrame() == 0; +bool AVISurface::isNextFrame() { + if (!_decoder->endOfVideo()) + return _decoder->getTimeToNextFrame() == 0; + + // We're at the end of the video, so we need to manually + // keep track of frame delays. Hardcoded at the moment for 15FPS + const uint FRAME_TIME = 1000 / 15; + uint32 currTime = g_system->getMillis(); + if (currTime >= (_priorFrameTime + FRAME_TIME)) { + _priorFrameTime = currTime; + return true; + } + + return false; } bool AVISurface::renderFrame() { @@ -347,7 +361,12 @@ bool AVISurface::addEvent(int frameNumber, CGameObject *obj) { } void AVISurface::setFrameRate(double rate) { - _decoder->setRate(Common::Rational((int)rate)); + // Convert rate from fps to relative to 1.0 (normal speed) + const int PRECISION = 10000; + double playRate = rate / 15.0; // Standard 15 FPS + Common::Rational pRate(playRate * PRECISION, PRECISION); + + _decoder->setRate(pRate); } Graphics::ManagedSurface *AVISurface::getSecondarySurface() { @@ -370,10 +389,12 @@ void AVISurface::playCutscene(const Rect &r, uint startFrame, uint endFrame) { _movieFrameSurface[0]->h != r.height(); startAtFrame(startFrame); + _currentFrame = startFrame; + while (_currentFrame < (int)endFrame && !g_vm->shouldQuit()) { if (isNextFrame()) { renderFrame(); - _currentFrame = _decoder->getCurFrame(); + ++_currentFrame; if (isDifferent) { // Clear the destination area, and use the transBlitFrom method, diff --git a/engines/titanic/support/avi_surface.h b/engines/titanic/support/avi_surface.h index b4e6d420cb..2a4b321f0f 100644 --- a/engines/titanic/support/avi_surface.h +++ b/engines/titanic/support/avi_surface.h @@ -66,6 +66,7 @@ private: Graphics::ManagedSurface *_framePixels; bool _isReversed; int _currentFrame; + uint32 _priorFrameTime; private: /** * Render a frame to the video surface @@ -132,7 +133,9 @@ public: /** * Return true if a video is currently playing */ - virtual bool isPlaying() const { return _decoder->isPlaying(); } + virtual bool isPlaying() const { + return _decoder->isPlaying(); + } /** * Handle any movie events relevent for the frame @@ -194,7 +197,7 @@ public: /** * Returns true if it's time for the next */ - bool isNextFrame() const; + bool isNextFrame(); /** * Plays an interruptable cutscene diff --git a/engines/titanic/support/font.cpp b/engines/titanic/support/font.cpp index 625d03720b..f81251b10b 100644 --- a/engines/titanic/support/font.cpp +++ b/engines/titanic/support/font.cpp @@ -90,7 +90,7 @@ int STFont::getTextBounds(const CString &str, int maxWidth, Point *sizeOut) cons strP += 4; } else { if (*strP == ' ') { - // Check fo rline wrapping + // Check for line wrapping checkLineWrap(textSize, maxWidth, strP); } @@ -152,7 +152,7 @@ int STFont::writeString(CVideoSurface *surface, const Rect &rect1, const Rect &d setColor(r, g, b); } else { if (*srcP == ' ') { - // Check fo rline wrapping + // Check for line wrapping checkLineWrap(textSize, rect1.width(), srcP); if (!*srcP) return endP - str.c_str(); @@ -245,11 +245,8 @@ WriteCharacterResult STFont::writeChar(CVideoSurface *surface, unsigned char c, if (c == 233) c = '$'; - Rect tempRect; - tempRect.left = _chars[c]._offset; - tempRect.right = _chars[c]._offset + _chars[c]._width; - tempRect.top = 0; - tempRect.bottom = _fontHeight; + Rect charRect(_chars[c]._offset, 0, + _chars[c]._offset + _chars[c]._width, _fontHeight); Point destPos(pt.x + destRect.left, pt.y + destRect.top); if (srcRect->isEmpty()) @@ -257,34 +254,34 @@ WriteCharacterResult STFont::writeChar(CVideoSurface *surface, unsigned char c, if (destPos.y > srcRect->bottom) return WC_OUTSIDE_BOTTOM; - if ((destPos.y + tempRect.height()) > srcRect->bottom) { - tempRect.bottom += tempRect.top - destPos.y; + if ((destPos.y + charRect.height()) > srcRect->bottom) { + charRect.bottom += srcRect->bottom - (destPos.y + charRect.height()); } if (destPos.y < srcRect->top) { - if ((tempRect.height() + destPos.y) < srcRect->top) + if ((charRect.height() + destPos.y) < srcRect->top) return WC_OUTSIDE_TOP; - tempRect.top += srcRect->top - destPos.y; + charRect.top += srcRect->top - destPos.y; destPos.y = srcRect->top; } if (destPos.x < srcRect->left) { - if ((tempRect.width() + destPos.x) < srcRect->left) + if ((charRect.width() + destPos.x) < srcRect->left) return WC_OUTSIDE_LEFT; - tempRect.left += srcRect->left - destPos.x; + charRect.left += srcRect->left - destPos.x; destPos.x = srcRect->left; } else { - if ((tempRect.width() + destPos.x) > srcRect->right) { + if ((charRect.width() + destPos.x) > srcRect->right) { if (destPos.x > srcRect->right) return WC_OUTSIDE_RIGHT; - tempRect.right += srcRect->left - destPos.x; + charRect.right += srcRect->left - destPos.x; } } - copyRect(surface, destPos, tempRect); + copyRect(surface, destPos, charRect); return WC_IN_BOUNDS; } @@ -318,10 +315,10 @@ void STFont::extendBounds(Point &textSize, byte c, int maxWidth) const { void STFont::checkLineWrap(Point &textSize, int maxWidth, const char *&str) const { bool flag = false; int totalWidth = 0; - for (const char *srcPtr = str; *srcPtr && *srcPtr != ' '; ++srcPtr) { - if (*srcPtr == ' ' && flag) - break; + // Loop forward getting the width of the word (including preceding space) + // until a space is encountered following at least one character + for (const char *srcPtr = str; *srcPtr && (*srcPtr != ' ' || !flag); ++srcPtr) { if (*srcPtr == TEXTCMD_NPC) { srcPtr += 3; } else if (*srcPtr == TEXTCMD_SET_COLOR) { diff --git a/engines/titanic/support/mouse_cursor.cpp b/engines/titanic/support/mouse_cursor.cpp index 4dd1ab4366..6300f65a3b 100644 --- a/engines/titanic/support/mouse_cursor.cpp +++ b/engines/titanic/support/mouse_cursor.cpp @@ -54,10 +54,11 @@ CMouseCursor::CursorEntry::~CursorEntry() { } CMouseCursor::CMouseCursor(CScreenManager *screenManager) : - _screenManager(screenManager), _cursorId(CURSOR_HOURGLASS), - _setCursorCount(0), _fieldE4(0), _fieldE8(0) { + _screenManager(screenManager), _cursorId(CURSOR_HOURGLASS), _hideCounter(0), + _hiddenCount(0), _cursorSuppressed(false), _setCursorCount(0), _inputEnabled(true), _fieldE8(0) { loadCursorImages(); setCursor(CURSOR_ARROW); + CursorMan.showMouse(true); } CMouseCursor::~CMouseCursor() { @@ -87,11 +88,38 @@ void CMouseCursor::loadCursorImages() { } void CMouseCursor::show() { - CursorMan.showMouse(true); + assert(_hiddenCount > 0); + + if (--_hiddenCount == 0) + CursorMan.showMouse(!_cursorSuppressed); } void CMouseCursor::hide() { CursorMan.showMouse(false); + ++_hiddenCount; +} + +void CMouseCursor::incHideCounter() { + if (_hideCounter++ == 0) + hide(); +} + +void CMouseCursor::decHideCounter() { + --_hideCounter; + assert(_hideCounter >= 0); + if (_hideCounter == 0) + show(); +} + +void CMouseCursor::suppressCursor() { + _cursorSuppressed = true; + hide(); +} + +void CMouseCursor::unsuppressCursor() { + _cursorSuppressed = false; + if (_hideCounter == 0) + show(); } void CMouseCursor::setCursor(CursorId cursorId) { @@ -108,7 +136,7 @@ void CMouseCursor::setCursor(CursorId cursorId) { Graphics::ManagedSurface surface(CURSOR_SIZE, CURSOR_SIZE, g_system->getScreenFormat()); const uint16 *srcP = srcSurface.getPixels(); - CTransparencySurface transSurface(&ce._transSurface->rawSurface(), TRANS_DEFAULT); + CTransparencySurface transSurface(&ce._transSurface->rawSurface(), TRANS_ALPHA0); uint16 *destP = (uint16 *)surface.getPixels(); for (int y = 0; y < CURSOR_SIZE; ++y) { @@ -131,25 +159,45 @@ void CMouseCursor::setCursor(CursorId cursorId) { } void CMouseCursor::update() { - // No implementation needed + if (!_inputEnabled && _moveStartTime) { + uint32 time = CLIP(g_system->getMillis(), _moveStartTime, _moveEndTime); + Common::Point pt( + _moveStartPos.x + (_moveDestPos.x - _moveStartPos.x) * + (int)(time - _moveStartTime) / (int)(_moveEndTime - _moveStartTime), + _moveStartPos.y + (_moveDestPos.y - _moveStartPos.y) * + (int)(time - _moveStartTime) / (int)(_moveEndTime - _moveStartTime) + ); + + if (pt != g_vm->_events->getMousePos()) { + g_vm->_events->setMousePos(pt); + + CInputHandler &inputHandler = *CScreenManager::_screenManagerPtr->_inputHandler; + CMouseMoveMsg msg(pt, 0); + inputHandler.handleMessage(msg, false); + } + + if (time == _moveEndTime) + _moveStartTime = _moveEndTime = 0; + } } -void CMouseCursor::lockE4() { - _fieldE4 = 0; +void CMouseCursor::disableControl() { + _inputEnabled = false; CScreenManager::_screenManagerPtr->_inputHandler->incLockCount(); } -void CMouseCursor::unlockE4() { - _fieldE4 = 1; +void CMouseCursor::enableControl() { + _inputEnabled = true; _fieldE8 = 0; CScreenManager::_screenManagerPtr->_inputHandler->decLockCount(); } -void CMouseCursor::setPosition(const Point &pt, double rate) { - assert(rate >= 0.0 && rate <= 1.0); - - // TODO: Figure out use of the rate parameter - g_system->warpMouse(pt.x, pt.y); +void CMouseCursor::setPosition(const Point &pt, double duration) { + _moveStartPos = g_vm->_events->getMousePos(); + _moveDestPos = pt; + _moveStartTime = g_system->getMillis(); + _moveEndTime = _moveStartTime + duration; + update(); } } // End of namespace Titanic diff --git a/engines/titanic/support/mouse_cursor.h b/engines/titanic/support/mouse_cursor.h index 08de28e29d..1662ce743d 100644 --- a/engines/titanic/support/mouse_cursor.h +++ b/engines/titanic/support/mouse_cursor.h @@ -66,14 +66,22 @@ private: CursorId _cursorId; CursorEntry _cursors[NUM_CURSORS]; uint _setCursorCount; - int _fieldE4; + int _hideCounter; + int _hiddenCount; + bool _cursorSuppressed; int _fieldE8; + uint32 _priorMoveTime; + Common::Point _moveStartPos; + Common::Point _moveDestPos; + uint _moveStartTime, _moveEndTime; /** * Load the images for each cursor */ void loadCursorImages(); public: + bool _inputEnabled; +public: CMouseCursor(CScreenManager *screenManager); ~CMouseCursor(); @@ -88,6 +96,29 @@ public: void hide(); /** + * Decrements the hide counter, and shows the mouse if + * it's reached zero + */ + void incHideCounter(); + + /** + * Increments the hide counter, hiding the mouse if it's the first call + */ + void decHideCounter(); + + /** + * Suppresses the cursor. When suppressed, the cursor isn't drawn, + * even if it's not otherwise being hidden + */ + void suppressCursor(); + + /** + * Unflags the cursor as being suppressed, allowing it to be drawn + * again if it's enabled + */ + void unsuppressCursor(); + + /** * Set the cursor */ void setCursor(CursorId cursorId); @@ -102,13 +133,20 @@ public: */ uint getChangeCount() const { return _setCursorCount; } - void lockE4(); - void unlockE4(); + /** + * Disables user control of the mouse + */ + void disableControl(); + + /** + * Re-enables user control of the mouse + */ + void enableControl(); /** - * Sets the mouse to a new position + * Move the mouse to a new position */ - void setPosition(const Point &pt, double rate); + void setPosition(const Point &pt, double duration); }; diff --git a/engines/titanic/support/movie.cpp b/engines/titanic/support/movie.cpp index 2115906992..949ed62fca 100644 --- a/engines/titanic/support/movie.cpp +++ b/engines/titanic/support/movie.cpp @@ -126,19 +126,12 @@ void OSMovie::playCutscene(const Rect &drawRect, uint startFrame, uint endFrame) if (!_movieSurface) _movieSurface = CScreenManager::_screenManagerPtr->createSurface(600, 340); - bool widthLess = _videoSurface->getWidth() < 600; - bool heightLess = _videoSurface->getHeight() < 340; - Rect r(drawRect.left, drawRect.top, - drawRect.left + (widthLess ? CLIP_WIDTH_REDUCED : CLIP_WIDTH), - drawRect.top + (heightLess ? CLIP_HEIGHT_REDUCED : CLIP_HEIGHT) - ); - // Set a new event target whilst the clip plays, so standard scene drawing isn't called CEventTarget eventTarget; g_vm->_events->addTarget(&eventTarget); _aviSurface.setFrame(startFrame); - _aviSurface.playCutscene(r, startFrame, endFrame); + _aviSurface.playCutscene(drawRect, startFrame, endFrame); g_vm->_events->removeTarget(); } diff --git a/engines/titanic/support/screen_manager.cpp b/engines/titanic/support/screen_manager.cpp index 2e9bbcb6de..2a675394b5 100644 --- a/engines/titanic/support/screen_manager.cpp +++ b/engines/titanic/support/screen_manager.cpp @@ -308,11 +308,11 @@ CVideoSurface *OSScreenManager::createSurface(const CResourceKey &key) { } void OSScreenManager::showCursor() { - CScreenManager::_screenManagerPtr->_mouseCursor->show(); + CScreenManager::_screenManagerPtr->_mouseCursor->unsuppressCursor(); } void OSScreenManager::hideCursor() { - CScreenManager::_screenManagerPtr->_mouseCursor->hide(); + CScreenManager::_screenManagerPtr->_mouseCursor->suppressCursor(); } void OSScreenManager::destroyFrontAndBackBuffers() { @@ -330,7 +330,6 @@ void OSScreenManager::loadCursors() { delete _mouseCursor; } _mouseCursor = new CMouseCursor(this); - showCursor(); if (!_textCursor) { _textCursor = new CTextCursor(this); diff --git a/engines/titanic/support/simple_file.cpp b/engines/titanic/support/simple_file.cpp index 7e3cea35be..65d2c85273 100644 --- a/engines/titanic/support/simple_file.cpp +++ b/engines/titanic/support/simple_file.cpp @@ -467,7 +467,7 @@ void SimpleFile::skipSpaces() { /*------------------------------------------------------------------------*/ bool StdCWadFile::open(const Common::String &filename) { - File f; + Common::File f; CString name = filename; // Check for whether it is indeed a file/resource pair @@ -476,9 +476,11 @@ bool StdCWadFile::open(const Common::String &filename) { if (idx < 0) { // Nope, so open up file for standard reading assert(!name.empty()); - f.open(name); + if (!f.open(name)) + return false; SimpleFile::open(f.readStream(f.size())); + f.close(); return true; } @@ -489,7 +491,8 @@ bool StdCWadFile::open(const Common::String &filename) { int resIndex = resStr.readInt(); // Open up the index for access - f.open(fname); + if (!f.open(fname)) + return false; int indexSize = f.readUint32LE() / 4; assert(resIndex < indexSize); diff --git a/engines/titanic/support/strings.cpp b/engines/titanic/support/strings.cpp index 7664b6849b..a8ab45800f 100644 --- a/engines/titanic/support/strings.cpp +++ b/engines/titanic/support/strings.cpp @@ -25,7 +25,7 @@ namespace Titanic { -Strings::Strings() { +void Strings::load() { Common::SeekableReadStream *r = g_vm->_filesManager->getResource("TEXT/STRINGS"); while (r->pos() < r->size()) push_back(readStringFromStream(r)); diff --git a/engines/titanic/support/strings.h b/engines/titanic/support/strings.h index 5164897522..30aad89697 100644 --- a/engines/titanic/support/strings.h +++ b/engines/titanic/support/strings.h @@ -87,6 +87,57 @@ enum StringId { BOWL_OF_NUTS, NOT_A_BOWL_OF_NUTS, + CANT_SUMMON_DOORBOT, + CANT_SUMMON_BELLBOT, + NO_ONE_TO_TALK_TO, + TALKING_TO, + DOORBOT_NAME, + DESKBOT_NAME, + LIFTBOT_NAME, + PARROT_NAME, + BARBOT_NAME, + CHATTERBOT_NAME, + BELLBOT_NAME, + MAITRED_NAME, + SUCCUBUS_NAME, + UNKNOWN_NAME, + ARM_ALREADY_HOLDING, + YOU_CANT_GET_THIS, + DOESNT_DO_ANYTHING, + DOESNT_WANT_THIS, + DOES_NOT_REACH, + CHICKEN_ALREADY_CLEAN, + HOSE_INCOMPATIBLE, + INCORRECTLY_CALIBRATED, + GONDOLIERS_FIRST_CLASS_ONLY, + NOTHING_ON_CHANNEL, + TELEVISION_CONTROL, + OPERATE_ENTERTAINMENT, + OPERATE_LIGHTS, + DEPLOY_FLORAL_ENHANCEMENT, + DEPLOY_FULLY_RELAXATION, + DEPLOY_COMFORT_WORKSTATION, + DEPLOY_MINOR_STORAGE, + DEPLOY_MAJOR_RELAXATION, + INFLATE_RELAXATION_DEVICE, + DEPLOY_MAINTENANCE_HUB, + DEPLOY_EXECUTIVE_SURFACE, + DEPLOY_MINOR_RELAXATION, + DEPLOY_SINK, + DEPLOY_MAJOR_STORAGE, + SUCCUBUS_DELIVERY_SYSTEM, + NAVIGATION_CONTROLLER, + SUMMON_ELEVATOR, + SUMMON_PELLERATOR, + GO_TO_BOTTOM_OF_WELL, + GO_TO_TOP_OF_WELL, + GO_TO_STATEROOM, + GO_TO_BAR, + GO_TO_PROMENADE_DECK, + GO_TO_ARBORETUM, + GO_TO_MUSIC_ROOM, + GO_TO_1ST_CLASS_RESTAURANT, + // German version only DE_SUMMER, DE_AUTUMN, @@ -139,7 +190,7 @@ enum StringId { class Strings : public Common::StringArray { public: - Strings(); + void load(); }; } // End of namespace Titanic diff --git a/engines/titanic/support/time_event_info.cpp b/engines/titanic/support/time_event_info.cpp index e088a8e0c2..c61ceb29ae 100644 --- a/engines/titanic/support/time_event_info.cpp +++ b/engines/titanic/support/time_event_info.cpp @@ -100,8 +100,8 @@ CTimeEventInfo::CTimeEventInfo() : ListItem(), _lockCounter(0), CTimeEventInfo::CTimeEventInfo(uint ticks, bool repeated, uint firstDuration, uint repeatDuration, CTreeItem *target, int endVal, const CString &action) : ListItem(), _lockCounter(0), _repeated(repeated), _firstDuration(firstDuration), - _repeatDuration(repeatDuration), _target(target), _actionVal(endVal), _done(false), - _timerCtr(0), _lastTimerTicks(ticks), _relativeTicks(0), _persisent(true) { + _repeatDuration(repeatDuration), _target(target), _actionVal(endVal), _action(action), + _done(false), _timerCtr(0), _lastTimerTicks(ticks), _relativeTicks(0), _persisent(true) { _id = _nextId++; } diff --git a/engines/titanic/support/transparency_surface.cpp b/engines/titanic/support/transparency_surface.cpp index 5ffa8b99b1..8b5cbecc5f 100644 --- a/engines/titanic/support/transparency_surface.cpp +++ b/engines/titanic/support/transparency_surface.cpp @@ -46,7 +46,7 @@ CTransparencySurface::CTransparencySurface(const Graphics::Surface *surface, _flag1 = false; break; case TRANS_DEFAULT: - if (*(byte *)surface->getPixels() < 0x80) { + if (*(const byte *)surface->getPixels() < 0x80) { _flag1 = true; _flag2 = false; } @@ -66,8 +66,17 @@ int CTransparencySurface::moveX() { } uint CTransparencySurface::getPixel() const { - const byte *pixelP = (const byte *)_surface->getBasePtr(_pos.x, _pos.y); - return *pixelP; + byte pixel = *(const byte *)_surface->getBasePtr(_pos.x, _pos.y); + return pixel; +} + +uint CTransparencySurface::getAlpha() const { + byte pixel = getPixel(); + return _flag1 ? 0xFF - pixel : pixel; +} + +bool CTransparencySurface::isPixelTransparent() { + return getAlpha() == 0xff; } } // End of namespace Titanic diff --git a/engines/titanic/support/transparency_surface.h b/engines/titanic/support/transparency_surface.h index 0391b6d5b7..1b4587a9db 100644 --- a/engines/titanic/support/transparency_surface.h +++ b/engines/titanic/support/transparency_surface.h @@ -42,6 +42,11 @@ private: bool _flag; bool _flag1; bool _flag2; +private: + /** + * Returns a a pixel from the transparency surface + */ + uint getPixel() const; public: /** * Constructor @@ -64,19 +69,14 @@ public: int moveX(); /** - * Returns a byte from the transparency surface - */ - uint getPixel() const; - - /** * Returns the alpha value for the pixel (0-31) */ - uint getAlpha() const { return 31 - (getPixel() >> 3); } + uint getAlpha() const; /** * Returns true if the pixel is completely transparent */ - bool isPixelTransparent() const { return getAlpha() == 31; } + bool isPixelTransparent(); }; } // End of namespace Titanic diff --git a/engines/titanic/support/video_surface.cpp b/engines/titanic/support/video_surface.cpp index 293232860c..52610379b5 100644 --- a/engines/titanic/support/video_surface.cpp +++ b/engines/titanic/support/video_surface.cpp @@ -246,9 +246,7 @@ void CVideoSurface::transBlitRect(const Rect &srcRect, const Rect &destRect, CVi transSurface.setCol(srcRect.left); for (int srcX = srcRect.left; srcX < srcRect.right; ++srcX) { - if (!transSurface.isPixelTransparent()) { - copyPixel(lineDestP, lineSrcP, transSurface.getAlpha(), srcSurface->format, isAlpha); - } + copyPixel(lineDestP, lineSrcP, transSurface.getAlpha() >> 3, srcSurface->format, isAlpha); ++lineSrcP; ++lineDestP; diff --git a/engines/titanic/titanic.cpp b/engines/titanic/titanic.cpp index 2566a36d7c..29730c36c8 100644 --- a/engines/titanic/titanic.cpp +++ b/engines/titanic/titanic.cpp @@ -60,6 +60,12 @@ TitanicEngine::TitanicEngine(OSystem *syst, const TitanicGameDescription *gameDe _scriptHandler = nullptr; _script = nullptr; CMusicRoom::_musicHandler = nullptr; + + // Set up debug channels + DebugMan.addDebugChannel(kDebugCore, "core", "Core engine debug level"); + DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts"); + DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling"); + DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling"); } TitanicEngine::~TitanicEngine() { @@ -78,12 +84,6 @@ void TitanicEngine::initializePath(const Common::FSNode &gamePath) { } void TitanicEngine::initialize() { - // Set up debug channels - DebugMan.addDebugChannel(kDebugCore, "core", "Core engine debug level"); - DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts"); - DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling"); - DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling"); - _debugger = new Debugger(this); _filesManager = new CFilesManager(this); @@ -107,6 +107,7 @@ void TitanicEngine::initialize() { _screen = new Graphics::Screen(0, 0); _screenManager = new OSScreenManager(this); _window = new CMainGameWindow(this); + _strings.load(); setItemNames(); setRoomNames(); @@ -149,7 +150,7 @@ void TitanicEngine::setItemNames() { r = g_vm->_filesManager->getResource("TEXT/ITEM_DESCRIPTIONS"); while (r->pos() < r->size()) - _itemNames.push_back(readStringFromStream(r)); + _itemDescriptions.push_back(readStringFromStream(r)); delete r; r = g_vm->_filesManager->getResource("TEXT/ITEM_IDS"); diff --git a/engines/titanic/titanic.h b/engines/titanic/titanic.h index b0776c55a5..d8e0f880f7 100644 --- a/engines/titanic/titanic.h +++ b/engines/titanic/titanic.h @@ -122,6 +122,7 @@ public: CString _itemObjects[TOTAL_ITEMS]; StringArray _itemIds; StringArray _roomNames; + Strings _strings; public: TitanicEngine(OSystem *syst, const TitanicGameDescription *gameDesc); virtual ~TitanicEngine(); diff --git a/engines/titanic/true_talk/barbot_script.cpp b/engines/titanic/true_talk/barbot_script.cpp index 76f28540ea..ad10f5a57f 100644 --- a/engines/titanic/true_talk/barbot_script.cpp +++ b/engines/titanic/true_talk/barbot_script.cpp @@ -167,7 +167,7 @@ int BarbotScript::process(const TTroomScript *roomScript, const TTsentence *sent dialogueId = ARRAY2[0]; _arrIndex = 1; } else if (getRandomNumber(100) > 60) { - switch (sentence->_field2C) { + switch (sentence->_category) { case 2: dialogueId = 51914; break; @@ -209,7 +209,7 @@ int BarbotScript::process(const TTroomScript *roomScript, const TTsentence *sent int val34 = getState(); setState(0); - int val2C = sentence->_field2C; + int val2C = sentence->_category; bool flag = val2C == 11 || val2C == 13; bool flag2 = val2C == 12; diff --git a/engines/titanic/true_talk/bellbot_script.cpp b/engines/titanic/true_talk/bellbot_script.cpp index 038285ec8d..c51c918a2e 100644 --- a/engines/titanic/true_talk/bellbot_script.cpp +++ b/engines/titanic/true_talk/bellbot_script.cpp @@ -239,11 +239,11 @@ int BellbotScript::process(const TTroomScript *roomScript, const TTsentence *sen if (processEntries(&_entries, _entryCount, roomScript, sentence) == 2) return 2; - if ((sentence->_field2C == 4 && sentence->localWord("am") && sentence->localWord("i")) + if ((sentence->_category == 4 && sentence->localWord("am") && sentence->localWord("i")) || (sentence->localWord("are") && sentence->localWord("we")) - || (sentence->_field2C == 3 && sentence->localWord("room") + || (sentence->_category == 3 && sentence->localWord("room") && sentence->localWord("we") && sentence->localWord("in")) - || (sentence->_field2C == 3 && sentence->localWord("rom") + || (sentence->_category == 3 && sentence->localWord("rom") && sentence->localWord("is") && sentence->localWord("this")) ) { uint id = getRangeValue(getRoomDialogueId(roomScript)); @@ -527,7 +527,7 @@ int BellbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence int BellbotScript::updateState(uint oldId, uint newId, int index) { if (!getValue(25)) { - newId = 202043 - getValue(1) <= 2 ? 994 : 0; + newId = 202043 - (getValue(1) <= 2 ? 994 : 0); CTrueTalkManager::setFlags(25, 1); } @@ -795,22 +795,22 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * bool applyFlag = false, stateFlag = true; switch (getValue(23)) { case 1: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200818)); applyFlag = true; } - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(200817)); applyFlag = true; } break; case 2: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200835)); addResponse(getDialogueId(200830)); applyFlag = true; - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { addResponse(getDialogueId(200834)); addResponse(getDialogueId(200830)); applyFlag = true; @@ -818,7 +818,7 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 3: - if (sentence->_field2C >= 11 && sentence->_field2C <= 13) { + if (sentence->_category >= 11 && sentence->_category <= 13) { addResponse(getDialogueId(200831)); addResponse(getDialogueId(200833)); applyFlag = true; @@ -826,36 +826,36 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 4: - if (sentence->_field2C == 11) { + if (sentence->_category == 11) { addResponse(getDialogueId(200872)); applyFlag = true; } - if (sentence->_field2C == 12 || sentence->_field2C == 13) { + if (sentence->_category == 12 || sentence->_category == 13) { addResponse(getDialogueId(200873)); applyFlag = true; } break; case 5: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200492)); applyFlag = true; } - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(200491)); applyFlag = true; } break; case 6: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200496)); applyResponse(); setState(0); CTrueTalkManager::setFlags(23, 7); return 2; } - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(200127)); applyFlag = true; } @@ -880,7 +880,7 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 10: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200317)); applyResponse(); setState(0); @@ -888,14 +888,14 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * return 2; } - addResponse(getDialogueId(sentence->_field2C == 12 ? 200316 : 200315)); + addResponse(getDialogueId(sentence->_category == 12 ? 200316 : 200315)); applyFlag = true; break; case 11: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200055)); - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { addResponse(getDialogueId(200318)); } else { addResponse(getDialogueId(200315)); @@ -905,30 +905,30 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 12: - if (sentence->_field2C == 6) { + if (sentence->_category == 6) { addResponse(getDialogueId(200259)); applyFlag = true; } break; case 13: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200207)); applyFlag = true; - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { addResponse(getDialogueId(200206)); applyFlag = true; } break; case 14: - if (sentence->_field2C == 6) { + if (sentence->_category == 6) { addResponse(getDialogueId(200349)); applyFlag = true; } case 15: - if (sentence->_field2C == 6) { + if (sentence->_category == 6) { addResponse(getDialogueId(200130)); applyResponse(); setState(0); @@ -945,7 +945,7 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 17: - if ((sentence->_field2C == 3 && sentence->localWord("code")) + if ((sentence->_category == 3 && sentence->localWord("code")) || (sentence->localWord("which") && sentence->localWord("is")) || sentence->localWord("remember") || sentence->localWord("know") @@ -957,7 +957,7 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 19: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200223)); applyFlag = true; } @@ -1042,12 +1042,12 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 31: - addResponse(getDialogueId(sentence->_field2C == 11 ? 200810 : 200811)); + addResponse(getDialogueId(sentence->_category == 11 ? 200810 : 200811)); applyFlag = true; break; case 32: - addResponse(getDialogueId(sentence->_field2C == 11 ? 200810 : 200812)); + addResponse(getDialogueId(sentence->_category == 11 ? 200810 : 200812)); applyFlag = true; break; @@ -1062,7 +1062,7 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 35: - if (sentence->_field2C == 3 && sentence->localWord("it") + if (sentence->_category == 3 && sentence->localWord("it") && (sentence->localWord("for") || sentence->localWord("do"))) { addResponse(getDialogueId(200768)); applyFlag = true; @@ -1070,7 +1070,7 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 36: - if (sentence->_field2C == 11) { + if (sentence->_category == 11) { CTrueTalkManager::triggerAction(14, 0); addResponse(getDialogueId(200761)); applyFlag = true; @@ -1083,14 +1083,14 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 38: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(200631)); applyFlag = true; } break; case 39: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200632)); stateFlag = false; } else { @@ -1124,7 +1124,7 @@ int BellbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 43: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200643)); applyFlag = true; } @@ -1157,7 +1157,7 @@ case44: goto case44; case 47: - if (sentence->_field2C == 12) + if (sentence->_category == 12) addResponse(getDialogueId(200368)); addResponse(getDialogueId(200366)); applyFlag = true; @@ -1177,7 +1177,7 @@ case44: break; case 49: - if (sentence->_field2C >= 11 && sentence->_field2C <= 13) { + if (sentence->_category >= 11 && sentence->_category <= 13) { addResponse(getDialogueId(200407)); applyFlag = true; stateFlag = false; @@ -1185,7 +1185,7 @@ case44: break; case 50: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200408)); stateFlag = false; } else { @@ -1204,24 +1204,24 @@ case44: break; case 52: - if (sentence->_field2C >= 11 && sentence->_field2C <= 13) { + if (sentence->_category >= 11 && sentence->_category <= 13) { addResponse(getDialogueId(200872)); applyFlag = true; } break; case 53: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(200525)); applyFlag = true; - } else if (sentence->_field2C == 11 || sentence->_field2C == 13) { + } else if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200526)); applyFlag = true; } break; case 54: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(200095)); applyFlag = true; stateFlag = false; @@ -1229,7 +1229,7 @@ case44: break; case 55: - if (sentence->_field2C == 6) { + if (sentence->_category == 6) { addResponse(getDialogueId(200112)); applyFlag = true; } @@ -1258,7 +1258,7 @@ case44: case 58: if (sentence->localWord("more") || (sentence->localWord("go") && sentence->localWord("on")) - || (sentence->_field2C == 11 && sentence->localWord("really"))) { + || (sentence->_category == 11 && sentence->localWord("really"))) { addResponse(getDialogueId(200650)); applyFlag = true; stateFlag = false; @@ -1274,7 +1274,7 @@ case44: break; case 60: - if (sentence->_field2C == 3 && sentence->localWord("they") && sentence->localWord("do")) { + if (sentence->_category == 3 && sentence->localWord("they") && sentence->localWord("do")) { addResponse(getDialogueId(200652)); applyFlag = true; stateFlag = false; @@ -1351,7 +1351,7 @@ stateFlag = false; break; case 70: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(201012)); applyFlag = true; stateFlag = false; @@ -1366,10 +1366,10 @@ stateFlag = false; break; case 72: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(200921)); applyFlag = true; - } else if (sentence->_field2C == 11 || sentence->_field2C == 13) { + } else if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(200920)); applyFlag = true; } @@ -1384,7 +1384,7 @@ stateFlag = false; break; case 74: - if (sentence->_field2C == 6) { + if (sentence->_category == 6) { addResponse(getDialogueId(201022)); applyFlag = true; stateFlag = false; @@ -1392,7 +1392,7 @@ stateFlag = false; break; case 75: - if (sentence->_field2C == 3) { + if (sentence->_category == 3) { if (sentence->localWord("that") || sentence->localWord("worb")) { addResponse(getDialogueId(201802)); applyFlag = true; @@ -1401,7 +1401,7 @@ stateFlag = false; break; case 76: - if (sentence->_field2C == 2 && (sentence->localWord("that") || sentence->localWord("gat"))) { + if (sentence->_category == 2 && (sentence->localWord("that") || sentence->localWord("gat"))) { addResponse(getDialogueId(201034)); applyFlag = true; stateFlag = false; @@ -1409,7 +1409,7 @@ stateFlag = false; break; case 77: - if (sentence->_field2C == 4 || sentence->_field2C == 3) { + if (sentence->_category == 4 || sentence->_category == 3) { if (sentence->localWord("that") || sentence->localWord("blerontis")) { addResponse(getDialogueId(201035)); applyFlag = true; @@ -1418,11 +1418,11 @@ stateFlag = false; break; case 78: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(201034)); applyFlag = true; stateFlag = false; - } else if (sentence->_field2C == 11) { + } else if (sentence->_category == 11) { addResponse(getDialogueId(201040)); applyFlag = true; } else if ((sentence->localWord("not") && sentence->localWord("remember")) @@ -1438,12 +1438,12 @@ stateFlag = false; break; case 79: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(201052)); CTrueTalkManager::triggerAction(14, 0); applyFlag = true; stateFlag = false; - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { addResponse(getDialogueId(202119)); addResponse(getDialogueId(200256)); applyFlag = true; @@ -1454,7 +1454,7 @@ stateFlag = false; if ((!sentence->localWord("what") && sentence->localWord("how")) || sentence->localWord("about") || sentence->localWord("you")) { - if (sentence->_field2C != 3 && sentence->_field2C != 4 && sentence->_field2C != 7) { + if (sentence->_category != 3 && sentence->_category != 4 && sentence->_category != 7) { addResponse(getDialogueId(201694)); applyFlag = true; stateFlag = false; @@ -1477,7 +1477,7 @@ stateFlag = false; break; case 82: - if ((sentence->_field2C == 3 && sentence->localWord("mean")) + if ((sentence->_category == 3 && sentence->localWord("mean")) || sentence->localWord("surf") || (sentence->localWord("what") && sentence->localWord("talk") && sentence->localWord("about"))) { @@ -1488,14 +1488,14 @@ stateFlag = false; break; case 83: - if (sentence->_field2C != 3 && sentence->_field2C != 4 && sentence->_field2C != 7) { + if (sentence->_category != 3 && sentence->_category != 4 && sentence->_category != 7) { addResponse(getDialogueId(201083)); applyFlag = true; } break; case 84: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(202119)); switch (getValue(1)) { @@ -1511,7 +1511,7 @@ stateFlag = false; default: break; } - } else if (sentence->_field2C == 11) { + } else if (sentence->_category == 11) { addResponse(getDialogueId(201060)); addResponse(getDialogueId(201079)); applyFlag = true; @@ -1520,22 +1520,22 @@ stateFlag = false; break; case 85: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(201814)); applyFlag = true; } - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(201813)); applyFlag = true; } break; case 86: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(202109)); applyFlag = true; } - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(202108)); applyFlag = true; } @@ -1548,14 +1548,14 @@ stateFlag = false; break; case 88: - if (sentence->_field2C == 6 || sentence->contains("upside down")) { + if (sentence->_category == 6 || sentence->contains("upside down")) { addResponse(getDialogueId(202142)); applyFlag = true; } break; case 89: - if (sentence->_field2C == 2) { + if (sentence->_category == 2) { addResponse(getDialogueId(200739)); applyFlag = true; stateFlag = false; @@ -1710,7 +1710,7 @@ bool BellbotScript::checkCommonWords(const TTroomScript *roomScript, const TTsen if (!roomScript || !sentence) return 0; CTrueTalkManager::setFlags(23, 0); - if (sentence->_field2C != 4) + if (sentence->_category != 4) return 0; if (sentence->localWord("garage")) { diff --git a/engines/titanic/true_talk/deskbot_script.cpp b/engines/titanic/true_talk/deskbot_script.cpp index 4df47e0af1..d38d53a296 100644 --- a/engines/titanic/true_talk/deskbot_script.cpp +++ b/engines/titanic/true_talk/deskbot_script.cpp @@ -29,6 +29,8 @@ namespace Titanic { int DeskbotScript::_oldId; +#define CURRENT_STATE 17 + DeskbotScript::DeskbotScript(int val1, const char *charClass, int v2, const char *charName, int v3, int val2) : TTnpcScript(val1, charClass, v2, charName, v3, val2, -1, -1, -1, 0) { @@ -73,7 +75,7 @@ int DeskbotScript::process(const TTroomScript *roomScript, const TTsentence *sen if (preprocess(roomScript, sentence) != 1) return 1; - CTrueTalkManager::setFlags(17, 0); + CTrueTalkManager::setFlags(CURRENT_STATE, 0); setState(0); updateCurrentDial(false); @@ -344,7 +346,7 @@ int DeskbotScript::updateState(uint oldId, uint newId, int index) { exit: _oldId = oldId; - setFlags17(newId, index); + setCurrentState(newId, index); return newId; } @@ -509,8 +511,8 @@ uint DeskbotScript::getStateDialogueId() const { } } -void DeskbotScript::setFlags17(uint newId, uint index) { - int newValue = getValue(17); +void DeskbotScript::setCurrentState(uint newId, uint index) { + int newValue = getValue(CURRENT_STATE); for (uint idx = 0; idx < _states.size(); ++idx) { const TTupdateState &us = _states[idx]; @@ -528,7 +530,7 @@ void DeskbotScript::setFlags17(uint newId, uint index) { } } - CTrueTalkManager::setFlags(17, newValue); + CTrueTalkManager::setFlags(CURRENT_STATE, newValue); } int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence *sentence) { @@ -536,11 +538,11 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * return 1; bool stateFlag = true, applyFlag = false; - switch (getValue(17)) { + switch (getValue(CURRENT_STATE)) { case 1: - if (sentence->_field2C != 3 && sentence->_field2C != 4 - && sentence->_field2C != 6 && sentence->_field2C != 10 - && sentence->_field2C != 2) { + if (sentence->_category != 3 && sentence->_category != 4 + && sentence->_category != 6 && sentence->_category != 10 + && sentence->_category != 2) { addResponse(getDialogueId(240423)); applyFlag = true; } @@ -554,15 +556,15 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 3: - if (sentence->_field2C == 11 || sentence->_field2C == 13 - || sentence->_field2C == 3 || sentence->localWord("upgrade")) { + if (sentence->_category == 11 || sentence->_category == 13 + || sentence->_category == 3 || sentence->localWord("upgrade")) { addResponse(getDialogueId(240433)); applyFlag = true; } break; case 4: - addResponse(getDialogueId(sentence->_field2C == 11 || sentence->_field2C == 13 + addResponse(getDialogueId(sentence->_category == 11 || sentence->_category == 13 ? 240495 : 240494)); applyFlag = true; break; @@ -594,7 +596,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * stateFlag = true; } else if (sentence->localWord("name") || sentence->localWord("called") - || sentence->_field2C == 11) { + || sentence->_category == 11) { addResponse(getDialogueId(240448)); stateFlag = true; } else { @@ -605,7 +607,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 7: - if (sentence->_field2C == 11 || sentence->_field2C == 13 + if (sentence->_category == 11 || sentence->_category == 13 || (sentence->localWord("what") && sentence->localWord("wrong")) || sentence->localWord("clothes")) { addResponse(getDialogueId(240489)); @@ -616,7 +618,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * case 8: if (isDial1Low() && getValue(1) == 4) { - if (sentence->_field2C == 12 || sentence->_field2C == 13 + if (sentence->_category == 12 || sentence->_category == 13 || sentence->contains("do not")) { addResponse(getDialogueId(240447)); @@ -626,7 +628,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * CTrueTalkManager::triggerAction(19, 3); CTrueTalkManager::setFlags(22, 1); applyFlag = true; - } else if (sentence->_field2C == 11) { + } else if (sentence->_category == 11) { addResponse(getDialogueId(240746)); applyFlag = true; stateFlag = false; @@ -1089,7 +1091,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * case 74: case 75: - if (sentence->_field2C == 24) { + if (sentence->_category == 24) { addResponse(getDialogueId(240972)); applyFlag = true; } else if (sentence->localWord("good") || sentence->localWord("yes") @@ -1101,7 +1103,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 76: - if (sentence->_field2C == 6) { + if (sentence->_category == 6) { addResponse(getDialogueId(240767)); applyFlag = true; stateFlag = false; @@ -1109,21 +1111,25 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 77: - if (sentence->_field2C == 3) { + if (sentence->_category == 3) { addResponse(getDialogueId(241109)); applyFlag = true; stateFlag = false; } case 78: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + // "Do you have a reservation?" + if (sentence->_category == 11 || sentence->_category == 13) { + // Player said they have a reservation addResponse(getDialogueId(241262)); - } else if (sentence->_field2C == 12 || sentence->contains("do not")) { + } else if (sentence->_category == 12 || sentence->contains("do not")) { + // Player said they don't have a reservation setDialRegion(0, 0); setDialRegion(1, 0); addResponse(getDialogueId(241268)); - add241716(); + addAskBreakfast(); } else { + // Player didn't say yes or no addResponse(getDialogueId(240745)); } @@ -1142,27 +1148,27 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * case 3: addAssignedRoom(); setState(0); - CTrueTalkManager::setFlags(17, 0); + CTrueTalkManager::setFlags(CURRENT_STATE, 0); return 2; default: addResponse(getDialogueId(241267)); break; } - add241716(); + addAskBreakfast(); applyFlag = true; stateFlag = false; break; case 81: - addResponse(getDialogueId(sentence->_field2C == 12 ? 240602 : 241337)); + addResponse(getDialogueId(sentence->_category == 12 ? 240602 : 241337)); applyResponse(); setState(0); - CTrueTalkManager::setFlags(17, 0); + CTrueTalkManager::setFlags(CURRENT_STATE, 0); return 2; case 82: - if (sentence->_field2C == 2) { + if (sentence->_category == 2) { addResponse(getDialogueId(241339)); applyFlag = true; } @@ -1181,7 +1187,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 84: - if (sentence->_field2C == 12 || sentence->contains("vegetarian") + if (sentence->_category == 12 || sentence->contains("vegetarian") || sentence->contains("vegitarian")) { addResponse(getDialogueId(241718)); addResponse(getDialogueId(241709)); @@ -1208,7 +1214,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 85: - if (sentence->_field2C == 12 || sentence->contains("bugle") + if (sentence->_category == 12 || sentence->contains("bugle") || sentence->contains("buggle") || sentence->contains("trumpet") || sentence->contains("saxophone") || sentence->contains("kazoo") || sentence->contains("blerontin 1") || sentence->contains("the 1") @@ -1229,7 +1235,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 86: - if (sentence->_field2C == 12 || sentence->_field2C == 11 || sentence->contains("view")) { + if (sentence->_category == 12 || sentence->_category == 11 || sentence->contains("view")) { addResponse(getDialogueId(241714)); addResponse(getDialogueId(241699)); } else { @@ -1352,19 +1358,25 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * case 92: case 93: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + // "Fish?" + if (sentence->_category == 11 || sentence->_category == 13) { + // Yes addResponse(getDialogueId(241077)); addResponse(getDialogueId(241706)); - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { + // No addAssignedRoom(); setState(0); - CTrueTalkManager::setFlags(17, 0); + CTrueTalkManager::setFlags(CURRENT_STATE, 0); return 2; - } else if (g_vm->_trueTalkManager->_quotes.find(sentence->_normalizedLine.c_str()) + } else if (g_vm->_trueTalkManager->_quotes.find(sentence->_initialLine.c_str()) == MKTAG('F', 'I', 'S', 'H')) { + // WORKAROUND: Original passed _normalizedLine, but "fish" is one of the common + // phrases replaced with with alternative text "completelyrandom" addResponse(getDialogueId(240877)); addResponse(getDialogueId(241706)); }else { + // All other responses if (getRandomNumber(100) < 80 && sentence2C(sentence)) addResponse(getDialogueId(241707)); @@ -1387,7 +1399,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * || sentence->contains("same room")) { addAssignedRoom(); setState(0); - CTrueTalkManager::setFlags(17, 0); + CTrueTalkManager::setFlags(CURRENT_STATE, 0); return 2; } else { if (getRandomNumber(100) < 80 && sentence2C(sentence)) @@ -1410,7 +1422,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 96: - if (sentence->_field2C == 2) { + if (sentence->_category == 2) { addResponse(getDialogueId(241350)); applyFlag = true; stateFlag = false; @@ -1433,7 +1445,7 @@ int DeskbotScript::preprocess(const TTroomScript *roomScript, const TTsentence * applyResponse(); if (stateFlag) { setState(0); - CTrueTalkManager::setFlags(17, 0); + CTrueTalkManager::setFlags(CURRENT_STATE, 0); } return applyFlag ? 2 : 1; @@ -1480,7 +1492,7 @@ int DeskbotScript::checkCommonWords(const TTsentence *sentence) { return 0; } -void DeskbotScript::add241716() { +void DeskbotScript::addAskBreakfast() { addResponse(getDialogueId(241716)); } @@ -1497,6 +1509,7 @@ void DeskbotScript::addAssignedRoom() { addResponse(getDialogueId(241271 + floorNum)); addResponse(getDialogueId(241317 + roomNum)); addResponse(getDialogueId(241698)); + applyResponse(); } } // End of namespace Titanic diff --git a/engines/titanic/true_talk/deskbot_script.h b/engines/titanic/true_talk/deskbot_script.h index f5978553ce..ffb6b1063c 100644 --- a/engines/titanic/true_talk/deskbot_script.h +++ b/engines/titanic/true_talk/deskbot_script.h @@ -61,9 +61,9 @@ private: uint getStateDialogueId() const; /** - * Sets state data in flags 17 + * Sets the current state for what the Deskbot is doing/asking */ - void setFlags17(uint newId, uint index); + void setCurrentState(uint newId, uint index); /** * Does preprocessing for the sentence @@ -81,9 +81,9 @@ private: int checkCommonWords(const TTsentence *sentence); /** - * Adds response dialogue 241716 + * Adds a dialogue for asking the player what kind of breakfast they'd like */ - void add241716(); + void addAskBreakfast(); /** * Adds a dialogue description for the player's assigned room diff --git a/engines/titanic/true_talk/doorbot_script.cpp b/engines/titanic/true_talk/doorbot_script.cpp index 5c7403f187..b1d6a91107 100644 --- a/engines/titanic/true_talk/doorbot_script.cpp +++ b/engines/titanic/true_talk/doorbot_script.cpp @@ -196,13 +196,13 @@ int DoorbotScript::process(const TTroomScript *roomScript, const TTsentence *sen return setResponse(getDialogueId(220113)); } - if (sentence->_field2C == 6 && sentence->contains("why not")) { + if (sentence->_category == 6 && sentence->contains("why not")) { return setResponse(11871, 8); } currState = getState(); if (currState) { - int sentMode = sentence->_field2C; + int sentMode = sentence->_category; bool flag1 = sentMode == 11 || sentMode == 13; bool flag2 = sentMode == 12; @@ -475,7 +475,7 @@ int DoorbotScript::process(const TTroomScript *roomScript, const TTsentence *sen || defaultProcess(roomScript, sentence)) return 2; - switch (sentence->_field2C) { + switch (sentence->_category) { case 11: if (getRandomNumber(100) > 90) return setResponse(10839, 42); diff --git a/engines/titanic/true_talk/liftbot_script.cpp b/engines/titanic/true_talk/liftbot_script.cpp index ab995b71b9..e52e47d99d 100644 --- a/engines/titanic/true_talk/liftbot_script.cpp +++ b/engines/titanic/true_talk/liftbot_script.cpp @@ -92,7 +92,7 @@ int LiftbotScript::process(const TTroomScript *roomScript, const TTsentence *sen checkItems(roomScript, sentence); int currState = getState(); - int sentMode = sentence->_field2C; + int sentMode = sentence->_category; TTtreeResult treeResult; if (currState) { @@ -180,6 +180,7 @@ ScriptChangedResult LiftbotScript::scriptChanged(const TTroomScript *roomScript, addResponse(getDialogueId(210033)); } CTrueTalkManager::setFlags(27, 1); + applyResponse(); break; case 155: @@ -580,14 +581,14 @@ int LiftbotScript::sentence1(const TTsentence *sentence) { return 1; } - int classNum = 1; + PassengerClass classNum = FIRST_CLASS; bool classSet = true; if (sentence->localWord("firstclass")) - classNum = 1; + classNum = FIRST_CLASS; else if (sentence->localWord("secondclass")) - classNum = 2; + classNum = SECOND_CLASS; else if (sentence->localWord("thirdclass")) - classNum = 3; + classNum = THIRD_CLASS; else classSet = false; @@ -650,7 +651,7 @@ int LiftbotScript::sentence1(const TTsentence *sentence) { return 1; } - if (sentence->_field2C == 4 || sentence->localWord("find") + if (sentence->_category == 4 || sentence->localWord("find") || sentence->contains("get to")) { if (getCurrentFloor() != diff) { selectResponse(diff == 1 ? 210769 : 210764); diff --git a/engines/titanic/true_talk/maitred_script.cpp b/engines/titanic/true_talk/maitred_script.cpp index e902169ed0..cb1a3a5460 100644 --- a/engines/titanic/true_talk/maitred_script.cpp +++ b/engines/titanic/true_talk/maitred_script.cpp @@ -726,7 +726,7 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 7: - if (sentence->_field2C == 12) { + if (sentence->_category == 12) { addResponse(getDialogueId(260089)); applyFlag = true; stateFlag = false; @@ -738,7 +738,7 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 8: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { trigger12(false); addResponse(getDialogueId(260094)); CTrueTalkManager::setFlags(11, 1); @@ -819,7 +819,7 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 16: - addResponse(getDialogueId(sentence->_field2C == 11 ? 260209 : 260210)); + addResponse(getDialogueId(sentence->_category == 11 ? 260209 : 260210)); applyFlag = true; stateFlag = false; break; @@ -843,13 +843,13 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 18: - if (sentence->_field2C == 11) { + if (sentence->_category == 11) { addResponse(getDialogueId(260232)); applyFlag = true; - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { addResponse(getDialogueId(260231)); applyFlag = true; - } else if (sentence->_field2C == 13) { + } else if (sentence->_category == 13) { addResponse(getDialogueId(260444)); addResponse(getDialogueId(260233)); applyFlag = true; @@ -937,7 +937,7 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 23: - if (sentence->_field2C == 11) { + if (sentence->_category == 11) { addResponse(getDialogueId(260237)); applyFlag = true; stateFlag = false; @@ -992,7 +992,7 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * addResponse(getDialogueId(260256)); applyFlag = true; stateFlag = false; - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { addResponse(getDialogueId(260255)); applyFlag = true; stateFlag = false; @@ -1024,7 +1024,7 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 29: - if (sentence->_field2C == 11) { + if (sentence->_category == 11) { setFlags12(); addResponse(getDialogueId(260131)); } else { @@ -1034,10 +1034,10 @@ int MaitreDScript::preprocess(const TTroomScript *roomScript, const TTsentence * break; case 30: - if (sentence->_field2C == 11 || sentence->_field2C == 13) { + if (sentence->_category == 11 || sentence->_category == 13) { addResponse(getDialogueId(260695)); applyFlag = true; - } else if (sentence->_field2C == 12) { + } else if (sentence->_category == 12) { addResponse(getDialogueId(260696)); applyFlag = true; } diff --git a/engines/titanic/true_talk/parrot_script.cpp b/engines/titanic/true_talk/parrot_script.cpp index b09e74505c..25b54f0715 100644 --- a/engines/titanic/true_talk/parrot_script.cpp +++ b/engines/titanic/true_talk/parrot_script.cpp @@ -55,7 +55,7 @@ int ParrotScript::process(const TTroomScript *roomScript, const TTsentence *sent if (processEntries(roomScript, sentence) == 2) { int tagId = g_vm->_trueTalkManager->_quotes.find(sentence->_normalizedLine); if (!tagId || chooseResponse(roomScript, sentence, tagId) != 2) { - addResponse(getDialogueId(sentence->check2C() ? 280248 : 280235)); + addResponse(getDialogueId(sentence->checkCategory() ? 280248 : 280235)); applyResponse(); } } diff --git a/engines/titanic/true_talk/script_support.cpp b/engines/titanic/true_talk/script_support.cpp index 14560e2d9f..52676139f0 100644 --- a/engines/titanic/true_talk/script_support.cpp +++ b/engines/titanic/true_talk/script_support.cpp @@ -38,7 +38,7 @@ int TTnpcScriptResponse::size() const { TTscriptRange::TTscriptRange(uint id, const Common::Array<uint> &values, bool isRandom, bool isSequential) : - _id(id), _nextP(nullptr) { + _id(id), _nextP(nullptr), _priorIndex(0) { _mode = SF_NONE; if (isRandom) _mode = SF_RANDOM; @@ -57,7 +57,7 @@ bool TTsentenceEntry::load(Common::SeekableReadStream *s) { return false; _field0 = s->readUint32LE(); - _field4 = s->readUint32LE(); + _category = s->readUint32LE(); _string8 = readStringFromStream(s); _fieldC = s->readUint32LE(); _string10 = readStringFromStream(s); diff --git a/engines/titanic/true_talk/script_support.h b/engines/titanic/true_talk/script_support.h index bdaec6e7c9..3c5b919ccf 100644 --- a/engines/titanic/true_talk/script_support.h +++ b/engines/titanic/true_talk/script_support.h @@ -40,6 +40,10 @@ struct TTnpcScriptResponse { uint _tag; uint _values[4]; + TTnpcScriptResponse() : _tag(0) { + _values[0] = _values[1] = _values[2] = _values[3] = 0; + } + /** * Returns the size of the values list plus 1 */ @@ -59,10 +63,9 @@ struct TTscriptRange { bool isSequential); }; - struct TTsentenceEntry { int _field0; - int _field4; + int _category; CString _string8; int _fieldC; CString _string10; @@ -75,7 +78,7 @@ struct TTsentenceEntry { int _field2C; int _field30; - TTsentenceEntry() : _field0(0), _field4(0), _fieldC(0), + TTsentenceEntry() : _field0(0), _category(0), _fieldC(0), _field20(0), _field28(0), _field2C(0), _field30(0) {} /** diff --git a/engines/titanic/true_talk/succubus_script.cpp b/engines/titanic/true_talk/succubus_script.cpp index db537c6470..888d23e17c 100644 --- a/engines/titanic/true_talk/succubus_script.cpp +++ b/engines/titanic/true_talk/succubus_script.cpp @@ -85,7 +85,7 @@ int SuccUBusScript::process(const TTroomScript *roomScript, const TTsentence *se int currState = getState(); if (currState) { - int currMode = sentence->_field2C; + int currMode = sentence->_category; bool modeFlag1 = currMode == 11 || currMode == 13; bool modeFlag2 = currMode == 12; setState(0); diff --git a/engines/titanic/true_talk/title_engine.cpp b/engines/titanic/true_talk/title_engine.cpp index 079067389d..bfa97e3f92 100644 --- a/engines/titanic/true_talk/title_engine.cpp +++ b/engines/titanic/true_talk/title_engine.cpp @@ -40,7 +40,8 @@ void CTitleEngine::setup(int val1, int val2) { /*------------------------------------------------------------------------*/ -STtitleEngine::STtitleEngine(): CTitleEngine(), _responseP(nullptr) { +STtitleEngine::STtitleEngine(): CTitleEngine(), + _responseP(nullptr), _stream(nullptr) { } STtitleEngine::~STtitleEngine() { diff --git a/engines/titanic/true_talk/true_talk_manager.cpp b/engines/titanic/true_talk/true_talk_manager.cpp index 0e90676eef..fbb9d17407 100644 --- a/engines/titanic/true_talk/true_talk_manager.cpp +++ b/engines/titanic/true_talk/true_talk_manager.cpp @@ -245,7 +245,7 @@ void CTrueTalkManager::start4(CTrueTalkNPC *npc, CViewItem *view) { TTnpcScript *CTrueTalkManager::getTalker(const CString &name) const { if (name.contains("Doorbot")) return _scripts.getNpcScript(104); - else if (name.contains("DeskBot")) + else if (name.contains("Deskbot")) return _scripts.getNpcScript(103); else if (name.contains("LiftBot")) return _scripts.getNpcScript(105); @@ -271,6 +271,7 @@ TTnpcScript *CTrueTalkManager::getNpcScript(CTrueTalkNPC *npc) const { if (!script) { // Fall back on the default NPC script + warning("Could not find NPC script for %s, using fallback", npc->getName().c_str()); script = _scripts.getNpcScript(101); } @@ -513,7 +514,9 @@ void CTrueTalkManager::playSpeech(TTtalker *talker, TTroomScript *roomScript, CV _gameManager->_sound.stopChannel(p1._channelMode); if (view) { p1._positioningMode = POSMODE_VECTOR; +#if 0 view->getPosition(p1._posX, p1._posY, p1._posZ); +#endif } // Loop through adding each of the speech portions in. We use the @@ -537,12 +540,16 @@ void CTrueTalkManager::playSpeech(TTtalker *talker, TTroomScript *roomScript, CV if (idx == 0) g_vm->_events->sleep(milli); - +#if 0 + // TODO: Figure out if these below are needed. It kinda looks like they were + // simply playing the same speech at different spatial co-ordinates. And since + // we don't support spatial processing in ScummVM yet, they're being left disabled p3._priorSoundHandle = _gameManager->_sound.playSpeech(_dialogueFile, id - _dialogueId, p3); if (idx == 0) g_vm->_events->sleep(milli); p2._priorSoundHandle = _gameManager->_sound.playSpeech(_dialogueFile, id - _dialogueId, p2); +#endif } } diff --git a/engines/titanic/true_talk/tt_concept.cpp b/engines/titanic/true_talk/tt_concept.cpp index 9aad88722b..acb2e61e9e 100644 --- a/engines/titanic/true_talk/tt_concept.cpp +++ b/engines/titanic/true_talk/tt_concept.cpp @@ -131,7 +131,7 @@ void TTconcept::setScriptType(ScriptType scriptType) { int TTconcept::initializeWordRef(TTword *word) { delete _wordP; - _wordP = word; + _wordP = word->copy(); return 0; } @@ -213,11 +213,9 @@ void TTconcept::copyFrom(TTconcept *src) { } int TTconcept::setOwner(TTconcept *src) { - if (this) { - if (src->_wordP) { - TTword *newWord = src->_wordP->copy(); - return setOwner(newWord, 1); - } + if (src->_wordP) { + TTword *newWord = src->_wordP->copy(); + return setOwner(newWord, 1); } return 0; @@ -298,11 +296,11 @@ TTconcept *TTconcept::findBy20(int val) { } bool TTconcept::isWordId(int id) const { - return this && _wordP && _wordP->_id == id; + return _wordP && _wordP->_id == id; } int TTconcept::getWordId() const { - return this && _wordP ? _wordP->_id : 0; + return _wordP ? _wordP->_id : 0; } } // End of namespace Titanic diff --git a/engines/titanic/true_talk/tt_concept_node.cpp b/engines/titanic/true_talk/tt_concept_node.cpp index 454ca59971..f6512b3d81 100644 --- a/engines/titanic/true_talk/tt_concept_node.cpp +++ b/engines/titanic/true_talk/tt_concept_node.cpp @@ -29,11 +29,14 @@ namespace Titanic { TTconceptNode::TTconceptNode() : _concept0P(_concepts[0]), _concept1P(_concepts[1]), _concept2P(_concepts[2]), _concept3P(_concepts[3]), _concept4P(_concepts[4]), _concept5P(_concepts[5]), _field18(0), _field1C(0), _nextP(nullptr), _status(0) { + Common::fill(&_concepts[0], &_concepts[6], (TTconcept *)nullptr); } TTconceptNode::TTconceptNode(const TTconceptNode &src) : _concept0P(_concepts[0]), _concept1P(_concepts[1]), _concept2P(_concepts[2]), _concept3P(_concepts[3]), _concept4P(_concepts[4]), _concept5P(_concepts[5]), _field18(0), _field1C(0), _nextP(nullptr), _status(0) { + Common::fill(&_concepts[0], &_concepts[6], (TTconcept *)nullptr); + if (src._status) { _status = SS_5; } else { diff --git a/engines/titanic/true_talk/tt_npc_script.cpp b/engines/titanic/true_talk/tt_npc_script.cpp index 74e2f4f66b..527e33f14c 100644 --- a/engines/titanic/true_talk/tt_npc_script.cpp +++ b/engines/titanic/true_talk/tt_npc_script.cpp @@ -338,12 +338,13 @@ uint TTnpcScript::getRangeValue(uint id) { case SF_RANDOM: { uint count = range->_values.size(); - uint index = getRandomNumber(count) - 1; + int index = (int)getRandomNumber(count) - 1; if (count > 1 && range->_values[index] == range->_priorIndex) { - for (int retry = 0; retry < 8 && index != range->_priorIndex; ++retry) - index = getRandomNumber(count) - 1; + for (int retry = 0; retry < 8 && index != (int)range->_priorIndex; ++retry) + index = (int)getRandomNumber(count) - 1; } + assert(index >= 0); range->_priorIndex = index; return range->_values[index]; } @@ -699,12 +700,12 @@ int TTnpcScript::processEntries(const TTsentenceEntries *entries, uint entryCoun if (!entryCount) // No count specified, so use entire list entryCount = entries->size(); - int entryId = _field2C; + int categoryNum = sentence->_category; for (uint loopCtr = 0; loopCtr < 2; ++loopCtr) { for (uint entryCtr = 0; entryCtr < entryCount; ++entryCtr) { const TTsentenceEntry &entry = (*entries)[entryCtr]; - if (entry._field4 != entryId && (loopCtr == 0 || entry._field4)) + if (entry._category != categoryNum && (loopCtr == 0 || entry._category)) continue; bool flag; @@ -955,7 +956,7 @@ bool TTnpcScript::getStateValue() const { } bool TTnpcScript::sentence2C(const TTsentence *sentence) { - return sentence->_field2C >= 2 && sentence->_field2C <= 7; + return sentence->_category >= 2 && sentence->_category <= 7; } void TTnpcScript::getAssignedRoom(int *roomNum, int *floorNum, int *elevatorNum) const { diff --git a/engines/titanic/true_talk/tt_parser.cpp b/engines/titanic/true_talk/tt_parser.cpp index d67f5235c4..e432677c7c 100644 --- a/engines/titanic/true_talk/tt_parser.cpp +++ b/engines/titanic/true_talk/tt_parser.cpp @@ -482,28 +482,30 @@ int TTparser::findFrames(TTsentence *sentence) { TTstring *line = sentence->_normalizedLine.copy(); TTstring wordString; int status = 0; - for (int ctr = 1; !status; ++ctr) { + for (int ctr = 1; status <= 1; ++ctr) { // Keep stripping words off the start of the passed input wordString = line->tokenize(" \n"); if (wordString.empty()) break; TTword *srcWord = nullptr; - TTword *word = _owner->_vocab->getWord(wordString, &word); + TTword *word = _owner->_vocab->getWord(wordString, &srcWord); sentence->storeVocabHit(srcWord); if (!word && ctr == 1) { word = new TTword(wordString, WC_UNKNOWN, 0); } - for (TTword *currP = word; currP && !status; currP = currP->_nextP) + for (TTword *currP = word; currP && status <= 1; currP = currP->_nextP) status = processRequests(currP); - word->deleteSiblings(); - delete word; + if (word) { + word->deleteSiblings(); + delete word; + } } - if (!status) { + if (status <= 1) { status = checkForAction(); } @@ -514,7 +516,7 @@ int TTparser::findFrames(TTsentence *sentence) { int TTparser::loadRequests(TTword *word) { int status = 0; - if (word->_tag != MKTAG('Z', 'Z', 'Z', 'T')) + if (word->_tag != MKTAG('Z', 'Z', 'Z', '[')) addNode(word->_tag); switch (word->_wordClass) { @@ -620,7 +622,7 @@ int TTparser::loadRequests(TTword *word) { break; case WC_CONJUNCTION: - if (_sentence->check2C()) { + if (_sentence->checkCategory()) { _sentenceConcept->_field1C = 1; _sentenceConcept = _sentenceConcept->addSibling(); delete this; @@ -674,7 +676,7 @@ int TTparser::loadRequests(TTword *word) { case 901: case 902: case 904: - if (_sentence->_field2C == 9) { + if (_sentence->_category == 9) { _sentenceConcept->_field1C = 1; _sentenceConcept = _sentenceConcept->addSibling(); addNode(1); @@ -736,7 +738,7 @@ int TTparser::considerRequests(TTword *word) { for (TTparserNode *nodeP = _nodesP; nodeP; ) { switch (nodeP->_tag) { case CHECK_COMMAND_FORM: - if (_sentenceConcept->_concept1P && _sentence->_field2C == 1 && + if (_sentenceConcept->_concept1P && _sentence->_category == 1 && !_sentenceConcept->_concept0P) { concept = new TTconcept(_sentence->_npcScript, ST_NPC_SCRIPT); _sentenceConcept->_concept0P = concept; @@ -1030,7 +1032,7 @@ int TTparser::considerRequests(TTword *word) { addNode(15); } - if (_sentence->check2C() && word->_id == 113) + if (_sentence->checkCategory() && word->_id == 113) addNode(4); if (word->_wordClass == WC_ACTION) @@ -1053,10 +1055,10 @@ int TTparser::considerRequests(TTword *word) { break; case WORD_TYPE_IS_SENTENCE_TYPE: - if (_sentence->_field2C == 1 || _sentence->_field2C == 10) { + if (_sentence->_category == 1 || _sentence->_category == 10) { for (TTword *wordP = _currentWordP; wordP; wordP = wordP->_nextP) { if (wordP->_id == 906) { - _sentence->_field2C = 12; + _sentence->_category = 12; flag = true; break; } @@ -1066,44 +1068,44 @@ int TTparser::considerRequests(TTword *word) { TTconcept *newConceptP; switch (word->_id) { case 108: - _sentence->_field2C = 8; + _sentence->_category = 8; break; case 113: if (!_sentenceConcept->_concept3P) - _sentence->_field2C = 22; + _sentence->_category = 22; break; - case 304: - _sentence->_field2C = 25; + case 306: + _sentence->_category = 7; break; - case 305: - _sentence->_field2C = 24; + case 307: + _sentence->_category = 24; break; - case 306: - _sentence->_field2C = 7; + case 308: + _sentence->_category = 25; break; case 501: - _sentence->_field2C = 9; + _sentence->_category = 9; break; case 900: - _sentence->_field2C = 5; + _sentence->_category = 5; break; case 901: - _sentence->_field2C = 4; + _sentence->_category = 4; break; case 904: - _sentence->_field2C = 6; + _sentence->_category = 6; break; case 905: - _sentence->_field2C = 11; + _sentence->_category = 11; break; case 906: - _sentence->_field2C = 12; + _sentence->_category = 12; break; case 907: - _sentence->_field2C = 13; + _sentence->_category = 13; break; case 908: - _sentence->_field2C = 2; + _sentence->_category = 2; if (!_sentenceConcept->_concept0P) { newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0); newConceptP = new TTconcept(newPictP); @@ -1114,7 +1116,7 @@ int TTparser::considerRequests(TTword *word) { } break; case 909: - _sentence->_field2C = 3; + _sentence->_category = 3; newPictP = new TTpicture(TTstring("?"), WC_THING, 0, 0, 0, 0, 0); newConceptP = new TTconcept(newPictP); @@ -1152,8 +1154,8 @@ int TTparser::considerRequests(TTword *word) { addNode(5); addNode(21); - if (!_sentence->_field2C) - _sentence->_field2C = 15; + if (!_sentence->_category) + _sentence->_category = 15; break; case MKTAG('C', 'U', 'R', 'S'): @@ -1168,8 +1170,8 @@ int TTparser::considerRequests(TTword *word) { addNode(5); addNode(21); - if (!_sentence->_field2C) - _sentence->_field2C = 14; + if (!_sentence->_category) + _sentence->_category = 14; break; case MKTAG('F', 'A', 'R', 'R'): @@ -1186,15 +1188,15 @@ int TTparser::considerRequests(TTword *word) { addNode(5); addNode(21); - if (_sentence->_field2C == 1) - _sentence->_field2C = 14; + if (_sentence->_category == 1) + _sentence->_category = 14; flag = true; break; case MKTAG('H', 'E', 'L', 'P'): - if (_sentence->_field2C == 1) - _sentence->_field2C = 18; + if (_sentence->_category == 1) + _sentence->_category = 18; flag = true; break; @@ -1257,8 +1259,8 @@ int TTparser::considerRequests(TTword *word) { break; case MKTAG('T', 'E', 'A', 'C'): - if (_sentence->_field2C == 1) - _sentence->_field2C = 10; + if (_sentence->_category == 1) + _sentence->_category = 10; flag = true; break; @@ -1275,7 +1277,7 @@ int TTparser::considerRequests(TTword *word) { TTparserNode *nextP = dynamic_cast<TTparserNode *>(nodeP->_nextP); if (flag) - delete nodeP; + removeNode(nodeP); nodeP = nextP; } @@ -1402,8 +1404,8 @@ int TTparser::checkForAction() { } else { // No chain, so singular word can simply be removed _currentWordP = nullptr; - if (word->_id == 906 && _sentence->_field2C == 1) - _sentence->_field2C = 12; + if (word->_id == 906 && _sentence->_category == 1) + _sentence->_category = 12; } if (word->_text == "do" || word->_text == "doing" || word->_text == "does" || @@ -1453,28 +1455,28 @@ int TTparser::checkForAction() { if (_sentence->fn2(3, TTstring("thePlayer"), _sentenceConcept) && !flag) { if (_sentenceConcept->concept1WordId() == 101) { - _sentence->_field2C = 16; - } else if (_sentence->_field2C != 18 && _sentenceConcept->concept1WordId() == 102) { + _sentence->_category = 16; + } else if (_sentence->_category != 18 && _sentenceConcept->concept1WordId() == 102) { if (_sentence->fn2(0, TTstring("targetNpc"), _sentenceConcept)) - _sentence->_field2C = 15; + _sentence->_category = 15; } } if (_sentence->fn2(2, TTstring("thePlayer"), _sentenceConcept) && _sentenceConcept->concept1WordId() == 101 && flag) - _sentence->_field2C = 17; + _sentence->_category = 17; if (!_sentenceConcept->_concept0P && !_sentenceConcept->_concept1P && !_sentenceConcept->_concept2P && !_sentenceConcept->_concept5P && !flag) { if (_conceptP) filterConcepts(5, 2); - if (!_sentenceConcept->_concept2P && _sentence->_field2C == 1) - _sentence->_field2C = 0; + if (!_sentenceConcept->_concept2P && _sentence->_category == 1) + _sentence->_category = 0; } - if (_sentence->_field58 < 5 && _sentence->_field2C == 1 && !flag) - _sentence->_field2C = 19; + if (_sentence->_field58 < 5 && _sentence->_category == 1 && !flag) + _sentence->_category = 19; for (TTconceptNode *nodeP = &_sentence->_sentenceConcept; nodeP; nodeP = nodeP->_nextP) { if (nodeP->_field18 == 0 && nodeP->_concept1P) { @@ -1490,15 +1492,15 @@ int TTparser::checkForAction() { } } - if (_sentence->_field2C == 1 && _sentenceConcept->_concept5P && + if (_sentence->_category == 1 && _sentenceConcept->_concept5P && _sentenceConcept->_concept2P) { if (_sentence->fn4(1, 113, nullptr)) { if (_sentence->fn2(2, TTstring("targetNpc"), nullptr)) { - _sentence->_field2C = 20; + _sentence->_category = 20; } else if (_sentence->fn2(2, TTstring("thePlayer"), nullptr)) { - _sentence->_field2C = 21; + _sentence->_category = 21; } else { - _sentence->_field2C = 22; + _sentence->_category = 22; } } } else if (!_sentenceConcept->_concept0P && !_sentenceConcept->_concept1P && @@ -1506,8 +1508,8 @@ int TTparser::checkForAction() { if (_conceptP) filterConcepts(5, 2); - if (!_sentenceConcept->_concept2P && _sentence->_field2C == 1) - _sentence->_field2C = 0; + if (!_sentenceConcept->_concept2P && _sentence->_category == 1) + _sentence->_category = 0; } return status; @@ -1632,7 +1634,7 @@ int TTparser::processModifiers(int modifier, TTword *word) { TTconcept *newConcept = new TTconcept(word, ST_UNKNOWN_SCRIPT); // Cycles through each word - for (TTword *currP = _currentWordP; currP != word; currP = _currentWordP) { + for (TTword *currP = _currentWordP; currP && currP != word; currP = _currentWordP) { if ((modifier == 2 && currP->_wordClass == WC_ADJECTIVE) || (modifier == 1 && currP->_wordClass == WC_ADVERB)) { newConcept->_string2 += ' '; @@ -1658,8 +1660,8 @@ int TTparser::processModifiers(int modifier, TTword *word) { case 204: newConcept->_field34 = 1; - if (_sentence->_field2C == 1) - _sentence->_field2C = 12; + if (_sentence->_category == 1) + _sentence->_category = 12; newConcept->_field14 = 1; break; diff --git a/engines/titanic/true_talk/tt_quotes_tree.cpp b/engines/titanic/true_talk/tt_quotes_tree.cpp index 16453a10ae..e2293887a8 100644 --- a/engines/titanic/true_talk/tt_quotes_tree.cpp +++ b/engines/titanic/true_talk/tt_quotes_tree.cpp @@ -66,7 +66,7 @@ int TTquotesTree::search(const char *str, QuoteTreeNum treeNum, return -1; if (remainder) { - while (*str) { + for (; *str; ++str) { if (*str >= 'a' && *str != 's') *remainder += *str; } diff --git a/engines/titanic/true_talk/tt_response.cpp b/engines/titanic/true_talk/tt_response.cpp index 1c784ad320..3cb8d2c9ec 100644 --- a/engines/titanic/true_talk/tt_response.cpp +++ b/engines/titanic/true_talk/tt_response.cpp @@ -49,6 +49,12 @@ TTresponse::~TTresponse() { } } +TTresponse *TTresponse::appendResponse(int id) { + TTresponse *resp = new TTresponse(id, 3); + _nextP = resp; + return resp; +} + TTresponse *TTresponse::copyChain() const { TTresponse *returnResponseP = new TTresponse(this); diff --git a/engines/titanic/true_talk/tt_response.h b/engines/titanic/true_talk/tt_response.h index d39d18c193..950e1cd23e 100644 --- a/engines/titanic/true_talk/tt_response.h +++ b/engines/titanic/true_talk/tt_response.h @@ -42,6 +42,12 @@ public: virtual ~TTresponse(); /** + * Creates a new response and adds it as the current + * response's next response + */ + TTresponse *appendResponse(int id); + + /** * Makes a copy of the chain of responses */ TTresponse *copyChain() const; diff --git a/engines/titanic/true_talk/tt_script_base.cpp b/engines/titanic/true_talk/tt_script_base.cpp index 2f58ad1400..1fa1ce3315 100644 --- a/engines/titanic/true_talk/tt_script_base.cpp +++ b/engines/titanic/true_talk/tt_script_base.cpp @@ -129,7 +129,7 @@ void TTscriptBase::appendResponse(int index, int *maxP, int id) { if (id && (!maxP || index <= *maxP)) { if (_respTailP) { // Prior fragments already exist, so append to end of chain - _respTailP = new TTresponse(_respTailP); + _respTailP = _respTailP->appendResponse(id); } else { // Currently no tail _respTailP = new TTresponse(id, 3); diff --git a/engines/titanic/true_talk/tt_sentence.cpp b/engines/titanic/true_talk/tt_sentence.cpp index 3fae527770..451582383d 100644 --- a/engines/titanic/true_talk/tt_sentence.cpp +++ b/engines/titanic/true_talk/tt_sentence.cpp @@ -41,7 +41,7 @@ TTsentenceConcept *TTsentenceConcept::addSibling() { TTsentence::TTsentence(int inputCtr, const TTstring &line, CScriptHandler *owner, TTroomScript *roomScript, TTnpcScript *npcScript) : - _owner(owner), _field2C(1), _inputCtr(inputCtr), _field34(0), + _owner(owner), _category(1), _inputCtr(inputCtr), _field34(0), _field38(0), _initialLine(line), _nodesP(nullptr), _roomScript(roomScript), _npcScript(npcScript), _field58(0), _field5C(0) { _status = _initialLine.isValid() && _normalizedLine.isValid() ? SS_11: SS_VALID; @@ -77,7 +77,7 @@ void TTsentence::copyFrom(const TTsentence &src) { _field5C = src._field5C; _field34 = src._field34; _field38 = src._field38; - _field2C = src._field2C; + _category = src._category; _nodesP = nullptr; if (src._nodesP) { @@ -109,10 +109,10 @@ int TTsentence::storeVocabHit(TTword *word) { bool TTsentence::fn1(const CString &str, int wordId1, const CString &str1, const CString &str2, const CString &str3, int wordId2, int val1, int val2, const TTconceptNode *node) const { - if (node) + if (!node) node = &_sentenceConcept; - if (!node && !node) + if (!node) return false; if (val1 && !is18(val1, node)) return false; @@ -265,7 +265,9 @@ TTconcept *TTsentence::getFrameEntry(int slotIndex, const TTconceptNode *concept TTconcept *TTsentence::getFrameSlot(int slotIndex, const TTconceptNode *conceptNode) const { TTconcept *newConcept = new TTconcept(); TTconcept *concept = getFrameEntry(slotIndex, conceptNode); - newConcept->copyFrom(concept); + + if (concept) + newConcept->copyFrom(concept); if (!newConcept->isValid()) { delete newConcept; diff --git a/engines/titanic/true_talk/tt_sentence.h b/engines/titanic/true_talk/tt_sentence.h index 7b2c6400c5..cbaef26831 100644 --- a/engines/titanic/true_talk/tt_sentence.h +++ b/engines/titanic/true_talk/tt_sentence.h @@ -67,7 +67,7 @@ public: int _field58; TTroomScript *_roomScript; TTnpcScript *_npcScript; - int _field2C; + int _category; public: TTsentence(int inputCtr, const TTstring &line, CScriptHandler *owner, TTroomScript *roomScript, TTnpcScript *npcScript); @@ -76,7 +76,7 @@ public: void setState(int v) { _field34 = v; } void set38(int v) { _field38 = v; } - bool check2C() const { return _field2C > 1 && _field2C <= 10; } + bool checkCategory() const { return _category > 1 && _category <= 10; } int concept18(TTconceptNode *conceptNode) { return conceptNode ? conceptNode->get18() : 0; } diff --git a/engines/titanic/true_talk/tt_string.cpp b/engines/titanic/true_talk/tt_string.cpp index 198a8c2e80..43910fc823 100644 --- a/engines/titanic/true_talk/tt_string.cpp +++ b/engines/titanic/true_talk/tt_string.cpp @@ -111,7 +111,7 @@ TTstring TTstring::tokenize(const char *delim) { const char *strP = _data->_string.c_str(); const char *splitP = nullptr, *chP; - for (const char *d = delim; d; ++d) { + for (const char *d = delim; *d; ++d) { chP = strchr(strP, *d); if (chP && (splitP == nullptr || chP < splitP)) splitP = chP; @@ -122,7 +122,9 @@ TTstring TTstring::tokenize(const char *delim) { _data->_string = CString(splitP + 1); return result; } else { - return TTstring(); + TTstring result(strP); + _data->_string = CString(); + return result; } } diff --git a/engines/titanic/true_talk/tt_vocab.cpp b/engines/titanic/true_talk/tt_vocab.cpp index 062a6b65c0..6748d6535b 100644 --- a/engines/titanic/true_talk/tt_vocab.cpp +++ b/engines/titanic/true_talk/tt_vocab.cpp @@ -196,10 +196,12 @@ TTword *TTvocab::getPrimeWord(TTstring &str, TTword **srcWord) const { TTword *newWord = nullptr;
TTword *vocabP;
- if (!Common::isDigit(c)) {
+ if (Common::isDigit(c)) {
+ // Number
vocabP = _headP;
newWord = new TTword(str, WC_ABSTRACT, 300);
} else {
+ // Standard word
for (vocabP = _headP; vocabP && !newWord; vocabP = vocabP->_nextP) {
if (_vocabMode == 3 && !strcmp(str.c_str(), vocabP->c_str())) {
newWord = vocabP->copy();
@@ -550,7 +552,6 @@ TTword *TTvocab::getPrefixedWord(TTstring &str) const { word->_text = str;
}
- delete tempStr;
return word;
}
|