diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/draci/animation.cpp | 4 | ||||
-rw-r--r-- | engines/draci/draci.h | 5 | ||||
-rw-r--r-- | engines/draci/game.cpp | 8 | ||||
-rw-r--r-- | engines/draci/script.cpp | 4 | ||||
-rw-r--r-- | engines/draci/sprite.cpp | 8 | ||||
-rw-r--r-- | engines/draci/walking.cpp | 121 | ||||
-rw-r--r-- | engines/draci/walking.h | 10 |
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> ¶ms) { // 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> ¶ms) { 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 |