scummvm/engines/lastexpress/game/action.cpp
2012-08-27 21:49:40 -04:00

1979 lines
54 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "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 Arg, class Res, class T>
class Functor1MemConst : public Common::Functor1<Arg, Res> {
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