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 * Additional copyright for this file:
8 * Copyright (C) 1995-2013 Presto Studios, Inc.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "pegasus/pegasus.h"
27 #include "pegasus/neighborhood/mars/canyonchase.h"
28 #include "pegasus/neighborhood/mars/mars.h"
29
30 namespace Pegasus {
31
32 // Segment start and end points.
33
34 //static const TimeValue kPrepStart = 0;
35 static const TimeValue kPrepEnd = 3000;
36 static const TimeValue kLaunchStart = kPrepEnd;
37 static const TimeValue kLaunchEnd = 6640;
38 static const TimeValue kBranch1Start = kLaunchEnd;
39 static const TimeValue kBranch1End = 22240;
40 static const TimeValue kBranch2Start = kBranch1End;
41 static const TimeValue kBranch2End = 28440;
42 static const TimeValue kBranch3Start = kBranch2End;
43 static const TimeValue kBranch3End = 38640;
44 static const TimeValue kBranch4Start = kBranch3End;
45 static const TimeValue kBranch4End = 43880;
46 static const TimeValue kBranch5Start = kBranch4End;
47 static const TimeValue kBranch5End = 58680;
48 static const TimeValue kExitStart = kBranch5End;
49 static const TimeValue kExitEnd = 66480;
50 static const TimeValue kExitLoopPoint = 66200;
51 static const TimeValue kExitGenoPoint = 62560;
52
53 // Death start and end points.
54
55 static const TimeValue kDeath1Start = 0;
56 static const TimeValue kDeath1End = 2400;
57 static const TimeValue kDeath2Start = kDeath1End;
58 static const TimeValue kDeath2End = 4720;
59 static const TimeValue kDeath3Start = kDeath2End;
60 static const TimeValue kDeath3End = 7120;
61 static const TimeValue kDeath4Start = kDeath3End;
62 static const TimeValue kDeath4End = 9280;
63 static const TimeValue kDeath5Start = kDeath4End;
64 static const TimeValue kDeath5End = 12000;
65
66 // Chase state.
67
68 enum {
69 kCanyonLaunch,
70 kCanyonBranch1Left,
71 kCanyonBranch1Right,
72 kCanyonBranch2Left,
73 kCanyonBranch2Right,
74 kCanyonBranch3Left,
75 kCanyonBranch4Left,
76 kCanyonBranch4Right,
77 kCanyonBranch5Left,
78 kCanyonBranch5Right,
79 kCanyonExit,
80 kCanyonLoop
81 };
82
fire()83 void MusicTimerEvent::fire() {
84 canyonChase->musicTimerExpired(*this);
85 }
86
CanyonChase(Neighborhood * handler)87 CanyonChase::CanyonChase(Neighborhood *handler) : ChaseInteraction(kMarsCanyonChaseInteractionID, handler,
88 kMarsCanyonChaseNotificationID, (PegasusEngine *)g_engine), _canyonMovie1(kNoDisplayElement),
89 _canyonMovie2(kNoDisplayElement), _deathMovie(kNoDisplayElement), _genoMovie(kNoDisplayElement) {
90 _currentMovie = NULL;
91 _currentCallBack = NULL;
92 }
93
setSoundFXLevel(const uint16 fxLevel)94 void CanyonChase::setSoundFXLevel(const uint16 fxLevel) {
95 _canyonMovie1.setVolume(fxLevel);
96 _canyonMovie2.setVolume(fxLevel);
97 _deathMovie.setVolume(fxLevel);
98 }
99
setAmbienceLevel(const uint16 level)100 void CanyonChase::setAmbienceLevel(const uint16 level) {
101 _genoMovie.setVolume(level);
102 _musicFader.setMasterVolume(level);
103 }
104
startCanyonMusicLoop(void)105 void CanyonChase::startCanyonMusicLoop(void) {
106 FaderMoveSpec spec;
107
108 _musicLoop.loopSound();
109 spec.makeTwoKnotFaderSpec(10, 0, 0, 1, 255);
110 _musicFader.startFader(spec);
111 }
112
stopCanyonMusicLoop(const long ticks)113 void CanyonChase::stopCanyonMusicLoop(const long ticks) {
114 FaderMoveSpec spec;
115
116 spec.makeTwoKnotFaderSpec(10, 0, 255, ticks, 0);
117 _musicFader.startFader(spec);
118 }
119
openInteraction()120 void CanyonChase::openInteraction() {
121 _canyonMovie1.initFromMovieFile("Images/Mars/Canyon_hq1.mov");
122 _canyonMovie1.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
123 _canyonMovie1.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
124 _canyonMovie1.setDisplayOrder(kShuttleMonitorOrder);
125
126 _canyon1CallBack.setNotification(&_chaseNotification);
127 _canyon1CallBack.initCallBack(&_canyonMovie1, kCallBackAtExtremes);
128 _canyon1CallBack.setCallBackFlag(kChaseEnteredBranchZone);
129 _canyon1CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
130
131 _canyonMovie2.initFromMovieFile("Images/Mars/Canyon_hq2.mov");
132 _canyonMovie2.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
133 _canyonMovie2.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
134 _canyonMovie2.setDisplayOrder(kShuttleMonitorOrder);
135
136 _canyon2CallBack.setNotification(&_chaseNotification);
137 _canyon2CallBack.initCallBack(&_canyonMovie2, kCallBackAtExtremes);
138 _canyon2CallBack.setCallBackFlag(kChaseEnteredBranchZone);
139 _canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
140
141 _deathMovie.initFromMovieFile("Images/Mars/Canyon_hqD.mov");
142 _deathMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
143 _deathMovie.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
144 _deathMovie.setDisplayOrder(kShuttleMonitorOrder);
145
146 _deathCallBack.setNotification(&_chaseNotification);
147 _deathCallBack.initCallBack(&_deathMovie, kCallBackAtExtremes);
148 _deathCallBack.setCallBackFlag(kChaseFinished);
149 _deathCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
150
151 _musicLoop.attachFader(&_musicFader);
152 _musicLoop.initFromAIFFFile("Sounds/Mars/Canyon Loop.44K.16.AIFF");
153 _musicFader.setMasterVolume(((PegasusEngine *)g_engine)->getAmbienceLevel());
154
155 ChaseInteraction::openInteraction();
156
157 _steerPict.setDisplayOrder(kShuttleMonitorOrder + 1);
158 _steerPict.moveElementTo(kShuttleSteerLeft, kShuttleSteerTop);
159 }
160
initInteraction()161 void CanyonChase::initInteraction() {
162 _steerPict.startDisplaying();
163
164 // Launch branch is identical in both movies
165 _canyonState = kCanyonLaunch;
166 _canyonMovie1.setSegment(kLaunchStart, kLaunchEnd - kDecisionTime);
167 _canyonMovie1.setTime(kLaunchStart);
168 switchTo(_canyonMovie1, _canyon1CallBack);
169 startCanyonMusicLoop();
170 ChaseInteraction::initInteraction();
171 }
172
closeInteraction()173 void CanyonChase::closeInteraction() {
174 _canyonMovie1.stop();
175 _canyonMovie1.stopDisplaying();
176 _canyonMovie1.releaseMovie();
177 _canyon1CallBack.releaseCallBack();
178
179 _canyonMovie2.stop();
180 _canyonMovie2.stopDisplaying();
181 _canyonMovie2.releaseMovie();
182 _canyon2CallBack.releaseCallBack();
183
184 _deathMovie.stop();
185 _deathMovie.stopDisplaying();
186 _deathMovie.releaseMovie();
187 _deathCallBack.releaseCallBack();
188
189 _genoMovie.stop();
190 _genoMovie.stopDisplaying();
191 _genoMovie.releaseMovie();
192 _genoCallBack.releaseCallBack();
193
194 ChaseInteraction::closeInteraction();
195 }
196
receiveNotification(Notification * notification,const NotificationFlags flags)197 void CanyonChase::receiveNotification(Notification *notification, const NotificationFlags flags) {
198 Input input;
199
200 if (notification == &_chaseNotification && flags == kChaseFinished) {
201 if (_canyonState == kCanyonLoop) {
202 // Swallow the notification if we loop back to the beginning
203 InputDevice.getInput(input, kFilterAllInput);
204 if (JMPPPInput::isEasterEggModifierInput(input)) {
205 stopCanyonMusicLoop(15);
206 doGenoChase();
207 } else {
208 _canyonMovie2.setSegment(kExitGenoPoint, kExitLoopPoint - kDecisionTime);
209 _canyonMovie2.setTime(kExitGenoPoint);
210 switchTo(_canyonMovie2, _canyon2CallBack);
211 _canyon2CallBack.setCallBackFlag(kChaseEnteredBranchZone);
212 _canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
213 _canyonState = kCanyonLaunch;
214 }
215 return;
216 } else if (_canyonState != kCanyonExit) {
217 // We died
218 ((Mars *)_owner)->die(kDeathRanIntoCanyonWall);
219 }
220 }
221 ChaseInteraction::receiveNotification(notification, flags);
222 }
223
setUpBranch()224 void CanyonChase::setUpBranch() {
225 TimeValue branchStart, branchEnd;
226
227 branchStart = 0;
228 branchEnd = 0;
229 switch (_canyonState) {
230 case kCanyonLaunch:
231 case kCanyonExit:
232 branchStart = kLaunchEnd - kDecisionTime;
233 branchEnd = kLaunchEnd;
234 break;
235 case kCanyonBranch1Left:
236 case kCanyonBranch1Right:
237 branchStart = kBranch1End - kDecisionTime;
238 branchEnd = kBranch1End;
239 break;
240 case kCanyonBranch2Left:
241 case kCanyonBranch2Right:
242 branchStart = kBranch2End - kDecisionTime;
243 branchEnd = kBranch2End;
244 break;
245 case kCanyonBranch3Left:
246 branchStart = kBranch3End - kDecisionTime;
247 branchEnd = kBranch3End;
248 break;
249 case kCanyonBranch4Left:
250 case kCanyonBranch4Right:
251 branchStart = kBranch4End - kDecisionTime;
252 branchEnd = kBranch4End;
253 break;
254 case kCanyonBranch5Left:
255 case kCanyonBranch5Right:
256 branchStart = kBranch5End - kDecisionTime;
257 branchEnd = kBranch5End;
258 break;
259 default:
260 break;
261 }
262
263 _currentMovie->setSegment(branchStart, branchEnd);
264 // Need to call SetTime here in case we loop
265 _currentMovie->setTime(branchStart);
266
267 _currentCallBack->setCallBackFlag(kChaseExitedBranchZone);
268 _currentCallBack->scheduleCallBack(kTriggerAtStop, 0, 0);
269 }
270
branchLeft()271 void CanyonChase::branchLeft() {
272 TimeValue branchStart, branchEnd;
273 Movie *movie;
274 NotificationCallBack *callBack;
275
276 branchStart = 0;
277 branchEnd = 0;
278 switch (_canyonState) {
279 case kCanyonLaunch:
280 branchStart = kBranch1Start;
281 branchEnd = kBranch1End - kDecisionTime;
282 _canyonState = kCanyonBranch1Left;
283 break;
284 case kCanyonBranch1Left:
285 case kCanyonBranch1Right:
286 branchStart = kBranch2Start;
287 branchEnd = kBranch2End - kDecisionTime;
288 _canyonState = kCanyonBranch2Left;
289 break;
290 case kCanyonBranch2Left:
291 case kCanyonBranch2Right:
292 branchStart = kBranch3Start;
293 branchEnd = kBranch3End - kDecisionTime;
294 _canyonState = kCanyonBranch3Left;
295 break;
296 case kCanyonBranch3Left:
297 branchStart = kBranch4Start;
298 branchEnd = kBranch4End - kDecisionTime;
299 _canyonState = kCanyonBranch4Left;
300 break;
301 case kCanyonBranch4Left:
302 case kCanyonBranch4Right:
303 branchStart = kBranch5Start;
304 branchEnd = kBranch5End - kDecisionTime;
305 _canyonState = kCanyonBranch5Left;
306 break;
307 case kCanyonBranch5Left:
308 case kCanyonBranch5Right:
309 dontBranch();
310 return;
311 default:
312 break;
313 }
314
315 // Left branches are in hq2 (except exit)
316 // Segment 5 branches are switched
317 if (_canyonState == kCanyonBranch5Left || _canyonState == kCanyonBranch5Right) {
318 movie = &_canyonMovie1;
319 callBack = &_canyon1CallBack;
320 } else {
321 movie = &_canyonMovie2;
322 callBack = &_canyon2CallBack;
323 }
324
325 movie->setSegment(branchStart, branchEnd);
326 movie->setTime(branchStart);
327
328 switchTo(*movie, *callBack);
329
330 callBack->setCallBackFlag(kChaseEnteredBranchZone);
331 callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
332 }
333
branchRight()334 void CanyonChase::branchRight() {
335 TimeValue branchStart, branchEnd;
336 NotificationFlags flag;
337 Movie *movie;
338 NotificationCallBack *callBack;
339
340 branchStart = 0;
341 branchEnd = 0;
342 flag = 0;
343 switch (_canyonState) {
344 case kCanyonLaunch:
345 branchStart = kBranch1Start;
346 branchEnd = kBranch1End - kDecisionTime;
347 _canyonState = kCanyonBranch1Right;
348 flag = kChaseEnteredBranchZone;
349 break;
350 case kCanyonBranch1Left:
351 case kCanyonBranch1Right:
352 branchStart = kBranch2Start;
353 branchEnd = kBranch2End - kDecisionTime;
354 _canyonState = kCanyonBranch2Right;
355 flag = kChaseEnteredBranchZone;
356 break;
357 case kCanyonBranch2Left:
358 case kCanyonBranch2Right:
359 dontBranch();
360 return;
361 case kCanyonBranch3Left:
362 branchStart = kBranch4Start;
363 branchEnd = kBranch4End - kDecisionTime;
364 _canyonState = kCanyonBranch4Right;
365 flag = kChaseEnteredBranchZone;
366 break;
367 case kCanyonBranch4Left:
368 case kCanyonBranch4Right:
369 branchStart = kBranch5Start;
370 branchEnd = kBranch5End - kDecisionTime;
371 _canyonState = kCanyonBranch5Right;
372 flag = kChaseEnteredBranchZone;
373 break;
374 case kCanyonBranch5Left:
375 case kCanyonBranch5Right:
376 // Exit loop branch is in hq2
377 branchStart = kExitStart;
378 branchEnd = kExitEnd;
379 _canyonState = kCanyonExit;
380 flag = kChaseFinished;
381 startMusicTimer(kCanyonChaseStart + kCanyonChaseExitedTime - kExitStart, kMovieTicksPerSecond,
382 kCanyonExited);
383 break;
384 default:
385 break;
386 }
387
388 // Right branches are in hq1 (except exit)
389 // Segment 5 branches are switched
390 if (_canyonState == kCanyonBranch5Left || _canyonState == kCanyonBranch5Right) {
391 movie = &_canyonMovie2;
392 callBack = &_canyon2CallBack;
393 } else {
394 movie = &_canyonMovie1;
395 callBack = &_canyon1CallBack;
396 }
397
398 movie->setSegment(branchStart, branchEnd);
399 movie->setTime(branchStart);
400
401 switchTo(*movie, *callBack);
402
403 callBack->setCallBackFlag(flag);
404 callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
405 }
406
dontBranch()407 void CanyonChase::dontBranch() {
408 TimeValue branchStart, branchEnd;
409
410 branchStart = 0;
411 branchEnd = 0;
412 switch (_canyonState) {
413 case kCanyonLaunch:
414 branchStart = kDeath1Start;
415 branchEnd = kDeath1End;
416 break;
417 case kCanyonBranch1Left:
418 case kCanyonBranch1Right:
419 branchStart = kDeath2Start;
420 branchEnd = kDeath2End;
421 break;
422 case kCanyonBranch2Left:
423 case kCanyonBranch2Right:
424 branchStart = kDeath3Start;
425 branchEnd = kDeath3End;
426 break;
427 case kCanyonBranch3Left:
428 branchStart = kDeath4Start;
429 branchEnd = kDeath4End;
430 break;
431 case kCanyonBranch4Left:
432 case kCanyonBranch4Right:
433 branchStart = kDeath5Start;
434 branchEnd = kDeath5End;
435 break;
436 case kCanyonBranch5Left:
437 case kCanyonBranch5Right:
438 _canyonMovie2.setSegment(kExitStart, kExitGenoPoint);
439 _canyonMovie2.setTime(kExitStart);
440 switchTo(_canyonMovie2, _canyon2CallBack);
441 _canyon2CallBack.setCallBackFlag(kChaseFinished);
442 _canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
443 _canyonState = kCanyonLoop;
444 return;
445 default:
446 break;
447 }
448
449 _deathMovie.setSegment(branchStart, branchEnd);
450 _deathMovie.setTime(branchStart);
451
452 switchTo(_deathMovie, _deathCallBack);
453
454 startMusicTimer(10, 10, kCanyonRanIntoWall);
455 }
456
showControlsHint()457 void CanyonChase::showControlsHint() {
458 ((Mars *)_owner)->_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftKeypadHintTime);
459 ((Mars *)_owner)->_lowerLeftShuttleMovie.redrawMovieWorld();
460 ChaseInteraction::showControlsHint();
461 }
462
hideControlsHint()463 void CanyonChase::hideControlsHint() {
464 ((Mars *)_owner)->_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftCollisionTime);
465 ((Mars *)_owner)->_lowerLeftShuttleMovie.redrawMovieWorld();
466 ChaseInteraction::hideControlsHint();
467 }
468
switchTo(Movie & movie,NotificationCallBack & callBack)469 void CanyonChase::switchTo(Movie &movie, NotificationCallBack &callBack) {
470 if (_currentMovie != &movie) {
471 if (_currentMovie != NULL) {
472 _currentMovie->stop();
473 _currentMovie->hide();
474 _currentMovie->stopDisplaying();
475 }
476
477 _currentMovie = &movie;
478
479 _currentMovie->startDisplaying();
480 _currentMovie->show();
481 _currentMovie->start();
482 }
483
484 if (_currentCallBack != &callBack) {
485 _currentCallBack = &callBack;
486 }
487 }
488
startMusicTimer(TimeValue time,TimeScale scale,MusicTimerCode code)489 void CanyonChase::startMusicTimer(TimeValue time, TimeScale scale, MusicTimerCode code) {
490 _musicFuse.primeFuse(time, scale);
491 _musicEvent.canyonChase = this;
492 _musicEvent.theEvent = code;
493 _musicFuse.setFunctor(new Common::Functor0Mem<void, MusicTimerEvent>(&_musicEvent, &MusicTimerEvent::fire));
494 _musicFuse.lightFuse();
495 }
496
musicTimerExpired(MusicTimerEvent & event)497 void CanyonChase::musicTimerExpired(MusicTimerEvent &event) {
498 FaderMoveSpec spec;
499
500 switch (event.theEvent) {
501 case kCanyonRanIntoWall:
502 stopCanyonMusicLoop(5);
503 break;
504 case kCanyonExited:
505 spec.makeTwoKnotFaderSpec(20, 0, 255, 5, 160);
506 _musicFader.startFader(spec);
507 startMusicTimer(kCanyonChaseFadedTime, kMovieTicksPerSecond, kCanyonFaded);
508 break;
509 case kCanyonFaded:
510 spec.makeTwoKnotFaderSpec(10, 0, 160, 30, 0);
511 _musicFader.startFader(spec);
512 ((Mars *)_owner)->startMarsTimer(kLaunchTubeDVDReachedTime, kMovieTicksPerSecond,
513 kMarsLaunchTubeReached);
514 break;
515 default:
516 break;
517 }
518 }
519
doGenoChase()520 void CanyonChase::doGenoChase() {
521 _genoMovie.initFromMovieFile("Images/Mars/Canyon_hqG.mov");
522 _genoMovie.setVolume(((PegasusEngine *)g_engine)->getAmbienceLevel());
523 _genoMovie.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
524 _genoMovie.setDisplayOrder(kShuttleMonitorOrder);
525 _genoMovie.startDisplaying();
526 _genoMovie.show();
527 _genoMovie.start();
528
529 _genoCallBack.setNotification(&_chaseNotification);
530 _genoCallBack.initCallBack(&_genoMovie, kCallBackAtExtremes);
531 _genoCallBack.setCallBackFlag(kChaseFinished);
532 _genoCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
533
534 _canyonState = kCanyonExit;
535
536 ((Mars *)_owner)->startMarsTimer(_genoMovie.getDuration() - 5 * kMovieTicksPerSecond,
537 kMovieTicksPerSecond, kMarsLaunchTubeReached);
538 }
539
540 } // End of namespace Pegasus
541