aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/boxes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm/boxes.cpp')
-rw-r--r--engines/scumm/boxes.cpp248
1 files changed, 117 insertions, 131 deletions
diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp
index ebda290853..534426b635 100644
--- a/engines/scumm/boxes.cpp
+++ b/engines/scumm/boxes.cpp
@@ -86,11 +86,83 @@ struct Box { /* Internal walkbox file format */
#define BOX_DEBUG 0
-static bool compareSlope(int X1, int Y1, int X2, int Y2, int X3, int Y3);
-static Common::Point closestPtOnLine(const Common::Point &start, const Common::Point &end, int x, int y);
-static uint distanceFromPt(int x, int y, int ptx, int pty);
static void getGates(const BoxCoords &box1, const BoxCoords &box2, Common::Point gateA[2], Common::Point gateB[2]);
+static bool compareSlope(const Common::Point &p1, const Common::Point &p2, const Common::Point &p3) {
+ return (p2.y - p1.y) * (p3.x - p1.x) <= (p3.y - p1.y) * (p2.x - p1.x);
+}
+
+/**
+ * Find the point on a line segment which is closest to a given point.
+ *
+ * @param lineStart the start of the line segment
+ * @param lineEnd the end of the line segment
+ * @param p the point which we want to 'project' on the line segment
+ * @return the point on the line segment closest to the given point
+ */
+static Common::Point closestPtOnLine(const Common::Point &lineStart, const Common::Point &lineEnd, const Common::Point &p) {
+ Common::Point result;
+
+ const int lxdiff = lineEnd.x - lineStart.x;
+ const int lydiff = lineEnd.y - lineStart.y;
+
+ if (lineEnd.x == lineStart.x) { // Vertical line?
+ result.x = lineStart.x;
+ result.y = p.y;
+ } else if (lineEnd.y == lineStart.y) { // Horizontal line?
+ result.x = p.x;
+ result.y = lineStart.y;
+ } else {
+ const int dist = lxdiff * lxdiff + lydiff * lydiff;
+ int a, b, c;
+ if (ABS(lxdiff) > ABS(lydiff)) {
+ a = lineStart.x * lydiff / lxdiff;
+ b = p.x * lxdiff / lydiff;
+
+ c = (a + b - lineStart.y + p.y) * lydiff * lxdiff / dist;
+
+ result.x = c;
+ result.y = c * lydiff / lxdiff - a + lineStart.y;
+ } else {
+ a = lineStart.y * lxdiff / lydiff;
+ b = p.y * lydiff / lxdiff;
+
+ c = (a + b - lineStart.x + p.x) * lydiff * lxdiff / dist;
+
+ result.x = c * lxdiff / lydiff - a + lineStart.x;
+ result.y = c;
+ }
+ }
+
+ if (ABS(lydiff) < ABS(lxdiff)) {
+ if (lxdiff > 0) {
+ if (result.x < lineStart.x)
+ result = lineStart;
+ else if (result.x > lineEnd.x)
+ result = lineEnd;
+ } else {
+ if (result.x > lineStart.x)
+ result = lineStart;
+ else if (result.x < lineEnd.x)
+ result = lineEnd;
+ }
+ } else {
+ if (lydiff > 0) {
+ if (result.y < lineStart.y)
+ result = lineStart;
+ else if (result.y > lineEnd.y)
+ result = lineEnd;
+ } else {
+ if (result.y > lineStart.y)
+ result = lineStart;
+ else if (result.y < lineEnd.y)
+ result = lineEnd;
+ }
+ }
+
+ return result;
+}
+
byte ScummEngine::getMaskFromBox(int box) {
// WORKAROUND for bug #740244 and #755863. This appears to have been a
// long standing bug in the original engine?
@@ -440,7 +512,12 @@ int ScummEngine_v6::getSpecialBox(int x, int y) {
bool ScummEngine::checkXYInBoxBounds(int b, int x, int y) {
BoxCoords box = getBoxCoordinates(b);
+ const Common::Point p(x, y);
+ // Quick check: If the x (resp. y) coordinate of the point is
+ // strictly smaller (bigger) than the x (y) coordinates of all
+ // corners of the quadrangle, then it certainly is *not* contained
+ // inside the quadrangle.
if (x < box.ul.x && x < box.ur.x && x < box.lr.x && x < box.ll.x)
return false;
@@ -453,25 +530,28 @@ bool ScummEngine::checkXYInBoxBounds(int b, int x, int y) {
if (y > box.ul.y && y > box.ur.y && y > box.lr.y && y > box.ll.y)
return false;
+ // Corner case: If the box is a simple line segment, we consider the
+ // point to be contained "in" (or rather, lying on) the line if it
+ // is very close to its projection to the line segment.
if (box.ul == box.ur && box.lr == box.ll ||
box.ul == box.ll && box.ur == box.lr) {
- Common::Point pt;
- pt = closestPtOnLine(box.ul, box.lr, x, y);
- if (distanceFromPt(x, y, pt.x, pt.y) <= 4)
+ Common::Point tmp;
+ tmp = closestPtOnLine(box.ul, box.lr, p);
+ if (p.sqrDist(tmp) <= 4)
return true;
}
-
- if (!compareSlope(box.ul.x, box.ul.y, box.ur.x, box.ur.y, x, y))
+
+ if (!compareSlope(box.ul, box.ur, p))
return false;
- if (!compareSlope(box.ur.x, box.ur.y, box.lr.x, box.lr.y, x, y))
+ if (!compareSlope(box.ur, box.lr, p))
return false;
-
- if (!compareSlope(box.ll.x, box.ll.y, x, y, box.lr.x, box.lr.y))
+
+ if (!compareSlope(box.ll, p, box.lr))
return false;
- if (!compareSlope(box.ul.x, box.ul.y, x, y, box.ll.x, box.ll.y))
+ if (!compareSlope(box.ul, p, box.ll))
return false;
return true;
@@ -544,100 +624,6 @@ BoxCoords ScummEngine::getBoxCoordinates(int boxnum) {
return *box;
}
-uint distanceFromPt(int x, int y, int ptx, int pty) {
- int diffx, diffy;
-
- diffx = ABS(ptx - x);
-
- if (diffx >= 0x1000)
- return 0xFFFFFF;
-
- diffy = ABS(pty - y);
-
- if (diffy >= 0x1000)
- return 0xFFFFFF;
- diffx *= diffx;
- diffy *= diffy;
- return diffx + diffy;
-}
-
-
-bool compareSlope(int X1, int Y1, int X2, int Y2, int X3, int Y3) {
- return (Y2 - Y1) * (X3 - X1) <= (Y3 - Y1) * (X2 - X1);
-}
-
-/**
- * Find the point on a line segment which is closest to a given point.
- *
- * @param start the start of the line segment
- * @param end the end of the line segment
- * @param x the x coordinate of the point which we want to 'project' on the line segment
- * @param y the y coordinate of the point which we want to 'project' on the line segment
- * @return the point on the line segmen closes to the given point
- */
-Common::Point closestPtOnLine(const Common::Point &start, const Common::Point &end, int x, int y) {
- Common::Point pt;
-
- const int lxdiff = end.x - start.x;
- const int lydiff = end.y - start.y;
-
- if (end.x == start.x) { // Vertical line?
- pt.x = start.x;
- pt.y = y;
- } else if (end.y == start.y) { // Horizontal line?
- pt.x = x;
- pt.y = start.y;
- } else {
- const int dist = lxdiff * lxdiff + lydiff * lydiff;
- int a, b, c;
- if (ABS(lxdiff) > ABS(lydiff)) {
- a = start.x * lydiff / lxdiff;
- b = x * lxdiff / lydiff;
-
- c = (a + b - start.y + y) * lydiff * lxdiff / dist;
-
- pt.x = c;
- pt.y = c * lydiff / lxdiff - a + start.y;
- } else {
- a = start.y * lxdiff / lydiff;
- b = y * lydiff / lxdiff;
-
- c = (a + b - start.x + x) * lydiff * lxdiff / dist;
-
- pt.y = c;
- pt.x = c * lxdiff / lydiff - a + start.x;
- }
- }
-
- if (ABS(lydiff) < ABS(lxdiff)) {
- if (lxdiff > 0) {
- if (pt.x < start.x)
- pt = start;
- else if (pt.x > end.x)
- pt = end;
- } else {
- if (pt.x > start.x)
- pt = start;
- else if (pt.x < end.x)
- pt = end;
- }
- } else {
- if (lydiff > 0) {
- if (pt.y < start.y)
- pt = start;
- else if (pt.y > end.y)
- pt = end;
- } else {
- if (pt.y > start.y)
- pt = start;
- else if (pt.y < end.y)
- pt = end;
- }
- }
-
- return pt;
-}
-
bool inBoxQuickReject(const BoxCoords &box, int x, int y, int threshold) {
int t;
@@ -661,40 +647,41 @@ bool inBoxQuickReject(const BoxCoords &box, int x, int y, int threshold) {
}
int getClosestPtOnBox(const BoxCoords &box, int x, int y, int16& outX, int16& outY) {
- Common::Point pt;
+ const Common::Point p(x, y);
+ Common::Point tmp;
uint dist;
uint bestdist = 0xFFFFFF;
- pt = closestPtOnLine(box.ul, box.ur, x, y);
- dist = distanceFromPt(x, y, pt.x, pt.y);
+ tmp = closestPtOnLine(box.ul, box.ur, p);
+ dist = p.sqrDist(tmp);
if (dist < bestdist) {
bestdist = dist;
- outX = pt.x;
- outY = pt.y;
+ outX = tmp.x;
+ outY = tmp.y;
}
- pt = closestPtOnLine(box.ur, box.lr, x, y);
- dist = distanceFromPt(x, y, pt.x, pt.y);
+ tmp = closestPtOnLine(box.ur, box.lr, p);
+ dist = p.sqrDist(tmp);
if (dist < bestdist) {
bestdist = dist;
- outX = pt.x;
- outY = pt.y;
+ outX = tmp.x;
+ outY = tmp.y;
}
- pt = closestPtOnLine(box.lr, box.ll, x, y);
- dist = distanceFromPt(x, y, pt.x, pt.y);
+ tmp = closestPtOnLine(box.lr, box.ll, p);
+ dist = p.sqrDist(tmp);
if (dist < bestdist) {
bestdist = dist;
- outX = pt.x;
- outY = pt.y;
+ outX = tmp.x;
+ outY = tmp.y;
}
- pt = closestPtOnLine(box.ll, box.ul, x, y);
- dist = distanceFromPt(x, y, pt.x, pt.y);
+ tmp = closestPtOnLine(box.ll, box.ul, p);
+ dist = p.sqrDist(tmp);
if (dist < bestdist) {
bestdist = dist;
- outX = pt.x;
- outY = pt.y;
+ outX = tmp.x;
+ outY = tmp.y;
}
return bestdist;
@@ -1174,20 +1161,19 @@ void Actor::findPathTowardsOld(byte box1, byte box2, byte finalBox, Common::Poin
// 'maze' in the zeppelin (see bug #1032964).
if (_vm->_game.id != GID_INDY3 || _vm->getMaskFromBox(box1) == _vm->getMaskFromBox(box2)) {
// Is the actor (x,y) between both gates?
- if (compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateA[0].x, gateA[0].y) !=
- compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateB[0].x, gateB[0].y) &&
- compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateA[1].x, gateA[1].y) !=
- compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateB[1].x, gateB[1].y)) {
+ if (compareSlope(_pos, _walkdata.dest, gateA[0]) !=
+ compareSlope(_pos, _walkdata.dest, gateB[0]) &&
+ compareSlope(_pos, _walkdata.dest, gateA[1]) !=
+ compareSlope(_pos, _walkdata.dest, gateB[1])) {
return;
}
}
}
- p3 = pt = closestPtOnLine(gateA[1], gateB[1], _pos.x, _pos.y);
+ p3 = pt = closestPtOnLine(gateA[1], gateB[1], _pos);
- if (compareSlope(_pos.x, _pos.y, p3.x, p3.y, gateA[0].x, gateA[0].y) ==
- compareSlope(_pos.x, _pos.y, p3.x, p3.y, gateB[0].x, gateB[0].y)) {
- closestPtOnLine(gateA[0], gateB[0], _pos.x, _pos.y);
+ if (compareSlope(_pos, p3, gateA[0]) == compareSlope(_pos, p3, gateB[0])) {
+ closestPtOnLine(gateA[0], gateB[0], _pos);
p2 = pt; // if point 2 between gates, ignore!
}
}