From c45f0343f4c386263025f1c39536ec9c6a2e566e Mon Sep 17 00:00:00 2001 From: Robert Špalek Date: Mon, 2 Nov 2009 02:28:43 +0000 Subject: Refactored running loop(). - shouldExitLoop() is a bool again and introduced new flag isReloaded() instead of adding special hacky value 2 - loop() accepts 2 parameters: loop substatus and shouldExit flag, because each caller previously had to set and restore these manually. loop() now also tests whether the substatuses are properly nested. reordered the loop-exitting code. - renamed loop substatuses to logical names - enterNewRoom() returns bool whether loop() should continue so that start() doesn't have to test and clear shouldEndProgram(). it doesn't need force_reload as a parameter anymore. - dialog selections use new inner substatus instead of outer substatus, for consistency svn-id: r45607 --- engines/draci/draci.cpp | 10 +++-- engines/draci/game.cpp | 103 ++++++++++++++++++++++++++------------------- engines/draci/game.h | 23 +++++----- engines/draci/saveload.cpp | 2 +- engines/draci/script.cpp | 46 ++++---------------- 5 files changed, 89 insertions(+), 95 deletions(-) (limited to 'engines/draci') diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp index a491ee6fe6..2831ff605a 100644 --- a/engines/draci/draci.cpp +++ b/engines/draci/draci.cpp @@ -232,7 +232,7 @@ void DraciEngine::handleEvents() { break; case Common::KEYCODE_ESCAPE: { if (_game->getLoopStatus() == kStatusInventory && - _game->getLoopSubstatus() == kSubstatusOrdinary) { + _game->getLoopSubstatus() == kOuterLoop) { _game->inventoryDone(); break; } @@ -246,6 +246,8 @@ void DraciEngine::handleEvents() { // Schedule room change // TODO: gate 0 is not always the best one for returning from the map _game->scheduleEnteringRoomUsingGate(escRoom, 0); + + // Immediately cancel any running animation or dubbing. _game->setExitLoop(true); // End any currently running GPL programs @@ -278,7 +280,7 @@ void DraciEngine::handleEvents() { break; case Common::KEYCODE_i: if (_game->getRoomNum() == _game->getMapRoom() || - _game->getLoopSubstatus() != kSubstatusOrdinary) { + _game->getLoopSubstatus() != kOuterLoop) { break; } if (_game->getLoopStatus() == kStatusInventory) { @@ -403,7 +405,7 @@ Common::Error DraciEngine::loadGameState(int slot) { bool DraciEngine::canLoadGameStateCurrently() { return (_game->getLoopStatus() == kStatusOrdinary) && - (_game->getLoopSubstatus() == kSubstatusOrdinary); + (_game->getLoopSubstatus() == kOuterLoop); } Common::Error DraciEngine::saveGameState(int slot, const char *desc) { @@ -412,7 +414,7 @@ Common::Error DraciEngine::saveGameState(int slot, const char *desc) { bool DraciEngine::canSaveGameStateCurrently() { return (_game->getLoopStatus() == kStatusOrdinary) && - (_game->getLoopSubstatus() == kSubstatusOrdinary); + (_game->getLoopSubstatus() == kOuterLoop); } } // End of namespace Draci diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp index 67b7b04ef6..64c959af57 100644 --- a/engines/draci/game.cpp +++ b/engines/draci/game.cpp @@ -149,36 +149,24 @@ Game::Game(DraciEngine *vm) : _vm(vm) { void Game::start() { while (!shouldQuit()) { - debugC(1, kDraciGeneralDebugLevel, "Game::start()"); - - // Whenever the top-level loop is entered, it should not finish unless - // the exit is triggered by a script - const bool force_reload = shouldExitLoop() > 1; - setExitLoop(false); - _vm->_script->endCurrentProgram(false); - - enterNewRoom(force_reload); - - if (_vm->_script->shouldEndProgram()) { - // Room changed during the initialization (intro or Escape pressed). - continue; + if (enterNewRoom()) { + // Call the outer loop doing all the hard job. + loop(kOuterLoop, false); } - - // Call the outer loop doing all the hard job. - loop(); } } void Game::init() { setQuit(false); setExitLoop(false); + setIsReloaded(false); _scheduledPalette = 0; _fadePhases = _fadePhase = 0; setEnableQuickHero(true); setWantQuickHero(false); setEnableSpeedText(true); setLoopStatus(kStatusGate); - setLoopSubstatus(kSubstatusOrdinary); + setLoopSubstatus(kOuterLoop); _animUnderCursor = kOverlayImage; @@ -240,22 +228,32 @@ void Game::init() { _pushedNewRoom = _pushedNewGate = -1; } -void Game::loop() { +void Game::loop(LoopSubstatus substatus, bool shouldExit) { + assert(getLoopSubstatus() == kOuterLoop); + setLoopSubstatus(substatus); + setExitLoop(shouldExit); + // Can run both as an outer and inner loop. In both mode it updates - // the screen according to the timer. It the outer mode - // (kSubstatusOrdinary) it also reacts to user events. In the inner - // mode (all other kSubstatus* enums), the loop runs until its stopping - // condition, possibly stopping earlier if the user interrupts it, - // however no other user intervention is allowed. + // the screen according to the timer. It the outer mode (kOuterLoop) + // it also reacts to user events. In the inner mode (all kInner* + // enums), the loop runs until its stopping condition, possibly + // stopping earlier if the user interrupts it, however no other user + // intervention is allowed. Surface *surface = _vm->_screen->getSurface(); + // Always enter the first pass of the loop, even if shouldExitLoop() is + // true, exactly to ensure to make at least one pass. do { debugC(4, kDraciLogicDebugLevel, "loopstatus: %d, loopsubstatus: %d", _loopStatus, _loopSubstatus); _vm->handleEvents(); - if (shouldExitLoop() > 1) // after loading + if (isReloaded()) { + // Cannot continue with the same animation objects, + // because the real data structures of the game have + // completely been changed. break; + } if (_fadePhase > 0 && (_vm->_system->getMillis() - _fadeTick) >= kFadingTimeUnit) { _fadeTick = _vm->_system->getMillis(); @@ -263,7 +261,7 @@ void Game::loop() { const byte *startPal = _currentRoom._palette >= 0 ? _vm->_paletteArchive->getFile(_currentRoom._palette)->_data : NULL; const byte *endPal = getScheduledPalette() >= 0 ? _vm->_paletteArchive->getFile(getScheduledPalette())->_data : NULL; _vm->_screen->interpolatePalettes(startPal, endPal, 0, kNumColours, _fadePhases - _fadePhase, _fadePhases); - if (_loopSubstatus == kSubstatusFade && _fadePhase == 0) { + if (_loopSubstatus == kInnerWhileFade && _fadePhase == 0) { setExitLoop(true); // Rewrite the palette index of the current // room. This is necessary when two fadings @@ -277,7 +275,7 @@ void Game::loop() { int x = _vm->_mouse->getPosX(); int y = _vm->_mouse->getPosY(); - if (_loopStatus == kStatusDialogue && _loopSubstatus == kSubstatusOrdinary) { + if (_loopStatus == kStatusDialogue && _loopSubstatus == kInnerDuringDialogue) { Text *text; for (int i = 0; i < kDialogueLines; ++i) { text = reinterpret_cast(_dialogueAnims[i]->getCurrentFrame()); @@ -306,7 +304,7 @@ void Game::loop() { // During the normal game-play, in particular not when // running the init-scripts, enable interactivity. - if (_loopStatus == kStatusOrdinary && _loopSubstatus == kSubstatusOrdinary) { + if (_loopStatus == kStatusOrdinary && _loopSubstatus == kOuterLoop) { if (_vm->_mouse->lButtonPressed()) { _vm->_mouse->lButtonSet(false); @@ -380,7 +378,7 @@ void Game::loop() { } } - if (_loopStatus == kStatusInventory && _loopSubstatus == kSubstatusOrdinary) { + if (_loopStatus == kStatusInventory && _loopSubstatus == kOuterLoop) { if (_inventoryExit) { inventoryDone(); } @@ -455,7 +453,7 @@ void Game::loop() { debugC(5, kDraciLogicDebugLevel, "Anim under cursor: %d", _animUnderCursor); // Handle character talking (if there is any) - if (_loopSubstatus == kSubstatusTalk) { + if (_loopSubstatus == kInnerWhileTalk) { // If the current speech text has expired or the user clicked a mouse button, // advance to the next line of text if ((getEnableSpeedText() && (_vm->_mouse->lButtonPressed() || _vm->_mouse->rButtonPressed())) || @@ -467,20 +465,27 @@ void Game::loop() { _vm->_mouse->rButtonSet(false); } + // A script has scheduled changing the room (either triggered + // by the user clicking on something or run at the end of a + // gate script in the intro). + if ((_loopStatus == kStatusOrdinary || _loopStatus == kStatusGate) && _newRoom != getRoomNum()) { + setExitLoop(true); + } + // This returns true if we got a signal to quit the game - if (shouldQuit()) - return; + if (shouldQuit()) { + setExitLoop(true); + } // Advance animations and redraw screen _vm->_anims->drawScene(surface); _vm->_screen->copyToScreen(); _vm->_system->delayMillis(20); - // HACK: Won't be needed once the game loop is implemented properly - setExitLoop(shouldExitLoop() || (_newRoom != getRoomNum() && - (_loopStatus == kStatusOrdinary || _loopStatus == kStatusGate))); - } while (!shouldExitLoop()); + + setLoopSubstatus(kOuterLoop); + setExitLoop(false); } void Game::updateCursor() { @@ -502,7 +507,7 @@ void Game::updateCursor() { // If we are in inventory mode, we do a different kind of updating that handles // inventory items and return early - if (_loopStatus == kStatusInventory && _loopSubstatus == kSubstatusOrdinary) { + if (_loopStatus == kStatusInventory && _loopSubstatus == kOuterLoop) { if (_itemUnderCursor != kNoItem) { const GameItem *item = &_items[_itemUnderCursor]; @@ -708,7 +713,7 @@ void Game::putItem(int itemID, int position) { // If we are in inventory mode, we need to play the item animation, immediately // upon returning it to its slot but *not* in other modes because it should be // invisible then (along with the inventory) - if (_loopStatus == kStatusInventory && _loopSubstatus == kSubstatusOrdinary) { + if (_loopStatus == kStatusInventory && _loopSubstatus == kOuterLoop) { _vm->_anims->play(anim_id); } } @@ -850,8 +855,7 @@ int Game::dialogueDraw() { // Call the game loop to enable interactivity until the user // selects his choice. _vm->_mouse->cursorOn(); - setExitLoop(false); - loop(); + loop(kInnerDuringDialogue, false); _vm->_mouse->cursorOff(); bool notDialogueAnim = true; @@ -1302,9 +1306,10 @@ int Game::playingObjectAnimation(const GameObject *obj) const { return -1; } -void Game::enterNewRoom(bool force_reload) { - if (_newRoom == getRoomNum() && !force_reload) { - return; +bool Game::enterNewRoom() { + if (_newRoom == getRoomNum() && !isReloaded()) { + // If the game has been reloaded, force reloading all animations. + return true; } debugC(1, kDraciLogicDebugLevel, "Entering room %d using gate %d", _newRoom, _newGate); @@ -1357,7 +1362,9 @@ void Game::enterNewRoom(bool force_reload) { // Set the appropriate loop statu before loading the room setLoopStatus(kStatusGate); - setLoopSubstatus(kSubstatusOrdinary); + // Reset the flag allowing to run the scripts. It may have been turned + // on by pressing Escape in the intro or in the map room. + _vm->_script->endCurrentProgram(false); loadRoom(_newRoom); loadOverlays(); @@ -1379,6 +1386,16 @@ void Game::enterNewRoom(bool force_reload) { setLoopStatus(kStatusOrdinary); _vm->_mouse->setCursorType(kNormalCursor); + + setIsReloaded(false); + if (_vm->_script->shouldEndProgram()) { + // Escape pressed during the intro or map animations run in the + // init scripts. This flag was turned on to skip the rest of + // those programs. Return false to make start() rerun us from + // the beginning, because the room number has changed. + return false; + } + return true; } void Game::runGateProgram(int gate) { diff --git a/engines/draci/game.h b/engines/draci/game.h index 3cd55a77ea..168da220f6 100644 --- a/engines/draci/game.h +++ b/engines/draci/game.h @@ -166,10 +166,11 @@ enum LoopStatus { }; enum LoopSubstatus { - kSubstatusOrdinary, // outer loop: everything is allowed - kSubstatusTalk, // playing a voice: inner loop will exit afterwards - kSubstatusFade, // fading a palette: inner loop will exit when done - kSubstatusStrange // other inner loop: either immediately exiting or waiting for an animation to end (whose callback ends the loop) + kOuterLoop, // outer loop: everything is allowed + kInnerWhileTalk, // playing a voice: inner loop will exit afterwards + kInnerWhileFade, // fading a palette: inner loop will exit when done + kInnerDuringDialogue, // selecting continuation block: inner block will exit afterwards + kInnerUntilExit // other inner loop: either immediately exiting or waiting for an animation to end (whose callback ends the loop) }; class Game { @@ -179,7 +180,7 @@ public: void init(); void start(); - void loop(); + void loop(LoopSubstatus substatus, bool shouldExit); // HACK: this is only for testing int nextRoomNum() const { @@ -271,9 +272,10 @@ public: bool shouldQuit() const { return _shouldQuit; } void setQuit(bool quit) { _shouldQuit = quit; } - - int shouldExitLoop() const { return _shouldExitLoop; } - void setExitLoop(int exit) { _shouldExitLoop = exit; } + bool shouldExitLoop() const { return _shouldExitLoop; } + void setExitLoop(bool exit) { _shouldExitLoop = exit; } + bool isReloaded() const { return _isReloaded; } + void setIsReloaded(bool value) { _isReloaded = value; } void setSpeechTiming(uint tick, uint duration); void shiftSpeechAndFadeTick(int delta); @@ -318,7 +320,7 @@ public: void DoSync(Common::Serializer &s); private: - void enterNewRoom(bool force_reload); + bool enterNewRoom(); // Returns false if another room change has been triggered and therefore loop() shouldn't be called yet. void loadRoom(int roomNum); void runGateProgram(int gate); void redrawWalkingPath(int id, byte colour, const WalkingMap::Path &path); @@ -367,7 +369,8 @@ private: LoopSubstatus _loopSubstatus; bool _shouldQuit; - int _shouldExitLoop; // 0=false and 1=true are normal, 2=immediate exit after loading + bool _shouldExitLoop; + bool _isReloaded; uint _speechTick; uint _speechDuration; diff --git a/engines/draci/saveload.cpp b/engines/draci/saveload.cpp index 56f22ea420..318d1fcad1 100644 --- a/engines/draci/saveload.cpp +++ b/engines/draci/saveload.cpp @@ -152,7 +152,7 @@ Common::Error loadSavegameData(int saveGameIdx, DraciEngine *vm) { // Post-processing vm->_game->scheduleEnteringRoomUsingGate(vm->_game->getRoomNum(), 0); vm->_game->setRoomNum(vm->_game->getPreviousRoomNum()); - vm->_game->setExitLoop(2); // 2 > true means immediate exit for the loop + vm->_game->setIsReloaded(true); vm->_game->inventoryReload(); diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp index f938737728..6a2eaeddd4 100644 --- a/engines/draci/script.cpp +++ b/engines/draci/script.cpp @@ -359,11 +359,7 @@ void Script::play(Common::Queue ¶ms) { // Runs just one phase of the loop and exits. Used when waiting for a // particular animation phase to come. - _vm->_game->setLoopSubstatus(kSubstatusStrange); - _vm->_game->setExitLoop(true); - _vm->_game->loop(); - _vm->_game->setExitLoop(false); - _vm->_game->setLoopSubstatus(kSubstatusOrdinary); + _vm->_game->loop(kInnerUntilExit, true); } Animation *Script::loadObjectAnimation(GameObject *obj, int animID) { @@ -463,19 +459,14 @@ void Script::startPlay(Common::Queue ¶ms) { anim->registerCallback(&Animation::exitGameLoop); - _vm->_game->setLoopSubstatus(kSubstatusStrange); - bool visible = (obj->_location == _vm->_game->getRoomNum() && obj->_visible); - if (objID == kDragonObject || visible) { _vm->_anims->play(animID); } // Runs an inner loop until the animation ends. - _vm->_game->loop(); - _vm->_game->setExitLoop(false); + _vm->_game->loop(kInnerUntilExit, false); _vm->_anims->stop(animID); - _vm->_game->setLoopSubstatus(kSubstatusOrdinary); anim->registerCallback(&Animation::doNothing); } @@ -677,17 +668,12 @@ void Script::walkOnPlay(Common::Queue ¶ms) { int y = params.pop(); SightDirection dir = static_cast (params.pop()); - // HACK: This should be an onDest action when hero walking is properly implemented - // For now, we just go throught the loop-body once to redraw the screen. - _vm->_game->setExitLoop(true); - _vm->_game->walkHero(x, y, dir); - _vm->_game->setLoopSubstatus(kSubstatusStrange); - _vm->_game->loop(); - _vm->_game->setLoopSubstatus(kSubstatusOrdinary); - - _vm->_game->setExitLoop(false); + // HACK: This (shouldExit==true) should be an onDest action when hero + // walking is properly implemented For now, we just go throught the + // loop-body once to redraw the screen. + _vm->_game->loop(kInnerUntilExit, true); } void Script::newRoom(Common::Queue ¶ms) { @@ -737,9 +723,6 @@ void Script::talk(Common::Queue ¶ms) { speechFrame->setFont(_vm->_smallFont); } - // Set the loop substatus to an appropriate value - _vm->_game->setLoopSubstatus(kSubstatusTalk); - // Speak the dubbing if possible uint dubbingDuration = 0; if (sample) { @@ -776,12 +759,8 @@ void Script::talk(Common::Queue ¶ms) { speechFrame->setX(x); speechFrame->setY(y); - // Prevent the loop from exiting early if other things left the loop in the - // "exit immediately" state - _vm->_game->setExitLoop(false); - // Call the game loop to enable interactivity until the text expires. - _vm->_game->loop(); + _vm->_game->loop(kInnerWhileTalk, false); // Delete the text _vm->_screen->getSurface()->markDirtyRect(speechFrame->getRect(kNoDisplacement)); @@ -793,10 +772,6 @@ void Script::talk(Common::Queue ¶ms) { _vm->_sound->stopVoice(); sample->close(); } - - // Revert to "normal" loop status - _vm->_game->setLoopSubstatus(kSubstatusOrdinary); - _vm->_game->setExitLoop(false); } void Script::dialogue(Common::Queue ¶ms) { @@ -877,7 +852,7 @@ void Script::fadePalette(Common::Queue ¶ms) { int phases = params.pop(); // Let the palette fade in the background while the game continues. - // Since we don't set substatus to kSubstatusFade, the outer loop will + // Since we don't set substatus to kInnerWhileFade, the outer loop will // just continue rather than exit. _vm->_game->initializeFading(phases); } @@ -889,10 +864,7 @@ void Script::fadePalettePlay(Common::Queue ¶ms) { _vm->_game->initializeFading(phases); // Call the game loop to enable interactivity until the fading is done. - _vm->_game->setLoopSubstatus(kSubstatusFade); - _vm->_game->loop(); - _vm->_game->setExitLoop(false); - _vm->_game->setLoopSubstatus(kSubstatusOrdinary); + _vm->_game->loop(kInnerWhileFade, false); } void Script::setPalette(Common::Queue ¶ms) { -- cgit v1.2.3