aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/draci/draci.cpp1
-rw-r--r--engines/draci/draci.h3
-rw-r--r--engines/draci/game.cpp69
-rw-r--r--engines/draci/game.h7
-rw-r--r--engines/draci/script.cpp13
-rw-r--r--engines/draci/walking.cpp25
-rw-r--r--engines/draci/walking.h17
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> &params) {
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> &params) {
@@ -675,6 +675,7 @@ void Script::walkOn(Common::Queue<int> &params) {
// 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> &params) {
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> &params) {
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;