aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/engine/kmath.cpp82
1 files changed, 70 insertions, 12 deletions
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index 14a20b8da1..b2aaa01b45 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -77,18 +77,7 @@ reg_t kSqrt(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, (int16) sqrt((float) ABS(argv[0].toSint16())));
}
-/**
- * Returns the angle (in degrees) between the two points determined by (x1, y1)
- * and (x2, y2). The angle ranges from 0 to 359 degrees.
- * What this function does is pretty simple but apparently the original is not
- * accurate.
- */
-uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) {
- // SCI1 games (QFG2 and newer) use a simple atan implementation. SCI0 games
- // use a somewhat less accurate calculation (below).
- if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
- return (int16)(360 - atan2((double)(x1 - x2), (double)(y1 - y2)) * 180 / M_PI + 0.5) % 360;
-
+uint16 kGetAngle_SCI0(int16 x1, int16 y1, int16 x2, int16 y2) {
int16 xRel = x2 - x1;
int16 yRel = y1 - y2; // y-axis is mirrored.
int16 angle;
@@ -118,6 +107,75 @@ uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) {
return angle;
}
+// atan2 for first octant, x >= y >= 0. Returns [0,45] (inclusive)
+int kGetAngle_SCI1_atan2_base(int y, int x) {
+ if (x == 0)
+ return 0;
+
+ // fixed point tan(a)
+ int tan_fp = 10000 * y / x;
+
+ if ( tan_fp >= 1000 ) {
+ // For tan(a) >= 0.1, interpolate between multiples of 5 degrees
+
+ // 10000 * tan([5, 10, 15, 20, 25, 30, 35, 40, 45])
+ const int tan_table[] = { 875, 1763, 2679, 3640, 4663, 5774,
+ 7002, 8391, 10000 };
+
+ // Look up tan(a) in our table
+ int i = 1;
+ while (tan_fp > tan_table[i]) ++i;
+
+ // The angle a is between 5*i and 5*(i+1). We linearly interpolate.
+ int dist = tan_table[i] - tan_table[i-1];
+ int interp = (5 * (tan_fp - tan_table[i-1]) + dist/2) / dist;
+ return 5*i + interp;
+ } else {
+ // for tan(a) < 0.1, tan(a) is approximately linear in a.
+ // tan'(0) = 1, so in degrees the slope of atan is 180/pi = 57.29...
+ return (57 * y + x/2) / x;
+ }
+}
+
+int kGetAngle_SCI1_atan2(int y, int x) {
+ if (y < 0) {
+ int a = kGetAngle_SCI1_atan2(-y, -x);
+ if (a == 180)
+ return 0;
+ else
+ return 180 + a;
+ }
+ if (x < 0)
+ return 90 + kGetAngle_SCI1_atan2(-x, y);
+ if (y > x)
+ return 90 - kGetAngle_SCI1_atan2_base(x, y);
+ else
+ return kGetAngle_SCI1_atan2_base(y, x);
+
+}
+
+uint16 kGetAngle_SCI1(int16 x1, int16 y1, int16 x2, int16 y2) {
+ // We flip things around to get into the standard atan2 coordinate system
+ return kGetAngle_SCI1_atan2(x2 - x1, y1 - y2);
+
+}
+
+/**
+ * Returns the angle (in degrees) between the two points determined by (x1, y1)
+ * and (x2, y2). The angle ranges from 0 to 359 degrees.
+ * What this function does is pretty simple but apparently the original is not
+ * accurate.
+ */
+
+uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) {
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
+ return kGetAngle_SCI1(x1, y1, x2, y2);
+ else
+ return kGetAngle_SCI0(x1, y1, x2, y2);
+}
+
+
+
reg_t kGetAngle(EngineState *s, int argc, reg_t *argv) {
// Based on behavior observed with a test program created with
// SCI Studio.