2021-09-24 13:50:31 +03:00
|
|
|
#include "framework.h"
|
|
|
|
#include "effects/debris.h"
|
|
|
|
#include "collide.h"
|
|
|
|
#include "camera.h"
|
|
|
|
#include "animation.h"
|
|
|
|
#include "level.h"
|
|
|
|
#include "room.h"
|
|
|
|
#include "Sound/sound.h"
|
|
|
|
#include "Specific/prng.h"
|
|
|
|
|
|
|
|
using namespace TEN::Math::Random;
|
|
|
|
|
2021-09-24 13:58:56 +03:00
|
|
|
constexpr auto FALLINGBLOCK_INITIAL_SPEED = 10;
|
2021-09-25 05:00:06 +03:00
|
|
|
constexpr auto FALLINGBLOCK_MAX_SPEED = 100;
|
|
|
|
constexpr auto FALLINGBLOCK_FALL_VELOCITY = 4;
|
2021-09-24 13:58:56 +03:00
|
|
|
constexpr auto FALLINGBLOCK_FALL_ROTATION_SPEED = 1;
|
2021-09-24 13:50:31 +03:00
|
|
|
constexpr auto FALLINGBLOCK_DELAY = 52;
|
2021-09-24 13:58:56 +03:00
|
|
|
constexpr auto FALLINGBLOCK_WIBBLE = 3;
|
2021-09-24 13:50:31 +03:00
|
|
|
constexpr auto FALLINGBLOCK_CRUMBLE_DELAY = 100;
|
|
|
|
|
|
|
|
void InitialiseFallingBlock(short itemNumber)
|
|
|
|
{
|
|
|
|
auto item = &g_Level.Items[itemNumber];
|
|
|
|
|
|
|
|
g_Level.Items[itemNumber].meshBits = 1;
|
|
|
|
TEN::Floordata::AddBridge(itemNumber);
|
2021-09-24 15:51:32 +03:00
|
|
|
|
|
|
|
// Set mutators to 0 by default
|
|
|
|
for (int i = 0; i < item->mutator.size(); i++)
|
|
|
|
item->mutator[i].Rotation = Vector3::Zero;
|
2021-09-24 13:50:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void FallingBlockCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNum];
|
|
|
|
if (!item->itemFlags[0] && !item->triggerFlags && item->pos.yPos == l->pos.yPos)
|
|
|
|
{
|
|
|
|
if (!((item->pos.xPos ^ l->pos.xPos) & 0xFFFFFC00) && !((l->pos.zPos ^ item->pos.zPos) & 0xFFFFFC00))
|
|
|
|
{
|
|
|
|
SoundEffect(SFX_TR4_ROCK_FALL_CRUMBLE, &item->pos, 0);
|
|
|
|
AddActiveItem(itemNum);
|
|
|
|
|
|
|
|
item->itemFlags[0] = 0;
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
item->flags |= 0x3E00;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FallingBlockControl(short itemNumber)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
|
|
|
|
|
|
|
if (item->triggerFlags)
|
|
|
|
{
|
|
|
|
item->triggerFlags--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (item->itemFlags[0])
|
|
|
|
{
|
|
|
|
if (item->itemFlags[0] < FALLINGBLOCK_DELAY)
|
|
|
|
{
|
|
|
|
// Subtly shake all meshes separately
|
|
|
|
for (int i = 0; i < item->mutator.size(); i++)
|
|
|
|
{
|
|
|
|
item->mutator[i].Rotation.x = RADIAN * GenerateFloat(-FALLINGBLOCK_WIBBLE, FALLINGBLOCK_WIBBLE);
|
|
|
|
item->mutator[i].Rotation.y = RADIAN * GenerateFloat(-FALLINGBLOCK_WIBBLE, FALLINGBLOCK_WIBBLE);
|
|
|
|
item->mutator[i].Rotation.z = RADIAN * GenerateFloat(-FALLINGBLOCK_WIBBLE, FALLINGBLOCK_WIBBLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Make rotational falling movement with some random seed
|
|
|
|
for (int i = 0; i < item->mutator.size(); i++)
|
|
|
|
{
|
|
|
|
auto rotSpeed = i % 2 ? FALLINGBLOCK_FALL_ROTATION_SPEED : -FALLINGBLOCK_FALL_ROTATION_SPEED;
|
2021-09-24 13:58:56 +03:00
|
|
|
rotSpeed += i % 3 ? rotSpeed / 2 : rotSpeed;
|
2021-09-24 13:50:31 +03:00
|
|
|
item->mutator[i].Rotation.x += RADIAN * rotSpeed + (RADIAN * GenerateFloat(-1, 1));
|
|
|
|
item->mutator[i].Rotation.y += RADIAN * rotSpeed + (RADIAN * GenerateFloat(-1, 1));
|
|
|
|
item->mutator[i].Rotation.z += RADIAN * rotSpeed + (RADIAN * GenerateFloat(-1, 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->itemFlags[0] == FALLINGBLOCK_DELAY)
|
|
|
|
item->itemFlags[1] = FALLINGBLOCK_INITIAL_SPEED;
|
|
|
|
|
|
|
|
if (item->itemFlags[1] > 0)
|
|
|
|
{
|
2021-09-25 05:00:06 +03:00
|
|
|
item->itemFlags[1] += FALLINGBLOCK_FALL_VELOCITY;
|
|
|
|
|
|
|
|
if (item->itemFlags[1] > FALLINGBLOCK_MAX_SPEED)
|
|
|
|
item->itemFlags[1] = FALLINGBLOCK_MAX_SPEED;
|
|
|
|
|
2021-09-24 13:50:31 +03:00
|
|
|
item->pos.yPos += item->itemFlags[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
item->itemFlags[0]++;
|
|
|
|
|
2021-09-24 18:17:06 +03:00
|
|
|
if (GetDistanceToFloor(itemNumber) >= 0)
|
2021-09-24 13:50:31 +03:00
|
|
|
{
|
|
|
|
// If crumbled before actual delay (e.g. too low position), force delay to be correct
|
|
|
|
if (item->itemFlags[0] < FALLINGBLOCK_DELAY)
|
|
|
|
item->itemFlags[0] = FALLINGBLOCK_DELAY;
|
|
|
|
|
2021-09-25 05:15:35 +03:00
|
|
|
auto bounds = GetBoundsAccurate(item);
|
|
|
|
auto x = (bounds->X2 - bounds->X1) / 2;
|
|
|
|
auto z = (bounds->Z2 - bounds->Z1) / 2;
|
|
|
|
|
2021-09-24 13:50:31 +03:00
|
|
|
// Convert object to shatter item
|
|
|
|
MESH* meshpp = &g_Level.Meshes[Objects[item->objectNumber].meshIndex];
|
|
|
|
ShatterItem.yRot = item->pos.yRot;
|
|
|
|
ShatterItem.meshp = meshpp;
|
2021-09-25 05:15:35 +03:00
|
|
|
ShatterItem.sphere.x = x + item->pos.xPos;
|
2021-09-24 18:17:06 +03:00
|
|
|
ShatterItem.sphere.y = item->pos.yPos - STEP_SIZE; // So debris won't spawn below floor
|
2021-09-25 05:15:35 +03:00
|
|
|
ShatterItem.sphere.z = z + item->pos.zPos;
|
2021-09-24 13:50:31 +03:00
|
|
|
ShatterItem.bit = 0;
|
2021-09-25 05:15:35 +03:00
|
|
|
ShatterImpactData.impactDirection = Vector3(0, -(float)item->itemFlags[1] / (float)FALLINGBLOCK_MAX_SPEED, 0);
|
2021-09-25 05:00:06 +03:00
|
|
|
ShatterImpactData.impactLocation = { (float)ShatterItem.sphere.x, (float)ShatterItem.sphere.y, (float)ShatterItem.sphere.z };
|
2021-09-24 13:50:31 +03:00
|
|
|
ShatterObject(&ShatterItem, nullptr, 0, item->roomNumber, false);
|
|
|
|
|
|
|
|
SoundEffect(SFX_TR4_ROCK_FALL_LAND, &item->pos, 0);
|
|
|
|
KillItem(itemNumber);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->meshBits = -2;
|
|
|
|
item->itemFlags[0]++;
|
|
|
|
SoundEffect(SFX_TR4_ROCK_FALL_CRUMBLE, &item->pos, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<int> FallingBlockFloor(short itemNumber, int x, int y, int z)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
|
|
|
if (!item->meshBits || item->itemFlags[0] >= FALLINGBLOCK_DELAY)
|
|
|
|
return std::nullopt;
|
|
|
|
|
2021-09-24 18:17:06 +03:00
|
|
|
int height = item->pos.yPos + GetBoundsAccurate(item)->Y1;
|
2021-09-24 13:50:31 +03:00
|
|
|
return std::optional{ height };
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<int> FallingBlockCeiling(short itemNumber, int x, int y, int z)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
|
|
|
|
|
|
|
if (!item->meshBits || item->itemFlags[0] >= FALLINGBLOCK_DELAY)
|
|
|
|
return std::nullopt;
|
|
|
|
|
2021-09-24 18:17:06 +03:00
|
|
|
int height = item->pos.yPos + GetBoundsAccurate(item)->Y2;
|
2021-09-24 13:50:31 +03:00
|
|
|
return std::optional{ height };
|
|
|
|
}
|
|
|
|
|
|
|
|
int FallingBlockFloorBorder(short itemNumber)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
2021-09-24 18:17:06 +03:00
|
|
|
return item->pos.yPos + GetBoundsAccurate(item)->Y1;
|
2021-09-24 13:50:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int FallingBlockCeilingBorder(short itemNumber)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
2021-09-24 18:17:06 +03:00
|
|
|
return item->pos.yPos + GetBoundsAccurate(item)->Y2;
|
2021-09-24 13:50:31 +03:00
|
|
|
}
|