tr2/objects: use common OBJECT_BOUNDS

This updates all relevant TR2 objects to use OBJECT_BOUNDS rather than
an arbitrary array of values for testing bounds. The approach is
equivalent to TR1. Lara_TestPosition has also been moved to TRX, and
kept in Lara's module rather than in items.c, as it's only used by
Lara.
This commit is contained in:
lahm86 2025-04-17 21:44:32 +01:00
parent 8b78a7f001
commit 1f5a2d0cfa
19 changed files with 224 additions and 303 deletions

View file

@ -2,6 +2,7 @@
#include "game/const.h"
#include "game/item_actions.h"
#include "game/matrix.h"
#include "game/rooms/const.h"
void Lara_Animate(ITEM *const item)
@ -156,3 +157,52 @@ bool Lara_TestBoundsCollide(const ITEM *const item, const int32_t radius)
{
return Item_TestBoundsCollide(item, Lara_GetItem(), radius);
}
bool Lara_TestPosition(
const ITEM *const item, const OBJECT_BOUNDS *const bounds)
{
const ITEM *const lara = Lara_GetItem();
const XYZ_16 rot = {
.x = lara->rot.x - item->rot.x,
.y = lara->rot.y - item->rot.y,
.z = lara->rot.z - item->rot.z,
};
const XYZ_32 dist = {
.x = lara->pos.x - item->pos.x,
.y = lara->pos.y - item->pos.y,
.z = lara->pos.z - item->pos.z,
};
// clang-format off
if (rot.x < bounds->rot.min.x ||
rot.x > bounds->rot.max.x ||
rot.y < bounds->rot.min.y ||
rot.y > bounds->rot.max.y ||
rot.z < bounds->rot.min.z ||
rot.z > bounds->rot.max.z
) {
return false;
}
// clang-format on
Matrix_PushUnit();
Matrix_Rot16(item->rot);
const MATRIX *const m = g_MatrixPtr;
const XYZ_32 shift = {
.x = (dist.x * m->_00 + dist.y * m->_10 + dist.z * m->_20) >> W2V_SHIFT,
.y = (dist.x * m->_01 + dist.y * m->_11 + dist.z * m->_21) >> W2V_SHIFT,
.z = (dist.x * m->_02 + dist.y * m->_12 + dist.z * m->_22) >> W2V_SHIFT,
};
Matrix_Pop();
// clang-format off
return (
shift.x >= bounds->shift.min.x &&
shift.x <= bounds->shift.max.x &&
shift.y >= bounds->shift.min.y &&
shift.y <= bounds->shift.max.y &&
shift.z >= bounds->shift.min.z &&
shift.z <= bounds->shift.max.z
);
// clang-format on
}

View file

@ -15,3 +15,4 @@ void Lara_TakeDamage(int16_t damage, bool hit_status);
bool Lara_TestBoundsCollide(const ITEM *item, int32_t radius);
void Lara_Push(const ITEM *item, COLL_INFO *coll, bool hit_on, bool big_push);
bool Lara_TestPosition(const ITEM *item, const OBJECT_BOUNDS *bounds);

View file

