1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "lastexpress/game/action.h"
24
25 #include "lastexpress/data/animation.h"
26 #include "lastexpress/data/cursor.h"
27 #include "lastexpress/data/snd.h"
28 #include "lastexpress/data/scene.h"
29
30 #include "lastexpress/entities/abbot.h"
31 #include "lastexpress/entities/anna.h"
32
33 #include "lastexpress/game/beetle.h"
34 #include "lastexpress/game/entities.h"
35 #include "lastexpress/game/inventory.h"
36 #include "lastexpress/game/logic.h"
37 #include "lastexpress/game/object.h"
38 #include "lastexpress/game/savegame.h"
39 #include "lastexpress/game/savepoint.h"
40 #include "lastexpress/game/scenes.h"
41 #include "lastexpress/game/state.h"
42
43 #include "lastexpress/sound/queue.h"
44
45 #include "lastexpress/lastexpress.h"
46 #include "lastexpress/resource.h"
47
48 namespace LastExpress {
49
50 static const int _animationListSize = 273;
51
52 // List of animations
53 static const struct {
54 const char *filename;
55 uint16 time;
56 } _animationList[_animationListSize] = {
57 {"", 0},
58 {"1002", 255},
59 {"1002D", 255},
60 {"1003", 0},
61 {"1005", 195},
62 {"1006", 750}, // 5
63 {"1006A", 750},
64 {"1008", 765},
65 {"1008N", 765},
66 {"1008A", 750},
67 {"1008AN", 750}, // 10
68 {"1009", 0},
69 {"1011", 1005},
70 {"1011A", 780},
71 {"1012", 300},
72 {"1013", 285},
73 {"1017", 870}, // 15
74 {"1017A", 0}, // Not in the data files?
75 {"1019", 120},
76 {"1019D", 120},
77 {"1020", 120}, // 20
78 {"1022", 525},
79 {"1022A", 180},
80 {"1022AD", 210},
81 {"1022B", 210},
82 {"1022C", 210}, // 25
83 {"1023", 135},
84 {"1025", 945},
85 {"1028", 300},
86 {"1030", 390},
87 {"1031", 375}, // 30
88 {"1032", 1050},
89 {"1033", 945},
90 {"1034", 495},
91 {"1035", 1230},
92 {"1037", 1425}, // 35
93 {"1038", 195},
94 {"1038A", 405},
95 {"1039", 600},
96 {"1040", 945},
97 {"1041", 510}, // 40
98 {"1042", 540},
99 {"1043", 855},
100 {"1044", 645},
101 {"1046", 0},
102 {"1047", 0}, // 45
103 {"1047A", 0},
104 {"1059", 1005},
105 {"1060", 255},
106 {"1063", 0},
107 {"1101", 255}, // 50
108 {"1102", 1320},
109 {"1103", 210},
110 {"1104", 120},
111 {"1105", 1350},
112 {"1106", 315}, // 55
113 {"1106A", 315},
114 {"1106D", 315},
115 {"1107", 1},
116 {"1107A", 660},
117 {"1108", 300}, // 60
118 {"1109", 1305},
119 {"1110", 300},
120 {"1112", 0},
121 {"1115", 0},
122 {"1115A", 0}, // 65
123 {"1115B", 0},
124 {"1115C", 0},
125 {"1115D", 0},
126 {"1115E", 0},
127 {"1115F", 0}, // 70
128 {"1115G", 0},
129 {"1115H", 0},
130 {"1116", 0},
131 {"1117", 0},
132 {"1118", 105}, // 75
133 {"1202", 510},
134 {"1202A", 510},
135 {"1203", 720},
136 {"1204", 120},
137 {"1205", 465}, // 80
138 {"1206", 690},
139 {"1206A", 450},
140 {"1208", 465},
141 {"1210", 1020},
142 {"1211", 600}, // 85
143 {"1212", 435},
144 {"1213", 525},
145 {"1213A", 150},
146 {"1215", 390},
147 {"1216", 0}, // 90
148 {"1219", 240},
149 {"1222", 1095},
150 {"1223", 0},
151 {"1224", 720},
152 {"1225", 1005}, // 95
153 {"1227", 840},
154 {"1227A", 840},
155 {"1303", 450},
156 {"1303N", 450},
157 {"1304", 450}, // 100
158 {"1304N", 450},
159 {"1305", 630},
160 {"1309", 0},
161 {"1311", 1710},
162 {"1312", 240}, // 105
163 {"1312D", 240},
164 {"1313", 930},
165 {"1315", 1035},
166 {"1315A", 1035},
167 {"1401", 540}, // 110
168 {"1402", 150},
169 {"1402B", 150},
170 {"1403", 90},
171 {"1404", 885},
172 {"1404A", 0}, // 115
173 {"1405", 135},
174 {"1406", 1665},
175 {"1501", 285},
176 {"1501A", 285},
177 {"1502", 165}, // 120
178 {"1502A", 165},
179 {"1502D", 165},
180 {"1503", 0},
181 {"1504", 0},
182 {"1505", 0}, // 125
183 {"1505A", 0},
184 {"1506", 300},
185 {"1506A", 180},
186 {"1508", 0},
187 {"1509", 450}, // 130
188 {"1509S", 450},
189 {"1509A", 450},
190 {"1509AS", 450},
191 {"1509N", 450},
192 {"1509SN", 450}, // 135
193 {"1509AN", 450},
194 {"1509BN", 450},
195 {"1511", 150},
196 {"1511A", 150},
197 {"1511B", 90}, // 140
198 {"1511BA", 90},
199 {"1511C", 135},
200 {"1511D", 105},
201 {"1930", 0},
202 {"1511E", 150}, // 145
203 {"1512", 165},
204 {"1513", 180},
205 {"1517", 0},
206 {"1517A", 165},
207 {"1518", 165}, // 150
208 {"1518A", 165},
209 {"1518B", 165},
210 {"1591", 450},
211 {"1592", 450},
212 {"1593", 450}, // 155
213 {"1594", 450},
214 {"1595", 450},
215 {"1596", 450},
216 {"1601", 0},
217 {"1603", 0}, // 160
218 {"1606B", 315},
219 {"1607A", 0},
220 {"1610", 0},
221 {"1611", 0},
222 {"1612", 0}, // 165
223 {"1615", 0},
224 {"1619", 0},
225 {"1620", 120},
226 {"1621", 105},
227 {"1622", 105}, // 170
228 {"1629", 450},
229 {"1630", 450},
230 {"1631", 525},
231 {"1632", 0},
232 {"1633", 615}, // 175
233 {"1634", 180},
234 {"1702", 180},
235 {"1702DD", 180},
236 {"1702NU", 180},
237 {"1702ND", 180}, // 180
238 {"1704", 300},
239 {"1704D", 300},
240 {"1705", 195},
241 {"1705D", 195},
242 {"1706", 195}, // 185
243 {"1706DD", 195},
244 {"1706ND", 195},
245 {"1706NU", 195},
246 {"1901", 135},
247 {"1902", 1410}, // 190
248 {"1903", 0},
249 {"1904", 1920},
250 {"1908", 600},
251 {"1908A", 195},
252 {"1908B", 105}, // 195
253 {"1908C", 165},
254 {"1908CD", 0},
255 {"1909A", 150},
256 {"1909B", 150},
257 {"1909C", 150}, // 200
258 {"1910A", 180},
259 {"1910B", 180},
260 {"1910C", 180},
261 {"1911A", 90},
262 {"1911B", 90}, // 205
263 {"1911C", 90},
264 {"1912", 0},
265 {"1913", 0},
266 {"1917", 0},
267 {"1918", 390}, // 210
268 {"1919", 360},
269 {"1919A", 105},
270 {"1920", 75},
271 {"1922", 75},
272 {"1923", 150}, // 215
273 {"8001", 120},
274 {"8001A", 120},
275 {"8002", 120},
276 {"8002A", 120},
277 {"8002B", 120}, // 220
278 {"8003", 105},
279 {"8003A", 105},
280 {"8004", 105},
281 {"8004A", 105},
282 {"8005", 270}, // 225
283 {"8005B", 270},
284 {"8010", 270},
285 {"8013", 120},
286 {"8013A", 120},
287 {"8014", 165}, // 230
288 {"8014A", 165},
289 {"8014R", 165},
290 {"8014AR", 165},
291 {"8015", 150},
292 {"8015A", 150}, // 235
293 {"8015R", 150},
294 {"8015AR", 150},
295 {"8017", 120},
296 {"8017A", 120},
297 {"8017R", 120}, // 240
298 {"8017AR", 120},
299 {"8017N", 90},
300 {"8023", 135},
301 {"8023A", 135},
302 {"8023M", 135}, // 245
303 {"8024", 150},
304 {"8024A", 180},
305 {"8024M", 180},
306 {"8025", 150},
307 {"8025A", 150}, // 250
308 {"8025M", 150},
309 {"8027", 75},
310 {"8028", 75},
311 {"8029", 120},
312 {"8029A", 120}, // 255
313 {"8031", 375},
314 {"8032", 0},
315 {"8032A", 0},
316 {"8033", 105},
317 {"8035", 195}, // 260
318 {"8035A", 120},
319 {"8035B", 180},
320 {"8035C", 135},
321 {"8036", 105},
322 {"8037", 195}, // 265
323 {"8037A", 195},
324 {"8040", 240},
325 {"8040A", 240},
326 {"8041", 195},
327 {"8041A", 195}, // 270
328 {"8042", 600},
329 {"8042A", 600}
330 };
331
332 template<class Arg, class Res, class T>
333 class Functor1MemConst : public Common::Functor1<Arg, Res> {
334 public:
335 typedef Res (T::*FuncType)(Arg) const;
336
Functor1MemConst(T * t,const FuncType & func)337 Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {}
338
isValid() const339 bool isValid() const { return _func != 0 && _t != 0; }
operator ()(Arg v1) const340 Res operator()(Arg v1) const {
341 return (_t->*_func)(v1);
342 }
343 private:
344 mutable T *_t;
345 const FuncType _func;
346 };
347
Action(LastExpressEngine * engine)348 Action::Action(LastExpressEngine *engine) : _engine(engine) {
349 ADD_ACTION(dummy);
350 ADD_ACTION(inventory);
351 ADD_ACTION(savePoint);
352 ADD_ACTION(playSound);
353 ADD_ACTION(playMusic);
354 ADD_ACTION(knock); // 5
355 ADD_ACTION(compartment);
356 ADD_ACTION(playSounds);
357 ADD_ACTION(playAnimation);
358 ADD_ACTION(openCloseObject);
359 ADD_ACTION(setModel); // 10
360 ADD_ACTION(setItem);
361 ADD_ACTION(knockInside);
362 ADD_ACTION(pickItem);
363 ADD_ACTION(dropItem);
364 ADD_ACTION(dummy); // 15
365 ADD_ACTION(enterCompartment);
366 ADD_ACTION(dummy);
367 ADD_ACTION(leanOutWindow);
368 ADD_ACTION(almostFall);
369 ADD_ACTION(climbInWindow); // 20
370 ADD_ACTION(climbLadder);
371 ADD_ACTION(climbDownTrain);
372 ADD_ACTION(kronosSanctum);
373 ADD_ACTION(escapeBaggage);
374 ADD_ACTION(enterBaggage); // 25
375 ADD_ACTION(bombPuzzle);
376 ADD_ACTION(27);
377 ADD_ACTION(kronosConcert);
378 ADD_ACTION(29);
379 ADD_ACTION(catchBeetle); // 30
380 ADD_ACTION(exitCompartment);
381 ADD_ACTION(outsideTrain);
382 ADD_ACTION(firebirdPuzzle);
383 ADD_ACTION(openMatchBox);
384 ADD_ACTION(openBed); // 35
385 ADD_ACTION(dummy);
386 ADD_ACTION(dialog);
387 ADD_ACTION(eggBox);
388 ADD_ACTION(39);
389 ADD_ACTION(bed); // 40
390 ADD_ACTION(playMusicChapter);
391 ADD_ACTION(playMusicChapterSetupTrain);
392 ADD_ACTION(switchChapter);
393 ADD_ACTION(44);
394 }
395
~Action()396 Action::~Action() {
397 for (uint i = 0; i < _actions.size(); i++)
398 SAFE_DELETE(_actions[i]);
399
400 _actions.clear();
401
402 // Zero-out passed pointers
403 _engine = NULL;
404 }
405
406 //////////////////////////////////////////////////////////////////////////
407 // Processing hotspot
408 //////////////////////////////////////////////////////////////////////////
processHotspot(const SceneHotspot & hotspot)409 SceneIndex Action::processHotspot(const SceneHotspot &hotspot) {
410 if (!hotspot.action || hotspot.action >= (int)_actions.size())
411 return kSceneInvalid;
412
413 return (*_actions[hotspot.action])(hotspot);
414 }
415
416 //////////////////////////////////////////////////////////////////////////
417 // Actions
418 //////////////////////////////////////////////////////////////////////////
419
420 //////////////////////////////////////////////////////////////////////////
421 // Action 0
422 IMPLEMENT_ACTION(dummy)
423 error("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action);
424 }
425
426 //////////////////////////////////////////////////////////////////////////
427 // Action 1
428 IMPLEMENT_ACTION(inventory)
429 if (!getState()->sceneUseBackup)
430 return kSceneInvalid;
431
432 SceneIndex index = kSceneNone;
433 if (getState()->sceneBackup2) {
434 index = getState()->sceneBackup2;
435 getState()->sceneBackup2 = kSceneNone;
436 } else {
437 getState()->sceneUseBackup = false;
438 index = getState()->sceneBackup;
439
440 Scene *backup = getScenes()->get(getState()->sceneBackup);
441 if (getEntities()->getPosition(backup->car, backup->position))
442 index = getScenes()->processIndex(getState()->sceneBackup);
443 }
444
445 getScenes()->loadScene(index);
446
447 if (!getInventory()->getSelectedItem())
448 return kSceneInvalid;
449
450 if (!getInventory()->getSelectedEntry()->isSelectable || (!getState()->sceneBackup2 && getInventory()->getFirstExaminableItem()))
451 getInventory()->selectItem(getInventory()->getFirstExaminableItem());
452
453 return kSceneInvalid;
454 }
455
456 //////////////////////////////////////////////////////////////////////////
457 // Action 2
458 IMPLEMENT_ACTION(savePoint)
459 getSavePoints()->push(kEntityPlayer, (EntityIndex)hotspot.param1, (ActionIndex)hotspot.param2);
460
461 return kSceneInvalid;
462 }
463
464 //////////////////////////////////////////////////////////////////////////
465 // Action 3
466 IMPLEMENT_ACTION(playSound)
467
468 // Check that the file is not already buffered
469 if (hotspot.param2 || !getSoundQueue()->isBuffered(Common::String::format("LIB%03d", hotspot.param1), true))
470 getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
471
472 return kSceneInvalid;
473 }
474
475 //////////////////////////////////////////////////////////////////////////
476 // Action 4
477 IMPLEMENT_ACTION(playMusic)
478 // Check that the file is not already buffered
479 Common::String filename = Common::String::format("MUS%03d", hotspot.param1);
480
481 if (!getSoundQueue()->isBuffered(filename) && (hotspot.param1 != 50 || getProgress().chapter == kChapter5))
482 getSound()->playSound(kEntityPlayer, filename, kVolumeFull, hotspot.param2);
483
484 return kSceneInvalid;
485 }
486
487 //////////////////////////////////////////////////////////////////////////
488 // Action 5
489 IMPLEMENT_ACTION(knock)
490 ObjectIndex object = (ObjectIndex)hotspot.param1;
491 if (object >= kObjectMax)
492 return kSceneInvalid;
493
494 if (getObjects()->get(object).entity) {
495 getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
496 } else {
497 if (!getSoundQueue()->isBuffered("LIB012", true))
498 getSound()->playSoundEvent(kEntityPlayer, 12);
499 }
500
501 return kSceneInvalid;
502 }
503
504 //////////////////////////////////////////////////////////////////////////
505 // Action 6
506 IMPLEMENT_ACTION(compartment)
507 ObjectIndex compartment = (ObjectIndex)hotspot.param1;
508
509 if (compartment >= kObjectMax)
510 return kSceneInvalid;
511
512 if (getObjects()->get(compartment).entity) {
513 getSavePoints()->push(kEntityPlayer, getObjects()->get(compartment).entity, kActionOpenDoor, compartment);
514
515 // Stop processing further
516 return kSceneNone;
517 }
518
519 if (handleOtherCompartment(compartment, true, true)) {
520 // Stop processing further
521 return kSceneNone;
522 }
523
524 ObjectLocation location = getObjects()->get(compartment).status;
525 if (location == kObjectLocation1 || location == kObjectLocation3 || getEntities()->checkFields2(compartment)) {
526
527 if (location != kObjectLocation1 || getEntities()->checkFields2(compartment)
528 || (getInventory()->getSelectedItem() != kItemKey
529 && (compartment != kObjectCompartment1
530 || !getInventory()->hasItem(kItemKey)
531 || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))) {
532 if (!getSoundQueue()->isBuffered("LIB13"))
533 getSound()->playSoundEvent(kEntityPlayer, 13);
534
535 // Stop processing further
536 return kSceneNone;
537 }
538
539 getSound()->playSoundEvent(kEntityPlayer, 32);
540
541 if ((compartment >= kObjectCompartment1 && compartment <= kObjectCompartment3) || (compartment >= kObjectCompartmentA && compartment <= kObjectCompartmentF))
542 getObjects()->update(compartment, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
543
544 getSound()->playSoundEvent(kEntityPlayer, 15, 22);
545 getInventory()->unselectItem();
546
547 return kSceneInvalid;
548 }
549
550 if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->getSelectedItem() != kItemKey) {
551 if (compartment == kObjectCageMax) {
552 getSound()->playSoundEvent(kEntityPlayer, 26);
553 } else {
554 getSound()->playSoundEvent(kEntityPlayer, 14);
555 getSound()->playSoundEvent(kEntityPlayer, 15, 22);
556 }
557 return kSceneInvalid;
558 }
559
560 getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
561 getSound()->playSoundEvent(kEntityPlayer, 16);
562 getInventory()->unselectItem();
563
564 // Stop processing further
565 return kSceneNone;
566 }
567
568 //////////////////////////////////////////////////////////////////////////
569 // Action 7
570 IMPLEMENT_ACTION(playSounds)
571 getSound()->playSoundEvent(kEntityPlayer, hotspot.param1);
572 getSound()->playSoundEvent(kEntityPlayer, hotspot.param3, hotspot.param2);
573
574 return kSceneInvalid;
575 }
576
577 //////////////////////////////////////////////////////////////////////////
578 // Action 8
579 IMPLEMENT_ACTION(playAnimation)
580 if (getEvent(hotspot.param1))
581 return kSceneInvalid;
582
583 playAnimation((EventIndex)hotspot.param1);
584
585 if (!hotspot.scene)
586 getScenes()->processScene();
587
588 return kSceneInvalid;
589 }
590
591 //////////////////////////////////////////////////////////////////////////
592 // Action 9
593 IMPLEMENT_ACTION(openCloseObject)
594 ObjectIndex object = (ObjectIndex)hotspot.param1;
595 ObjectLocation location = (ObjectLocation)hotspot.param2;
596
597 if (object >= kObjectMax)
598 return kSceneInvalid;
599
600 getObjects()->update(object, getObjects()->get(object).entity, location, kCursorKeepValue, kCursorKeepValue);
601
602 bool isNotWindow = ((object <= kObjectCompartment8 || object >= kObjectHandleBathroom) && (object <= kObjectCompartmentH || object >= kObject48));
603
604 switch (location) {
605 default:
606 break;
607
608 case kObjectLocation1:
609 if (isNotWindow)
610 getSound()->playSoundEvent(kEntityPlayer, 24);
611 else
612 getSound()->playSoundEvent(kEntityPlayer, 21);
613 break;
614
615 case kObjectLocation2:
616 if (isNotWindow)
617 getSound()->playSoundEvent(kEntityPlayer, 36);
618 else
619 getSound()->playSoundEvent(kEntityPlayer, 20);
620 break;
621 }
622
623 return kSceneInvalid;
624 }
625
626 //////////////////////////////////////////////////////////////////////////
627 // Action 10
628 IMPLEMENT_ACTION(setModel)
629 ObjectIndex object = (ObjectIndex)hotspot.param1;
630 ObjectModel model = (ObjectModel)hotspot.param2;
631
632 if (object >= kObjectMax)
633 return kSceneInvalid;
634
635 getObjects()->updateModel(object, model);
636
637 if (object != kObject112 || getSoundQueue()->isBuffered("LIB096")) {
638 if (object == 1)
639 getSound()->playSoundEvent(kEntityPlayer, 73);
640 } else {
641 getSound()->playSoundEvent(kEntityPlayer, 96);
642 }
643
644 return kSceneInvalid;
645 }
646
647 //////////////////////////////////////////////////////////////////////////
648 // Action 11
649 IMPLEMENT_ACTION(setItem)
650 InventoryItem item = (InventoryItem)hotspot.param1;
651 if (item >= kPortraitOriginal)
652 return kSceneInvalid;
653
654 Inventory::InventoryEntry *entry = getInventory()->get(item);
655 if (entry->inPocket)
656 return kSceneInvalid;
657
658 entry->location = (ObjectLocation)hotspot.param2;
659
660 if (item == kItemCorpse) {
661 ObjectLocation corpseLocation = getInventory()->get(kItemCorpse)->location;
662 getProgress().eventCorpseMovedFromFloor = (corpseLocation == kObjectLocation3 || corpseLocation == kObjectLocation4);
663 }
664
665 return kSceneInvalid;
666 }
667
668 //////////////////////////////////////////////////////////////////////////
669 // Action 12
670 IMPLEMENT_ACTION(knockInside)
671 ObjectIndex object = (ObjectIndex)hotspot.param1;
672 if (object >= kObjectMax)
673 return kSceneInvalid;
674
675 if (getObjects()->get(object).entity)
676 getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
677
678 return kSceneInvalid;
679 }
680
681 //////////////////////////////////////////////////////////////////////////
682 // Action 13
683 IMPLEMENT_ACTION(pickItem)
684 InventoryItem item = (InventoryItem)hotspot.param1;
685 ObjectLocation location = (ObjectLocation)hotspot.param2;
686 bool process = (hotspot.scene == 0);
687 SceneIndex sceneIndex = kSceneInvalid;
688
689 if (item >= kPortraitOriginal)
690 return kSceneInvalid;
691
692 Inventory::InventoryEntry *entry = getInventory()->get(item);
693 if (!entry->location)
694 return kSceneInvalid;
695
696 // Special case for corpse
697 if (item == kItemCorpse) {
698 pickCorpse(location, process);
699 return kSceneInvalid;
700 }
701
702 // Add and process items
703 getInventory()->addItem(item);
704
705 switch (item) {
706 default:
707 break;
708
709 case kItemGreenJacket:
710 pickGreenJacket(process);
711 break;
712
713 case kItemScarf:
714 pickScarf(process);
715
716 // stop processing
717 return kSceneInvalid;
718
719 case kItemParchemin:
720 if (location != kObjectLocation2)
721 break;
722
723 getInventory()->addItem(kItemParchemin);
724 getInventory()->get(kItem11)->location = kObjectLocation1;
725 getSound()->playSoundEvent(kEntityPlayer, 9);
726 break;
727
728 case kItemBomb:
729 RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_catchCath);
730 break;
731
732 case kItemBriefcase:
733 getSound()->playSoundEvent(kEntityPlayer, 83);
734 break;
735 }
736
737 // Load item scene
738 if (getInventory()->get(item)->scene) {
739 if (!getState()->sceneUseBackup) {
740 getState()->sceneUseBackup = true;
741 getState()->sceneBackup = (hotspot.scene ? hotspot.scene : getState()->scene);
742 }
743
744 getScenes()->loadScene(getInventory()->get(item)->scene);
745
746 // do not process further
747 sceneIndex = kSceneNone;
748 }
749
750 // Select item
751 if (getInventory()->get(item)->isSelectable) {
752 getInventory()->selectItem(item);
753 _engine->getCursor()->setStyle(getInventory()->get(item)->cursor);
754 }
755
756 return sceneIndex;
757 }
758
759 //////////////////////////////////////////////////////////////////////////
760 // Action 14
761 IMPLEMENT_ACTION(dropItem)
762 InventoryItem item = (InventoryItem)hotspot.param1;
763 ObjectLocation location = (ObjectLocation)hotspot.param2;
764 bool process = (hotspot.scene == kSceneNone);
765
766 if (item >= kPortraitOriginal)
767 return kSceneInvalid;
768
769 if (!getInventory()->hasItem(item))
770 return kSceneInvalid;
771
772 if (location < kObjectLocation1)
773 return kSceneInvalid;
774
775 // Handle actions
776 if (item == kItemBriefcase) {
777 getSound()->playSoundEvent(kEntityPlayer, 82);
778
779 if (location == kObjectLocation2) {
780 if (!getProgress().field_58) {
781 getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
782 getProgress().field_58 = 1;
783 }
784
785 if (getInventory()->get(kItemParchemin)->location == kObjectLocation2) {
786 getInventory()->addItem(kItemParchemin);
787 getInventory()->get(kItem11)->location = kObjectLocation1;
788 getSound()->playSoundEvent(kEntityPlayer, 9);
789 }
790 }
791 }
792
793 // Update item location
794 getInventory()->removeItem(item, location);
795
796 if (item == kItemCorpse)
797 dropCorpse(process);
798
799 // Unselect item
800 getInventory()->unselectItem();
801
802 return kSceneInvalid;
803 }
804
805 //////////////////////////////////////////////////////////////////////////
806 // Action 15: Dummy action
807
808 //////////////////////////////////////////////////////////////////////////
809 // Action 16
810 IMPLEMENT_ACTION(enterCompartment)
811 if (getObjects()->get(kObjectCompartment1).status == kObjectLocation1 || getObjects()->get(kObjectCompartment1).status == kObjectLocation3 || getInventory()->getSelectedItem() == kItemKey)
812 return action_compartment(hotspot);
813
814 if (getProgress().eventCorpseFound) {
815 if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->get(kItemBriefcase)->location != kObjectLocation2)
816 return action_compartment(hotspot);
817
818 getSound()->playSoundEvent(kEntityPlayer, 14);
819 getSound()->playSoundEvent(kEntityPlayer, 15, 22);
820
821 if (getProgress().field_78 && !getSoundQueue()->isBuffered("MUS003")) {
822 getSound()->playSound(kEntityPlayer, "MUS003", kVolumeFull);
823 getProgress().field_78 = 0;
824 }
825
826 getScenes()->loadSceneFromPosition(kCarGreenSleeping, 77);
827
828 return kSceneNone;
829 }
830
831 getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
832 getSound()->playSound(kEntityPlayer, "LIB014");
833 playAnimation(kEventCathFindCorpse);
834 getSound()->playSound(kEntityPlayer, "LIB015");
835 getProgress().eventCorpseFound = true;
836
837 return kSceneCompartmentCorpse;
838 }
839
840 //////////////////////////////////////////////////////////////////////////
841 // Action 17: Dummy action
842
843 //////////////////////////////////////////////////////////////////////////
844 // Action 18
845 IMPLEMENT_ACTION(leanOutWindow)
846 ObjectIndex object = (ObjectIndex)hotspot.param1;
847
848 if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowNight) || getObjects()->get(kObjectCompartment1).model == kObjectModel1)
849 && getProgress().isTrainRunning
850 && (object != kObjectOutsideAnnaCompartment || (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).status == kObjectLocation2))
851 && getInventory()->getSelectedItem() != kItemFirebird
852 && getInventory()->getSelectedItem() != kItemBriefcase) {
853
854 switch (object) {
855 default:
856 return kSceneInvalid;
857
858 case kObjectOutsideTylerCompartment:
859 getEvent(kEventCathLookOutsideWindowDay) = 1;
860 playAnimation(isNight() ? kEventCathGoOutsideTylerCompartmentNight : kEventCathGoOutsideTylerCompartmentDay);
861 getProgress().field_C8 = 1;
862 break;
863
864 case kObjectOutsideBetweenCompartments:
865 getEvent(kEventCathLookOutsideWindowDay) = 1;
866 playAnimation(isNight() ? kEventCathGoOutsideNight : kEventCathGoOutsideDay);
867 getProgress().field_C8 = 1;
868 break;
869
870 case kObjectOutsideAnnaCompartment:
871 getEvent(kEventCathLookOutsideWindowDay) = 1;
872 playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
873 if (!hotspot.scene)
874 getScenes()->processScene();
875 break;
876 }
877 } else {
878 if (object == kObjectOutsideTylerCompartment || object == kObjectOutsideBetweenCompartments || object == kObjectOutsideAnnaCompartment) {
879 playAnimation(isNight() ? kEventCathLookOutsideWindowNight : kEventCathLookOutsideWindowDay);
880 getScenes()->processScene();
881 return kSceneNone;
882 }
883 }
884
885 return kSceneInvalid;
886 }
887
888 //////////////////////////////////////////////////////////////////////////
889 // Action 19
890 IMPLEMENT_ACTION(almostFall)
891 switch((ObjectIndex)hotspot.param1) {
892 default:
893 return kSceneInvalid;
894
895 case kObjectOutsideTylerCompartment:
896 playAnimation(isNight() ? kEventCathSlipTylerCompartmentNight : kEventCathSlipTylerCompartmentDay);
897 break;
898
899 case kObjectOutsideBetweenCompartments:
900 playAnimation(isNight() ? kEventCathSlipNight : kEventCathSlipDay);
901 break;
902 }
903
904 getProgress().field_C8 = 0;
905
906 if (!hotspot.scene)
907 getScenes()->processScene();
908
909 return kSceneInvalid;
910 }
911
912 //////////////////////////////////////////////////////////////////////////
913 // Action 20
914 IMPLEMENT_ACTION(climbInWindow)
915 switch ((ObjectIndex)hotspot.param1) {
916 default:
917 return kSceneInvalid;
918
919 case kObjectOutsideTylerCompartment:
920 playAnimation(isNight() ? kEventCathGetInsideTylerCompartmentNight : kEventCathGetInsideTylerCompartmentDay);
921 break;
922
923 case kObjectOutsideBetweenCompartments:
924 playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
925 break;
926
927 case kObjectOutsideAnnaCompartment:
928 playAnimation(kEventCathGettingInsideAnnaCompartment);
929 break;
930 }
931
932 if (!hotspot.scene)
933 getScenes()->processScene();
934
935 return kSceneInvalid;
936 }
937
938 //////////////////////////////////////////////////////////////////////////
939 // Action 21
940 IMPLEMENT_ACTION(climbLadder)
941 byte action = hotspot.param1;
942
943 if (action != 1 && action != 2)
944 return kSceneInvalid;
945
946 switch (getProgress().chapter) {
947 default:
948 break;
949
950 case kChapter2:
951 case kChapter3:
952 if (action == 2)
953 playAnimation(kEventCathClimbUpTrainGreenJacket);
954
955 playAnimation(kEventCathTopTrainGreenJacket);
956 break;
957
958 case kChapter5:
959 if (action == 2)
960 playAnimation(getProgress().isNightTime ? kEventCathClimbUpTrainNoJacketNight : kEventCathClimbUpTrainNoJacketDay);
961
962 playAnimation(getProgress().isNightTime ? kEventCathTopTrainNoJacketNight : kEventCathTopTrainNoJacketDay);
963 break;
964 }
965
966 if (!hotspot.scene)
967 getScenes()->processScene();
968
969 return kSceneInvalid;
970 }
971
972 //////////////////////////////////////////////////////////////////////////
973 // Action 22
974 IMPLEMENT_ACTION(climbDownTrain)
975 EventIndex evt = kEventNone;
976 switch (getProgress().chapter) {
977 default:
978 return kSceneInvalid;
979
980 case kChapter2:
981 case kChapter3:
982 evt = kEventCathClimbDownTrainGreenJacket;
983 break;
984
985 case kChapter5:
986 evt = (getProgress().isNightTime ? kEventCathClimbDownTrainNoJacketNight : kEventCathClimbDownTrainNoJacketDay);
987 break;
988 }
989
990 playAnimation(evt);
991 if (evt == kEventCathClimbDownTrainNoJacketDay)
992 getSound()->playSoundEvent(kEntityPlayer, 37);
993
994 if (!hotspot.scene)
995 getScenes()->processScene();
996
997 return kSceneInvalid;
998 }
999
1000 //////////////////////////////////////////////////////////////////////////
1001 // Action 23
1002 IMPLEMENT_ACTION(kronosSanctum)
1003 switch (hotspot.param1) {
1004 default:
1005 break;
1006
1007 case 1:
1008 getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionBreakCeiling);
1009 break;
1010
1011 case 2:
1012 getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionJumpDownCeiling);
1013 break;
1014
1015 case 3:
1016 if (getInventory()->getSelectedItem() == kItemBriefcase) {
1017 getInventory()->removeItem(kItemBriefcase, kObjectLocation3);
1018 getSound()->playSoundEvent(kEntityPlayer, 82);
1019 getInventory()->unselectItem();
1020 }
1021
1022 // Show animation with or without briefcase
1023 playAnimation((getInventory()->get(kItemBriefcase)->location - 3) ? kEventCathJumpUpCeilingBriefcase : kEventCathJumpUpCeiling);
1024
1025 if (!hotspot.scene)
1026 getScenes()->processScene();
1027
1028 break;
1029
1030 case 4:
1031 if (getProgress().chapter == kChapter1)
1032 getSavePoints()->push(kEntityPlayer, kEntityKronos, kAction202621266);
1033 break;
1034 }
1035
1036 return kSceneInvalid;
1037 }
1038
1039 //////////////////////////////////////////////////////////////////////////
1040 // Action 24
1041 IMPLEMENT_ACTION(escapeBaggage)
1042 byte action = hotspot.param1;
1043
1044 switch (action) {
1045 default:
1046 break;
1047
1048 case 1:
1049 playAnimation(kEventCathStruggleWithBonds);
1050 if (hotspot.scene)
1051 getScenes()->processScene();
1052 break;
1053
1054 case 2:
1055 playAnimation(kEventCathBurnRope);
1056 if (hotspot.scene)
1057 getScenes()->processScene();
1058 break;
1059
1060 case 3:
1061 if (getEvent(kEventCathBurnRope)) {
1062 playAnimation(kEventCathRemoveBonds);
1063 getProgress().field_84 = 0;
1064 getScenes()->loadSceneFromPosition(kCarBaggageRear, 89);
1065 return kSceneNone;
1066 }
1067 break;
1068
1069 case 4:
1070 if (!getEvent(kEventCathStruggleWithBonds2)) {
1071 playAnimation(kEventCathStruggleWithBonds2);
1072 getSound()->playSoundEvent(kEntityPlayer, 101);
1073 getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation2);
1074 if (!hotspot.scene)
1075 getScenes()->processScene();
1076 }
1077 break;
1078
1079 case 5:
1080 getSavePoints()->push(kEntityPlayer, kEntityIvo, kAction192637492);
1081 break;
1082 }
1083
1084 return kSceneInvalid;
1085 }
1086
1087 //////////////////////////////////////////////////////////////////////////
1088 // Action 25
1089 IMPLEMENT_ACTION(enterBaggage)
1090 switch(hotspot.param1) {
1091 default:
1092 break;
1093
1094 case 1:
1095 getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction272177921);
1096 break;
1097
1098 case 2:
1099 if (!getSoundQueue()->isBuffered("MUS021"))
1100 getSound()->playSound(kEntityPlayer, "MUS021", kVolumeFull);
1101 break;
1102
1103 case 3:
1104 getSound()->playSoundEvent(kEntityPlayer, 43);
1105 if (!getInventory()->hasItem(kItemKey) && !getEvent(kEventAnnaBaggageArgument)) {
1106 RESET_ENTITY_STATE(kEntityAnna, Anna, setup_baggageFight);
1107 return kSceneNone;
1108 }
1109 break;
1110 }
1111
1112 return kSceneInvalid;
1113 }
1114
1115 //////////////////////////////////////////////////////////////////////////
1116 // Action 26
1117 IMPLEMENT_ACTION(bombPuzzle)
1118 switch(hotspot.param1) {
1119 default:
1120 return kSceneInvalid;
1121
1122 case 1:
1123 getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction158610240);
1124 break;
1125
1126 case 2:
1127 getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225367984);
1128 getInventory()->unselectItem();
1129 return kSceneNone;
1130
1131 case 3:
1132 getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction191001984);
1133 return kSceneNone;
1134
1135 case 4:
1136 getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction201959744);
1137 return kSceneNone;
1138
1139 case 5:
1140 getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction169300225);
1141 break;
1142 }
1143
1144 return kSceneInvalid;
1145 }
1146
1147 //////////////////////////////////////////////////////////////////////////
1148 // Action 27
1149 IMPLEMENT_ACTION(27)
1150 if (!getSoundQueue()->isBuffered("LIB031", true))
1151 getSound()->playSoundEvent(kEntityPlayer, 31);
1152
1153 switch (getEntityData(kEntityPlayer)->car) {
1154 default:
1155 break;
1156
1157 case kCarGreenSleeping:
1158 getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction225358684, hotspot.param1);
1159 break;
1160
1161 case kCarRedSleeping:
1162 getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction225358684, hotspot.param1);
1163 break;
1164 }
1165
1166 return kSceneInvalid;
1167 }
1168
1169 //////////////////////////////////////////////////////////////////////////
1170 // Action 28
1171 IMPLEMENT_ACTION(kronosConcert)
1172 switch(hotspot.param1) {
1173 default:
1174 return kSceneInvalid;
1175
1176 case 1:
1177 playAnimation(kEventConcertSit);
1178 break;
1179
1180 case 2:
1181 playAnimation(kEventConcertCough);
1182 break;
1183 }
1184
1185 if (!hotspot.scene)
1186 getScenes()->processScene();
1187
1188 return kSceneInvalid;
1189 }
1190
1191 //////////////////////////////////////////////////////////////////////////
1192 // Action 29
1193 IMPLEMENT_ACTION(29)
1194 getProgress().field_C = 1;
1195 getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
1196
1197 Common::String filename = Common::String::format("MUS%03d", hotspot.param3);
1198 if (!getSoundQueue()->isBuffered(filename))
1199 getSound()->playSound(kEntityPlayer, filename, kVolumeFull);
1200
1201 return kSceneInvalid;
1202 }
1203
1204 //////////////////////////////////////////////////////////////////////////
1205 // Action 30
1206 IMPLEMENT_ACTION(catchBeetle)
1207 if (!getBeetle()->isLoaded())
1208 return kSceneInvalid;
1209
1210 if (getBeetle()->catchBeetle()) {
1211 getBeetle()->unload();
1212 getInventory()->get(kItemBeetle)->location = kObjectLocation1;
1213 getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionCatchBeetle);
1214 }
1215
1216 return kSceneInvalid;
1217 }
1218
1219 //////////////////////////////////////////////////////////////////////////
1220 // Action 31
1221 IMPLEMENT_ACTION(exitCompartment)
1222 if (!getProgress().field_30 && getProgress().jacket != kJacketOriginal) {
1223 getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
1224 getProgress().field_30 = 1;
1225 }
1226
1227 getObjects()->updateModel(kObjectCompartment1, (ObjectModel)hotspot.param2);
1228
1229 // fall to case enterCompartment action
1230 return action_enterCompartment(hotspot);
1231 }
1232
1233 //////////////////////////////////////////////////////////////////////////
1234 // Action 32
1235 IMPLEMENT_ACTION(outsideTrain)
1236 switch(hotspot.param1) {
1237 default:
1238 break;
1239
1240 case 1:
1241 getSavePoints()->push(kEntityPlayer, kEntitySalko, kAction167992577);
1242 break;
1243
1244 case 2:
1245 getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction202884544);
1246 break;
1247
1248 case 3:
1249 if (getProgress().chapter == kChapter5) {
1250 getSavePoints()->push(kEntityPlayer, kEntityAbbot, kAction168646401);
1251 getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction168646401);
1252 } else {
1253 getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction203339360);
1254 }
1255 // Stop processing further scenes
1256 return kSceneNone;
1257
1258 case 4:
1259 getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction169773228);
1260 break;
1261
1262 case 5:
1263 getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction167992577);
1264 break;
1265
1266 case 6:
1267 getSavePoints()->push(kEntityPlayer, kEntityAugust, kAction203078272);
1268 break;
1269 }
1270
1271 return kSceneInvalid;
1272 }
1273
1274 //////////////////////////////////////////////////////////////////////////
1275 // Action 33
1276 IMPLEMENT_ACTION(firebirdPuzzle)
1277 EventIndex evt = kEventNone;
1278 SceneIndex sceneIndex = kSceneInvalid;
1279
1280 switch (hotspot.param1) {
1281 default:
1282 break;
1283
1284 case 1:
1285 if (getEvent(kEventKronosBringFirebird)) {
1286 getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction205294778);
1287 break;
1288 }
1289
1290 if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) {
1291 evt = kEventCathOpenEgg;
1292
1293 Scene *scene = getScenes()->get(hotspot.scene);
1294 if (scene->getHotspot())
1295 sceneIndex = scene->getHotspot()->scene;
1296
1297 } else {
1298 evt = kEventCathOpenEggNoBackground;
1299 }
1300 getProgress().isEggOpen = true;
1301 break;
1302
1303 case 2:
1304 if (getEvent(kEventKronosBringFirebird)) {
1305 getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction224309120);
1306 break;
1307 }
1308
1309 evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathCloseEgg : kEventCathCloseEggNoBackground;
1310 getProgress().isEggOpen = false;
1311 break;
1312
1313 case 3:
1314 if (getEvent(kEventKronosBringFirebird)) {
1315 getSavePoints()->push(kEntityPlayer, kEntityAnna, kActionUseWhistle);
1316 break;
1317 }
1318
1319 evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathUseWhistleOpenEgg : kEventCathUseWhistleOpenEggNoBackground;
1320 break;
1321
1322 }
1323
1324 if (evt != kEventNone) {
1325 playAnimation(evt);
1326 if (sceneIndex == kSceneNone || !hotspot.scene)
1327 getScenes()->processScene();
1328 }
1329
1330 return sceneIndex;
1331 }
1332
1333 //////////////////////////////////////////////////////////////////////////
1334 // Action 34
1335 IMPLEMENT_ACTION(openMatchBox)
1336 // If the match is already in the inventory, do nothing
1337 if (!getInventory()->get(kItemMatch)->location
1338 || getInventory()->get(kItemMatch)->inPocket)
1339 return kSceneInvalid;
1340
1341 getInventory()->addItem(kItemMatch);
1342 getSound()->playSoundEvent(kEntityPlayer, 102);
1343
1344 return kSceneInvalid;
1345 }
1346
1347 //////////////////////////////////////////////////////////////////////////
1348 // Action 35
1349 IMPLEMENT_ACTION(openBed)
1350 getSound()->playSoundEvent(kEntityPlayer, 59);
1351
1352 return kSceneInvalid;
1353 }
1354
1355 //////////////////////////////////////////////////////////////////////////
1356 // Action 36: Dummy action
1357
1358 //////////////////////////////////////////////////////////////////////////
1359 // Action 37
1360 IMPLEMENT_ACTION(dialog)
1361 getSound()->playDialog(kEntityTables4, (EntityIndex)hotspot.param1, kVolumeFull, 0);
1362
1363 return kSceneInvalid;
1364 }
1365
1366 //////////////////////////////////////////////////////////////////////////
1367 // Action 38
1368 IMPLEMENT_ACTION(eggBox)
1369 getSound()->playSoundEvent(kEntityPlayer, 43);
1370 if (getProgress().field_7C && !getSoundQueue()->isBuffered("MUS003")) {
1371 getSound()->playSound(kEntityPlayer, "MUS003", kVolumeFull);
1372 getProgress().field_7C = 0;
1373 }
1374
1375 return kSceneInvalid;
1376 }
1377
1378 //////////////////////////////////////////////////////////////////////////
1379 // Action 39
1380 IMPLEMENT_ACTION(39)
1381 getSound()->playSoundEvent(kEntityPlayer, 24);
1382 if (getProgress().field_80 && !getSoundQueue()->isBuffered("MUS003")) {
1383 getSound()->playSound(kEntityPlayer, "MUS003", kVolumeFull);
1384 getProgress().field_80 = 0;
1385 }
1386
1387 return kSceneInvalid;
1388 }
1389
1390 //////////////////////////////////////////////////////////////////////////
1391 // Action 40
1392 IMPLEMENT_ACTION(bed)
1393 getSound()->playSoundEvent(kEntityPlayer, 85);
1394 // falls to case knockNoSound
1395 return action_knockInside(hotspot);
1396 }
1397
1398 //////////////////////////////////////////////////////////////////////////
1399 // Action 41
1400 IMPLEMENT_ACTION(playMusicChapter)
1401 byte id = 0;
1402 switch (getProgress().chapter) {
1403 default:
1404 break;
1405
1406 case kChapter1:
1407 id = hotspot.param1;
1408 break;
1409
1410 case kChapter2:
1411 case kChapter3:
1412 id = hotspot.param2;
1413 break;
1414
1415 case kChapter4:
1416 case kChapter5:
1417 id = hotspot.param3;
1418 break;
1419 }
1420
1421 if (id) {
1422 Common::String filename = Common::String::format("MUS%03d", id);
1423
1424 if (!getSoundQueue()->isBuffered(filename))
1425 getSound()->playSound(kEntityPlayer, filename, kVolumeFull);
1426 }
1427
1428 return kSceneInvalid;
1429 }
1430
1431 //////////////////////////////////////////////////////////////////////////
1432 // Action 42
1433 IMPLEMENT_ACTION(playMusicChapterSetupTrain)
1434 int id = 0;
1435 switch (getProgress().chapter) {
1436 default:
1437 break;
1438
1439 case kChapter1:
1440 id = 1;
1441 break;
1442
1443 case kChapter2:
1444 case kChapter3:
1445 id = 2;
1446 break;
1447
1448 case kChapter4:
1449 case kChapter5:
1450 id = 4;
1451 break;
1452 }
1453
1454 Common::String filename = Common::String::format("MUS%03d", hotspot.param1);
1455
1456 if (!getSoundQueue()->isBuffered(filename) && hotspot.param3 & id) {
1457 getSound()->playSound(kEntityPlayer, filename, kVolumeFull);
1458
1459 getSavePoints()->call(kEntityPlayer, kEntityTrain, kAction203863200, filename);
1460 getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction222746496, hotspot.param2);
1461 }
1462
1463 return kSceneInvalid;
1464 }
1465
1466 //////////////////////////////////////////////////////////////////////////
1467 // // Action 43
1468 IMPLEMENT_ACTION(switchChapter)
1469 // Nothing to do here as an hotspot action
1470 return kSceneInvalid;
1471 }
1472
1473 //////////////////////////////////////////////////////////////////////////
1474 // Action 44
1475 IMPLEMENT_ACTION(44)
1476 switch (hotspot.param1) {
1477 default:
1478 break;
1479
1480 case 1:
1481 getSavePoints()->push(kEntityPlayer, kEntityRebecca, kAction205034665);
1482 break;
1483
1484 case 2:
1485 getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225358684);
1486 break;
1487 }
1488
1489 return kSceneInvalid;
1490 }
1491
1492 //////////////////////////////////////////////////////////////////////////
1493 // Helper functions
1494 //////////////////////////////////////////////////////////////////////////
1495 void Action::pickGreenJacket(bool process) const {
1496 getProgress().jacket = kJacketGreen;
1497 getInventory()->addItem(kItemMatchBox);
1498
1499 getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
1500 playAnimation(kEventPickGreenJacket);
1501
1502 getInventory()->setPortrait(kPortraitGreen);
1503
1504 if (process)
1505 getScenes()->processScene();
1506 }
1507
1508 void Action::pickScarf(bool process) const {
1509 playAnimation(getProgress().jacket == kJacketGreen ? kEventPickScarfGreen : kEventPickScarfOriginal);
1510
1511 if (process)
1512 getScenes()->processScene();
1513 }
1514
1515 void Action::pickCorpse(ObjectLocation bedPosition, bool process) const {
1516
1517 if (getProgress().jacket == kJacketOriginal)
1518 getProgress().jacket = kJacketBlood;
1519
1520 switch(getInventory()->get(kItemCorpse)->location) {
1521 // No way to pick the corpse
1522 default:
1523 break;
1524
1525 // Floor
1526 case kObjectLocation1:
1527 // Bed is fully opened, move corpse directly there
1528 if (bedPosition == 4) {
1529 playAnimation(kEventCorpsePickFloorOpenedBedOriginal);
1530
1531 getInventory()->get(kItemCorpse)->location = kObjectLocation5;
1532 break;
1533 }
1534
1535 playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickFloorGreen : kEventCorpsePickFloorOriginal);
1536 break;
1537
1538 // Bed
1539 case kObjectLocation2:
1540 playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickBedGreen : kEventCorpsePickBedOriginal);
1541 break;
1542 }
1543
1544 if (process)
1545 getScenes()->processScene();
1546
1547 // Add corpse to inventory
1548 if (bedPosition != 4) { // bed is not fully opened
1549 getInventory()->addItem(kItemCorpse);
1550 getInventory()->selectItem(kItemCorpse);
1551 _engine->getCursor()->setStyle(kCursorCorpse);
1552 }
1553 }
1554
1555 void Action::dropCorpse(bool process) const {
1556 switch(getInventory()->get(kItemCorpse)->location) {
1557 default:
1558 break;
1559
1560 case kObjectLocation1: // Floor
1561 playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropFloorGreen : kEventCorpseDropFloorOriginal);
1562 break;
1563
1564 case kObjectLocation2: // Bed
1565 playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropBedGreen : kEventCorpseDropBedOriginal);
1566 break;
1567
1568 case kObjectLocation4: // Window
1569 // Say goodbye to an old friend
1570 getInventory()->get(kItemCorpse)->location = kObjectLocationNone;
1571 getProgress().eventCorpseThrown = true;
1572
1573 if (getState()->time <= kTime1138500) {
1574 playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropWindowGreen : kEventCorpseDropWindowOriginal);
1575
1576 getProgress().field_24 = true;
1577 } else {
1578 playAnimation(kEventCorpseDropBridge);
1579 }
1580
1581 getProgress().eventCorpseMovedFromFloor = true;
1582 break;
1583 }
1584
1585 if (process)
1586 getScenes()->processScene();
1587 }
1588
1589 bool Action::handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const {
1590 #define ENTITY_PARAMS(entity, index, id) \
1591 ((EntityData::EntityParametersIIII*)getEntities()->get(entity)->getParamData()->getParameters(8, index))->param##id
1592
1593 // Only handle compartments
1594 if (getEntityData(kEntityPlayer)->location != kLocationOutsideCompartment
1595 || ((object < kObjectCompartment2 || object > kObjectCompartment8) && (object < kObjectCompartmentA || object > kObjectCompartmentH)))
1596 return false;
1597
1598 //////////////////////////////////////////////////////////////////////////
1599 // Gendarmes
1600 if (getEntityData(kEntityPlayer)->car == getEntityData(kEntityGendarmes)->car
1601 && getEntityData(kEntityGendarmes)->location == kLocationOutsideCompartment
1602 && !getEntities()->compare(kEntityPlayer, kEntityGendarmes)) {
1603 if (doPlaySound)
1604 playCompartmentSoundEvents(object);
1605
1606 if (doLoadScene)
1607 getScenes()->loadSceneFromObject(object);
1608
1609 return true;
1610 }
1611
1612 //////////////////////////////////////////////////////////////////////////
1613 // Mertens
1614 if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping
1615 && getEntityData(kEntityMertens)->car == kCarGreenSleeping
1616 && !getEntityData(kEntityMertens)->location
1617 && !ENTITY_PARAMS(kEntityMertens, 0, 1)) {
1618
1619 if (!getEntities()->compare(kEntityPlayer, kEntityMertens)) {
1620
1621 if (getEntityData(kEntityMertens)->entityPosition < kPosition_2740
1622 && getEntityData(kEntityMertens)->entityPosition > kPosition_850
1623 && (getEntityData(kEntityCoudert)->car != kCarGreenSleeping || getEntityData(kEntityCoudert)->entityPosition > kPosition_2740)
1624 && (getEntityData(kEntityVerges)->car != kCarGreenSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)) {
1625 if (doPlaySound)
1626 playCompartmentSoundEvents(object);
1627
1628 if (!getSoundQueue()->isBuffered(kEntityMertens))
1629 getSound()->playWarningCompartment(kEntityMertens, object);
1630
1631 getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction305159806);
1632
1633 if (doLoadScene)
1634 getScenes()->loadSceneFromObject(object);
1635
1636 return true;
1637 }
1638
1639 if (getEntityData(kEntityMertens)->direction == kDirectionUp
1640 && getEntityData(kEntityMertens)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
1641 if (doPlaySound)
1642 playCompartmentSoundEvents(object);
1643
1644 if (!getSoundQueue()->isBuffered(kEntityMertens))
1645 getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
1646
1647 if (doLoadScene)
1648 getScenes()->loadSceneFromObject(object);
1649 }
1650
1651 if (getEntityData(kEntityMertens)->direction == kDirectionDown
1652 && getEntityData(kEntityMertens)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
1653 if (doPlaySound)
1654 playCompartmentSoundEvents(object);
1655
1656 if (!getSoundQueue()->isBuffered(kEntityMertens))
1657 getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
1658
1659 if (doLoadScene)
1660 getScenes()->loadSceneFromObject(object, true);
1661 }
1662 }
1663 }
1664
1665 //////////////////////////////////////////////////////////////////////////
1666 // Coudert
1667 if (getEntityData(kEntityPlayer)->car != kCarRedSleeping
1668 || !getEntityData(kEntityCoudert)->car
1669 || getEntityData(kEntityCoudert)->location != kLocationOutsideCompartment
1670 || ENTITY_PARAMS(kEntityCoudert, 0, 1))
1671 return false;
1672
1673 if (!getEntities()->compare(kEntityPlayer, kEntityCoudert)) {
1674
1675 if (getEntityData(kEntityCoudert)->entityPosition < kPosition_2740
1676 && getEntityData(kEntityCoudert)->entityPosition > kPosition_850
1677 && (getEntityData(kEntityMertens)->car != kCarRedSleeping || getEntityData(kEntityMertens)->entityPosition > kPosition_2740)
1678 && (getEntityData(kEntityVerges)->car != kCarRedSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)
1679 && (getEntityData(kEntityMmeBoutarel)->car != kCarRedSleeping || getEntityData(kEntityMmeBoutarel)->entityPosition > kPosition_2740)) {
1680 if (doPlaySound)
1681 playCompartmentSoundEvents(object);
1682
1683 if (!getSoundQueue()->isBuffered(kEntityCoudert))
1684 getSound()->playWarningCompartment(kEntityCoudert, object);
1685
1686 getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction305159806);
1687
1688 if (doLoadScene)
1689 getScenes()->loadSceneFromObject(object);
1690
1691 return true;
1692 }
1693
1694 // Direction = Up
1695 if (getEntityData(kEntityCoudert)->direction == kDirectionUp
1696 && getEntityData(kEntityCoudert)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
1697 if (doPlaySound)
1698 playCompartmentSoundEvents(object);
1699
1700 if (!getSoundQueue()->isBuffered(kEntityCoudert))
1701 getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
1702
1703 if (doLoadScene)
1704 getScenes()->loadSceneFromObject(object);
1705
1706 return true;
1707 }
1708
1709 // Direction = down
1710 if (getEntityData(kEntityCoudert)->direction == kDirectionDown
1711 && getEntityData(kEntityCoudert)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
1712 if (doPlaySound)
1713 playCompartmentSoundEvents(object);
1714
1715 if (!getSoundQueue()->isBuffered(kEntityCoudert))
1716 getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
1717
1718 if (doLoadScene)
1719 getScenes()->loadSceneFromObject(object, true);
1720 }
1721 }
1722
1723 return false;
1724 }
1725
1726 void Action::playCompartmentSoundEvents(ObjectIndex object) const {
1727 if (getObjects()->get(object).status == kObjectLocation1 || getObjects()->get(object).status == kObjectLocation3 || getEntities()->checkFields2(object)) {
1728 getSound()->playSoundEvent(kEntityPlayer, 13);
1729 } else {
1730 getSound()->playSoundEvent(kEntityPlayer, 14);
1731 getSound()->playSoundEvent(kEntityPlayer, 15, 3);
1732 }
1733 }
1734
1735 //////////////////////////////////////////////////////////////////////////
1736 // Cursors
1737 //////////////////////////////////////////////////////////////////////////
1738 CursorStyle Action::getCursor(const SceneHotspot &hotspot) const {
1739 // Simple cursor style
1740 if (hotspot.cursor != kCursorProcess)
1741 return (CursorStyle)hotspot.cursor;
1742
1743 ObjectIndex object = (ObjectIndex)hotspot.param1;
1744
1745 switch (hotspot.action) {
1746 default:
1747 return kCursorNormal;
1748
1749 case SceneHotspot::kActionInventory:
1750 if (!getState()->sceneBackup2 && (getEvent(kEventKronosBringFirebird) || getProgress().isEggOpen))
1751 return kCursorNormal;
1752 else
1753 return kCursorBackward;
1754
1755 case SceneHotspot::kActionKnockOnDoor:
1756 debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object);
1757 if (object >= kObjectMax)
1758 return kCursorNormal;
1759 else
1760 return (CursorStyle)getObjects()->get(object).windowCursor;
1761
1762 case SceneHotspot::kAction12:
1763 debugC(2, kLastExpressDebugScenes, "================================= OBJECT %03d =================================", object);
1764 if (object >= kObjectMax)
1765 return kCursorNormal;
1766
1767 if (getObjects()->get(object).entity)
1768 return (CursorStyle)getObjects()->get(object).windowCursor;
1769 else
1770 return kCursorNormal;
1771
1772 case SceneHotspot::kActionPickItem:
1773 debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object);
1774 if (object >= kObjectCompartmentA)
1775 return kCursorNormal;
1776
1777 if ((!getInventory()->getSelectedItem() || getInventory()->getSelectedEntry()->floating)
1778 && (object != kObject21 || getProgress().eventCorpseMovedFromFloor))
1779 return kCursorHand;
1780 else
1781 return kCursorNormal;
1782
1783 case SceneHotspot::kActionDropItem:
1784 debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object);
1785 if (object >= kObjectCompartmentA)
1786 return kCursorNormal;
1787
1788 if (getInventory()->getSelectedItem() != (InventoryItem)object)
1789 return kCursorNormal;
1790
1791 if (object == kObject20 && hotspot.param2 == 4 && !getProgress().isTrainRunning)
1792 return kCursorNormal;
1793
1794 if (object == kObjectHandleInsideBathroom && hotspot.param2 == 1 && getProgress().field_5C)
1795 return kCursorNormal;
1796
1797 return (CursorStyle)getInventory()->getSelectedEntry()->cursor;
1798
1799 case SceneHotspot::kAction15:
1800 if (object >= kObjectMax)
1801 return kCursorNormal;
1802
1803 if (getProgress().isEqual(object, hotspot.param2))
1804 return (CursorStyle)hotspot.param3;
1805
1806 return kCursorNormal;
1807
1808 case SceneHotspot::kActionEnterCompartment:
1809 if ((getInventory()->getSelectedItem() != kItemKey || getObjects()->get(kObjectCompartment1).status)
1810 && (getObjects()->get(kObjectCompartment1).status != 1 || !getInventory()->hasItem(kItemKey)
1811 || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))
1812 goto LABEL_KEY;
1813
1814 return (CursorStyle)getInventory()->get(kItemKey)->cursor; // TODO is that always the same as kCursorKey ?
1815
1816 case SceneHotspot::kActionGetOutsideTrain:
1817 if (getProgress().jacket != kJacketGreen)
1818 return kCursorNormal;
1819
1820 if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowNight) || getObjects()->get(kObjectCompartment1).model == kObjectModel1)
1821 && getProgress().isTrainRunning
1822 && (object != kObjectOutsideAnnaCompartment || (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).status == 2))
1823 && getInventory()->getSelectedItem() != kItemBriefcase && getInventory()->getSelectedItem() != kItemFirebird)
1824 return kCursorForward;
1825
1826 return (getObjects()->get(kObjectCompartment1).model < kObjectModel2) ? kCursorNormal : kCursorMagnifier;
1827
1828 case SceneHotspot::kActionSlip:
1829 return (getProgress().field_C8 < 1) ? kCursorNormal : kCursorLeft;
1830
1831 case SceneHotspot::kActionClimbUpTrain:
1832 if (getProgress().isTrainRunning
1833 && (getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3 || getProgress().chapter == kChapter5)
1834 && getInventory()->getSelectedItem() != kItemFirebird
1835 && getInventory()->getSelectedItem() != kItemBriefcase)
1836 return kCursorUp;
1837
1838 return kCursorNormal;
1839
1840 case SceneHotspot::kActionJumpUpDownTrain:
1841 if (object != kObjectCompartment1)
1842 return kCursorNormal;
1843
1844 return (getObjects()->get(kObjectCeiling).status < kObjectLocation1) ? kCursorHand : kCursorNormal;
1845
1846 case SceneHotspot::kActionUnbound:
1847 if (hotspot.param2 != 2)
1848 return kCursorNormal;
1849
1850 if (getEvent(kEventCathBurnRope) || !getEvent(kEventCathStruggleWithBonds2))
1851 return kCursorNormal;
1852
1853 return kCursorHand;
1854
1855 case SceneHotspot::kActionCatchBeetle:
1856 if (!getBeetle()->isLoaded())
1857 return kCursorNormal;
1858
1859 if (!getBeetle()->isCatchable())
1860 return kCursorNormal;
1861
1862 if (getInventory()->getSelectedItem() == kItemMatchBox && getInventory()->hasItem(kItemMatch))
1863 return (CursorStyle)getInventory()->get(kItemMatchBox)->cursor;
1864
1865 return kCursorHandPointer;
1866
1867 case SceneHotspot::KActionUseWhistle:
1868 if (object != kObjectCompartment3)
1869 return kCursorNormal;
1870
1871 if (getInventory()->getSelectedItem() == kItemWhistle)
1872 return kCursorWhistle;
1873 else
1874 return kCursorNormal;
1875
1876 case SceneHotspot::kActionOpenBed:
1877 if (getProgress().chapter < kChapter2)
1878 return kCursorHand;
1879
1880 return kCursorNormal;
1881
1882 case SceneHotspot::kActionDialog:
1883 if (getSound()->getDialogName((EntityIndex)object))
1884 return kCursorHandPointer;
1885
1886 return kCursorNormal;
1887
1888 case SceneHotspot::kActionBed:
1889 if (getProgress().field_18 == 2 && !getProgress().field_E4
1890 && (getState()->time > kTimeBedTime
1891 || (getProgress().eventMetAugust && getProgress().field_CC
1892 && (!getProgress().field_24 || getProgress().field_3C))))
1893 return kCursorSleep;
1894
1895 return kCursorNormal;
1896
1897 LABEL_KEY:
1898 // Handle compartment action
1899 case SceneHotspot::kActionCompartment:
1900 case SceneHotspot::kActionExitCompartment:
1901 debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object);
1902 if (object >= kObjectMax)
1903 return kCursorNormal;
1904
1905 if (getInventory()->getSelectedItem() != kItemKey
1906 || getObjects()->get(object).entity
1907 || getObjects()->get(object).status != 1
1908 || !getObjects()->get(object).handleCursor
1909 || getEntities()->isInsideCompartments(kEntityPlayer)
1910 || getEntities()->checkFields2(object))
1911 return (CursorStyle)getObjects()->get(object).handleCursor;
1912 else
1913 return (CursorStyle)getInventory()->get(kItemKey)->cursor;
1914 }
1915 }
1916
1917 //////////////////////////////////////////////////////////////////////////
1918 // Animation
1919 //////////////////////////////////////////////////////////////////////////
1920
1921 // Play an animation and add delta time to global game time
1922 void Action::playAnimation(EventIndex index, bool debugMode) const {
1923 if (index >= _animationListSize)
1924 error("[Action::playAnimation] Invalid event index (value=%i, max=%i)", index, _animationListSize);
1925
1926 // In debug mode, just show the animation
1927 if (debugMode) {
1928 Animation animation;
1929 if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis")))
1930 animation.play();
1931 return;
1932 }
1933
1934 getFlags()->flag_3 = true;
1935
1936 // Hide cursor
1937 _engine->getCursor()->show(false);
1938
1939 // Show inventory & hourglass
1940 getInventory()->show();
1941 getInventory()->showHourGlass();
1942
1943 if (!getFlags()->mouseRightClick) {
1944
1945 if (getGlobalTimer()) {
1946 if (getSoundQueue()->isBuffered("TIMER")) {
1947 getSoundQueue()->fade("TIMER");
1948 setGlobalTimer(105);
1949 }
1950 }
1951
1952 bool processSound = false;
1953 if (index >= kEventCorpseDropFloorOriginal
1954 || index == kEventCathWakingUp
1955 || index == kEventConcertCough
1956 || index == kEventConcertSit
1957 || index == kEventConcertLeaveWithBriefcase)
1958 processSound = true;
1959
1960 Animation animation;
1961 if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis") , processSound ? Animation::kFlagDefault : Animation::kFlagProcess))
1962 animation.play();
1963
1964 if (getSoundQueue()->isBuffered("TIMER"))
1965 getSoundQueue()->stop("TIMER");
1966 }
1967
1968 // Show cursor
1969 _engine->getCursor()->show(true);
1970
1971 getEvent(index) = 1;
1972
1973 // Adjust game time
1974 getState()->timeTicks += _animationList[index].time;
1975 getState()->time = (TimeValue)(getState()->time + (TimeValue)(_animationList[index].time * getState()->timeDelta));
1976 }
1977
1978 } // End of namespace LastExpress
1979