Updates to pushables

Introduced PUSHABLE_INFO struct, which is stored in item->data of pushable.

Tried to implement as many TRNG pushable OCBs as I could.
This commit is contained in:
KrysB4k 2020-12-30 02:12:14 +01:00
parent 1d4e69ba36
commit cf1db753da
3 changed files with 381 additions and 209 deletions

View file

@ -17,6 +17,7 @@
#include "tr5_rats_emitter.h"
#include "tr5_bats_emitter.h"
#include "tr5_spider_emitter.h"
#include "tr5_pushableblock.h"
#include "pickup.h"
#include "puzzles_keys.h"
#include "lara_fire.h"
@ -48,8 +49,8 @@ function<EffectFunction> effect_routines[59] =
draw_left_pistol,
shoot_right_gun,
shoot_left_gun,
void_effect,
void_effect,
pushLoop,
pushEnd,
void_effect,
invisibility_on,
invisibility_off,

View file

@ -25,7 +25,7 @@ void ClearMovableBlockSplitters(int x, int y, int z, short roomNumber)
g_Level.Boxes[floor->box].flags &= (~BLOCKED);
short height = g_Level.Boxes[floor->box].height;
short baseRoomNumber = roomNumber;
floor = GetFloor(x + 1024, y, z, &roomNumber);
if (floor->box != NO_BOX)
{
@ -61,17 +61,57 @@ void ClearMovableBlockSplitters(int x, int y, int z, short roomNumber)
void InitialisePushableBlock(short itemNum)
{
ITEM_INFO* item = &g_Level.Items[itemNum];
item->itemFlags[1] = NO_ITEM; // need to use itemFlags[1] to hold linked index for now
ClearMovableBlockSplitters(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
item->itemFlags[1] = NO_ITEM; // used for linking pushables together in stack
//if (item->status != ITEM_INVISIBLE && item->triggerFlags >= 64)
// AlterFloorHeight(item, -((item->triggerFlags - 64) * 256));
PUSHABLE_INFO* pushable = new PUSHABLE_INFO;
pushable->stackLimit = 3; // LUA
pushable->gravity = 9; // LUA
pushable->weight = 100; // LUA
// read flags from OCB
int OCB = item->triggerFlags;
pushable->canFall = OCB & 0x20;
pushable->hasFloorCeiling = OCB & 0x40;
pushable->disablePull = OCB & 0x80;
pushable->disablePush = OCB & 0x100;
pushable->disableW = pushable->disableE = OCB & 0x200;
pushable->disableN = pushable->disableS = OCB & 0x400;
pushable->climb = 0; // maybe there will be better way to handle this than OCBs?
/*
pushable->climb |= (OCB & 0x800) ? CLIMB_WEST : 0;
pushable->climb |= (OCB & 0x1000) ? CLIMB_NORTH : 0;
pushable->climb |= (OCB & 0x2000) ? CLIMB_EAST : 0;
pushable->climb |= (OCB & 0x4000) ? CLIMB_SOUTH : 0;
*/
int height;
if (pushable->hasFloorCeiling)
height = (OCB & 0x1F) * CLICK(1);
else
height = -GetBoundsAccurate(item)->Y1;
pushable->height = height;
pushable->loopSound = SFX_PUSHABLE_SOUND; // LUA
pushable->stopSound = SFX_PUSH_BLOCK_END; // LUA
pushable->fallSound = SFX_TR4_BOULDER_FALL; // LUA
item->data = (void*) pushable;
FindStack(itemNum); // check for stack formation on init (can't use pushable->linkedIndex with current setup)
}
void PushableBlockControl(short itemNumber)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
Lara.generalPtr = (void*)itemNumber;
PHD_VECTOR pos;
pos.x = 0;
pos.y = 0;
@ -80,58 +120,86 @@ void PushableBlockControl(short itemNumber)
short quadrant = (unsigned short)(LaraItem->pos.yRot + ANGLE(45)) / ANGLE(90);
int x, z;
short roomNumber;
FLOOR_INFO* floor;
ROOM_INFO* r;
int height;
short roomNumber;
PUSHABLE_INFO* pushable = pushable_info(item);
int blockHeight = GetStackHeight(item);
int stackIndex; // used for stacked pushables
int blockHeight;
if (item->triggerFlags > 64)
blockHeight = CLICK(item->triggerFlags - 64);
else
blockHeight = -(GetBoundsAccurate(item)->Y1);
// get total height of stack
stackIndex = item->itemFlags[1];
while (stackIndex != NO_ITEM)
// do sound effects, it works for now
if (DoPushPull > 0)
{
auto stackItem = &g_Level.Items[stackIndex];
if (stackItem->triggerFlags > 64)
blockHeight += CLICK(stackItem->triggerFlags - 64);
else
blockHeight += -(GetBoundsAccurate(stackItem)->Y1);
stackIndex = stackItem->itemFlags[1];
int blockIndex = (short)Lara.generalPtr;
ITEM_INFO* block = &g_Level.Items[blockIndex];
SoundEffect(pushable_info(block)->loopSound, &block->pos, 2);
}
else if (DoPushPull < 0)
{
DoPushPull = 0;
int blockIndex = (short)Lara.generalPtr;
ITEM_INFO* block = &g_Level.Items[blockIndex];
SoundEffect(pushable_info(block)->stopSound, &block->pos, 2);
}
// control block falling
if (item->gravityStatus)
{
roomNumber = item->roomNumber;
floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
int floorHeight = GetFloorHeight(floor, item->pos.xPos, item->pos.yPos + 10, item->pos.zPos);
if (item->pos.yPos < floorHeight - item->fallspeed)
{
if (item->fallspeed + pushable->gravity < 128)
item->fallspeed += pushable->gravity;
else
item->fallspeed = 128;
item->pos.yPos += item->fallspeed;
MoveStackY(itemNumber, item->fallspeed);
}
else
{
item->gravityStatus = false;
int relY = floorHeight - item->pos.yPos;
item->pos.yPos = floorHeight;
if (item->fallspeed >= 128)
floor_shake_effect(item);
item->fallspeed = 0;
SoundEffect(pushable->fallSound, &item->pos, 2);
MoveStackY(itemNumber, relY);
if (FindStack(itemNumber) == NO_ITEM) // if fallen on some existing pushables, don't test triggers
{
roomNumber = item->roomNumber;
TestTriggersAtXYZ(item->pos.xPos, item->pos.yPos, item->pos.zPos, roomNumber, 1, item->flags & 0x3E00);
}
RemoveActiveItem(itemNumber);
item->status = ITEM_NOT_ACTIVE;
if (pushable->hasFloorCeiling)
{
//AlterFloorHeight(item, -((item->triggerFlags - 64) * 256));
AdjustStopperFlag(item, item->itemFlags[0] + 0x8000, 0);
}
}
return;
}
switch (LaraItem->animNumber)
{
case LA_PUSHABLE_PUSH:
if ((LaraItem->frameNumber < g_Level.Anims[LaraItem->animNumber].frameBase + 30
|| LaraItem->frameNumber > g_Level.Anims[LaraItem->animNumber].frameBase + 67)
&& (LaraItem->frameNumber < g_Level.Anims[LaraItem->animNumber].frameBase + 78
|| LaraItem->frameNumber > g_Level.Anims[LaraItem->animNumber].frameBase + 125)
&& (LaraItem->frameNumber < g_Level.Anims[LaraItem->animNumber].frameBase + 140
|| LaraItem->frameNumber > g_Level.Anims[LaraItem->animNumber].frameBase + 160))
{
if (DoPushPull)
{
SoundEffect(SFX_PUSH_BLOCK_END, &item->pos, 2);
DoPushPull = 0;
}
}
else
{
SoundEffect(SFX_PUSHABLE_SOUND, &item->pos, 2);
DoPushPull = 1;
}
if (LaraItem->frameNumber == g_Level.Anims[LaraItem->animNumber].frameBase)
RemoveFromStack(itemNumber);
GetLaraJointPosition(&pos, LM_LHAND);
switch (quadrant)
// TODO: come up with better code that doesn't rely on itemFlags
switch (quadrant)
{
case 0:
z = pos.z + item->itemFlags[2] - LaraItem->itemFlags[2];
@ -162,31 +230,24 @@ void PushableBlockControl(short itemNumber)
break;
}
stackIndex = item->itemFlags[1];
while (stackIndex != NO_ITEM) // move pushblock stack together with bottom pushblock
{
auto stackItem = &g_Level.Items[stackIndex];
stackItem->pos.xPos = item->pos.xPos;
stackItem->pos.zPos = item->pos.zPos;
MoveStackXZ(itemNumber);
stackIndex = stackItem->itemFlags[1];
}
if (LaraItem->frameNumber == g_Level.Anims[LaraItem->animNumber].frameBase)
if (LaraItem->frameNumber == g_Level.Anims[LaraItem->animNumber].frameEnd - 16)
{
// unlink pushable from stack if linked
for (int i = 0; i < g_Level.NumItems; i++)
if (pushable->canFall)
{
if (i == itemNumber)
continue;
auto belowItem = &g_Level.Items[i];
int objectNum = belowItem->objectNumber;
if (objectNum >= ID_PUSHABLE_OBJECT1 && objectNum <= ID_PUSHABLE_OBJECT10)
roomNumber = item->roomNumber;
floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
if (GetFloorHeight(floor, item->pos.xPos, item->pos.yPos + 10, item->pos.zPos) > item->pos.yPos)
{
if (belowItem->itemFlags[1] == itemNumber)
belowItem->itemFlags[1] = NO_ITEM;
item->pos.xPos = item->pos.xPos & 0xFFFFFE00 | 0x200;
item->pos.zPos = item->pos.zPos & 0xFFFFFE00 | 0x200;
MoveStackXZ(itemNumber);
SoundEffect(pushable->stopSound, &item->pos, 2);
DoPushPull = 0;
LaraItem->goalAnimState = LS_STOP;
item->gravityStatus = true; // do fall
}
}
}
@ -196,13 +257,11 @@ void PushableBlockControl(short itemNumber)
if (TrInput & IN_ACTION)
{
if (!TestBlockPush(item, blockHeight, quadrant))
{
LaraItem->goalAnimState = LS_STOP;
}
else
{
int newRoomNumber = T5M::Floordata::GetRoom(item->roomNumber, item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (newRoomNumber != item->roomNumber)
ItemNewRoom(itemNumber, newRoomNumber);
TestTriggersAtXYZ(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber, 1, item->flags & 0x3E00);
}
}
@ -214,22 +273,9 @@ void PushableBlockControl(short itemNumber)
break;
case LA_PUSHABLE_PULL:
if ((LaraItem->frameNumber < g_Level.Anims[LaraItem->animNumber].frameBase + 40
|| LaraItem->frameNumber > g_Level.Anims[LaraItem->animNumber].frameBase + 122)
&& (LaraItem->frameNumber < g_Level.Anims[LaraItem->animNumber].frameBase + 130
|| LaraItem->frameNumber > g_Level.Anims[LaraItem->animNumber].frameBase + 170))
{
if (DoPushPull)
{
SoundEffect(SFX_PUSH_BLOCK_END, &item->pos, 2);
DoPushPull = 0;
}
}
else
{
SoundEffect(SFX_PUSHABLE_SOUND, &item->pos, 2);
DoPushPull = 1;
}
if (LaraItem->frameNumber == g_Level.Anims[LaraItem->animNumber].frameBase)
RemoveFromStack(itemNumber);
GetLaraJointPosition(&pos, LM_LHAND);
@ -263,48 +309,18 @@ void PushableBlockControl(short itemNumber)
break;
}
stackIndex = item->itemFlags[1];
while (stackIndex != NO_ITEM) // move pushblock stack together with bottom pushblock
{
auto stackItem = &g_Level.Items[stackIndex];
stackItem->pos.xPos = item->pos.xPos;
stackItem->pos.zPos = item->pos.zPos;
stackIndex = stackItem->itemFlags[1];
}
if (LaraItem->frameNumber == g_Level.Anims[LaraItem->animNumber].frameBase)
{
// unlink pushable from stack if linked
for (int i = 0; i < g_Level.NumItems; i++)
{
if (i == itemNumber)
continue;
auto belowItem = &g_Level.Items[i];
int objectNum = belowItem->objectNumber;
if (objectNum >= ID_PUSHABLE_OBJECT1 && objectNum <= ID_PUSHABLE_OBJECT10)
{
if (belowItem->itemFlags[1] == itemNumber)
belowItem->itemFlags[1] = NO_ITEM;
}
}
}
MoveStackXZ(itemNumber);
if (LaraItem->frameNumber == g_Level.Anims[LaraItem->animNumber].frameEnd - 1)
{
if (TrInput & IN_ACTION)
{
if (!TestBlockPull(item, blockHeight, quadrant))
{
LaraItem->goalAnimState = LS_STOP;
}
else
{
int newRoomNumber = T5M::Floordata::GetRoom(item->roomNumber, item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (newRoomNumber != item->roomNumber)
ItemNewRoom(itemNumber, newRoomNumber);
TestTriggersAtXYZ(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber, 1, item->flags & 0x3E00);
}
}
@ -314,6 +330,7 @@ void PushableBlockControl(short itemNumber)
}
}
break;
case LA_PUSHABLE_PUSH_TO_STAND:
case LA_PUSHABLE_PULL_TO_STAND:
if (LaraItem->frameNumber == g_Level.Anims[LA_PUSHABLE_PUSH_TO_STAND].frameBase
@ -322,71 +339,23 @@ void PushableBlockControl(short itemNumber)
item->pos.xPos = item->pos.xPos & 0xFFFFFE00 | 0x200;
item->pos.zPos = item->pos.zPos & 0xFFFFFE00 | 0x200;
stackIndex = item->itemFlags[1];
while (stackIndex != NO_ITEM) // move pushblock stack together with bottom pushblock
{
auto stackItem = &g_Level.Items[stackIndex];
stackItem->pos.xPos = item->pos.xPos;
stackItem->pos.zPos = item->pos.zPos;
MoveStackXZ(itemNumber);
FindStack(itemNumber);
stackIndex = stackItem->itemFlags[1];
}
roomNumber = item->roomNumber;
TestTriggersAtXYZ(item->pos.xPos, item->pos.yPos, item->pos.zPos, roomNumber, 1, item->flags & 0x3E00);
}
if (LaraItem->frameNumber == g_Level.Anims[LaraItem->animNumber].frameEnd)
{
int newRoomNumber = T5M::Floordata::GetRoom(item->roomNumber, item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (newRoomNumber != item->roomNumber)
ItemNewRoom(itemNumber, newRoomNumber);
roomNumber = item->roomNumber;
floor = GetFloor(item->pos.xPos, item->pos.yPos - 256, item->pos.zPos, &roomNumber);
GetFloorHeight(floor, item->pos.xPos, item->pos.yPos - 256, item->pos.zPos);
TestTriggers(TriggerIndex, 1, item->flags & 0x3E00);
RemoveActiveItem(itemNumber);
item->status = ITEM_NOT_ACTIVE;
if (item->triggerFlags >= 64)
if (pushable->hasFloorCeiling)
{
//AlterFloorHeight(item, -((item->triggerFlags - 64) * 256));
AdjustStopperFlag(item, item->itemFlags[0] + 0x8000, 0);
}
int stackTop = NO_ITEM; // index of heighest (yPos) pushable in stack
int stackYmin = CLICK(256); // set starting height
//Check for pushable directly below current one in same sector
for (int i = 0; i < g_Level.NumItems; i++)
{
if (i == itemNumber)
continue;
auto belowItem = &g_Level.Items[i];
int objectNum = belowItem->objectNumber;
if (objectNum >= ID_PUSHABLE_OBJECT1 && objectNum <= ID_PUSHABLE_OBJECT10)
{
int x = item->pos.xPos;
int y = item->pos.yPos;
int z = item->pos.zPos;
if (belowItem->pos.xPos == x && belowItem->pos.zPos == z)
{
int belowY = belowItem->pos.yPos;
if (belowY > y && belowY < stackYmin)
{
// set heighest pushable so far as top of stack
stackTop = i;
stackYmin = belowItem->pos.yPos;
}
}
}
}
// if top pushable in stack was located, link index of current pushable to below pushable via itemFlags[1]
if (stackTop != NO_ITEM)
g_Level.Items[stackTop].itemFlags[1] = itemNumber;
}
break;
}
@ -398,29 +367,10 @@ void PushableBlockCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
short roomNumber = item->roomNumber;
FLOOR_INFO* floor = GetFloor(item->pos.xPos, item->pos.yPos - 256, item->pos.zPos, &roomNumber);
PUSHABLE_INFO* pushable = pushable_info(item);
int stackIndex; // used for stacked pushables
int blockHeight;
if (item->triggerFlags > 64)
blockHeight = CLICK(item->triggerFlags - 64);
else
blockHeight = -(GetBoundsAccurate(item)->Y1);
// get total height of stack
stackIndex = item->itemFlags[1];
while (stackIndex != NO_ITEM)
{
auto stackItem = &g_Level.Items[stackIndex];
if (stackItem->triggerFlags > 64)
blockHeight += CLICK(stackItem->triggerFlags - 64);
else
blockHeight += -(GetBoundsAccurate(stackItem)->Y1);
stackIndex = stackItem->itemFlags[1];
}
int blockHeight = GetStackHeight(item);
if ((!(TrInput & IN_ACTION)
|| l->currentAnimState != LS_STOP
|| l->animNumber != LA_STAND_IDLE
@ -434,22 +384,45 @@ void PushableBlockCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
|| (l->frameNumber != g_Level.Anims[LA_PUSHABLE_GRAB].frameBase + 19)
|| Lara.cornerX != (int)item))
{
if (item->triggerFlags < 64)
if (!pushable->hasFloorCeiling)
ObjectCollision(itemNum, l, coll);
return;
}
short quadrant = (unsigned short)(LaraItem->pos.yRot + ANGLE(45)) / ANGLE(90);
bool quadrantDisabled = false;
switch (quadrant)
{
case NORTH:
quadrantDisabled = pushable->disableN;
break;
case EAST:
quadrantDisabled = pushable->disableE;
break;
case SOUTH:
quadrantDisabled = pushable->disableS;
break;
case WEST:
quadrantDisabled = pushable->disableW;
break;
}
if (quadrantDisabled)
return;
if (!CheckStackLimit(item))
return;
if (TrInput & IN_FORWARD)
{
if (!TestBlockPush(item, blockHeight, quadrant))
if (!TestBlockPush(item, blockHeight, quadrant) || pushable->disablePush)
return;
l->goalAnimState = LS_PUSHABLE_PUSH;
}
else if (TrInput & IN_BACK)
{
if (!TestBlockPull(item, blockHeight, quadrant))
if (!TestBlockPull(item, blockHeight, quadrant) || pushable->disablePull)
return;
l->goalAnimState = LS_PUSHABLE_PULL;
}
@ -472,13 +445,14 @@ void PushableBlockCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
GetLaraJointPosition(&pos, LM_LHAND);
// TODO: come up with better code that doesn't rely on itemFlags
l->itemFlags[0] = pos.x;
l->itemFlags[2] = pos.z;
item->itemFlags[0] = item->pos.xPos;
item->itemFlags[2] = item->pos.zPos;
if (item->triggerFlags >= 64)
if (pushable->hasFloorCeiling)
{
//AlterFloorHeight(item, ((item->triggerFlags - 64) * 256));
AdjustStopperFlag(item, item->itemFlags[0], 0);
@ -508,7 +482,7 @@ void PushableBlockCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
else
PushableBlockPos.z = bounds->Z1 - 35;
if (item->triggerFlags > 64)
if (pushable->hasFloorCeiling)
{
// For now don't use auto-align function because it can collide with climb up moves of Lara
@ -558,6 +532,19 @@ void PushableBlockCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
}
}
void pushLoop(ITEM_INFO* item) // Do Flipeffect 18 in anims
{
DoPushPull = 1;
}
void pushEnd(ITEM_INFO* item) // Do Flipeffect 19 in anims
{
if (DoPushPull == 1)
{
DoPushPull = -1;
}
}
int TestBlockMovable(ITEM_INFO* item, int blokhite)
{
short roomNumber = item->roomNumber;
@ -602,9 +589,18 @@ int TestBlockPush(ITEM_INFO* item, int blockhite, unsigned short quadrant)
if (XZ_GET_SECTOR(r, x - r->x, z - r->z).stopper)
return 0;
if (GetFloorHeight(floor, x, y - 256, z) != y)
return 0;
int floorHeight = GetFloorHeight(floor, x, y - 256, z);
if (pushable_info(item)->canFall)
{
if (floorHeight < y)
return 0;
}
else
{
if (floorHeight != y)
return 0;
}
GetFloorHeight(floor, x, y, z);
if (HeightType)
return 0;
@ -727,12 +723,154 @@ int TestBlockPull(ITEM_INFO* item, int blockhite, short quadrant)
return 1;
}
void MoveStackXZ(int itemNum)
{
auto item = &g_Level.Items[itemNum];
int newRoomNumber = T5M::Floordata::GetRoom(item->roomNumber, item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (newRoomNumber != item->roomNumber)
ItemNewRoom(itemNum, newRoomNumber);
auto stackItem = item;
while (stackItem->itemFlags[1] != NO_ITEM) // move pushblock stack together with bottom pushblock
{
int stackIndex = stackItem->itemFlags[1];
stackItem = &g_Level.Items[stackIndex];
stackItem->pos.xPos = item->pos.xPos;
stackItem->pos.zPos = item->pos.zPos;
int newRoomNumber = T5M::Floordata::GetRoom(stackItem->roomNumber, stackItem->pos.xPos, stackItem->pos.yPos, stackItem->pos.zPos);
if (newRoomNumber != stackItem->roomNumber)
ItemNewRoom(stackIndex, newRoomNumber);
}
}
void MoveStackY(int itemNum, int y)
{
auto item = &g_Level.Items[itemNum];
int newRoomNumber = T5M::Floordata::GetRoom(item->roomNumber, item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (newRoomNumber != item->roomNumber)
ItemNewRoom(itemNum, newRoomNumber);
while (item->itemFlags[1] != NO_ITEM) // move pushblock stack together with bottom pushblock
{
int stackIndex = item->itemFlags[1];
item = &g_Level.Items[stackIndex];
item->pos.yPos += y;
int newRoomNumber = T5M::Floordata::GetRoom(item->roomNumber, item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (newRoomNumber != item->roomNumber)
ItemNewRoom(stackIndex, newRoomNumber);
}
}
void RemoveFromStack(int itemNum) // unlink pushable from stack if linked
{
for (int i = 0; i < g_Level.NumItems; i++)
{
if (i == itemNum)
continue;
auto belowItem = &g_Level.Items[i];
int objectNum = belowItem->objectNumber;
if (objectNum >= ID_PUSHABLE_OBJECT1 && objectNum <= ID_PUSHABLE_OBJECT10)
{
if (belowItem->itemFlags[1] == itemNum)
belowItem->itemFlags[1] = NO_ITEM;
}
}
}
int FindStack(int itemNum)
{
int stackTop = NO_ITEM; // index of heighest (yPos) pushable in stack
int stackYmin = CLICK(256); // set starting height
//Check for pushable directly below current one in same sector
for (int i = 0; i < g_Level.NumItems; i++)
{
if (i == itemNum)
continue;
auto belowItem = &g_Level.Items[i];
int objectNum = belowItem->objectNumber;
if (objectNum >= ID_PUSHABLE_OBJECT1 && objectNum <= ID_PUSHABLE_OBJECT10)
{
auto item = &g_Level.Items[itemNum];
int x = item->pos.xPos;
int y = item->pos.yPos;
int z = item->pos.zPos;
if (belowItem->pos.xPos == x && belowItem->pos.zPos == z)
{
int belowY = belowItem->pos.yPos;
if (belowY > y && belowY < stackYmin)
{
// set heighest pushable so far as top of stack
stackTop = i;
stackYmin = belowItem->pos.yPos;
}
}
}
}
if (stackTop != NO_ITEM)
g_Level.Items[stackTop].itemFlags[1] = itemNum;
return stackTop;
}
int GetStackHeight(ITEM_INFO* item)
{
int height = pushable_info(item)->height;
auto stackItem = item;
while (stackItem->itemFlags[1] != NO_ITEM)
{
stackItem = &g_Level.Items[stackItem->itemFlags[1]];
height += pushable_info(stackItem)->height;
}
return height;
}
int CheckStackLimit(ITEM_INFO* item)
{
int limit = pushable_info(item)->stackLimit;
int count = 1;
auto stackItem = item;
while (stackItem->itemFlags[1] != NO_ITEM)
{
stackItem = &g_Level.Items[stackItem->itemFlags[1]];
count++;
if (count > limit)
return 0;
}
return 1;
}
PUSHABLE_INFO* pushable_info(ITEM_INFO* item) // retrieve PUSHABLE_INFO* from void* data
{
return (PUSHABLE_INFO*)item->data;
}
std::tuple<std::optional<int>, bool> PushableBlockFloor(short itemNumber, int x, int y, int z)
{
const auto& item = g_Level.Items[itemNumber];
if (item.status != ITEM_INVISIBLE && item.triggerFlags >= 64 && abs(item.pos.xPos - x) <= SECTOR(1) / 2 && abs(item.pos.zPos - z) <= SECTOR(1) / 2)
const auto pushable = pushable_info(&g_Level.Items[itemNumber]);
if (item.status != ITEM_INVISIBLE && pushable->hasFloorCeiling && !item.gravityStatus &&
abs(item.pos.xPos - x) <= SECTOR(1) / 2 && abs(item.pos.zPos - z) <= SECTOR(1) / 2)
{
auto height = item.pos.yPos - (item.triggerFlags - 64) * CLICK(1);
auto height = item.pos.yPos - pushable->height;
return std::make_tuple(std::optional{height}, y > height && y <= item.pos.yPos);
}
return std::make_tuple(std::nullopt, false);
@ -741,10 +879,13 @@ std::tuple<std::optional<int>, bool> PushableBlockFloor(short itemNumber, int x,
std::tuple<std::optional<int>, bool> PushableBlockCeiling(short itemNumber, int x, int y, int z)
{
const auto& item = g_Level.Items[itemNumber];
if (item.status != ITEM_INVISIBLE && item.triggerFlags >= 64 && abs(item.pos.xPos - x) <= SECTOR(1) / 2 && abs(item.pos.zPos - z) <= SECTOR(1) / 2)
const auto pushable = pushable_info(&g_Level.Items[itemNumber]);
if (item.status != ITEM_INVISIBLE && pushable->hasFloorCeiling && !item.gravityStatus &&
abs(item.pos.xPos - x) <= SECTOR(1) / 2 && abs(item.pos.zPos - z) <= SECTOR(1) / 2)
{
auto height = item.pos.yPos - (item.triggerFlags - 64) * CLICK(1);
auto height = item.pos.yPos - pushable->height;
return std::make_tuple(std::optional{item.pos.yPos}, y >= height && y < item.pos.yPos);
}
return std::make_tuple(std::nullopt, false);
}
}

View file

@ -2,6 +2,27 @@
#include "items.h"
#include "collide.h"
typedef struct PUSHABLE_INFO // void* data of pushable
{
int height; // height for collision, also in floor procedure
int weight;
int stackLimit;
short linkedIndex; // using itemFlags[1] for now
short gravity; // fall acceleration
short loopSound; // looped sound index for movement
short stopSound; // ending sound index
short fallSound; // sound on hitting floor (if dropped)
short climb; // not used for now
bool canFall; // OCB 32
bool hasFloorCeiling; // has floor and ceiling procedures (OCB 64)
bool disablePull; // OCB 128
bool disablePush; // OCB 256
bool disableW; // OCB 512 (W+E)
bool disableE; // OCB 512 (W+E)
bool disableN; // OCB 1024 (N+S)
bool disableS; // OCB 1024 (N+S)
};
void ClearMovableBlockSplitters(int x, int y, int z, short roomNumber);
void InitialisePushableBlock(short itemNum);
void PushableBlockControl(short itemNum);
@ -9,5 +30,14 @@ void PushableBlockCollision(short itemNum, ITEM_INFO* laraitem, COLL_INFO* coll)
int TestBlockMovable(ITEM_INFO* item, int blokhite);
int TestBlockPush(ITEM_INFO* item, int blockhite, unsigned short quadrant);
int TestBlockPull(ITEM_INFO* item, int blockhite, short quadrant);
void MoveStackXZ(int itemNum);
void MoveStackY(int itemNum, int y);
void RemoveFromStack(int itemNum);
int FindStack(int itemNum);
int GetStackHeight(ITEM_INFO* item);
int CheckStackLimit(ITEM_INFO* item);
void pushLoop(ITEM_INFO* item);
void pushEnd(ITEM_INFO* item);
PUSHABLE_INFO* pushable_info(ITEM_INFO* item);
std::tuple<std::optional<int>, bool> PushableBlockFloor(short itemNumber, int x, int y, int z);
std::tuple<std::optional<int>, bool> PushableBlockCeiling(short itemNumber, int x, int y, int z);