[Scummvm-cvs-logs] SF.net SVN: scummvm:[45688] scummvm/trunk/engines/draci
spalek at users.sourceforge.net
spalek at users.sourceforge.net
Thu Nov 5 15:22:44 CET 2009
Revision: 45688
http://scummvm.svn.sourceforge.net/scummvm/?rev=45688&view=rev
Author: spalek
Date: 2009-11-05 14:22:39 +0000 (Thu, 05 Nov 2009)
Log Message:
-----------
Implemented proper walking.
First shot, not debugged yet, but seems to work (even though a bit hairy)!
Modified Paths:
--------------
scummvm/trunk/engines/draci/animation.cpp
scummvm/trunk/engines/draci/animation.h
scummvm/trunk/engines/draci/game.cpp
scummvm/trunk/engines/draci/game.h
scummvm/trunk/engines/draci/walking.cpp
scummvm/trunk/engines/draci/walking.h
Modified: scummvm/trunk/engines/draci/animation.cpp
===================================================================
--- scummvm/trunk/engines/draci/animation.cpp 2009-11-05 13:51:32 UTC (rev 45687)
+++ scummvm/trunk/engines/draci/animation.cpp 2009-11-05 14:22:39 UTC (rev 45688)
@@ -210,6 +210,10 @@
_vm->_game->setExitLoop(true);
}
+void Animation::tellWalkingState() {
+ _vm->_game->heroAnimationFinished();
+}
+
Animation *AnimationManager::addAnimation(int id, uint z, bool playing) {
// Increment animation index
++_lastIndex;
Modified: scummvm/trunk/engines/draci/animation.h
===================================================================
--- scummvm/trunk/engines/draci/animation.h 2009-11-05 13:51:32 UTC (rev 45687)
+++ scummvm/trunk/engines/draci/animation.h 2009-11-05 14:22:39 UTC (rev 45688)
@@ -114,6 +114,7 @@
void doNothing() {}
void stopAnimation();
void exitGameLoop();
+ void tellWalkingState();
private:
uint nextFrameNum() const;
Modified: scummvm/trunk/engines/draci/game.cpp
===================================================================
--- scummvm/trunk/engines/draci/game.cpp 2009-11-05 13:51:32 UTC (rev 45687)
+++ scummvm/trunk/engines/draci/game.cpp 2009-11-05 14:22:39 UTC (rev 45688)
@@ -956,6 +956,11 @@
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,30 +976,16 @@
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 {
Surface *surface = _vm->_screen->getSurface();
return _walkingMap.findNearestWalkable(x, y, surface->getDimensions());
Modified: scummvm/trunk/engines/draci/game.h
===================================================================
--- scummvm/trunk/engines/draci/game.h 2009-11-05 13:51:32 UTC (rev 45687)
+++ scummvm/trunk/engines/draci/game.h 2009-11-05 14:22:39 UTC (rev 45688)
@@ -207,9 +207,11 @@
}
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);
Modified: scummvm/trunk/engines/draci/walking.cpp
===================================================================
--- scummvm/trunk/engines/draci/walking.cpp 2009-11-05 13:51:32 UTC (rev 45687)
+++ scummvm/trunk/engines/draci/walking.cpp 2009-11-05 14:22:39 UTC (rev 45688)
@@ -347,11 +347,11 @@
}
}
-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 @@
_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,15 +488,108 @@
}
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) {
const int dx = there.x - here.x;
const int dy = there.y - here.y;
@@ -503,6 +600,14 @@
}
}
+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::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
}
+
+}
Modified: scummvm/trunk/engines/draci/walking.h
===================================================================
--- scummvm/trunk/engines/draci/walking.h 2009-11-05 13:51:32 UTC (rev 45687)
+++ scummvm/trunk/engines/draci/walking.h 2009-11-05 14:22:39 UTC (rev 45688)
@@ -53,6 +53,9 @@
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 @@
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 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 @@
// 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 @@
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
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the Scummvm-git-logs
mailing list