[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