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/draci.h5
-rw-r--r--engines/draci/game.cpp8
-rw-r--r--engines/draci/script.cpp4
-rw-r--r--engines/draci/sprite.cpp8
-rw-r--r--engines/draci/walking.cpp121
-rw-r--r--engines/draci/walking.h10
7 files changed, 95 insertions, 65 deletions
diff --git a/engines/draci/animation.cpp b/engines/draci/animation.cpp
index ffdfa6c1e2..9b7694377f 100644
--- a/engines/draci/animation.cpp
+++ b/engines/draci/animation.cpp
@@ -59,8 +59,8 @@ void Animation::setRelative(int relx, int rely) {
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;
}
diff --git a/engines/draci/draci.h b/engines/draci/draci.h
index 1707fc29ad..68e56bb2d7 100644
--- a/engines/draci/draci.h
+++ b/engines/draci/draci.h
@@ -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 @@ enum {
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
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index f18b2fda32..23a037e5e7 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -1482,8 +1482,8 @@ void Game::positionAnimAsHero(Animation *anim) {
// 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 @@ void Game::positionHeroAsAnim(Animation *anim) {
// 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() {
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index 7d9dd4126f..340942a7e2 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -666,11 +666,13 @@ void Script::stayOn(Common::Queue<int> &params) {
// 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) {
diff --git a/engines/draci/sprite.cpp b/engines/draci/sprite.cpp
index 32c4044e4e..64374e70e3 100644
--- a/engines/draci/sprite.cpp
+++ b/engines/draci/sprite.cpp
@@ -114,10 +114,6 @@ Sprite::~Sprite() {
}
}
-// 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 @@ void Sprite::draw(Surface *surface, bool markDirty, int relX, int relY) const {
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,
diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp
index 51b77cb049..bda91b79cc 100644
--- a/engines/draci/walking.cpp
+++ b/engines/draci/walking.cpp
@@ -443,7 +443,7 @@ void WalkingState::startWalking(const Common::Point &p1, const Common::Point &p2
// 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 @@ void WalkingState::startWalking(const Common::Point &p1, const Common::Point &p2
_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 @@ bool WalkingState::continueWalking() {
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 @@ bool WalkingState::continueWalking() {
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 @@ void WalkingState::turnForTheNextSegment() {
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 @@ void WalkingState::turnForTheNextSegment() {
// 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 @@ void WalkingState::heroAnimationFinished() {
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::animationForDirection(const Common::Point &here, const Co
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::transitionBetweenAnimations(Movement previous, Movement n
}
}
-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;
+ }
}
}
}
diff --git a/engines/draci/walking.h b/engines/draci/walking.h
index e2d86a5653..b178c2164b 100644
--- a/engines/draci/walking.h
+++ b/engines/draci/walking.h
@@ -132,7 +132,7 @@ public:
// 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 @@ private:
WalkingPath _path;
Common::Point _mouse;
SightDirection _dir;
+ Movement _startingDirection;
int _segment;
int _lastAnimPhase;
@@ -168,9 +169,10 @@ private:
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