@ -38,14 +38,12 @@ typedef struct {
bool disable_lighting;
} OBJECT_MESH;
#if TR_VERSION == 1
typedef struct {
struct {
XYZ_16 min;
XYZ_16 max;
} shift, rot;
} OBJECT_BOUNDS;
#endif
typedef struct OBJECT {
int16_t mesh_count;
@ -66,8 +64,8 @@ typedef struct OBJECT {
void (*activate_func)(ITEM *item);
void (*handle_flip_func)(ITEM *item, ROOM_FLIP_STATUS flip_status);
void (*handle_save_func)(ITEM *item, SAVEGAME_STAGE stage);
#if TR_VERSION == 1
const OBJECT_BOUNDS *(*bounds_func)(void);
#if TR_VERSION == 1
bool (*is_usable_func)(int16_t item_num);
#endif

View file

@ -196,49 +196,6 @@ bool Item_Test3DRange(int32_t x, int32_t y, int32_t z, int32_t range)
&& (SQUARE(x) + SQUARE(y) + SQUARE(z) < SQUARE(range));
}
bool Item_TestPosition(
const ITEM *const src_item, const ITEM *const dst_item,
const OBJECT_BOUNDS *const bounds)
{
const XYZ_16 rot = {
.x = src_item->rot.x - dst_item->rot.x,
.y = src_item->rot.y - dst_item->rot.y,
.z = src_item->rot.z - dst_item->rot.z,
};
if (rot.x < bounds->rot.min.x || rot.x > bounds->rot.max.x
|| rot.y < bounds->rot.min.y || rot.y > bounds->rot.max.y
|| rot.z < bounds->rot.min.z || rot.z > bounds->rot.max.z) {
return false;
}
const XYZ_32 dist = {
.x = src_item->pos.x - dst_item->pos.x,
.y = src_item->pos.y - dst_item->pos.y,
.z = src_item->pos.z - dst_item->pos.z,
};
Matrix_PushUnit();
Matrix_Rot16(dst_item->rot);
MATRIX *mptr = g_MatrixPtr;
const XYZ_32 shift = {
.x = (mptr->_00 * dist.x + mptr->_10 * dist.y + mptr->_20 * dist.z)
>> W2V_SHIFT,
.y = (mptr->_01 * dist.x + mptr->_11 * dist.y + mptr->_21 * dist.z)
>> W2V_SHIFT,
.z = (mptr->_02 * dist.x + mptr->_12 * dist.y + mptr->_22 * dist.z)
>> W2V_SHIFT,
};
Matrix_Pop();
if (shift.x < bounds->shift.min.x || shift.x > bounds->shift.max.x
|| shift.y < bounds->shift.min.y || shift.y > bounds->shift.max.y
|| shift.z < bounds->shift.min.z || shift.z > bounds->shift.max.z) {
return false;
}
return true;
}
void Item_AlignPosition(ITEM *src_item, ITEM *dst_item, XYZ_32 *vec)
{
src_item->rot.x = dst_item->rot.x;

View file

@ -11,8 +11,6 @@ int16_t Item_Spawn(const ITEM *item, GAME_OBJECT_ID obj_id);
bool Item_IsNearItem(const ITEM *item, const XYZ_32 *pos, int32_t distance);
bool Item_Test3DRange(int32_t x, int32_t y, int32_t z, int32_t range);
bool Item_TestPosition(
const ITEM *src_item, const ITEM *dst_item, const OBJECT_BOUNDS *bounds);
void Item_AlignPosition(ITEM *src_item, ITEM *dst_item, XYZ_32 *vec);
bool Item_MovePosition(
ITEM *src_item, const ITEM *dst_item, const XYZ_32 *vec, int32_t velocity);

View file

@ -701,11 +701,6 @@ bool Lara_IsNearItem(const XYZ_32 *pos, int32_t distance)
return Item_IsNearItem(g_LaraItem, pos, distance);
}
bool Lara_TestPosition(const ITEM *item, const OBJECT_BOUNDS *const bounds)
{
return Item_TestPosition(g_LaraItem, item, bounds);
}
void Lara_AlignPosition(ITEM *item, XYZ_32 *vec)
{
Item_AlignPosition(g_LaraItem, item, vec);

View file

@ -26,7 +26,6 @@ void Lara_SwapMeshExtra(void);
bool Lara_IsNearItem(const XYZ_32 *pos, int32_t distance);
void Lara_UseItem(GAME_OBJECT_ID obj_id);
bool Lara_TestPosition(const ITEM *item, const OBJECT_BOUNDS *bounds);
void Lara_AlignPosition(ITEM *item, XYZ_32 *vec);
bool Lara_MovePosition(ITEM *item, XYZ_32 *vec);

View file

@ -20,40 +20,6 @@
static BOUNDS_16 m_NullBounds = {};
static BOUNDS_16 m_InterpolatedBounds = {};
static OBJECT_BOUNDS M_ConvertBounds(const int16_t *bounds_in);
static OBJECT_BOUNDS M_ConvertBounds(const int16_t *const bounds_in)
{
// TODO: remove this conversion utility once we gain control over its
// incoming arguments
return (OBJECT_BOUNDS) {
.shift = {
.min = {
.x = bounds_in[0],
.y = bounds_in[2],
.z = bounds_in[4],
},
.max = {
.x = bounds_in[1],
.y = bounds_in[3],
.z = bounds_in[5],
},
},
.rot = {
.min = {
.x = bounds_in[6],
.y = bounds_in[8],
.z = bounds_in[10],
},
.max = {
.x = bounds_in[7],
.y = bounds_in[9],
.z = bounds_in[11],
},
},
};
}
void Item_Control(void)
{
int16_t item_num = Item_GetNextActive();
@ -188,57 +154,6 @@ int16_t Item_GetHeight(const ITEM *const item)
return height;
}
int32_t Item_TestPosition(
const int16_t *const bounds_in, const ITEM *const src_item,
const ITEM *const dst_item)
{
const OBJECT_BOUNDS bounds = M_ConvertBounds(bounds_in);
const XYZ_16 rot = {
.x = dst_item->rot.x - src_item->rot.x,
.y = dst_item->rot.y - src_item->rot.y,
.z = dst_item->rot.z - src_item->rot.z,
};
const XYZ_32 dist = {
.x = dst_item->pos.x - src_item->pos.x,
.y = dst_item->pos.y - src_item->pos.y,
.z = dst_item->pos.z - src_item->pos.z,
};
// clang-format off
if (rot.x < bounds.rot.min.x ||
rot.x > bounds.rot.max.x ||
rot.y < bounds.rot.min.y ||
rot.y > bounds.rot.max.y ||
rot.z < bounds.rot.min.z ||
rot.z > bounds.rot.max.z
) {
return false;
}
// clang-format on
Matrix_PushUnit();
Matrix_Rot16(src_item->rot);
const MATRIX *const m = g_MatrixPtr;
const XYZ_32 shift = {
.x = (dist.x * m->_00 + dist.y * m->_10 + dist.z * m->_20) >> W2V_SHIFT,
.y = (dist.x * m->_01 + dist.y * m->_11 + dist.z * m->_21) >> W2V_SHIFT,
.z = (dist.x * m->_02 + dist.y * m->_12 + dist.z * m->_22) >> W2V_SHIFT,
};
Matrix_Pop();
// clang-format off
return (
shift.x >= bounds.shift.min.x &&
shift.x <= bounds.shift.max.x &&
shift.y >= bounds.shift.min.y &&
shift.y <= bounds.shift.max.y &&
shift.z >= bounds.shift.min.z &&
shift.z <= bounds.shift.max.z
);
// clang-format on
}
void Item_AlignPosition(
const XYZ_32 *const vec, const ITEM *const src_item, ITEM *const dst_item)
{

View file

@ -8,8 +8,6 @@ void Item_Control(void);
void Item_ClearKilled(void);
void Item_ShiftCol(ITEM *item, COLL_INFO *coll);
void Item_UpdateRoom(ITEM *item, int32_t height);
int32_t Item_TestPosition(
const int16_t *bounds, const ITEM *src_item, const ITEM *dst_item);
void Item_AlignPosition(
const XYZ_32 *vec, const ITEM *src_item, ITEM *dst_item);
int32_t Item_GetFrames(const ITEM *item, ANIM_FRAME *frmptr[], int32_t *rate);

View file

@ -10,33 +10,37 @@
#include "game/sound.h"
#include "global/vars.h"
#include <libtrx/game/lara.h>
#define EXPLOSION_START_FRAME 76
#define EXPLOSION_END_FRAME 99
#define EXPLOSION_ACTION_FRAME 80
static XYZ_32 m_DetonatorPosition = { .x = 0, .y = 0, .z = 0 };
static int16_t m_GongBounds[12] = {
-WALL_L / 2,
+WALL_L,
-100,
+100,
-WALL_L / 2 - 300,
-WALL_L / 2 + 100,
-30 * DEG_1,
+30 * DEG_1,
+0,
+0,
+0,
+0,
static const OBJECT_BOUNDS m_GongBounds = {
.shift = {
.min = { .x = -WALL_L / 2, .y = -100, .z = -WALL_L / 2 - 300, },
.max = { .x = +WALL_L, .y = +100, .z = -WALL_L / 2 + 100, },
},
.rot = {
.min = { .x = -30 * DEG_1, .y = 0, .z = 0, },
.max = { .x = +30 * DEG_1, .y = 0, .z = 0, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_CreateGongBonger(ITEM *lara_item);
static void M_Setup1(OBJECT *obj);
static void M_Setup2(OBJECT *obj);
static void M_Control(int16_t item_num);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_GongBounds;
}
static void M_CreateGongBonger(ITEM *const lara_item)
{
const int16_t item_gong_bonger_num = Item_Create();
@ -64,12 +68,14 @@ static void M_CreateGongBonger(ITEM *const lara_item)
static void M_Setup1(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
}
static void M_Setup2(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->control_func = M_Control;
obj->bounds_func = Pickup_Bounds;
obj->save_flags = 1;
obj->save_anim = 1;
}
@ -102,6 +108,7 @@ static void M_Collision(
}
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
const XYZ_16 old_rot = item->rot;
const int16_t x = item->rot.x;
const int16_t y = item->rot.y;
@ -117,17 +124,12 @@ static void M_Collision(
goto normal_collision;
}
if (item->object_id == O_DETONATOR_2) {
if (!Item_TestPosition(g_PickupBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
goto normal_collision;
}
} else {
if (!Item_TestPosition(m_GongBounds, item, lara_item)) {
goto normal_collision;
} else {
if (item->object_id == O_DETONATOR_1) {
item->rot = old_rot;
}
}
if (g_Inv_Chosen == NO_OBJECT) {
GF_ShowInventoryKeys(item->object_id);

View file

@ -6,6 +6,7 @@ static void M_Setup(OBJECT *obj);
static void M_Setup(OBJECT *const obj)
{
obj->collision_func = Pickup_Collision;
obj->bounds_func = Pickup_Bounds;
obj->control_func = Flare_Control;
obj->draw_func = Flare_DrawInAir;
obj->save_position = 1;

View file

@ -17,29 +17,29 @@ static XYZ_32 m_KeyholePosition = {
.z = WALL_L / 2 - LARA_RADIUS - 50,
};
static int16_t m_KeyholeBounds[12] = {
// clang-format off
-200,
+200,
+0,
+0,
+WALL_L / 2 - 200,
+WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_KeyHoleBounds = {
.shift = {
.min = { .x = -200, .y = +0, .z = +WALL_L / 2 - 200, },
.max = { .x = +200, .y = +0, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_Consume(
ITEM *lara_item, ITEM *keyhole_item, GAME_OBJECT_ID key_obj_id);
static void M_Refuse(const ITEM *lara_item);
static void M_Setup(OBJECT *obj);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_KeyHoleBounds;
}
static void M_Refuse(const ITEM *const lara_item)
{
if (lara_item->pos.x == g_InteractPosition.x
@ -71,6 +71,7 @@ static void M_Consume(
static void M_Setup(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
obj->save_flags = 1;
}
@ -82,12 +83,13 @@ static void M_Collision(
}
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if ((g_Inv_Chosen == NO_OBJECT && !g_Input.action)
|| g_Lara.gun_status != LGS_ARMLESS || lara_item->gravity) {
return;
}
if (!Item_TestPosition(m_KeyholeBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -20,21 +20,18 @@ typedef enum {
MOVABLE_BLOCK_STATE_PULL = 3,
} MOVABLE_BLOCK_STATE;
static int16_t m_MovableBlockBounds[12] = {
-300,
+300,
+0,
+0,
-WALL_L / 2 - LARA_RADIUS - 80,
-WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
static const OBJECT_BOUNDS m_MovableBlockBounds = {
.shift = {
.min = { .x = -300, .y = 0, .z = -WALL_L / 2 - LARA_RADIUS - 80, },
.max = { .x = +300, .y = 0, .z = -WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static bool M_TestDestination(const ITEM *item, int32_t block_height);
static bool M_TestPush(
const ITEM *item, int32_t block_height, DIRECTION quadrant);
@ -47,6 +44,11 @@ static void M_Draw(const ITEM *item);
static void M_Control(int16_t item_num);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_MovableBlockBounds;
}
static bool M_TestDestination(
const ITEM *const item, const int32_t block_height)
{
@ -199,6 +201,7 @@ static void M_Setup(OBJECT *const obj)
obj->handle_save_func = M_HandleSave;
obj->control_func = M_Control;
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
obj->draw_func = M_Draw;
obj->save_position = 1;
obj->save_flags = 1;
@ -303,7 +306,7 @@ static void M_Collision(
break;
}
if (!Item_TestPosition(m_MovableBlockBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}
@ -346,7 +349,7 @@ static void M_Collision(
} else if (
Item_TestAnimEqual(lara_item, LA_PUSHABLE_GRAB)
&& Item_TestFrameEqual(lara_item, LF_PPREADY)) {
if (!Item_TestPosition(m_MovableBlockBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -26,41 +26,29 @@
#define LF_PICKUP_FLARE_UW 20
#define LF_PICKUP_UW 18
int16_t g_PickupBounds[12] = {
// clang-format off
-WALL_L / 4,
+WALL_L / 4,
-100,
+100,
-WALL_L / 4,
+WALL_L / 4,
-10 * DEG_1,
+10 * DEG_1,
+0,
+0,
+0,
+0,
// clang-format on
};
static XYZ_32 m_PickupPosition = { .x = 0, .y = 0, .z = -100 };
static XYZ_32 m_PickupPositionUW = { .x = 0, .y = -200, .z = -350 };
static int16_t m_PickupBoundsUW[12] = {
// clang-format off
-WALL_L / 2,
+WALL_L / 2,
-WALL_L / 2,
+WALL_L / 2,
-WALL_L / 2,
+WALL_L / 2,
-45 * DEG_1,
+45 * DEG_1,
-45 * DEG_1,
+45 * DEG_1,
-45 * DEG_1,
+45 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_PickUpBounds = {
.shift = {
.min = { .x = -WALL_L / 4, .y = -100, .z = -WALL_L / 4, },
.max = { .x = +WALL_L / 4, .y = +100, .z = +WALL_L / 4, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = 0, .z = 0, },
.max = { .x = +10 * DEG_1, .y = 0, .z = 0, },
},
};
static const OBJECT_BOUNDS m_PickUpBoundsUW = {
.shift = {
.min = { .x = -WALL_L / 2, .y = -WALL_L / 2, .z = -WALL_L / 2, },
.max = { .x = +WALL_L / 2, .y = +WALL_L / 2, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -45 * DEG_1, .y = -45 * DEG_1, .z = -45 * DEG_1, },
.max = { .x = +45 * DEG_1, .y = +45 * DEG_1, .z = +45 * DEG_1, },
},
};
static void M_DoPickup(int16_t item_num);
@ -107,13 +95,14 @@ static void M_DoFlarePickup(const int16_t item_num)
static void M_DoAboveWater(const int16_t item_num, ITEM *const lara_item)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
const XYZ_16 old_rot = item->rot;
item->rot.x = 0;
item->rot.y = lara_item->rot.y;
item->rot.z = 0;
if (!Item_TestPosition(g_PickupBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
goto cleanup;
}
@ -163,13 +152,14 @@ cleanup:
static void M_DoUnderwater(const int16_t item_num, ITEM *const lara_item)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
const XYZ_16 old_rot = item->rot;
item->rot.x = -25 * DEG_1;
item->rot.y = lara_item->rot.y;
item->rot.z = 0;
if (!Item_TestPosition(m_PickupBoundsUW, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
goto cleanup;
}
@ -224,6 +214,7 @@ static void M_Setup(OBJECT *const obj)
obj->handle_save_func = M_HandleSave;
obj->activate_func = M_Activate;
obj->collision_func = Pickup_Collision;
obj->bounds_func = Pickup_Bounds;
obj->draw_func = M_Draw;
obj->save_position = 1;
obj->save_flags = 1;
@ -343,6 +334,15 @@ static void M_Draw(const ITEM *const item)
Matrix_Pop();
}
const OBJECT_BOUNDS *Pickup_Bounds(void)
{
if (g_Lara.water_status == LWS_UNDERWATER) {
return &m_PickUpBoundsUW;
} else {
return &m_PickUpBounds;
}
}
void Pickup_Collision(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{

View file

@ -2,7 +2,6 @@
#include "global/types.h"
extern int16_t g_PickupBounds[];
void Pickup_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
bool Pickup_Trigger(int16_t item_num);
const OBJECT_BOUNDS *Pickup_Bounds(void);

View file

@ -17,23 +17,18 @@ static XYZ_32 m_PuzzleHolePosition = {
.z = WALL_L / 2 - LARA_RADIUS - 85,
};
static int16_t m_PuzzleHoleBounds[12] = {
// clang-format off
-200,
+200,
+0,
+0,
+WALL_L / 2 - 200,
+WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_PuzzleHoleBounds = {
.shift = {
.min = { .x = -200, .y = 0, .z = WALL_L / 2 - 200, },
.max = { .x = +200, .y = 0, .z = WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_Refuse(const ITEM *lara_item);
static void M_Consume(
ITEM *lara_item, ITEM *puzzle_hole_item, GAME_OBJECT_ID puzzle_obj_id);
@ -43,6 +38,11 @@ static void M_SetupDone(OBJECT *obj);
static void M_HandleSave(ITEM *item, SAVEGAME_STAGE stage);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_PuzzleHoleBounds;
}
static void M_Refuse(const ITEM *const lara_item)
{
if (lara_item->pos.x != g_InteractPosition.x
@ -82,6 +82,7 @@ static void M_SetupEmpty(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->handle_save_func = M_HandleSave;
obj->bounds_func = M_Bounds;
obj->save_flags = 1;
}
@ -103,10 +104,11 @@ static void M_Collision(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if (lara_item->current_anim_state != LS_STOP) {
if (lara_item->current_anim_state != LS_USE_PUZZLE
|| !Item_TestPosition(m_PuzzleHoleBounds, item, lara_item)
|| !Lara_TestPosition(item, obj->bounds_func())
|| !Item_TestFrameEqual(lara_item, LF_USE_PUZZLE)) {
return;
}
@ -120,7 +122,7 @@ static void M_Collision(
return;
}
if (!Item_TestPosition(m_PuzzleHoleBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -17,40 +17,30 @@ static XYZ_32 g_PushSwitchPosition = { .x = 0, .y = 0, .z = 292 };
static XYZ_32 m_AirlockPosition = { .x = 0, .y = 0, .z = 212 };
static XYZ_32 m_SwitchUWPosition = { .x = 0, .y = 0, .z = 108 };
static int16_t m_SwitchBounds[12] = {
// clang-format off
-220,
+220,
+0,
+0,
+WALL_L / 2 - 220,
+WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_SwitchBounds = {
.shift = {
.min = { .x = -220, .y = +0, .z = +WALL_L / 2 - 220, },
.max = { .x = +220, .y = +0, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static int16_t m_SwitchBoundsUW[12] = {
// clang-format off
-WALL_L,
+WALL_L,
-WALL_L,
+WALL_L,
-WALL_L,
+WALL_L / 2,
-80 * DEG_1,
+80 * DEG_1,
-80 * DEG_1,
+80 * DEG_1,
-80 * DEG_1,
+80 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_SwitchBoundsUW = {
.shift = {
.min = { .x = -WALL_L, .y = -WALL_L, .z = -WALL_L, },
.max = { .x = +WALL_L, .y = +WALL_L, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -80 * DEG_1, .y = -80 * DEG_1, .z = -80 * DEG_1, },
.max = { .x = +80 * DEG_1, .y = +80 * DEG_1, .z = +80 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static const OBJECT_BOUNDS *M_BoundsUW(void);
static void M_AlignLara(ITEM *lara_item, ITEM *switch_item);
static void M_SwitchOn(ITEM *switch_item, ITEM *lara_item);
static void M_SwitchOff(ITEM *switch_item, ITEM *lara_item);
@ -62,6 +52,16 @@ static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static void M_CollisionUW(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static void M_Control(int16_t item_num);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_SwitchBounds;
}
static const OBJECT_BOUNDS *M_BoundsUW(void)
{
return &m_SwitchBoundsUW;
}
static void M_AlignLara(ITEM *const lara_item, ITEM *const switch_item)
{
switch (switch_item->object_id) {
@ -139,28 +139,32 @@ static void M_Setup(OBJECT *const obj)
{
M_SetupBase(obj);
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
}
static void M_SetupPushButton(OBJECT *const obj)
{
M_Setup(obj);
obj->enable_interpolation = false;
obj->bounds_func = M_Bounds;
}
static void M_SetupUW(OBJECT *const obj)
{
M_SetupBase(obj);
obj->collision_func = M_CollisionUW;
obj->bounds_func = M_BoundsUW;
}
static void M_Collision(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if (!g_Input.action || item->status != IS_INACTIVE
|| g_Lara.gun_status != LGS_ARMLESS || lara_item->gravity
|| lara_item->current_anim_state != LS_STOP
|| !Item_TestPosition(m_SwitchBounds, item, lara_item)) {
|| !Lara_TestPosition(item, obj->bounds_func())) {
return;
}
@ -193,6 +197,7 @@ static void M_CollisionUW(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if (!g_Input.action || item->status != IS_INACTIVE
|| g_Lara.water_status != LWS_UNDERWATER
@ -201,7 +206,7 @@ static void M_CollisionUW(
return;
}
if (!Item_TestPosition(m_SwitchBoundsUW, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -22,33 +22,35 @@ static XYZ_32 m_ZiplineHandlePosition = {
.y = 0,
.z = WALL_L / 2 - 141,
};
static int16_t m_ZiplineHandleBounds[12] = {
// clang-format off
-WALL_L / 4,
+WALL_L / 4,
-100,
+100,
+WALL_L / 4,
+WALL_L / 2,
+0,
+0,
-25 * DEG_1,
+25 * DEG_1,
+0,
+0,
// clang-format on
static const OBJECT_BOUNDS m_ZiplineHandleBounds = {
.shift = {
.min = { .x = -WALL_L / 4, .y = -100, .z = +WALL_L / 4, },
.max = { .x = +WALL_L / 4, .y = +100, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = +0, .y = -25 * DEG_1, .z = +0, },
.max = { .x = +0, .y = +25 * DEG_1, .z = +0, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_Setup(OBJECT *obj);
static void M_Initialise(int16_t item_num);
static void M_Control(int16_t item_num);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_ZiplineHandleBounds;
}
static void M_Setup(OBJECT *const obj)
{
obj->initialise_func = M_Initialise;
obj->control_func = M_Control;
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
obj->save_position = 1;
obj->save_flags = 1;
obj->save_anim = 1;
@ -149,7 +151,8 @@ static void M_Collision(
return;
}
if (!Item_TestPosition(m_ZiplineHandleBounds, item, lara_item)) {
const OBJECT *const obj = Object_Get(item->object_id);
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -151,13 +151,6 @@ typedef struct {
int32_t pitch;
} SKIDOO_INFO;
typedef struct {
struct {
XYZ_16 min;
XYZ_16 max;
} shift, rot;
} OBJECT_BOUNDS;
typedef struct {
int32_t xv;
int32_t yv;