* 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
This commit is contained in:
parent
fc6ff00cbc
commit
19d5d66fd7
2 changed files with 124 additions and 1 deletions
|
@ -38,6 +38,7 @@ namespace Draci {
|
||||||
static double real_to_double(byte real[6]);
|
static double real_to_double(byte real[6]);
|
||||||
|
|
||||||
Game::Game(DraciEngine *vm) : _vm(vm) {
|
Game::Game(DraciEngine *vm) : _vm(vm) {
|
||||||
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
BArchive *initArchive = _vm->_initArchive;
|
BArchive *initArchive = _vm->_initArchive;
|
||||||
|
@ -283,6 +284,15 @@ void Game::loop() {
|
||||||
|
|
||||||
_vm->_mouse->cursorOff();
|
_vm->_mouse->cursorOff();
|
||||||
_vm->_mouse->lButtonSet(false);
|
_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->_script->run(obj->_program, obj->_look);
|
||||||
_vm->_mouse->cursorOn();
|
_vm->_mouse->cursorOn();
|
||||||
}
|
}
|
||||||
|
@ -293,6 +303,15 @@ void Game::loop() {
|
||||||
|
|
||||||
_vm->_mouse->cursorOff();
|
_vm->_mouse->cursorOff();
|
||||||
_vm->_mouse->rButtonSet(false);
|
_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->_script->run(obj->_program, obj->_use);
|
||||||
_vm->_mouse->cursorOn();
|
_vm->_mouse->cursorOn();
|
||||||
}
|
}
|
||||||
|
@ -311,7 +330,6 @@ void Game::loop() {
|
||||||
// If the player clicked on a walkable position and we are in the
|
// If the player clicked on a walkable position and we are in the
|
||||||
// appropriate loop status, move the dragon there
|
// appropriate loop status, move the dragon there
|
||||||
if (_vm->_mouse->lButtonPressed() &&
|
if (_vm->_mouse->lButtonPressed() &&
|
||||||
_currentRoom._walkingMap.isWalkable(x, y) &&
|
|
||||||
_loopSubstatus == kStatusOrdinary) {
|
_loopSubstatus == kStatusOrdinary) {
|
||||||
|
|
||||||
walkHero(x, y);
|
walkHero(x, y);
|
||||||
|
@ -375,6 +393,13 @@ int Game::getObjectWithAnimation(int animID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::walkHero(int x, int y) {
|
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
|
// Fetch dragon's animation ID
|
||||||
// FIXME: Need to add proper walking (this only warps the dragon to position)
|
// FIXME: Need to add proper walking (this only warps the dragon to position)
|
||||||
int animID = getObject(kDragonObject)->_anims[0];
|
int animID = getObject(kDragonObject)->_anims[0];
|
||||||
|
@ -852,6 +877,103 @@ bool WalkingMap::isWalkable(int x, int y) {
|
||||||
return mapByte & (1 << pixelIndex % 8);
|
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]) {
|
static double real_to_double(byte real[6]) {
|
||||||
|
|
||||||
// Extract sign bit
|
// Extract sign bit
|
||||||
|
|
|
@ -89,6 +89,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWalkable(int x, int y);
|
bool isWalkable(int x, int y);
|
||||||
|
Common::Point findNearestWalkable(int x, int y, Common::Rect searchRect);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _realWidth, _realHeight;
|
int _realWidth, _realHeight;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue