/* 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/gamestate.h" #include "pegasus/neighborhood/norad/alpha/subchase.h" #include "pegasus/neighborhood/norad/alpha/noradalpha.h" #include "pegasus/neighborhood/norad/constants.h" namespace Pegasus { static const TimeScale kSubChaseScale = 600; static const DisplayOrder kSubChaseOrder = 27000; // Segment start and end points. static const TimeValue kIntroStart = 0; static const TimeValue kIntroEnd = 2400; static const TimeValue kDialogStart = kIntroEnd; static const TimeValue kDialogEnd = 20920; static const TimeValue kBranch1Start = kDialogEnd; static const TimeValue kBranch1End = 32120; static const TimeValue kBranch2LeftStart = kBranch1End; static const TimeValue kBranch2LeftEnd = 48080; static const TimeValue kBranch3Start = kBranch2LeftEnd; static const TimeValue kBranch3End = 61080; static const TimeValue kBranch4Start = kBranch3End; static const TimeValue kBranch4End = 84080; static const TimeValue kBranch5Start = kBranch4End; static const TimeValue kBranch5End = 94840; static const TimeValue kBranch6Start = kBranch5End; static const TimeValue kBranch6End = 106040; static const TimeValue kBranch7LeftStart = kBranch6End; static const TimeValue kBranch7LeftEnd = 118840; static const TimeValue kExitStart = kBranch7LeftEnd; static const TimeValue kExitEnd = 133200; static const TimeValue kBranch2RightStart = 133200; static const TimeValue kBranch2RightEnd = 149160; static const TimeValue kBranch7RightStart = 168000; static const TimeValue kBranch7RightEnd = 180800; // Death start and end points. static const TimeValue kDeath4Start = 149160; static const TimeValue kDeath4End = 158040; static const TimeValue kDeath5Start = kDeath4End; static const TimeValue kDeath5End = 163760; static const TimeValue kDeath6Start = kDeath5End; static const TimeValue kDeath6End = 168000; static const TimeValue kDeath7Start = 180800; static const TimeValue kDeath7End = 187040; // Chase state. enum { kSubDialog, kSubBranch1, kSubBranch2Left, kSubBranch2Right, kSubBranch3, kSubBranch4, kSubBranch5, kSubBranch6, kSubBranch7Left, kSubBranch7Right, kSubExit }; void HintTimerEvent::fire() { subChase->hintTimerExpired(*this); } void BlinkTimerEvent::fire() { subChase->blinkTimerExpired(*this); } SubChase::SubChase(Neighborhood *handler) : ChaseInteraction(kNoradSubChaseInteractionID, handler, kNoradSubChaseNotificationID, g_vm), _subMovie(kNoDisplayElement), _hintPict(kNoDisplayElement), _blinkPict(kNoDisplayElement), _canSteerSub(true) { } void SubChase::setSoundFXLevel(const uint16 fxLevel) { _subMovie.setVolume(fxLevel); } void SubChase::openInteraction() { _subMovie.initFromMovieFile("Images/Norad Alpha/Sub Chase Movie"); _subMovie.setVolume(g_vm->getSoundFXLevel()); _subMovie.moveElementTo(0, 0); _subMovie.setDisplayOrder(kSubChaseOrder); _subMovie.startDisplaying(); _subMovie.show(); _subCallBack.setNotification(&_chaseNotification); _subCallBack.initCallBack(&_subMovie, kCallBackAtExtremes); ChaseInteraction::openInteraction(); _steerPict.setDisplayOrder(kSubChaseOrder + 1); _steerPict.moveElementTo(kNoradSubSteerLeft, kNoradSubSteerTop); _hintPict.initFromPICTFile("Images/Norad Alpha/Sub Chase steerk1.pict", true); _hintPict.setDisplayOrder(kSubChaseOrder + 1); _hintPict.moveElementTo(kNoradSubHintLeft, kNoradSubHintTop); _blinkPict.initFromPICTFile("Images/Norad Alpha/Sub Chase steerk0.pict", true); _blinkPict.setDisplayOrder(kSubChaseOrder + 1); _blinkPict.moveElementTo(kNoradSubHintLeft, kNoradSubHintTop); } void SubChase::initInteraction() { _canSteerSub = !GameState.getWalkthroughMode(); _owner->playMovieSegment(&_subMovie, kIntroStart, kIntroEnd); _subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); if (_canSteerSub) { _steerPict.startDisplaying(); _hintPict.startDisplaying(); _blinkPict.startDisplaying(); startHintTimer(20000 - kDialogStart, kSubChaseScale, kStartedHint); _subCallBack.setCallBackFlag(kChaseEnteredBranchZone); _subMovie.setSegment(kDialogStart, kBranch1End - kDecisionTime); } else { _subCallBack.setCallBackFlag(kChaseFinished); _subMovie.setSegment(kDialogStart, kExitEnd); } _subState = kSubDialog; _subMovie.setTime(kDialogStart); _subMovie.start(); ChaseInteraction::initInteraction(); } void SubChase::closeInteraction() { _subMovie.stop(); _subMovie.stopDisplaying(); _subMovie.releaseMovie(); _subCallBack.releaseCallBack(); _hintPict.hide(); _hintPict.deallocateSurface(); _blinkPict.hide(); _blinkPict.deallocateSurface(); ChaseInteraction::closeInteraction(); } void SubChase::receiveNotification(Notification *notification, const NotificationFlags flags) { if (notification == &_chaseNotification && flags == kChaseFinished) { if (_subState != kSubDialog && _subState != kSubExit) { // We died ((NoradAlpha *)_owner)->die(kDeathSubDestroyed); } else { _subMovie.stopDisplaying(); g_vm->_gfx->enableErase(); g_vm->_gfx->updateDisplay(); g_vm->_gfx->disableErase(); g_vm->jumpToNewEnvironment(kNoradDeltaID, kNorad41, kEast); } } ChaseInteraction::receiveNotification(notification, flags); } void SubChase::handleInput(const Input &input, const Hotspot *cursorSpot) { if (_subMovie.getTime() < kBranch1Start && input.anyInput()) { if (_canSteerSub) { if (!_blinkFuse.isFuseLit()) { // If the hint is not blinking then it must not be running, so display it _hintPict.show(); startBlinkTimer(10, 10, kEnteredBlinkState); } _subState = kSubBranch1; startHintTimer(3000, kSubChaseScale, kEndedHint); } else { _subState = kSubExit; } _subMovie.setTime(kBranch1Start); } else { ChaseInteraction::handleInput(input, cursorSpot); } } void SubChase::setUpBranch() { TimeValue branchStart, branchEnd; branchStart = 0; branchEnd = 0; switch (_subState) { case kSubDialog: case kSubBranch1: branchStart = kBranch1End - kDecisionTime; branchEnd = kBranch1End; break; case kSubBranch2Left: branchStart = kBranch2LeftEnd - kDecisionTime; branchEnd = kBranch2LeftEnd; break; case kSubBranch2Right: branchStart = kBranch2RightEnd - kDecisionTime; branchEnd = kBranch2RightEnd; break; case kSubBranch3: branchStart = kBranch3End - kDecisionTime; branchEnd = kBranch3End; break; case kSubBranch4: branchStart = kBranch4End - kDecisionTime; branchEnd = kBranch4End; break; case kSubBranch5: branchStart = kBranch5End - kDecisionTime; branchEnd = kBranch5End; break; case kSubBranch6: branchStart = kBranch6End - kDecisionTime; branchEnd = kBranch6End; break; case kSubBranch7Left: branchStart = kBranch7LeftEnd - kDecisionTime; branchEnd = kBranch7LeftEnd; break; case kSubBranch7Right: branchStart = kBranch7RightEnd - kDecisionTime; branchEnd = kBranch7RightEnd; break; default: break; } _subMovie.setSegment(branchStart, branchEnd); _subCallBack.setCallBackFlag(kChaseExitedBranchZone); _subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); } void SubChase::branchLeft() { TimeValue branchStart, branchEnd; NotificationFlags flag; branchStart = 0; branchEnd = 0; flag = 0; switch (_subState) { case kSubDialog: case kSubBranch1: branchStart = kBranch2LeftStart; branchEnd = kBranch2LeftEnd; // Don't show the controls hint when we approach the whale // since the branch segments here are identical flag = kChaseExitedBranchZone; _subState = kSubBranch2Left; break; case kSubBranch2Left: case kSubBranch2Right: branchStart = kBranch3Start; branchEnd = kBranch3End - kDecisionTime; flag = kChaseEnteredBranchZone; _subState = kSubBranch3; break; case kSubBranch3: branchStart = kBranch4Start; branchEnd = kBranch4End - kDecisionTime; flag = kChaseEnteredBranchZone; _subState = kSubBranch4; break; case kSubBranch4: branchStart = kDeath5Start; branchEnd = kDeath5End; flag = kChaseFinished; _subState = kSubBranch5; break; case kSubBranch5: branchStart = kBranch6Start; branchEnd = kBranch6End - kDecisionTime; flag = kChaseEnteredBranchZone; _subState = kSubBranch6; break; case kSubBranch6: branchStart = kBranch7LeftStart; branchEnd = kBranch7LeftEnd; flag = kChaseExitedBranchZone; _subState = kSubBranch7Left; break; case kSubBranch7Left: case kSubBranch7Right: branchStart = kExitStart; branchEnd = kExitEnd; flag = kChaseFinished; _subState = kSubExit; break; default: break; } _subMovie.setSegment(branchStart, branchEnd); _subMovie.setTime(branchStart); _subCallBack.setCallBackFlag(flag); _subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); } void SubChase::branchRight() { TimeValue branchStart, branchEnd; NotificationFlags flag; branchStart = 0; branchEnd = 0; flag = 0; switch (_subState) { case kSubDialog: case kSubBranch1: branchStart = kBranch2RightStart; branchEnd = kBranch2RightEnd; // Don't show the controls hint when we approach the whale // since the branch segments here are identical flag = kChaseExitedBranchZone; _subState = kSubBranch2Right; break; case kSubBranch2Left: case kSubBranch2Right: branchStart = kBranch3Start; branchEnd = kBranch3End - kDecisionTime; flag = kChaseEnteredBranchZone; _subState = kSubBranch3; break; case kSubBranch3: branchStart = kDeath4Start; branchEnd = kDeath4End; flag = kChaseFinished; _subState = kSubBranch4; break; case kSubBranch4: branchStart = kBranch5Start; branchEnd = kBranch5End - kDecisionTime; flag = kChaseEnteredBranchZone; _subState = kSubBranch5; break; case kSubBranch5: branchStart = kDeath6Start; branchEnd = kDeath6End; flag = kChaseFinished; _subState = kSubBranch6; break; case kSubBranch6: if (g_vm->getRandomBit()) { branchStart = kBranch7RightStart; branchEnd = kBranch7RightEnd; flag = kChaseExitedBranchZone; } else { branchStart = kDeath7Start; branchEnd = kDeath7End; flag = kChaseFinished; } _subState = kSubBranch7Right; break; case kSubBranch7Left: case kSubBranch7Right: branchStart = kExitStart; branchEnd = kExitEnd; flag = kChaseFinished; _subState = kSubExit; break; default: break; } _subMovie.setSegment(branchStart, branchEnd); _subMovie.setTime(branchStart); _subCallBack.setCallBackFlag(flag); _subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); } void SubChase::dontBranch() { if (g_vm->getRandomBit()) branchLeft(); else branchRight(); } void SubChase::startHintTimer(TimeValue time, TimeScale scale, HintTimerCode code) { if (_canSteerSub) { _hintFuse.primeFuse(time, scale); _hintEvent.subChase = this; _hintEvent.theEvent = code; _hintFuse.setFunctor(new Common::Functor0Mem(&_hintEvent, &HintTimerEvent::fire)); _hintFuse.lightFuse(); } } void SubChase::hintTimerExpired(HintTimerEvent &event) { switch (event.theEvent) { case kStartedHint: _hintPict.show(); startBlinkTimer(10, 10, kEnteredBlinkState); startHintTimer(3920, kSubChaseScale, kEndedHint); break; case kEndedHint: _hintPict.hide(); _blinkPict.hide(); _blinkFuse.stopFuse(); break; default: break; } } void SubChase::startBlinkTimer(TimeValue time, TimeScale scale, BlinkTimerCode code) { _blinkFuse.primeFuse(time, scale); _blinkEvent.subChase = this; _blinkEvent.theEvent = code; _blinkFuse.setFunctor(new Common::Functor0Mem(&_blinkEvent, &BlinkTimerEvent::fire)); _blinkFuse.lightFuse(); } void SubChase::blinkTimerExpired(BlinkTimerEvent &event) { switch (event.theEvent) { case kEnteredBlinkState: _hintPict.hide(); _blinkPict.show(); startBlinkTimer(5, 10, kExitedBlinkState); break; case kExitedBlinkState: _blinkPict.hide(); _hintPict.show(); startBlinkTimer(10, 10, kEnteredBlinkState); break; default: break; } } } // End of namespace Pegasus