aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/actor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm/actor.cpp')
-rw-r--r--engines/scumm/actor.cpp204
1 files changed, 157 insertions, 47 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index bb9b935be2..df143f6288 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -164,12 +164,8 @@ void Actor::initActor(int mode) {
void Actor_v2::initActor(int mode) {
Actor::initActor(mode);
- if (mode == -1) {
- _speedx = 1;
- _speedy = 1;
- }
-
- setActorWalkSpeed(1, 1);
+ _speedx = 1;
+ _speedy = 1;
_initFrame = 2;
_walkFrame = 0;
@@ -378,23 +374,29 @@ void Actor::startWalkActor(int destX, int destY, int dir) {
return;
}
- if (_ignoreBoxes) {
- abr.box = kInvalidBox;
- _walkbox = kInvalidBox;
+ if (_vm->_game.version <= 2) {
+ abr = adjustXYToBeInBox(abr.x, abr.y);
+ if (_pos.x == abr.x && _pos.y == abr.y && (dir == -1 || _facing == dir))
+ return;
} else {
- if (_vm->checkXYInBoxBounds(_walkdata.destbox, abr.x, abr.y)) {
- abr.box = _walkdata.destbox;
+ if (_ignoreBoxes) {
+ abr.box = kInvalidBox;
+ _walkbox = kInvalidBox;
} else {
- abr = adjustXYToBeInBox(abr.x, abr.y);
+ if (_vm->checkXYInBoxBounds(_walkdata.destbox, abr.x, abr.y)) {
+ abr.box = _walkdata.destbox;
+ } else {
+ abr = adjustXYToBeInBox(abr.x, abr.y);
+ }
+ if (_moving && _walkdata.destdir == dir && _walkdata.dest.x == abr.x && _walkdata.dest.y == abr.y)
+ return;
}
- if (_moving && _walkdata.destdir == dir && _walkdata.dest.x == abr.x && _walkdata.dest.y == abr.y)
+
+ if (_pos.x == abr.x && _pos.y == abr.y) {
+ if (dir != _facing)
+ turnToDirection(dir);
return;
- }
-
- if (_pos.x == abr.x && _pos.y == abr.y) {
- if (dir != _facing)
- turnToDirection(dir);
- return;
+ }
}
_walkdata.dest.x = abr.x;
@@ -500,7 +502,7 @@ void Actor::walkActor() {
if (_walkbox == _walkdata.destbox)
break;
- next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+ next_box = _vm->getNextBox(_walkbox, _walkdata.destbox);
if (next_box < 0) {
_walkdata.destbox = _walkbox;
_moving |= MF_LAST_LEG;
@@ -522,13 +524,13 @@ void Actor::walkActor() {
calcMovementFactor(_walkdata.dest);
}
-/*
-void Actor::walkActor() {
+void Actor_v2::walkActor() {
Common::Point foundPath, tmp;
int new_dir, next_box;
if (_moving & MF_TURN) {
new_dir = updateActorDirection(false);
+ // FIXME -- is this correct?
if (_facing != new_dir)
setDirection(new_dir);
else
@@ -553,7 +555,7 @@ void Actor::walkActor() {
foundPath = _walkdata.dest;
_moving |= MF_LAST_LEG;
} else {
- next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+ next_box = _vm->getNextBox(_walkbox, _walkdata.destbox);
if (next_box < 0) {
_moving |= MF_LAST_LEG;
return;
@@ -561,20 +563,20 @@ void Actor::walkActor() {
// Can't walk through locked boxes
int flags = _vm->getBoxFlags(next_box);
- if (flags & kBoxLocked && !(flags & kBoxPlayerOnly && !isPlayer())) {
+ if ((flags & kBoxLocked) && !((flags & kBoxPlayerOnly) && !isPlayer())) {
_moving |= MF_LAST_LEG;
+ //_walkdata.destdir = -1;
}
_walkdata.curbox = next_box;
- getClosestPtOnBox(_vm->getBoxCoordinates(_walkdata.curbox), x, y, tmp.x, tmp.y);
+ getClosestPtOnBox(_vm->getBoxCoordinates(_walkdata.curbox), _pos.x, _pos.y, tmp.x, tmp.y);
getClosestPtOnBox(_vm->getBoxCoordinates(_walkbox), tmp.x, tmp.y, foundPath.x, foundPath.y);
}
calcMovementFactor(foundPath);
}
}
}
-*/
void Actor_v3::walkActor() {
Common::Point p2, p3; // Gate locations
@@ -627,7 +629,7 @@ void Actor_v3::walkActor() {
if (_walkbox == _walkdata.destbox)
break;
- next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+ next_box = _vm->getNextBox(_walkbox, _walkdata.destbox);
// WORKAROUND: To fully fix bug #774783, we add a special case
// here, resulting in a different next_box value for Hitler.
@@ -641,32 +643,22 @@ void Actor_v3::walkActor() {
// Can't walk through locked boxes
int flags = _vm->getBoxFlags(next_box);
- if (flags & kBoxLocked && !(flags & kBoxPlayerOnly && !isPlayer())) {
+ if ((flags & kBoxLocked) && !((flags & kBoxPlayerOnly) && !isPlayer())) {
_moving |= MF_LAST_LEG;
-// FIXME: Work in progress
-// _walkdata.destdir = _facing;
return;
}
_walkdata.curbox = next_box;
- if (_vm->_game.version <= 2) {
- getClosestPtOnBox(_vm->getBoxCoordinates(_walkdata.curbox), _pos.x, _pos.y, p2.x, p2.y);
- getClosestPtOnBox(_vm->getBoxCoordinates(_walkbox), p2.x, p2.y, p3.x, p3.y);
-// FIXME: Work in progress
-// calcMovementFactor(p3);
-// return;
- } else {
- findPathTowardsOld(_walkbox, next_box, _walkdata.destbox, p2, p3);
- if (p2.x == 32000 && p3.x == 32000) {
- break;
- }
+ findPathTowardsOld(_walkbox, next_box, _walkdata.destbox, p2, p3);
+ if (p2.x == 32000 && p3.x == 32000) {
+ break;
+ }
- if (p2.x != 32000) {
- if (calcMovementFactor(p2)) {
- _walkdata.point3 = p3;
- return;
- }
+ if (p2.x != 32000) {
+ if (calcMovementFactor(p2)) {
+ _walkdata.point3 = p3;
+ return;
}
}
if (calcMovementFactor(p3))
@@ -950,6 +942,124 @@ static bool inBoxQuickReject(const BoxCoords &box, int x, int y, int threshold)
return false;
}
+static int checkXYInBoxBounds(int boxnum, int x, int y, int &destX, int &destY) {
+ BoxCoords box = g_scumm->getBoxCoordinates(boxnum);
+ int xmin, xmax;
+
+ // We are supposed to determine the point (destX,destY) contained in
+ // the given box which is closest to the point (x,y), and then return
+ // some kind of "distance" between the two points.
+
+ // First, we determine destY and a range (xmin to xmax) in which destX
+ // is contained.
+ if (y < box.ul.y) {
+ // Point is above the box
+ destY = box.ul.y;
+ xmin = box.ul.x;
+ xmax = box.ur.x;
+ } else if (y >= box.ll.y) {
+ // Point is below the box
+ destY = box.ll.y;
+ xmin = box.ll.x;
+ xmax = box.lr.x;
+ } else if ((x >= box.ul.x) && (x >= box.ll.x) && (x < box.ur.x) && (x < box.lr.x)) {
+ // Point is strictly inside the box
+ destX = x;
+ destY = y;
+ xmin = xmax = x;
+ } else {
+ // Point is to the left or right of the box,
+ // so the y coordinate remains unchanged
+ destY = y;
+ int ul = box.ul.x;
+ int ll = box.ll.x;
+ int ur = box.ur.x;
+ int lr = box.lr.x;
+ int top = box.ul.y;
+ int bottom = box.ll.y;
+ int cury;
+
+ // Perform a binary search to determine the x coordinate.
+ // Note: It would be possible to compute this value in a
+ // single step simply by calculating the slope of the left
+ // resp. right side and using that to find the correct
+ // result. However, the original engine did use the search
+ // approach, so we do that, too.
+ do {
+ xmin = (ul + ll) / 2;
+ xmax = (ur + lr) / 2;
+ cury = (top + bottom) / 2;
+
+ if (cury < y) {
+ top = cury;
+ ul = xmin;
+ ur = xmax;
+ } else if (cury > y) {
+ bottom = cury;
+ ll = xmin;
+ lr = xmax;
+ }
+ } while (cury != y);
+ }
+
+ // Now that we have limited the value of destX to a fixed
+ // interval, it's a trivial matter to finally determine it.
+ if (x < xmin) {
+ destX = xmin;
+ } else if (x > xmax) {
+ destX = xmax;
+ } else {
+ destX = x;
+ }
+
+ // Compute the distance of the points. We measure the
+ // distance with a granularity of 8x8 blocks only (hence
+ // yDist must be divided by 4, as we are using 8x2 pixels
+ // blocks for actor coordinates).
+ int xDist = ABS(x - destX);
+ int yDist = ABS(y - destY) / 4;
+ int dist;
+
+ if (xDist < yDist)
+ dist = (xDist >> 1) + yDist;
+ else
+ dist = (yDist >> 1) + xDist;
+
+ return dist;
+}
+
+AdjustBoxResult Actor_v2::adjustXYToBeInBox(const int dstX, const int dstY) {
+ AdjustBoxResult abr;
+
+ abr.x = dstX;
+ abr.y = dstY;
+ abr.box = kInvalidBox;
+
+ int numBoxes = _vm->getNumBoxes() - 1;
+ int bestDist = 0xFF;
+ for (int box = numBoxes; box >= 0; box--) {
+ int foundX, foundY;
+ int flags = _vm->getBoxFlags(box);
+ if ((flags & kBoxInvisible) && !((flags & kBoxPlayerOnly) && !isPlayer()))
+ continue;
+ int dist = checkXYInBoxBounds(box, dstX, dstY, foundX, foundY); // also merged with getClosestPtOnBox
+ if (dist == 0) {
+ abr.x = foundX;
+ abr.y = foundY;
+ abr.box = box;
+ break;
+ }
+ if (dist < bestDist) {
+ bestDist = dist;
+ abr.x = foundX;
+ abr.y = foundY;
+ abr.box = box;
+ }
+ }
+
+ return abr;
+}
+
AdjustBoxResult Actor::adjustXYToBeInBox(int dstX, int dstY) {
const uint thresholdTable[] = { 30, 80, 0 };
AdjustBoxResult abr;
@@ -982,7 +1092,7 @@ AdjustBoxResult Actor::adjustXYToBeInBox(int dstX, int dstY) {
flags = _vm->getBoxFlags(box);
// Skip over invisible boxes
- if (flags & kBoxInvisible && !(flags & kBoxPlayerOnly && !isPlayer()))
+ if ((flags & kBoxInvisible) && !((flags & kBoxPlayerOnly) && !isPlayer()))
continue;
// For increased performance, we perform a quick test if