aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorRobert Špalek2009-11-04 00:42:37 +0000
committerRobert Špalek2009-11-04 00:42:37 +0000
commit14f268513487f0eafbe1cbf7c9b7b928d77a6148 (patch)
tree7a6829fd0cae0a2b66c75eae36b6914023387c3c /engines
parent692aea8e8d09163da3e05181784fd0b79efea8af (diff)
downloadscummvm-rg350-14f268513487f0eafbe1cbf7c9b7b928d77a6148.tar.gz
scummvm-rg350-14f268513487f0eafbe1cbf7c9b7b928d77a6148.tar.bz2
scummvm-rg350-14f268513487f0eafbe1cbf7c9b7b928d77a6148.zip
Implemented and debugged the walking framework.
The hero does not walk yet (it still teleports to the target immediately), but that is just because the actual walking algorithm is left trivial first. However, the main game loop, callbacks, and waiting all already work with the general framework. svn-id: r45648
Diffstat (limited to 'engines')
-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;