diff --git a/src/platform/gba/Makefile b/src/platform/gba/Makefile
index 35217d8..d11b487 100644
--- a/src/platform/gba/Makefile
+++ b/src/platform/gba/Makefile
@@ -26,6 +26,7 @@ SOURCES := .
INCLUDES := include
DATA := data
MUSIC :=
+LIBTONC := $(DEVKITPRO)/libtonc
#---------------------------------------------------------------------------------
# options for code generation
@@ -48,14 +49,14 @@ LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
-LIBS := -lmm -lgba
+LIBS := -lmm -ltonc
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
-LIBDIRS := $(LIBGBA)
+LIBDIRS := $(LIBGBA) $(LIBTONC)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
diff --git a/src/platform/gba/OpenLara.vcxproj b/src/platform/gba/OpenLara.vcxproj
index 8f5cecc..b6e42bd 100644
--- a/src/platform/gba/OpenLara.vcxproj
+++ b/src/platform/gba/OpenLara.vcxproj
@@ -25,8 +25,15 @@
+
+
+
+
+
+
+
15.0
@@ -105,6 +112,7 @@
Console
true
+ winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
@@ -139,6 +147,7 @@
true
true
true
+ winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
diff --git a/src/platform/gba/camera.h b/src/platform/gba/camera.h
index cfbbb32..0255b41 100644
--- a/src/platform/gba/camera.h
+++ b/src/platform/gba/camera.h
@@ -3,64 +3,163 @@
#include "common.h"
-#define CAM_SPEED (1 << 3)
-#define CAM_ROT_SPEED (1 << 9)
-#define CAM_ROT_X_MAX int16(85 * 0x8000 / 180)
+#define CAM_SPEED (1 << 3)
+#define CAM_ROT_SPEED (1 << 9)
+#define CAM_ROT_X_MAX int16(85 * 0x8000 / 180)
+#define CAM_DIST_FOLLOW (1024 + 512)
-struct Camera {
- vec3i pos;
- int16 rotX, rotY;
+enum CameraMode
+{
+ CAMERA_MODE_FREE = 0,
+ CAMERA_MODE_FOLLOW = 1,
+ CAMERA_MODE_COMBAT = 2,
+ CAMERA_MODE_FIXED = 3,
+};
+
+struct Camera
+{
+ vec3i viewPos;
+ vec3i targetPos;
+
+ int16 targetAngleX;
+ int16 targetAngleY;
+ int16 targetDist;
+
+ int16 angleX;
+ int16 angleY;
int32 room;
- void init() {
- pos.x = 75162;
- pos.y = 2048;
- pos.z = 5000;
+ Item* item;
- rotX = 0;
- rotY = 16 << 8;
+ CameraMode mode;
+ bool modeSwitch;
- //rotX = -0x1000;
- //rotY = int16(0x8000);
+ void init()
+ {
+ item = NULL;
+ mode = CAMERA_MODE_FOLLOW;
+ modeSwitch = false;
+
+ viewPos.x = 75162;
+ viewPos.y = 2048;
+ viewPos.z = 5000;
+
+ targetPos = viewPos;
+ targetAngleX = 0;
+ targetAngleY = 0;
+ targetDist = CAM_DIST_FOLLOW;
+
+ angleX = 0;
+ angleY = 16 << 8;
+
+ //angleX = -0x1000;
+ //angleY = int16(0x8000);
}
- void update() {
- if (keys[IK_UP]) rotX -= CAM_ROT_SPEED;
- if (keys[IK_DOWN]) rotX += CAM_ROT_SPEED;
- if (keys[IK_LEFT]) rotY -= CAM_ROT_SPEED;
- if (keys[IK_RIGHT]) rotY += CAM_ROT_SPEED;
-
- rotX = clamp(rotX, -CAM_ROT_X_MAX, CAM_ROT_X_MAX);
-
- matrixSetView(pos.x, pos.y, pos.z, rotX, rotY);
+ void freeControl()
+ {
+ matrixSetView(viewPos, angleX, angleY);
Matrix &m = matrixGet();
- if (keys[IK_R]) {
- pos.x += m[0].x * CAM_SPEED >> 10;
- pos.y += m[0].y * CAM_SPEED >> 10;
- pos.z += m[0].z * CAM_SPEED >> 10;
+ if (keys & IK_UP) angleX -= CAM_ROT_SPEED;
+ if (keys & IK_DOWN) angleX += CAM_ROT_SPEED;
+ if (keys & IK_LEFT) angleY -= CAM_ROT_SPEED;
+ if (keys & IK_RIGHT) angleY += CAM_ROT_SPEED;
+
+ angleX = X_CLAMP(angleX, -CAM_ROT_X_MAX, CAM_ROT_X_MAX);
+
+ if (keys & IK_A)
+ {
+ viewPos.x += m[2].x * CAM_SPEED >> 10;
+ viewPos.y += m[2].y * CAM_SPEED >> 10;
+ viewPos.z += m[2].z * CAM_SPEED >> 10;
}
- if (keys[IK_L]) {
- pos.x -= m[0].x * CAM_SPEED >> 10;
- pos.y -= m[0].y * CAM_SPEED >> 10;
- pos.z -= m[0].z * CAM_SPEED >> 10;
+ if (keys & IK_B)
+ {
+ viewPos.x -= m[2].x * CAM_SPEED >> 10;
+ viewPos.y -= m[2].y * CAM_SPEED >> 10;
+ viewPos.z -= m[2].z * CAM_SPEED >> 10;
+ }
+ }
+
+ void updateRoom()
+ {
+ room = getRoomIndex(room, viewPos.x, viewPos.y, viewPos.z);
+ const RoomInfo::Sector* sector = getSector(room, viewPos.x, viewPos.z);
+ int32 floor = getFloor(sector, viewPos.x, viewPos.y, viewPos.z);
+ int32 ceiling = getCeiling(sector, viewPos.x, viewPos.y, viewPos.z) - 256;
+
+ if (floor != WALL) {
+ viewPos.y = X_MIN(viewPos.y, floor - 256);
}
- if (keys[IK_A]) {
- pos.x += m[2].x * CAM_SPEED >> 10;
- pos.y += m[2].y * CAM_SPEED >> 10;
- pos.z += m[2].z * CAM_SPEED >> 10;
+ if (ceiling != WALL)
+ {
+ viewPos.y = X_MAX(viewPos.y, ceiling + 256);
+ }
+ }
+
+ void update()
+ {
+ if (keys & IK_START)
+ {
+ if (!modeSwitch)
+ {
+ modeSwitch = true;
+ if (mode != CAMERA_MODE_FREE) {
+ mode = CAMERA_MODE_FREE;
+ } else {
+ mode = CAMERA_MODE_FOLLOW;
+ }
+ }
+ } else {
+ modeSwitch = false;
}
- if (keys[IK_B]) {
- pos.x -= m[2].x * CAM_SPEED >> 10;
- pos.y -= m[2].y * CAM_SPEED >> 10;
- pos.z -= m[2].z * CAM_SPEED >> 10;
+ if (mode == CAMERA_MODE_FREE)
+ {
+ freeControl();
}
- room = getRoomIndex(room, &pos);
+ if (mode == CAMERA_MODE_FOLLOW && item) {
+ int32 tx = item->pos.x;
+ int32 ty = item->pos.y;
+ int32 tz = item->pos.z;
+
+ const Box &box = getBoundingBox(item);
+ ty += box.maxY + ((box.minY - box.maxY) * 3 >> 2);
+
+ targetPos.x = tx;
+ targetPos.y += (ty - targetPos.y) >> 2;
+ targetPos.z = tz;
+
+ int16 angle = item->angleY + targetAngleY;
+
+ int32 dy = targetDist * phd_sin(targetAngleX) >> FIXED_SHIFT;
+ int32 dz = targetDist * phd_cos(targetAngleX) >> FIXED_SHIFT;
+
+ int32 cx = targetPos.x - (phd_sin(angle) * dz >> FIXED_SHIFT);
+ int32 cy = targetPos.y - 256 + dy;
+ int32 cz = targetPos.z - (phd_cos(angle) * dz >> FIXED_SHIFT);
+
+ viewPos.x += (cx - viewPos.x) >> 2;
+ viewPos.y += (cy - viewPos.y) >> 2;
+ viewPos.z += (cz - viewPos.z) >> 2;
+
+ updateRoom();
+
+ anglesFromVector(targetPos.x - viewPos.x, targetPos.y - viewPos.y, targetPos.z - viewPos.z, angleX, angleY);
+ }
+
+ matrixSetView(viewPos, angleX, angleY);
+
+ updateRoom();
+
+ targetAngleX = 0;
+ targetAngleY = 0;
+ targetDist = CAM_DIST_FOLLOW;
}
};
diff --git a/src/platform/gba/collision.h b/src/platform/gba/collision.h
new file mode 100644
index 0000000..1413c54
--- /dev/null
+++ b/src/platform/gba/collision.h
@@ -0,0 +1,334 @@
+#ifndef H_COLLISION
+#define H_COLLISION
+
+#include "common.h"
+
+enum CollisionType {
+ CT_NONE = 0,
+ CT_FRONT = (1 << 0),
+ CT_LEFT = (1 << 1),
+ CT_RIGHT = (1 << 2),
+ CT_CEILING = (1 << 3),
+ CT_FRONT_CEILING = (1 << 4),
+ CT_FLOOR_CEILING = (1 << 5),
+};
+
+struct CollisionInfo {
+
+ enum SideType {
+ ST_MIDDLE,
+ ST_FRONT,
+ ST_LEFT,
+ ST_RIGHT,
+ ST_MAX
+ };
+
+ struct Side {
+ int32 floor;
+ int32 ceiling;
+ SlantType slantType;
+ };
+
+ int16 *trigger;
+
+ Side m;
+ Side f;
+ Side l;
+ Side r;
+
+ int32 radius;
+
+ int32 gapPos;
+ int32 gapNeg;
+ int32 gapCeiling;
+
+ vec3i offset;
+ vec3i pos;
+
+ int16 angle;
+ int16 quadrant;
+
+ CollisionType type;
+
+ int8 slantX;
+ int8 slantZ;
+
+ bool enemyPush;
+ bool enemyHit;
+ bool staticHit;
+ bool stopOnSlant;
+ bool stopOnLava;
+
+ void setSide(SideType st, int32 floor, int32 ceiling)
+ {
+ SlantType slantType;
+
+ if (floorSlant.slantX == 0 && floorSlant.slantZ == 0) {
+ slantType = SLANT_NONE;
+ } else if (abs(floorSlant.slantX) < 3 && abs(floorSlant.slantZ) < 3) {
+ slantType = SLANT_LOW;
+ } else {
+ slantType = SLANT_HIGH;
+ }
+
+ if (st != ST_MIDDLE) {
+ if (stopOnSlant && floor < 0 && slantType == SLANT_HIGH) {
+ floor -= 0x7FFF;
+ } else if (stopOnSlant && floor > 0 && slantType == SLANT_HIGH) {
+ floor = 512;
+ }/* TODO lava else if (stopOnLava && floor > 0 && trigger && FloorData(*(uint16*)trigger).cmd.func == FloorData::LAVA) {
+ floor = 512;
+ }*/
+ }
+
+ Side *s = &m + st;
+ s->slantType = slantType;
+ s->floor = floor;
+ s->ceiling = ceiling;
+ }
+};
+
+CollisionInfo cinfo;
+
+int32 alignOffset(int32 a, int32 b)
+{
+ int32 ca = a / 1024;
+ int32 cb = b / 1024;
+
+ if (ca == cb) {
+ return 0;
+ }
+
+ a &= 1023;
+
+ if (ca < cb) {
+ return 1025 - a;
+ }
+
+ return -(a + 1);
+}
+
+bool collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height) {
+ /*
+ cinfo.staticHit = false;
+ cinfo.offset = vec3i(0);
+
+ BoxV2 objBox(v2pos - vec3i(cinfo.radius, height, cinfo.radius),
+ v2pos + vec3i(cinfo.radius, 0, cinfo.radius));
+
+// TODO: check linked rooms
+ for (int j = 0; j < level->roomsCount; j++) {
+ const TR::Room &room = level->rooms[j];
+
+ for (int i = 0; i < room.meshesCount; i++) {
+ TR::Room::Mesh &m = room.meshes[i];
+ TR::StaticMesh &sm = level->staticMeshes[m.meshIndex];
+
+ if (sm.flags & 1) continue;
+
+ BoxV2 meshBox = sm.getBox(true);
+ meshBox.rotate90(m.rotation.value / ANGLE_90);
+ meshBox.translate(m.pos);
+
+ if (!objBox.intersect(meshBox)) continue;
+
+ cinfo.offset = meshBox.pushOut2D(objBox);
+
+ bool flip = (cinfo.quadrant > 1);
+
+ if (cinfo.quadrant & 1) {
+ if (abs(cinfo.offset.z) > cinfo.radius) {
+ cinfo.offset.z = cinfo.pos.z - p.z;
+ cinfo.type = CT_FRONT;
+ } else if (cinfo.offset.z != 0) {
+ cinfo.offset.x = 0;
+ cinfo.type = ((cinfo.offset.z > 0) ^ flip) ? CT_RIGHT : CT_LEFT;
+ } else {
+ cinfo.offset = vec3i(0);
+ }
+ } else {
+ if (abs(cinfo.offset.x) > cinfo.radius) {
+ cinfo.offset.x = cinfo.pos.x - p.x;
+ cinfo.type = CT_FRONT;
+ } else if (cinfo.offset.x != 0) {
+ cinfo.offset.z = 0;
+ cinfo.type = ((cinfo.offset.x > 0) ^ flip) ? CT_LEFT : CT_RIGHT;
+ } else {
+ cinfo.offset = vec3i(0);
+ }
+ }
+
+ cinfo.staticHit = true;
+
+ return true;
+ }
+ }
+ */
+ return false;
+}
+
+void collideRoom(Item* item, int32 height, int32 yOffset = 0)
+{
+ cinfo.type = CT_NONE;
+ cinfo.offset = vec3i(0, 0, 0);
+
+ vec3i p = item->pos;
+ p.y += yOffset;
+
+ int32 y = p.y - height;
+
+ int32 cy = y - 160;
+
+ int32 floor, ceiling;
+
+ #define CHECK_HEIGHT(v) {\
+ int32 roomIndex = getRoomIndex(item->room, v.x, cy, v.z);\
+ const RoomInfo::Sector* sector = getSector(roomIndex, v.x, v.z);\
+ floor = getFloor(sector, v.x, cy, v.z);\
+ if (floor != WALL) floor -= p.y;\
+ ceiling = getCeiling(sector, v.x, cy, v.z);\
+ if (ceiling != WALL) ceiling -= y;\
+ }
+
+// middle
+ CHECK_HEIGHT(p);
+
+ //cinfo.trigger = floorTrigger; TODO
+ cinfo.slantX = floorSlant.slantX;
+ cinfo.slantZ = floorSlant.slantZ;
+
+ cinfo.setSide(CollisionInfo::ST_MIDDLE, floor, ceiling);
+
+ vec3i f, l, r;
+ int32 R = cinfo.radius;
+
+ switch (cinfo.quadrant) {
+ case 0 : {
+ f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, R);
+ l = vec3i(-R, 0, R);
+ r = vec3i( R, 0, R);
+ break;
+ }
+ case 1 : {
+ f = vec3i( R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT);
+ l = vec3i( R, 0, R);
+ r = vec3i( R, 0, -R);
+ break;
+ }
+ case 2 : {
+ f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, -R);
+ l = vec3i( R, 0, -R);
+ r = vec3i(-R, 0, -R);
+ break;
+ }
+ case 3 : {
+ f = vec3i(-R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT);
+ l = vec3i(-R, 0, -R);
+ r = vec3i(-R, 0, R);
+ break;
+ }
+ default : {
+ f.x = f.y = f.z = 0;
+ l.x = l.y = l.z = 0;
+ r.x = r.y = r.z = 0;
+ ASSERT(false);
+ }
+ }
+
+ f += p;
+ l += p;
+ r += p;
+
+ vec3i delta;
+ delta.x = cinfo.pos.x - p.x;
+ delta.y = cinfo.pos.y - p.y;
+ delta.z = cinfo.pos.z - p.z;
+
+// front
+ CHECK_HEIGHT(f);
+ cinfo.setSide(CollisionInfo::ST_FRONT, floor, ceiling);
+
+// left
+ CHECK_HEIGHT(l);
+ cinfo.setSide(CollisionInfo::ST_LEFT, floor, ceiling);
+
+// right
+ CHECK_HEIGHT(r);
+ cinfo.setSide(CollisionInfo::ST_RIGHT, floor, ceiling);
+
+// static objects
+ collideStatic(cinfo, p, height);
+
+// check middle
+ if (cinfo.m.floor == WALL)
+ {
+ cinfo.offset = delta;
+ cinfo.type = CT_FRONT;
+ return;
+ }
+
+ if (cinfo.m.floor <= cinfo.m.ceiling)
+ {
+ cinfo.offset = delta;
+ cinfo.type = CT_FLOOR_CEILING;
+ return;
+ }
+
+ if (cinfo.m.ceiling >= 0)
+ {
+ cinfo.offset.y = cinfo.m.ceiling;
+ cinfo.type = CT_CEILING;
+ }
+
+// front
+ if (cinfo.f.floor > cinfo.gapPos ||
+ cinfo.f.floor < cinfo.gapNeg ||
+ cinfo.f.ceiling > cinfo.gapCeiling)
+ {
+ if (cinfo.quadrant & 1)
+ {
+ cinfo.offset.x = alignOffset(f.x, p.x);
+ cinfo.offset.z = delta.z;
+ } else {
+ cinfo.offset.x = delta.x;
+ cinfo.offset.z = alignOffset(f.z, p.z);
+ }
+
+ cinfo.type = CT_FRONT;
+ return;
+ }
+
+// front ceiling
+ if (cinfo.f.ceiling >= cinfo.gapCeiling)
+ {
+ cinfo.offset = delta;
+ cinfo.type = CT_FRONT_CEILING;
+ return;
+ }
+
+// left
+ if (cinfo.l.floor > cinfo.gapPos || cinfo.l.floor < cinfo.gapNeg)
+ {
+ if (cinfo.quadrant & 1) {
+ cinfo.offset.z = alignOffset(l.z, f.z);
+ } else {
+ cinfo.offset.x = alignOffset(l.x, f.x);
+ }
+ cinfo.type = CT_LEFT;
+ return;
+ }
+
+// right
+ if (cinfo.r.floor > cinfo.gapPos || cinfo.r.floor < cinfo.gapNeg)
+ {
+ if (cinfo.quadrant & 1) {
+ cinfo.offset.z = alignOffset(r.z, f.z);
+ } else {
+ cinfo.offset.x = alignOffset(r.x, f.x);
+ }
+ cinfo.type = CT_RIGHT;
+ return;
+ }
+}
+
+#endif
diff --git a/src/platform/gba/common.cpp b/src/platform/gba/common.cpp
index 197356a..52f4b0e 100644
--- a/src/platform/gba/common.cpp
+++ b/src/platform/gba/common.cpp
@@ -1,11 +1,144 @@
#include "common.h"
-uint16 divTable[DIV_TABLE_SIZE]; // IWRAM 0.5 kb
+Rect clip;
vec3i viewPos;
Matrix matrixStack[MAX_MATRICES];
int32 matrixStackIndex = 0;
-const int16 sin_table[1025] = { // IWRAM 2 kb
+uint32 keys;
+
+const uint16 divTable[DIV_TABLE_SIZE] = {
+ 0xFFFF, 0xFFFF, 0x8000, 0x5555, 0x4000, 0x3333, 0x2AAA, 0x2492,
+ 0x2000, 0x1C71, 0x1999, 0x1745, 0x1555, 0x13B1, 0x1249, 0x1111,
+ 0x1000, 0x0F0F, 0x0E38, 0x0D79, 0x0CCC, 0x0C30, 0x0BA2, 0x0B21,
+ 0x0AAA, 0x0A3D, 0x09D8, 0x097B, 0x0924, 0x08D3, 0x0888, 0x0842,
+ 0x0800, 0x07C1, 0x0787, 0x0750, 0x071C, 0x06EB, 0x06BC, 0x0690,
+ 0x0666, 0x063E, 0x0618, 0x05F4, 0x05D1, 0x05B0, 0x0590, 0x0572,
+ 0x0555, 0x0539, 0x051E, 0x0505, 0x04EC, 0x04D4, 0x04BD, 0x04A7,
+ 0x0492, 0x047D, 0x0469, 0x0456, 0x0444, 0x0432, 0x0421, 0x0410,
+ 0x0400, 0x03F0, 0x03E0, 0x03D2, 0x03C3, 0x03B5, 0x03A8, 0x039B,
+ 0x038E, 0x0381, 0x0375, 0x0369, 0x035E, 0x0353, 0x0348, 0x033D,
+ 0x0333, 0x0329, 0x031F, 0x0315, 0x030C, 0x0303, 0x02FA, 0x02F1,
+ 0x02E8, 0x02E0, 0x02D8, 0x02D0, 0x02C8, 0x02C0, 0x02B9, 0x02B1,
+ 0x02AA, 0x02A3, 0x029C, 0x0295, 0x028F, 0x0288, 0x0282, 0x027C,
+ 0x0276, 0x0270, 0x026A, 0x0264, 0x025E, 0x0259, 0x0253, 0x024E,
+ 0x0249, 0x0243, 0x023E, 0x0239, 0x0234, 0x0230, 0x022B, 0x0226,
+ 0x0222, 0x021D, 0x0219, 0x0214, 0x0210, 0x020C, 0x0208, 0x0204,
+ 0x0200, 0x01FC, 0x01F8, 0x01F4, 0x01F0, 0x01EC, 0x01E9, 0x01E5,
+ 0x01E1, 0x01DE, 0x01DA, 0x01D7, 0x01D4, 0x01D0, 0x01CD, 0x01CA,
+ 0x01C7, 0x01C3, 0x01C0, 0x01BD, 0x01BA, 0x01B7, 0x01B4, 0x01B2,
+ 0x01AF, 0x01AC, 0x01A9, 0x01A6, 0x01A4, 0x01A1, 0x019E, 0x019C,
+ 0x0199, 0x0197, 0x0194, 0x0192, 0x018F, 0x018D, 0x018A, 0x0188,
+ 0x0186, 0x0183, 0x0181, 0x017F, 0x017D, 0x017A, 0x0178, 0x0176,
+ 0x0174, 0x0172, 0x0170, 0x016E, 0x016C, 0x016A, 0x0168, 0x0166,
+ 0x0164, 0x0162, 0x0160, 0x015E, 0x015C, 0x015A, 0x0158, 0x0157,
+ 0x0155, 0x0153, 0x0151, 0x0150, 0x014E, 0x014C, 0x014A, 0x0149,
+ 0x0147, 0x0146, 0x0144, 0x0142, 0x0141, 0x013F, 0x013E, 0x013C,
+ 0x013B, 0x0139, 0x0138, 0x0136, 0x0135, 0x0133, 0x0132, 0x0130,
+ 0x012F, 0x012E, 0x012C, 0x012B, 0x0129, 0x0128, 0x0127, 0x0125,
+ 0x0124, 0x0123, 0x0121, 0x0120, 0x011F, 0x011E, 0x011C, 0x011B,
+ 0x011A, 0x0119, 0x0118, 0x0116, 0x0115, 0x0114, 0x0113, 0x0112,
+ 0x0111, 0x010F, 0x010E, 0x010D, 0x010C, 0x010B, 0x010A, 0x0109,
+ 0x0108, 0x0107, 0x0106, 0x0105, 0x0104, 0x0103, 0x0102, 0x0101,
+ 0x0100, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FB, 0x00FA, 0x00F9,
+ 0x00F8, 0x00F7, 0x00F6, 0x00F5, 0x00F4, 0x00F3, 0x00F2, 0x00F1,
+ 0x00F0, 0x00F0, 0x00EF, 0x00EE, 0x00ED, 0x00EC, 0x00EB, 0x00EA,
+ 0x00EA, 0x00E9, 0x00E8, 0x00E7, 0x00E6, 0x00E5, 0x00E5, 0x00E4,
+ 0x00E3, 0x00E2, 0x00E1, 0x00E1, 0x00E0, 0x00DF, 0x00DE, 0x00DE,
+ 0x00DD, 0x00DC, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D8,
+ 0x00D7, 0x00D6, 0x00D6, 0x00D5, 0x00D4, 0x00D4, 0x00D3, 0x00D2,
+ 0x00D2, 0x00D1, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CD,
+ 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C9, 0x00C8,
+ 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C4, 0x00C3,
+ 0x00C3, 0x00C2, 0x00C1, 0x00C1, 0x00C0, 0x00C0, 0x00BF, 0x00BF,
+ 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BB, 0x00BA,
+ 0x00BA, 0x00B9, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 0x00B7, 0x00B6,
+ 0x00B6, 0x00B5, 0x00B5, 0x00B4, 0x00B4, 0x00B3, 0x00B3, 0x00B2,
+ 0x00B2, 0x00B1, 0x00B1, 0x00B0, 0x00B0, 0x00AF, 0x00AF, 0x00AE,
+ 0x00AE, 0x00AD, 0x00AD, 0x00AC, 0x00AC, 0x00AC, 0x00AB, 0x00AB,
+ 0x00AA, 0x00AA, 0x00A9, 0x00A9, 0x00A8, 0x00A8, 0x00A8, 0x00A7,
+ 0x00A7, 0x00A6, 0x00A6, 0x00A5, 0x00A5, 0x00A5, 0x00A4, 0x00A4,
+ 0x00A3, 0x00A3, 0x00A3, 0x00A2, 0x00A2, 0x00A1, 0x00A1, 0x00A1,
+ 0x00A0, 0x00A0, 0x009F, 0x009F, 0x009F, 0x009E, 0x009E, 0x009D,
+ 0x009D, 0x009D, 0x009C, 0x009C, 0x009C, 0x009B, 0x009B, 0x009A,
+ 0x009A, 0x009A, 0x0099, 0x0099, 0x0099, 0x0098, 0x0098, 0x0098,
+ 0x0097, 0x0097, 0x0097, 0x0096, 0x0096, 0x0095, 0x0095, 0x0095,
+ 0x0094, 0x0094, 0x0094, 0x0093, 0x0093, 0x0093, 0x0092, 0x0092,
+ 0x0092, 0x0091, 0x0091, 0x0091, 0x0090, 0x0090, 0x0090, 0x0090,
+ 0x008F, 0x008F, 0x008F, 0x008E, 0x008E, 0x008E, 0x008D, 0x008D,
+ 0x008D, 0x008C, 0x008C, 0x008C, 0x008C, 0x008B, 0x008B, 0x008B,
+ 0x008A, 0x008A, 0x008A, 0x0089, 0x0089, 0x0089, 0x0089, 0x0088,
+ 0x0088, 0x0088, 0x0087, 0x0087, 0x0087, 0x0087, 0x0086, 0x0086,
+ 0x0086, 0x0086, 0x0085, 0x0085, 0x0085, 0x0084, 0x0084, 0x0084,
+ 0x0084, 0x0083, 0x0083, 0x0083, 0x0083, 0x0082, 0x0082, 0x0082,
+ 0x0082, 0x0081, 0x0081, 0x0081, 0x0081, 0x0080, 0x0080, 0x0080,
+ 0x0080, 0x007F, 0x007F, 0x007F, 0x007F, 0x007E, 0x007E, 0x007E,
+ 0x007E, 0x007D, 0x007D, 0x007D, 0x007D, 0x007C, 0x007C, 0x007C,
+ 0x007C, 0x007B, 0x007B, 0x007B, 0x007B, 0x007A, 0x007A, 0x007A,
+ 0x007A, 0x007A, 0x0079, 0x0079, 0x0079, 0x0079, 0x0078, 0x0078,
+ 0x0078, 0x0078, 0x0078, 0x0077, 0x0077, 0x0077, 0x0077, 0x0076,
+ 0x0076, 0x0076, 0x0076, 0x0076, 0x0075, 0x0075, 0x0075, 0x0075,
+ 0x0075, 0x0074, 0x0074, 0x0074, 0x0074, 0x0073, 0x0073, 0x0073,
+ 0x0073, 0x0073, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0071,
+ 0x0071, 0x0071, 0x0071, 0x0071, 0x0070, 0x0070, 0x0070, 0x0070,
+ 0x0070, 0x0070, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0x006E,
+ 0x006E, 0x006E, 0x006E, 0x006E, 0x006D, 0x006D, 0x006D, 0x006D,
+ 0x006D, 0x006D, 0x006C, 0x006C, 0x006C, 0x006C, 0x006C, 0x006B,
+ 0x006B, 0x006B, 0x006B, 0x006B, 0x006B, 0x006A, 0x006A, 0x006A,
+ 0x006A, 0x006A, 0x006A, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069,
+ 0x0069, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0067,
+ 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0066, 0x0066, 0x0066,
+ 0x0066, 0x0066, 0x0066, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065,
+ 0x0065, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064,
+ 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0062, 0x0062,
+ 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0061, 0x0061, 0x0061,
+ 0x0061, 0x0061, 0x0061, 0x0061, 0x0060, 0x0060, 0x0060, 0x0060,
+ 0x0060, 0x0060, 0x0060, 0x005F, 0x005F, 0x005F, 0x005F, 0x005F,
+ 0x005F, 0x005F, 0x005E, 0x005E, 0x005E, 0x005E, 0x005E, 0x005E,
+ 0x005E, 0x005E, 0x005D, 0x005D, 0x005D, 0x005D, 0x005D, 0x005D,
+ 0x005D, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C,
+ 0x005C, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B,
+ 0x005B, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A,
+ 0x005A, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059,
+ 0x0059, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058,
+ 0x0058, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057,
+ 0x0057, 0x0057, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+ 0x0056, 0x0056, 0x0056, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055,
+ 0x0055, 0x0055, 0x0055, 0x0055, 0x0054, 0x0054, 0x0054, 0x0054,
+ 0x0054, 0x0054, 0x0054, 0x0054, 0x0054, 0x0053, 0x0053, 0x0053,
+ 0x0053, 0x0053, 0x0053, 0x0053, 0x0053, 0x0053, 0x0052, 0x0052,
+ 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052,
+ 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051,
+ 0x0051, 0x0051, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050,
+ 0x0050, 0x0050, 0x0050, 0x0050, 0x004F, 0x004F, 0x004F, 0x004F,
+ 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x004E, 0x004E,
+ 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E,
+ 0x004E, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D,
+ 0x004D, 0x004D, 0x004D, 0x004D, 0x004C, 0x004C, 0x004C, 0x004C,
+ 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004B,
+ 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B,
+ 0x004B, 0x004B, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A,
+ 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x0049, 0x0049,
+ 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049,
+ 0x0049, 0x0049, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048,
+ 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0047,
+ 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047,
+ 0x0047, 0x0047, 0x0047, 0x0047, 0x0046, 0x0046, 0x0046, 0x0046,
+ 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046,
+ 0x0046, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045,
+ 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0044, 0x0044,
+ 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044,
+ 0x0044, 0x0044, 0x0044, 0x0044, 0x0043, 0x0043, 0x0043, 0x0043,
+ 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043,
+ 0x0043, 0x0043, 0x0043, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042,
+ 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042,
+ 0x0042, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041,
+ 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041,
+ 0x0041, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040,
+ 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040
+};
+
+const int16 sinTable[1025] = {
0x0000, 0x0019, 0x0032, 0x004B, 0x0065, 0x007E, 0x0097, 0x00B0,
0x00C9, 0x00E2, 0x00FB, 0x0114, 0x012E, 0x0147, 0x0160, 0x0179,
0x0192, 0x01AB, 0x01C4, 0x01DD, 0x01F7, 0x0210, 0x0229, 0x0242,
@@ -136,6 +269,271 @@ const int16 sin_table[1025] = { // IWRAM 2 kb
0x3FFF, 0x3FFF, 0x3FFF, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000
};
+const int16 atanTable[2050] = {
+ 0x0000, 0x0005, 0x000A, 0x000F, 0x0014, 0x0019, 0x001F, 0x0024,
+ 0x0029, 0x002E, 0x0033, 0x0038, 0x003D, 0x0042, 0x0047, 0x004C,
+ 0x0051, 0x0057, 0x005C, 0x0061, 0x0066, 0x006B, 0x0070, 0x0075,
+ 0x007A, 0x007F, 0x0084, 0x008A, 0x008F, 0x0094, 0x0099, 0x009E,
+ 0x00A3, 0x00A8, 0x00AD, 0x00B2, 0x00B7, 0x00BC, 0x00C2, 0x00C7,
+ 0x00CC, 0x00D1, 0x00D6, 0x00DB, 0x00E0, 0x00E5, 0x00EA, 0x00EF,
+ 0x00F4, 0x00FA, 0x00FF, 0x0104, 0x0109, 0x010E, 0x0113, 0x0118,
+ 0x011D, 0x0122, 0x0127, 0x012C, 0x0131, 0x0137, 0x013C, 0x0141,
+ 0x0146, 0x014B, 0x0150, 0x0155, 0x015A, 0x015F, 0x0164, 0x0169,
+ 0x016F, 0x0174, 0x0179, 0x017E, 0x0183, 0x0188, 0x018D, 0x0192,
+ 0x0197, 0x019C, 0x01A1, 0x01A6, 0x01AC, 0x01B1, 0x01B6, 0x01BB,
+ 0x01C0, 0x01C5, 0x01CA, 0x01CF, 0x01D4, 0x01D9, 0x01DE, 0x01E3,
+ 0x01E9, 0x01EE, 0x01F3, 0x01F8, 0x01FD, 0x0202, 0x0207, 0x020C,
+ 0x0211, 0x0216, 0x021B, 0x0220, 0x0226, 0x022B, 0x0230, 0x0235,
+ 0x023A, 0x023F, 0x0244, 0x0249, 0x024E, 0x0253, 0x0258, 0x025D,
+ 0x0262, 0x0268, 0x026D, 0x0272, 0x0277, 0x027C, 0x0281, 0x0286,
+ 0x028B, 0x0290, 0x0295, 0x029A, 0x029F, 0x02A4, 0x02A9, 0x02AF,
+ 0x02B4, 0x02B9, 0x02BE, 0x02C3, 0x02C8, 0x02CD, 0x02D2, 0x02D7,
+ 0x02DC, 0x02E1, 0x02E6, 0x02EB, 0x02F0, 0x02F6, 0x02FB, 0x0300,
+ 0x0305, 0x030A, 0x030F, 0x0314, 0x0319, 0x031E, 0x0323, 0x0328,
+ 0x032D, 0x0332, 0x0337, 0x033C, 0x0341, 0x0347, 0x034C, 0x0351,
+ 0x0356, 0x035B, 0x0360, 0x0365, 0x036A, 0x036F, 0x0374, 0x0379,
+ 0x037E, 0x0383, 0x0388, 0x038D, 0x0392, 0x0397, 0x039C, 0x03A2,
+ 0x03A7, 0x03AC, 0x03B1, 0x03B6, 0x03BB, 0x03C0, 0x03C5, 0x03CA,
+ 0x03CF, 0x03D4, 0x03D9, 0x03DE, 0x03E3, 0x03E8, 0x03ED, 0x03F2,
+ 0x03F7, 0x03FC, 0x0401, 0x0407, 0x040C, 0x0411, 0x0416, 0x041B,
+ 0x0420, 0x0425, 0x042A, 0x042F, 0x0434, 0x0439, 0x043E, 0x0443,
+ 0x0448, 0x044D, 0x0452, 0x0457, 0x045C, 0x0461, 0x0466, 0x046B,
+ 0x0470, 0x0475, 0x047A, 0x047F, 0x0484, 0x0489, 0x048E, 0x0494,
+ 0x0499, 0x049E, 0x04A3, 0x04A8, 0x04AD, 0x04B2, 0x04B7, 0x04BC,
+ 0x04C1, 0x04C6, 0x04CB, 0x04D0, 0x04D5, 0x04DA, 0x04DF, 0x04E4,
+ 0x04E9, 0x04EE, 0x04F3, 0x04F8, 0x04FD, 0x0502, 0x0507, 0x050C,
+ 0x0511, 0x0516, 0x051B, 0x0520, 0x0525, 0x052A, 0x052F, 0x0534,
+ 0x0539, 0x053E, 0x0543, 0x0548, 0x054D, 0x0552, 0x0557, 0x055C,
+ 0x0561, 0x0566, 0x056B, 0x0570, 0x0575, 0x057A, 0x057F, 0x0584,
+ 0x0589, 0x058E, 0x0593, 0x0598, 0x059D, 0x05A2, 0x05A7, 0x05AC,
+ 0x05B1, 0x05B6, 0x05BB, 0x05C0, 0x05C5, 0x05CA, 0x05CF, 0x05D4,
+ 0x05D9, 0x05DE, 0x05E3, 0x05E8, 0x05ED, 0x05F2, 0x05F7, 0x05FC,
+ 0x0601, 0x0606, 0x060B, 0x0610, 0x0615, 0x061A, 0x061F, 0x0624,
+ 0x0629, 0x062E, 0x0633, 0x0638, 0x063D, 0x0642, 0x0647, 0x064C,
+ 0x0651, 0x0656, 0x065B, 0x0660, 0x0665, 0x066A, 0x066E, 0x0673,
+ 0x0678, 0x067D, 0x0682, 0x0687, 0x068C, 0x0691, 0x0696, 0x069B,
+ 0x06A0, 0x06A5, 0x06AA, 0x06AF, 0x06B4, 0x06B9, 0x06BE, 0x06C3,
+ 0x06C8, 0x06CD, 0x06D2, 0x06D7, 0x06DC, 0x06E1, 0x06E5, 0x06EA,
+ 0x06EF, 0x06F4, 0x06F9, 0x06FE, 0x0703, 0x0708, 0x070D, 0x0712,
+ 0x0717, 0x071C, 0x0721, 0x0726, 0x072B, 0x0730, 0x0735, 0x0739,
+ 0x073E, 0x0743, 0x0748, 0x074D, 0x0752, 0x0757, 0x075C, 0x0761,
+ 0x0766, 0x076B, 0x0770, 0x0775, 0x077A, 0x077E, 0x0783, 0x0788,
+ 0x078D, 0x0792, 0x0797, 0x079C, 0x07A1, 0x07A6, 0x07AB, 0x07B0,
+ 0x07B5, 0x07B9, 0x07BE, 0x07C3, 0x07C8, 0x07CD, 0x07D2, 0x07D7,
+ 0x07DC, 0x07E1, 0x07E6, 0x07EB, 0x07EF, 0x07F4, 0x07F9, 0x07FE,
+ 0x0803, 0x0808, 0x080D, 0x0812, 0x0817, 0x081C, 0x0820, 0x0825,
+ 0x082A, 0x082F, 0x0834, 0x0839, 0x083E, 0x0843, 0x0848, 0x084C,
+ 0x0851, 0x0856, 0x085B, 0x0860, 0x0865, 0x086A, 0x086F, 0x0873,
+ 0x0878, 0x087D, 0x0882, 0x0887, 0x088C, 0x0891, 0x0896, 0x089A,
+ 0x089F, 0x08A4, 0x08A9, 0x08AE, 0x08B3, 0x08B8, 0x08BD, 0x08C1,
+ 0x08C6, 0x08CB, 0x08D0, 0x08D5, 0x08DA, 0x08DF, 0x08E3, 0x08E8,
+ 0x08ED, 0x08F2, 0x08F7, 0x08FC, 0x0901, 0x0905, 0x090A, 0x090F,
+ 0x0914, 0x0919, 0x091E, 0x0922, 0x0927, 0x092C, 0x0931, 0x0936,
+ 0x093B, 0x093F, 0x0944, 0x0949, 0x094E, 0x0953, 0x0958, 0x095C,
+ 0x0961, 0x0966, 0x096B, 0x0970, 0x0975, 0x0979, 0x097E, 0x0983,
+ 0x0988, 0x098D, 0x0992, 0x0996, 0x099B, 0x09A0, 0x09A5, 0x09AA,
+ 0x09AE, 0x09B3, 0x09B8, 0x09BD, 0x09C2, 0x09C6, 0x09CB, 0x09D0,
+ 0x09D5, 0x09DA, 0x09DE, 0x09E3, 0x09E8, 0x09ED, 0x09F2, 0x09F6,
+ 0x09FB, 0x0A00, 0x0A05, 0x0A0A, 0x0A0E, 0x0A13, 0x0A18, 0x0A1D,
+ 0x0A22, 0x0A26, 0x0A2B, 0x0A30, 0x0A35, 0x0A39, 0x0A3E, 0x0A43,
+ 0x0A48, 0x0A4D, 0x0A51, 0x0A56, 0x0A5B, 0x0A60, 0x0A64, 0x0A69,
+ 0x0A6E, 0x0A73, 0x0A77, 0x0A7C, 0x0A81, 0x0A86, 0x0A8B, 0x0A8F,
+ 0x0A94, 0x0A99, 0x0A9E, 0x0AA2, 0x0AA7, 0x0AAC, 0x0AB1, 0x0AB5,
+ 0x0ABA, 0x0ABF, 0x0AC4, 0x0AC8, 0x0ACD, 0x0AD2, 0x0AD7, 0x0ADB,
+ 0x0AE0, 0x0AE5, 0x0AE9, 0x0AEE, 0x0AF3, 0x0AF8, 0x0AFC, 0x0B01,
+ 0x0B06, 0x0B0B, 0x0B0F, 0x0B14, 0x0B19, 0x0B1E, 0x0B22, 0x0B27,
+ 0x0B2C, 0x0B30, 0x0B35, 0x0B3A, 0x0B3F, 0x0B43, 0x0B48, 0x0B4D,
+ 0x0B51, 0x0B56, 0x0B5B, 0x0B60, 0x0B64, 0x0B69, 0x0B6E, 0x0B72,
+ 0x0B77, 0x0B7C, 0x0B80, 0x0B85, 0x0B8A, 0x0B8F, 0x0B93, 0x0B98,
+ 0x0B9D, 0x0BA1, 0x0BA6, 0x0BAB, 0x0BAF, 0x0BB4, 0x0BB9, 0x0BBD,
+ 0x0BC2, 0x0BC7, 0x0BCB, 0x0BD0, 0x0BD5, 0x0BD9, 0x0BDE, 0x0BE3,
+ 0x0BE7, 0x0BEC, 0x0BF1, 0x0BF5, 0x0BFA, 0x0BFF, 0x0C03, 0x0C08,
+ 0x0C0D, 0x0C11, 0x0C16, 0x0C1B, 0x0C1F, 0x0C24, 0x0C29, 0x0C2D,
+ 0x0C32, 0x0C37, 0x0C3B, 0x0C40, 0x0C45, 0x0C49, 0x0C4E, 0x0C53,
+ 0x0C57, 0x0C5C, 0x0C60, 0x0C65, 0x0C6A, 0x0C6E, 0x0C73, 0x0C78,
+ 0x0C7C, 0x0C81, 0x0C86, 0x0C8A, 0x0C8F, 0x0C93, 0x0C98, 0x0C9D,
+ 0x0CA1, 0x0CA6, 0x0CAB, 0x0CAF, 0x0CB4, 0x0CB8, 0x0CBD, 0x0CC2,
+ 0x0CC6, 0x0CCB, 0x0CCF, 0x0CD4, 0x0CD9, 0x0CDD, 0x0CE2, 0x0CE6,
+ 0x0CEB, 0x0CF0, 0x0CF4, 0x0CF9, 0x0CFD, 0x0D02, 0x0D07, 0x0D0B,
+ 0x0D10, 0x0D14, 0x0D19, 0x0D1E, 0x0D22, 0x0D27, 0x0D2B, 0x0D30,
+ 0x0D34, 0x0D39, 0x0D3E, 0x0D42, 0x0D47, 0x0D4B, 0x0D50, 0x0D54,
+ 0x0D59, 0x0D5E, 0x0D62, 0x0D67, 0x0D6B, 0x0D70, 0x0D74, 0x0D79,
+ 0x0D7D, 0x0D82, 0x0D87, 0x0D8B, 0x0D90, 0x0D94, 0x0D99, 0x0D9D,
+ 0x0DA2, 0x0DA6, 0x0DAB, 0x0DAF, 0x0DB4, 0x0DB9, 0x0DBD, 0x0DC2,
+ 0x0DC6, 0x0DCB, 0x0DCF, 0x0DD4, 0x0DD8, 0x0DDD, 0x0DE1, 0x0DE6,
+ 0x0DEA, 0x0DEF, 0x0DF3, 0x0DF8, 0x0DFC, 0x0E01, 0x0E05, 0x0E0A,
+ 0x0E0F, 0x0E13, 0x0E18, 0x0E1C, 0x0E21, 0x0E25, 0x0E2A, 0x0E2E,
+ 0x0E33, 0x0E37, 0x0E3C, 0x0E40, 0x0E45, 0x0E49, 0x0E4E, 0x0E52,
+ 0x0E56, 0x0E5B, 0x0E5F, 0x0E64, 0x0E68, 0x0E6D, 0x0E71, 0x0E76,
+ 0x0E7A, 0x0E7F, 0x0E83, 0x0E88, 0x0E8C, 0x0E91, 0x0E95, 0x0E9A,
+ 0x0E9E, 0x0EA3, 0x0EA7, 0x0EAC, 0x0EB0, 0x0EB4, 0x0EB9, 0x0EBD,
+ 0x0EC2, 0x0EC6, 0x0ECB, 0x0ECF, 0x0ED4, 0x0ED8, 0x0EDC, 0x0EE1,
+ 0x0EE5, 0x0EEA, 0x0EEE, 0x0EF3, 0x0EF7, 0x0EFC, 0x0F00, 0x0F04,
+ 0x0F09, 0x0F0D, 0x0F12, 0x0F16, 0x0F1B, 0x0F1F, 0x0F23, 0x0F28,
+ 0x0F2C, 0x0F31, 0x0F35, 0x0F3A, 0x0F3E, 0x0F42, 0x0F47, 0x0F4B,
+ 0x0F50, 0x0F54, 0x0F58, 0x0F5D, 0x0F61, 0x0F66, 0x0F6A, 0x0F6E,
+ 0x0F73, 0x0F77, 0x0F7C, 0x0F80, 0x0F84, 0x0F89, 0x0F8D, 0x0F91,
+ 0x0F96, 0x0F9A, 0x0F9F, 0x0FA3, 0x0FA7, 0x0FAC, 0x0FB0, 0x0FB5,
+ 0x0FB9, 0x0FBD, 0x0FC2, 0x0FC6, 0x0FCA, 0x0FCF, 0x0FD3, 0x0FD7,
+ 0x0FDC, 0x0FE0, 0x0FE5, 0x0FE9, 0x0FED, 0x0FF2, 0x0FF6, 0x0FFA,
+ 0x0FFF, 0x1003, 0x1007, 0x100C, 0x1010, 0x1014, 0x1019, 0x101D,
+ 0x1021, 0x1026, 0x102A, 0x102E, 0x1033, 0x1037, 0x103B, 0x1040,
+ 0x1044, 0x1048, 0x104D, 0x1051, 0x1055, 0x105A, 0x105E, 0x1062,
+ 0x1067, 0x106B, 0x106F, 0x1073, 0x1078, 0x107C, 0x1080, 0x1085,
+ 0x1089, 0x108D, 0x1092, 0x1096, 0x109A, 0x109E, 0x10A3, 0x10A7,
+ 0x10AB, 0x10B0, 0x10B4, 0x10B8, 0x10BC, 0x10C1, 0x10C5, 0x10C9,
+ 0x10CE, 0x10D2, 0x10D6, 0x10DA, 0x10DF, 0x10E3, 0x10E7, 0x10EB,
+ 0x10F0, 0x10F4, 0x10F8, 0x10FD, 0x1101, 0x1105, 0x1109, 0x110E,
+ 0x1112, 0x1116, 0x111A, 0x111F, 0x1123, 0x1127, 0x112B, 0x1130,
+ 0x1134, 0x1138, 0x113C, 0x1140, 0x1145, 0x1149, 0x114D, 0x1151,
+ 0x1156, 0x115A, 0x115E, 0x1162, 0x1166, 0x116B, 0x116F, 0x1173,
+ 0x1177, 0x117C, 0x1180, 0x1184, 0x1188, 0x118C, 0x1191, 0x1195,
+ 0x1199, 0x119D, 0x11A1, 0x11A6, 0x11AA, 0x11AE, 0x11B2, 0x11B6,
+ 0x11BB, 0x11BF, 0x11C3, 0x11C7, 0x11CB, 0x11CF, 0x11D4, 0x11D8,
+ 0x11DC, 0x11E0, 0x11E4, 0x11E9, 0x11ED, 0x11F1, 0x11F5, 0x11F9,
+ 0x11FD, 0x1202, 0x1206, 0x120A, 0x120E, 0x1212, 0x1216, 0x121A,
+ 0x121F, 0x1223, 0x1227, 0x122B, 0x122F, 0x1233, 0x1237, 0x123C,
+ 0x1240, 0x1244, 0x1248, 0x124C, 0x1250, 0x1254, 0x1259, 0x125D,
+ 0x1261, 0x1265, 0x1269, 0x126D, 0x1271, 0x1275, 0x127A, 0x127E,
+ 0x1282, 0x1286, 0x128A, 0x128E, 0x1292, 0x1296, 0x129A, 0x129F,
+ 0x12A3, 0x12A7, 0x12AB, 0x12AF, 0x12B3, 0x12B7, 0x12BB, 0x12BF,
+ 0x12C3, 0x12C7, 0x12CC, 0x12D0, 0x12D4, 0x12D8, 0x12DC, 0x12E0,
+ 0x12E4, 0x12E8, 0x12EC, 0x12F0, 0x12F4, 0x12F8, 0x12FC, 0x1301,
+ 0x1305, 0x1309, 0x130D, 0x1311, 0x1315, 0x1319, 0x131D, 0x1321,
+ 0x1325, 0x1329, 0x132D, 0x1331, 0x1335, 0x1339, 0x133D, 0x1341,
+ 0x1345, 0x1349, 0x134D, 0x1351, 0x1355, 0x135A, 0x135E, 0x1362,
+ 0x1366, 0x136A, 0x136E, 0x1372, 0x1376, 0x137A, 0x137E, 0x1382,
+ 0x1386, 0x138A, 0x138E, 0x1392, 0x1396, 0x139A, 0x139E, 0x13A2,
+ 0x13A6, 0x13AA, 0x13AE, 0x13B2, 0x13B6, 0x13BA, 0x13BE, 0x13C2,
+ 0x13C6, 0x13CA, 0x13CE, 0x13D2, 0x13D6, 0x13DA, 0x13DE, 0x13E2,
+ 0x13E6, 0x13E9, 0x13ED, 0x13F1, 0x13F5, 0x13F9, 0x13FD, 0x1401,
+ 0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D, 0x1421,
+ 0x1425, 0x1429, 0x142D, 0x1431, 0x1435, 0x1439, 0x143D, 0x1440,
+ 0x1444, 0x1448, 0x144C, 0x1450, 0x1454, 0x1458, 0x145C, 0x1460,
+ 0x1464, 0x1468, 0x146C, 0x1470, 0x1473, 0x1477, 0x147B, 0x147F,
+ 0x1483, 0x1487, 0x148B, 0x148F, 0x1493, 0x1497, 0x149B, 0x149E,
+ 0x14A2, 0x14A6, 0x14AA, 0x14AE, 0x14B2, 0x14B6, 0x14BA, 0x14BE,
+ 0x14C1, 0x14C5, 0x14C9, 0x14CD, 0x14D1, 0x14D5, 0x14D9, 0x14DD,
+ 0x14E0, 0x14E4, 0x14E8, 0x14EC, 0x14F0, 0x14F4, 0x14F8, 0x14FB,
+ 0x14FF, 0x1503, 0x1507, 0x150B, 0x150F, 0x1513, 0x1516, 0x151A,
+ 0x151E, 0x1522, 0x1526, 0x152A, 0x152D, 0x1531, 0x1535, 0x1539,
+ 0x153D, 0x1541, 0x1544, 0x1548, 0x154C, 0x1550, 0x1554, 0x1558,
+ 0x155B, 0x155F, 0x1563, 0x1567, 0x156B, 0x156E, 0x1572, 0x1576,
+ 0x157A, 0x157E, 0x1581, 0x1585, 0x1589, 0x158D, 0x1591, 0x1594,
+ 0x1598, 0x159C, 0x15A0, 0x15A4, 0x15A7, 0x15AB, 0x15AF, 0x15B3,
+ 0x15B7, 0x15BA, 0x15BE, 0x15C2, 0x15C6, 0x15C9, 0x15CD, 0x15D1,
+ 0x15D5, 0x15D8, 0x15DC, 0x15E0, 0x15E4, 0x15E8, 0x15EB, 0x15EF,
+ 0x15F3, 0x15F7, 0x15FA, 0x15FE, 0x1602, 0x1606, 0x1609, 0x160D,
+ 0x1611, 0x1614, 0x1618, 0x161C, 0x1620, 0x1623, 0x1627, 0x162B,
+ 0x162F, 0x1632, 0x1636, 0x163A, 0x163E, 0x1641, 0x1645, 0x1649,
+ 0x164C, 0x1650, 0x1654, 0x1658, 0x165B, 0x165F, 0x1663, 0x1666,
+ 0x166A, 0x166E, 0x1671, 0x1675, 0x1679, 0x167D, 0x1680, 0x1684,
+ 0x1688, 0x168B, 0x168F, 0x1693, 0x1696, 0x169A, 0x169E, 0x16A1,
+ 0x16A5, 0x16A9, 0x16AC, 0x16B0, 0x16B4, 0x16B7, 0x16BB, 0x16BF,
+ 0x16C2, 0x16C6, 0x16CA, 0x16CD, 0x16D1, 0x16D5, 0x16D8, 0x16DC,
+ 0x16E0, 0x16E3, 0x16E7, 0x16EB, 0x16EE, 0x16F2, 0x16F6, 0x16F9,
+ 0x16FD, 0x1700, 0x1704, 0x1708, 0x170B, 0x170F, 0x1713, 0x1716,
+ 0x171A, 0x171D, 0x1721, 0x1725, 0x1728, 0x172C, 0x1730, 0x1733,
+ 0x1737, 0x173A, 0x173E, 0x1742, 0x1745, 0x1749, 0x174C, 0x1750,
+ 0x1754, 0x1757, 0x175B, 0x175E, 0x1762, 0x1766, 0x1769, 0x176D,
+ 0x1770, 0x1774, 0x1778, 0x177B, 0x177F, 0x1782, 0x1786, 0x1789,
+ 0x178D, 0x1791, 0x1794, 0x1798, 0x179B, 0x179F, 0x17A2, 0x17A6,
+ 0x17AA, 0x17AD, 0x17B1, 0x17B4, 0x17B8, 0x17BB, 0x17BF, 0x17C2,
+ 0x17C6, 0x17C9, 0x17CD, 0x17D1, 0x17D4, 0x17D8, 0x17DB, 0x17DF,
+ 0x17E2, 0x17E6, 0x17E9, 0x17ED, 0x17F0, 0x17F4, 0x17F7, 0x17FB,
+ 0x17FE, 0x1802, 0x1806, 0x1809, 0x180D, 0x1810, 0x1814, 0x1817,
+ 0x181B, 0x181E, 0x1822, 0x1825, 0x1829, 0x182C, 0x1830, 0x1833,
+ 0x1837, 0x183A, 0x183E, 0x1841, 0x1845, 0x1848, 0x184C, 0x184F,
+ 0x1853, 0x1856, 0x185A, 0x185D, 0x1860, 0x1864, 0x1867, 0x186B,
+ 0x186E, 0x1872, 0x1875, 0x1879, 0x187C, 0x1880, 0x1883, 0x1887,
+ 0x188A, 0x188E, 0x1891, 0x1894, 0x1898, 0x189B, 0x189F, 0x18A2,
+ 0x18A6, 0x18A9, 0x18AD, 0x18B0, 0x18B3, 0x18B7, 0x18BA, 0x18BE,
+ 0x18C1, 0x18C5, 0x18C8, 0x18CC, 0x18CF, 0x18D2, 0x18D6, 0x18D9,
+ 0x18DD, 0x18E0, 0x18E3, 0x18E7, 0x18EA, 0x18EE, 0x18F1, 0x18F5,
+ 0x18F8, 0x18FB, 0x18FF, 0x1902, 0x1906, 0x1909, 0x190C, 0x1910,
+ 0x1913, 0x1917, 0x191A, 0x191D, 0x1921, 0x1924, 0x1928, 0x192B,
+ 0x192E, 0x1932, 0x1935, 0x1938, 0x193C, 0x193F, 0x1943, 0x1946,
+ 0x1949, 0x194D, 0x1950, 0x1953, 0x1957, 0x195A, 0x195D, 0x1961,
+ 0x1964, 0x1968, 0x196B, 0x196E, 0x1972, 0x1975, 0x1978, 0x197C,
+ 0x197F, 0x1982, 0x1986, 0x1989, 0x198C, 0x1990, 0x1993, 0x1996,
+ 0x199A, 0x199D, 0x19A0, 0x19A4, 0x19A7, 0x19AA, 0x19AE, 0x19B1,
+ 0x19B4, 0x19B8, 0x19BB, 0x19BE, 0x19C2, 0x19C5, 0x19C8, 0x19CC,
+ 0x19CF, 0x19D2, 0x19D5, 0x19D9, 0x19DC, 0x19DF, 0x19E3, 0x19E6,
+ 0x19E9, 0x19ED, 0x19F0, 0x19F3, 0x19F6, 0x19FA, 0x19FD, 0x1A00,
+ 0x1A04, 0x1A07, 0x1A0A, 0x1A0D, 0x1A11, 0x1A14, 0x1A17, 0x1A1B,
+ 0x1A1E, 0x1A21, 0x1A24, 0x1A28, 0x1A2B, 0x1A2E, 0x1A31, 0x1A35,
+ 0x1A38, 0x1A3B, 0x1A3E, 0x1A42, 0x1A45, 0x1A48, 0x1A4B, 0x1A4F,
+ 0x1A52, 0x1A55, 0x1A58, 0x1A5C, 0x1A5F, 0x1A62, 0x1A65, 0x1A69,
+ 0x1A6C, 0x1A6F, 0x1A72, 0x1A76, 0x1A79, 0x1A7C, 0x1A7F, 0x1A83,
+ 0x1A86, 0x1A89, 0x1A8C, 0x1A8F, 0x1A93, 0x1A96, 0x1A99, 0x1A9C,
+ 0x1A9F, 0x1AA3, 0x1AA6, 0x1AA9, 0x1AAC, 0x1AB0, 0x1AB3, 0x1AB6,
+ 0x1AB9, 0x1ABC, 0x1AC0, 0x1AC3, 0x1AC6, 0x1AC9, 0x1ACC, 0x1ACF,
+ 0x1AD3, 0x1AD6, 0x1AD9, 0x1ADC, 0x1ADF, 0x1AE3, 0x1AE6, 0x1AE9,
+ 0x1AEC, 0x1AEF, 0x1AF2, 0x1AF6, 0x1AF9, 0x1AFC, 0x1AFF, 0x1B02,
+ 0x1B05, 0x1B09, 0x1B0C, 0x1B0F, 0x1B12, 0x1B15, 0x1B18, 0x1B1C,
+ 0x1B1F, 0x1B22, 0x1B25, 0x1B28, 0x1B2B, 0x1B2E, 0x1B32, 0x1B35,
+ 0x1B38, 0x1B3B, 0x1B3E, 0x1B41, 0x1B44, 0x1B48, 0x1B4B, 0x1B4E,
+ 0x1B51, 0x1B54, 0x1B57, 0x1B5A, 0x1B5D, 0x1B61, 0x1B64, 0x1B67,
+ 0x1B6A, 0x1B6D, 0x1B70, 0x1B73, 0x1B76, 0x1B79, 0x1B7D, 0x1B80,
+ 0x1B83, 0x1B86, 0x1B89, 0x1B8C, 0x1B8F, 0x1B92, 0x1B95, 0x1B98,
+ 0x1B9C, 0x1B9F, 0x1BA2, 0x1BA5, 0x1BA8, 0x1BAB, 0x1BAE, 0x1BB1,
+ 0x1BB4, 0x1BB7, 0x1BBA, 0x1BBD, 0x1BC1, 0x1BC4, 0x1BC7, 0x1BCA,
+ 0x1BCD, 0x1BD0, 0x1BD3, 0x1BD6, 0x1BD9, 0x1BDC, 0x1BDF, 0x1BE2,
+ 0x1BE5, 0x1BE8, 0x1BEB, 0x1BEE, 0x1BF2, 0x1BF5, 0x1BF8, 0x1BFB,
+ 0x1BFE, 0x1C01, 0x1C04, 0x1C07, 0x1C0A, 0x1C0D, 0x1C10, 0x1C13,
+ 0x1C16, 0x1C19, 0x1C1C, 0x1C1F, 0x1C22, 0x1C25, 0x1C28, 0x1C2B,
+ 0x1C2E, 0x1C31, 0x1C34, 0x1C37, 0x1C3A, 0x1C3D, 0x1C40, 0x1C43,
+ 0x1C46, 0x1C49, 0x1C4C, 0x1C4F, 0x1C52, 0x1C55, 0x1C58, 0x1C5B,
+ 0x1C5E, 0x1C61, 0x1C64, 0x1C67, 0x1C6A, 0x1C6D, 0x1C70, 0x1C73,
+ 0x1C76, 0x1C79, 0x1C7C, 0x1C7F, 0x1C82, 0x1C85, 0x1C88, 0x1C8B,
+ 0x1C8E, 0x1C91, 0x1C94, 0x1C97, 0x1C9A, 0x1C9D, 0x1CA0, 0x1CA3,
+ 0x1CA6, 0x1CA9, 0x1CAC, 0x1CAF, 0x1CB2, 0x1CB5, 0x1CB8, 0x1CBB,
+ 0x1CBE, 0x1CC1, 0x1CC3, 0x1CC6, 0x1CC9, 0x1CCC, 0x1CCF, 0x1CD2,
+ 0x1CD5, 0x1CD8, 0x1CDB, 0x1CDE, 0x1CE1, 0x1CE4, 0x1CE7, 0x1CEA,
+ 0x1CED, 0x1CF0, 0x1CF3, 0x1CF5, 0x1CF8, 0x1CFB, 0x1CFE, 0x1D01,
+ 0x1D04, 0x1D07, 0x1D0A, 0x1D0D, 0x1D10, 0x1D13, 0x1D16, 0x1D18,
+ 0x1D1B, 0x1D1E, 0x1D21, 0x1D24, 0x1D27, 0x1D2A, 0x1D2D, 0x1D30,
+ 0x1D33, 0x1D35, 0x1D38, 0x1D3B, 0x1D3E, 0x1D41, 0x1D44, 0x1D47,
+ 0x1D4A, 0x1D4D, 0x1D4F, 0x1D52, 0x1D55, 0x1D58, 0x1D5B, 0x1D5E,
+ 0x1D61, 0x1D64, 0x1D66, 0x1D69, 0x1D6C, 0x1D6F, 0x1D72, 0x1D75,
+ 0x1D78, 0x1D7B, 0x1D7D, 0x1D80, 0x1D83, 0x1D86, 0x1D89, 0x1D8C,
+ 0x1D8E, 0x1D91, 0x1D94, 0x1D97, 0x1D9A, 0x1D9D, 0x1DA0, 0x1DA2,
+ 0x1DA5, 0x1DA8, 0x1DAB, 0x1DAE, 0x1DB1, 0x1DB3, 0x1DB6, 0x1DB9,
+ 0x1DBC, 0x1DBF, 0x1DC2, 0x1DC4, 0x1DC7, 0x1DCA, 0x1DCD, 0x1DD0,
+ 0x1DD3, 0x1DD5, 0x1DD8, 0x1DDB, 0x1DDE, 0x1DE1, 0x1DE3, 0x1DE6,
+ 0x1DE9, 0x1DEC, 0x1DEF, 0x1DF1, 0x1DF4, 0x1DF7, 0x1DFA, 0x1DFD,
+ 0x1DFF, 0x1E02, 0x1E05, 0x1E08, 0x1E0B, 0x1E0D, 0x1E10, 0x1E13,
+ 0x1E16, 0x1E19, 0x1E1B, 0x1E1E, 0x1E21, 0x1E24, 0x1E26, 0x1E29,
+ 0x1E2C, 0x1E2F, 0x1E32, 0x1E34, 0x1E37, 0x1E3A, 0x1E3D, 0x1E3F,
+ 0x1E42, 0x1E45, 0x1E48, 0x1E4A, 0x1E4D, 0x1E50, 0x1E53, 0x1E55,
+ 0x1E58, 0x1E5B, 0x1E5E, 0x1E60, 0x1E63, 0x1E66, 0x1E69, 0x1E6B,
+ 0x1E6E, 0x1E71, 0x1E74, 0x1E76, 0x1E79, 0x1E7C, 0x1E7F, 0x1E81,
+ 0x1E84, 0x1E87, 0x1E8A, 0x1E8C, 0x1E8F, 0x1E92, 0x1E94, 0x1E97,
+ 0x1E9A, 0x1E9D, 0x1E9F, 0x1EA2, 0x1EA5, 0x1EA8, 0x1EAA, 0x1EAD,
+ 0x1EB0, 0x1EB2, 0x1EB5, 0x1EB8, 0x1EBA, 0x1EBD, 0x1EC0, 0x1EC3,
+ 0x1EC5, 0x1EC8, 0x1ECB, 0x1ECD, 0x1ED0, 0x1ED3, 0x1ED5, 0x1ED8,
+ 0x1EDB, 0x1EDE, 0x1EE0, 0x1EE3, 0x1EE6, 0x1EE8, 0x1EEB, 0x1EEE,
+ 0x1EF0, 0x1EF3, 0x1EF6, 0x1EF8, 0x1EFB, 0x1EFE, 0x1F00, 0x1F03,
+ 0x1F06, 0x1F08, 0x1F0B, 0x1F0E, 0x1F10, 0x1F13, 0x1F16, 0x1F18,
+ 0x1F1B, 0x1F1E, 0x1F20, 0x1F23, 0x1F26, 0x1F28, 0x1F2B, 0x1F2E,
+ 0x1F30, 0x1F33, 0x1F36, 0x1F38, 0x1F3B, 0x1F3D, 0x1F40, 0x1F43,
+ 0x1F45, 0x1F48, 0x1F4B, 0x1F4D, 0x1F50, 0x1F53, 0x1F55, 0x1F58,
+ 0x1F5A, 0x1F5D, 0x1F60, 0x1F62, 0x1F65, 0x1F68, 0x1F6A, 0x1F6D,
+ 0x1F6F, 0x1F72, 0x1F75, 0x1F77, 0x1F7A, 0x1F7C, 0x1F7F, 0x1F82,
+ 0x1F84, 0x1F87, 0x1F8A, 0x1F8C, 0x1F8F, 0x1F91, 0x1F94, 0x1F97,
+ 0x1F99, 0x1F9C, 0x1F9E, 0x1FA1, 0x1FA4, 0x1FA6, 0x1FA9, 0x1FAB,
+ 0x1FAE, 0x1FB0, 0x1FB3, 0x1FB6, 0x1FB8, 0x1FBB, 0x1FBD, 0x1FC0,
+ 0x1FC3, 0x1FC5, 0x1FC8, 0x1FCA, 0x1FCD, 0x1FCF, 0x1FD2, 0x1FD5,
+ 0x1FD7, 0x1FDA, 0x1FDC, 0x1FDF, 0x1FE1, 0x1FE4, 0x1FE6, 0x1FE9,
+ 0x1FEC, 0x1FEE, 0x1FF1, 0x1FF3, 0x1FF6, 0x1FF8, 0x1FFB, 0x1FFD,
+ 0x2000, 0x2000,
+};
+
+const int32 atanOctant[] = {
+ 0, -16384, -65535, 49152, -32768, 16384, 32768, -49152
+};
+
+
int32 phd_sin(int32 x)
{
x &= 0xFFFF;
@@ -146,7 +544,7 @@ int32 phd_sin(int32 x)
x = 0x8000 - x;
}
- x = sin_table[x >> 4];
+ x = sinTable[x >> 4];
return neg ? -x : x;
}
@@ -156,17 +554,86 @@ int32 phd_cos(int32 x)
return phd_sin(x + 0x4000);
}
-int32 clamp(int32 x, int32 a, int32 b) {
- return x < a ? a : (x > b ? b : x);
+int32 phd_atan(int32 x, int32 y)
+{
+ if (x == 0 && y == 0)
+ return 0;
+
+ int32 o = 0;
+
+ if (x < 0) {
+ o += 4;
+ x = -x;
+ }
+
+ if (y < 0) {
+ o += 2;
+ y = -y;
+ }
+
+ if (y > x) {
+ o++;
+ swap(x, y);
+ }
+
+ return abs(atanTable[(y << 11) / x] + atanOctant[o]);
}
-void initLUT() {
+uint32 phd_sqrt(uint32 x)
+{
+ uint32 m = 0x40000000;
+ uint32 y = 0;
+ uint32 z = 0;
+
+ do {
+ y += m;
+
+ if (y > x) {
+ y = z;
+ } else {
+ x -= y;
+ y = m | z;
+ }
+
+ z = y >> 1;
+ } while (m >>= 2);
+
+ return y;
+}
+
+void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY)
+{
+ angleY = phd_atan(z, x);
+ angleX = phd_atan(phd_sqrt(x * x + z * z), y);
+
+ if ((y > 0 && angleX > 0) || (y < 0 && angleX < 0))
+ {
+ angleX = -angleX;
+ }
+}
+
+/*
+void initDivTable()
+{
+ uint16 divTable[DIV_TABLE_SIZE];
+
divTable[0] = 0xFFFF;
divTable[1] = 0xFFFF;
for (uint32 i = 2; i < DIV_TABLE_SIZE; i++) {
divTable[i] = (1 << 16) / i;
}
+
+ ASSERT((DIV_TABLE_SIZE & 8) == 0)
+
+ printf("const uint16 divTable[DIV_TABLE_SIZE] = {\n");
+ for (int i = 0; i < DIV_TABLE_SIZE; i += 8) {
+ printf(" 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X,\n",
+ divTable[i + 0], divTable[i + 1], divTable[i + 2], divTable[i + 3],
+ divTable[i + 4], divTable[i + 5], divTable[i + 6], divTable[i + 7]);
+ }
+ printf("};\n");
}
+*/
Matrix& matrixGet()
{
@@ -188,22 +655,17 @@ void matrixPop()
matrixStackIndex--;
}
-void matrixTranslate(int32 x, int32 y, int32 z)
+void matrixTranslate(const vec3i &pos)
{
Matrix &m = matrixGet();
-
- vec3i offset(x, y, z);
- m[0][3] += DP33(m[0], offset);
- m[1][3] += DP33(m[1], offset);
- m[2][3] += DP33(m[2], offset);
+ m[0][3] += DP33(m[0], pos);
+ m[1][3] += DP33(m[1], pos);
+ m[2][3] += DP33(m[2], pos);
}
-void matrixTranslateAbs(int32 x, int32 y, int32 z)
+void matrixTranslateAbs(const vec3i &pos)
{
- vec3i d;
- d.x = x - viewPos.x;
- d.y = y - viewPos.y;
- d.z = z - viewPos.z;
+ vec3i d = pos - viewPos;
Matrix &m = matrixGet();
m[0][3] = DP33(m[0], d);
@@ -290,17 +752,41 @@ void matrixRotateYXZ(int32 angleX, int32 angleY, int32 angleZ)
if (angleZ) matrixRotateZ(angleZ);
}
-void matrixFrame(int32 x, int32 y, int32 z, uint16* angles)
+#define LERP_FAST(a, b, mul, div) a = (a + b) >> 1
+#define LERP_SLOW(a, b, mul, div) a = a + (b - a) * mul / div
+
+#define LERP_ROW(lerp_func, a, b, mul, div) \
+ lerp_func(a[0], b[0], mul, div); \
+ lerp_func(a[1], b[1], mul, div); \
+ lerp_func(a[2], b[2], mul, div); \
+ lerp_func(a[3], b[3], mul, div);
+
+void matrixLerp(const Matrix &n, int32 multiplier, int32 divider)
+{
+ Matrix &m = matrixGet();
+
+ if (divider == 2) {
+ LERP_ROW(LERP_FAST, m[0], n[0], multiplier, divider);
+ LERP_ROW(LERP_FAST, m[1], n[1], multiplier, divider);
+ LERP_ROW(LERP_FAST, m[2], n[2], multiplier, divider);
+ } else {
+ LERP_ROW(LERP_SLOW, m[0], n[0], multiplier, divider);
+ LERP_ROW(LERP_SLOW, m[1], n[1], multiplier, divider);
+ LERP_ROW(LERP_SLOW, m[2], n[2], multiplier, divider);
+ }
+}
+
+void matrixFrame(const vec3i &pos, uint16* angles)
{
int32 angleX = (angles[1] & 0x3FF0) << 2;
int32 angleY = (angles[1] & 0x000F) << 12 | (angles[0] & 0xFC00) >> 4;
int32 angleZ = (angles[0] & 0x03FF) << 6;
- matrixTranslate(x, y, z);
+ matrixTranslate(pos);
matrixRotateYXZ(angleX, angleY, angleZ);
}
-void matrixSetView(int32 x, int32 y, int32 z, int32 angleX, int32 angleY)
+void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY)
{
int32 sx = phd_sin(angleX);
int32 cx = phd_cos(angleX);
@@ -309,22 +795,37 @@ void matrixSetView(int32 x, int32 y, int32 z, int32 angleX, int32 angleY)
Matrix &m = matrixGet();
- m[0][0] = cy;
- m[0][1] = 0;
- m[0][2] = -sy;
- m[0][3] = x;
+#ifdef ROTATE90_MODE
+ m[1][0] = -(cy);
+ m[1][1] = -(0);
+ m[1][2] = -(-sy);
+ m[1][3] = pos.x;
- m[1][0] = (sx * sy) >> FIXED_SHIFT;
- m[1][1] = cx;
- m[1][2] = (sx * cy) >> FIXED_SHIFT;
- m[1][3] = y;
+ m[0][0] = (sx * sy) >> FIXED_SHIFT;
+ m[0][1] = cx;
+ m[0][2] = (sx * cy) >> FIXED_SHIFT;
+ m[0][3] = pos.y;
m[2][0] = (cx * sy) >> FIXED_SHIFT;
m[2][1] = -sx;
m[2][2] = (cx * cy) >> FIXED_SHIFT;
- m[2][3] = z;
+ m[2][3] = pos.z;
+#else
+ m[0][0] = cy;
+ m[0][1] = 0;
+ m[0][2] = -sy;
+ m[0][3] = pos.x;
- viewPos.x = x;
- viewPos.y = y;
- viewPos.z = z;
+ m[1][0] = (sx * sy) >> FIXED_SHIFT;
+ m[1][1] = cx;
+ m[1][2] = (sx * cy) >> FIXED_SHIFT;
+ m[1][3] = pos.y;
+
+ m[2][0] = (cx * sy) >> FIXED_SHIFT;
+ m[2][1] = -sx;
+ m[2][2] = (cx * cy) >> FIXED_SHIFT;
+ m[2][3] = pos.z;
+#endif
+
+ viewPos = pos;
}
\ No newline at end of file
diff --git a/src/platform/gba/common.h b/src/platform/gba/common.h
index ba242d0..0ab78f8 100644
--- a/src/platform/gba/common.h
+++ b/src/platform/gba/common.h
@@ -3,20 +3,13 @@
//#define TEST
//#define PROFILE
+#define ROTATE90_MODE
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS
#include
#elif defined(__GBA__)
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
+ #include
#elif defined(__TNS__)
#include
#endif
@@ -30,25 +23,24 @@
//#define DEBUG_FACES
#if defined(__TNS__)
- #define WIDTH SCREEN_WIDTH
- #define HEIGHT SCREEN_HEIGHT
- #define FRAME_WIDTH WIDTH
- #define FRAME_HEIGHT HEIGHT
- #define FOV_SHIFT 8
+ #define FRAME_WIDTH SCREEN_WIDTH
+ #define FRAME_HEIGHT SCREEN_HEIGHT
#else
- #define WIDTH 160
- #define HEIGHT 128
- #define FRAME_WIDTH 160
- #define FRAME_HEIGHT 128
- #define FOV_SHIFT 7
+ #ifdef ROTATE90_MODE
+ #define FRAME_WIDTH 160
+ #define FRAME_HEIGHT 120
+ #else
+ #define FRAME_WIDTH 160
+ #define FRAME_HEIGHT 128
+ #endif
#endif
#if defined(_WIN32)
- #define INLINE inline
- #define NOINLINE __declspec(noinline)
+ #define X_INLINE inline
+ #define X_NOINLINE __declspec(noinline)
#elif defined(__GBA__) || defined(__TNS__)
- #define INLINE __attribute__((always_inline)) inline
- #define NOINLINE __attribute__((noinline))
+ #define X_INLINE __attribute__((always_inline)) inline
+ #define X_NOINLINE __attribute__((noinline))
#endif
typedef signed char int8;
@@ -60,12 +52,6 @@ typedef unsigned short uint16;
typedef unsigned int uint32;
typedef int16 Index;
-#define PI 3.14159265358979323846f
-#define PIH (PI * 0.5f)
-#define PI2 (PI * 2.0f)
-#define DEG2RAD (PI / 180.0f)
-#define RAD2DEG (180.0f / PI)
-
#ifdef __GBA__
#define ARM_CODE __attribute__((target("arm")))
#define THUMB_CODE __attribute__((target("thumb")))
@@ -88,10 +74,10 @@ typedef int16 Index;
#if defined(_WIN32)
#define ALIGN4 __declspec(align(4))
-#elif defined(__GBA__)
- #define ALIGN4 __attribute__((aligned(4)))
-#elif defined(__TNS__)
+ #define ALIGN16 __declspec(align(16))
+#elif defined(__GBA__) || defined(__TNS__)
#define ALIGN4 __attribute__((aligned(4)))
+ #define ALIGN16 __attribute__((aligned(16)))
#endif
#if defined(_WIN32)
@@ -100,6 +86,14 @@ typedef int16 Index;
#define ASSERT(x)
#endif
+#if defined(_WIN32)
+ extern uint16 fb[FRAME_WIDTH * FRAME_HEIGHT];
+#elif defined(__GBA__)
+ extern uint32 fb;
+#elif defined(__TNS__)
+ extern uint16 fb[FRAME_WIDTH * FRAME_HEIGHT];
+#endif
+
#ifdef PROFILE
#if defined(_WIN32)
@@ -124,13 +118,13 @@ typedef int16 Index;
#endif
#define PROFILE_START() {\
- REG_TM0CNT_L = 0;\
- REG_TM0CNT_H = (1 << 7) | TIMER_FREQ_DIV;\
+ REG_TM2CNT_L = 0;\
+ REG_TM2CNT_H = (1 << 7) | TIMER_FREQ_DIV;\
}
#define PROFILE_STOP(value) {\
- value += REG_TM0CNT_L;\
- REG_TM0CNT_H = 0;\
+ value += REG_TM2CNT_L;\
+ REG_TM2CNT_H = 0;\
}
#else
@@ -147,34 +141,42 @@ typedef int16 Index;
#endif
enum InputKey {
- IK_UP,
- IK_RIGHT,
- IK_DOWN,
- IK_LEFT,
- IK_A,
- IK_B,
- IK_L,
- IK_R,
- IK_MAX
+ IK_NONE = 0,
+ IK_UP = (1 << 0),
+ IK_RIGHT = (1 << 1),
+ IK_DOWN = (1 << 2),
+ IK_LEFT = (1 << 3),
+ IK_A = (1 << 4),
+ IK_B = (1 << 5),
+ IK_L = (1 << 6),
+ IK_R = (1 << 7),
+ IK_START = (1 << 8),
+ IK_SELECT = (1 << 9),
};
-extern bool keys[IK_MAX];
+struct vec3s {
+ int16 x, y, z;
+
+ vec3s() = default;
+ X_INLINE vec3s(int16 x, int16 y, int16 z) : x(x), y(y), z(z) {}
+};
struct vec3i {
int32 x, y, z;
vec3i() = default;
- INLINE vec3i(int32 x, int32 y, int32 z) : x(x), y(y), z(z) {}
-};
-
-struct vec3s {
- int16 x, y, z;
+ X_INLINE vec3i(int32 x, int32 y, int32 z) : x(x), y(y), z(z) {}
+ X_INLINE vec3i(vec3s &v) : x(v.x), y(v.y), z(v.z) {}
+ X_INLINE vec3i operator + (const vec3i &v) const { return vec3i(x + v.x, y + v.y, z + v.z); }
+ X_INLINE vec3i operator - (const vec3i &v) const { return vec3i(x - v.x, y - v.y, z - v.z); }
+ X_INLINE vec3i& operator += (const vec3i &v) { x += v.x; y += v.y; z += v.z; return *this; }
+ X_INLINE vec3i& operator -= (const vec3i &v) { x -= v.x; y -= v.y; z -= v.z; return *this; }
};
struct vec4i {
int32 x, y, z, w;
- INLINE int32& operator [] (int32 index) const {
+ X_INLINE int32& operator [] (int32 index) const {
ASSERT(index >= 0 && index <= 3);
return ((int32*)this)[index];
}
@@ -192,6 +194,37 @@ struct Triangle {
uint16 flags;
};
+struct Rect {
+ int32 x0;
+ int32 y0;
+ int32 x1;
+ int32 y1;
+};
+
+struct Vertex {
+ int16 x, y, z;
+ uint8 g, clip;
+};
+
+union UV {
+ struct { uint16 v, u; };
+ uint32 uv;
+};
+
+struct VertexUV {
+ Vertex v;
+ UV t;
+ VertexUV *prev;
+ VertexUV *next;
+};
+
+struct Face {
+ Face* next;
+ uint16 flags;
+ int16 start;
+ int8 indices[4];
+};
+
struct Box {
int16 minX;
int16 maxX;
@@ -227,11 +260,10 @@ struct RoomInfo {
int8 ceiling;
};
- struct Light {
- // int32 x, y, z;
- // uint16 intensity;
- // uint32 radius;
- uint8 dummy[18];
+ struct Light { // TODO align struct
+ uint8 pos[12];
+ int16 intensity;
+ uint8 radius[4];
};
struct Mesh {
@@ -262,6 +294,32 @@ struct RoomInfo {
*/
};
+struct Room {
+ Rect clip;
+ uint8 firstItem;
+ bool visible;
+
+ // TODO leave in ROM
+ int32 x, z;
+ uint16 vCount;
+ uint16 qCount;
+ uint16 tCount;
+ uint16 pCount;
+ uint16 lCount;
+ uint16 mCount;
+ uint16 zSectors;
+ uint16 xSectors;
+ uint16 ambient;
+
+ const RoomInfo::Vertex* vertices;
+ const Quad* quads;
+ const Triangle* triangles;
+ const RoomInfo::Portal* portals;
+ const RoomInfo::Sector* sectors;
+ const RoomInfo::Light* lights;
+ const RoomInfo::Mesh* meshes;
+};
+
struct Node {
uint32 flags;
vec3i pos;
@@ -296,7 +354,7 @@ struct ItemInfo { // 24
union {
struct {
- uint16 gravity:1; // TODO
+ uint16 gravity:1, shadow:1; // TODO
};
uint16 value;
} flags;
@@ -306,12 +364,12 @@ struct ItemInfo { // 24
#define FILE_ITEM_SIZE (sizeof(ItemInfo) - 2)
-struct Item : ItemInfo { // 24 + 20 = 44
+struct Item : ItemInfo { // 24 + 28 = 52
int16 angleX;
int16 angleZ;
- uint16 vSpeed;
- uint16 hSpeed;
+ int16 vSpeed;
+ int16 hSpeed;
uint16 animIndex;
uint16 frameIndex;
@@ -319,11 +377,28 @@ struct Item : ItemInfo { // 24 + 20 = 44
uint8 state;
uint8 nextState;
uint8 goalState;
- uint8 timer;
+ uint8 waterState;
uint16 health;
+ uint16 timer;
+
+ int16 moveAngle;
uint8 nextItem;
uint8 nextActive;
+
+ int16 turnSpeed;
+ int16 vSpeedHack;
+};
+
+struct SoundInfo
+{
+ uint16 index;
+ uint16 volume;
+ uint16 chance;
+ union {
+ struct { uint16 mode:2, count:4, unused:6, camera:1, pitch:1, gain:1, :1; };
+ uint16 value;
+ } flags;
};
struct Anim {
@@ -343,14 +418,27 @@ struct Anim {
uint16 nextAnimIndex;
uint16 nextFrameIndex;
- uint16 scCount;
- uint16 scOffset;
+ uint16 statesCount;
+ uint16 statesStart;
- uint16 acCount;
- uint16 animCommand;
+ uint16 commandsCount;
+ uint16 commandsStart;
};
-struct Frame {
+struct AnimState {
+ uint16 state;
+ uint16 rangesCount;
+ uint16 rangesStart;
+};
+
+struct AnimRange {
+ uint16 frameBegin;
+ uint16 frameEnd;
+ uint16 nextAnimIndex;
+ uint16 nextFrameIndex;
+};
+
+struct AnimFrame {
Box box;
vec3s pos;
uint16 angles[1];
@@ -379,52 +467,367 @@ struct SpriteSeq {
int16 sStart;
};
-struct Rect {
- int32 x0;
- int32 y0;
- int32 x1;
- int32 y1;
+union FloorData { // 2 bytes
+ uint16 value;
+
+ union Command {
+ struct {
+ uint16 func:5, tri:3, sub:7, end:1;
+ };
+ struct {
+ int16 :5, a:5, b:5, :1;
+ } triangle;
+ } cmd;
+
+ struct {
+ int8 slantX;
+ int8 slantZ;
+ };
+
+ struct {
+ uint16 a:4, b:4, c:4, d:4;
+ };
+
+ struct TriggerInfo {
+ uint16 timer:8, once:1, mask:5, :2;
+ } triggerInfo;
+
+ union TriggerCommand {
+ struct {
+ uint16 args:10, action:5, end:1;
+ };
+ struct {
+ uint16 timer:8, once:1, speed:5, :2;
+ };
+ } triggerCmd;
};
-struct Vertex {
- int16 x, y, z;
- uint8 g, clip;
+enum FloorType {
+ FLOOR_TYPE_NONE,
+ FLOOR_TYPE_PORTAL,
+ FLOOR_TYPE_FLOOR,
+ FLOOR_TYPE_CEILING,
+ FLOOR_TYPE_TRIGGER,
+ FLOOR_TYPE_LAVA
};
-union UV {
- struct { uint16 v, u; };
- uint32 uv;
+enum TriggerAction {
+ TRIGGER_ACTION_ACTIVATE,
+ TRIGGER_ACTION_CAMERA_SWITCH,
+ TRIGGER_ACTION_FLOW,
+ TRIGGER_ACTION_FLIP,
+ TRIGGER_ACTION_FLIP_ON,
+ TRIGGER_ACTION_FLIP_OFF,
+ TRIGGER_ACTION_CAMERA_TARGET,
+ TRIGGER_ACTION_END,
+ TRIGGER_ACTION_SOUNDTRACK,
+ TRIGGER_ACTION_EFFECT,
+ TRIGGER_ACTION_SECRET,
+ TRIGGER_ACTION_CLEAR_BODIES,
+ TRIGGER_ACTION_FLYBY,
+ TRIGGER_ACTION_CUTSCENE
};
-struct VertexUV {
- Vertex v;
- UV t;
+enum SlantType {
+ SLANT_NONE,
+ SLANT_LOW,
+ SLANT_HIGH
};
-struct Face {
- uint16 flags;
- int16 depth;
- int16 start;
- int8 indices[4];
+enum WaterState {
+ WATER_STATE_ABOVE,
+ WATER_STATE_SURFACE,
+ WATER_STATE_UNDER,
+ WATER_STATE_WADE,
};
+enum AnimCommand {
+ ANIM_CMD_NONE,
+ ANIM_CMD_OFFSET,
+ ANIM_CMD_JUMP,
+ ANIM_CMD_EMPTY,
+ ANIM_CMD_KILL,
+ ANIM_CMD_SOUND,
+ ANIM_CMD_EFFECT,
+};
+
+enum EffectType {
+ FX_NONE = -1,
+ FX_ROTATE_180 ,
+ FX_FLOOR_SHAKE ,
+ FX_LARA_NORMAL ,
+ FX_LARA_BUBBLES ,
+ FX_FINISH_LEVEL ,
+ FX_EARTHQUAKE ,
+ FX_FLOOD ,
+ FX_UNK1 ,
+ FX_STAIRS2SLOPE ,
+ FX_UNK3 ,
+ FX_UNK4 ,
+ FX_EXPLOSION ,
+ FX_LARA_HANDSFREE ,
+ FX_FLIP_MAP ,
+ FX_DRAW_RIGHTGUN ,
+ FX_DRAW_LEFTGUN ,
+ FX_SHOT_RIGHTGUN ,
+ FX_SHOT_LEFTGUN ,
+ FX_MESH_SWAP_1 ,
+ FX_MESH_SWAP_2 ,
+ FX_MESH_SWAP_3 ,
+ FX_INV_ON ,
+ FX_INV_OFF ,
+ FX_DYN_ON ,
+ FX_DYN_OFF ,
+ FX_STATUE_FX ,
+ FX_RESET_HAIR ,
+ FX_BOILER_FX ,
+ FX_ASSAULT_RESET ,
+ FX_ASSAULT_STOP ,
+ FX_ASSAULT_START ,
+ FX_ASSAULT_FINISH ,
+ FX_FOOTPRINT ,
+// specific
+ FX_TR1_FLICKER = 16,
+};
+
+#define ITEM_TYPES(E) \
+ E( LARA ) \
+ E( LARA_PISTOLS ) \
+ E( LARA_SHOTGUN ) \
+ E( LARA_MAGNUMS ) \
+ E( LARA_UZIS ) \
+ E( LARA_SPEC ) \
+ E( DOPPELGANGER ) \
+ E( WOLF ) \
+ E( BEAR ) \
+ E( BAT ) \
+ E( CROCODILE_LAND ) \
+ E( CROCODILE_WATER ) \
+ E( LION_MALE ) \
+ E( LION_FEMALE ) \
+ E( PUMA ) \
+ E( GORILLA ) \
+ E( RAT_LAND ) \
+ E( RAT_WATER ) \
+ E( REX ) \
+ E( RAPTOR ) \
+ E( MUTANT_1 ) \
+ E( MUTANT_2 ) \
+ E( MUTANT_3 ) \
+ E( CENTAUR ) \
+ E( MUMMY ) \
+ E( UNUSED_1 ) \
+ E( UNUSED_2 ) \
+ E( LARSON ) \
+ E( PIERRE ) \
+ E( SKATEBOARD ) \
+ E( SKATEBOY ) \
+ E( COWBOY ) \
+ E( MR_T ) \
+ E( NATLA ) \
+ E( GIANT_MUTANT ) \
+ E( TRAP_FLOOR ) \
+ E( TRAP_SWING_BLADE ) \
+ E( TRAP_SPIKES ) \
+ E( TRAP_BOULDER ) \
+ E( DART ) \
+ E( TRAP_DART_EMITTER ) \
+ E( DRAWBRIDGE ) \
+ E( TRAP_SLAM ) \
+ E( TRAP_SWORD ) \
+ E( HAMMER_HANDLE ) \
+ E( HAMMER_BLOCK ) \
+ E( LIGHTNING ) \
+ E( MOVING_OBJECT ) \
+ E( BLOCK_1 ) \
+ E( BLOCK_2 ) \
+ E( BLOCK_3 ) \
+ E( BLOCK_4 ) \
+ E( MOVING_BLOCK ) \
+ E( TRAP_CEILING_1 ) \
+ E( TRAP_CEILING_2 ) \
+ E( SWITCH ) \
+ E( SWITCH_WATER ) \
+ E( DOOR_1 ) \
+ E( DOOR_2 ) \
+ E( DOOR_3 ) \
+ E( DOOR_4 ) \
+ E( DOOR_5 ) \
+ E( DOOR_6 ) \
+ E( DOOR_7 ) \
+ E( DOOR_8 ) \
+ E( TRAP_DOOR_1 ) \
+ E( TRAP_DOOR_2 ) \
+ E( UNUSED_3 ) \
+ E( BRIDGE_1 ) \
+ E( BRIDGE_2 ) \
+ E( BRIDGE_3 ) \
+ E( INV_PASSPORT ) \
+ E( INV_COMPASS ) \
+ E( INV_HOME ) \
+ E( GEARS_1 ) \
+ E( GEARS_2 ) \
+ E( GEARS_3 ) \
+ E( CUT_1 ) \
+ E( CUT_2 ) \
+ E( CUT_3 ) \
+ E( CUT_4 ) \
+ E( INV_PASSPORT_CLOSED ) \
+ E( INV_MAP ) \
+ E( CRYSTAL ) \
+ E( PISTOLS ) \
+ E( SHOTGUN ) \
+ E( MAGNUMS ) \
+ E( UZIS ) \
+ E( AMMO_PISTOLS ) \
+ E( AMMO_SHOTGUN ) \
+ E( AMMO_MAGNUMS ) \
+ E( AMMO_UZIS ) \
+ E( EXPLOSIVE ) \
+ E( MEDIKIT_SMALL ) \
+ E( MEDIKIT_BIG ) \
+ E( INV_DETAIL ) \
+ E( INV_SOUND ) \
+ E( INV_CONTROLS ) \
+ E( INV_GAMMA ) \
+ E( INV_PISTOLS ) \
+ E( INV_SHOTGUN ) \
+ E( INV_MAGNUMS ) \
+ E( INV_UZIS ) \
+ E( INV_AMMO_PISTOLS ) \
+ E( INV_AMMO_SHOTGUN ) \
+ E( INV_AMMO_MAGNUMS ) \
+ E( INV_AMMO_UZIS ) \
+ E( INV_EXPLOSIVE ) \
+ E( INV_MEDIKIT_SMALL ) \
+ E( INV_MEDIKIT_BIG ) \
+ E( PUZZLE_1 ) \
+ E( PUZZLE_2 ) \
+ E( PUZZLE_3 ) \
+ E( PUZZLE_4 ) \
+ E( INV_PUZZLE_1 ) \
+ E( INV_PUZZLE_2 ) \
+ E( INV_PUZZLE_3 ) \
+ E( INV_PUZZLE_4 ) \
+ E( PUZZLE_HOLE_1 ) \
+ E( PUZZLE_HOLE_2 ) \
+ E( PUZZLE_HOLE_3 ) \
+ E( PUZZLE_HOLE_4 ) \
+ E( PUZZLE_DONE_1 ) \
+ E( PUZZLE_DONE_2 ) \
+ E( PUZZLE_DONE_3 ) \
+ E( PUZZLE_DONE_4 ) \
+ E( LEADBAR ) \
+ E( INV_LEADBAR ) \
+ E( MIDAS_HAND ) \
+ E( KEY_ITEM_1 ) \
+ E( KEY_ITEM_2 ) \
+ E( KEY_ITEM_3 ) \
+ E( KEY_ITEM_4 ) \
+ E( INV_KEY_ITEM_1 ) \
+ E( INV_KEY_ITEM_2 ) \
+ E( INV_KEY_ITEM_3 ) \
+ E( INV_KEY_ITEM_4 ) \
+ E( KEY_HOLE_1 ) \
+ E( KEY_HOLE_2 ) \
+ E( KEY_HOLE_3 ) \
+ E( KEY_HOLE_4 ) \
+ E( UNUSED_4 ) \
+ E( UNUSED_5 ) \
+ E( SCION_PICKUP_QUALOPEC ) \
+ E( SCION_PICKUP_DROP ) \
+ E( SCION_TARGET ) \
+ E( SCION_PICKUP_HOLDER ) \
+ E( SCION_HOLDER ) \
+ E( UNUSED_6 ) \
+ E( UNUSED_7 ) \
+ E( INV_SCION ) \
+ E( EXPLOSION ) \
+ E( UNUSED_8 ) \
+ E( WATER_SPLASH ) \
+ E( UNUSED_9 ) \
+ E( BUBBLE ) \
+ E( UNUSED_10 ) \
+ E( UNUSED_11 ) \
+ E( BLOOD ) \
+ E( UNUSED_12 ) \
+ E( SMOKE ) \
+ E( CENTAUR_STATUE ) \
+ E( CABIN ) \
+ E( MUTANT_EGG_SMALL ) \
+ E( RICOCHET ) \
+ E( SPARKLES ) \
+ E( MUZZLE_FLASH ) \
+ E( UNUSED_13 ) \
+ E( UNUSED_14 ) \
+ E( VIEW_TARGET ) \
+ E( WATERFALL ) \
+ E( NATLA_BULLET ) \
+ E( MUTANT_BULLET ) \
+ E( CENTAUR_BULLET ) \
+ E( UNUSED_16 ) \
+ E( UNUSED_17 ) \
+ E( LAVA_PARTICLE ) \
+ E( TRAP_LAVA_EMITTER ) \
+ E( FLAME ) \
+ E( TRAP_FLAME_EMITTER ) \
+ E( TRAP_LAVA ) \
+ E( MUTANT_EGG_BIG ) \
+ E( BOAT ) \
+ E( EARTHQUAKE ) \
+ E( UNUSED_18 ) \
+ E( UNUSED_19 ) \
+ E( UNUSED_20 ) \
+ E( UNUSED_21 ) \
+ E( UNUSED_22 ) \
+ E( LARA_BRAID ) \
+ E( GLYPHS )
+
+#define DECL_ENUM(v) ITEM_##v,
+
+enum ItemTypes {
+ ITEM_TYPES(DECL_ENUM)
+ ITEM_MAX
+};
+
+#undef DECL_ENUM
+
+extern int32 fps;
+
#ifdef PROFILE
extern uint32 dbg_transform;
extern uint32 dbg_poly;
- extern uint32 dbg_sort;
extern uint32 dbg_flush;
extern uint32 dbg_vert_count;
extern uint32 dbg_poly_count;
#endif
-#define DIV_TABLE_SIZE 256
-#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x])
-#define FixedInvU(x) divTable[x]
-
-extern uint16 divTable[DIV_TABLE_SIZE];
-
#define FIXED_SHIFT 14
+#define SND_MAX_DIST (8 * 1024)
+#define SND_SHIFT 2
+#define SND_CHANNELS (1 << SND_SHIFT)
+#define SND_FIXED_SHIFT 8
+#define SND_VOL_SHIFT 15
+#define SND_PITCH_SHIFT 7
+
+#if defined(_WIN32)
+ #define SND_SAMPLES 1024
+ #define SND_OUTPUT_FREQ 22050
+ #define SND_SAMPLE_FREQ 22050
+ #define SND_ENCODE(x) ((x) + 128)
+ #define SND_DECODE(x) ((x) - 128)
+#elif defined(__GBA__)
+ #define SND_SAMPLES 176
+ #define SND_OUTPUT_FREQ 10512
+ #define SND_SAMPLE_FREQ 22050
+ #define SND_ENCODE(x) (x)
+ #define SND_DECODE(x) ((x) - 128)
+#endif
+
+#define MAX_UPDATE_FRAMES 10
+
+#define FOV_SHIFT 3
#define MAX_MATRICES 8
#define MAX_MODELS 64
#define MAX_ITEMS 256
@@ -441,53 +844,134 @@ extern uint16 divTable[DIV_TABLE_SIZE];
#define FACE_COLORED 0x4000
#define FACE_CLIPPED 0x2000
#define FACE_FLAT 0x1000
-#define FACE_TEXTURE 0x0FFF
+#define FACE_SHADOW 0x0800
+#define FACE_TEXTURE 0x07FF
#define NO_ROOM 0xFF
#define NO_ITEM 0xFF
#define NO_MODEL 0xFF
+#define WALL (-127 * 256)
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#define SQR(x) ((x) * (x))
+#define DEG2SHORT (0x10000 / 360)
+#define ANGLE_0 0
+#define ANGLE_1 DEG2SHORT
+#define ANGLE_45 0x2000 // != 45 * DEG2SHORT !!!
+#define ANGLE_90 0x4000
+#define ANGLE_180 0x8000
+
+#define X_CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)))
+#define X_MIN(a,b) ((a) < (b) ? (a) : (b))
+#define X_MAX(a,b) ((a) > (b) ? (a) : (b))
+#define X_SQR(x) ((x) * (x))
#define DP43(a,b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z + (a).w)
#define DP33(a,b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z)
-#define ITEM_LARA 0
-#define ITEM_WOLF 7
-#define ITEM_BEAR 8
-#define ITEM_BAT 9
-#define ITEM_CRYSTAL 83
+#define DIV_TABLE_SIZE 1024
+#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x])
+#define FixedInvU(x) divTable[x]
+#define OT_SHIFT 4
+#define OT_SIZE ((VIEW_MAX_F >> (FIXED_SHIFT + OT_SHIFT)) + 1)
+
+/*
+#define PERSPECTIVE(x, y, z) {\
+ x = (x / (z >> 7));\
+ y = (y / (z >> 6));\
+}
+*/
+
+#ifdef ROTATE90_MODE
+ #define PERSPECTIVE(x, y, z) {\
+ int32 dz = (z >> (FIXED_SHIFT + FOV_SHIFT - 1)) / 3;\
+ if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\
+ int32 d = FixedInvU(dz);\
+ x = d * (x >> FIXED_SHIFT) >> 12;\
+ y = d * (y >> FIXED_SHIFT) >> 13;\
+ }
+#else
+ #define PERSPECTIVE(x, y, z) {\
+ int32 dz = z >> (FIXED_SHIFT + FOV_SHIFT);\
+ if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\
+ int32 d = FixedInvU(dz);\
+ x = d * (x >> FIXED_SHIFT) >> 12;\
+ y = d * (y >> FIXED_SHIFT) >> 12;\
+ }
+#endif
+
+extern const uint16 divTable[DIV_TABLE_SIZE];
+
+// renderer internal
+extern Rect clip;
extern vec3i viewPos;
extern Matrix matrixStack[MAX_MATRICES];
extern int32 matrixStackIndex;
+extern uint32 gVerticesCount;
+extern uint32 keys;
+
+// level data
+extern const Anim* anims;
+extern const AnimState* animStates;
+extern const AnimRange* animRanges;
+extern const uint16* animFrames;
+
+extern int32 modelsCount;
+extern Model models[MAX_MODELS];
+extern uint8 modelsMap[MAX_ITEMS];
+extern uint8 staticMeshesMap[MAX_MESHES];
+
+template
+X_INLINE void swap(T &a, T &b) {
+ T tmp = a;
+ a = b;
+ b = tmp;
+}
+
+X_INLINE int32 classify(const Vertex* v, const Rect &clip) {
+ return (v->x < clip.x0 ? 1 : 0) |
+ (v->x > clip.x1 ? 2 : 0) |
+ (v->y < clip.y0 ? 4 : 0) |
+ (v->y > clip.y1 ? 8 : 0);
+}
-int32 clamp(int32 x, int32 a, int32 b);
int32 phd_sin(int32 x);
int32 phd_cos(int32 x);
+int32 phd_atan(int32 x, int32 y);
+uint32 phd_sqrt(uint32 x);
-void initLUT();
+X_INLINE int32 dot(const vec3i &a, const vec3i &b)
+{
+ return X_SQR(a.x) + X_SQR(a.y) + X_SQR(a.z);
+}
+
+void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY);
+
+AnimFrame* getFrame(const Item* item, const Model* model);
+const Box& getBoundingBox(const Item* item);
Matrix& matrixGet();
void matrixPush();
void matrixPop();
-void matrixTranslate(int32 x, int32 y, int32 z);
-void matrixTranslateAbs(int32 x, int32 y, int32 z);
+void matrixTranslate(const vec3i &pos);
+void matrixTranslateAbs(const vec3i &pos);
void matrixRotateX(int32 angle);
void matrixRotateY(int32 angle);
void matrixRotateZ(int32 angle);
void matrixRotateYXZ(int32 angleX, int32 angleY, int32 angleZ);
-void matrixFrame(int32 x, int32 y, int32 z, uint16* angles);
-void matrixSetView(int32 x, int32 y, int32 z, int32 angleX, int32 angleY);
+void matrixFrame(const vec3i &pos, uint16* angles);
+void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY);
void drawGlyph(const Sprite *sprite, int32 x, int32 y);
void clear();
+bool rectIsVisible(const Rect* rect);
bool boxIsVisible(const Box* box);
+void transform(const vec3s &v, int32 vg);
+bool transformBoxRect(const Box* box, Rect* rect);
void transformRoom(const RoomInfo::Vertex* vertices, int32 vCount);
void transformMesh(const vec3s* vertices, int32 vCount, uint16 intensity);
+void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex);
+void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex);
void faceAddRoom(const Quad* quads, int32 qCount, const Triangle* triangles, int32 tCount, int32 startVertex);
void faceAddMesh(const Quad* rFaces, const Quad* crFaces, const Triangle* tFaces, const Triangle* ctFaces, int32 rCount, int32 crCount, int32 tCount, int32 ctCount, int32 startVertex);
@@ -495,6 +979,26 @@ void flush();
void readLevel(const uint8 *data);
const RoomInfo::Sector* getSector(int32 roomIndex, int32 x, int32 z);
-int32 getRoomIndex(int32 roomIndex, const vec3i* pos);
+int32 getRoomIndex(int32 roomIndex, int32 x, int32 y, int32 z);
+
+X_INLINE void dmaFill(void *dst, uint8 value, uint32 count)
+{
+ ASSERT((count & 3) == 0);
+#ifdef __GBA__
+ vu32 v = value;
+ dma3_fill(dst, v, count);
+#else
+ memset(dst, value, count);
+#endif
+}
+
+X_INLINE int16 xRand()
+{
+#ifdef __GBA__
+ return qran();
+#else
+ return rand();
+#endif
+}
#endif
diff --git a/src/platform/gba/deploy.sh b/src/platform/gba/deploy.sh
index 0f21462..3b29030 100644
--- a/src/platform/gba/deploy.sh
+++ b/src/platform/gba/deploy.sh
@@ -1,2 +1,2 @@
make
-./NO\$GBA.exe C:\\Projects\\OpenLara\\src\\platform\\gba\\OpenLara.elf
+./mGBA.exe C:\\Projects\\OpenLara\\src\\platform\\gba\\OpenLara.gba
diff --git a/src/platform/gba/draw.h b/src/platform/gba/draw.h
new file mode 100644
index 0000000..452ae5e
--- /dev/null
+++ b/src/platform/gba/draw.h
@@ -0,0 +1,474 @@
+#ifndef H_DRAW
+#define H_DRAW
+
+#include "common.h"
+#include "item.h"
+
+int32 seqGlyphs;
+
+void drawNumber(int32 number, int32 x, int32 y)
+{
+ const int32 widths[] = {
+ 12, 8, 10, 10, 10, 10, 10, 10, 10, 10
+ };
+
+ const Sprite *glyphSprites = sprites + spritesSeq[seqGlyphs].sStart;
+
+ while (number > 0)
+ {
+ x -= widths[number % 10];
+ drawGlyph(glyphSprites + 52 + (number % 10), x, y);
+ number /= 10;
+ }
+}
+
+void drawMesh(int16 meshIndex, uint16 intensity)
+{
+ int32 offset = meshOffsets[meshIndex];
+ const uint8* ptr = meshData + offset;
+
+ ptr += 2 * 5; // skip [cx, cy, cz, radius, flags]
+
+ int16 vCount = *(int16*)ptr; ptr += 2;
+ const vec3s* vertices = (vec3s*)ptr;
+ ptr += vCount * 3 * sizeof(int16);
+
+ int16 nCount = *(int16*)ptr; ptr += 2;
+ //const int16* normals = (int16*)ptr;
+ if (nCount > 0) { // normals
+ ptr += nCount * 3 * sizeof(int16);
+ } else { // intensity
+ ptr += vCount * sizeof(int16);
+ }
+
+ int16 rCount = *(int16*)ptr; ptr += 2;
+ Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad);
+
+ int16 tCount = *(int16*)ptr; ptr += 2;
+ Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle);
+
+ int16 crCount = *(int16*)ptr; ptr += 2;
+ Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad);
+
+ int16 ctCount = *(int16*)ptr; ptr += 2;
+ Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle);
+
+ int32 startVertex = gVerticesCount;
+
+ PROFILE_START();
+ transformMesh(vertices, vCount, intensity);
+ PROFILE_STOP(dbg_transform);
+
+ PROFILE_START();
+ faceAddMesh(rFaces, crFaces, tFaces, ctFaces, rCount, crCount, tCount, ctCount, startVertex);
+ PROFILE_STOP(dbg_poly);
+}
+
+void drawShadow(const Item* item)
+{
+ const RoomInfo::Sector* sector = getSector(item->room, item->pos.x, item->pos.z);
+ int32 floor = getFloor(sector, item->pos.x, item->pos.y, item->pos.z);
+
+ if (floor == WALL)
+ return;
+
+ int32 shadowSize = 160; // TODO per item
+
+ const Box& box = getBoundingBox(item);
+ int32 x = (box.maxX + box.minX) >> 1;
+ int32 z = (box.maxZ + box.minZ) >> 1;
+ int32 sx = (box.maxX - box.minX) * shadowSize >> 10;
+ int32 sz = (box.maxZ - box.minZ) * shadowSize >> 10;
+ int32 sx2 = sx << 1;
+ int32 sz2 = sz << 1;
+
+ int32 startVertex = gVerticesCount;
+
+ int32 y = floor - item->pos.y;
+
+ transform(vec3s(x - sx, y, z + sz2), 4096);
+ transform(vec3s(x + sx, y, z + sz2), 4096);
+ transform(vec3s(x + sx2, y, z + sz), 4096);
+ transform(vec3s(x + sx2, y, z - sz), 4096);
+
+ transform(vec3s(x + sx, y, z - sz2), 4096);
+ transform(vec3s(x - sx, y, z - sz2), 4096);
+ transform(vec3s(x - sx2, y, z - sz), 4096);
+ transform(vec3s(x - sx2, y, z + sz), 4096);
+
+ static const Index indices[] = {
+ 0, 1, 2, 7,
+ 7, 2, 3, 6,
+ 6, 3, 4, 5
+ };
+
+ faceAddQuad(FACE_COLORED | FACE_FLAT | FACE_SHADOW, indices + 0, startVertex);
+ faceAddQuad(FACE_COLORED | FACE_FLAT | FACE_SHADOW, indices + 4, startVertex);
+ faceAddQuad(FACE_COLORED | FACE_FLAT | FACE_SHADOW, indices + 8, startVertex);
+}
+
+void drawItem(const Item* item)
+{
+ int32 modelIndex = modelsMap[item->type];
+ if (modelIndex == NO_MODEL) {
+ return; // TODO sprite items
+ }
+
+ const Model* model = models + modelIndex;
+
+ if (model->mCount == 1 && meshOffsets[model->mStart] == 0)
+ return;
+
+ AnimFrame* frame = getFrame(item, model);
+ uint16* frameAngles = frame->angles + 1;
+
+ matrixPush();
+ matrixTranslateAbs(item->pos);
+ matrixRotateYXZ(item->angleX, item->angleY, item->angleZ);
+
+ int32 intensity = item->intensity;
+
+ if (intensity == 0xFFFF) {
+ intensity = itemCalcLighting(item, frame->box);
+ }
+
+ bool isVisible = boxIsVisible(&frame->box);
+ if (isVisible)
+ {
+ // non-aligned access (TODO)
+ uint32 nodeIndex;
+ memcpy(&nodeIndex, &model->nodeIndex, sizeof(nodeIndex));
+ Node nodes[32];
+ memcpy(nodes, nodesPtr + nodeIndex, (model->mCount - 1) * sizeof(Node));
+
+ const Node* node = nodes;
+
+ matrixFrame(frame->pos, frameAngles);
+
+ drawMesh(model->mStart, intensity);
+
+ for (int32 i = 1; i < model->mCount; i++)
+ {
+ if (node->flags & 1) matrixPop();
+ if (node->flags & 2) matrixPush();
+
+ frameAngles += 2;
+ matrixFrame(node->pos, frameAngles);
+
+ drawMesh(model->mStart + i, intensity);
+
+ node++;
+ }
+ }
+
+ matrixPop();
+
+// shadow
+ if (isVisible & item->flags.shadow) {
+ matrixPush();
+ matrixTranslateAbs(item->pos);
+ matrixRotateY(item->angleY);
+
+ drawShadow(item);
+
+ matrixPop();
+ }
+}
+
+void drawRoom(const Room* room)
+{
+ clip = room->clip;
+
+ int32 startVertex = gVerticesCount;
+
+ matrixPush();
+ matrixTranslateAbs(vec3i(room->x, 0, room->z));
+
+ PROFILE_START();
+ transformRoom(room->vertices, room->vCount);
+ PROFILE_STOP(dbg_transform);
+
+ matrixPop();
+
+ PROFILE_START();
+ faceAddRoom(room->quads, room->qCount, room->triangles, room->tCount, startVertex);
+
+ for (int32 i = 0; i < room->mCount; i++)
+ {
+ const RoomInfo::Mesh* mesh = room->meshes + i;
+ const StaticMesh* staticMesh = staticMeshes + staticMeshesMap[mesh->staticMeshId];
+
+ if (!(staticMesh->flags & 2)) continue; // invisible
+
+ // TODO align RoomInfo::Mesh (room relative int16?)
+ vec3i pos;
+ memcpy(&pos, &mesh->pos, sizeof(pos));
+
+ matrixPush();
+ matrixTranslateAbs(pos);
+ matrixRotateY(mesh->rotation);
+
+ if (boxIsVisible(&staticMesh->vbox)) {
+ drawMesh(staticMesh->meshIndex, mesh->intensity);
+ }
+
+ matrixPop();
+ }
+
+ int32 itemIndex = room->firstItem;
+ while (itemIndex != NO_ITEM)
+ {
+ drawItem(items + itemIndex);
+ itemIndex = items[itemIndex].nextItem;
+ }
+ PROFILE_STOP(dbg_poly);
+
+ flush();
+}
+
+bool checkPortal(int32 roomIndex, const RoomInfo::Portal* portal)
+{
+ Room &room = rooms[roomIndex];
+
+ vec3i d;
+ d.x = portal->v[0].x - camera.viewPos.x + room.x;
+ d.y = portal->v[0].y - camera.viewPos.y;
+ d.z = portal->v[0].z - camera.viewPos.z + room.z;
+
+ if (DP33(portal->n, d) >= 0) {
+ return false;
+ }
+
+ int32 x0 = room.clip.x1;
+ int32 y0 = room.clip.y1;
+ int32 x1 = room.clip.x0;
+ int32 y1 = room.clip.y0;
+
+ int32 znear = 0, zfar = 0;
+
+ Matrix &m = matrixGet();
+
+ vec3i pv[4];
+
+ for (int32 i = 0; i < 4; i++)
+ {
+ const vec3s &v = portal->v[i];
+
+ int32 x = DP43(m[0], v);
+ int32 y = DP43(m[1], v);
+ int32 z = DP43(m[2], v);
+
+ pv[i].x = x;
+ pv[i].y = y;
+ pv[i].z = z;
+
+ if (z <= VIEW_MIN_F) {
+ znear++;
+ continue;
+ }
+
+ if (z >= VIEW_MAX_F) {
+ zfar++;
+ }
+
+ if (z != 0) {
+ PERSPECTIVE(x, y, z);
+
+ x += FRAME_WIDTH >> 1;
+ y += FRAME_HEIGHT >> 1;
+ } else {
+ x = (x > 0) ? clip.x1 : clip.x0;
+ y = (y > 0) ? clip.y1 : clip.y0;
+ }
+
+ if (x < x0) x0 = x;
+ if (x > x1) x1 = x;
+ if (y < y0) y0 = y;
+ if (y > y1) y1 = y;
+ }
+
+ if (znear == 4 || zfar == 4) return false;
+
+ if (znear) {
+ vec3i *a = pv;
+ vec3i *b = pv + 3;
+ for (int32 i = 0; i < 4; i++) {
+ if ((a->z < 0) ^ (b->z < 0)) {
+ if (a->x < 0 && b->x < 0) {
+ x0 = 0;
+ } else if (a->x > 0 && b->x > 0) {
+ x1 = FRAME_WIDTH;
+ } else {
+ x0 = 0;
+ x1 = FRAME_WIDTH;
+ }
+
+ if (a->y < 0 && b->y < 0) {
+ y0 = 0;
+ } else if (a->y > 0 && b->y > 0) {
+ y1 = FRAME_HEIGHT;
+ } else {
+ y0 = 0;
+ y1 = FRAME_HEIGHT;
+ }
+ }
+ b = a;
+ a++;
+ }
+ }
+
+ if (x0 < room.clip.x0) x0 = room.clip.x0;
+ if (x1 > room.clip.x1) x1 = room.clip.x1;
+ if (y0 < room.clip.y0) y0 = room.clip.y0;
+ if (y1 > room.clip.y1) y1 = room.clip.y1;
+
+ if (x0 >= x1 || y0 >= y1) return false;
+
+ Room &nextRoom = rooms[portal->roomIndex];
+
+ if (x0 < nextRoom.clip.x0) nextRoom.clip.x0 = x0;
+ if (x1 > nextRoom.clip.x1) nextRoom.clip.x1 = x1;
+ if (y0 < nextRoom.clip.y0) nextRoom.clip.y0 = y0;
+ if (y1 > nextRoom.clip.y1) nextRoom.clip.y1 = y1;
+
+ if (!nextRoom.visible) {
+ nextRoom.visible = true;
+ visRooms[visRoomsCount++] = portal->roomIndex;
+ }
+
+ return true;
+}
+
+void getVisibleRooms(int32 roomIndex)
+{
+ const Room* room = rooms + roomIndex;
+
+ matrixPush();
+ matrixTranslateAbs(vec3i(room->x, 0, room->z));
+
+ for (int32 i = 0; i < room->pCount; i++)
+ {
+ const RoomInfo::Portal* portal = room->portals + i;
+
+ if (checkPortal(roomIndex, portal))
+ {
+ getVisibleRooms(portal->roomIndex);
+ }
+ }
+
+ matrixPop();
+}
+
+void roomReset(Room* room)
+{
+ room->visible = false;
+ room->clip = { FRAME_WIDTH, FRAME_HEIGHT, 0, 0 };
+}
+
+void drawRooms()
+{
+ rooms[camera.room].clip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT };
+ visRoomsCount = 0;
+ visRooms[visRoomsCount++] = camera.room;
+
+ getVisibleRooms(camera.room);
+
+ while (visRoomsCount--)
+ {
+ Room* room = rooms + visRooms[visRoomsCount];
+ drawRoom(room);
+ roomReset(room);
+ }
+}
+
+#ifdef TEST
+void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex);
+
+extern Vertex gVertices[MAX_VERTICES];
+
+void drawTest() {
+ static Rect testClip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT };
+ static int32 testTile = 10; // 707 // 712
+
+#ifdef _WIN32
+ Sleep(16);
+#endif
+
+ int dx = 0;
+ int dy = 0;
+
+ if (keys & IK_LEFT) dy++;
+ if (keys & IK_RIGHT) dy--;
+ if (keys & IK_UP) dx--;
+ if (keys & IK_DOWN) dx++;
+
+ if (keys & IK_L) {
+ testClip.x0 += dx;
+ testClip.y0 += dy;
+ }
+
+ if (keys & IK_R) {
+ testClip.x1 += dx;
+ testClip.y1 += dy;
+ }
+
+ if (keys & IK_A) {
+ testTile++;
+ //Sleep(100);
+ }
+
+ if (keys & IK_B) {
+ testTile--;
+ //Sleep(100);
+ }
+
+ testTile = (testTile + texturesCount) % texturesCount;
+
+ clip = testClip;
+
+ static int vidx = 0;
+
+ if (keys & IK_SELECT) {
+ vidx++;
+ //Sleep(100);
+ }
+
+ gVertices[(vidx + 0) % 4].x = -25;
+ gVertices[(vidx + 0) % 4].y = -25;
+
+ gVertices[(vidx + 1) % 4].x = 25;
+ gVertices[(vidx + 1) % 4].y = -25;
+
+ gVertices[(vidx + 2) % 4].x = 50;
+ gVertices[(vidx + 2) % 4].y = 25;
+
+ gVertices[(vidx + 3) % 4].x = -50;
+ gVertices[(vidx + 3) % 4].y = 25;
+
+ for (int i = 0; i < 4; i++) {
+ gVertices[i].x += FRAME_WIDTH/2;
+ gVertices[i].y += FRAME_HEIGHT/2;
+ gVertices[i].z = 100;
+ gVertices[i].g = 16;
+ gVertices[i].clip = classify(gVertices + i, clip);
+ }
+ gVerticesCount = 4;
+
+ Index indices[] = { 0, 1, 2, 3, 0, 2, 3 };
+
+ faceAddQuad(testTile, indices, 0);
+
+#ifdef _WIN32
+ for (int y = 0; y < FRAME_HEIGHT; y++) {
+ for (int x = 0; x < FRAME_WIDTH; x++) {
+ if (x == clip.x0 || x == clip.x1 - 1 || y == clip.y0 || y == clip.y1 - 1)
+ ((uint16*)fb)[y * FRAME_WIDTH + x] = 255;
+ }
+ }
+#endif
+
+ flush();
+}
+#endif
+
+#endif
diff --git a/src/platform/gba/enemy.h b/src/platform/gba/enemy.h
new file mode 100644
index 0000000..9c2263d
--- /dev/null
+++ b/src/platform/gba/enemy.h
@@ -0,0 +1,19 @@
+#ifndef H_ENEMY
+#define H_ENEMY
+
+struct Bat
+{
+
+};
+
+struct Wolf
+{
+
+};
+
+struct Bear
+{
+
+};
+
+#endif
diff --git a/src/platform/gba/game.h b/src/platform/gba/game.h
new file mode 100644
index 0000000..80e5c48
--- /dev/null
+++ b/src/platform/gba/game.h
@@ -0,0 +1,160 @@
+#ifndef H_GAME
+#define H_GAME
+
+#include "common.h"
+#include "sound.h"
+#include "level.h"
+#include "camera.h"
+#include "item.h"
+#include "lara.h"
+#include "draw.h"
+
+struct Game
+{
+ void init()
+ {
+ loadLevel(LEVEL1_PHD);
+ }
+
+ void loadLevel(const void* data)
+ {
+ camera.init();
+
+ readLevel((uint8*)LEVEL1_PHD);
+
+ // prepare rooms
+ for (int32 i = 0; i < roomsCount; i++)
+ {
+ roomReset(rooms + i);
+ }
+
+ // prepare items
+ for (int32 i = 0; i < itemsCount; i++)
+ {
+ Item* item = items + i;
+
+ itemInit(item);
+
+ if (item->room > -1) {
+ roomItemAdd(item->room, i);
+ }
+
+ if (item->type == ITEM_LARA) {
+ camera.item = item;
+
+ // debug
+ //resetItem(i, 14, vec3i(20215, 6656, 52942), 0); // level 1 (bridge)
+ //resetItem(i, 26, vec3i(24475, 6912, 83505), 90 * DEG2SHORT); // level 1 (switch timer)
+
+ camera.room = item->room;
+ }
+
+ // TODO remove
+ if (item->type == ITEM_LARA ||
+ item->type == ITEM_WOLF ||
+ item->type == ITEM_BEAR ||
+ item->type == ITEM_BAT ||
+ item->type == ITEM_CRYSTAL)
+ {
+ activateItem(i);
+ }
+ }
+
+ // prepare glyphs
+ for (int32 i = 0; i < spritesSeqCount; i++) {
+ if (spritesSeq[i].type == ITEM_GLYPHS) {
+ seqGlyphs = i;
+ break;
+ }
+ }
+ }
+
+ void resetItem(int32 itemIndex, int32 roomIndex, const vec3i &pos, int32 angleY)
+ {
+ roomItemRemove(itemIndex);
+
+ Item* item = items + itemIndex;
+ item->pos = vec3i(20215, 6656, 52942);
+ item->angleY = angleY;
+ item->health = LARA_MAX_HEALTH;
+
+ roomItemAdd(roomIndex, itemIndex);
+ }
+
+ void updateItems()
+ {
+ curItemIndex = firstActive;
+ while (curItemIndex != NO_ITEM)
+ {
+ Item* item = items + curItemIndex;
+
+ if (item->type == ITEM_LARA) {
+ Lara* lara = (Lara*)item;
+ lara->update();
+ } else {
+ itemControl(item);
+ }
+
+ curItemIndex = item->nextActive;
+ }
+ }
+
+ void update(int32 frames)
+ {
+ if (frames > MAX_UPDATE_FRAMES) {
+ frames = MAX_UPDATE_FRAMES;
+ }
+
+ for (int32 i = 0; i < frames; i++) {
+ updateItems();
+ camera.update();
+ }
+ }
+
+#ifdef ROTATE90_MODE
+ int32 TEXT_POSX = FRAME_HEIGHT;
+#else
+ int32 TEXT_POSX = FRAME_WIDTH;
+#endif
+
+ void render()
+ {
+ clear();
+
+ #ifdef TEST
+ #ifdef __GBA__
+ VBlankIntrWait();
+ #endif
+
+ int32 cycles = 0;
+ PROFILE_START();
+ drawTest();
+ PROFILE_STOP(cycles);
+
+ drawNumber(cycles, TEXT_POSX, 32);
+ #else
+ #ifdef PROFILE
+ dbg_transform = 0;
+ dbg_poly = 0;
+ dbg_flush = 0;
+ dbg_vert_count = 0;
+ dbg_poly_count = 0;
+ #endif
+
+ drawRooms();
+
+ #ifdef PROFILE
+ drawNumber(dbg_transform, TEXT_POSX, 32);
+ drawNumber(dbg_poly, TEXT_POSX, 48);
+ drawNumber(dbg_flush, TEXT_POSX, 64);
+ drawNumber(dbg_vert_count, TEXT_POSX, 84);
+ drawNumber(dbg_poly_count, TEXT_POSX, 100);
+ #endif
+
+ #endif
+
+ drawNumber(fps, TEXT_POSX, 16);
+ }
+};
+
+#endif
diff --git a/src/platform/gba/item.h b/src/platform/gba/item.h
new file mode 100644
index 0000000..9d447d2
--- /dev/null
+++ b/src/platform/gba/item.h
@@ -0,0 +1,384 @@
+#ifndef H_ITEM
+#define H_ITEM
+
+#include "common.h"
+#include "sound.h"
+#include "camera.h"
+
+int32 curItemIndex;
+
+#define GRAVITY 6
+
+int16 angleDec(int16 angle, int32 value) {
+ if (angle < -value) {
+ return angle + value;
+ } else if (angle > value) {
+ return angle - value;
+ }
+ return 0;
+}
+
+AnimFrame* getFrame(const Item* item, const Model* model)
+{
+ const Anim* anim = anims + item->animIndex;
+
+ int32 frameSize = sizeof(AnimFrame) / 2 + model->mCount * 2;
+ int32 frameIndex = (item->frameIndex - anim->frameBegin) / anim->frameRate; // TODO fixed div? check the range
+
+ return (AnimFrame*)(animFrames + anim->frameOffset / 2 + frameIndex * frameSize);
+}
+
+const Box& getBoundingBox(const Item* item)
+{
+ const Model* model = models + modelsMap[item->type];
+ AnimFrame* frame = getFrame(item, model);
+ return frame->box;
+}
+
+Sound::Sample* soundPlay(int16 id, const vec3i &pos)
+{
+ int16 a = soundMap[id];
+
+ if (a == -1) {
+ return NULL;
+ }
+
+ const SoundInfo* b = soundInfos + a;
+
+ if (b->chance && b->chance < xRand()) {
+ return NULL;
+ }
+
+ vec3i d = pos - camera.targetPos;
+
+ if (abs(d.x) >= SND_MAX_DIST || abs(d.y) >= SND_MAX_DIST || abs(d.z) >= SND_MAX_DIST) {
+ return NULL;
+ }
+
+ int32 volume = b->volume - (phd_sqrt(dot(d, d)) << 2);
+
+ if (b->flags.gain) {
+ volume -= xRand() >> 2;
+ }
+
+ if (volume <= 0) {
+ return NULL;
+ }
+
+ volume = X_MIN(volume, 0x7FFF);
+
+ int32 pitch = 128;
+
+ if (b->flags.pitch) {
+ pitch += ((xRand() * 13) >> 14) - 13;
+ }
+
+ int32 index = b->index;
+ if (b->flags.count > 1) {
+ index += (xRand() * b->flags.count) >> 15;
+ }
+
+ const uint8 *data = soundData + soundOffsets[index];
+
+ int32 size;
+ memcpy(&size, data + 40, 4); // TODO preprocess and remove wave header
+ data += 44;
+
+ return sound.play(data, size, volume, pitch, b->flags.mode);
+}
+
+void move(Item* item, const Anim* anim)
+{
+ int32 speed = anim->speed;
+
+ if (item->flags.gravity)
+ {
+ speed += anim->accel * (item->frameIndex - anim->frameBegin - 1);
+ item->hSpeed -= speed >> 16;
+ speed += anim->accel;
+ item->hSpeed += speed >> 16;
+
+ item->vSpeed += (item->vSpeed < 128) ? GRAVITY : 1;
+
+ item->pos.y += item->vSpeed;
+ } else {
+ speed += anim->accel * (item->frameIndex - anim->frameBegin);
+
+ item->hSpeed = speed >> 16;
+ }
+
+ item->pos.x += phd_sin(item->moveAngle) * item->hSpeed >> FIXED_SHIFT;
+ item->pos.z += phd_cos(item->moveAngle) * item->hSpeed >> FIXED_SHIFT;
+}
+
+const Anim* animSet(Item* item, int32 animIndex, bool resetState, int32 frameOffset = 0) {
+ const Anim* anim = anims + animIndex;
+
+ item->animIndex = animIndex;
+ item->frameIndex = anim->frameBegin + frameOffset;
+
+ if (resetState) {
+ item->state = item->goalState = anim->state;
+ }
+
+ return anim;
+}
+
+const Anim* animChange(Item* item, const Anim* anim)
+{
+ if (!anim->statesCount || item->goalState == item->state)
+ return anim;
+
+ const AnimState* animState = animStates + anim->statesStart;
+
+ for (int32 i = 0; i < anim->statesCount; i++)
+ {
+ if (item->goalState == animState->state)
+ {
+ const AnimRange* animRange = animRanges + animState->rangesStart;
+
+ for (int32 j = 0; j < animState->rangesCount; j++)
+ {
+ if ((item->frameIndex >= animRange->frameBegin) && (item->frameIndex <= animRange->frameEnd))
+ {
+ if ((item->type != ITEM_LARA) && (item->nextState == animState->state)) {
+ item->nextState = 0;
+ }
+
+ item->frameIndex = animRange->nextFrameIndex;
+ item->animIndex = animRange->nextAnimIndex;
+ anim = anims + animRange->nextAnimIndex;
+ item->state = anim->state;
+ return anim;
+ }
+ animRange++;
+ }
+ }
+ animState++;
+ }
+
+ return anim;
+}
+
+void animCmd(bool fx, Item* item, const Anim* anim)
+{
+ if (!anim->commandsCount) return;
+
+ const int16 *ptr = animCommands + anim->commandsStart;
+
+ for (int32 i = 0; i < anim->commandsCount; i++)
+ {
+ int32 cmd = *ptr++;
+
+ switch (cmd) {
+ case ANIM_CMD_NONE:
+ break;
+ case ANIM_CMD_OFFSET:
+ {
+ if (!fx)
+ {
+ int32 s = phd_sin(item->moveAngle);
+ int32 c = phd_cos(item->moveAngle);
+ int32 x = ptr[0];
+ int32 y = ptr[1];
+ int32 z = ptr[2];
+ item->pos.x += (c * x + s * z) >> FIXED_SHIFT;
+ item->pos.y += y;
+ item->pos.z += (c * z - s * x) >> FIXED_SHIFT;
+ }
+ ptr += 3;
+ break;
+ }
+ case ANIM_CMD_JUMP:
+ if (!fx)
+ {
+ if (item->vSpeedHack) {
+ item->vSpeed = item->vSpeedHack;
+ item->vSpeedHack = 0;
+ } else {
+ item->vSpeed = ptr[0];
+ }
+ item->hSpeed = ptr[1];
+ item->flags.gravity = true;
+ }
+ ptr += 2;
+ break;
+ case ANIM_CMD_EMPTY:
+ break;
+ case ANIM_CMD_KILL:
+ break;
+ case ANIM_CMD_SOUND:
+ if (fx && item->frameIndex == ptr[0])
+ {
+ soundPlay(ptr[1] & 0x03FFF, item->pos);
+ }
+ ptr += 2;
+ break;
+ case ANIM_CMD_EFFECT:
+ if (fx && item->frameIndex == ptr[0]) {
+ switch (ptr[1]) {
+ case FX_ROTATE_180 : item->angleY -= ANGLE_180; break;
+ /*
+ case FX_FLOOR_SHAKE : ASSERT(false);
+ case FX_LARA_NORMAL : animation.setAnim(ANIM_STAND); break;
+ case FX_LARA_BUBBLES : doBubbles(); break;
+ case FX_LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break;
+ case FX_DRAW_RIGHTGUN : drawGun(true); break;
+ case FX_DRAW_LEFTGUN : drawGun(false); break;
+ case FX_SHOT_RIGHTGUN : game->addMuzzleFlash(this, LARA_RGUN_JOINT, LARA_RGUN_OFFSET, 1 + camera->cameraIndex); break;
+ case FX_SHOT_LEFTGUN : game->addMuzzleFlash(this, LARA_LGUN_JOINT, LARA_LGUN_OFFSET, 1 + camera->cameraIndex); break;
+ case FX_MESH_SWAP_1 :
+ case FX_MESH_SWAP_2 :
+ case FX_MESH_SWAP_3 : Character::cmdEffect(fx);
+ case 26 : break; // TODO TR2 reset_hair
+ case 32 : break; // TODO TR3 footprint
+ default : LOG("unknown effect command %d (anim %d)\n", fx, animation.index); ASSERT(false);
+ */
+ default : ;
+ }
+ }
+ ptr += 2;
+ break;
+ }
+ }
+}
+
+void animUpdate(Item* item)
+{
+ if (modelsMap[item->type] == NO_MODEL)
+ return;
+
+ const Anim* anim = anims + item->animIndex;
+
+ item->frameIndex++;
+
+ anim = animChange(item, anim);
+
+ if (item->frameIndex > anim->frameEnd)
+ {
+ animCmd(false, item, anim);
+
+ item->frameIndex = anim->nextFrameIndex;
+ item->animIndex = anim->nextAnimIndex;
+ anim = anims + anim->nextAnimIndex;
+ item->state = anim->state;
+ }
+
+ animCmd(true, item, anim);
+
+ move(item, anim);
+}
+
+int32 calcLight(const vec3i &pos, int32 roomIndex)
+{
+ Room* room = rooms + roomIndex;
+
+ if (!room->lCount) {
+ return room->ambient;
+ }
+
+ int32 ambient = 8191 - room->ambient;
+ int32 intensity = 0;
+
+ for (int i = 0; i < room->lCount; i++)
+ {
+ const RoomInfo::Light* light = room->lights + i;
+
+ // TODO preprocess align
+ vec3i lpos;
+ int32 lradius;
+ memcpy(&lpos, &light->pos[0], sizeof(lpos));
+ memcpy(&lradius, &light->radius[0], sizeof(lradius));
+
+ vec3i d = pos - lpos;
+ int32 dist = dot(d, d) >> 12;
+ int32 att = X_SQR(lradius) >> 12;
+
+ int32 lum = (light->intensity * att) / (dist + att) + ambient;
+
+ if (lum > intensity)
+ {
+ intensity = lum;
+ }
+ }
+
+ return 8191 - ((intensity + ambient) >> 1);
+}
+
+int32 itemCalcLighting(const Item* item, const Box& box)
+{
+ matrixPush();
+ Matrix &m = matrixGet();
+ m[0][3] = m[1][3] = m[2][3] = 0;
+ matrixRotateYXZ(item->angleX, item->angleY, item->angleZ);
+
+ vec3i v((box.maxX + box.minX) >> 1,
+ (box.maxY + box.minY) >> 1,
+ (box.maxZ + box.minZ) >> 1);
+
+ matrixTranslate(v);
+
+ v = vec3i(m[0][3] >> FIXED_SHIFT,
+ m[1][3] >> FIXED_SHIFT,
+ m[2][3] >> FIXED_SHIFT) + item->pos;
+ matrixPop();
+
+ return calcLight(v, item->room);
+}
+
+void itemControl(Item* item)
+{
+ animUpdate(item);
+}
+
+void itemInit(Item* item)
+{
+ item->angleX = 0;
+ item->angleZ = 0;
+ item->vSpeed = 0;
+ item->hSpeed = 0;
+ item->nextItem = NO_ITEM;
+ item->nextActive = NO_ITEM;
+ item->animIndex = models[modelsMap[item->type]].animIndex;
+ item->frameIndex = anims[item->animIndex].frameBegin;
+ item->state = anims[item->animIndex].state;
+ item->nextState = item->state;
+ item->goalState = item->state;
+ item->flags.gravity = false;
+ item->flags.shadow = true;
+
+ switch (item->type) {
+ case ITEM_LARA : item->health = 1000; break;
+ case ITEM_DOPPELGANGER : item->health = 1000; break;
+ case ITEM_WOLF : item->health = 6; break;
+ case ITEM_BEAR : item->health = 20; break;
+ case ITEM_BAT : item->health = 1; break;
+ case ITEM_CROCODILE_LAND : item->health = 20; break;
+ case ITEM_CROCODILE_WATER : item->health = 20; break;
+ case ITEM_LION_MALE : item->health = 30; break;
+ case ITEM_LION_FEMALE : item->health = 25; break;
+ case ITEM_PUMA : item->health = 45; break;
+ case ITEM_GORILLA : item->health = 22; break;
+ case ITEM_RAT_LAND : item->health = 5; break;
+ case ITEM_RAT_WATER : item->health = 5; break;
+ case ITEM_REX : item->health = 100; break;
+ case ITEM_RAPTOR : item->health = 20; break;
+ case ITEM_MUTANT_1 : item->health = 50; break;
+ case ITEM_MUTANT_2 : item->health = 50; break;
+ case ITEM_MUTANT_3 : item->health = 50; break;
+ case ITEM_CENTAUR : item->health = 120; break;
+ case ITEM_MUMMY : item->health = 18; break;
+ case ITEM_LARSON : item->health = 50; break;
+ case ITEM_PIERRE : item->health = 70; break;
+ case ITEM_SKATEBOY : item->health = 125; break;
+ case ITEM_COWBOY : item->health = 150; break;
+ case ITEM_MR_T : item->health = 200; break;
+ case ITEM_NATLA : item->health = 400; break;
+ case ITEM_GIANT_MUTANT : item->health = 500; break;
+ default :
+ item->health = 0;
+ item->flags.shadow = false;
+ }
+}
+
+#endif
diff --git a/src/platform/gba/lara.h b/src/platform/gba/lara.h
new file mode 100644
index 0000000..75b239c
--- /dev/null
+++ b/src/platform/gba/lara.h
@@ -0,0 +1,2044 @@
+#ifndef H_LARA
+#define H_LARA
+
+#include "common.h"
+#include "item.h"
+#include "collision.h"
+#include "camera.h"
+
+uint32 input;
+
+enum {
+ IN_LEFT = (1 << 1),
+ IN_RIGHT = (1 << 2),
+ IN_UP = (1 << 3),
+ IN_DOWN = (1 << 4),
+ IN_JUMP = (1 << 5),
+ IN_WALK = (1 << 6),
+ IN_ACTION = (1 << 7),
+ IN_WEAPON = (1 << 8),
+ IN_LOOK = (1 << 9),
+};
+
+#define LARA_STATES(E) \
+ E( WALK ) \
+ E( RUN ) \
+ E( STOP ) \
+ E( FORWARD_JUMP ) \
+ E( POSE ) \
+ E( FAST_BACK ) \
+ E( TURN_RIGHT ) \
+ E( TURN_LEFT ) \
+ E( DEATH ) \
+ E( FALL ) \
+ E( HANG ) \
+ E( REACH ) \
+ E( SPLAT ) \
+ E( TREAD ) \
+ E( LAND ) \
+ E( COMPRESS ) \
+ E( BACK ) \
+ E( SWIM ) \
+ E( GLIDE ) \
+ E( HANG_UP ) \
+ E( FAST_TURN ) \
+ E( STEP_RIGHT ) \
+ E( STEP_LEFT ) \
+ E( ROLL_END ) \
+ E( SLIDE ) \
+ E( BACK_JUMP ) \
+ E( RIGHT_JUMP ) \
+ E( LEFT_JUMP ) \
+ E( UP_JUMP ) \
+ E( FALL_BACK ) \
+ E( HANG_LEFT ) \
+ E( HANG_RIGHT ) \
+ E( SLIDE_BACK ) \
+ E( SURF_TREAD ) \
+ E( SURF_SWIM ) \
+ E( DIVE ) \
+ E( PUSH_BLOCK ) \
+ E( PULL_BLOCK ) \
+ E( PUSH_PULL_READY ) \
+ E( PICK_UP ) \
+ E( SWITCH_DOWN ) \
+ E( SWITCH_UP ) \
+ E( USE_KEY ) \
+ E( USE_PUZZLE ) \
+ E( UW_DEATH ) \
+ E( ROLL_START ) \
+ E( SPECIAL ) \
+ E( SURF_BACK ) \
+ E( SURF_LEFT ) \
+ E( SURF_RIGHT ) \
+ E( MIDAS_USE ) \
+ E( MIDAS_DEATH ) \
+ E( SWAN_DIVE ) \
+ E( FAST_DIVE ) \
+ E( HANDSTAND ) \
+ E( WATER_OUT )
+
+#define DECL_ENUM(v) v,
+#define DECL_S_HANDLER(v) &Lara::s_##v,
+#define DECL_C_HANDLER(v) &Lara::c_##v,
+#define S_HANDLER(state) void s_##state()
+#define C_HANDLER(state) void c_##state()
+
+#define LARA_HANG_SLANT 60
+#define LARA_HANG_OFFSET 724
+#define LARA_HEIGHT 762
+#define LARA_HEIGHT_JUMP 870 // LARA_HEIGHT + hands up
+#define LARA_HEIGHT_UW 400
+#define LARA_HEIGHT_SURF 700
+#define LARA_RADIUS 100
+#define LARA_RADIUS_WATER 300
+#define LARA_RADIUS_CLIMB 220
+#define LARA_MAX_HEALTH 1000
+#define LARA_MAX_OXYGEN 1800
+#define LARA_HEIGHT 762
+#define LARA_TURN_ACCEL (2 * DEG2SHORT + DEG2SHORT / 4)
+#define LARA_TURN_JUMP (3 * DEG2SHORT)
+#define LARA_TURN_VERY_SLOW (2 * DEG2SHORT)
+#define LARA_TURN_SLOW (4 * DEG2SHORT)
+#define LARA_TURN_MED (6 * DEG2SHORT)
+#define LARA_TURN_FAST (8 * DEG2SHORT)
+#define LARA_TILT_ACCEL (DEG2SHORT + DEG2SHORT / 2)
+#define LARA_TILT_MAX (11 * DEG2SHORT)
+#define LARA_STEP_HEIGHT 384
+#define LARA_SMASH_HEIGHT 640
+#define LARA_FLOAT_UP_SPEED 5
+#define LARA_SWIM_FRICTION 6
+#define LARA_SWIM_ACCEL 8
+#define LARA_SWIM_SPEED_MIN 133
+#define LARA_SWIM_SPEED_MAX 200
+#define LARA_SWIM_TIMER 10 // 1/3 sec
+#define LARA_SURF_FRICTION 4
+#define LARA_SURF_ACCEL 8
+#define LARA_SURF_SPEED_MAX 60
+#define LARA_DIVE_SPEED 80
+
+struct Lara : Item
+{
+ enum State {
+ LARA_STATES(DECL_ENUM)
+ X_MAX
+ };
+
+ enum {
+ ANIM_RUN = 0,
+
+ ANIM_STAND_LEFT = 2,
+ ANIM_STAND_RIGHT = 3,
+
+ ANIM_RUN_START = 6,
+
+ ANIM_STAND = 11,
+
+ ANIM_LANDING = 24,
+
+ ANIM_CLIMB_JUMP = 26,
+
+ ANIM_FALL_HANG = 28,
+
+ ANIM_SMASH_JUMP = 32,
+
+ ANIM_FALL_FORTH = 34,
+
+ ANIM_BACK = 41,
+ ANIM_CLIMB_3 = 42,
+
+ ANIM_CLIMB_2 = 50,
+
+ ANIM_SMASH_RUN_LEFT = 53,
+ ANIM_SMASH_RUN_RIGHT = 54,
+ ANIM_RUN_ASCEND_LEFT = 55,
+ ANIM_RUN_ASCEND_RIGHT = 56,
+ ANIM_WALK_ASCEND_LEFT = 57,
+ ANIM_WALK_ASCEND_RIGHT = 58,
+ ANIM_WALK_DESCEND_RIGHT = 59,
+ ANIM_WALK_DESCEND_LEFT = 60,
+ ANIM_BACK_DESCEND_LEFT = 61,
+ ANIM_BACK_DESCEND_RIGHT = 62,
+
+ ANIM_SLIDE_FORTH = 70,
+
+ ANIM_UW_GLIDE = 87,
+
+ ANIM_FALL_BACK = 93,
+
+ ANIM_HANG = 96,
+
+ ANIM_STAND_NORMAL = 103,
+
+ ANIM_SLIDE_BACK = 104,
+
+ ANIM_UNDERWATER = 108,
+
+ ANIM_WATER_OUT = 111,
+ ANIM_WATER_FALL = 112,
+ ANIM_SURF = 114,
+ ANIM_SURF_SWIM = 116,
+ ANIM_SURF_DIVE = 119,
+
+ ANIM_HIT_FRONT = 125,
+ ANIM_HIT_BACK = 126,
+ ANIM_HIT_LEFT = 127,
+ ANIM_HIT_RIGHT = 128,
+
+ ANIM_DEATH_BOULDER = 139,
+ ANIM_SURF_BACK = 140,
+
+ ANIM_SURF_LEFT = 143,
+ ANIM_SURF_RIGHT = 144,
+
+ ANIM_STAND_ROLL_BEGIN = 146,
+ ANIM_STAND_ROLL_END = 147,
+
+ ANIM_DEATH_SPIKES = 149,
+ ANIM_HANG_SWING = 150,
+
+ ANIM_CLIMB_START = 164,
+
+ ANIM_WADE_SWIM = 176,
+ ANIM_WADE = 177,
+ ANIM_WADE_RUN_LEFT = 178,
+ ANIM_WADE_RUN_RIGHT = 179,
+ ANIM_WADE_STAND = 186,
+ ANIM_WADE_ASCEND = 190,
+ ANIM_SURF_OUT = 191,
+ ANIM_SWIM_STAND = 192,
+ ANIM_SURF_STAND = 193,
+
+ ANIM_SWITCH_BIG_DOWN = 195,
+ ANIM_SWITCH_BIG_UP = 196,
+ ANIM_PUSH_BUTTON = 197,
+
+ ANIM_UW_ROLL = 203,
+ };
+
+ typedef void (Lara::*Handler)();
+
+ static const Handler sHandlers[X_MAX];
+ static const Handler cHandlers[X_MAX];
+
+ void updateState()
+ {
+ (this->*sHandlers[state])();
+ }
+
+ void updateCollision()
+ {
+ (this->*cHandlers[state])();
+ }
+
+ void startScreaming()
+ {
+ // TODO
+ }
+
+ void stopScreaming()
+ {
+ // TODO
+ }
+
+// common
+ bool alignAngle(int16 &angle, int16 threshold)
+ {
+ if (angle >= -threshold && angle <= threshold) {
+ angle = 0;
+ } else if (angle >= ANGLE_90 - threshold && angle <= ANGLE_90 + threshold) {
+ angle = ANGLE_90;
+ } else if (angle >= -ANGLE_90 - threshold && angle <= -ANGLE_90 + threshold) {
+ angle = -ANGLE_90;
+ } else if (angle >= (ANGLE_180 - 1) - threshold || angle <= -(ANGLE_180 - 1) + threshold) {
+ angle = -ANGLE_180;
+ }
+
+ return (angle & (ANGLE_90 - 1)) > 0;
+ }
+
+ void alignWall()
+ {
+ int x = (~1023) & pos.x;
+ int z = (~1023) & pos.z;
+
+ switch (angleY)
+ {
+ case ANGLE_0 : pos.z = z + 1024 + LARA_RADIUS; break;
+ case ANGLE_90 : pos.x = x + 1024 + LARA_RADIUS; break;
+ case -ANGLE_90 : pos.x = x - LARA_RADIUS; break;
+ case -ANGLE_180 : pos.z = z - LARA_RADIUS; break;
+ default : ASSERT(false);
+ }
+ }
+
+ int16 getFront(int16 angle)
+ {
+ angle += angleY;
+
+ int32 x = pos.x + (phd_sin(angle) >> (FIXED_SHIFT - 8));
+ int32 y = pos.y - LARA_HEIGHT;
+ int32 z = pos.z + (phd_cos(angle) >> (FIXED_SHIFT - 8));
+
+ int32 roomIndex = getRoomIndex(room, x, y, z);
+ const RoomInfo::Sector* sector = getSector(roomIndex, x, z);
+ int16 floor = getFloor(sector, x, y, z);
+
+ if (floor != WALL) {
+ floor -= pos.y;
+ }
+
+ return floor;
+ }
+
+ const Box& getBounds()
+ {
+ int32 modelIndex = modelsMap[type];
+ ASSERT(modelIndex != NO_MODEL);
+
+ const Model* model = models + modelIndex;
+
+ AnimFrame* frame = getFrame(this, model);
+
+ return frame->box;
+ }
+
+ bool checkDeath()
+ {
+ return health <= 0;
+ }
+
+ void updateRoom(int32 itemIndex, int32 height)
+ {
+ int16 nextRoom = getRoomIndex(room, pos.x, pos.y + height, pos.z);
+ if (room != nextRoom) {
+ roomItemRemove(itemIndex);
+ roomItemAdd(nextRoom, itemIndex);
+ room = nextRoom;
+ }
+ }
+
+ int32 getWaterLevel(int32 roomIndex)
+ {
+ /*
+ RoomInfo::Sector* sector = getWaterLevelSector(roomIndex, pos.x, pos.z);
+
+ if (sector) {
+ return sector->ceiling * 256;
+ }
+ */
+ return WALL;
+ }
+
+ int32 getWaterDepth(int32 roomIndex)
+ {
+ /*
+ RoomInfo::Sector* sector = getWaterLevelSector(roomIndex, pos.x, pos.z);
+
+ if (sector) {
+ return getFloor(sector, pos.x, pos.y, pos.z) - (sector->ceiling * 256);
+ }
+ */
+ return WALL;
+ }
+
+// state control
+ bool s_checkFront(int16 angle)
+ {
+ return true; // TODO
+ }
+
+ void s_ignoreEnemy()
+ {
+ cinfo.enemyPush = false;
+ cinfo.enemyHit = false;
+ }
+
+ void s_rotate(int32 maxSpeed, int32 tilt)
+ {
+ tilt *= LARA_TILT_ACCEL;
+
+ if (input & IN_LEFT) {
+ turnSpeed = X_MAX(turnSpeed - LARA_TURN_ACCEL, -maxSpeed);
+ angleZ = X_MAX(angleZ - tilt, -LARA_TILT_MAX);
+ } else if (input & IN_RIGHT) {
+ turnSpeed = X_MIN(turnSpeed + LARA_TURN_ACCEL, maxSpeed);
+ angleZ = X_MIN(angleZ + tilt, LARA_TILT_MAX);
+ }
+ }
+
+ bool s_checkFall()
+ {
+ if (vSpeed > 131)
+ {
+ if (state == SWAN_DIVE) {
+ goalState = FAST_DIVE;
+ } else {
+ goalState = FALL;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void s_checkWalk(int32 stopState)
+ {
+ if ((input & IN_UP) && s_checkFront(ANGLE_0)) {
+ if (input & IN_WALK) {
+ goalState = WALK;
+ } else {
+ goalState = RUN;
+ }
+ } else {
+ goalState = stopState;
+ }
+ }
+
+ bool s_checkRoll()
+ {
+ if ((waterState != WATER_STATE_ABOVE) && (waterState != WATER_STATE_UNDER)) {
+ return false;
+ }
+
+ bool roll = (input & (IN_UP | IN_DOWN)) == (IN_UP | IN_DOWN);
+
+ if ((waterState == WATER_STATE_ABOVE) && roll)
+ {
+ if ((state == RUN) || (state == STOP))
+ {
+ animSet(this, ANIM_STAND_ROLL_BEGIN, true, 2);
+ goalState = STOP;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ S_HANDLER( WALK )
+ {
+ if (checkDeath()) {
+ goalState = STOP;
+ return;
+ }
+
+ s_rotate(LARA_TURN_SLOW, 0);
+
+ s_checkWalk(STOP);
+ }
+
+ S_HANDLER( RUN )
+ {
+ if (checkDeath()) {
+ goalState = DEATH;
+ return;
+ }
+
+ if (s_checkRoll()) {
+ return;
+ }
+
+ s_rotate(LARA_TURN_FAST, 1);
+
+ if ((input & IN_JUMP) && !flags.gravity) {
+ goalState = FORWARD_JUMP;
+ } else {
+ s_checkWalk(STOP);
+ }
+ }
+
+ S_HANDLER( STOP )
+ {
+ if (checkDeath())
+ {
+ nextState = DEATH;
+ return;
+ }
+
+ if (s_checkRoll()) {
+ return;
+ }
+
+ goalState = STOP;
+ /*
+ if ((input & (IN_UP | IN_ACTION)) == (IN_UP | IN_ACTION)) {
+ c_angle(ANGLE_0);
+ cinfo.radius = LARA_RADIUS + 4;
+ c_default();
+ cinfo.radius = LARA_RADIUS;
+ if (c_checkClimbUp()) {
+ return;
+ }
+ }
+ */
+ if (input & IN_WALK) {
+ if ((input & IN_LEFT) && s_checkFront(-ANGLE_90)) {
+ goalState = STEP_LEFT;
+ } else if ((input & IN_RIGHT) && s_checkFront(ANGLE_90)) {
+ goalState = STEP_RIGHT;
+ }
+ } else {
+ if (input & IN_LEFT) {
+ goalState = TURN_LEFT;
+ } else if (input & IN_RIGHT) {
+ goalState = TURN_RIGHT;
+ }
+ }
+
+ if (input & IN_JUMP) {
+ goalState = COMPRESS;
+ } else if ((input & IN_UP) && s_checkFront(ANGLE_0)) {
+ if (input & IN_WALK) {
+ s_WALK();
+ } else {
+ s_RUN();
+ }
+ } else if ((input & IN_DOWN) && s_checkFront(-ANGLE_180)) {
+ if (input & IN_WALK) {
+ s_BACK();
+ } else {
+ goalState = FAST_BACK;
+ }
+ }
+ }
+
+
+ S_HANDLER( FORWARD_JUMP )
+ {
+ if (goalState == SWAN_DIVE ||
+ goalState == REACH)
+ {
+ goalState = FORWARD_JUMP;
+ }
+
+ if (goalState != DEATH &&
+ goalState != STOP &&
+ goalState != RUN)
+ {
+ if (input & IN_ACTION)
+ {
+ goalState = REACH;
+ }
+
+ if (input & IN_WALK)
+ {
+ goalState = SWAN_DIVE;
+ }
+
+ s_checkRoll();
+ s_checkFall();
+ }
+
+ s_rotate(LARA_TURN_JUMP, 0);
+ }
+
+ S_HANDLER( POSE )
+ {
+ // empty
+ }
+
+ S_HANDLER( FAST_BACK )
+ {
+ s_rotate(LARA_TURN_MED, 0);
+ goalState = STOP;
+ }
+
+ S_HANDLER( TURN_RIGHT )
+ {
+ if (checkDeath() || (input & IN_LOOK))
+ {
+ goalState = STOP;
+ return;
+ }
+
+ turnSpeed += LARA_TURN_ACCEL;
+
+ if (turnSpeed > LARA_TURN_SLOW)
+ {
+ goalState = FAST_TURN;
+ }
+
+ s_checkWalk((input & IN_RIGHT) ? goalState : STOP);
+ }
+
+ S_HANDLER( TURN_LEFT )
+ {
+ if (checkDeath() || (input & IN_LOOK))
+ {
+ goalState = STOP;
+ return;
+ }
+
+ turnSpeed -= LARA_TURN_ACCEL;
+
+ if (turnSpeed < -LARA_TURN_SLOW)
+ {
+ goalState = FAST_TURN;
+ }
+
+ s_checkWalk((input & IN_LEFT) ? goalState : STOP);
+ }
+
+ S_HANDLER( DEATH )
+ {
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( FALL )
+ {
+ hSpeed = (hSpeed * 95) / 100;
+ if (vSpeed >= 154)
+ {
+ startScreaming();
+ }
+ }
+
+ S_HANDLER( HANG )
+ {
+ camera.targetAngleX = -60 * DEG2SHORT;
+
+ s_ignoreEnemy();
+ if (input & IN_LEFT) {
+ goalState = HANG_LEFT;
+ } else if (input & IN_RIGHT) {
+ goalState = HANG_RIGHT;
+ }
+ }
+
+ S_HANDLER( REACH )
+ {
+ camera.targetAngleY = 85 * DEG2SHORT;
+
+ s_checkFall();
+ }
+
+ S_HANDLER( SPLAT )
+ {
+ // empty
+ }
+
+ S_HANDLER( TREAD ) {} // TODO
+
+ S_HANDLER( LAND )
+ {
+ // empty
+ }
+
+ S_HANDLER( COMPRESS )
+ {
+ if ((input & IN_UP) && getFront(ANGLE_0) >= -LARA_STEP_HEIGHT) {
+ goalState = FORWARD_JUMP;
+ } else if ((input & IN_LEFT) && getFront(-ANGLE_90) >= -LARA_STEP_HEIGHT) {
+ goalState = LEFT_JUMP;
+ } else if ((input & IN_RIGHT) && getFront(ANGLE_90) >= -LARA_STEP_HEIGHT) {
+ goalState = RIGHT_JUMP;
+ } else if ((input & IN_DOWN) && getFront(-ANGLE_180) >= -LARA_STEP_HEIGHT) {
+ goalState = BACK_JUMP;
+ }
+ s_checkFall();
+ }
+
+ S_HANDLER( BACK )
+ {
+ if (checkDeath()) {
+ goalState = STOP;
+ return;
+ }
+
+ if ((input & (IN_WALK | IN_DOWN)) != (IN_WALK | IN_DOWN)) {
+ goalState = STOP;
+ } else {
+ goalState = BACK;
+ }
+
+ s_rotate(LARA_TURN_SLOW, 0);
+ }
+
+ S_HANDLER( SWIM ) {} // TODO
+ S_HANDLER( GLIDE ) {} // TODO
+
+ S_HANDLER( HANG_UP )
+ {
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( FAST_TURN )
+ {
+ if (checkDeath() || (input & IN_LOOK))
+ {
+ goalState = STOP;
+ return;
+ }
+
+ if (turnSpeed < 0) {
+ turnSpeed = -LARA_TURN_FAST;
+ if (!(input & IN_LEFT)) {
+ goalState = STOP;
+ }
+ } else {
+ turnSpeed = LARA_TURN_FAST;
+ if (!(input & IN_RIGHT)) {
+ goalState = STOP;
+ }
+ }
+ }
+
+ S_HANDLER( STEP_RIGHT )
+ {
+ if (checkDeath() || (input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT))
+ {
+ goalState = STOP;
+ }
+ }
+
+ S_HANDLER( STEP_LEFT )
+ {
+ if (checkDeath() || (input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT))
+ {
+ goalState = STOP;
+ }
+ }
+
+ S_HANDLER( ROLL_END )
+ {
+ // empty
+ }
+
+ S_HANDLER( SLIDE )
+ {
+ camera.targetAngleX = -45 * DEG2SHORT;
+
+ if (input & IN_JUMP)
+ {
+ goalState = FORWARD_JUMP;
+ }
+ }
+
+ S_HANDLER( BACK_JUMP )
+ {
+ camera.targetAngleY = 135 * DEG2SHORT;
+
+ if (s_checkFall()) return;
+
+ if (goalState == RUN) {
+ goalState = STOP;
+ }
+ }
+
+ S_HANDLER( RIGHT_JUMP )
+ {
+ s_checkFall();
+ }
+
+ S_HANDLER( LEFT_JUMP )
+ {
+ s_checkFall();
+ }
+
+ S_HANDLER( UP_JUMP )
+ {
+ s_checkFall();
+ }
+
+ S_HANDLER( FALL_BACK )
+ {
+ s_checkFall();
+
+ if (input & IN_ACTION)
+ {
+ goalState = REACH;
+ }
+ }
+
+ S_HANDLER( HANG_LEFT )
+ {
+ camera.targetAngleX = -60 * DEG2SHORT;
+ camera.targetAngleY = -10 * DEG2SHORT;
+
+ s_ignoreEnemy();
+
+ if (!(input & IN_LEFT))
+ {
+ goalState = HANG;
+ }
+ }
+
+ S_HANDLER( HANG_RIGHT )
+ {
+ camera.targetAngleX = -60 * DEG2SHORT;
+ camera.targetAngleY = 10 * DEG2SHORT;
+
+ s_ignoreEnemy();
+
+ if (!(input & IN_RIGHT))
+ {
+ goalState = HANG;
+ }
+ }
+
+ S_HANDLER( SLIDE_BACK )
+ {
+ if (input & IN_JUMP)
+ {
+ goalState = BACK_JUMP;
+ }
+ }
+
+ S_HANDLER( SURF_TREAD ) {} // TODO
+ S_HANDLER( SURF_SWIM ) {} // TODO
+ S_HANDLER( DIVE ) {} // TODO
+
+ S_HANDLER( PUSH_BLOCK )
+ {
+ camera.targetAngleX = -25 * DEG2SHORT;
+ camera.targetAngleY = 35 * DEG2SHORT;
+
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( PULL_BLOCK )
+ {
+ camera.targetAngleX = -25 * DEG2SHORT;
+ camera.targetAngleY = 35 * DEG2SHORT;
+
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( PUSH_PULL_READY )
+ {
+ camera.targetAngleY = 75 * DEG2SHORT;
+
+ s_ignoreEnemy();
+
+ if (!(input & IN_ACTION))
+ {
+ goalState = STOP;
+ }
+ }
+
+ S_HANDLER( PICK_UP )
+ {
+ camera.targetAngleX = -15 * DEG2SHORT;
+ camera.targetAngleY = -130 * DEG2SHORT;
+ camera.targetDist = 1024;
+
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( SWITCH_DOWN )
+ {
+ camera.targetAngleX = -25 * DEG2SHORT;
+ camera.targetAngleY = 80 * DEG2SHORT;
+ camera.targetDist = 1024;
+
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( SWITCH_UP )
+ {
+ camera.targetAngleX = -25 * DEG2SHORT;
+ camera.targetAngleY = 80 * DEG2SHORT;
+ camera.targetDist = 1024;
+
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( USE_KEY )
+ {
+ camera.targetAngleX = -25 * DEG2SHORT;
+ camera.targetAngleY = -80 * DEG2SHORT;
+ camera.targetDist = 1024;
+
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( USE_PUZZLE )
+ {
+ camera.targetAngleX = -25 * DEG2SHORT;
+ camera.targetAngleY = -80 * DEG2SHORT;
+ camera.targetDist = 1024;
+
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( UW_DEATH )
+ {
+ vSpeed = X_MAX(vSpeed - LARA_SWIM_ACCEL, 0);
+ angleX = angleDec(angleX, 2 * DEG2SHORT);
+ }
+
+ S_HANDLER( ROLL_START )
+ {
+ // empty
+ }
+
+ S_HANDLER( SPECIAL )
+ {
+ camera.targetAngleX = -25 * DEG2SHORT;
+ camera.targetAngleY = 170 * DEG2SHORT;
+ }
+
+ S_HANDLER( SURF_BACK ) {} // TODO
+ S_HANDLER( SURF_LEFT ) {} // TODO
+ S_HANDLER( SURF_RIGHT ) {} // TODO
+
+ S_HANDLER( MIDAS_USE )
+ {
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( MIDAS_DEATH )
+ {
+ s_ignoreEnemy();
+ flags.gravity = false;
+ }
+
+ S_HANDLER( SWAN_DIVE ) {} // TODO
+ S_HANDLER( FAST_DIVE ) {} // TODO
+
+ S_HANDLER( HANDSTAND )
+ {
+ s_ignoreEnemy();
+ }
+
+ S_HANDLER( WATER_OUT )
+ {
+ s_ignoreEnemy();
+ }
+
+// collision control
+ void c_applyOffset() {
+ pos.x += cinfo.offset.x;
+ pos.y += cinfo.offset.y;
+ pos.z += cinfo.offset.z;
+ cinfo.offset = vec3i(0, 0, 0);
+ }
+
+ void c_angle(int32 angleDelta) {
+ moveAngle = angleY + angleDelta;
+
+ cinfo.angle = moveAngle;
+ cinfo.quadrant = uint16(cinfo.angle + ANGLE_45) / ANGLE_90;
+ }
+
+ bool c_checkCeiling() {
+ if (cinfo.type != CT_CEILING && cinfo.type != CT_FLOOR_CEILING) {
+ return false;
+ }
+
+ animSet(this, ANIM_STAND, true);
+ goalState = state;
+ hSpeed = 0;
+ vSpeed = 0;
+ flags.gravity = false;
+ pos = cinfo.pos;
+
+ return true;
+ }
+
+ bool c_checkWall() {
+ if (cinfo.type == CT_FRONT || cinfo.type == CT_FRONT_CEILING) {
+ c_applyOffset();
+ goalState = STOP;
+ hSpeed = 0;
+ flags.gravity = false;
+ return true;
+ }
+
+ if (cinfo.type == CT_LEFT) {
+ c_applyOffset();
+ angleY += 5 * DEG2SHORT;
+ angleZ = angleDec(angleZ, 2 * DEG2SHORT);
+ } else if (cinfo.type == CT_RIGHT) {
+ c_applyOffset();
+ angleY -= 5 * DEG2SHORT;
+ angleZ = angleDec(angleZ, 2 * DEG2SHORT);
+ }
+
+ return false;
+ }
+
+ bool c_checkWallUW() {
+ if (cinfo.type == CT_FRONT) {
+ if (angleX > 35 * DEG2SHORT) {
+ angleX += 2 * DEG2SHORT;
+ } else if (angleX < -35 * DEG2SHORT) {
+ angleX -= 2 * DEG2SHORT;
+ } else {
+ vSpeed = 0;
+ }
+ } else if (cinfo.type == CT_CEILING) {
+ if (angleX >= -45 * DEG2SHORT) {
+ angleX -= 2 * DEG2SHORT;
+ }
+ } else if (cinfo.type == CT_FRONT_CEILING) {
+ vSpeed = 0;
+ } else if (cinfo.type == CT_LEFT) {
+ angleY += 5 * DEG2SHORT;
+ } else if (cinfo.type == CT_RIGHT) {
+ angleY -= 5 * DEG2SHORT;
+ } else if (cinfo.type == CT_FLOOR_CEILING) {
+ pos = cinfo.pos;
+ vSpeed = 0;
+ return true;
+ }
+
+ if (cinfo.m.floor < 0) {
+ pos.y += cinfo.m.floor;
+ angleX += 2 * DEG2SHORT;
+ }
+
+ int32 waterDepth = getWaterDepth(room);
+
+ if (waterDepth == WALL) {
+ vSpeed = 0;
+ pos = cinfo.pos;
+ } else if (waterDepth <= 512) {
+ waterState = WATER_STATE_WADE;
+
+ animSet(this, ANIM_SWIM_STAND, true);
+ goalState = STOP;
+
+ angleX = 0;
+ angleZ = 0;
+ hSpeed = 0;
+ vSpeed = 0;
+ flags.gravity = false;
+ /* TODO
+ RoomInfo::Sector* sector = getSector(roomIndex, pos.x, pos.y, pos.z);
+ pos.y = getFloor(sector, pos.x, pos.y, pos.z);
+ */
+ }
+
+ return false;
+ }
+
+ bool c_checkWallSurf() {
+ if ((cinfo.m.floor < 0 && cinfo.m.slantType == SLANT_HIGH) || (cinfo.type & (CT_FRONT | CT_CEILING | CT_FRONT_CEILING | CT_FLOOR_CEILING))) {
+ pos = cinfo.pos;
+ vSpeed = 0;
+ } else if (cinfo.type == CT_LEFT) {
+ angleY += 5 * DEG2SHORT;
+ } else if (cinfo.type == CT_RIGHT) {
+ angleY -= 5 * DEG2SHORT;
+ }
+
+ return true;
+ }
+
+ bool c_checkSlide() {
+ if (waterState == WATER_STATE_WADE) {
+ return false;
+ }
+
+ if (cinfo.m.slantType != SLANT_HIGH) {
+ return false;
+ }
+
+ c_applyOffset();
+
+ int16 angle;
+
+ if (cinfo.slantX > 2) {
+ angle = -ANGLE_90;
+ } else if (cinfo.slantX < -2) {
+ angle = ANGLE_90;
+ } else if (cinfo.slantZ > 2) {
+ angle = -ANGLE_180;
+ } else {
+ angle = 0;
+ }
+
+ if (abs(angle - angleY) <= ANGLE_90) {
+ if (state != SLIDE) {
+ animSet(this, ANIM_SLIDE_FORTH, true);
+ }
+ moveAngle = angle;
+ angleY = angle;
+ } else {
+ if (state != SLIDE_BACK) {
+ animSet(this, ANIM_SLIDE_BACK, true);
+ }
+ moveAngle = angle;
+ angleY = angle - ANGLE_180;
+ }
+
+ return true;
+ }
+
+ bool c_checkFall(int32 height, int32 fallAnimIndex = ANIM_FALL_FORTH) {
+ if (waterState == WATER_STATE_WADE) {
+ return false;
+ }
+
+ if (cinfo.m.floor <= height) {
+ return false;
+ }
+
+ animSet(this, fallAnimIndex, true);
+
+ vSpeed = 0;
+ flags.gravity = true;
+
+ return true;
+ }
+
+ bool c_checkLanding() {
+ if ((state == FAST_DIVE /*|| state == AIR_ROLL*/) && vSpeed > 133) {
+ health = 0;
+ return true;
+ }
+
+ int32 y = pos.y;
+ pos.y += cinfo.m.floor;
+ /* TODO
+ v2floor = pos.y;
+
+ checkTrigger(cinfo.trigger, false);
+ */
+ pos.y = y;
+
+ if (vSpeed <= 140) {
+ return false;
+ }
+
+ if (vSpeed > 154) {
+ health = 0;
+ } else {
+ health -= (X_SQR(vSpeed - 140) * LARA_MAX_HEALTH) / 196;
+ }
+
+ return checkDeath();
+ }
+
+ bool c_checkSwing() {
+ int32 x = pos.x;
+ int32 y = pos.y;
+ int32 z = pos.z;
+
+ switch (angleY) {
+ case ANGLE_0 : z += 256; break;
+ case ANGLE_90 : x += 256; break;
+ case -ANGLE_90 : x -= 256; break;
+ case -ANGLE_180 : z -= 256; break;
+ }
+
+ int32 roomIndex = getRoomIndex(room, x, y, z);
+ const RoomInfo::Sector* sector = getSector(roomIndex, x, z);
+ int32 floor = getFloor(sector, x, y, z);
+
+ if (floor != WALL) {
+ int32 ceiling = getCeiling(sector, x, y, z);
+
+ floor -= y;
+ ceiling -= y;
+
+ if (floor > 0 && ceiling < -400) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool c_checkGrab() {
+ return !(input & IN_ACTION) || cinfo.type != CT_FRONT || abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT;
+ }
+
+ bool c_checkSpace() {
+ return (cinfo.f.floor < cinfo.f.ceiling ||
+ cinfo.l.floor < cinfo.l.ceiling ||
+ cinfo.r.floor < cinfo.r.ceiling);
+ }
+
+ bool c_checkClimbStart() {
+ return false;
+ }
+
+ bool c_checkClimbUp() {
+ if (c_checkGrab()) {
+ return false;
+ }
+
+ int16 angle = angleY;
+ if (alignAngle(angle, 30 * DEG2SHORT)) {
+ return false;
+ }
+
+ if (cinfo.f.floor >= -640 && cinfo.f.floor <= -384) {
+ if (c_checkSpace()) return false;
+
+ animSet(this, ANIM_CLIMB_2, true);
+ state = HANG_UP;
+
+ pos.y += 512 + cinfo.f.floor;
+ } else if (cinfo.f.floor >= -896 && cinfo.f.floor <= -640) {
+ if (c_checkSpace()) return false;
+
+ animSet(this, ANIM_CLIMB_3, true);
+ state = HANG_UP;
+
+ pos.y += 768 + cinfo.f.floor;
+ } else if (cinfo.f.floor >= -1920 && cinfo.f.floor <= -896) {
+ animSet(this, ANIM_STAND, true);
+ goalState = UP_JUMP;
+ vSpeedHack = -int32(phd_sqrt(-2 * GRAVITY * (cinfo.f.floor + 800)) + 3);
+ animUpdate(this);
+ } /* TODO for main branch
+ else if ((waterState != WATER_STATE_WADE) && (cinfo.f.floor <= -1920) && (cinfo.l.floor <= -1920) && (cinfo.r.floor <= -1920) && (cinfo.m.ceiling <= -1158)) {
+ animSet(this, ANIM_STAND, true);
+ goalState = UP_JUMP;
+ vSpeedHack = -116;
+ animUpdate(this);
+ }
+ else if (((cinfo.f.floor < -1024) && (cinfo.f.ceiling >= 506)) || (cinfo.m.ceiling <= -518) && c_checkClimbStart()) {
+ animSet(this, ANIM_STAND, true);
+ goalState = CLIMB_START;
+ processAnimation();
+ }*/ else {
+ return false;
+ }
+
+ angleY = angle;
+ c_applyOffset();
+
+ return true;
+ }
+
+ bool c_checkHang() {
+ if (c_checkGrab()) {
+ return false;
+ }
+
+ if (cinfo.f.ceiling > 0 || cinfo.m.ceiling > -LARA_STEP_HEIGHT || (cinfo.m.floor < 200 && state == REACH)) {
+ return false;
+ }
+
+ int32 h = cinfo.f.floor - getBounds().minY;
+ int32 v = h + vSpeed;
+
+ if ((h < 0 && v < 0) || (h > 0 && v > 0)) {
+ return false;
+ }
+
+ if (alignAngle(angleY, 35 * DEG2SHORT)) {
+ return false;
+ }
+
+ if (state == REACH)
+ {
+ if (c_checkSwing()) {
+ animSet(this, ANIM_HANG_SWING, true);
+ } else {
+ animSet(this, ANIM_HANG, true);
+ }
+ } else {
+ animSet(this, ANIM_HANG, true, 12);
+ }
+
+ cinfo.offset.y = cinfo.f.floor - getBounds().minY;
+
+ c_applyOffset();
+
+ flags.gravity = false;
+ hSpeed = 0;
+ vSpeed = 0;
+
+ return true;
+ }
+
+ bool c_checkDrop() {
+ // TODO getTrigger here
+
+ if ((health > 0) && (input & IN_ACTION)) {
+ flags.gravity = false;
+ vSpeed = 0;
+ return false;
+ }
+
+ flags.gravity = true;
+ hSpeed = 2;
+ vSpeed = 1;
+
+ animSet(this, ANIM_FALL_FORTH, true);
+
+ return true;
+ }
+
+ bool c_checkWaterOut() {
+ if (!(input & IN_ACTION) ||
+ (cinfo.type != CT_FRONT) ||
+ (cinfo.f.ceiling > 0) ||
+ (cinfo.m.ceiling > -LARA_STEP_HEIGHT) ||
+ (abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT))
+ {
+ return false;
+ }
+
+ int32 h = cinfo.f.floor + LARA_HEIGHT_SURF;
+
+ if (h <= -512 || h > 316) {
+ return false;
+ }
+
+ if (alignAngle(angleY, 35 * DEG2SHORT)) {
+ return false;
+ }
+
+ angleY += h - 5;
+
+ updateRoom(curItemIndex, -LARA_HEIGHT / 2);
+
+ alignWall();
+
+ /* TODO for main branch
+ if ((h < -128) || (level->version & TR::VER_TR1)) {
+ setAnimV2(ANIM_WATER_OUT, true);
+ specular = LARA_WET_SPECULAR;
+ } else if (h < 128) {
+ setAnimV2(ANIM_SURF_OUT, true);
+ } else {
+ setAnimV2(ANIM_SURF_STAND, true);
+ }
+ game->waterDrop(pos, 128.0f, 0.2f);
+ */
+ animSet(this, ANIM_WATER_OUT, true);
+
+ waterState = WATER_STATE_ABOVE;
+ goalState = STOP;
+ angleX = 0;
+ angleZ = 0;
+ hSpeed = 0;
+ vSpeed = 0;
+ flags.gravity = false;
+
+ return true;
+ }
+
+ void c_default() {
+ cinfo.gapPos = LARA_STEP_HEIGHT;
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+ cinfo.gapCeiling = 0;
+ cinfo.stopOnSlant = true;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+ }
+
+ void c_step() {
+ cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : 128;
+ cinfo.gapNeg = -128;
+ cinfo.gapCeiling = 0;
+ cinfo.stopOnSlant = true;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ if (c_checkCeiling()) return;
+
+ if (c_checkWall()) {
+ animSet(this, ANIM_STAND, true);
+ }
+
+ if (c_checkSlide()) return;
+
+ pos.y += cinfo.m.floor;
+ }
+
+ void c_fall() {
+ if (vSpeed <= 0 || cinfo.m.floor > 0)
+ return;
+
+ if (c_checkLanding()) {
+ goalState = DEATH;
+ } else if (state == FORWARD_JUMP && (input & IN_UP) && !(input & IN_WALK)) {
+ goalState = RUN;
+ } else if (state == FALL) {
+ animSet(this, ANIM_LANDING, true);
+ } else {
+ goalState = STOP;
+ }
+
+ stopScreaming();
+
+ pos.y += cinfo.m.floor;
+ vSpeed = 0;
+ flags.gravity = false;
+
+ if (state == FORWARD_JUMP) {
+ animUpdate(this);
+ }
+ }
+
+ void c_jump() {
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = (state == REACH) ? 0 : -LARA_STEP_HEIGHT;
+ cinfo.gapCeiling = 192;
+
+ collideRoom(this, state == UP_JUMP ? LARA_HEIGHT_JUMP : LARA_HEIGHT, 0);
+
+ if ((state == REACH || state == UP_JUMP) && c_checkHang()) {
+ return;
+ }
+
+ c_applyOffset();
+
+ bool slide = (state == FALL) || (state == REACH) || (state == UP_JUMP);
+
+ if ((cinfo.type == CT_CEILING) || ((cinfo.type == CT_FRONT_CEILING) && slide)) {
+ if (vSpeed <= 0) {
+ vSpeed = 1;
+ }
+ } else if (!slide && ((cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING))) {
+ animSet(this, ANIM_SMASH_JUMP, true, 1);
+ moveAngle -= ANGLE_180;
+ hSpeed /= 4;
+ if (vSpeed <= 0) {
+ vSpeed = 1;
+ }
+ } else if (cinfo.type == CT_FLOOR_CEILING) {
+ pos.x -= (LARA_RADIUS * phd_sin(cinfo.angle)) >> FIXED_SHIFT;
+ pos.z -= (LARA_RADIUS * phd_cos(cinfo.angle)) >> FIXED_SHIFT;
+ cinfo.m.floor = 0;
+ hSpeed = 0;
+ if (vSpeed <= 0) {
+ vSpeed = 16;
+ }
+ } else if (cinfo.type == CT_LEFT) {
+ angleY += 5 * DEG2SHORT;
+ } else if (cinfo.type == CT_RIGHT) {
+ angleY -= 5 * DEG2SHORT;
+ }
+
+ c_fall();
+ }
+
+ void c_slide() {
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -512;
+ cinfo.gapCeiling = 0;
+ cinfo.stopOnSlant = true;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ if (c_checkCeiling()) return;
+
+ c_checkWall();
+
+ if (c_checkFall(200, state == SLIDE ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) return;
+
+ c_checkSlide();
+
+ pos.y += cinfo.m.floor;
+
+ if (cinfo.m.slantType != SLANT_HIGH) {
+ goalState = STOP;
+ }
+ }
+
+ void c_roll() {
+ vSpeed = 0;
+ flags.gravity = false;
+
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+ cinfo.gapCeiling = 0;
+ cinfo.stopOnSlant = true;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ if (c_checkCeiling()) return;
+
+ if (c_checkSlide()) return;
+
+ if (c_checkFall(200, state == ROLL_START ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) return;
+
+ c_applyOffset();
+
+ pos.y += cinfo.m.floor;
+ }
+
+ void c_hang(int32 angleDelta) {
+ c_angle(angleDelta);
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = WALL;
+ cinfo.gapCeiling = 0;
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ bool noFloor = cinfo.f.floor < 200;
+
+ c_angle(ANGLE_0);
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+ cinfo.gapCeiling = 0;
+
+ switch (cinfo.quadrant) {
+ case 0 : pos.z += 2; break;
+ case 1 : pos.x += 2; break;
+ case 2 : pos.z -= 2; break;
+ case 3 : pos.x -= 2; break;
+ }
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ moveAngle = angleY + angleDelta;
+
+ if (health <= 0 || !(input & IN_ACTION)) {
+ animSet(this, ANIM_FALL_HANG, true, 9);
+
+ cinfo.offset.y = cinfo.f.floor - getBounds().minY + 2;
+ c_applyOffset();
+
+ hSpeed = 2;
+ vSpeed = 1;
+ flags.gravity = true;
+ return;
+ }
+
+ vSpeed = 0;
+ flags.gravity = false;
+
+ if (noFloor || (cinfo.type != CT_FRONT) || (cinfo.m.ceiling >= 0) || abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT)
+ {
+ if (state != HANG) {
+ animSet(this, ANIM_HANG, true, 21);
+ }
+ pos = cinfo.pos;
+ return;
+ }
+
+ if (cinfo.quadrant & 1) {
+ pos.x += cinfo.offset.x;
+ } else {
+ pos.z += cinfo.offset.z;
+ }
+
+ int32 h = cinfo.f.floor - getBounds().minY;
+ if (abs(h) <= 256) {
+ pos.y += h;
+ }
+ }
+
+ C_HANDLER( WALK )
+ {
+ vSpeed = 0;
+ flags.gravity = false;
+ cinfo.stopOnLava = true;
+
+ c_angle(ANGLE_0);
+ c_default();
+
+ if (c_checkCeiling()) return;
+
+ if (c_checkClimbUp()) return;
+
+ if (c_checkWall()) {
+ if (frameIndex >= 29 && frameIndex <= 47) {
+ animSet(this, ANIM_STAND_RIGHT, false);
+ } else if ((frameIndex >= 22 && frameIndex <= 28) || (frameIndex >= 48 && frameIndex <= 57)) {
+ animSet(this, ANIM_STAND_LEFT, false);
+ } else {
+ animSet(this, ANIM_STAND, false);
+ }
+ }
+
+ if (c_checkFall(LARA_STEP_HEIGHT)) return;
+
+ // descend
+ if (cinfo.m.floor > 128) {
+ if (frameIndex >= 28 && frameIndex <= 45) {
+ animSet(this, ANIM_WALK_DESCEND_RIGHT, false);
+ } else {
+ animSet(this, ANIM_WALK_DESCEND_LEFT, false);
+ }
+ }
+
+ // ascend
+ if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) {
+ if (frameIndex >= 27 && frameIndex <= 44) {
+ animSet(this, ANIM_WALK_ASCEND_RIGHT, false);
+ } else {
+ animSet(this, ANIM_WALK_ASCEND_LEFT, false);
+ }
+ }
+
+ if (c_checkSlide()) return;
+
+ pos.y += cinfo.m.floor;
+ }
+
+ C_HANDLER( RUN )
+ {
+ c_angle(ANGLE_0);
+
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+ cinfo.gapCeiling = 0;
+ cinfo.stopOnSlant = true;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ if (c_checkCeiling()) return;
+
+ if (c_checkClimbUp()) return;
+
+ if (c_checkWall()) {
+ angleZ = 0;
+
+ if (cinfo.f.slantType == SLANT_NONE && cinfo.f.floor < -LARA_SMASH_HEIGHT && frameIndex < 22) {
+ animSet(this, frameIndex < 10 ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT, false);
+ state = SPLAT;
+ return;
+ }
+
+ animSet(this, ANIM_STAND, true);
+ }
+
+ if (c_checkFall(LARA_STEP_HEIGHT)) return;
+
+ // ascend
+ if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) {
+ if (frameIndex >= 3 && frameIndex <= 14) {
+ animSet(this, ANIM_RUN_ASCEND_RIGHT, false);
+ } else {
+ animSet(this, ANIM_RUN_ASCEND_LEFT, false);
+ }
+ }
+
+ if (c_checkSlide()) return;
+
+ if (cinfo.m.floor >= 50) {
+ pos.y += 50;
+ return;
+ }
+
+ pos.y += cinfo.m.floor;
+ }
+
+ C_HANDLER( STOP )
+ {
+ vSpeed = 0;
+ flags.gravity = false;
+
+ c_angle(ANGLE_0);
+ c_default();
+
+ if (c_checkCeiling()) return;
+
+ if (c_checkFall(100)) return;
+
+ if (c_checkSlide()) return;
+
+ c_applyOffset();
+
+ pos.y += cinfo.m.floor;
+ }
+
+ C_HANDLER( FORWARD_JUMP )
+ {
+ c_angle(ANGLE_0);
+ c_jump();
+ }
+
+ C_HANDLER( POSE )
+ {
+ c_STOP();
+ }
+
+ C_HANDLER( FAST_BACK )
+ {
+ vSpeed = 0;
+ flags.gravity = false;
+
+ c_angle(-ANGLE_180);
+
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+ cinfo.gapCeiling = 0;
+ cinfo.stopOnSlant = true;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ if (c_checkCeiling()) return;
+
+ if (c_checkFall(200, ANIM_FALL_BACK)) return;
+
+ if (c_checkWall()) {
+ animSet(this, ANIM_STAND, false);
+ }
+
+ pos.y += cinfo.m.floor;
+ }
+
+ C_HANDLER( TURN_RIGHT )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+
+ if (c_checkFall(100)) return;
+
+ if (c_checkSlide()) return;
+
+ pos.y += cinfo.m.floor;
+ }
+
+ C_HANDLER( TURN_LEFT )
+ {
+ c_TURN_RIGHT();
+ }
+
+ C_HANDLER( DEATH )
+ {
+ cinfo.radius = LARA_RADIUS * 4;
+
+ c_angle(ANGLE_0);
+ c_default();
+
+ c_applyOffset();
+ pos.y += cinfo.m.floor;
+ }
+
+ C_HANDLER( FALL )
+ {
+ flags.gravity = true;
+ c_jump();
+ }
+
+ C_HANDLER( HANG )
+ {
+ c_hang(0);
+
+ if ((input & IN_UP) && goalState == HANG)
+ {
+ if (cinfo.f.floor <= -850 ||
+ cinfo.f.floor >= -650 ||
+ c_checkSpace() || cinfo.staticHit) return;
+
+ if (input & IN_WALK) {
+ goalState = HANDSTAND;
+ } else {
+ goalState = HANG_UP;
+ }
+ }
+ }
+
+ C_HANDLER( REACH )
+ {
+ flags.gravity = true;
+ c_angle(ANGLE_0);
+ c_jump();
+ }
+
+ C_HANDLER( SPLAT )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ c_applyOffset();
+ }
+
+ C_HANDLER( TREAD ) {} // TODO
+
+ C_HANDLER( LAND )
+ {
+ c_STOP();
+ }
+
+ C_HANDLER( COMPRESS )
+ {
+ vSpeed = 0;
+ flags.gravity = false;
+
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = WALL;
+ cinfo.gapCeiling = 0;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ if (cinfo.m.ceiling > -100) {
+ animSet(this, ANIM_STAND, true);
+ pos = cinfo.pos;
+ hSpeed = 0;
+ vSpeed = 0;
+ flags.gravity = false;
+ }
+ }
+
+ C_HANDLER( BACK )
+ {
+ vSpeed = 0;
+ flags.gravity = false;
+
+ c_angle(-ANGLE_180);
+
+ cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : LARA_STEP_HEIGHT;
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+ cinfo.gapCeiling = 0;
+ cinfo.stopOnSlant = true;
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ if (c_checkCeiling()) return;
+
+ if (c_checkWall())
+ {
+ animSet(this, ANIM_STAND, true);
+ }
+
+ if (cinfo.m.floor > 128 && cinfo.m.floor < LARA_STEP_HEIGHT)
+ {
+ if (frameIndex < 568) {
+ animSet(this, ANIM_BACK_DESCEND_LEFT, false);
+ } else {
+ animSet(this, ANIM_BACK_DESCEND_RIGHT, false);
+ }
+ }
+
+ if (c_checkSlide()) return;
+
+ pos.y += cinfo.m.floor;
+ }
+
+ C_HANDLER( SWIM ) {} // TODO
+ C_HANDLER( GLIDE ) {} // TODO
+
+ C_HANDLER( HANG_UP )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( FAST_TURN )
+ {
+ c_STOP();
+ }
+
+ C_HANDLER( STEP_RIGHT )
+ {
+ c_angle(+ANGLE_90);
+ c_step();
+ }
+
+ C_HANDLER( STEP_LEFT )
+ {
+ c_angle(-ANGLE_90);
+ c_step();
+ }
+
+ C_HANDLER( ROLL_END )
+ {
+ c_angle(-ANGLE_180);
+ c_roll();
+ }
+
+ C_HANDLER( SLIDE )
+ {
+ c_angle(ANGLE_0);
+ c_slide();
+ }
+
+ C_HANDLER( BACK_JUMP )
+ {
+ c_angle(-ANGLE_180);
+ c_jump();
+ }
+
+ C_HANDLER( RIGHT_JUMP )
+ {
+ c_angle(ANGLE_90);
+ c_jump();
+ }
+
+ C_HANDLER( LEFT_JUMP )
+ {
+ c_angle(-ANGLE_90);
+ c_jump();
+ }
+
+ C_HANDLER( UP_JUMP )
+ {
+ c_angle(ANGLE_0);
+ c_jump();
+ }
+
+ C_HANDLER( FALL_BACK )
+ {
+ c_angle(-ANGLE_180);
+ c_jump();
+ }
+
+ C_HANDLER( HANG_LEFT )
+ {
+ c_hang(-ANGLE_90);
+ }
+
+ C_HANDLER( HANG_RIGHT )
+ {
+ c_hang(ANGLE_90);
+ }
+
+ C_HANDLER( SLIDE_BACK )
+ {
+ c_angle(-ANGLE_180);
+ c_slide();
+ }
+
+ C_HANDLER( SURF_TREAD ) {} // TODO
+ C_HANDLER( SURF_SWIM ) {} // TODO
+ C_HANDLER( DIVE ) {} // TODO
+
+ C_HANDLER( PUSH_BLOCK )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( PULL_BLOCK )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( PUSH_PULL_READY )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( PICK_UP )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( SWITCH_DOWN )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( SWITCH_UP )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( USE_KEY )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( USE_PUZZLE )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( UW_DEATH ) {} // TODO
+
+ C_HANDLER( ROLL_START )
+ {
+ c_angle(ANGLE_0);
+ c_roll();
+ }
+
+ C_HANDLER( SPECIAL )
+ {
+ // empty
+ }
+
+ C_HANDLER( SURF_BACK ) {} // TODO
+ C_HANDLER( SURF_LEFT ) {} // TODO
+ C_HANDLER( SURF_RIGHT ) {} // TODO
+
+ C_HANDLER( MIDAS_USE )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( MIDAS_DEATH )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( SWAN_DIVE )
+ {
+ c_angle(ANGLE_0);
+ c_jump();
+ }
+
+ C_HANDLER( FAST_DIVE )
+ {
+ c_angle(ANGLE_0);
+ c_jump();
+ }
+
+ C_HANDLER( HANDSTAND )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+ C_HANDLER( WATER_OUT )
+ {
+ c_angle(ANGLE_0);
+ c_default();
+ }
+
+// update control
+ void updateInput()
+ {
+ input = 0;
+ if (camera.mode == CAMERA_MODE_FREE) return;
+
+ if (keys & IK_LEFT) input |= IN_LEFT;
+ if (keys & IK_RIGHT) input |= IN_RIGHT;
+ if (keys & IK_UP) input |= IN_UP;
+ if (keys & IK_DOWN) input |= IN_DOWN;
+ if (keys & IK_R) input |= IN_WALK;
+ if (keys & IK_A) input |= IN_ACTION;
+ if (keys & IK_B) input |= IN_JUMP;
+ if ((keys & (IK_L | IK_A)) == (IK_L | IK_A)) input |= IN_WEAPON;
+ if ((keys & (IK_L | IK_B)) == (IK_L | IK_B)) input |= IN_UP | IN_DOWN;
+ if ((keys & (IK_L | IK_R)) == (IK_L | IK_R)) input |= IN_LOOK;
+ }
+
+ void update()
+ {
+ updateInput();
+
+ cinfo.trigger = NULL;
+ cinfo.radius = LARA_RADIUS;
+ cinfo.pos = pos;
+ cinfo.enemyPush = true;
+ cinfo.enemyHit = true;
+ cinfo.stopOnSlant = false;
+ cinfo.stopOnLava = false;
+
+ updateState();
+
+ angleZ = angleDec(angleZ, 1 * DEG2SHORT);
+ turnSpeed = angleDec(turnSpeed, 2 * DEG2SHORT);
+ angleY += turnSpeed;
+
+ animUpdate(this);
+
+ updateCollision();
+
+ updateRoom(curItemIndex, -LARA_HEIGHT / 2);
+
+ //updateWeapon();
+ //checkTrigger(cinfo.trigger, false);
+ }
+};
+
+const Lara::Handler Lara::sHandlers[X_MAX] = { LARA_STATES(DECL_S_HANDLER) };
+const Lara::Handler Lara::cHandlers[X_MAX] = { LARA_STATES(DECL_C_HANDLER) };
+
+#undef DECL_ENUM
+#undef DECL_S_HANDLER
+#undef DECL_C_HANDLER
+#undef S_HANDLER
+#undef C_HANDLER
+
+#endif
diff --git a/src/platform/gba/level.h b/src/platform/gba/level.h
index 971bb93..0913f81 100644
--- a/src/platform/gba/level.h
+++ b/src/platform/gba/level.h
@@ -2,12 +2,9 @@
#define H_LEVEL
#include "common.h"
-#include "camera.h"
-
-#define GRAVITY 6
// level file data -------------------
-int32 tilesCount;
+//int32 tilesCount;
extern const uint8* tiles;
extern uint16 palette[256];
@@ -27,11 +24,20 @@ const int32* meshOffsets;
const int32* nodesPtr;
-int32 animsCount;
+//int32 animsCount;
const Anim* anims;
-int32 framesCount;
-const uint16* frames;
+//int32 animStatesCount;
+const AnimState* animStates;
+
+//int32 animRangesCount;
+const AnimRange* animRanges;
+
+//int32 animCommandsCount;
+const int16* animCommands;
+
+//int32 animFramesCount;
+const uint16* animFrames;
int32 modelsCount;
EWRAM_DATA Model models[MAX_MODELS];
@@ -43,34 +49,19 @@ const StaticMesh* staticMeshes;
int32 itemsCount;
EWRAM_DATA Item items[MAX_ITEMS];
+
+const uint16* soundMap;
+
+//int32 soundInfosCount;
+const SoundInfo* soundInfos;
+
+//int32 soundDataSize;
+const uint8* soundData;
+
+//int32 soundOffsetsCount;
+const uint32* soundOffsets;
// -----------------------------------
-struct Room {
- Rect clip;
- uint8 firstItem;
- bool visible;
-
- // TODO leave in ROM
- int32 x, z;
- uint16 vCount;
- uint16 qCount;
- uint16 tCount;
- uint16 pCount;
- uint16 lCount;
- uint16 mCount;
- uint16 zSectors;
- uint16 xSectors;
- uint16 ambient;
-
- const RoomInfo::Vertex* vertices;
- const Quad* quads;
- const Triangle* triangles;
- const RoomInfo::Portal* portals;
- const RoomInfo::Sector* sectors;
- const RoomInfo::Light* lights;
- const RoomInfo::Mesh* meshes;
-};
-
int16 roomsCount;
EWRAM_DATA Room rooms[64];
@@ -80,41 +71,24 @@ int32 visRoomsCount;
int32 visRooms[16];
#define ROOM_VISIBLE (1 << 15)
-#define SEQ_GLYPH 190
-
-enum FloorType {
- FLOOR_TYPE_NONE,
- FLOOR_TYPE_PORTAL,
- FLOOR_TYPE_FLOOR,
- FLOOR_TYPE_CEILING,
-};
-
-int32 seqGlyphs;
-
-extern uint32 gVerticesCount;
-extern Rect clip;
-
-void roomReset(int32 roomIndex)
-{
- Room* room = rooms + roomIndex;
-
- room->visible = false;
- room->clip = { FRAME_WIDTH, FRAME_HEIGHT, 0, 0 };
-}
void roomItemAdd(int32 roomIndex, int32 itemIndex)
{
- ASSERT(items[itemIndex].nextItem == NO_ITEM);
-
+ Item* item = items + itemIndex;
Room* room = rooms + roomIndex;
- items[itemIndex].nextItem = room->firstItem;
+ ASSERT(item->nextItem == NO_ITEM);
+
+ item->room = roomIndex;
+ item->nextItem = room->firstItem;
room->firstItem = itemIndex;
}
-void roomItemRemove(int32 roomIndex, int32 itemIndex)
+void roomItemRemove(int32 itemIndex)
{
- Room* room = rooms + roomIndex;
+ Item* item = items + itemIndex;
+ Room* room = rooms + item->room;
+ item->room = NO_ROOM;
int32 prevIndex = NO_ITEM;
int32 index = room->firstItem;
@@ -174,8 +148,28 @@ void deactivateItem(int32 itemIndex)
}
}
-void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_OFF alignment bytes
- tilesCount = *((int32*)(data + 4));
+void fixLightmap(int32 palIndex)
+{
+ uint16 color = palette[palIndex];
+
+ int32 r = 0x1F & (color);
+ int32 g = 0x1F & (color >> 5);
+ int32 b = 0x1F & (color >> 10);
+
+ for (int32 i = 0; i < 32; i++)
+ {
+ int32 lum = 31 - i;
+
+ int32 lumR = X_CLAMP((r * lum) / 14, 0, 31);
+ int32 lumG = X_CLAMP((g * lum) / 14, 0, 31);
+ int32 lumB = X_CLAMP((b * lum) / 14, 0, 31);
+
+ palette[lightmap[i * 256 + palIndex]] = lumR | (lumG << 5) | (lumB << 10);
+ }
+}
+
+void readLevel(const uint8* data) { // TODO non-hardcode level loader, added *_OFF alignment bytes
+// tilesCount = *((int32*)(data + 4));
tiles = data + 8;
#define MDL_OFF 2
@@ -189,13 +183,25 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O
meshData = data + 908172 + 4;
meshOffsets = (int32*)(data + 975724 + 4);
- animsCount = *((int32*)(data + 976596));
+// animsCount = *((int32*)(data + 976596));
anims = (Anim*)(data + 976596 + 4);
ASSERT((intptr_t)anims % 4 == 0);
- framesCount = *((int32*)(data + 992990));
- frames = (uint16*)(data + 992990 + 4);
- ASSERT((intptr_t)frames % 2 == 0);
+// animStatesCount = *((int32*)(data + 985464));
+ animStates = (AnimState*)(data + 985464 + 4);
+ ASSERT((intptr_t)animStates % 2 == 0);
+
+// animRangesCount = *((int32*)(data + 986872));
+ animRanges = (AnimRange*)(data + 986872 + 4);
+ ASSERT((intptr_t)animRanges % 2 == 0);
+
+// animCommandsCount = *((int32*)(data + 988868));
+ animCommands = (int16*)(data + 988868 + 4);
+ ASSERT((intptr_t)animCommands % 2 == 0);
+
+// animFramesCount = *((int32*)(data + 992990));
+ animFrames = (uint16*)(data + 992990 + 4);
+ ASSERT((intptr_t)animFrames % 2 == 0);
nodesPtr = (int32*)(data + 990318);
@@ -218,6 +224,18 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O
itemsCount = *((int32*)(data + 1319252 + MDL_OFF + ITM_OFF));
const uint8* itemsPtr = (data + 1319252 + 4 + MDL_OFF + ITM_OFF);
+ soundMap = (uint16*)(data + 1329540 + MDL_OFF + ITM_OFF);
+
+// soundInfosCount = *((int32*)(data + 1330052 + MDL_OFF + ITM_OFF));
+ soundInfos = (SoundInfo*)(data + 1330052 + 4 + MDL_OFF + ITM_OFF);
+
+// soundDataSize = *((int32*)(data + 1330624 + MDL_OFF + ITM_OFF));
+ soundData = (uint8*)(data + 1330624 + 4 + MDL_OFF + ITM_OFF);
+
+// soundOffsetsCount = *((int32*)(data + 2533294 + MDL_OFF + ITM_OFF));
+ soundOffsets = (uint32*)(data + 2533294 + 4 + MDL_OFF + ITM_OFF);
+
+ memset(items, 0, sizeof(items));
for (int32 i = 0; i < itemsCount; i++) {
memcpy(items + i, itemsPtr, FILE_ITEM_SIZE);
itemsPtr += FILE_ITEM_SIZE;
@@ -235,19 +253,28 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O
for (int i = 0; i < 256; i++)
{
#if defined(_WIN32) || defined(__GBA__)
+ // grayscale palette
+ //uint8 c = ((p[0] + p[1] + p[2]) / 3) >> 1;
+ //palette[i] = c | (c << 5) | (c << 10);
+
palette[i] = (p[0] >> 1) | ((p[1] >> 1) << 5) | ((p[2] >> 1) << 10);
#elif defined(__TNS__)
palette[i] = (p[2] >> 1) | ((p[1] >> 1) << 5) | ((p[0] >> 1) << 10);
#endif
p += 3;
}
+ palette[0] = 0; // black or transparent
+
+ // TODO preprocess fix Laras palette
+ fixLightmap(6); // boots
+ fixLightmap(14); // skin
// prepare rooms
- uint8 *ptr = (uint8*)roomsPtr;
+ uint8* ptr = (uint8*)roomsPtr;
for (int32 roomIndex = 0; roomIndex < roomsCount; roomIndex++)
{
- const RoomInfo *room = (RoomInfo*)ptr;
+ const RoomInfo* room = (RoomInfo*)ptr;
ptr += sizeof(RoomInfo);
uint32 dataSize;
@@ -255,7 +282,6 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O
uint8* skipPtr = ptr + dataSize * 2;
Room &desc = rooms[roomIndex];
- roomReset(roomIndex);
desc.firstItem = NO_ITEM;
@@ -327,485 +353,321 @@ void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_O
{
staticMeshesMap[staticMeshes[i].id] = i;
}
+}
-// prepare items
- for (int32 i = 0; i < itemsCount; i++) {
- Item* item = items + i;
-
- item->angleX = 0;
- item->angleZ = 0;
- item->vSpeed = 0;
- item->hSpeed = 0;
- item->nextItem = NO_ITEM;
- item->nextActive = NO_ITEM;
- item->animIndex = models[modelsMap[item->type]].animIndex;
- item->frameIndex = anims[item->animIndex].frameBegin;
- item->state = anims[item->animIndex].state;
- item->nextState = item->state;
- item->goalState = item->state;
- item->intensity = 4096; // TODO lighting
-
- item->flags.gravity = 0;
-
- if (item->room > -1) {
- roomItemAdd(item->room, i);
- }
-
- if (item->type == ITEM_LARA) {
- activateItem(i);
- }
-
- // TODO remove
- if (item->type == ITEM_WOLF ||
- item->type == ITEM_BEAR ||
- item->type == ITEM_BAT ||
- item->type == ITEM_CRYSTAL)
- {
- activateItem(i);
- }
+int32 getBridgeFloor(const Item* item, int32 x, int32 z)
+{
+ if (item->type == ITEM_BRIDGE_1) {
+ return item->pos.y;
}
-// prepare glyphs
- for (int32 i = 0; i < spritesSeqCount; i++) {
- if (spritesSeq[i].type == SEQ_GLYPH) {
- seqGlyphs = i;
+ int32 h;
+ if (item->angleY == ANGLE_0) {
+ h = 1024 - x;
+ } else if (item->angleY == -ANGLE_180) {
+ h = x;
+ } else if (item->angleY == ANGLE_90) {
+ h = z;
+ } else {
+ h = 1024 - z;
+ }
+
+ h &= 1023;
+
+ return item->pos.y + ((item->type == ITEM_BRIDGE_2) ? (h >> 2) : (h >> 1));
+}
+
+int32 getTrapDoorFloor(const Item* item, int32 x, int32 z)
+{
+ int32 dx = (item->pos.x >> 10) - (x >> 10);
+ int32 dz = (item->pos.z >> 10) - (z >> 10);
+
+ if (((dx == 0) && (dz == 0)) ||
+ ((dx == 0) && (dz == 1) && (item->angleY == ANGLE_0)) ||
+ ((dx == 0) && (dz == -1) && (item->angleY == -ANGLE_180)) ||
+ ((dx == 1) && (dz == 0) && (item->angleY == ANGLE_90)) ||
+ ((dx == -1) && (dz == 0) && (item->angleY == -ANGLE_90)))
+ {
+ return item->pos.y;
+ }
+
+ return WALL;
+}
+
+int32 getDrawBridgeFloor(const Item* item, int32 x, int32 z)
+{
+ int32 dx = (item->pos.x >> 10) - (x >> 10);
+ int32 dz = (item->pos.z >> 10) - (z >> 10);
+
+ if (((dx == 0) && ((dz == -1) || (dz == -2)) && (item->angleY == ANGLE_0)) ||
+ ((dx == 0) && ((dz == 1) || (dz == 2)) && (item->angleY == -ANGLE_180)) ||
+ ((dz == 0) && ((dx == -1) || (dz == -2)) && (item->angleY == ANGLE_90)) ||
+ ((dz == 0) && ((dx == 1) || (dz == 2)) && (item->angleY == -ANGLE_90)))
+ {
+ return item->pos.y;
+ }
+
+ return WALL;
+}
+
+void getItemFloorData(const Item* item, int32 x, int32 y, int32 z, int32* floor, int32* ceiling)
+{
+ int32 h = WALL;
+
+ switch (item->type)
+ {
+ case ITEM_TRAP_FLOOR:
+ {
+ if (item->state == 0 || item->state == 1) {
+ h = item->pos.y - 512;
+ }
break;
}
- }
-
- camera.init();
- camera.room = 0;
-}
-
-void drawMesh(int16 meshIndex, uint16 intensity) {
- int32 offset = meshOffsets[meshIndex];
- const uint8* ptr = meshData + offset;
-
- ptr += 2 * 5; // skip [cx, cy, cz, radius, flags]
-
- int16 vCount = *(int16*)ptr; ptr += 2;
- const vec3s* vertices = (vec3s*)ptr;
- ptr += vCount * 3 * sizeof(int16);
-
- int16 nCount = *(int16*)ptr; ptr += 2;
- //const int16* normals = (int16*)ptr;
- if (nCount > 0) { // normals
- ptr += nCount * 3 * sizeof(int16);
- } else { // intensity
- ptr += vCount * sizeof(int16);
- }
-
- int16 rCount = *(int16*)ptr; ptr += 2;
- Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad);
-
- int16 tCount = *(int16*)ptr; ptr += 2;
- Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle);
-
- int16 crCount = *(int16*)ptr; ptr += 2;
- Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad);
-
- int16 ctCount = *(int16*)ptr; ptr += 2;
- Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle);
-
- int32 startVertex = gVerticesCount;
-
- PROFILE_START();
- transformMesh(vertices, vCount, intensity);
- PROFILE_STOP(dbg_transform);
-
- PROFILE_START();
- faceAddMesh(rFaces, crFaces, tFaces, ctFaces, rCount, crCount, tCount, ctCount, startVertex);
- PROFILE_STOP(dbg_poly);
-}
-
-Frame* getFrame(const Item* item, const Model* model)
-{
- const Anim* anim = anims + item->animIndex;
-
- int32 frameSize = sizeof(Frame) / 2 + model->mCount * 2;
- int32 frameIndex = (item->frameIndex - anim->frameBegin) / anim->frameRate;//* FixedInvU(anim->frameRate) >> 16;
-
- return (Frame*)(frames + anim->frameOffset / 2 + frameIndex * frameSize);
-}
-
-void drawItem(const Item* item) {
- int32 modelIndex = modelsMap[item->type];
- if (modelIndex == NO_MODEL) {
- return; // TODO sprite items
- }
-
- const Model* model = models + modelIndex;
-
- if (model->mCount == 1 && meshOffsets[model->mStart] == 0) return;
-
- Frame* frame = getFrame(item, model);
- uint16* frameAngles = frame->angles + 1;
-
- matrixPush();
- matrixTranslateAbs(item->pos.x, item->pos.y, item->pos.z);
- matrixRotateYXZ(item->angleX, item->angleY, item->angleZ);
-
- if (boxIsVisible(&frame->box)) {
- // non-aligned access (TODO)
- uint32 nodeIndex;
- memcpy(&nodeIndex, &model->nodeIndex, sizeof(nodeIndex));
- Node nodes[32];
- memcpy(nodes, nodesPtr + nodeIndex, (model->mCount - 1) * sizeof(Node));
-
- const Node* node = nodes;
-
- matrixFrame(frame->pos.x, frame->pos.y, frame->pos.z, frameAngles);
-
- drawMesh(model->mStart, item->intensity);
-
- for (int32 i = 1; i < model->mCount; i++)
+ case ITEM_DRAWBRIDGE:
{
- if (node->flags & 1) matrixPop();
- if (node->flags & 2) matrixPush();
-
- frameAngles += 2;
- matrixFrame(node->pos.x, node->pos.y, node->pos.z, frameAngles);
-
- drawMesh(model->mStart + i, item->intensity);
-
- node++;
+ if (item->state == 1) {
+ h = getDrawBridgeFloor(item, x, z);
+ }
+ break;
}
- }
-
- matrixPop();
-}
-
-void drawNumber(int32 number, int32 x, int32 y) {
- const int32 widths[] = { 12, 8, 10, 10, 10, 10, 10, 10, 10, 10 };
-
- const Sprite *glyphSprites = sprites + spritesSeq[seqGlyphs].sStart;
-
- while (number > 0) {
- x -= widths[number % 10];
- drawGlyph(glyphSprites + 52 + (number % 10), x, y);
- number /= 10;
- }
-}
-
-void drawRoom(int32 roomIndex) {
- const Room* room = rooms + roomIndex;
-
- clip = room->clip;
-
- int32 startVertex = gVerticesCount;
-
- matrixPush();
- matrixTranslateAbs(room->x, 0, room->z);
-
- PROFILE_START();
- transformRoom(room->vertices, room->vCount);
- PROFILE_STOP(dbg_transform);
-
- matrixPop();
-
- PROFILE_START();
- faceAddRoom(room->quads, room->qCount, room->triangles, room->tCount, startVertex);
-
- for (int32 i = 0; i < room->mCount; i++)
- {
- const RoomInfo::Mesh* mesh = room->meshes + i;
- const StaticMesh* staticMesh = staticMeshes + staticMeshesMap[mesh->staticMeshId];
-
- if (!(staticMesh->flags & 2)) continue; // invisible
-
- // TODO align RoomInfo::Mesh (room relative int16?)
- vec3i pos;
- memcpy(&pos, &mesh->pos, sizeof(pos));
-
- matrixPush();
- matrixTranslateAbs(pos.x, pos.y, pos.z);
- matrixRotateY(mesh->rotation);
-
- if (boxIsVisible(&staticMesh->vbox)) {
- drawMesh(staticMesh->meshIndex, mesh->intensity);
+ case ITEM_BRIDGE_1:
+ case ITEM_BRIDGE_2:
+ case ITEM_BRIDGE_3:
+ {
+ h = getBridgeFloor(item, x, z);
+ break;
}
+ case ITEM_TRAP_DOOR_1:
+ case ITEM_TRAP_DOOR_2:
+ {
+ if (item->state == 0) {
+ h = getTrapDoorFloor(item, x, z);
+ }
- matrixPop();
+ if ((floor && (h >= *floor)) || (ceiling && (h <= *ceiling)))
+ {
+ h = WALL;
+ }
+ }
+ default : return;
}
- int32 itemIndex = room->firstItem;
- while (itemIndex != NO_ITEM)
+ if (floor && (y <= h))
{
- drawItem(items + itemIndex);
- itemIndex = items[itemIndex].nextItem;
+ *floor = h;
}
- PROFILE_STOP(dbg_poly);
- roomReset(roomIndex);
-
- flush();
+ if (ceiling && (y > h))
+ {
+ *ceiling = h + 256;
+ }
}
-const RoomInfo::Sector* getSector(int32 roomIndex, int32 x, int32 z) {
+const RoomInfo::Sector* getSector(int32 roomIndex, int32 x, int32 z)
+{
Room &room = rooms[roomIndex];
- int32 sx = clamp((x - room.x) >> 10, 0, room.xSectors - 1);
- int32 sz = clamp((z - room.z) >> 10, 0, room.zSectors - 1);
+ int32 sx = X_CLAMP((x - room.x) >> 10, 0, room.xSectors - 1);
+ int32 sz = X_CLAMP((z - room.z) >> 10, 0, room.zSectors - 1);
return room.sectors + sx * room.zSectors + sz;
}
-int32 getRoomIndex(int32 roomIndex, const vec3i* pos) {
- const RoomInfo::Sector *sector = getSector(roomIndex, pos->x, pos->z);
-
- if (sector->floorIndex) {
- const uint16 *data = floors + sector->floorIndex;
- int16 type = *data++;
-
- if (type == FLOOR_TYPE_FLOOR) {
- data++;
- type = *data++;
- }
-
- if (type == FLOOR_TYPE_CEILING) {
- data++;
- type = *data++;
- }
-
- if ((type & 0xFF) == FLOOR_TYPE_PORTAL) {
- roomIndex = *data;
- }
+const RoomInfo::Sector* getSectorBelow(const RoomInfo::Sector* sector, int32 x, int32 z)
+{
+ while (sector->roomBelow != NO_ROOM)
+ {
+ Room* room = rooms + sector->roomBelow;
+ int32 sx = (x - room->x) >> 10;
+ int32 sz = (z - room->z) >> 10;
+ sector = room->sectors + sx * room->zSectors + sz;
}
+ return sector;
+}
- while (sector->roomAbove != NO_ROOM && pos->y < (sector->ceiling << 8)) {
+const RoomInfo::Sector* getSectorAbove(const RoomInfo::Sector* sector, int32 x, int32 z)
+{
+ while (sector->roomAbove != NO_ROOM)
+ {
+ Room* room = rooms + sector->roomAbove;
+ int32 sx = (x - room->x) >> 10;
+ int32 sz = (z - room->z) >> 10;
+ sector = room->sectors + sx * room->zSectors + sz;
+ }
+ return sector;
+}
+
+int32 getRoomIndex(int32 roomIndex, int32 x, int32 y, int32 z)
+{
+ const RoomInfo::Sector* sector = getSector(roomIndex, x, z);
+
+ while (1)
+ {
+ if (!sector->floorIndex)
+ break;
+
+ // always in this order
+ // - floor
+ // - ceiling
+ // - portal
+ // - other
+
+ FloorData* fd = (FloorData*)(floors + sector->floorIndex);
+ FloorData::Command cmd = (fd++)->cmd;
+
+ if (cmd.func == FLOOR_TYPE_FLOOR) // skip floor
+ {
+ if (cmd.end) break;
+ fd++;
+ cmd = (fd++)->cmd;
+ }
+
+ if (cmd.func == FLOOR_TYPE_CEILING) // skip ceiling
+ {
+ if (cmd.end) break;
+ fd++;
+ cmd = (fd++)->cmd;
+ }
+
+ if (cmd.func != FLOOR_TYPE_PORTAL) // no portal
+ break;
+
+ roomIndex = fd->value;
+ sector = getSector(roomIndex, x, z);
+ };
+
+ while (sector->roomAbove != NO_ROOM && y < (sector->ceiling << 8))
+ {
roomIndex = sector->roomAbove;
- sector = getSector(roomIndex, pos->x, pos->z);
+ sector = getSector(roomIndex, x, z);
}
- while (sector->roomBelow != 0xFF && pos->y >= (sector->floor << 8)) {
+ while (sector->roomBelow != NO_ROOM && y >= (sector->floor << 8))
+ {
roomIndex = sector->roomBelow;
- sector = getSector(roomIndex, pos->x, pos->z);
+ sector = getSector(roomIndex, x, z);
}
return roomIndex;
}
-bool checkPortal(int32 roomIndex, const RoomInfo::Portal* portal) {
- Room &room = rooms[roomIndex];
-
- vec3i d;
- d.x = portal->v[0].x - camera.pos.x + room.x;
- d.y = portal->v[0].y - camera.pos.y;
- d.z = portal->v[0].z - camera.pos.z + room.z;
-
- if (DP33(portal->n, d) >= 0) {
- return false;
- }
-
- int32 x0 = room.clip.x1;
- int32 y0 = room.clip.y1;
- int32 x1 = room.clip.x0;
- int32 y1 = room.clip.y0;
-
- int32 znear = 0, zfar = 0;
-
- Matrix &m = matrixGet();
-
- vec3i pv[4];
-
- for (int32 i = 0; i < 4; i++) {
- const vec3s &v = portal->v[i];
-
- int32 x = DP43(m[0], v);
- int32 y = DP43(m[1], v);
- int32 z = DP43(m[2], v);
-
- pv[i].x = x;
- pv[i].y = y;
- pv[i].z = z;
-
- if (z <= VIEW_MIN_F) {
- znear++;
- continue;
- }
-
- if (z >= VIEW_MAX_F) {
- zfar++;
- }
-
- if (z != 0) {
- z >>= FOV_SHIFT;
- x = (x / z) + (FRAME_WIDTH / 2);
- y = (y / z) + (FRAME_HEIGHT / 2);
- } else {
- x = (x > 0) ? clip.x1 : clip.x0;
- y = (y > 0) ? clip.y1 : clip.y0;
- }
-
- if (x < x0) x0 = x;
- if (x > x1) x1 = x;
- if (y < y0) y0 = y;
- if (y > y1) y1 = y;
- }
-
- if (znear == 4 || zfar == 4) return false;
-
- if (znear) {
- vec3i *a = pv;
- vec3i *b = pv + 3;
- for (int32 i = 0; i < 4; i++) {
- if ((a->z < 0) ^ (b->z < 0)) {
- if (a->x < 0 && b->x < 0) {
- x0 = 0;
- } else if (a->x > 0 && b->x > 0) {
- x1 = FRAME_WIDTH;
- } else {
- x0 = 0;
- x1 = FRAME_WIDTH;
- }
-
- if (a->y < 0 && b->y < 0) {
- y0 = 0;
- } else if (a->y > 0 && b->y > 0) {
- y1 = FRAME_HEIGHT;
- } else {
- y0 = 0;
- y1 = FRAME_HEIGHT;
- }
- }
- b = a;
- a++;
- }
- }
-
- if (x0 < room.clip.x0) x0 = room.clip.x0;
- if (x1 > room.clip.x1) x1 = room.clip.x1;
- if (y0 < room.clip.y0) y0 = room.clip.y0;
- if (y1 > room.clip.y1) y1 = room.clip.y1;
-
- if (x0 >= x1 || y0 >= y1) return false;
-
- Room &nextRoom = rooms[portal->roomIndex];
-
- if (x0 < nextRoom.clip.x0) nextRoom.clip.x0 = x0;
- if (x1 > nextRoom.clip.x1) nextRoom.clip.x1 = x1;
- if (y0 < nextRoom.clip.y0) nextRoom.clip.y0 = y0;
- if (y1 > nextRoom.clip.y1) nextRoom.clip.y1 = y1;
-
- if (!nextRoom.visible) {
- nextRoom.visible = true;
- visRooms[visRoomsCount++] = portal->roomIndex;
- }
-
- return true;
-}
-
-void getVisibleRooms(int32 roomIndex)
+void getTriggerFloorData(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z, int32* floor, int32* ceiling)
{
- const Room* room = rooms + roomIndex;
+ if (!sector->floorIndex)
+ return;
- matrixPush();
- matrixTranslateAbs(room->x, 0, room->z);
+ FloorData::Command cmd;
+ FloorData* fd = (FloorData*)(floors + sector->floorIndex);
- for (int32 i = 0; i < room->pCount; i++)
- {
- const RoomInfo::Portal* portal = room->portals + i;
-
- if (checkPortal(roomIndex, portal))
+ do {
+ cmd = (fd++)->cmd;
+
+ switch (cmd.func)
{
- getVisibleRooms(portal->roomIndex);
+ case FLOOR_TYPE_PORTAL:
+ case FLOOR_TYPE_FLOOR:
+ case FLOOR_TYPE_CEILING:
+ {
+ fd++;
+ break;
+ }
+
+ case FLOOR_TYPE_TRIGGER:
+ {
+ fd++;
+ FloorData::TriggerCommand trigger;
+
+ do {
+ trigger = (fd++)->triggerCmd;
+
+ if (trigger.action == TRIGGER_ACTION_ACTIVATE)
+ {
+ getItemFloorData(items + trigger.args, x, y, z, floor, ceiling);
+ }
+
+ if (trigger.action == TRIGGER_ACTION_CAMERA_SWITCH)
+ {
+ trigger = (fd++)->triggerCmd; // skip camera index
+ }
+
+ } while (!trigger.end);
+
+ break;
+ }
+
+ case FLOOR_TYPE_LAVA:
+ break;
+ }
+
+ } while (!cmd.end);
+}
+
+FloorData floorSlant;
+
+int32 getFloor(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z)
+{
+ const RoomInfo::Sector* lowerSector = getSectorBelow(sector, x, z);
+
+ int32 floor = lowerSector->floor << 8;
+
+ floorSlant.value = 0;
+
+ if (lowerSector->floorIndex)
+ {
+ FloorData* fd = (FloorData*)(floors + lowerSector->floorIndex);
+ FloorData::Command cmd = (fd++)->cmd;
+
+ if (cmd.func == FLOOR_TYPE_FLOOR) // found floor
+ {
+ floorSlant = *fd;
+ int32 sx = fd->slantX;
+ int32 sz = fd->slantZ;
+ int32 dx = x & 1023;
+ int32 dz = z & 1023;
+ floor -= sx * (sx < 0 ? dx : (dx - 1023)) >> 2;
+ floor -= sz * (sz < 0 ? dz : (dz - 1023)) >> 2;
}
}
- matrixPop();
+ getTriggerFloorData(lowerSector, x, y, z, &floor, NULL);
+
+ return floor;
}
-void drawRooms()
+int32 getCeiling(const RoomInfo::Sector* sector, int32 x, int32 y, int32 z)
{
- rooms[camera.room].clip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT };
- visRoomsCount = 0;
- visRooms[visRoomsCount++] = camera.room;
+ const RoomInfo::Sector* upperSector = getSectorAbove(sector, x, z);
- getVisibleRooms(camera.room);
+ int32 ceiling = upperSector->ceiling << 8;
- while (visRoomsCount--)
+ if (upperSector->floorIndex)
{
- drawRoom(visRooms[visRoomsCount]);
- }
-}
+ FloorData* fd = (FloorData*)(floors + upperSector->floorIndex);
+ FloorData::Command cmd = (fd++)->cmd;
-void move(Item* item, const Anim* anim)
-{
- int32 speed = anim->speed;
-
- if (item->flags.gravity)
- {
- speed += anim->accel * (item->frameIndex - anim->frameBegin - 1);
- item->hSpeed -= speed >> 16;
- speed += anim->accel;
- item->hSpeed += speed >> 16;
-
- item->vSpeed += (item->vSpeed < 128) ? GRAVITY : 1;
-
- item->pos.y += item->vSpeed;
- } else {
- speed += anim->accel * (item->frameIndex - anim->frameBegin);
-
- item->hSpeed = speed >> 16;
- }
-
- item->pos.x += phd_sin(item->angleY) * item->hSpeed >> FIXED_SHIFT;
- item->pos.z += phd_cos(item->angleY) * item->hSpeed >> FIXED_SHIFT;
-}
-
-void animChange(Item* item, const Anim* anim)
-{
- if (!anim->scCount) return;
- // check state change
-}
-
-void animCommand(bool fx, Item* item, const Anim* anim)
-{
- if (!anim->acCount) return;
- // check animation command
-}
-
-const Anim* animSet(Item* item, int32 animIndex, int32 frameIndex)
-{
- item->animIndex = animIndex;
- item->frameIndex = frameIndex;
- item->state = anims[animIndex].state;
-
- return anims + animIndex;
-}
-
-void animUpdate(Item* item)
-{
- const Anim* anim = anims + item->animIndex;
-
- item->frameIndex++;
-
- animChange(item, anim);
-
- if (item->frameIndex > anim->frameEnd)
- {
- animCommand(false, item, anim);
- anim = animSet(item, anim->nextAnimIndex, anim->nextFrameIndex);
- }
-
- animCommand(true, item, anim);
-
- //move(item, anim);
-}
-
-void updateItems()
-{
- int32 itemIndex = firstActive;
- while (itemIndex != NO_ITEM)
- {
- Item* item = items + itemIndex;
-
- if (modelsMap[item->type] != NO_MODEL) {
- animUpdate(item);
+ if (cmd.func == FLOOR_TYPE_FLOOR) // skip floor
+ {
+ fd++;
+ cmd = (fd++)->cmd;
}
- itemIndex = item->nextActive;
+ if (cmd.func == FLOOR_TYPE_CEILING) // found ceiling
+ {
+ int32 sx = fd->slantX;
+ int32 sz = fd->slantZ;
+ int32 dx = x & 1023;
+ int32 dz = z & 1023;
+ ceiling -= sx * (sx < 0 ? (dx - 1023) : dx) >> 2;
+ ceiling += sz * (sz < 0 ? dz : (dz - 1023)) >> 2;
+ }
}
+
+ const RoomInfo::Sector* lowerSector = getSectorBelow(sector, x, z);
+
+ getTriggerFloorData(lowerSector, x, y, z, NULL, &ceiling);
+
+ return ceiling;
}
+
#endif
diff --git a/src/platform/gba/main.cpp b/src/platform/gba/main.cpp
index 87dcf8b..605c42f 100644
--- a/src/platform/gba/main.cpp
+++ b/src/platform/gba/main.cpp
@@ -1,27 +1,28 @@
-#include "common.h"
-#include "level.h"
-#include "camera.h"
+#if defined(_WIN32)
+ void* LEVEL1_PHD;
+#elif defined(__GBA__)
+ #include "LEVEL1_PHD.h"
+#elif defined(__TNS__)
+ void* LEVEL1_PHD;
+#endif
+
+#include "game.h"
+
+Game game;
#if defined(_WIN32)
- uint8* LEVEL1_PHD;
+ uint32 SCREEN[FRAME_WIDTH * FRAME_HEIGHT];
- uint32 SCREEN[WIDTH * HEIGHT];
-
- extern uint16 fb[WIDTH * HEIGHT];
+ HWND hWnd;
LARGE_INTEGER g_timer;
LARGE_INTEGER g_current;
- #define WND_SCALE 4
+ #define WND_WIDTH 240*4
+ #define WND_HEIGHT 160*4
#elif defined(__GBA__)
- #include "LEVEL1_PHD.h"
-
- extern uint32 fb;
+ //
#elif defined(__TNS__)
- uint8* LEVEL1_PHD;
-
- extern uint16 fb[WIDTH * HEIGHT];
-
unsigned int osTime;
volatile unsigned int *timerBUS;
volatile unsigned int *timerCLK;
@@ -78,8 +79,6 @@
}
#endif
-bool keys[IK_MAX] = {};
-
int32 fps;
int32 frameIndex = 0;
int32 fpsCounter = 0;
@@ -87,188 +86,146 @@ int32 fpsCounter = 0;
#ifdef PROFILE
uint32 dbg_transform;
uint32 dbg_poly;
- uint32 dbg_sort;
uint32 dbg_flush;
uint32 dbg_vert_count;
uint32 dbg_poly_count;
#endif
-void update(int32 frames) {
- for (int32 i = 0; i < frames; i++) {
- updateItems();
- camera.update();
+EWRAM_DATA ALIGN16 uint8 soundBuffer[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt
+uint32 curSoundBuffer = 0;
+
+#if defined(_WIN32)
+HWAVEOUT waveOut;
+WAVEFORMATEX waveFmt = { WAVE_FORMAT_PCM, 1, SND_OUTPUT_FREQ, SND_OUTPUT_FREQ, 1, 8, sizeof(waveFmt) };
+WAVEHDR waveBuf[2];
+
+void soundInit() {
+ sound.init();
+
+ if (waveOutOpen(&waveOut, WAVE_MAPPER, &waveFmt, (INT_PTR)hWnd, 0, CALLBACK_WINDOW) != MMSYSERR_NOERROR) {
+ return;
+ }
+
+ memset(&waveBuf, 0, sizeof(waveBuf));
+ for (int i = 0; i < 2; i++) {
+ WAVEHDR *waveHdr = waveBuf + i;
+ waveHdr->dwBufferLength = SND_SAMPLES;
+ waveHdr->lpData = (LPSTR)(soundBuffer + i * SND_SAMPLES);
+ waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR));
+ waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR));
}
}
-#ifdef TEST
-void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex);
+void soundFill() {
+ WAVEHDR *waveHdr = waveBuf + curSoundBuffer;
+ waveOutUnprepareHeader(waveOut, waveHdr, sizeof(WAVEHDR));
+ sound.fill((uint8*)waveHdr->lpData, SND_SAMPLES);
+ waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR));
+ waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR));
+ curSoundBuffer ^= 1;
+}
+#elif defined(__GBA__)
+void soundInit()
+{
+ sound.init();
-extern Vertex gVertices[MAX_VERTICES];
-
-INLINE int32 classify(const Vertex* v) {
- return (v->x < clip.x0 ? 1 : 0) |
- (v->x > clip.x1 ? 2 : 0) |
- (v->y < clip.y0 ? 4 : 0) |
- (v->y > clip.y1 ? 8 : 0);
+ REG_SOUNDCNT_X = SSTAT_ENABLE;
+ REG_SOUNDCNT_H = SDS_ATMR0 | SDS_A100 | SDS_AL | SDS_AR | SDS_ARESET;
+ REG_TM0D = 65536 - (16777216 / SND_OUTPUT_FREQ);
+ REG_TM0CNT = TM_ENABLE;
+ REG_DMA1DAD = (u32)®_FIFO_A;
}
-void drawTest() {
- static Rect testClip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT };
- static int32 testTile = 707; // 712
-
-#ifdef _WIN32
- Sleep(16);
-
- int dx = 0;
- int dy = 0;
-
- if (GetAsyncKeyState(VK_LEFT)) dx--;
- if (GetAsyncKeyState(VK_RIGHT)) dx++;
- if (GetAsyncKeyState(VK_UP)) dy--;
- if (GetAsyncKeyState(VK_DOWN)) dy++;
-
- if (GetAsyncKeyState('T')) {
- testClip.x0 += dx;
- testClip.y0 += dy;
+void soundFill()
+{
+ if (curSoundBuffer == 1) {
+ REG_DMA1CNT = 0;
+ REG_DMA1SAD = (u32)soundBuffer;
+ REG_DMA1CNT = DMA_DST_FIXED | DMA_REPEAT | DMA_16 | DMA_AT_FIFO | DMA_ENABLE;
}
- if (GetAsyncKeyState('B')) {
- testClip.x1 += dx;
- testClip.y1 += dy;
- }
-
- if (GetAsyncKeyState('U')) {
- testTile += dx;
- if (testTile < 0) testTile = 0;
- if (testTile >= texturesCount) testTile = texturesCount - 1;
- }
-#endif
-
- clip = testClip;
-
- gVertices[0].x = 50 + 50;
- gVertices[0].y = 50;
-
- gVertices[1].x = FRAME_WIDTH - 50 - 50;
- gVertices[1].y = 50;
-
- gVertices[2].x = FRAME_WIDTH - 50;
- gVertices[2].y = FRAME_HEIGHT - 50;
-
- gVertices[3].x = 50;
- gVertices[3].y = FRAME_HEIGHT - 50;
-
- for (int i = 0; i < 4; i++) {
- gVertices[i].z = 100;
- gVertices[i].g = 128;
- gVertices[i].clip = classify(gVertices + i);
- }
- gVerticesCount = 4;
-
- Index indices[] = { 0, 1, 2, 3, 0, 2, 3 };
-
- faceAddQuad(testTile, indices, 0);
-
-#ifdef _WIN32
- for (int y = 0; y < FRAME_HEIGHT; y++) {
- for (int x = 0; x < FRAME_WIDTH; x++) {
- if (x == clip.x0 || x == clip.x1 - 1 || y == clip.y0 || y == clip.y1 - 1)
- fb[y * FRAME_WIDTH + x] = 255;
- }
- }
-#endif
-
- flush();
+ sound.fill(soundBuffer + curSoundBuffer * SND_SAMPLES, SND_SAMPLES);
+ curSoundBuffer ^= 1;
}
#endif
-void render() {
- clear();
-
- #ifdef TEST
- #ifdef __GBA__
- VBlankIntrWait();
- #endif
-
- int32 cycles = 0;
- PROFILE_START();
- drawTest();
- PROFILE_STOP(cycles);
-
- drawNumber(cycles, FRAME_WIDTH, 32);
- #else
- #ifdef PROFILE
- dbg_transform = 0;
- dbg_poly = 0;
- dbg_sort = 0;
- dbg_flush = 0;
- dbg_vert_count = 0;
- dbg_poly_count = 0;
- #endif
-
- drawRooms();
-
- #ifdef PROFILE
- drawNumber(dbg_transform, FRAME_WIDTH, 32);
- drawNumber(dbg_poly, FRAME_WIDTH, 48);
- drawNumber(dbg_sort, FRAME_WIDTH, 64);
- drawNumber(dbg_flush, FRAME_WIDTH, 80);
- drawNumber(dbg_vert_count, FRAME_WIDTH, 96);
- drawNumber(dbg_poly_count, FRAME_WIDTH, 112);
- #endif
-
- #endif
-
- drawNumber(fps, FRAME_WIDTH, 16);
-}
-
#if defined(_WIN32)
HDC hDC;
void blit() {
- for (int i = 0; i < WIDTH * HEIGHT; i++) {
+#ifdef ROTATE90_MODE
+ for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; i++) {
+ int32 x = FRAME_HEIGHT - (i % FRAME_HEIGHT) - 1;
+ int32 y = i / FRAME_HEIGHT;
+ uint16 c = ((uint16*)fb)[x * FRAME_WIDTH + y];
+ SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000;
+ }
+ const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), FRAME_HEIGHT, -FRAME_WIDTH, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
+ StretchDIBits(hDC, 0, 0, WND_WIDTH, WND_HEIGHT, 0, 0, FRAME_HEIGHT, FRAME_WIDTH, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY);
+#else
+ for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; i++) {
uint16 c = ((uint16*)fb)[i];
SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000;
}
-
- const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), WIDTH, -HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
- StretchDIBits(hDC, 0, 0, 240 * WND_SCALE, 160 * WND_SCALE, 0, 0, WIDTH, HEIGHT, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY);
+ const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), FRAME_WIDTH, -FRAME_HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
+ StretchDIBits(hDC, 0, 0, WND_WIDTH, WND_HEIGHT, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY);
+#endif
}
LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY :
+ {
PostQuitMessage(0);
break;
+ }
+
case WM_KEYDOWN :
- case WM_KEYUP : {
- InputKey key = IK_MAX;
+ case WM_KEYUP :
+ {
+ InputKey key = IK_NONE;
switch (wParam) {
- case VK_UP : key = IK_UP; break;
- case VK_RIGHT : key = IK_RIGHT; break;
- case VK_DOWN : key = IK_DOWN; break;
- case VK_LEFT : key = IK_LEFT; break;
- case 'Z' : key = IK_A; break;
- case 'X' : key = IK_B; break;
- case 'A' : key = IK_L; break;
- case 'S' : key = IK_R; break;
+ case VK_UP : key = IK_UP; break;
+ case VK_RIGHT : key = IK_RIGHT; break;
+ case VK_DOWN : key = IK_DOWN; break;
+ case VK_LEFT : key = IK_LEFT; break;
+ case 'Z' : key = IK_B; break;
+ case 'X' : key = IK_A; break;
+ case 'A' : key = IK_L; break;
+ case 'S' : key = IK_R; break;
+ case VK_RETURN : key = IK_START; break;
+ case VK_SPACE : key = IK_SELECT; break;
}
- if (key != IK_MAX) {
- keys[key] = msg != WM_KEYUP;
+
+ if (msg != WM_KEYUP) {
+ keys |= key;
+ } else {
+ keys &= ~key;
}
break;
}
+
+ case MM_WOM_DONE :
+ {
+ soundFill();
+ break;
+ }
+
default :
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
-#endif
+
+#elif defined(__GBA__)
void vblank() {
frameIndex++;
+ soundFill();
}
+#endif
+
int main(void) {
#if defined(_WIN32) || defined(__TNS__)
{
@@ -293,27 +250,26 @@ int main(void) {
}
#elif defined(__GBA__)
// set low latency mode via WAITCNT register (thanks to GValiente)
- #define REG_WAITCNT_NV *(u16*)(REG_BASE + 0x0204)
- REG_WAITCNT_NV |= (0x0008 | 0x0010 | 0x4000);
+ REG_WSCNT = WS_ROM0_N2 | WS_ROM0_S1 | WS_PREFETCH;
#endif
- initLUT();
-
- readLevel(LEVEL1_PHD);
+ game.init();
#if defined(_WIN32)
- RECT r = { 0, 0, 240 * WND_SCALE, 160 * WND_SCALE };
+ RECT r = { 0, 0, WND_WIDTH, WND_HEIGHT };
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false);
int wx = (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2;
int wy = (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2;
- HWND hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0);
+ hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0);
hDC = GetDC(hWnd);
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&wndProc);
ShowWindow(hWnd, SW_SHOWDEFAULT);
+ soundInit();
+
MSG msg;
int startTime = GetTickCount() - 33;
@@ -326,51 +282,68 @@ int main(void) {
} else {
int frame = (GetTickCount() - startTime) / 33;
- update(frame - lastFrame);
+ game.update(frame - lastFrame);
lastFrame = frame;
- render();
+ game.render();
blit();
}
} while (msg.message != WM_QUIT);
#elif defined(__GBA__)
- irqInit();
- irqSet(IRQ_VBLANK, vblank);
- irqEnable(IRQ_VBLANK);
+ irq_init(NULL);
+ irq_add(II_VBLANK, vblank);
+ irq_enable(II_VBLANK);
- uint16 mode = BG2_ON | BACKBUFFER;
+ // Undocumented - Green Swap trick for fake-AA feature (thanks to GValiente)
+ // https://mgba-emu.github.io/gbatek/#4000002h---undocumented---green-swap-rw
+ //uint16 &GreenSwap = *(uint16*)0x4000002;
+ //GreenSwap = 1;
- mode |= MODE_5;
+ soundInit();
+ uint16 mode = DCNT_MODE5 | DCNT_BG2 | DCNT_PAGE;
+
+#ifdef ROTATE90_MODE
+ REG_BG2PA = 0;
+ REG_BG2PB = (1 << 8);
+ REG_BG2PC = -(1 << 7);
+ REG_BG2PD = 0;
+ REG_BG2Y = (FRAME_HEIGHT << 8) - 128;
+#else
REG_BG2PA = 256 - 64 - 16 - 4 - 1;
REG_BG2PD = 256 - 48 - 2;
+#endif
int32 lastFrameIndex = -1;
while (1) {
//VBlankIntrWait();
+ REG_DISPCNT = (mode ^= DCNT_PAGE);
- SetMode(mode ^= BACKBUFFER);
fb ^= 0xA000;
- scanKeys();
- uint16 key = keysDown() | keysHeld();
- keys[IK_UP] = (key & KEY_UP);
- keys[IK_RIGHT] = (key & KEY_RIGHT);
- keys[IK_DOWN] = (key & KEY_DOWN);
- keys[IK_LEFT] = (key & KEY_LEFT);
- keys[IK_A] = (key & KEY_A);
- keys[IK_B] = (key & KEY_B);
- keys[IK_L] = (key & KEY_L);
- keys[IK_R] = (key & KEY_R);
+ { // input
+ keys = 0;
+ key_poll();
+ if (key_is_down(KEY_UP)) keys |= IK_UP;
+ if (key_is_down(KEY_RIGHT)) keys |= IK_RIGHT;
+ if (key_is_down(KEY_DOWN)) keys |= IK_DOWN;
+ if (key_is_down(KEY_LEFT)) keys |= IK_LEFT;
+ if (key_is_down(KEY_A)) keys |= IK_A;
+ if (key_is_down(KEY_B)) keys |= IK_B;
+ if (key_is_down(KEY_L)) keys |= IK_L;
+ if (key_is_down(KEY_R)) keys |= IK_R;
+ if (key_is_down(KEY_START)) keys |= IK_START;
+ if (key_is_down(KEY_SELECT)) keys |= IK_SELECT;
+ }
int32 frame = frameIndex / 2;
- update(frame - lastFrameIndex);
+ game.update(frame - lastFrameIndex);
lastFrameIndex = frame;
- render();
+ game.render();
fpsCounter++;
if (frameIndex >= 60) {
@@ -400,39 +373,40 @@ int main(void) {
while (1)
{
- inputUpdate();
-
- if (keyDown(KEY_NSPIRE_ESC))
{
- break;
+ keys = 0;
+
+ inputUpdate();
+
+ if (keyDown(KEY_NSPIRE_ESC))
+ {
+ break;
+ }
+
+ if (touchInfo && touchReport.contact)
+ {
+ float tx = float(touchReport.x) / float(touchInfo->width) * 2.0f - 1.0f;
+ float ty = float(touchReport.y) / float(touchInfo->height) * 2.0f - 1.0f;
+
+ if (tx < -0.5f) keys |= IK_LEFT;
+ if (tx > 0.5f) keys |= IK_RIGHT;
+ if (ty > 0.5f) keys |= IK_UP;
+ if (ty < -0.5f) keys |= IK_DOWN];
+ }
+
+ if (keyDown(KEY_NSPIRE_2)) keys |= IK_A;
+ if (keyDown(KEY_NSPIRE_3)) keys |= IK_B;
+ if (keyDown(KEY_NSPIRE_7)) keys |= IK_L;
+ if (keyDown(KEY_NSPIRE_9)) keys |= IK_R;
+ if (keyDown(KEY_NSPIRE_ENTER)) keys |= IK_START;
+ if (keyDown(KEY_NSPIRE_SPACE)) keys |= IK_SELECT;
}
- if (touchInfo && touchReport.contact)
- {
- float tx = float(touchReport.x) / float(touchInfo->width) * 2.0f - 1.0f;
- float ty = float(touchReport.y) / float(touchInfo->height) * 2.0f - 1.0f;
-
- keys[IK_LEFT] = tx < -0.5f;
- keys[IK_RIGHT] = tx > 0.5f;
- keys[IK_UP] = ty > 0.5f;
- keys[IK_DOWN] = ty < -0.5f;
- } else {
- keys[IK_LEFT] =
- keys[IK_RIGHT] =
- keys[IK_UP] =
- keys[IK_DOWN] = false;
- }
-
- keys[IK_A] = keyDown(KEY_NSPIRE_2);
- keys[IK_B] = keyDown(KEY_NSPIRE_3);
- keys[IK_L] = keyDown(KEY_NSPIRE_7);
- keys[IK_R] = keyDown(KEY_NSPIRE_9);
-
int time = GetTickCount() - startTime;
- update((time - lastTime) / 16);
+ game.update((time - lastTime) / 16);
lastTime = time;
- render();
+ game.render();
lcd_blit(fb, SCR_320x240_8);
//msleep(16);
diff --git a/src/platform/gba/rasterizer.s b/src/platform/gba/rasterizer.s
new file mode 100644
index 0000000..71f5d3d
--- /dev/null
+++ b/src/platform/gba/rasterizer.s
@@ -0,0 +1,396 @@
+.section .iwram
+.arm
+
+// Thanks to Gericom
+
+.macro LIGHTMAP_INDEX res, g
+ and \res, \g, #0x1F00 // res = g & 0x1F00
+.endm
+
+.macro LIGHTMAP_OFFSET res, index, lightmap
+ add \res, \res, \lightmap // res += lightmap
+.endm
+
+.macro FETCH_LIGHTMAP res, index, lightmap
+ ldrb \res, [\lightmap, \index] // res = lightmap[index]
+.endm
+
+.macro FETCH_TILE res, uv, tile
+ and \res, \uv, #0xFF00
+ orr \res, \res, \uv, lsr #24 // res = (uv & 0xFF00) | (uv >> 24)
+ ldrb \res, [\tile, \res] // res = tile[res]
+.endm
+
+.macro FETCH_PALETTE res, idx, pal
+ mov \res, \idx, lsl #1
+ ldrh \res, [\pal, \res] // res = pal[index]
+.endm
+
+.macro PUT_PIXEL buf, col, off
+ strh \col, [\buf, #\off] // buf[off/2] = col
+.endm
+
+.macro INC_PIXEL buf, col, off
+ ldrh \col, [\buf, #\off]
+ add \col, \col, #4
+ strh \col, [\buf, #\off] // buf[off/2] = col
+.endm
+
+.macro PUT_PIXEL_A buf, col, off
+ cmp \col, #0 // if (col != 0)
+ strneh \col, [\buf, #\off] // buf[off/2] = col
+.endm
+
+.macro CHECK_WIDTH width, stack
+ subs \width, \width, #1
+ ldmmifd sp!, \stack // if (--width < 0) return
+.endm
+
+.macro PUT_PIXELS_F width, pix0, pix1, stack
+ PUT_PIXEL r0, r2, \pix0
+
+ CHECK_WIDTH \width, "\stack"
+
+ PUT_PIXEL r0, r2, \pix1
+.endm
+
+.macro PUT_PIXELS_G width, pix0, pix1, stack
+// r2 - g
+// r3 - dgdx
+// r11 - palette
+// lr - lightmap
+ LIGHTMAP_INDEX r6, r2
+ FETCH_LIGHTMAP r6, r6, lr
+ FETCH_PALETTE r6, r6, r11
+
+ PUT_PIXEL r0, r6, \pix0
+
+ CHECK_WIDTH \width, "\stack"
+
+ PUT_PIXEL r0, r6, \pix1
+
+ add r2, r2, r3 // g += dgdx
+.endm
+
+.macro PUT_PIXELS_FT width, pix0, pix1, stack
+ FETCH_TILE r6, r2, r12
+ FETCH_LIGHTMAP r6, r6, lr
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL r0, r6, \pix0
+
+ CHECK_WIDTH \width, "\stack"
+
+ add r2, r2, r3 // t += dtdx
+
+ FETCH_TILE r6, r2, r12
+ FETCH_LIGHTMAP r6, r6, lr
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL r0, r6, \pix1
+
+ add r2, r2, r3 // t += dtdx
+.endm
+
+.macro PUT_PIXELS_GT width, pix0, pix1, stack
+ LIGHTMAP_INDEX r7, r2
+ LIGHTMAP_OFFSET r7, r7, lr
+
+ FETCH_TILE r6, r3, r12
+ FETCH_LIGHTMAP r6, r6, r7
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL r0, r6, \pix0
+
+ CHECK_WIDTH \width, "\stack"
+
+ add r3, r3, r5 // t += dtdx
+
+ FETCH_TILE r6, r3, r12
+ FETCH_LIGHTMAP r6, r6, r7
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL r0, r6, \pix1
+
+ add r3, r3, r5 // t += dtdx
+ add r2, r2, r4 // g += dgdx
+.endm
+
+.macro PUT_PIXELS_FTA width, pix0, pix1, stack
+ FETCH_TILE r6, r2, r12
+ FETCH_LIGHTMAP r6, r6, lr
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL_A r0, r6, \pix0
+
+ CHECK_WIDTH \width, "\stack"
+
+ add r2, r2, r3 // t += dtdx
+
+ FETCH_TILE r6, r2, r12
+ FETCH_LIGHTMAP r6, r6, lr
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL_A r0, r6, \pix1
+
+ add r2, r2, r3 // t += dtdx
+.endm
+
+.macro PUT_PIXELS_GTA width, pix0, pix1, stack
+ LIGHTMAP_INDEX r7, r2
+ LIGHTMAP_OFFSET r7, r7, lr
+
+ FETCH_TILE r6, r3, r12
+ FETCH_LIGHTMAP r6, r6, r7
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL_A r0, r6, \pix0
+
+ CHECK_WIDTH \width, "\stack"
+
+ add r3, r3, r5 // t += dtdx
+
+ FETCH_TILE r6, r3, r12
+ FETCH_LIGHTMAP r6, r6, r7
+ FETCH_PALETTE r6, r6, r11
+ PUT_PIXEL_A r0, r6, \pix1
+
+ add r3, r3, r5 // t += dtdx
+ add r2, r2, r4 // g += dgdx
+.endm
+
+.macro SCANLINE buf, width, func, stack
+ sub \width, \width, #1 //--width
+1:
+ \func \width, 0, 2, "\stack" // 0, 1
+ CHECK_WIDTH \width, "\stack"
+
+ \func \width, 4, 6, "\stack" // 2, 3
+ CHECK_WIDTH \width, "\stack"
+
+ \func \width, 8, 10, "\stack" // 4, 5
+ CHECK_WIDTH \width, "\stack"
+
+ \func \width, 12, 14, "\stack" // 6, 7
+
+ add \buf, \buf, #16 // buf += 8px
+
+ subs \width, \width, #1
+ bpl 1b
+ ldmfd sp!, \stack
+.endm
+
+.global scanlineF_asm
+scanlineF_asm:
+// r0 = pixel
+// r1 = width
+// r2 = color
+ stmfd sp!, {lr}
+
+ SCANLINE r0, r1, PUT_PIXELS_F, "{pc}"
+
+.global scanlineG_asm
+scanlineG_asm:
+// r0 = pixel
+// r1 = width
+// r2 = g
+// r3 = dgdx
+ stmfd sp!, {r6,r11,lr}
+ ldr lr,= ft_lightmap
+ ldr r11,= palette
+ ldr lr, [lr] // ft_lightmap = *ft_lightmap
+
+ SCANLINE r0, r1, PUT_PIXELS_G, "{r6,r11,pc}"
+
+.global scanlineFT_asm
+scanlineFT_asm:
+// r0 = pixel
+// r1 = width
+// r2 = t
+// r3 = dtdx
+ stmfd sp!, {r6,r7,r11,lr}
+ ldr r11,= palette
+ ldr r12,= tile
+ ldr lr,= ft_lightmap
+ ldr r12, [r12] // tile = *tile
+ ldr lr, [lr] // ft_lightmap = *ft_lightmap
+
+ SCANLINE r0, r1, PUT_PIXELS_FT, "{r6,r7,r11,pc}"
+
+.global scanlineGT_asm
+scanlineGT_asm:
+// r0 = pixel
+// r1 = width
+// r2 = g
+// r3 = t
+ stmfd sp!, {r4,r5,r6,r7,r11,lr}
+stack_offset = 6 * 4
+dgdx = stack_offset + 0
+dtdx = stack_offset + 4
+ ldr r4, [sp, #dgdx]
+ ldr r5, [sp, #dtdx]
+ ldr r11,= palette
+ ldr r12,= tile
+ ldr lr,= lightmap
+ ldr r12, [r12]
+
+ SCANLINE r0, r1, PUT_PIXELS_GT, "{r4,r5,r6,r7,r11,pc}"
+
+.global scanlineFTA_asm
+scanlineFTA_asm:
+// r0 = pixel
+// r1 = width
+// r2 = t
+// r3 = dtdx
+ stmfd sp!, {r6,r7,r11,lr}
+ ldr r11,= palette
+ ldr r12,= tile
+ ldr lr,= ft_lightmap
+ ldr r12, [r12] // tile = *tile
+ ldr lr, [lr] // ft_lightmap = *ft_lightmap
+
+ SCANLINE r0, r1, PUT_PIXELS_FTA, "{r6,r7,r11,pc}"
+
+.global scanlineGTA_asm
+scanlineGTA_asm:
+// r0 = pixel
+// r1 = width
+// r2 = g
+// r3 = t
+ stmfd sp!, {r4,r5,r6,r7,r11,lr}
+stack_offset = 6 * 4
+dgdx = stack_offset + 0
+dtdx = stack_offset + 4
+ ldr r4, [sp, #dgdx]
+ ldr r5, [sp, #dtdx]
+ ldr r11,= palette
+ ldr r12,= tile
+ ldr lr,= lightmap
+ ldr r12, [r12]
+
+ SCANLINE r0, r1, PUT_PIXELS_GTA, "{r4,r5,r6,r7,r11,pc}"
+
+
+
+//------------ WORK IN PROGRESS ---------------
+
+.macro CHECK_WIDTH_INNER width
+ subs \width, \width, #1
+ bmi 2f // if (--width < 0) return
+.endm
+
+.macro PUT_PIXELS_F_INNER buf, width, color, pix0, pix1
+ PUT_PIXEL \buf, \color, \pix0
+
+ CHECK_WIDTH_INNER \width
+
+ PUT_PIXEL \buf, \color, \pix1
+.endm
+
+.macro SCANLINE_INNER buf, width, color, func
+ sub \width, \width, #1 //--width
+3:
+ \func \buf, \width, \color, 0, 2 // 0, 1
+ CHECK_WIDTH_INNER \width
+
+ \func \buf, \width, \color, 4, 6 // 2, 3
+ CHECK_WIDTH_INNER \width
+
+ \func \buf, \width, \color, 8, 10 // 4, 5
+ CHECK_WIDTH_INNER \width
+
+ \func \buf, \width, \color, 12, 14 // 6, 7
+
+ add \buf, \buf, #16 // buf += 8px
+
+ subs \width, \width, #1
+ bpl 3b
+.endm
+
+
+.global rasterizeF_inner_asm
+rasterizeF_inner_asm:
+// r0 = pixel
+// r1 = L edge
+// r2 = R edge
+// r3 = color
+ stmfd sp!, {r4,r5,r6,r7,r8,lr}
+// ip - h
+ ldr ip, [r1] // ip = L.h
+ ldr lr, [r2] // lr = R.h
+ cmp lr, ip
+ movlt r4, lr // if (R.h < L.h) h = R.h
+ movge r4, ip // else h = L.h
+ sub ip, ip, r4
+ sub lr, lr, r4
+ str ip, [r1] // L.h -= h
+ str lr, [r2] // R.h -= h
+
+ ldr ip, [r1, #4] // ip = L.x
+ ldr lr, [r2, #4] // lr = R.x
+ ldr r7, [r1, #16] // r1 = L.dx
+ ldr r8, [r2, #16] // r2 = R.dx
+
+ sub r4, r4, #1
+1:
+ lsr r6, ip, #16 // r6 = x1 = (L.x >> 16)
+ rsbs r5, r6, lr, lsr #16 // r5 = width = (R.x >> 16) - r6
+ ble 2f // if (width <= 0) go next scanline
+
+ add r6, r0, r6, lsl #1 // r6 = pixel + x1
+
+ SCANLINE_INNER r6, r5, r3, PUT_PIXELS_F_INNER
+
+2:
+ add ip, ip, r7 // L.x += L.dx
+ add lr, lr, r8 // R.x += R.dx
+ add r0, r0, #320 // pixel += FRAME_WIDTH (160)
+
+ subs r4, r4, #1
+ bpl 1b
+ str ip, [r1, #4]
+ str lr, [r2, #4]
+
+ ldmfd sp!, {r4,r5,r6,r7,r8,pc}
+
+
+/* TODO
+.global rasterizeF_asm
+rasterizeF_asm:
+// r0 = pixel
+// r1 = L edge
+// r2 = R edge
+// r3 = palIndex -> color
+
+// r4 - Lh
+// r5 - Lx
+// r6 - Ldx
+// r7 - Rh
+// r8 - Rx
+// r9 - Rdx
+ // x1 - asr
+ // r12 - width
+
+ stmfd sp!, {r4,r5,r6,r7,r8,r9,lr}
+
+ ldr lr,= lightmap
+ ldr r12,= palette
+
+ // uint16 color = palette[lightmap[(L.top->v.g << 8) | palIndex]];
+ ldr r6, [r1, #28] // r6 = L.top
+ ldrb r6, [r6, #6] // r6 = L.top->v.g
+ orr r3, r3, r6, lsl #8 // r3 = (L.top->v.g << 8) | palIndex
+ ldrb r3, [lr, r3] // r3 = lightmap[r3]
+ add r3, r12, r3, lsl #1 // r3 = palette + r6
+ ldrh r3, [r3] // r3 = palette[r3] // color
+
+1:
+ 2:
+ ldr r7, [r1]
+ cmp r7, #0
+ ldreq r7, [r1, #28]
+ ldreq r7, [r7, #12]
+
+
+ beq 2b
+
+
+
+
+ SCANLINE r0, r1, PUT_PIXELS_F, "{r4,r5,r6,r7,r11,pc}"
+
+ b 1b
+*/
diff --git a/src/platform/gba/sound.h b/src/platform/gba/sound.h
new file mode 100644
index 0000000..5a385f0
--- /dev/null
+++ b/src/platform/gba/sound.h
@@ -0,0 +1,118 @@
+#ifndef H_SOUND
+#define H_SOUND
+
+#include "common.h"
+
+struct Sound
+{
+ enum SoundMode {
+ UNIQUE,
+ REPLAY,
+ LOOP,
+ };
+
+ struct Sample
+ {
+ const uint8* data;
+ int32 size;
+ int32 pos;
+ int32 inc;
+ int32 volume;
+
+ X_INLINE void fill(int16* buffer, int32 count)
+ {
+ for (int32 i = 0; i < count; i++)
+ {
+ buffer[i] += SND_DECODE(data[pos >> SND_FIXED_SHIFT]) * volume >> SND_VOL_SHIFT;
+
+ pos += inc;
+ if (pos >= size)
+ {
+ // TODO LOOP
+ data = NULL;
+ return;
+ }
+ }
+ }
+ };
+
+ Sample channels[SND_CHANNELS];
+ int32 channelsCount;
+
+ void fill(uint8* buffer, int32 count)
+ {
+ if (channelsCount == 0)
+ {
+ dmaFill(buffer, SND_ENCODE(0), count);
+ return;
+ }
+
+ int16 tmp[SND_SAMPLES];
+ dmaFill(tmp, 0, sizeof(tmp));
+
+ int32 ch = channelsCount;
+ while (ch--)
+ {
+ Sample* sample = channels + ch;
+
+ sample->fill(tmp, count);
+
+ if (!sample->data) {
+ channels[ch] = channels[--channelsCount];
+ }
+ }
+
+ for (int32 i = 0; i < count; i++)
+ {
+ buffer[i] = SND_ENCODE(X_CLAMP(tmp[i], -128, 127));
+ }
+ }
+
+ #define CALC_INC (((SND_SAMPLE_FREQ << SND_FIXED_SHIFT) / SND_OUTPUT_FREQ) * pitch >> SND_PITCH_SHIFT)
+
+ Sample* play(const uint8* data, int32 size, int32 volume, int32 pitch, int32 mode)
+ {
+ if (mode == UNIQUE || mode == REPLAY)
+ {
+ for (int32 i = 0; i < channelsCount; i++)
+ {
+ Sample* sample = channels + i;
+
+ if (sample->data != data)
+ continue;
+
+ sample->inc = CALC_INC;
+ sample->volume = volume;
+
+ if (mode == REPLAY)
+ {
+ sample->pos = 0;
+ }
+
+ return sample;
+ }
+ }
+
+ if (channelsCount >= SND_CHANNELS) {
+ return NULL;
+ }
+
+ Sample* sample = channels + channelsCount++;
+ sample->data = data;
+ sample->size = size << SND_FIXED_SHIFT;
+ sample->pos = 0;
+ sample->inc = CALC_INC;
+ sample->volume = volume + 1;
+
+ return sample;
+ }
+
+ void init()
+ {
+ channelsCount = 0;
+ }
+};
+
+Sound sound;
+
+#endif