aboutsummaryrefslogtreecommitdiff
path: root/engines/draci
diff options
context:
space:
mode:
authorDenis Kasak2009-08-08 12:31:49 +0000
committerDenis Kasak2009-08-08 12:31:49 +0000
commit19d5d66fd7989f9028ee2ad54fd36d163fdb1b1c (patch)
tree86f22ceb0155c25b9f5db7fb1d7576db1fa2f5cd /engines/draci
parentfc6ff00cbce11f4ffee62d964531f71c88590a11 (diff)
downloadscummvm-rg350-19d5d66fd7989f9028ee2ad54fd36d163fdb1b1c.tar.gz
scummvm-rg350-19d5d66fd7989f9028ee2ad54fd36d163fdb1b1c.tar.bz2
scummvm-rg350-19d5d66fd7989f9028ee2ad54fd36d163fdb1b1c.zip
* Implemented WalkingMap::findNearestWalkable() which mimics a heuristic from the original game that attempts to find walkable spots near the given point
* Implemented moving to the right place when looking / using objects. svn-id: r43125
Diffstat (limited to 'engines/draci')
-rw-r--r--engines/draci/game.cpp124
-rw-r--r--engines/draci/game.h1
2 files changed, 124 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
diff --git a/engines/draci/game.h b/engines/draci/game.h
index c041490e04..7d0f8349f5 100644
--- a/engines/draci/game.h
+++ b/engines/draci/game.h
@@ -89,6 +89,7 @@ public:
}
bool isWalkable(int x, int y);
+ Common::Point findNearestWalkable(int x, int y, Common::Rect searchRect);
private:
int _realWidth, _realHeight;