diff --git a/src/platform/gba/Makefile b/src/platform/gba/Makefile index 0fa56e90..b52eee00 100644 --- a/src/platform/gba/Makefile +++ b/src/platform/gba/Makefile @@ -156,7 +156,7 @@ soundbank.bin soundbank.h : $(AUDIOFILES) @echo $(notdir $<) @$(bin2o) -%.WAV.o %_WAV.h : %.WAV +%.IMA.o %_IMA.h : %.IMA @echo $(notdir $<) @$(bin2o) diff --git a/src/platform/gba/OpenLara.vcxproj b/src/platform/gba/OpenLara.vcxproj index dabd3638..de3706da 100644 --- a/src/platform/gba/OpenLara.vcxproj +++ b/src/platform/gba/OpenLara.vcxproj @@ -25,7 +25,6 @@ - diff --git a/src/platform/gba/camera.h b/src/platform/gba/camera.h index 8f0d74e8..a5dba29a 100644 --- a/src/platform/gba/camera.h +++ b/src/platform/gba/camera.h @@ -5,11 +5,12 @@ #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) +#define CAM_DIST_FOLLOW 1536 +#define CAM_DIST_LOOK 768 +#define CAM_DIST_COMBAT 2048 +#define CAMERA_ANGLE_FOLLOW ANGLE(-10) +#define CAMERA_ANGLE_COMBAT ANGLE(-10) +#define CAMERA_ANGLE_MAX ANGLE(85) enum CameraMode { @@ -19,15 +20,11 @@ enum CameraMode CAMERA_MODE_LOOK, CAMERA_MODE_FIXED, CAMERA_MODE_OBJECT, + CAMERA_MODE_CUTSCENE, }; struct Camera { - struct Location { - Room* room; - vec3i pos; - }; - Location view; Location target; @@ -57,6 +54,8 @@ struct Camera void init(Item* lara) { + ASSERT(lara->extraL); + target.pos = lara->pos; target.pos.y -= 1024; target.room = lara->room; @@ -113,110 +112,8 @@ struct Camera view.pos.y -= m[2].y * CAM_SPEED >> 10; view.pos.z -= m[2].z * CAM_SPEED >> 10; } - } - void updateRoom() - { view.room = view.room->getRoom(view.pos.x, view.pos.y, view.pos.z); - - const Sector* sector = view.room->getSector(view.pos.x, view.pos.z); - int32 floor = sector->getFloor(view.pos.x, view.pos.y, view.pos.z); - int32 ceiling = sector->getCeiling(view.pos.x, view.pos.y, view.pos.z) - 256; - - if (floor != WALL) { - view.pos.y = X_MIN(view.pos.y, floor - 256); - } - - if (ceiling != WALL) - { - view.pos.y = X_MAX(view.pos.y, ceiling + 256); - } - } - - int32 traceX() - { - return 1; - } - - int32 traceZ() - { - return 1; - } - - bool trace() - { - int32 dx = abs(view.pos.x - target.pos.x); - int32 dz = abs(view.pos.z - target.pos.z); - - int32 tx, tz; - - if (dx < dz) { - tx = traceX(); - tz = traceZ(); - if (tz == 0) return false; - } else { - tz = traceZ(); - tx = traceX(); - if (tx == 0) return false; - } - - 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) @@ -229,14 +126,34 @@ struct Camera return res; } - Location getBestLocation(Item* item) + void clip(Location &loc) + { + const Sector* sector = loc.room->getSector(loc.pos.x, loc.pos.z); + + int32 floor = sector->getFloor(loc.pos.x, loc.pos.y, loc.pos.z); + if (floor != WALL && loc.pos.y > floor - 128) { + loc.pos.y = floor - 128; + } + + int32 ceiling = sector->getCeiling(loc.pos.x, loc.pos.y, loc.pos.z); + if (ceiling != WALL && loc.pos.y < ceiling + 128) { + loc.pos.y = ceiling + 128; + } + + // TODO clip walls? + } + + Location getBestLocation(Item* item, bool clip) { 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); + Location best = getLocationForAngle(targetAngleY, distH, distV); + + if (trace(target, best)) + return best; - if (trace(target, best, 200)) + if (clip && best.pos != target.pos) return best; int32 distQ = X_SQR(target.pos.x - best.pos.x) + X_SQR(target.pos.z - best.pos.z); @@ -248,36 +165,36 @@ struct Camera for (int32 i = 0; i < 4; i++) { - Location tmp = getLocationForAngle(i * ANGLE_90, distH, distV); + Location tmpDest = getLocationForAngle(i * ANGLE_90, distH, distV); + Location tmpView = view; - if (!trace(target, tmp, 200) || !trace(tmp, view, 0)) { + if (!trace(target, tmpDest) || !trace(tmpDest, tmpView)) continue; - } - distQ = X_SQR(view.pos.x - tmp.pos.x) + X_SQR(view.pos.z - tmp.pos.z); + distQ = X_SQR(view.pos.x - tmpDest.pos.x) + X_SQR(view.pos.z - tmpDest.pos.z); if (distQ < minDistQ) { minDistQ = distQ; - best = tmp; + best = tmpDest; } } return best; } - void move(const Location &to, int32 speed) + void move(Location &to, int32 speed) { + clip(to); + vec3i d = to.pos - view.pos; - if (speed > 1) - { - d.x /= speed; - d.y /= speed; - d.z /= speed; + + if (speed > 1) { + d /= speed; } view.pos += d; - view.room = to.room->getRoom(view.pos.x, view.pos.y, view.pos.z); + view.room = to.room->getRoom(view.pos.x, view.pos.y, view.pos.z); } void updateFollow(Item* item) @@ -287,20 +204,55 @@ struct Camera } targetAngleX = X_CLAMP(targetAngleX + item->angle.x, -CAMERA_ANGLE_MAX, CAMERA_ANGLE_MAX); + targetAngleY += item->angle.y; - Location best = getBestLocation(item); + Location best = getBestLocation(item, false); move(best, lastFixed ? speed : 12); } - void updateCombat() + void updateCombat(Item* item) { - // + ASSERT(item->type == ITEM_LARA); + + targetAngleX = item->angle.x + CAMERA_ANGLE_COMBAT; + targetAngleY = item->angle.y; + + if (item->extraL->armR.target || item->extraL->armL.target) + { + int32 aX = item->extraL->armR.angleAim.x + item->extraL->armL.angleAim.x; + int32 aY = item->extraL->armR.angleAim.y + item->extraL->armL.angleAim.y; + + if (item->extraL->armR.target && item->extraL->armL.target) { + targetAngleX += aX >> 1; + targetAngleY += aY >> 1; + } else { + targetAngleX += aX; + targetAngleY += aY; + } + } else { + targetAngleX += item->extraL->head.angle.x + item->extraL->torso.angle.x; + targetAngleY += item->extraL->head.angle.y + item->extraL->torso.angle.y; + } + + targetDist = CAM_DIST_COMBAT; + + Location best = getBestLocation(item, true); + + move(best, speed); } - void updateLook() + void updateLook(Item* item) { - // + ASSERT(item->type == ITEM_LARA); + + targetAngleX = item->extraL->head.angle.x + item->extraL->torso.angle.x + item->angle.x; + targetAngleY = item->extraL->head.angle.y + item->extraL->torso.angle.y + item->angle.y; + targetDist = lookAtItem ? CAM_DIST_FOLLOW : CAM_DIST_LOOK; + + Location best = getBestLocation(item, true); + + move(best, speed); } void updateFixed() @@ -323,6 +275,40 @@ struct Camera } } + void lookAt(int32 offset) + { + int32 dx = lookAtItem->pos.x - laraItem->pos.x; + int32 dz = lookAtItem->pos.z - laraItem->pos.z; + + int16 ay = int16(phd_atan(dz, dx) - laraItem->angle.y) >> 1; + + if (abs(ay) >= LARA_LOOK_ANGLE_Y) + { + lookAtItem = NULL; + return; + } + + const Bounds& box = lookAtItem->getBoundingBox(true); + + offset -= lookAtItem->pos.y + ((box.minY + box.maxY) >> 1); + + int16 ax = int16(phd_atan(phd_sqrt(X_SQR(dx) + X_SQR(dz)), offset)) >> 1; + + if (ax < LARA_LOOK_ANGLE_MIN || ax > LARA_LOOK_ANGLE_MAX) + { + lookAtItem = NULL; + return; + } + + laraItem->extraL->head.angle.x = angleLerp(laraItem->extraL->head.angle.x, ax, LARA_LOOK_TURN_SPEED); + laraItem->extraL->head.angle.y = angleLerp(laraItem->extraL->head.angle.y, ay, LARA_LOOK_TURN_SPEED); + + laraItem->extraL->torso.angle = laraItem->extraL->head.angle; + + lookAtItem->flags.animated = true; // use as once flag + mode = CAMERA_MODE_LOOK; + } + void update() { if (keys & IK_START) @@ -345,7 +331,6 @@ struct Camera updateFree(); prepareFrustum(); matrixSetView(view.pos, angleX, angleY); - updateRoom(); return; } @@ -361,8 +346,10 @@ struct Camera ASSERT(item); target.room = item->room; + target.pos.x = item->pos.x; + target.pos.z = item->pos.z; - const Bounds &box = item->getBoundingBox(); + const Bounds &box = item->getBoundingBox(true); int32 y = item->pos.y; if (isFixed) { @@ -371,6 +358,10 @@ struct Camera y += box.maxY + ((box.minY - box.maxY) * 3 >> 2); } + if (!isFixed && lookAtItem) { + lookAt(y); + } + if (mode == CAMERA_MODE_LOOK || mode == CAMERA_MODE_COMBAT) { y -= 256; @@ -382,9 +373,8 @@ struct Camera 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) { @@ -406,8 +396,8 @@ struct Camera switch (mode) { case CAMERA_MODE_FOLLOW : updateFollow(item); break; - case CAMERA_MODE_COMBAT : updateCombat(); break; - case CAMERA_MODE_LOOK : updateLook(); break; + case CAMERA_MODE_COMBAT : updateCombat(item); break; + case CAMERA_MODE_LOOK : updateLook(item); break; default : updateFixed(); } @@ -432,15 +422,12 @@ struct Camera prepareFrustum(); matrixSetView(view.pos, angleX, angleY); - - updateRoom(); } void prepareFrustum() { matrixSetIdentity(); - matrixRotateY(angleY); - matrixRotateX(angleX); + matrixRotateYXZ(angleX, angleY, 0); static const vec3i v[5] = { // near plane @@ -492,8 +479,20 @@ struct Camera frustumAABB.minZ = frustumBase.minZ - offsetZ; frustumAABB.maxZ = frustumBase.maxZ - offsetZ; } + + void toCombat() + { + if (mode == CAMERA_MODE_CUTSCENE) + return; + + if (mode == CAMERA_MODE_LOOK) + return; + + mode = CAMERA_MODE_COMBAT; + } }; -Camera camera; +Camera* gCamera; +Camera viewCameras[MAX_PLAYERS]; #endif diff --git a/src/platform/gba/collision.h b/src/platform/gba/collision.h deleted file mode 100644 index d04b85f0..00000000 --- a/src/platform/gba/collision.h +++ /dev/null @@ -1,358 +0,0 @@ -#ifndef H_COLLISION -#define H_COLLISION - -#include "common.h" - -enum CollisionType { - CT_NONE = 0, - CT_FRONT = (1 << 0), - CT_LEFT = (1 << 1), - CT_RIGHT = (1 << 2), - CT_CEILING = (1 << 3), - CT_FRONT_CEILING = (1 << 4), - CT_FLOOR_CEILING = (1 << 5), -}; - -struct CollisionInfo -{ - enum SideType { - ST_MIDDLE, - ST_FRONT, - ST_LEFT, - ST_RIGHT, - ST_MAX - }; - - struct Side - { - int32 floor; - int32 ceiling; - SlantType slantType; - }; - - const FloorData* trigger; - - Side m; - Side f; - Side l; - Side r; - - int32 radius; - - int32 gapPos; - int32 gapNeg; - int32 gapCeiling; - - vec3i offset; - vec3i pos; - - int16 angle; - uint16 quadrant; - - CollisionType type; - - int8 slantX; - int8 slantZ; - - bool enemyPush; - bool enemyHit; - bool staticHit; - bool stopOnSlant; - bool stopOnLava; - - void setSide(SideType st, int32 floor, int32 ceiling) - { - SlantType slantType; - - if (gLastFloorSlant.slantX == 0 && gLastFloorSlant.slantZ == 0) { - slantType = SLANT_NONE; - } else if (abs(gLastFloorSlant.slantX) < 3 && abs(gLastFloorSlant.slantZ) < 3) { - slantType = SLANT_LOW; - } else { - slantType = SLANT_HIGH; - } - - if (st != ST_MIDDLE) { - if (stopOnSlant && floor < 0 && slantType == SLANT_HIGH) { - floor -= 0x7FFF; - } else if (stopOnSlant && floor > 0 && slantType == SLANT_HIGH) { - floor = 512; - }/* TODO lava else if (stopOnLava && floor > 0 && trigger && FloorData(*(uint16*)trigger).cmd.func == FloorData::LAVA) { - floor = 512; - }*/ - } - - Side *s = &m + st; - s->slantType = slantType; - s->floor = floor; - s->ceiling = ceiling; - } -}; - -CollisionInfo cinfo; - -int32 alignOffset(int32 a, int32 b) -{ - int32 ca = a / 1024; - int32 cb = b / 1024; - - if (ca == cb) { - return 0; - } - - a &= 1023; - - if (ca < cb) { - return 1025 - a; - } - - return -(a + 1); -} - -bool collideStatic(Room* room, CollisionInfo &cinfo, const vec3i &p, int32 height) -{ - cinfo.staticHit = false; - cinfo.offset = vec3i(0); - - Bounds objBox; - objBox.minX = -cinfo.radius; - objBox.maxX = cinfo.radius; - objBox.minZ = -cinfo.radius; - objBox.maxZ = cinfo.radius; - objBox.minY = -height; - objBox.maxY = 0; - - Room** nearRoom = room->getNearRooms(p, cinfo.radius, height); - - while (*nearRoom) - { - const Room* room = *nearRoom++; - - for (int i = 0; i < room->info->meshesCount; i++) - { - const RoomMesh* mesh = room->data.meshes + i; - - #ifdef NO_STATIC_MESH_PLANTS - if (mesh->id < 10) continue; - #endif - - const StaticMesh* staticMesh = staticMeshes + mesh->id; - - if (staticMesh->flags & STATIC_MESH_FLAG_NO_COLLISION) continue; - - Bounds meshBox = boxRotate(staticMesh->cbox, (mesh->rot - 2) * ANGLE_90); - - // TODO align RoomInfo::Mesh (room relative int16?) - vec3i pos; - pos.x = mesh->pos.x + (room->info->x << 8); - pos.y = mesh->pos.y; - pos.z = mesh->pos.z + (room->info->z << 8); - - pos -= p; - - boxTranslate(meshBox, pos); - - if (!boxIntersect(meshBox, objBox)) continue; - - cinfo.offset = boxPushOut(meshBox, objBox); - - bool flip = (cinfo.quadrant > 1); - - if (cinfo.quadrant & 1) { - if (abs(cinfo.offset.z) > cinfo.radius) { - cinfo.offset.z = cinfo.pos.z - p.z; - cinfo.type = CT_FRONT; - } else if (cinfo.offset.z != 0) { - cinfo.offset.x = 0; - cinfo.type = ((cinfo.offset.z > 0) ^ flip) ? CT_RIGHT : CT_LEFT; - } else { - cinfo.offset = vec3i(0); - } - } else { - if (abs(cinfo.offset.x) > cinfo.radius) { - cinfo.offset.x = cinfo.pos.x - p.x; - cinfo.type = CT_FRONT; - } else if (cinfo.offset.x != 0) { - cinfo.offset.z = 0; - cinfo.type = ((cinfo.offset.x > 0) ^ flip) ? CT_LEFT : CT_RIGHT; - } else { - cinfo.offset = vec3i(0); - } - } - - cinfo.staticHit = true; - - return true; - } - } - - return false; -} - -void collideRoom(Item* item, int32 height, int32 yOffset = 0) -{ - cinfo.type = CT_NONE; - cinfo.offset = vec3i(0, 0, 0); - - vec3i p = item->pos; - p.y += yOffset; - - int32 y = p.y - height; - - int32 cy = y - 160; - - int32 floor, ceiling; - - Room* room = item->room; - - #define CHECK_HEIGHT(v) {\ - 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;\ - ceiling = sector->getCeiling(v.x, cy, v.z);\ - if (ceiling != WALL) ceiling -= y;\ - } - -// middle - CHECK_HEIGHT(p); - - cinfo.trigger = gLastFloorData; - cinfo.slantX = gLastFloorSlant.slantX; - cinfo.slantZ = gLastFloorSlant.slantZ; - - cinfo.setSide(CollisionInfo::ST_MIDDLE, floor, ceiling); - - vec3i f, l, r; - int32 R = cinfo.radius; - - switch (cinfo.quadrant) { - case 0 : { - f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, R); - l = vec3i(-R, 0, R); - r = vec3i( R, 0, R); - break; - } - case 1 : { - f = vec3i( R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT); - l = vec3i( R, 0, R); - r = vec3i( R, 0, -R); - break; - } - case 2 : { - f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, -R); - l = vec3i( R, 0, -R); - r = vec3i(-R, 0, -R); - break; - } - case 3 : { - f = vec3i(-R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT); - l = vec3i(-R, 0, -R); - r = vec3i(-R, 0, R); - break; - } - default : { - f.x = f.y = f.z = 0; - l.x = l.y = l.z = 0; - r.x = r.y = r.z = 0; - ASSERT(false); - } - } - - f += p; - l += p; - r += p; - - vec3i delta; - delta.x = cinfo.pos.x - p.x; - delta.y = cinfo.pos.y - p.y; - delta.z = cinfo.pos.z - p.z; - -// front - CHECK_HEIGHT(f); - cinfo.setSide(CollisionInfo::ST_FRONT, floor, ceiling); - -// left - CHECK_HEIGHT(l); - cinfo.setSide(CollisionInfo::ST_LEFT, floor, ceiling); - -// right - CHECK_HEIGHT(r); - cinfo.setSide(CollisionInfo::ST_RIGHT, floor, ceiling); - -// static objects - collideStatic(item->room, cinfo, p, height); - -// check middle - if (cinfo.m.floor == WALL) - { - cinfo.offset = delta; - cinfo.type = CT_FRONT; - return; - } - - if (cinfo.m.floor <= cinfo.m.ceiling) - { - cinfo.offset = delta; - cinfo.type = CT_FLOOR_CEILING; - return; - } - - if (cinfo.m.ceiling >= 0) - { - cinfo.offset.y = cinfo.m.ceiling; - cinfo.type = CT_CEILING; - } - -// front - if (cinfo.f.floor > cinfo.gapPos || - cinfo.f.floor < cinfo.gapNeg || - cinfo.f.ceiling > cinfo.gapCeiling) - { - if (cinfo.quadrant & 1) - { - cinfo.offset.x = alignOffset(f.x, p.x); - cinfo.offset.z = delta.z; - } else { - cinfo.offset.x = delta.x; - cinfo.offset.z = alignOffset(f.z, p.z); - } - - cinfo.type = CT_FRONT; - return; - } - -// front ceiling - if (cinfo.f.ceiling >= cinfo.gapCeiling) - { - cinfo.offset = delta; - cinfo.type = CT_FRONT_CEILING; - return; - } - -// left - if (cinfo.l.floor > cinfo.gapPos || cinfo.l.floor < cinfo.gapNeg) - { - if (cinfo.quadrant & 1) { - cinfo.offset.z = alignOffset(l.z, f.z); - } else { - cinfo.offset.x = alignOffset(l.x, f.x); - } - cinfo.type = CT_LEFT; - return; - } - -// right - if (cinfo.r.floor > cinfo.gapPos || cinfo.r.floor < cinfo.gapNeg) - { - if (cinfo.quadrant & 1) { - cinfo.offset.z = alignOffset(r.z, f.z); - } else { - cinfo.offset.x = alignOffset(r.x, f.x); - } - cinfo.type = CT_RIGHT; - return; - } -} - -#endif diff --git a/src/platform/gba/common.cpp b/src/platform/gba/common.cpp index bd170baf..85482965 100644 --- a/src/platform/gba/common.cpp +++ b/src/platform/gba/common.cpp @@ -7,17 +7,25 @@ vec3i cameraViewPos; Matrix matrixStack[MAX_MATRICES]; int32 matrixStackIndex = 0; -EWRAM_DATA SaveGame gSaveGame; - const FloorData* gLastFloorData; FloorData gLastFloorSlant; -int32 rand_seed_ctrl; +EWRAM_DATA SaveGame gSaveGame; +EWRAM_DATA Settings gSettings; +EWRAM_DATA int32 gCurTrack; + +EWRAM_DATA ExtraInfoLara playersExtra[MAX_PLAYERS]; + +#ifdef PROFILING + uint32 gCounters[CNT_MAX]; +#endif + +int32 rand_seed_logic; int32 rand_seed_draw; -void set_seed_ctrl(int32 seed) +void set_seed_logic(int32 seed) { - rand_seed_ctrl = seed; + rand_seed_logic = seed; } void set_seed_draw(int32 seed) @@ -27,12 +35,12 @@ void set_seed_draw(int32 seed) #define X_RAND(seed) (((seed = 0x3039 + seed * 0x41C64E6D) >> 10) & 0x7FFF); -int16 rand_ctrl() +int32 rand_logic() { - return X_RAND(rand_seed_ctrl); + return X_RAND(rand_seed_logic); } -int16 rand_draw() +int32 rand_draw() { return X_RAND(rand_seed_draw); } @@ -622,7 +630,7 @@ uint32 phd_sqrt(uint32 x) y = z; } else { x -= y; - y = m | z; + y = z + m; } z = y >> 1; @@ -716,94 +724,73 @@ void initDivTable() } */ -void matrixTranslate(const vec3i &pos) +X_INLINE int16 lerpAngle(int16 a, int16 b, uint32 t) +{ + int32 d = b - a; + + if (d > 0x8000) { + d -= 0x10000; + } else if (d < -0x8000) { + d += 0x10000; + } + + return a + ((d * t) >> FIXED_SHIFT); +} + +void matrixTranslate(int32 x, int32 y, int32 z) { Matrix &m = matrixGet(); - m[0][3] += DP33(m[0], pos); - m[1][3] += DP33(m[1], pos); - m[2][3] += DP33(m[2], pos); + m[0][3] += DP33c(m[0], x, y, z); + m[1][3] += DP33c(m[1], x, y, z); + m[2][3] += DP33c(m[2], x, y, z); } -void matrixTranslateAbs(const vec3i &pos) +void matrixTranslateAbs(int32 x, int32 y, int32 z) { - vec3i d = pos - cameraViewPos; + x -= cameraViewPos.x; + y -= cameraViewPos.y; + z -= cameraViewPos.z; Matrix &m = matrixGet(); - m[0][3] = DP33(m[0], d); - m[1][3] = DP33(m[1], d); - m[2][3] = DP33(m[2], d); + m[0][3] = DP33c(m[0], x, y, z); + m[1][3] = DP33c(m[1], x, y, z); + m[2][3] = DP33c(m[2], x, y, z); } void matrixRotateX(int32 angle) { + Matrix &m = matrixGet(); + int32 s = phd_sin(angle); int32 c = phd_cos(angle); - Matrix &m = matrixGet(); - int32 a, b; - - a = c * m[0][1] + s * m[0][2]; - b = c * m[0][2] - s * m[0][1]; - m[0][1] = a >> FIXED_SHIFT; - m[0][2] = b >> FIXED_SHIFT; - - a = c * m[1][1] + s * m[1][2]; - b = c * m[1][2] - s * m[1][1]; - m[1][1] = a >> FIXED_SHIFT; - m[1][2] = b >> FIXED_SHIFT; - - a = c * m[2][1] + s * m[2][2]; - b = c * m[2][2] - s * m[2][1]; - m[2][1] = a >> FIXED_SHIFT; - m[2][2] = b >> FIXED_SHIFT; + X_ROTXY(m[0][2], m[0][1], s, c); + X_ROTXY(m[1][2], m[1][1], s, c); + X_ROTXY(m[2][2], m[2][1], s, c); } void matrixRotateY(int32 angle) { + Matrix &m = matrixGet(); + int32 s = phd_sin(angle); int32 c = phd_cos(angle); - Matrix &m = matrixGet(); - int32 a, b; - - a = c * m[0][0] - s * m[0][2]; - b = c * m[0][2] + s * m[0][0]; - m[0][0] = a >> FIXED_SHIFT; - m[0][2] = b >> FIXED_SHIFT; - - a = c * m[1][0] - s * m[1][2]; - b = c * m[1][2] + s * m[1][0]; - m[1][0] = a >> FIXED_SHIFT; - m[1][2] = b >> FIXED_SHIFT; - - a = c * m[2][0] - s * m[2][2]; - b = c * m[2][2] + s * m[2][0]; - m[2][0] = a >> FIXED_SHIFT; - m[2][2] = b >> FIXED_SHIFT; + X_ROTXY(m[0][0], m[0][2], s, c); + X_ROTXY(m[1][0], m[1][2], s, c); + X_ROTXY(m[2][0], m[2][2], s, c); } void matrixRotateZ(int32 angle) { + Matrix &m = matrixGet(); + int32 s = phd_sin(angle); int32 c = phd_cos(angle); - Matrix &m = matrixGet(); - int32 a, b; - - a = c * m[0][0] + s * m[0][1]; - b = c * m[0][1] - s * m[0][0]; - m[0][0] = a >> FIXED_SHIFT; - m[0][1] = b >> FIXED_SHIFT; - - a = c * m[1][0] + s * m[1][1]; - b = c * m[1][1] - s * m[1][0]; - m[1][0] = a >> FIXED_SHIFT; - m[1][1] = b >> FIXED_SHIFT; - - a = c * m[2][0] + s * m[2][1]; - b = c * m[2][1] - s * m[2][0]; - m[2][0] = a >> FIXED_SHIFT; - m[2][1] = b >> FIXED_SHIFT; + X_ROTXY(m[0][1], m[0][0], s, c); + X_ROTXY(m[1][1], m[1][0], s, c); + X_ROTXY(m[2][1], m[2][0], s, c); } void matrixRotateYXZ(int32 angleX, int32 angleY, int32 angleZ) @@ -820,65 +807,87 @@ void matrixRotateZXY(int32 angleX, int32 angleY, int32 angleZ) if (angleY) matrixRotateY(angleY); } -void matrixFrame(const vec3i &pos, uint16* angles) +void matrixFrame(const vec3s &pos, const uint32* angles) { - int32 angleX = (angles[1] & 0x3FF0) << 2; - int32 angleY = (angles[1] & 0x000F) << 12 | (angles[0] & 0xFC00) >> 4; - int32 angleZ = (angles[0] & 0x03FF) << 6; + int16 aX, aY, aZ; + DECODE_ANGLES(angles, aX, aY, aZ); - matrixTranslate(pos); - matrixRotateYXZ(angleX, angleY, angleZ); + matrixTranslate(pos.x, pos.y, pos.z); + matrixRotateYXZ(aX, aY, aZ); } -#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) \ - lerp_func(a[0], b[0], mul, div); \ - lerp_func(a[1], b[1], mul, div); \ - lerp_func(a[2], b[2], mul, div); \ - lerp_func(a[3], b[3], mul, div); - +#ifdef IWRAM_MATRIX_LERP +void matrixLerp(const Matrix &n, int32 multiplier, int32 divider); +#else void matrixLerp(const Matrix &n, int32 multiplier, int32 divider) { Matrix &m = matrixGet(); 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); + LERP_MATRIX(LERP_1_2); + } else if (divider == 4) { + + if (multiplier == 1) { + LERP_MATRIX(LERP_1_4); + } else { + LERP_MATRIX(LERP_3_4); + } + + } else if (divider == 3) { + + if (multiplier == 1) { + LERP_MATRIX(LERP_1_3); + } else { + LERP_MATRIX(LERP_2_3); + } + + } else if (divider == 5) { + + switch (multiplier) + { + case 4 : LERP_MATRIX(LERP_4_5); break; + case 3 : LERP_MATRIX(LERP_3_5); break; + case 2 : LERP_MATRIX(LERP_2_5); break; + case 1 : LERP_MATRIX(LERP_1_5); break; + } + } else { - 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_MATRIX(LERP_SLOW); } +} +#endif - //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 matrixFrameLerp(const vec3s &pos, const uint32* anglesA, const uint32* anglesB, int32 delta, int32 rate) +{ + int16 aX, aY, aZ; + int16 bX, bY, bZ; + + DECODE_ANGLES(anglesA, aX, aY, aZ); + DECODE_ANGLES(anglesB, bX, bY, bZ); + + matrixTranslate(pos.x, pos.y, pos.z); + +#if 0 // TODO oh, damn Gimbal Lock! + if (aX != bX) aX = lerpAngle(aX, bX, t); + if (aY != bY) aY = lerpAngle(aY, bY, t); + if (aZ != bZ) aZ = lerpAngle(aZ, bZ, t); + matrixRotateYXZ(aX, aY, aZ); +#else + matrixPush(); + matrixRotateYXZ(bX, bY, bZ); + Matrix &m = matrixGet(); + matrixPop(); + + matrixRotateYXZ(aX, aY, aZ); + + matrixLerp(m, delta, rate); +#endif } void matrixSetIdentity() { Matrix &m = matrixGet(); -#ifdef ROTATE90_MODE - m[0][0] = 0; - m[0][1] = 0x4000; - m[0][2] = 0; - m[0][3] = 0; - - m[1][0] = -0x4000; - m[1][1] = 0; - m[1][2] = 0; - m[1][3] = 0; -#else m[0][0] = 0x4000; m[0][1] = 0; m[0][2] = 0; @@ -888,7 +897,6 @@ void matrixSetIdentity() m[1][1] = 0x4000; m[1][2] = 0; m[1][3] = 0; -#endif m[2][0] = 0; m[2][1] = 0; @@ -905,17 +913,6 @@ void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY) Matrix &m = matrixGet(); -#ifdef ROTATE90_MODE - m[0][0] = (sx * sy) >> FIXED_SHIFT; - m[0][1] = cx; - m[0][2] = (sx * cy) >> FIXED_SHIFT; - m[0][3] = 0; - - m[1][0] = -cy; - m[1][1] = 0; - m[1][2] = sy; - m[1][3] = 0; -#else m[0][0] = cy; m[0][1] = 0; m[0][2] = -sy; @@ -925,7 +922,6 @@ void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY) m[1][1] = cx; m[1][2] = (sx * cy) >> FIXED_SHIFT; m[1][3] = 0; -#endif m[2][0] = (cx * sy) >> FIXED_SHIFT; m[2][1] = -sx; @@ -934,3 +930,31 @@ void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY) cameraViewPos = pos; } + +void CollisionInfo::setSide(CollisionInfo::SideType st, int32 floor, int32 ceiling) +{ + SlantType slantType; + + if (gLastFloorSlant.slantX == 0 && gLastFloorSlant.slantZ == 0) { + slantType = SLANT_NONE; + } else if (abs(gLastFloorSlant.slantX) < 3 && abs(gLastFloorSlant.slantZ) < 3) { + slantType = SLANT_LOW; + } else { + slantType = SLANT_HIGH; + } + + if (st != ST_MIDDLE) { + if (stopOnSlant && floor < 0 && slantType == SLANT_HIGH) { + floor = -0x7FFF; + } else if (stopOnSlant && floor > 0 && slantType == SLANT_HIGH) { + floor = 512; + }/* TODO lava else if (stopOnLava && floor > 0 && trigger && FloorData(*(uint16*)trigger).cmd.func == FloorData::LAVA) { + floor = 512; + }*/ + } + + Side *s = &m + st; + s->slantType = slantType; + s->floor = floor; + s->ceiling = ceiling; +} \ No newline at end of file diff --git a/src/platform/gba/common.h b/src/platform/gba/common.h index e97724b3..32bc39dc 100644 --- a/src/platform/gba/common.h +++ b/src/platform/gba/common.h @@ -1,12 +1,15 @@ #ifndef H_COMMON #define H_COMMON -//#define TEST -//#define PROFILE -#ifdef PROFILE - #define STATIC_ITEMS +//#define PROFILING +#ifdef PROFILING +// #define STATIC_ITEMS +// #define PROFILE_FRAMETIME + #define PROFILE_SOUNDTIME #endif +#define IWRAM_MATRIX_LERP + #if defined(_WIN32) #define MODE4 //#define MODE5 @@ -25,7 +28,6 @@ #endif #if defined(MODE5) - #define ROTATE90_MODE //#define DEBUG_OVERDRAW #endif @@ -61,14 +63,8 @@ #define FRAME_HEIGHT 160 #elif defined(MODE5) #define VRAM_WIDTH 160 - - #ifdef ROTATE90_MODE - #define FRAME_WIDTH 160 - #define FRAME_HEIGHT 120 - #else - #define FRAME_WIDTH 120 - #define FRAME_HEIGHT 80 - #endif + #define FRAME_WIDTH 120 + #define FRAME_HEIGHT 80 #elif defined(MODE13) #define MODE_PAL #define VRAM_WIDTH 160 // in shorts (320 bytes) @@ -118,6 +114,8 @@ typedef int16 Index; #define EWRAM_CODE #define dmaCopy(src,dst,size) memcpy(dst,src,size) +#else + #define ARM_CODE __attribute__((target("arm"))) #endif #if defined(_WIN32) @@ -150,9 +148,47 @@ typedef int16 Index; // system int32 osGetSystemTimeMS(); -#ifdef PROFILE - #if defined(_WIN32) +#ifdef PROFILING + #define PROFILE_FRAME\ + CNT_UPDATE,\ + CNT_RENDER + + #define PROFILE_STAGES\ + CNT_TRANSFORM,\ + CNT_ADD,\ + CNT_FLUSH,\ + CNT_VERT,\ + CNT_POLY + + #define PROFILE_SOUND\ + CNT_SOUND + + #if defined(PROFILE_FRAMETIME) + enum ProfileCounterId { + PROFILE_FRAME, + CNT_MAX, + PROFILE_STAGES, + PROFILE_SOUND, + }; + #elif defined(PROFILE_SOUNDTIME) + enum ProfileCounterId { + PROFILE_SOUND, + CNT_MAX, + PROFILE_FRAME, + PROFILE_STAGES, + }; + #else + enum ProfileCounterId { + PROFILE_STAGES, + CNT_MAX, + PROFILE_FRAME, + PROFILE_SOUND, + }; + #endif + + extern uint32 gCounters[CNT_MAX]; + #if defined(_WIN32) extern LARGE_INTEGER g_timer; extern LARGE_INTEGER g_current; @@ -164,14 +200,13 @@ int32 osGetSystemTimeMS(); QueryPerformanceCounter(&g_current);\ value += (g_current.QuadPart - g_timer.QuadPart);\ } - #elif defined(__GBA__) - #ifdef TEST + #ifdef PROFILE_SOUNDTIME #define TIMER_FREQ_DIV 1 #else #define TIMER_FREQ_DIV 3 #endif - + #define PROFILE_START() {\ REG_TM2CNT_L = 0;\ REG_TM2CNT_H = (1 << 7) | TIMER_FREQ_DIV;\ @@ -186,15 +221,164 @@ int32 osGetSystemTimeMS(); #define PROFILE_START() #define PROFILE_STOP(value) #endif + + struct ProfileCounter + { + ProfileCounterId cnt; + + ProfileCounter(ProfileCounterId cnt) : cnt(cnt) { + if (cnt < CNT_MAX) { + PROFILE_START() + } + } + + ~ProfileCounter() { + if (cnt < CNT_MAX) { + PROFILE_STOP(gCounters[cnt]); + } + } + }; + + #define PROFILE(cnt) ProfileCounter profileCounter(cnt) + #define PROFILE_CLEAR() memset(gCounters, 0, sizeof(gCounters)); #else - #define PROFILE_START() - #define PROFILE_STOP(value) + #define PROFILE(cnt) + #define PROFILE_CLEAR() #endif #ifdef __TNS__ void paletteSet(unsigned short* palette); #endif +#define STATIC_MESH_GATE 10 + +#define STATIC_MESH_FLAG_NO_COLLISION 1 +#define STATIC_MESH_FLAG_VISIBLE 2 + +extern int32 fps; + +#define FIXED_SHIFT 14 + +#define SND_MAX_DIST (8 * 1024) +#define SND_CHANNELS 6 +#define SND_FIXED_SHIFT 8 +#define SND_VOL_SHIFT 6 +#define SND_PITCH_SHIFT 7 + +#if defined(_WIN32) + #define SND_SAMPLES 1024 + #define SND_OUTPUT_FREQ 22050 + #define SND_SAMPLE_FREQ 22050 + #define SND_ENCODE(x) ((x) + 128) + #define SND_DECODE(x) ((x) - 128) + #define SND_MIN -128 + #define SND_MAX 127 +#elif defined(__GBA__) + #define SND_SAMPLES 176 + #define SND_OUTPUT_FREQ 10512 + #define SND_SAMPLE_FREQ 22050 + #define SND_ENCODE(x) (x) + #define SND_DECODE(x) ((x) - 128) + + #ifdef USE_9BIT_SOUND + #define SND_MIN -256 + #define SND_MAX 255 + #else + #define SND_MIN -128 + #define SND_MAX 127 + #endif +#elif defined(__DOS__) + #define SND_SAMPLES 1024 + #define SND_OUTPUT_FREQ 11025 + #define SND_SAMPLE_FREQ 11025 + #define SND_ENCODE(x) ((x) + 128) + #define SND_DECODE(x) ((x) - 128) + #define SND_MIN -128 + #define SND_MAX 127 +#endif + +#define MAX_UPDATE_FRAMES 10 + +#define MAX_PLAYERS 1 // TODO 2 players for non-potato platforms +#define MAX_ENEMIES 8 +#define MAX_SPHERES 32 +#define MAX_MATRICES 8 +#define MAX_ROOMS 139 // LEVEL7A +#define MAX_ITEMS 256 +#define MAX_MODELS ITEM_MAX +#define MAX_MESHES 512 +#define MAX_STATIC_MESHES 50 +#define MAX_CAMERAS 16 +#define MAX_BOXES 1024 +#define MAX_VERTICES (4*1024) +#define MAX_TEXTURES 1536 +#define MAX_FACES 1024 +#define MAX_ROOM_LIST 16 +#define MAX_CAUSTICS 32 +#define MAX_RAND_TABLE 32 + +#define FOV_SHIFT 3 +#define FOG_SHIFT 1 +#define FOG_MAX (10 * 1024) +#define FOG_MIN (FOG_MAX - (8192 >> FOG_SHIFT)) +#define VIEW_MIN_F (32 << FIXED_SHIFT) +#define VIEW_MAX_F (FOG_MAX << FIXED_SHIFT) + +#define FRUSTUM_FAR_X 5 << 10 +#define FRUSTUM_FAR_Y 3 << 10 +#define FRUSTUM_FAR_Z 9 << 10 + +#define FACE_TRIANGLE 0x8000 +#define FACE_COLORED 0x4000 +#define FACE_CLIPPED 0x2000 +#define FACE_FLAT 0x1000 +#define FACE_SPRITE 0x0800 +#define FACE_SHADOW (FACE_COLORED | FACE_FLAT | FACE_SPRITE) +#define FACE_TEXTURE 0x07FF + +#define NOT_ENEMY -0x4000 // default hp for non enemies +#define NO_ROOM 0xFF +#define NO_MODEL 0xFF +#define NO_BOX 0xFFFF +#define NO_FLOOR -127 +#define WALL (NO_FLOOR * 256) + +#define ANGLE_0 0 +#define ANGLE_1 (0x10000 / 360) +#define ANGLE_45 (0x10000 / 8) // != 45 * ANGLE_1 !!! +#define ANGLE_90 (0x10000 / 4) // != 90 * ANGLE_1 !!! +#define ANGLE_180 -(0x10000 / 2) // INT16_MIN +#define ANGLE(x) ((x) * ANGLE_1) + +#define X_CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) +#define X_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define X_MAX(a,b) ((a) > (b) ? (a) : (b)) +#define X_SQR(x) ((x) * (x)) +#define X_COUNT(x) int32(sizeof(x) / sizeof(x[0])) + +#define X_ROTX(x,y,s,c) (((x) * (c) - (y) * (s)) >> FIXED_SHIFT) +#define X_ROTY(x,y,s,c) (((y) * (c) + (x) * (s)) >> FIXED_SHIFT) +#define X_ROTXY(x,y,s,c) {\ + int32 _x = X_ROTX(x,y,s,c);\ + int32 _y = X_ROTY(x,y,s,c);\ + x = _x;\ + y = _y;\ +} + +#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) + +#define DP43(a,b) DP43c(a, b.x, b.y, b.z) +#define DP33(a,b) DP33c(a, b.x, b.y, b.z) + + +#define DIV_TABLE_SIZE 1024 +#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x]) +#define FixedInvU(x) divTable[x] + +#define OT_SHIFT 4 +#define OT_SIZE ((VIEW_MAX_F >> (FIXED_SHIFT + OT_SHIFT)) + 1) + // system keys (keys) enum InputKey { IK_NONE = 0, @@ -223,7 +407,6 @@ enum { IN_LOOK = (1 << 9), }; - struct vec3s { int16 x, y, z; @@ -253,6 +436,8 @@ struct vec3i { 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; } + X_INLINE vec3i& operator *= (int32 s) { x *= s; y *= s; z *= s; return *this; } + X_INLINE vec3i& operator /= (int32 s) { x /= s; y /= s; z /= s; return *this; } }; struct vec4i { @@ -335,6 +520,11 @@ struct AABB { int32 maxZ; }; +struct Sphere { + vec3i center; + int32 radius; +}; + struct Room; struct RoomVertex { @@ -430,6 +620,8 @@ struct RoomInfo RoomData data; }; +struct CollisionInfo; + struct Room { Item* firstItem; const RoomInfo* info; @@ -450,6 +642,7 @@ struct Room { 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 collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height); bool checkPortal(const Portal* portal); Room** addVisibleRoom(Room** list); Room** addNearRoom(Room** list, int32 x, int32 y, int32 z); @@ -459,8 +652,8 @@ struct Room { }; struct Node { - uint32 flags; - vec3i pos; + uint16 flags; + vec3s pos; }; struct Model { @@ -473,6 +666,13 @@ struct Model { #define FILE_MODEL_SIZE (sizeof(Model) - 2) // -padding +struct Mesh { + vec3s center; + int16 radius; + uint16 flags; + // data... +}; + struct StaticMesh { uint16 id; uint16 meshIndex; @@ -739,7 +939,7 @@ struct Lara; E( INV_SCION ) \ E( EXPLOSION ) \ E( UNUSED_9 ) \ - E( WATER_SPLASH ) \ + E( SPLASH ) \ E( UNUSED_10 ) \ E( BUBBLE ) \ E( UNUSED_11 ) \ @@ -788,6 +988,164 @@ enum ItemType { #undef DECL_ENUM +struct Camera; + +struct Location { + Room* room; + vec3i pos; +}; + +struct Navigation +{ + struct Cell + { + int16 zone; + int16 weight; + int16 end; + int16 next; + }; + + Cell* cells[MAX_BOXES]; +}; + +enum WeaponState { + WEAPON_STATE_FREE, + WEAPON_STATE_BUSY, + WEAPON_STATE_DRAW, + WEAPON_STATE_HOLSTER, + WEAPON_STATE_READY +}; + +enum Weapon { + WEAPON_PISTOLS, + WEAPON_MAGNUMS, + WEAPON_UZIS, + WEAPON_SHOTGUN, + // WEAPON_DESERT_EAGLE, + // WEAPON_REVOLVER, + // WEAPON_M16 + // WEAPON_MP5 + // WEAPON_HK + // WEAPON_ROCKET + // WEAPON_GRENADE + // WEAPON_HARPOON + // WEAPON_CROSSBOW + // WEAPON_GRAPPLING + // WEAPON_FLARE + WEAPON_MAX +}; + +struct WeaponParams { + ItemType modelType; + ItemType animType; + uint16 damage; + uint16 spread; + uint16 range; + int16 height; + int16 soundId; + uint8 reloadTimer; + uint8 flashOffset; + uint8 flashTimer; + uint8 flashIntensity; + int16 aimX; + int16 aimY; + int16 armX; + int16 armMinY; + int16 armMaxY; +}; + +enum LaraArm { + LARA_ARM_R, + LARA_ARM_L, + LARA_ARM_MAX, +}; + +enum { + JOINT_HIPS = 0, + JOINT_LEG_L1, + JOINT_LEG_L2, + JOINT_LEG_L3, + JOINT_LEG_R1, + JOINT_LEG_R2, + JOINT_LEG_R3, + JOINT_TORSO, + JOINT_ARM_R1, + JOINT_ARM_R2, + JOINT_ARM_R3, + JOINT_ARM_L1, + JOINT_ARM_L2, + JOINT_ARM_L3, + JOINT_HEAD, + JOINT_MAX +}; + +struct ExtraInfoLara { + int16 swimTimer; + uint8 weaponState; + uint8 vSpeedHack; + + int16 moveAngle; + int16 hitFrame; + + int8 hitTimer; + int8 hitQuadrant; + + uint8 weapon; + uint8 goalWeapon; + + struct Head { + vec3s angle; + } head; + + struct Torso { + vec3s angle; + } torso; + + struct Arm { + vec3s angle; + vec3s angleAim; + + uint16 animIndex; + uint16 frameIndex; + + struct Flash { + int16 timer; + int16 angle; + int16 offset; + int16 intensity; + } flash; + + Item* target; + + bool aim; + bool useBasis; + }; + + Arm armR; + Arm armL; + + Camera* camera; + + uint16 meshes[JOINT_MAX]; + + uint16 ammo[WEAPON_MAX]; + + Navigation nav; +}; + +extern ExtraInfoLara playersExtra[MAX_PLAYERS]; + +struct Enemy; + +struct ExtraInfoEnemy { + int16 rotHead; + int16 rotNeck; + + Enemy* enemy; + + Navigation nav; +}; + struct Item { Room* room; @@ -796,7 +1154,7 @@ struct Item { union { struct { - 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 save:1, gravity:1, active:1, status:2, collision:1, dozy:1, animated:1, once:1, mask:5, reverse:1, shadow:1; // TODO }; uint16 value; } flags; @@ -804,7 +1162,11 @@ struct Item { int16 vSpeed; int16 hSpeed; - uint16 animIndex; + union { + uint16 animIndex; + int16 tick; // effects only + }; + uint16 frameIndex; uint8 state; @@ -818,18 +1180,20 @@ struct Item { int16 oxygen; }; - int16 moveAngle; + uint16 input; int16 turnSpeed; - uint16 input; uint8 type; uint8 intensity; - int16 roomFloor; - int16 vSpeedHack; - int16 swimTimer; - uint8 weaponState; - uint8 _reserved; + + int32 hitMask; + + union { + uint8* extra; + ExtraInfoLara* extraL; + ExtraInfoEnemy* extraE; + }; Item* nextItem; Item* nextActive; @@ -839,11 +1203,16 @@ struct Item { static Item* add(ItemType type, Room* room, const vec3i &pos, int32 angleY); void remove(); - void activate(); - void deactivate(); - AnimFrame* getFrame(const Model* model) const; - const Bounds& getBoundingBox() const; + void fxBubbles(Room *fxRoom, int32 fxJoint, const vec3i &fxOffset); + void fxRicochet(Room *fxRoom, const vec3i &fxPos, bool fxSound); + void fxBlood(const vec3i &fxPos, int16 fxAngleY, int16 fxSpeed); + void fxSmoke(const vec3i &fxPos); + void fxSplash(); + + int32 getFrames(const AnimFrame* &frameA, const AnimFrame* &frameB, int32 &animFrameRate) const; + const AnimFrame* getFrame() const; + const Bounds& getBoundingBox(bool lerp) const; void move(); const Anim* animSet(int32 newAnimIndex, bool resetState, int32 frameOffset = 0); const Anim* animChange(const Anim* anim); @@ -851,52 +1220,63 @@ struct Item { void animSkip(int32 stateBefore, int32 stateAfter, bool advance = false); void animProcess(bool movement = true); bool animIsEnd(int32 offset) const; + void animHit(int32 dirX, int32 dirZ, int32 hitTimer); bool moveTo(const vec3i &point, Item* item, bool lerp); void updateRoom(int32 offset = 0); vec3i getRelative(const vec3i &point) const; - int32 getWaterLevel(); - int32 getWaterDepth(); + int32 getWaterLevel() const; + int32 getWaterDepth() const; + int32 getBridgeFloor(int32 x, int32 z) const; + int32 getTrapDoorFloor(int32 x, int32 z) const; + int32 getDrawBridgeFloor(int32 x, int32 z) const; + void getItemFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int32* ceiling) const; + + vec3i getJoint(int32 jointIndex, const vec3i &offset) const; + int32 getSpheres(Sphere* spheres, bool flag) const; + + uint32 collideSpheres(Lara* lara) const; + bool collideBounds(Lara* lara, CollisionInfo* cinfo) const; + void collidePush(Lara* lara, CollisionInfo* cinfo, bool enemyHit) const; + void collideRoom(int32 height, int32 yOffset) const; + + uint32 updateHitMask(Lara* lara, CollisionInfo* cinfo); Item* init(Room* room); X_INLINE Item() {} Item(Room* room); + virtual void activate(); + virtual void deactivate(); + virtual void hit(int32 damage, const vec3i &point, int32 soundId); virtual void collide(Lara* lara, CollisionInfo* cinfo); virtual void update(); virtual void draw(); }; -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 -{ +struct SaveGame { struct TrackFlags { uint8 once:1, mask:5, :2; }; - uint8 secrets; - uint8 pickups; + uint32 time; + uint32 distance; + uint32 secrets; + uint32 pickups; + uint32 mediUsed; + uint32 ammoUsed; + uint32 kills; TrackFlags tracks[64]; }; +struct Settings { + struct Controls { + bool retarget; + } controls; +}; + union FloorData { // 2 bytes uint16 value; @@ -1037,7 +1417,7 @@ enum { SND_LANDING = 4, - SND_UNHOLSTER = 6, + SND_DRAW = 6, SND_HOLSTER = 7, SND_PISTOLS_SHOT = 8, SND_SHOTGUN_RELOAD = 9, @@ -1047,9 +1427,10 @@ enum { SND_HIT_WOLF = 20, SND_SCREAM = 30, - SND_HIT = 31, + SND_HIT = 27, + SND_DAMAGE = 31, - SND_WATER_SPLASH = 33, + SND_SPLASH = 33, SND_BUBBLE = 37, @@ -1089,9 +1470,9 @@ enum { SND_NATLA_SHOT = 123, - SND_HIT_SKATEBOY = 132, + SND_HIT_SKATER = 132, - SND_HIT_MUTANT = 142, + SND_HIT_ADAM = 142, SND_STOMP = 147, SND_LAVA = 149, @@ -1110,6 +1491,77 @@ enum { SND_WINSTON_TRAY = 347, }; +#define LARA_LOOK_ANGLE_MAX ANGLE(22) +#define LARA_LOOK_ANGLE_MIN ANGLE(-42) +#define LARA_LOOK_ANGLE_Y ANGLE(44) +#define LARA_LOOK_TURN_SPEED ANGLE(2) + +enum CollisionType { + CT_NONE = 0, + CT_FRONT = (1 << 0), + CT_LEFT = (1 << 1), + CT_RIGHT = (1 << 2), + CT_CEILING = (1 << 3), + CT_FRONT_CEILING = (1 << 4), + CT_FLOOR_CEILING = (1 << 5), +}; + +struct CollisionInfo +{ + enum SideType { + ST_MIDDLE, + ST_FRONT, + ST_LEFT, + ST_RIGHT, + ST_MAX + }; + + struct Side + { + int32 floor; + int32 ceiling; + SlantType slantType; + }; + + const FloorData* trigger; + + Side m; + Side f; + Side l; + Side r; + + int32 radius; + + int32 gapPos; + int32 gapNeg; + int32 gapCeiling; + + vec3i offset; + vec3i pos; + + int16 angle; + uint16 quadrant; + + CollisionType type; + + int8 slantX; + int8 slantZ; + + bool enemyPush; + bool enemyHit; + bool staticHit; + bool stopOnSlant; + bool stopOnLava; + + void setSide(SideType st, int32 floor, int32 ceiling); + + X_INLINE void setAngle(int16 value) + { + angle = value; + quadrant = uint16(value + ANGLE_45) / ANGLE_90; + } +}; + struct Box { int8 minZ, maxZ; int8 minX, maxX; @@ -1123,12 +1575,12 @@ struct Level { uint16 tilesCount; uint16 roomsCount; uint16 modelsCount; + uint16 meshesCount; uint16 staticMeshesCount; uint16 spriteSequencesCount; uint16 soundSourcesCount; uint16 boxesCount; uint16 texturesCount; - uint16 animTexDataSize; uint16 itemsCount; uint16 camerasCount; uint16 cameraFramesCount; @@ -1167,127 +1619,10 @@ struct Level { extern Level level; -#define STATIC_MESH_GATE 10 - -#define STATIC_MESH_FLAG_NO_COLLISION 1 -#define STATIC_MESH_FLAG_VISIBLE 2 - -extern int32 fps; - -#ifdef PROFILE - extern uint32 dbg_transform; - extern uint32 dbg_poly; - extern uint32 dbg_flush; - extern uint32 dbg_vert_count; - extern uint32 dbg_poly_count; -#endif - -#define FIXED_SHIFT 14 - -#define SND_MAX_DIST (8 * 1024) -#define SND_SHIFT 2 -#define SND_CHANNELS (1 << SND_SHIFT) -#define SND_FIXED_SHIFT 8 -#define SND_VOL_SHIFT 6 -#define SND_PITCH_SHIFT 7 - -#if defined(_WIN32) - #define SND_SAMPLES 1024 - #define SND_OUTPUT_FREQ 22050 - #define SND_SAMPLE_FREQ 22050 - #define SND_ENCODE(x) ((x) + 128) - #define SND_DECODE(x) ((x) - 128) - #define SND_MIN -128 - #define SND_MAX 127 -#elif defined(__GBA__) - #define SND_SAMPLES 176 - #define SND_OUTPUT_FREQ 10512 - #define SND_SAMPLE_FREQ 22050 - #define SND_ENCODE(x) (x) - #define SND_DECODE(x) (((x) - 128) << 1) - - #ifdef USE_9BIT_SOUND - #define SND_MIN -255 - #define SND_MAX 254 - #else - #define SND_MIN -128 - #define SND_MAX 127 - #endif -#elif defined(__DOS__) - #define SND_SAMPLES 1024 - #define SND_OUTPUT_FREQ 11025 - #define SND_SAMPLE_FREQ 11025 - #define SND_ENCODE(x) ((x) + 128) - #define SND_DECODE(x) ((x) - 128) - #define SND_MIN -128 - #define SND_MAX 127 -#endif - -#define MAX_UPDATE_FRAMES 10 - -#define MAX_MATRICES 8 -#define MAX_ITEMS 256 -#define MAX_MODELS ITEM_MAX -#define MAX_ROOMS 139 // LEVEL7A -#define MAX_STATIC_MESHES 50 -#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 -#define FOG_MAX (10 * 1024) -#define FOG_MIN (FOG_MAX - (8192 >> FOG_SHIFT)) -#define VIEW_MIN_F (32 << FIXED_SHIFT) -#define VIEW_MAX_F (FOG_MAX << FIXED_SHIFT) - -#define FRUSTUM_FAR_X 5 << 10 -#define FRUSTUM_FAR_Y 3 << 10 -#define FRUSTUM_FAR_Z 9 << 10 - -#define FACE_TRIANGLE 0x8000 -#define FACE_COLORED 0x4000 -#define FACE_CLIPPED 0x2000 -#define FACE_FLAT 0x1000 -#define FACE_SPRITE 0x0800 -#define FACE_SHADOW (FACE_COLORED | FACE_FLAT | FACE_SPRITE) -#define FACE_TEXTURE 0x07FF - -#define NO_ROOM 0xFF -#define NO_MODEL 0xFF -#define NO_BOX 0xFFFF -#define NO_FLOOR -127 -#define WALL (NO_FLOOR * 256) - -#define DEG2SHORT (0x10000 / 360) -#define ANGLE_0 0 -#define ANGLE_1 DEG2SHORT -#define ANGLE_45 0x2000 // != 45 * DEG2SHORT !!! -#define ANGLE_90 0x4000 -#define ANGLE_180 -0x8000 - -#define X_CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) -#define X_MIN(a,b) ((a) < (b) ? (a) : (b)) -#define X_MAX(a,b) ((a) > (b) ? (a) : (b)) -#define X_SQR(x) ((x) * (x)) -#define 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) - -#define DP43(a,b) DP43c(a, b.x, b.y, b.z) -#define DP33(a,b) DP33c(a, b.x, b.y, b.z) - - -#define DIV_TABLE_SIZE 1024 -#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x]) -#define FixedInvU(x) divTable[x] - -#define OT_SHIFT 4 -#define OT_SIZE ((VIEW_MAX_F >> (FIXED_SHIFT + OT_SHIFT)) + 1) +struct IMA_STATE { + int32 smp; + int32 idx; +}; /* #define PERSPECTIVE(x, y, z) {\ @@ -1312,14 +1647,6 @@ extern int32 fps; x = d * (x >> FIXED_SHIFT) >> 12;\ y = d * (y >> FIXED_SHIFT) >> 12;\ } -#elif defined(ROTATE90_MODE) - #define PERSPECTIVE(x, y, z) {\ - int32 dz = (z >> (FIXED_SHIFT + FOV_SHIFT - 1)) / 3;\ - if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\ - int32 d = FixedInvU(dz);\ - x = d * (x >> FIXED_SHIFT) >> 12;\ - y = d * (y >> FIXED_SHIFT) >> 13;\ - } #else #define PERSPECTIVE(x, y, z) {\ int32 dz = (z >> (FIXED_SHIFT + FOV_SHIFT - 1)) / 3;\ @@ -1342,10 +1669,18 @@ extern int32 matrixStackIndex; extern uint32 gVerticesCount; extern SaveGame gSaveGame; +extern Settings gSettings; +extern int32 gCurTrack; extern const FloorData* gLastFloorData; extern FloorData gLastFloorSlant; +extern Room rooms[MAX_ROOMS]; +extern Model models[MAX_MODELS]; +extern const Mesh* meshes[MAX_MESHES]; +extern StaticMesh staticMeshes[MAX_STATIC_MESHES]; +extern FixedCamera cameras[MAX_CAMERAS]; + // level data extern bool enableClipping; @@ -1356,10 +1691,10 @@ X_INLINE void swap(T &a, T &b) { b = tmp; } -void set_seed_ctrl(int32 seed); +void set_seed_logic(int32 seed); void set_seed_draw(int32 seed); -int16 rand_ctrl(); -int16 rand_draw(); +int32 rand_logic(); +int32 rand_draw(); int32 phd_sin(int32 x); int32 phd_cos(int32 x); @@ -1373,12 +1708,49 @@ X_INLINE int32 dot(const vec3i &a, const vec3i &b) void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY); +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) + Bounds boxRotate(const Bounds &box, int16 angle); void boxTranslate(Bounds &box, const vec3i &offset); bool boxIntersect(const Bounds &a, const Bounds &b); bool boxContains(const Bounds &a, const vec3i &p); vec3i boxPushOut(const Bounds &a, const Bounds &b); +#define DECODE_ANGLES(a,x,y,z)\ + x = (((uint16*)(a))[1] & 0x3FF0) << 2;\ + y = (((uint16*)(a))[1] & 0x000F) << 12 | (((uint16*)(a))[0] & 0xFC00) >> 4;\ + z = (((uint16*)(a))[0] & 0x03FF) << 6; + +#define LERP_1_2(a, b, mul, div) a = (b + a) >> 1 +#define LERP_1_3(a, b, mul, div) a = a + ((b - a) / 3) +#define LERP_2_3(a, b, mul, div) a = b - ((b - a) / 3) +#define LERP_1_4(a, b, mul, div) a = a + ((b - a) >> 2) +#define LERP_3_4(a, b, mul, div) a = b - ((b - a) >> 2) +#define LERP_1_5(a, b, mul, div) a = a + ((b - a) / 5) +#define LERP_2_5(a, b, mul, div) a = a + ((b - a) * 2 / 5) +#define LERP_3_5(a, b, mul, div) a = b - ((b - a) * 2 / 5) +#define LERP_4_5(a, b, mul, div) a = b - ((b - a) / 5) +#define LERP_SLOW(a, b, mul, div) a = a + ((b - a) * mul / div) + +#define LERP_ROW(lerp_func, a, b, mul, div) \ + lerp_func(a[0], b[0], mul, div); \ + lerp_func(a[1], b[1], mul, div); \ + lerp_func(a[2], b[2], mul, div); \ + lerp_func(a[3], b[3], mul, div); + +#define LERP_MATRIX(lerp_func) \ + LERP_ROW(lerp_func, m[0], n[0], multiplier, divider); \ + LERP_ROW(lerp_func, m[1], n[1], multiplier, divider); \ + LERP_ROW(lerp_func, m[2], n[2], multiplier, divider); + X_INLINE Matrix& matrixGet() { return matrixStack[matrixStackIndex]; @@ -1399,14 +1771,36 @@ X_INLINE void matrixPop() matrixStackIndex--; } -void matrixTranslate(const vec3i &pos); -void matrixTranslateAbs(const vec3i &pos); +X_INLINE void matrixSetBasis(Matrix &dst, const Matrix &src) +{ + dst[0].x = src[0].x; + dst[0].y = src[0].y; + dst[0].z = src[0].z; + + dst[1].x = src[1].x; + dst[1].y = src[1].y; + dst[1].z = src[1].z; + + dst[1].x = src[1].x; + dst[1].y = src[1].y; + dst[1].z = src[1].z; +} + +X_INLINE vec3i matrixGetDir(Matrix &m) +{ + return vec3i(m[2].x, m[2].y, m[2].z); +} + +void matrixTranslate(int32 x, int32 y, int32 z); +void matrixTranslateAbs(int32 x, int32 y, int32 z); void matrixRotateX(int32 angle); void matrixRotateY(int32 angle); void matrixRotateZ(int32 angle); void matrixRotateYXZ(int32 angleX, int32 angleY, int32 angleZ); void matrixRotateZXY(int32 angleX, int32 angleY, int32 angleZ); -void matrixFrame(const vec3i &pos, uint16* angles); +void matrixLerp(const Matrix &n, int32 multiplier, int32 divider); +void matrixFrame(const vec3s &pos, const uint32* angles); +void matrixFrameLerp(const vec3s &pos, const uint32* anglesA, const uint32* anglesB, int32 delta, int32 rate); void matrixSetIdentity(); void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY); @@ -1430,6 +1824,7 @@ void faceAddMesh(const Quad* rFaces, const Quad* crFaces, const Triangle* tFaces void flush(); void readLevel(const uint8 *data); +bool trace(const Location &from, Location &to); Lara* getLara(const vec3i &pos); diff --git a/src/platform/gba/data/TRACK_13.WAV b/src/platform/gba/data/TRACK_13.WAV deleted file mode 100644 index bf669f06..00000000 Binary files a/src/platform/gba/data/TRACK_13.WAV and /dev/null differ diff --git a/src/platform/gba/draw.h b/src/platform/gba/draw.h index a886f208..580c56e4 100644 --- a/src/platform/gba/draw.h +++ b/src/platform/gba/draw.h @@ -6,17 +6,21 @@ int32 lightAmbient; +int32 randTable[MAX_RAND_TABLE]; int32 caustics[MAX_CAUSTICS]; -int32 causticsRand[MAX_CAUSTICS]; int32 causticsFrame; void drawInit() { + for (int32 i = 0; i < MAX_RAND_TABLE; i++) + { + randTable[i] = (rand_draw() >> 5) - 511; + } + 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; } } @@ -55,8 +59,7 @@ void calcLightingDynamic(const Room* room, const vec3i &point) int32 lum = (intensity * att) / (dist + att) + lightAmbient; - if (lum > maxLum) - { + if (lum > maxLum) { maxLum = lum; } } @@ -102,10 +105,7 @@ void drawNumber(int32 number, int32 x, int32 y) void drawMesh(int16 meshIndex) { - int32 offset = level.meshOffsets[meshIndex]; - const uint8* ptr = level.meshData + offset; - - ptr += 2 * 5; // skip [cx, cy, cz, radius, flags] + const uint8* ptr = (uint8*)meshes[meshIndex] + sizeof(Mesh); int16 vCount = *(int16*)ptr; ptr += 2; const vec3s* vertices = (vec3s*)ptr; @@ -138,13 +138,15 @@ void drawMesh(int16 meshIndex) int32 startVertex = gVerticesCount; - PROFILE_START(); - transformMesh(vertices, vCount, vIntensity, vNormal); - PROFILE_STOP(dbg_transform); + { + PROFILE(CNT_TRANSFORM); + transformMesh(vertices, vCount, vIntensity, vNormal); + } - PROFILE_START(); - faceAddMesh(rFaces, crFaces, tFaces, ctFaces, rCount, crCount, tCount, ctCount, startVertex); - PROFILE_STOP(dbg_poly); + { + PROFILE(CNT_ADD); + faceAddMesh(rFaces, crFaces, tFaces, ctFaces, rCount, crCount, tCount, ctCount, startVertex); + } } void drawShadow(const Item* item, int32 size) @@ -157,7 +159,7 @@ void drawShadow(const Item* item, int32 size) enableClipping = true; - const Bounds& box = item->getBoundingBox(); + const Bounds& box = item->getBoundingBox(true); int32 x = (box.maxX + box.minX) >> 1; int32 z = (box.maxZ + box.minZ) >> 1; int32 sx = (box.maxX - box.minX) * size >> 10; @@ -170,7 +172,7 @@ void drawShadow(const Item* item, int32 size) int32 y = floor - item->pos.y; matrixPush(); - matrixTranslateAbs(item->pos); + matrixTranslateAbs(item->pos.x, item->pos.y, item->pos.z); matrixRotateY(item->angle.y); transform(x - sx, y, z + sz2, 4096); @@ -199,63 +201,379 @@ void drawShadow(const Item* item, int32 size) void drawSprite(const Item* item) { vec3i d = item->pos - cameraViewPos; - faceAddSprite(d.x, d.y, d.z, item->intensity << 5, models[item->type].start); + faceAddSprite(d.x, d.y, d.z, item->intensity << 5, models[item->type].start + item->frameIndex); +} + +void drawFlash(const ExtraInfoLara::Arm::Flash &flash) +{ + matrixPush(); + matrixTranslate(0, flash.offset, 55); + matrixRotateYXZ(-ANGLE_90, 0, flash.angle); + + int32 tmp = lightAmbient; + calcLightingStatic(flash.intensity); + + drawMesh(models[ITEM_MUZZLE_FLASH].start); + + lightAmbient = tmp; + + matrixPop(); } -void drawModel(const Item* item, uint16* meshOverrides) +void drawNodes(const Item* item, const AnimFrame* frameA) { const Model* model = models + item->type; + const Node* node = level.nodes + model->nodeIndex; + + const uint32* angles = (uint32*)(frameA->angles + 1); + + matrixFrame(frameA->pos, angles); + + drawMesh(model->start); + + for (int32 i = 1; i < model->count; i++) + { + if (node->flags & 1) matrixPop(); + if (node->flags & 2) matrixPush(); + + matrixFrame(node->pos, ++angles); + + drawMesh(model->start + i); + + node++; + } +} + +void drawNodesLerp(const Item* item, const AnimFrame* frameA, const AnimFrame* frameB, int32 frameDelta, int32 frameRate) +{ + if (frameDelta == 0) + { + drawNodes(item, frameA); + return; + } + + const Model* model = models + item->type; + const Node* node = level.nodes + model->nodeIndex; + + const uint32* anglesA = (uint32*)(frameA->angles + 1); + const uint32* anglesB = (uint32*)(frameB->angles + 1); + + int32 t = FixedInvU(frameRate) * frameDelta; + + vec3s posLerp; + posLerp.x = frameA->pos.x + ((frameB->pos.x - frameA->pos.x) * t >> 16); + posLerp.y = frameA->pos.y + ((frameB->pos.y - frameA->pos.y) * t >> 16); + posLerp.z = frameA->pos.z + ((frameB->pos.z - frameA->pos.z) * t >> 16); + + matrixFrameLerp(posLerp, anglesA, anglesB, frameDelta, frameRate); + + drawMesh(model->start); + + for (int32 i = 1; i < model->count; i++) + { + if (node->flags & 1) matrixPop(); + if (node->flags & 2) matrixPush(); + + matrixFrameLerp(node->pos, ++anglesA, ++anglesB, frameDelta, frameRate); + + drawMesh(model->start + i); + + node++; + } +} + +#define DEF_TORSO_ANGLE vec3s(1216, -832, -192) + +void drawLaraNodes(const Item* lara, const AnimFrame* frameA) +{ + const Model* model = models + lara->type; + const Node* node = level.nodes + model->nodeIndex; + const ExtraInfoLara* extraL = lara->extraL; + + const uint16* mesh = extraL->meshes; + + const uint32* anglesArm[LARA_ARM_MAX]; + const uint32* angles = anglesArm[LARA_ARM_R] = anglesArm[LARA_ARM_L] = (uint32*)(frameA->angles + 1); + int32 frameSize = (sizeof(AnimFrame) >> 1) + (model->count << 1); + + vec3s torsoAngle = extraL->torso.angle; + + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; - AnimFrame* frame = item->getFrame(model); - uint16* frameAngles = frame->angles + 1; + if (arm->animIndex) + { + const Anim* anim = level.anims + arm->animIndex; + const AnimFrame* frame = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + arm->frameIndex * frameSize); + anglesArm[i] = (uint32*)(frame->angles + 1); + + // additional torso animation for shotgun + if (extraL->weapon == WEAPON_SHOTGUN && i == LARA_ARM_R) + { + vec3s ang; + DECODE_ANGLES((uint32*)(frame->angles + 1) + JOINT_TORSO, ang.x, ang.y, ang.z); + torsoAngle += ang - DEF_TORSO_ANGLE; + } + } + } + + anglesArm[LARA_ARM_R] += JOINT_ARM_R1; + anglesArm[LARA_ARM_L] += JOINT_ARM_L1; - camera.updateFrustum(item->pos.x, item->pos.y, item->pos.z); + const Matrix& basis = matrixGet(); matrixPush(); - matrixTranslateAbs(item->pos); - matrixRotateYXZ(item->angle.x, item->angle.y, item->angle.z); + { // JOINT_HIPS + matrixFrame(frameA->pos, angles++); + drawMesh(*mesh++); - int32 intensity = item->intensity << 5; + for (int32 i = 0; i < 2; i++) // draw Left & Right legs + { + matrixPush(); + { // JOINT_LEG_1 + matrixFrame((node++)->pos, angles++); + drawMesh(*mesh++); + + { // JOINT_LEG_2 + matrixFrame((node++)->pos, angles++); + drawMesh(*mesh++); + + { // JOINT_LEG_3 + matrixFrame((node++)->pos, angles++); + drawMesh(*mesh++); + } + } + } + matrixPop(); + } - if (intensity == 0) { - vec3i point = item->getRelative(frame->box.getCenter()); - calcLightingDynamic(item->room, point); - } else { - calcLightingStatic(intensity); + { // JOINT_TORSO + matrixFrame((node++)->pos, angles++); + matrixRotateYXZ(torsoAngle.x, torsoAngle.y, torsoAngle.z); + drawMesh(*mesh++); + + for (int32 i = 0; i < LARA_ARM_MAX; i++) // draw Right & Left arms + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; + + matrixPush(); + // JOINT_ARM_1 + matrixTranslate(node->pos.x, node->pos.y, node->pos.z); + node++; + if (arm->useBasis) { // hands are rotated relative to the basis + matrixSetBasis(matrixGet(), basis); + } + matrixRotateYXZ(arm->angle.x, arm->angle.y, arm->angle.z); + matrixFrame(vec3s(0, 0, 0), anglesArm[i]++); + drawMesh(*mesh++); + + { // JOINT_ARM_2 + matrixFrame((node++)->pos, anglesArm[i]++); + drawMesh(*mesh++); + + { // JOINT_ARM_3 + matrixFrame((node++)->pos, anglesArm[i]); + drawMesh(*mesh++); + + if (arm->flash.timer) { // muzzle flash + drawFlash(arm->flash); + } + } + } + matrixPop(); + } + + { // JOINT_HEAD + matrixFrame((node++)->pos, angles + 3 * LARA_ARM_MAX); + matrixRotateYXZ(extraL->head.angle.x, extraL->head.angle.y, extraL->head.angle.z); + drawMesh(*mesh++); + } + } } + matrixPop(); +} - int32 vis = boxIsVisible(&frame->box); - if (vis != 0) +void drawLaraNodesLerp(const Item* lara, const AnimFrame* frameA, const AnimFrame* frameB, int32 frameDelta, int32 frameRate) +{ + if (frameDelta == 0) { - enableClipping = vis < 0; + drawLaraNodes(lara, frameA); + return; + } + + const Model* model = models + lara->type; + const Node* node = level.nodes + model->nodeIndex; + const ExtraInfoLara* extraL = lara->extraL; + + const uint16* mesh = extraL->meshes; + + const uint32* anglesArmA[LARA_ARM_MAX]; + const uint32* anglesArmB[LARA_ARM_MAX]; + const uint32* anglesA = anglesArmA[LARA_ARM_R] = anglesArmA[LARA_ARM_L] = (uint32*)(frameA->angles + 1); + const uint32* anglesB = anglesArmB[LARA_ARM_R] = anglesArmB[LARA_ARM_L] = (uint32*)(frameB->angles + 1); + int32 frameRateArm[2]; + frameRateArm[0] = frameRateArm[1] = frameRate; + + int32 frameSize = (sizeof(AnimFrame) >> 1) + (model->count << 1); + + vec3s torsoAngle = extraL->torso.angle; - if (item->type == ITEM_BAT || - item->type == ITEM_TRAP_FLOOR) // some objects have the wrong AABB // TODO preprocess + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; + + if (arm->animIndex) { - enableClipping = true; + const Anim* anim = level.anims + arm->animIndex; + const AnimFrame* frame = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + arm->frameIndex * frameSize); + anglesArmA[i] = anglesArmB[i] = (uint32*)(frame->angles + 1); // no lerp for armed hands (frameRate == 1) + ASSERT(anim->frameRate == 1); + frameRateArm[i] = anim->frameRate; + + // additional torso animation for shotgun + if (extraL->weapon == WEAPON_SHOTGUN && i == LARA_ARM_R) + { + vec3s ang; + DECODE_ANGLES((uint32*)(frame->angles + 1) + JOINT_TORSO, ang.x, ang.y, ang.z); + torsoAngle += ang - DEF_TORSO_ANGLE; + } } + } - // skip rooms portal clipping for objects - Rect oldViewport = viewport; - viewport = Rect( 0, 0, FRAME_WIDTH, FRAME_HEIGHT ); + anglesArmA[LARA_ARM_R] += JOINT_ARM_R1; + anglesArmB[LARA_ARM_R] += JOINT_ARM_R1; + anglesArmA[LARA_ARM_L] += JOINT_ARM_L1; + anglesArmB[LARA_ARM_L] += JOINT_ARM_L1; - const Node* node = level.nodes + model->nodeIndex; + const Matrix& basis = matrixGet(); - matrixFrame(frame->pos, frameAngles); + matrixPush(); + { // JOINT_HIPS + int32 t = FixedInvU(frameRate) * frameDelta; - drawMesh(meshOverrides ? meshOverrides[0] : model->start); + vec3s posLerp; + posLerp.x = frameA->pos.x + ((frameB->pos.x - frameA->pos.x) * t >> 16); + posLerp.y = frameA->pos.y + ((frameB->pos.y - frameA->pos.y) * t >> 16); + posLerp.z = frameA->pos.z + ((frameB->pos.z - frameA->pos.z) * t >> 16); - for (int32 i = 1; i < model->count; i++) + matrixFrameLerp(posLerp, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + + for (int32 i = 0; i < 2; i++) // draw Left & Right legs { - if (node->flags & 1) matrixPop(); - if (node->flags & 2) matrixPush(); + matrixPush(); + { // JOINT_LEG_1 + matrixFrameLerp((node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + + { // JOINT_LEG_2 + matrixFrameLerp((node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + + { // JOINT_LEG_3 + matrixFrameLerp((node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + } + } + } + matrixPop(); + } + + { // JOINT_TORSO + matrixFrameLerp((node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + matrixRotateYXZ(torsoAngle.x, torsoAngle.y, torsoAngle.z); + drawMesh(*mesh++); + + for (int32 i = 0; i < LARA_ARM_MAX; i++) // draw Right & Left arms + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; + + matrixPush(); + // JOINT_ARM_1 + matrixTranslate(node->pos.x, node->pos.y, node->pos.z); + node++; + if (arm->useBasis) { // hands are rotated relative to the basis + matrixSetBasis(matrixGet(), basis); + } + matrixRotateYXZ(arm->angle.x, arm->angle.y, arm->angle.z); + + bool useLerp = frameRateArm[i] > 1; // armed hands always use frameRate == 1 (i.e. useLerp == false) + + if (useLerp) { + matrixFrameLerp(vec3s(0, 0, 0), anglesArmA[i]++, anglesArmB[i]++, frameDelta, frameRate); + } else { + matrixFrame(vec3s(0, 0, 0), anglesArmA[i]++); + } + drawMesh(*mesh++); + + { // JOINT_ARM_2 + if (useLerp) { + matrixFrameLerp((node++)->pos, anglesArmA[i]++, anglesArmB[i]++, frameDelta, frameRate); + } else { + matrixFrame((node++)->pos, anglesArmA[i]++); + } + drawMesh(*mesh++); + + { // JOINT_ARM_3 + if (useLerp) { + matrixFrameLerp((node++)->pos, anglesArmA[i], anglesArmB[i], frameDelta, frameRate); + } else { + matrixFrame((node++)->pos, anglesArmA[i]); + } + drawMesh(*mesh++); + + if (arm->flash.timer) { // muzzle flash + drawFlash(arm->flash); + } + } + } + matrixPop(); + } + + { // JOINT_HEAD + matrixFrameLerp((node++)->pos, anglesA + 3 * LARA_ARM_MAX, anglesB + 3 * LARA_ARM_MAX, frameDelta, frameRate); + matrixRotateYXZ(extraL->head.angle.x, extraL->head.angle.y, extraL->head.angle.z); + drawMesh(*mesh++); + } + } + } + matrixPop(); +} - frameAngles += 2; - matrixFrame(node->pos, frameAngles); +void drawModel(const Item* item) +{ + const AnimFrame *frameA, *frameB; + + int32 frameRate; + int32 frameDelta = item->getFrames(frameA, frameB, frameRate); // TODO lerp + + matrixPush(); + matrixTranslateAbs(item->pos.x, item->pos.y, item->pos.z); + matrixRotateYXZ(item->angle.x, item->angle.y, item->angle.z); - drawMesh(meshOverrides ? meshOverrides[i] : (model->start + i)); + int32 vis = boxIsVisible(&frameA->box); + if (vis != 0) + { + enableClipping = vis < 0; - node++; + int32 intensity = item->intensity << 5; + + if (intensity == 0) { + vec3i point = item->getRelative(frameA->box.getCenter()); + calcLightingDynamic(item->room, point); + } else { + calcLightingStatic(intensity); + } + + // skip rooms portal clipping for objects + Rect oldViewport = viewport; + viewport = Rect( 0, 0, FRAME_WIDTH, FRAME_HEIGHT ); + + if (item->type == ITEM_LARA) { + drawLaraNodesLerp(item, frameA, frameB, frameDelta, frameRate); + } else { + drawNodesLerp(item, frameA, frameB, frameDelta, frameRate); } viewport = oldViewport; @@ -264,8 +582,7 @@ void drawModel(const Item* item, uint16* meshOverrides) matrixPop(); // shadow - if (vis != 0 && item->flags.shadow) - { + if (vis != 0 && item->flags.shadow) { drawShadow(item, 160); // TODO per item shadow size } } @@ -273,7 +590,7 @@ void drawModel(const Item* item, uint16* meshOverrides) void drawItem(const Item* item) { if (models[item->type].count > 0) { - drawModel(item, NULL); + drawModel(item); } else { drawSprite(item); } @@ -289,19 +606,21 @@ void drawRoom(const Room* room) const RoomData& data = room->data; matrixPush(); - matrixTranslateAbs(vec3i(info->x << 8, 0, info->z << 8)); + matrixTranslateAbs(info->x << 8, 0, info->z << 8); - camera.updateFrustum(info->x << 8, 0, info->z << 8); + gCamera->updateFrustum(info->x << 8, 0, info->z << 8); enableClipping = true; - PROFILE_START(); - transformRoom(data.vertices, info->verticesCount, info->flags.water); - PROFILE_STOP(dbg_transform); + { + PROFILE(CNT_TRANSFORM); + transformRoom(data.vertices, info->verticesCount, info->flags.water); + } - PROFILE_START(); - faceAddRoom(data.quads, info->quadsCount, data.triangles, info->trianglesCount, startVertex); - PROFILE_STOP(dbg_poly); + { + PROFILE(CNT_ADD); + faceAddRoom(data.quads, info->quadsCount, data.triangles, info->trianglesCount, startVertex); + } for (int32 i = 0; i < info->spritesCount; i++) { @@ -328,11 +647,9 @@ void drawRoom(const Room* room) pos.y = mesh->pos.y; pos.z = mesh->pos.z + (info->z << 8); - camera.updateFrustum(pos.x, pos.y, pos.z); - matrixPush(); - matrixTranslateAbs(pos); - matrixRotateY((mesh->rot - 2) * 0x4000); + matrixTranslateAbs(pos.x, pos.y, pos.z); + matrixRotateY((mesh->rot - 2) * ANGLE_90); int32 vis = boxIsVisible(&staticMesh->vbox); if (vis != 0) { @@ -357,9 +674,9 @@ void drawRoom(const Room* room) void drawRooms() { - camera.view.room->clip = Rect( 0, 0, FRAME_WIDTH, FRAME_HEIGHT ); + gCamera->view.room->clip = Rect( 0, 0, FRAME_WIDTH, FRAME_HEIGHT ); - Room** visRoom = camera.view.room->getVisibleRooms(); + Room** visRoom = gCamera->view.room->getVisibleRooms(); while (*visRoom) { diff --git a/src/platform/gba/enemy.h b/src/platform/gba/enemy.h index 7b13a1f8..244a9b46 100644 --- a/src/platform/gba/enemy.h +++ b/src/platform/gba/enemy.h @@ -3,23 +3,170 @@ #include "item.h" +EWRAM_DATA ExtraInfoEnemy enemiesExtra[MAX_ENEMIES]; + struct Enemy : Item { Enemy(Room* room, int32 hp) : Item(room) { flags.shadow = true; + + #ifndef STATIC_ITEMS + angle.y += (rand_logic() - 0x4000) >> 1; + #endif + health = hp; } + void setExtra(ExtraInfoEnemy* extra) + { + ASSERT(!extraE); + + extraE = extra; + + if (!extraE) + return; + + if (extra->enemy) + { + extra->enemy->flags.status = ITEM_FLAGS_STATUS_INVISIBLE; + extra->enemy->disable(); + } + + extra->enemy = this; + extra->rotHead = extra->rotNeck = 0; + + // TODO initialize enemy and navigation + } + + bool enable(bool forced) + { + //return false; + + if (extraE) + return true; + + for (int32 i = 0; i < MAX_ENEMIES; i++) + { + if (!enemiesExtra[i].enemy) + { + setExtra(enemiesExtra + i); + return true; + } + } + + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (!players[i]) + continue; + + vec3i &viewPos = players[i]->extraL->camera->view.pos; + + int32 index = -1; + int32 maxDistQ = 0; + + if (!forced) + { + vec3i d = pos - viewPos; + maxDistQ = X_SQR(d.x >> 8) + X_SQR(d.y >> 8) + X_SQR(d.z >> 8); + } + + for (int32 j = 0; j < MAX_ENEMIES; j++) + { + vec3i d = enemiesExtra[j].enemy->pos - viewPos; + int32 distQ = X_SQR(d.x >> 8) + X_SQR(d.y >> 8) + X_SQR(d.z >> 8); + if (distQ > maxDistQ) + { + maxDistQ = distQ; + index = j; + } + } + + if (index != -1) + { + setExtra(enemiesExtra + index); + return true; + } + } + + return false; + } + + void disable() + { + ASSERT(extraE); + extraE->enemy = NULL; + extraE = NULL; + } + + virtual void activate() + { + Item::activate(); + + if (!enable(flags.status == ITEM_FLAGS_STATUS_NONE)) { + flags.status = ITEM_FLAGS_STATUS_INVISIBLE; + } + } + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + if (health > 0) { + health -= damage; + + if (health <= 0) { + gSaveGame.kills++; + } + } + + if (health > 0) + { + if (type != ITEM_MUMMY) { + fxBlood(point, 0, 0); + } + + if (soundId) { + soundPlay(soundId, pos); + } + } + } + virtual void collide(Lara* lara, CollisionInfo* cinfo) { - // + if (health <= 0) // TODO T-Rex still collide after death + return; + + if (!extraE) // disabled + return; + + if (!updateHitMask(lara, cinfo)) + return; + + if (!cinfo->enemyPush) + return; + + int32 r = cinfo->radius; + cinfo->radius = 0; + collidePush(lara, cinfo, true); + cinfo->radius = r; } virtual void update() { + if (flags.status == ITEM_FLAGS_STATUS_INVISIBLE) + { + if (!enable(false)) + return; + flags.status = ITEM_FLAGS_STATUS_ACTIVE; + } + + if (extraE) { + logic(); + } + animProcess(); } + + virtual void logic() {} }; @@ -32,12 +179,22 @@ struct Doppelganger : Enemy struct Wolf : Enemy { Wolf(Room* room) : Enemy(room, 6) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_WOLF); + } }; struct Bear : Enemy { Bear(Room* room) : Enemy(room, 20) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_BEAR); + } }; @@ -64,6 +221,11 @@ struct Lion : Enemy case ITEM_PUMA : health = 40; break; } } + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, (type == ITEM_PUMA) ? 0 : SND_HIT_LION); + } }; @@ -76,6 +238,11 @@ struct Gorilla : Enemy struct Rat : Enemy { Rat(Room* room) : Enemy(room, 5) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_RAT); + } }; @@ -124,6 +291,11 @@ struct Pierre : Enemy struct Skater : Enemy { Skater(Room* room) : Enemy(room, 125) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_SKATER); + } }; @@ -148,6 +320,11 @@ struct Natla : Enemy struct Adam : Enemy { Adam(Room* room) : Enemy(room, 500) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_ADAM); + } }; #endif diff --git a/src/platform/gba/game.h b/src/platform/gba/game.h index 60eba650..4ec4c6ee 100644 --- a/src/platform/gba/game.h +++ b/src/platform/gba/game.h @@ -15,7 +15,9 @@ struct Game void init() { - set_seed_ctrl(osGetSystemTimeMS() * 3); + gSettings.controls.retarget = true; + + set_seed_logic(osGetSystemTimeMS() * 3); set_seed_draw(osGetSystemTimeMS() * 7); animTexFrame = 0; @@ -26,15 +28,32 @@ struct Game void loadLevel(const void* data) { drawFree(); + + memset(&gSaveGame, 0, sizeof(gSaveGame)); + + Item::sFirstActive = NULL; + Item::sFirstFree = NULL; + + gCurTrack = -1; + + dynSectorsCount = 0; + readLevel((uint8*)data); - // prepare rooms + // prepare rooms for (int32 i = 0; i < level.roomsCount; i++) { rooms[i].reset(); } - // prepare items + // prepare items free list + for (int32 i = MAX_ITEMS - 1; i >= level.itemsCount; i--) + { + items[i].nextItem = items + i + 1; + } + Item::sFirstFree = items + level.itemsCount; + + // init items for (int32 i = 0; i < level.itemsCount; i++) { const ItemInfo* info = level.itemsInfo + i; @@ -50,55 +69,59 @@ struct Game item->angle.y = (info->flags.angle - 2) * ANGLE_90; item->flags.value = info->flags.value; + if (item->type == ITEM_LARA) { + players[0] = (Lara*)item; + } + item->init(rooms + info->roomIndex); - if (item->type == ITEM_LARA) + if (item->type == ITEM_LARA || item->type == ITEM_CUT_1) { - camera.init(item); - camera.laraItem = item; + ASSERT(item->extraL); + + players[0] = (Lara*)item; + players[0]->extraL->camera = &viewCameras[0]; + players[0]->extraL->camera->init(item); - //#ifdef PROFILE // gym - //resetItem(item, 13, vec3i(38953, 3328, 63961), ANGLE_90 + ANGLE_45); // pool + //resetLara(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 + //resetLara(item, 0, vec3i(74588, 3072, 19673), ANGLE_0); // first darts + //resetLara(item, 9, vec3i(49669, 7680, 57891), ANGLE_0); // first door + //resetLara(item, 10, vec3i(43063, 7168, 61198), ANGLE_0); // transp + //resetLara(item, 14, vec3i(20215, 6656, 52942), ANGLE_90 + ANGLE_45); // bridge + //resetLara(item, 17, vec3i(16475, 6656, 59845), ANGLE_90); // bear + //resetLara(item, 26, vec3i(24475, 6912, 83505), ANGLE_90); // switch timer 1 + //resetLara(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 + //resetLara(item, 15, vec3i(66179, 0, 25920), -ANGLE_90 - ANGLE_45); // sprites + //resetLara(item, 19, vec3i(61018, 1024, 31214), ANGLE_180); // block + //resetLara(item, 14, vec3i(64026, 512, 20806), ANGLE_0); // key and puzzle + //resetLara(item, 5, vec3i(55644, 0, 29155), -ANGLE_90); // keyhole + //resetLara(item, 71, vec3i(12705, -768, 30195), -ANGLE_90); // puzzle + //resetLara(item, 63, vec3i(31055, -2048, 33406), ANGLE_0); // right room + //resetLara(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; - camera.view.room = item->room; - - players[0] = (Lara*)item; + //resetLara(item, 44, vec3i(73798, 2304, 9819), ANGLE_90); // uw gears } } drawInit(); } - void resetItem(Item* item, int32 roomIndex, const vec3i &pos, int32 angleY) + void resetLara(Item* lara, int32 roomIndex, const vec3i &pos, int32 angleY) { - item->room->remove(item); + lara->room->remove(lara); - item->pos = pos; - item->angle.y = angleY; - item->health = LARA_MAX_HEALTH; + lara->pos = pos; + lara->angle.y = angleY; + lara->health = LARA_MAX_HEALTH; - rooms[roomIndex].add(item); + lara->extraL->camera->target.pos = lara->pos; + lara->extraL->camera->target.room = lara->room; + lara->extraL->camera->view = lara->extraL->camera->target; + + rooms[roomIndex].add(lara); } void updateItems() @@ -110,6 +133,13 @@ struct Game item->update(); item = next; } + + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (players[i]) { + players[i]->update(); + } + } } void nextFrame(int32 frames) @@ -126,6 +156,8 @@ struct Game void update(int32 frames) { + PROFILE(CNT_UPDATE); + if (frames > MAX_UPDATE_FRAMES) { frames = MAX_UPDATE_FRAMES; } @@ -133,45 +165,47 @@ struct Game for (int32 i = 0; i < frames; i++) { updateItems(); - camera.update(); } nextFrame(frames); if (keys & IK_SELECT) { - mixer.playMusic(TRACK_13_WAV); + musicPlay(4); } } void render() { - #ifdef ROTATE90_MODE - #define TEXT_POSX FRAME_HEIGHT - #else #define TEXT_POSX FRAME_WIDTH - #endif - clear(); + { + PROFILE(CNT_RENDER); + + clear(); - #ifdef PROFILE - dbg_transform = 0; - dbg_poly = 0; - dbg_flush = 0; - dbg_vert_count = 0; - dbg_poly_count = 0; - #endif + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + gCamera = players[0]->extraL->camera; + ASSERT(gCamera); + // TODO set viewports for coop + #ifndef PROFILE_SOUNDTIME + drawRooms(); + #endif + } - drawRooms(); + drawNumber(fps, TEXT_POSX, 16); + } - #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); + #ifdef PROFILING + for (int32 i = 0; i < CNT_MAX; i++) + { + drawNumber(gCounters[i], TEXT_POSX, 32 + i * 16); + } #endif - drawNumber(fps, TEXT_POSX, 16); + #ifndef PROFILE_SOUNDTIME + PROFILE_CLEAR(); + #endif } }; diff --git a/src/platform/gba/item.h b/src/platform/gba/item.h index 0f7bc86b..e82ab3f4 100644 --- a/src/platform/gba/item.h +++ b/src/platform/gba/item.h @@ -11,18 +11,30 @@ int32 curItemIndex; #define GRAVITY 6 -X_INLINE int16 angleLerp(int16 a, int16 b, int32 w) +int32 alignOffset(int32 a, int32 b) { - int16 d = b - a; - if (d > +w) return a + w; - if (d < -w) return a - w; - return b; -} + int32 ca = a >> 10; + int32 cb = b >> 10; + + if (ca == cb) { + return 0; + } -#define angleDec(angle, value) angleLerp(angle, 0, value) + a &= 1023; + + if (ca < cb) { + return 1025 - a; + } + + return -(a + 1); +} Mixer::Sample* soundPlay(int16 id, const vec3i &pos) { + // TODO gym + // 0 -> 200 + // 4 -> 204 + int16 a = level.soundMap[id]; if (a == -1) @@ -33,7 +45,7 @@ Mixer::Sample* soundPlay(int16 id, const vec3i &pos) if (b->chance && b->chance < rand_draw()) return NULL; - vec3i d = pos - camera.target.pos; + vec3i d = pos - viewCameras[0].target.pos; // TODO find nearest camera for coop if (abs(d.x) >= SND_MAX_DIST || abs(d.y) >= SND_MAX_DIST || abs(d.z) >= SND_MAX_DIST) return NULL; @@ -49,6 +61,8 @@ Mixer::Sample* soundPlay(int16 id, const vec3i &pos) if (volume <= 0) return NULL; + volume += 1; // 63 to 64 (1 << SND_VOL_SHIFT) for 100% vol samples + int32 pitch = 128; if (b->flags.pitch) { @@ -66,10 +80,6 @@ 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); } @@ -98,32 +108,106 @@ void soundStop(int16 id) void musicPlay(int32 track) { - if (track > 25 && track < 57) // gym tutorial - { - soundPlay(148 + track, camera.view.pos); // play embedded tracks + if (track == 13) { + gCurTrack = 0; } + + if (track == gCurTrack) + return; + + gCurTrack = track; + + struct TrackInfo { + int32 offset; + int32 size; + } *info = (TrackInfo*)((uint8*)TRACKS_IMA + track * 8); + + if (!info->size) + return; + + mixer.playMusic((uint8*)TRACKS_IMA + info->offset, info->size); } void musicStop() { - // TODO + mixer.stopMusic(); } -AnimFrame* Item::getFrame(const Model* model) const +int32 Item::getFrames(const AnimFrame* &frameA, const AnimFrame* &frameB, int32 &animFrameRate) const { const Anim* anim = level.anims + animIndex; - int32 frameSize = sizeof(AnimFrame) / 2 + model->count * 2; - int32 keyFrame = (frameIndex - anim->frameBegin) / anim->frameRate; // TODO fixed div? check the range + animFrameRate = anim->frameRate; + + int32 frameSize = (sizeof(AnimFrame) >> 1) + (models[type].count << 1); - return (AnimFrame*)(level.animFrames + anim->frameOffset / 2 + keyFrame * frameSize); + int32 frame = frameIndex - anim->frameBegin; + +// int32 d = FixedInvU(animFrameRate); +// int32 indexA = frame * d >> 16; + + int32 indexA = frame / animFrameRate; + int32 frameDelta = frame - indexA * animFrameRate; + int32 indexB = indexA + 1; + + if (indexB * animFrameRate >= anim->frameEnd) + { + indexB = indexA; + } + + frameA = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + indexA * frameSize); + frameB = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + indexB * frameSize); + + if (!frameDelta) + return 0; + + indexB *= animFrameRate; + if (indexB > anim->frameEnd) { + animFrameRate -= indexB - anim->frameEnd; + } + + return frameDelta; } -const Bounds& Item::getBoundingBox() const +const AnimFrame* Item::getFrame() const { - const Model* model = models + type; - AnimFrame* frame = getFrame(model); - return frame->box; + const AnimFrame *frameA, *frameB; + + int32 frameRate; + int32 frameDelta = getFrames(frameA, frameB, frameRate); + + return (frameDelta <= (frameRate >> 1)) ? frameA : frameB; +} + +Bounds tmpBox; + +const Bounds& Item::getBoundingBox(bool lerp) const +{ + if (!lerp) + return getFrame()->box; + + const AnimFrame *frameA, *frameB; + + int32 frameRate; + int32 frameDelta = getFrames(frameA, frameB, frameRate); + + if (!frameDelta) + return frameA->box; + + int32 d = FixedInvU(frameRate) * frameDelta; + + #define COMP_LERP(COMP) tmpBox.COMP = frameA->box.COMP + ((frameB->box.COMP - frameA->box.COMP) * d >> 16); + + COMP_LERP(minX); + COMP_LERP(maxX); + COMP_LERP(minY); + COMP_LERP(maxY); + COMP_LERP(minZ); + COMP_LERP(maxZ); + + #undef COMP_LERP + + return tmpBox; } void Item::move() @@ -148,7 +232,7 @@ void Item::move() hSpeed = s >> 16; } - int16 realAngle = (type == ITEM_LARA) ? moveAngle : angle.y; + int16 realAngle = (type == ITEM_LARA) ? extraL->moveAngle : angle.y; pos.x += phd_sin(realAngle) * hSpeed >> FIXED_SHIFT; pos.z += phd_cos(realAngle) * hSpeed >> FIXED_SHIFT; @@ -226,11 +310,10 @@ void Item::animCmd(bool fx, const Anim* anim) int32 s = phd_sin(angle.y); int32 c = phd_cos(angle.y); int32 x = ptr[0]; - int32 y = ptr[1]; int32 z = ptr[2]; - pos.x += (c * x + s * z) >> FIXED_SHIFT; - pos.y += y; - pos.z += (c * z - s * x) >> FIXED_SHIFT; + pos.x += X_ROTX(x, z, -s, c); + pos.y += ptr[1]; + pos.z += X_ROTY(x, z, -s, c); } ptr += 3; break; @@ -240,9 +323,9 @@ void Item::animCmd(bool fx, const Anim* anim) { if (!fx) { - if (vSpeedHack) { - vSpeed = vSpeedHack; - vSpeedHack = 0; + if (type == ITEM_LARA && extraL->vSpeedHack) { + vSpeed = -extraL->vSpeedHack; + extraL->vSpeedHack = 0; } else { vSpeed = ptr[0]; } @@ -256,7 +339,8 @@ void Item::animCmd(bool fx, const Anim* anim) case ANIM_CMD_EMPTY: { if (!fx) { - weaponState = WEAPON_STATE_FREE; + ASSERT(type == ITEM_LARA); + extraL->weaponState = WEAPON_STATE_FREE; } break; } @@ -283,12 +367,36 @@ void Item::animCmd(bool fx, const Anim* anim) if (fx && frameIndex == ptr[0]) { switch (ptr[1]) { - case FX_ROTATE_180 : angle.y += ANGLE_180; break; + case FX_ROTATE_180 : + { + angle.y += ANGLE_180; + break; + } + /* case FX_FLOOR_SHAKE : ASSERT(false); - case FX_LARA_NORMAL : animation.setAnim(ANIM_STAND); break; - case FX_LARA_BUBBLES : doBubbles(); break; - case FX_LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break; + */ + + case FX_LARA_NORMAL : + { + ASSERT(type == ITEM_LARA); + animSet(11, true); // Lara::ANIM_STAND + break; + } + + case FX_LARA_BUBBLES : + { + fxBubbles(room, JOINT_HEAD, vec3i(0, 0, 50)); + break; + } + + case FX_LARA_HANDSFREE : + { + ASSERT(type == ITEM_LARA && extraL); + extraL->weaponState = WEAPON_STATE_FREE; + break; + } + /* case FX_DRAW_RIGHTGUN : drawGun(true); break; case FX_DRAW_LEFTGUN : drawGun(false); break; case FX_SHOT_RIGHTGUN : game->addMuzzleFlash(this, LARA_RGUN_JOINT, LARA_RGUN_OFFSET, 1 + camera->cameraIndex); break; @@ -333,14 +441,10 @@ void Item::animSkip(int32 stateBefore, int32 stateAfter, bool advance) } #define ANIM_MOVE_LERP_POS (16) -#define ANIM_MOVE_LERP_ROT (2 * DEG2SHORT) +#define ANIM_MOVE_LERP_ROT ANGLE(2) 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; @@ -375,6 +479,15 @@ bool Item::animIsEnd(int32 offset) const return frameIndex == level.anims[animIndex].frameEnd - offset; } +void Item::animHit(int32 dirX, int32 dirZ, int32 hitTimer) +{ + ASSERT(type == ITEM_LARA); + ASSERT(extraL != NULL); + + extraL->hitQuadrant = uint16(angle.y - phd_atan(dirZ, dirX) + ANGLE_180 + ANGLE_45) / ANGLE_90; + extraL->hitTimer = hitTimer; +} + bool Item::moveTo(const vec3i &point, Item* item, bool lerp) { // lerp position @@ -430,13 +543,19 @@ void Item::remove() deactivate(); room->remove(this); + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (playersExtra[i].armR.target == this) playersExtra[i].armR.target = NULL; + if (playersExtra[i].armL.target == this) playersExtra[i].armL.target = NULL; + } + nextItem = Item::sFirstFree; Item::sFirstFree = this; } void Item::activate() { - //ASSERT(!flags.active) + ASSERT(!flags.active) flags.active = true; @@ -472,6 +591,82 @@ void Item::deactivate() } } +void Item::hit(int32 damage, const vec3i &point, int32 soundId) +{ + // +} + +void Item::fxBubbles(Room *fxRoom, int32 fxJoint, const vec3i &fxOffset) +{ + int32 count = rand_draw() % 3; + + if (!count) + return; + + vec3i fxPos = pos + getJoint(fxJoint, fxOffset); + + for (int32 i = 0; i < count; i++) + { + Item::add(ITEM_BUBBLE, fxRoom, fxPos, 0); + } +} + +void Item::fxRicochet(Room *fxRoom, const vec3i &fxPos, bool fxSound) +{ + Item* ricochet = Item::add(ITEM_RICOCHET, fxRoom, fxPos, 0); + + if (!ricochet) + return; + + ricochet->timer = 4; + ricochet->frameIndex = rand_draw() % (-models[ricochet->type].count); + + if (fxSound) { + soundPlay(SND_RICOCHET, ricochet->pos); + } +} + +void Item::fxBlood(const vec3i &fxPos, int16 fxAngleY, int16 fxSpeed) +{ + Item* blood = Item::add(ITEM_BLOOD, room, fxPos, fxAngleY); + + if (!blood) + return; + + blood->hSpeed = fxSpeed; + blood->timer = 4; + blood->flags.animated = true; +} + +void Item::fxSmoke(const vec3i &fxPos) +{ + Item* smoke = Item::add(ITEM_SMOKE, room, fxPos, 0); + + if (!smoke) + return; + + smoke->timer = 3; + smoke->flags.animated = true; +} + +void Item::fxSplash() +{ + vec3i fxPos = pos; + fxPos.y = getWaterLevel(); + + // TODO TR3+ + for (int32 i = 0; i < 10; i++) + { + Item* splash = Item::add(ITEM_SPLASH, room, fxPos, int16(rand_draw() - ANGLE_90) << 1); + + if (!splash) + return; + + splash->hSpeed = int16(rand_draw() >> 8); + splash->flags.animated = true; + } +} + void Item::updateRoom(int32 offset) { Room* nextRoom = room->getRoom(pos.x, pos.y + offset, pos.z); @@ -505,27 +700,254 @@ vec3i Item::getRelative(const vec3i &point) const return p; } -int32 Item::getWaterLevel() +int32 Item::getWaterLevel() const { const Sector* sector = room->getWaterSector(pos.x, pos.z); if (sector) { - return sector->ceiling * 256; + if (sector->roomAbove == NO_ROOM) { + return sector->getCeiling(pos.x, pos.y, pos.z); + } else { + return sector->ceiling << 8; + } } return WALL; } -int32 Item::getWaterDepth() +int32 Item::getWaterDepth() const { const Sector* sector = room->getWaterSector(pos.x, pos.z); - if (sector) { + if (sector) return sector->getFloor(pos.x, pos.y, pos.z) - (sector->ceiling * 256); + + return WALL; +} + +int32 Item::getBridgeFloor(int32 x, int32 z) const +{ + if (type == ITEM_BRIDGE_FLAT) + return pos.y; + + int32 h; + if (angle.y == ANGLE_0) { + h = 1024 - x; + } else if (angle.y == ANGLE_180) { + h = x; + } else if (angle.y == ANGLE_90) { + h = z; + } else { + h = 1024 - z; + } + + h &= 1023; + + return pos.y + ((type == ITEM_BRIDGE_TILT_1) ? (h >> 2) : (h >> 1)); +} + +int32 Item::getTrapDoorFloor(int32 x, int32 z) const +{ + int32 dx = (pos.x >> 10) - (x >> 10); + int32 dz = (pos.z >> 10) - (z >> 10); + + if (((dx == 0) && (dz == 0)) || + ((dx == 0) && (dz == 1) && (angle.y == ANGLE_0)) || + ((dx == 0) && (dz == -1) && (angle.y == ANGLE_180)) || + ((dx == 1) && (dz == 0) && (angle.y == ANGLE_90)) || + ((dx == -1) && (dz == 0) && (angle.y == -ANGLE_90))) + { + return pos.y; } return WALL; } +int32 Item::getDrawBridgeFloor(int32 x, int32 z) const +{ + int32 dx = (pos.x >> 10) - (x >> 10); + int32 dz = (pos.z >> 10) - (z >> 10); + + if (((dx == 0) && ((dz == -1) || (dz == -2)) && (angle.y == ANGLE_0)) || + ((dx == 0) && ((dz == 1) || (dz == 2)) && (angle.y == ANGLE_180)) || + ((dz == 0) && ((dx == -1) || (dz == -2)) && (angle.y == ANGLE_90)) || + ((dz == 0) && ((dx == 1) || (dz == 2)) && (angle.y == -ANGLE_90))) + { + return pos.y; + } + + return WALL; +} + +void Item::getItemFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int32* ceiling) const +{ + int32 h = WALL; + + switch (type) + { + case ITEM_TRAP_FLOOR: + { + if (state == 0 || state == 1) { + h = pos.y - 512; + } + break; + } + case ITEM_DRAWBRIDGE: + { + if (state == 1) { + h = getDrawBridgeFloor(x, z); + } + break; + } + case ITEM_BRIDGE_FLAT: + case ITEM_BRIDGE_TILT_1: + case ITEM_BRIDGE_TILT_2: + { + h = getBridgeFloor(x, z); + break; + } + case ITEM_TRAP_DOOR_1: + case ITEM_TRAP_DOOR_2: + { + if (state != 0) + return; + + h = getTrapDoorFloor(x, z); + + if ((floor && (h >= *floor)) || (ceiling && (h <= *ceiling))) + return; + } + } + + if (h == WALL) + return; + + if (floor && (y <= h)) + { + *floor = h; + } + + if (ceiling && (y > h)) + { + *ceiling = h + 256; + } +} + +vec3i Item::getJoint(int32 jointIndex, const vec3i &offset) const +{ + const Model* model = models + type; + + const AnimFrame* frame = getFrame(); + + const uint32* frameAngles = (uint32*)(frame->angles + 1); + + matrixPush(); + matrixSetIdentity(); + matrixRotateYXZ(angle.x, angle.y, angle.z); + + const Node* node = level.nodes + model->nodeIndex; + + matrixFrame(frame->pos, frameAngles); + + ASSERT(jointIndex < model->count); + + for (int32 i = 0; i < jointIndex; i++) + { + if (node->flags & 1) matrixPop(); + if (node->flags & 2) matrixPush(); + + matrixFrame(node->pos, ++frameAngles); + + // TODO extra rotations + + node++; + } + + matrixTranslate(offset.x, offset.y, offset.z); + + Matrix &m = matrixGet(); + vec3i result = vec3i(m[0].w >> FIXED_SHIFT, m[1].w >> FIXED_SHIFT, m[2].w >> FIXED_SHIFT); + + matrixPop(); + + return result; +} + +int32 Item::getSpheres(Sphere* spheres, bool flag) const +{ + const Model* model = models + type; + + const AnimFrame* frame = getFrame(); + const uint32* frameAngles = (uint32*)(frame->angles + 1); + + const Mesh** meshPtr = meshes + model->start; + + int32 x, y, z; + + if (flag) { + x = pos.x; + y = pos.y; + z = pos.z; + matrixPush(); + matrixSetIdentity(); + } else { + x = y = z = 0; + matrixPush(); + matrixTranslateAbs(pos.x, pos.y, pos.z); + } + + matrixRotateYXZ(angle.x, angle.y, angle.z); + + const Node* node = level.nodes + model->nodeIndex; + + matrixFrame(frame->pos, frameAngles); + + Sphere* sphere = spheres; + + matrixPush(); + { + const Mesh* mesh = *meshPtr; + matrixTranslate(mesh->center.x, mesh->center.y, mesh->center.z); + Matrix &m = matrixGet(); + sphere->center.x = x + (m[0].w >> FIXED_SHIFT); + sphere->center.y = y + (m[1].w >> FIXED_SHIFT); + sphere->center.z = z + (m[2].w >> FIXED_SHIFT); + sphere->radius = mesh->radius; + sphere++; + meshPtr++; + } + matrixPop(); + + for (int32 i = 1; i < model->count; i++) + { + if (node->flags & 1) matrixPop(); + if (node->flags & 2) matrixPush(); + + matrixFrame(node->pos, ++frameAngles); + + // TODO extra rotations + + matrixPush(); + { + const Mesh* mesh = *meshPtr; + matrixTranslate(mesh->center.x, mesh->center.y, mesh->center.z); + Matrix &m = matrixGet(); + sphere->center.x = x + (m[0].w >> FIXED_SHIFT); + sphere->center.y = y + (m[1].w >> FIXED_SHIFT); + sphere->center.z = z + (m[2].w >> FIXED_SHIFT); + sphere->radius = mesh->radius; + sphere++; + meshPtr++; + } + matrixPop(); + + node++; + } + + matrixPop(); + + return sphere - spheres; +} + #include "lara.h" #include "enemy.h" #include "object.h" @@ -543,13 +965,15 @@ Item::Item(Room* room) state = uint8(level.anims[animIndex].state); nextState = state; goalState = state; + extra = NULL; + health = NOT_ENEMY; flags.save = true; flags.gravity = false; flags.active = false; flags.status = ITEM_FLAGS_STATUS_NONE; flags.collision = true; - flags.custom = 0; + flags.animated = false; flags.shadow = false; if (flags.once) // once -> invisible @@ -588,6 +1012,317 @@ void Item::collide(Lara* lara, CollisionInfo* cinfo) // empty } +uint32 Item::collideSpheres(Lara* lara) const +{ + Sphere a[MAX_SPHERES]; + Sphere b[MAX_SPHERES]; + + int32 aCount = getSpheres(a, true); + int32 bCount = lara->getSpheres(b, true); + + uint32 mask = 0; + + for (int32 i = 0; i < aCount; i++) + { + if (a[i].radius <= 0) + continue; + + for (int32 j = 0; j < bCount; j++) + { + if (b[j].radius <= 0) + continue; + + vec3i d = b[j].center - a[i].center; + int32 r = b[j].radius + a[i].radius; + + if (X_SQR(d.x) + X_SQR(d.y) + X_SQR(d.z) < X_SQR(r)) + { + mask |= (1 << i); + } + } + } + + return mask; +} + +bool Item::collideBounds(Lara* lara, CollisionInfo* cinfo) const +{ + const Bounds &a = getBoundingBox(false); + const Bounds &b = lara->getBoundingBox(false); + + int32 dy = lara->pos.y - pos.y; + + if ((a.maxY - b.minY <= dy) || + (a.minY - b.maxY >= dy)) + return false; + + int32 dx = lara->pos.x - pos.x; + int32 dz = lara->pos.z - pos.z; + + int32 s = phd_sin(angle.y); + int32 c = phd_cos(angle.y); + + int32 px = X_ROTX(dx, dz, s, c); + int32 pz = X_ROTY(dx, dz, s, c); + + int32 r = cinfo->radius; + + return (px >= a.minX - r) && + (px <= a.maxX + r) && + (pz >= a.minZ - r) && + (pz <= a.maxZ + r); +} + +void Item::collidePush(Lara* lara, CollisionInfo* cinfo, bool enemyHit) const +{ + int32 dx = lara->pos.x - pos.x; + int32 dz = lara->pos.z - pos.z; + + int32 s = phd_sin(angle.y); + int32 c = phd_cos(angle.y); + + int32 px = X_ROTX(dx, dz, s, c); + int32 pz = X_ROTY(dx, dz, s, c); + + const Bounds &box = getBoundingBox(false); + int32 minX = box.minX - cinfo->radius; + int32 maxX = box.maxX + cinfo->radius; + int32 minZ = box.minZ - cinfo->radius; + int32 maxZ = box.maxZ + cinfo->radius; + + if ((px < minX) || (px > maxX) || (pz < minZ) || (pz > maxZ)) + return; + + enemyHit &= cinfo->enemyHit && (box.maxY - box.minY) > 256; + + int32 ax = px - minX; + int32 bx = maxX - px; + int32 az = pz - minZ; + int32 bz = maxZ - pz; + + if (ax <= bx && ax <= az && ax <= bz) { + px -= ax; + } else if (bx <= ax && bx <= az && bx <= bz) { + px += bx; + } else if (az <= ax && az <= bx && az <= bz) { + pz -= az; + } else { + pz += bz; + } + + s = -s; + + lara->pos.x = pos.x + X_ROTX(px, pz, s, c); + lara->pos.z = pos.z + X_ROTY(px, pz, s, c); + + if (enemyHit) + { + int32 cx = (minX + maxX) >> 1; + int32 cz = (minZ + maxZ) >> 1; + dx -= X_ROTX(cx, cz, s, c); + dz -= X_ROTY(cx, cz, s, c); + lara->animHit(dx, dz, 5); + } + + int32 tmpAngle = cinfo->angle; + + cinfo->gapPos = -WALL; + cinfo->gapNeg = -LARA_STEP_HEIGHT; + cinfo->gapCeiling = 0; + + cinfo->setAngle(phd_atan(lara->pos.z - cinfo->pos.z, lara->pos.x - cinfo->pos.x)); + + collideRoom(LARA_HEIGHT, 0); + + cinfo->setAngle(tmpAngle); + + if (cinfo->type != CT_NONE) { + lara->pos.x = cinfo->pos.x; + lara->pos.z = cinfo->pos.z; + } else { + cinfo->pos = lara->pos; + lara->updateRoom(-10); + } +} + +void Item::collideRoom(int32 height, int32 yOffset) const +{ + cinfo.type = CT_NONE; + cinfo.offset = vec3i(0, 0, 0); + + vec3i p = pos; + p.y += yOffset; + + int32 y = p.y - height; + + int32 cy = y - 160; + + int32 floor, ceiling; + + Room* nextRoom = room; + + #define CHECK_HEIGHT(v) {\ + nextRoom = nextRoom->getRoom(v.x, cy, v.z);\ + const Sector* sector = nextRoom->getSector(v.x, v.z);\ + floor = sector->getFloor(v.x, cy, v.z);\ + if (floor != WALL) floor -= p.y;\ + ceiling = sector->getCeiling(v.x, cy, v.z);\ + if (ceiling != WALL) ceiling -= y;\ + } + +// middle + CHECK_HEIGHT(p); + + cinfo.trigger = gLastFloorData; + cinfo.slantX = gLastFloorSlant.slantX; + cinfo.slantZ = gLastFloorSlant.slantZ; + + cinfo.setSide(CollisionInfo::ST_MIDDLE, floor, ceiling); + + vec3i f, l, r; + int32 R = cinfo.radius; + + switch (cinfo.quadrant) { + case 0 : { + f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, R); + l = vec3i(-R, 0, R); + r = vec3i( R, 0, R); + break; + } + case 1 : { + f = vec3i( R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT); + l = vec3i( R, 0, R); + r = vec3i( R, 0, -R); + break; + } + case 2 : { + f = vec3i((R * phd_sin(cinfo.angle)) >> FIXED_SHIFT, 0, -R); + l = vec3i( R, 0, -R); + r = vec3i(-R, 0, -R); + break; + } + case 3 : { + f = vec3i(-R, 0, (R * phd_cos(cinfo.angle)) >> FIXED_SHIFT); + l = vec3i(-R, 0, -R); + r = vec3i(-R, 0, R); + break; + } + default : { + f.x = f.y = f.z = 0; + l.x = l.y = l.z = 0; + r.x = r.y = r.z = 0; + ASSERT(false); + } + } + + f += p; + l += p; + r += p; + + vec3i delta; + delta.x = cinfo.pos.x - p.x; + delta.y = cinfo.pos.y - p.y; + delta.z = cinfo.pos.z - p.z; + +// front + CHECK_HEIGHT(f); + cinfo.setSide(CollisionInfo::ST_FRONT, floor, ceiling); + +// left + CHECK_HEIGHT(l); + cinfo.setSide(CollisionInfo::ST_LEFT, floor, ceiling); + +// right + CHECK_HEIGHT(r); + cinfo.setSide(CollisionInfo::ST_RIGHT, floor, ceiling); + +// static objects + room->collideStatic(cinfo, p, height); + +// check middle + if (cinfo.m.floor == WALL) + { + cinfo.offset = delta; + cinfo.type = CT_FRONT; + return; + } + + if (cinfo.m.floor <= cinfo.m.ceiling) + { + cinfo.offset = delta; + cinfo.type = CT_FLOOR_CEILING; + return; + } + + if (cinfo.m.ceiling >= 0) + { + cinfo.offset.y = cinfo.m.ceiling; + cinfo.type = CT_CEILING; + } + +// front + if (cinfo.f.floor > cinfo.gapPos || + cinfo.f.floor < cinfo.gapNeg || + cinfo.f.ceiling > cinfo.gapCeiling) + { + if (cinfo.quadrant & 1) + { + cinfo.offset.x = alignOffset(f.x, p.x); + cinfo.offset.z = delta.z; + } else { + cinfo.offset.x = delta.x; + cinfo.offset.z = alignOffset(f.z, p.z); + } + + cinfo.type = CT_FRONT; + return; + } + +// front ceiling + if (cinfo.f.ceiling >= cinfo.gapCeiling) + { + cinfo.offset = delta; + cinfo.type = CT_FRONT_CEILING; + return; + } + +// left + if (cinfo.l.floor > cinfo.gapPos || cinfo.l.floor < cinfo.gapNeg) + { + if (cinfo.quadrant & 1) { + cinfo.offset.z = alignOffset(l.z, f.z); + } else { + cinfo.offset.x = alignOffset(l.x, f.x); + } + cinfo.type = CT_LEFT; + return; + } + +// right + if (cinfo.r.floor > cinfo.gapPos || cinfo.r.floor < cinfo.gapNeg) + { + if (cinfo.quadrant & 1) { + cinfo.offset.z = alignOffset(r.z, f.z); + } else { + cinfo.offset.x = alignOffset(r.x, f.x); + } + cinfo.type = CT_RIGHT; + return; + } +} + +uint32 Item::updateHitMask(Lara* lara, CollisionInfo* cinfo) +{ + hitMask = 0; + + if (!collideBounds(lara, cinfo)) // check bound box intersection + return false; + + hitMask = collideSpheres(lara); // get hitMask = spheres collision mask + + return hitMask; +} + Item* Item::init(Room* room) { #define INIT_ITEM(type, className) case ITEM_##type : return new (this) className(room) @@ -672,7 +1407,7 @@ Item* Item::init(Room* room) // INIT_ITEM( CUT_4 , ??? ); // INIT_ITEM( INV_PASSPORT_CLOSED , ??? ); // INIT_ITEM( INV_MAP , ??? ); - // INIT_ITEM( CRYSTAL , ??? ); + INIT_ITEM( CRYSTAL , Crystal ); INIT_ITEM( PISTOLS , Pickup ); INIT_ITEM( SHOTGUN , Pickup ); INIT_ITEM( MAGNUMS , Pickup ); @@ -742,19 +1477,19 @@ Item* Item::init(Room* room) // INIT_ITEM( INV_SCION , ??? ); // INIT_ITEM( EXPLOSION , ??? ); // INIT_ITEM( UNUSED_8 , ??? ); - // INIT_ITEM( WATER_SPLASH , ??? ); + INIT_ITEM( SPLASH , SpriteEffect ); // INIT_ITEM( UNUSED_9 , ??? ); - // INIT_ITEM( BUBBLE , ??? ); + INIT_ITEM( BUBBLE , Bubble ); // INIT_ITEM( UNUSED_10 , ??? ); // INIT_ITEM( UNUSED_11 , ??? ); - // INIT_ITEM( BLOOD , ??? ); + INIT_ITEM( BLOOD , SpriteEffect ); // INIT_ITEM( UNUSED_12 , ??? ); - // INIT_ITEM( SMOKE , ??? ); + INIT_ITEM( SMOKE , SpriteEffect ); // INIT_ITEM( CENTAUR_STATUE , ??? ); // INIT_ITEM( CABIN , ??? ); // INIT_ITEM( MUTANT_EGG_SMALL , ??? ); - // INIT_ITEM( RICOCHET , ??? ); - // INIT_ITEM( SPARKLES , ??? ); + INIT_ITEM( RICOCHET , SpriteEffect ); + INIT_ITEM( SPARKLES , SpriteEffect ); // INIT_ITEM( MUZZLE_FLASH , ??? ); // INIT_ITEM( UNUSED_13 , ??? ); // INIT_ITEM( UNUSED_14 , ??? ); diff --git a/src/platform/gba/lara.h b/src/platform/gba/lara.h index 6f0a9ee7..09446b61 100644 --- a/src/platform/gba/lara.h +++ b/src/platform/gba/lara.h @@ -3,29 +3,8 @@ #include "common.h" #include "item.h" -#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( STATE_WALK ) \ E( STATE_RUN ) \ @@ -117,14 +96,14 @@ #define LARA_MAX_HEALTH 1000 #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) -#define LARA_TURN_VERY_SLOW (2 * DEG2SHORT) -#define LARA_TURN_SLOW (4 * DEG2SHORT) -#define LARA_TURN_MED (6 * DEG2SHORT) -#define LARA_TURN_FAST (8 * DEG2SHORT) -#define LARA_TILT_ACCEL (DEG2SHORT + DEG2SHORT / 2) -#define LARA_TILT_MAX (11 * DEG2SHORT) +#define LARA_TURN_ACCEL (ANGLE(9) / 4) +#define LARA_TURN_JUMP ANGLE(3) +#define LARA_TURN_VERY_SLOW ANGLE(2) +#define LARA_TURN_SLOW ANGLE(4) +#define LARA_TURN_MED ANGLE(6) +#define LARA_TURN_FAST ANGLE(8) +#define LARA_TILT_ACCEL (ANGLE(3) / 2) +#define LARA_TILT_MAX ANGLE(11) #define LARA_STEP_HEIGHT 384 #define LARA_SMASH_HEIGHT 640 #define LARA_FLOAT_UP_SPEED 5 @@ -141,25 +120,6 @@ #define LARA_WADE_MAX_DEPTH 730 #define LARA_SWIM_MIN_DEPTH 512 -enum { - JOINT_HIPS = 0, - JOINT_LEG_L1, - JOINT_LEG_L2, - JOINT_LEG_L3, - JOINT_LEG_R1, - JOINT_LEG_R2, - JOINT_LEG_R3, - JOINT_CHEST, - JOINT_ARM_R1, - JOINT_ARM_R2, - JOINT_ARM_R3, - JOINT_ARM_L1, - JOINT_ARM_L2, - JOINT_ARM_L3, - JOINT_HEAD, - JOINT_MAX -}; - enum { JOINT_MASK_HIPS = 1 << JOINT_HIPS, JOINT_MASK_LEG_L1 = 1 << JOINT_LEG_L1, @@ -168,7 +128,7 @@ enum { JOINT_MASK_LEG_R1 = 1 << JOINT_LEG_R1, JOINT_MASK_LEG_R2 = 1 << JOINT_LEG_R2, JOINT_MASK_LEG_R3 = 1 << JOINT_LEG_R3, - JOINT_MASK_CHEST = 1 << JOINT_CHEST, + JOINT_MASK_TORSO = 1 << JOINT_TORSO, JOINT_MASK_ARM_R1 = 1 << JOINT_ARM_R1, JOINT_MASK_ARM_R2 = 1 << JOINT_ARM_R2, JOINT_MASK_ARM_R3 = 1 << JOINT_ARM_R3, @@ -180,12 +140,88 @@ enum { JOINT_MASK_ARM_R = JOINT_MASK_ARM_R1 | JOINT_MASK_ARM_R2 | JOINT_MASK_ARM_R3, JOINT_MASK_LEG_L = JOINT_MASK_LEG_L1 | JOINT_MASK_LEG_L2 | JOINT_MASK_LEG_L3, JOINT_MASK_LEG_R = JOINT_MASK_LEG_R1 | JOINT_MASK_LEG_R2 | JOINT_MASK_LEG_R3, - JOINT_MASK_UPPER = JOINT_MASK_CHEST | JOINT_MASK_ARM_L | JOINT_MASK_ARM_R, // without head + JOINT_MASK_UPPER = JOINT_MASK_TORSO | JOINT_MASK_ARM_L | JOINT_MASK_ARM_R, // without head JOINT_MASK_LOWER = JOINT_MASK_HIPS | JOINT_MASK_LEG_L | JOINT_MASK_LEG_R, - 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, + JOINT_MASK_BRAID = JOINT_MASK_HEAD | JOINT_MASK_TORSO | JOINT_MASK_ARM_L1 | JOINT_MASK_ARM_L2 | JOINT_MASK_ARM_R1 | JOINT_MASK_ARM_R2, }; -int32 swimTimer; +Lara* players[MAX_PLAYERS]; +CollisionInfo cinfo; + +const WeaponParams weaponParams[WEAPON_MAX] = { + { // WEAPON_PISTOLS + ITEM_LARA_PISTOLS, // modelType + ITEM_LARA_PISTOLS, // animType + 1, // damage + ANGLE(8), // spread + 8192, // range + 650, // height + SND_PISTOLS_SHOT, // soundId + 9, // reloadTimer + 155, // flashOffset + 3, // flashTimer + 20, // flashIntensity + ANGLE(60), // aimX + ANGLE(60), // aimY + ANGLE(80), // armX + ANGLE(-60), // armMinY + ANGLE(170), // armMaxY + }, + { // WEAPON_MAGNUMS + ITEM_LARA_MAGNUMS, // modelType + ITEM_LARA_PISTOLS, // animType + 2, // damage + ANGLE(8), // spread + 8192, // range + 650, // height + SND_MAGNUMS_SHOT, // soundId + 9, // reloadTimer + 155, // flashOffset + 3, // flashTimer + 16, // flashIntensity + ANGLE(60), // aimX + ANGLE(60), // aimY + ANGLE(80), // armX + ANGLE(-60), // armMinY + ANGLE(170), // armMaxY + }, + { // WEAPON_UZIS + ITEM_LARA_UZIS, // modelType + ITEM_LARA_PISTOLS, // animType + 1, // damage + ANGLE(8), // spread + 8192, // range + 650, // height + SND_UZIS_SHOT, // soundId + 3, // reloadTimer + 180, // flashOffset + 2, // flashTimer + 10, // flashIntensity + ANGLE(60), // aimX + ANGLE(60), // aimY + ANGLE(80), // armX + ANGLE(-60), // armMinY + ANGLE(170), // armMaxY + }, + { // WEAPON_SHOTGUN + ITEM_LARA_SHOTGUN, // modelType + ITEM_LARA_SHOTGUN, // animType + 4, // damage + ANGLE(20), // spread + 8192, // range + 500, // height + SND_SHOTGUN_SHOT, // soundId + 26, // reloadTimer + 0, // flashOffset + 0, // flashTimer + 0, // flashIntensity + ANGLE(60), // aimX + ANGLE(60), // aimY + ANGLE(80), // armX + ANGLE(-60), // armMinY + ANGLE(170), // armMaxY + }, +}; struct Lara : Item { @@ -194,6 +230,17 @@ struct Lara : Item X_MAX }; + enum { + ANIM_PISTOLS_AIM = 0, + ANIM_PISTOLS_PICK, + ANIM_PISTOLS_DRAW, + ANIM_PISTOLS_FIRE, + + ANIM_SHOTGUN_AIM = 0, + ANIM_SHOTGUN_DRAW, + ANIM_SHOTGUN_FIRE, + }; + enum { ANIM_RUN = 0, @@ -296,8 +343,14 @@ struct Lara : Item (this->*sHandlers[state])(); } - void updateCollision() + void updateObjectsCollision() { + if (health <= 0) + { + extraL->hitQuadrant = -1; + return; + } + Room** adjRoom = room->getAdjRooms(); while (*adjRoom) { @@ -320,8 +373,33 @@ struct Lara : Item item = item->nextItem; } } + } + + void updateCollision() + { + updateObjectsCollision(); (this->*cHandlers[state])(); + + // control hit animation + if (extraL->hitTimer <= 0) + return; + + if (!extraL->hitFrame) { + soundPlay(SND_HIT, pos); + } + + extraL->hitFrame++; + if (extraL->hitFrame > 34) { + extraL->hitFrame = 34; + } + + extraL->hitTimer--; + if (extraL->hitTimer == 0) + { + extraL->hitQuadrant = -1; + extraL->hitFrame = 0; + } } void startScreaming() @@ -365,7 +443,7 @@ struct Lara : Item } } - int16 getFront(int16 rot) + bool s_getFront(int16 rot) { rot += angle.y; @@ -381,19 +459,10 @@ struct Lara : Item floor -= pos.y; } - return floor; + return floor >= -LARA_STEP_HEIGHT; } - const Bounds& getBounds() - { - const Model* model = models + type; - - AnimFrame* frame = getFrame(model); - - return frame->box; - } - - bool checkDeath(State deathState) + bool s_checkDeath(int32 deathState) { if (health <= 0) { goalState = deathState; @@ -403,14 +472,27 @@ struct Lara : Item } // state control - bool s_checkFront(int16 angleDelta) + bool s_checkFront(int16 angleDelta, int32 radius) { + /* + int16 frontAngle = angle.y + angleDelta; + + int32 x = pos.x + ((phd_sin(frontAngle) * radius) >> FIXED_SHIFT); + int32 y = pos.y - LARA_HEIGHT; + int32 z = pos.z + ((phd_cos(frontAngle) * radius) >> FIXED_SHIFT); + + const Room* nextRoom = room->getRoom(x, y, z); + const Sector* sector = nextRoom->getSector(x, z); + int32 floor = sector->getFloor(x, y, z); + + return (floor != WALL && (pos.y - floor) < LARA_STEP_HEIGHT); + */ CollisionInfo tmpInfo = cinfo; - int16 tmpAngle = moveAngle; + int16 tmpAngle = extraL->moveAngle; c_angle(angleDelta); - cinfo.radius = LARA_RADIUS + 4; - + cinfo.radius = radius; + cinfo.type = CT_NONE; cinfo.gapPos = -WALL; cinfo.gapNeg = -LARA_STEP_HEIGHT; cinfo.stopOnSlant = true; @@ -418,14 +500,15 @@ struct Lara : Item if ((angleDelta == ANGLE_180) && ((input & IN_WALK) || (waterState == WATER_STATE_WADE))) { cinfo.gapPos = LARA_STEP_HEIGHT; + cinfo.stopOnLava = true; } - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); bool collide = (cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING); cinfo = tmpInfo; - moveAngle = tmpAngle; + extraL->moveAngle = tmpAngle; return !collide; } @@ -465,7 +548,7 @@ struct Lara : Item void s_checkWalk(int32 stopState) { - if ((input & IN_UP) && s_checkFront(ANGLE_0)) { + if ((input & IN_UP) && s_checkFront(ANGLE_0, LARA_RADIUS + 4)) { if (input & IN_WALK) { goalState = STATE_WALK; } else { @@ -500,16 +583,16 @@ struct Lara : Item void s_turnUW() { if (input & IN_UP) { - angle.x -= 2 * DEG2SHORT; + angle.x -= ANGLE(2); } else if (input & IN_DOWN) { - angle.x += 2 * DEG2SHORT; + angle.x += ANGLE(2); } if (input & IN_LEFT) { - turnSpeed = max(turnSpeed - LARA_TURN_ACCEL, -LARA_TURN_MED); + turnSpeed = X_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); + turnSpeed = X_MIN(turnSpeed + LARA_TURN_ACCEL, LARA_TURN_MED); angle.z += LARA_TILT_ACCEL * 2; } } @@ -517,14 +600,28 @@ struct Lara : Item void s_dive() { animSet(ANIM_SURF_DIVE, true); - angle.x = -45 * DEG2SHORT; + angle.x = ANGLE(-45); vSpeed = LARA_DIVE_SPEED; waterState = WATER_STATE_UNDER; } + bool s_checkLook() + { + if (input & IN_LOOK) { + gCamera->mode = CAMERA_MODE_LOOK; + return true; + } + + if (gCamera->mode == CAMERA_MODE_LOOK) { + gCamera->mode = CAMERA_MODE_FOLLOW; + } + + return false; + } + S_HANDLER( STATE_WALK ) { - if (checkDeath(STATE_STOP)) + if (s_checkDeath(STATE_STOP)) return; s_rotate(LARA_TURN_SLOW, 0); @@ -534,7 +631,7 @@ struct Lara : Item S_HANDLER( STATE_RUN ) { - if (checkDeath(STATE_DEATH)) + if (s_checkDeath(STATE_DEATH)) return; if (s_checkRoll()) @@ -551,7 +648,7 @@ struct Lara : Item S_HANDLER( STATE_STOP ) { - if (checkDeath(STATE_DEATH)) + if (s_checkDeath(STATE_DEATH)) return; if (s_checkRoll()) @@ -559,21 +656,13 @@ struct Lara : Item goalState = STATE_STOP; - if ((input & (IN_UP | IN_ACTION)) == (IN_UP | IN_ACTION)) - { - c_angle(ANGLE_0); - cinfo.radius = LARA_RADIUS + 4; - c_default(); - cinfo.radius = LARA_RADIUS; - - if (c_checkClimbUp()) - return; - } + if (s_checkLook()) + return; if (input & IN_WALK) { - if ((input & IN_LEFT) && s_checkFront(-ANGLE_90)) { + if ((input & IN_LEFT) && s_checkFront(-ANGLE_90, LARA_RADIUS + 16)) { goalState = STATE_STEP_LEFT; - } else if ((input & IN_RIGHT) && s_checkFront(ANGLE_90)) { + } else if ((input & IN_RIGHT) && s_checkFront(ANGLE_90, LARA_RADIUS + 16)) { goalState = STATE_STEP_RIGHT; } } else { @@ -586,13 +675,13 @@ struct Lara : Item if (input & IN_JUMP) { goalState = STATE_COMPRESS; - } else if ((input & IN_UP) && s_checkFront(ANGLE_0)) { + } else if ((input & IN_UP) && s_checkFront(ANGLE_0, LARA_RADIUS + 4)) { if (input & IN_WALK) { s_STATE_WALK(); } else { s_STATE_RUN(); } - } else if ((input & IN_DOWN) && s_checkFront(ANGLE_180)) { + } else if ((input & IN_DOWN) && s_checkFront(ANGLE_180, LARA_RADIUS + 4)) { if (input & IN_WALK) { s_STATE_BACK(); } else { @@ -614,14 +703,17 @@ struct Lara : Item goalState != STATE_STOP && goalState != STATE_RUN) { - if (input & IN_ACTION) + if (extraL->weaponState == WEAPON_STATE_FREE) { - goalState = STATE_REACH; - } + if (input & IN_ACTION) + { + goalState = STATE_REACH; + } - if (input & IN_WALK) - { - goalState = STATE_SWAN_DIVE; + if (input & IN_WALK) + { + goalState = STATE_SWAN_DIVE; + } } s_checkRoll(); @@ -644,7 +736,7 @@ struct Lara : Item S_HANDLER( STATE_TURN_RIGHT ) { - if (checkDeath(STATE_STOP)) + if (s_checkDeath(STATE_STOP)) return; if (input & IN_LOOK) @@ -655,17 +747,21 @@ struct Lara : Item turnSpeed += LARA_TURN_ACCEL; - if (turnSpeed > LARA_TURN_SLOW) + if ((turnSpeed > LARA_TURN_SLOW || extraL->weaponState == WEAPON_STATE_READY) && (waterState != WATER_STATE_WADE) && !(input & IN_WALK)) { goalState = STATE_TURN_FAST; } + if (goalState == state) { + turnSpeed = X_MIN(turnSpeed, LARA_TURN_SLOW); + } + s_checkWalk((input & IN_RIGHT) ? goalState : STATE_STOP); } S_HANDLER( STATE_TURN_LEFT ) { - if (checkDeath(STATE_STOP)) + if (s_checkDeath(STATE_STOP)) return; if (input & IN_LOOK) @@ -676,11 +772,15 @@ struct Lara : Item turnSpeed -= LARA_TURN_ACCEL; - if (turnSpeed < -LARA_TURN_SLOW) + if ((turnSpeed < -LARA_TURN_SLOW || extraL->weaponState == WEAPON_STATE_READY) && (waterState != WATER_STATE_WADE) && !(input & IN_WALK)) { goalState = STATE_TURN_FAST; } + if (goalState == state) { + turnSpeed = X_MAX(turnSpeed, -LARA_TURN_SLOW); + } + s_checkWalk((input & IN_LEFT) ? goalState : STATE_STOP); } @@ -700,7 +800,7 @@ struct Lara : Item S_HANDLER( STATE_HANG ) { - camera.targetAngleX = -60 * DEG2SHORT; + gCamera->targetAngleX = ANGLE(-60); s_ignoreEnemy(); if (input & IN_LEFT) { @@ -712,7 +812,7 @@ struct Lara : Item S_HANDLER( STATE_REACH ) { - camera.targetAngleY = 85 * DEG2SHORT; + gCamera->targetAngleY = ANGLE(85); s_checkFall(); } @@ -724,7 +824,7 @@ struct Lara : Item S_HANDLER( STATE_UW_TREAD ) { - if (checkDeath(STATE_DEATH_UW)) + if (s_checkDeath(STATE_DEATH_UW)) return; if (s_checkRoll()) @@ -736,7 +836,7 @@ struct Lara : Item goalState = STATE_UW_SWIM; } - vSpeed = max(vSpeed - LARA_SWIM_FRICTION, 0); + vSpeed = X_MAX(vSpeed - LARA_SWIM_FRICTION, 0); } S_HANDLER( STATE_LAND ) @@ -746,13 +846,13 @@ struct Lara : Item S_HANDLER( STATE_COMPRESS ) { - if ((input & IN_UP) && getFront(ANGLE_0) >= -LARA_STEP_HEIGHT) { + if ((input & IN_UP) && s_getFront(ANGLE_0)) { goalState = STATE_JUMP; - } else if ((input & IN_LEFT) && getFront(-ANGLE_90) >= -LARA_STEP_HEIGHT) { + } else if ((input & IN_LEFT) && s_getFront(-ANGLE_90)) { goalState = STATE_JUMP_LEFT; - } else if ((input & IN_RIGHT) && getFront(ANGLE_90) >= -LARA_STEP_HEIGHT) { + } else if ((input & IN_RIGHT) && s_getFront(ANGLE_90)) { goalState = STATE_JUMP_RIGHT; - } else if ((input & IN_DOWN) && getFront(ANGLE_180) >= -LARA_STEP_HEIGHT) { + } else if ((input & IN_DOWN) && s_getFront(ANGLE_180)) { goalState = STATE_JUMP_BACK; } s_checkFall(); @@ -760,7 +860,7 @@ struct Lara : Item S_HANDLER( STATE_BACK ) { - if (checkDeath(STATE_STOP)) + if (s_checkDeath(STATE_STOP)) return; if ((input & (IN_WALK | IN_DOWN)) != (IN_WALK | IN_DOWN)) { @@ -774,7 +874,7 @@ struct Lara : Item S_HANDLER( STATE_UW_SWIM ) { - if (checkDeath(STATE_DEATH_UW)) + if (s_checkDeath(STATE_DEATH_UW)) return; if (s_checkRoll()) @@ -782,7 +882,7 @@ struct Lara : Item s_turnUW(); - vSpeed = min(vSpeed + LARA_SWIM_ACCEL, LARA_SWIM_SPEED_MAX); + vSpeed = X_MIN(vSpeed + LARA_SWIM_ACCEL, LARA_SWIM_SPEED_MAX); if (!(input & IN_JUMP)) { goalState = STATE_UW_GLIDE; @@ -791,7 +891,7 @@ struct Lara : Item S_HANDLER( STATE_UW_GLIDE ) { - if (checkDeath(STATE_DEATH_UW)) + if (s_checkDeath(STATE_DEATH_UW)) return; if (s_checkRoll()) @@ -803,7 +903,7 @@ struct Lara : Item goalState = STATE_UW_SWIM; } - vSpeed = max(vSpeed - LARA_SWIM_FRICTION, 0); + vSpeed = X_MAX(vSpeed - LARA_SWIM_FRICTION, 0); if (vSpeed <= LARA_SWIM_SPEED_MIN) { goalState = STATE_UW_TREAD; @@ -817,7 +917,7 @@ struct Lara : Item S_HANDLER( STATE_TURN_FAST ) { - if (checkDeath(STATE_STOP)) + if (s_checkDeath(STATE_STOP)) return; if (input & IN_LOOK) @@ -841,7 +941,7 @@ struct Lara : Item S_HANDLER( STATE_STEP_RIGHT ) { - if (checkDeath(STATE_STOP)) + if (s_checkDeath(STATE_STOP)) return; if ((input & (IN_WALK | IN_RIGHT)) != (IN_WALK | IN_RIGHT)) @@ -852,7 +952,7 @@ struct Lara : Item S_HANDLER( STATE_STEP_LEFT ) { - if (checkDeath(STATE_STOP)) + if (s_checkDeath(STATE_STOP)) return; if ((input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT)) @@ -868,17 +968,16 @@ struct Lara : Item S_HANDLER( STATE_SLIDE ) { - camera.targetAngleX = -45 * DEG2SHORT; + gCamera->targetAngleX = ANGLE(-45); - if (input & IN_JUMP) - { + if (input & IN_JUMP) { goalState = STATE_JUMP; } } S_HANDLER( STATE_JUMP_BACK ) { - camera.targetAngleY = 135 * DEG2SHORT; + gCamera->targetAngleY = ANGLE(135); if (s_checkFall()) return; @@ -907,47 +1006,49 @@ struct Lara : Item { s_checkFall(); - if (input & IN_ACTION) - { + if ((input & IN_ACTION) && (extraL->weaponState == WEAPON_STATE_FREE)) { goalState = STATE_REACH; } } S_HANDLER( STATE_HANG_LEFT ) { - camera.targetAngleX = -60 * DEG2SHORT; + gCamera->targetAngleX = ANGLE(-60); s_ignoreEnemy(); - if (!(input & IN_LEFT)) - { + if (!(input & IN_LEFT)) { goalState = STATE_HANG; } } S_HANDLER( STATE_HANG_RIGHT ) { - camera.targetAngleX = -60 * DEG2SHORT; + gCamera->targetAngleX = ANGLE(-60); s_ignoreEnemy(); - if (!(input & IN_RIGHT)) - { + if (!(input & IN_RIGHT)) { goalState = STATE_HANG; } } S_HANDLER( STATE_SLIDE_BACK ) { - if (input & IN_JUMP) - { + if (input & IN_JUMP) { goalState = STATE_JUMP_BACK; } } S_HANDLER( STATE_SURF_TREAD ) { - vSpeed = max(vSpeed - LARA_SURF_FRICTION, 0); + vSpeed = X_MAX(vSpeed - LARA_SURF_FRICTION, 0); + + if (s_checkDeath(STATE_DEATH_UW)) + return; + + if (s_checkLook()) + return; if (input & IN_LEFT) { angle.y -= LARA_TURN_SLOW; @@ -966,21 +1067,21 @@ struct Lara : Item } if (input & IN_JUMP) { - swimTimer++; - if (swimTimer == LARA_SWIM_TIMER) { + extraL->swimTimer++; + if (extraL->swimTimer == LARA_SWIM_TIMER) { s_dive(); } } else { - swimTimer = 0; + extraL->swimTimer = 0; } } S_HANDLER( STATE_SURF_SWIM ) { - if (checkDeath(STATE_DEATH_UW)) + if (s_checkDeath(STATE_DEATH_UW)) return; - swimTimer = 0; + extraL->swimTimer = 0; if (input & IN_LEFT) { angle.y -= LARA_TURN_SLOW; @@ -992,7 +1093,7 @@ struct Lara : Item goalState = STATE_SURF_TREAD; } - vSpeed = min(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); } S_HANDLER( STATE_UW_DIVE ) @@ -1004,75 +1105,74 @@ struct Lara : Item S_HANDLER( STATE_BLOCK_PUSH ) { - camera.targetAngleX = -25 * DEG2SHORT; - camera.targetAngleY = 35 * DEG2SHORT; - camera.center = true; + gCamera->targetAngleX = ANGLE(-25); + gCamera->targetAngleY = ANGLE(35); + gCamera->center = true; s_ignoreEnemy(); } S_HANDLER( STATE_BLOCK_PULL ) { - camera.targetAngleX = -25 * DEG2SHORT; - camera.targetAngleY = 35 * DEG2SHORT; - camera.center = true; + gCamera->targetAngleX = ANGLE(-25); + gCamera->targetAngleY = ANGLE(35); + gCamera->center = true; s_ignoreEnemy(); } S_HANDLER( STATE_BLOCK_READY ) { - camera.targetAngleY = 75 * DEG2SHORT; + gCamera->targetAngleY = ANGLE(75); s_ignoreEnemy(); - if (!(input & IN_ACTION)) - { + if (!(input & IN_ACTION)) { goalState = STATE_STOP; } } S_HANDLER( STATE_PICKUP ) { - camera.targetAngleX = -15 * DEG2SHORT; - camera.targetAngleY = -130 * DEG2SHORT; - camera.targetDist = 1024; + gCamera->targetAngleX = ANGLE(-15); + gCamera->targetAngleY = ANGLE(-130); + gCamera->targetDist = 1024; s_ignoreEnemy(); } S_HANDLER( STATE_SWITCH_DOWN ) { - camera.targetAngleX = -25 * DEG2SHORT; - camera.targetAngleY = 80 * DEG2SHORT; - camera.targetDist = 1024; + gCamera->targetAngleX = ANGLE(-25); + gCamera->targetAngleY = ANGLE(80); + gCamera->targetDist = 1024; s_ignoreEnemy(); } S_HANDLER( STATE_SWITCH_UP ) { - camera.targetAngleX = -25 * DEG2SHORT; - camera.targetAngleY = 80 * DEG2SHORT; - camera.targetDist = 1024; + gCamera->targetAngleX = ANGLE(-25); + gCamera->targetAngleY = ANGLE(80); + gCamera->targetDist = 1024; s_ignoreEnemy(); } S_HANDLER( STATE_USE_KEY ) { - camera.targetAngleX = -25 * DEG2SHORT; - camera.targetAngleY = -80 * DEG2SHORT; - camera.targetDist = 1024; + gCamera->targetAngleX = ANGLE(-25); + gCamera->targetAngleY = ANGLE(-80); + gCamera->targetDist = 1024; s_ignoreEnemy(); } S_HANDLER( STATE_USE_PUZZLE ) { - camera.targetAngleX = -25 * DEG2SHORT; - camera.targetAngleY = -80 * DEG2SHORT; - camera.targetDist = 1024; + gCamera->targetAngleX = ANGLE(-25); + gCamera->targetAngleY = ANGLE(-80); + gCamera->targetDist = 1024; s_ignoreEnemy(); } @@ -1080,7 +1180,7 @@ struct Lara : Item S_HANDLER( STATE_DEATH_UW ) { vSpeed = X_MAX(vSpeed - LARA_SWIM_ACCEL, 0); - angle.x = angleDec(angle.x, 2 * DEG2SHORT); + angle.x = angleDec(angle.x, ANGLE(2)); } S_HANDLER( STATE_ROLL_START ) @@ -1090,17 +1190,17 @@ struct Lara : Item S_HANDLER( STATE_SPECIAL ) { - camera.targetAngleX = -25 * DEG2SHORT; - camera.targetAngleY = 170 * DEG2SHORT; - camera.center = true; + gCamera->targetAngleX = ANGLE(-25); + gCamera->targetAngleY = ANGLE(170); + gCamera->center = true; } S_HANDLER( STATE_SURF_BACK ) { - if (checkDeath(STATE_DEATH_UW)) + if (s_checkDeath(STATE_DEATH_UW)) return; - swimTimer = 0; + extraL->swimTimer = 0; if (input & IN_LEFT) { angle.y -= LARA_TURN_VERY_SLOW; @@ -1112,35 +1212,35 @@ struct Lara : Item goalState = STATE_SURF_TREAD; } - vSpeed = min(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); } S_HANDLER( STATE_SURF_LEFT ) { - if (checkDeath(STATE_DEATH_UW)) + if (s_checkDeath(STATE_DEATH_UW)) return; - swimTimer = 0; + extraL->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); + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); } S_HANDLER( STATE_SURF_RIGHT ) { - if (checkDeath(STATE_DEATH_UW)) + if (s_checkDeath(STATE_DEATH_UW)) return; - swimTimer = 0; + extraL->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); + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); } S_HANDLER( STATE_USE_MIDAS ) @@ -1179,7 +1279,7 @@ struct Lara : Item S_HANDLER( STATE_WATER_OUT ) { s_ignoreEnemy(); - camera.center = true; + gCamera->center = true; } S_HANDLER( STATE_CLIMB_START ) {} @@ -1206,12 +1306,12 @@ struct Lara : Item cinfo.offset = vec3i(0, 0, 0); } - void c_angle(int32 angleDelta) + void c_angle(int16 angleDelta) { - moveAngle = angle.y + angleDelta; + angleDelta += angle.y; - cinfo.angle = moveAngle; - cinfo.quadrant = uint16(cinfo.angle + ANGLE_45) / ANGLE_90; + extraL->moveAngle = angleDelta; + cinfo.setAngle(angleDelta); } bool c_checkCeiling() @@ -1243,12 +1343,12 @@ struct Lara : Item if (cinfo.type == CT_LEFT) { c_applyOffset(); - angle.y += 5 * DEG2SHORT; - angle.z = angleDec(angle.z, 2 * DEG2SHORT); + angle.y += ANGLE(5); + angle.z = angleDec(angle.z, ANGLE(2)); } else if (cinfo.type == CT_RIGHT) { c_applyOffset(); - angle.y -= 5 * DEG2SHORT; - angle.z = angleDec(angle.z, 2 * DEG2SHORT); + angle.y -= ANGLE(5); + angle.z = angleDec(angle.z, ANGLE(2)); } return false; @@ -1257,23 +1357,23 @@ struct Lara : Item bool c_checkWallUW() { if (cinfo.type == CT_FRONT) { - if (angle.x > 35 * DEG2SHORT) { - angle.x += 2 * DEG2SHORT; - } else if (angle.x < -35 * DEG2SHORT) { - angle.x -= 2 * DEG2SHORT; + if (angle.x > ANGLE(35)) { + angle.x += ANGLE(2); + } else if (angle.x < ANGLE(-35)) { + angle.x -= ANGLE(2); } else { vSpeed = 0; } } else if (cinfo.type == CT_CEILING) { - if (angle.x >= -45 * DEG2SHORT) { - angle.x -= 2 * DEG2SHORT; + if (angle.x >= ANGLE(-45)) { + angle.x -= ANGLE(2); } } else if (cinfo.type == CT_FRONT_CEILING) { vSpeed = 0; } else if (cinfo.type == CT_LEFT) { - angle.y += 5 * DEG2SHORT; + angle.y += ANGLE(5); } else if (cinfo.type == CT_RIGHT) { - angle.y -= 5 * DEG2SHORT; + angle.y -= ANGLE(5); } else if (cinfo.type == CT_FLOOR_CEILING) { pos = cinfo.pos; vSpeed = 0; @@ -1282,7 +1382,7 @@ struct Lara : Item if (cinfo.m.floor < 0) { pos.y += cinfo.m.floor; - angle.x += 2 * DEG2SHORT; + angle.x += ANGLE(2); } int32 waterDepth = getWaterDepth(); @@ -1312,9 +1412,9 @@ struct Lara : Item pos = cinfo.pos; vSpeed = 0; } else if (cinfo.type == CT_LEFT) { - angle.y += 5 * DEG2SHORT; + angle.y += ANGLE(5); } else if (cinfo.type == CT_RIGHT) { - angle.y -= 5 * DEG2SHORT; + angle.y -= ANGLE(5); } return true; @@ -1322,13 +1422,11 @@ struct Lara : Item bool c_checkSlide() { - if (waterState == WATER_STATE_WADE) { + if (waterState == WATER_STATE_WADE) return false; - } - if (cinfo.m.slantType != SLANT_HIGH) { + if (cinfo.m.slantType != SLANT_HIGH) return false; - } c_applyOffset(); @@ -1348,13 +1446,13 @@ struct Lara : Item if (state != STATE_SLIDE) { animSet(ANIM_SLIDE_FORTH, true); } - moveAngle = realAngle; + extraL->moveAngle = realAngle; angle.y = realAngle; } else { if (state != STATE_SLIDE_BACK) { animSet(ANIM_SLIDE_BACK, true); } - moveAngle = realAngle; + extraL->moveAngle = realAngle; angle.y = realAngle + ANGLE_180; } @@ -1363,13 +1461,11 @@ struct Lara : Item bool c_checkFall(int32 height, int32 fallAnimIndex = ANIM_FALL_FORTH) { - if (waterState == WATER_STATE_WADE) { + if (waterState == WATER_STATE_WADE) return false; - } - if (cinfo.m.floor <= height) { + if (cinfo.m.floor <= height) return false; - } animSet(fallAnimIndex, true); @@ -1404,7 +1500,7 @@ struct Lara : Item health -= (X_SQR(vSpeed - 140) * LARA_MAX_HEALTH) / 196; } - return checkDeath((State)state); + return health <= 0; } bool c_checkSwing() @@ -1424,15 +1520,15 @@ struct Lara : Item const Sector* sector = roomBelow->getSector(x, z); int32 floor = sector->getFloor(x, y, z); - if (floor != WALL) { + if (floor != WALL) + { int32 ceiling = sector->getCeiling(x, y, z); floor -= y; ceiling -= y; - if (floor > 0 && ceiling < -400) { + if (floor > 0 && ceiling < -400) return true; - } } return false; @@ -1440,7 +1536,7 @@ struct Lara : Item bool c_checkGrab() { - return !(input & IN_ACTION) || (cinfo.type != CT_FRONT) || (abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT); + return (extraL->weaponState != WEAPON_STATE_FREE) || !(input & IN_ACTION) || (cinfo.type != CT_FRONT) || (abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT); } bool c_checkSpace() @@ -1460,25 +1556,27 @@ struct Lara : Item if (cinfo.f.floor == WALL) return false; - if (c_checkGrab()) { + if (c_checkGrab()) return false; - } int16 realAngle = angle.y; - if (alignAngle(realAngle, 30 * DEG2SHORT)) { + if (alignAngle(realAngle, ANGLE(30))) return false; - } if (cinfo.f.floor >= -640 && cinfo.f.floor <= -384) { - if (c_checkSpace()) return false; + if (c_checkSpace()) + return false; + setWeaponState(WEAPON_STATE_BUSY); animSet(ANIM_CLIMB_2, true); 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; + if (c_checkSpace()) + return false; + setWeaponState(WEAPON_STATE_BUSY); animSet(ANIM_CLIMB_3, true); state = STATE_HANG_UP; @@ -1486,13 +1584,13 @@ struct Lara : Item } else if (cinfo.f.floor >= -1920 && cinfo.f.floor <= -896) { animSet(ANIM_STAND, true); goalState = STATE_JUMP_UP; - vSpeedHack = -int32(phd_sqrt(-2 * GRAVITY * (cinfo.f.floor + 800)) + 3); + extraL->vSpeedHack = int32(phd_sqrt(-2 * GRAVITY * (cinfo.f.floor + 800)) + 3); 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 = STATE_JUMP_UP; - vSpeedHack = -116; + vSpeedHack = 116; animProcess(); } else if (((cinfo.f.floor < -1024) && (cinfo.f.ceiling >= 506)) || ((cinfo.m.ceiling <= -518) && c_checkClimbStart())) { animSet(ANIM_STAND, true); @@ -1510,9 +1608,8 @@ struct Lara : Item bool c_checkHang() { - if (c_checkGrab()) { + if (c_checkGrab()) return false; - } if ((cinfo.f.ceiling > 0) || (cinfo.m.ceiling > -LARA_STEP_HEIGHT) || @@ -1521,16 +1618,14 @@ struct Lara : Item return false; } - int32 h = cinfo.f.floor - getBounds().minY; + int32 h = cinfo.f.floor - getBoundingBox(true).minY; int32 v = h + vSpeed; - if ((h < 0 && v < 0) || (h > 0 && v > 0)) { + if ((h < 0 && v < 0) || (h > 0 && v > 0)) return false; - } - if (alignAngle(angle.y, 35 * DEG2SHORT)) { + if (alignAngle(angle.y, ANGLE(35))) return false; - } if (state == STATE_REACH) { @@ -1543,7 +1638,8 @@ struct Lara : Item animSet(ANIM_HANG, true, 12); } - cinfo.offset.y = cinfo.f.floor - getBounds().minY; + setWeaponState(WEAPON_STATE_BUSY); + cinfo.offset.y = cinfo.f.floor - getBoundingBox(true).minY; c_applyOffset(); @@ -1587,13 +1683,11 @@ struct Lara : Item int32 h = cinfo.f.floor + LARA_HEIGHT_SURF; - if (h <= -512 || h > 316) { + if (h <= -512 || h > 316) return false; - } - if (alignAngle(angle.y, 35 * DEG2SHORT)) { + if (alignAngle(angle.y, ANGLE(35))) return false; - } pos.y += h - 5; @@ -1632,7 +1726,7 @@ struct Lara : Item cinfo.gapCeiling = 0; cinfo.stopOnSlant = true; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); } void c_step() @@ -1642,7 +1736,7 @@ struct Lara : Item cinfo.gapCeiling = 0; cinfo.stopOnSlant = true; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); if (c_checkCeiling()) return; @@ -1689,7 +1783,7 @@ struct Lara : Item cinfo.gapNeg = (state == STATE_REACH) ? 0 : -LARA_STEP_HEIGHT; cinfo.gapCeiling = 192; - collideRoom(this, state == STATE_JUMP_UP ? LARA_HEIGHT_JUMP : LARA_HEIGHT, 0); + collideRoom(state == STATE_JUMP_UP ? LARA_HEIGHT_JUMP : LARA_HEIGHT, 0); if ((state == STATE_REACH || state == STATE_JUMP_UP) && c_checkHang()) return; @@ -1706,8 +1800,8 @@ struct Lara : Item } } else if (!slide && ((cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING))) { animSet(ANIM_SMASH_JUMP, true, 1); - moveAngle += ANGLE_180; - hSpeed /= 4; + extraL->moveAngle += ANGLE_180; + hSpeed >>= 2; if (vSpeed <= 0) { vSpeed = 1; } @@ -1720,9 +1814,9 @@ struct Lara : Item vSpeed = 16; } } else if (cinfo.type == CT_LEFT) { - angle.y += 5 * DEG2SHORT; + angle.y += ANGLE(5); } else if (cinfo.type == CT_RIGHT) { - angle.y -= 5 * DEG2SHORT; + angle.y -= ANGLE(5); } c_fall(); @@ -1735,7 +1829,7 @@ struct Lara : Item cinfo.gapCeiling = 0; cinfo.stopOnSlant = true; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); if (c_checkCeiling()) return; @@ -1764,7 +1858,7 @@ struct Lara : Item cinfo.gapCeiling = 0; cinfo.stopOnSlant = true; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); if (c_checkCeiling()) return; @@ -1786,7 +1880,7 @@ struct Lara : Item cinfo.gapPos = -WALL; cinfo.gapNeg = WALL; cinfo.gapCeiling = 0; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); bool noFloor = cinfo.f.floor < 200; @@ -1803,20 +1897,21 @@ struct Lara : Item case 3 : pos.x -= 2; break; } - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); - moveAngle = angle.y + angleDelta; + extraL->moveAngle = angle.y + angleDelta; if (health <= 0 || !(input & IN_ACTION)) { animSet(ANIM_FALL_HANG, true, 9); - cinfo.offset.y = cinfo.f.floor - getBounds().minY + 2; + cinfo.offset.y = cinfo.f.floor - getBoundingBox(true).minY + 2; c_applyOffset(); hSpeed = 2; vSpeed = 1; flags.gravity = true; + setWeaponState(WEAPON_STATE_FREE); return; } @@ -1838,7 +1933,7 @@ struct Lara : Item pos.z += cinfo.offset.z; } - int32 h = cinfo.f.floor - getBounds().minY; + int32 h = cinfo.f.floor - getBoundingBox(true).minY; if (abs(h) <= 256) { pos.y += h; } @@ -1926,10 +2021,11 @@ struct Lara : Item } } - void c_swim() { + void c_swim() + { c_angle(ANGLE_0); - collideRoom(this, LARA_HEIGHT_UW, LARA_HEIGHT_UW / 2); + collideRoom(LARA_HEIGHT_UW, LARA_HEIGHT_UW / 2); c_applyOffset(); @@ -1939,7 +2035,7 @@ struct Lara : Item void c_surf() { - collideRoom(this, LARA_HEIGHT_SURF + 100, LARA_HEIGHT_SURF); + collideRoom(LARA_HEIGHT_SURF + 100, LARA_HEIGHT_SURF); c_applyOffset(); @@ -2059,7 +2155,7 @@ struct Lara : Item cinfo.gapCeiling = 0; cinfo.stopOnSlant = true; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); if (c_checkCeiling()) return; @@ -2111,9 +2207,16 @@ struct Lara : Item flags.gravity = false; c_angle(ANGLE_0); + + if (input == (IN_UP | IN_ACTION)) // to check front climb up from STOP state + { + pos.x += (phd_sin(cinfo.angle) * 4) >> FIXED_SHIFT; + pos.z += (phd_cos(cinfo.angle) * 4) >> FIXED_SHIFT; + } + c_default(); - if (c_checkCeiling()) + if (c_checkClimbUp()) return; if (c_checkFall(100)) @@ -2150,7 +2253,7 @@ struct Lara : Item cinfo.gapCeiling = 0; cinfo.stopOnSlant = true; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); if (c_checkCeiling()) return; @@ -2254,7 +2357,7 @@ struct Lara : Item cinfo.gapNeg = WALL; cinfo.gapCeiling = 0; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); if (cinfo.m.ceiling > -100) { @@ -2278,7 +2381,7 @@ struct Lara : Item cinfo.gapCeiling = 0; cinfo.stopOnSlant = true; - collideRoom(this, LARA_HEIGHT, 0); + collideRoom(LARA_HEIGHT, 0); if (c_checkCeiling()) return; @@ -2558,12 +2661,52 @@ struct Lara : Item Lara(Room* room) : Item(room) { + int32 playerIndex = -1; + + for (int32 i = 0; i < X_COUNT(players); i++) + { + if (players[i] == this) { + playerIndex = i; + break; + } + } + + ASSERT(playerIndex != -1); + + extraL = &playersExtra[playerIndex]; + memset(extraL, 0, sizeof(*extraL)); + extraL->hitQuadrant = -1; + + extraL->weapon = extraL->goalWeapon = WEAPON_PISTOLS; // TODO LEVEL10A + setWeaponState(WEAPON_STATE_FREE); + + meshSwap(ITEM_LARA, 0xFFFFFFFF); + + bool isHome = false; + + if (isHome) { + meshSwap(ITEM_LARA_SPEC, JOINT_MASK_UPPER | JOINT_MASK_LOWER); + extraL->ammo[WEAPON_PISTOLS] = 0; + } else { + extraL->ammo[WEAPON_PISTOLS] = -1; + extraL->ammo[WEAPON_MAGNUMS] = -1; + extraL->ammo[WEAPON_UZIS] = -1; + extraL->ammo[WEAPON_SHOTGUN] = -1; + + if (extraL->weapon != WEAPON_MAX) + { + meshSwapPistols(JOINT_MASK_LEG_R1 | JOINT_MASK_LEG_L1, JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3); + // TODO check if shotgun on back + meshSwapShotgun(false); + } + + //extraL->weapon = extraL->goalWeapon = WEAPON_SHOTGUN; + } + health = LARA_MAX_HEALTH; oxygen = LARA_MAX_OXYGEN; flags.shadow = true; - activate(); - animSet(ANIM_STAND, true, 0); } @@ -2572,19 +2715,79 @@ struct Lara : Item { input = 0; - if (camera.mode == CAMERA_MODE_FREE) + if (gCamera->mode == CAMERA_MODE_FREE) return; if (keys & IK_LEFT) input |= IN_LEFT; if (keys & IK_RIGHT) input |= IN_RIGHT; if (keys & IK_UP) input |= IN_UP; if (keys & IK_DOWN) input |= IN_DOWN; - if (keys & IK_R) input |= IN_WALK; - if (keys & IK_A) input |= IN_ACTION; - if (keys & IK_B) input |= IN_JUMP; - if ((keys & (IK_L | IK_A)) == (IK_L | IK_A)) input |= IN_WEAPON; - if ((keys & (IK_L | IK_B)) == (IK_L | IK_B)) input |= IN_UP | IN_DOWN; - if ((keys & (IK_L | IK_R)) == (IK_L | IK_R)) input |= IN_LOOK; + + if (keys & IK_A) + { + if (keys & IK_L) { + if (extraL->weaponState != WEAPON_STATE_BUSY && extraL->ammo[WEAPON_PISTOLS] != 0) { + input |= IN_WEAPON; + } else { + input |= IN_ACTION; + } + } else { + input |= IN_ACTION; + } + } + + if (keys & IK_B) + { + if (keys & IK_L) { + input |= IN_UP | IN_DOWN; + } else { + input |= IN_JUMP; + } + } + + if (keys & IK_R) + { + if (keys & IK_L) { + input |= IN_LOOK; + } else { + input |= IN_WALK; + } + } + } + + void updateLook() + { + if (health <= 0) { + extraL->head.angle = vec3s(0, 0, 0); + } else if ((input & IN_LOOK) && gCamera->mode != CAMERA_MODE_FIXED) { + gCamera->lookAtItem = NULL; + + if (input & IN_UP) { + extraL->head.angle.x -= LARA_LOOK_TURN_SPEED; + } + + if (input & IN_DOWN) { + extraL->head.angle.x += LARA_LOOK_TURN_SPEED; + } + + if (input & IN_LEFT) { + extraL->head.angle.y -= LARA_LOOK_TURN_SPEED; + } + + if (input & IN_RIGHT) { + extraL->head.angle.y += LARA_LOOK_TURN_SPEED; + } + + extraL->head.angle.x = X_CLAMP(extraL->head.angle.x, LARA_LOOK_ANGLE_MIN, LARA_LOOK_ANGLE_MAX); + extraL->head.angle.y = X_CLAMP(extraL->head.angle.y, -LARA_LOOK_ANGLE_Y, LARA_LOOK_ANGLE_Y); + + input &= ~(IN_RIGHT | IN_LEFT | IN_UP | IN_DOWN); + } else if (gCamera->lastItem == NULL) { + extraL->head.angle.x = angleDec(extraL->head.angle.x, abs(extraL->head.angle.x) >> 3); + extraL->head.angle.y = angleDec(extraL->head.angle.y, abs(extraL->head.angle.y) >> 3); + } + + extraL->torso.angle = extraL->head.angle; } void updateWaterState() @@ -2620,19 +2823,19 @@ struct Lara : Item stopScreaming(); if (state == STATE_SWAN_DIVE) { - angle.x = -45 * DEG2SHORT; + angle.x = ANGLE(-45); goalState = STATE_UW_DIVE; animProcess(); vSpeed *= 2; //game->waterDrop(pos, 128.0f, 0.2f); } else if (state == STATE_FAST_DIVE) { - angle.x = -85 * DEG2SHORT; + angle.x = ANGLE(-85); goalState = STATE_UW_DIVE; animProcess(); vSpeed *= 2; //game->waterDrop(pos, 128.0f, 0.2f); } else { - angle.x = -45 * DEG2SHORT; + angle.x = ANGLE(-45); animSet(ANIM_WATER_FALL, true); state = STATE_UW_DIVE; // TODO check necessary goalState = STATE_UW_SWIM; @@ -2640,9 +2843,7 @@ struct Lara : Item //game->waterDrop(pos, 256.0f, 0.2f); } - //v2head.x = v2head.y = 0; - //v2torso.x = v2torso.y = 0; - //waterSplash(); + fxSplash(); } else if (waterDist > LARA_WADE_MIN_DEPTH) { waterState = WATER_STATE_WADE; if (!flags.gravity) { @@ -2671,8 +2872,6 @@ struct Lara : Item vSpeed = 0; angle.x = angle.z = 0; - //v2head.x = v2head.y = 0; - //v2torso.x = v2torso.y = 0; break; } @@ -2686,7 +2885,7 @@ struct Lara : Item pos.y -= (waterDist - 1); animSet(ANIM_SURF, true); vSpeed = 0; - swimTimer = LARA_SWIM_TIMER + 1; // block dive before we press jump button again + extraL->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 { @@ -2698,8 +2897,6 @@ struct Lara : Item } angle.x = angle.z = 0; - //v2head.x = v2head.y = 0; - //v2torso.x = v2torso.y = 0; break; } @@ -2725,12 +2922,10 @@ struct Lara : Item animSet(ANIM_SURF_SWIM, true); } - swimTimer = 0; + extraL->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; @@ -2750,8 +2945,8 @@ struct Lara : Item updateState(); - angle.z = angleDec(angle.z, 1 * DEG2SHORT); - turnSpeed = angleDec(turnSpeed, 2 * DEG2SHORT); + angle.z = angleDec(angle.z, ANGLE(1)); + turnSpeed = angleDec(turnSpeed, ANGLE(2)); angle.y += turnSpeed; } @@ -2770,12 +2965,12 @@ struct Lara : Item updateState(); - angle.z = angleDec(angle.z, 2 * DEG2SHORT); + angle.z = angleDec(angle.z, ANGLE(2)); - pos.x += (phd_sin(moveAngle) * vSpeed) >> 16; - pos.z += (phd_cos(moveAngle) * vSpeed) >> 16; + pos.x += (phd_sin(extraL->moveAngle) * vSpeed) >> 16; + pos.z += (phd_cos(extraL->moveAngle) * vSpeed) >> 16; - camera.targetAngleX = -22 * DEG2SHORT; + gCamera->targetAngleX = ANGLE(-22); } void updateUnder() @@ -2793,12 +2988,12 @@ struct Lara : Item updateState(); - angle.z = angleDec(angle.z, 2 * DEG2SHORT); - turnSpeed = angleDec(turnSpeed, 2 * DEG2SHORT); + angle.z = angleDec(angle.z, ANGLE(2)); + turnSpeed = angleDec(turnSpeed, ANGLE(2)); angle.y += turnSpeed; - angle.x = X_CLAMP(angle.x, -85 * DEG2SHORT, 85 * DEG2SHORT); - angle.z = X_CLAMP(angle.z, -22 * DEG2SHORT, 22 * DEG2SHORT); + angle.x = X_CLAMP(angle.x, ANGLE(-85), ANGLE(85)); + angle.z = X_CLAMP(angle.z, ANGLE(-22), ANGLE(22)); int32 c = phd_cos(angle.x); int32 s = phd_sin(angle.x); @@ -2808,15 +3003,717 @@ struct Lara : Item pos.z += (c * ((phd_cos(angle.y) * vSpeed) >> 16)) >> FIXED_SHIFT; } + bool weaponShoot(const ExtraInfoLara::Arm* arm) + { + int16 ammo = extraL->ammo[extraL->weapon]; + + if (!ammo) { + soundPlay(SND_EMPTY, pos); + extraL->goalWeapon = WEAPON_PISTOLS; + return false; + } + + if (ammo > 0) { + ammo--; + } + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + Location from; + from.pos.x = pos.x; + from.pos.y = pos.y - params.height; + from.pos.z = pos.z; + from.room = room; + + int32 aimX = int32(rand_logic() - 0x4000) * params.spread >> 16; + int32 aimY = int32(rand_logic() - 0x4000) * params.spread >> 16; + + aimX += arm->angle.x; + aimY += arm->angle.y; + aimY += angle.y; + + matrixSetView(from.pos, aimX, aimY); + + int32 minDist = INT_MAX; + + if (arm->target) + { + Sphere spheres[MAX_SPHERES]; + int32 spheresCount = arm->target->getSpheres(spheres, false); + + for (int32 i = 0; i < spheresCount; i++) + { + Sphere &s = spheres[i]; + + if (abs(s.center.x) >= s.radius) + continue; + + if (abs(s.center.y) >= s.radius) + continue; + + if (s.center.z <= s.radius) + continue; + + if (X_SQR(s.center.x) + X_SQR(s.center.y) > X_SQR(s.radius)) + continue; + + int32 dist = s.center.z - s.radius; + + if (dist < minDist) { + minDist = dist; + } + } + } + + vec3i dir = matrixGetDir(matrixGet()); + + Location to = from; + + if (minDist != INT_MAX) + { + to.pos.x += dir.x * minDist >> FIXED_SHIFT; + to.pos.y += dir.y * minDist >> FIXED_SHIFT; + to.pos.z += dir.z * minDist >> FIXED_SHIFT; + + arm->target->hit(params.damage, to.pos, 0); + } else { + to.pos += dir; + + trace(from, to); + fxRicochet(to.room, to.pos, true); + } + + return true; + } + + void setWeaponState(WeaponState weaponState) + { + if (weaponState == extraL->weaponState) + return; + extraL->weaponState = weaponState; + + if (weaponState == WEAPON_STATE_DRAW) + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + int32 anim = (extraL->weapon == WEAPON_SHOTGUN) ? ANIM_SHOTGUN_DRAW : ANIM_PISTOLS_PICK; + extraL->armR.animIndex = extraL->armL.animIndex = models[params.animType].animIndex + anim; + extraL->armR.frameIndex = extraL->armL.frameIndex = 0; + } + + if (weaponState == WEAPON_STATE_FREE) + { + extraL->armR.useBasis = extraL->armL.useBasis = false; + extraL->armR.animIndex = extraL->armL.animIndex = 0; + extraL->armR.frameIndex = extraL->armL.frameIndex = 0; + } + } + + void weaponAim(ExtraInfoLara::Arm &arm) + { + if (arm.aim) { + arm.angle.x = angleLerp(arm.angle.x, arm.angleAim.x, ANGLE(10)); + arm.angle.y = angleLerp(arm.angle.y, arm.angleAim.y, ANGLE(10)); + } else { + arm.angle.x = angleLerp(arm.angle.x, 0, ANGLE(10)); + arm.angle.y = angleLerp(arm.angle.y, 0, ANGLE(10)); + } + } + + void weaponDrawPistols() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm* arm = &extraL->armR; + + const Anim* animPtr = level.anims + arm->animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm->frameIndex + 1; + int32 anim = arm->animIndex - models[params.animType].animIndex; + + if (frame > animLength) + { + anim++; + + if (anim == ANIM_PISTOLS_DRAW) { + meshSwapPistols(JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3, JOINT_MASK_LEG_R1 | JOINT_MASK_LEG_L1); + soundPlay(SND_DRAW, pos); + } else if (anim == ANIM_PISTOLS_FIRE) { + anim = ANIM_PISTOLS_AIM; + setWeaponState(WEAPON_STATE_READY); + } + + frame = 0; + } + + extraL->armR.angle = extraL->armL.angle = vec3s(0, 0, 0); + extraL->armR.animIndex = extraL->armL.animIndex = anim + models[params.animType].animIndex; + extraL->armR.frameIndex = extraL->armL.frameIndex = frame; + } + + void weaponHolsterPistols() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + ExtraInfoLara::Arm* arm = &extraL->armR + i; + + if (!arm->animIndex) + continue; + + int32 frame = arm->frameIndex; + int32 anim = arm->animIndex - models[params.animType].animIndex; + + if (frame) + { + if (anim == ANIM_PISTOLS_AIM) { + arm->angle.x -= arm->angle.x / frame; + arm->angle.y -= arm->angle.y / frame; + } + + if (anim == ANIM_PISTOLS_FIRE) { + frame = 0; + } else { + frame--; + } + } else { + if (anim == ANIM_PISTOLS_AIM) { + anim = ANIM_PISTOLS_DRAW; + } else if (anim == ANIM_PISTOLS_PICK) { + arm->animIndex = 0; + continue; + } else if (anim == ANIM_PISTOLS_DRAW) { + anim = ANIM_PISTOLS_PICK; + if (i == LARA_ARM_R) { + meshSwapPistols(JOINT_MASK_LEG_R1, JOINT_MASK_ARM_R3); + } else { + meshSwapPistols(JOINT_MASK_LEG_L1, JOINT_MASK_ARM_L3); + } + soundPlay(SND_HOLSTER, pos); + } else if (anim == ANIM_PISTOLS_FIRE) { + anim = ANIM_PISTOLS_AIM; + } + + arm->animIndex = anim + models[params.animType].animIndex; + frame = level.anims[arm->animIndex].frameEnd - level.anims[arm->animIndex].frameBegin; + } + + arm->frameIndex = frame; + } + + if (!extraL->armR.animIndex && !extraL->armL.animIndex) { + setWeaponState(WEAPON_STATE_FREE); + } + } + + void weaponDrawShotgun() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm &arm = extraL->armR; + + const Anim* animPtr = level.anims + arm.animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm.frameIndex + 1; + int32 anim = arm.animIndex - models[params.animType].animIndex; + + ASSERT(anim == ANIM_SHOTGUN_DRAW); + + if (frame == 10) { + meshSwapShotgun(true); + soundPlay(SND_DRAW, pos); + } + + if (frame == animLength) { + setWeaponState(WEAPON_STATE_READY); + } + + extraL->armR.angle = extraL->armL.angle = vec3s(0, 0, 0); + extraL->armR.animIndex = extraL->armL.animIndex = anim + models[params.animType].animIndex; + extraL->armR.frameIndex = extraL->armL.frameIndex = frame; + } + + void weaponHolsterShotgun() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm &arm = extraL->armR; + + int32 frame = arm.frameIndex; + int32 anim = arm.animIndex - models[params.animType].animIndex; + + if (anim == ANIM_SHOTGUN_AIM) { + if (frame == 0) { + anim = ANIM_SHOTGUN_DRAW; + const Anim* animPtr = level.anims + models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin; + } else { + frame--; + } + } else if (anim == ANIM_SHOTGUN_FIRE) { + frame++; + if (frame > 12) { + anim = ANIM_SHOTGUN_DRAW; + const Anim* animPtr = level.anims + models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin; + } + } else if (anim == ANIM_SHOTGUN_DRAW) { + if (frame == 0) { + setWeaponState(WEAPON_STATE_FREE); + return; + } else { + if (frame == 10) { + meshSwapShotgun(false); + soundPlay(SND_HOLSTER, pos); + } + frame--; + } + } + + extraL->armR.angle = extraL->armL.angle = vec3s(0, 0, 0); + extraL->armR.animIndex = extraL->armL.animIndex = anim + models[params.animType].animIndex; + extraL->armR.frameIndex = extraL->armL.frameIndex = frame; + } + + void weaponDraw() + { + switch (extraL->weapon) + { + case WEAPON_PISTOLS: + case WEAPON_MAGNUMS: + case WEAPON_UZIS: + weaponDrawPistols(); + break; + case WEAPON_SHOTGUN: + weaponDrawShotgun(); + break; + default: ASSERT(false); + } + } + + void weaponHolster() + { + meshSwap(ITEM_LARA, JOINT_MASK_HEAD); + + switch (extraL->weapon) + { + case WEAPON_PISTOLS: + case WEAPON_MAGNUMS: + case WEAPON_UZIS: + weaponHolsterPistols(); + break; + case WEAPON_SHOTGUN: + weaponHolsterShotgun(); + break; + default: ASSERT(false); + } + } + + void weaponUpdatePistols() + { + ExtraInfoLara::Arm &R = extraL->armR; + ExtraInfoLara::Arm &L = extraL->armL; + vec3s &H = extraL->head.angle; + vec3s &T = extraL->torso.angle; + + weaponAim(R); + weaponAim(L); + + int32 aX = R.angle.x + L.angle.x; + int32 aY = R.angle.y + L.angle.y; + + if (R.aim && L.aim) { + H.x = T.x = aX >> 2; + H.y = T.y = aY >> 2; + } else if (R.aim ^ L.aim) { + H.x = T.x = aX >> 1; + H.y = T.y = aY >> 1; + } + + bool shotFlag = false; + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + ExtraInfoLara::Arm* arm = &extraL->armR + i; + + const Anim* animPtr = level.anims + arm->animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm->frameIndex; + int32 anim = arm->animIndex - models[params.animType].animIndex; + + if (((input & IN_ACTION) && !arm->target) || arm->aim) + { + if (anim == ANIM_PISTOLS_AIM) + { + if (frame == animLength) + { + if ((input & IN_ACTION) && weaponShoot(arm)) + { + anim = ANIM_PISTOLS_FIRE; + frame = 0; + + arm->flash.timer = params.flashTimer; + arm->flash.angle = int16(rand_draw() << 1); + arm->flash.offset = params.flashOffset; + arm->flash.intensity = params.flashIntensity << 8; + + if (!shotFlag) // skip sound replay if double shoot + { + shotFlag = true; + soundPlay(params.soundId, pos); + } + } + } else { + frame++; + } + } else { // ANIM_DUAL_FIRE + frame++; + if (frame == params.reloadTimer) + { + anim = ANIM_PISTOLS_AIM; + const Anim* animPtr = level.anims + anim + models[params.animType].animIndex; + frame = animPtr->frameEnd - animPtr->frameBegin; + } + } + } else { + if (anim == ANIM_PISTOLS_FIRE) + { + anim = ANIM_PISTOLS_AIM; + const Anim* animPtr = level.anims + anim + models[params.animType].animIndex; + frame = animPtr->frameEnd - animPtr->frameBegin; + } else if (frame) { + frame--; + }; + } + + arm->animIndex = anim + models[params.animType].animIndex; + arm->frameIndex = frame; + arm->useBasis = (anim == ANIM_PISTOLS_AIM && frame) || (anim == ANIM_PISTOLS_FIRE); + } + } + + void weaponUpdateShotgun() + { + ExtraInfoLara::Arm &R = extraL->armR; + vec3s &H = extraL->head.angle; + vec3s &T = extraL->torso.angle; + + weaponAim(R); + + if (R.aim) + { + T.x = R.angle.x >> 1; + T.y = R.angle.y >> 1; + H.x = H.y = 0; + } + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm* arm = &extraL->armR; + + const Anim* animPtr = level.anims + arm->animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm->frameIndex; + int32 anim = arm->animIndex - models[params.animType].animIndex; + + switch (anim) + { + case ANIM_SHOTGUN_FIRE: + { + frame++; + if (frame == 10) { + soundPlay(SND_SHOTGUN_RELOAD, pos); + } else if (frame == params.reloadTimer) { + anim = ANIM_SHOTGUN_AIM; + animPtr = level.anims + models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin; + } else if ((animLength - frame < 10) && !(input & IN_ACTION)) { + anim = ANIM_SHOTGUN_AIM; + frame = animLength - frame; // how many frames left for fire animation + animPtr = level.anims + models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin - frame; // offset aim frames from the end + } + break; + } + case ANIM_SHOTGUN_DRAW: + { + if ((input & IN_ACTION) || arm->aim) { + anim = ANIM_SHOTGUN_AIM; + frame = 1; + } + break; + } + case ANIM_SHOTGUN_AIM: + { + if ((input & IN_ACTION) || arm->aim) + { + if (frame == animLength) + { + frame = 1; + anim = ANIM_SHOTGUN_FIRE; + + for (int32 i = 0; i < 6; i++) + { + if (!weaponShoot(arm)) + break; + + if (i == 5) { + soundPlay(params.soundId, pos); + } + } + } else { + frame++; + } + } else { + if (frame == 0) { + anim = ANIM_SHOTGUN_DRAW; + animPtr = level.anims + models[params.animType].animIndex + anim; + animLength = animPtr->frameEnd - animPtr->frameBegin; + frame = animLength; + } else { + frame--; + } + } + break; + } + } + + extraL->armR.angle = extraL->armL.angle = vec3s(0, 0, 0); + extraL->armR.animIndex = extraL->armL.animIndex = anim + models[params.animType].animIndex; + extraL->armR.frameIndex = extraL->armL.frameIndex = frame; + } + + void weaponUpdateState() + { + bool change = false; + if (waterState == WATER_STATE_ABOVE || waterState == WATER_STATE_WADE) + { + if (extraL->weapon != extraL->goalWeapon) + { + if (extraL->weaponState == WEAPON_STATE_FREE) { + extraL->weapon = extraL->goalWeapon; + change = true; + } else if (extraL->weaponState == WEAPON_STATE_READY) { + change = true; + } + } else if (input & IN_WEAPON) { + change = true; + } + } else if (extraL->weaponState == WEAPON_STATE_READY) { + change = true; + } + + if (!change) + return; + + if (extraL->weaponState == WEAPON_STATE_FREE) { + setWeaponState(WEAPON_STATE_DRAW); + } + + if (extraL->weaponState == WEAPON_STATE_READY) { + setWeaponState(WEAPON_STATE_HOLSTER); + } + } + + void weaponGetAimPoint(Item* target, Location &point) + { + const Bounds &box = target->getBoundingBox(false); + vec3i p; + p.x = (box.minX + box.maxX) >> 1; + p.y = box.minY + (box.maxY - box.minY) / 3; + p.z = (box.minZ + box.maxZ) >> 1; + int32 s = phd_sin(target->angle.y); + int32 c = phd_cos(target->angle.y); + X_ROTXY(p.x, p.z, -s, c); + + point.pos = target->pos + p; + point.room = target->room; + } + + void weaponTrackTargets() + { + ExtraInfoLara::Arm &arm = extraL->armR; + + if (arm.target && arm.target->health <= 0 && gSettings.controls.retarget) + { + arm.target = NULL; + } + + if (!arm.target) + { + extraL->armR.aim = extraL->armL.aim = false; + return; + } + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + Location from; + from.pos.x = pos.x; + from.pos.y = pos.y - params.height; + from.pos.z = pos.z; + from.room = room; + + Location to; + weaponGetAimPoint(arm.target, to); + + vec3i dir = to.pos - from.pos; + vec3s angleAim; + + anglesFromVector(dir.x, dir.y, dir.z, angleAim.x, angleAim.y); + + angleAim.x -= angle.x; + angleAim.y -= angle.y; + + if (trace(from, to)) + { + if (abs(angleAim.x) <= params.aimX && abs(angleAim.y) <= params.aimY) { + extraL->armR.aim = extraL->armL.aim = true; + } else { + extraL->armR.aim &= abs(angleAim.x) <= params.armX && angleAim.y >= params.armMinY && angleAim.y <= params.armMaxY; + extraL->armL.aim &= abs(angleAim.x) <= params.armX && angleAim.y >= -params.armMaxY && angleAim.y <= -params.armMinY; + } + } else { + extraL->armR.aim = extraL->armL.aim = false; + } + + extraL->armR.angleAim = extraL->armL.angleAim = angleAim; + } + + void weaponFindTargets() + { + if (!Item::sFirstActive) + return; + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + int32 range = params.range; + int32 rangeQ = X_SQR(range); + int32 minAimY = params.aimY; + + Location from; + from.pos.x = pos.x; + from.pos.y = pos.y - params.height; + from.pos.z = pos.z; + from.room = room; + + Item* item = Item::sFirstActive; + do + { + if (item->health <= 0) + continue; + + if (item->flags.status != ITEM_FLAGS_STATUS_ACTIVE) + continue; + + vec3i d = item->pos - pos; + int32 distQ = X_SQR(d.x) + X_SQR(d.y) + X_SQR(d.z); + + if (distQ > rangeQ) + continue; + + Location to; + weaponGetAimPoint(item, to); + + if (!trace(from, to)) + continue; + + vec3i dir = to.pos - from.pos; + vec3s angleAim; + + anglesFromVector(dir.x, dir.y, dir.z, angleAim.x, angleAim.y); + + angleAim.x -= angle.x + extraL->torso.angle.x; + angleAim.y -= angle.y + extraL->torso.angle.y; + + angleAim.x = abs(angleAim.x); + angleAim.y = abs(angleAim.y); + + if (angleAim.x > params.aimX || angleAim.y > params.aimY || angleAim.y > minAimY) + continue; + + minAimY = angleAim.y; + extraL->armR.target = item; + } while ((item = item->nextActive)); + } + + void weaponUpdateTargets() + { + if (input & IN_ACTION) { + meshSwap(ITEM_LARA_UZIS, JOINT_MASK_HEAD); + } else { + meshSwap(ITEM_LARA, JOINT_MASK_HEAD); + extraL->armR.target = NULL; + } + + if (extraL->armR.target == NULL) { + weaponFindTargets(); + } + + weaponTrackTargets(); + + extraL->armL.target = extraL->armR.target; + } + void updateWeapon() { - // TODO + if (extraL->armR.flash.timer) { + extraL->armR.flash.timer--; + } + + if (extraL->armL.flash.timer) { + extraL->armL.flash.timer--; + } + + if (extraL->weapon == WEAPON_MAX) + return; + + if (health <= 0) + return; + + weaponUpdateState(); + + switch (extraL->weaponState) + { + case WEAPON_STATE_DRAW: + { + gCamera->toCombat(); + weaponDraw(); + break; + } + + case WEAPON_STATE_HOLSTER: + { + weaponHolster(); + break; + } + + case WEAPON_STATE_READY: + { + gCamera->toCombat(); + + weaponUpdateTargets(); + + if (extraL->weapon < WEAPON_SHOTGUN) { + weaponUpdatePistols(); + } else { + weaponUpdateShotgun(); + } + } + + default: ; + } } virtual void update() { + gCamera = extraL->camera; + ASSERT(gCamera); + updateInput(); + updateLook(); + updateWaterState(); if (health > 0) @@ -2858,31 +3755,65 @@ struct Lara : Item updateWeapon(); checkTrigger(cinfo.trigger, this); + + extraL->camera->update(); } - void meshSwap(uint16* meshList, uint16 start, uint32 mask) + void meshSwap(ItemType type, uint32 mask) { - for (int32 i = 0; i < JOINT_MAX; i++) + int32 start = models[type].start; + + for (int32 i = 0; i < JOINT_MAX && mask; i++, mask >>= 1) { - if ((1 << i) & mask) - { - meshList[i] = start + i; + if (mask & 1) { + extraL->meshes[i] = start + i; } } } + void meshSwapPistols(uint32 weaponMask, uint32 bodyMask) + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + meshSwap(ITEM_LARA, bodyMask); + meshSwap(params.modelType, weaponMask); + } + + void meshSwapShotgun(bool armed) + { + const WeaponParams ¶ms = weaponParams[WEAPON_SHOTGUN]; + + if (armed) { + meshSwap(ITEM_LARA, JOINT_MASK_TORSO); + meshSwap(params.modelType, JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3); + } else { + meshSwap(ITEM_LARA, JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3); + meshSwap(params.modelType, JOINT_MASK_TORSO); + } + } + virtual void draw() { - uint16 meshList[JOINT_MAX]; + int32 tmpAnimIndex = animIndex; + int32 tmpFrameIndex = frameIndex; - meshSwap(meshList, models[ITEM_LARA].start, 0xFFFFFFFF); + if (extraL->hitQuadrant != -1) + { + switch (extraL->hitQuadrant) + { + case 0 : animIndex = ANIM_HIT_FRONT; break; + case 1 : animIndex = ANIM_HIT_LEFT; break; + case 2 : animIndex = ANIM_HIT_BACK; break; + case 3 : animIndex = ANIM_HIT_RIGHT; break; + default : ASSERT(false); + } + frameIndex = level.anims[animIndex].frameBegin + extraL->hitFrame; + } - // gym - // t-rex death - // midas death (gold) - //meshSwap(meshList, models[ITEM_LARA_SPEC].start, JOINT_MASK_UPPER | JOINT_MASK_LOWER); + drawModel(this); - drawModel(this, meshList); + animIndex = tmpAnimIndex; + frameIndex = tmpFrameIndex; } }; @@ -2899,11 +3830,31 @@ int32 doTutorial(Item* lara, int32 track) { switch (track) { - case 28 : if (gSaveGame.tracks[track].once && lara->state == Lara::STATE_JUMP_UP) return 29; + case 28 : + if (gSaveGame.tracks[track].once && lara->state == Lara::STATE_JUMP_UP) { + track = 29; + } + break; + case 37 : - 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 41 : + if (lara->state != Lara::STATE_HANG) { + track = 0; + } + break; + + case 42 : + if (gSaveGame.tracks[track].once && lara->state == Lara::STATE_HANG) { + track = 43; + } + break; + + case 49 : + if (lara->state != Lara::STATE_SURF_TREAD) { + track = 0; + } + break; + case 50 : // end of GYM if (gSaveGame.tracks[track].once) { //timer += Core::deltaTime; @@ -2911,7 +3862,7 @@ int32 doTutorial(Item* lara, int32 track) // game->loadNextLevel(); } else { if (lara->state != Lara::STATE_WATER_OUT) - return 0; + track = 0; //timer = 0.0f; } break; @@ -2920,8 +3871,6 @@ int32 doTutorial(Item* lara, int32 track) return track; } -Lara* players[2]; - Lara* getLara(const vec3i &pos) { return players[0]; // TODO find nearest player diff --git a/src/platform/gba/level.h b/src/platform/gba/level.h index 42d40a2b..d9f6a37f 100644 --- a/src/platform/gba/level.h +++ b/src/platform/gba/level.h @@ -2,6 +2,7 @@ #define H_LEVEL #include "common.h" +#include "camera.h" Level level; @@ -22,6 +23,7 @@ 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 const Mesh* meshes[MAX_MESHES]; EWRAM_DATA StaticMesh staticMeshes[MAX_STATIC_MESHES]; EWRAM_DATA FixedCamera cameras[MAX_CAMERAS]; @@ -30,33 +32,8 @@ Item* Item::sFirstFree; Room* roomsList[MAX_ROOM_LIST]; -void fixLightmap(uint16* palette, int32 palIndex) -{ - uint16 color = palette[palIndex]; - - int32 r = 0x1F & (color); - int32 g = 0x1F & (color >> 5); - int32 b = 0x1F & (color >> 10); - - for (int32 i = 0; i < 32; i++) - { - int32 lum = 31 - i; - - int32 lumR = X_CLAMP((r * lum) / 14, 0, 31); - int32 lumG = X_CLAMP((g * lum) / 14, 0, 31); - int32 lumB = X_CLAMP((b * lum) / 14, 0, 31); - - palette[lightmap[i * 256 + palIndex]] = lumR | (lumG << 5) | (lumB << 10); - } -} - void readLevel(const uint8* data) { - Item::sFirstActive = NULL; - Item::sFirstFree = NULL; - - dynSectorsCount = 0; - memcpy(&level, data, sizeof(level)); { // fix level data offsets @@ -88,6 +65,8 @@ void readLevel(const uint8* data) // initialize global pointers #ifdef MODE_PAL paletteSet(level.palette); +#else + memcpy(palette, level.palette, sizeof(palette)); #endif memcpy(lightmap, level.lightmap, sizeof(lightmap)); @@ -107,6 +86,12 @@ void readLevel(const uint8* data) models[model->type] = *model; } + // prepare meshes + for (int32 i = 0; i < level.meshesCount; i++) + { + meshes[i] = (Mesh*)(level.meshData + level.meshOffsets[i]); + } + // prepare static meshes // TODO preprocess memset(staticMeshes, 0, sizeof(staticMeshes)); for (int32 i = 0; i < level.staticMeshesCount; i++) @@ -132,13 +117,497 @@ void readLevel(const uint8* data) // prepare fixed cameras memcpy(cameras, level.cameras, level.camerasCount * sizeof(FixedCamera)); +} + +#define TRACE_SHIFT 10 // trace precision + +#define TRACE_CHECK(r, x, y, z) \ +{ \ + const Sector* sector = r->getSector(x, z); \ + if (y > sector->getFloor(x, y, z) || y < sector->getCeiling(x, y, z)) \ + { \ + to.pos = p; \ + to.room = room; \ + return false; \ + } \ +} + +bool traceX(const Location &from, Location &to) +{ + vec3i d = to.pos - from.pos; + + if (!d.x) + return true; + + d.y = (d.y << TRACE_SHIFT) / d.x; + d.z = (d.z << TRACE_SHIFT) / d.x; + + vec3i p = from.pos; + + Room* room = from.room; + + if (d.x < 0) + { + d.x = 1024; + p.x &= ~1023; + p.y += d.y * (p.x - from.pos.x) >> TRACE_SHIFT; + p.z += d.z * (p.x - from.pos.x) >> TRACE_SHIFT; + + while (p.x > to.pos.x) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x - 1, p.y, p.z); + TRACE_CHECK(nextRoom, p.x - 1, p.y, p.z); + + room = nextRoom; + p -= d; + } + } + else + { + d.x = 1024; + p.x |= 1023; + p.y += d.y * (p.x - from.pos.x) >> TRACE_SHIFT; + p.z += d.z * (p.x - from.pos.x) >> TRACE_SHIFT; + + while (p.x < to.pos.x) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x + 1, p.y, p.z); + TRACE_CHECK(nextRoom, p.x + 1, p.y, p.z); + + room = nextRoom; + p += d; + } + } + + to.room = room; + + return true; +} + +bool traceZ(const Location &from, Location &to) +{ + vec3i d = to.pos - from.pos; + + if (!d.z) + return true; + + d.x = (d.x << TRACE_SHIFT) / d.z; + d.y = (d.y << TRACE_SHIFT) / d.z; + + vec3i p = from.pos; + + Room* room = from.room; + + if (d.z < 0) + { + d.z = 1024; + p.z &= ~1023; + p.x += d.x * (p.z - from.pos.z) >> TRACE_SHIFT; + p.y += d.y * (p.z - from.pos.z) >> TRACE_SHIFT; + + while (p.z > to.pos.z) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x, p.y, p.z - 1); + TRACE_CHECK(nextRoom, p.x, p.y, p.z - 1); + + room = nextRoom; + p -= d; + } + } + else + { + d.z = 1024; + p.z |= 1023; + p.x += d.x * (p.z - from.pos.z) >> TRACE_SHIFT; + p.y += d.y * (p.z - from.pos.z) >> TRACE_SHIFT; + + while (p.z < to.pos.z) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x, p.y, p.z + 1); + TRACE_CHECK(nextRoom, p.x, p.y, p.z + 1); + + room = nextRoom; + p += d; + } + } + + to.room = room; + + return true; +} + +#undef TRACE_CHECK + +bool trace(const Location &from, Location &to) +{ + int32 dx = abs(to.pos.x - from.pos.x); + int32 dz = abs(to.pos.z - from.pos.z); + int32 dy; + + bool res; + + if (dz > dx) { + res = traceX(from, to); + if (!traceZ(from, to)) + return false; + } else { + res = traceZ(from, to); + if (!traceX(from, to)) + return false; + } + + dy = to.pos.y - from.pos.y; + + if (dy) + { + const Sector* sector = to.room->getSector(to.pos.x, to.pos.z); + + int32 h = sector->getFloor(to.pos.x, to.pos.y, to.pos.z); + if (to.pos.y <= h || from.pos.y >= h) + { + h = sector->getCeiling(to.pos.x, to.pos.y, to.pos.z); + if (to.pos.y >= h || from.pos.y <= h) + { + h = WALL; + } + } + + if (h != WALL) + { + to.pos.y = h; + h -= from.pos.y; + to.pos.x = from.pos.x + (to.pos.x - from.pos.x) * h / dy; + to.pos.z = from.pos.z + (to.pos.z - from.pos.z) * h / dy; + return false; + } + } + + return res; +} + +void checkCamera(const FloorData* fd) +{ + if (gCamera->mode == CAMERA_MODE_OBJECT) + return; + + bool checkItem = true; + + while (1) + { + FloorData::TriggerCommand triggerCmd = (fd++)->triggerCmd; + + switch (triggerCmd.action) + { + case TRIGGER_ACTION_ACTIVATE_CAMERA: + { + triggerCmd.end = (fd++)->triggerCmd.end; + + if (triggerCmd.args != gCamera->lastIndex) + { + gCamera->lookAtItem = NULL; + break; + } + + gCamera->index = triggerCmd.args; + + if (gCamera->timer < 0 || gCamera->mode == CAMERA_MODE_LOOK || gCamera->mode == CAMERA_MODE_COMBAT) + { + gCamera->timer = -1; + gCamera->lookAtItem = NULL; + break; + } + + gCamera->mode = CAMERA_MODE_FIXED; + checkItem = false; + break; + } + + case TRIGGER_ACTION_CAMERA_TARGET: + { + if (gCamera->mode == CAMERA_MODE_LOOK || gCamera->mode == CAMERA_MODE_COMBAT) + break; + + ASSERT(triggerCmd.args < level.itemsCount); + gCamera->lookAtItem = items + triggerCmd.args; + break; + } + + case TRIGGER_ACTION_FLYBY: + { + triggerCmd.end = (fd++)->triggerCmd.end; + break; + } + } + + if (triggerCmd.end) + break; + }; + + if (checkItem && gCamera->lookAtItem && gCamera->lookAtItem != gCamera->lastItem && gCamera->lookAtItem->flags.animated) { + gCamera->lookAtItem = NULL; + } +} + +void checkTrigger(const FloorData* fd, Item* lara) +{ + if (!fd) + return; + + if (fd->cmd.func == FLOOR_TYPE_LAVA) + { + // TODO lava + + if (fd->cmd.end) + return; + + fd++; + } + + FloorData::Command cmd = (fd++)->cmd; + FloorData::TriggerInfo info = (fd++)->triggerInfo; + + Item* switchItem = NULL; + Item* cameraItem = NULL; + + checkCamera(fd); + + if (!lara && cmd.type != TRIGGER_TYPE_OBJECT) + return; + + if (lara) + { + switch (cmd.type) + { + case TRIGGER_TYPE_ACTIVATE: + break; + + case TRIGGER_TYPE_PAD: + case TRIGGER_TYPE_ANTIPAD: + { + 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: + { + Item* keyItem = items + fd->triggerCmd.args; + if (!useKey(keyItem, lara)) + return; + fd++; + break; + } + + case TRIGGER_TYPE_PICKUP: + { + Item* pickupItem = items + fd->triggerCmd.args; + if (!usePickup(pickupItem)) + return; + fd++; + break; + } + + case TRIGGER_TYPE_OBJECT: + return; + + case TRIGGER_TYPE_COMBAT: + { + if (lara->extraL->weaponState != WEAPON_STATE_READY) + return; + break; + } + + case TRIGGER_TYPE_DUMMY: + return; + } + } + + while (1) + { + FloorData::TriggerCommand triggerCmd = (fd++)->triggerCmd; + + switch (triggerCmd.action) + { + 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) { + item->timer *= 30; + } + + if (cmd.type == TRIGGER_TYPE_SWITCH) { + item->flags.mask ^= info.mask; + } else if (cmd.type == TRIGGER_TYPE_ANTIPAD) { + item->flags.mask &= ~info.mask; + } else { + item->flags.mask |= info.mask; + } + + if (item->flags.mask != ITEM_FLAGS_MASK_ALL) + break; + + item->flags.once |= info.once; + + if (item->flags.active) + break; + + item->activate(); + + if (item->flags.status == ITEM_FLAGS_STATUS_NONE) { + item->flags.status = ITEM_FLAGS_STATUS_ACTIVE; + } + + break; + } + + case TRIGGER_ACTION_ACTIVATE_CAMERA: + { + FloorData::TriggerCommand cam = (fd++)->triggerCmd; + triggerCmd.end = cam.end; + + if (cameras[triggerCmd.args].flags.once) + break; + + gCamera->index = triggerCmd.args; + + if (gCamera->mode == CAMERA_MODE_LOOK || gCamera->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 || gCamera->index != gCamera->lastIndex) + { + gCamera->timer = cam.timer; + if (gCamera->timer != 1) { + gCamera->timer *= 30; + } + + if (cam.once) { + cameras[gCamera->index].flags.once = true; + } + + gCamera->speed = (cam.speed << 3) + 1; + gCamera->mode = lara ? CAMERA_MODE_FIXED : CAMERA_MODE_OBJECT; + } + break; + } + + case TRIGGER_ACTION_FLOW: + // TODO flow + break; + + case TRIGGER_ACTION_FLIP: + // TODO flipmap + break; + + case TRIGGER_ACTION_FLIP_ON: + // TODO flipmap + break; + + case TRIGGER_ACTION_FLIP_OFF: + // TODO flipmap + break; + + case TRIGGER_ACTION_CAMERA_TARGET: + { + cameraItem = items + triggerCmd.args; + break; + } + + case TRIGGER_ACTION_END: + // TODO go to the next level + break; + + case TRIGGER_ACTION_SOUNDTRACK: + { + int32 track = doTutorial(lara, triggerCmd.args); + + if (track == 0) break; + + SaveGame::TrackFlags &flags = gSaveGame.tracks[track]; + + if (flags.once) + break; + + if (cmd.type == TRIGGER_TYPE_SWITCH) + flags.mask ^= info.mask; + else if (cmd.type == TRIGGER_TYPE_ANTIPAD) + flags.mask &= ~info.mask; + else + flags.mask |= info.mask; + + if (flags.mask == ITEM_FLAGS_MASK_ALL) { + flags.once |= info.once; + musicPlay(track); + } else { + musicStop(); + } + break; + } + + case TRIGGER_ACTION_EFFECT: + // TODO effect + break; + + case TRIGGER_ACTION_SECRET: + { + if (gSaveGame.secrets & (1 << triggerCmd.args)) + break; + gSaveGame.secrets |= (1 << triggerCmd.args); + musicPlay(13); + break; + } + + case TRIGGER_ACTION_CLEAR_BODIES: + break; + + case TRIGGER_ACTION_FLYBY: + triggerCmd.end = (fd++)->triggerCmd.end; + break; + + case TRIGGER_ACTION_CUTSCENE: + break; + } + + if (triggerCmd.end) + break; + }; - // prepare free list - for (int32 i = MAX_ITEMS - 1; i >= level.itemsCount; i--) + if (cameraItem && (gCamera->mode == CAMERA_MODE_FIXED || gCamera->mode == CAMERA_MODE_OBJECT)) { - items[i].nextItem = items + i + 1; + gCamera->lookAtItem = cameraItem; } - Item::sFirstFree = items + level.itemsCount; } #endif diff --git a/src/platform/gba/main.cpp b/src/platform/gba/main.cpp index cb5a7c1a..75ea0d38 100644 --- a/src/platform/gba/main.cpp +++ b/src/platform/gba/main.cpp @@ -1,9 +1,9 @@ #if defined(_WIN32) || defined(__DOS__) - const void* TRACK_13_WAV; + const void* TRACKS_IMA; const void* levelData; #define LEVEL_NAME "LEVEL1.PKD" #elif defined(__GBA__) - #include "TRACK_13_WAV.h" + #include "TRACKS_IMA.h" #include "LEVEL1_PKD.h" const void* levelData = LEVEL1_PKD; #elif defined(__TNS__) @@ -259,14 +259,6 @@ int32 fpsCounter = 0; } #endif -#ifdef PROFILE - uint32 dbg_transform; - uint32 dbg_poly; - uint32 dbg_flush; - uint32 dbg_vert_count; - uint32 dbg_poly_count; -#endif - EWRAM_DATA ALIGN16 uint8 soundBufferA[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt #ifdef USE_9BIT_SOUND EWRAM_DATA ALIGN16 uint8 soundBufferB[2 * SND_SAMPLES + 32]; // for 9-bit mixer support via Direct Mixer B channel @@ -310,9 +302,11 @@ void soundInit() mixer.init(); REG_SOUNDCNT_X = SSTAT_ENABLE; - REG_SOUNDCNT_H = SDS_ATMR0 | SDS_A100 | SDS_AL | SDS_AR | SDS_ARESET; #ifdef USE_9BIT_SOUND - REG_SOUNDCNT_H |= SDS_BTMR0 | SDS_B100 | SDS_BL | SDS_BR | SDS_BRESET; + REG_SOUNDCNT_H = SDS_ATMR0 | SDS_AL | SDS_AR | SDS_ARESET | SDS_A50 | + SDS_BTMR0 | SDS_BL | SDS_BR | SDS_BRESET | SDS_B50; +#else + REG_SOUNDCNT_H = SDS_ATMR0 | SDS_AL | SDS_AR | SDS_ARESET | SDS_A100; #endif REG_TM0D = 65536 - (16777216 / SND_OUTPUT_FREQ); REG_TM0CNT = TM_ENABLE; @@ -357,15 +351,6 @@ void blit() { } const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), FRAME_WIDTH, -FRAME_HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; StretchDIBits(hDC, 0, 0, WND_WIDTH, WND_HEIGHT, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY); -#elif defined(ROTATE90_MODE) - for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; i++) { - int32 x = FRAME_HEIGHT - (i % FRAME_HEIGHT) - 1; - int32 y = i / FRAME_HEIGHT; - uint16 c = ((uint16*)fb)[x * FRAME_WIDTH + y]; - SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; - } - const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), FRAME_HEIGHT, -FRAME_WIDTH, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; - StretchDIBits(hDC, 0, 0, WND_WIDTH, WND_HEIGHT, 0, 0, FRAME_HEIGHT, FRAME_WIDTH, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY); #else for (int i = 0; i < VRAM_WIDTH * FRAME_HEIGHT; i++) { uint16 c = ((uint16*)fb)[i]; @@ -403,6 +388,11 @@ LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { case VK_SPACE : key = IK_SELECT; break; } + if (wParam == '1') players[0]->extraL->goalWeapon = WEAPON_PISTOLS; + if (wParam == '2') players[0]->extraL->goalWeapon = WEAPON_MAGNUMS; + if (wParam == '3') players[0]->extraL->goalWeapon = WEAPON_UZIS; + if (wParam == '4') players[0]->extraL->goalWeapon = WEAPON_SHOTGUN; + if (msg != WM_KEYUP && msg != WM_SYSKEYUP) { keys |= key; } else { @@ -462,7 +452,7 @@ int main(void) { // track 13 #if defined(_WIN32) || defined(__DOS__) { - FILE *f = fopen("data/TRACK_13.WAV", "rb"); + FILE *f = fopen("data/TRACKS.IMA", "rb"); if (!f) { return 0; } @@ -474,7 +464,7 @@ int main(void) { fread(data, 1, size, f); fclose(f); - TRACK_13_WAV = data; + TRACKS_IMA = data; } #endif } @@ -507,8 +497,11 @@ int main(void) { TranslateMessage(&msg); DispatchMessage(&msg); } else { - int frame = (GetTickCount() - startTime) / 33; - game.update(frame - lastFrame); + int32 frame = (GetTickCount() - startTime) / 33; + if (GetAsyncKeyState('R')) frame /= 10; + int32 count = frame - lastFrame; + if (GetAsyncKeyState('T')) count *= 10; + game.update(count); lastFrame = frame; game.render(); @@ -561,13 +554,6 @@ int main(void) { mode |= DCNT_MODE4; REG_BG2PA = (1 << 8); REG_BG2PD = (1 << 8); -#elif defined(ROTATE90_MODE) - mode |= DCNT_MODE5; - REG_BG2PA = 0; - REG_BG2PB = (1 << 8); - REG_BG2PC = -(1 << 7); - REG_BG2PD = 0; - REG_BG2Y = (FRAME_HEIGHT << 8) - 128; #else mode |= DCNT_MODE5; REG_BG2PA = (1 << 7); @@ -577,11 +563,6 @@ int main(void) { int32 lastFrameIndex = -1; while (1) { - //VBlankIntrWait(); - REG_DISPCNT = (mode ^= DCNT_PAGE); - - fb ^= 0xA000; - { // input keys = 0; key_poll(); @@ -601,6 +582,12 @@ int main(void) { game.update(frame - lastFrameIndex); lastFrameIndex = frame; + #ifdef PROFILING + VBlankIntrWait(); + #endif + REG_DISPCNT = (mode ^= DCNT_PAGE); + fb ^= 0xA000; + game.render(); fpsCounter++; diff --git a/src/platform/gba/object.h b/src/platform/gba/object.h index 998e4798..a151ca79 100644 --- a/src/platform/gba/object.h +++ b/src/platform/gba/object.h @@ -26,32 +26,32 @@ namespace Limits { static const Limit SWITCH = { Bounds( -200, 200, 0, 0, 312, 512 ), - vec3s( 10 * DEG2SHORT, 30 * DEG2SHORT, 10 * DEG2SHORT ) + vec3s( ANGLE(10), ANGLE(30), ANGLE(10) ) }; static const Limit SWITCH_UW = { Bounds( -1024, 1024, -1024, 1024, -1024, 1024 ), - vec3s( 80 * DEG2SHORT, 80 * DEG2SHORT, 80 * DEG2SHORT ) + vec3s( ANGLE(80), ANGLE(80), ANGLE(80) ) }; static const Limit BLOCK = { Bounds( -300, 300, 0, 0, -692, -512 ), - vec3s( 10 * DEG2SHORT, 30 * DEG2SHORT, 10 * DEG2SHORT ) + vec3s( ANGLE(10), ANGLE(30), ANGLE(10) ) }; static const Limit PICKUP = { Bounds( -256, 256, -100, 100, -256, 100 ), - vec3s( 10 * DEG2SHORT, 0, 0 ) + vec3s( ANGLE(10), 0, 0 ) }; static const Limit PICKUP_UW = { Bounds( -512, 512, -512, 512, -512, 512 ), - vec3s( 45 * DEG2SHORT, 45 * DEG2SHORT, 45 * DEG2SHORT ) + vec3s( ANGLE(45), ANGLE(45), ANGLE(45) ) }; static const Limit HOLE = { Bounds( -200, 200, 0, 0, 312, 512 ), - vec3s( 10 * DEG2SHORT, 30 * DEG2SHORT, 10 * DEG2SHORT ) + vec3s( ANGLE(10), ANGLE(30), ANGLE(10) ) }; }; @@ -103,6 +103,99 @@ struct Object : Item return boxContains(limit.box, p); } + + void collideDefault(Lara* lara, CollisionInfo* cinfo) + { + if (!updateHitMask(lara, cinfo)) + return; + + if (!cinfo->enemyPush) + return; + + collidePush(lara, cinfo, false); + } +}; + + +struct SpriteEffect : Item +{ + SpriteEffect(Room* room) : Item(room) + { + tick = 0; + timer = 0; + hSpeed = 0; + frameIndex = 0; + activate(); + } + + virtual void update() + { + tick++; + if (tick >= timer) + { + tick = 0; + + if (flags.animated) + { + frameIndex++; + if (frameIndex >= -models[type].count) + { + remove(); + return; + } + } else { + remove(); + return; + } + } + + if (hSpeed) + { + pos.x += phd_sin(angle.y) * hSpeed >> FIXED_SHIFT; + pos.z += phd_cos(angle.y) * hSpeed >> FIXED_SHIFT; + } + } +}; + + +struct Bubble : Item +{ + Bubble(Room* room) : Item(room) + { + soundPlay(SND_BUBBLE, pos); + frameIndex = rand_draw() % (-models[type].count); + vSpeed = -(10 + (rand_draw() % 6)); + angle = vec3s(0, 0, ANGLE_90); + activate(); + + roomFloor = getWaterLevel(); + } + + virtual void update() + { + pos.y += vSpeed; + if (roomFloor > pos.y) + { + remove(); + return; + } + + angle.x += ANGLE(9); + angle.z += ANGLE(13); + + int32 dx = phd_sin(angle.x); + int32 dz = phd_sin(angle.z); + + pos.x += dx * 11 >> FIXED_SHIFT; + pos.z += dz * 8 >> FIXED_SHIFT; + + Room* nextRoom = room->getRoom(pos.x, pos.y, pos.z); + if (nextRoom != room) + { + room->remove(this); + nextRoom->add(this); + } + } }; @@ -129,6 +222,7 @@ struct LavaEmitter : Object virtual void draw() {} }; + struct Door : Object { enum { @@ -235,6 +329,21 @@ struct TrapDoor : Object }; +struct Crystal : Object +{ + Crystal(Room* room) : Object(room) + { + activate(); + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + collideDefault(lara, cinfo); + // TODO + } +}; + + struct Switch : Object { enum { @@ -257,7 +366,7 @@ struct Switch : Object virtual void collide(Lara* lara, CollisionInfo* cinfo) { - if (lara->weaponState != WEAPON_STATE_FREE) + if (lara->extraL->weaponState != WEAPON_STATE_FREE) return; if (!(lara->input & IN_ACTION)) @@ -282,7 +391,7 @@ struct Switch : Object lara->animSkip(isDown ? Lara::STATE_SWITCH_DOWN : Lara::STATE_SWITCH_UP, Lara::STATE_STOP, true); activate(); flags.status = ITEM_FLAGS_STATUS_ACTIVE; - lara->weaponState = WEAPON_STATE_BUSY; + lara->extraL->weaponState = WEAPON_STATE_BUSY; } bool use(int32 t) @@ -314,7 +423,7 @@ struct SwitchWater : Switch virtual void collide(Lara* lara, CollisionInfo* cinfo) { - if (lara->weaponState != WEAPON_STATE_FREE) + if (lara->extraL->weaponState != WEAPON_STATE_FREE) return; if (!(lara->input & IN_ACTION)) @@ -348,7 +457,7 @@ struct Key : Object bool use(Item* lara) { - if (flags.status == ITEM_FLAGS_STATUS_ACTIVE && lara->weaponState == WEAPON_STATE_FREE) // TODO check weapons + if (flags.status == ITEM_FLAGS_STATUS_ACTIVE && lara->extraL->weaponState == WEAPON_STATE_FREE) // TODO check weapons { flags.status = ITEM_FLAGS_STATUS_INACTIVE; return true; @@ -360,7 +469,10 @@ struct Key : Object struct Pickup : Object { - Pickup(Room* room) : Object(room) {} + Pickup(Room* room) : Object(room) + { + frameIndex = 0; + } bool use() { @@ -396,7 +508,7 @@ struct Pickup : Object } else if (lara->state == Lara::STATE_STOP) { - if (lara->weaponState != WEAPON_STATE_FREE) + if (lara->extraL->weaponState != WEAPON_STATE_FREE) return; if (!(lara->input & IN_ACTION)) @@ -406,13 +518,13 @@ struct Pickup : Object return; lara->animSkip(Lara::STATE_PICKUP, Lara::STATE_STOP); - lara->weaponState = WEAPON_STATE_BUSY; + lara->extraL->weaponState = WEAPON_STATE_BUSY; } } if (lara->waterState == WATER_STATE_UNDER) { - angle.x = -25 * DEG2SHORT; + angle.x = ANGLE(-25); if (!checkLimit(lara, Limits::PICKUP_UW)) return; @@ -473,7 +585,7 @@ struct Hole : Object // parent class for KeyHole and PuzzleHole void apply(int32 offset, Lara* lara, Lara::State stateUse) { - if (lara->weaponState != WEAPON_STATE_FREE) + if (lara->extraL->weaponState != WEAPON_STATE_FREE) return; if (flags.status != ITEM_FLAGS_STATUS_NONE) @@ -499,7 +611,7 @@ struct Hole : Object // parent class for KeyHole and PuzzleHole { lara->moveTo(vec3i(0, 0, offset), this, false); lara->animSkip(stateUse, Lara::STATE_STOP); - lara->weaponState = WEAPON_STATE_BUSY; + lara->extraL->weaponState = WEAPON_STATE_BUSY; flags.status = ITEM_FLAGS_STATUS_ACTIVE; return; } @@ -629,7 +741,20 @@ struct TrapSwingBlade : Object virtual void collide(Lara* lara, CollisionInfo* cinfo) { - // + if (flags.status != ITEM_FLAGS_STATUS_ACTIVE) + return; + + if (state != STATE_SWING) + return; + + if (!updateHitMask(lara, cinfo)) + return; + + vec3i offsetPos = vec3i((rand_logic() - 0x4000) >> 8, -256 - (rand_logic() >> 6), (rand_logic() - 0x4000) >> 8); + int32 offsetAngle = (rand_logic() - 0x4000) >> 3; + lara->fxBlood(lara->pos + offsetPos, lara->angle.y + offsetAngle, lara->hSpeed); + + lara->health -= 100; // TODO TR2 50? } virtual void update() @@ -644,8 +769,6 @@ struct TrapSwingBlade : Object } } - // TODO damage - animProcess(); } }; @@ -656,23 +779,30 @@ struct Dart : Object Dart(Room* room) : Object(room) { flags.shadow = true; + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + collideDefault(lara, cinfo); - soundPlay(SND_DART, pos); - // TODO create smoke + if (hitMask) + { + lara->fxBlood(pos, lara->angle.y, lara->hSpeed); + lara->health -= 50; + } } virtual void update() { - // TODO collide with Lara - animProcess(); updateRoom(); - if (pos.y >= roomFloor) - { - // TODO create spark - remove(); - } + if (pos.y < roomFloor) + return; + + remove(); + + fxRicochet(room, pos, false); } }; @@ -706,9 +836,13 @@ struct TrapDartEmitter : Object if (dart) { + soundPlay(SND_DART, p); + dart->intensity = 0; dart->flags.status = ITEM_FLAGS_STATUS_ACTIVE; dart->activate(); + + fxSmoke(p); } } @@ -803,7 +937,7 @@ struct Block : Object virtual void collide(Lara* lara, CollisionInfo* cinfo) { - if (lara->weaponState != WEAPON_STATE_FREE) + if (lara->extraL->weaponState != WEAPON_STATE_FREE) return; if (!(lara->input & IN_ACTION)) diff --git a/src/platform/gba/packer/main.cpp b/src/platform/gba/packer/main.cpp index 2c647337..97569778 100644 --- a/src/platform/gba/packer/main.cpp +++ b/src/platform/gba/packer/main.cpp @@ -205,7 +205,7 @@ void saveBitmap(const char* fileName, uint8* data, int32 width, int32 height) uint32 bfOffBits; } fhdr; - struct BITMAPINFOHEADER{ + struct BITMAPINFOHEADER { uint32 biSize; uint32 biWidth; uint32 biHeight; @@ -728,6 +728,12 @@ struct LevelPC vec3i pos; }; + struct NodeComp + { + uint16 flags; + vec3s pos; + }; + int32 tilesCount; Tile* tiles; @@ -1323,11 +1329,32 @@ struct LevelPC f.write(commands, commandsCount); header.nodes = f.align4(); - f.write(nodesData, nodesDataSize); + for (int32 i = 0; i < nodesDataSize / 4; i++) + { + const Node* node = (Node*)(nodesData + i * 4); + + ASSERT(node->pos.x > -32768); + ASSERT(node->pos.x < 32767); + ASSERT(node->pos.y > -32768); + ASSERT(node->pos.y < 32767); + ASSERT(node->pos.z > -32768); + ASSERT(node->pos.z < 32767); + ASSERT(node->flags < 0xFFFF); + + LevelPC::NodeComp comp; + comp.flags = uint16(node->flags); + comp.pos.x = int16(node->pos.x); + comp.pos.y = int16(node->pos.y); + comp.pos.z = int16(node->pos.z); + f.write(comp); + } + //f.write(nodesData, nodesDataSize); header.frameData = f.align4(); f.write(frameData, frameDataSize); + static int32 maxMeshes = 0; + header.models = f.align4(); for (int32 i = 0; i < modelsCount; i++) { @@ -2273,8 +2300,105 @@ struct WAD } }; +#define MAX_TRACKS 256 + +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) + +void pack_tracks(const char* dir) +{ + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFile(dir, &fd); + + if (h == INVALID_HANDLE_VALUE) + return; + + struct Track { + int32 size; + char* data; + }; + Track tracks[MAX_TRACKS]; + memset(tracks, 0, sizeof(tracks)); + + char buf[256]; + + do + { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + const char* src = fd.cFileName; + char* dst = buf; + + while (*src) + { + if (*src >= '0' && *src <= '9') + { + *dst++ = *src; + } + src++; + } + *dst++ = 0; + + int32 index = atoi(buf); + + if (index != 0) + { + strcpy(buf, dir); + buf[strlen(buf) - 5] = 0; + strcat(buf, fd.cFileName); + + FILE* f = fopen(buf, "rb"); + + if (!f) + continue; + + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + tracks[index].data = new char[size + 4]; + fread(tracks[index].data, 1, size, f); + fclose(f); + + tracks[index].size = ALIGN(*((int32*)tracks[index].data + 2), 4) - 4; + + ASSERT(tracks[index].size % 4 == 0); + } + } + } + while (FindNextFile(h, &fd)); + FindClose(h); + + FILE* f = fopen("../data/TRACKS.IMA", "wb"); + + int32 offset = MAX_TRACKS * (4 + 4); + + for (int32 i = 0; i < MAX_TRACKS; i++) + { + if (tracks[i].size == 0) { + int32 zero = 0; + fwrite(&zero, 4, 1, f); + } else { + fwrite(&offset, 4, 1, f); + } + fwrite(&tracks[i].size, 4, 1, f); + offset += tracks[i].size; + } + + for (int32 i = 0; i < MAX_TRACKS; i++) + { + if (tracks[i].size == 0) + continue; + fwrite((uint8*)tracks[i].data + 16, 1, tracks[i].size, f); + delete[] tracks[i].data; + } + + fclose(f); +} + int main() { + pack_tracks("tracks/conv_demo/*.ima"); + return 0; + for (int32 i = 0; i < MAX_LEVELS; i++) { char path[64]; diff --git a/src/platform/gba/rasterizer_mode4.h b/src/platform/gba/rasterizer_mode4.h index b0e05254..1f3192e5 100644 --- a/src/platform/gba/rasterizer_mode4.h +++ b/src/platform/gba/rasterizer_mode4.h @@ -985,16 +985,19 @@ 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; + if (w <= 0 || w >= DIV_TABLE_SIZE) return; int32 h = R->v.y - L->v.y; - if (h <= 0) return; + if (h <= 0 || h >= DIV_TABLE_SIZE) 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; + int32 iw = FixedInvU(w); + int32 ih = FixedInvU(h); + + int32 du = (R->t.u << 8) * iw >> 16; + int32 dv = (R->t.v << 8) * ih >> 16; if (L->v.y < 0) { diff --git a/src/platform/gba/rasterizer_mode5.h b/src/platform/gba/rasterizer_mode5.h index c4dca7ad..dbcad1f6 100644 --- a/src/platform/gba/rasterizer_mode5.h +++ b/src/platform/gba/rasterizer_mode5.h @@ -10,6 +10,7 @@ #define rasterizeGT rasterizeGT_mode5_c #define rasterizeFTA rasterizeFTA_mode5_c #define rasterizeGTA rasterizeGTA_mode5_c +#define rasterizeSprite rasterizeSprite_mode5_c extern uint16 palette[256]; extern uint8 lightmap[256 * 32]; @@ -834,4 +835,9 @@ void rasterizeGTA_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R) } } +void rasterizeSprite_mode5_c(uint16* pixel, const VertexUV* L, const VertexUV* R) +{ + // TODO +} + #endif diff --git a/src/platform/gba/render.cpp b/src/platform/gba/render.cpp index 4fcd3769..5681aaa8 100644 --- a/src/platform/gba/render.cpp +++ b/src/platform/gba/render.cpp @@ -38,8 +38,8 @@ extern Texture textures[MAX_TEXTURES]; extern const Sprite* sprites; extern const uint8* tiles; extern int32 lightAmbient; +extern int32 randTable[MAX_RAND_TABLE]; extern int32 caustics[MAX_CAUSTICS]; -extern int32 causticsRand[MAX_CAUSTICS]; extern int32 causticsFrame; const uint8* tile; @@ -242,7 +242,7 @@ void transformRoom(const RoomVertex* vertices, int32 vCount, bool applyCaustics) for (int32 i = 0; i < vCount; i++) { if (applyCaustics) { - causticsValue = caustics[(causticsRand[i & (MAX_CAUSTICS - 1)] + causticsFrame) & (MAX_CAUSTICS - 1)]; + causticsValue = caustics[(randTable[i & (MAX_RAND_TABLE - 1)] + causticsFrame) & (MAX_CAUSTICS - 1)]; } transformRoomVertex(vertices, causticsValue); @@ -517,8 +517,6 @@ void drawGlyph(const Sprite *sprite, int32 x, int32 y) #if defined(MODE_PAL) uint16* pixel = (uint16*)fb + iy * VRAM_WIDTH + (ix >> 1); -#elif defined(ROTATE90_MODE) - uint16* pixel = (uint16*)fb + iy; #else uint16* pixel = (uint16*)fb + iy * VRAM_WIDTH + ix; #endif @@ -548,19 +546,11 @@ void drawGlyph(const Sprite *sprite, int32 x, int32 y) for (int32 i = 0; i < w; i++) { if (glyphData[i] == 0) continue; - #ifdef ROTATE90_MODE - pixel[(FRAME_HEIGHT - (ix + i) - 1) * FRAME_WIDTH] = palette[glyphData[i]]; - #else pixel[i] = palette[glyphData[i]]; - #endif } #endif - #ifdef ROTATE90_MODE - pixel += 1; - #else pixel += VRAM_WIDTH; - #endif glyphData += 256; } @@ -650,8 +640,6 @@ void faceAddTriangle(uint32 flags, const Index* indices, int32 startVertex) 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); @@ -674,18 +662,18 @@ void faceAddSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index) PERSPECTIVE(l, t, z); l += (FRAME_WIDTH >> 1); - if (l >= FRAME_WIDTH) return; + if (l >= viewport.x1) return; t += (FRAME_HEIGHT >> 1); - if (t >= FRAME_HEIGHT) return; + if (t >= viewport.y1) return; PERSPECTIVE(r, b, z); r += (FRAME_WIDTH >> 1); - if (r < 0) return; + if (r < viewport.x0) return; b += (FRAME_HEIGHT >> 1); - if (b < 0) return; + if (b < viewport.y0) return; if (l == r) return; if (t == b) return; @@ -700,6 +688,8 @@ void faceAddSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index) } vg >>= 8; + ASSERT(gVerticesCount + 1 < MAX_VERTICES); + Vertex &v1 = gVertices[gVerticesCount++]; v1.x = l; v1.y = t; @@ -710,11 +700,13 @@ void faceAddSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index) v2.x = r; v2.y = b; //v2.z = z; - //v2.g = g; + //v2.g = vg; ASSERT(v2.x >= v1.x); ASSERT(v2.y >= v1.y); + fogZ -= 128; + Face* f = faceAdd(fogZ >> OT_SHIFT); f->flags = uint16(FACE_SPRITE); f->indices[0] = gVerticesCount - 2; @@ -759,7 +751,7 @@ void flush() { if (gFacesCount) { - PROFILE_START(); + PROFILE(CNT_FLUSH); for (int32 i = otMax; i >= otMin; i--) { @@ -823,8 +815,6 @@ void flush() } while (face); } - PROFILE_STOP(dbg_flush); - otMin = OT_SIZE - 1; otMax = 0; } @@ -838,9 +828,11 @@ void flush() } #endif -#ifdef PROFILE - dbg_vert_count += gVerticesCount; - dbg_poly_count += gFacesCount; +#ifdef PROFILING + #if !defined(PROFILE_FRAMETIME) && !defined(PROFILE_SOUNDTIME) + gCounters[CNT_VERT] += gVerticesCount; + gCounters[CNT_POLY] += gFacesCount; + #endif #endif gVerticesCount = 0; @@ -851,3 +843,114 @@ void clear() { dmaFill((void*)fb, 0, VRAM_WIDTH * FRAME_HEIGHT * 2); } + +#ifdef IWRAM_MATRIX_LERP +void matrixLerp(const Matrix &n, int32 multiplier, int32 divider) +{ + Matrix &m = matrixGet(); + + if ((divider == 2) || ((divider == 4) && (multiplier == 2))) { + LERP_MATRIX(LERP_1_2); + } else if (divider == 4) { + + if (multiplier == 1) { + LERP_MATRIX(LERP_1_4); + } else { + LERP_MATRIX(LERP_3_4); + } + + } else { + LERP_MATRIX(LERP_SLOW); + } +} +#endif + +// TODO move to sound.iwram.cpp? +int16 IMA_STEP[] = { // IWRAM ! + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 +}; + +#define DECODE_IMA_4(n)\ + step = IMA_STEP[idx];\ + index = n & 7;\ + step += index * step << 1;\ + if (index < 4) {\ + idx = X_MAX(idx - 1, 0);\ + } else {\ + idx = X_MIN(idx + ((index - 3) << 1), X_COUNT(IMA_STEP) - 1);\ + }\ + if (n & 8) {\ + smp -= step >> 3;\ + } else {\ + smp += step >> 3;\ + }\ + *buffer++ = smp >> (16 - (8 + SND_VOL_SHIFT)); + +void decodeIMA(IMA_STATE &state, const uint8* data, int32* buffer, int32 size) +{ + uint32 step, index; + + int32 idx = state.idx; + int32 smp = state.smp; + + for (int32 i = 0; i < size; i++) + { + uint32 n = *data++; + DECODE_IMA_4(n); + n >>= 4; + DECODE_IMA_4(n); + } + + state.idx = idx; + state.smp = smp; +} + +/* TODO OUT OF IWRAM! +#define DECODE_IMA_4_sample(n)\ + step = IMA_STEP[idx];\ + index = n & 7;\ + step += index * step << 1;\ + if (index < 4) {\ + idx = X_MAX(idx - 1, 0);\ + } else {\ + idx = X_MIN(idx + ((index - 3) << 1), X_COUNT(IMA_STEP) - 1);\ + }\ + if (n & 8) {\ + smp -= step >> 3;\ + } else {\ + smp += step >> 3;\ + }\ + *buffer++ += smp * volume >> (16 - (8 + SND_VOL_SHIFT)); + +void decodeIMA_sample(IMA_STATE &state, const uint8* data, int32* buffer, int32 size, int32 inc, int32 volume) +{ + uint32 step, index; + + int32 idx = state.idx; + int32 smp = state.smp; + + for (int32 i = 0; i < size; i++) + { + uint32 n = *data; + DECODE_IMA_4_sample(n); + n >>= 4; + DECODE_IMA_4_sample(n); + + data += inc; + } + + state.idx = idx; + state.smp = smp; +} +*/ \ No newline at end of file diff --git a/src/platform/gba/room.h b/src/platform/gba/room.h index ccff3ca8..2e257ad0 100644 --- a/src/platform/gba/room.h +++ b/src/platform/gba/room.h @@ -2,7 +2,6 @@ #define H_ROOM #include "common.h" -#include "camera.h" void animTexturesShift() { @@ -25,118 +24,6 @@ void animTexturesShift() } } -int32 getBridgeFloor(const Item* item, int32 x, int32 z) -{ - if (item->type == ITEM_BRIDGE_FLAT) { - return item->pos.y; - } - - int32 h; - if (item->angle.y == ANGLE_0) { - h = 1024 - x; - } else if (item->angle.y == ANGLE_180) { - h = x; - } else if (item->angle.y == ANGLE_90) { - h = z; - } else { - h = 1024 - z; - } - - h &= 1023; - - return item->pos.y + ((item->type == ITEM_BRIDGE_TILT_1) ? (h >> 2) : (h >> 1)); -} - -int32 getTrapDoorFloor(const Item* item, int32 x, int32 z) -{ - int32 dx = (item->pos.x >> 10) - (x >> 10); - int32 dz = (item->pos.z >> 10) - (z >> 10); - - if (((dx == 0) && (dz == 0)) || - ((dx == 0) && (dz == 1) && (item->angle.y == ANGLE_0)) || - ((dx == 0) && (dz == -1) && (item->angle.y == ANGLE_180)) || - ((dx == 1) && (dz == 0) && (item->angle.y == ANGLE_90)) || - ((dx == -1) && (dz == 0) && (item->angle.y == -ANGLE_90))) - { - return item->pos.y; - } - - return WALL; -} - -int32 getDrawBridgeFloor(const Item* item, int32 x, int32 z) -{ - int32 dx = (item->pos.x >> 10) - (x >> 10); - int32 dz = (item->pos.z >> 10) - (z >> 10); - - if (((dx == 0) && ((dz == -1) || (dz == -2)) && (item->angle.y == ANGLE_0)) || - ((dx == 0) && ((dz == 1) || (dz == 2)) && (item->angle.y == ANGLE_180)) || - ((dz == 0) && ((dx == -1) || (dz == -2)) && (item->angle.y == ANGLE_90)) || - ((dz == 0) && ((dx == 1) || (dz == 2)) && (item->angle.y == -ANGLE_90))) - { - return item->pos.y; - } - - return WALL; -} - -void getItemFloorCeiling(const Item* item, int32 x, int32 y, int32 z, int32* floor, int32* ceiling) -{ - int32 h = WALL; - - switch (item->type) - { - case ITEM_TRAP_FLOOR: - { - if (item->state == 0 || item->state == 1) { - h = item->pos.y - 512; - } - break; - } - case ITEM_DRAWBRIDGE: - { - if (item->state == 1) { - h = getDrawBridgeFloor(item, x, z); - } - break; - } - case ITEM_BRIDGE_FLAT: - case ITEM_BRIDGE_TILT_1: - case ITEM_BRIDGE_TILT_2: - { - h = getBridgeFloor(item, x, z); - break; - } - case ITEM_TRAP_DOOR_1: - case ITEM_TRAP_DOOR_2: - { - if (item->state != 0) - return; - - h = getTrapDoorFloor(item, x, z); - - if ((floor && (h >= *floor)) || (ceiling && (h <= *ceiling))) - { - return; - } - } - } - - if (h == WALL) - return; - - if (floor && (y <= h)) - { - *floor = h; - } - - if (ceiling && (y > h)) - { - *ceiling = h + 256; - } -} - - const Sector* Sector::getSectorBelow(int32 posX, int32 posZ) const { if (roomBelow == NO_ROOM) @@ -289,7 +176,7 @@ void Sector::getTriggerFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int if (trigger.action == TRIGGER_ACTION_ACTIVATE_OBJECT) { - getItemFloorCeiling(items + trigger.args, x, y, z, floor, ceiling); + items[trigger.args].getItemFloorCeiling(x, y, z, floor, ceiling); } if (trigger.action == TRIGGER_ACTION_ACTIVATE_CAMERA) @@ -315,6 +202,7 @@ void Sector::getTriggerFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int const Sector* Room::getSector(int32 x, int32 z) const { + // TODO remove clamp? 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); @@ -386,6 +274,93 @@ Room* Room::getRoom(int32 x, int32 y, int32 z) return room; } +bool Room::collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height) +{ + cinfo.staticHit = false; + cinfo.offset = vec3i(0); + + Bounds objBox; + objBox.minX = -cinfo.radius; + objBox.maxX = cinfo.radius; + objBox.minZ = -cinfo.radius; + objBox.maxZ = cinfo.radius; + objBox.minY = -height; + objBox.maxY = 0; + + Room** nearRoom = getNearRooms(p, cinfo.radius, height); + + while (*nearRoom) + { + const Room* room = *nearRoom++; + + for (int i = 0; i < room->info->meshesCount; i++) + { + const RoomMesh* mesh = room->data.meshes + i; + + #ifdef NO_STATIC_MESH_PLANTS + if (mesh->id < 10) + continue; + #endif + + const StaticMesh* staticMesh = staticMeshes + mesh->id; + + if (staticMesh->flags & STATIC_MESH_FLAG_NO_COLLISION) + continue; + + Bounds meshBox = boxRotate(staticMesh->cbox, (mesh->rot - 2) * ANGLE_90); + + // TODO align RoomInfo::Mesh (room relative int16?) + vec3i pos; + pos.x = mesh->pos.x + (room->info->x << 8); + pos.y = mesh->pos.y; + pos.z = mesh->pos.z + (room->info->z << 8); + + pos -= p; + + boxTranslate(meshBox, pos); + + if (!boxIntersect(meshBox, objBox)) + continue; + + cinfo.offset = boxPushOut(meshBox, objBox); + + bool flip = (cinfo.quadrant > 1); + + if (cinfo.quadrant & 1) { + if (abs(cinfo.offset.z) > cinfo.radius) { + cinfo.offset.z = cinfo.pos.z - p.z; + if ((cinfo.offset.x < 0 && cinfo.quadrant == 1) || (cinfo.offset.x > 0 && cinfo.quadrant == 3)) { + cinfo.type = CT_FRONT; + } + } else if (cinfo.offset.z != 0) { + cinfo.offset.x = 0; + cinfo.type = ((cinfo.offset.z > 0) ^ flip) ? CT_RIGHT : CT_LEFT; + } else { + cinfo.offset = vec3i(0); + } + } else { + if (abs(cinfo.offset.x) > cinfo.radius) { + cinfo.offset.x = cinfo.pos.x - p.x; + if ((cinfo.offset.z < 0 && cinfo.quadrant == 0) || (cinfo.offset.z > 0 && cinfo.quadrant == 2)) { + cinfo.type = CT_FRONT; + } + } else if (cinfo.offset.x != 0) { + cinfo.offset.z = 0; + cinfo.type = ((cinfo.offset.x > 0) ^ flip) ? CT_LEFT : CT_RIGHT; + } else { + cinfo.offset = vec3i(0); + } + } + + cinfo.staticHit = (cinfo.offset.x != 0 || cinfo.offset.z != 0); + + return true; + } + } + + return false; +} + bool Room::checkPortal(const Portal* portal) { vec3i d; @@ -393,9 +368,8 @@ bool Room::checkPortal(const Portal* portal) d.y = portal->v[0].y - cameraViewPos.y; d.z = portal->v[0].z - cameraViewPos.z + (info->z << 8); - if (DP33(portal->n, d) >= 0) { + if (DP33(portal->n, d) >= 0) return false; - } int32 x0 = clip.x1; int32 y0 = clip.y1; @@ -446,7 +420,8 @@ bool Room::checkPortal(const Portal* portal) if (y > y1) y1 = y; } - if (znear == 4 || zfar == 4) return false; + if (znear == 4 || zfar == 4) + return false; if (znear) { @@ -484,7 +459,8 @@ bool Room::checkPortal(const Portal* portal) if (y0 < clip.y0) y0 = clip.y0; if (y1 > clip.y1) y1 = clip.y1; - if (x0 >= x1 || y0 >= y1) return false; + if (x0 >= x1 || y0 >= y1) + return false; Room* nextRoom = rooms + portal->roomIndex; @@ -499,7 +475,7 @@ bool Room::checkPortal(const Portal* portal) Room** Room::addVisibleRoom(Room** list) { matrixPush(); - matrixTranslateAbs(vec3i(info->x << 8, 0, info->z << 8)); + matrixTranslateAbs(info->x << 8, 0, info->z << 8); for (int32 i = 0; i < info->portalsCount; i++) { @@ -511,7 +487,8 @@ Room** Room::addVisibleRoom(Room** list) list = nextRoom->addVisibleRoom(list); - if (!nextRoom->visible) { + if (!nextRoom->visible) + { nextRoom->visible = true; *list++ = nextRoom; } @@ -549,7 +526,8 @@ Room** Room::addNearRoom(Room** list, int32 x, int32 y, int32 z) int32 count = list - roomsList; for (int32 i = 0; i < count; i++) { - if (roomsList[i] == nearRoom) return list; + if (roomsList[i] == nearRoom) + return list; } *list++ = nearRoom; @@ -618,8 +596,6 @@ void Room::remove(Item* item) { ASSERT(item && item->room == this); - item->room = NULL; - Item* prev = NULL; Item* curr = firstItem; @@ -645,302 +621,4 @@ 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->cmd.func == FLOOR_TYPE_LAVA) - { - // TODO lava - - if (fd->cmd.end) - return; - - fd++; - } - - FloorData::Command cmd = (fd++)->cmd; - FloorData::TriggerInfo info = (fd++)->triggerInfo; - - Item* switchItem = NULL; - Item* cameraItem = NULL; - - checkCamera(fd); - - if (!lara && cmd.type != TRIGGER_TYPE_OBJECT) - return; - - if (lara) - { - switch (cmd.type) - { - case TRIGGER_TYPE_ACTIVATE: - break; - - case TRIGGER_TYPE_PAD: - case TRIGGER_TYPE_ANTIPAD: - { - 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: - { - Item* keyItem = items + fd->triggerCmd.args; - if (!useKey(keyItem, lara)) - return; - fd++; - break; - } - - case TRIGGER_TYPE_PICKUP: - { - Item* pickupItem = items + fd->triggerCmd.args; - if (!usePickup(pickupItem)) - return; - fd++; - break; - } - - case TRIGGER_TYPE_OBJECT: - return; - - case TRIGGER_TYPE_COMBAT: - { - if (lara->weaponState != WEAPON_STATE_READY) - return; - break; - } - - case TRIGGER_TYPE_DUMMY: - return; - } - } - - while (1) - { - FloorData::TriggerCommand triggerCmd = (fd++)->triggerCmd; - - switch (triggerCmd.action) - { - 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) { - item->timer *= 30; - } - - if (cmd.type == TRIGGER_TYPE_SWITCH) { - item->flags.mask ^= info.mask; - } else if (cmd.type == TRIGGER_TYPE_ANTIPAD) { - item->flags.mask &= ~info.mask; - } else { - item->flags.mask |= info.mask; - } - - if (item->flags.mask != ITEM_FLAGS_MASK_ALL) - break; - - item->flags.once |= info.once; - - if (item->flags.active) - break; - - item->flags.status = ITEM_FLAGS_STATUS_ACTIVE; - - item->activate(); - break; - } - - case TRIGGER_ACTION_ACTIVATE_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 - break; - - case TRIGGER_ACTION_FLIP: - // TODO flipmap - break; - - case TRIGGER_ACTION_FLIP_ON: - // TODO flipmap - break; - - case TRIGGER_ACTION_FLIP_OFF: - // TODO flipmap - break; - - case TRIGGER_ACTION_CAMERA_TARGET: - { - cameraItem = items + triggerCmd.args; - break; - } - - case TRIGGER_ACTION_END: - // TODO go to the next level - break; - - case TRIGGER_ACTION_SOUNDTRACK: - { - int32 track = doTutorial(lara, triggerCmd.args); - - if (track == 0) break; - - SaveGame::TrackFlags &flags = gSaveGame.tracks[track]; - - if (flags.once) - break; - - if (cmd.type == TRIGGER_TYPE_SWITCH) - flags.mask ^= info.mask; - else if (cmd.type == TRIGGER_TYPE_ANTIPAD) - flags.mask &= ~info.mask; - else - flags.mask |= info.mask; - - if (flags.mask == ITEM_FLAGS_MASK_ALL) { - flags.once |= info.once; - musicPlay(track); - } else { - musicStop(); - } - break; - } - - case TRIGGER_ACTION_EFFECT: - // TODO effect - break; - - case TRIGGER_ACTION_SECRET: - { - if ((gSaveGame.secrets >> triggerCmd.args) & 1) - break; - - gSaveGame.secrets |= (1 << triggerCmd.args); - mixer.playMusic(TRACK_13_WAV); // TODO play sample? - break; - } - - case TRIGGER_ACTION_CLEAR_BODIES: - break; - - case TRIGGER_ACTION_FLYBY: - triggerCmd.end = (fd++)->triggerCmd.end; - break; - - case TRIGGER_ACTION_CUTSCENE: - break; - } - - 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 276d9ffd..4f8d6147 100644 --- a/src/platform/gba/sound.h +++ b/src/platform/gba/sound.h @@ -3,24 +3,7 @@ #include "common.h" -int16 IMA_INDEX[] = { // IWRAM ! - -1, -1, -1, -1, 2, 4, 6, 8, -}; - -int16 IMA_STEP[] = { // IWRAM ! - 7, 8, 9, 10, 11, 12, 13, 14, - 16, 17, 19, 21, 23, 25, 28, 31, - 34, 37, 41, 45, 50, 55, 60, 66, - 73, 80, 88, 97, 107, 118, 130, 143, - 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, - 724, 796, 876, 963, 1060, 1166, 1282, 1411, - 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, - 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, - 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, - 32767 -}; +void decodeIMA(IMA_STATE &state, const uint8* data, int32* buffer, int32 size); struct Mixer { @@ -35,41 +18,20 @@ struct Mixer const uint8* data; int32 size; int32 pos; - int32 smp; - int32 idx; + IMA_STATE state; - X_INLINE int32 getSample(uint32 n) + void fill(int32* buffer, int32 count) { - int32 step = IMA_STEP[idx]; - int32 index = n & 7; - - idx = X_CLAMP(idx + IMA_INDEX[index], 0, 88); + int32 len = X_MIN(size - pos, count >> 1); - int32 diff = (2 * index + 1) * step >> 3; + decodeIMA(state, data + pos, buffer, len); - if (n & 8) { - smp = X_MAX(smp - diff, -32768); - } else { - smp = X_MIN(smp + diff, 32767); - } - - return smp >> 1; - } + pos += len; - X_INLINE void fill(int32* buffer, int32 count) - { - for (int32 i = 0; i < count; i += 2) + if (pos >= size) { - if (pos >= size) - { - data = NULL; - memset(buffer, 0, (count - i) * sizeof(buffer[0])); - return; - } - - uint32 n = data[pos++]; - *buffer++ = getSample(n); - *buffer++ = getSample(n >> 4); + data = NULL; + memset(buffer, 0, (count - (len << 1)) * sizeof(buffer[0])); } } }; @@ -82,7 +44,7 @@ struct Mixer int32 inc; int32 volume; - X_INLINE void fill(int32* buffer, int32 count) + void fill(int32* buffer, int32 count) { for (int32 i = 0; i < count; i++) { @@ -103,54 +65,6 @@ struct Mixer Sample channels[SND_CHANNELS]; int32 channelsCount; - void fill(uint8* bufferA, uint8* bufferB, int32 count) - { - if ((channelsCount == 0) && !music.data) - { - dmaFill(bufferA, SND_ENCODE(0), count); - #ifdef USE_9BIT_SOUND - dmaFill(bufferB, SND_ENCODE(0), count); - #endif - return; - } - - int32 tmp[SND_SAMPLES]; - - if (music.data) { - music.fill(tmp, count); - } else { - dmaFill(tmp, 0, sizeof(tmp)); - } - - int32 ch = channelsCount; - while (ch--) - { - Sample* sample = channels + ch; - - sample->fill(tmp, count); - - if (!sample->data) { - channels[ch] = channels[--channelsCount]; - } - } - - for (int32 i = 0; i < count; i++) - { - int32 samp = X_CLAMP(tmp[i] >> SND_VOL_SHIFT, SND_MIN, SND_MAX); - - #if defined(_WIN32) - bufferA[i] = SND_ENCODE(samp); - #elif defined(__GBA__) - #ifdef USE_9BIT_SOUND - bufferA[i] = (samp >> 1); - bufferB[i] = (samp >> 1) + (samp & 1); // TODO - #else - bufferA[i] = samp; - #endif - #endif - } - } - #define CALC_INC (((SND_SAMPLE_FREQ << SND_FIXED_SHIFT) / SND_OUTPUT_FREQ) * pitch >> SND_PITCH_SHIFT) Sample* playSample(const uint8* data, int32 size, int32 volume, int32 pitch, int32 mode) @@ -189,7 +103,7 @@ struct Mixer sample->size = size << SND_FIXED_SHIFT; sample->pos = 0; sample->inc = CALC_INC; - sample->volume = volume + 1; + sample->volume = volume; return sample; } @@ -207,14 +121,19 @@ struct Mixer } } - void playMusic(const void* data) + void playMusic(const void* data, int32 size) { - music.data = (uint8*)data + 16; - music.size = *((int32*)data + 2); - music.pos = 0; + music.data = (uint8*)data; + music.size = size; + music.pos = 0; //music.volume = (1 << SND_VOL_SHIFT); - music.smp = 0; - music.idx = 0; + music.state.smp = 0; + music.state.idx = 0; + } + + void stopMusic() + { + music.data = NULL; } void init() @@ -222,6 +141,59 @@ struct Mixer channelsCount = 0; music.data = NULL; } + + void fill(uint8* bufferA, uint8* bufferB, int32 count) + { + #ifdef PROFILE_SOUNDTIME + PROFILE_CLEAR(); + PROFILE(CNT_SOUND); + #endif + + if ((channelsCount == 0) && !music.data) + { + dmaFill(bufferA, SND_ENCODE(0), count); + #ifdef USE_9BIT_SOUND + dmaFill(bufferB, SND_ENCODE(0), count); + #endif + return; + } + + int32 tmp[SND_SAMPLES]; + + if (music.data) { + music.fill(tmp, count); + } else { + dmaFill(tmp, 0, sizeof(tmp)); + } + + int32 ch = channelsCount; + while (ch--) + { + Sample* sample = channels + ch; + + sample->fill(tmp, count); + + if (!sample->data) { + channels[ch] = channels[--channelsCount]; + } + } + + for (int32 i = 0; i < count; i++) + { + #ifdef USE_9BIT_SOUND + int32 samp = tmp[i] >> (SND_VOL_SHIFT - 1); + //samp += (rand_draw() & 1) - 1; + samp = X_CLAMP(samp, SND_MIN, SND_MAX); + + bufferA[i] = SND_ENCODE((samp >> 1)); + bufferB[i] = SND_ENCODE((samp >> 1) + (samp & 1)); + #else + int32 samp = X_CLAMP(tmp[i] >> SND_VOL_SHIFT, SND_MIN, SND_MAX); + + bufferA[i] = SND_ENCODE(samp); + #endif + } + } }; Mixer mixer;