[Scummvm-cvs-logs] SF.net SVN: scummvm:[45742] scummvm/trunk/engines/draci

spalek at users.sourceforge.net spalek at users.sourceforge.net
Sun Nov 8 04:16:22 CET 2009


Revision: 45742
          http://scummvm.svn.sourceforge.net/scummvm/?rev=45742&view=rev
Author:   spalek
Date:     2009-11-08 03:16:22 +0000 (Sun, 08 Nov 2009)

Log Message:
-----------
Debugged smooth walking except for 1 bug.

Adjusting to the edge is done such that it respects slight sideways movements of the dragon.
Fixed rounding issues in the whole game.  Improved debug messages.  Made sure that the dragon
does not turn like crazy around when clicking on the same pixel: the final point is always the
clicked one although the middle points made by shifted to make the animations smooth, and
preserve the dragons direction if he has not walked.

There is a bug with running turning animations as they seem to disappear for 1 frame and have
incorrect Z coordinate.  Will investigate it next.

Modified Paths:
--------------
    scummvm/trunk/engines/draci/animation.cpp
    scummvm/trunk/engines/draci/draci.h
    scummvm/trunk/engines/draci/game.cpp
    scummvm/trunk/engines/draci/script.cpp
    scummvm/trunk/engines/draci/sprite.cpp
    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-07 23:56:16 UTC (rev 45741)
+++ scummvm/trunk/engines/draci/animation.cpp	2009-11-08 03:16:22 UTC (rev 45742)
@@ -59,8 +59,8 @@
 
 Displacement Animation::getCurrentFrameDisplacement() const {
 	Displacement dis = _displacement;
-	dis.relX += (int) (dis.extraScaleX * _shift.x);
-	dis.relY += (int) (dis.extraScaleY * _shift.y);
+	dis.relX += scummvm_lround(dis.extraScaleX * _shift.x);
+	dis.relY += scummvm_lround(dis.extraScaleY * _shift.y);
 	return dis;
 }
 

Modified: scummvm/trunk/engines/draci/draci.h
===================================================================
--- scummvm/trunk/engines/draci/draci.h	2009-11-07 23:56:16 UTC (rev 45741)
+++ scummvm/trunk/engines/draci/draci.h	2009-11-08 03:16:22 UTC (rev 45742)
@@ -26,6 +26,8 @@
 #ifndef DRACI_H
 #define DRACI_H
 
+#include <math.h>
+
 #include "common/system.h"
 #include "engines/engine.h"
 #include "engines/advancedDetector.h"
@@ -109,6 +111,9 @@
 	kDraciWalkingDebugLevel   = 1 << 6
 };
 
+// Macro to simulate lround() for non-C99 compilers
+static inline long scummvm_lround(double val) { return (long)floor(val + 0.5); }
+
 } // End of namespace Draci
 
 #endif // DRACI_H

Modified: scummvm/trunk/engines/draci/game.cpp
===================================================================
--- scummvm/trunk/engines/draci/game.cpp	2009-11-07 23:56:16 UTC (rev 45741)
+++ scummvm/trunk/engines/draci/game.cpp	2009-11-08 03:16:22 UTC (rev 45742)
@@ -1482,8 +1482,8 @@
 	// click but sprites are drawn from their top-left corner so we subtract
 	// the current height of the dragon's sprite
 	Common::Point p = _hero;
-	p.x -= (int)(scale * frame->getWidth() / 2);
-	p.y -= (int)(scale * frame->getHeight());
+	p.x -= scummvm_lround(scale * frame->getWidth() / 2);
+	p.y -= scummvm_lround(scale * frame->getHeight());
 
 	// Since _persons[] is used for placing talking text, we use the non-adjusted x value
 	// so the text remains centered over the dragon.
@@ -1511,8 +1511,8 @@
 	// elsewhere).
 	// TODO: what about rounding errors?
 	Drawable *frame = anim->getCurrentFrame();
