/* 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 "lastexpress/game/action.h" #include "lastexpress/data/animation.h" #include "lastexpress/data/cursor.h" #include "lastexpress/data/snd.h" #include "lastexpress/data/scene.h" #include "lastexpress/entities/abbot.h" #include "lastexpress/entities/anna.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/savegame.h" #include "lastexpress/game/savepoint.h" #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" namespace LastExpress { static const int _animationListSize = 273; // List of animations static const struct { const char *filename; uint16 time; } _animationList[_animationListSize] = { {"", 0}, {"1002", 255}, {"1002D", 255}, {"1003", 0}, {"1005", 195}, {"1006", 750}, // 5 {"1006A", 750}, {"1008", 765}, {"1008N", 765}, {"1008A", 750}, {"1008AN", 750}, // 10 {"1009", 0}, {"1011", 1005}, {"1011A", 780}, {"1012", 300}, {"1013", 285}, {"1017", 870}, // 15 {"1017A", 0}, // Not in the data files? {"1019", 120}, {"1019D", 120}, {"1020", 120}, // 20 {"1022", 525}, {"1022A", 180}, {"1022AD", 210}, {"1022B", 210}, {"1022C", 210}, // 25 {"1023", 135}, {"1025", 945}, {"1028", 300}, {"1030", 390}, {"1031", 375}, // 30 {"1032", 1050}, {"1033", 945}, {"1034", 495}, {"1035", 1230}, {"1037", 1425}, // 35 {"1038", 195}, {"1038A", 405}, {"1039", 600}, {"1040", 945}, {"1041", 510}, // 40 {"1042", 540}, {"1043", 855}, {"1044", 645}, {"1046", 0}, {"1047", 0}, // 45 {"1047A", 0}, {"1059", 1005}, {"1060", 255}, {"1063", 0}, {"1101", 255}, // 50 {"1102", 1320}, {"1103", 210}, {"1104", 120}, {"1105", 1350}, {"1106", 315}, // 55 {"1106A", 315}, {"1106D", 315}, {"1107", 1}, {"1107A", 660}, {"1108", 300}, // 60 {"1109", 1305}, {"1110", 300}, {"1112", 0}, {"1115", 0}, {"1115A", 0}, // 65 {"1115B", 0}, {"1115C", 0}, {"1115D", 0}, {"1115E", 0}, {"1115F", 0}, // 70 {"1115G", 0}, {"1115H", 0}, {"1116", 0}, {"1117", 0}, {"1118", 105}, // 75 {"1202", 510}, {"1202A", 510}, {"1203", 720}, {"1204", 120}, {"1205", 465}, // 80 {"1206", 690}, {"1206A", 450}, {"1208", 465}, {"1210", 1020}, {"1211", 600}, // 85 {"1212", 435}, {"1213", 525}, {"1213A", 150}, {"1215", 390}, {"1216", 0}, // 90 {"1219", 240}, {"1222", 1095}, {"1223", 0}, {"1224", 720}, {"1225", 1005}, // 95 {"1227", 840}, {"1227A", 840}, {"1303", 450}, {"1303N", 450}, {"1304", 450}, // 100 {"1304N", 450}, {"1305", 630}, {"1309", 0}, {"1311", 1710}, {"1312", 240}, // 105 {"1312D", 240}, {"1313", 930}, {"1315", 1035}, {"1315A", 1035}, {"1401", 540}, // 110 {"1402", 150}, {"1402B", 150}, {"1403", 90}, {"1404", 885}, {"1404A", 0}, // 115 {"1405", 135}, {"1406", 1665}, {"1501", 285}, {"1501A", 285}, {"1502", 165}, // 120 {"1502A", 165}, {"1502D", 165}, {"1503", 0}, {"1504", 0}, {"1505", 0}, // 125 {"1505A", 0}, {"1506", 300}, {"1506A", 180}, {"1508", 0}, {"1509", 450}, // 130 {"1509S", 450}, {"1509A", 450}, {"1509AS", 450}, {"1509N", 450}, {"1509SN", 450}, // 135 {"1509AN", 450}, {"1509BN", 450}, {"1511", 150}, {"1511A", 150}, {"1511B", 90}, // 140 {"1511BA", 90}, {"1511C", 135}, {"1511D", 105}, {"1930", 0}, {"1511E", 150}, // 145 {"1512", 165}, {"1513", 180}, {"1517", 0}, {"1517A", 165}, {"1518", 165}, // 150 {"1518A", 165}, {"1518B", 165}, {"1591", 450}, {"1592", 450}, {"1593", 450}, // 155 {"1594", 450}, {"1595", 450}, {"1596", 450}, {"1601", 0}, {"1603", 0}, // 160 {"1606B", 315}, {"1607A", 0}, {"1610", 0}, {"1611", 0}, {"1612", 0}, // 165 {"1615", 0}, {"1619", 0}, {"1620", 120}, {"1621", 105}, {"1622", 105}, // 170 {"1629", 450}, {"1630", 450}, {"1631", 525}, {"1632", 0}, {"1633", 615}, // 175 {"1634", 180}, {"1702", 180}, {"1702DD", 180}, {"1702NU", 180}, {"1702ND", 180}, // 180 {"1704", 300}, {"1704D", 300}, {"1705", 195}, {"1705D", 195}, {"1706", 195}, // 185 {"1706DD", 195}, {"1706ND", 195}, {"1706NU", 195}, {"1901", 135}, {"1902", 1410}, // 190 {"1903", 0}, {"1904", 1920}, {"1908", 600}, {"1908A", 195}, {"1908B", 105}, // 195 {"1908C", 165}, {"1908CD", 0}, {"1909A", 150}, {"1909B", 150}, {"1909C", 150}, // 200 {"1910A", 180}, {"1910B", 180}, {"1910C", 180}, {"1911A", 90}, {"1911B", 90}, // 205 {"1911C", 90}, {"1912", 0}, {"1913", 0}, {"1917", 0}, {"1918", 390}, // 210 {"1919", 360}, {"1919A", 105}, {"1920", 75}, {"1922", 75}, {"1923", 150}, // 215 {"8001", 120}, {"8001A", 120}, {"8002", 120}, {"8002A", 120}, {"8002B", 120}, // 220 {"8003", 105}, {"8003A", 105}, {"8004", 105}, {"8004A", 105}, {"8005", 270}, // 225 {"8005B", 270}, {"8010", 270}, {"8013", 120}, {"8013A", 120}, {"8014", 165}, // 230 {"8014A", 165}, {"8014R", 165}, {"8014AR", 165}, {"8015", 150}, {"8015A", 150}, // 235 {"8015R", 150}, {"8015AR", 150}, {"8017", 120}, {"8017A", 120}, {"8017R", 120}, // 240 {"8017AR", 120}, {"8017N", 90}, {"8023", 135}, {"8023A", 135}, {"8023M", 135}, // 245 {"8024", 150}, {"8024A", 180}, {"8024M", 180}, {"8025", 150}, {"8025A", 150}, // 250 {"8025M", 150}, {"8027", 75}, {"8028", 75}, {"8029", 120}, {"8029A", 120}, // 255 {"8031", 375}, {"8032", 0}, {"8032A", 0}, {"8033", 105}, {"8035", 195}, // 260 {"8035A", 120}, {"8035B", 180}, {"8035C", 135}, {"8036", 105}, {"8037", 195}, // 265 {"8037A", 195}, {"8040", 240}, {"8040A", 240}, {"8041", 195}, {"8041A", 195}, // 270 {"8042", 600}, {"8042A", 600} }; template class Functor1MemConst : public Common::Functor1 { public: typedef Res (T::*FuncType)(Arg) const; Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {} bool isValid() const { return _func != 0 && _t != 0; } Res operator()(Arg v1) const { return (_t->*_func)(v1); } private: mutable T *_t; const FuncType _func; }; Action::Action(LastExpressEngine *engine) : _engine(engine) { ADD_ACTION(dummy); ADD_ACTION(inventory); ADD_ACTION(savePoint); ADD_ACTION(playSound); ADD_ACTION(playMusic); ADD_ACTION(knock); ADD_ACTION(compartment); ADD_ACTION(playSounds); ADD_ACTION(playAnimation); ADD_ACTION(openCloseObject); ADD_ACTION(updateObjetLocation2); ADD_ACTION(setItemLocation); ADD_ACTION(knockNoSound); ADD_ACTION(pickItem); ADD_ACTION(dropItem); ADD_ACTION(dummy); ADD_ACTION(enterCompartment); ADD_ACTION(dummy); ADD_ACTION(getOutsideTrain); ADD_ACTION(slip); ADD_ACTION(getInsideTrain); ADD_ACTION(climbUpTrain); ADD_ACTION(climbDownTrain); ADD_ACTION(jumpUpDownTrain); ADD_ACTION(unbound); ADD_ACTION(25); ADD_ACTION(26); ADD_ACTION(27); ADD_ACTION(concertSitCough); ADD_ACTION(29); ADD_ACTION(catchBeetle); ADD_ACTION(exitCompartment); ADD_ACTION(32); ADD_ACTION(useWhistle); ADD_ACTION(openMatchBox); ADD_ACTION(openBed); ADD_ACTION(dummy); ADD_ACTION(dialog); ADD_ACTION(eggBox); ADD_ACTION(39); ADD_ACTION(bed); ADD_ACTION(playMusicChapter); ADD_ACTION(playMusicChapterSetupTrain); ADD_ACTION(switchChapter); ADD_ACTION(44); } Action::~Action() { for (uint i = 0; i < _actions.size(); i++) SAFE_DELETE(_actions[i]); _actions.clear(); // Zero-out passed pointers _engine = NULL; } ////////////////////////////////////////////////////////////////////////// // Processing hotspot ////////////////////////////////////////////////////////////////////////// SceneIndex Action::processHotspot(const SceneHotspot &hotspot) { if (!hotspot.action || hotspot.action >= (int)_actions.size()) return kSceneInvalid; return (*_actions[hotspot.action])(hotspot); } ////////////////////////////////////////////////////////////////////////// // Actions ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Action 0 IMPLEMENT_ACTION(dummy) error("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); } ////////////////////////////////////////////////////////////////////////// // Action 1 IMPLEMENT_ACTION(inventory) if (!getState()->sceneUseBackup) return kSceneInvalid; SceneIndex index = kSceneNone; if (getState()->sceneBackup2) { index = getState()->sceneBackup2; getState()->sceneBackup2 = kSceneNone; } else { getState()->sceneUseBackup = false; index = getState()->sceneBackup; Scene *backup = getScenes()->get(getState()->sceneBackup); if (getEntities()->getPosition(backup->car, backup->position)) index = getScenes()->processIndex(getState()->sceneBackup); } getScenes()->loadScene(index); if (!getInventory()->getSelectedItem()) return kSceneInvalid; if (!getInventory()->getSelectedEntry()->isSelectable || (!getState()->sceneBackup2 && getInventory()->getFirstExaminableItem())) getInventory()->selectItem(getInventory()->getFirstExaminableItem()); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 2 IMPLEMENT_ACTION(savePoint) getSavePoints()->push(kEntityPlayer, (EntityIndex)hotspot.param1, (ActionIndex)hotspot.param2); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 3 IMPLEMENT_ACTION(playSound) // Check that the file is not already buffered if (hotspot.param2 || !getSoundQueue()->isBuffered(Common::String::format("LIB%03d", hotspot.param1), true)) getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 4 IMPLEMENT_ACTION(playMusic) // Check that the file is not already buffered Common::String filename = Common::String::format("MUS%03d", hotspot.param1); if (!getSoundQueue()->isBuffered(filename) && (hotspot.param1 != 50 || getProgress().chapter == kChapter5)) getSound()->playSound(kEntityPlayer, filename, kFlagDefault, hotspot.param2); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 5 IMPLEMENT_ACTION(knock) ObjectIndex object = (ObjectIndex)hotspot.param1; if (object >= kObjectMax) return kSceneInvalid; if (getObjects()->get(object).entity) { getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object); } else { if (!getSoundQueue()->isBuffered("LIB012", true)) getSound()->playSoundEvent(kEntityPlayer, 12); } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 6 IMPLEMENT_ACTION(compartment) ObjectIndex compartment = (ObjectIndex)hotspot.param1; if (compartment >= kObjectMax) return kSceneInvalid; if (getObjects()->get(compartment).entity) { getSavePoints()->push(kEntityPlayer, getObjects()->get(compartment).entity, kActionOpenDoor, compartment); // Stop processing further return kSceneNone; } if (handleOtherCompartment(compartment, true, true)) { // Stop processing further return kSceneNone; } ObjectLocation location = getObjects()->get(compartment).location; if (location == kObjectLocation1 || location == kObjectLocation3 || getEntities()->checkFields2(compartment)) { if (location != kObjectLocation1 || getEntities()->checkFields2(compartment) || (getInventory()->getSelectedItem() != kItemKey && (compartment != kObjectCompartment1 || !getInventory()->hasItem(kItemKey) || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))) { if (!getSoundQueue()->isBuffered("LIB13")) getSound()->playSoundEvent(kEntityPlayer, 13); // Stop processing further return kSceneNone; } getSound()->playSoundEvent(kEntityPlayer, 32); if ((compartment >= kObjectCompartment1 && compartment <= kObjectCompartment3) || (compartment >= kObjectCompartmentA && compartment <= kObjectCompartmentF)) getObjects()->update(compartment, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getSound()->playSoundEvent(kEntityPlayer, 15, 22); getInventory()->unselectItem(); return kSceneInvalid; } if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->getSelectedItem() != kItemKey) { if (compartment == kObjectCageMax) { getSound()->playSoundEvent(kEntityPlayer, 26); } else { getSound()->playSoundEvent(kEntityPlayer, 14); getSound()->playSoundEvent(kEntityPlayer, 15, 22); } return kSceneInvalid; } getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getSound()->playSoundEvent(kEntityPlayer, 16); getInventory()->unselectItem(); // Stop processing further return kSceneNone; } ////////////////////////////////////////////////////////////////////////// // Action 7 IMPLEMENT_ACTION(playSounds) getSound()->playSoundEvent(kEntityPlayer, hotspot.param1); getSound()->playSoundEvent(kEntityPlayer, hotspot.param3, hotspot.param2); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 8 IMPLEMENT_ACTION(playAnimation) if (getEvent(hotspot.param1)) return kSceneInvalid; playAnimation((EventIndex)hotspot.param1); if (!hotspot.scene) getScenes()->processScene(); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 9 IMPLEMENT_ACTION(openCloseObject) ObjectIndex object = (ObjectIndex)hotspot.param1; ObjectLocation location = (ObjectLocation)hotspot.param2; if (object >= kObjectMax) return kSceneInvalid; getObjects()->update(object, getObjects()->get(object).entity, location, kCursorKeepValue, kCursorKeepValue); bool isNotWindow = ((object <= kObjectCompartment8 || object >= kObjectHandleBathroom) && (object <= kObjectCompartmentH || object >= kObject48)); switch (location) { default: break; case kObjectLocation1: if (isNotWindow) getSound()->playSoundEvent(kEntityPlayer, 24); else getSound()->playSoundEvent(kEntityPlayer, 21); break; case kObjectLocation2: if (isNotWindow) getSound()->playSoundEvent(kEntityPlayer, 36); else getSound()->playSoundEvent(kEntityPlayer, 20); break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 10 IMPLEMENT_ACTION(updateObjetLocation2) ObjectIndex object = (ObjectIndex)hotspot.param1; ObjectLocation location = (ObjectLocation)hotspot.param2; if (object >= kObjectMax) return kSceneInvalid; getObjects()->updateLocation2(object, location); if (object != kObject112 || getSoundQueue()->isBuffered("LIB096")) { if (object == 1) getSound()->playSoundEvent(kEntityPlayer, 73); } else { getSound()->playSoundEvent(kEntityPlayer, 96); } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 11 IMPLEMENT_ACTION(setItemLocation) InventoryItem item = (InventoryItem)hotspot.param1; if (item >= kPortraitOriginal) return kSceneInvalid; Inventory::InventoryEntry *entry = getInventory()->get(item); if (entry->isPresent) return kSceneInvalid; entry->location = (ObjectLocation)hotspot.param2; if (item == kItemCorpse) { ObjectLocation corpseLocation = getInventory()->get(kItemCorpse)->location; getProgress().eventCorpseMovedFromFloor = (corpseLocation == kObjectLocation3 || corpseLocation == kObjectLocation4); } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 12 IMPLEMENT_ACTION(knockNoSound) ObjectIndex object = (ObjectIndex)hotspot.param1; if (object >= kObjectMax) return kSceneInvalid; if (getObjects()->get(object).entity) getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 13 IMPLEMENT_ACTION(pickItem) InventoryItem item = (InventoryItem)hotspot.param1; ObjectLocation location = (ObjectLocation)hotspot.param2; bool process = (hotspot.scene == 0); SceneIndex sceneIndex = kSceneInvalid; if (item >= kPortraitOriginal) return kSceneInvalid; Inventory::InventoryEntry *entry = getInventory()->get(item); if (!entry->location) return kSceneInvalid; // Special case for corpse if (item == kItemCorpse) { pickCorpse(location, process); return kSceneInvalid; } // Add and process items getInventory()->addItem(item); switch (item) { default: break; case kItemGreenJacket: pickGreenJacket(process); break; case kItemScarf: pickScarf(process); // stop processing return kSceneInvalid; case kItemParchemin: if (location != kObjectLocation2) break; getInventory()->addItem(kItemParchemin); getInventory()->get(kItem11)->location = kObjectLocation1; getSound()->playSoundEvent(kEntityPlayer, 9); break; case kItemBomb: RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_pickBomb); break; case kItemBriefcase: getSound()->playSoundEvent(kEntityPlayer, 83); break; } // Load item scene if (getInventory()->get(item)->scene) { if (!getState()->sceneUseBackup) { getState()->sceneUseBackup = true; getState()->sceneBackup = (hotspot.scene ? hotspot.scene : getState()->scene); } getScenes()->loadScene(getInventory()->get(item)->scene); // do not process further sceneIndex = kSceneNone; } // Select item if (getInventory()->get(item)->isSelectable) { getInventory()->selectItem(item); _engine->getCursor()->setStyle(getInventory()->get(item)->cursor); } return sceneIndex; } ////////////////////////////////////////////////////////////////////////// // Action 14 IMPLEMENT_ACTION(dropItem) InventoryItem item = (InventoryItem)hotspot.param1; ObjectLocation location = (ObjectLocation)hotspot.param2; bool process = (hotspot.scene == kSceneNone); if (item >= kPortraitOriginal) return kSceneInvalid; if (!getInventory()->hasItem(item)) return kSceneInvalid; if (location < kObjectLocation1) return kSceneInvalid; // Handle actions if (item == kItemBriefcase) { getSound()->playSoundEvent(kEntityPlayer, 82); if (location == kObjectLocation2) { if (!getProgress().field_58) { getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone); getProgress().field_58 = 1; } if (getInventory()->get(kItemParchemin)->location == kObjectLocation2) { getInventory()->addItem(kItemParchemin); getInventory()->get(kItem11)->location = kObjectLocation1; getSound()->playSoundEvent(kEntityPlayer, 9); } } } // Update item location getInventory()->removeItem(item, location); if (item == kItemCorpse) dropCorpse(process); // Unselect item getInventory()->unselectItem(); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 15: Dummy action ////////////////////////////////////////////////////////////////////////// // Action 16 IMPLEMENT_ACTION(enterCompartment) if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1 || getObjects()->get(kObjectCompartment1).location == kObjectLocation3 || getInventory()->getSelectedItem() == kItemKey) return action_compartment(hotspot); if (getProgress().eventCorpseFound) { if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->get(kItemBriefcase)->location != kObjectLocation2) return action_compartment(hotspot); getSound()->playSoundEvent(kEntityPlayer, 14); getSound()->playSoundEvent(kEntityPlayer, 15, 22); if (getProgress().field_78 && !getSoundQueue()->isBuffered("MUS003")) { getSound()->playSound(kEntityPlayer, "MUS003", kFlagDefault); getProgress().field_78 = 0; } getScenes()->loadSceneFromPosition(kCarGreenSleeping, 77); return kSceneNone; } getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone); getSound()->playSound(kEntityPlayer, "LIB014"); playAnimation(kEventCathFindCorpse); getSound()->playSound(kEntityPlayer, "LIB015"); getProgress().eventCorpseFound = true; return kSceneCompartmentCorpse; } ////////////////////////////////////////////////////////////////////////// // Action 17: Dummy action ////////////////////////////////////////////////////////////////////////// // Action 18 IMPLEMENT_ACTION(getOutsideTrain) ObjectIndex object = (ObjectIndex)hotspot.param1; if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowNight) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) && getProgress().isTrainRunning && (object != kObjectOutsideAnnaCompartment || (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == kObjectLocation2)) && getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase) { switch (object) { default: return kSceneInvalid; case kObjectOutsideTylerCompartment: getEvent(kEventCathLookOutsideWindowDay) = 1; playAnimation(isNight() ? kEventCathGoOutsideTylerCompartmentNight : kEventCathGoOutsideTylerCompartmentDay); getProgress().field_C8 = 1; break; case kObjectOutsideBetweenCompartments: getEvent(kEventCathLookOutsideWindowDay) = 1; playAnimation(isNight() ? kEventCathGoOutsideNight : kEventCathGoOutsideDay); getProgress().field_C8 = 1; break; case kObjectOutsideAnnaCompartment: getEvent(kEventCathLookOutsideWindowDay) = 1; playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay); if (!hotspot.scene) getScenes()->processScene(); break; } } else { if (object == kObjectOutsideTylerCompartment || object == kObjectOutsideBetweenCompartments || object == kObjectOutsideAnnaCompartment) { playAnimation(isNight() ? kEventCathLookOutsideWindowNight : kEventCathLookOutsideWindowDay); getScenes()->processScene(); return kSceneNone; } } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 19 IMPLEMENT_ACTION(slip) switch((ObjectIndex)hotspot.param1) { default: return kSceneInvalid; case kObjectOutsideTylerCompartment: playAnimation(isNight() ? kEventCathSlipTylerCompartmentNight : kEventCathSlipTylerCompartmentDay); break; case kObjectOutsideBetweenCompartments: playAnimation(isNight() ? kEventCathSlipNight : kEventCathSlipDay); break; } getProgress().field_C8 = 0; if (!hotspot.scene) getScenes()->processScene(); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 20 IMPLEMENT_ACTION(getInsideTrain) switch ((ObjectIndex)hotspot.param1) { default: return kSceneInvalid; case kObjectOutsideTylerCompartment: playAnimation(isNight() ? kEventCathGetInsideTylerCompartmentNight : kEventCathGetInsideTylerCompartmentDay); break; case kObjectOutsideBetweenCompartments: playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay); break; case kObjectOutsideAnnaCompartment: playAnimation(kEventCathGettingInsideAnnaCompartment); break; } if (!hotspot.scene) getScenes()->processScene(); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 21 IMPLEMENT_ACTION(climbUpTrain) byte action = hotspot.param1; if (action != 1 && action != 2) return kSceneInvalid; switch (getProgress().chapter) { default: break; case kChapter2: case kChapter3: if (action == 2) playAnimation(kEventCathClimbUpTrainGreenJacket); playAnimation(kEventCathTopTrainGreenJacket); break; case kChapter5: if (action == 2) playAnimation(getProgress().isNightTime ? kEventCathClimbUpTrainNoJacketNight : kEventCathClimbUpTrainNoJacketDay); playAnimation(getProgress().isNightTime ? kEventCathTopTrainNoJacketNight : kEventCathTopTrainNoJacketDay); break; } if (!hotspot.scene) getScenes()->processScene(); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 22 IMPLEMENT_ACTION(climbDownTrain) EventIndex evt = kEventNone; switch (getProgress().chapter) { default: return kSceneInvalid; case kChapter2: case kChapter3: evt = kEventCathClimbDownTrainGreenJacket; break; case kChapter5: evt = (getProgress().isNightTime ? kEventCathClimbDownTrainNoJacketNight : kEventCathClimbDownTrainNoJacketDay); break; } playAnimation(evt); if (evt == kEventCathClimbDownTrainNoJacketDay) getSound()->playSoundEvent(kEntityPlayer, 37); if (!hotspot.scene) getScenes()->processScene(); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 23 IMPLEMENT_ACTION(jumpUpDownTrain) switch (hotspot.param1) { default: break; case 1: getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionBreakCeiling); break; case 2: getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionJumpDownCeiling); break; case 3: if (getInventory()->getSelectedItem() == kItemBriefcase) { getInventory()->removeItem(kItemBriefcase, kObjectLocation3); getSound()->playSoundEvent(kEntityPlayer, 82); getInventory()->unselectItem(); } // Show animation with or without briefcase playAnimation((getInventory()->get(kItemBriefcase)->location - 3) ? kEventCathJumpUpCeilingBriefcase : kEventCathJumpUpCeiling); if (!hotspot.scene) getScenes()->processScene(); break; case 4: if (getProgress().chapter == kChapter1) getSavePoints()->push(kEntityPlayer, kEntityKronos, kAction202621266); break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 24 IMPLEMENT_ACTION(unbound) byte action = hotspot.param1; switch (action) { default: break; case 1: playAnimation(kEventCathStruggleWithBonds); if (hotspot.scene) getScenes()->processScene(); break; case 2: playAnimation(kEventCathBurnRope); if (hotspot.scene) getScenes()->processScene(); break; case 3: if (getEvent(kEventCathBurnRope)) { playAnimation(kEventCathRemoveBonds); getProgress().field_84 = 0; getScenes()->loadSceneFromPosition(kCarBaggageRear, 89); return kSceneNone; } break; case 4: if (!getEvent(kEventCathStruggleWithBonds2)) { playAnimation(kEventCathStruggleWithBonds2); getSound()->playSoundEvent(kEntityPlayer, 101); getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation2); if (!hotspot.scene) getScenes()->processScene(); } break; case 5: getSavePoints()->push(kEntityPlayer, kEntityIvo, kAction192637492); break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 25 IMPLEMENT_ACTION(25) switch(hotspot.param1) { default: break; case 1: getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction272177921); break; case 2: if (!getSoundQueue()->isBuffered("MUS021")) getSound()->playSound(kEntityPlayer, "MUS021", kFlagDefault); break; case 3: getSound()->playSoundEvent(kEntityPlayer, 43); if (!getInventory()->hasItem(kItemKey) && !getEvent(kEventAnnaBaggageArgument)) { RESET_ENTITY_STATE(kEntityAnna, Anna, setup_baggage); return kSceneNone; } break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 26 IMPLEMENT_ACTION(26) switch(hotspot.param1) { default: return kSceneInvalid; case 1: getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction158610240); break; case 2: getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225367984); getInventory()->unselectItem(); return kSceneNone; case 3: getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction191001984); return kSceneNone; case 4: getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction201959744); return kSceneNone; case 5: getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction169300225); break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 27 IMPLEMENT_ACTION(27) if (!getSoundQueue()->isBuffered("LIB031", true)) getSound()->playSoundEvent(kEntityPlayer, 31); switch (getEntityData(kEntityPlayer)->car) { default: break; case kCarGreenSleeping: getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction225358684, hotspot.param1); break; case kCarRedSleeping: getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction225358684, hotspot.param1); break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 28 IMPLEMENT_ACTION(concertSitCough) switch(hotspot.param1) { default: return kSceneInvalid; case 1: playAnimation(kEventConcertSit); break; case 2: playAnimation(kEventConcertCough); break; } if (!hotspot.scene) getScenes()->processScene(); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 29 IMPLEMENT_ACTION(29) getProgress().field_C = 1; getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2); Common::String filename = Common::String::format("MUS%03d", hotspot.param3); if (!getSoundQueue()->isBuffered(filename)) getSound()->playSound(kEntityPlayer, filename, kFlagDefault); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 30 IMPLEMENT_ACTION(catchBeetle) if (!getBeetle()->isLoaded()) return kSceneInvalid; if (getBeetle()->catchBeetle()) { getBeetle()->unload(); getInventory()->get(kItemBeetle)->location = kObjectLocation1; getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionCatchBeetle); } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 31 IMPLEMENT_ACTION(exitCompartment) if (!getProgress().field_30 && getProgress().jacket != kJacketOriginal) { getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone); getProgress().field_30 = 1; } getObjects()->updateLocation2(kObjectCompartment1, (ObjectLocation)hotspot.param2); // fall to case enterCompartment action return action_enterCompartment(hotspot); } ////////////////////////////////////////////////////////////////////////// // Action 32 IMPLEMENT_ACTION(32) switch(hotspot.param1) { default: break; case 1: getSavePoints()->push(kEntityPlayer, kEntitySalko, kAction167992577); break; case 2: getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction202884544); break; case 3: if (getProgress().chapter == kChapter5) { getSavePoints()->push(kEntityPlayer, kEntityAbbot, kAction168646401); getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction168646401); } else { getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction203339360); } // Stop processing further scenes return kSceneNone; case 4: getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction169773228); break; case 5: getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction167992577); break; case 6: getSavePoints()->push(kEntityPlayer, kEntityAugust, kAction203078272); break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 33 IMPLEMENT_ACTION(useWhistle) EventIndex evt = kEventNone; SceneIndex sceneIndex = kSceneInvalid; switch (hotspot.param1) { default: break; case 1: if (getEvent(kEventKronosBringFirebird)) { getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction205294778); break; } if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) { evt = kEventCathOpenEgg; Scene *scene = getScenes()->get(hotspot.scene); if (scene->getHotspot()) sceneIndex = scene->getHotspot()->scene; } else { evt = kEventCathOpenEggNoBackground; } getProgress().isEggOpen = true; break; case 2: if (getEvent(kEventKronosBringFirebird)) { getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction224309120); break; } evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathCloseEgg : kEventCathCloseEggNoBackground; getProgress().isEggOpen = false; break; case 3: if (getEvent(kEventKronosBringFirebird)) { getSavePoints()->push(kEntityPlayer, kEntityAnna, kActionUseWhistle); break; } evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathUseWhistleOpenEgg : kEventCathUseWhistleOpenEggNoBackground; break; } if (evt != kEventNone) { playAnimation(evt); if (sceneIndex == kSceneNone || !hotspot.scene) getScenes()->processScene(); } return sceneIndex; } ////////////////////////////////////////////////////////////////////////// // Action 34 IMPLEMENT_ACTION(openMatchBox) // If the match is already in the inventory, do nothing if (!getInventory()->get(kItemMatch)->location || getInventory()->get(kItemMatch)->isPresent) return kSceneInvalid; getInventory()->addItem(kItemMatch); getSound()->playSoundEvent(kEntityPlayer, 102); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 35 IMPLEMENT_ACTION(openBed) getSound()->playSoundEvent(kEntityPlayer, 59); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 36: Dummy action ////////////////////////////////////////////////////////////////////////// // Action 37 IMPLEMENT_ACTION(dialog) getSound()->playDialog(kEntityTables4, (EntityIndex)hotspot.param1, kFlagDefault, 0); return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 38 IMPLEMENT_ACTION(eggBox) getSound()->playSoundEvent(kEntityPlayer, 43); if (getProgress().field_7C && !getSoundQueue()->isBuffered("MUS003")) { getSound()->playSound(kEntityPlayer, "MUS003", kFlagDefault); getProgress().field_7C = 0; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 39 IMPLEMENT_ACTION(39) getSound()->playSoundEvent(kEntityPlayer, 24); if (getProgress().field_80 && !getSoundQueue()->isBuffered("MUS003")) { getSound()->playSound(kEntityPlayer, "MUS003", kFlagDefault); getProgress().field_80 = 0; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 40 IMPLEMENT_ACTION(bed) getSound()->playSoundEvent(kEntityPlayer, 85); // falls to case knockNoSound return action_knockNoSound(hotspot); } ////////////////////////////////////////////////////////////////////////// // Action 41 IMPLEMENT_ACTION(playMusicChapter) byte id = 0; switch (getProgress().chapter) { default: break; case kChapter1: id = hotspot.param1; break; case kChapter2: case kChapter3: id = hotspot.param2; break; case kChapter4: case kChapter5: id = hotspot.param3; break; } if (id) { Common::String filename = Common::String::format("MUS%03d", id); if (!getSoundQueue()->isBuffered(filename)) getSound()->playSound(kEntityPlayer, filename, kFlagDefault); } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 42 IMPLEMENT_ACTION(playMusicChapterSetupTrain) int id = 0; switch (getProgress().chapter) { default: break; case kChapter1: id = 1; break; case kChapter2: case kChapter3: id = 2; break; case kChapter4: case kChapter5: id = 4; break; } Common::String filename = Common::String::format("MUS%03d", hotspot.param1); if (!getSoundQueue()->isBuffered(filename) && hotspot.param3 & id) { getSound()->playSound(kEntityPlayer, filename, kFlagDefault); getSavePoints()->call(kEntityPlayer, kEntityTrain, kAction203863200, filename.c_str()); getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction222746496, hotspot.param2); } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // // Action 43 IMPLEMENT_ACTION(switchChapter) // Nothing to do here as an hotspot action return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Action 44 IMPLEMENT_ACTION(44) switch (hotspot.param1) { default: break; case 1: getSavePoints()->push(kEntityPlayer, kEntityRebecca, kAction205034665); break; case 2: getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225358684); break; } return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// // Helper functions ////////////////////////////////////////////////////////////////////////// void Action::pickGreenJacket(bool process) const { getProgress().jacket = kJacketGreen; getInventory()->addItem(kItemMatchBox); getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue); playAnimation(kEventPickGreenJacket); getInventory()->setPortrait(kPortraitGreen); if (process) getScenes()->processScene(); } void Action::pickScarf(bool process) const { playAnimation(getProgress().jacket == kJacketGreen ? kEventPickScarfGreen : kEventPickScarfOriginal); if (process) getScenes()->processScene(); } void Action::pickCorpse(ObjectLocation bedPosition, bool process) const { if (getProgress().jacket == kJacketOriginal) getProgress().jacket = kJacketBlood; switch(getInventory()->get(kItemCorpse)->location) { // No way to pick the corpse default: break; // Floor case kObjectLocation1: // Bed is fully opened, move corpse directly there if (bedPosition == 4) { playAnimation(kEventCorpsePickFloorOpenedBedOriginal); getInventory()->get(kItemCorpse)->location = kObjectLocation5; break; } playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickFloorGreen : kEventCorpsePickFloorOriginal); break; // Bed case kObjectLocation2: playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickBedGreen : kEventCorpsePickBedOriginal); break; } if (process) getScenes()->processScene(); // Add corpse to inventory if (bedPosition != 4) { // bed is not fully opened getInventory()->addItem(kItemCorpse); getInventory()->selectItem(kItemCorpse); _engine->getCursor()->setStyle(kCursorCorpse); } } void Action::dropCorpse(bool process) const { switch(getInventory()->get(kItemCorpse)->location) { default: break; case kObjectLocation1: // Floor playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropFloorGreen : kEventCorpseDropFloorOriginal); break; case kObjectLocation2: // Bed playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropBedGreen : kEventCorpseDropBedOriginal); break; case kObjectLocation4: // Window // Say goodbye to an old friend getInventory()->get(kItemCorpse)->location = kObjectLocationNone; getProgress().eventCorpseThrown = true; if (getState()->time <= kTime1138500) { playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropWindowGreen : kEventCorpseDropWindowOriginal); getProgress().field_24 = true; } else { playAnimation(kEventCorpseDropBridge); } getProgress().eventCorpseMovedFromFloor = true; break; } if (process) getScenes()->processScene(); } bool Action::handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const { #define ENTITY_PARAMS(entity, index, id) \ ((EntityData::EntityParametersIIII*)getEntities()->get(entity)->getParamData()->getParameters(8, index))->param##id // Only handle compartments if (getEntityData(kEntityPlayer)->location != kLocationOutsideCompartment || ((object < kObjectCompartment2 || object > kObjectCompartment8) && (object < kObjectCompartmentA || object > kObjectCompartmentH))) return false; ////////////////////////////////////////////////////////////////////////// // Gendarmes if (getEntityData(kEntityPlayer)->car == getEntityData(kEntityGendarmes)->car && getEntityData(kEntityGendarmes)->location == kLocationOutsideCompartment && !getEntities()->compare(kEntityPlayer, kEntityGendarmes)) { if (doPlaySound) playCompartmentSoundEvents(object); if (doLoadScene) getScenes()->loadSceneFromObject(object); return true; } ////////////////////////////////////////////////////////////////////////// // Mertens if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping && getEntityData(kEntityMertens)->car == kCarGreenSleeping && !getEntityData(kEntityMertens)->location && !ENTITY_PARAMS(kEntityMertens, 0, 1)) { if (!getEntities()->compare(kEntityPlayer, kEntityMertens)) { if (getEntityData(kEntityMertens)->entityPosition < kPosition_2740 && getEntityData(kEntityMertens)->entityPosition > kPosition_850 && (getEntityData(kEntityCoudert)->car != kCarGreenSleeping || getEntityData(kEntityCoudert)->entityPosition > kPosition_2740) && (getEntityData(kEntityVerges)->car != kCarGreenSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)) { if (doPlaySound) playCompartmentSoundEvents(object); if (!getSoundQueue()->isBuffered(kEntityMertens)) getSound()->playWarningCompartment(kEntityMertens, object); getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction305159806); if (doLoadScene) getScenes()->loadSceneFromObject(object); return true; } if (getEntityData(kEntityMertens)->direction == kDirectionUp && getEntityData(kEntityMertens)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) { if (doPlaySound) playCompartmentSoundEvents(object); if (!getSoundQueue()->isBuffered(kEntityMertens)) getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A"); if (doLoadScene) getScenes()->loadSceneFromObject(object); } if (getEntityData(kEntityMertens)->direction == kDirectionDown && getEntityData(kEntityMertens)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) { if (doPlaySound) playCompartmentSoundEvents(object); if (!getSoundQueue()->isBuffered(kEntityMertens)) getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A"); if (doLoadScene) getScenes()->loadSceneFromObject(object, true); } } } ////////////////////////////////////////////////////////////////////////// // Coudert if (getEntityData(kEntityPlayer)->car != kCarRedSleeping || !getEntityData(kEntityCoudert)->car || getEntityData(kEntityCoudert)->location != kLocationOutsideCompartment || ENTITY_PARAMS(kEntityCoudert, 0, 1)) return false; if (!getEntities()->compare(kEntityPlayer, kEntityCoudert)) { if (getEntityData(kEntityCoudert)->entityPosition < kPosition_2740 && getEntityData(kEntityCoudert)->entityPosition > kPosition_850 && (getEntityData(kEntityMertens)->car != kCarRedSleeping || getEntityData(kEntityMertens)->entityPosition > kPosition_2740) && (getEntityData(kEntityVerges)->car != kCarRedSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740) && (getEntityData(kEntityMmeBoutarel)->car != kCarRedSleeping || getEntityData(kEntityMmeBoutarel)->entityPosition > kPosition_2740)) { if (doPlaySound) playCompartmentSoundEvents(object); if (!getSoundQueue()->isBuffered(kEntityCoudert)) getSound()->playWarningCompartment(kEntityCoudert, object); getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction305159806); if (doLoadScene) getScenes()->loadSceneFromObject(object); return true; } // Direction = Up if (getEntityData(kEntityCoudert)->direction == kDirectionUp && getEntityData(kEntityCoudert)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) { if (doPlaySound) playCompartmentSoundEvents(object); if (!getSoundQueue()->isBuffered(kEntityCoudert)) getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A"); if (doLoadScene) getScenes()->loadSceneFromObject(object); return true; } // Direction = down if (getEntityData(kEntityCoudert)->direction == kDirectionDown && getEntityData(kEntityCoudert)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) { if (doPlaySound) playCompartmentSoundEvents(object); if (!getSoundQueue()->isBuffered(kEntityCoudert)) getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A"); if (doLoadScene) getScenes()->loadSceneFromObject(object, true); } } return false; } void Action::playCompartmentSoundEvents(ObjectIndex object) const { if (getObjects()->get(object).location == kObjectLocation1 || getObjects()->get(object).location == kObjectLocation3 || getEntities()->checkFields2(object)) { getSound()->playSoundEvent(kEntityPlayer, 13); } else { getSound()->playSoundEvent(kEntityPlayer, 14); getSound()->playSoundEvent(kEntityPlayer, 15, 3); } } ////////////////////////////////////////////////////////////////////////// // Cursors ////////////////////////////////////////////////////////////////////////// CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { // Simple cursor style if (hotspot.cursor != kCursorProcess) return (CursorStyle)hotspot.cursor; ObjectIndex object = (ObjectIndex)hotspot.param1; switch (hotspot.action) { default: return kCursorNormal; case SceneHotspot::kActionInventory: if (!getState()->sceneBackup2 && (getEvent(kEventKronosBringFirebird) || getProgress().isEggOpen)) return kCursorNormal; else return kCursorBackward; case SceneHotspot::kActionKnockOnDoor: debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; else return (CursorStyle)getObjects()->get(object).cursor; case SceneHotspot::kAction12: debugC(2, kLastExpressDebugScenes, "================================= OBJECT %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; if (getObjects()->get(object).entity) return (CursorStyle)getObjects()->get(object).cursor; else return kCursorNormal; case SceneHotspot::kActionPickItem: debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; if ((!getInventory()->getSelectedItem() || getInventory()->getSelectedEntry()->manualSelect) && (object != kObject21 || getProgress().eventCorpseMovedFromFloor)) return kCursorHand; else return kCursorNormal; case SceneHotspot::kActionDropItem: debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; if (getInventory()->getSelectedItem() != (InventoryItem)object) return kCursorNormal; if (object == kObject20 && hotspot.param2 == 4 && !getProgress().isTrainRunning) return kCursorNormal; if (object == kObjectHandleInsideBathroom && hotspot.param2 == 1 && getProgress().field_5C) return kCursorNormal; return (CursorStyle)getInventory()->getSelectedEntry()->cursor; case SceneHotspot::kAction15: if (object >= kObjectMax) return kCursorNormal; if (getProgress().isEqual(object, hotspot.param2)) return (CursorStyle)hotspot.param3; return kCursorNormal; case SceneHotspot::kActionEnterCompartment: if ((getInventory()->getSelectedItem() != kItemKey || getObjects()->get(kObjectCompartment1).location) && (getObjects()->get(kObjectCompartment1).location != 1 || !getInventory()->hasItem(kItemKey) || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase))) goto LABEL_KEY; return (CursorStyle)getInventory()->get(kItemKey)->cursor; // TODO is that always the same as kCursorKey ? case SceneHotspot::kActionGetOutsideTrain: if (getProgress().jacket != kJacketGreen) return kCursorNormal; if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowNight) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) && getProgress().isTrainRunning && (object != kObjectOutsideAnnaCompartment || (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == 2)) && getInventory()->getSelectedItem() != kItemBriefcase && getInventory()->getSelectedItem() != kItemFirebird) return kCursorForward; return (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation2) ? kCursorNormal : kCursorMagnifier; case SceneHotspot::kActionSlip: return (getProgress().field_C8 < 1) ? kCursorNormal : kCursorLeft; case SceneHotspot::kActionClimbUpTrain: if (getProgress().isTrainRunning && (getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3 || getProgress().chapter == kChapter5) && getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase) return kCursorUp; return kCursorNormal; case SceneHotspot::kActionJumpUpDownTrain: if (object != kObjectCompartment1) return kCursorNormal; return (getObjects()->get(kObjectCeiling).location < kObjectLocation1) ? kCursorHand : kCursorNormal; case SceneHotspot::kActionUnbound: if (hotspot.param2 != 2) return kCursorNormal; if (getEvent(kEventCathBurnRope) || !getEvent(kEventCathStruggleWithBonds2)) return kCursorNormal; return kCursorHand; case SceneHotspot::kActionCatchBeetle: if (!getBeetle()->isLoaded()) return kCursorNormal; if (!getBeetle()->isCatchable()) return kCursorNormal; if (getInventory()->getSelectedItem() == kItemMatchBox && getInventory()->hasItem(kItemMatch)) return (CursorStyle)getInventory()->get(kItemMatchBox)->cursor; return kCursorHandPointer; case SceneHotspot::KActionUseWhistle: if (object != kObjectCompartment3) return kCursorNormal; if (getInventory()->getSelectedItem() == kItemWhistle) return kCursorWhistle; else return kCursorNormal; case SceneHotspot::kActionOpenBed: if (getProgress().chapter < kChapter2) return kCursorHand; return kCursorNormal; case SceneHotspot::kActionDialog: if (getSound()->getDialogName((EntityIndex)object)) return kCursorHandPointer; return kCursorNormal; case SceneHotspot::kActionBed: if (getProgress().field_18 == 2 && !getProgress().field_E4 && (getState()->time > kTimeBedTime || (getProgress().eventMetAugust && getProgress().field_CC && (!getProgress().field_24 || getProgress().field_3C)))) return kCursorSleep; return kCursorNormal; LABEL_KEY: // Handle compartment action case SceneHotspot::kActionCompartment: case SceneHotspot::kActionExitCompartment: debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; if (getInventory()->getSelectedItem() != kItemKey || getObjects()->get(object).entity || getObjects()->get(object).location != 1 || !getObjects()->get(object).cursor2 || getEntities()->isInsideCompartments(kEntityPlayer) || getEntities()->checkFields2(object)) return (CursorStyle)getObjects()->get(object).cursor2; else return (CursorStyle)getInventory()->get(kItemKey)->cursor; } } ////////////////////////////////////////////////////////////////////////// // Animation ////////////////////////////////////////////////////////////////////////// // Play an animation and add delta time to global game time void Action::playAnimation(EventIndex index, bool debugMode) const { if (index >= _animationListSize) error("[Action::playAnimation] Invalid event index (value=%i, max=%i)", index, _animationListSize); // In debug mode, just show the animation if (debugMode) { Animation animation; if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis"))) animation.play(); return; } getFlags()->flag_3 = true; // Hide cursor _engine->getCursor()->show(false); // Show inventory & hourglass getInventory()->show(); getInventory()->showHourGlass(); if (!getFlags()->mouseRightClick) { if (getGlobalTimer()) { if (getSoundQueue()->isBuffered("TIMER")) { getSoundQueue()->processEntry("TIMER"); setGlobalTimer(105); } } bool processSound = false; if (index >= kEventCorpseDropFloorOriginal || index == kEventCathWakingUp || index == kEventConcertCough || index == kEventConcertSit || index == kEventConcertLeaveWithBriefcase) processSound = true; Animation animation; if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis") , processSound ? Animation::kFlagDefault : Animation::kFlagProcess)) animation.play(); if (getSoundQueue()->isBuffered("TIMER")) getSoundQueue()->removeFromQueue("TIMER"); } // Show cursor _engine->getCursor()->show(true); getEvent(index) = 1; // Adjust game time getState()->timeTicks += _animationList[index].time; getState()->time = (TimeValue)(getState()->time + (TimeValue)(_animationList[index].time * getState()->timeDelta)); } } // End of namespace LastExpress