diff options
Diffstat (limited to 'engines/draci/game.cpp')
-rw-r--r-- | engines/draci/game.cpp | 124 |
1 files changed, 123 insertions, 1 deletions
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp index 9e18eb5f3c..a83ba68901 100644 --- a/engines/draci/game.cpp +++ b/engines/draci/game.cpp @@ -38,6 +38,7 @@ namespace Draci { static double real_to_double(byte real[6]); Game::Game(DraciEngine *vm) : _vm(vm) { + unsigned int i; BArchive *initArchive = _vm->_initArchive; @@ -283,6 +284,15 @@ void Game::loop() { _vm->_mouse->cursorOff(); _vm->_mouse->lButtonSet(false); + + if (!obj->_imLook) { + if (obj->_lookDir == 0) { + walkHero(x, y); + } else { + walkHero(obj->_lookX, obj->_lookY); + } + } + _vm->_script->run(obj->_program, obj->_look); _vm->_mouse->cursorOn(); } @@ -293,6 +303,15 @@ void Game::loop() { _vm->_mouse->cursorOff(); _vm->_mouse->rButtonSet(false); + + if (!obj->_imUse) { + if (obj->_useDir == 0) { + walkHero(x, y); + } else { + walkHero(obj->_useX, obj->_useY); + } + } + _vm->_script->run(obj->_program, obj->_use); _vm->_mouse->cursorOn(); } @@ -311,7 +330,6 @@ void Game::loop() { // If the player clicked on a walkable position and we are in the // appropriate loop status, move the dragon there if (_vm->_mouse->lButtonPressed() && - _currentRoom._walkingMap.isWalkable(x, y) && _loopSubstatus == kStatusOrdinary) { walkHero(x, y); @@ -375,6 +393,13 @@ int Game::getObjectWithAnimation(int animID) { } void Game::walkHero(int x, int y) { + + Surface *surface = _vm->_screen->getSurface(); + Common::Point p = _currentRoom._walkingMap.findNearestWalkable(x, y, surface->getRect()); + + x = p.x; + y = p.y; + // Fetch dragon's animation ID // FIXME: Need to add proper walking (this only warps the dragon to position) int animID = getObject(kDragonObject)->_anims[0]; @@ -852,6 +877,103 @@ bool WalkingMap::isWalkable(int x, int y) { return mapByte & (1 << pixelIndex % 8); } +/** + * @brief For a given point, find a nearest walkable point on the walking map + * + * @param startX x coordinate of the point + * @param startY y coordinate of the point + * + * @return A Common::Point representing the nearest walkable point + * + * The algorithm was copied from the original engine for exactness. + * TODO: Study this algorithm in more detail so it can be documented properly and + * possibly improved / simplified. + */ +Common::Point WalkingMap::findNearestWalkable(int startX, int startY, Common::Rect searchRect) { + + int signs[] = { 1, -1 }; + const uint kSignsNum = 2; + + int radius = 0; + int x, y; + int dx, dy; + int prediction; + + // The place where, eventually, the result coordinates will be stored + int finalX, finalY; + + // The algorithm appears to start off with an ellipse with the minor radius equal to + // zero and the major radius equal to the walking map delta (the number of pixels + // one map pixel represents). It then uses a heuristic to gradually reshape it into + // a circle (by shortening the major radius and lengthening the minor one). At each + // such resizing step, it checks some select points on the ellipse for walkability. + // It also does the same check for the ellipse perpendicular to it (rotated by 90 degrees). + + while(1) { + + // The default major radius + radius += _deltaX; + + // The ellipse radii (minor, major) that get resized + x = 0; + y = radius; + + // Heuristic variables + prediction = 1 - radius; + dx = 3; + dy = 2 * radius - 2; + + do { + + // The following two loops serve the purpose of checking the points on the two + // ellipses for walkability. The signs[] array is there to obliterate the need + // of writing out all combinations manually. + + for (uint i = 0; i < kSignsNum; ++i) { + finalY = startY + y * signs[i]; + + for (uint j = 0; j < kSignsNum; ++j) { + finalX = startX + x * signs[j]; + + // If the current point is walkable, return it + if (searchRect.contains(finalX, finalY) && isWalkable(finalX, finalY)) { + return Common::Point(finalX, finalY); + } + } + } + + for (uint i = 0; i < kSignsNum; ++i) { + finalY = startY + x * signs[i]; + + for (uint j = 0; j < kSignsNum; ++j) { + finalX = startX + y * signs[j]; + + // If the current point is walkable, return it + if (searchRect.contains(finalX, finalY) && isWalkable(finalX, finalY)) { + return Common::Point(finalX, finalY); + } + } + } + + // If prediction is non-negative, we need to decrease the major radius of the + // ellipse + if (prediction >= 0) { + prediction -= dy; + dy -= 2 * _deltaX; + y -= _deltaX; + } + + // Increase the minor radius of the ellipse and update heuristic variables + prediction += dx; + dx += 2 * _deltaX; + x += _deltaX; + + // If the current ellipse has been reshaped into a circle, + // end this loop and enlarge the radius + } while (x <= y); + } +} + static double real_to_double(byte real[6]) { // Extract sign bit |