diff --git a/src/platform/gba/OpenLara.vcxproj b/src/platform/gba/OpenLara.vcxproj
index e8ada82..dabd363 100644
--- a/src/platform/gba/OpenLara.vcxproj
+++ b/src/platform/gba/OpenLara.vcxproj
@@ -30,6 +30,7 @@
+
@@ -146,6 +147,7 @@
true
WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
+ MultiThreaded
Console
@@ -166,6 +168,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
+ MultiThreaded
Console
diff --git a/src/platform/gba/camera.h b/src/platform/gba/camera.h
index 4dcce9f..8f0d74e 100644
--- a/src/platform/gba/camera.h
+++ b/src/platform/gba/camera.h
@@ -3,18 +3,22 @@
#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_DIST_FOLLOW (1024 + 512)
+#define CAM_SPEED (1 << 3)
+#define CAM_ROT_SPEED (1 << 9)
+#define CAM_DIST_FOLLOW (1024 + 512)
+#define CAMERA_ANGLE_FOLLOW -10 * DEG2SHORT
+#define CAMERA_ANGLE_MAX 85 * DEG2SHORT
+#define CAMERA_TRACE_SHIFT 3
+#define CAMERA_TRACE_STEPS (1 << CAMERA_TRACE_SHIFT)
enum CameraMode
{
- CAMERA_MODE_FREE = 0,
- CAMERA_MODE_FOLLOW = 1,
- CAMERA_MODE_COMBAT = 2,
- CAMERA_MODE_FIXED = 3,
- CAMERA_MODE_OBJECT = 4,
+ CAMERA_MODE_FREE,
+ CAMERA_MODE_FOLLOW,
+ CAMERA_MODE_COMBAT,
+ CAMERA_MODE_LOOK,
+ CAMERA_MODE_FIXED,
+ CAMERA_MODE_OBJECT,
};
struct Camera
@@ -27,37 +31,63 @@ struct Camera
Location view;
Location target;
+ int32 targetDist;
int16 targetAngleX;
int16 targetAngleY;
- int16 targetDist;
int16 angleX;
int16 angleY;
AABB frustumBase;
- Item* item;
+ Item* laraItem;
+ Item* lastItem;
+ Item* lookAtItem;
+
+ int32 speed;
+ int32 timer;
+ int32 index;
+ int32 lastIndex;
CameraMode mode;
- bool modeSwitch;
- void init()
+ bool modeSwitch;
+ bool lastFixed;
+ bool center;
+
+ void init(Item* lara)
{
- item = NULL;
- mode = CAMERA_MODE_FOLLOW;
- modeSwitch = false;
+ target.pos = lara->pos;
+ target.pos.y -= 1024;
+ target.room = lara->room;
+
+ view = target;
+ view.pos.z -= 100;
angleX = 0;
angleY = 0;
- view.pos = vec3i(0);
- target.pos = view.pos;
- targetDist = CAM_DIST_FOLLOW;
targetAngleX = 0;
targetAngleY = 0;
+ targetDist = CAM_DIST_FOLLOW;
+
+ laraItem = lara;
+ lastItem = NULL;
+ lookAtItem = NULL;
+
+ speed = 1;
+ timer = 0;
+ index = -1;
+ lastIndex = -1;
+
+ mode = CAMERA_MODE_FOLLOW;
+
+ modeSwitch = false;
+ lastFixed = false;
+ center = false;
}
- void freeControl()
+ void updateFree()
{
matrixSetView(view.pos, angleX, angleY);
@@ -68,7 +98,7 @@ struct Camera
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);
+ angleX = X_CLAMP(angleX, -CAMERA_ANGLE_MAX, CAMERA_ANGLE_MAX);
if (keys & IK_A)
{
@@ -133,6 +163,166 @@ struct Camera
return true;
}
+ bool trace(const Location &from, Location &to, int32 radius)
+ {
+ vec3i d = to.pos - from.pos;
+ d.x >>= CAMERA_TRACE_SHIFT;
+ d.y >>= CAMERA_TRACE_SHIFT;
+ d.z >>= CAMERA_TRACE_SHIFT;
+
+ Room* room = from.room;
+ vec3i pos = from.pos;
+ int32 i;
+
+ for (i = 0; i < CAMERA_TRACE_STEPS; i++)
+ {
+ if (radius)
+ {
+ to.pos = pos;
+ to.room = room;
+ }
+
+ pos += d;
+ room = room->getRoom(pos.x, pos.y, pos.z);
+
+ const Sector* sector = room->getSector(pos.x, pos.z);
+ int32 floor = sector->getFloor(pos.x, pos.y, pos.z);
+ int32 ceiling = sector->getCeiling(pos.x, pos.y, pos.z);
+
+ if (floor == WALL || ceiling == WALL || ceiling >= floor)
+ {
+ return false;
+ }
+
+ int32 h = pos.y - floor;
+ if (h > 0)
+ {
+ if (h >= radius) {
+ return false;
+ }
+ pos.y = floor;
+ }
+
+ h = ceiling - pos.y;
+ if (h > 0)
+ {
+ if (h >= radius) {
+ return false;
+ }
+ pos.y = ceiling;
+ }
+ }
+
+ to.pos = pos;
+ to.room = room;
+
+ return true;
+ }
+
+ Location getLocationForAngle(int32 angle, int32 distH, int32 distV)
+ {
+ Location res;
+ res.pos.x = target.pos.x - (distH * phd_sin(angle) >> FIXED_SHIFT);
+ res.pos.y = target.pos.y + (distV);
+ res.pos.z = target.pos.z - (distH * phd_cos(angle) >> FIXED_SHIFT);
+ res.room = target.room;
+ return res;
+ }
+
+ Location getBestLocation(Item* item)
+ {
+ int32 distH = targetDist * phd_cos(targetAngleX) >> FIXED_SHIFT;
+ int32 distV = targetDist * phd_sin(targetAngleX) >> FIXED_SHIFT;
+
+ Location best = getLocationForAngle(targetAngleY + item->angle.y, distH, distV);
+
+ if (trace(target, best, 200))
+ return best;
+
+ int32 distQ = X_SQR(target.pos.x - best.pos.x) + X_SQR(target.pos.z - best.pos.z);
+
+ if (distQ > X_SQR(768))
+ return best;
+
+ int32 minDistQ = INT_MAX;
+
+ for (int32 i = 0; i < 4; i++)
+ {
+ Location tmp = getLocationForAngle(i * ANGLE_90, distH, distV);
+
+ if (!trace(target, tmp, 200) || !trace(tmp, view, 0)) {
+ continue;
+ }
+
+ distQ = X_SQR(view.pos.x - tmp.pos.x) + X_SQR(view.pos.z - tmp.pos.z);
+
+ if (distQ < minDistQ)
+ {
+ minDistQ = distQ;
+ best = tmp;
+ }
+ }
+
+ return best;
+ }
+
+ void move(const Location &to, int32 speed)
+ {
+ vec3i d = to.pos - view.pos;
+ if (speed > 1)
+ {
+ d.x /= speed;
+ d.y /= speed;
+ d.z /= speed;
+ }
+
+ view.pos += d;
+ view.room = to.room->getRoom(view.pos.x, view.pos.y, view.pos.z);
+ }
+
+ void updateFollow(Item* item)
+ {
+ if (targetAngleX == 0) {
+ targetAngleX = CAMERA_ANGLE_FOLLOW;
+ }
+
+ targetAngleX = X_CLAMP(targetAngleX + item->angle.x, -CAMERA_ANGLE_MAX, CAMERA_ANGLE_MAX);
+
+ Location best = getBestLocation(item);
+
+ move(best, lastFixed ? speed : 12);
+ }
+
+ void updateCombat()
+ {
+ //
+ }
+
+ void updateLook()
+ {
+ //
+ }
+
+ void updateFixed()
+ {
+ const FixedCamera* cam = cameras + index;
+
+ Location best;
+ best.pos = cam->pos;
+ best.room = rooms + cam->roomIndex;
+
+ lastFixed = true;
+ move(best, 1);
+
+ if (timer != 0)
+ {
+ timer--;
+ if (timer == 0) {
+ timer = -1;
+ }
+ }
+ }
+
void update()
{
if (keys & IK_START)
@@ -152,51 +342,98 @@ struct Camera
if (mode == CAMERA_MODE_FREE)
{
- freeControl();
- }
-
- if (mode == CAMERA_MODE_FOLLOW && item)
- {
- int32 tx = item->pos.x;
- int32 ty = item->pos.y;
- int32 tz = item->pos.z;
-
- const Bounds &box = item->getBoundingBox();
- ty += box.maxY + ((box.minY - box.maxY) * 3 >> 2);
-
- target.pos.x = tx;
- target.pos.y += (ty - target.pos.y) >> 2;
- target.pos.z = tz;
-
- int16 angle = item->angle.y + targetAngleY;
-
- int32 dy = targetDist * phd_sin(targetAngleX) >> FIXED_SHIFT;
- int32 dz = targetDist * phd_cos(targetAngleX) >> FIXED_SHIFT;
-
- int32 cx = target.pos.x - (phd_sin(angle) * dz >> FIXED_SHIFT);
- int32 cy = target.pos.y - 256 + dy;
- int32 cz = target.pos.z - (phd_cos(angle) * dz >> FIXED_SHIFT);
-
- view.pos.x += (cx - view.pos.x) >> 2;
- view.pos.y += (cy - view.pos.y) >> 2;
- view.pos.z += (cz - view.pos.z) >> 2;
-
+ updateFree();
+ prepareFrustum();
+ matrixSetView(view.pos, angleX, angleY);
updateRoom();
-
- vec3i dir = target.pos - view.pos;
- anglesFromVector(dir.x, dir.y, dir.z, angleX, angleY);
+ return;
}
+ bool isFixed = false;
+ Item* item = laraItem;
+
+ if (lookAtItem && (mode == CAMERA_MODE_FIXED || mode == CAMERA_MODE_OBJECT))
+ {
+ isFixed = true;
+ item = lookAtItem;
+ }
+
+ ASSERT(item);
+
+ target.room = item->room;
+
+ const Bounds &box = item->getBoundingBox();
+
+ int32 y = item->pos.y;
+ if (isFixed) {
+ y += (box.minY + box.maxY) >> 1;
+ } else {
+ y += box.maxY + ((box.minY - box.maxY) * 3 >> 2);
+ }
+
+ if (mode == CAMERA_MODE_LOOK || mode == CAMERA_MODE_COMBAT)
+ {
+ y -= 256;
+
+ if (lastFixed) {
+ target.pos.y = 0;
+ speed = 1;
+ } else {
+ target.pos.y += (y - target.pos.y) >> 2;
+ speed = (mode == CAMERA_MODE_LOOK) ? 4 : 8;
+ }
+ } else {
+ target.pos.x = item->pos.x;
+ target.pos.z = item->pos.z;
+
+ if (center)
+ {
+ int32 offset = (box.minZ + box.maxZ) >> 1;
+ target.pos.x += (phd_sin(item->angle.y) * offset) >> FIXED_SHIFT;
+ target.pos.z += (phd_cos(item->angle.y) * offset) >> FIXED_SHIFT;
+ }
+
+ lastFixed ^= isFixed;
+
+ if (lastFixed) {
+ target.pos.y = y;
+ speed = 1;
+ } else {
+ target.pos.y += (y - target.pos.y) >> 2;
+ }
+ }
+
+ switch (mode)
+ {
+ case CAMERA_MODE_FOLLOW : updateFollow(item); break;
+ case CAMERA_MODE_COMBAT : updateCombat(); break;
+ case CAMERA_MODE_LOOK : updateLook(); break;
+ default : updateFixed();
+ }
+
+ lastFixed = isFixed;
+ lastIndex = index;
+
+ if (mode != CAMERA_MODE_OBJECT || timer == -1)
+ {
+ mode = CAMERA_MODE_FOLLOW;
+ index = -1;
+ lastItem = lookAtItem;
+ lookAtItem = NULL;
+ targetAngleX = 0;
+ targetAngleY = 0;
+ targetDist = CAM_DIST_FOLLOW;
+ center = false;
+ }
+
+ vec3i dir = target.pos - view.pos;
+ anglesFromVector(dir.x, dir.y, dir.z, angleX, angleY);
+
prepareFrustum();
matrixSetView(view.pos, angleX, angleY);
updateRoom();
-
- // reset additional angles, Lara states can override it during the update proc
- targetAngleX = 0;
- targetAngleY = 0;
- targetDist = CAM_DIST_FOLLOW;
}
void prepareFrustum()
diff --git a/src/platform/gba/collision.h b/src/platform/gba/collision.h
index 0d717d5..d04b85f 100644
--- a/src/platform/gba/collision.h
+++ b/src/platform/gba/collision.h
@@ -47,7 +47,7 @@ struct CollisionInfo
vec3i pos;
int16 angle;
- int16 quadrant;
+ uint16 quadrant;
CollisionType type;
@@ -132,8 +132,8 @@ bool collideStatic(Room* room, CollisionInfo &cinfo, const vec3i &p, int32 heigh
{
const RoomMesh* mesh = room->data.meshes + i;
- #ifdef NO_STATIC_MESHES
- if (mesh->id != STATIC_MESH_GATE) continue;
+ #ifdef NO_STATIC_MESH_PLANTS
+ if (mesh->id < 10) continue;
#endif
const StaticMesh* staticMesh = staticMeshes + mesh->id;
@@ -191,8 +191,8 @@ bool collideStatic(Room* room, CollisionInfo &cinfo, const vec3i &p, int32 heigh
void collideRoom(Item* item, int32 height, int32 yOffset = 0)
{
- cinfo.type = CT_NONE;
- cinfo.offset = vec3i(0, 0, 0);
+ cinfo.type = CT_NONE;
+ cinfo.offset = vec3i(0, 0, 0);
vec3i p = item->pos;
p.y += yOffset;
@@ -203,8 +203,10 @@ void collideRoom(Item* item, int32 height, int32 yOffset = 0)
int32 floor, ceiling;
+ Room* room = item->room;
+
#define CHECK_HEIGHT(v) {\
- const Room* room = item->room->getRoom(v.x, cy, v.z);\
+ room = room->getRoom(v.x, cy, v.z);\
const Sector* sector = room->getSector(v.x, v.z);\
floor = sector->getFloor(v.x, cy, v.z);\
if (floor != WALL) floor -= p.y;\
diff --git a/src/platform/gba/common.cpp b/src/platform/gba/common.cpp
index a858e39..bd170ba 100644
--- a/src/platform/gba/common.cpp
+++ b/src/platform/gba/common.cpp
@@ -12,6 +12,31 @@ EWRAM_DATA SaveGame gSaveGame;
const FloorData* gLastFloorData;
FloorData gLastFloorSlant;
+int32 rand_seed_ctrl;
+int32 rand_seed_draw;
+
+void set_seed_ctrl(int32 seed)
+{
+ rand_seed_ctrl = seed;
+}
+
+void set_seed_draw(int32 seed)
+{
+ rand_seed_draw = seed;
+}
+
+#define X_RAND(seed) (((seed = 0x3039 + seed * 0x41C64E6D) >> 10) & 0x7FFF);
+
+int16 rand_ctrl()
+{
+ return X_RAND(rand_seed_ctrl);
+}
+
+int16 rand_draw()
+{
+ return X_RAND(rand_seed_draw);
+}
+
const uint16 divTable[DIV_TABLE_SIZE] = { // ROM, not a big difference with IWRAM
0xFFFF, 0xFFFF, 0x8000, 0x5555, 0x4000, 0x3333, 0x2AAA, 0x2492,
0x2000, 0x1C71, 0x1999, 0x1745, 0x1555, 0x13B1, 0x1249, 0x1111,
@@ -805,7 +830,9 @@ void matrixFrame(const vec3i &pos, uint16* angles)
matrixRotateYXZ(angleX, angleY, angleZ);
}
-#define LERP_FAST(a, b, mul, div) a = (a + b) >> 1
+#define LERP_1(a, b, mul, div) a = (b + a) >> 1
+#define LERP_2(a, b, mul, div) a = a + ((b - a) >> 2)
+#define LERP_3(a, b, mul, div) a = b - ((b - a) >> 2)
#define LERP_SLOW(a, b, mul, div) a = a + (b - a) * mul / div
#define LERP_ROW(lerp_func, a, b, mul, div) \
@@ -818,15 +845,23 @@ 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);
+ if ((divider == 2) || ((divider == 4) && (multiplier == 2))) {
+ LERP_ROW(LERP_1, m[0], n[0], multiplier, divider);
+ LERP_ROW(LERP_1, m[1], n[1], multiplier, divider);
+ LERP_ROW(LERP_1, m[2], n[2], multiplier, divider);
+ } else if (multiplier == 1) {
+ LERP_ROW(LERP_2, m[0], n[0], multiplier, divider);
+ LERP_ROW(LERP_2, m[1], n[1], multiplier, divider);
+ LERP_ROW(LERP_2, 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);
+ LERP_ROW(LERP_3, m[0], n[0], multiplier, divider);
+ LERP_ROW(LERP_3, m[1], n[1], multiplier, divider);
+ LERP_ROW(LERP_3, m[2], n[2], multiplier, divider);
}
+
+ //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 matrixSetIdentity()
diff --git a/src/platform/gba/common.h b/src/platform/gba/common.h
index b48cb66..e97724b 100644
--- a/src/platform/gba/common.h
+++ b/src/platform/gba/common.h
@@ -29,7 +29,7 @@
//#define DEBUG_OVERDRAW
#endif
-#define NO_STATIC_MESHES
+#define NO_STATIC_MESH_PLANTS
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS
@@ -98,8 +98,6 @@
#define ALIGN16 __attribute__((aligned(16)))
#endif
-#define UNUSED(x) (void)(x)
-
typedef signed char int8;
typedef signed short int16;
typedef signed int int32;
@@ -149,6 +147,9 @@ typedef int16 Index;
extern uint16 fb[VRAM_WIDTH * FRAME_HEIGHT];
#endif
+// system
+int32 osGetSystemTimeMS();
+
#ifdef PROFILE
#if defined(_WIN32)
@@ -228,6 +229,13 @@ struct vec3s {
vec3s() {}
X_INLINE vec3s(int16 x, int16 y, int16 z) : x(x), y(y), z(z) {}
+
+ X_INLINE vec3s operator + (const vec3s &v) const { return vec3s(x + v.x, y + v.y, z + v.z); }
+ X_INLINE vec3s operator - (const vec3s &v) const { return vec3s(x - v.x, y - v.y, z - v.z); }
+ X_INLINE bool operator == (const vec3s &v) { return x == v.x && y == v.y && z == v.z; }
+ X_INLINE bool operator != (const vec3s &v) { return x != v.x || y != v.y || z != v.z; }
+ X_INLINE vec3s& operator += (const vec3s &v) { x += v.x; y += v.y; z += v.z; return *this; }
+ X_INLINE vec3s& operator -= (const vec3s &v) { x -= v.x; y -= v.y; z -= v.z; return *this; }
};
struct vec3i {
@@ -239,6 +247,10 @@ struct vec3i {
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 * (int32 s) const { return vec3i(x * s, y * s, z * s); }
+ X_INLINE vec3i operator / (int32 s) const { return vec3i(x / s, y / s, z / s); }
+ X_INLINE bool operator == (const vec3i &v) const { return x == v.x && y == v.y && z == v.z; }
+ X_INLINE bool operator != (const vec3i &v) const { return 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; }
};
@@ -308,6 +320,10 @@ struct Bounds {
X_INLINE Bounds() {}
X_INLINE Bounds(int16 minX, int16 maxX, int16 minY, int16 maxY, int16 minZ, int16 maxZ) :
minX(minX), maxX(maxX), minY(minY), maxY(maxY), minZ(minZ), maxZ(maxZ) {}
+
+ X_INLINE vec3i getCenter() const {
+ return vec3i((maxX + minX) >> 1, (maxY + minY) >> 1, (maxZ + minZ) >> 1);
+ }
};
struct AABB {
@@ -327,10 +343,9 @@ struct RoomVertex {
};
struct RoomSprite {
- int16 x, y, z;
+ vec3s pos;
uint8 g;
- uint8 reserved;
- uint16 texture;
+ uint8 index;
};
struct Portal {
@@ -405,7 +420,12 @@ struct RoomInfo
uint8 xSectors;
uint8 zSectors;
uint8 alternateRoom;
- uint8 flags;
+ union {
+ uint8 value;
+ struct {
+ uint8 water:1, :7;
+ };
+ } flags;
RoomData data;
};
@@ -427,7 +447,8 @@ struct Room {
void add(Item* item);
void remove(Item* item);
- const Sector* getSector(int32 posX, int32 posZ) const;
+ const Sector* getSector(int32 x, int32 z) const;
+ const Sector* getWaterSector(int32 x, int32 z) const;
Room* getRoom(int32 x, int32 y, int32 z);
bool checkPortal(const Portal* portal);
Room** addVisibleRoom(Room** list);
@@ -435,19 +456,16 @@ struct Room {
Room** getNearRooms(const vec3i &pos, int32 radius, int32 height);
Room** getAdjRooms();
Room** getVisibleRooms();
-
- int32 getWaterLevel();
- int32 getWaterDepth();
};
struct Node {
uint32 flags;
- vec3i pos;
+ vec3i pos;
};
struct Model {
- uint8 type;
- int8 count;
+ uint8 type;
+ int8 count;
uint16 start;
uint16 nodeIndex;
uint16 animIndex;
@@ -475,69 +493,73 @@ struct SoundInfo
};
struct Anim {
- uint32 frameOffset;
-
- uint8 frameRate;
- uint8 frameSize;
- uint16 state;
-
- int32 speed;
-
- int32 accel;
-
- uint16 frameBegin;
- uint16 frameEnd;
-
- uint16 nextAnimIndex;
- uint16 nextFrameIndex;
-
- uint16 statesCount;
- uint16 statesStart;
-
- uint16 commandsCount;
- uint16 commandsStart;
+ uint32 frameOffset;
+ uint8 frameRate;
+ uint8 frameSize;
+ uint16 state;
+ int32 speed;
+ int32 accel;
+ uint16 frameBegin;
+ uint16 frameEnd;
+ uint16 nextAnimIndex;
+ uint16 nextFrameIndex;
+ uint16 statesCount;
+ uint16 statesStart;
+ uint16 commandsCount;
+ uint16 commandsStart;
};
struct AnimState {
- uint8 state;
- uint8 rangesCount;
+ uint8 state;
+ uint8 rangesCount;
uint16 rangesStart;
};
struct AnimRange {
- uint16 frameBegin;
- uint16 frameEnd;
- uint16 nextAnimIndex;
- uint16 nextFrameIndex;
+ uint16 frameBegin;
+ uint16 frameEnd;
+ uint16 nextAnimIndex;
+ uint16 nextFrameIndex;
};
struct AnimFrame {
- Bounds box;
- vec3s pos;
+ Bounds box;
+ vec3s pos;
uint16 angles[1];
};
struct Texture {
- uint16 attribute;
- uint16 tile:14, :2;
- uint32 uv0;
- uint32 uv1;
- uint32 uv2;
- uint32 uv3;
+ uint16 attribute;
+ uint16 tile:14, :2;
+ uint32 uv0;
+ uint32 uv1;
+ uint32 uv2;
+ uint32 uv3;
};
struct Sprite {
- uint16 tile;
- uint8 u, v;
- uint16 w, h;
- int16 l, t, r, b;
+ uint16 tile;
+ uint8 u, v;
+ uint8 w, h;
+ int16 l, t, r, b;
};
struct SpriteSeq {
- uint16 type;
- uint16 unused;
- int16 count;
- int16 start;
+ uint16 type;
+ uint16 unused;
+ int16 count;
+ int16 start;
+};
+
+struct FixedCamera {
+ vec3i pos;
+ int16 roomIndex;
+ union {
+ uint16 value;
+ struct {
+ uint16 timer:8, once:1, speed:5, :2;
+ };
+ } flags;
};
struct ItemInfo
@@ -546,7 +568,9 @@ struct ItemInfo
uint8 roomIndex;
vec3s pos;
uint16 intensity;
- uint16 flags;
+ struct {
+ uint16 value:14, angle:2;
+ } flags;
};
#define ITEM_FLAGS_MASK_ALL 0x1F
@@ -631,8 +655,8 @@ struct Lara;
E( TRAP_DOOR_2 ) \
E( UNUSED_4 ) \
E( BRIDGE_FLAT ) \
- E( BRIDGE_TILT1 ) \
- E( BRIDGE_TILT2 ) \
+ E( BRIDGE_TILT_1 ) \
+ E( BRIDGE_TILT_2 ) \
E( INV_PASSPORT ) \
E( INV_COMPASS ) \
E( INV_HOME ) \
@@ -680,14 +704,14 @@ struct Lara;
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( PUZZLEHOLE_1 ) \
+ E( PUZZLEHOLE_2 ) \
+ E( PUZZLEHOLE_3 ) \
+ E( PUZZLEHOLE_4 ) \
+ E( PUZZLEHOLE_DONE_1 ) \
+ E( PUZZLEHOLE_DONE_2 ) \
+ E( PUZZLEHOLE_DONE_3 ) \
+ E( PUZZLEHOLE_DONE_4 ) \
E( LEADBAR ) \
E( INV_LEADBAR ) \
E( MIDAS_HAND ) \
@@ -699,10 +723,10 @@ struct Lara;
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( KEYHOLE_1 ) \
+ E( KEYHOLE_2 ) \
+ E( KEYHOLE_3 ) \
+ E( KEYHOLE_4 ) \
E( UNUSED_5 ) \
E( UNUSED_6 ) \
E( SCION_PICKUP_QUALOPEC ) \
@@ -739,9 +763,9 @@ struct Lara;
E( UNUSED_16 ) \
E( UNUSED_17 ) \
E( LAVA_PARTICLE ) \
- E( TRAP_LAVA_EMITTER ) \
+ E( LAVA_EMITTER ) \
E( FLAME ) \
- E( TRAP_FLAME_EMITTER ) \
+ E( FLAME_EMITTER ) \
E( TRAP_LAVA ) \
E( MUTANT_EGG_BIG ) \
E( BOAT ) \
@@ -772,7 +796,7 @@ struct Item {
union {
struct {
- uint16 save:1, gravity:1, active:1, status:2, collision:1, custom:2, once:1, mask:5, reverse:1, shadow:1; // TODO
+ uint16 save:1, gravity:1, active:1, status:2, collision:1, dozy:1, custom:1, once:1, mask:5, reverse:1, shadow:1; // TODO
};
uint16 value;
} flags;
@@ -795,15 +819,18 @@ struct Item {
};
int16 moveAngle;
- int16 floor;
-
int16 turnSpeed;
- int16 vSpeedHack;
uint16 input;
uint8 type;
uint8 intensity;
+ int16 roomFloor;
+ int16 vSpeedHack;
+ int16 swimTimer;
+ uint8 weaponState;
+ uint8 _reserved;
+
Item* nextItem;
Item* nextActive;
@@ -821,19 +848,42 @@ struct Item {
const Anim* animSet(int32 newAnimIndex, bool resetState, int32 frameOffset = 0);
const Anim* animChange(const Anim* anim);
void animCmd(bool fx, const Anim* anim);
+ void animSkip(int32 stateBefore, int32 stateAfter, bool advance = false);
+ void animProcess(bool movement = true);
+ bool animIsEnd(int32 offset) const;
+ bool moveTo(const vec3i &point, Item* item, bool lerp);
- void skipAnim();
- void updateAnim(bool movement = true);
void updateRoom(int32 offset = 0);
- int32 calcLighting(const Bounds& box) const;
+
+ vec3i getRelative(const vec3i &point) const;
+
+ int32 getWaterLevel();
+ int32 getWaterDepth();
Item* init(Room* room);
X_INLINE Item() {}
Item(Room* room);
+ virtual void collide(Lara* lara, CollisionInfo* cinfo);
virtual void update();
virtual void draw();
- virtual void collide(Lara* lara, CollisionInfo* cinfo);
+};
+
+enum WeaponState {
+ WEAPON_STATE_FREE,
+ WEAPON_STATE_BUSY,
+ WEAPON_STATE_DRAW,
+ WEAPON_STATE_HOLSTER,
+ WEAPON_STATE_READY
+};
+
+enum WeaponType {
+ WEAPON_TYPE_NONE,
+ WEAPON_TYPE_PISTOLS,
+ WEAPON_TYPE_MAGNUMS,
+ WEAPON_TYPE_UZIS,
+ WEAPON_TYPE_SHOTGUN,
+ WEAPON_TYPE_MAX
};
struct SaveGame
@@ -842,7 +892,8 @@ struct SaveGame
uint8 once:1, mask:5, :2;
};
- uint8 secrets;
+ uint8 secrets;
+ uint8 pickups;
TrackFlags tracks[64];
};
@@ -927,9 +978,9 @@ enum SlantType {
enum WaterState {
WATER_STATE_ABOVE,
+ WATER_STATE_WADE,
WATER_STATE_SURFACE,
WATER_STATE_UNDER,
- WATER_STATE_WADE,
};
enum AnimCommand {
@@ -1076,8 +1127,10 @@ struct Level {
uint16 spriteSequencesCount;
uint16 soundSourcesCount;
uint16 boxesCount;
+ uint16 texturesCount;
uint16 animTexDataSize;
uint16 itemsCount;
+ uint16 camerasCount;
uint16 cameraFramesCount;
const uint16* palette;
@@ -1098,12 +1151,12 @@ struct Level {
const Texture* textures;
const Sprite* sprites;
const SpriteSeq* spriteSequences;
- uint32 cameras;
+ const FixedCamera* cameras;
uint32 soundSources;
const Box* boxes;
const uint16* overlaps;
const uint16* zones[2][3];
- uint32 animTexData;
+ const int16* animTexData;
const ItemInfo* itemsInfo;
uint32 cameraFrames;
const uint16* soundMap;
@@ -1177,9 +1230,12 @@ extern int32 fps;
#define MAX_MODELS ITEM_MAX
#define MAX_ROOMS 139 // LEVEL7A
#define MAX_STATIC_MESHES 50
-#define MAX_VERTICES 3072
+#define MAX_CAMERAS 16
+#define MAX_VERTICES (4*1024)
+#define MAX_TEXTURES 1536
#define MAX_FACES 1024
#define MAX_ROOM_LIST 16
+#define MAX_CAUSTICS 32
#define FOV_SHIFT 3
#define FOG_SHIFT 1
@@ -1196,7 +1252,8 @@ extern int32 fps;
#define FACE_COLORED 0x4000
#define FACE_CLIPPED 0x2000
#define FACE_FLAT 0x1000
-#define FACE_SHADOW 0x0800
+#define FACE_SPRITE 0x0800
+#define FACE_SHADOW (FACE_COLORED | FACE_FLAT | FACE_SPRITE)
#define FACE_TEXTURE 0x07FF
#define NO_ROOM 0xFF
@@ -1216,6 +1273,7 @@ extern int32 fps;
#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 X_COUNT(x) int32(sizeof(x) / sizeof(x[0]))
#define DP43c(a,bx,by,bz) ((a).x * bx + (a).y * by + (a).z * bz + (a).w)
#define DP33c(a,bx,by,bz) ((a).x * bx + (a).y * by + (a).z * bz)
@@ -1298,12 +1356,10 @@ X_INLINE void swap(T &a, T &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);
-}
+void set_seed_ctrl(int32 seed);
+void set_seed_draw(int32 seed);
+int16 rand_ctrl();
+int16 rand_draw();
int32 phd_sin(int32 x);
int32 phd_cos(int32 x);
@@ -1363,10 +1419,11 @@ int32 rectIsVisible(const Rect* rect);
int32 boxIsVisible(const Bounds* box);
void transform(int32 vx, int32 vy, int32 vz, int32 vg);
bool transformBoxRect(const Bounds* box, Rect* rect);
-void transformRoom(const RoomVertex* vertices, int32 vCount);
-void transformMesh(const vec3s* vertices, int32 vCount, uint16 intensity);
+void transformRoom(const RoomVertex* vertices, int32 vCount, bool applyCaustics);
+void transformMesh(const vec3s* vertices, int32 vCount, const uint16* vIntensity, const vec3s* vNormal);
void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex);
void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex);
+void faceAddSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index);
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);
@@ -1377,7 +1434,7 @@ void readLevel(const uint8 *data);
Lara* getLara(const vec3i &pos);
bool useSwitch(Item* item, int32 timer);
-bool useKey(Item* item);
+bool useKey(Item* item, Item* lara);
bool usePickup(Item* item);
void musicPlay(int32 track);
@@ -1395,13 +1452,4 @@ X_INLINE void dmaFill(void *dst, uint8 value, uint32 count)
#endif
}
-X_INLINE int16 xRand()
-{
-#ifdef __GBA__
- return qran();
-#else
- return rand();
-#endif
-}
-
#endif
diff --git a/src/platform/gba/draw.h b/src/platform/gba/draw.h
index 253b577..a886f20 100644
--- a/src/platform/gba/draw.h
+++ b/src/platform/gba/draw.h
@@ -4,7 +4,85 @@
#include "common.h"
#include "item.h"
-extern AABB fastClipAABB;
+int32 lightAmbient;
+
+int32 caustics[MAX_CAUSTICS];
+int32 causticsRand[MAX_CAUSTICS];
+int32 causticsFrame;
+
+void drawInit()
+{
+ for (int32 i = 0; i < MAX_CAUSTICS; i++)
+ {
+ int16 rot = i * (ANGLE_90 * 4) / MAX_CAUSTICS;
+ caustics[i] = phd_sin(rot) * 768 >> FIXED_SHIFT;
+ causticsRand[i] = (rand_draw() >> 5) - 511;
+ }
+}
+
+void drawFree()
+{
+ //
+}
+
+void calcLightingDynamic(const Room* room, const vec3i &point)
+{
+ const RoomInfo* info = room->info;
+
+ lightAmbient = (info->ambient << 5);
+
+ if (!info->lightsCount)
+ return;
+
+ lightAmbient = 8191 - lightAmbient;
+ int32 maxLum = 0;
+
+ for (int i = 0; i < info->lightsCount; i++)
+ {
+ const Light* light = room->data.lights + i;
+
+ vec3i pos;
+ pos.x = light->pos.x + (info->x << 8);
+ pos.y = light->pos.y;
+ pos.z = light->pos.z + (info->z << 8);
+
+ int32 radius = light->radius << 8;
+ int32 intensity = light->intensity << 5;
+
+ vec3i d = point - pos;
+ int32 dist = dot(d, d) >> 12;
+ int32 att = X_SQR(radius) >> 12;
+
+ int32 lum = (intensity * att) / (dist + att) + lightAmbient;
+
+ if (lum > maxLum)
+ {
+ maxLum = lum;
+ }
+ }
+
+ lightAmbient = 8191 - ((maxLum + lightAmbient) >> 1);
+
+ Matrix &m = matrixGet();
+
+ int32 fogZ = m[2].w >> FIXED_SHIFT;
+ if (fogZ > FOG_MIN) {
+ lightAmbient += (fogZ - FOG_MIN) << FOG_SHIFT;
+ lightAmbient = X_MIN(lightAmbient, 8191);
+ }
+}
+
+void calcLightingStatic(int32 intensity)
+{
+ lightAmbient = intensity - 4096;
+
+ Matrix &m = matrixGet();
+
+ int32 fogZ = m[2].w >> FIXED_SHIFT;
+ if (fogZ > FOG_MIN) {
+ lightAmbient += (fogZ - FOG_MIN) << FOG_SHIFT;
+ }
+}
void drawNumber(int32 number, int32 x, int32 y)
{
@@ -22,7 +100,7 @@ void drawNumber(int32 number, int32 x, int32 y)
}
}
-void drawMesh(int16 meshIndex, uint16 intensity)
+void drawMesh(int16 meshIndex)
{
int32 offset = level.meshOffsets[meshIndex];
const uint8* ptr = level.meshData + offset;
@@ -33,12 +111,17 @@ void drawMesh(int16 meshIndex, uint16 intensity)
const vec3s* vertices = (vec3s*)ptr;
ptr += vCount * 3 * sizeof(int16);
+ const uint16* vIntensity = NULL;
+ const vec3s* vNormal = NULL;
+
int16 nCount = *(int16*)ptr; ptr += 2;
//const int16* normals = (int16*)ptr;
if (nCount > 0) { // normals
+ vNormal = (vec3s*)ptr;
ptr += nCount * 3 * sizeof(int16);
} else { // intensity
- ptr += vCount * sizeof(int16);
+ vIntensity = (uint16*)ptr;
+ ptr += vCount * sizeof(uint16);
}
int16 rCount = *(int16*)ptr; ptr += 2;
@@ -56,7 +139,7 @@ void drawMesh(int16 meshIndex, uint16 intensity)
int32 startVertex = gVerticesCount;
PROFILE_START();
- transformMesh(vertices, vCount, intensity);
+ transformMesh(vertices, vCount, vIntensity, vNormal);
PROFILE_STOP(dbg_transform);
PROFILE_START();
@@ -106,16 +189,17 @@ void drawShadow(const Item* item, int32 size)
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);
+ faceAddQuad(FACE_SHADOW, indices + 0, startVertex);
+ faceAddQuad(FACE_SHADOW, indices + 4, startVertex);
+ faceAddQuad(FACE_SHADOW, indices + 8, startVertex);
matrixPop();
}
void drawSprite(const Item* item)
{
- // TODO
+ vec3i d = item->pos - cameraViewPos;
+ faceAddSprite(d.x, d.y, d.z, item->intensity << 5, models[item->type].start);
}
void drawModel(const Item* item, uint16* meshOverrides)
@@ -134,7 +218,10 @@ void drawModel(const Item* item, uint16* meshOverrides)
int32 intensity = item->intensity << 5;
if (intensity == 0) {
- intensity = item->calcLighting(frame->box);
+ vec3i point = item->getRelative(frame->box.getCenter());
+ calcLightingDynamic(item->room, point);
+ } else {
+ calcLightingStatic(intensity);
}
int32 vis = boxIsVisible(&frame->box);
@@ -156,7 +243,7 @@ void drawModel(const Item* item, uint16* meshOverrides)
matrixFrame(frame->pos, frameAngles);
- drawMesh(meshOverrides ? meshOverrides[0] : model->start, intensity);
+ drawMesh(meshOverrides ? meshOverrides[0] : model->start);
for (int32 i = 1; i < model->count; i++)
{
@@ -166,7 +253,7 @@ void drawModel(const Item* item, uint16* meshOverrides)
frameAngles += 2;
matrixFrame(node->pos, frameAngles);
- drawMesh(meshOverrides ? meshOverrides[i] : (model->start + i), intensity);
+ drawMesh(meshOverrides ? meshOverrides[i] : (model->start + i));
node++;
}
@@ -209,21 +296,27 @@ void drawRoom(const Room* room)
enableClipping = true;
PROFILE_START();
- transformRoom(data.vertices, info->verticesCount);
+ transformRoom(data.vertices, info->verticesCount, info->flags.water);
PROFILE_STOP(dbg_transform);
- matrixPop();
-
PROFILE_START();
faceAddRoom(data.quads, info->quadsCount, data.triangles, info->trianglesCount, startVertex);
PROFILE_STOP(dbg_poly);
+ for (int32 i = 0; i < info->spritesCount; i++)
+ {
+ const RoomSprite* sprite = data.sprites + i;
+ faceAddSprite(sprite->pos.x, sprite->pos.y, sprite->pos.z, sprite->g << 5, sprite->index);
+ }
+
+ matrixPop();
+
for (int32 i = 0; i < info->meshesCount; i++)
{
const RoomMesh* mesh = data.meshes + i;
- #ifdef NO_STATIC_MESHES
- if (mesh->id != STATIC_MESH_GATE) continue;
+ #ifdef NO_STATIC_MESH_PLANTS
+ if (mesh->id < 10) continue;
#endif
const StaticMesh* staticMesh = staticMeshes + mesh->id;
@@ -243,8 +336,10 @@ void drawRoom(const Room* room)
int32 vis = boxIsVisible(&staticMesh->vbox);
if (vis != 0) {
- enableClipping = vis < 0;
- drawMesh(staticMesh->meshIndex, mesh->intensity << 5);
+ enableClipping = true;//vis < 0; // TODO wrong visibility BBox?
+
+ calcLightingStatic(mesh->intensity << 5);
+ drawMesh(staticMesh->meshIndex);
}
matrixPop();
@@ -277,103 +372,4 @@ void drawRooms()
flush();
}
-#ifdef TEST
-void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex);
-
-extern Vertex gVertices[MAX_VERTICES];
-
-Rect testClip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT };
-int32 testTile = 10; // 707 // 712
-
-void drawTest() {
-#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)
- {
- #ifdef MODE4
- ((uint8*)fb)[y * FRAME_WIDTH + x] = 255;
- #else
- ((uint16*)fb)[y * FRAME_WIDTH + x] = 255;
- #endif
- }
- }
- }
-#endif
-
- flush();
-}
-#endif
-
#endif
diff --git a/src/platform/gba/enemy.h b/src/platform/gba/enemy.h
index 49d7da5..7b13a1f 100644
--- a/src/platform/gba/enemy.h
+++ b/src/platform/gba/enemy.h
@@ -14,13 +14,11 @@ struct Enemy : Item
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
//
- UNUSED(lara);
- UNUSED(cinfo);
}
virtual void update()
{
- updateAnim();
+ animProcess();
}
};
diff --git a/src/platform/gba/game.h b/src/platform/gba/game.h
index 8a83bce..60eba65 100644
--- a/src/platform/gba/game.h
+++ b/src/platform/gba/game.h
@@ -9,25 +9,23 @@
#include "item.h"
#include "draw.h"
-Lara* players[2];
-
-Lara* getLara(const vec3i &pos)
-{
- UNUSED(pos); // TODO two players
- return players[0]; // TODO find nearest player
-}
-
struct Game
{
+ int32 animTexFrame;
+
void init()
{
- loadLevel(LEVEL1_PKD);
+ set_seed_ctrl(osGetSystemTimeMS() * 3);
+ set_seed_draw(osGetSystemTimeMS() * 7);
+
+ animTexFrame = 0;
+
+ loadLevel(levelData);
}
void loadLevel(const void* data)
{
- camera.init();
-
+ drawFree();
readLevel((uint8*)data);
// prepare rooms
@@ -39,34 +37,47 @@ struct Game
// prepare items
for (int32 i = 0; i < level.itemsCount; i++)
{
- Item* item = items + i;
const ItemInfo* info = level.itemsInfo + i;
+ Item* item = items + i;
item->type = info->type;
- item->intensity = info->intensity << 5;
+ item->intensity = info->intensity;
item->pos.x = info->pos.x + (rooms[info->roomIndex].info->x << 8);
item->pos.y = info->pos.y;
item->pos.z = info->pos.z + (rooms[info->roomIndex].info->z << 8);
- item->angle.y = ((info->flags >> 14) - 2) * ANGLE_90;
- item->flags.value = info->flags & 0x3FFF;
+ item->angle.y = (info->flags.angle - 2) * ANGLE_90;
+ item->flags.value = info->flags.value;
item->init(rooms + info->roomIndex);
if (item->type == ITEM_LARA)
{
- camera.item = item;
+ camera.init(item);
+ camera.laraItem = item;
//#ifdef PROFILE
- // debug
- //resetItem(item, 0, vec3i(74588, 3072, 19673), 0); // level 1 (first darts)
- //resetItem(item, 9, vec3i(49669, 7680, 57891), 0); // level 1 (first door)
- //resetItem(item, 10, vec3i(43063, 7168, 61198), 0); // level 1 (transp)
- //resetItem(item, 14, vec3i(20215, 6656, 52942), ANGLE_90 + ANGLE_45); // level 1 (bridge)
- //resetItem(item, 17, vec3i(16475, 6656, 59845), ANGLE_90); // level 1 (bear)
- //resetItem(item, 26, vec3i(24475, 6912, 83505), ANGLE_90); // level 1 (switch timer)
- //resetItem(item, 35, vec3i(35149, 2048, 74189), ANGLE_90); // level 1 (switch timer)
+ // gym
+ //resetItem(item, 13, vec3i(38953, 3328, 63961), ANGLE_90 + ANGLE_45); // pool
+ // level 1
+ //resetItem(item, 0, vec3i(74588, 3072, 19673), ANGLE_0); // first darts
+ //resetItem(item, 9, vec3i(49669, 7680, 57891), ANGLE_0); // first door
+ //resetItem(item, 10, vec3i(43063, 7168, 61198), ANGLE_0); // transp
+ //resetItem(item, 14, vec3i(20215, 6656, 52942), ANGLE_90 + ANGLE_45); // bridge
+ //resetItem(item, 17, vec3i(16475, 6656, 59845), ANGLE_90); // bear
+ //resetItem(item, 26, vec3i(24475, 6912, 83505), ANGLE_90); // switch timer 1
+ //resetItem(item, 35, vec3i(35149, 2048, 74189), ANGLE_90); // switch timer 2
+ // level 2
+ //resetItem(item, 15, vec3i(66179, 0, 25920), -ANGLE_90 - ANGLE_45); // sprites
+ //resetItem(item, 19, vec3i(61018, 1024, 31214), ANGLE_180); // block
+ //resetItem(item, 14, vec3i(64026, 512, 20806), ANGLE_0); // key and puzzle
+ //resetItem(item, 5, vec3i(55644, 0, 29155), -ANGLE_90); // keyhole
+ //resetItem(item, 71, vec3i(12705, -768, 30195), -ANGLE_90); // puzzle
+ //resetItem(item, 63, vec3i(31055, -2048, 33406), ANGLE_0); // right room
+ //resetItem(item, 44, vec3i(27868, -1024, 29191), -ANGLE_90); // swing blades
+ // level 3a
+ //resetItem(item, 44, vec3i(73798, 2304, 9819), ANGLE_90); // uw gears
//#endif
camera.view.pos = camera.target.pos = item->pos;
@@ -75,6 +86,8 @@ struct Game
players[0] = (Lara*)item;
}
}
+
+ drawInit();
}
void resetItem(Item* item, int32 roomIndex, const vec3i &pos, int32 angleY)
@@ -99,11 +112,20 @@ struct Game
}
}
+ void nextFrame(int32 frames)
+ {
+ causticsFrame += frames;
+
+ animTexFrame += frames;
+ while (animTexFrame > 5)
+ {
+ animTexturesShift();
+ animTexFrame -= 5;
+ }
+ }
+
void update(int32 frames)
{
- #ifdef TEST
- return;
- #endif
if (frames > MAX_UPDATE_FRAMES) {
frames = MAX_UPDATE_FRAMES;
}
@@ -114,6 +136,8 @@ struct Game
camera.update();
}
+ nextFrame(frames);
+
if (keys & IK_SELECT) {
mixer.playMusic(TRACK_13_WAV);
}
@@ -129,36 +153,22 @@ struct Game
clear();
- #ifdef TEST
- #ifdef __GBA__
- VBlankIntrWait();
- #endif
+ #ifdef PROFILE
+ dbg_transform = 0;
+ dbg_poly = 0;
+ dbg_flush = 0;
+ dbg_vert_count = 0;
+ dbg_poly_count = 0;
+ #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
+ 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
drawNumber(fps, TEXT_POSX, 16);
diff --git a/src/platform/gba/inventory.h b/src/platform/gba/inventory.h
new file mode 100644
index 0000000..3e7504f
--- /dev/null
+++ b/src/platform/gba/inventory.h
@@ -0,0 +1,319 @@
+#ifndef H_INVENTORY
+#define H_INVENTORY
+
+#include "common.h"
+
+enum StringID {
+ STR_PASSPORT
+ , STR_COMPASS
+ , STR_HOME
+ , STR_MAP
+ , STR_DETAIL
+ , STR_SOUND
+ , STR_CONTROLS
+ , STR_GAMMA
+ , STR_PISTOLS
+ , STR_SHOTGUN
+ , STR_MAGNUMS
+ , STR_UZIS
+ , STR_AMMO_PISTOLS
+ , STR_AMMO_SHOTGUN
+ , STR_AMMO_MAGNUMS
+ , STR_AMMO_UZIS
+ , STR_EXPLOSIVE
+ , STR_MEDIKIT_SMALL
+ , STR_MEDIKIT_BIG
+ , STR_PUZZLE
+ , STR_PUZZLE_GOLD_IDOL
+ , STR_PUZZLE_GOLD_BAR
+ , STR_PUZZLE_COG
+ , STR_PUZZLE_FUSE
+ , STR_PUZZLE_ANKH
+ , STR_PUZZLE_HORUS
+ , STR_PUZZLE_ANUBIS
+ , STR_PUZZLE_SCARAB
+ , STR_PUZZLE_PYRAMID
+ , STR_LEADBAR
+ , STR_KEY
+ , STR_KEY_SILVER
+ , STR_KEY_RUSTY
+ , STR_KEY_GOLD
+ , STR_KEY_SAPPHIRE
+ , STR_KEY_NEPTUNE
+ , STR_KEY_ATLAS
+ , STR_KEY_DAMOCLES
+ , STR_KEY_THOR
+ , STR_KEY_ORNATE
+ , STR_SCION
+ , STR_MAX
+};
+
+const char* InvNames[STR_MAX] = {
+ "Game"
+ , "Compass"
+ , "Lara's Home"
+ , "Map"
+ , "Detail Levels"
+ , "Sound"
+ , "Controls"
+ , "Gamma"
+ , "Pistols"
+ , "Shotgun"
+ , "Magnums"
+ , "Uzis"
+ , "Pistol Clips"
+ , "Shotgun Shells"
+ , "Magnum Clips"
+ , "Uzi Clips"
+ , "Explosive"
+ , "Small Medi Pack"
+ , "Large Medi Pack"
+ , "Puzzle"
+ , "Gold Idol"
+ , "Gold Bar"
+ , "Machine Cog"
+ , "Fuse"
+ , "Ankh"
+ , "Eye of Horus"
+ , "Seal of Anubis"
+ , "Scarab"
+ , "Pyramid Key"
+ , "Lead Bar"
+ , "Key"
+ , "Silver Key"
+ , "Rusty Key"
+ , "Gold Key"
+ , "Sapphire Key"
+ , "Neptune Key"
+ , "Atlas Key"
+ , "Damocles Key"
+ , "Thor Key"
+ , "Ornate Key"
+ , "Scion"
+};
+
+enum InvSlot {
+// Items
+ SLOT_LEADBAR
+ , SLOT_KEY_ITEM_1
+ , SLOT_KEY_ITEM_2
+ , SLOT_KEY_ITEM_3
+ , SLOT_KEY_ITEM_4
+ , SLOT_PUZZLE_4
+ , SLOT_PUZZLE_3
+ , SLOT_PUZZLE_2
+ , SLOT_PUZZLE_1
+ , SLOT_SCION
+// Inventory
+ , SLOT_COMPASS
+ , SLOT_MAP
+ , SLOT_PISTOLS
+ , SLOT_AMMO_PISTOLS
+ , SLOT_SHOTGUN
+ , SLOT_AMMO_SHOTGUN
+ , SLOT_MAGNUMS
+ , SLOT_AMMO_MAGNUMS
+ , SLOT_UZIS
+ , SLOT_AMMO_UZIS
+ , SLOT_EXPLOSIVE
+ , SLOT_MEDIKIT_BIG
+ , SLOT_MEDIKIT_SMALL
+// Options
+ , SLOT_PASSPORT
+ , SLOT_DETAIL
+ , SLOT_SOUND
+ , SLOT_CONTROLS
+ , SLOT_GAMMA
+ , SLOT_HOME
+ , SLOT_MAX
+};
+
+
+struct InvItem
+{
+ uint8 type;
+ uint8 sid;
+ // TODO params
+};
+
+const InvItem INV_SLOTS[SLOT_MAX] = {
+ { ITEM_INV_LEADBAR , STR_LEADBAR }
+ , { ITEM_INV_KEY_ITEM_1 , STR_KEY }
+ , { ITEM_INV_KEY_ITEM_2 , STR_KEY }
+ , { ITEM_INV_KEY_ITEM_3 , STR_KEY }
+ , { ITEM_INV_KEY_ITEM_4 , STR_KEY }
+ , { ITEM_INV_PUZZLE_4 , STR_PUZZLE }
+ , { ITEM_INV_PUZZLE_3 , STR_PUZZLE }
+ , { ITEM_INV_PUZZLE_2 , STR_PUZZLE }
+ , { ITEM_INV_PUZZLE_1 , STR_PUZZLE }
+ , { ITEM_INV_SCION , STR_SCION }
+ , { ITEM_INV_COMPASS , STR_COMPASS }
+ , { ITEM_INV_MAP , STR_MAP }
+ , { ITEM_INV_PISTOLS , STR_PISTOLS }
+ , { ITEM_INV_AMMO_PISTOLS , STR_AMMO_PISTOLS }
+ , { ITEM_INV_SHOTGUN , STR_SHOTGUN }
+ , { ITEM_INV_AMMO_SHOTGUN , STR_AMMO_SHOTGUN }
+ , { ITEM_INV_MAGNUMS , STR_MAGNUMS }
+ , { ITEM_INV_AMMO_MAGNUMS , STR_AMMO_MAGNUMS }
+ , { ITEM_INV_UZIS , STR_UZIS }
+ , { ITEM_INV_AMMO_UZIS , STR_AMMO_UZIS }
+ , { ITEM_INV_EXPLOSIVE , STR_EXPLOSIVE }
+ , { ITEM_INV_MEDIKIT_BIG , STR_MEDIKIT_BIG }
+ , { ITEM_INV_MEDIKIT_SMALL , STR_MEDIKIT_SMALL }
+ , { ITEM_INV_PASSPORT , STR_PASSPORT }
+ , { ITEM_INV_DETAIL , STR_DETAIL }
+ , { ITEM_INV_SOUND , STR_SOUND }
+ , { ITEM_INV_CONTROLS , STR_CONTROLS }
+ , { ITEM_INV_GAMMA , STR_GAMMA }
+ , { ITEM_INV_HOME , STR_HOME }
+};
+
+
+struct Inventory
+{
+ InvSlot useSlot;
+
+ int32 numKeys;
+
+ int32 counts[X_COUNT(INV_SLOTS)];
+
+ Inventory()
+ {
+ memset(counts, 0, sizeof(counts));
+
+ useSlot = SLOT_MAX;
+ numKeys = 0;
+
+ add(ITEM_INV_PASSPORT);
+ add(ITEM_INV_DETAIL);
+ add(ITEM_INV_SOUND);
+ add(ITEM_INV_CONTROLS);
+ //add(ITEM_INV_GAMMA);
+ //add(ITEM_INV_HOME);
+
+ add(ITEM_INV_COMPASS);
+ add(ITEM_INV_PISTOLS);
+
+ //add(ITEM_INV_KEY_ITEM_1);
+ //add(ITEM_INV_PUZZLE_1);
+ }
+
+ ItemType remapToInv(ItemType type)
+ {
+ switch (type)
+ {
+ case ITEM_PISTOLS : return ITEM_INV_PISTOLS;
+ case ITEM_SHOTGUN : return ITEM_INV_SHOTGUN;
+ case ITEM_MAGNUMS : return ITEM_INV_MAGNUMS;
+ case ITEM_UZIS : return ITEM_INV_UZIS;
+ case ITEM_AMMO_PISTOLS : return ITEM_INV_AMMO_PISTOLS;
+ case ITEM_AMMO_SHOTGUN : return ITEM_INV_AMMO_SHOTGUN;
+ case ITEM_AMMO_MAGNUMS : return ITEM_INV_AMMO_MAGNUMS;
+ case ITEM_AMMO_UZIS : return ITEM_INV_AMMO_UZIS;
+ case ITEM_MEDIKIT_SMALL : return ITEM_INV_MEDIKIT_SMALL;
+ case ITEM_MEDIKIT_BIG : return ITEM_INV_MEDIKIT_BIG;
+ case ITEM_PUZZLE_1 : return ITEM_INV_PUZZLE_1;
+ case ITEM_PUZZLE_2 : return ITEM_INV_PUZZLE_2;
+ case ITEM_PUZZLE_3 : return ITEM_INV_PUZZLE_3;
+ case ITEM_PUZZLE_4 : return ITEM_INV_PUZZLE_4;
+ case ITEM_LEADBAR : return ITEM_INV_LEADBAR;
+ case ITEM_KEY_ITEM_1 : return ITEM_INV_KEY_ITEM_1;
+ case ITEM_KEY_ITEM_2 : return ITEM_INV_KEY_ITEM_2;
+ case ITEM_KEY_ITEM_3 : return ITEM_INV_KEY_ITEM_3;
+ case ITEM_KEY_ITEM_4 : return ITEM_INV_KEY_ITEM_4;
+ case ITEM_SCION_PICKUP_QUALOPEC :
+ case ITEM_SCION_PICKUP_DROP :
+ case ITEM_SCION_PICKUP_HOLDER : return ITEM_INV_SCION;
+ default : return type;
+ }
+ }
+
+ InvSlot remapToSlot(ItemType type)
+ {
+ type = remapToInv(type);
+
+ for (int32 i = 0; i < X_COUNT(INV_SLOTS); i++)
+ {
+ if (INV_SLOTS[i].type == type)
+ return (InvSlot)i;
+ }
+
+ ASSERT(false);
+ return SLOT_COMPASS;
+ }
+
+ InvSlot remapHoleToSlot(ItemType type)
+ {
+ switch (type)
+ {
+ case ITEM_PUZZLEHOLE_1 : return SLOT_PUZZLE_1;
+ case ITEM_PUZZLEHOLE_2 : return SLOT_PUZZLE_2;
+ case ITEM_PUZZLEHOLE_3 : return SLOT_PUZZLE_3;
+ case ITEM_PUZZLEHOLE_4 : return SLOT_PUZZLE_4;
+ case ITEM_KEYHOLE_1 : return SLOT_KEY_ITEM_1;
+ case ITEM_KEYHOLE_2 : return SLOT_KEY_ITEM_2;
+ case ITEM_KEYHOLE_3 : return SLOT_KEY_ITEM_3;
+ case ITEM_KEYHOLE_4 : return SLOT_KEY_ITEM_4;
+ default : return SLOT_MAX;
+ }
+ }
+
+ void add(ItemType type, int32 count = 1)
+ {
+ InvSlot slot = remapToSlot(type);
+ counts[slot] += count;
+ // TODO check max
+
+ if (slot < SLOT_COMPASS) {
+ numKeys += count;
+ }
+ }
+
+ void remove(InvSlot slot, int32 count)
+ {
+ counts[slot] -= count;
+
+ if (slot < SLOT_COMPASS) {
+ numKeys -= count;
+ }
+ }
+
+ void show(Item* lara, Item* hole)
+ {
+ if (hole) {
+ useSlot = remapHoleToSlot((ItemType)hole->type);
+ }
+ }
+
+ bool applyItem(Item* hole)
+ {
+ #define CHECK_CASE(A, B) case A: { if (useSlot != B) return false; break; }
+
+ switch (hole->type)
+ {
+ CHECK_CASE(ITEM_PUZZLEHOLE_1, SLOT_PUZZLE_1);
+ CHECK_CASE(ITEM_PUZZLEHOLE_2, SLOT_PUZZLE_2);
+ CHECK_CASE(ITEM_PUZZLEHOLE_3, SLOT_PUZZLE_3);
+ CHECK_CASE(ITEM_PUZZLEHOLE_4, SLOT_PUZZLE_4);
+ CHECK_CASE(ITEM_KEYHOLE_1, SLOT_KEY_ITEM_1);
+ CHECK_CASE(ITEM_KEYHOLE_2, SLOT_KEY_ITEM_2);
+ CHECK_CASE(ITEM_KEYHOLE_3, SLOT_KEY_ITEM_3);
+ CHECK_CASE(ITEM_KEYHOLE_4, SLOT_KEY_ITEM_4);
+ default: return false;
+ }
+
+ remove(useSlot, 1);
+ useSlot = SLOT_MAX;
+
+ return true;
+ }
+
+ void draw()
+ {
+ //
+ }
+};
+
+Inventory inventory;
+
+#endif
diff --git a/src/platform/gba/item.h b/src/platform/gba/item.h
index f3ae6b2..0f7bc86 100644
--- a/src/platform/gba/item.h
+++ b/src/platform/gba/item.h
@@ -5,61 +5,59 @@
#include "sound.h"
#include "camera.h"
#include "draw.h"
+#include "room.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;
+X_INLINE int16 angleLerp(int16 a, int16 b, int32 w)
+{
+ int16 d = b - a;
+ if (d > +w) return a + w;
+ if (d < -w) return a - w;
+ return b;
}
+#define angleDec(angle, value) angleLerp(angle, 0, value)
+
Mixer::Sample* soundPlay(int16 id, const vec3i &pos)
{
int16 a = level.soundMap[id];
- if (a == -1) {
+ if (a == -1)
return NULL;
- }
const SoundInfo* b = level.soundsInfo + a;
- if (b->chance && b->chance < xRand()) {
+ if (b->chance && b->chance < rand_draw())
return NULL;
- }
vec3i d = pos - camera.target.pos;
- if (abs(d.x) >= SND_MAX_DIST || abs(d.y) >= SND_MAX_DIST || abs(d.z) >= SND_MAX_DIST) {
+ 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;
+ volume -= rand_draw() >> 2;
}
volume = X_MIN(volume, 0x7FFF) >> 9;
- if (volume <= 0) {
+ if (volume <= 0)
return NULL;
- }
int32 pitch = 128;
if (b->flags.pitch) {
- pitch += ((xRand() * 13) >> 14) - 13;
+ pitch += ((rand_draw() * 13) >> 14) - 13;
}
int32 index = b->index;
if (b->flags.count > 1) {
- index += (xRand() * b->flags.count) >> 15;
+ index += (rand_draw() * b->flags.count) >> 15;
}
const uint8 *data = level.soundData + level.soundOffsets[index];
@@ -68,9 +66,36 @@ Mixer::Sample* soundPlay(int16 id, const vec3i &pos)
memcpy(&size, data + 40, 4); // TODO preprocess and remove wave header
data += 44;
+ if (id >= 148 + 25) {
+ pitch >>= 1; // GYM PC sample rate hack
+ }
+
return mixer.playSample(data, size, volume, pitch, b->flags.mode);
}
+void soundStop(int16 id)
+{
+ int16 a = level.soundMap[id];
+
+ if (a == -1)
+ return;
+
+ const SoundInfo* b = level.soundsInfo + a;
+
+ for (int32 i = 0; i < b->flags.count; i++)
+ {
+ int32 index = b->index + i;
+
+ const uint8 *data = level.soundData + level.soundOffsets[index];
+
+ int32 size;
+ memcpy(&size, data + 40, 4); // TODO preprocess and remove wave header
+ data += 44;
+
+ mixer.stopSample(data);
+ }
+}
+
void musicPlay(int32 track)
{
if (track > 25 && track < 57) // gym tutorial
@@ -189,15 +214,17 @@ void Item::animCmd(bool fx, const Anim* anim)
{
int32 cmd = *ptr++;
- switch (cmd) {
+ switch (cmd)
+ {
case ANIM_CMD_NONE:
break;
+
case ANIM_CMD_OFFSET:
{
if (!fx)
{
- int32 s = phd_sin(moveAngle);
- int32 c = phd_cos(moveAngle);
+ int32 s = phd_sin(angle.y);
+ int32 c = phd_cos(angle.y);
int32 x = ptr[0];
int32 y = ptr[1];
int32 z = ptr[2];
@@ -208,7 +235,9 @@ void Item::animCmd(bool fx, const Anim* anim)
ptr += 3;
break;
}
+
case ANIM_CMD_JUMP:
+ {
if (!fx)
{
if (vSpeedHack) {
@@ -222,23 +251,37 @@ void Item::animCmd(bool fx, const Anim* anim)
}
ptr += 2;
break;
+ }
+
case ANIM_CMD_EMPTY:
+ {
+ if (!fx) {
+ weaponState = WEAPON_STATE_FREE;
+ }
break;
+ }
+
case ANIM_CMD_KILL:
- if (!fx)
- {
+ {
+ if (!fx) {
flags.status = ITEM_FLAGS_STATUS_INACTIVE;
}
break;
+ }
+
case ANIM_CMD_SOUND:
- if (fx && frameIndex == ptr[0])
- {
+ {
+ if (fx && frameIndex == ptr[0]) {
soundPlay(ptr[1] & 0x03FFF, pos);
}
ptr += 2;
break;
+ }
+
case ANIM_CMD_EFFECT:
- if (fx && frameIndex == ptr[0]) {
+ {
+ if (fx && frameIndex == ptr[0])
+ {
switch (ptr[1]) {
case FX_ROTATE_180 : angle.y += ANGLE_180; break;
/*
@@ -262,26 +305,42 @@ void Item::animCmd(bool fx, const Anim* anim)
}
ptr += 2;
break;
+ }
}
}
}
-void Item::skipAnim()
+void Item::animSkip(int32 stateBefore, int32 stateAfter, bool advance)
{
+ goalState = stateBefore;
+
vec3i p = pos;
while (state != goalState)
{
- updateAnim(false);
+ animProcess(false);
+ }
+
+ if (advance) {
+ animProcess();
}
pos = p;
vSpeed = 0;
hSpeed = 0;
+
+ goalState = stateAfter;
}
-void Item::updateAnim(bool movement)
+#define ANIM_MOVE_LERP_POS (16)
+#define ANIM_MOVE_LERP_ROT (2 * DEG2SHORT)
+
+void Item::animProcess(bool movement)
{
+ if (models[type].count <= 0) {
+ return; // TODO sprite animation
+ }
+
ASSERT(models[type].count > 0);
const Anim* anim = level.anims + animIndex;
@@ -311,6 +370,41 @@ void Item::updateAnim(bool movement)
#endif
}
+bool Item::animIsEnd(int32 offset) const
+{
+ return frameIndex == level.anims[animIndex].frameEnd - offset;
+}
+
+bool Item::moveTo(const vec3i &point, Item* item, bool lerp)
+{
+ // lerp position
+ vec3i p = item->getRelative(point);
+
+ if (!lerp)
+ {
+ pos = p;
+ angle = item->angle;
+ return true;
+ }
+
+ vec3i posDelta = p - pos;
+
+ int32 dist = phd_sqrt(X_SQR(posDelta.x) + X_SQR(posDelta.y) + X_SQR(posDelta.z));
+
+ if (dist > ANIM_MOVE_LERP_POS) {
+ pos += (posDelta * ANIM_MOVE_LERP_POS) / dist;
+ } else {
+ pos = p;
+ }
+
+ // lerp rotation
+ angle.x = angleLerp(angle.x, item->angle.x, ANIM_MOVE_LERP_ROT);
+ angle.y = angleLerp(angle.y, item->angle.y, ANIM_MOVE_LERP_ROT);
+ angle.z = angleLerp(angle.z, item->angle.z, ANIM_MOVE_LERP_ROT);
+
+ return (pos == p && angle == item->angle);
+}
+
Item* Item::add(ItemType type, Room* room, const vec3i &pos, int32 angleY)
{
if (!Item::sFirstFree) {
@@ -324,7 +418,7 @@ Item* Item::add(ItemType type, Room* room, const vec3i &pos, int32 angleY)
item->type = type;
item->pos = pos;
item->angle.y = angleY;
- item->intensity = 0;
+ item->intensity = 128;
item->init(room);
@@ -342,7 +436,7 @@ void Item::remove()
void Item::activate()
{
- ASSERT(!flags.active)
+ //ASSERT(!flags.active)
flags.active = true;
@@ -389,62 +483,47 @@ void Item::updateRoom(int32 offset)
}
const Sector* sector = room->getSector(pos.x, pos.z);
- floor = sector->getFloor(pos.x, pos.y, pos.z);
+ roomFloor = sector->getFloor(pos.x, pos.y, pos.z);
}
-int32 Item::calcLighting(const Bounds& box) const
+vec3i Item::getRelative(const vec3i &point) const
{
matrixPush();
+
Matrix &m = matrixGet();
- m[0][3] = m[1][3] = m[2][3] = 0;
+
+ matrixSetIdentity();
matrixRotateYXZ(angle.x, angle.y, angle.z);
- vec3i p((box.maxX + box.minX) >> 1,
- (box.maxY + box.minY) >> 1,
- (box.maxZ + box.minZ) >> 1);
+ vec3i p;
+ p.x = pos.x + (DP33(m[0], point) >> FIXED_SHIFT);
+ p.y = pos.y + (DP33(m[1], point) >> FIXED_SHIFT);
+ p.z = pos.z + (DP33(m[2], point) >> FIXED_SHIFT);
- matrixTranslate(p);
-
- p = vec3i(m[0][3] >> FIXED_SHIFT,
- m[1][3] >> FIXED_SHIFT,
- m[2][3] >> FIXED_SHIFT) + pos;
matrixPop();
- const RoomInfo* info = room->info;
+ return p;
+}
- if (!info->lightsCount) {
- return info->ambient << 5;
+int32 Item::getWaterLevel()
+{
+ const Sector* sector = room->getWaterSector(pos.x, pos.z);
+ if (sector) {
+ return sector->ceiling * 256;
}
- int32 ambient = 8191 - (info->ambient << 5);
- int32 maxLum = 0;
+ return WALL;
+}
- for (int i = 0; i < info->lightsCount; i++)
- {
- const Light* light = room->data.lights + i;
+int32 Item::getWaterDepth()
+{
+ const Sector* sector = room->getWaterSector(pos.x, pos.z);
- // TODO preprocess align
- vec3i pos;
- pos.x = light->pos.x + (info->x << 8);
- pos.y = light->pos.y;
- pos.z = light->pos.z + (info->z << 8);
-
- int32 radius = light->radius << 8;
- int32 intensity = light->intensity << 5;
-
- vec3i d = p - pos;
- int32 dist = dot(d, d) >> 12;
- int32 att = X_SQR(radius) >> 12;
-
- int32 lum = (intensity * att) / (dist + att) + ambient;
-
- if (lum > maxLum)
- {
- maxLum = lum;
- }
+ if (sector) {
+ return sector->getFloor(pos.x, pos.y, pos.z) - (sector->ceiling * 256);
}
- return 8191 - ((maxLum + ambient) >> 1);
+ return WALL;
}
#include "lara.h"
@@ -506,8 +585,7 @@ void Item::draw()
void Item::collide(Lara* lara, CollisionInfo* cinfo)
{
- UNUSED(lara);
- UNUSED(cinfo);
+ // empty
}
Item* Item::init(Room* room)
@@ -547,7 +625,7 @@ Item* Item::init(Room* room)
INIT_ITEM( NATLA , Natla );
INIT_ITEM( ADAM , Adam );
INIT_ITEM( TRAP_FLOOR , TrapFloor );
- // INIT_ITEM( TRAP_SWING_BLADE , ??? );
+ INIT_ITEM( TRAP_SWING_BLADE , TrapSwingBlade );
// INIT_ITEM( TRAP_SPIKES , ??? );
// INIT_ITEM( TRAP_BOULDER , ??? );
INIT_ITEM( DART , Dart );
@@ -559,10 +637,10 @@ Item* Item::init(Room* room)
// INIT_ITEM( HAMMER_BLOCK , ??? );
// INIT_ITEM( LIGHTNING , ??? );
// INIT_ITEM( MOVING_OBJECT , ??? );
- // INIT_ITEM( BLOCK_1 , ??? );
- // INIT_ITEM( BLOCK_2 , ??? );
- // INIT_ITEM( BLOCK_3 , ??? );
- // INIT_ITEM( BLOCK_4 , ??? );
+ INIT_ITEM( BLOCK_1 , Block );
+ INIT_ITEM( BLOCK_2 , Block );
+ INIT_ITEM( BLOCK_3 , Block );
+ INIT_ITEM( BLOCK_4 , Block );
// INIT_ITEM( MOVING_BLOCK , ??? );
// INIT_ITEM( TRAP_CEILING_1 , ??? );
// INIT_ITEM( TRAP_CEILING_2 , ??? );
@@ -576,12 +654,12 @@ Item* Item::init(Room* room)
INIT_ITEM( DOOR_6 , Door );
INIT_ITEM( DOOR_7 , Door );
INIT_ITEM( DOOR_8 , Door );
- // INIT_ITEM( TRAP_DOOR_1 , ??? );
- // INIT_ITEM( TRAP_DOOR_2 , ??? );
+ INIT_ITEM( TRAP_DOOR_1 , TrapDoor );
+ INIT_ITEM( TRAP_DOOR_2 , TrapDoor );
// INIT_ITEM( UNUSED_3 , ??? );
// INIT_ITEM( BRIDGE_FLAT , ??? );
- // INIT_ITEM( BRIDGE_TILT1 , ??? );
- // INIT_ITEM( BRIDGE_TILT2 , ??? );
+ // INIT_ITEM( BRIDGE_TILT_1 , ??? );
+ // INIT_ITEM( BRIDGE_TILT_2 , ??? );
// INIT_ITEM( INV_PASSPORT , ??? );
// INIT_ITEM( INV_COMPASS , ??? );
// INIT_ITEM( INV_HOME , ??? );
@@ -629,10 +707,10 @@ Item* Item::init(Room* room)
// INIT_ITEM( INV_PUZZLE_2 , ??? );
// INIT_ITEM( INV_PUZZLE_3 , ??? );
// INIT_ITEM( INV_PUZZLE_4 , ??? );
- // INIT_ITEM( PUZZLE_HOLE_1 , ??? );
- // INIT_ITEM( PUZZLE_HOLE_2 , ??? );
- // INIT_ITEM( PUZZLE_HOLE_3 , ??? );
- // INIT_ITEM( PUZZLE_HOLE_4 , ??? );
+ INIT_ITEM( PUZZLEHOLE_1 , PuzzleHole );
+ INIT_ITEM( PUZZLEHOLE_2 , PuzzleHole );
+ INIT_ITEM( PUZZLEHOLE_3 , PuzzleHole );
+ INIT_ITEM( PUZZLEHOLE_4 , PuzzleHole );
// INIT_ITEM( PUZZLE_DONE_1 , ??? );
// INIT_ITEM( PUZZLE_DONE_2 , ??? );
// INIT_ITEM( PUZZLE_DONE_3 , ??? );
@@ -648,10 +726,10 @@ Item* Item::init(Room* room)
// INIT_ITEM( INV_KEY_ITEM_2 , ??? );
// INIT_ITEM( INV_KEY_ITEM_3 , ??? );
// INIT_ITEM( INV_KEY_ITEM_4 , ??? );
- // INIT_ITEM( KEY_HOLE_1 , ??? );
- // INIT_ITEM( KEY_HOLE_2 , ??? );
- // INIT_ITEM( KEY_HOLE_3 , ??? );
- // INIT_ITEM( KEY_HOLE_4 , ??? );
+ INIT_ITEM( KEYHOLE_1 , KeyHole );
+ INIT_ITEM( KEYHOLE_2 , KeyHole );
+ INIT_ITEM( KEYHOLE_3 , KeyHole );
+ INIT_ITEM( KEYHOLE_4 , KeyHole );
// INIT_ITEM( UNUSED_4 , ??? );
// INIT_ITEM( UNUSED_5 , ??? );
// INIT_ITEM( SCION_PICKUP_QUALOPEC , ??? );
@@ -681,16 +759,16 @@ Item* Item::init(Room* room)
// INIT_ITEM( UNUSED_13 , ??? );
// INIT_ITEM( UNUSED_14 , ??? );
INIT_ITEM( VIEW_TARGET , ViewTarget );
- // INIT_ITEM( WATERFALL , ??? );
+ INIT_ITEM( WATERFALL , Waterfall );
// INIT_ITEM( NATLA_BULLET , ??? );
// INIT_ITEM( MUTANT_BULLET , ??? );
// INIT_ITEM( CENTAUR_BULLET , ??? );
// INIT_ITEM( UNUSED_15 , ??? );
// INIT_ITEM( UNUSED_16 , ??? );
// INIT_ITEM( LAVA_PARTICLE , ??? );
- // INIT_ITEM( TRAP_LAVA_EMITTER , ??? );
+ INIT_ITEM( LAVA_EMITTER , LavaEmitter );
// INIT_ITEM( FLAME , ??? );
- // INIT_ITEM( TRAP_FLAME_EMITTER , ??? );
+ // INIT_ITEM( FLAME_EMITTER , ??? );
// INIT_ITEM( TRAP_LAVA , ??? );
// INIT_ITEM( MUTANT_EGG_BIG , ??? );
// INIT_ITEM( BOAT , ??? );
diff --git a/src/platform/gba/lara.h b/src/platform/gba/lara.h
index b84a5e3..6f0a9ee 100644
--- a/src/platform/gba/lara.h
+++ b/src/platform/gba/lara.h
@@ -6,63 +6,98 @@
#include "collision.h"
#include "camera.h"
+// -------------
+// TODO list - GBA demo (GYM, LEVEL1, LEVEL2)
+// -------------
+// fix portals flickering
+// darts damage
+// swing blade damage
+// multi-pickups
+// sprite effects (splash, smoke, ricochet)
+// animation lerp for Lara (enemies?)
+// inventory
+// camera look
+// lookat
+// weapons
+// enemies (Bat, Wolf, Bear)
+// main menu
+// save game
+// ADPCM sounds and tracks
+// gameflow
+// -------------
+
#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 )
+ E( STATE_WALK ) \
+ E( STATE_RUN ) \
+ E( STATE_STOP ) \
+ E( STATE_JUMP ) \
+ E( STATE_POSE ) \
+ E( STATE_BACK_FAST ) \
+ E( STATE_TURN_RIGHT ) \
+ E( STATE_TURN_LEFT ) \
+ E( STATE_DEATH ) \
+ E( STATE_FALL ) \
+ E( STATE_HANG ) \
+ E( STATE_REACH ) \
+ E( STATE_SPLAT ) \
+ E( STATE_UW_TREAD ) \
+ E( STATE_LAND ) \
+ E( STATE_COMPRESS ) \
+ E( STATE_BACK ) \
+ E( STATE_UW_SWIM ) \
+ E( STATE_UW_GLIDE ) \
+ E( STATE_HANG_UP ) \
+ E( STATE_TURN_FAST ) \
+ E( STATE_STEP_RIGHT ) \
+ E( STATE_STEP_LEFT ) \
+ E( STATE_ROLL_END ) \
+ E( STATE_SLIDE ) \
+ E( STATE_JUMP_BACK ) \
+ E( STATE_JUMP_RIGHT ) \
+ E( STATE_JUMP_LEFT ) \
+ E( STATE_JUMP_UP ) \
+ E( STATE_FALL_BACK ) \
+ E( STATE_HANG_LEFT ) \
+ E( STATE_HANG_RIGHT ) \
+ E( STATE_SLIDE_BACK ) \
+ E( STATE_SURF_TREAD ) \
+ E( STATE_SURF_SWIM ) \
+ E( STATE_UW_DIVE ) \
+ E( STATE_BLOCK_PUSH ) \
+ E( STATE_BLOCK_PULL ) \
+ E( STATE_BLOCK_READY ) \
+ E( STATE_PICKUP ) \
+ E( STATE_SWITCH_DOWN ) \
+ E( STATE_SWITCH_UP ) \
+ E( STATE_USE_KEY ) \
+ E( STATE_USE_PUZZLE ) \
+ E( STATE_DEATH_UW ) \
+ E( STATE_ROLL_START ) \
+ E( STATE_SPECIAL ) \
+ E( STATE_SURF_BACK ) \
+ E( STATE_SURF_LEFT ) \
+ E( STATE_SURF_RIGHT ) \
+ E( STATE_USE_MIDAS ) \
+ E( STATE_DEATH_MIDAS ) \
+ E( STATE_SWAN_DIVE ) \
+ E( STATE_FAST_DIVE ) \
+ E( STATE_HANDSTAND ) \
+ E( STATE_WATER_OUT ) \
+ E( STATE_CLIMB_START ) \
+ E( STATE_CLIMB_UP ) \
+ E( STATE_CLIMB_LEFT ) \
+ E( STATE_CLIMB_END ) \
+ E( STATE_CLIMB_RIGHT ) \
+ E( STATE_CLIMB_DOWN ) \
+ E( STATE_UNUSED_1 ) \
+ E( STATE_UNUSED_2 ) \
+ E( STATE_UNUSED_3 ) \
+ E( STATE_WADE ) \
+ E( STATE_ROLL_UW ) \
+ E( STATE_PICKUP_FLARE ) \
+ E( STATE_ROLL_AIR ) \
+ E( STATE_UNUSED_4 ) \
+ E( STATE_ZIPLINE )
#define DECL_ENUM(v) v,
#define DECL_S_HANDLER(v) &Lara::s_##v,
@@ -80,7 +115,7 @@
#define LARA_RADIUS_WATER 300
#define LARA_RADIUS_CLIMB 220
#define LARA_MAX_HEALTH 1000
-#define LARA_MAX_OXYGEN 1800
+#define LARA_MAX_OXYGEN 1800 // TODO +30 sec for TR5
#define LARA_HEIGHT 762
#define LARA_TURN_ACCEL (2 * DEG2SHORT + DEG2SHORT / 4)
#define LARA_TURN_JUMP (3 * DEG2SHORT)
@@ -102,6 +137,9 @@
#define LARA_SURF_ACCEL 8
#define LARA_SURF_SPEED_MAX 60
#define LARA_DIVE_SPEED 80
+#define LARA_WADE_MIN_DEPTH 384
+#define LARA_WADE_MAX_DEPTH 730
+#define LARA_SWIM_MIN_DEPTH 512
enum {
JOINT_HIPS = 0,
@@ -147,6 +185,8 @@ enum {
JOINT_MASK_BRAID = JOINT_MASK_HEAD | JOINT_MASK_CHEST | JOINT_MASK_ARM_L1 | JOINT_MASK_ARM_L2 | JOINT_MASK_ARM_R1 | JOINT_MASK_ARM_R2,
};
+int32 swimTimer;
+
struct Lara : Item
{
enum State {
@@ -291,7 +331,7 @@ struct Lara : Item
void stopScreaming()
{
- // TODO
+ soundStop(SND_SCREAM);
}
// common
@@ -310,17 +350,17 @@ struct Lara : Item
return (angle & (ANGLE_90 - 1)) > 0;
}
- void alignWall()
+ void alignWall(int32 radius)
{
- int x = (~1023) & pos.x;
- int z = (~1023) & pos.z;
+ int x = pos.x & ~1023;
+ int z = pos.z & ~1023;
switch (angle.y)
{
- 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;
+ case ANGLE_0 : pos.z = z + 1024 - radius; break;
+ case ANGLE_90 : pos.x = x + 1024 - radius; break;
+ case -ANGLE_90 : pos.x = x + radius; break;
+ case ANGLE_180 : pos.z = z + radius; break;
default : ASSERT(false);
}
}
@@ -335,7 +375,7 @@ struct Lara : Item
Room* roomFront = room->getRoom(x, y, z);
const Sector* sector = roomFront->getSector(x, z);
- int16 floor = sector->getFloor(x, y, z);
+ int32 floor = sector->getFloor(x, y, z);
if (floor != WALL) {
floor -= pos.y;
@@ -353,16 +393,41 @@ struct Lara : Item
return frame->box;
}
- bool checkDeath()
+ bool checkDeath(State deathState)
{
- return health <= 0;
+ if (health <= 0) {
+ goalState = deathState;
+ return true;
+ }
+ return false;
}
// state control
- bool s_checkFront(int16 angle)
+ bool s_checkFront(int16 angleDelta)
{
- UNUSED(angle); // TODO
- return true; // TODO
+ CollisionInfo tmpInfo = cinfo;
+ int16 tmpAngle = moveAngle;
+
+ c_angle(angleDelta);
+ cinfo.radius = LARA_RADIUS + 4;
+
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+ cinfo.stopOnSlant = true;
+ cinfo.gapCeiling = 0;
+
+ if ((angleDelta == ANGLE_180) && ((input & IN_WALK) || (waterState == WATER_STATE_WADE))) {
+ cinfo.gapPos = LARA_STEP_HEIGHT;
+ }
+
+ collideRoom(this, LARA_HEIGHT, 0);
+
+ bool collide = (cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING);
+
+ cinfo = tmpInfo;
+ moveAngle = tmpAngle;
+
+ return !collide;
}
void s_ignoreEnemy()
@@ -388,10 +453,10 @@ struct Lara : Item
{
if (vSpeed > 131)
{
- if (state == SWAN_DIVE) {
- goalState = FAST_DIVE;
+ if (state == STATE_SWAN_DIVE) {
+ goalState = STATE_FAST_DIVE;
} else {
- goalState = FALL;
+ goalState = STATE_FALL;
}
return true;
}
@@ -402,9 +467,9 @@ struct Lara : Item
{
if ((input & IN_UP) && s_checkFront(ANGLE_0)) {
if (input & IN_WALK) {
- goalState = WALK;
+ goalState = STATE_WALK;
} else {
- goalState = RUN;
+ goalState = STATE_RUN;
}
} else {
goalState = stopState;
@@ -421,10 +486,10 @@ struct Lara : Item
if ((waterState == WATER_STATE_ABOVE) && roll)
{
- if ((state == RUN) || (state == STOP))
+ if ((state == STATE_RUN) || (state == STATE_STOP))
{
animSet(ANIM_STAND_ROLL_BEGIN, true, 2);
- goalState = STOP;
+ goalState = STATE_STOP;
return true;
}
}
@@ -432,114 +497,131 @@ struct Lara : Item
return false;
}
- S_HANDLER( WALK )
+ void s_turnUW()
{
- if (checkDeath()) {
- goalState = STOP;
- return;
+ if (input & IN_UP) {
+ angle.x -= 2 * DEG2SHORT;
+ } else if (input & IN_DOWN) {
+ angle.x += 2 * DEG2SHORT;
}
+ if (input & IN_LEFT) {
+ turnSpeed = max(turnSpeed - LARA_TURN_ACCEL, -LARA_TURN_MED);
+ angle.z -= LARA_TILT_ACCEL * 2;
+ } else if (input & IN_RIGHT) {
+ turnSpeed = min(turnSpeed + LARA_TURN_ACCEL, LARA_TURN_MED);
+ angle.z += LARA_TILT_ACCEL * 2;
+ }
+ }
+
+ void s_dive()
+ {
+ animSet(ANIM_SURF_DIVE, true);
+ angle.x = -45 * DEG2SHORT;
+ vSpeed = LARA_DIVE_SPEED;
+ waterState = WATER_STATE_UNDER;
+ }
+
+ S_HANDLER( STATE_WALK )
+ {
+ if (checkDeath(STATE_STOP))
+ return;
+
s_rotate(LARA_TURN_SLOW, 0);
- s_checkWalk(STOP);
+ s_checkWalk(STATE_STOP);
}
- S_HANDLER( RUN )
+ S_HANDLER( STATE_RUN )
{
- if (checkDeath()) {
- goalState = DEATH;
+ if (checkDeath(STATE_DEATH))
return;
- }
- if (s_checkRoll()) {
+ if (s_checkRoll())
return;
- }
s_rotate(LARA_TURN_FAST, 1);
if ((input & IN_JUMP) && !flags.gravity) {
- goalState = FORWARD_JUMP;
+ goalState = STATE_JUMP;
} else {
- s_checkWalk(STOP);
+ s_checkWalk(STATE_STOP);
}
}
- S_HANDLER( STOP )
+ S_HANDLER( STATE_STOP )
{
- if (checkDeath())
+ if (checkDeath(STATE_DEATH))
+ return;
+
+ if (s_checkRoll())
+ return;
+
+ goalState = STATE_STOP;
+
+ if ((input & (IN_UP | IN_ACTION)) == (IN_UP | IN_ACTION))
{
- 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()) {
+
+ if (c_checkClimbUp())
return;
- }
}
- */
+
if (input & IN_WALK) {
if ((input & IN_LEFT) && s_checkFront(-ANGLE_90)) {
- goalState = STEP_LEFT;
+ goalState = STATE_STEP_LEFT;
} else if ((input & IN_RIGHT) && s_checkFront(ANGLE_90)) {
- goalState = STEP_RIGHT;
+ goalState = STATE_STEP_RIGHT;
}
} else {
if (input & IN_LEFT) {
- goalState = TURN_LEFT;
+ goalState = STATE_TURN_LEFT;
} else if (input & IN_RIGHT) {
- goalState = TURN_RIGHT;
+ goalState = STATE_TURN_RIGHT;
}
}
if (input & IN_JUMP) {
- goalState = COMPRESS;
+ goalState = STATE_COMPRESS;
} else if ((input & IN_UP) && s_checkFront(ANGLE_0)) {
if (input & IN_WALK) {
- s_WALK();
+ s_STATE_WALK();
} else {
- s_RUN();
+ s_STATE_RUN();
}
} else if ((input & IN_DOWN) && s_checkFront(ANGLE_180)) {
if (input & IN_WALK) {
- s_BACK();
+ s_STATE_BACK();
} else {
- goalState = FAST_BACK;
+ goalState = STATE_BACK_FAST;
}
}
}
- S_HANDLER( FORWARD_JUMP )
+ S_HANDLER( STATE_JUMP )
{
- if (goalState == SWAN_DIVE ||
- goalState == REACH)
+ if (goalState == STATE_SWAN_DIVE ||
+ goalState == STATE_REACH)
{
- goalState = FORWARD_JUMP;
+ goalState = STATE_JUMP;
}
- if (goalState != DEATH &&
- goalState != STOP &&
- goalState != RUN)
+ if (goalState != STATE_DEATH &&
+ goalState != STATE_STOP &&
+ goalState != STATE_RUN)
{
if (input & IN_ACTION)
{
- goalState = REACH;
+ goalState = STATE_REACH;
}
if (input & IN_WALK)
{
- goalState = SWAN_DIVE;
+ goalState = STATE_SWAN_DIVE;
}
s_checkRoll();
@@ -549,22 +631,25 @@ struct Lara : Item
s_rotate(LARA_TURN_JUMP, 0);
}
- S_HANDLER( POSE )
+ S_HANDLER( STATE_POSE )
{
// empty
}
- S_HANDLER( FAST_BACK )
+ S_HANDLER( STATE_BACK_FAST )
{
s_rotate(LARA_TURN_MED, 0);
- goalState = STOP;
+ goalState = STATE_STOP;
}
- S_HANDLER( TURN_RIGHT )
+ S_HANDLER( STATE_TURN_RIGHT )
{
- if (checkDeath() || (input & IN_LOOK))
+ if (checkDeath(STATE_STOP))
+ return;
+
+ if (input & IN_LOOK)
{
- goalState = STOP;
+ goalState = STATE_STOP;
return;
}
@@ -572,17 +657,20 @@ struct Lara : Item
if (turnSpeed > LARA_TURN_SLOW)
{
- goalState = FAST_TURN;
+ goalState = STATE_TURN_FAST;
}
- s_checkWalk((input & IN_RIGHT) ? goalState : STOP);
+ s_checkWalk((input & IN_RIGHT) ? goalState : STATE_STOP);
}
- S_HANDLER( TURN_LEFT )
+ S_HANDLER( STATE_TURN_LEFT )
{
- if (checkDeath() || (input & IN_LOOK))
+ if (checkDeath(STATE_STOP))
+ return;
+
+ if (input & IN_LOOK)
{
- goalState = STOP;
+ goalState = STATE_STOP;
return;
}
@@ -590,184 +678,242 @@ struct Lara : Item
if (turnSpeed < -LARA_TURN_SLOW)
{
- goalState = FAST_TURN;
+ goalState = STATE_TURN_FAST;
}
- s_checkWalk((input & IN_LEFT) ? goalState : STOP);
+ s_checkWalk((input & IN_LEFT) ? goalState : STATE_STOP);
}
- S_HANDLER( DEATH )
+ S_HANDLER( STATE_DEATH )
{
s_ignoreEnemy();
}
- S_HANDLER( FALL )
+ S_HANDLER( STATE_FALL )
{
hSpeed = (hSpeed * 95) / 100;
- if (vSpeed >= 154)
- {
+
+ if (vSpeed >= 154) {
startScreaming();
}
}
- S_HANDLER( HANG )
+ S_HANDLER( STATE_HANG )
{
camera.targetAngleX = -60 * DEG2SHORT;
s_ignoreEnemy();
if (input & IN_LEFT) {
- goalState = HANG_LEFT;
+ goalState = STATE_HANG_LEFT;
} else if (input & IN_RIGHT) {
- goalState = HANG_RIGHT;
+ goalState = STATE_HANG_RIGHT;
}
}
- S_HANDLER( REACH )
+ S_HANDLER( STATE_REACH )
{
camera.targetAngleY = 85 * DEG2SHORT;
s_checkFall();
}
- S_HANDLER( SPLAT )
+ S_HANDLER( STATE_SPLAT )
{
// empty
}
- S_HANDLER( TREAD ) {} // TODO
+ S_HANDLER( STATE_UW_TREAD )
+ {
+ if (checkDeath(STATE_DEATH_UW))
+ return;
- S_HANDLER( LAND )
+ if (s_checkRoll())
+ return;
+
+ s_turnUW();
+
+ if (input & IN_JUMP) {
+ goalState = STATE_UW_SWIM;
+ }
+
+ vSpeed = max(vSpeed - LARA_SWIM_FRICTION, 0);
+ }
+
+ S_HANDLER( STATE_LAND )
{
// empty
}
- S_HANDLER( COMPRESS )
+ S_HANDLER( STATE_COMPRESS )
{
if ((input & IN_UP) && getFront(ANGLE_0) >= -LARA_STEP_HEIGHT) {
- goalState = FORWARD_JUMP;
+ goalState = STATE_JUMP;
} else if ((input & IN_LEFT) && getFront(-ANGLE_90) >= -LARA_STEP_HEIGHT) {
- goalState = LEFT_JUMP;
+ goalState = STATE_JUMP_LEFT;
} else if ((input & IN_RIGHT) && getFront(ANGLE_90) >= -LARA_STEP_HEIGHT) {
- goalState = RIGHT_JUMP;
+ goalState = STATE_JUMP_RIGHT;
} else if ((input & IN_DOWN) && getFront(ANGLE_180) >= -LARA_STEP_HEIGHT) {
- goalState = BACK_JUMP;
+ goalState = STATE_JUMP_BACK;
}
s_checkFall();
}
- S_HANDLER( BACK )
+ S_HANDLER( STATE_BACK )
{
- if (checkDeath()) {
- goalState = STOP;
+ if (checkDeath(STATE_STOP))
return;
- }
if ((input & (IN_WALK | IN_DOWN)) != (IN_WALK | IN_DOWN)) {
- goalState = STOP;
+ goalState = STATE_STOP;
} else {
- goalState = BACK;
+ goalState = STATE_BACK;
}
s_rotate(LARA_TURN_SLOW, 0);
}
- S_HANDLER( SWIM ) {} // TODO
- S_HANDLER( GLIDE ) {} // TODO
+ S_HANDLER( STATE_UW_SWIM )
+ {
+ if (checkDeath(STATE_DEATH_UW))
+ return;
- S_HANDLER( HANG_UP )
+ if (s_checkRoll())
+ return;
+
+ s_turnUW();
+
+ vSpeed = min(vSpeed + LARA_SWIM_ACCEL, LARA_SWIM_SPEED_MAX);
+
+ if (!(input & IN_JUMP)) {
+ goalState = STATE_UW_GLIDE;
+ }
+ }
+
+ S_HANDLER( STATE_UW_GLIDE )
+ {
+ if (checkDeath(STATE_DEATH_UW))
+ return;
+
+ if (s_checkRoll())
+ return;
+
+ s_turnUW();
+
+ if (input & IN_JUMP) {
+ goalState = STATE_UW_SWIM;
+ }
+
+ vSpeed = max(vSpeed - LARA_SWIM_FRICTION, 0);
+
+ if (vSpeed <= LARA_SWIM_SPEED_MIN) {
+ goalState = STATE_UW_TREAD;
+ }
+ }
+
+ S_HANDLER( STATE_HANG_UP )
{
s_ignoreEnemy();
}
- S_HANDLER( FAST_TURN )
+ S_HANDLER( STATE_TURN_FAST )
{
- if (checkDeath() || (input & IN_LOOK))
+ if (checkDeath(STATE_STOP))
+ return;
+
+ if (input & IN_LOOK)
{
- goalState = STOP;
+ goalState = STATE_STOP;
return;
}
if (turnSpeed < 0) {
turnSpeed = -LARA_TURN_FAST;
if (!(input & IN_LEFT)) {
- goalState = STOP;
+ goalState = STATE_STOP;
}
} else {
turnSpeed = LARA_TURN_FAST;
if (!(input & IN_RIGHT)) {
- goalState = STOP;
+ goalState = STATE_STOP;
}
}
}
- S_HANDLER( STEP_RIGHT )
+ S_HANDLER( STATE_STEP_RIGHT )
{
- if (checkDeath() || (input & (IN_WALK | IN_RIGHT)) != (IN_WALK | IN_RIGHT))
+ if (checkDeath(STATE_STOP))
+ return;
+
+ if ((input & (IN_WALK | IN_RIGHT)) != (IN_WALK | IN_RIGHT))
{
- goalState = STOP;
+ goalState = STATE_STOP;
}
}
- S_HANDLER( STEP_LEFT )
+ S_HANDLER( STATE_STEP_LEFT )
{
- if (checkDeath() || (input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT))
+ if (checkDeath(STATE_STOP))
+ return;
+
+ if ((input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT))
{
- goalState = STOP;
+ goalState = STATE_STOP;
}
}
- S_HANDLER( ROLL_END )
+ S_HANDLER( STATE_ROLL_END )
{
// empty
}
- S_HANDLER( SLIDE )
+ S_HANDLER( STATE_SLIDE )
{
camera.targetAngleX = -45 * DEG2SHORT;
if (input & IN_JUMP)
{
- goalState = FORWARD_JUMP;
+ goalState = STATE_JUMP;
}
}
- S_HANDLER( BACK_JUMP )
+ S_HANDLER( STATE_JUMP_BACK )
{
camera.targetAngleY = 135 * DEG2SHORT;
- if (s_checkFall()) return;
+ if (s_checkFall())
+ return;
- if (goalState == RUN) {
- goalState = STOP;
+ if (goalState == STATE_RUN) {
+ goalState = STATE_STOP;
}
}
- S_HANDLER( RIGHT_JUMP )
+ S_HANDLER( STATE_JUMP_RIGHT )
{
s_checkFall();
}
- S_HANDLER( LEFT_JUMP )
+ S_HANDLER( STATE_JUMP_LEFT )
{
s_checkFall();
}
- S_HANDLER( UP_JUMP )
+ S_HANDLER( STATE_JUMP_UP )
{
s_checkFall();
}
- S_HANDLER( FALL_BACK )
+ S_HANDLER( STATE_FALL_BACK )
{
s_checkFall();
if (input & IN_ACTION)
{
- goalState = REACH;
+ goalState = STATE_REACH;
}
}
- S_HANDLER( HANG_LEFT )
+ S_HANDLER( STATE_HANG_LEFT )
{
camera.targetAngleX = -60 * DEG2SHORT;
@@ -775,11 +921,11 @@ struct Lara : Item
if (!(input & IN_LEFT))
{
- goalState = HANG;
+ goalState = STATE_HANG;
}
}
- S_HANDLER( HANG_RIGHT )
+ S_HANDLER( STATE_HANG_RIGHT )
{
camera.targetAngleX = -60 * DEG2SHORT;
@@ -787,39 +933,94 @@ struct Lara : Item
if (!(input & IN_RIGHT))
{
- goalState = HANG;
+ goalState = STATE_HANG;
}
}
- S_HANDLER( SLIDE_BACK )
+ S_HANDLER( STATE_SLIDE_BACK )
{
if (input & IN_JUMP)
{
- goalState = BACK_JUMP;
+ goalState = STATE_JUMP_BACK;
}
}
- S_HANDLER( SURF_TREAD ) {} // TODO
- S_HANDLER( SURF_SWIM ) {} // TODO
- S_HANDLER( DIVE ) {} // TODO
+ S_HANDLER( STATE_SURF_TREAD )
+ {
+ vSpeed = max(vSpeed - LARA_SURF_FRICTION, 0);
- S_HANDLER( PUSH_BLOCK )
+ if (input & IN_LEFT) {
+ angle.y -= LARA_TURN_SLOW;
+ } else if (input & IN_RIGHT) {
+ angle.y += LARA_TURN_SLOW;
+ }
+
+ if (input & IN_UP) {
+ goalState = STATE_SURF_SWIM;
+ } else if (input & IN_DOWN) {
+ goalState = STATE_SURF_BACK;
+ } else if ((input & (IN_WALK | IN_LEFT)) == (IN_WALK | IN_LEFT)) {
+ goalState = STATE_SURF_LEFT;
+ } else if ((input & (IN_WALK | IN_RIGHT)) == (IN_WALK | IN_RIGHT)) {
+ goalState = STATE_SURF_RIGHT;
+ }
+
+ if (input & IN_JUMP) {
+ swimTimer++;
+ if (swimTimer == LARA_SWIM_TIMER) {
+ s_dive();
+ }
+ } else {
+ swimTimer = 0;
+ }
+ }
+
+ S_HANDLER( STATE_SURF_SWIM )
+ {
+ if (checkDeath(STATE_DEATH_UW))
+ return;
+
+ swimTimer = 0;
+
+ if (input & IN_LEFT) {
+ angle.y -= LARA_TURN_SLOW;
+ } else if (input & IN_RIGHT) {
+ angle.y += LARA_TURN_SLOW;
+ }
+
+ if (!(input & IN_UP) || (input & IN_JUMP)) {
+ goalState = STATE_SURF_TREAD;
+ }
+
+ vSpeed = min(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX);
+ }
+
+ S_HANDLER( STATE_UW_DIVE )
+ {
+ if (input & IN_UP) {
+ angle.x -= ANGLE_1;
+ }
+ }
+
+ S_HANDLER( STATE_BLOCK_PUSH )
{
camera.targetAngleX = -25 * DEG2SHORT;
camera.targetAngleY = 35 * DEG2SHORT;
+ camera.center = true;
s_ignoreEnemy();
}
- S_HANDLER( PULL_BLOCK )
+ S_HANDLER( STATE_BLOCK_PULL )
{
camera.targetAngleX = -25 * DEG2SHORT;
camera.targetAngleY = 35 * DEG2SHORT;
+ camera.center = true;
s_ignoreEnemy();
}
- S_HANDLER( PUSH_PULL_READY )
+ S_HANDLER( STATE_BLOCK_READY )
{
camera.targetAngleY = 75 * DEG2SHORT;
@@ -827,11 +1028,11 @@ struct Lara : Item
if (!(input & IN_ACTION))
{
- goalState = STOP;
+ goalState = STATE_STOP;
}
}
- S_HANDLER( PICK_UP )
+ S_HANDLER( STATE_PICKUP )
{
camera.targetAngleX = -15 * DEG2SHORT;
camera.targetAngleY = -130 * DEG2SHORT;
@@ -840,7 +1041,7 @@ struct Lara : Item
s_ignoreEnemy();
}
- S_HANDLER( SWITCH_DOWN )
+ S_HANDLER( STATE_SWITCH_DOWN )
{
camera.targetAngleX = -25 * DEG2SHORT;
camera.targetAngleY = 80 * DEG2SHORT;
@@ -849,7 +1050,7 @@ struct Lara : Item
s_ignoreEnemy();
}
- S_HANDLER( SWITCH_UP )
+ S_HANDLER( STATE_SWITCH_UP )
{
camera.targetAngleX = -25 * DEG2SHORT;
camera.targetAngleY = 80 * DEG2SHORT;
@@ -858,7 +1059,7 @@ struct Lara : Item
s_ignoreEnemy();
}
- S_HANDLER( USE_KEY )
+ S_HANDLER( STATE_USE_KEY )
{
camera.targetAngleX = -25 * DEG2SHORT;
camera.targetAngleY = -80 * DEG2SHORT;
@@ -867,7 +1068,7 @@ struct Lara : Item
s_ignoreEnemy();
}
- S_HANDLER( USE_PUZZLE )
+ S_HANDLER( STATE_USE_PUZZLE )
{
camera.targetAngleX = -25 * DEG2SHORT;
camera.targetAngleY = -80 * DEG2SHORT;
@@ -876,65 +1077,145 @@ struct Lara : Item
s_ignoreEnemy();
}
- S_HANDLER( UW_DEATH )
+ S_HANDLER( STATE_DEATH_UW )
{
vSpeed = X_MAX(vSpeed - LARA_SWIM_ACCEL, 0);
angle.x = angleDec(angle.x, 2 * DEG2SHORT);
}
- S_HANDLER( ROLL_START )
+ S_HANDLER( STATE_ROLL_START )
{
// empty
}
- S_HANDLER( SPECIAL )
+ S_HANDLER( STATE_SPECIAL )
{
camera.targetAngleX = -25 * DEG2SHORT;
camera.targetAngleY = 170 * DEG2SHORT;
+ camera.center = true;
}
- S_HANDLER( SURF_BACK ) {} // TODO
- S_HANDLER( SURF_LEFT ) {} // TODO
- S_HANDLER( SURF_RIGHT ) {} // TODO
+ S_HANDLER( STATE_SURF_BACK )
+ {
+ if (checkDeath(STATE_DEATH_UW))
+ return;
- S_HANDLER( MIDAS_USE )
+ swimTimer = 0;
+
+ if (input & IN_LEFT) {
+ angle.y -= LARA_TURN_VERY_SLOW;
+ } else if (input & IN_RIGHT) {
+ angle.y += LARA_TURN_VERY_SLOW;
+ }
+
+ if (!(input & IN_DOWN)) {
+ goalState = STATE_SURF_TREAD;
+ }
+
+ vSpeed = min(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX);
+ }
+
+ S_HANDLER( STATE_SURF_LEFT )
+ {
+ if (checkDeath(STATE_DEATH_UW))
+ return;
+
+ swimTimer = 0;
+
+ if ((input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT)) {
+ goalState = STATE_SURF_TREAD;
+ }
+
+ vSpeed = min(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX);
+ }
+
+ S_HANDLER( STATE_SURF_RIGHT )
+ {
+ if (checkDeath(STATE_DEATH_UW))
+ return;
+
+ swimTimer = 0;
+
+ if ((input & (IN_WALK | IN_RIGHT)) != (IN_WALK | IN_RIGHT)) {
+ goalState = STATE_SURF_TREAD;
+ }
+
+ vSpeed = min(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX);
+ }
+
+ S_HANDLER( STATE_USE_MIDAS )
{
s_ignoreEnemy();
}
- S_HANDLER( MIDAS_DEATH )
+ S_HANDLER( STATE_DEATH_MIDAS )
{
s_ignoreEnemy();
flags.gravity = false;
}
- S_HANDLER( SWAN_DIVE ) {} // TODO
- S_HANDLER( FAST_DIVE ) {} // TODO
+ S_HANDLER( STATE_SWAN_DIVE )
+ {
+ cinfo.enemyPush = true;
+ cinfo.enemyHit = false;
- S_HANDLER( HANDSTAND )
+ s_checkFall();
+ }
+
+ S_HANDLER( STATE_FAST_DIVE )
+ {
+ cinfo.enemyPush = true;
+ cinfo.enemyHit = false;
+ hSpeed = (hSpeed * 95) / 100;
+
+ s_checkRoll();
+ }
+
+ S_HANDLER( STATE_HANDSTAND )
{
s_ignoreEnemy();
}
- S_HANDLER( WATER_OUT )
+ S_HANDLER( STATE_WATER_OUT )
{
s_ignoreEnemy();
+ camera.center = true;
}
+ S_HANDLER( STATE_CLIMB_START ) {}
+ S_HANDLER( STATE_CLIMB_UP ) {}
+ S_HANDLER( STATE_CLIMB_LEFT ) {}
+ S_HANDLER( STATE_CLIMB_END ) {}
+ S_HANDLER( STATE_CLIMB_RIGHT ) {}
+ S_HANDLER( STATE_CLIMB_DOWN ) {}
+ S_HANDLER( STATE_UNUSED_1 ) {}
+ S_HANDLER( STATE_UNUSED_2 ) {}
+ S_HANDLER( STATE_UNUSED_3 ) {}
+ S_HANDLER( STATE_WADE ) {}
+ S_HANDLER( STATE_ROLL_UW ) {}
+ S_HANDLER( STATE_PICKUP_FLARE ) {}
+ S_HANDLER( STATE_ROLL_AIR ) {}
+ S_HANDLER( STATE_UNUSED_4 ) {}
+ S_HANDLER( STATE_ZIPLINE ) {}
+
+
// collision control
- void c_applyOffset() {
+ void c_applyOffset()
+ {
pos += cinfo.offset;
cinfo.offset = vec3i(0, 0, 0);
}
- void c_angle(int32 angleDelta) {
+ void c_angle(int32 angleDelta)
+ {
moveAngle = angle.y + angleDelta;
cinfo.angle = moveAngle;
cinfo.quadrant = uint16(cinfo.angle + ANGLE_45) / ANGLE_90;
}
- bool c_checkCeiling() {
+ bool c_checkCeiling()
+ {
if (cinfo.type != CT_CEILING && cinfo.type != CT_FLOOR_CEILING) {
return false;
}
@@ -949,10 +1230,12 @@ struct Lara : Item
return true;
}
- bool c_checkWall() {
- if (cinfo.type == CT_FRONT || cinfo.type == CT_FRONT_CEILING) {
+ bool c_checkWall()
+ {
+ if (cinfo.type == CT_FRONT || cinfo.type == CT_FRONT_CEILING)
+ {
c_applyOffset();
- goalState = STOP;
+ goalState = STATE_STOP;
hSpeed = 0;
flags.gravity = false;
return true;
@@ -971,7 +1254,8 @@ struct Lara : Item
return false;
}
- bool c_checkWallUW() {
+ bool c_checkWallUW()
+ {
if (cinfo.type == CT_FRONT) {
if (angle.x > 35 * DEG2SHORT) {
angle.x += 2 * DEG2SHORT;
@@ -1001,7 +1285,7 @@ struct Lara : Item
angle.x += 2 * DEG2SHORT;
}
- int32 waterDepth = room->getWaterDepth();
+ int32 waterDepth = getWaterDepth();
if (waterDepth == WALL) {
vSpeed = 0;
@@ -1010,7 +1294,7 @@ struct Lara : Item
waterState = WATER_STATE_WADE;
animSet(ANIM_SWIM_STAND, true);
- goalState = STOP;
+ goalState = STATE_STOP;
angle.x = 0;
angle.z = 0;
@@ -1022,7 +1306,8 @@ struct Lara : Item
return false;
}
- bool c_checkWallSurf() {
+ 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;
@@ -1035,7 +1320,8 @@ struct Lara : Item
return true;
}
- bool c_checkSlide() {
+ bool c_checkSlide()
+ {
if (waterState == WATER_STATE_WADE) {
return false;
}
@@ -1059,13 +1345,13 @@ struct Lara : Item
}
if (abs(realAngle - angle.y) <= ANGLE_90) {
- if (state != SLIDE) {
+ if (state != STATE_SLIDE) {
animSet(ANIM_SLIDE_FORTH, true);
}
moveAngle = realAngle;
angle.y = realAngle;
} else {
- if (state != SLIDE_BACK) {
+ if (state != STATE_SLIDE_BACK) {
animSet(ANIM_SLIDE_BACK, true);
}
moveAngle = realAngle;
@@ -1075,7 +1361,8 @@ struct Lara : Item
return true;
}
- bool c_checkFall(int32 height, int32 fallAnimIndex = ANIM_FALL_FORTH) {
+ bool c_checkFall(int32 height, int32 fallAnimIndex = ANIM_FALL_FORTH)
+ {
if (waterState == WATER_STATE_WADE) {
return false;
}
@@ -1092,24 +1379,24 @@ struct Lara : Item
return true;
}
- bool c_checkLanding() {
- if ((state == FAST_DIVE /*|| state == AIR_ROLL*/) && vSpeed > 133) {
+ bool c_checkLanding()
+ {
+ if ((state == STATE_FAST_DIVE || state == STATE_ROLL_AIR) && vSpeed > 133)
+ {
health = 0;
return true;
}
int32 y = pos.y;
pos.y += cinfo.m.floor;
- /* TODO
- v2floor = pos.y;
+ roomFloor = pos.y;
checkTrigger(cinfo.trigger, this);
- */
+
pos.y = y;
- if (vSpeed <= 140) {
+ if (vSpeed <= 140)
return false;
- }
if (vSpeed > 154) {
health = 0;
@@ -1117,10 +1404,11 @@ struct Lara : Item
health -= (X_SQR(vSpeed - 140) * LARA_MAX_HEALTH) / 196;
}
- return checkDeath();
+ return checkDeath((State)state);
}
- bool c_checkSwing() {
+ bool c_checkSwing()
+ {
int32 x = pos.x;
int32 y = pos.y;
int32 z = pos.z;
@@ -1150,21 +1438,28 @@ struct Lara : Item
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_checkGrab()
+ {
+ return !(input & IN_ACTION) || (cinfo.type != CT_FRONT) || (abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT);
}
- bool c_checkSpace() {
+ 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() {
+ bool c_checkClimbStart()
+ {
return false;
}
- bool c_checkClimbUp() {
+ bool c_checkClimbUp()
+ {
+ if (cinfo.f.floor == WALL)
+ return false;
+
if (c_checkGrab()) {
return false;
}
@@ -1178,34 +1473,32 @@ struct Lara : Item
if (c_checkSpace()) return false;
animSet(ANIM_CLIMB_2, true);
- state = HANG_UP;
+ state = 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(ANIM_CLIMB_3, true);
- state = HANG_UP;
+ state = STATE_HANG_UP;
pos.y += 768 + cinfo.f.floor;
} else if (cinfo.f.floor >= -1920 && cinfo.f.floor <= -896) {
animSet(ANIM_STAND, true);
- goalState = UP_JUMP;
+ goalState = STATE_JUMP_UP;
vSpeedHack = -int32(phd_sqrt(-2 * GRAVITY * (cinfo.f.floor + 800)) + 3);
-
- updateAnim();
- } /* TODO for main branch
+ animProcess();
+ /*} TODO climb
else if ((waterState != WATER_STATE_WADE) && (cinfo.f.floor <= -1920) && (cinfo.l.floor <= -1920) && (cinfo.r.floor <= -1920) && (cinfo.m.ceiling <= -1158)) {
animSet(ANIM_STAND, true);
- goalState = UP_JUMP;
+ goalState = STATE_JUMP_UP;
vSpeedHack = -116;
- animUpdate(this);
- }
- else if (((cinfo.f.floor < -1024) && (cinfo.f.ceiling >= 506)) || (cinfo.m.ceiling <= -518) && c_checkClimbStart()) {
+ animProcess();
+ } else if (((cinfo.f.floor < -1024) && (cinfo.f.ceiling >= 506)) || ((cinfo.m.ceiling <= -518) && c_checkClimbStart())) {
animSet(ANIM_STAND, true);
- goalState = CLIMB_START;
- processAnimation();
- }*/ else {
+ goalState = STATE_CLIMB_START;
+ animProcess();*/
+ } else {
return false;
}
@@ -1215,12 +1508,16 @@ struct Lara : Item
return true;
}
- bool c_checkHang() {
+ 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)) {
+ if ((cinfo.f.ceiling > 0) ||
+ (cinfo.m.ceiling > -LARA_STEP_HEIGHT) ||
+ (cinfo.m.floor < 200 && state == STATE_REACH))
+ {
return false;
}
@@ -1235,7 +1532,7 @@ struct Lara : Item
return false;
}
- if (state == REACH)
+ if (state == STATE_REACH)
{
if (c_checkSwing()) {
animSet(ANIM_HANG_SWING, true);
@@ -1257,10 +1554,12 @@ struct Lara : Item
return true;
}
- bool c_checkDrop() {
+ bool c_checkDrop()
+ {
// TODO getTrigger here
- if ((health > 0) && (input & IN_ACTION)) {
+ if ((health > 0) && (input & IN_ACTION))
+ {
flags.gravity = false;
vSpeed = 0;
return false;
@@ -1275,7 +1574,8 @@ struct Lara : Item
return true;
}
- bool c_checkWaterOut() {
+ bool c_checkWaterOut()
+ {
if (!(input & IN_ACTION) ||
(cinfo.type != CT_FRONT) ||
(cinfo.f.ceiling > 0) ||
@@ -1295,27 +1595,27 @@ struct Lara : Item
return false;
}
- angle.y += h - 5;
+ pos.y += h - 5;
updateRoom(-LARA_HEIGHT / 2);
- alignWall();
+ alignWall(-LARA_RADIUS);
- /* TODO for main branch
- if ((h < -128) || (level->version & TR::VER_TR1)) {
- setAnimV2(ANIM_WATER_OUT, true);
- specular = LARA_WET_SPECULAR;
+ if ((h < -128)) { // TODO || (level->version & TR::VER_TR1)) {
+ animSet(ANIM_WATER_OUT, true);
+ // specular = LARA_WET_SPECULAR;
} else if (h < 128) {
- setAnimV2(ANIM_SURF_OUT, true);
+ animSet(ANIM_SURF_OUT, true);
} else {
- setAnimV2(ANIM_SURF_STAND, true);
+ animSet(ANIM_SURF_STAND, true);
}
- game->waterDrop(pos, 128.0f, 0.2f);
- */
+
+ //game->waterDrop(pos, 128.0f, 0.2f);
+
animSet(ANIM_WATER_OUT, true);
waterState = WATER_STATE_ABOVE;
- goalState = STOP;
+ goalState = STATE_STOP;
angle.x = 0;
angle.z = 0;
hSpeed = 0;
@@ -1325,7 +1625,8 @@ struct Lara : Item
return true;
}
- void c_default() {
+ void c_default()
+ {
cinfo.gapPos = LARA_STEP_HEIGHT;
cinfo.gapNeg = -LARA_STEP_HEIGHT;
cinfo.gapCeiling = 0;
@@ -1334,7 +1635,8 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
}
- void c_step() {
+ void c_step()
+ {
cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : 128;
cinfo.gapNeg = -128;
cinfo.gapCeiling = 0;
@@ -1342,29 +1644,32 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
if (c_checkWall()) {
animSet(ANIM_STAND, true);
}
- if (c_checkSlide()) return;
+ if (c_checkSlide())
+ return;
pos.y += cinfo.m.floor;
}
- void c_fall() {
+ 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) {
+ goalState = STATE_DEATH;
+ } else if (state == STATE_JUMP && (input & IN_UP) && !(input & IN_WALK)) {
+ goalState = STATE_RUN;
+ } else if (state == STATE_FALL) {
animSet(ANIM_LANDING, true);
} else {
- goalState = STOP;
+ goalState = STATE_STOP;
}
stopScreaming();
@@ -1373,27 +1678,29 @@ struct Lara : Item
vSpeed = 0;
flags.gravity = false;
- if (state == FORWARD_JUMP) {
- updateAnim();
+ if (state == STATE_JUMP) {
+ animProcess();
}
}
- void c_jump() {
+ void c_jump()
+ {
cinfo.gapPos = -WALL;
- cinfo.gapNeg = (state == REACH) ? 0 : -LARA_STEP_HEIGHT;
+ cinfo.gapNeg = (state == STATE_REACH) ? 0 : -LARA_STEP_HEIGHT;
cinfo.gapCeiling = 192;
- collideRoom(this, state == UP_JUMP ? LARA_HEIGHT_JUMP : LARA_HEIGHT, 0);
+ collideRoom(this, state == STATE_JUMP_UP ? LARA_HEIGHT_JUMP : LARA_HEIGHT, 0);
- if ((state == REACH || state == UP_JUMP) && c_checkHang()) {
+ if ((state == STATE_REACH || state == STATE_JUMP_UP) && c_checkHang())
return;
- }
c_applyOffset();
- bool slide = (state == FALL) || (state == REACH) || (state == UP_JUMP);
+ // TODO long up jump
+ // TODO can't side jump near walls
+ bool slide = (state == STATE_FALL) || (state == STATE_REACH) || (state == STATE_JUMP_UP);
- if ((cinfo.type == CT_CEILING) || ((cinfo.type == CT_FRONT_CEILING) && slide)) {
+ if ((cinfo.type == CT_CEILING) || (slide && (cinfo.type == CT_FRONT_CEILING))) {
if (vSpeed <= 0) {
vSpeed = 1;
}
@@ -1405,8 +1712,8 @@ struct Lara : Item
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;
+ pos.x -= (phd_sin(cinfo.angle) * LARA_RADIUS) >> FIXED_SHIFT;
+ pos.z -= (phd_cos(cinfo.angle) * LARA_RADIUS) >> FIXED_SHIFT;
cinfo.m.floor = 0;
hSpeed = 0;
if (vSpeed <= 0) {
@@ -1421,7 +1728,8 @@ struct Lara : Item
c_fall();
}
- void c_slide() {
+ void c_slide()
+ {
cinfo.gapPos = -WALL;
cinfo.gapNeg = -512;
cinfo.gapCeiling = 0;
@@ -1429,22 +1737,25 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
c_checkWall();
- if (c_checkFall(200, state == SLIDE ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) return;
+ if (c_checkFall(200, state == STATE_SLIDE ? ANIM_FALL_FORTH : ANIM_FALL_BACK))
+ return;
c_checkSlide();
pos.y += cinfo.m.floor;
if (cinfo.m.slantType != SLANT_HIGH) {
- goalState = STOP;
+ goalState = STATE_STOP;
}
}
- void c_roll() {
+ void c_roll()
+ {
vSpeed = 0;
flags.gravity = false;
@@ -1455,18 +1766,22 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
- if (c_checkSlide()) return;
+ if (c_checkSlide())
+ return;
- if (c_checkFall(200, state == ROLL_START ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) return;
+ if (c_checkFall(200, state == STATE_ROLL_START ? ANIM_FALL_FORTH : ANIM_FALL_BACK))
+ return;
c_applyOffset();
pos.y += cinfo.m.floor;
}
- void c_hang(int32 angleDelta) {
+ void c_hang(int32 angleDelta)
+ {
c_angle(angleDelta);
cinfo.gapPos = -WALL;
cinfo.gapNeg = WALL;
@@ -1480,7 +1795,8 @@ struct Lara : Item
cinfo.gapNeg = -LARA_STEP_HEIGHT;
cinfo.gapCeiling = 0;
- switch (cinfo.quadrant) {
+ switch (cinfo.quadrant)
+ {
case 0 : pos.z += 2; break;
case 1 : pos.x += 2; break;
case 2 : pos.z -= 2; break;
@@ -1491,7 +1807,8 @@ struct Lara : Item
moveAngle = angle.y + angleDelta;
- if (health <= 0 || !(input & IN_ACTION)) {
+ if (health <= 0 || !(input & IN_ACTION))
+ {
animSet(ANIM_FALL_HANG, true, 9);
cinfo.offset.y = cinfo.f.floor - getBounds().minY + 2;
@@ -1508,7 +1825,7 @@ struct Lara : Item
if (noFloor || (cinfo.type != CT_FRONT) || (cinfo.m.ceiling >= 0) || abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT)
{
- if (state != HANG) {
+ if (state != STATE_HANG) {
animSet(ANIM_HANG, true, 21);
}
pos = cinfo.pos;
@@ -1527,7 +1844,158 @@ struct Lara : Item
}
}
- C_HANDLER( WALK )
+ enum ClimbState {
+ CLIMB_HANG,
+ CLIMB_COLLIDE,
+ CLIMB_OK,
+ };
+
+ ClimbState c_climbCollide(int32 width)
+ {
+ /* TODO
+ int32 dx = 0, dz = 0;
+
+ int32 x = pos.x;
+ int32 y = pos.y - 512;
+ int32 z = pos.z;
+
+ switch (cinfo.quadrant) {
+ case 0 :
+ x += width;
+ z += cinfo.radius;
+ dz = 4;
+ break;
+ case 1 :
+ x += cinfo.radius;
+ z -= width;
+ dx = 4;
+ break;
+ case 2 :
+ x -= width;
+ z -= cinfo.radius;
+ dz = -4;
+ break;
+ case 3 :
+ x -= cinfo.radius;
+ z += width;
+ dx = -4;
+ break;
+ }
+
+ // TODO
+ cinfo.offset.y = 0;
+ */
+ return CLIMB_OK;
+ }
+
+ void c_climbSide(int32 width)
+ {
+ if (c_checkDrop())
+ return;
+
+ switch (c_climbCollide(width))
+ {
+ case CLIMB_HANG:
+ {
+ pos.x = cinfo.pos.x;
+ pos.z = cinfo.pos.z;
+ goalState = STATE_HANG;
+ break;
+ }
+
+ case CLIMB_COLLIDE:
+ {
+ pos.x = cinfo.pos.x;
+ pos.z = cinfo.pos.z;
+ animSet(ANIM_CLIMB_START, true);
+ break;
+ }
+
+ case CLIMB_OK:
+ {
+ if (input & IN_LEFT) {
+ goalState = STATE_CLIMB_LEFT;
+ } else if (input & IN_RIGHT) {
+ goalState = STATE_CLIMB_RIGHT;
+ } else {
+ goalState = STATE_CLIMB_START;
+ }
+ pos.y += cinfo.offset.y;
+ break;
+ }
+ }
+ }
+
+ void c_swim() {
+ c_angle(ANGLE_0);
+
+ collideRoom(this, LARA_HEIGHT_UW, LARA_HEIGHT_UW / 2);
+
+ c_applyOffset();
+
+ if (c_checkWallUW())
+ return;
+ }
+
+ void c_surf()
+ {
+ collideRoom(this, LARA_HEIGHT_SURF + 100, LARA_HEIGHT_SURF);
+
+ c_applyOffset();
+
+ c_checkWallSurf();
+
+ if (state == STATE_SURF_TREAD) {
+ if (frameIndex == 0) {
+ //game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.03f);
+ }
+ } else {
+ if (frameIndex % 4 == 0) {
+ //game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f);
+ }
+ }
+
+ int32 waterLevel = getWaterLevel();
+ if (waterLevel - pos.y <= -100) {
+ s_dive();
+ return;
+ }
+
+ /* TODO
+ if (level->version & TR::VER_TR1) {
+ return;
+ }
+
+ if ((cinfo.type == CT_FRONT) || (cinfo.m.slantType == TR::SLANT_HIGH) || (cinfo.m.floor >= 0)) {
+ return;
+ }
+
+ if (cinfo.m.floor >= -128) {
+ if (targetState == STATE_SURF_LEFT) {
+ targetState = STATE_STEP_LEFT;
+ } else if (targetState == STATE_SURF_RIGHT) {
+ targetState = STATE_STEP_RIGHT;
+ } else {
+ setAnimV2(ANIM_WADE, true);
+ }
+ } else {
+ setAnimV2(ANIM_WADE_ASCEND, true);
+ targetState = STATE_STOP;
+ }
+
+ v2pos.y += cinfo.f.floor + LARA_HEIGHT - 5;
+ updateRoomV2(-LARA_HEIGHT / 2);
+
+ gravity = false;
+ v2rot.x = 0;
+ v2rot.z = 0;
+ hSpeed = 0;
+ vSpeed = 0;
+ waterState = WATER_STATE_WADE;
+ */
+ }
+
+ C_HANDLER( STATE_WALK )
{
vSpeed = 0;
flags.gravity = false;
@@ -1536,11 +2004,14 @@ struct Lara : Item
c_angle(ANGLE_0);
c_default();
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
- if (c_checkClimbUp()) return;
+ if (c_checkClimbUp())
+ return;
- if (c_checkWall()) {
+ if (c_checkWall())
+ {
if (frameIndex >= 29 && frameIndex <= 47) {
animSet(ANIM_STAND_RIGHT, false);
} else if ((frameIndex >= 22 && frameIndex <= 28) || (frameIndex >= 48 && frameIndex <= 57)) {
@@ -1550,10 +2021,12 @@ struct Lara : Item
}
}
- if (c_checkFall(LARA_STEP_HEIGHT)) return;
+ if (c_checkFall(LARA_STEP_HEIGHT))
+ return;
// descend
- if (cinfo.m.floor > 128) {
+ if (cinfo.m.floor > 128)
+ {
if (frameIndex >= 28 && frameIndex <= 45) {
animSet(ANIM_WALK_DESCEND_RIGHT, false);
} else {
@@ -1562,7 +2035,8 @@ struct Lara : Item
}
// ascend
- if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) {
+ if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128)
+ {
if (frameIndex >= 27 && frameIndex <= 44) {
animSet(ANIM_WALK_ASCEND_RIGHT, false);
} else {
@@ -1570,12 +2044,13 @@ struct Lara : Item
}
}
- if (c_checkSlide()) return;
+ if (c_checkSlide())
+ return;
pos.y += cinfo.m.floor;
}
- C_HANDLER( RUN )
+ C_HANDLER( STATE_RUN )
{
c_angle(ANGLE_0);
@@ -1586,26 +2061,31 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
- if (c_checkClimbUp()) return;
+ if (c_checkClimbUp())
+ return;
if (c_checkWall()) {
angle.z = 0;
- if (cinfo.f.slantType == SLANT_NONE && cinfo.f.floor < -LARA_SMASH_HEIGHT && frameIndex < 22) {
+ if (cinfo.f.slantType == SLANT_NONE && cinfo.f.floor < -LARA_SMASH_HEIGHT && frameIndex < 22)
+ {
animSet(frameIndex < 10 ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT, false);
- state = SPLAT;
+ state = STATE_SPLAT;
return;
}
animSet(ANIM_STAND, true);
}
- if (c_checkFall(LARA_STEP_HEIGHT)) return;
+ if (c_checkFall(LARA_STEP_HEIGHT))
+ return;
// ascend
- if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) {
+ if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128)
+ {
if (frameIndex >= 3 && frameIndex <= 14) {
animSet(ANIM_RUN_ASCEND_RIGHT, false);
} else {
@@ -1613,9 +2093,11 @@ struct Lara : Item
}
}
- if (c_checkSlide()) return;
+ if (c_checkSlide())
+ return;
- if (cinfo.m.floor >= 50) {
+ if (cinfo.m.floor >= 50)
+ {
pos.y += 50;
return;
}
@@ -1623,7 +2105,7 @@ struct Lara : Item
pos.y += cinfo.m.floor;
}
- C_HANDLER( STOP )
+ C_HANDLER( STATE_STOP )
{
vSpeed = 0;
flags.gravity = false;
@@ -1631,29 +2113,32 @@ struct Lara : Item
c_angle(ANGLE_0);
c_default();
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
- if (c_checkFall(100)) return;
+ if (c_checkFall(100))
+ return;
- if (c_checkSlide()) return;
+ if (c_checkSlide())
+ return;
c_applyOffset();
pos.y += cinfo.m.floor;
}
- C_HANDLER( FORWARD_JUMP )
+ C_HANDLER( STATE_JUMP )
{
c_angle(ANGLE_0);
c_jump();
}
- C_HANDLER( POSE )
+ C_HANDLER( STATE_POSE )
{
- c_STOP();
+ c_STATE_STOP();
}
- C_HANDLER( FAST_BACK )
+ C_HANDLER( STATE_BACK_FAST )
{
vSpeed = 0;
flags.gravity = false;
@@ -1667,9 +2152,11 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
- if (c_checkFall(200, ANIM_FALL_BACK)) return;
+ if (c_checkFall(200, ANIM_FALL_BACK))
+ return;
if (c_checkWall()) {
animSet(ANIM_STAND, false);
@@ -1678,24 +2165,26 @@ struct Lara : Item
pos.y += cinfo.m.floor;
}
- C_HANDLER( TURN_RIGHT )
+ C_HANDLER( STATE_TURN_RIGHT )
{
c_angle(ANGLE_0);
c_default();
- if (c_checkFall(100)) return;
+ if (c_checkFall(100))
+ return;
- if (c_checkSlide()) return;
+ if (c_checkSlide())
+ return;
pos.y += cinfo.m.floor;
}
- C_HANDLER( TURN_LEFT )
+ C_HANDLER( STATE_TURN_LEFT )
{
- c_TURN_RIGHT();
+ c_STATE_TURN_RIGHT();
}
- C_HANDLER( DEATH )
+ C_HANDLER( STATE_DEATH )
{
cinfo.radius = LARA_RADIUS * 4;
@@ -1706,52 +2195,57 @@ struct Lara : Item
pos.y += cinfo.m.floor;
}
- C_HANDLER( FALL )
+ C_HANDLER( STATE_FALL )
{
flags.gravity = true;
c_jump();
}
- C_HANDLER( HANG )
+ C_HANDLER( STATE_HANG )
{
c_hang(0);
- if ((input & IN_UP) && goalState == HANG)
+ if ((input & IN_UP) && goalState == STATE_HANG)
{
- if (cinfo.f.floor <= -850 ||
- cinfo.f.floor >= -650 ||
- c_checkSpace() || cinfo.staticHit) return;
+ if (abs(cinfo.f.floor) >= 850)
+ return;
+
+ if (c_checkSpace() || cinfo.staticHit)
+ return;
if (input & IN_WALK) {
- goalState = HANDSTAND;
+ goalState = STATE_HANDSTAND;
} else {
- goalState = HANG_UP;
+ goalState = STATE_HANG_UP;
}
}
}
- C_HANDLER( REACH )
+ C_HANDLER( STATE_REACH )
{
flags.gravity = true;
c_angle(ANGLE_0);
c_jump();
}
- C_HANDLER( SPLAT )
+ C_HANDLER( STATE_SPLAT )
{
c_angle(ANGLE_0);
c_default();
c_applyOffset();
}
- C_HANDLER( TREAD ) {} // TODO
-
- C_HANDLER( LAND )
+ C_HANDLER( STATE_UW_TREAD )
{
- c_STOP();
+ c_swim();
}
- C_HANDLER( COMPRESS )
+ C_HANDLER( STATE_LAND )
+ {
+ c_STATE_STOP();
+ }
+
+ C_HANDLER( STATE_COMPRESS )
{
vSpeed = 0;
flags.gravity = false;
@@ -1762,7 +2256,8 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
- if (cinfo.m.ceiling > -100) {
+ if (cinfo.m.ceiling > -100)
+ {
animSet(ANIM_STAND, true);
pos = cinfo.pos;
hSpeed = 0;
@@ -1771,7 +2266,7 @@ struct Lara : Item
}
}
- C_HANDLER( BACK )
+ C_HANDLER( STATE_BACK )
{
vSpeed = 0;
flags.gravity = false;
@@ -1785,7 +2280,8 @@ struct Lara : Item
collideRoom(this, LARA_HEIGHT, 0);
- if (c_checkCeiling()) return;
+ if (c_checkCeiling())
+ return;
if (c_checkWall())
{
@@ -1801,200 +2297,265 @@ struct Lara : Item
}
}
- if (c_checkSlide()) return;
+ if (c_checkSlide())
+ return;
pos.y += cinfo.m.floor;
}
- C_HANDLER( SWIM ) {} // TODO
- C_HANDLER( GLIDE ) {} // TODO
+ C_HANDLER( STATE_UW_SWIM )
+ {
+ c_swim();
+ }
- C_HANDLER( HANG_UP )
+ C_HANDLER( STATE_UW_GLIDE )
+ {
+ c_swim();
+ }
+
+ C_HANDLER( STATE_HANG_UP )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( FAST_TURN )
+ C_HANDLER( STATE_TURN_FAST )
{
- c_STOP();
+ c_STATE_STOP();
}
- C_HANDLER( STEP_RIGHT )
+ C_HANDLER( STATE_STEP_RIGHT )
{
c_angle(+ANGLE_90);
c_step();
}
- C_HANDLER( STEP_LEFT )
+ C_HANDLER( STATE_STEP_LEFT )
{
c_angle(-ANGLE_90);
c_step();
}
- C_HANDLER( ROLL_END )
+ C_HANDLER( STATE_ROLL_END )
{
c_angle(ANGLE_180);
c_roll();
}
- C_HANDLER( SLIDE )
+ C_HANDLER( STATE_SLIDE )
{
c_angle(ANGLE_0);
c_slide();
}
- C_HANDLER( BACK_JUMP )
+ C_HANDLER( STATE_JUMP_BACK )
{
c_angle(ANGLE_180);
c_jump();
}
- C_HANDLER( RIGHT_JUMP )
+ C_HANDLER( STATE_JUMP_RIGHT )
{
c_angle(ANGLE_90);
c_jump();
}
- C_HANDLER( LEFT_JUMP )
+ C_HANDLER( STATE_JUMP_LEFT )
{
c_angle(-ANGLE_90);
c_jump();
}
- C_HANDLER( UP_JUMP )
+ C_HANDLER( STATE_JUMP_UP )
{
c_angle(ANGLE_0);
c_jump();
}
- C_HANDLER( FALL_BACK )
+ C_HANDLER( STATE_FALL_BACK )
{
c_angle(ANGLE_180);
c_jump();
}
- C_HANDLER( HANG_LEFT )
+ C_HANDLER( STATE_HANG_LEFT )
{
c_hang(-ANGLE_90);
}
- C_HANDLER( HANG_RIGHT )
+ C_HANDLER( STATE_HANG_RIGHT )
{
c_hang(ANGLE_90);
}
- C_HANDLER( SLIDE_BACK )
+ C_HANDLER( STATE_SLIDE_BACK )
{
c_angle(ANGLE_180);
c_slide();
}
- C_HANDLER( SURF_TREAD ) {} // TODO
- C_HANDLER( SURF_SWIM ) {} // TODO
- C_HANDLER( DIVE ) {} // TODO
+ C_HANDLER( STATE_SURF_TREAD )
+ {
+ c_angle(ANGLE_0);
+ c_surf();
+ }
- C_HANDLER( PUSH_BLOCK )
+ C_HANDLER( STATE_SURF_SWIM )
+ {
+ cinfo.gapNeg = -LARA_STEP_HEIGHT;
+
+ c_angle(ANGLE_0);
+ c_surf();
+ c_checkWaterOut();
+ }
+
+ C_HANDLER( STATE_UW_DIVE )
+ {
+ c_swim();
+ }
+
+ C_HANDLER( STATE_BLOCK_PUSH )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( PULL_BLOCK )
+ C_HANDLER( STATE_BLOCK_PULL )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( PUSH_PULL_READY )
+ C_HANDLER( STATE_BLOCK_READY )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( PICK_UP )
+ C_HANDLER( STATE_PICKUP )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( SWITCH_DOWN )
+ C_HANDLER( STATE_SWITCH_DOWN )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( SWITCH_UP )
+ C_HANDLER( STATE_SWITCH_UP )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( USE_KEY )
+ C_HANDLER( STATE_USE_KEY )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( USE_PUZZLE )
+ C_HANDLER( STATE_USE_PUZZLE )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( UW_DEATH ) {} // TODO
+ C_HANDLER( STATE_DEATH_UW )
+ {
+ health = 0;
+ oxygen = 0;
- C_HANDLER( ROLL_START )
+ int16 waterLevel = getWaterLevel();
+ if (waterLevel != WALL && waterLevel < pos.y - LARA_RADIUS) {
+ pos.y -= LARA_FLOAT_UP_SPEED;
+ }
+
+ c_swim();
+ }
+
+ C_HANDLER( STATE_ROLL_START )
{
c_angle(ANGLE_0);
c_roll();
}
- C_HANDLER( SPECIAL )
+ C_HANDLER( STATE_SPECIAL )
{
// empty
}
- C_HANDLER( SURF_BACK ) {} // TODO
- C_HANDLER( SURF_LEFT ) {} // TODO
- C_HANDLER( SURF_RIGHT ) {} // TODO
+ C_HANDLER( STATE_SURF_BACK )
+ {
+ c_angle(ANGLE_180);
+ c_surf();
+ }
- C_HANDLER( MIDAS_USE )
+ C_HANDLER( STATE_SURF_LEFT )
+ {
+ c_angle(-ANGLE_90);
+ c_surf();
+ }
+
+ C_HANDLER( STATE_SURF_RIGHT )
+ {
+ c_angle(ANGLE_90);
+ c_surf();
+ }
+
+ C_HANDLER( STATE_USE_MIDAS )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( MIDAS_DEATH )
+ C_HANDLER( STATE_DEATH_MIDAS )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( SWAN_DIVE )
+ C_HANDLER( STATE_SWAN_DIVE )
{
c_angle(ANGLE_0);
c_jump();
}
- C_HANDLER( FAST_DIVE )
+ C_HANDLER( STATE_FAST_DIVE )
{
c_angle(ANGLE_0);
c_jump();
}
- C_HANDLER( HANDSTAND )
+ C_HANDLER( STATE_HANDSTAND )
{
c_angle(ANGLE_0);
c_default();
}
- C_HANDLER( WATER_OUT )
+ C_HANDLER( STATE_WATER_OUT )
{
c_angle(ANGLE_0);
c_default();
}
+ C_HANDLER( STATE_CLIMB_START ) {}
+ C_HANDLER( STATE_CLIMB_UP ) {}
+ C_HANDLER( STATE_CLIMB_LEFT ) {}
+ C_HANDLER( STATE_CLIMB_END ) {}
+ C_HANDLER( STATE_CLIMB_RIGHT ) {}
+ C_HANDLER( STATE_CLIMB_DOWN ) {}
+ C_HANDLER( STATE_UNUSED_1 ) {}
+ C_HANDLER( STATE_UNUSED_2 ) {}
+ C_HANDLER( STATE_UNUSED_3 ) {}
+ C_HANDLER( STATE_WADE ) {}
+ C_HANDLER( STATE_ROLL_UW ) {}
+ C_HANDLER( STATE_PICKUP_FLARE ) {}
+ C_HANDLER( STATE_ROLL_AIR ) {}
+ C_HANDLER( STATE_UNUSED_4 ) {}
+ C_HANDLER( STATE_ZIPLINE ) {}
+
Lara(Room* room) : Item(room)
{
health = LARA_MAX_HEALTH;
@@ -2002,13 +2563,17 @@ struct Lara : Item
flags.shadow = true;
activate();
+
+ animSet(ANIM_STAND, true, 0);
}
// update control
void updateInput()
{
input = 0;
- if (camera.mode == CAMERA_MODE_FREE) return;
+
+ if (camera.mode == CAMERA_MODE_FREE)
+ return;
if (keys & IK_LEFT) input |= IN_LEFT;
if (keys & IK_RIGHT) input |= IN_RIGHT;
@@ -2022,10 +2587,159 @@ struct Lara : Item
if ((keys & (IK_L | IK_R)) == (IK_L | IK_R)) input |= IN_LOOK;
}
- virtual void update()
+ void updateWaterState()
{
- updateInput();
+ int32 waterLevel = getWaterLevel();
+ int32 waterDist = WALL;
+ if (waterLevel != WALL) {
+ waterDist = pos.y - waterLevel;
+ }
+
+ // change water state
+ switch (waterState)
+ {
+ case WATER_STATE_ABOVE:
+ {
+ if (waterDist == WALL || waterDist < LARA_WADE_MIN_DEPTH) {
+ break;
+ }
+
+ int32 waterDepth = getWaterDepth();
+ if (waterDepth > LARA_WADE_MAX_DEPTH - 256)
+ {
+ if (!room->info->flags.water) // go dive
+ break;
+
+ waterState = WATER_STATE_UNDER;
+ flags.gravity = false;
+ oxygen = LARA_MAX_OXYGEN;
+
+ pos.y += 100;
+ updateRoom(0);
+ stopScreaming();
+
+ if (state == STATE_SWAN_DIVE) {
+ angle.x = -45 * DEG2SHORT;
+ goalState = STATE_UW_DIVE;
+ animProcess();
+ vSpeed *= 2;
+ //game->waterDrop(pos, 128.0f, 0.2f);
+ } else if (state == STATE_FAST_DIVE) {
+ angle.x = -85 * DEG2SHORT;
+ goalState = STATE_UW_DIVE;
+ animProcess();
+ vSpeed *= 2;
+ //game->waterDrop(pos, 128.0f, 0.2f);
+ } else {
+ angle.x = -45 * DEG2SHORT;
+ animSet(ANIM_WATER_FALL, true);
+ state = STATE_UW_DIVE; // TODO check necessary
+ goalState = STATE_UW_SWIM;
+ vSpeed = vSpeed * 3 / 2;
+ //game->waterDrop(pos, 256.0f, 0.2f);
+ }
+
+ //v2head.x = v2head.y = 0;
+ //v2torso.x = v2torso.y = 0;
+ //waterSplash();
+ } else if (waterDist > LARA_WADE_MIN_DEPTH) {
+ waterState = WATER_STATE_WADE;
+ if (!flags.gravity) {
+ goalState = STATE_STOP;
+ }
+ }
+ break;
+ }
+
+ case WATER_STATE_SURFACE:
+ {
+ if (room->info->flags.water)
+ break;
+
+ if (waterDist > LARA_WADE_MIN_DEPTH) {
+ waterState = WATER_STATE_WADE;
+ animSet(ANIM_STAND_NORMAL, true);
+ goalState = STATE_WADE;
+ animProcess();
+ } else {
+ waterState = WATER_STATE_ABOVE;
+ animSet(ANIM_FALL_FORTH, true);
+ hSpeed = vSpeed / 4;
+ flags.gravity = true;
+ }
+
+ vSpeed = 0;
+ angle.x = angle.z = 0;
+ //v2head.x = v2head.y = 0;
+ //v2torso.x = v2torso.y = 0;
+ break;
+ }
+
+ case WATER_STATE_UNDER:
+ {
+ if (room->info->flags.water || flags.dozy)
+ break;
+
+ if ((getWaterDepth() != WALL) && abs(waterDist) < 256) {
+ waterState = WATER_STATE_SURFACE;
+ pos.y -= (waterDist - 1);
+ animSet(ANIM_SURF, true);
+ vSpeed = 0;
+ swimTimer = LARA_SWIM_TIMER + 1; // block dive before we press jump button again
+ updateRoom(-LARA_HEIGHT / 2);
+ //game->playSound(TR::SND_BREATH, pos, Sound::PAN | Sound::UNIQUE);
+ } else {
+ waterState = WATER_STATE_ABOVE;
+ animSet(ANIM_FALL_FORTH, true);
+ hSpeed = vSpeed / 4;
+ vSpeed = 0;
+ flags.gravity = true;
+ }
+
+ angle.x = angle.z = 0;
+ //v2head.x = v2head.y = 0;
+ //v2torso.x = v2torso.y = 0;
+ break;
+ }
+
+ case WATER_STATE_WADE:
+ {
+ if (waterDist < LARA_WADE_MIN_DEPTH)
+ {
+ waterState = WATER_STATE_ABOVE;
+ if (state == STATE_WADE) {
+ goalState = STATE_RUN;
+ }
+ } else if (waterDist > LARA_WADE_MAX_DEPTH) {
+ waterState = WATER_STATE_SURFACE;
+ pos.y -= (waterDist - 1);
+
+ if (state == STATE_BACK) {
+ animSet(ANIM_SURF_BACK, true);
+ } else if (state == STATE_STEP_RIGHT) {
+ animSet(ANIM_SURF_RIGHT, true);
+ } else if (state == STATE_STEP_LEFT) {
+ animSet(ANIM_SURF_LEFT, true);
+ } else {
+ animSet(ANIM_SURF_SWIM, true);
+ }
+
+ swimTimer = 0;
+ vSpeed = 0;
+ flags.gravity = false;
+ angle.x = angle.z = 0;
+ //v2head.x = v2head.y = 0;
+ //v2torso.x = v2torso.y = 0;
+ updateRoom(0);
+ }
+ break;
+ }
+ }
+ }
+
+ void updateAbove()
+ {
cinfo.trigger = NULL;
cinfo.radius = LARA_RADIUS;
cinfo.pos = pos;
@@ -2039,14 +2753,109 @@ struct Lara : Item
angle.z = angleDec(angle.z, 1 * DEG2SHORT);
turnSpeed = angleDec(turnSpeed, 2 * DEG2SHORT);
angle.y += turnSpeed;
+ }
- updateAnim();
+ void updateSurface()
+ {
+ cinfo.trigger = NULL;
+ cinfo.radius = LARA_RADIUS;
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -128;
+ cinfo.gapCeiling = 100;
+ cinfo.pos = pos;
+ cinfo.enemyPush = false;
+ cinfo.enemyHit = false;
+ cinfo.stopOnSlant = false;
+ cinfo.stopOnLava = false;
+
+ updateState();
+
+ angle.z = angleDec(angle.z, 2 * DEG2SHORT);
+
+ pos.x += (phd_sin(moveAngle) * vSpeed) >> 16;
+ pos.z += (phd_cos(moveAngle) * vSpeed) >> 16;
+
+ camera.targetAngleX = -22 * DEG2SHORT;
+ }
+
+ void updateUnder()
+ {
+ cinfo.trigger = NULL;
+ cinfo.radius = LARA_RADIUS_WATER;
+ cinfo.gapPos = -WALL;
+ cinfo.gapNeg = -LARA_HEIGHT_UW;
+ cinfo.gapCeiling = LARA_HEIGHT_UW;
+ cinfo.pos = pos;
+ cinfo.enemyPush = false;
+ cinfo.enemyHit = false;
+ cinfo.stopOnSlant = false;
+ cinfo.stopOnLava = false;
+
+ updateState();
+
+ angle.z = angleDec(angle.z, 2 * DEG2SHORT);
+ turnSpeed = angleDec(turnSpeed, 2 * DEG2SHORT);
+ angle.y += turnSpeed;
+
+ angle.x = X_CLAMP(angle.x, -85 * DEG2SHORT, 85 * DEG2SHORT);
+ angle.z = X_CLAMP(angle.z, -22 * DEG2SHORT, 22 * DEG2SHORT);
+
+ int32 c = phd_cos(angle.x);
+ int32 s = phd_sin(angle.x);
+
+ pos.y -= (s * vSpeed) >> 16;
+ pos.x += (c * ((phd_sin(angle.y) * vSpeed) >> 16)) >> FIXED_SHIFT;
+ pos.z += (c * ((phd_cos(angle.y) * vSpeed) >> 16)) >> FIXED_SHIFT;
+ }
+
+ void updateWeapon()
+ {
+ // TODO
+ }
+
+ virtual void update()
+ {
+ updateInput();
+
+ updateWaterState();
+
+ if (health > 0)
+ {
+ if (waterState == WATER_STATE_UNDER)
+ {
+ if (oxygen > 0) {
+ oxygen--;
+ } else {
+ health -= 5;
+ }
+ } else {
+ oxygen = X_MIN(oxygen + 10, LARA_MAX_OXYGEN);
+ }
+ }
+
+ switch (waterState)
+ {
+ case WATER_STATE_ABOVE :
+ case WATER_STATE_WADE : updateAbove(); break;
+ case WATER_STATE_SURFACE : updateSurface(); break;
+ case WATER_STATE_UNDER : updateUnder(); break;
+ }
+
+ animProcess();
updateCollision();
- updateRoom(-LARA_HEIGHT / 2);
+ int32 offset;
+ if (waterState == WATER_STATE_SURFACE) {
+ offset = LARA_RADIUS;
+ } else if (waterState == WATER_STATE_UNDER) {
+ offset = 0;
+ } else {
+ offset = -LARA_HEIGHT / 2;
+ }
+ updateRoom(offset);
- //updateWeapon();
+ updateWeapon();
checkTrigger(cinfo.trigger, this);
}
@@ -2090,18 +2899,18 @@ int32 doTutorial(Item* lara, int32 track)
{
switch (track)
{
- case 28 : if (gSaveGame.tracks[track].once && lara->state == Lara::UP_JUMP) return 29;
+ case 28 : if (gSaveGame.tracks[track].once && lara->state == Lara::STATE_JUMP_UP) return 29;
case 37 :
- case 41 : if (lara->state != Lara::HANG) return 0;
- case 42 : if (gSaveGame.tracks[track].once && lara->state == Lara::HANG) return 43;
- case 49 : if (lara->state != Lara::SURF_TREAD) return 0;
+ case 41 : if (lara->state != Lara::STATE_HANG) return 0;
+ case 42 : if (gSaveGame.tracks[track].once && lara->state == Lara::STATE_HANG) return 43;
+ case 49 : if (lara->state != Lara::STATE_SURF_TREAD) return 0;
case 50 : // end of GYM
if (gSaveGame.tracks[track].once) {
//timer += Core::deltaTime;
//if (timer > 3.0f)
// game->loadNextLevel();
} else {
- if (lara->state != Lara::WATER_OUT)
+ if (lara->state != Lara::STATE_WATER_OUT)
return 0;
//timer = 0.0f;
}
@@ -2111,4 +2920,11 @@ int32 doTutorial(Item* lara, int32 track)
return track;
}
+Lara* players[2];
+
+Lara* getLara(const vec3i &pos)
+{
+ return players[0]; // TODO find nearest player
+}
+
#endif
diff --git a/src/platform/gba/level.h b/src/platform/gba/level.h
index 3586a48..42d40a2 100644
--- a/src/platform/gba/level.h
+++ b/src/platform/gba/level.h
@@ -5,7 +5,7 @@
Level level;
-const Texture* textures;
+const Sprite* sprites;
const uint8* tiles;
#ifndef MODE_PAL
@@ -16,13 +16,14 @@ IWRAM_DATA uint8 lightmap[256 * 32]; // IWRAM 8k
EWRAM_DATA Item items[MAX_ITEMS];
-#define MAX_DYN_SECTORS 1024
+#define MAX_DYN_SECTORS (1024*3)
int32 dynSectorsCount;
EWRAM_DATA Sector dynSectors[MAX_DYN_SECTORS]; // EWRAM 8k
-
+EWRAM_DATA Texture textures[MAX_TEXTURES];
EWRAM_DATA Room rooms[MAX_ROOMS];
EWRAM_DATA Model models[MAX_MODELS];
EWRAM_DATA StaticMesh staticMeshes[MAX_STATIC_MESHES];
+EWRAM_DATA FixedCamera cameras[MAX_CAMERAS];
Item* Item::sFirstActive;
Item* Item::sFirstFree;
@@ -93,7 +94,10 @@ void readLevel(const uint8* data)
tiles = level.tiles;
- textures = level.textures;
+ // prepare textures (required by anim tex logic)
+ memcpy(textures, level.textures, level.texturesCount * sizeof(Texture));
+
+ sprites = level.sprites;
// prepare models // TODO prerocess
memset(models, 0, sizeof(models));
@@ -126,6 +130,9 @@ void readLevel(const uint8* data)
m->start = spriteSeq->start;
}
+ // prepare fixed cameras
+ memcpy(cameras, level.cameras, level.camerasCount * sizeof(FixedCamera));
+
// prepare free list
for (int32 i = MAX_ITEMS - 1; i >= level.itemsCount; i--)
{
diff --git a/src/platform/gba/main.cpp b/src/platform/gba/main.cpp
index e620de8..cb5a7c1 100644
--- a/src/platform/gba/main.cpp
+++ b/src/platform/gba/main.cpp
@@ -1,11 +1,13 @@
#if defined(_WIN32) || defined(__DOS__)
- void* LEVEL1_PKD;
- void* TRACK_13_WAV;
+ const void* TRACK_13_WAV;
+ const void* levelData;
+ #define LEVEL_NAME "LEVEL1.PKD"
#elif defined(__GBA__)
- #include "LEVEL1_PKD.h"
#include "TRACK_13_WAV.h"
+ #include "LEVEL1_PKD.h"
+ const void* levelData = LEVEL1_PKD;
#elif defined(__TNS__)
- void* LEVEL1_PHD;
+ const void* levelData;
#endif
#include "game.h"
@@ -41,11 +43,22 @@ int32 fpsCounter = 0;
memcpy(MEM_PAL_BG, palette, 256 * 2);
#endif
}
+
+ int32 osGetSystemTimeMS()
+ {
+ return GetTickCount();
+ }
+
#elif defined(__GBA__)
void paletteSet(const uint16* palette)
{
memcpy((uint16*)MEM_PAL_BG, palette, 256 * 2);
}
+
+ int32 osGetSystemTimeMS()
+ {
+ return 0; // TODO
+ }
#elif defined(__TNS__)
unsigned int osTime;
volatile unsigned int *timerBUS;
@@ -67,11 +80,16 @@ int32 fpsCounter = 0;
osTime = *timerCLK;
}
- int GetTickCount()
+ int32 GetTickCount()
{
return (osTime - *timerCLK) / 33;
}
+ int32 osGetSystemTimeMS()
+ {
+ return *timerCLK / 33;
+ }
+
void paletteSet(uint16* palette)
{
memcpy((uint16*)0xC0000200, palette, 256 * 2);
@@ -419,7 +437,7 @@ int main(void) {
{
// level1
#if defined(_WIN32) || defined(__DOS__)
- FILE *f = fopen("data/LEVEL1.PKD", "rb");
+ FILE *f = fopen("data/" LEVEL_NAME, "rb");
#elif defined(__TNS__)
FILE *f = fopen("/documents/OpenLara/LEVEL1.PHD.tns", "rb");
#else
@@ -430,12 +448,16 @@ int main(void) {
return 0;
}
- fseek(f, 0, SEEK_END);
- int32 size = ftell(f);
- fseek(f, 0, SEEK_SET);
- LEVEL1_PKD = new uint8[size];
- fread(LEVEL1_PKD, 1, size, f);
- fclose(f);
+ {
+ fseek(f, 0, SEEK_END);
+ int32 size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ uint8* data = new uint8[size];
+ fread(data, 1, size, f);
+ fclose(f);
+
+ levelData = data;
+ }
// track 13
#if defined(_WIN32) || defined(__DOS__)
@@ -448,16 +470,14 @@ int main(void) {
fseek(f, 0, SEEK_END);
int32 size = ftell(f);
fseek(f, 0, SEEK_SET);
- TRACK_13_WAV = new uint8[size];
- fread(TRACK_13_WAV, 1, size, f);
+ uint8* data = new uint8[size];
+ fread(data, 1, size, f);
fclose(f);
+
+ TRACK_13_WAV = data;
}
#endif
}
-#elif defined(__GBA__)
- // set low latency mode via WAITCNT register (thanks to GValiente)
- REG_WSCNT = WS_ROM0_N2 | WS_ROM0_S1 | WS_PREFETCH;
- //*(vu32*)(REG_BASE+0x0800) = 0x0E000020; // Undocumented - Internal Memory Control (R/W)
#endif
#if defined(_WIN32)
@@ -507,6 +527,30 @@ int main(void) {
//uint16 &GreenSwap = *(uint16*)0x4000002;
//GreenSwap = 1;
+ // set low latency mode via WAITCNT register (thanks to GValiente)
+ REG_WSCNT = WS_ROM0_N2 | WS_ROM0_S1 | WS_PREFETCH; // fast ROM
+
+ // Undocumented - Internal Memory Control (R/W)
+ #define MEM_CHECK_MAGIC 14021968
+ vu32& fastAccessReg = *(vu32*)(REG_BASE+0x0800);
+
+ fastAccessReg = 0x0E000020; // fast EWRAM
+
+ vu32* fastAccessMem = (vu32*)soundBufferA; // check EWRAM access
+ // write
+ for (int32 i = 0; i < 16; i++)
+ {
+ fastAccessMem[i] = MEM_CHECK_MAGIC + i;
+ }
+ // read
+ for (int32 i = 0; i < 16; i++)
+ {
+ if (fastAccessMem[i] != vu32(MEM_CHECK_MAGIC + i))
+ {
+ fastAccessReg = 0x0D000020; // device doesn't support this feature, revert reg value
+ }
+ }
+
soundInit();
game.init();
diff --git a/src/platform/gba/object.h b/src/platform/gba/object.h
index d1ff9f4..998e479 100644
--- a/src/platform/gba/object.h
+++ b/src/platform/gba/object.h
@@ -3,6 +3,7 @@
#include "item.h"
#include "lara.h"
+#include "inventory.h"
vec3i getBlockOffset(int16 angleY, int32 offset)
{
@@ -27,6 +28,31 @@ namespace Limits
Bounds( -200, 200, 0, 0, 312, 512 ),
vec3s( 10 * DEG2SHORT, 30 * DEG2SHORT, 10 * DEG2SHORT )
};
+
+ static const Limit SWITCH_UW = {
+ Bounds( -1024, 1024, -1024, 1024, -1024, 1024 ),
+ vec3s( 80 * DEG2SHORT, 80 * DEG2SHORT, 80 * DEG2SHORT )
+ };
+
+ static const Limit BLOCK = {
+ Bounds( -300, 300, 0, 0, -692, -512 ),
+ vec3s( 10 * DEG2SHORT, 30 * DEG2SHORT, 10 * DEG2SHORT )
+ };
+
+ static const Limit PICKUP = {
+ Bounds( -256, 256, -100, 100, -256, 100 ),
+ vec3s( 10 * DEG2SHORT, 0, 0 )
+ };
+
+ static const Limit PICKUP_UW = {
+ Bounds( -512, 512, -512, 512, -512, 512 ),
+ vec3s( 45 * DEG2SHORT, 45 * DEG2SHORT, 45 * DEG2SHORT )
+ };
+
+ static const Limit HOLE = {
+ Bounds( -200, 200, 0, 0, 312, 512 ),
+ vec3s( 10 * DEG2SHORT, 30 * DEG2SHORT, 10 * DEG2SHORT )
+ };
};
@@ -36,7 +62,7 @@ struct Object : Item
virtual void update()
{
- updateAnim();
+ animProcess();
}
bool isActive()
@@ -88,6 +114,21 @@ struct ViewTarget : Object
};
+struct Waterfall : Object
+{
+ Waterfall(Room* room) : Object(room) {}
+
+ virtual void draw() {}
+};
+
+
+struct LavaEmitter : Object
+{
+ LavaEmitter(Room* room) : Object(room) {}
+
+ virtual void draw() {}
+};
+
struct Door : Object
{
enum {
@@ -117,13 +158,12 @@ struct Door : Object
}
}
- updateAnim();
+ animProcess();
}
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
- UNUSED(lara);
- UNUSED(cinfo);
+ // TODO door collision
}
void action(bool close)
@@ -160,6 +200,8 @@ struct Door : Object
nextRoom = sector->getNextRoom();
}
+ // TODO modify level.boxes
+
if (!behind && nextRoom) {
activate(close, true, nextRoom, pos.x, pos.z); // use sector from item pos
}
@@ -167,6 +209,32 @@ struct Door : Object
};
+struct TrapDoor : Object
+{
+ enum {
+ STATE_CLOSE,
+ STATE_OPEN,
+ };
+
+ TrapDoor(Room* room) : Object(room) {}
+
+ virtual void update()
+ {
+ if (isActive()) {
+ if (state == STATE_CLOSE) {
+ goalState = STATE_OPEN;
+ }
+ } else {
+ if (state == STATE_OPEN) {
+ goalState = STATE_CLOSE;
+ }
+ }
+
+ animProcess();
+ }
+};
+
+
struct Switch : Object
{
enum {
@@ -189,12 +257,13 @@ struct Switch : Object
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
- UNUSED(cinfo);
+ if (lara->weaponState != WEAPON_STATE_FREE)
+ return;
if (!(lara->input & IN_ACTION))
return;
- if (lara->state != Lara::STOP)
+ if (lara->state != Lara::STATE_STOP)
return;
if (flags.status != ITEM_FLAGS_STATUS_NONE)
@@ -207,22 +276,13 @@ struct Switch : Object
ASSERT(state == STATE_DOWN || state == STATE_UP);
- if (state == STATE_DOWN) {
- lara->goalState = Lara::SWITCH_DOWN;
- goalState = STATE_UP;
- } else {
- lara->goalState = Lara::SWITCH_UP;
- goalState = STATE_DOWN;
- }
+ bool isDown = (state == STATE_DOWN);
- flags.status = ITEM_FLAGS_STATUS_ACTIVE;
+ goalState = isDown ? STATE_UP : STATE_DOWN;
+ lara->animSkip(isDown ? Lara::STATE_SWITCH_DOWN : Lara::STATE_SWITCH_UP, Lara::STATE_STOP, true);
activate();
-
- lara->skipAnim();
-
- updateAnim();
-
- lara->goalState = Lara::STOP;
+ flags.status = ITEM_FLAGS_STATUS_ACTIVE;
+ lara->weaponState = WEAPON_STATE_BUSY;
}
bool use(int32 t)
@@ -254,9 +314,30 @@ struct SwitchWater : Switch
virtual void collide(Lara* lara, CollisionInfo* cinfo)
{
- UNUSED(lara);
- UNUSED(cinfo);
- // TODO
+ if (lara->weaponState != WEAPON_STATE_FREE)
+ return;
+
+ if (!(lara->input & IN_ACTION))
+ return;
+
+ if (lara->state != Lara::STATE_UW_TREAD)
+ return;
+
+ if (!checkLimit(lara, Limits::SWITCH_UW))
+ return;
+
+ if (!lara->moveTo(vec3i(0, 0, 108), this, true))
+ return;
+
+ lara->vSpeed = 0; // underwater speed
+ goalState = (state == STATE_UP) ? STATE_DOWN : STATE_UP;
+
+ lara->animSkip(Lara::STATE_SWITCH_DOWN, Lara::STATE_UW_TREAD, true);
+ activate();
+ flags.status = ITEM_FLAGS_STATUS_ACTIVE;
+
+ //TODO TR2+
+ //lara->weaponState = WEAPON_STATE_BUSY;
}
};
@@ -265,9 +346,9 @@ struct Key : Object
{
Key(Room* room) : Object(room) {}
- bool use()
+ bool use(Item* lara)
{
- if (flags.status == ITEM_FLAGS_STATUS_ACTIVE) // TODO check weapons
+ if (flags.status == ITEM_FLAGS_STATUS_ACTIVE && lara->weaponState == WEAPON_STATE_FREE) // TODO check weapons
{
flags.status = ITEM_FLAGS_STATUS_INACTIVE;
return true;
@@ -290,6 +371,81 @@ struct Pickup : Object
}
return false;
}
+
+ virtual void collide(Lara* lara, CollisionInfo* cinfo)
+ {
+ angle.y = lara->angle.y;
+ angle.z = 0;
+
+ if (lara->waterState == WATER_STATE_ABOVE || lara->waterState == WATER_STATE_WADE)
+ {
+ angle.x = 0;
+
+ if (!checkLimit(lara, Limits::PICKUP))
+ return;
+
+ if (lara->state == Lara::STATE_PICKUP)
+ {
+ if (!lara->animIsEnd(23))
+ return;
+
+ inventory.add((ItemType)type);
+ gSaveGame.pickups++;
+ room->remove(this);
+ flags.status = ITEM_FLAGS_STATUS_INVISIBLE;
+ }
+ else if (lara->state == Lara::STATE_STOP)
+ {
+ if (lara->weaponState != WEAPON_STATE_FREE)
+ return;
+
+ if (!(lara->input & IN_ACTION))
+ return;
+
+ if (!lara->moveTo(vec3i(0, 0, -100), this, false))
+ return;
+
+ lara->animSkip(Lara::STATE_PICKUP, Lara::STATE_STOP);
+ lara->weaponState = WEAPON_STATE_BUSY;
+ }
+ }
+
+ if (lara->waterState == WATER_STATE_UNDER)
+ {
+ angle.x = -25 * DEG2SHORT;
+
+ if (!checkLimit(lara, Limits::PICKUP_UW))
+ return;
+
+ if (lara->state == Lara::STATE_PICKUP)
+ {
+ if (!lara->animIsEnd(14))
+ return;
+
+ inventory.add((ItemType)type);
+ gSaveGame.pickups++;
+ room->remove(this);
+ flags.status = ITEM_FLAGS_STATUS_INVISIBLE;
+ }
+ else if (lara->state == Lara::STATE_UW_TREAD)
+ {
+ // TODO TR2+
+ //if (lara->weaponState != WEAPON_STATE_FREE)
+ // return;
+
+ if (!(lara->input & IN_ACTION))
+ return;
+
+ if (!lara->moveTo(vec3i(0, -200, -350), this, true))
+ return;
+
+ lara->animSkip(Lara::STATE_PICKUP, Lara::STATE_UW_TREAD);
+
+ // TODO TR2+
+ //lara->weaponState = WEAPON_STATE_BUSY; // TODO check CMD_EMPTY event
+ }
+ }
+ }
};
@@ -298,9 +454,9 @@ bool useSwitch(Item* item, int32 timer)
return ((Switch*)item)->use(timer);
}
-bool useKey(Item* item)
+bool useKey(Item* item, Item* lara)
{
- return ((Key*)item)->use();
+ return ((Key*)item)->use(lara);
}
bool usePickup(Item* item)
@@ -309,6 +465,98 @@ bool usePickup(Item* item)
}
+vec3i tmpPos;
+
+struct Hole : Object // parent class for KeyHole and PuzzleHole
+{
+ Hole(Room* room) : Object(room) {}
+
+ void apply(int32 offset, Lara* lara, Lara::State stateUse)
+ {
+ if (lara->weaponState != WEAPON_STATE_FREE)
+ return;
+
+ if (flags.status != ITEM_FLAGS_STATUS_NONE)
+ return;
+
+ if (lara->state != Lara::STATE_STOP || lara->animIndex != Lara::ANIM_STAND_NORMAL)
+ return;
+
+ if (!(lara->input & IN_ACTION) && (inventory.useSlot == SLOT_MAX))
+ return;
+
+ if (!checkLimit(lara, Limits::HOLE))
+ return;
+
+ if (inventory.useSlot == SLOT_MAX)
+ {
+ if (inventory.numKeys > 0) {
+ inventory.show(lara, this);
+ return;
+ }
+ } else {
+ if (inventory.applyItem(this))
+ {
+ lara->moveTo(vec3i(0, 0, offset), this, false);
+ lara->animSkip(stateUse, Lara::STATE_STOP);
+ lara->weaponState = WEAPON_STATE_BUSY;
+ flags.status = ITEM_FLAGS_STATUS_ACTIVE;
+ return;
+ }
+
+ tmpPos.x = ~lara->pos.x;
+ inventory.useSlot = SLOT_MAX;
+ }
+
+ if (tmpPos != lara->pos)
+ {
+ tmpPos = lara->pos;
+ soundPlay(SND_NO, lara->pos);
+ }
+ }
+};
+
+
+struct KeyHole : Hole
+{
+ KeyHole(Room* room) : Hole(room) {}
+
+ virtual void collide(Lara* lara, CollisionInfo* cinfo)
+ {
+ apply(362, lara, Lara::STATE_USE_KEY);
+ }
+};
+
+
+struct PuzzleHole : Hole
+{
+ PuzzleHole(Room* room) : Hole(room) {}
+
+ virtual void collide(Lara* lara, CollisionInfo* cinfo)
+ {
+ if (lara->state == Lara::STATE_USE_PUZZLE)
+ {
+ if (!checkLimit(lara, Limits::HOLE))
+ return;
+
+ if (!lara->animIsEnd(28))
+ return;
+
+ switch (type) {
+ case ITEM_PUZZLEHOLE_1 : type = ITEM_PUZZLEHOLE_DONE_1; break;
+ case ITEM_PUZZLEHOLE_2 : type = ITEM_PUZZLEHOLE_DONE_2; break;
+ case ITEM_PUZZLEHOLE_3 : type = ITEM_PUZZLEHOLE_DONE_3; break;
+ case ITEM_PUZZLEHOLE_4 : type = ITEM_PUZZLEHOLE_DONE_4; break;
+ }
+
+ return;
+ }
+
+ apply(327, lara, Lara::STATE_USE_PUZZLE);
+ }
+};
+
+
struct TrapFloor : Object
{
enum {
@@ -344,7 +592,7 @@ struct TrapFloor : Object
break;
}
- updateAnim();
+ animProcess();
if (flags.status == ITEM_FLAGS_STATUS_INACTIVE)
{
@@ -354,9 +602,9 @@ struct TrapFloor : Object
updateRoom();
- if (state == STATE_FALL && pos.y >= floor)
+ if (state == STATE_FALL && pos.y >= roomFloor)
{
- pos.y = floor;
+ pos.y = roomFloor;
vSpeed = 0;
flags.gravity = false;
goalState = STATE_DOWN;
@@ -365,6 +613,44 @@ struct TrapFloor : Object
};
+struct TrapSwingBlade : Object
+{
+ enum {
+ STATE_STATIC,
+ STATE_BEGIN,
+ STATE_SWING,
+ STATE_END,
+ };
+
+ TrapSwingBlade(Room* room) : Object(room)
+ {
+ flags.shadow = true;
+ }
+
+ virtual void collide(Lara* lara, CollisionInfo* cinfo)
+ {
+ //
+ }
+
+ virtual void update()
+ {
+ if (isActive()) {
+ if (state == STATE_STATIC) {
+ goalState = STATE_SWING;
+ }
+ } else {
+ if (state == STATE_SWING) {
+ goalState = STATE_STATIC;
+ }
+ }
+
+ // TODO damage
+
+ animProcess();
+ }
+};
+
+
struct Dart : Object
{
Dart(Room* room) : Object(room)
@@ -379,10 +665,10 @@ struct Dart : Object
{
// TODO collide with Lara
- updateAnim();
+ animProcess();
updateRoom();
- if (pos.y >= floor)
+ if (pos.y >= roomFloor)
{
// TODO create spark
remove();
@@ -420,12 +706,206 @@ struct TrapDartEmitter : Object
if (dart)
{
+ dart->intensity = 0;
dart->flags.status = ITEM_FLAGS_STATUS_ACTIVE;
dart->activate();
}
}
- updateAnim();
+ animProcess();
+ }
+};
+
+
+struct Block : Object
+{
+ enum {
+ STATE_NONE,
+ STATE_READY,
+ STATE_PUSH,
+ STATE_PULL,
+ };
+
+ Block(Room* room) : Object(room)
+ {
+ if (flags.status != ITEM_FLAGS_STATUS_INVISIBLE) {
+ updateFloor(-1024);
+ }
+ }
+
+ void updateFloor(int32 offset)
+ {
+ room->modify();
+
+ Sector* sector = (Sector*)room->getSector(pos.x, pos.z);
+
+ if (sector->floor == NO_FLOOR) {
+ sector->floor = sector->ceiling + (offset >> 8);
+ } else {
+ sector->floor += (offset >> 8);
+ if (sector->floor == sector->ceiling) {
+ sector->floor = NO_FLOOR;
+ }
+ }
+
+ // TODO modify level.boxes
+ }
+
+ bool checkBlocking()
+ {
+ const Sector* sector = room->getSector(pos.x, pos.z);
+
+ return (sector->floor == NO_FLOOR) || ((sector->floor << 8) + 1024 == pos.y);
+ }
+
+ bool checkObstacles(int32 x, int32 z, int32 height)
+ {
+ Room* nextRoom = room->getRoom(x, pos.y, z);
+ const Sector* sector = nextRoom->getSector(x, z);
+
+ int32 floor = pos.y;
+ int32 ceiling = pos.y - height;
+
+ if ((sector->floor << 8) != floor)
+ return false;
+
+ nextRoom = nextRoom->getRoom(x, ceiling, z);
+ sector = nextRoom->getSector(x, z);
+
+ if ((sector->ceiling << 8) > ceiling)
+ return false;
+
+ return true;
+ }
+
+ bool checkPush()
+ {
+ if (!checkBlocking())
+ return false;
+
+ vec3i offset = getBlockOffset(angle.y, -1024);
+
+ return checkObstacles(pos.x + offset.x, pos.z + offset.z, 1024);
+ }
+
+ bool checkPull(Item* lara)
+ {
+ if (!checkBlocking())
+ return false;
+
+ vec3i offset = getBlockOffset(angle.y, 1024);
+
+ if (!checkObstacles(pos.x + offset.x, pos.z + offset.z, 1024))
+ return false;
+
+ return checkObstacles(lara->pos.x + offset.x, lara->pos.z + offset.z, LARA_HEIGHT);
+ }
+
+ virtual void collide(Lara* lara, CollisionInfo* cinfo)
+ {
+ if (lara->weaponState != WEAPON_STATE_FREE)
+ return;
+
+ if (!(lara->input & IN_ACTION))
+ return;
+
+ if (flags.status == ITEM_FLAGS_STATUS_ACTIVE)
+ return;
+
+ if (lara->pos.y != pos.y)
+ return;
+
+ uint16 quadrant = uint16(lara->angle.y + ANGLE_45) / ANGLE_90;
+
+ if (lara->state == Lara::STATE_BLOCK_READY)
+ {
+ if (!lara->animIsEnd(0))
+ return;
+
+ if (!checkLimit(lara, Limits::BLOCK))
+ return;
+
+ if (lara->input & IN_UP)
+ {
+ if (!checkPush())
+ return;
+
+ lara->goalState = Lara::STATE_BLOCK_PUSH;
+ goalState = STATE_PUSH;
+ }
+ else if (lara->input & IN_DOWN)
+ {
+ if (!checkPull(lara))
+ return;
+
+ lara->goalState = Lara::STATE_BLOCK_PULL;
+ goalState = STATE_PULL;
+ }
+ else
+ {
+ return;
+ }
+
+ updateFloor(1024);
+
+ activate();
+ flags.status = ITEM_FLAGS_STATUS_ACTIVE;
+
+ animProcess();
+ lara->animProcess();
+ }
+
+ if (lara->state == Lara::STATE_STOP)
+ {
+ if (lara->input & (IN_UP | IN_DOWN))
+ return;
+
+ angle.y = quadrant * ANGLE_90;
+
+ if (!checkLimit(lara, Limits::BLOCK))
+ return;
+
+ lara->goalState = Lara::STATE_BLOCK_READY;
+ lara->angle.y = angle.y;
+ lara->alignWall(LARA_RADIUS);
+ lara->animProcess();
+ }
+ }
+
+ virtual void update()
+ {
+ if (flags.once)
+ {
+ updateFloor(1024);
+ remove();
+ return;
+ }
+
+ animProcess();
+
+ updateRoom(); // it'll get roomFloor and gLastFloorData
+
+ if (pos.y > roomFloor)
+ {
+ flags.gravity = true;
+ }
+ else if (flags.gravity)
+ {
+ flags.gravity = false;
+ flags.status = ITEM_FLAGS_STATUS_INACTIVE;
+ pos.y = roomFloor;
+ // TODO EarthQuake + playSound 70 (Thor room)
+ }
+
+ if (flags.status == ITEM_FLAGS_STATUS_INACTIVE)
+ {
+ deactivate();
+ flags.status = ITEM_FLAGS_STATUS_NONE;
+
+ updateFloor(-1024);
+
+ checkTrigger(gLastFloorData, NULL);
+ }
}
};
diff --git a/src/platform/gba/rasterizeFTA_mode4.s b/src/platform/gba/rasterizeFTA_mode4.s
index eeb73d6..87edab6 100644
--- a/src/platform/gba/rasterizeFTA_mode4.s
+++ b/src/platform/gba/rasterizeFTA_mode4.s
@@ -65,8 +65,10 @@ SP_RDT = 12
ldrb indexB, [TILE, indexB]
add t, dtdx
- // cheap non-accurate alpha test, skip pixels pair if both are transparent
- orrs indexB, indexA, indexB, lsl #8 // indexB = indexA | (indexB << 8)
+ // cheap non-accurate alpha test, skip pixels pair if one or both are transparent
+ ands indexA, #255
+ andnes indexB, #255
+ orrne indexB, indexA, indexB, lsl #8 // indexB = indexA | (indexB << 8)
ldrneb indexA, [LMAP, indexA]
ldrneb indexB, [LMAP, indexB, lsr #8]
orrne indexA, indexB, lsl #8
@@ -206,19 +208,19 @@ rasterizeFTA_mode4_asm:
.align_left:
tst tmp, #1 // if (tmp & 1)
beq .align_right
- ldrb indexB, [tmp, #-1]! // read pal index from VRAM (byte)
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // res = (t & 0xFF00) | (t >> 24)
- add t, dtdx
ldrb indexA, [TILE, indexA]
- ldrb indexA, [LMAP, indexA]
cmp indexA, #0
+ ldrneb indexB, [tmp, #-1] // read pal index from VRAM (byte)
ldrneb indexA, [LMAP, indexA]
orrne indexB, indexA, lsl #8
strneh indexB, [tmp]
- add tmp, #2
+
+ add tmp, #1
+ add t, dtdx
subs width, #1 // width--
beq .scanline_end // if (width == 0)
@@ -226,7 +228,6 @@ rasterizeFTA_mode4_asm:
.align_right:
tst width, #1
beq .align_block_4px
- ldrb indexB, [tmp, width]
sub Rt, dtdx
and indexA, Rt, #0xFF00
@@ -236,6 +237,7 @@ rasterizeFTA_mode4_asm:
cmp indexA, #0
ldrneb indexA, [LMAP, indexA]
+ ldrneb indexB, [tmp, width]
orrne indexB, indexA, indexB, lsl #8
strneh indexB, [tmp, width]
diff --git a/src/platform/gba/rasterizeGTA_mode4.s b/src/platform/gba/rasterizeGTA_mode4.s
index 6e15a2c..14fa5b5 100644
--- a/src/platform/gba/rasterizeGTA_mode4.s
+++ b/src/platform/gba/rasterizeGTA_mode4.s
@@ -91,8 +91,10 @@ SP_RDT = 20
ldrb indexB, [TILE, indexB]
add t, dtdx
- // cheap non-accurate alpha test, skip pixels pair if both are transparent
- orrs indexB, indexA, indexB, lsl #8 // indexB = indexA | (indexB << 8)
+ // cheap non-accurate alpha test, skip pixels pair if one or both are transparent
+ ands indexA, #255
+ andnes indexB, #255
+ orrne indexB, indexA, indexB, lsl #8 // indexB = indexA | (indexB << 8)
ldrneb indexA, [LMAP, indexA]
ldrneb indexB, [LMAP, indexB, lsr #8]
orrne indexA, indexB, lsl #8
@@ -256,19 +258,25 @@ rasterizeGTA_mode4_asm:
.align_left:
tst ptr, #1 // if (ptr & 1)
beq .align_right
- ldrb indexB, [ptr, #-1]! // read pal index from VRAM (byte)
-
- bic LMAP, g, #255
- add g, dgdx, asr #1
and indexA, t, #0xFF00
orr indexA, t, lsr #24 // res = (t & 0xFF00) | (t >> 24)
ldrb indexA, [TILE, indexA]
+
+ cmp indexA, #0
+ beq .skip_left
+
+ bic LMAP, g, #255
ldrb indexA, [LMAP, indexA]
+ ldrb indexB, [ptr, #-1] // read pal index from VRAM (byte)
orr indexB, indexA, lsl #8
- strh indexB, [ptr], #2
+ strh indexB, [ptr]
+
+.skip_left:
+ add ptr, #1
add t, dtdx
+ add g, dgdx, asr #1
subs width, #1 // width--
beq .scanline_end // if (width == 0)
@@ -276,15 +284,19 @@ rasterizeGTA_mode4_asm:
.align_right:
tst width, #1
beq .align_block_4px
+
ldrb indexB, [ptr, width]
- subs width, #1 // width--
+ sub width, #1 // width--
mla Rti, width, dtdx, t // Rti = width * dtdx + t
and indexA, Rti, #0xFF00
orr indexA, Rti, lsr #24 // res = (t & 0xFF00) | (t >> 24)
ldrb indexA, [TILE, indexA]
+ cmp indexA, #0
+ beq .skip_right
+
asr Rgi, dgdx, #1
mla Rgi, width, Rgi, g // Rgi = width * (dgdx / 2) + g
bic LMAP, Rgi, #255
@@ -294,6 +306,8 @@ rasterizeGTA_mode4_asm:
orr indexB, indexA, indexB, lsl #8
strh indexB, [ptr, width]
+.skip_right:
+ cmp width, #0 // width--
beq .scanline_end // if (width == 0)
.align_block_4px:
diff --git a/src/platform/gba/rasterizer_mode4.h b/src/platform/gba/rasterizer_mode4.h
index df2775b..b0e0525 100644
--- a/src/platform/gba/rasterizer_mode4.h
+++ b/src/platform/gba/rasterizer_mode4.h
@@ -5,6 +5,7 @@
extern uint8 lightmap[256 * 32];
extern const uint8* tile;
+extern const Sprite* sprite;
#if defined(__GBA__)
#define USE_ASM
@@ -29,6 +30,7 @@ extern const uint8* tile;
#define rasterizeGT rasterizeGT_mode4_asm
#define rasterizeFTA rasterizeFTA_mode4_asm
#define rasterizeGTA rasterizeGTA_mode4_asm
+ #define rasterizeSprite rasterizeSprite_mode4_c
#else
#define rasterizeS rasterizeS_mode4_c
#define rasterizeF rasterizeF_mode4_c
@@ -37,6 +39,7 @@ extern const uint8* tile;
#define rasterizeGT rasterizeGT_mode4_c
#define rasterizeFTA rasterizeFTA_mode4_c
#define rasterizeGTA rasterizeGTA_mode4_c
+ #define rasterizeSprite rasterizeSprite_mode4_c
void rasterizeS_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
@@ -517,7 +520,7 @@ void rasterizeGT_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
// 8-bit fractional part precision for Gouraud component
// has some artifacts but allow to save one reg for inner loop
- // for aligned by 64k address of lightmap array
+ // with aligned by 64k address of lightmap array
while (1)
{
@@ -528,7 +531,7 @@ void rasterizeGT_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
if (N->v.y < L->v.y) return;
Lh = N->v.y - L->v.y;
- Lx = L->v.x;
+ Lx = L->v.x;
Lg = L->v.g;
Lt = L->t.uv;
@@ -800,8 +803,295 @@ void rasterizeFTA_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
void rasterizeGTA_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
{
- rasterizeGT(pixel, L, R);
-}
+#ifdef ALIGNED_LIGHTMAP
+ ASSERT((intptr_t(lightmap) & 0xFFFF) == 0); // lightmap should be 64k aligned
#endif
+ int32 Lh = 0, Rh = 0;
+ int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0;
+ uint32 Lt, Rt, Ldt, Rdt;
+ Ldt = 0;
+ Rdt = 0;
+
+ // 8-bit fractional part precision for Gouraud component
+ // has some artifacts but allow to save one reg for inner loop
+ // with aligned by 64k address of lightmap array
+
+ while (1)
+ {
+ while (!Lh)
+ {
+ const VertexUV* N = L->prev;
+
+ if (N->v.y < L->v.y) return;
+
+ Lh = N->v.y - L->v.y;
+ Lx = L->v.x;
+ Lg = L->v.g;
+ Lt = L->t.uv;
+
+ if (Lh > 1)
+ {
+ int32 tmp = FixedInvU(Lh);
+ Ldx = tmp * (N->v.x - Lx);
+ Ldg = tmp * (N->v.g - Lg) >> 8;
+
+ uint32 duv = N->t.uv - Lt;
+ uint32 du = tmp * int16(duv >> 16);
+ uint32 dv = tmp * int16(duv);
+ Ldt = (du & 0xFFFF0000) | (dv >> 16);
+ }
+
+ Lx <<= 16;
+ Lg <<= 8;
+ L = N;
+ }
+
+ while (!Rh)
+ {
+ const VertexUV* N = R->next;
+
+ if (N->v.y < R->v.y) return;
+
+ Rh = N->v.y - R->v.y;
+ Rx = R->v.x;
+ Rg = R->v.g;
+ Rt = R->t.uv;
+
+ if (Rh > 1)
+ {
+ int32 tmp = FixedInvU(Rh);
+ Rdx = tmp * (N->v.x - Rx);
+ Rdg = tmp * (N->v.g - Rg) >> 8;
+
+ uint32 duv = N->t.uv - Rt;
+ uint32 du = tmp * int16(duv >> 16);
+ uint32 dv = tmp * int16(duv);
+ Rdt = (du & 0xFFFF0000) | (dv >> 16);
+ }
+
+ Rx <<= 16;
+ Rg <<= 8;
+ R = N;
+ }
+
+ int32 h = X_MIN(Lh, Rh);
+ Lh -= h;
+ Rh -= h;
+
+ while (h--)
+ {
+ int32 x1 = Lx >> 16;
+ int32 x2 = Rx >> 16;
+
+ int32 width = x2 - x1;
+
+ if (width > 0)
+ {
+ int32 tmp = FixedInvU(width);
+
+ int32 dgdx = tmp * (Rg - Lg) >> 15;
+
+ uint32 duv = Rt - Lt;
+ uint32 u = tmp * int16(duv >> 16);
+ uint32 v = tmp * int16(duv);
+ uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
+
+ int32 g = Lg;
+ uint32 t = Lt;
+
+ volatile uint8* ptr = (uint8*)pixel + x1;
+
+ if (intptr_t(ptr) & 1)
+ {
+ ptr--;
+
+ uint8 indexB = tile[(t & 0xFF00) | (t >> 24)];
+
+ if (indexB) {
+ *(uint16*)ptr = *ptr | (lightmap[(g >> 8 << 8) | indexB] << 8);
+ }
+
+ ptr += 2;
+ t += dtdx;
+ g += dgdx >> 1;
+ width--;
+ }
+
+ if (width & 1)
+ {
+ uint32 tmp = Rt - dtdx;
+
+ uint8 indexA = tile[(tmp & 0xFF00) | (tmp >> 24)];
+
+ if (indexA) {
+ *(uint16*)(ptr + width - 1) = (ptr[width] << 8) | lightmap[(Rg >> 8 << 8) | indexA];
+ }
+ }
+
+ #ifdef ALIGNED_LIGHTMAP
+ g += intptr_t(lightmap);
+ #endif
+
+ width >>= 1;
+
+ while (width--)
+ {
+ #ifdef ALIGNED_LIGHTMAP
+ uint8 indexA = tile[(t & 0xFF00) | (t >> 24)];
+ t += dtdx;
+ uint8 indexB = tile[(t & 0xFF00) | (t >> 24)];
+ t += dtdx;
+ g += dgdx;
+
+
+ if (indexA && indexB) {
+ const uint8* LMAP = (uint8*)(g >> 8 << 8);
+ *(uint16*)ptr = LMAP[indexA] | (LMAP[indexB] << 8);
+ }
+ #else
+ uint8 indexA = tile[(t & 0xFF00) | (t >> 24)];
+ t += dtdx;
+ uint8 indexB = tile[(t & 0xFF00) | (t >> 24)];
+ t += dtdx;
+ g += dgdx;
+
+ if (indexA && indexB) {
+ *(uint16*)ptr = lightmap[(g >> 8 << 8) | indexA] | (lightmap[(g >> 8 << 8) | indexB] << 8);
+ }
+ #endif
+
+ ptr += 2;
+ }
+ }
+
+ pixel += VRAM_WIDTH;
+
+ Lx += Ldx;
+ Rx += Rdx;
+ Lg += Ldg;
+ Rg += Rdg;
+ Lt += Ldt;
+ Rt += Rdt;
+ }
+ }
+}
+
+#endif
+
+// TODO ARM version
+void rasterizeSprite_mode4_c(uint16* pixel, const VertexUV* L, const VertexUV* R)
+{
+ const uint8* ft_lightmap = &lightmap[L->v.g << 8];
+
+ int32 w = R->v.x - L->v.x;
+ if (w <= 0) return;
+
+ int32 h = R->v.y - L->v.y;
+ if (h <= 0) return;
+
+ int32 u = L->t.u << 8;
+ int32 v = L->t.v << 8;
+
+ int32 du = (R->t.u << 8) / w;
+ int32 dv = (R->t.v << 8) / h;
+
+ if (L->v.y < 0)
+ {
+ pixel -= L->v.y * VRAM_WIDTH;
+ v -= L->v.y * dv;
+ h += L->v.y;
+ }
+
+ if (R->v.y > FRAME_HEIGHT)
+ {
+ h -= R->v.y - FRAME_HEIGHT;
+ }
+
+ uint8* ptr = (uint8*)pixel;
+
+ if (h <= 0) return;
+
+ ptr += L->v.x;
+
+ if (L->v.x < 0)
+ {
+ ptr -= L->v.x;
+ u -= L->v.x * du;
+ w += L->v.x;
+ }
+
+ if (R->v.x > FRAME_WIDTH)
+ {
+ w -= R->v.x - FRAME_WIDTH;
+ }
+
+ if (w <= 0) return;
+
+ bool alignL = intptr_t(ptr) & 1;
+ if (alignL)
+ {
+ ptr--;
+ w--;
+ }
+
+ bool alignR = w & 1;
+ if (alignR)
+ {
+ w--;
+ }
+
+ w >>= 1;
+
+ for (int32 y = 0; y < h; y++)
+ {
+ const uint8* xtile = tile + (v & 0xFF00);
+
+ volatile uint8* xptr = ptr;
+
+ int32 xu = u;
+
+ if (alignL)
+ {
+ uint8 indexB = ft_lightmap[xtile[xu >> 8]];
+ if (indexB) {
+ *(uint16*)xptr = *xptr | (indexB << 8);
+ }
+
+ xptr += 2;
+ xu += du;
+ }
+
+ for (int32 x = 0; x < w; x++)
+ {
+ uint8 indexA = ft_lightmap[xtile[xu >> 8]];
+ xu += du;
+ uint8 indexB = ft_lightmap[xtile[xu >> 8]];
+ xu += du;
+
+ if (indexA | indexB)
+ {
+ if (indexA == 0) indexA = xptr[0];
+ if (indexB == 0) indexB = xptr[1];
+ *(uint16*)xptr = indexA | (indexB << 8);
+ }
+
+ xptr += 2;
+ }
+
+ if (alignR)
+ {
+ uint8 indexA = ft_lightmap[xtile[xu >> 8]];
+ if (indexA) {
+ *(uint16*)xptr = indexA | (xptr[1] << 8);
+ }
+ }
+
+ v += dv;
+
+ ptr += FRAME_WIDTH;
+ }
+}
+
+
#endif
diff --git a/src/platform/gba/render.cpp b/src/platform/gba/render.cpp
index 8a36f2c..4fcd376 100644
--- a/src/platform/gba/render.cpp
+++ b/src/platform/gba/render.cpp
@@ -34,10 +34,16 @@ uint16 palette[256]; // IWRAM 0.5k
#endif
extern uint8 lightmap[256 * 32];
-extern const Texture* textures;
+extern Texture textures[MAX_TEXTURES];
+extern const Sprite* sprites;
extern const uint8* tiles;
+extern int32 lightAmbient;
+extern int32 caustics[MAX_CAUSTICS];
+extern int32 causticsRand[MAX_CAUSTICS];
+extern int32 causticsFrame;
const uint8* tile;
+const Sprite* sprite;
uint32 gVerticesCount = 0;
int32 gFacesCount = 0; // 1 is reserved as OT terminator
@@ -72,10 +78,11 @@ bool transformBoxRect(const Bounds* box, Rect* rect)
*rect = Rect( INT_MAX, INT_MAX, INT_MIN, INT_MIN );
- for (int32 i = 0; i < 8; i++) {
+ for (int32 i = 0; i < 8; i++)
+ {
int32 z = DP43(m[2], v[i]);
- if (z < VIEW_MIN_F || z >= VIEW_MAX_F) { // TODO znear clip
+ if (z < VIEW_MIN_F || z >= VIEW_MAX_F) {
continue;
}
@@ -122,6 +129,13 @@ int32 boxIsVisible(const Bounds* box)
return rectIsVisible(&rect);
}
+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);
+}
+
void transform(int32 vx, int32 vy, int32 vz, int32 vg)
{
ASSERT(gVerticesCount < MAX_VERTICES);
@@ -132,26 +146,16 @@ void transform(int32 vx, int32 vy, int32 vz, int32 vg)
int32 z = DP43c(m[2], vx, vy, vz);
- if (z < VIEW_MIN_F || z >= VIEW_MAX_F) // TODO znear clip
+ if (z < VIEW_MIN_F || z >= VIEW_MAX_F)
{
- res.clip = 16;
+ res.clip = 32;
return;
}
int32 x = DP43c(m[0], vx, vy, vz);
int32 y = DP43c(m[1], vx, vy, vz);
- int32 fogZ = z >> FIXED_SHIFT;
- res.z = fogZ;
-
- if (fogZ > FOG_MIN)
- {
- vg += (fogZ - FOG_MIN) << FOG_SHIFT;
- if (vg > 8191) {
- vg = 8191;
- }
- }
-
+ res.z = z >> FIXED_SHIFT;
res.g = vg >> 8;
PERSPECTIVE(x, y, z);
@@ -162,10 +166,10 @@ void transform(int32 vx, int32 vy, int32 vz, int32 vg)
res.x = x + (FRAME_WIDTH >> 1);
res.y = y + (FRAME_HEIGHT >> 1);
- res.clip = enableClipping ? classify(res, viewport) : 0;
+ res.clip = classify(res, viewport); // enableClipping ? classify(res, viewport) : 0; TODO fix clip boxes for static meshes
}
-void transformRoomVertex(const RoomVertex* v)
+void transformRoomVertex(const RoomVertex* v, int32 caustics)
{
int32 vx = v->x << 10;
int32 vz = v->z << 10;
@@ -178,7 +182,7 @@ void transformRoomVertex(const RoomVertex* v)
if (vz < frustumAABB.minZ || vz > frustumAABB.maxZ ||
vx < frustumAABB.minX || vx > frustumAABB.maxX)
{
- res.clip = 16;
+ res.clip = 32;
return;
}
#endif
@@ -189,10 +193,13 @@ void transformRoomVertex(const RoomVertex* v)
int32 z = DP43c(m[2], vx, vy, vz);
- if (z < VIEW_MIN_F || z >= VIEW_MAX_F) // TODO znear clip
+ if (z < VIEW_MIN_F || z >= VIEW_MAX_F)
{
+ if (z < VIEW_MIN_F) z = VIEW_MIN_F;
+ if (z >= VIEW_MAX_F) z = VIEW_MAX_F;
res.clip = 16;
- return;
+ } else {
+ res.clip = 0;
}
int32 fogZ = z >> FIXED_SHIFT;
@@ -200,6 +207,10 @@ void transformRoomVertex(const RoomVertex* v)
int32 vg = v->g << 5;
+ if (caustics) {
+ vg = X_CLAMP(vg + caustics, 0, 8191);
+ }
+
if (fogZ > FOG_MIN)
{
vg += (fogZ - FOG_MIN) << FOG_SHIFT;
@@ -221,23 +232,32 @@ void transformRoomVertex(const RoomVertex* v)
res.x = x + (FRAME_WIDTH >> 1);
res.y = y + (FRAME_HEIGHT >> 1);
- res.clip = classify(res, viewport);
+ res.clip |= classify(res, viewport);
}
-void transformRoom(const RoomVertex* vertices, int32 vCount)
+void transformRoom(const RoomVertex* vertices, int32 vCount, bool applyCaustics)
{
+ int32 causticsValue = 0;
+
for (int32 i = 0; i < vCount; i++)
{
- transformRoomVertex(vertices);
+ if (applyCaustics) {
+ causticsValue = caustics[(causticsRand[i & (MAX_CAUSTICS - 1)] + causticsFrame) & (MAX_CAUSTICS - 1)];
+ }
+
+ transformRoomVertex(vertices, causticsValue);
vertices++;
}
}
-void transformMesh(const vec3s* vertices, int32 vCount, uint16 intensity)
+void transformMesh(const vec3s* vertices, int32 vCount, const uint16* vIntensity, const vec3s* vNormal)
{
+ // TODO calc lighting for vNormal
for (int32 i = 0; i < vCount; i++)
{
- transform(vertices->x, vertices->y, vertices->z, intensity);
+ ASSERT(!vIntensity || (vIntensity[i] + lightAmbient >= 0)); // ohhh, use X_CLAMP...
+
+ transform(vertices->x, vertices->y, vertices->z, vIntensity ? X_MIN(vIntensity[i] + lightAmbient, 8191) : lightAmbient);
vertices++;
}
}
@@ -313,7 +333,7 @@ void rasterize(const Face* face, const VertexUV *top)
#else
if (face->flags & FACE_COLORED) {
if (face->flags & FACE_FLAT) {
- if (face->flags & FACE_SHADOW) {
+ if (face->flags & FACE_SPRITE) {
rasterizeS(pixel, top, top);
} else {
rasterizeF(pixel, top, top, face->flags & FACE_TEXTURE);
@@ -322,7 +342,9 @@ void rasterize(const Face* face, const VertexUV *top)
rasterizeG(pixel, top, top, face->flags & FACE_TEXTURE);
}
} else {
- if (enableAlphaTest) {
+ if (face->flags & FACE_SPRITE) {
+ rasterizeSprite(pixel, top, top + 1);
+ } else if (enableAlphaTest) {
if (face->flags & FACE_FLAT) {
rasterizeFTA(pixel, top, top);
} else {
@@ -475,6 +497,14 @@ void drawPoly(Face* face, VertexUV* v)
rasterize(face, top);
}
+void drawSprite(Face* face, VertexUV* v)
+{
+ sprite = sprites + face->indices[1];
+ tile = tiles + (sprite->tile << 16);
+
+ rasterize(face, v);
+}
+
void drawGlyph(const Sprite *sprite, int32 x, int32 y)
{
int32 w = sprite->r - sprite->l;
@@ -536,14 +566,17 @@ void drawGlyph(const Sprite *sprite, int32 x, int32 y)
}
}
-X_INLINE void faceAddToOTable(Face* face, int32 depth)
+X_INLINE Face* faceAdd(int32 depth)
{
ASSERT(depth < OT_SIZE);
+ Face* face = gFaces + gFacesCount++;
face->next = otFaces[depth];
otFaces[depth] = face;
if (depth < otMin) otMin = depth;
if (depth > otMax) otMax = depth;
+
+ return face;
}
void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex)
@@ -556,17 +589,15 @@ void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex)
const Vertex* v3 = v + indices[2];
const Vertex* v4 = v + indices[3];
- if (v1->clip == 16 || v2->clip == 16 || v3->clip == 16 || v4->clip == 16)
+ if (v1->clip & v2->clip & v3->clip & v4->clip)
return;
- if (enableClipping)
- {
- if (v1->clip & v2->clip & v3->clip & v4->clip)
- return;
+ int32 clip = (v1->clip | v2->clip | v3->clip | v4->clip);
+ if (clip & 32)
+ return;
- if (v1->clip | v2->clip | v3->clip | v4->clip) {
- flags |= FACE_CLIPPED;
- }
+ if (clip) {
+ flags |= FACE_CLIPPED;
}
if (v1->g == v2->g && v1->g == v3->g && v1->g == v4->g) {
@@ -575,19 +606,12 @@ void faceAddQuad(uint32 flags, const Index* indices, int32 startVertex)
int32 depth = X_MAX(v1->z, X_MAX(v2->z, X_MAX(v3->z, v4->z))) >> OT_SHIFT;
- // z-bias hack for the shadow plane
- if (flags & FACE_SHADOW) {
- depth = X_MAX(0, depth - 8);
- }
-
- Face *f = gFaces + gFacesCount++;
- faceAddToOTable(f, depth);
-
- f->flags = uint16(flags);
- f->indices[0] = startVertex + indices[0];
- f->indices[1] = startVertex + indices[1];
- f->indices[2] = startVertex + indices[2];
- f->indices[3] = startVertex + indices[3];
+ Face* f = faceAdd(depth);
+ f->flags = uint16(flags);
+ f->indices[0] = v1 - gVertices;
+ f->indices[1] = v2 - gVertices;
+ f->indices[2] = v3 - gVertices;
+ f->indices[3] = v4 - gVertices;
}
void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex)
@@ -599,17 +623,16 @@ void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex)
const Vertex* v2 = v + indices[1];
const Vertex* v3 = v + indices[2];
- if (v1->clip == 16 || v2->clip == 16 || v3->clip == 16)
+ if (v1->clip & v2->clip & v3->clip)
return;
- if (enableClipping)
- {
- if (v1->clip & v2->clip & v3->clip)
- return;
+ int32 clip = (v1->clip | v2->clip | v3->clip);
- if (v1->clip | v2->clip | v3->clip) {
- flags |= FACE_CLIPPED;
- }
+ if (clip & 32)
+ return;
+
+ if (clip) {
+ flags |= FACE_CLIPPED;
}
if (v1->g == v2->g && v1->g == v3->g) {
@@ -618,13 +641,84 @@ void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex)
int32 depth = X_MAX(v1->z, X_MAX(v2->z, v3->z)) >> OT_SHIFT;
- Face *f = gFaces + gFacesCount++;
- faceAddToOTable(f, depth);
+ Face* f = faceAdd(depth);
+ f->flags = uint16(flags | FACE_TRIANGLE);
+ f->indices[0] = v1 - gVertices;
+ f->indices[1] = v2 - gVertices;
+ f->indices[2] = v3 - gVertices;
+}
- f->flags = uint16(flags | FACE_TRIANGLE);
- f->indices[0] = startVertex + indices[0];
- f->indices[1] = startVertex + indices[1];
- f->indices[2] = startVertex + indices[2];
+void faceAddSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index)
+{
+ ASSERT(gVerticesCount + 1 < MAX_VERTICES);
+
+ const Matrix &m = matrixGet();
+
+ int32 z = DP43c(m[2], vx, vy, vz);
+
+ if (z < VIEW_MIN_F || z >= VIEW_MAX_F)
+ {
+ return;
+ }
+
+ int32 x = DP43c(m[0], vx, vy, vz);
+ int32 y = DP43c(m[1], vx, vy, vz);
+
+ const Sprite* sprite = sprites + index;
+
+ int32 l = x + (sprite->l << FIXED_SHIFT);
+ int32 r = x + (sprite->r << FIXED_SHIFT);
+ int32 t = y + (sprite->t << FIXED_SHIFT);
+ int32 b = y + (sprite->b << FIXED_SHIFT);
+
+ PERSPECTIVE(l, t, z);
+
+ l += (FRAME_WIDTH >> 1);
+ if (l >= FRAME_WIDTH) return;
+
+ t += (FRAME_HEIGHT >> 1);
+ if (t >= FRAME_HEIGHT) return;
+
+ PERSPECTIVE(r, b, z);
+
+ r += (FRAME_WIDTH >> 1);
+ if (r < 0) return;
+
+ b += (FRAME_HEIGHT >> 1);
+ if (b < 0) return;
+
+ if (l == r) return;
+ if (t == b) return;
+
+ int32 fogZ = z >> FIXED_SHIFT;
+ if (fogZ > FOG_MIN)
+ {
+ vg += (fogZ - FOG_MIN) << FOG_SHIFT;
+ if (vg > 8191) {
+ vg = 8191;
+ }
+ }
+ vg >>= 8;
+
+ Vertex &v1 = gVertices[gVerticesCount++];
+ v1.x = l;
+ v1.y = t;
+ //v1.z = z;
+ v1.g = vg;
+
+ Vertex &v2 = gVertices[gVerticesCount++];
+ v2.x = r;
+ v2.y = b;
+ //v2.z = z;
+ //v2.g = g;
+
+ ASSERT(v2.x >= v1.x);
+ ASSERT(v2.y >= v1.y);
+
+ Face* f = faceAdd(fogZ >> OT_SHIFT);
+ f->flags = uint16(FACE_SPRITE);
+ f->indices[0] = gVerticesCount - 2;
+ f->indices[1] = index;
}
void faceAddRoom(const Quad* quads, int32 qCount, const Triangle* triangles, int32 tCount, int32 startVertex)
@@ -666,6 +760,7 @@ void flush()
if (gFacesCount)
{
PROFILE_START();
+
for (int32 i = otMax; i >= otMin; i--)
{
if (!otFaces[i]) continue;
@@ -678,34 +773,51 @@ void flush()
uint32 flags = face->flags;
- if (!(flags & FACE_COLORED))
- {
- const Texture &tex = textures[face->flags & FACE_TEXTURE];
- tile = tiles + (tex.tile << 16);
+ if (flags == FACE_SPRITE) {
+ const Sprite &sprite = sprites[face->indices[1]];
- v[0].t.uv = tex.uv0;
- v[1].t.uv = tex.uv1;
- v[2].t.uv = tex.uv2;
- v[3].t.uv = tex.uv3;
- enableAlphaTest = (tex.attribute == 1);
- }
+ v[0].v = gVertices[face->indices[0] + 0];
+ v[0].t.u = sprite.u;
+ v[0].t.v = sprite.v;
+ v[1].v = gVertices[face->indices[0] + 1];
+ v[1].t.u = sprite.w;
+ v[1].t.v = sprite.h;
- v[0].v = gVertices[face->indices[0]];
- v[1].v = gVertices[face->indices[1]];
- v[2].v = gVertices[face->indices[2]];
- if (!(flags & FACE_TRIANGLE)) {
- v[3].v = gVertices[face->indices[3]];
- }
+ ASSERT(v[0].v.x <= v[1].v.x);
+ ASSERT(v[0].v.y <= v[1].v.y);
- if (flags & FACE_CLIPPED) {
- drawPoly(face, v);
+ drawSprite(face, v);
} else {
- if (flags & FACE_TRIANGLE) {
- drawTriangle(face, v);
- } else {
- drawQuad(face, v);
+ if (!(flags & FACE_COLORED))
+ {
+ const Texture &tex = textures[flags & FACE_TEXTURE];
+ tile = tiles + (tex.tile << 16);
+
+ v[0].t.uv = tex.uv0;
+ v[1].t.uv = tex.uv1;
+ v[2].t.uv = tex.uv2;
+ v[3].t.uv = tex.uv3;
+
+ enableAlphaTest = (tex.attribute == 1);
}
- };
+
+ v[0].v = gVertices[face->indices[0]];
+ v[1].v = gVertices[face->indices[1]];
+ v[2].v = gVertices[face->indices[2]];
+ if (!(flags & FACE_TRIANGLE)) {
+ v[3].v = gVertices[face->indices[3]];
+ }
+
+ if (flags & FACE_CLIPPED) {
+ drawPoly(face, v);
+ } else {
+ if (flags & FACE_TRIANGLE) {
+ drawTriangle(face, v);
+ } else {
+ drawQuad(face, v);
+ }
+ };
+ }
face = face->next;
} while (face);
diff --git a/src/platform/gba/room.h b/src/platform/gba/room.h
index 616d2ac..ccff3ca 100644
--- a/src/platform/gba/room.h
+++ b/src/platform/gba/room.h
@@ -2,6 +2,28 @@
#define H_ROOM
#include "common.h"
+#include "camera.h"
+
+void animTexturesShift()
+{
+ const int16* data = level.animTexData;
+
+ int32 texRangesCount = *data++;
+
+ for (int32 i = 0; i < texRangesCount; i++)
+ {
+ int32 count = *data++;
+
+ Texture tmp = textures[*data];
+ while (count > 0)
+ {
+ textures[data[0]] = textures[data[1]];
+ data++;
+ count--;
+ }
+ textures[*data++] = tmp;
+ }
+}
int32 getBridgeFloor(const Item* item, int32 x, int32 z)
{
@@ -22,7 +44,7 @@ int32 getBridgeFloor(const Item* item, int32 x, int32 z)
h &= 1023;
- return item->pos.y + ((item->type == ITEM_BRIDGE_TILT1) ? (h >> 2) : (h >> 1));
+ return item->pos.y + ((item->type == ITEM_BRIDGE_TILT_1) ? (h >> 2) : (h >> 1));
}
int32 getTrapDoorFloor(const Item* item, int32 x, int32 z)
@@ -79,8 +101,8 @@ void getItemFloorCeiling(const Item* item, int32 x, int32 y, int32 z, int32* flo
break;
}
case ITEM_BRIDGE_FLAT:
- case ITEM_BRIDGE_TILT1:
- case ITEM_BRIDGE_TILT2:
+ case ITEM_BRIDGE_TILT_1:
+ case ITEM_BRIDGE_TILT_2:
{
h = getBridgeFloor(item, x, z);
break;
@@ -119,14 +141,14 @@ const Sector* Sector::getSectorBelow(int32 posX, int32 posZ) const
{
if (roomBelow == NO_ROOM)
return this;
- return rooms[roomBelow].getSector(posX, posZ);
+ return rooms[roomBelow].getSector(posX, posZ)->getSectorBelow(posX, posZ);
}
const Sector* Sector::getSectorAbove(int32 posX, int32 posZ) const
{
if (roomAbove == NO_ROOM)
return this;
- return rooms[roomAbove].getSector(posX, posZ);
+ return rooms[roomAbove].getSector(posX, posZ)->getSectorAbove(posX, posZ);
}
int32 Sector::getFloor(int32 x, int32 y, int32 z) const
@@ -291,14 +313,49 @@ void Sector::getTriggerFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int
}
-const Sector* Room::getSector(int32 posX, int32 posZ) const
+const Sector* Room::getSector(int32 x, int32 z) const
{
- int32 sx = X_CLAMP((posX - (info->x << 8)) >> 10, 0, info->xSectors - 1);
- int32 sz = X_CLAMP((posZ - (info->z << 8)) >> 10, 0, info->zSectors - 1);
+ int32 sx = X_CLAMP((x - (info->x << 8)) >> 10, 0, info->xSectors - 1);
+ int32 sz = X_CLAMP((z - (info->z << 8)) >> 10, 0, info->zSectors - 1);
return sectors + sx * info->zSectors + sz;
}
+const Sector* Room::getWaterSector(int32 x, int32 z) const
+{
+ const Room* room = this;
+ const Sector* sector = room->getSector(x, z);
+
+ // go up to the air
+ if (room->info->flags.water)
+ {
+ while (sector->roomAbove != NO_ROOM)
+ {
+ room = rooms + sector->roomAbove;
+
+ if (!room->info->flags.water) {
+ return sector;
+ }
+
+ sector = room->getSector(x, z);
+ }
+ return sector;
+ }
+
+ // go down to the water
+ while (sector->roomBelow != NO_ROOM)
+ {
+ room = rooms + sector->roomBelow;
+ sector = room->getSector(x, z);
+
+ if (room->info->flags.water) {
+ return sector;
+ }
+ }
+
+ return NULL;
+}
+
Room* Room::getRoom(int32 x, int32 y, int32 z)
{
const Sector* sector = getSector(x, z);
@@ -329,16 +386,6 @@ Room* Room::getRoom(int32 x, int32 y, int32 z)
return room;
}
-int32 Room::getWaterLevel()
-{
- return WALL; // TODO
-}
-
-int32 Room::getWaterDepth()
-{
- return WALL; // TODO
-}
-
bool Room::checkPortal(const Portal* portal)
{
vec3i d;
@@ -598,10 +645,61 @@ void Room::remove(Item* item)
}
}
+void checkCamera(const FloorData* fd)
+{
+ if (camera.mode == CAMERA_MODE_OBJECT)
+ return;
+
+ while (1)
+ {
+ FloorData::TriggerCommand triggerCmd = (fd++)->triggerCmd;
+
+ switch (triggerCmd.action)
+ {
+ case TRIGGER_ACTION_ACTIVATE_CAMERA:
+ {
+ triggerCmd.end = (fd++)->triggerCmd.end;
+
+ if (triggerCmd.args != camera.lastIndex)
+ break;
+
+ camera.index = triggerCmd.args;
+
+ if (camera.timer < 0 || camera.mode == CAMERA_MODE_LOOK || camera.mode == CAMERA_MODE_COMBAT)
+ {
+ camera.timer = -1;
+ break;
+ }
+
+ camera.mode = CAMERA_MODE_FIXED;
+ break;
+ }
+
+ case TRIGGER_ACTION_CAMERA_TARGET:
+ {
+ if (camera.mode == CAMERA_MODE_LOOK || camera.mode == CAMERA_MODE_COMBAT)
+ break;
+
+ ASSERT(triggerCmd.args < level.itemsCount);
+ camera.lookAtItem = items + triggerCmd.args;
+ break;
+ }
+
+ case TRIGGER_ACTION_FLYBY:
+ {
+ triggerCmd.end = (fd++)->triggerCmd.end;
+ break;
+ }
+ }
+
+ if (triggerCmd.end) break;
+ };
+}
void checkTrigger(const FloorData* fd, Item* lara)
{
- if (!fd) return;
+ if (!fd)
+ return;
if (fd->cmd.func == FLOOR_TYPE_LAVA)
{
@@ -609,6 +707,7 @@ void checkTrigger(const FloorData* fd, Item* lara)
if (fd->cmd.end)
return;
+
fd++;
}
@@ -616,8 +715,9 @@ void checkTrigger(const FloorData* fd, Item* lara)
FloorData::TriggerInfo info = (fd++)->triggerInfo;
Item* switchItem = NULL;
- Item* keyItem = NULL;
- Item* pickupItem = NULL;
+ Item* cameraItem = NULL;
+
+ checkCamera(fd);
if (!lara && cmd.type != TRIGGER_TYPE_OBJECT)
return;
@@ -631,37 +731,48 @@ void checkTrigger(const FloorData* fd, Item* lara)
case TRIGGER_TYPE_PAD:
case TRIGGER_TYPE_ANTIPAD:
- if (lara->pos.y != lara->floor)
+ {
+ if (lara->pos.y != lara->roomFloor)
return;
break;
+ }
case TRIGGER_TYPE_SWITCH:
+ {
switchItem = items + fd->triggerCmd.args;
if (!useSwitch(switchItem, info.timer))
return;
fd++;
break;
+ }
case TRIGGER_TYPE_KEY:
- keyItem = items + fd->triggerCmd.args;
- if (!useKey(keyItem))
+ {
+ Item* keyItem = items + fd->triggerCmd.args;
+ if (!useKey(keyItem, lara))
return;
fd++;
break;
+ }
case TRIGGER_TYPE_PICKUP:
- pickupItem = items + fd->triggerCmd.args;
+ {
+ Item* pickupItem = items + fd->triggerCmd.args;
if (!usePickup(pickupItem))
return;
fd++;
break;
+ }
case TRIGGER_TYPE_OBJECT:
return;
case TRIGGER_TYPE_COMBAT:
- // TODO unused?
+ {
+ if (lara->weaponState != WEAPON_STATE_READY)
+ return;
break;
+ }
case TRIGGER_TYPE_DUMMY:
return;
@@ -674,15 +785,18 @@ void checkTrigger(const FloorData* fd, Item* lara)
switch (triggerCmd.action)
{
- case TRIGGER_ACTION_ACTIVATE_OBJECT: {
+ case TRIGGER_ACTION_ACTIVATE_OBJECT:
+ {
+ ASSERT(triggerCmd.args < level.itemsCount);
Item* item = items + triggerCmd.args;
if (item->flags.once)
break;
item->timer = info.timer;
- if (item->timer != 1)
+ if (item->timer != 1) {
item->timer *= 30;
+ }
if (cmd.type == TRIGGER_TYPE_SWITCH) {
item->flags.mask ^= info.mask;
@@ -707,8 +821,40 @@ void checkTrigger(const FloorData* fd, Item* lara)
}
case TRIGGER_ACTION_ACTIVATE_CAMERA:
- // TODO fixed camera
+ {
+ FloorData::TriggerCommand cam = (fd++)->triggerCmd;
+ triggerCmd.end = cam.end;
+
+ if (cameras[triggerCmd.args].flags.once)
+ break;
+
+ camera.index = triggerCmd.args;
+
+ if (camera.mode == CAMERA_MODE_LOOK || camera.mode == CAMERA_MODE_COMBAT)
+ break;
+
+ if (cmd.type == TRIGGER_TYPE_COMBAT)
+ break;
+
+ if (cmd.type == TRIGGER_TYPE_SWITCH && (switchItem->state == 1) && (info.timer != 0))
+ break;
+
+ if (cmd.type == TRIGGER_TYPE_SWITCH || camera.index != camera.lastIndex)
+ {
+ camera.timer = cam.timer;
+ if (camera.timer != 1) {
+ camera.timer *= 30;
+ }
+
+ if (cam.once) {
+ cameras[camera.index].flags.once = true;
+ }
+
+ camera.speed = (cam.speed << 3) + 1;
+ camera.mode = lara ? CAMERA_MODE_FIXED : CAMERA_MODE_OBJECT;
+ }
break;
+ }
case TRIGGER_ACTION_FLOW:
// TODO flow
@@ -727,14 +873,17 @@ void checkTrigger(const FloorData* fd, Item* lara)
break;
case TRIGGER_ACTION_CAMERA_TARGET:
- // TODO change fixed camera target
+ {
+ cameraItem = items + triggerCmd.args;
break;
+ }
case TRIGGER_ACTION_END:
// TODO go to the next level
break;
- case TRIGGER_ACTION_SOUNDTRACK: {
+ case TRIGGER_ACTION_SOUNDTRACK:
+ {
int32 track = doTutorial(lara, triggerCmd.args);
if (track == 0) break;
@@ -764,7 +913,8 @@ void checkTrigger(const FloorData* fd, Item* lara)
// TODO effect
break;
- case TRIGGER_ACTION_SECRET: {
+ case TRIGGER_ACTION_SECRET:
+ {
if ((gSaveGame.secrets >> triggerCmd.args) & 1)
break;
@@ -777,6 +927,7 @@ void checkTrigger(const FloorData* fd, Item* lara)
break;
case TRIGGER_ACTION_FLYBY:
+ triggerCmd.end = (fd++)->triggerCmd.end;
break;
case TRIGGER_ACTION_CUTSCENE:
@@ -785,6 +936,11 @@ void checkTrigger(const FloorData* fd, Item* lara)
if (triggerCmd.end) break;
};
+
+ if (cameraItem && (camera.mode == CAMERA_MODE_FIXED || camera.mode == CAMERA_MODE_OBJECT))
+ {
+ camera.lookAtItem = cameraItem;
+ }
}
#endif
diff --git a/src/platform/gba/sound.h b/src/platform/gba/sound.h
index c7f5d89..276d9ff 100644
--- a/src/platform/gba/sound.h
+++ b/src/platform/gba/sound.h
@@ -105,8 +105,6 @@ struct Mixer
void fill(uint8* bufferA, uint8* bufferB, int32 count)
{
- UNUSED(bufferB);
-
if ((channelsCount == 0) && !music.data)
{
dmaFill(bufferA, SND_ENCODE(0), count);
@@ -178,9 +176,8 @@ struct Mixer
}
}
- if (channelsCount >= SND_CHANNELS) {
+ if (channelsCount >= SND_CHANNELS)
return NULL;
- }
#ifdef USE_9BIT_SOUND
// expand 8 to 9-bit
@@ -197,6 +194,19 @@ struct Mixer
return sample;
}
+ void stopSample(const uint8* data)
+ {
+ int32 i = channelsCount;
+
+ while (--i >= 0)
+ {
+ if (channels[i].data == data)
+ {
+ channels[i] = channels[--channelsCount];
+ }
+ }
+ }
+
void playMusic(const void* data)
{
music.data = (uint8*)data + 16;