diff options
Diffstat (limited to 'engines/draci')
-rw-r--r-- | engines/draci/draci.cpp | 1 | ||||
-rw-r--r-- | engines/draci/draci.h | 3 | ||||
-rw-r--r-- | engines/draci/game.cpp | 69 | ||||
-rw-r--r-- | engines/draci/game.h | 7 | ||||
-rw-r--r-- | engines/draci/script.cpp | 13 | ||||
-rw-r--r-- | engines/draci/walking.cpp | 25 | ||||
-rw-r--r-- | engines/draci/walking.h | 17 |
7 files changed, 100 insertions, 35 deletions
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp index 2831ff605a..d9720b77fd 100644 --- a/engines/draci/draci.cpp +++ b/engines/draci/draci.cpp @@ -84,6 +84,7 @@ DraciEngine::DraciEngine(OSystem *syst, const ADGameDescription *gameDesc) Common::addDebugChannel(kDraciLogicDebugLevel, "logic", "Game logic debug info"); Common::addDebugChannel(kDraciAnimationDebugLevel, "animation", "Animation debug info"); Common::addDebugChannel(kDraciSoundDebugLevel, "sound", "Sound debug info"); + Common::addDebugChannel(kDraciWalkingDebugLevel, "walking", "Walking debug info"); // Don't forget to register your random source g_eventRec.registerRandomSource(_rnd, "draci"); diff --git a/engines/draci/draci.h b/engines/draci/draci.h index c09b086cf2..1707fc29ad 100644 --- a/engines/draci/draci.h +++ b/engines/draci/draci.h @@ -105,7 +105,8 @@ enum { kDraciArchiverDebugLevel = 1 << 2, kDraciLogicDebugLevel = 1 << 3, kDraciAnimationDebugLevel = 1 << 4, - kDraciSoundDebugLevel = 1 << 5 + kDraciSoundDebugLevel = 1 << 5, + kDraciWalkingDebugLevel = 1 << 6 }; } // End of namespace Draci diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp index 51be193631..c404b50618 100644 --- a/engines/draci/game.cpp +++ b/engines/draci/game.cpp @@ -248,16 +248,17 @@ void Game::handleOrdinaryLoop(int x, int y) { _walkingState.setCallback(&obj->_program, obj->_look); - if (!obj->_imLook) { + if (obj->_imLook || !_currentRoom._heroOn) { + _walkingState.callback(); + } else { if (obj->_lookDir == kDirectionLast) { walkHero(x, y, obj->_lookDir); } else { walkHero(obj->_lookX, obj->_lookY, obj->_lookDir); } } - - _walkingState.callback(); } else { + _walkingState.setCallback(NULL, 0); walkHero(x, y, kDirectionLast); } } @@ -272,16 +273,17 @@ void Game::handleOrdinaryLoop(int x, int y) { if (_vm->_script->testExpression(obj->_program, obj->_canUse)) { _walkingState.setCallback(&obj->_program, obj->_use); - if (!obj->_imUse) { + if (obj->_imUse || !_currentRoom._heroOn) { + _walkingState.callback(); + } else { if (obj->_useDir == kDirectionLast) { walkHero(x, y, obj->_useDir); } else { walkHero(obj->_useX, obj->_useY, obj->_useDir); } } - - _walkingState.callback(); } else { + _walkingState.setCallback(NULL, 0); walkHero(x, y, kDirectionLast); } } else { @@ -289,6 +291,7 @@ void Game::handleOrdinaryLoop(int x, int y) { _walkingState.setCallback(&_currentRoom._program, _currentRoom._use); _walkingState.callback(); } else { + _walkingState.setCallback(NULL, 0); walkHero(x, y, kDirectionLast); } } @@ -436,6 +439,29 @@ void Game::advanceAnimationsAndTestLoopExit() { setExitLoop(true); } + // Walk the hero. The WalkingState class handles everything including + // proper timing. + if (_walkingState.isActive()) { + if (!_walkingState.continueWalking()) { + // Walking has finished. + bool exitLoop = false; + if (_loopSubstatus == kInnerUntilExit) { + // The callback may run another inner loop (for + // example, a dialogue). Reset the loop + // substatus temporarily to the outer one. + exitLoop = true; + setLoopSubstatus(kOuterLoop); + } + debugC(2, kDraciWalkingDebugLevel, "Finished walking"); + _walkingState.callback(); // clears callback pointer first + if (exitLoop) { + debugC(3, kDraciWalkingDebugLevel, "Exiting from the inner loop"); + setExitLoop(true); + setLoopSubstatus(kInnerUntilExit); + } + } + } + // Advance animations (this may also call setExitLoop(true) in the // callbacks) and redraw screen _vm->_anims->drawScene(_vm->_screen->getSurface()); @@ -942,7 +968,7 @@ void Game::redrawWalkingPath(int id, byte colour, const WalkingPath &path) { } void Game::positionHero(const Common::Point &p, SightDirection dir) { - debugC(3, kDraciLogicDebugLevel, "Jump to x: %d y: %d", p.x, p.y); + debugC(3, kDraciWalkingDebugLevel, "Jump to x: %d y: %d", p.x, p.y); _hero = p; Movement movement = kStopRight; @@ -965,33 +991,38 @@ void Game::positionHero(const Common::Point &p, SightDirection dir) { playHeroAnimation(movement); } +Common::Point Game::findNearestWalkable(int x, int y) const { + Surface *surface = _vm->_screen->getSurface(); + return _walkingMap.findNearestWalkable(x, y, surface->getDimensions()); +} + void Game::walkHero(int x, int y, SightDirection dir) { - // Needed for the map room with empty walking map. For some reason, - // findNearestWalkable() takes several seconds with 100% CPU to finish - // (correctly). - if (!_currentRoom._heroOn) + if (!_currentRoom._heroOn) { + // Nothing to do. Happens for example in the map. return; + } - Common::Point target; - Surface *surface = _vm->_screen->getSurface(); - target = _walkingMap.findNearestWalkable(x, y, surface->getDimensions()); - debugC(3, kDraciLogicDebugLevel, "Walk to x: %d y: %d", target.x, target.y); + Common::Point target = findNearestWalkable(x, y); // Compute the shortest and obliqued path. WalkingPath shortestPath, obliquePath; _walkingMap.findShortestPath(_hero, target, &shortestPath); // TODO: test reachability and react _walkingMap.obliquePath(shortestPath, &obliquePath); + debugC(2, kDraciWalkingDebugLevel, "Walking path lengths: shortest=%d oblique=%d", shortestPath.size(), obliquePath.size()); if (_vm->_showWalkingMap) { redrawWalkingPath(kWalkingShortestPathOverlay, kWalkingShortestPathOverlayColour, shortestPath); redrawWalkingPath(kWalkingObliquePathOverlay, kWalkingObliquePathOverlayColour, obliquePath); } - _walkingState.setPath(_hero, target, Common::Point(x, y), + // Start walking. Walking will be gradually advanced by + // advanceAnimationsAndTestLoopExit(), which also handles calling the + // callback and stopping the walk at the end. If the hero is already + // walking at this point, this command will cancel the previous path + // and replace it by the current one (the callback has already been + // reset by our caller). + _walkingState.startWalking(_hero, target, Common::Point(x, y), dir, _walkingMap.getDelta(), obliquePath); - - // FIXME: Need to add proper walking (this only warps the dragon to position) - positionHero(target, dir); } void Game::loadItem(int itemID) { diff --git a/engines/draci/game.h b/engines/draci/game.h index ee7853a1f9..bb4d97e6af 100644 --- a/engines/draci/game.h +++ b/engines/draci/game.h @@ -206,9 +206,10 @@ public: return n; } - void clearPath() { _walkingState.clearPath(); } - void positionHero(const Common::Point &p, SightDirection dir); - void walkHero(int x, int y, SightDirection dir); + Common::Point findNearestWalkable(int x, int y) const; + void stopWalking() { _walkingState.stopWalking(); } // and clear callback + void positionHero(const Common::Point &p, SightDirection dir); // teleport the dragon + void walkHero(int x, int y, SightDirection dir); // start walking and leave callback as is int getHeroX() const { return _hero.x; } int getHeroY() const { return _hero.y; } void positionAnimAsHero(Animation *anim); diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp index 14cca84062..1bb833da22 100644 --- a/engines/draci/script.cpp +++ b/engines/draci/script.cpp @@ -660,8 +660,8 @@ void Script::stayOn(Common::Queue<int> ¶ms) { SightDirection dir = static_cast<SightDirection> (params.pop()); // Jumps into the given position regardless of the walking map. - _vm->_game->positionHero(Common::Point(x, y), dir); - _vm->_game->clearPath(); + _vm->_game->stopWalking(); + _vm->_game->positionHero(_vm->_game->findNearestWalkable(x, y), dir); } void Script::walkOn(Common::Queue<int> ¶ms) { @@ -675,6 +675,7 @@ void Script::walkOn(Common::Queue<int> ¶ms) { // Constructs an optimal path and starts walking there. No callback // will be called at the end nor will the loop-body exit. + _vm->_game->stopWalking(); _vm->_game->walkHero(x, y, dir); } @@ -687,12 +688,12 @@ void Script::walkOnPlay(Common::Queue<int> ¶ms) { int y = params.pop(); SightDirection dir = static_cast<SightDirection> (params.pop()); + _vm->_game->stopWalking(); _vm->_game->walkHero(x, y, dir); - // 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); + // Walk in an inner loop until the hero has arrived at the target + // point. Then the loop-body will exit. + _vm->_game->loop(kInnerUntilExit, false); } void Script::newRoom(Common::Queue<int> ¶ms) { diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp index f50a90732a..9868486c2d 100644 --- a/engines/draci/walking.cpp +++ b/engines/draci/walking.cpp @@ -422,14 +422,17 @@ bool WalkingMap::managedToOblique(WalkingPath *path) const { return improved; } -void WalkingState::clearPath() { +void WalkingState::stopWalking() { _path.clear(); _callback = NULL; } -void WalkingState::setPath(const Common::Point &p1, const Common::Point &p2, const Common::Point &mouse, const Common::Point &delta, const WalkingPath& path) { +void WalkingState::startWalking(const Common::Point &p1, const Common::Point &p2, + const Common::Point &mouse, SightDirection dir, + const Common::Point &delta, const WalkingPath& path) { _path = path; _mouse = mouse; + _dir = dir; if (!_path.size()) { return; @@ -440,6 +443,8 @@ void WalkingState::setPath(const Common::Point &p1, const Common::Point &p2, con // they are different pixels. _path.push_back(p2); } + debugC(2, kDraciWalkingDebugLevel, "Starting walking [%d,%d] -> [%d,%d] in %d segments", + p1.x, p1.y, p2.x, p2.y, _path.size()); // The first and last point are available with pixel accurracy. _path[0] = p1; @@ -461,6 +466,7 @@ void WalkingState::callback() { if (!_callback) { return; } + debugC(2, kDraciWalkingDebugLevel, "Calling walking callback"); // Fetch the dedicated objects' title animation / current frame Animation *titleAnim = _vm->_anims->getAnimation(kTitleText); @@ -470,8 +476,21 @@ void WalkingState::callback() { titleAnim->markDirtyRect(_vm->_screen->getSurface()); title->setText(""); - _vm->_script->run(*_callback, _callbackOffset); + const GPL2Program *originalCallback = _callback; + _callback = NULL; + _vm->_script->run(*originalCallback, _callbackOffset); + _vm->_mouse->cursorOn(); } +bool WalkingState::continueWalking() { + // FIXME: do real walking instead of immediately exiting. Compare the + // current dragon's animation phase with the stored one, and if they + // differ, walk another step. + debugC(2, kDraciWalkingDebugLevel, "Continuing walking"); + _vm->_game->positionHero(_path[_path.size() - 1], _dir); + _path.clear(); + return false; // finished +} + } diff --git a/engines/draci/walking.h b/engines/draci/walking.h index 4e368fdfe5..bc53231d5a 100644 --- a/engines/draci/walking.h +++ b/engines/draci/walking.h @@ -99,21 +99,32 @@ struct GPL2Program; class WalkingState { public: - explicit WalkingState(DraciEngine *vm) : _vm(vm) { clearPath(); } + explicit WalkingState(DraciEngine *vm) : _vm(vm) { stopWalking(); } ~WalkingState() {} - void clearPath(); - void setPath(const Common::Point &p1, const Common::Point &p2, const Common::Point &mouse, const Common::Point &delta, const WalkingPath& path); + void stopWalking(); + void startWalking(const Common::Point &p1, const Common::Point &p2, + const Common::Point &mouse, SightDirection dir, + const Common::Point &delta, const WalkingPath& path); const WalkingPath& getPath() const { return _path; } void setCallback(const GPL2Program *program, uint16 offset); void callback(); + bool isActive() const { return _path.size() > 0; } + + // Advances the hero along the path and changes animation accordingly. + // Walking MUST be active when calling this method. When the hero has + // arrived to the target, clears the path and returns false, but leaves + // the callback untouched (the caller must call it). + bool continueWalking(); + private: DraciEngine *_vm; WalkingPath _path; Common::Point _mouse; + SightDirection _dir; const GPL2Program *_callback; uint16 _callbackOffset; |