/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1995-2013 Presto Studios, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "pegasus/pegasus.h"
#include "pegasus/neighborhood/mars/canyonchase.h"
#include "pegasus/neighborhood/mars/mars.h"
namespace Pegasus {
// Segment start and end points.
//static const TimeValue kPrepStart = 0;
static const TimeValue kPrepEnd = 3000;
static const TimeValue kLaunchStart = kPrepEnd;
static const TimeValue kLaunchEnd = 6640;
static const TimeValue kBranch1Start = kLaunchEnd;
static const TimeValue kBranch1End = 22240;
static const TimeValue kBranch2Start = kBranch1End;
static const TimeValue kBranch2End = 28440;
static const TimeValue kBranch3Start = kBranch2End;
static const TimeValue kBranch3End = 38640;
static const TimeValue kBranch4Start = kBranch3End;
static const TimeValue kBranch4End = 43880;
static const TimeValue kBranch5Start = kBranch4End;
static const TimeValue kBranch5End = 58680;
static const TimeValue kExitStart = kBranch5End;
static const TimeValue kExitEnd = 66480;
static const TimeValue kExitLoopPoint = 66200;
static const TimeValue kExitGenoPoint = 62560;
// Death start and end points.
static const TimeValue kDeath1Start = 0;
static const TimeValue kDeath1End = 2400;
static const TimeValue kDeath2Start = kDeath1End;
static const TimeValue kDeath2End = 4720;
static const TimeValue kDeath3Start = kDeath2End;
static const TimeValue kDeath3End = 7120;
static const TimeValue kDeath4Start = kDeath3End;
static const TimeValue kDeath4End = 9280;
static const TimeValue kDeath5Start = kDeath4End;
static const TimeValue kDeath5End = 12000;
// Chase state.
enum {
kCanyonLaunch,
kCanyonBranch1Left,
kCanyonBranch1Right,
kCanyonBranch2Left,
kCanyonBranch2Right,
kCanyonBranch3Left,
kCanyonBranch4Left,
kCanyonBranch4Right,
kCanyonBranch5Left,
kCanyonBranch5Right,
kCanyonExit,
kCanyonLoop
};
void MusicTimerEvent::fire() {
canyonChase->musicTimerExpired(*this);
}
CanyonChase::CanyonChase(Neighborhood *handler) : ChaseInteraction(kMarsCanyonChaseInteractionID, handler,
kMarsCanyonChaseNotificationID, g_vm), _canyonMovie1(kNoDisplayElement),
_canyonMovie2(kNoDisplayElement), _deathMovie(kNoDisplayElement), _genoMovie(kNoDisplayElement) {
_currentMovie = nullptr;
_currentCallBack = nullptr;
}
void CanyonChase::setSoundFXLevel(const uint16 fxLevel) {
_canyonMovie1.setVolume(fxLevel);
_canyonMovie2.setVolume(fxLevel);
_deathMovie.setVolume(fxLevel);
}
void CanyonChase::setAmbienceLevel(const uint16 level) {
_genoMovie.setVolume(level);
_musicFader.setMasterVolume(level);
}
void CanyonChase::startCanyonMusicLoop(void) {
FaderMoveSpec spec;
_musicLoop.loopSound();
spec.makeTwoKnotFaderSpec(10, 0, 0, 1, 255);
_musicFader.startFader(spec);
}
void CanyonChase::stopCanyonMusicLoop(const long ticks) {
FaderMoveSpec spec;
spec.makeTwoKnotFaderSpec(10, 0, 255, ticks, 0);
_musicFader.startFader(spec);
}
void CanyonChase::openInteraction() {
_canyonMovie1.initFromMovieFile("Images/Mars/Canyon_hq1.mov");
_canyonMovie1.setVolume(g_vm->getSoundFXLevel());
_canyonMovie1.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
_canyonMovie1.setDisplayOrder(kShuttleMonitorOrder);
_canyon1CallBack.setNotification(&_chaseNotification);
_canyon1CallBack.initCallBack(&_canyonMovie1, kCallBackAtExtremes);
_canyon1CallBack.setCallBackFlag(kChaseEnteredBranchZone);
_canyon1CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_canyonMovie2.initFromMovieFile("Images/Mars/Canyon_hq2.mov");
_canyonMovie2.setVolume(g_vm->getSoundFXLevel());
_canyonMovie2.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
_canyonMovie2.setDisplayOrder(kShuttleMonitorOrder);
_canyon2CallBack.setNotification(&_chaseNotification);
_canyon2CallBack.initCallBack(&_canyonMovie2, kCallBackAtExtremes);
_canyon2CallBack.setCallBackFlag(kChaseEnteredBranchZone);
_canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_deathMovie.initFromMovieFile("Images/Mars/Canyon_hqD.mov");
_deathMovie.setVolume(g_vm->getSoundFXLevel());
_deathMovie.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
_deathMovie.setDisplayOrder(kShuttleMonitorOrder);
_deathCallBack.setNotification(&_chaseNotification);
_deathCallBack.initCallBack(&_deathMovie, kCallBackAtExtremes);
_deathCallBack.setCallBackFlag(kChaseFinished);
_deathCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_musicLoop.attachFader(&_musicFader);
_musicLoop.initFromAIFFFile("Sounds/Mars/Canyon Loop.44K.16.AIFF");
_musicFader.setMasterVolume(g_vm->getAmbienceLevel());
ChaseInteraction::openInteraction();
_steerPict.setDisplayOrder(kShuttleMonitorOrder + 1);
_steerPict.moveElementTo(kShuttleSteerLeft, kShuttleSteerTop);
}
void CanyonChase::initInteraction() {
_steerPict.startDisplaying();
// Launch branch is identical in both movies
_canyonState = kCanyonLaunch;
_canyonMovie1.setSegment(kLaunchStart, kLaunchEnd - kDecisionTime);
_canyonMovie1.setTime(kLaunchStart);
switchTo(_canyonMovie1, _canyon1CallBack);
startCanyonMusicLoop();
ChaseInteraction::initInteraction();
}
void CanyonChase::closeInteraction() {
_canyonMovie1.stop();
_canyonMovie1.stopDisplaying();
_canyonMovie1.releaseMovie();
_canyon1CallBack.releaseCallBack();
_canyonMovie2.stop();
_canyonMovie2.stopDisplaying();
_canyonMovie2.releaseMovie();
_canyon2CallBack.releaseCallBack();
_deathMovie.stop();
_deathMovie.stopDisplaying();
_deathMovie.releaseMovie();
_deathCallBack.releaseCallBack();
_genoMovie.stop();
_genoMovie.stopDisplaying();
_genoMovie.releaseMovie();
_genoCallBack.releaseCallBack();
ChaseInteraction::closeInteraction();
}
void CanyonChase::receiveNotification(Notification *notification, const NotificationFlags flags) {
Input input;
if (notification == &_chaseNotification && flags == kChaseFinished) {
if (_canyonState == kCanyonLoop) {
// Swallow the notification if we loop back to the beginning
InputDevice.getInput(input, kFilterAllInput);
if (JMPPPInput::isEasterEggModifierInput(input)) {
stopCanyonMusicLoop(15);
doGenoChase();
} else {
_canyonMovie2.setSegment(kExitGenoPoint, kExitLoopPoint - kDecisionTime);
_canyonMovie2.setTime(kExitGenoPoint);
switchTo(_canyonMovie2, _canyon2CallBack);
_canyon2CallBack.setCallBackFlag(kChaseEnteredBranchZone);
_canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_canyonState = kCanyonLaunch;
}
return;
} else if (_canyonState != kCanyonExit) {
// We died
((Mars *)_owner)->die(kDeathRanIntoCanyonWall);
}
}
ChaseInteraction::receiveNotification(notification, flags);
}
void CanyonChase::setUpBranch() {
TimeValue branchStart, branchEnd;
branchStart = 0;
branchEnd = 0;
switch (_canyonState) {
case kCanyonLaunch:
case kCanyonExit:
branchStart = kLaunchEnd - kDecisionTime;
branchEnd = kLaunchEnd;
break;
case kCanyonBranch1Left:
case kCanyonBranch1Right:
branchStart = kBranch1End - kDecisionTime;
branchEnd = kBranch1End;
break;
case kCanyonBranch2Left:
case kCanyonBranch2Right:
branchStart = kBranch2End - kDecisionTime;
branchEnd = kBranch2End;
break;
case kCanyonBranch3Left:
branchStart = kBranch3End - kDecisionTime;
branchEnd = kBranch3End;
break;
case kCanyonBranch4Left:
case kCanyonBranch4Right:
branchStart = kBranch4End - kDecisionTime;
branchEnd = kBranch4End;
break;
case kCanyonBranch5Left:
case kCanyonBranch5Right:
branchStart = kBranch5End - kDecisionTime;
branchEnd = kBranch5End;
break;
default:
break;
}
_currentMovie->setSegment(branchStart, branchEnd);
// Need to call SetTime here in case we loop
_currentMovie->setTime(branchStart);
_currentCallBack->setCallBackFlag(kChaseExitedBranchZone);
_currentCallBack->scheduleCallBack(kTriggerAtStop, 0, 0);
}
void CanyonChase::branchLeft() {
TimeValue branchStart, branchEnd;
Movie *movie;
NotificationCallBack *callBack;
branchStart = 0;
branchEnd = 0;
switch (_canyonState) {
case kCanyonLaunch:
branchStart = kBranch1Start;
branchEnd = kBranch1End - kDecisionTime;
_canyonState = kCanyonBranch1Left;
break;
case kCanyonBranch1Left:
case kCanyonBranch1Right:
branchStart = kBranch2Start;
branchEnd = kBranch2End - kDecisionTime;
_canyonState = kCanyonBranch2Left;
break;
case kCanyonBranch2Left:
case kCanyonBranch2Right:
branchStart = kBranch3Start;
branchEnd = kBranch3End - kDecisionTime;
_canyonState = kCanyonBranch3Left;
break;
case kCanyonBranch3Left:
branchStart = kBranch4Start;
branchEnd = kBranch4End - kDecisionTime;
_canyonState = kCanyonBranch4Left;
break;
case kCanyonBranch4Left:
case kCanyonBranch4Right:
branchStart = kBranch5Start;
branchEnd = kBranch5End - kDecisionTime;
_canyonState = kCanyonBranch5Left;
break;
case kCanyonBranch5Left:
case kCanyonBranch5Right:
dontBranch();
return;
default:
break;
}
// Left branches are in hq2 (except exit)
// Segment 5 branches are switched
if (_canyonState == kCanyonBranch5Left || _canyonState == kCanyonBranch5Right) {
movie = &_canyonMovie1;
callBack = &_canyon1CallBack;
} else {
movie = &_canyonMovie2;
callBack = &_canyon2CallBack;
}
movie->setSegment(branchStart, branchEnd);
movie->setTime(branchStart);
switchTo(*movie, *callBack);
callBack->setCallBackFlag(kChaseEnteredBranchZone);
callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
}
void CanyonChase::branchRight() {
TimeValue branchStart, branchEnd;
NotificationFlags flag;
Movie *movie;
NotificationCallBack *callBack;
branchStart = 0;
branchEnd = 0;
flag = 0;
switch (_canyonState) {
case kCanyonLaunch:
branchStart = kBranch1Start;
branchEnd = kBranch1End - kDecisionTime;
_canyonState = kCanyonBranch1Right;
flag = kChaseEnteredBranchZone;
break;
case kCanyonBranch1Left:
case kCanyonBranch1Right:
branchStart = kBranch2Start;
branchEnd = kBranch2End - kDecisionTime;
_canyonState = kCanyonBranch2Right;
flag = kChaseEnteredBranchZone;
break;
case kCanyonBranch2Left:
case kCanyonBranch2Right:
dontBranch();
return;
case kCanyonBranch3Left:
branchStart = kBranch4Start;
branchEnd = kBranch4End - kDecisionTime;
_canyonState = kCanyonBranch4Right;
flag = kChaseEnteredBranchZone;
break;
case kCanyonBranch4Left:
case kCanyonBranch4Right:
branchStart = kBranch5Start;
branchEnd = kBranch5End - kDecisionTime;
_canyonState = kCanyonBranch5Right;
flag = kChaseEnteredBranchZone;
break;
case kCanyonBranch5Left:
case kCanyonBranch5Right:
// Exit loop branch is in hq2
branchStart = kExitStart;
branchEnd = kExitEnd;
_canyonState = kCanyonExit;
flag = kChaseFinished;
startMusicTimer(kCanyonChaseStart + kCanyonChaseExitedTime - kExitStart, kMovieTicksPerSecond,
kCanyonExited);
break;
default:
break;
}
// Right branches are in hq1 (except exit)
// Segment 5 branches are switched
if (_canyonState == kCanyonBranch5Left || _canyonState == kCanyonBranch5Right) {
movie = &_canyonMovie2;
callBack = &_canyon2CallBack;
} else {
movie = &_canyonMovie1;
callBack = &_canyon1CallBack;
}
movie->setSegment(branchStart, branchEnd);
movie->setTime(branchStart);
switchTo(*movie, *callBack);
callBack->setCallBackFlag(flag);
callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
}
void CanyonChase::dontBranch() {
TimeValue branchStart, branchEnd;
branchStart = 0;
branchEnd = 0;
switch (_canyonState) {
case kCanyonLaunch:
branchStart = kDeath1Start;
branchEnd = kDeath1End;
break;
case kCanyonBranch1Left:
case kCanyonBranch1Right:
branchStart = kDeath2Start;
branchEnd = kDeath2End;
break;
case kCanyonBranch2Left:
case kCanyonBranch2Right:
branchStart = kDeath3Start;
branchEnd = kDeath3End;
break;
case kCanyonBranch3Left:
branchStart = kDeath4Start;
branchEnd = kDeath4End;
break;
case kCanyonBranch4Left:
case kCanyonBranch4Right:
branchStart = kDeath5Start;
branchEnd = kDeath5End;
break;
case kCanyonBranch5Left:
case kCanyonBranch5Right:
_canyonMovie2.setSegment(kExitStart, kExitGenoPoint);
_canyonMovie2.setTime(kExitStart);
switchTo(_canyonMovie2, _canyon2CallBack);
_canyon2CallBack.setCallBackFlag(kChaseFinished);
_canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_canyonState = kCanyonLoop;
return;
default:
break;
}
_deathMovie.setSegment(branchStart, branchEnd);
_deathMovie.setTime(branchStart);
switchTo(_deathMovie, _deathCallBack);
startMusicTimer(10, 10, kCanyonRanIntoWall);
}
void CanyonChase::showControlsHint() {
((Mars *)_owner)->_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftKeypadHintTime);
((Mars *)_owner)->_lowerLeftShuttleMovie.redrawMovieWorld();
ChaseInteraction::showControlsHint();
}
void CanyonChase::hideControlsHint() {
((Mars *)_owner)->_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftCollisionTime);
((Mars *)_owner)->_lowerLeftShuttleMovie.redrawMovieWorld();
ChaseInteraction::hideControlsHint();
}
void CanyonChase::switchTo(Movie &movie, NotificationCallBack &callBack) {
if (_currentMovie != &movie) {
if (_currentMovie != nullptr) {
_currentMovie->stop();
_currentMovie->hide();
_currentMovie->stopDisplaying();
}
_currentMovie = &movie;
_currentMovie->startDisplaying();
_currentMovie->show();
_currentMovie->start();
}
if (_currentCallBack != &callBack) {
_currentCallBack = &callBack;
}
}
void CanyonChase::startMusicTimer(TimeValue time, TimeScale scale, MusicTimerCode code) {
_musicFuse.primeFuse(time, scale);
_musicEvent.canyonChase = this;
_musicEvent.theEvent = code;
_musicFuse.setFunctor(new Common::Functor0Mem(&_musicEvent, &MusicTimerEvent::fire));
_musicFuse.lightFuse();
}
void CanyonChase::musicTimerExpired(MusicTimerEvent &event) {
FaderMoveSpec spec;
switch (event.theEvent) {
case kCanyonRanIntoWall:
stopCanyonMusicLoop(5);
break;
case kCanyonExited:
spec.makeTwoKnotFaderSpec(20, 0, 255, 5, 160);
_musicFader.startFader(spec);
startMusicTimer(kCanyonChaseFadedTime, kMovieTicksPerSecond, kCanyonFaded);
break;
case kCanyonFaded:
spec.makeTwoKnotFaderSpec(10, 0, 160, 30, 0);
_musicFader.startFader(spec);
((Mars *)_owner)->startMarsTimer(kLaunchTubeDVDReachedTime, kMovieTicksPerSecond,
kMarsLaunchTubeReached);
break;
default:
break;
}
}
void CanyonChase::doGenoChase() {
_genoMovie.initFromMovieFile("Images/Mars/Canyon_hqG.mov");
_genoMovie.setVolume(g_vm->getAmbienceLevel());
_genoMovie.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
_genoMovie.setDisplayOrder(kShuttleMonitorOrder);
_genoMovie.startDisplaying();
_genoMovie.show();
_genoMovie.start();
_genoCallBack.setNotification(&_chaseNotification);
_genoCallBack.initCallBack(&_genoMovie, kCallBackAtExtremes);
_genoCallBack.setCallBackFlag(kChaseFinished);
_genoCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_canyonState = kCanyonExit;
((Mars *)_owner)->startMarsTimer(_genoMovie.getDuration() - 5 * kMovieTicksPerSecond,
kMovieTicksPerSecond, kMarsLaunchTubeReached);
}
} // End of namespace Pegasus