tr2/movable_block: fix pushblock stacking issues

This resolves issues with being unable to stack pushblocks across
multiple rooms.
This commit is contained in:
lahm86 2025-02-21 15:12:14 +00:00
parent e89d6932bf
commit 34ff171fa3
4 changed files with 112 additions and 79 deletions

View file

@ -9,6 +9,7 @@
- fixed several issues with pushblocks (#2036)
- fixed an invisible wall above stacked pushblocks if near a ceiling portal
- fixed floor height issues with pushblocks poised to fall in various scenarios
- fixed being unable to stack multiple pushblocks over multiple rooms
- fixed play any level causing the game to hang when no gym level is present (#2560, regression from 0.9)
- fixed extremely large item quantities crashing the game (#2497, regression from 0.3)
- fixed missing new game text in the passport when play any level is enabled (#2563, regression from 0.9)

View file

@ -81,8 +81,10 @@ game with new enhancements and features.
- fixed smashed windows blocking enemy pathing after loading a save
- fixed Lara getting stuck in a T-pose after jumping/falling and then dying before reaching fast fall speed
- fixed several issues with pushblocks
- fixed several issues with pushblocks:
- fixed an invisible wall above stacked pushblocks if near a ceiling portal
- fixed floor height issues with pushblocks poised to fall in various scenarios
- fixed being unable to stack multiple pushblocks over multiple rooms
- improved the animation of Lara's braid
#### Cheats

View file

@ -36,9 +36,9 @@ static int16_t m_MovableBlockBounds[12] = {
static bool M_TestDestination(const ITEM *item, int32_t block_height);
static bool M_TestPush(
const ITEM *item, int32_t block_height, uint16_t quadrant);
const ITEM *item, int32_t block_height, DIRECTION quadrant);
static bool M_TestPull(
const ITEM *item, int32_t block_height, uint16_t quadrant);
const ITEM *item, int32_t block_height, DIRECTION quadrant);
static void M_Setup(OBJECT *obj);
static void M_Initialise(int16_t item_num);
@ -55,12 +55,14 @@ static bool M_TestDestination(
const SECTOR *const sector =
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
const int16_t floor = sector->floor.height;
const int16_t floor =
Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);
return floor == NO_HEIGHT || (floor == item->pos.y - block_height);
}
static bool M_TestPush(
const ITEM *const item, const int32_t block_height, const uint16_t quadrant)
const ITEM *const item, const int32_t block_height,
const DIRECTION quadrant)
{
if (!M_TestDestination(item, block_height)) {
return false;
@ -88,6 +90,8 @@ static bool M_TestPush(
break;
}
const SECTOR *sector = Room_GetSector(x, y, z, &room_num);
COLL_INFO coll = {
.quadrant = quadrant,
.radius = 500,
@ -97,13 +101,8 @@ static bool M_TestPush(
return false;
}
const SECTOR *sector = Room_GetSector(x, y, z, &room_num);
if (sector->floor.height != y) {
return false;
}
Room_GetHeight(sector, x, y, z);
if (Room_GetHeightType() != HT_WALL) {
const int16_t height = Room_GetHeight(sector, x, y, z);
if (height != y || Room_GetHeightType() != HT_WALL) {
return false;
}
@ -117,7 +116,8 @@ static bool M_TestPush(
}
static bool M_TestPull(
const ITEM *const item, const int32_t block_height, const uint16_t quadrant)
const ITEM *const item, const int32_t block_height,
const DIRECTION quadrant)
{
if (!M_TestDestination(item, block_height)) {
return false;
@ -146,6 +146,7 @@ static bool M_TestPull(
int32_t y = item->pos.y;
int32_t z = item->pos.z + z_add;
int16_t room_num = item->room_num;
const SECTOR *sector = Room_GetSector(x, y, z, &room_num);
COLL_INFO coll = {
.quadrant = quadrant,
@ -156,14 +157,13 @@ static bool M_TestPull(
return false;
}
const SECTOR *sector = Room_GetSector(x, y, z, &room_num);
if (sector->floor.height != y) {
if (Room_GetHeight(sector, x, y, z) != y) {
return false;
}
const int32_t y_min = y - block_height;
sector = Room_GetSector(x, y_min, z, &room_num);
if (sector->ceiling.height > y_min) {
if (Room_GetCeiling(sector, x, y_min, z) > y_min) {
return false;
}
@ -171,12 +171,12 @@ static bool M_TestPull(
z += z_add;
room_num = item->room_num;
sector = Room_GetSector(x, y, z, &room_num);
if (sector->floor.height != y) {
if (Room_GetHeight(sector, x, y, z) != y) {
return false;
}
sector = Room_GetSector(x, y - LARA_HEIGHT, z, &room_num);
if (sector->ceiling.height > y - LARA_HEIGHT) {
if (Room_GetCeiling(sector, x, y - LARA_HEIGHT, z) > y - LARA_HEIGHT) {
return false;
}
@ -184,7 +184,7 @@ static bool M_TestPull(
z = g_LaraItem->pos.z + z_add;
y = g_LaraItem->pos.y;
room_num = g_LaraItem->room_num;
Room_GetSector(x, y, z, &room_num);
sector = Room_GetSector(x, y, z, &room_num);
coll.quadrant = (quadrant + 2) & 3;
coll.radius = LARA_RADIUS;
if (Collide_CollideStaticObjects(&coll, x, y, z, room_num, LARA_HEIGHT)) {
@ -308,63 +308,67 @@ static void M_Collision(
const DIRECTION quadrant = Math_GetDirection(lara_item->rot.y);
if (lara_item->current_anim_state == LS_STOP) {
if (g_Lara.gun_status == LGS_ARMLESS) {
switch (quadrant) {
case DIR_NORTH:
item->rot.y = 0;
break;
case DIR_EAST:
item->rot.y = DEG_90;
break;
case DIR_SOUTH:
item->rot.y = -DEG_180;
break;
case DIR_WEST:
item->rot.y = -DEG_90;
break;
default:
break;
}
if (g_Input.forward || g_Input.back
|| g_Lara.gun_status != LGS_ARMLESS) {
return;
}
if (!Item_TestPosition(m_MovableBlockBounds, item, lara_item)) {
return;
}
switch (quadrant) {
case DIR_NORTH:
item->rot.y = 0;
break;
case DIR_EAST:
item->rot.y = DEG_90;
break;
case DIR_SOUTH:
item->rot.y = -DEG_180;
break;
case DIR_WEST:
item->rot.y = -DEG_90;
break;
default:
break;
}
int16_t room_num = lara_item->room_num;
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
if (room_num != item->room_num) {
return;
}
if (!Item_TestPosition(m_MovableBlockBounds, item, lara_item)) {
return;
}
switch (quadrant) {
case DIR_NORTH:
lara_item->pos.z = ROUND_TO_SECTOR(lara_item->pos.z);
lara_item->pos.z += WALL_L - LARA_RADIUS;
break;
case DIR_EAST:
lara_item->pos.x = ROUND_TO_SECTOR(lara_item->pos.x);
lara_item->pos.x += WALL_L - LARA_RADIUS;
break;
case DIR_SOUTH:
lara_item->pos.z = ROUND_TO_SECTOR(lara_item->pos.z);
lara_item->pos.z += LARA_RADIUS;
break;
case DIR_WEST:
lara_item->pos.x = ROUND_TO_SECTOR(lara_item->pos.x);
lara_item->pos.x += LARA_RADIUS;
break;
default:
break;
}
int16_t room_num = lara_item->room_num;
Room_GetSector(
item->pos.x, item->pos.y - STEP_L / 2, item->pos.z, &room_num);
if (room_num != item->room_num) {
return;
}
lara_item->rot.y = item->rot.y;
lara_item->goal_anim_state = LS_PP_READY;
switch (quadrant) {
case DIR_NORTH:
lara_item->pos.z = ROUND_TO_SECTOR(lara_item->pos.z);
lara_item->pos.z += WALL_L - LARA_RADIUS;
break;
case DIR_EAST:
lara_item->pos.x = ROUND_TO_SECTOR(lara_item->pos.x);
lara_item->pos.x += WALL_L - LARA_RADIUS;
break;
case DIR_SOUTH:
lara_item->pos.z = ROUND_TO_SECTOR(lara_item->pos.z);
lara_item->pos.z += LARA_RADIUS;
break;
case DIR_WEST:
lara_item->pos.x = ROUND_TO_SECTOR(lara_item->pos.x);
lara_item->pos.x += LARA_RADIUS;
break;
default:
break;
}
Lara_Animate(lara_item);
lara_item->rot.y = item->rot.y;
lara_item->goal_anim_state = LS_PP_READY;
if (lara_item->current_anim_state == LS_PP_READY) {
g_Lara.gun_status = LGS_HANDS_BUSY;
}
Lara_Animate(lara_item);
if (lara_item->current_anim_state == LS_PP_READY) {
g_Lara.gun_status = LGS_HANDS_BUSY;
}
} else if (
Item_TestAnimEqual(lara_item, LA_PUSHABLE_GRAB)

View file

@ -558,21 +558,47 @@ void Room_TestTriggers(const ITEM *const item)
void Room_AlterFloorHeight(const ITEM *const item, const int32_t height)
{
int16_t room_num = item->room_num;
if (height == 0) {
return;
}
SECTOR *const sector =
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
const SECTOR *ceiling = Room_GetSector(
item->pos.x, item->pos.y + height - WALL_L, item->pos.z, &room_num);
int16_t portal_room;
SECTOR *sector;
const ROOM *room = Room_Get(item->room_num);
if (sector->floor.height == NO_HEIGHT) {
sector->floor.height = ceiling->ceiling.height + ROUND_TO_CLICK(height);
} else {
do {
int32_t z_sector = (item->pos.z - room->pos.z) >> WALL_SHIFT;
int32_t x_sector = (item->pos.x - room->pos.x) >> WALL_SHIFT;
if (z_sector <= 0) {
z_sector = 0;
CLAMP(x_sector, 1, room->size.x - 2);
} else if (z_sector >= room->size.z - 1) {
z_sector = room->size.z - 1;
CLAMP(x_sector, 1, room->size.x - 2);
} else {
CLAMP(x_sector, 0, room->size.x - 1);
}
sector = Room_GetUnitSector(room, x_sector, z_sector);
portal_room = sector->portal_room.wall;
if (portal_room != NO_ROOM) {
room = Room_Get(portal_room);
}
} while (portal_room != NO_ROOM);
const SECTOR *const sky_sector =
Room_GetSkySector(sector, item->pos.x, item->pos.z);
sector = Room_GetPitSector(sector, item->pos.x, item->pos.z);
if (sector->floor.height != NO_HEIGHT) {
sector->floor.height += ROUND_TO_CLICK(height);
if (sector->floor.height == ceiling->ceiling.height
&& sector->portal_room.sky == NO_ROOM) {
if (sector->floor.height == sky_sector->ceiling.height) {
sector->floor.height = NO_HEIGHT;
}
} else {
sector->floor.height =
sky_sector->ceiling.height + ROUND_TO_CLICK(height);
}
BOX_INFO *const box = Box_GetBox(sector->box);