aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/draci/animation.cpp4
-rw-r--r--engines/draci/animation.h1
-rw-r--r--engines/draci/game.cpp31
-rw-r--r--engines/draci/game.h2
-rw-r--r--engines/draci/walking.cpp143
-rw-r--r--engines/draci/walking.h34
6 files changed, 183 insertions, 32 deletions
diff --git a/engines/draci/animation.cpp b/engines/draci/animation.cpp
index 560f03ffcf..2bbb960fb1 100644
--- a/engines/draci/animation.cpp
+++ b/engines/draci/animation.cpp
@@ -210,6 +210,10 @@ void Animation::exitGameLoop() {
_vm->_game->setExitLoop(true);
}
+void Animation::tellWalkingState() {
+ _vm->_game->heroAnimationFinished();
+}
+
Animation *AnimationManager::addAnimation(int id, uint z, bool playing) {
// Increment animation index
++_lastIndex;
diff --git a/engines/draci/animation.h b/engines/draci/animation.h
index e7ffc9d761..473e875941 100644
--- a/engines/draci/animation.h
+++ b/engines/draci/animation.h
@@ -114,6 +114,7 @@ public:
void doNothing() {}
void stopAnimation();
void exitGameLoop();
+ void tellWalkingState();
private:
uint nextFrameNum() const;
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index 11e9546b39..8539751c87 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -956,6 +956,11 @@ void Game::runDialogueProg(GPL2Program prog, int offset) {
void Game::playHeroAnimation(int anim_index) {
const GameObject *dragon = getObject(kDragonObject);
+ const int current_anim_index = playingObjectAnimation(dragon);
+ if (anim_index == current_anim_index) {
+ return;
+ }
+
const int animID = dragon->_anim[anim_index];
Animation *anim = _vm->_anims->getAnimation(animID);
stopObjectAnimations(dragon);
@@ -971,28 +976,14 @@ void Game::redrawWalkingPath(int id, byte colour, const WalkingPath &path) {
anim->markDirtyRect(_vm->_screen->getSurface());
}
-void Game::positionHero(const Common::Point &p, SightDirection dir) {
+void Game::setHeroPosition(const Common::Point &p) {
debugC(3, kDraciWalkingDebugLevel, "Jump to x: %d y: %d", p.x, p.y);
-
_hero = p;
- Movement movement = kStopRight;
- switch (dir) {
- case kDirectionLeft:
- movement = kStopLeft;
- break;
- case kDirectionRight:
- movement = kStopRight;
- break;
- default: {
- const GameObject *dragon = getObject(kDragonObject);
- const int anim_index = playingObjectAnimation(dragon);
- if (anim_index >= 0) {
- movement = static_cast<Movement> (anim_index);
- }
- break;
- }
- }
- playHeroAnimation(movement);
+}
+
+void Game::positionHero(const Common::Point &p, SightDirection dir) {
+ setHeroPosition(p);
+ playHeroAnimation(_walkingState.animationForSightDirection(dir));
}
Common::Point Game::findNearestWalkable(int x, int y) const {
diff --git a/engines/draci/game.h b/engines/draci/game.h
index bb4d97e6af..f57b917e91 100644
--- a/engines/draci/game.h
+++ b/engines/draci/game.h
@@ -207,9 +207,11 @@ public:
}
Common::Point findNearestWalkable(int x, int y) const;
+ void heroAnimationFinished() { _walkingState.heroAnimationFinished(); }
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
+ void setHeroPosition(const Common::Point &p);
int getHeroX() const { return _hero.x; }
int getHeroY() const { return _hero.y; }
void positionAnimAsHero(Animation *anim);
diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp
index 0f2dda4b88..1400471f04 100644
--- a/engines/draci/walking.cpp
+++ b/engines/draci/walking.cpp
@@ -347,11 +347,11 @@ void WalkingMap::drawOverlayRectangle(const Common::Point &p, byte colour, byte
}
}
-int WalkingMap::pointsBetween(const Common::Point &p1, const Common::Point &p2) const {
+int WalkingMap::pointsBetween(const Common::Point &p1, const Common::Point &p2) {
return MAX(abs(p2.x - p1.x), abs(p2.y - p1.y));
}
-Common::Point WalkingMap::interpolate(const Common::Point &p1, const Common::Point &p2, int i, int n) const {
+Common::Point WalkingMap::interpolate(const Common::Point &p1, const Common::Point &p2, int i, int n) {
const int x = (p1.x * (n-i) + p2.x * i + n/2) / n;
const int y = (p1.y * (n-i) + p2.y * i + n/2) / n;
return Common::Point(x, y);
@@ -455,6 +455,10 @@ void WalkingState::startWalking(const Common::Point &p1, const Common::Point &p2
_path[i].x *= delta.x;
_path[i].y *= delta.y;
}
+
+ // Going to start with the first segment.
+ _segment = _lastAnimPhase = _position = _length = -1;
+ turnForTheNextSegment();
}
void WalkingState::setCallback(const GPL2Program *program, uint16 offset) {
@@ -484,13 +488,106 @@ void WalkingState::callback() {
}
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
+ const GameObject *dragon = _vm->_game->getObject(kDragonObject);
+ const Movement anim_index = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+
+ // If the current animation is a turning animation, wait a bit more.
+ // When this animation has finished, heroAnimationFinished() callback
+ // will be called, which starts a new scheduled one, so the code never
+ // gets here if it hasn't finished yet.
+ if (isTurningMovement(anim_index)) {
+ return true;
+ }
+
+ // If the current segment is the last one, we have reached the
+ // destination and are already facing in the right direction ===>
+ // return false.
+ if (_segment >= (int) (_path.size() - 1)) {
+ _path.clear();
+ return false;
+ }
+
+ // Read the dragon's animation's current phase. Determine if it has
+ // changed from the last time. If not, wait until it has.
+ const int animID = dragon->_anim[anim_index];
+ Animation *anim = _vm->_anims->getAnimation(animID);
+ const int animPhase = anim->currentFrameNum();
+ const bool wasUpdated = animPhase != _lastAnimPhase;
+ if (!wasUpdated) {
+ return true;
+ }
+
+ debugC(3, kDraciWalkingDebugLevel, "Continuing walking in segment %d and position %d/%d", _segment, _position, _length);
+
+ // We are walking in the middle of an edge. The animation phase has
+ // just changed. Update the position of the hero.
+ Common::Point newPos = WalkingMap::interpolate(
+ _path[_segment], _path[_segment+1], ++_position, _length);
+ _vm->_game->setHeroPosition(newPos);
+ _vm->_game->positionAnimAsHero(anim);
+
+ // If the hero has reached the end of the edge, start transition to the
+ // next phase. This will increment _segment, either immediately (if no
+ // transition is needed) or in the callback (after the transition is
+ // done).
+ if (_position >= _length) {
+ turnForTheNextSegment();
+ }
+
+ return true;
+}
+
+void WalkingState::turnForTheNextSegment() {
+ const GameObject *dragon = _vm->_game->getObject(kDragonObject);
+ const Movement currentAnim = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+ const Movement wantAnim = directionForNextPhase();
+ Movement transition = transitionBetweenAnimations(currentAnim, wantAnim);
+
+ debugC(2, kDraciWalkingDebugLevel, "Turning for segment %d", _segment+1);
+
+ if (transition == kMoveUndefined) {
+ // Start the next segment right away as if the turning has just finished.
+ heroAnimationFinished();
+ } else {
+ // Otherwise start the transition and wait until the Animation
+ // class calls heroAnimationFinished() as a callback.
+ assert(isTurningMovement(transition));
+ _vm->_game->playHeroAnimation(transition);
+ const int animID = dragon->_anim[transition];
+ Animation *anim = _vm->_anims->getAnimation(animID);
+ anim->registerCallback(&Animation::tellWalkingState);
+
+ debugC(2, kDraciWalkingDebugLevel, "Starting turning animation %d", transition);
+ }
+}
+
+void WalkingState::heroAnimationFinished() {
+ // The hero is turned well for the next line segment or for facing the
+ // target direction.
+
+ // Start the desired next animation. playHeroAnimation() takes care of
+ // stopping the current animation.
+ // Don't use any callbacks, because continueWalking() will decide the
+ // end on its own and after walking is done callbacks shouldn't be
+ // called either. It wouldn't make much sense anyway, since the
+ // walking/staying/talking animations are cyclic.
+ Movement nextAnim = directionForNextPhase();
+ _vm->_game->playHeroAnimation(nextAnim);
+ _lastAnimPhase = 0;
+
+ debugC(2, kDraciWalkingDebugLevel, "Turned for segment %d, starting animation %d", _segment+1, nextAnim);
+
+ if (++_segment < (int) (_path.size() - 1)) {
+ // We are on an edge: track where the hero is on this edge.
+ _position = 0;
+ _length = WalkingMap::pointsBetween(_path[_segment], _path[_segment+1]);
+ debugC(2, kDraciWalkingDebugLevel, "Next segment %d has length %d", _segment, _length);
+ } else {
+ // Otherwise we are done. continueWalking() will return false next time.
+ debugC(2, kDraciWalkingDebugLevel, "We have walked the whole path");
+ }
+
+ // TODO: do we need to clear this callback for the animation?
}
Movement WalkingState::animationForDirection(const Common::Point &here, const Common::Point &there) {
@@ -503,6 +600,14 @@ Movement WalkingState::animationForDirection(const Common::Point &here, const Co
}
}
+Movement WalkingState::directionForNextPhase() const {
+ if (_segment >= (int) (_path.size() - 2)) {
+ return animationForSightDirection(_dir);
+ } else {
+ return animationForDirection(_path[_segment+1], _path[_segment+2]);
+ }
+}
+
Movement WalkingState::transitionBetweenAnimations(Movement previous, Movement next) {
switch (next) {
case kMoveUp:
@@ -584,4 +689,24 @@ Movement WalkingState::transitionBetweenAnimations(Movement previous, Movement n
}
}
+Movement WalkingState::animationForSightDirection(SightDirection dir) const {
+ switch (dir) {
+ case kDirectionLeft:
+ return kStopLeft;
+ case kDirectionRight:
+ return kStopRight;
+ default: {
+ const GameObject *dragon = _vm->_game->getObject(kDragonObject);
+ const int anim_index = _vm->_game->playingObjectAnimation(dragon);
+ if (anim_index >= 0) {
+ return static_cast<Movement> (anim_index);
+ } else {
+ return kStopRight; // TODO
+ }
+ break;
+ }
+ }
+ // TODO: implement all needed functionality
+}
+
}
diff --git a/engines/draci/walking.h b/engines/draci/walking.h
index b85b77d53d..180c3cf855 100644
--- a/engines/draci/walking.h
+++ b/engines/draci/walking.h
@@ -53,6 +53,9 @@ public:
Sprite *newOverlayFromPath(const WalkingPath &path, byte colour) const;
Common::Point getDelta() const { return Common::Point(_deltaX, _deltaY); }
+ static int pointsBetween(const Common::Point &p1, const Common::Point &p2);
+ static Common::Point interpolate(const Common::Point &p1, const Common::Point &p2, int i, int n);
+
private:
int _realWidth, _realHeight;
int _deltaX, _deltaY;
@@ -66,8 +69,6 @@ private:
static int kDirections[][2];
void drawOverlayRectangle(const Common::Point &p, byte colour, byte *buf) const;
- int pointsBetween(const Common::Point &p1, const Common::Point &p2) const;
- Common::Point interpolate(const Common::Point &p1, const Common::Point &p2, int i, int n) const;
bool lineIsCovered(const Common::Point &p1, const Common::Point &p2) const;
// Returns true if the number of vertices on the path was decreased.
@@ -88,9 +89,13 @@ enum SightDirection {
enum Movement {
kMoveUndefined = -1,
kMoveDown, kMoveUp, kMoveRight, kMoveLeft,
- kMoveRightDown, kMoveRightUp, kMoveLeftDown, kMoveLeftUp,
+
+ kFirstTurning,
+ kMoveRightDown = kFirstTurning, kMoveRightUp, kMoveLeftDown, kMoveLeftUp,
kMoveDownRight, kMoveUpRight, kMoveDownLeft, kMoveUpLeft,
kMoveLeftRight, kMoveRightLeft, kMoveUpStopLeft, kMoveUpStopRight,
+ kLastTurning = kMoveUpStopRight,
+
kSpeakRight, kSpeakLeft, kStopRight, kStopLeft
};
@@ -119,6 +124,13 @@ public:
// the callback untouched (the caller must call it).
bool continueWalking();
+ // Called when the hero's turning animation has finished. Starts
+ // scheduled animation.
+ void heroAnimationFinished();
+
+ // Returns the hero's animation corresponding to looking into given direction.
+ Movement animationForSightDirection(SightDirection dir) const;
+
private:
DraciEngine *_vm;
@@ -126,17 +138,33 @@ private:
Common::Point _mouse;
SightDirection _dir;
+ int _segment;
+ int _position, _length;
+ int _lastAnimPhase;
+
const GPL2Program *_callback;
uint16 _callbackOffset;
+ // Initiates turning of the dragon into the direction for the next segment / after walking.
+ void turnForTheNextSegment();
+
// Return one of the 4 animations kMove{Down,Up,Right,Left}
// corresponding to the walking from here to there.
static Movement animationForDirection(const Common::Point &here, const Common::Point &there);
+ // Returns the desired facing direction to begin the next phase of the
+ // walk. It's either a direction for the given edge or the desired
+ // final direction.
+ Movement directionForNextPhase() const;
+
// Returns either animation that needs to be played between given two
// animations (e.g., kMoveRightDown after kMoveRight and before
// kMoveDown), or kMoveUndefined if none animation is to be played.
static Movement transitionBetweenAnimations(Movement previous, Movement next);
+
+ static bool isTurningMovement(Movement m) {
+ return m >= kFirstTurning && m <= kLastTurning;
+ }
};
} // End of namespace Draci