aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/draci/game.cpp42
-rw-r--r--engines/draci/game.h13
-rw-r--r--engines/draci/script.cpp7
-rw-r--r--engines/draci/walking.cpp71
-rw-r--r--engines/draci/walking.h5
5 files changed, 77 insertions, 61 deletions
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index dde305d848..f18b2fda32 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -954,18 +954,25 @@ void Game::runDialogueProg(GPL2Program prog, int offset) {
deleteAnimationsAfterIndex(lastAnimIndex);
}
-void Game::playHeroAnimation(int anim_index) {
+int 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);
+
+ if (anim_index == current_anim_index) {
+ anim->markDirtyRect(_vm->_screen->getSurface());
+ } else {
+ stopObjectAnimations(dragon);
+ }
positionAnimAsHero(anim);
- _vm->_anims->play(animID);
+ if (anim_index == current_anim_index) {
+ anim->markDirtyRect(_vm->_screen->getSurface());
+ } else {
+ _vm->_anims->play(animID);
+ }
+
+ return anim->currentFrameNum();
}
void Game::redrawWalkingPath(int id, byte colour, const WalkingPath &path) {
@@ -981,12 +988,6 @@ void Game::setHeroPosition(const Common::Point &p) {
_hero = p;
}
-void Game::positionHero(const Common::Point &p, SightDirection dir) {
- setHeroPosition(p);
- Common::Point mousePos(_vm->_mouse->getPosX(), _vm->_mouse->getPosY());
- playHeroAnimation(WalkingState::animationForSightDirection(dir, _hero, mousePos, WalkingPath()));
-}
-
Common::Point Game::findNearestWalkable(int x, int y) const {
Surface *surface = _vm->_screen->getSurface();
return _walkingMap.findNearestWalkable(x, y, surface->getDimensions());
@@ -1493,6 +1494,12 @@ void Game::positionAnimAsHero(Animation *anim) {
anim->setScaleFactors(scale, scale);
anim->setRelative(p.x, p.y);
+
+ // Clear the animation's shift so that the real sprite stays at place
+ // regardless of what the current phase is. If the animation starts
+ // from the beginning, the shift is already [0,0], but if it is in the
+ // middle, it may be different.
+ anim->clearShift();
}
void Game::positionHeroAsAnim(Animation *anim) {
@@ -1502,17 +1509,10 @@ void Game::positionHeroAsAnim(Animation *anim) {
// Update our hero coordinates (don't forget that our control point is
// 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());
-
- // Clear the animation's shift so that by updating the coordinates the
- // animation will stay in place.
- anim->clearShift();
-
- // Call the inverse procedure to calculate new scaling factors.
- // TODO: what about rounding errors?
- positionAnimAsHero(anim);
}
void Game::pushNewRoom() {
diff --git a/engines/draci/game.h b/engines/draci/game.h
index d905f68bd8..5414195c6e 100644
--- a/engines/draci/game.h
+++ b/engines/draci/game.h
@@ -209,14 +209,19 @@ public:
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; }
+ const Common::Point &getHeroPosition() const { return _hero; }
void positionAnimAsHero(Animation *anim);
void positionHeroAsAnim(Animation *anim);
- void playHeroAnimation(int anim_index);
+
+ // Makes sure animation anim_index plays on the hero. If the hero's
+ // position has changed, it updates the animation position. If the new
+ // animation is different, it stops the old one and starts the new one,
+ // otherwise it just marks dirty rectangles for moving the position.
+ // Returns the current animation phase of the new animation (usually 0
+ // unless the animation hasn't changed).
+ int playHeroAnimation(int anim_index);
int loadAnimation(uint animNum, uint z);
void loadOverlays();
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index f47cda4567..7d9dd4126f 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -664,8 +664,13 @@ void Script::stayOn(Common::Queue<int> &params) {
SightDirection dir = static_cast<SightDirection> (params.pop());
// 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());
+
_vm->_game->stopWalking();
- _vm->_game->positionHero(_vm->_game->findNearestWalkable(x, y), dir);
+ _vm->_game->setHeroPosition(heroPos);
+ _vm->_game->playHeroAnimation(WalkingState::animationForSightDirection(
+ dir, heroPos, mousePos, WalkingPath()));
}
void Script::walkOn(Common::Queue<int> &params) {
diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp
index fed91dd8ef..af64daf522 100644
--- a/engines/draci/walking.cpp
+++ b/engines/draci/walking.cpp
@@ -457,7 +457,7 @@ void WalkingState::startWalking(const Common::Point &p1, const Common::Point &p2
}
// Going to start with the first segment.
- _segment = _lastAnimPhase = _position = _length = -1;
+ _segment = _lastAnimPhase = -1;
turnForTheNextSegment();
}
@@ -489,13 +489,13 @@ void WalkingState::callback() {
bool WalkingState::continueWalking() {
const GameObject *dragon = _vm->_game->getObject(kDragonObject);
- const Movement anim_index = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+ const Movement movement = 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)) {
+ if (isTurningMovement(movement)) {
return true;
}
@@ -509,41 +509,51 @@ bool WalkingState::continueWalking() {
// 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];
+ const int animID = dragon->_anim[movement];
Animation *anim = _vm->_anims->getAnimation(animID);
const int animPhase = anim->currentFrameNum();
const bool wasUpdated = animPhase != _lastAnimPhase;
if (!wasUpdated) {
return true;
}
- _lastAnimPhase = animPhase;
-
- 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.
+ // just changed.
+
+ // Read the position of the hero from the animation object, and project
+ // it to the current edge.
_vm->_game->positionHeroAsAnim(anim);
- // TODO: take the [XY] coordinate determined by the animation, update
- // the other one so that the hero stays on the edge, remove _position
- // and _length, and instead test reaching the destination by computing
- // the scalar product
- _position += 4;
- // Common::Point newPos = WalkingMap::interpolate(
- // _path[_segment], _path[_segment+1], _position, _length);
- // _vm->_game->setHeroPosition(newPos);
- // _vm->_game->positionAnimAsHero(anim);
+ const Common::Point &hero = _vm->_game->getHeroPosition();
+ Common::Point newHero = hero;
+ const bool reachedEnd = alignHeroToEdge(_path[_segment], _path[_segment+1], &newHero);
+
+ debugC(3, kDraciWalkingDebugLevel, "Continuing walking in segment %d and position [%d,%d] projected to [%d,%d]",
+ _segment, hero.x, hero.y, newHero.x, newHero.y);
+
+ // Update the hero position to the projected one. The animation number
+ // is not changing, so this will just move the sprite and return the
+ // current frame number.
+ _vm->_game->setHeroPosition(newHero);
+ _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).
- if (_position >= _length) {
+ // done). The position is equal to the end-point of the edge thanks to
+ // alignHeroToEdge().
+ if (reachedEnd) {
turnForTheNextSegment();
}
return true;
}
+bool WalkingState::alignHeroToEdge(const Common::Point &p1, const Common::Point &p2, Common::Point *hero) {
+ // TODO
+ *hero = p2;
+ return true;
+}
+
void WalkingState::turnForTheNextSegment() {
const GameObject *dragon = _vm->_game->getObject(kDragonObject);
const Movement currentAnim = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
@@ -565,37 +575,30 @@ void WalkingState::turnForTheNextSegment() {
anim->registerCallback(&Animation::tellWalkingState);
debugC(2, kDraciWalkingDebugLevel, "Starting turning animation %d", transition);
+ _lastAnimPhase = -1;
}
}
void WalkingState::heroAnimationFinished() {
// The hero is turned well for the next line segment or for facing the
- // target direction.
+ // target direction. It is also standing on the right spot thanks to
+ // the entry condition for turnForTheNextSegment().
- // Start the desired next animation. playHeroAnimation() takes care of
- // stopping the current animation.
+ // Start the desired next animation and retrieve the current animation
+ // phase.
// 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);
-
- // Retrieve the current animation phase. Don't just use 0, because we
- // cannot assume that nextAnim has just started. If it was already
- // playing before, then playHeroAnimation(nextAnim) does nothing.
- const GameObject *dragon = _vm->_game->getObject(kDragonObject);
- const int animID = dragon->_anim[nextAnim];
- Animation *anim = _vm->_anims->getAnimation(animID);
- _lastAnimPhase = anim->currentFrameNum();
+ _lastAnimPhase = _vm->_game->playHeroAnimation(nextAnim);
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);
+ int 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");
diff --git a/engines/draci/walking.h b/engines/draci/walking.h
index a3dae88eb7..e2d86a5653 100644
--- a/engines/draci/walking.h
+++ b/engines/draci/walking.h
@@ -142,7 +142,6 @@ private:
SightDirection _dir;
int _segment;
- int _position, _length;
int _lastAnimPhase;
const GPL2Program *_callback;
@@ -168,6 +167,10 @@ private:
static bool isTurningMovement(Movement m) {
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);
};
} // End of namespace Draci