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