TombEngine/TR5Main/Game/collide.cpp

3096 lines
83 KiB
C++
Raw Normal View History

2020-12-21 13:16:29 -03:00
#include "framework.h"
#include "control.h"
#include "collide.h"
#include "draw.h"
#include "Lara.h"
#include "items.h"
#include "effects\effects.h"
2020-12-21 13:16:29 -03:00
#include "sphere.h"
#include "misc.h"
#include "setup.h"
2021-09-08 18:19:06 +03:00
#include "Sound\sound.h"
2021-09-08 18:31:35 +03:00
#include "Specific\trmath.h"
#include "Specific\prng.h"
#include "room.h"
2021-08-25 05:45:10 +03:00
2020-12-21 13:16:29 -03:00
using std::vector;
2021-08-30 18:03:21 +03:00
using namespace TEN::Math::Random;
using namespace TEN::Floordata;
2021-09-04 07:59:00 +02:00
using namespace TEN::Renderer;
2021-08-25 05:45:10 +03:00
2020-12-21 13:16:29 -03:00
BOUNDING_BOX GlobalCollisionBounds;
ITEM_INFO* CollidedItems[MAX_COLLIDED_OBJECTS];
MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS];
2020-12-21 13:16:29 -03:00
int GetCollidedObjects(ITEM_INFO* collidingItem, int radius, int onlyVisible, ITEM_INFO** collidedItems, MESH_INFO** collidedMeshes, int ignoreLara)
{
ROOM_INFO* room;
short roomsArray[255];
short numRooms;
short numItems = 0, numMeshes = 0;
float c, s;
int rx, rz;
// Collect all the rooms where to check
GetRoomList(collidingItem->roomNumber, roomsArray, &numRooms);
if (collidedMeshes)
{
for (int i = 0; i < numRooms; i++)
{
room = &g_Level.Rooms[roomsArray[i]];
for (int j = 0; j < room->mesh.size(); j++)
{
MESH_INFO* mesh = &room->mesh[j];
STATIC_INFO* staticMesh = &StaticObjects[mesh->staticNumber];
2021-09-08 14:05:09 +03:00
if (mesh->flags & StaticMeshFlags::SM_VISIBLE)
2020-12-21 13:16:29 -03:00
{
if (collidingItem->pos.yPos + radius + STEP_SIZE/2 >= mesh->y + staticMesh->collisionBox.Y1)
{
if (collidingItem->pos.yPos <= mesh->y + staticMesh->collisionBox.Y2)
{
s = phd_sin(mesh->yRot);
c = phd_cos(mesh->yRot);
rx = (collidingItem->pos.xPos - mesh->x) * c - s * (collidingItem->pos.zPos - mesh->z);
rz = (collidingItem->pos.zPos - mesh->z) * c + s * (collidingItem->pos.xPos - mesh->x);
if (radius + rx + STEP_SIZE/2 >= staticMesh->collisionBox.X1 && rx - radius - STEP_SIZE/2 <= staticMesh->collisionBox.X2)
{
if (radius + rz + STEP_SIZE/2 >= staticMesh->collisionBox.Z1 && rz - radius - STEP_SIZE/2 <= staticMesh->collisionBox.Z2)
{
collidedMeshes[numMeshes++] = mesh;
if (!radius)
{
collidedItems[0] = NULL;
return 1;
}
}
}
}
}
}
}
}
collidedMeshes[numMeshes] = NULL;
}
if (collidedItems)
{
for (int i = 0; i < numRooms; i++)
{
ROOM_INFO* room = &g_Level.Rooms[roomsArray[i]];
int itemNumber = room->itemNumber;
if (itemNumber != NO_ITEM)
{
do
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
if ((item == collidingItem)
|| (item->objectNumber == ID_LARA && ignoreLara)
|| (item->flags & 0x8000)
|| (item->meshBits == 0)
|| (Objects[item->objectNumber].drawRoutine == NULL && item->objectNumber != ID_LARA)
|| (Objects[item->objectNumber].collision == NULL && item->objectNumber != ID_LARA)
|| (onlyVisible && item->status == ITEM_INVISIBLE)
|| (item->objectNumber == ID_BURNING_FLOOR))
2020-12-21 13:16:29 -03:00
{
itemNumber = item->nextItem;
continue;
}
2020-12-21 13:16:29 -03:00
/*this is awful*/
/*if (item->objectNumber == ID_UPV && item->hitPoints == 1)
2020-12-21 13:16:29 -03:00
{
itemNumber = item->nextItem;
continue;
}
if (item->objectNumber == ID_BIGGUN && item->hitPoints == 1)
{
itemNumber = item->nextItem;
continue;
}*/
2020-12-21 13:16:29 -03:00
/*we need a better system*/
int dx = collidingItem->pos.xPos - item->pos.xPos;
int dy = collidingItem->pos.yPos - item->pos.yPos;
int dz = collidingItem->pos.zPos - item->pos.zPos;
ANIM_FRAME* framePtr = GetBestFrame(item);
if (dx >= -2048
2020-12-21 13:16:29 -03:00
&& dx <= 2048
&& dy >= -2048
&& dy <= 2048
&& dz >= -2048
&& dz <= 2048
&& collidingItem->pos.yPos + radius + 128 >= item->pos.yPos + framePtr->boundingBox.Y1
&& collidingItem->pos.yPos - radius - 128 <= item->pos.yPos + framePtr->boundingBox.Y2
&& collidingItem->floor >= item->pos.yPos)
2020-12-21 13:16:29 -03:00
{
float s = phd_sin(item->pos.yRot);
float c = phd_cos(item->pos.yRot);
int rx = dx * c - s * dz;
int rz = dz * c + s * dx;
if (item->objectNumber == ID_TURN_SWITCH)
{
framePtr->boundingBox.X1 = -256;
framePtr->boundingBox.X2 = 256;
framePtr->boundingBox.Z1 = -256;
framePtr->boundingBox.Z1 = 256;
2020-12-21 13:16:29 -03:00
}
if (radius + rx + 128 >= framePtr->boundingBox.X1 && rx - radius - 128 <= framePtr->boundingBox.X2)
{
if (radius + rz + 128 >= framePtr->boundingBox.Z1 && rz - radius - 128 <= framePtr->boundingBox.Z2)
{
collidedItems[numItems++] = item;
if (!radius)
return 1;
}
}
}
itemNumber = item->nextItem;
} while (itemNumber != NO_ITEM);
}
}
collidedItems[numItems] = NULL;
}
return (numItems || numMeshes);
}
2021-09-07 20:37:31 +03:00
void CollideSolidStatics(ITEM_INFO* item, COLL_INFO* coll)
2021-09-04 12:40:14 +03:00
{
2021-09-10 00:18:47 +03:00
coll->HitTallBounds = false;
coll->ObjectHeadroom = INT_MAX;
2021-09-09 20:21:05 +03:00
2021-09-04 12:40:14 +03:00
short roomsToCheck[128];
short numRoomsToCheck = 0;
roomsToCheck[numRoomsToCheck++] = item->roomNumber;
ROOM_INFO* room = &g_Level.Rooms[item->roomNumber];
for (int i = 0; i < room->doors.size(); i++)
{
roomsToCheck[numRoomsToCheck++] = room->doors[i].room;
}
for (int i = 0; i < numRoomsToCheck; i++)
{
for (int j = 0; j < g_Level.Rooms[roomsToCheck[i]].mesh.size(); j++)
{
auto mesh = &g_Level.Rooms[roomsToCheck[i]].mesh[j];
2021-09-08 13:39:29 +03:00
2021-09-08 14:22:58 +03:00
// Only process meshes which are visible and solid
if ((mesh->flags & StaticMeshFlags::SM_VISIBLE) && (mesh->flags & StaticMeshFlags::SM_SOLID))
2021-09-08 13:39:29 +03:00
{
2021-09-08 14:22:58 +03:00
int x = abs(item->pos.xPos - mesh->x);
int y = abs(item->pos.yPos - mesh->y);
int z = abs(item->pos.zPos - mesh->z);
if (x < COLLISION_CHECK_DISTANCE &&
y < COLLISION_CHECK_DISTANCE &&
z < COLLISION_CHECK_DISTANCE)
{
auto stInfo = StaticObjects[mesh->staticNumber];
auto stPos = PHD_3DPOS(mesh->x, mesh->y, mesh->z, 0, mesh->yRot, 0);
if (CollideSolidBounds(item, stInfo.collisionBox, stPos, coll))
2021-09-10 00:18:47 +03:00
coll->HitStatic = true;
}
2021-09-08 13:39:29 +03:00
}
2021-09-04 12:40:14 +03:00
}
}
}
bool CollideSolidBounds(ITEM_INFO* item, BOUNDING_BOX box, PHD_3DPOS pos, COLL_INFO* coll)
2021-09-04 12:40:14 +03:00
{
bool result = false;
2021-09-07 19:54:35 +03:00
2021-09-04 12:40:14 +03:00
// Get DX static bounds in global coords
auto staticBounds = TO_DX_BBOX(&pos, &box);
2021-09-04 12:40:14 +03:00
// Get local TR bounds and DX item bounds in global coords
auto itemBBox = GetBoundsAccurate(item);
auto itemBounds = TO_DX_BBOX(&item->pos, itemBBox);
// Extend bounds a bit for visual testing
2021-09-09 20:21:05 +03:00
itemBounds.Extents = itemBounds.Extents + Vector3(WALL_SIZE);
2021-09-04 12:40:14 +03:00
// Filter out any further checks if static isn't nearby
if (!staticBounds.Intersects(itemBounds))
return false;
// Bring back extents
2021-09-09 20:21:05 +03:00
itemBounds.Extents = itemBounds.Extents - Vector3(WALL_SIZE);
2021-09-04 12:40:14 +03:00
// Draw static bounds
g_Renderer.addDebugBox(staticBounds, Vector4(1, 0.3, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
// Calculate item coll bounds according to radius
BOUNDING_BOX collBox;
2021-09-10 00:20:59 +03:00
collBox.X1 = -coll->Setup.Radius;
collBox.X2 = coll->Setup.Radius;
collBox.Z1 = -coll->Setup.Radius;
collBox.Z2 = coll->Setup.Radius;
2021-09-04 12:40:14 +03:00
collBox.Y1 = itemBBox->Y1;
collBox.Y2 = itemBBox->Y2;
2021-09-09 20:21:05 +03:00
// Get and test DX item coll bounds
2021-09-08 13:39:29 +03:00
auto collBounds = TO_DX_BBOX(&PHD_3DPOS(item->pos.xPos, item->pos.yPos, item->pos.zPos), &collBox);
2021-09-09 20:21:05 +03:00
bool intersects = staticBounds.Intersects(collBounds);
2021-09-04 12:40:14 +03:00
// Draw item coll bounds
2021-09-09 20:21:05 +03:00
g_Renderer.addDebugBox(collBounds, intersects ? Vector4(1, 0, 0, 1) : Vector4(0, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
2021-09-08 13:39:29 +03:00
// Decompose static bounds into top/bottom plane vertices
Vector3 corners[8];
staticBounds.GetCorners(corners);
Vector3 planeVertices[4][3] =
{
{ corners[0], corners[4], corners[1] },
{ corners[5], corners[4], corners[1] },
{ corners[3], corners[6], corners[7] },
{ corners[3], corners[6], corners[2] }
};
// Determine collision box vertical dimensions
auto height = collBox.Y2 - collBox.Y1;
auto halfHeight = height / 2;
auto center = item->pos.yPos - halfHeight;
// Do a series of angular tests with 90 degree steps to determine top/bottom collision.
int closestPlane = -1;
Ray closestRay;
auto minDistance = std::numeric_limits<float>::max();
for (int i = 0; i < 4; i++)
{
// Calculate ray direction
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(item->pos.yRot), TO_RAD(item->pos.xRot + (ANGLE(90 * i))), 0);
auto mxT = Matrix::CreateTranslation(Vector3::UnitY);
auto direction = (mxT * mxR).Translation();
// Make a ray and do ray tests against all decomposed planes
auto ray = Ray(collBounds.Center, direction);
// Determine if top/bottom planes are closest ones or not
for (int p = 0; p < 4; p++)
{
// No plane intersection, quickly discard
float d = 0.0f;
if (!ray.Intersects(planeVertices[p][0], planeVertices[p][1], planeVertices[p][2], d))
continue;
// Process plane intersection only if distance is smaller
// than already found minimum
if (d < minDistance)
{
closestRay = ray;
closestPlane = p;
minDistance = d;
}
}
}
2021-09-09 20:21:05 +03:00
if (closestPlane != -1) // Top/bottom plane found
2021-09-08 13:39:29 +03:00
{
auto bottom = closestPlane >= 2;
auto yPoint = abs((closestRay.direction * minDistance).y);
auto distanceToVerticalPlane = halfHeight - yPoint;
2021-09-09 20:21:05 +03:00
// Correct position according to top/bottom bounds, if collided and plane is nearby
if (intersects && minDistance < height)
2021-09-08 13:39:29 +03:00
{
2021-09-09 20:21:05 +03:00
if (bottom)
{
// HACK: additionally subtract 2 from bottom plane, or else false positives may occur.
item->pos.yPos += distanceToVerticalPlane + 2;
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_TOP;
2021-09-09 20:21:05 +03:00
}
else
{
// Set collision type only if dry room (in water rooms it causes stucking)
item->pos.yPos -= distanceToVerticalPlane;
2021-09-10 00:18:47 +03:00
coll->CollisionType = (g_Level.Rooms[item->roomNumber].flags & 1) ? coll->CollisionType : CT_CLAMP;
2021-09-09 20:21:05 +03:00
}
result = true;
2021-09-08 13:39:29 +03:00
}
2021-09-10 00:18:47 +03:00
if (bottom && coll->ObjectHeadroom > abs(distanceToVerticalPlane))
coll->ObjectHeadroom = abs(distanceToVerticalPlane);
2021-09-08 13:39:29 +03:00
}
2021-09-09 20:21:05 +03:00
// If no actual intersection occured, stop testing.
if (!intersects)
return false;
2021-09-08 13:39:29 +03:00
// Check if bounds still collide after top/bottom position correction
if (!staticBounds.Intersects(TO_DX_BBOX(&PHD_3DPOS(item->pos.xPos, item->pos.yPos, item->pos.zPos), &collBox)))
return result;
2021-09-07 19:54:35 +03:00
2021-09-07 21:36:13 +03:00
// Determine identity rotation/distance
auto distance = Vector3(item->pos.xPos, item->pos.yPos, item->pos.zPos) - Vector3(pos.xPos, pos.yPos, pos.zPos);
auto c = phd_cos(pos.yRot);
auto s = phd_sin(pos.yRot);
2021-09-07 19:54:35 +03:00
2021-09-07 21:36:13 +03:00
// Rotate item to collision bounds identity
auto x = round(distance.x * c - distance.z * s) + pos.xPos;
2021-09-07 19:54:35 +03:00
auto y = item->pos.yPos;
auto z = round(distance.x * s + distance.z * c) + pos.zPos;
2021-09-08 13:39:29 +03:00
// Determine identity static collision bounds
auto XMin = pos.xPos + box.X1;
auto XMax = pos.xPos + box.X2;
auto YMin = pos.yPos + box.Y1;
auto YMax = pos.yPos + box.Y2;
auto ZMin = pos.zPos + box.Z1;
auto ZMax = pos.zPos + box.Z2;
2021-09-07 19:54:35 +03:00
2021-09-07 21:36:13 +03:00
// Determine item collision bounds
2021-09-10 00:20:59 +03:00
auto inXMin = x - coll->Setup.Radius;
auto inXMax = x + coll->Setup.Radius;
2021-09-08 13:39:29 +03:00
auto inYMin = y - height;
2021-09-07 19:54:35 +03:00
auto inYMax = y;
2021-09-10 00:20:59 +03:00
auto inZMin = z - coll->Setup.Radius;
auto inZMax = z + coll->Setup.Radius;
2021-09-07 19:54:35 +03:00
2021-09-07 21:36:13 +03:00
// Don't calculate shifts if not in bounds
2021-09-07 19:54:35 +03:00
if (inXMax <= XMin || inXMin >= XMax ||
inYMax <= YMin || inYMin >= YMax ||
inZMax <= ZMin || inZMin >= ZMax)
return result;
2021-09-07 21:36:13 +03:00
// Calculate shifts
2021-09-04 12:40:14 +03:00
2021-09-07 19:54:35 +03:00
PHD_VECTOR rawShift = {};
2021-09-04 12:40:14 +03:00
2021-09-07 19:54:35 +03:00
auto shiftLeft = inXMax - XMin;
auto shiftRight = XMax - inXMin;
2021-09-07 21:36:13 +03:00
2021-09-07 19:54:35 +03:00
if (shiftLeft < shiftRight)
rawShift.x = -shiftLeft;
else
rawShift.x = shiftRight;
2021-09-04 15:43:25 +03:00
2021-09-07 19:54:35 +03:00
shiftLeft = inZMax - ZMin;
shiftRight = ZMax - inZMin;
2021-09-07 21:36:13 +03:00
2021-09-07 19:54:35 +03:00
if (shiftLeft < shiftRight)
rawShift.z = -shiftLeft;
else
rawShift.z = shiftRight;
2021-09-04 12:40:14 +03:00
2021-09-07 21:36:13 +03:00
// Rotate previous collision position to identity
2021-09-10 00:20:59 +03:00
distance = Vector3(coll->Setup.OldPosition.x, coll->Setup.OldPosition.y, coll->Setup.OldPosition.z) - Vector3(pos.xPos, pos.yPos, pos.zPos);
auto ox = round(distance.x * c - distance.z * s) + pos.xPos;
auto oz = round(distance.x * s + distance.z * c) + pos.zPos;
2021-09-04 12:40:14 +03:00
2021-09-07 21:36:13 +03:00
// Calculate collisison type based on identity rotation
2021-09-10 00:20:59 +03:00
switch (GetQuadrant(coll->Setup.ForwardAngle - pos.yRot))
2021-09-04 12:40:14 +03:00
{
2021-09-07 19:54:35 +03:00
case NORTH:
2021-09-10 00:20:59 +03:00
if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.z = rawShift.z;
coll->Shift.x = ox - x;
coll->CollisionType = CT_FRONT;
2021-09-07 19:54:35 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.x = rawShift.x;
coll->Shift.z = 0;
coll->CollisionType = CT_LEFT;
2021-09-07 19:54:35 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.x = rawShift.x;
coll->Shift.z = 0;
coll->CollisionType = CT_RIGHT;
2021-09-07 19:54:35 +03:00
}
break;
2021-09-04 12:40:14 +03:00
2021-09-07 19:54:35 +03:00
case SOUTH:
2021-09-10 00:20:59 +03:00
if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius)
2021-09-04 12:40:14 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.z = rawShift.z;
coll->Shift.x = ox - x;
coll->CollisionType = CT_FRONT;
2021-09-04 12:40:14 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.x = rawShift.x;
coll->Shift.z = 0;
coll->CollisionType = CT_RIGHT;
2021-09-07 19:54:35 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.x = rawShift.x;
coll->Shift.z = 0;
coll->CollisionType = CT_LEFT;
2021-09-07 19:54:35 +03:00
}
break;
2021-09-04 12:40:14 +03:00
2021-09-07 19:54:35 +03:00
case EAST:
2021-09-10 00:20:59 +03:00
if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.x = rawShift.x;
coll->Shift.z = oz - z;
coll->CollisionType = CT_FRONT;
2021-09-07 19:54:35 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.z = rawShift.z;
coll->Shift.x = 0;
coll->CollisionType = CT_RIGHT;
2021-09-07 19:54:35 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.z = rawShift.z;
coll->Shift.x = 0;
coll->CollisionType = CT_LEFT;
2021-09-07 19:54:35 +03:00
}
break;
2021-09-04 15:48:21 +03:00
2021-09-07 19:54:35 +03:00
case WEST:
2021-09-10 00:20:59 +03:00
if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.x = rawShift.x;
coll->Shift.z = oz - z;
coll->CollisionType = CT_FRONT;
2021-09-07 19:54:35 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.z = rawShift.z;
coll->Shift.x = 0;
coll->CollisionType = CT_LEFT;
2021-09-07 19:54:35 +03:00
}
2021-09-10 00:20:59 +03:00
else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius)
2021-09-07 19:54:35 +03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.z = rawShift.z;
coll->Shift.x = 0;
coll->CollisionType = CT_RIGHT;
2021-09-07 19:54:35 +03:00
}
break;
2021-09-04 12:40:14 +03:00
}
2021-09-07 21:36:13 +03:00
// Determine final shifts rotation/distance
2021-09-10 00:18:47 +03:00
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - Vector3(pos.xPos, pos.yPos, pos.zPos);
c = phd_cos(-pos.yRot);
s = phd_sin(-pos.yRot);
2021-09-04 15:48:21 +03:00
2021-09-07 21:36:13 +03:00
// Calculate final shifts rotation/distance
2021-09-10 00:18:47 +03:00
coll->Shift.x = (round(distance.x * c - distance.z * s) + pos.xPos) - item->pos.xPos;
coll->Shift.z = (round(distance.x * s + distance.z * c) + pos.zPos) - item->pos.zPos;
2021-09-04 15:48:21 +03:00
2021-09-10 00:18:47 +03:00
if (coll->Shift.x == 0 && coll->Shift.z == 0)
coll->CollisionType = CT_NONE; // Paranoid
2021-09-04 15:48:21 +03:00
// Set splat state flag if item is Lara and bounds are taller than Lara's headroom
2021-09-10 00:18:47 +03:00
if (item == LaraItem && coll->CollisionType == CT_FRONT)
coll->HitTallBounds = (YMin <= inYMin + LARA_HEADROOM);
2021-09-04 12:40:14 +03:00
return true;
}
2020-12-21 13:16:29 -03:00
int TestWithGlobalCollisionBounds(ITEM_INFO* item, ITEM_INFO* lara, COLL_INFO* coll)
{
ANIM_FRAME* framePtr = GetBestFrame(lara);
if (item->pos.yPos + GlobalCollisionBounds.Y2 <= lara->pos.yPos + framePtr->boundingBox.Y1)
return false;
if (item->pos.yPos + GlobalCollisionBounds.Y1 >= framePtr->boundingBox.Y2)
return false;
float s = phd_sin(item->pos.yRot);
float c = phd_cos(item->pos.yRot);
int dx = lara->pos.xPos - item->pos.xPos;
int dz = lara->pos.zPos - item->pos.zPos;
int x = c * dx - s * dz;
int z = c * dz + s * dx;
2021-09-10 00:20:59 +03:00
if (x < GlobalCollisionBounds.X1 - coll->Setup.Radius ||
x > GlobalCollisionBounds.X2 + coll->Setup.Radius ||
z < GlobalCollisionBounds.Z1 - coll->Setup.Radius ||
z > GlobalCollisionBounds.Z2 + coll->Setup.Radius)
2020-12-21 13:16:29 -03:00
return false;
return true;
}
2021-09-10 00:18:47 +03:00
void TrapCollision(short itemNumber, ITEM_INFO* l, COLL_INFO* coll)
2020-12-21 13:16:29 -03:00
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
if (item->status == ITEM_ACTIVE)
{
2021-09-10 00:20:59 +03:00
if (!TestBoundsCollide(item, l, coll->Setup.Radius))
2020-12-21 13:16:29 -03:00
return;
TestCollision(item, LaraItem);
}
else if (item->status != ITEM_INVISIBLE)
2021-09-10 00:18:47 +03:00
ObjectCollision(itemNumber, l, coll);
2020-12-21 13:16:29 -03:00
}
2021-02-03 01:50:59 -03:00
void TestForObjectOnLedge(ITEM_INFO* item, COLL_INFO* coll)
2020-12-21 13:16:29 -03:00
{
for (int i = 0; i < 2; i++)
{
GAME_VECTOR s;
s.x = (i * 256) - 0x80;
s.y = -256;
s.z = 0;
GetLaraJointPosition((PHD_VECTOR*)&s, LM_HEAD);
s.roomNumber = LaraItem->roomNumber;
GAME_VECTOR d;
d.x = s.x + phd_sin(LaraItem->pos.yRot) * 768;
d.y = s.y;
d.z = s.z + phd_cos(LaraItem->pos.yRot) * 768;
LOS(&s, &d);
PHD_VECTOR v;
MESH_INFO* mesh;
// CHECK
/*if (ObjectOnLOS2(&s, &d, &v, &mesh) != 999)
{
2021-09-10 00:18:47 +03:00
coll->HitStatic = true;
2020-12-21 13:16:29 -03:00
}*/
}
}
void ShiftItem(ITEM_INFO* item, COLL_INFO* coll)
{
2021-09-10 00:18:47 +03:00
item->pos.xPos += coll->Shift.x;
item->pos.yPos += coll->Shift.y;
item->pos.zPos += coll->Shift.z;
coll->Shift.z = 0;
coll->Shift.y = 0;
coll->Shift.x = 0;
2020-12-21 13:16:29 -03:00
}
void UpdateLaraRoom(ITEM_INFO* item, int height)
{
int x = item->pos.xPos;
int y = height + item->pos.yPos;
int z = item->pos.zPos;
2021-01-07 11:16:21 -03:00
item->location = GetRoom(item->location, x, y, z);
item->floor = GetFloorHeight(item->location, x, z).value_or(NO_HEIGHT);
if (item->roomNumber != item->location.roomNumber)
ItemNewRoom(Lara.itemNumber, item->location.roomNumber);
2020-12-21 13:16:29 -03:00
}
int FindGridShift(int x, int z)
{
if ((x / SECTOR(1)) == (z / SECTOR(1)))
return 0;
if ((z / SECTOR(1)) <= (x / SECTOR(1)))
return (-1 - (x & (WALL_SIZE - 1)));
else
return ((WALL_SIZE + 1) - (x & (WALL_SIZE - 1)));
}
2021-09-07 20:37:31 +03:00
int TestBoundsCollideStatic(ITEM_INFO* item, MESH_INFO* mesh, int radius)
2020-12-21 13:16:29 -03:00
{
2021-09-07 20:37:31 +03:00
PHD_3DPOS pos;
pos.xPos = mesh->x;
pos.yPos = mesh->y;
pos.zPos = mesh->z;
pos.yRot = mesh->yRot;
auto bounds = StaticObjects[mesh->staticNumber].collisionBox;
if (!(bounds.Z2 != 0 || bounds.Z1 != 0 || bounds.X1 != 0 || bounds.X2 != 0 || bounds.Y1 != 0 || bounds.Y2 != 0))
2020-12-21 13:16:29 -03:00
return false;
ANIM_FRAME* frame = GetBestFrame(LaraItem);
2021-09-07 20:37:31 +03:00
if (pos.yPos + bounds.Y2 <= LaraItem->pos.yPos + frame->boundingBox.Y1)
2020-12-21 13:16:29 -03:00
return false;
2021-09-07 20:37:31 +03:00
if (pos.yPos + bounds.Y1 >= LaraItem->pos.yPos + frame->boundingBox.Y2)
2020-12-21 13:16:29 -03:00
return false;
float c, s;
int x, z, dx, dz;
2021-09-07 20:37:31 +03:00
c = phd_cos(pos.yRot);
s = phd_sin(pos.yRot);
x = LaraItem->pos.xPos - pos.xPos;
z = LaraItem->pos.zPos - pos.zPos;
2020-12-21 13:16:29 -03:00
dx = c * x - s * z;
dz = c * z + s * x;
2021-09-07 20:37:31 +03:00
if (dx <= radius + bounds.X2
&& dx >= bounds.X1 - radius
&& dz <= radius + bounds.Z2
&& dz >= bounds.Z1 - radius)
2020-12-21 13:16:29 -03:00
{
return true;
}
else
{
return false;
}
}
2021-09-07 20:37:31 +03:00
int ItemPushStatic(ITEM_INFO* l, MESH_INFO* mesh, COLL_INFO* coll) // previously ItemPushLaraStatic
2020-12-21 13:16:29 -03:00
{
2021-09-07 20:37:31 +03:00
PHD_3DPOS pos;
pos.xPos = mesh->x;
pos.yPos = mesh->y;
pos.zPos = mesh->z;
pos.yRot = mesh->yRot;
auto bounds = StaticObjects[mesh->staticNumber].collisionBox;
auto c = phd_cos(pos.yRot);
auto s = phd_sin(pos.yRot);
auto dx = l->pos.xPos - pos.xPos;
auto dz = l->pos.zPos - pos.zPos;
auto rx = c * dx - s * dz;
auto rz = c * dz + s * dx;
2021-09-10 00:20:59 +03:00
auto minX = bounds.X1 - coll->Setup.Radius;
auto maxX = bounds.X2 + coll->Setup.Radius;
auto minZ = bounds.Z1 - coll->Setup.Radius;
auto maxZ = bounds.Z2 + coll->Setup.Radius;
2020-12-21 13:16:29 -03:00
if (abs(dx) > 4608
|| abs(dz) > 4608
|| rx <= minX
|| rx >= maxX
|| rz <= minZ
|| rz >= maxZ)
return false;
2021-09-07 20:37:31 +03:00
auto left = rx - minX;
auto top = maxZ - rz;
auto bottom = rz - minZ;
auto right = maxX - rx;
2020-12-21 13:16:29 -03:00
if (right <= left && right <= top && right <= bottom)
rx += right;
else if (left <= right && left <= top && left <= bottom)
rx -= left;
else if (top <= left && top <= right && top <= bottom)
rz += top;
else
rz -= bottom;
2021-09-07 20:37:31 +03:00
l->pos.xPos = pos.xPos + c * rx + s * rz;
l->pos.zPos = pos.zPos + c * rz - s * rx;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
coll->Setup.BadHeightUp = NO_BAD_POS;
coll->Setup.BadHeightDown = -STEPUP_HEIGHT;
coll->Setup.BadCeilingHeight = 0;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
auto oldFacing = coll->Setup.ForwardAngle;
coll->Setup.ForwardAngle = phd_atan(l->pos.zPos - coll->Setup.OldPosition.z, l->pos.xPos - coll->Setup.OldPosition.x);
if (l == LaraItem)
2021-01-07 11:16:21 -03:00
{
GetCollisionInfo(coll, l->pos.xPos, l->pos.yPos, l->pos.zPos, l->roomNumber, LARA_HEIGHT);
2021-01-07 11:16:21 -03:00
}
else
{
GetObjectCollisionInfo(coll, l->pos.xPos, l->pos.yPos, l->pos.zPos, l->roomNumber, LARA_HEIGHT);
2021-01-07 11:16:21 -03:00
}
2021-09-10 00:20:59 +03:00
coll->Setup.ForwardAngle = oldFacing;
2020-12-21 13:16:29 -03:00
2021-09-10 00:18:47 +03:00
if (coll->CollisionType == CT_NONE)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Setup.OldPosition.x = l->pos.xPos;
coll->Setup.OldPosition.y = l->pos.yPos;
coll->Setup.OldPosition.z = l->pos.zPos;
2020-12-21 13:16:29 -03:00
UpdateLaraRoom(l, -10);
2020-12-21 13:16:29 -03:00
}
else
{
2021-09-10 00:20:59 +03:00
l->pos.xPos = coll->Setup.OldPosition.x;
l->pos.zPos = coll->Setup.OldPosition.z;
2020-12-21 13:16:29 -03:00
}
if (l == LaraItem && Lara.isMoving && Lara.moveCount > 15)
2020-12-21 13:16:29 -03:00
{
Lara.isMoving = false;
Lara.gunStatus = LG_NO_ARMS;
}
return true;
}
int ItemPushItem(ITEM_INFO* item, ITEM_INFO* l, COLL_INFO* coll, int spazon, char bigpush) // previously ItemPushLara
2020-12-21 13:16:29 -03:00
{
float c, s;
int dx, dz, rx, rz, minX, maxX, minZ, maxZ;
int left, right, bottom, top;
BOUNDING_BOX* bounds;
short facing;
// Get item's rotation
c = phd_cos(item->pos.yRot);
2020-12-21 13:16:29 -03:00
s = phd_sin(item->pos.yRot);
// Get vector from item to Lara
dx = LaraItem->pos.xPos - item->pos.xPos;
dz = LaraItem->pos.zPos - item->pos.zPos;
// Rotate Lara vector into item frame
rx = c * dx - s * dz;
rz = c * dz + s * dx;
2020-12-21 13:16:29 -03:00
if (bigpush & 2)
bounds = &GlobalCollisionBounds;
else
bounds = (BOUNDING_BOX*)GetBestFrame(item);
minX = bounds->X1;
maxX = bounds->X2;
minZ = bounds->Z1;
maxZ = bounds->Z2;
if (bigpush & 1)
{
2021-09-10 00:20:59 +03:00
minX -= coll->Setup.Radius;
maxX += coll->Setup.Radius;
minZ -= coll->Setup.Radius;
maxZ += coll->Setup.Radius;
2020-12-21 13:16:29 -03:00
}
// Big enemies
if (abs(dx) > 4608
|| abs(dz) > 4608
|| rx <= minX
|| rx >= maxX
|| rz <= minZ
|| rz >= maxZ)
return false;
left = rx - minX;
top = maxZ - rz;
bottom = rz - minZ;
right = maxX - rx;
if (right <= left && right <= top && right <= bottom)
rx += right;
else if (left <= right && left <= top && left <= bottom)
rx -= left;
else if (top <= left && top <= right && top <= bottom)
rz += top;
else
rz -= bottom;
l->pos.xPos = item->pos.xPos + c * rx + s * rz;
l->pos.zPos = item->pos.zPos + c * rz - s * rx;
if (spazon && bounds->Y2 - bounds->Y1 > STEP_SIZE)
{
rx = (bounds->X1 + bounds->X2) / 2;
rz = (bounds->Z1 + bounds->Z2) / 2;
dx -= c * rx + s * rz;
dz -= c * rz - s * rx;
Lara.hitDirection = (l->pos.yRot - phd_atan(dz, dz) - ANGLE(135)) / 16384;
2021-08-26 14:58:06 +03:00
static int hitSoundTimer = 0;
2020-12-21 13:16:29 -03:00
if ((!Lara.hitFrame) && (!hitSoundTimer))
{
SoundEffect(SFX_TR4_LARA_INJURY, &l->pos, 0);
2021-08-26 14:58:06 +03:00
hitSoundTimer = generateFloat(15, 35);
2020-12-21 13:16:29 -03:00
}
if (hitSoundTimer)
hitSoundTimer--;
Lara.hitFrame++;
if (Lara.hitFrame > 34)
Lara.hitFrame = 34;
}
2021-09-10 00:20:59 +03:00
coll->Setup.BadHeightUp = NO_BAD_POS;
coll->Setup.BadHeightDown = -STEPUP_HEIGHT;
coll->Setup.BadCeilingHeight = 0;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
facing = coll->Setup.ForwardAngle;
coll->Setup.ForwardAngle = phd_atan(l->pos.zPos - coll->Setup.OldPosition.z, l->pos.xPos - coll->Setup.OldPosition.x);
if (l == LaraItem)
{
GetCollisionInfo(coll, l->pos.xPos, l->pos.yPos, l->pos.zPos, l->roomNumber, LARA_HEIGHT);
}
else
{
GetObjectCollisionInfo(coll, l->pos.xPos, l->pos.yPos, l->pos.zPos, l->roomNumber, LARA_HEIGHT);
}
2021-09-10 00:20:59 +03:00
coll->Setup.ForwardAngle = facing;
2020-12-21 13:16:29 -03:00
2021-09-10 00:18:47 +03:00
if (coll->CollisionType == CT_NONE)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Setup.OldPosition.x = l->pos.xPos;
coll->Setup.OldPosition.y = l->pos.yPos;
coll->Setup.OldPosition.z = l->pos.zPos;
2020-12-21 13:16:29 -03:00
// Commented because causes Lara to jump out of the water if she touches an object on the surface. re: "kayak bug"
// UpdateLaraRoom(l, -10);
2020-12-21 13:16:29 -03:00
}
else
{
2021-09-10 00:20:59 +03:00
l->pos.xPos = coll->Setup.OldPosition.x;
l->pos.zPos = coll->Setup.OldPosition.z;
2020-12-21 13:16:29 -03:00
}
if (Lara.isMoving && Lara.moveCount > 15)
{
Lara.isMoving = false;
Lara.gunStatus = LG_NO_ARMS;
}
return true;
}
void AIPickupCollision(short itemNumber, ITEM_INFO* l, COLL_INFO* c)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
if (item->objectNumber == ID_SHOOT_SWITCH1 && !(item->meshBits & 1))
item->status = ITEM_INVISIBLE;
}
2021-09-10 00:18:47 +03:00
void ObjectCollision(short itemNumber, ITEM_INFO* l, COLL_INFO* coll)
2020-12-21 13:16:29 -03:00
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
2021-09-10 00:20:59 +03:00
if (TestBoundsCollide(item, l, coll->Setup.Radius))
2020-12-21 13:16:29 -03:00
{
if (TestCollision(item, l))
{
2021-09-10 00:20:59 +03:00
if (coll->Setup.EnableObjectPush)
2021-09-10 00:18:47 +03:00
ItemPushItem(item, l, coll, false, true);
2020-12-21 13:16:29 -03:00
}
}
}
void AlignLaraPosition(PHD_VECTOR* vec, ITEM_INFO* item, ITEM_INFO* l)
{
int x, y, z;
l->pos.xRot = item->pos.xRot;
l->pos.yRot = item->pos.yRot;
l->pos.zRot = item->pos.zRot;
Matrix matrix = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->pos.yRot),
TO_RAD(item->pos.xRot),
TO_RAD(item->pos.zRot)
);
Vector3 pos = Vector3::Transform(Vector3(vec->x, vec->y, vec->z), matrix);
l->pos.xPos = item->pos.xPos + pos.x;
l->pos.yPos = item->pos.yPos + pos.y;
l->pos.zPos = item->pos.zPos + pos.z;
}
int TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ITEM_INFO* item, ITEM_INFO* l)
{
int x, y, z, rx, ry, rz;
short xRotRel, yRotRel, zRotRel;
xRotRel = l->pos.xRot - item->pos.xRot;
yRotRel = l->pos.yRot - item->pos.yRot;
zRotRel = l->pos.zRot - item->pos.zRot;
if (xRotRel < bounds->rotX1)
return false;
if (xRotRel > bounds->rotX2)
return false;
if (yRotRel < bounds->rotY1)
return false;
if (yRotRel > bounds->rotY2)
return false;
if (zRotRel < bounds->rotZ1)
return false;
if (zRotRel > bounds->rotZ2)
2020-12-21 13:16:29 -03:00
return false;
x = l->pos.xPos - item->pos.xPos;
y = l->pos.yPos - item->pos.yPos;
z = l->pos.zPos - item->pos.zPos;
Vector3 pos = Vector3(x, y, z);
2020-12-21 13:16:29 -03:00
Matrix matrix = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->pos.yRot),
2020-12-21 13:16:29 -03:00
TO_RAD(item->pos.xRot),
TO_RAD(item->pos.zRot)
);
2021-08-23 13:37:13 +02:00
// This solves once for all the minus sign hack of CreateFromYawPitchRoll.
// In reality it should be the inverse, but the inverse of a rotation matrix is equal to the transpose
// and transposing a matrix is faster.
// It's the only piece of code that does it, because we want Lara's location relative to the identity frame
// of the object we are test against.
matrix = matrix.Transpose();
2020-12-21 13:16:29 -03:00
pos = Vector3::Transform(pos, matrix);
rx = pos.x;
ry = pos.y;
rz = pos.z;
if (rx < bounds->boundingBox.X1 || rx > bounds->boundingBox.X2
2020-12-21 13:16:29 -03:00
|| ry < bounds->boundingBox.Y1 || ry > bounds->boundingBox.Y2
|| rz < bounds->boundingBox.Z1 || rz > bounds->boundingBox.Z2)
return false;
return true;
}
int Move3DPosTo3DPos(PHD_3DPOS* src, PHD_3DPOS* dest, int velocity, short angAdd)
{
int x, y, z;
int distance, direction;
int angle;
x = dest->xPos - src->xPos;
y = dest->yPos - src->yPos;
z = dest->zPos - src->zPos;
distance = sqrt(SQUARE(x) + SQUARE(y) + SQUARE(z));
if (velocity < distance)
{
src->xPos += x * velocity / distance;
src->yPos += y * velocity / distance;
src->zPos += z * velocity / distance;
}
else
{
src->xPos = dest->xPos;
src->yPos = dest->yPos;
src->zPos = dest->zPos;
}
if (!Lara.isMoving)
{
if (Lara.waterStatus != LW_UNDERWATER)
{
angle = mGetAngle(dest->xPos, dest->zPos, src->xPos, src->zPos);
direction = (GetQuadrant(angle) - GetQuadrant(dest->yRot)) & 3;
switch (direction)
2020-12-21 13:16:29 -03:00
{
case 0:
LaraItem->animNumber = LA_SIDESTEP_LEFT;
LaraItem->frameNumber = GF(LA_SIDESTEP_LEFT, 0);
LaraItem->goalAnimState = LS_STEP_LEFT;
LaraItem->currentAnimState = LS_STEP_LEFT;
Lara.gunStatus = LG_HANDS_BUSY;
break;
case 1:
LaraItem->animNumber = LA_WALK;
LaraItem->frameNumber = GF(LA_WALK, 0);
LaraItem->goalAnimState = LS_WALK_FORWARD;
LaraItem->currentAnimState = LS_WALK_FORWARD;
Lara.gunStatus = LG_HANDS_BUSY;
break;
case 2:
LaraItem->animNumber = LA_SIDESTEP_RIGHT;
LaraItem->frameNumber = GF(LA_SIDESTEP_RIGHT, 0);
LaraItem->goalAnimState = LS_STEP_RIGHT;
LaraItem->currentAnimState = LS_STEP_RIGHT;
Lara.gunStatus = LG_HANDS_BUSY;
break;
case 3:
default:
LaraItem->animNumber = LA_WALK_BACK;
LaraItem->frameNumber = GF(LA_WALK_BACK, 0);
LaraItem->goalAnimState = LS_WALK_BACK;
LaraItem->currentAnimState = LS_WALK_BACK;
Lara.gunStatus = LG_HANDS_BUSY;
break;
2020-12-21 13:16:29 -03:00
}
}
Lara.isMoving = true;
Lara.moveCount = 0;
}
short deltaAngle = dest->xRot - src->xRot;
if (deltaAngle > angAdd)
src->xRot += angAdd;
else if (deltaAngle < -angAdd)
src->xRot -= angAdd;
2020-12-21 13:16:29 -03:00
else
src->xRot = dest->xRot;
2020-12-21 13:16:29 -03:00
deltaAngle = dest->yRot - src->yRot;
if (deltaAngle > angAdd)
src->yRot += angAdd;
else if (deltaAngle < -angAdd)
src->yRot -= angAdd;
2020-12-21 13:16:29 -03:00
else
src->yRot = dest->yRot;
2020-12-21 13:16:29 -03:00
deltaAngle = dest->zRot - src->zRot;
if (deltaAngle > angAdd)
src->zRot += angAdd;
else if (deltaAngle < -angAdd)
src->zRot -= angAdd;
2020-12-21 13:16:29 -03:00
else
src->zRot = dest->zRot;
2020-12-21 13:16:29 -03:00
return (src->xPos == dest->xPos
&& src->yPos == dest->yPos
&& src->zPos == dest->zPos
&& src->xRot == dest->xRot
&& src->yRot == dest->yRot
&& src->zRot == dest->zRot);
}
int MoveLaraPosition(PHD_VECTOR* vec, ITEM_INFO* item, ITEM_INFO* l)
{
FLOOR_INFO* floor;
PHD_3DPOS dest;
int height;
short roomNumber;
dest.xRot = item->pos.xRot;
dest.yRot = item->pos.yRot;
dest.zRot = item->pos.zRot;
Vector3 pos = Vector3(vec->x, vec->y, vec->z);
Matrix matrix = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->pos.yRot),
TO_RAD(item->pos.xRot),
TO_RAD(item->pos.zRot)
);
pos = Vector3::Transform(pos, matrix);
dest.xPos = item->pos.xPos + pos.x;
dest.yPos = item->pos.yPos + pos.y;
dest.zPos = item->pos.zPos + pos.z;
if (item->objectNumber != ID_FLARE_ITEM && item->objectNumber != ID_BURNING_TORCH_ITEM)
return Move3DPosTo3DPos(&l->pos, &dest, LARA_VELOCITY, ANGLE(2));
roomNumber = l->roomNumber;
floor = GetFloor(dest.xPos, dest.yPos, dest.zPos, &roomNumber);
height = GetFloorHeight(floor, dest.xPos, dest.yPos, dest.zPos);
if (abs(height - l->pos.yPos) <= CLICK(2))
{
if (sqrt(SQUARE(dest.xPos - l->pos.xPos) + SQUARE(dest.yPos - l->pos.yPos) + SQUARE(dest.zPos - l->pos.zPos)) < (STEP_SIZE/2))
return true;
return Move3DPosTo3DPos(&l->pos, &dest, LARA_VELOCITY, ANGLE(2));
}
if (Lara.isMoving)
{
Lara.isMoving = false;
Lara.gunStatus = LG_NO_ARMS;
}
return false;
}
int TestBoundsCollide(ITEM_INFO* item, ITEM_INFO* l, int radius)
{
BOUNDING_BOX* bounds;
BOUNDING_BOX* laraBounds;
float c, s;
int x, z;
int dx, dz;
bounds = (BOUNDING_BOX*)GetBestFrame(item);
laraBounds = (BOUNDING_BOX*)GetBestFrame(l);
if (item->pos.yPos + bounds->Y2 > l->pos.yPos + laraBounds->Y1)
{
if (item->pos.yPos + bounds->Y1 < l->pos.yPos + laraBounds->Y2)
{
c = phd_cos(item->pos.yRot);
s = phd_sin(item->pos.yRot);
x = l->pos.xPos - item->pos.xPos;
z = l->pos.zPos - item->pos.zPos;
dx = c * x - s * z;
dz = c * z + s * x;
if (dx >= bounds->X1 - radius
&& dx <= radius + bounds->X2
&& dz >= bounds->Z1 - radius
&& dz <= radius + bounds->Z2)
{
return true;
}
}
}
return false;
}
void CreatureCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
{
ITEM_INFO* item = &g_Level.Items[itemNum];
float c, s;
int x, z, rx, rz;
ANIM_FRAME* frame;
if (item->objectNumber != ID_HITMAN || item->currentAnimState != LS_INSERT_PUZZLE)
{
2021-09-10 00:20:59 +03:00
if (TestBoundsCollide(item, l, coll->Setup.Radius))
2020-12-21 13:16:29 -03:00
{
if (TestCollision(item, l))
{
2021-09-10 00:20:59 +03:00
if (coll->Setup.EnableObjectPush || Lara.waterStatus == LW_UNDERWATER || Lara.waterStatus == LW_SURFACE)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
ItemPushItem(item, l, coll, coll->Setup.EnableSpaz, 0);
2020-12-21 13:16:29 -03:00
}
2021-09-10 00:20:59 +03:00
else if (coll->Setup.EnableSpaz)
2020-12-21 13:16:29 -03:00
{
x = l->pos.xPos - item->pos.xPos;
z = l->pos.zPos - item->pos.zPos;
c = phd_cos(item->pos.yRot);
s = phd_sin(item->pos.yRot);
frame = GetBestFrame(item);
rx = (frame->boundingBox.X1 + frame->boundingBox.X2) / 2;
rz = (frame->boundingBox.X2 + frame->boundingBox.Z2) / 2;
if (frame->boundingBox.Y2 - frame->boundingBox.Y1 > STEP_SIZE)
{
int angle = (l->pos.yRot - phd_atan(z - c * rx - s * rz, x - c * rx + s * rz) - ANGLE(135)) / 16384;
Lara.hitDirection = (short)angle;
// TODO: check if a second Lara.hitFrame++; is required there !
Lara.hitFrame++;
if (Lara.hitFrame > 30)
Lara.hitFrame = 30;
}
}
}
}
}
}
2021-08-25 05:45:10 +03:00
// A handy overload of GetCollisionResult which can be used to quickly get collision parameters
// such as floor height under specific item.
COLL_RESULT GetCollisionResult(ITEM_INFO* item)
2021-02-06 18:33:33 -06:00
{
2021-08-25 05:45:10 +03:00
auto room = item->roomNumber;
auto floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &room);
auto result = GetCollisionResult(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
2021-08-25 05:45:10 +03:00
result.RoomNumber = room;
return result;
}
2021-08-25 05:45:10 +03:00
// This variation of GetCollisionResult is an universal wrapper to be used across whole
// collisional code to replace "holy trinity" of roomNumber-GetFloor-GetFloorHeight operations.
// The advantage of this wrapper is that it does NOT modify incoming roomNumber parameter,
// instead putting modified one returned by GetFloor into return COLL_RESULT structure.
// This way, function never modifies any external variables.
COLL_RESULT GetCollisionResult(int x, int y, int z, short roomNumber)
{
auto room = roomNumber;
auto floor = GetFloor(x, y, z, &room);
auto result = GetCollisionResult(floor, x, y, z);
result.RoomNumber = room;
return result;
}
2021-08-25 05:45:10 +03:00
// GetCollisionResult is a reworked legacy GetFloorHeight function, which does not
// write any data into globals, but instead into special COLL_RESULT struct.
// Additionally, it writes ceiling height for same coordinates, so this function
// may be reused instead both GetFloorHeight and GetCeilingHeight calls to increase
// readability.
COLL_RESULT GetCollisionResult(FLOOR_INFO* floor, int x, int y, int z)
{
COLL_RESULT result = {};
2021-08-25 05:45:10 +03:00
// Return provided block into result as itself.
result.Block = floor;
2021-02-06 18:33:33 -06:00
2021-08-27 15:48:22 +03:00
// Floor and ceiling heights are directly borrowed from new floordata.
result.FloorHeight = GetFloorHeight(ROOM_VECTOR{ floor->Room, y }, x, z).value_or(NO_HEIGHT);
result.CeilingHeight = GetCeilingHeight(ROOM_VECTOR{ floor->Room, y }, x, z).value_or(NO_HEIGHT);
2021-08-25 05:45:10 +03:00
// Probe bottom block through portals.
// TODO: Check if it is really needed, as GetFloor should take care of it itself?
2021-02-06 18:33:33 -06:00
ROOM_INFO* r;
while (floor->pitRoom != NO_ROOM)
{
if (CheckNoColFloorTriangle(floor, x, z) == 1)
break;
r = &g_Level.Rooms[floor->pitRoom];
floor = &XZ_GET_SECTOR(r, x - r->x, z - r->z);
}
2021-08-25 06:33:15 +03:00
// Return probed bottom block into result.
result.BottomBlock = floor;
// Check if block isn't a wall or there is no floordata
if (floor->floor * CLICK(1) != NO_HEIGHT && floor->index)
{
// TODO: For ChocolateFan: currently we use legacy floordata ONLY for getting TiltX/TiltY and
// SplitFloor/SplitCeiling values. These values can be derived from new floordata. After this
// we can remove this chain floordata parser.
2021-08-25 06:33:15 +03:00
short* data = &g_Level.FloorData[floor->index];
2021-08-25 06:33:15 +03:00
short type;
int zOff, xOff, trigger;
2021-08-25 06:33:15 +03:00
int tilts, t0, t1, t2, t3, t4, dx, dz;
2021-08-25 06:33:15 +03:00
do
2021-02-06 18:33:33 -06:00
{
2021-08-25 06:33:15 +03:00
type = *(data++);
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
switch (type & DATA_TYPE)
{
case DOOR_TYPE:
case ROOF_TYPE:
data++;
break;
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
case SPLIT3:
case SPLIT4:
case NOCOLC1T:
case NOCOLC1B:
case NOCOLC2T:
case NOCOLC2B:
result.SplitCeiling = type & DATA_TYPE;
data++;
break;
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
case TILT_TYPE:
result.TiltZ = zOff = (*data >> 8);
result.TiltX = xOff = *(char*)data;
2021-02-06 18:33:33 -06:00
if ((abs(zOff)) > 2 || (abs(xOff)) > 2)
2021-08-25 06:33:15 +03:00
result.HeightType = BIG_SLOPE;
else
result.HeightType = SMALL_SLOPE;
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
data++;
break;
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
case TRIGGER_TYPE:
data++;
do
{
2021-08-25 06:33:15 +03:00
trigger = *(data++);
if (TRIG_BITS(trigger) != TO_OBJECT)
2021-02-06 18:33:33 -06:00
{
2021-08-25 06:33:15 +03:00
if (TRIG_BITS(trigger) == TO_CAMERA ||
TRIG_BITS(trigger) == TO_FLYBY)
{
trigger = *(data++);
}
}
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
} while (!(trigger & END_BIT));
break;
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
case SPLIT1:
case SPLIT2:
case NOCOLF1T:
case NOCOLF1B:
case NOCOLF2T:
case NOCOLF2B:
tilts = *data;
t0 = tilts & 15;
t1 = (tilts >> 4) & 15;
t2 = (tilts >> 8) & 15;
t3 = (tilts >> 12) & 15;
dx = x & 1023;
dz = z & 1023;
result.HeightType = SPLIT_TRI;
result.SplitFloor = (type & DATA_TYPE);
if ((type & DATA_TYPE) == SPLIT1 ||
(type & DATA_TYPE) == NOCOLF1T ||
(type & DATA_TYPE) == NOCOLF1B)
{
2021-08-25 06:33:15 +03:00
if (dx <= (1024 - dz))
{
zOff = t2 - t1;
xOff = t0 - t1;
2021-08-25 06:33:15 +03:00
}
else
{
zOff = t3 - t0;
xOff = t3 - t2;
2021-08-25 06:33:15 +03:00
}
}
else
{
2021-08-25 06:33:15 +03:00
if (dx <= dz)
{
zOff = t2 - t1;
xOff = t3 - t2;
2021-08-25 06:33:15 +03:00
}
else
{
zOff = t3 - t0;
xOff = t0 - t1;
2021-08-25 06:33:15 +03:00
}
}
2021-02-06 18:33:33 -06:00
result.TiltZ = zOff;
2021-08-25 06:33:15 +03:00
result.TiltX = xOff;
2021-02-06 18:33:33 -06:00
if ((abs(zOff)) > 2 || (abs(xOff)) > 2)
2021-08-25 06:33:15 +03:00
result.HeightType = DIAGONAL;
else if (result.HeightType != SPLIT_TRI)
result.HeightType = SMALL_SLOPE;
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
data++;
break;
2021-02-06 18:33:33 -06:00
2021-08-25 06:33:15 +03:00
default:
break;
}
} while (!(type & END_BIT));
}
2021-08-25 05:45:10 +03:00
// TODO: check if we need to keep here this slope vs. bridge check from legacy GetTiltType
if ((y + CLICK(2)) < (floor->floor * CLICK(1)))
result.TiltZ = result.TiltX = 0;
2021-08-25 05:45:10 +03:00
return result;
2021-02-06 18:33:33 -06:00
}
2021-01-07 11:16:21 -03:00
void GetCollisionInfo(COLL_INFO* coll, int xPos, int yPos, int zPos, int roomNumber, int objectHeight)
{
int resetRoom;
if (objectHeight >= 0)
{
resetRoom = 0;
}
else
{
objectHeight = -objectHeight;
resetRoom = 1;
}
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_NONE;
coll->Shift.x = 0;
coll->Shift.y = 0;
coll->Shift.z = 0;
2021-09-10 00:20:59 +03:00
auto quadrant = GetQuadrant(coll->Setup.ForwardAngle);
2021-01-07 11:16:21 -03:00
int x = xPos;
int y = yPos - objectHeight;
2021-08-26 13:47:47 +03:00
int yTop = y - LARA_HEADROOM;
2021-01-07 11:16:21 -03:00
int z = zPos;
auto collResult = GetCollisionResult(x, yTop, z, roomNumber);
2021-08-25 05:45:10 +03:00
auto topRoomNumber = collResult.RoomNumber; // Keep top room number as we need it to re-probe from origin room
ROOM_VECTOR tfLocation = GetRoom(LaraItem->location, x, yTop, z);
2021-01-07 11:16:21 -03:00
int height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
if (height != NO_HEIGHT)
height -= yPos;
ROOM_VECTOR tcLocation = GetRoom(LaraItem->location, x, yTop - LaraItem->fallspeed, z);
int ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->Middle.Ceiling = ceiling;
coll->Middle.Floor = height;
coll->Middle.Type = collResult.HeightType;
coll->Middle.SplitFloor = collResult.SplitFloor;
coll->Middle.SplitCeiling = collResult.SplitCeiling;
collResult = GetCollisionResult(x, LaraItem->pos.yPos, z, roomNumber);
2021-09-10 00:18:47 +03:00
coll->TiltX = collResult.TiltX;
coll->TiltZ = collResult.TiltZ;
2021-01-07 11:16:21 -03:00
2021-08-25 05:45:10 +03:00
int xfront, xright, xleft, zfront, zright, zleft;
2021-01-07 11:16:21 -03:00
2021-09-10 00:18:47 +03:00
switch (quadrant)
2021-01-07 11:16:21 -03:00
{
case 0:
2021-09-10 00:20:59 +03:00
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
zfront = coll->Setup.Radius;
xleft = -(coll->Setup.Radius);
zleft = coll->Setup.Radius;
xright = coll->Setup.Radius;
zright = coll->Setup.Radius;
2021-01-07 11:16:21 -03:00
break;
case 1:
2021-09-10 00:20:59 +03:00
xfront = coll->Setup.Radius;
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
xleft = coll->Setup.Radius;
zleft = coll->Setup.Radius;
xright = coll->Setup.Radius;
zright = -(coll->Setup.Radius);
2021-01-07 11:16:21 -03:00
break;
case 2:
2021-09-10 00:20:59 +03:00
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
zfront = -coll->Setup.Radius;
xleft = coll->Setup.Radius;
zleft = -(coll->Setup.Radius);
xright = -(coll->Setup.Radius);
zright = -(coll->Setup.Radius);
2021-01-07 11:16:21 -03:00
break;
case 3:
2021-09-10 00:20:59 +03:00
xfront = -(coll->Setup.Radius);
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
xleft = -(coll->Setup.Radius);
zleft = -(coll->Setup.Radius);
xright = -(coll->Setup.Radius);
zright = coll->Setup.Radius;
2021-01-07 11:16:21 -03:00
break;
default:
xleft = zleft = 0;
xright = zright = 0;
2021-08-25 05:45:10 +03:00
xfront = zfront = 0;
2021-01-07 11:16:21 -03:00
break;
}
2021-08-25 05:45:10 +03:00
x = xfront + xPos;
z = zfront + zPos;
2021-01-07 11:16:21 -03:00
if (resetRoom)
{
tfLocation = LaraItem->location;
tcLocation = LaraItem->location;
2021-08-25 05:45:10 +03:00
topRoomNumber = roomNumber;
2021-01-07 11:16:21 -03:00
}
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x, yTop, z, topRoomNumber);
tfLocation = GetRoom(tfLocation, x, yTop, z);
2021-01-07 11:16:21 -03:00
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
if (height != NO_HEIGHT)
height -= yPos;
tcLocation = GetRoom(tcLocation, x, yTop - LaraItem->fallspeed, z);
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->Front.Ceiling = ceiling;
coll->Front.Floor = height;
coll->Front.Type = collResult.HeightType;
coll->Front.SplitFloor = collResult.SplitFloor;
coll->Front.SplitCeiling = collResult.SplitCeiling;
2021-01-07 11:16:21 -03:00
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x + xfront, yTop, z + zfront, topRoomNumber);
2021-02-06 18:33:33 -06:00
2021-08-25 05:45:10 +03:00
tfLocation = GetRoom(tfLocation, x + xfront, yTop, z + zfront);
height = GetFloorHeight(tfLocation, x + xfront, z + zfront).value_or(NO_HEIGHT);
2021-01-07 11:16:21 -03:00
if (height != NO_HEIGHT)
height -= yPos;
2021-09-10 00:20:59 +03:00
if ((coll->Setup.SlopesAreWalls)
2021-09-10 00:18:47 +03:00
&& ((coll->Front.Type == BIG_SLOPE) || (coll->Front.Type == DIAGONAL))
&& (coll->Front.Floor < coll->Middle.Floor)
&& (height < coll->Front.Floor)
&& (coll->Front.Floor < 0))
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Front.Floor = MAX_HEIGHT;
2021-01-07 11:16:21 -03:00
}
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits
2021-09-10 00:18:47 +03:00
&& ((coll->Front.Type == BIG_SLOPE) || (coll->Front.Type == DIAGONAL))
&& (coll->Front.Floor > coll->Middle.Floor))
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Front.Floor = STOP_SIZE;
2021-01-07 11:16:21 -03:00
}
2021-09-10 00:20:59 +03:00
else if ((coll->Setup.DeathIsPit)
2021-09-10 00:18:47 +03:00
&& (coll->Front.Floor > 0)
2021-08-25 06:33:15 +03:00
&& collResult.BottomBlock->Flags.Death)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Front.Floor = STOP_SIZE;
2021-01-07 11:16:21 -03:00
}
x = xPos + xleft;
z = zPos + zleft;
collResult = GetCollisionResult(x, yTop, z, roomNumber);
2021-02-06 18:33:33 -06:00
ROOM_VECTOR lrfLocation = GetRoom(LaraItem->location, x, yTop, z);
2021-01-07 11:16:21 -03:00
height = GetFloorHeight(lrfLocation, x, z).value_or(NO_HEIGHT);
if (height != NO_HEIGHT)
height -= yPos;
ROOM_VECTOR lrcLocation = GetRoom(LaraItem->location, x, yTop - LaraItem->fallspeed, z);
ceiling = GetCeilingHeight(lrcLocation, x, z).value_or(NO_HEIGHT);
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = height;
coll->MiddleLeft.Ceiling = ceiling;
coll->MiddleLeft.Type = collResult.HeightType;
coll->MiddleLeft.SplitFloor = collResult.SplitFloor;
coll->MiddleLeft.SplitCeiling = collResult.SplitCeiling;
2021-01-07 11:16:21 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->MiddleLeft.Type == BIG_SLOPE || coll->MiddleLeft.Type == DIAGONAL) && coll->MiddleLeft.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->MiddleLeft.Type == BIG_SLOPE || coll->MiddleLeft.Type == DIAGONAL) && coll->MiddleLeft.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->MiddleLeft.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = STOP_SIZE;
2021-01-07 11:16:21 -03:00
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x, yTop, z, topRoomNumber); // We use plain x/z values here, proposed by Choco
2021-02-06 18:33:33 -06:00
tfLocation = GetRoom(tfLocation, x, yTop, z);
2021-01-07 11:16:21 -03:00
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
if (height != NO_HEIGHT)
height -= yPos;
tcLocation = GetRoom(tcLocation, x, yTop - LaraItem->fallspeed, z);
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = height;
coll->FrontLeft.Ceiling = ceiling;
coll->FrontLeft.Type = collResult.HeightType;
coll->FrontLeft.SplitFloor = collResult.SplitFloor;
coll->FrontLeft.SplitCeiling = collResult.SplitCeiling;
2021-01-07 11:16:21 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->FrontLeft.Type == BIG_SLOPE || coll->FrontLeft.Type == DIAGONAL) && coll->FrontLeft.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->FrontLeft.Type == BIG_SLOPE || coll->FrontLeft.Type == DIAGONAL) && coll->FrontLeft.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->FrontLeft.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = STOP_SIZE;
2021-01-07 11:16:21 -03:00
x = xPos + xright;
z = zPos + zright;
collResult = GetCollisionResult(x, yTop, z, roomNumber);
2021-02-06 18:33:33 -06:00
lrfLocation = GetRoom(LaraItem->location, x, yTop, z);
2021-01-07 11:16:21 -03:00
height = GetFloorHeight(lrfLocation, x, z).value_or(NO_HEIGHT);
if (height != NO_HEIGHT)
height -= yPos;
lrcLocation = GetRoom(LaraItem->location, x, yTop - LaraItem->fallspeed, z);
ceiling = GetCeilingHeight(lrcLocation, x, z).value_or(NO_HEIGHT);
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = height;
coll->MiddleRight.Ceiling = ceiling;
coll->MiddleRight.Type = collResult.HeightType;
coll->MiddleRight.SplitFloor = collResult.SplitFloor;
coll->MiddleRight.SplitCeiling = collResult.SplitCeiling;
2021-01-07 11:16:21 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->MiddleRight.Type == BIG_SLOPE || coll->MiddleRight.Type == DIAGONAL) && coll->MiddleRight.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->MiddleRight.Type == BIG_SLOPE || coll->MiddleRight.Type == DIAGONAL) && coll->MiddleRight.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->MiddleRight.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = STOP_SIZE;
2021-01-07 11:16:21 -03:00
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x, yTop, z, topRoomNumber);
2021-02-06 18:33:33 -06:00
tfLocation = GetRoom(tfLocation, x, yTop, z);
2021-01-07 11:16:21 -03:00
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
if (height != NO_HEIGHT)
height -= yPos;
tcLocation = GetRoom(tcLocation, x, yTop - LaraItem->fallspeed, z);
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = height;
coll->FrontRight.Ceiling = ceiling;
coll->FrontRight.Type = collResult.HeightType;
coll->FrontRight.SplitFloor = collResult.SplitFloor;
coll->FrontRight.SplitCeiling = collResult.SplitCeiling;
2021-01-07 11:16:21 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->FrontRight.Type == BIG_SLOPE || coll->FrontRight.Type == DIAGONAL) && coll->FrontRight.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->FrontRight.Type == BIG_SLOPE || coll->FrontRight.Type == DIAGONAL) && coll->FrontRight.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->FrontRight.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = STOP_SIZE;
2021-01-07 11:16:21 -03:00
2021-09-07 20:37:31 +03:00
CollideSolidStatics(LaraItem, coll);
2021-09-04 07:59:00 +02:00
2021-09-10 00:18:47 +03:00
if (coll->Middle.Floor == NO_HEIGHT)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_FRONT;
2021-01-07 11:16:21 -03:00
return;
}
2021-09-10 00:18:47 +03:00
if (coll->Middle.Floor - coll->Middle.Ceiling <= 0)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_CLAMP;
2021-01-07 11:16:21 -03:00
return;
}
2021-09-10 00:18:47 +03:00
if (coll->Middle.Ceiling >= 0)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.y = coll->Middle.Ceiling;
coll->CollisionType = CT_TOP;
2021-01-07 11:16:21 -03:00
}
2021-09-10 00:20:59 +03:00
if ((coll->Front.Floor > coll->Setup.BadHeightUp)
|| (coll->Front.Floor < coll->Setup.BadHeightDown)
|| (coll->Front.Ceiling > coll->Setup.BadCeilingHeight))
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:18:47 +03:00
if ((coll->Front.Type == DIAGONAL)
|| (coll->Front.Type == SPLIT_TRI))
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-01-07 11:16:21 -03:00
}
else
{
2021-09-10 00:18:47 +03:00
switch (quadrant)
2021-01-07 11:16:21 -03:00
{
case 0:
case 2:
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
2021-09-10 00:18:47 +03:00
coll->Shift.z = FindGridShift(zPos + zfront, zPos);
2021-01-07 11:16:21 -03:00
break;
case 1:
case 3:
2021-09-10 00:18:47 +03:00
coll->Shift.x = FindGridShift(xPos + xfront, xPos);
2021-09-10 00:20:59 +03:00
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-01-07 11:16:21 -03:00
break;
}
}
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_FRONT;
2021-01-07 11:16:21 -03:00
return;
}
2021-09-10 00:20:59 +03:00
if (coll->Front.Ceiling >= coll->Setup.BadCeilingHeight)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_TOP_FRONT;
2021-01-07 11:16:21 -03:00
return;
}
2021-09-10 00:20:59 +03:00
if (coll->MiddleLeft.Floor > coll->Setup.BadHeightUp ||
coll->MiddleLeft.Floor < coll->Setup.BadHeightDown ||
coll->MiddleLeft.Ceiling > coll->Setup.BadCeilingHeight)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:18:47 +03:00
if (coll->MiddleLeft.Type == SPLIT_TRI && coll->Middle.Type == SPLIT_TRI)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-01-07 11:16:21 -03:00
}
else
{
2021-09-10 00:18:47 +03:00
switch (quadrant)
2021-01-07 11:16:21 -03:00
{
case 0:
case 2:
2021-09-10 00:18:47 +03:00
coll->Shift.x = FindGridShift(xPos + xleft, xPos + xfront);
2021-01-07 11:16:21 -03:00
break;
case 1:
case 3:
2021-09-10 00:18:47 +03:00
coll->Shift.z = FindGridShift(zPos + zleft, zPos + zfront);
2021-01-07 11:16:21 -03:00
break;
}
}
2021-09-10 00:18:47 +03:00
if (coll->MiddleLeft.SplitFloor && coll->MiddleLeft.SplitFloor == coll->Middle.SplitFloor)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
2021-01-07 11:16:21 -03:00
quarter %= 2;
2021-09-10 00:18:47 +03:00
switch (coll->MiddleLeft.SplitFloor)
2021-01-07 11:16:21 -03:00
{
case SPLIT1:
case NOCOLF1T:
case NOCOLF1B:
if (quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_LEFT;
2021-01-07 11:16:21 -03:00
break;
case SPLIT2:
case NOCOLF2T:
case NOCOLF2B:
if (!quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_LEFT;
2021-01-07 11:16:21 -03:00
break;
}
}
else
{
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_LEFT;
2021-01-07 11:16:21 -03:00
}
return;
}
2021-09-10 00:20:59 +03:00
if (coll->MiddleRight.Floor > coll->Setup.BadHeightUp ||
coll->MiddleRight.Floor < coll->Setup.BadHeightDown ||
coll->MiddleRight.Ceiling > coll->Setup.BadCeilingHeight)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:18:47 +03:00
if (coll->MiddleRight.Type == SPLIT_TRI && coll->Middle.Type == SPLIT_TRI)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-01-07 11:16:21 -03:00
}
else
{
2021-09-10 00:18:47 +03:00
switch (quadrant)
2021-01-07 11:16:21 -03:00
{
case 0:
case 2:
2021-09-10 00:18:47 +03:00
coll->Shift.x = FindGridShift(xPos + xright, xPos + xfront);
2021-01-07 11:16:21 -03:00
break;
case 1:
case 3:
2021-09-10 00:18:47 +03:00
coll->Shift.z = FindGridShift(zPos + zright, zPos + zfront);
2021-01-07 11:16:21 -03:00
break;
}
}
2021-09-10 00:18:47 +03:00
if (coll->MiddleRight.SplitFloor && coll->MiddleRight.SplitFloor == coll->Middle.SplitFloor)
2021-01-07 11:16:21 -03:00
{
2021-09-10 00:20:59 +03:00
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
2021-01-07 11:16:21 -03:00
quarter %= 2;
2021-09-10 00:18:47 +03:00
switch (coll->MiddleRight.SplitFloor)
2021-01-07 11:16:21 -03:00
{
case SPLIT1:
case NOCOLF1T:
case NOCOLF1B:
if (quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_RIGHT;
2021-01-07 11:16:21 -03:00
break;
case SPLIT2:
case NOCOLF2T:
case NOCOLF2B:
if (!quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_RIGHT;
2021-01-07 11:16:21 -03:00
break;
}
}
else
{
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_RIGHT;
2021-01-07 11:16:21 -03:00
}
return;
}
}
void GetObjectCollisionInfo(COLL_INFO* coll, int xPos, int yPos, int zPos, int roomNumber, int objectHeight)
2020-12-21 13:16:29 -03:00
{
int resetRoom;
if (objectHeight >= 0)
{
resetRoom = 0;
}
else
{
objectHeight = -objectHeight;
resetRoom = 1;
}
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_NONE;
coll->Shift.x = 0;
coll->Shift.y = 0;
coll->Shift.z = 0;
2021-09-10 00:20:59 +03:00
auto quadrant = GetQuadrant(coll->Setup.ForwardAngle);
2020-12-21 13:16:29 -03:00
int x = xPos;
int y = yPos - objectHeight;
2021-08-26 13:47:47 +03:00
int yTop = y - LARA_HEADROOM;
2020-12-21 13:16:29 -03:00
int z = zPos;
auto collResult = GetCollisionResult(x, yTop, z, roomNumber);
2021-08-25 05:45:10 +03:00
auto topRoomNumber = collResult.RoomNumber;
if (collResult.FloorHeight != NO_HEIGHT)
collResult.FloorHeight -= yPos;
2020-12-21 13:16:29 -03:00
int ceiling = GetCeiling(collResult.Block, x, yTop - LaraItem->fallspeed, z);
2020-12-21 13:16:29 -03:00
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->Middle.Ceiling = ceiling;
coll->Middle.Floor = collResult.FloorHeight;
coll->Middle.Type = collResult.HeightType;
coll->Middle.SplitFloor = collResult.SplitFloor;
coll->Middle.SplitCeiling = collResult.SplitCeiling;
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x, LaraItem->pos.yPos, z, roomNumber);
2021-09-10 00:18:47 +03:00
coll->TiltX = collResult.TiltX;
coll->TiltZ = collResult.TiltZ;
2021-08-25 05:45:10 +03:00
int xfront, xright, xleft, zfront, zright, zleft;
2020-12-21 13:16:29 -03:00
2021-09-10 00:18:47 +03:00
switch (quadrant)
2020-12-21 13:16:29 -03:00
{
case 0:
2021-09-10 00:20:59 +03:00
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
zfront = coll->Setup.Radius;
xleft = -(coll->Setup.Radius);
zleft = coll->Setup.Radius;
xright = coll->Setup.Radius;
zright = coll->Setup.Radius;
2020-12-21 13:16:29 -03:00
break;
case 1:
2021-09-10 00:20:59 +03:00
xfront = coll->Setup.Radius;
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
xleft = coll->Setup.Radius;
zleft = coll->Setup.Radius;
xright = coll->Setup.Radius;
zright = -(coll->Setup.Radius);
2020-12-21 13:16:29 -03:00
break;
case 2:
2021-09-10 00:20:59 +03:00
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
zfront = -coll->Setup.Radius;
xleft = coll->Setup.Radius;
zleft = -(coll->Setup.Radius);
xright = -(coll->Setup.Radius);
zright = -(coll->Setup.Radius);
2020-12-21 13:16:29 -03:00
break;
case 3:
2021-09-10 00:20:59 +03:00
xfront = -(coll->Setup.Radius);
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
xleft = -(coll->Setup.Radius);
zleft = -(coll->Setup.Radius);
xright = -(coll->Setup.Radius);
zright = coll->Setup.Radius;
2020-12-21 13:16:29 -03:00
break;
default:
xleft = zleft = 0;
xright = zright = 0;
2021-08-25 05:45:10 +03:00
xfront = zfront = 0;
2020-12-21 13:16:29 -03:00
break;
}
2021-08-25 05:45:10 +03:00
x = xfront + xPos;
z = zfront + zPos;
if (resetRoom)
topRoomNumber = roomNumber;
2020-12-21 13:16:29 -03:00
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x, yTop, z, topRoomNumber);
if (collResult.FloorHeight != NO_HEIGHT)
collResult.FloorHeight -= yPos;
ceiling = GetCeiling(collResult.Block, x, yTop - LaraItem->fallspeed, z);
2020-12-21 13:16:29 -03:00
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->Front.Ceiling = ceiling;
coll->Front.Floor = collResult.FloorHeight;
coll->Front.Type = collResult.HeightType;
coll->Front.SplitFloor = collResult.SplitFloor;
coll->Front.SplitCeiling = collResult.SplitCeiling;
2020-12-21 13:16:29 -03:00
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x + xfront, yTop, z + zfront, topRoomNumber);
if (collResult.FloorHeight != NO_HEIGHT)
collResult.FloorHeight -= yPos;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
if ((coll->Setup.SlopesAreWalls)
2021-09-10 00:18:47 +03:00
&& ((coll->Front.Type == BIG_SLOPE) || (coll->Front.Type == DIAGONAL))
&& (coll->Front.Floor < coll->Middle.Floor)
&& (collResult.FloorHeight < coll->Front.Floor)
&& (coll->Front.Floor < 0))
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Front.Floor = MAX_HEIGHT;
2020-12-21 13:16:29 -03:00
}
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits
2021-09-10 00:18:47 +03:00
&& ((coll->Front.Type == BIG_SLOPE) || (coll->Front.Type == DIAGONAL))
&& (coll->Front.Floor > coll->Middle.Floor))
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Front.Floor = STOP_SIZE;
2020-12-21 13:16:29 -03:00
}
2021-09-10 00:20:59 +03:00
else if ((coll->Setup.DeathIsPit)
2021-09-10 00:18:47 +03:00
&& (coll->Front.Floor > 0)
2021-08-25 06:33:15 +03:00
&& collResult.BottomBlock->Flags.Death)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Front.Floor = STOP_SIZE;
2020-12-21 13:16:29 -03:00
}
x = xPos + xleft;
z = zPos + zleft;
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x + xfront, yTop, z + zfront, roomNumber);
if (collResult.FloorHeight != NO_HEIGHT)
collResult.FloorHeight -= yPos;
2020-12-21 13:16:29 -03:00
ceiling = GetCeiling(collResult.Block, x, yTop - LaraItem->fallspeed, z);
2020-12-21 13:16:29 -03:00
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = collResult.FloorHeight;
coll->MiddleLeft.Ceiling = ceiling;
coll->MiddleLeft.Type = collResult.HeightType;
coll->MiddleLeft.SplitFloor = collResult.SplitFloor;
coll->MiddleLeft.SplitCeiling = collResult.SplitCeiling;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->MiddleLeft.Type == BIG_SLOPE || coll->MiddleLeft.Type == DIAGONAL) && coll->MiddleLeft.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->MiddleLeft.Type == BIG_SLOPE || coll->MiddleLeft.Type == DIAGONAL) && coll->MiddleLeft.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->MiddleLeft.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->MiddleLeft.Floor = STOP_SIZE;
2020-12-21 13:16:29 -03:00
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x, yTop, z, topRoomNumber);
if (collResult.FloorHeight != NO_HEIGHT)
collResult.FloorHeight -= yPos;
2020-12-21 13:16:29 -03:00
ceiling = GetCeiling(collResult.Block, x, yTop - LaraItem->fallspeed, z);
2020-12-21 13:16:29 -03:00
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = collResult.FloorHeight;
coll->FrontLeft.Ceiling = ceiling;
coll->FrontLeft.Type = collResult.HeightType;
coll->FrontLeft.SplitFloor = collResult.SplitFloor;
coll->FrontLeft.SplitCeiling = collResult.SplitCeiling;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->FrontLeft.Type == BIG_SLOPE || coll->FrontLeft.Type == DIAGONAL) && coll->FrontLeft.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->FrontLeft.Type == BIG_SLOPE || coll->FrontLeft.Type == DIAGONAL) && coll->FrontLeft.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->FrontLeft.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor = STOP_SIZE;
2020-12-21 13:16:29 -03:00
x = xPos + xright;
z = zPos + zright;
collResult = GetCollisionResult(x, yTop, z, roomNumber);
if (collResult.FloorHeight != NO_HEIGHT)
collResult.FloorHeight -= yPos;
2020-12-21 13:16:29 -03:00
ceiling = GetCeiling(collResult.Block, x, yTop - LaraItem->fallspeed, z);
2020-12-21 13:16:29 -03:00
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = collResult.FloorHeight;
coll->MiddleRight.Ceiling = ceiling;
coll->MiddleRight.Type = collResult.HeightType;
coll->MiddleRight.SplitFloor = collResult.SplitFloor;
coll->MiddleRight.SplitCeiling = collResult.SplitCeiling;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->MiddleRight.Type == BIG_SLOPE || coll->MiddleRight.Type == DIAGONAL) && coll->MiddleRight.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->MiddleRight.Type == BIG_SLOPE || coll->MiddleRight.Type == DIAGONAL) && coll->MiddleRight.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->MiddleRight.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->MiddleRight.Floor = STOP_SIZE;
2020-12-21 13:16:29 -03:00
2021-08-25 05:45:10 +03:00
collResult = GetCollisionResult(x, yTop, z, topRoomNumber);
if (collResult.FloorHeight != NO_HEIGHT)
collResult.FloorHeight -= yPos;
2020-12-21 13:16:29 -03:00
ceiling = GetCeiling(collResult.Block, x, yTop - LaraItem->fallspeed, z);
2020-12-21 13:16:29 -03:00
if (ceiling != NO_HEIGHT)
ceiling -= y;
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = collResult.FloorHeight;
coll->FrontRight.Ceiling = ceiling;
coll->FrontRight.Type = collResult.HeightType;
coll->FrontRight.SplitFloor = collResult.SplitFloor;
coll->FrontRight.SplitCeiling = collResult.SplitCeiling;
2021-09-10 00:20:59 +03:00
if (coll->Setup.SlopesAreWalls && (coll->FrontRight.Type == BIG_SLOPE || coll->FrontRight.Type == DIAGONAL) && coll->FrontRight.Floor < 0)
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = MAX_HEIGHT;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.SlopesArePits && (coll->FrontRight.Type == BIG_SLOPE || coll->FrontRight.Type == DIAGONAL) && coll->FrontRight.Floor > 0)
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = STOP_SIZE;
2021-09-10 00:20:59 +03:00
else if (coll->Setup.DeathIsPit && coll->FrontRight.Floor > 0 && collResult.BottomBlock->Flags.Death)
2021-09-10 00:18:47 +03:00
coll->FrontRight.Floor = STOP_SIZE;
2020-12-21 13:16:29 -03:00
2021-09-10 00:18:47 +03:00
if (coll->Middle.Floor == NO_HEIGHT)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_FRONT;
2020-12-21 13:16:29 -03:00
return;
}
2021-09-10 00:18:47 +03:00
if (coll->Middle.Floor - coll->Middle.Ceiling <= 0)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_CLAMP;
2020-12-21 13:16:29 -03:00
return;
}
2021-09-10 00:18:47 +03:00
if (coll->Middle.Ceiling >= 0)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
coll->Shift.y = coll->Middle.Ceiling;
coll->CollisionType = CT_TOP;
2020-12-21 13:16:29 -03:00
}
2021-09-10 00:20:59 +03:00
if ((coll->Front.Floor > coll->Setup.BadHeightUp)
|| (coll->Front.Floor < coll->Setup.BadHeightDown)
|| (coll->Front.Ceiling > coll->Setup.BadCeilingHeight))
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
if ((coll->Front.Type == DIAGONAL)
|| (coll->Front.Type == SPLIT_TRI))
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2020-12-21 13:16:29 -03:00
}
else
{
2021-09-10 00:18:47 +03:00
switch (quadrant)
2020-12-21 13:16:29 -03:00
{
case 0:
case 2:
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
2021-09-10 00:18:47 +03:00
coll->Shift.z = FindGridShift(zPos + zfront, zPos);
2020-12-21 13:16:29 -03:00
break;
case 1:
case 3:
2021-09-10 00:18:47 +03:00
coll->Shift.x = FindGridShift(xPos + xfront, xPos);
2021-09-10 00:20:59 +03:00
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2020-12-21 13:16:29 -03:00
break;
}
}
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_FRONT;
2020-12-21 13:16:29 -03:00
return;
}
2021-09-10 00:20:59 +03:00
if (coll->Front.Ceiling >= coll->Setup.BadCeilingHeight)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_TOP_FRONT;
2020-12-21 13:16:29 -03:00
return;
}
2021-09-10 00:20:59 +03:00
if (coll->MiddleLeft.Floor > coll->Setup.BadHeightUp ||
coll->MiddleLeft.Floor < coll->Setup.BadHeightDown ||
coll->MiddleLeft.Ceiling > coll->Setup.BadCeilingHeight)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
if (coll->MiddleLeft.Type == SPLIT_TRI && coll->Middle.Type == SPLIT_TRI)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2020-12-21 13:16:29 -03:00
}
else
{
2021-09-10 00:18:47 +03:00
switch (quadrant)
2020-12-21 13:16:29 -03:00
{
case 0:
case 2:
2021-09-10 00:18:47 +03:00
coll->Shift.x = FindGridShift(xPos + xleft, xPos + xfront);
2020-12-21 13:16:29 -03:00
break;
case 1:
case 3:
2021-09-10 00:18:47 +03:00
coll->Shift.z = FindGridShift(zPos + zleft, zPos + zfront);
2020-12-21 13:16:29 -03:00
break;
}
}
2021-09-10 00:18:47 +03:00
if (coll->MiddleLeft.SplitFloor && coll->MiddleLeft.SplitFloor == coll->Middle.SplitFloor)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
2020-12-21 13:16:29 -03:00
quarter %= 2;
2021-09-10 00:18:47 +03:00
switch (coll->MiddleLeft.SplitFloor)
2020-12-21 13:16:29 -03:00
{
case SPLIT1:
case NOCOLF1T:
case NOCOLF1B:
if (quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_LEFT;
2020-12-21 13:16:29 -03:00
break;
case SPLIT2:
case NOCOLF2T:
case NOCOLF2B:
if (!quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_LEFT;
2020-12-21 13:16:29 -03:00
break;
}
}
else
{
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_LEFT;
2020-12-21 13:16:29 -03:00
}
return;
}
2021-09-10 00:20:59 +03:00
if (coll->MiddleRight.Floor > coll->Setup.BadHeightUp ||
coll->MiddleRight.Floor < coll->Setup.BadHeightDown ||
coll->MiddleRight.Ceiling > coll->Setup.BadCeilingHeight)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
if (coll->MiddleRight.Type == SPLIT_TRI && coll->Middle.Type == SPLIT_TRI)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
2020-12-21 13:16:29 -03:00
}
else
{
2021-09-10 00:18:47 +03:00
switch (quadrant)
2020-12-21 13:16:29 -03:00
{
case 0:
case 2:
2021-09-10 00:18:47 +03:00
coll->Shift.x = FindGridShift(xPos + xright, xPos + xfront);
2020-12-21 13:16:29 -03:00
break;
case 1:
case 3:
2021-09-10 00:18:47 +03:00
coll->Shift.z = FindGridShift(zPos + zright, zPos + zfront);
2020-12-21 13:16:29 -03:00
break;
}
}
2021-09-10 00:18:47 +03:00
if (coll->MiddleRight.SplitFloor && coll->MiddleRight.SplitFloor == coll->Middle.SplitFloor)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
2020-12-21 13:16:29 -03:00
quarter %= 2;
2021-09-10 00:18:47 +03:00
switch (coll->MiddleRight.SplitFloor)
2020-12-21 13:16:29 -03:00
{
case SPLIT1:
case NOCOLF1T:
case NOCOLF1B:
if (quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_RIGHT;
2020-12-21 13:16:29 -03:00
break;
case SPLIT2:
case NOCOLF2T:
case NOCOLF2B:
if (!quarter)
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_RIGHT;
2020-12-21 13:16:29 -03:00
break;
}
}
else
{
2021-09-10 00:18:47 +03:00
coll->CollisionType = CT_RIGHT;
2020-12-21 13:16:29 -03:00
}
return;
}
}
void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv) // previously DoProperDetection
{
int bs, yang;
ITEM_INFO* item = &g_Level.Items[itemNumber];
auto oldCollResult = GetCollisionResult(x, y, z, item->roomNumber);
auto collResult = GetCollisionResult(item);
2021-08-27 19:29:18 +03:00
if (item->pos.yPos >= collResult.FloorHeight)
{
bs = 0;
if ((collResult.HeightType == BIG_SLOPE || collResult.HeightType == DIAGONAL) && oldCollResult.FloorHeight < collResult.FloorHeight)
{
yang = (long)((unsigned short)item->pos.yRot);
if (collResult.TiltX < 0)
{
if (yang >= 0x8000)
bs = 1;
}
else if (collResult.TiltX > 0)
{
if (yang <= 0x8000)
bs = 1;
}
if (collResult.TiltZ < 0)
{
if (yang >= 0x4000 && yang <= 0xc000)
bs = 1;
}
else if (collResult.TiltZ > 0)
{
if (yang <= 0x4000 || yang >= 0xc000)
bs = 1;
}
}
/* If last position of item was also below this floor height, we've hit a wall, else we've hit a floor */
if (y > (collResult.FloorHeight + 32) && bs == 0 &&
(((x / SECTOR(1)) != (item->pos.xPos / SECTOR(1))) ||
((z / SECTOR(1)) != (item->pos.zPos / SECTOR(1)))))
{
// Need to know which direction the wall is.
long xs;
if ((x & (~(WALL_SIZE - 1))) != (item->pos.xPos & (~(WALL_SIZE - 1))) && // X crossed boundary?
(z & (~(WALL_SIZE - 1))) != (item->pos.zPos & (~(WALL_SIZE - 1)))) // Z crossed boundary as well?
{
if (abs(x - item->pos.xPos) < abs(z - item->pos.zPos))
xs = 1; // X has travelled the shortest, so (maybe) hit first. (Seems to work ok).
else
xs = 0;
}
else
xs = 1;
if ((x & (~(WALL_SIZE - 1))) != (item->pos.xPos & (~(WALL_SIZE - 1))) && xs) // X crossed boundary?
{
if (xv <= 0) // Hit angle = 0xc000.
item->pos.yRot = 0x4000 + (0xc000 - item->pos.yRot);
else // Hit angle = 0x4000.
item->pos.yRot = 0xc000 + (0x4000 - item->pos.yRot);
}
else // Z crossed boundary.
item->pos.yRot = 0x8000 - item->pos.yRot;
item->speed /= 2;
/* Put item back in its last position */
item->pos.xPos = x;
item->pos.yPos = y;
item->pos.zPos = z;
}
else if (collResult.HeightType == BIG_SLOPE || collResult.HeightType == DIAGONAL) // Hit a steep slope?
{
// Need to know which direction the slope is.
item->speed -= (item->speed / 4);
if (collResult.TiltX < 0 && ((abs(collResult.TiltX)) - (abs(collResult.TiltZ)) >= 2)) // Hit angle = 0x4000
{
if (((unsigned short)item->pos.yRot) > 0x8000)
{
item->pos.yRot = 0x4000 + (0xc000 - (unsigned short)item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed -= collResult.TiltX * 2;
if ((unsigned short)item->pos.yRot > 0x4000 && (unsigned short)item->pos.yRot < 0xc000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot < 0x4000)
item->pos.yRot = 0x4000;
}
else if ((unsigned short)item->pos.yRot < 0x4000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot > 0x4000)
item->pos.yRot = 0x4000;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
else if (collResult.TiltX > 0 && ((abs(collResult.TiltX)) - (abs(collResult.TiltZ)) >= 2)) // Hit angle = 0xc000
{
if (((unsigned short)item->pos.yRot) < 0x8000)
{
item->pos.yRot = 0xc000 + (0x4000 - (unsigned short)item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed += collResult.TiltX * 2;
if ((unsigned short)item->pos.yRot > 0xc000 || (unsigned short)item->pos.yRot < 0x4000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot < 0xc000)
item->pos.yRot = 0xc000;
}
else if ((unsigned short)item->pos.yRot < 0xc000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot > 0xc000)
item->pos.yRot = 0xc000;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
else if (collResult.TiltZ < 0 && ((abs(collResult.TiltZ)) - (abs(collResult.TiltX)) >= 2)) // Hit angle = 0
{
if (((unsigned short)item->pos.yRot) > 0x4000 && ((unsigned short)item->pos.yRot) < 0xc000)
{
item->pos.yRot = (0x8000 - item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed -= collResult.TiltZ * 2;
if ((unsigned short)item->pos.yRot < 0x8000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot > 0xf000)
item->pos.yRot = 0;
}
else if ((unsigned short)item->pos.yRot >= 0x8000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot < 0x1000)
item->pos.yRot = 0;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
else if (collResult.TiltZ > 0 && ((abs(collResult.TiltZ)) - (abs(collResult.TiltX)) >= 2)) // Hit angle = 0x8000
{
if (((unsigned short)item->pos.yRot) > 0xc000 || ((unsigned short)item->pos.yRot) < 0x4000)
{
item->pos.yRot = (0x8000 - item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed += collResult.TiltZ * 2;
if ((unsigned short)item->pos.yRot > 0x8000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot < 0x8000)
item->pos.yRot = 0x8000;
}
else if ((unsigned short)item->pos.yRot < 0x8000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot > 0x8000)
item->pos.yRot = 0x8000;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
else if (collResult.TiltX < 0 && collResult.TiltZ < 0) // Hit angle = 0x2000
{
if (((unsigned short)item->pos.yRot) > 0x6000 && ((unsigned short)item->pos.yRot) < 0xe000)
{
item->pos.yRot = 0x2000 + (0xa000 - (unsigned short)item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed += (-collResult.TiltX) + (-collResult.TiltZ);
if ((unsigned short)item->pos.yRot > 0x2000 && (unsigned short)item->pos.yRot < 0xa000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot < 0x2000)
item->pos.yRot = 0x2000;
}
else if ((unsigned short)item->pos.yRot != 0x2000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot > 0x2000)
item->pos.yRot = 0x2000;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
else if (collResult.TiltX < 0 && collResult.TiltZ > 0) // Hit angle = 0x6000
{
if (((unsigned short)item->pos.yRot) > 0xa000 || ((unsigned short)item->pos.yRot) < 0x2000)
{
item->pos.yRot = 0x6000 + (0xe000 - (unsigned short)item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed += (-collResult.TiltX) + collResult.TiltZ;
if ((unsigned short)item->pos.yRot < 0xe000 && (unsigned short)item->pos.yRot > 0x6000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot < 0x6000)
item->pos.yRot = 0x6000;
}
else if ((unsigned short)item->pos.yRot != 0x6000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot > 0x6000)
item->pos.yRot = 0x6000;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
else if (collResult.TiltX > 0 && collResult.TiltZ > 0) // Hit angle = 0xa000
{
if (((unsigned short)item->pos.yRot) > 0xe000 || ((unsigned short)item->pos.yRot) < 0x6000)
{
item->pos.yRot = 0xa000 + (0x2000 - (unsigned short)item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed += collResult.TiltX + collResult.TiltZ;
if ((unsigned short)item->pos.yRot < 0x2000 || (unsigned short)item->pos.yRot > 0xa000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot < 0xa000)
item->pos.yRot = 0xa000;
}
else if ((unsigned short)item->pos.yRot != 0xa000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot > 0xa000)
item->pos.yRot = 0xa000;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
else if (collResult.TiltX > 0 && collResult.TiltZ < 0) // Hit angle = 0xe000
{
if (((unsigned short)item->pos.yRot) > 0x2000 && ((unsigned short)item->pos.yRot) < 0xa000)
{
item->pos.yRot = 0xe000 + (0x6000 - (unsigned short)item->pos.yRot - 1);
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
}
else
{
if (item->speed < 32)
{
item->speed += collResult.TiltX + (-collResult.TiltZ);
if ((unsigned short)item->pos.yRot < 0x6000 || (unsigned short)item->pos.yRot > 0xe000)
{
item->pos.yRot -= 4096;
if ((unsigned short)item->pos.yRot < 0xe000)
item->pos.yRot = 0xe000;
}
else if ((unsigned short)item->pos.yRot != 0xe000)
{
item->pos.yRot += 4096;
if ((unsigned short)item->pos.yRot > 0xe000)
item->pos.yRot = 0xe000;
}
}
if (item->fallspeed > 0)
item->fallspeed = -(item->fallspeed / 2);
else
item->fallspeed = 0;
}
}
/* Put item back in its last position */
item->pos.xPos = x;
item->pos.yPos = y;
item->pos.zPos = z;
}
else
{
/* Hit the floor; bounce and slow down */
if (item->fallspeed > 0)
{
if (item->fallspeed > 16)
{
if (item->objectNumber == ID_GRENADE)
item->fallspeed = -(item->fallspeed - (item->fallspeed / 2));
else
{
item->fallspeed = -(item->fallspeed / 2);
if (item->fallspeed < -100)
item->fallspeed = -100;
}
}
else
{
/* Roll on floor */
item->fallspeed = 0;
if (item->objectNumber == ID_GRENADE)
{
item->requiredAnimState = 1;
item->pos.xRot = 0;
item->speed--;
}
else
item->speed -= 3;
if (item->speed < 0)
item->speed = 0;
}
}
item->pos.yPos = collResult.FloorHeight;
}
}
else // Check for on top of object.
{
if (yv >= 0)
{
oldCollResult = GetCollisionResult(item->pos.xPos, y, item->pos.zPos, item->roomNumber);
collResult = GetCollisionResult(item);
2021-08-27 19:29:18 +03:00
// Bounce off floor.
// Removed weird OnObject global check from here which didnt make sense because OnObject
// was always set to 0 by GetHeight() function which was called before the check.
// Possibly a mistake or unfinished feature by Core? -- Lwmte, 27.08.21
if (item->pos.yPos >= oldCollResult.FloorHeight)
{
/* Hit the floor; bounce and slow down */
if (item->fallspeed > 0)
{
if (item->fallspeed > 16)
{
if (item->objectNumber == ID_GRENADE)
item->fallspeed = -(item->fallspeed - (item->fallspeed / 2));
else
{
item->fallspeed = -(item->fallspeed / 4);
if (item->fallspeed < -100)
item->fallspeed = -100;
}
}
else
{
/* Roll on floor */
item->fallspeed = 0;
if (item->objectNumber == ID_GRENADE)
{
item->requiredAnimState = 1;
item->pos.xRot = 0;
item->speed--;
}
else
item->speed -= 3;
if (item->speed < 0)
item->speed = 0;
}
}
item->pos.yPos = oldCollResult.FloorHeight;
}
}
// else
{
/* Bounce off ceiling */
collResult = GetCollisionResult(item);
if (item->pos.yPos < collResult.CeilingHeight)
{
if (y < collResult.CeilingHeight &&
(((x / SECTOR(1)) != (item->pos.xPos / SECTOR(1))) ||
((z / SECTOR(1)) != (item->pos.zPos / SECTOR(1)))))
{
// Need to know which direction the wall is.
if ((x & (~(WALL_SIZE - 1))) != (item->pos.xPos & (~(WALL_SIZE - 1)))) // X crossed boundary?
{
if (xv <= 0) // Hit angle = 0xc000.
item->pos.yRot = 0x4000 + (0xc000 - item->pos.yRot);
else // Hit angle = 0x4000.
item->pos.yRot = 0xc000 + (0x4000 - item->pos.yRot);
}
else // Z crossed boundary.
{
item->pos.yRot = 0x8000 - item->pos.yRot;
}
if (item->objectNumber == ID_GRENADE)
item->speed -= item->speed / 8;
else
item->speed /= 2;
/* Put item back in its last position */
item->pos.xPos = x;
item->pos.yPos = y;
item->pos.zPos = z;
}
else
item->pos.yPos = collResult.CeilingHeight;
if (item->fallspeed < 0)
item->fallspeed = -item->fallspeed;
}
}
}
collResult = GetCollisionResult(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
if (collResult.RoomNumber != item->roomNumber)
ItemNewRoom(itemNumber, collResult.RoomNumber);
}
void DoObjectCollision(ITEM_INFO* l, COLL_INFO* coll) // previously LaraBaddieCollision
2020-12-21 13:16:29 -03:00
{
ITEM_INFO* item;
OBJECT_INFO* obj;
l->hitStatus = false;
2021-09-10 00:18:47 +03:00
coll->HitStatic = false;
2021-09-02 22:28:55 +03:00
if (l == LaraItem)
Lara.hitDirection = -1;
2020-12-21 13:16:29 -03:00
if (l->hitPoints > 0)
{
short* door, numDoors;
short roomsToCheck[128];
short numRoomsToCheck = 0;
roomsToCheck[numRoomsToCheck++] = l->roomNumber;
ROOM_INFO* room = &g_Level.Rooms[l->roomNumber];
for (int i = 0; i < room->doors.size(); i++)
{
roomsToCheck[numRoomsToCheck++] = room->doors[i].room;
}
for (int i = 0; i < numRoomsToCheck; i++)
{
short itemNumber = g_Level.Rooms[roomsToCheck[i]].itemNumber;
while (itemNumber != NO_ITEM)
{
item = &g_Level.Items[itemNumber];
if (item->collidable && item->status != ITEM_INVISIBLE)
{
obj = &Objects[item->objectNumber];
if (obj->collision != nullptr)
{
2021-09-08 13:39:29 +03:00
int x = abs(l->pos.xPos - item->pos.xPos);
int y = abs(l->pos.yPos - item->pos.yPos);
int z = abs(l->pos.zPos - item->pos.zPos);
if (x < COLLISION_CHECK_DISTANCE &&
y < COLLISION_CHECK_DISTANCE &&
z < COLLISION_CHECK_DISTANCE)
2020-12-21 13:16:29 -03:00
obj->collision(itemNumber, l, coll);
}
}
itemNumber = item->nextItem;
}
2021-09-02 22:28:55 +03:00
for (int j = 0; j < g_Level.Rooms[roomsToCheck[i]].mesh.size(); j++)
2020-12-21 13:16:29 -03:00
{
2021-09-02 22:28:55 +03:00
MESH_INFO* mesh = &g_Level.Rooms[roomsToCheck[i]].mesh[j];
// Only process meshes which are visible and non-solid
2021-09-08 14:05:09 +03:00
if ((mesh->flags & StaticMeshFlags::SM_VISIBLE) && !(mesh->flags & StaticMeshFlags::SM_SOLID))
2020-12-21 13:16:29 -03:00
{
2021-09-08 13:39:29 +03:00
int x = abs(l->pos.xPos - mesh->x);
int y = abs(l->pos.yPos - mesh->y);
int z = abs(l->pos.zPos - mesh->z);
2020-12-21 13:16:29 -03:00
2021-09-08 13:39:29 +03:00
if (x < COLLISION_CHECK_DISTANCE &&
y < COLLISION_CHECK_DISTANCE &&
z < COLLISION_CHECK_DISTANCE)
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:20:59 +03:00
if (TestBoundsCollideStatic(l, mesh, coll->Setup.Radius))
2020-12-21 13:16:29 -03:00
{
2021-09-10 00:18:47 +03:00
coll->HitStatic = true;
2020-12-21 13:16:29 -03:00
2021-09-10 00:20:59 +03:00
if (coll->Setup.EnableObjectPush)
2021-09-07 20:37:31 +03:00
ItemPushStatic(l, mesh, coll);
2021-09-07 19:54:35 +03:00
else
break;
2020-12-21 13:16:29 -03:00
}
}
}
}
}
2021-09-02 22:28:55 +03:00
if (l == LaraItem && Lara.hitDirection == -1)
2020-12-21 13:16:29 -03:00
Lara.hitFrame = 0;
}
}
void GenericSphereBoxCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
{
ITEM_INFO* item = &g_Level.Items[itemNum];
if (item->status != ITEM_INVISIBLE)
{
2021-09-10 00:20:59 +03:00
if (TestBoundsCollide(item, l, coll->Setup.Radius))
2020-12-21 13:16:29 -03:00
{
int collidedBits = TestCollision(item, l);
if (collidedBits)
2020-12-21 13:16:29 -03:00
{
short oldRot = item->pos.yRot;
item->pos.yRot = 0;
GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
item->pos.yRot = oldRot;
int deadlyBits = item->itemFlags[0];
2020-12-21 13:16:29 -03:00
SPHERE* sphere = &CreatureSpheres[0];
if (item->itemFlags[2] != 0)
{
collidedBits &= ~1;
}
while (collidedBits)
2020-12-21 13:16:29 -03:00
{
if (collidedBits & 1)
2020-12-21 13:16:29 -03:00
{
GlobalCollisionBounds.X1 = sphere->x - sphere->r - item->pos.xPos;
GlobalCollisionBounds.X2 = sphere->x + sphere->r - item->pos.xPos;
GlobalCollisionBounds.Y1 = sphere->y - sphere->r - item->pos.yPos;
GlobalCollisionBounds.Y2 = sphere->y + sphere->r - item->pos.yPos;
GlobalCollisionBounds.Z1 = sphere->z - sphere->r - item->pos.zPos;
GlobalCollisionBounds.Z2 = sphere->z + sphere->r - item->pos.zPos;
int x = l->pos.xPos;
int y = l->pos.yPos;
int z = l->pos.zPos;
2021-09-10 00:20:59 +03:00
if (ItemPushItem(item, l, coll, ((deadlyBits & 1) & coll->Setup.EnableSpaz), 3) && (deadlyBits & 1))
2020-12-21 13:16:29 -03:00
{
l->hitPoints -= item->itemFlags[3];
2020-12-21 13:16:29 -03:00
int dx = x - l->pos.xPos;
int dy = y - l->pos.yPos;
int dz = z - l->pos.zPos;
if (dx || dy || dz)
{
if (TriggerActive(item))
TriggerLaraBlood();
}
2021-09-10 00:20:59 +03:00
if (!coll->Setup.EnableObjectPush)
2020-12-21 13:16:29 -03:00
{
l->pos.xPos += dx;
l->pos.yPos += dy;
l->pos.zPos += dz;
}
}
}
collidedBits >>= 1;
deadlyBits >>= 1;
2020-12-21 13:16:29 -03:00
sphere++;
}
}
}
}
}
2021-08-25 05:45:10 +03:00
// New function for rotating item along XZ slopes.
// (int radiusDivide) is for radiusZ, else the MaxZ is too high and cause rotation problem !
// Dont need to set a value in radiusDivide if you dont need it (radiusDivide is set to 1 by default).
// Warning: dont set it to 0 !!!!
2020-12-21 13:16:29 -03:00
void CalcItemToFloorRotation(ITEM_INFO* item, int radiusDivide)
{
FLOOR_INFO* floor;
BOUNDING_BOX* bounds;
GAME_VECTOR pos;
int ratioXZ, frontHDif, sideHDif;
int frontX, frontZ, leftX, leftZ, rightX, rightZ;
int frontHeight, backHeight, leftHeight, rightHeight;
int radiusZ, radiusX;
bounds = GetBoundsAccurate(item);
pos.x = item->pos.xPos;
pos.y = item->pos.yPos;
pos.z = item->pos.zPos;
pos.roomNumber = item->roomNumber;
radiusX = bounds->X2;
radiusZ = bounds->Z2 / radiusDivide; // need divide in any case else it's too much !
ratioXZ = radiusZ / radiusX;
frontX = phd_sin(item->pos.yRot) * radiusZ;
frontZ = phd_cos(item->pos.yRot) * radiusZ;
leftX = -frontZ * ratioXZ;
leftZ = frontX * ratioXZ;
rightX = frontZ * ratioXZ;
rightZ = -frontX * ratioXZ;
floor = GetFloor(pos.x + frontX, pos.y, pos.z + frontZ, &pos.roomNumber);
frontHeight = GetFloorHeight(floor, pos.x + frontX, pos.y, pos.z + frontZ);
floor = GetFloor(pos.x - frontX, pos.y, pos.z - frontZ, &pos.roomNumber);
backHeight = GetFloorHeight(floor, pos.x - frontX, pos.y, pos.z - frontZ);
floor = GetFloor(pos.x + leftX, pos.y, pos.z + leftZ, &pos.roomNumber);
leftHeight = GetFloorHeight(floor, pos.x + leftX, pos.y, pos.z + leftZ);
floor = GetFloor(pos.x + rightX, pos.y, pos.z + rightZ, &pos.roomNumber);
rightHeight = GetFloorHeight(floor, pos.x + rightX, pos.y, pos.z + rightZ);
frontHDif = backHeight - frontHeight;
sideHDif = rightHeight - leftHeight;
// NOTE: float(atan2()) is required, else warning about double !
item->pos.xRot = ANGLE(float(atan2(frontHDif, 2 * radiusZ)) / RADIAN);
item->pos.zRot = ANGLE(float(atan2(sideHDif, 2 * radiusX)) / RADIAN);
}
static bool ItemCollide(int value, int radius)
{
return value >= -radius && value <= radius;
}
static bool ItemInRange(int x, int z, int radius)
{
return (SQUARE(x) + SQUARE(z)) <= SQUARE(radius);
}
bool ItemNearLara(PHD_3DPOS* pos, int radius)
{
BOUNDING_BOX* bounds;
GAME_VECTOR target;
target.x = pos->xPos - LaraItem->pos.xPos;
target.y = pos->yPos - LaraItem->pos.yPos;
target.z = pos->zPos - LaraItem->pos.zPos;
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
return false;
if (!ItemCollide(target.x, radius) || !ItemCollide(target.z, radius))
return false;
if (!ItemInRange(target.x, target.z, radius))
return false;
bounds = GetBoundsAccurate(LaraItem);
if (target.y >= bounds->Y1 && target.y <= (bounds->Y2 + LARA_RAD))
return true;
return false;
}
bool ItemNearTarget(PHD_3DPOS* src, ITEM_INFO* target, int radius)
{
BOUNDING_BOX* bounds;
PHD_VECTOR pos;
pos.x = src->xPos - target->pos.xPos;
pos.y = src->yPos - target->pos.yPos;
pos.z = src->zPos - target->pos.zPos;
if (!ItemCollide(pos.y, ITEM_RADIUS_YMAX))
return false;
if (!ItemCollide(pos.x, radius) || !ItemCollide(pos.z, radius))
return false;
if (!ItemInRange(pos.x, pos.z, radius))
return false;
bounds = GetBoundsAccurate(target);
if (pos.y >= bounds->Y1 && pos.y <= bounds->Y2)
return true;
return false;
}
2020-12-21 13:16:29 -03:00
bool SnapToQuadrant(short& angle, int interval)
{
if (abs(angle) <= ANGLE(interval))
{
angle = 0;
return true;
}
else if (angle >= ANGLE(90 - interval) && angle <= ANGLE(interval + 90))
{
angle = ANGLE(90);
return true;
}
else if (angle >= ANGLE(180 - interval) || angle <= -ANGLE(180 - interval))
{
angle = ANGLE(180);
return true;
}
else if (angle >= -ANGLE(interval + 90) && angle <= -ANGLE(90 - interval))
{
angle = -ANGLE(90);
return true;
}
return false;
}
int GetQuadrant(short angle)
{
return (unsigned short) (angle + ANGLE(45)) / ANGLE(90);
}
bool SnapToDiagonal(short& angle, int interval)
{
if (angle >= ANGLE(45 - interval) && angle <= ANGLE(interval + 45))
{
angle = ANGLE(45);
return true;
}
else if (angle >= ANGLE(135 - interval) && angle <= ANGLE(interval + 135))
{
angle = ANGLE(135);
return true;
}
else if (angle >= -ANGLE(interval + 135) && angle <= -ANGLE(135 - interval))
{
angle = -ANGLE(135);
return true;
}
else if (angle >= -ANGLE(interval + 45) && angle <= -ANGLE(45 - interval))
{
angle = -ANGLE(45);
return true;
}
return false;
}
Vector2 GetDiagonalIntersect(int xPos, int zPos, int splitType, int radius, short yRot)
{
Vector2 vect;
int dx = (xPos % WALL_SIZE) - WALL_SIZE/2;
int dz = (zPos % WALL_SIZE) - WALL_SIZE/2;
int xGrid = xPos - dx;
int zGrid = zPos - dz;
switch (splitType)
{
case SPLIT1:
case NOCOLF1T:
case NOCOLF1B:
xPos = xGrid + (dx - dz) / 2;
zPos = zGrid - (dx - dz) / 2;
break;
case SPLIT2:
case NOCOLF2T:
case NOCOLF2B:
xPos = xGrid + (dx + dz) / 2;
zPos = zGrid + (dx + dz) / 2;
break;
default:
break;
}
if (splitType)
{
yRot = floor(yRot / 16384.0f) * ANGLE(90) + ANGLE(45);
xPos -= int(radius * sin(TO_RAD(yRot)));
zPos -= int(radius * cos(TO_RAD(yRot)));
}
vect.x = xPos;
vect.y = zPos;
return vect;
}
Vector2 GetOrthogonalIntersect(int xPos, int zPos, int radius, short yRot)
{
Vector2 vect;
int xGrid = xPos - (xPos % WALL_SIZE);
2020-12-21 13:16:29 -03:00
int zGrid = zPos - (zPos % WALL_SIZE);
int dir = (unsigned short)(yRot + ANGLE(45)) / ANGLE(90);
switch (dir)
{
case NORTH:
zPos = zGrid + (WALL_SIZE - 1) - radius;
break;
case EAST:
xPos = xGrid + (WALL_SIZE - 1) - radius;
break;
case SOUTH:
zPos = zGrid + radius;
break;
case WEST:
xPos = xGrid + radius;
2020-12-21 13:16:29 -03:00
break;
}
vect.x = xPos;
vect.y = zPos;
return vect;
2021-09-04 07:59:00 +02:00
}