-	_hero.x += (int) (anim->getScaleX() * frame->getWidth() / 2);
-	_hero.y += (int) (anim->getScaleY() * frame->getHeight());
+	_hero.x += scummvm_lround(anim->getScaleX() * frame->getWidth() / 2);
+	_hero.y += scummvm_lround(anim->getScaleY() * frame->getHeight());
 }
 
 void Game::pushNewRoom() {

Modified: scummvm/trunk/engines/draci/script.cpp
===================================================================
--- scummvm/trunk/engines/draci/script.cpp	2009-11-07 23:56:16 UTC (rev 45741)
+++ scummvm/trunk/engines/draci/script.cpp	2009-11-08 03:16:22 UTC (rev 45742)
@@ -666,11 +666,13 @@
 	// Jumps into the given position regardless of the walking map.
 	Common::Point heroPos(_vm->_game->findNearestWalkable(x, y));
 	Common::Point mousePos(_vm->_mouse->getPosX(), _vm->_mouse->getPosY());
+	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
+	Movement startingDirection = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
 
 	_vm->_game->stopWalking();
 	_vm->_game->setHeroPosition(heroPos);
 	_vm->_game->playHeroAnimation(WalkingState::animationForSightDirection(
-		  dir, heroPos, mousePos, WalkingPath()));
+		  dir, heroPos, mousePos, WalkingPath(), startingDirection));
 }
 
 void Script::walkOn(Common::Queue<int> &params) {

Modified: scummvm/trunk/engines/draci/sprite.cpp
===================================================================
--- scummvm/trunk/engines/draci/sprite.cpp	2009-11-07 23:56:16 UTC (rev 45741)
+++ scummvm/trunk/engines/draci/sprite.cpp	2009-11-08 03:16:22 UTC (rev 45742)
@@ -114,10 +114,6 @@
 	}
 }
 
-// Macro to simulate lround() for non-C99 compilers
-// TODO: get rid of it
-static inline long scummvm_lround(double val) { return (long)floor(val + 0.5); }
-
 int Sprite::getPixel(int x, int y, const Displacement &displacement) const {
 	Common::Rect rect = getRect(displacement);
 
@@ -262,8 +258,8 @@
 
 Common::Rect Sprite::getRect(const Displacement &displacement) const {
 	return Common::Rect(_x + displacement.relX, _y + displacement.relY,
-	    _x + displacement.relX + (int) (_scaledWidth * displacement.extraScaleX),
-	    _y + displacement.relY + (int) (_scaledHeight * displacement.extraScaleY));
+	    _x + displacement.relX + scummvm_lround(_scaledWidth * displacement.extraScaleX),
+	    _y + displacement.relY + scummvm_lround(_scaledHeight * displacement.extraScaleY));
 }
 
 Text::Text(const Common::String &str, const Font *font, byte fontColour,

Modified: scummvm/trunk/engines/draci/walking.cpp
===================================================================
--- scummvm/trunk/engines/draci/walking.cpp	2009-11-07 23:56:16 UTC (rev 45741)
+++ scummvm/trunk/engines/draci/walking.cpp	2009-11-08 03:16:22 UTC (rev 45742)
@@ -443,7 +443,7 @@
 		// they are different pixels.
 		_path.push_back(p2);
 	}
-	debugC(2, kDraciWalkingDebugLevel, "Starting walking [%d,%d] -> [%d,%d] in %d segments",
+	debugC(2, kDraciWalkingDebugLevel, "Starting walking [%d,%d] -> [%d,%d] with %d vertices",
 		p1.x, p1.y, p2.x, p2.y, _path.size());
 
 	// The first and last point are available with pixel accurracy.
@@ -456,6 +456,10 @@
 		_path[i].y *= delta.y;
 	}
 
+	// Remember the initial dragon's direction.
+	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
+	_startingDirection = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+
 	// Going to start with the first segment.
 	_segment = _lastAnimPhase = -1;
 	turnForTheNextSegment();
