diff options
Diffstat (limited to 'engines/lastexpress/game/scenes.cpp')
-rw-r--r-- | engines/lastexpress/game/scenes.cpp | 1195 |
1 files changed, 1195 insertions, 0 deletions
diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp new file mode 100644 index 0000000000..2187d331b5 --- /dev/null +++ b/engines/lastexpress/game/scenes.cpp @@ -0,0 +1,1195 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "lastexpress/game/scenes.h" + +#include "lastexpress/data/scene.h" + +#include "lastexpress/game/action.h" +#include "lastexpress/game/beetle.h" +#include "lastexpress/game/entities.h" +#include "lastexpress/game/inventory.h" +#include "lastexpress/game/logic.h" +#include "lastexpress/game/object.h" +#include "lastexpress/game/savepoint.h" +#include "lastexpress/game/sound.h" +#include "lastexpress/game/state.h" + +#include "lastexpress/graphics.h" +#include "lastexpress/helpers.h" +#include "lastexpress/lastexpress.h" +#include "lastexpress/resource.h" + +namespace LastExpress { + +SceneManager::SceneManager(LastExpressEngine *engine) : _engine(engine), + _flagNoEntity(false), _flagDrawEntities(false), _flagDrawSequences(false), _flagCoordinates(false), + _coords(0, 0, 480, 640), _clockHours(NULL), _clockMinutes(NULL) { + _sceneLoader = new SceneLoader(); +} + +SceneManager::~SceneManager() { + delete _sceneLoader; + + // Clear frames + for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door) + SAFE_DELETE(*door); + + _doors.clear(); + + SAFE_DELETE(_clockHours); + SAFE_DELETE(_clockMinutes); + + // Zero-out passed pointers + _engine = NULL; +} + +////////////////////////////////////////////////////////////////////////// +// Scene cache +////////////////////////////////////////////////////////////////////////// +void SceneManager::loadSceneDataFile(ArchiveIndex archive) { + // Demo only has CD2TRAIN.DAT file + if (_engine->isDemo()) + archive = kArchiveCd2; + + switch(archive) { + case kArchiveCd1: + case kArchiveCd2: + case kArchiveCd3: + if (!_sceneLoader->load(getArchive(Common::String::printf("CD%iTRAIN.DAT", archive)))) + error("SceneManager::loadSceneDataFile: cannot load data file CD%iTRAIN.DAT", archive); + break; + + default: + case kArchiveAll: + error("SceneManager::loadSceneDataFile: Invalid archive index (must be [1-3], was %d", archive); + break; + } +} + +////////////////////////////////////////////////////////////////////////// +// Scene loading +////////////////////////////////////////////////////////////////////////// +void SceneManager::loadScene(SceneIndex index) { + getFlags()->flag_0 = false; + getFlags()->flag_4 = true; + + if (getState()->sceneUseBackup) { + Scene *scene = getScenes()->get(index); + + if (scene->param3 != 255) { + getState()->sceneUseBackup = false; + getState()->sceneBackup2 = kSceneNone; + } + } + + // Save shouldRedraw state and redraw if necessary + bool shouldRedraw = getFlags()->shouldRedraw; + if (shouldRedraw) { + shouldRedraw = false; + // TODO check whether we need to do that here + askForRedraw(); + //redrawScreen(); + } + + // Set the scene + setScene(index); + + // TODO Events method call (might be a low level graphic that we don't need) + + if (getFlags()->isGameRunning && getFlags()->shouldDrawEggOrHourGlass) + getInventory()->drawEgg(); + + getFlags()->shouldRedraw = shouldRedraw; + + getLogic()->updateCursor(); +} + +void SceneManager::loadSceneFromObject(ObjectIndex object, bool alternate) { + switch (object) { + default: + break; + + case kObjectCompartment1: + case kObjectCompartment2: + case kObjectCompartment3: + case kObjectCompartment4: + case kObjectCompartment5: + case kObjectCompartment6: + case kObjectCompartment7: + if (alternate) + loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 1) * 2)); + else + loadSceneFromPosition(kCarGreenSleeping, (Position)(38 - (object - 1) * 2)); + break; + + case kObjectCompartmentA: + case kObjectCompartmentB: + case kObjectCompartmentC: + case kObjectCompartmentD: + case kObjectCompartmentE: + case kObjectCompartmentF: + case kObjectCompartmentG: + if (alternate) + loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 32) * 2)); + else + loadSceneFromPosition(kCarRedSleeping, (Position)(38 - (object - 32) * 2)); + break; + + case kObjectCompartment8: + case kObjectCompartmentH: + loadSceneFromPosition(object == kObjectCompartment8 ? kCarGreenSleeping : kCarRedSleeping, alternate ? 3 : 25); + break; + } +} + +void SceneManager::loadSceneFromItem(InventoryItem item) { + if (item >= kPortraitOriginal) + return; + + // Get the scene index from the item + SceneIndex index = getInventory()->get(item)->scene; + if (!index) + return; + + if (!getState()->sceneUseBackup) { + getState()->sceneUseBackup = true; + getState()->sceneBackup = getState()->scene; + } + + loadScene(index); +} + +void SceneManager::loadSceneFromPosition(CarIndex car, Position position, int param3) { + loadScene(getSceneIndexFromPosition(car, position, param3)); +} + +void SceneManager::loadSceneFromItemPosition(InventoryItem item) { + if (item >= kPortraitOriginal) + return; + + // Check item location + Inventory::InventoryEntry *entry = getInventory()->get(item); + if (!entry->location) + return; + + // Reset location + entry->location = kObjectLocationNone; + + if (item != kItem3 && item != kItem5 && item != kItem7) + return; + + // Set field value + CarIndex car = kCarRestaurant; + if (item == kItem5) car = kCarRedSleeping; + if (item == kItem7) car = kCarGreenSleeping; + + if (!getEntities()->isInsideTrainCar(kEntityPlayer, car)) + return; + + if (getFlags()->flag_0) + return; + + // Get current scene position + Scene *scene = getScenes()->get(getState()->scene); + Position position = scene->position; + + if (getState()->sceneUseBackup) { + Scene *sceneBackup = getScenes()->get(getState()->sceneBackup); + position = sceneBackup->position; + } + + // Checks are different for each item + if ((item == kItem3 && position == 56) + || (item == kItem5 && (position >= 23 && position <= 32)) + || (item == kItem7 && (position == 1 || (position >= 22 && position <= 33)))) { + if (getState()->sceneUseBackup) + getState()->sceneBackup = getSceneIndexFromPosition(car, position); + else + loadSceneFromPosition(car, position); + } +} + +////////////////////////////////////////////////////////////////////////// +// Scene drawing & processing +////////////////////////////////////////////////////////////////////////// +void SceneManager::setScene(SceneIndex index) { + _flagNoEntity = false; + + if (_flagDrawEntities) { + // TODO Setup screen size (0, 80)x(480x480) (is it necessary for our animations?) + drawScene(index); + _flagNoEntity = true; + } else { + _flagDrawEntities = true; + drawScene(index); + _flagDrawEntities = false; + } +} + +void SceneManager::drawScene(SceneIndex index) { + + ////////////////////////////////////////////////////////////////////////// + // Preprocess + preProcessScene(&index); + + ////////////////////////////////////////////////////////////////////////// + // Draw background + debugC(9, kLastExpressDebugScenes, "== Drawing scene: %d ==", index); + + // Update scene + _engine->getGraphicsManager()->draw(get(index), GraphicsManager::kBackgroundC, true); + getState()->scene = index; + + ////////////////////////////////////////////////////////////////////////// + // Update entities + Scene *scene = (getState()->sceneUseBackup ? get(getState()->sceneBackup) : get(index)); + + getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition; + getEntityData(kEntityPlayer)->car = scene->car; + + getFlags()->flag_3 = true; + + if (getFlags()->isGameRunning) { + getSavePoints()->pushAll(kEntityPlayer, kActionDrawScene); + getSavePoints()->process(); + + if (_flagNoEntity) + return; + + getEntities()->updateFields(); + getEntities()->updateSequences(); + getEntities()->updateCallbacks(); + } + + ////////////////////////////////////////////////////////////////////////// + // Show the scene + askForRedraw(); + redrawScreen(); + + //////////////////////////////////////////////////////////// + // Post process scene + postProcessScene(); +} + +void SceneManager::processScene() { + if (!getState()->sceneUseBackup) { + loadScene(getState()->scene); + return; + } + + getState()->sceneUseBackup = false; + + // Select item if needed + InventoryItem item = getInventory()->getFirstExaminableItem(); + if (item && getInventory()->getSelectedItem() == item) + getInventory()->selectItem(item); + + Scene *backup = getScenes()->get(getState()->sceneBackup); + + if (getEntities()->getPosition(backup->car, backup->position)) + loadScene(processIndex(getState()->sceneBackup)); + else + loadScene(getState()->sceneBackup); +} + +LastExpress::SceneIndex SceneManager::processIndex(SceneIndex index) { + Scene *scene = get(index); + CarIndex car = scene->car; + + switch (car) { + default: + break; + + case kCarRedSleeping: + if (checkPosition(index, kCheckPositionLookingAtDoors)) { + Position position = (Position)(scene->position + (checkPosition(kSceneNone, kCheckPositionLookingUp) ? -1 : 1)); + + if (position == 4) + position = 3; + + if (position == 24) + position = 25; + + if (getEntities()->getPosition(car, position)) + return index; + else + return getSceneIndexFromPosition(car, position); + } else { + switch (scene->position) { + default: + break; + + case 41: + case 51: + if (!getEntities()->getPosition(car, 39)) + return getSceneIndexFromPosition(car, 39); + // Fallback to next case + + case 42: + case 52: + if (!getEntities()->getPosition(car, 14)) + return getSceneIndexFromPosition(car, 14); + // Fallback to next case + + case 43: + case 53: + if (!getEntities()->getPosition(car, 35)) + return getSceneIndexFromPosition(car, 35); + // Fallback to next case + + case 44: + case 54: + if (!getEntities()->getPosition(car, 10)) + return getSceneIndexFromPosition(car, 10); + // Fallback to next case + + case 45: + case 55: + if (!getEntities()->getPosition(car, 32)) + return getSceneIndexFromPosition(car, 32); + // Fallback to next case + + case 46: + case 56: + if (!getEntities()->getPosition(car, 7)) + return getSceneIndexFromPosition(car, 7); + // Fallback to next case + + case 47: + case 57: + if (!getEntities()->getPosition(car, 27)) + return getSceneIndexFromPosition(car, 27); + // Fallback to next case + + case 48: + case 58: + if (!getEntities()->getPosition(car, 2)) + return getSceneIndexFromPosition(car, 2); + break; + } + } + break; + + case kCarRestaurant: + switch (scene->position) { + default: + break; + + case 52: + case 53: + case 54: + if (!getEntities()->getPosition(car, 51)) + return getSceneIndexFromPosition(car, 51); + // Fallback to next case + + case 50: + case 56: + case 57: + case 58: + if (!getEntities()->getPosition(car, 55)) + return getSceneIndexFromPosition(car, 55); + // Fallback to next case + + case 59: + if (!getEntities()->getPosition(car, 60)) + return getSceneIndexFromPosition(car, 60); + // Fallback to next case + + case 60: + if (!getEntities()->getPosition(car, 59)) + return getSceneIndexFromPosition(car, 59); + // Fallback to next case + + case 62: + case 63: + case 64: + if (!getEntities()->getPosition(car, 61)) + return getSceneIndexFromPosition(car, 61); + // Fallback to next case + + case 66: + case 67: + case 68: + if (!getEntities()->getPosition(car, 65)) + return getSceneIndexFromPosition(car, 65); + // Fallback to next case + + case 69: + case 71: + if (!getEntities()->getPosition(car, 70)) + return getSceneIndexFromPosition(car, 70); + break; + } + break; + } + + return index; +} + +////////////////////////////////////////////////////////////////////////// +// Checks +////////////////////////////////////////////////////////////////////////// +bool SceneManager::checkPosition(SceneIndex index, CheckPositionType type) const { + Scene *scene = getScenes()->get((index ? index : getState()->scene)); + + CarIndex car = (CarIndex)scene->car; + Position position = scene->position; + + bool isInSleepingCar = (car == kCarGreenSleeping || car == kCarRedSleeping); + + switch (type) { + default: + error("SceneManager::checkPosition: Invalid position type: %d", type); + + case kCheckPositionLookingUp: + return isInSleepingCar && (position >= 1 && position <= 19); + + case kCheckPositionLookingDown: + return isInSleepingCar && (position >= 21 && position <= 40); + + case kCheckPositionLookingAtDoors: + return isInSleepingCar && ((position >= 2 && position <= 17) || (position >= 23 && position <= 39)); + + case kCheckPositionLookingAtClock: + return car == kCarRestaurant && position == 81; + } +} + +bool SceneManager::checkCurrentPosition(bool doCheckOtherCars) const { + Scene *scene = getScenes()->get(getState()->scene); + + Position position = scene->position; + CarIndex car = (CarIndex)scene->car; + + if (!doCheckOtherCars) + return (car == kCarGreenSleeping || car == kCarRedSleeping) + && ((position >= 41 && position <= 48) || (position >= 51 && position <= 58)); + + if (position == 99) + return true; + + switch (car){ + default: + break; + + case kCarGreenSleeping: + case kCarRedSleeping: + case kCarLocomotive: + if ((position >= 1 && position <= 18) || (position >= 22 && position <= 40)) + return true; + break; + + case kCarRestaurant: + if (position >= 73 && position <= 80) + return true; + + if (position == 10 || position == 11) + return true; + + break; + + case kCarBaggage: + switch (position) { + default: + break; + + case 10: + case 11: + case 80: + case 81: + case 82: + case 83: + case 84: + case 90: + case 91: + return true; + } + break; + + case kCarCoalTender: + if (position == 2 || position == 10 || position == 11) + return true; + break; + } + + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Train +////////////////////////////////////////////////////////////////////////// +void SceneManager::updateDoorsAndClock() { + // Clear all sequences from the list + for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door) { + removeFromQueue(*door); + setCoordinates(*door); + SAFE_DELETE(*door); + } + + // Cleanup doors sequences + _doors.clear(); + + if (_clockHours) { + removeFromQueue(_clockHours); + setCoordinates(_clockHours); + SAFE_DELETE(_clockHours); + } + + if (_clockMinutes) { + removeFromQueue(_clockMinutes); + setCoordinates(_clockMinutes); + SAFE_DELETE(_clockMinutes); + } + + // Queue doors sequences for display + if (checkPosition(kSceneNone, kCheckPositionLookingAtDoors)) { + + ObjectIndex firstIndex = kObjectNone; + + // Init objectIndex (or exit if not in one of the two compartment cars + if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping) + firstIndex = kObjectCompartment1; + else if (getEntityData(kEntityPlayer)->car == kCarRedSleeping) + firstIndex = kObjectCompartmentA; + else + return; + + // Iterate over each door + for (ObjectIndex index = firstIndex; index < (ObjectIndex)(firstIndex + 8); index = (ObjectIndex)(index + 1)) { + + // Doors is not open, nothing to do + if (getObjects()->get(index).location != kObjectLocation2) + continue; + + // Load door sequence + Scene *scene = getScenes()->get(getState()->scene); + Common::String name = Common::String::printf("633X%c-%02d.seq", (index - firstIndex) + 65, scene->position); + Sequence *sequence = loadSequence1(name, 255); + + // If the sequence doesn't exists, skip + if (!sequence || !sequence->isLoaded()) + continue; + + // Adjust frame data and store in frame list + SequenceFrame *frame = new SequenceFrame(sequence, 0, true); + frame->getInfo()->location = (checkPosition(kSceneNone, kCheckPositionLookingUp) ? (firstIndex - index) - 1 : (index - firstIndex) - 8); + + _doors.push_back(frame); + + // Add frame to list + addToQueue(frame); + } + } + + // Queue clock sequences for display + if (checkPosition(kSceneNone, kCheckPositionLookingAtClock)) { + // Only used in scene 349 to show the hands on the clock + + Sequence *sequenceHours = loadSequence1("SCLKH-81.seq", 255); + Sequence *sequenceMinutes = loadSequence1("SCLKM-81.seq", 255); + + // Compute hours and minutes indexes + uint16 hoursIndex = getState()->time % 1296000 % 54000 / 900; + + uint hours = (getState()->time % 1296000) / 54000; + if (hours >= 12) + hours -= 12; + + uint16 minutesIndex = (uint16)(5 * hours + hoursIndex / 12); + + // Adjust z-order and store sequences to list + _clockHours = new SequenceFrame(sequenceHours, hoursIndex, true); + _clockHours->getInfo()->location = 65534; + + _clockMinutes = new SequenceFrame(sequenceMinutes, minutesIndex, true); + _clockMinutes->getInfo()->location = 65535; + + addToQueue(_clockHours); + addToQueue(_clockMinutes); + } +} + +void SceneManager::resetDoorsAndClock() { + for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door) + SAFE_DELETE(*door); + + _doors.clear(); + + SAFE_DELETE(_clockHours); + SAFE_DELETE(_clockMinutes); + + // Remove the beetle sequences too if needed + getBeetle()->unload(); +} + +////////////////////////////////////////////////////////////////////////// +// Sequence list +////////////////////////////////////////////////////////////////////////// +void SceneManager::drawFrames(bool refreshScreen) { + if (!_flagDrawSequences) + return; + + // TODO handle flag coordinates + + clearBg(GraphicsManager::kBackgroundOverlay); + + for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) + _engine->getGraphicsManager()->draw(*i, GraphicsManager::kBackgroundOverlay); + + if (refreshScreen) { + askForRedraw(); + //redrawScreen(); + + _flagDrawSequences = false; + } +} + +void SceneManager::addToQueue(SequenceFrame * const frame) { + if (!frame) + return; + + // First check that the frame is not already in the queue + for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) { + if (frame->equal(*i)) + return; + } + + debugC(8, kLastExpressDebugGraphics, "Adding frame: %s / %d", frame->getName().c_str(), frame->getFrame()); + + // Set flag + _flagDrawSequences = true; + + // Queue empty: just insert the frame + if (_queue.empty()) { + _queue.push_back(frame); + return; + } + + // Frame is closer: insert in first place + if (frame->getInfo()->location > _queue.front()->getInfo()->location) { + _queue.push_front(frame); + return; + } + + // Insert the frame in the queue based on location + for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) { + if (frame->getInfo()->location > (*i)->getInfo()->location) { + _queue.insert(i, frame); + return; + } + } + + // We are the last frame in location order, insert at the back of the queue + _queue.push_back(frame); +} + +void SceneManager::removeFromQueue(SequenceFrame *frame) { + if (!frame) + return; + + debugC(8, kLastExpressDebugGraphics, "Removing frame: %s / %d", frame->getName().c_str(), frame->getFrame()); + + // Check that the frame is in the queue and remove it + for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) { + if (frame->equal(*i)) { + _queue.erase(i); + _flagDrawSequences = true; + break; + } + } +} + +void SceneManager::removeAndRedraw(SequenceFrame **frame, bool doRedraw) { + if (!frame) + return; + + removeFromQueue(*frame); + + if (doRedraw) + drawFrames(true); + + SAFE_DELETE(*frame); +} + +void SceneManager::resetQueue() { + _flagDrawSequences = true; + + // The original engine only deletes decompressed data, not the "sequences" since they are just pointers to a memory pool + _queue.clear(); +} + +void SceneManager::setCoordinates(SequenceFrame *frame) { + + if (!frame || frame->getInfo()->subType == 3) + return; + + _flagCoordinates = true; + + if (_coords.right > (int)frame->getInfo()->xPos1) + _coords.right = (int16)frame->getInfo()->xPos1; + + if (_coords.bottom > (int)frame->getInfo()->yPos1) + _coords.bottom = (int16)frame->getInfo()->yPos1; + + if (_coords.left < (int)frame->getInfo()->xPos2) + _coords.left = (int16)frame->getInfo()->xPos2; + + if (_coords.top < (int)frame->getInfo()->yPos2) + _coords.top = (int16)frame->getInfo()->yPos2; +} + +void SceneManager::resetCoordinates() { + _coords.top = 0; + _coords.left = 0; + _coords.bottom = 480; + _coords.right = 640; + + _flagCoordinates = false; +} + +////////////////////////////////////////////////////////////////////////// +// Helpers +////////////////////////////////////////////////////////////////////////// +SceneIndex SceneManager::getSceneIndexFromPosition(CarIndex car, Position position, int param3) { + // Probably can't happen (can we be called during cd-swap?) + if (_sceneLoader->count() <= 1) + return getState()->scene; + + SceneIndex index = kSceneMenu; + + Scene *firstScene = getScenes()->get(index); + + while (firstScene->car != car + || firstScene->position != position + || ((param3 != -1 || firstScene->param3) && firstScene->param3 != param3 && firstScene->type != Scene::kTypeItem3)) { + + // Increment index and look at the next scene + index = (SceneIndex)(index + 1); + + if (index >= _sceneLoader->count()) + return getState()->scene; + + // Load the next scene + firstScene = getScenes()->get(index); + } + + // Process index if necessary + Scene *scene = getScenes()->get(index); + if (getEntities()->getPosition(scene->car, scene->position)) + return processIndex(index); + + return index; +} + +////////////////////////////////////////////////////////////////////////// +// Scene processing +////////////////////////////////////////////////////////////////////////// + +// Process hotspots +// - if it returns kSceneInvalid, the hotspot scene has not been modified +// - if it returns kSceneNone, it has been modified +// +// Note: we use the original hotspot scene to pre-process again +#define PROCESS_HOTSPOT_SCENE(hotspot, index) { \ + SceneIndex processedScene = getAction()->processHotspot(*hotspot); \ + SceneIndex testScene = (processedScene == kSceneInvalid) ? (hotspot)->scene : processedScene; \ + if (testScene) { \ + *index = (hotspot)->scene; \ + preProcessScene(index); \ + } \ +} + +void SceneManager::preProcessScene(SceneIndex *index) { + + // Check index validity + if (*index == 0 || *index > 2500) + *index = kSceneMenu; + + Scene *scene = getScenes()->get(*index); + + switch (scene->type) { + case Scene::kTypeObject: { + ObjectIndex object = (ObjectIndex)scene->param1; + + if (object >= kObjectMax) + break; + + if (getObjects()->get(object).location == kObjectLocationNone) + break; + + for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) { + if (getObjects()->get(object).location != (*it)->location) + continue; + + PROCESS_HOTSPOT_SCENE(*it, index); + break; + } + break; + } + + case Scene::kTypeItem: { + InventoryItem item = (InventoryItem)scene->param1; + + if (item >= kPortraitOriginal) + break; + + if (getInventory()->get(item)->location == kObjectLocationNone) + break; + + for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) { + if (getInventory()->get(item)->location != (*it)->location) + continue; + + PROCESS_HOTSPOT_SCENE(*it, index); + break; + } + break; + } + + case Scene::kTypeItem2: { + InventoryItem item1 = (InventoryItem)scene->param1; + InventoryItem item2 = (InventoryItem)scene->param2; + + if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal) + break; + + int location = kObjectLocationNone; + + if (getInventory()->get(item1)->location != kObjectLocationNone) + location = kObjectLocation1; + + if (getInventory()->get(item2)->location != kObjectLocationNone) + location |= kObjectLocation2; + + if (!location) + break; + + for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) { + if (location != (*it)->location) + continue; + + if (getInventory()->get(item1)->location != (*it)->param1) + continue; + + if (getInventory()->get(item2)->location != (*it)->param2) + continue; + + PROCESS_HOTSPOT_SCENE(*it, index); + break; + } + break; + } + + case Scene::kTypeObjectItem: { + ObjectIndex object = (ObjectIndex)scene->param1; + InventoryItem item = (InventoryItem)scene->param2; + + if (object >= kObjectMax) + break; + + if (item >= kPortraitOriginal) + break; + + int location = kObjectLocationNone; + + if (getObjects()->get(object).location == kObjectLocation2) + location = kObjectLocation1; + + if (getInventory()->get(item)->location != kObjectLocationNone) + location |= kObjectLocation2; + + if (!location) + break; + + for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) { + if (location != (*it)->location) + continue; + + if (getObjects()->get(object).location != (*it)->param1) + continue; + + if (getInventory()->get(item)->location != (*it)->param2) + continue; + + PROCESS_HOTSPOT_SCENE(*it, index); + break; + } + break; + } + + case Scene::kTypeItem3: { + InventoryItem item1 = (InventoryItem)scene->param1; + InventoryItem item2 = (InventoryItem)scene->param2; + InventoryItem item3 = (InventoryItem)scene->param3; + + if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal || item3 >= kPortraitOriginal) + break; + + int location = kObjectLocationNone; + + if (getInventory()->get(item1)->location != kObjectLocationNone) + location = kObjectLocation1; + + if (getInventory()->get(item2)->location != kObjectLocationNone) + location |= kObjectLocation2; + + if (getInventory()->get(item3)->location != kObjectLocationNone) + location |= kObjectLocation4; + + if (!location) + break; + + for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) { + if (location != (*it)->location) + continue; + + if (getInventory()->get(item1)->location != (*it)->param1) + continue; + + if (getInventory()->get(item2)->location != (*it)->param2) + continue; + + if (getInventory()->get(item3)->location != (*it)->param3) + continue; + + PROCESS_HOTSPOT_SCENE(*it, index); + break; + } + break; + } + + case Scene::kTypeObjectLocation2: { + ObjectIndex object = (ObjectIndex)scene->param1; + + if (object >= kObjectMax) + break; + + bool found = false; + for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) { + if (getObjects()->get(object).location2 != (*it)->location) + continue; + + PROCESS_HOTSPOT_SCENE(*it, index); + found = true; + break; + } + + // If we haven't found a proper hotspot, use the first hotspot from the current scene + if (!found) { + Scene *sceneHotspot = getScenes()->get(*index); + SceneHotspot *hotspot = sceneHotspot->getHotspot(); + + PROCESS_HOTSPOT_SCENE(hotspot, index); + } + break; + } + + case Scene::kTypeCompartments: + case Scene::kTypeCompartmentsItem: + if (scene->param1 >= 16) + break; + + if (getEntities()->getCompartments(scene->param1) || getEntities()->getCompartments1(scene->param1)) { + + Scene *currentScene = getScenes()->get(getState()->scene); + + if ((checkPosition(getState()->scene, kCheckPositionLookingUp) && checkPosition(*index, kCheckPositionLookingUp) && currentScene->entityPosition < scene->entityPosition) + || (checkPosition(getState()->scene, kCheckPositionLookingDown) && checkPosition(*index, kCheckPositionLookingDown) && currentScene->entityPosition > scene->entityPosition)) { + + if (State::getPowerOfTwo((uint32)getEntities()->getCompartments(scene->param1)) != 30 + && State::getPowerOfTwo((uint32)getEntities()->getCompartments1(scene->param1)) != 30 ) + getSound()->playSound(kEntityPlayer, "CAT1126A"); + + *index = scene->getHotspot()->scene; + } else { + *index = scene->getHotspot(1)->scene; + } + + preProcessScene(index); + } else { + // Stop processing here for kTypeCompartments + if (scene->type == Scene::kTypeCompartments) + break; + + InventoryItem item = (InventoryItem)scene->param2; + if (item >= kPortraitOriginal) + break; + + if (getInventory()->get(item)->location == kObjectLocationNone) + break; + + for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) { + if (getInventory()->get(item)->location != (*it)->location) + continue; + + PROCESS_HOTSPOT_SCENE(*it, index); + break; + } + } + break; + + default: + break; + } + + // Sound processing + Scene *newScene = getScenes()->get(*index); + if (getSound()->isBuffered(kEntityTables4)) { + if (newScene->type != Scene::kTypeReadText || newScene->param1) + getSound()->processEntry(kEntityTables4); + } + + // Cleanup beetle sequences + if (getBeetle()->isLoaded()) { + if (newScene->type != Scene::kTypeLoadBeetleSequences) + getBeetle()->unload(); + } +} + +void SceneManager::postProcessScene() { + + Scene *scene = getScenes()->get(getState()->scene); + + switch (scene->type) { + case Scene::kTypeList: { + + // Adjust time + getState()->time += (scene->param1 + 10) * getState()->timeDelta; + getState()->timeTicks += (scene->param1 + 10); + + // Wait for a number of frames unless right mouse is clicked + uint32 nextFrameCount = getFrameCount() + 4 * scene->param1; + if (!getFlags()->mouseRightClick) { + while (nextFrameCount > getFrameCount()) { + _engine->pollEvents(); + + if (getFlags()->mouseRightClick) + break; + + getSound()->updateQueue(); + getSound()->updateSubtitles(); + } + } + + // Process hotspots and load scenes in the list + SceneHotspot *hotspot = scene->getHotspot(); + SceneIndex processedScene = getAction()->processHotspot(*hotspot); + SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene; + + if (getFlags()->mouseRightClick) { + + while (getScenes()->get(testScene)->type == Scene::kTypeList) { + hotspot = getScenes()->get(testScene)->getHotspot(); + processedScene = getAction()->processHotspot(*hotspot); + testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene; + } + } + + // If several entities are there, choose one to sound "Excuse me" + EntityPosition entityPosition = getEntityData(kEntityPlayer)->entityPosition; + if (getEntityData(kEntityPlayer)->car == kCar9 && (entityPosition == kPosition_4 || entityPosition == kPosition_3)) { + EntityIndex entities[39]; + + // Init entities + entities[0] = kEntityPlayer; + + uint progress = 0; + + for (uint i = 1; i < 40 /* number of entities */; i++) { + CarIndex car = getEntityData((EntityIndex)i)->car; + EntityPosition position = getEntityData((EntityIndex)i)->entityPosition; + + if (entityPosition == kPosition_4) { + if ((car == kCarRedSleeping && position > kPosition_9270) || (car == kCarRestaurant && position < kPosition_1540)) + entities[progress++] = (EntityIndex)i; + } else { + if ((car == kCarGreenSleeping && position > kPosition_9270) || (car == kCarRedSleeping && position < kPosition_850)) + entities[progress++] = (EntityIndex)i; + } + } + + if (progress) + getSound()->excuseMe((progress == 1) ? entities[0] : entities[rnd(progress)], kEntityPlayer, SoundManager::kFlagDefault); + } + + if (hotspot->scene) + setScene(hotspot->scene); + break; + } + + case Scene::kTypeSavePointChapter: + if (getProgress().field_18 == 2) + getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionEndChapter); + break; + + case Scene::kTypeLoadBeetleSequences: + if ((getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3) + && getInventory()->get(kItemBeetle)->location == kObjectLocation3) { + if (!getBeetle()->isLoaded()) + getBeetle()->load(); + } + break; + + case Scene::kTypeGameOver: + if (getState()->time >= kTimeCityGalanta || getProgress().field_18 == 4) + break; + + getSound()->processEntry(SoundManager::kSoundType7); + getSound()->playSound(kEntityTrain, "LIB050", SoundManager::kFlagDefault); + + switch (getProgress().chapter) { + default: + getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverPolice2, true); + break; + + case kChapter1: + getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm, true); + break; + + case kChapter4: + getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm2, true); + break; + } + break; + + case Scene::kTypeReadText: + getSound()->readText(scene->param1); + break; + + case Scene::kType133: + if (getFlags()->flag_0) { + getFlags()->flag_0 = false; + getFlags()->shouldRedraw = true; + getLogic()->updateCursor(); + } + break; + + default: + break; + } +} + +} // End of namespace LastExpress |