@@ -491,11 +495,24 @@
 	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
 	const Movement movement = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
 
+	// 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[movement];
+	Animation *anim = _vm->_anims->getAnimation(animID);
+	const int animPhase = anim->currentFrameNum();
+	const bool wasUpdated = animPhase != _lastAnimPhase;
+	if (!wasUpdated) {
+		debugC(4, kDraciWalkingDebugLevel, "Waiting for an animation phase change: still %d", animPhase);
+		return true;
+	}
+
 	// 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(movement)) {
+		debugC(3, kDraciWalkingDebugLevel, "Continuing turning for edge %d with phase %d", _segment+1, animPhase);
+		_lastAnimPhase = animPhase;
 		return true;
 	}
 
@@ -507,66 +524,61 @@
 		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[movement];
-	Animation *anim = _vm->_anims->getAnimation(animID);
-	const int animPhase = anim->currentFrameNum();
-	const bool wasUpdated = animPhase != _lastAnimPhase;
-	if (!wasUpdated) {
-		debugC(4, kDraciWalkingDebugLevel, "Waiting for an animation phase change: still %d", animPhase);
-		return true;
-	}
-
 	// We are walking in the middle of an edge.  The animation phase has
 	// just changed.
 
-	// Read the position of the hero from the animation object, and project
+	// Read the position of the hero from the animation object, and adjust
 	// it to the current edge.
+	const Common::Point prevHero = _vm->_game->getHeroPosition();
 	_vm->_game->positionHeroAsAnim(anim);
-	const Common::Point &hero = _vm->_game->getHeroPosition();
-	Common::Point newHero = hero;
-	const bool reachedEnd = alignHeroToEdge(_path[_segment], _path[_segment+1], &newHero);
+	const Common::Point curHero = _vm->_game->getHeroPosition();
+	Common::Point adjustedHero = curHero;
+	const bool reachedEnd = alignHeroToEdge(_path[_segment], _path[_segment+1], prevHero, &adjustedHero);
+	if (reachedEnd && _segment >= (int) (_path.size() - 2)) {
+		// We don't want the dragon to jump around if we repeatedly
+		// click on the same pixel.  Let him always end where desired.
+		debugC(2, kDraciWalkingDebugLevel, "Adjusting position to the final node");
+		adjustedHero = _path[_segment+1];
+	}
 
-	debugC(3, kDraciWalkingDebugLevel, "Continuing walking in segment %d: phase %d and position [%d,%d] projected to [%d,%d]",
-		_segment, animPhase, hero.x, hero.y, newHero.x, newHero.y);
+	debugC(3, kDraciWalkingDebugLevel, "Continuing walking on edge %d: phase %d and position+=[%d,%d]->[%d,%d] adjusted to [%d,%d]",
+		_segment, animPhase, curHero.x - prevHero.x, curHero.y - prevHero.y, curHero.x, curHero.y, adjustedHero.x, adjustedHero.y);
 
-	// Update the hero position to the projected one.  The animation number
+	// Update the hero position to the adjusted one.  The animation number
 	// is not changing, so this will just move the sprite and return the
 	// current frame number.
-	_vm->_game->setHeroPosition(newHero);
+	_vm->_game->setHeroPosition(adjustedHero);
 	_lastAnimPhase = _vm->_game->playHeroAnimation(movement);
 
 	// 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).  The position is equal to the end-point of the edge thanks to
-	// alignHeroToEdge().
+	// done).  If the hero has arrived at a slightly different point due to
+	// animated sprites, adjust the path so that the animation can smoothly
+	// continue.
 	if (reachedEnd) {
+		if (adjustedHero != _path[_segment+1]) {
+			debugC(2, kDraciWalkingDebugLevel, "Adjusting node %d of the path [%d,%d]->[%d,%d]",
+				_segment+1, _path[_segment+1].x, _path[_segment+1].y, adjustedHero.x, adjustedHero.y);
+			_path[_segment+1] = adjustedHero;
+		}
 		turnForTheNextSegment();
 	}
 
 	return true;
 }
 
-bool WalkingState::alignHeroToEdge(const Common::Point &p1, const Common::Point &p2, Common::Point *hero) {
-	const Common::Point heroDiff(hero->x - p1.x, hero->y - p1.y);
+bool WalkingState::alignHeroToEdge(const Common::Point &p1, const Common::Point &p2, const Common::Point &prevHero, Common::Point *hero) {
+	const Movement movement = animationForDirection(p1, p2);
 	const Common::Point p2Diff(p2.x - p1.x, p2.y - p1.y);
-	const int scalarProduct = p2Diff.x * heroDiff.x + p2Diff.y * heroDiff.y;
-	const int p2DiffSqNorm = p2Diff.x * p2Diff.x + p2Diff.y * p2Diff.y;
-	double fraction = ((double) scalarProduct) / p2DiffSqNorm;
-	bool reachedEnd = false;
-	if (fraction >= 1.0) {
-		fraction = 1.0;
-		reachedEnd = true;
+	bool reachedEnd;
+	if (movement == kMoveLeft || movement == kMoveRight) {
+		reachedEnd = movement == kMoveLeft ? hero->x <= p2.x : hero->x >= p2.x;
+		hero->y += hero->x * p2Diff.y / p2Diff.x - prevHero.x * p2Diff.y / p2Diff.x;
+	} else {
+		reachedEnd = movement == kMoveUp ? hero->y <= p2.y : hero->y >= p2.y;
+		hero->x += hero->y * p2Diff.x / p2Diff.y - prevHero.y * p2Diff.x / p2Diff.y;
 	}
-	hero->x = p1.x + (int) (fraction * p2Diff.x + 0.5);
-	hero->y = p1.y + (int) (fraction * p2Diff.y + 0.5);
-	// TODO: unfortunately, the left-right walking animation jumps up and
-	// down by two pixels instead of going exactly horizontally, which
-	// means that the projection algorithm screws the vertical "shaking" as
-	// well as rounds horizontal positions improperly.  Fix it by better
-	// rounding or different projection.
 	return reachedEnd;
 }
 
@@ -576,7 +588,7 @@
 	const Movement wantAnim = directionForNextPhase();
 	Movement transition = transitionBetweenAnimations(currentAnim, wantAnim);
 
-	debugC(2, kDraciWalkingDebugLevel, "Turning for segment %d", _segment+1);
+	debugC(2, kDraciWalkingDebugLevel, "Turning for edge %d", _segment+1);
 
 	if (transition == kMoveUndefined) {
 		// Start the next segment right away as if the turning has just finished.
@@ -585,13 +597,12 @@
 		// Otherwise start the transition and wait until the Animation
 		// class calls heroAnimationFinished() as a callback.
 		assert(isTurningMovement(transition));
-		_vm->_game->playHeroAnimation(transition);
+		_lastAnimPhase = _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);
-		_lastAnimPhase = -1;
+		debugC(2, kDraciWalkingDebugLevel, "Starting turning animation %d with phase %d", transition, _lastAnimPhase);
 	}
 }
 
@@ -609,12 +620,12 @@
 	Movement nextAnim = directionForNextPhase();
 	_lastAnimPhase = _vm->_game->playHeroAnimation(nextAnim);
 
-	debugC(2, kDraciWalkingDebugLevel, "Turned for segment %d, starting animation %d", _segment+1, nextAnim);
+	debugC(2, kDraciWalkingDebugLevel, "Turned for edge %d, starting animation %d with phase %d", _segment+1, nextAnim, _lastAnimPhase);
 
 	if (++_segment < (int) (_path.size() - 1)) {
 		// We are on an edge: track where the hero is on this edge.
 		int length = WalkingMap::pointsBetween(_path[_segment], _path[_segment+1]);
-		debugC(2, kDraciWalkingDebugLevel, "Next segment %d has length %d", _segment, length);
+		debugC(2, kDraciWalkingDebugLevel, "Next edge %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");
@@ -635,7 +646,7 @@
 
 Movement WalkingState::directionForNextPhase() const {
 	if (_segment >= (int) (_path.size() - 2)) {
-		return animationForSightDirection(_dir, _path[_path.size()-1], _mouse, _path);
+		return animationForSightDirection(_dir, _path[_path.size()-1], _mouse, _path, _startingDirection);
 	} else {
 		return animationForDirection(_path[_segment+1], _path[_segment+2]);
 	}
@@ -722,21 +733,35 @@
 	}
 }
 
-Movement WalkingState::animationForSightDirection(SightDirection dir, const Common::Point &hero, const Common::Point &mouse, const WalkingPath &path) {
+Movement WalkingState::animationForSightDirection(SightDirection dir, const Common::Point &hero, const Common::Point &mouse, const WalkingPath &path, Movement startingDirection) {
 	switch (dir) {
 	case kDirectionMouse:
-		return mouse.x < hero.x ? kStopLeft : kStopRight;
+		if (mouse.x < hero.x) {
+			return kStopLeft;
+		} else if (mouse.x > hero.x) {
+			return kStopRight;
+		} else {
+			goto defaultCase;
+		}
 	case kDirectionLeft:
 		return kStopLeft;
 	case kDirectionRight:
 		return kStopRight;
 	default: {
+defaultCase:
 		// Find the last horizontal direction on the path.
 		int i = path.size() - 1;
 		while (i >= 0 && path[i].x == hero.x) {
 			--i;
 		}
-		return (i >= 0 && path[i].x < hero.x) ? kStopRight : kStopLeft;
+		if (i >= 0) {
+			return path[i].x < hero.x ? kStopRight : kStopLeft;
+		} else {
+			// Avoid changing the direction when no walking has
+			// been done.  Preserve the original direction.
+			return (startingDirection == kMoveLeft || startingDirection == kStopLeft || startingDirection == kSpeakLeft)
+				? kStopLeft : kStopRight;
+		}
 	}
 	}
 }

Modified: scummvm/trunk/engines/draci/walking.h
===================================================================
--- scummvm/trunk/engines/draci/walking.h	2009-11-07 23:56:16 UTC (rev 45741)
+++ scummvm/trunk/engines/draci/walking.h	2009-11-08 03:16:22 UTC (rev 45742)
@@ -132,7 +132,7 @@
 	// direction.  The direction can be smart and in that case this
 	// function needs to know the whole last path, the current position of
 	// the hero, or the mouse position.
-	static Movement animationForSightDirection(SightDirection dir, const Common::Point &hero, const Common::Point &mouse, const WalkingPath &path);
+	static Movement animationForSightDirection(SightDirection dir, const Common::Point &hero, const Common::Point &mouse, const WalkingPath &path, Movement startingDirection);
 
 private:
 	DraciEngine *_vm;
@@ -140,6 +140,7 @@
 	WalkingPath _path;
 	Common::Point _mouse;
 	SightDirection _dir;
+	Movement _startingDirection;
 
 	int _segment;
 	int _lastAnimPhase;
@@ -168,9 +169,10 @@
 		return m >= kFirstTurning && m <= kLastTurning;
 	}
 
-	// Projects hero to the given edge, but not behind p2.  Returns true
-	// when hero has reached at least p2.
-	static bool alignHeroToEdge(const Common::Point &p1, const Common::Point &p2, Common::Point *hero);
+	// Projects hero to the given edge.  Returns true when hero has reached
+	// at least p2.  prevHero is passed so that we can compute how much to
+	// adjust in the other-than-walking direction.
+	static bool alignHeroToEdge(const Common::Point &p1, const Common::Point &p2, const Common::Point &prevHero, Common::Point *hero);
 };
 
 } // 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