2020-12-21 13:16:29 -03:00
|
|
|
#include "framework.h"
|
2021-09-25 16:00:30 +03:00
|
|
|
#include "control/los.h"
|
2020-12-21 13:16:29 -03:00
|
|
|
#include "collide.h"
|
2021-09-16 05:06:03 +03:00
|
|
|
#include "animation.h"
|
2020-12-21 13:16:29 -03:00
|
|
|
#include "Lara.h"
|
|
|
|
#include "items.h"
|
2021-09-25 16:00:30 +03:00
|
|
|
#include "effects/effects.h"
|
2020-12-21 13:16:29 -03:00
|
|
|
#include "sphere.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "setup.h"
|
2021-09-25 16:00:30 +03:00
|
|
|
#include "Sound/sound.h"
|
|
|
|
#include "Specific/trmath.h"
|
|
|
|
#include "Specific/prng.h"
|
2021-09-08 14:02:32 +03:00
|
|
|
#include "room.h"
|
2021-09-10 18:13:40 +03:00
|
|
|
#include "Renderer11.h"
|
2021-09-25 11:27:47 +02: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;
|
2021-04-29 06:26:17 +02:00
|
|
|
ITEM_INFO* CollidedItems[MAX_COLLIDED_OBJECTS];
|
|
|
|
MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS];
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-09-13 10:15:53 +03:00
|
|
|
bool GetCollidedObjects(ITEM_INFO* collidingItem, int radius, int onlyVisible, ITEM_INFO** collidedItems, MESH_INFO** collidedMeshes, int ignoreLara)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
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
|
|
|
{
|
2021-09-10 13:49:45 +03:00
|
|
|
if (collidingItem->pos.yPos + radius + STEP_SIZE/2 >= mesh->pos.yPos + staticMesh->collisionBox.Y1)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-09-10 13:49:45 +03:00
|
|
|
if (collidingItem->pos.yPos <= mesh->pos.yPos + staticMesh->collisionBox.Y2)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-09-10 13:49:45 +03:00
|
|
|
s = phd_sin(mesh->pos.yRot);
|
|
|
|
c = phd_cos(mesh->pos.yRot);
|
|
|
|
rx = (collidingItem->pos.xPos - mesh->pos.xPos) * c - s * (collidingItem->pos.zPos - mesh->pos.zPos);
|
|
|
|
rz = (collidingItem->pos.zPos - mesh->pos.zPos) * c + s * (collidingItem->pos.xPos - mesh->pos.xPos);
|
2020-12-21 13:16:29 -03:00
|
|
|
|
|
|
|
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];
|
|
|
|
|
2021-08-27 16:30:03 +03:00
|
|
|
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;
|
|
|
|
}
|
2021-04-29 06:26:17 +02:00
|
|
|
|
2020-12-21 13:16:29 -03:00
|
|
|
/*this is awful*/
|
2021-10-04 12:27:45 +03:00
|
|
|
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;
|
2021-10-04 12:27:45 +03:00
|
|
|
}
|
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);
|
|
|
|
|
2021-04-29 06:26:17 +02:00
|
|
|
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
|
2021-06-07 12:03:53 -04:00
|
|
|
&& 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)
|
|
|
|
{
|
2021-04-29 06:26:17 +02:00
|
|
|
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-10-07 11:01:39 +03:00
|
|
|
coll->HitTallObject = false;
|
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-10 13:49:45 +03:00
|
|
|
int x = abs(item->pos.xPos - mesh->pos.xPos);
|
|
|
|
int y = abs(item->pos.yPos - mesh->pos.yPos);
|
|
|
|
int z = abs(item->pos.zPos - mesh->pos.zPos);
|
2021-09-08 14:22:58 +03:00
|
|
|
|
|
|
|
if (x < COLLISION_CHECK_DISTANCE &&
|
|
|
|
y < COLLISION_CHECK_DISTANCE &&
|
|
|
|
z < COLLISION_CHECK_DISTANCE)
|
2021-09-08 14:02:32 +03:00
|
|
|
{
|
|
|
|
auto stInfo = StaticObjects[mesh->staticNumber];
|
2021-09-10 13:49:45 +03:00
|
|
|
if (CollideSolidBounds(item, stInfo.collisionBox, mesh->pos, coll))
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->HitStatic = true;
|
2021-09-08 14:02:32 +03:00
|
|
|
}
|
2021-09-08 13:39:29 +03:00
|
|
|
}
|
2021-09-04 12:40:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-08 14:02:32 +03:00
|
|
|
bool CollideSolidBounds(ITEM_INFO* item, BOUNDING_BOX box, PHD_3DPOS pos, COLL_INFO* coll)
|
2021-09-04 12:40:14 +03:00
|
|
|
{
|
2021-09-08 14:02:32 +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
|
2021-09-14 14:38:59 +03:00
|
|
|
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);
|
2021-09-14 14:38:59 +03:00
|
|
|
auto itemBounds = TO_DX_BBOX(item->pos, itemBBox);
|
2021-09-04 12:40:14 +03:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2021-09-19 06:42:24 +03:00
|
|
|
// Calculate horizontal item coll bounds according to radius
|
2021-09-04 12:40:14 +03:00
|
|
|
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-19 06:42:24 +03:00
|
|
|
|
|
|
|
// Calculate vertical item coll bounds according to either height (land mode) or precise bounds (water mode).
|
|
|
|
// Water mode needs special processing because height calc in original engines is inconsistent in such cases.
|
|
|
|
if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
|
|
|
|
{
|
|
|
|
collBox.Y1 = itemBBox->Y1;
|
|
|
|
collBox.Y2 = itemBBox->Y2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
collBox.Y1 = -coll->Setup.Height;
|
|
|
|
collBox.Y2 = 0;
|
|
|
|
}
|
2021-09-04 12:40:14 +03:00
|
|
|
|
2021-09-09 20:21:05 +03:00
|
|
|
// Get and test DX item coll bounds
|
2021-09-14 14:38:59 +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;
|
2021-09-19 06:42:24 +03:00
|
|
|
auto center = item->pos.yPos - height / 2;
|
2021-09-08 13:39:29 +03:00
|
|
|
|
|
|
|
// 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);
|
2021-09-19 06:42:24 +03:00
|
|
|
auto distanceToVerticalPlane = height / 2 - yPoint;
|
2021-09-08 13:39:29 +03:00
|
|
|
|
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-10-07 11:01:39 +03:00
|
|
|
if (bottom && coll->Middle.Ceiling < distanceToVerticalPlane)
|
|
|
|
coll->Middle.Ceiling = 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
|
2021-09-14 14:38:59 +03:00
|
|
|
if (!staticBounds.Intersects(TO_DX_BBOX(PHD_3DPOS(item->pos.xPos, item->pos.yPos, item->pos.zPos), &collBox)))
|
2021-09-08 14:02:32 +03:00
|
|
|
return result;
|
2021-09-07 19:54:35 +03:00
|
|
|
|
2021-09-07 21:36:13 +03:00
|
|
|
// Determine identity rotation/distance
|
2021-09-08 14:02:32 +03:00
|
|
|
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
|
2021-09-08 14:02:32 +03:00
|
|
|
auto x = round(distance.x * c - distance.z * s) + pos.xPos;
|
2021-09-07 19:54:35 +03:00
|
|
|
auto y = item->pos.yPos;
|
2021-09-08 14:02:32 +03:00
|
|
|
auto z = round(distance.x * s + distance.z * c) + pos.zPos;
|
2021-09-08 13:39:29 +03:00
|
|
|
|
|
|
|
// Determine identity static collision bounds
|
2021-09-08 14:02:32 +03:00
|
|
|
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-19 06:42:24 +03:00
|
|
|
auto inXMin = x + collBox.X1;
|
|
|
|
auto inXMax = x + collBox.X2;
|
|
|
|
auto inYMin = y + collBox.Y1;
|
|
|
|
auto inYMax = y + collBox.Y2;
|
|
|
|
auto inZMin = z + collBox.Z1;
|
|
|
|
auto inZMax = z + collBox.Z2;
|
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)
|
2021-09-08 14:02:32 +03:00
|
|
|
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);
|
2021-09-08 14:02:32 +03:00
|
|
|
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);
|
2021-09-08 14:02:32 +03:00
|
|
|
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
|
|
|
|
2021-09-08 16:14:13 +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)
|
2021-10-07 12:34:16 +03:00
|
|
|
coll->HitTallObject = (YMin <= inYMin + LARA_HEADROOM);
|
2021-09-08 16:14:13 +03:00
|
|
|
|
2021-09-04 12:40:14 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-13 10:15:53 +03:00
|
|
|
bool TestWithGlobalCollisionBounds(ITEM_INFO* item, ITEM_INFO* lara, COLL_INFO* coll)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
TestCollision(item, l);
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
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);
|
2021-09-17 15:32:55 +03:00
|
|
|
s.roomNumber = item->roomNumber;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
|
|
|
GAME_VECTOR d;
|
2021-09-17 15:32:55 +03:00
|
|
|
d.x = s.x + phd_sin(item->pos.yRot) * 768;
|
2020-12-21 13:16:29 -03:00
|
|
|
d.y = s.y;
|
2021-09-17 15:32:55 +03:00
|
|
|
d.z = s.z + phd_cos(item->pos.yRot) * 768;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
|
|
|
LOS(&s, &d);
|
|
|
|
|
|
|
|
PHD_VECTOR v;
|
|
|
|
MESH_INFO* mesh;
|
|
|
|
|
|
|
|
// CHECK
|
2021-09-13 10:22:00 +03:00
|
|
|
/*if (ObjectOnLOS2(&s, &d, &v, &mesh) != NO_LOS_ITEM)
|
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
|
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-10-25 12:12:36 +03:00
|
|
|
void MoveItem(ITEM_INFO* item, short angle, int x, int y)
|
|
|
|
{
|
|
|
|
auto s = phd_sin(angle);
|
|
|
|
auto c = phd_cos(angle);
|
|
|
|
|
|
|
|
if (x != 0)
|
|
|
|
{
|
|
|
|
item->pos.xPos += round(x * s);
|
|
|
|
item->pos.zPos += round(x * c);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y != 0)
|
|
|
|
{
|
|
|
|
item->pos.xPos += round(y * s);
|
|
|
|
item->pos.zPos += round(y * c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 13:29:00 +03:00
|
|
|
void SnapItemToLedge(ITEM_INFO* item, COLL_INFO* coll, float offsetMultiplier)
|
|
|
|
{
|
|
|
|
item->pos.xRot = 0;
|
|
|
|
item->pos.yRot = coll->NearestLedgeAngle;
|
|
|
|
item->pos.zRot = 0;
|
2021-10-24 05:09:54 +03:00
|
|
|
item->pos.xPos += round(phd_sin(coll->NearestLedgeAngle) * (coll->NearestLedgeDistance + (coll->Setup.Radius * offsetMultiplier)));
|
|
|
|
item->pos.zPos += round(phd_cos(coll->NearestLedgeAngle) * (coll->NearestLedgeDistance + (coll->Setup.Radius * offsetMultiplier)));
|
2021-10-23 13:29:00 +03:00
|
|
|
}
|
|
|
|
|
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-13 10:15:53 +03:00
|
|
|
bool 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
|
|
|
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;
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
ANIM_FRAME* frame = GetBestFrame(item);
|
|
|
|
if (mesh->pos.yPos + bounds.Y2 <= item->pos.yPos + frame->boundingBox.Y1)
|
2020-12-21 13:16:29 -03:00
|
|
|
return false;
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
if (mesh->pos.yPos + bounds.Y1 >= item->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-10 13:49:45 +03:00
|
|
|
c = phd_cos(mesh->pos.yRot);
|
|
|
|
s = phd_sin(mesh->pos.yRot);
|
2021-09-17 15:32:55 +03:00
|
|
|
x = item->pos.xPos - mesh->pos.xPos;
|
|
|
|
z = item->pos.zPos - mesh->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-17 15:32:55 +03:00
|
|
|
bool ItemPushStatic(ITEM_INFO* item, MESH_INFO* mesh, COLL_INFO* coll) // previously ItemPushLaraStatic
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-09-07 20:37:31 +03:00
|
|
|
auto bounds = StaticObjects[mesh->staticNumber].collisionBox;
|
|
|
|
|
2021-09-10 13:49:45 +03:00
|
|
|
auto c = phd_cos(mesh->pos.yRot);
|
|
|
|
auto s = phd_sin(mesh->pos.yRot);
|
2021-09-07 20:37:31 +03:00
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
auto dx = item->pos.xPos - mesh->pos.xPos;
|
|
|
|
auto dz = item->pos.zPos - mesh->pos.zPos;
|
2021-09-07 20:37:31 +03:00
|
|
|
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-17 15:32:55 +03:00
|
|
|
item->pos.xPos = mesh->pos.xPos + c * rx + s * rz;
|
|
|
|
item->pos.zPos = mesh->pos.zPos + c * rz - s * rx;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-09-19 17:48:32 +03:00
|
|
|
coll->Setup.BadHeightDown = NO_BAD_POS;
|
|
|
|
coll->Setup.BadHeightUp = -STEPUP_HEIGHT;
|
2021-09-10 00:20:59 +03:00
|
|
|
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;
|
2021-09-17 15:32:55 +03:00
|
|
|
coll->Setup.ForwardAngle = phd_atan(item->pos.zPos - coll->Setup.OldPosition.z, item->pos.xPos - coll->Setup.OldPosition.x);
|
2021-10-07 12:34:16 +03:00
|
|
|
|
|
|
|
GetCollisionInfo(coll, item);
|
|
|
|
|
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-17 15:32:55 +03:00
|
|
|
coll->Setup.OldPosition.x = item->pos.xPos;
|
|
|
|
coll->Setup.OldPosition.y = item->pos.yPos;
|
|
|
|
coll->Setup.OldPosition.z = item->pos.zPos;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
UpdateItemRoom(item, -10);
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-17 15:32:55 +03:00
|
|
|
item->pos.xPos = coll->Setup.OldPosition.x;
|
|
|
|
item->pos.zPos = coll->Setup.OldPosition.z;
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
if (item == LaraItem && Lara.isMoving && Lara.moveCount > 15)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
Lara.isMoving = false;
|
|
|
|
Lara.gunStatus = LG_NO_ARMS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
bool ItemPushItem(ITEM_INFO* item, ITEM_INFO* item2, COLL_INFO* coll, bool 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;
|
|
|
|
|
2021-08-16 18:55:25 +03:00
|
|
|
// Get item's rotation
|
|
|
|
c = phd_cos(item->pos.yRot);
|
2020-12-21 13:16:29 -03:00
|
|
|
s = phd_sin(item->pos.yRot);
|
2021-08-16 18:55:25 +03:00
|
|
|
|
|
|
|
// Get vector from item to Lara
|
2021-09-17 15:32:55 +03:00
|
|
|
dx = item2->pos.xPos - item->pos.xPos;
|
|
|
|
dz = item2->pos.zPos - item->pos.zPos;
|
2021-08-16 18:55:25 +03:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
item2->pos.xPos = item->pos.xPos + c * rx + s * rz;
|
|
|
|
item2->pos.zPos = item->pos.zPos + c * rz - s * rx;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
if (item2 == LaraItem && spazon && bounds->Y2 - bounds->Y1 > STEP_SIZE)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
rx = (bounds->X1 + bounds->X2) / 2;
|
|
|
|
rz = (bounds->Z1 + bounds->Z2) / 2;
|
|
|
|
|
|
|
|
dx -= c * rx + s * rz;
|
|
|
|
dz -= c * rz - s * rx;
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
Lara.hitDirection = (item2->pos.yRot - phd_atan(dz, dz) - ANGLE(135)) / 16384;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-09-16 03:56:04 +03:00
|
|
|
if ((!Lara.hitFrame) && (!Lara.spazEffectCount))
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-09-17 15:32:55 +03:00
|
|
|
SoundEffect(SFX_TR4_LARA_INJURY, &item2->pos, 0);
|
2021-09-17 16:07:53 +03:00
|
|
|
Lara.spazEffectCount = GenerateInt(15, 35);
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
|
2021-09-16 03:56:04 +03:00
|
|
|
if (Lara.spazEffectCount)
|
|
|
|
Lara.spazEffectCount--;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
|
|
|
Lara.hitFrame++;
|
|
|
|
if (Lara.hitFrame > 34)
|
|
|
|
Lara.hitFrame = 34;
|
|
|
|
}
|
|
|
|
|
2021-09-19 17:48:32 +03:00
|
|
|
coll->Setup.BadHeightDown = NO_BAD_POS;
|
|
|
|
coll->Setup.BadHeightUp = -STEPUP_HEIGHT;
|
2021-09-10 00:20:59 +03:00
|
|
|
coll->Setup.BadCeilingHeight = 0;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-09-10 00:20:59 +03:00
|
|
|
facing = coll->Setup.ForwardAngle;
|
2021-09-17 15:32:55 +03:00
|
|
|
coll->Setup.ForwardAngle = phd_atan(item2->pos.zPos - coll->Setup.OldPosition.z, item2->pos.xPos - coll->Setup.OldPosition.x);
|
2021-08-16 18:55:25 +03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
GetCollisionInfo(coll, item2);
|
2021-08-16 18:55:25 +03:00
|
|
|
|
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-17 15:32:55 +03:00
|
|
|
coll->Setup.OldPosition.x = item2->pos.xPos;
|
|
|
|
coll->Setup.OldPosition.y = item2->pos.yPos;
|
|
|
|
coll->Setup.OldPosition.z = item2->pos.zPos;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-08-16 18:55:25 +03:00
|
|
|
// Commented because causes Lara to jump out of the water if she touches an object on the surface. re: "kayak bug"
|
2021-09-17 15:32:55 +03:00
|
|
|
// UpdateItemRoom(item2, -10);
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-17 15:32:55 +03:00
|
|
|
item2->pos.xPos = coll->Setup.OldPosition.x;
|
|
|
|
item2->pos.zPos = coll->Setup.OldPosition.z;
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
|
2021-09-17 15:32:55 +03:00
|
|
|
if (item2 == LaraItem && Lara.isMoving && Lara.moveCount > 15)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-09-13 10:15:53 +03:00
|
|
|
bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ITEM_INFO* item, ITEM_INFO* l)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
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;
|
2021-05-31 07:18:02 +02:00
|
|
|
if (zRotRel > bounds->rotZ2)
|
2020-12-21 13:16:29 -03:00
|
|
|
return false;
|
|
|
|
|
2021-05-31 07:18:02 +02:00
|
|
|
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(
|
2021-08-16 18:55:25 +03:00
|
|
|
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.
|
2021-08-23 13:35:26 +02:00
|
|
|
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;
|
|
|
|
|
2021-05-31 07:18:02 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-09-13 10:15:53 +03:00
|
|
|
bool Move3DPosTo3DPos(PHD_3DPOS* src, PHD_3DPOS* dest, int velocity, short angAdd)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
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);
|
2021-08-16 18:55:25 +03:00
|
|
|
direction = (GetQuadrant(angle) - GetQuadrant(dest->yRot)) & 3;
|
2021-06-04 06:02:19 +02:00
|
|
|
|
2021-08-16 18:55:25 +03:00
|
|
|
switch (direction)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-08-16 18:55:25 +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:
|
2021-08-24 18:19:15 +02:00
|
|
|
LaraItem->animNumber = LA_SIDESTEP_RIGHT;
|
2021-08-16 18:55:25 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-24 18:19:15 +02:00
|
|
|
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
|
2021-08-24 18:19:15 +02:00
|
|
|
src->xRot = dest->xRot;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-08-24 18:19:15 +02: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
|
2021-08-24 18:19:15 +02:00
|
|
|
src->yRot = dest->yRot;
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-08-24 18:19:15 +02: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
|
2021-08-24 18:19:15 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-09-13 10:15:53 +03:00
|
|
|
bool MoveLaraPosition(PHD_VECTOR* vec, ITEM_INFO* item, ITEM_INFO* l)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-09-13 10:15:53 +03:00
|
|
|
bool TestBoundsCollide(ITEM_INFO* item, ITEM_INFO* l, int radius)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
|
2021-08-22 01:39:14 +03:00
|
|
|
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);
|
2021-08-24 16:54:26 +03:00
|
|
|
auto result = GetCollisionResult(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
|
|
|
|
|
2021-08-25 05:45:10 +03:00
|
|
|
result.RoomNumber = room;
|
2021-08-24 16:54:26 +03:00
|
|
|
return result;
|
2021-08-22 01:39:14 +03:00
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
|
2021-08-22 01:39:14 +03:00
|
|
|
COLL_RESULT GetCollisionResult(int x, int y, int z, short roomNumber)
|
|
|
|
{
|
|
|
|
auto room = roomNumber;
|
|
|
|
auto floor = GetFloor(x, y, z, &room);
|
2021-08-24 16:54:26 +03:00
|
|
|
auto result = GetCollisionResult(floor, x, y, z);
|
|
|
|
|
|
|
|
result.RoomNumber = room;
|
|
|
|
return result;
|
2021-08-22 01:39:14 +03:00
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
|
2021-08-22 01:39:14 +03:00
|
|
|
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.
|
2021-08-22 01:39:14 +03:00
|
|
|
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.
|
2021-09-10 00:43:26 +03:00
|
|
|
result.Position.Floor = GetFloorHeight(ROOM_VECTOR{ floor->Room, y }, x, z).value_or(NO_HEIGHT);
|
|
|
|
result.Position.Ceiling = GetCeilingHeight(ROOM_VECTOR{ floor->Room, y }, x, z).value_or(NO_HEIGHT);
|
2021-08-27 15:48:22 +03:00
|
|
|
|
2021-08-25 05:45:10 +03:00
|
|
|
// Probe bottom block through portals.
|
2021-09-17 15:32:55 +03:00
|
|
|
while (floor->RoomBelow(x, z, y).value_or(NO_ROOM) != NO_ROOM)
|
2021-02-06 18:33:33 -06:00
|
|
|
{
|
2021-09-17 15:32:55 +03:00
|
|
|
auto r = &g_Level.Rooms[floor->RoomBelow(x, z, y).value_or(floor->Room)];
|
2021-09-17 16:07:53 +03:00
|
|
|
floor = GetSector(r, x - r->x, z - r->z);
|
2021-02-06 18:33:33 -06:00
|
|
|
}
|
|
|
|
|
2021-08-25 06:33:15 +03:00
|
|
|
// Return probed bottom block into result.
|
|
|
|
result.BottomBlock = floor;
|
|
|
|
|
2021-10-08 21:15:27 +03:00
|
|
|
// Get tilts from new floordata.
|
|
|
|
auto tilts = floor->TiltXZ(x, z);
|
|
|
|
result.TiltX = tilts.first;
|
|
|
|
result.TiltZ = tilts.second;
|
2021-09-10 18:13:40 +03:00
|
|
|
|
2021-09-17 22:55:09 +03:00
|
|
|
// Split and slope data
|
|
|
|
result.Position.DiagonalStep = floor->FloorIsDiagonalStep();
|
2021-10-08 21:15:27 +03:00
|
|
|
result.Position.Slope = ((abs(tilts.first)) > 2 || (abs(tilts.second)) > 2);
|
2021-09-17 22:55:09 +03:00
|
|
|
result.Position.SplitAngle = floor->FloorCollision.SplitAngle;
|
2021-09-10 18:13:40 +03:00
|
|
|
|
2021-10-08 21:15:27 +03:00
|
|
|
// TODO: check if we need to keep here this slope vs. bridge check from legacy GetTiltType
|
|
|
|
if ((y + CLICK(2)) < (floor->FloorHeight(x, z)))
|
|
|
|
result.TiltZ = result.TiltX = 0;
|
|
|
|
|
2021-08-22 01:39:14 +03:00
|
|
|
return result;
|
2021-02-06 18:33:33 -06:00
|
|
|
}
|
2021-08-19 18:43:52 +03:00
|
|
|
|
2021-10-09 19:19:02 +03:00
|
|
|
void GetCollisionInfo(COLL_INFO* coll, ITEM_INFO* item, bool resetRoom)
|
2021-09-10 12:14:28 +03:00
|
|
|
{
|
2021-09-19 06:42:24 +03:00
|
|
|
GetCollisionInfo(coll, item, PHD_VECTOR(), resetRoom);
|
2021-09-10 12:14:28 +03:00
|
|
|
}
|
|
|
|
|
2021-10-09 19:19:02 +03:00
|
|
|
void GetCollisionInfo(COLL_INFO* coll, ITEM_INFO* item, PHD_VECTOR offset, bool resetRoom)
|
2021-01-07 11:16:21 -03:00
|
|
|
{
|
2021-10-07 12:34:16 +03:00
|
|
|
// Player collision has several more precise checks for bridge collisions.
|
|
|
|
// Therefore, we should differentiate these code paths.
|
|
|
|
bool playerCollision = item->data.is<LaraInfo*>();
|
|
|
|
|
|
|
|
// Reset out collision parameters.
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->CollisionType = CT_NONE;
|
2021-10-07 12:34:16 +03:00
|
|
|
coll->Shift.x = 0;
|
|
|
|
coll->Shift.y = 0;
|
|
|
|
coll->Shift.z = 0;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// Offset base probe position by provided offset, if any.
|
2021-09-10 12:14:28 +03:00
|
|
|
int xPos = item->pos.xPos + offset.x;
|
|
|
|
int yPos = item->pos.yPos + offset.y;
|
|
|
|
int zPos = item->pos.zPos + offset.z;
|
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// Specify base probe position, Y position being bounds top side.
|
2021-01-07 11:16:21 -03:00
|
|
|
int x = xPos;
|
2021-09-19 06:42:24 +03:00
|
|
|
int y = yPos - coll->Setup.Height;
|
2021-01-07 11:16:21 -03:00
|
|
|
int z = zPos;
|
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// Define side probe offsets.
|
2021-08-25 05:45:10 +03:00
|
|
|
int xfront, xright, xleft, zfront, zright, zleft;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-09 19:19:02 +03:00
|
|
|
// Get nearest 90-degree snapped angle (quadrant).
|
2021-10-10 12:06:11 +03:00
|
|
|
auto quadrant = GetQuadrant(coll->Setup.ForwardAngle);
|
2021-10-09 19:19:02 +03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// Get side probe offsets depending on quadrant.
|
2021-10-10 12:06:11 +03:00
|
|
|
// If unconstrained mode is specified, don't use quadrant.
|
2021-10-24 04:08:54 +03:00
|
|
|
switch (coll->Setup.Mode ? -1 : quadrant)
|
2021-01-07 11:16:21 -03:00
|
|
|
{
|
|
|
|
case 0:
|
2021-10-01 16:12:02 +03:00
|
|
|
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
|
|
|
zfront = coll->Setup.Radius;
|
2021-09-10 00:49:45 +03:00
|
|
|
xleft = -coll->Setup.Radius;
|
2021-10-01 16:12:02 +03:00
|
|
|
zleft = coll->Setup.Radius;
|
|
|
|
xright = coll->Setup.Radius;
|
|
|
|
zright = coll->Setup.Radius;
|
2021-01-07 11:16:21 -03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
2021-10-01 16:12:02 +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;
|
2021-09-10 00:49:45 +03:00
|
|
|
zright = -coll->Setup.Radius;
|
2021-01-07 11:16:21 -03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
2021-10-01 16:12:02 +03:00
|
|
|
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
2021-09-10 00:20:59 +03:00
|
|
|
zfront = -coll->Setup.Radius;
|
2021-10-01 16:12:02 +03:00
|
|
|
xleft = coll->Setup.Radius;
|
2021-09-10 00:49:45 +03:00
|
|
|
zleft = -coll->Setup.Radius;
|
|
|
|
xright = -coll->Setup.Radius;
|
|
|
|
zright = -coll->Setup.Radius;
|
2021-01-07 11:16:21 -03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
2021-10-01 16:12:02 +03:00
|
|
|
xfront = -coll->Setup.Radius;
|
|
|
|
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
2021-09-10 00:49:45 +03:00
|
|
|
xleft = -coll->Setup.Radius;
|
|
|
|
zleft = -coll->Setup.Radius;
|
|
|
|
xright = -coll->Setup.Radius;
|
2021-10-01 16:12:02 +03:00
|
|
|
zright = coll->Setup.Radius;
|
2021-01-07 11:16:21 -03:00
|
|
|
break;
|
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
default:
|
|
|
|
// No valid quadrant, return true probe offsets from object rotation.
|
|
|
|
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
|
|
|
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
2021-10-24 04:08:54 +03:00
|
|
|
xleft = (xfront * (coll->Setup.Mode == CP_FREE_FORWARD ? 0.5f : 1.0f)) + phd_sin(coll->Setup.ForwardAngle - ANGLE(90)) * coll->Setup.Radius;
|
|
|
|
zleft = (zfront * (coll->Setup.Mode == CP_FREE_FORWARD ? 0.5f : 1.0f)) + phd_cos(coll->Setup.ForwardAngle - ANGLE(90)) * coll->Setup.Radius;
|
|
|
|
xright = (xfront * (coll->Setup.Mode == CP_FREE_FORWARD ? 0.5f : 1.0f)) + phd_sin(coll->Setup.ForwardAngle + ANGLE(90)) * coll->Setup.Radius;
|
|
|
|
zright = (zfront * (coll->Setup.Mode == CP_FREE_FORWARD ? 0.5f : 1.0f)) + phd_cos(coll->Setup.ForwardAngle + ANGLE(90)) * coll->Setup.Radius;
|
2021-01-07 11:16:21 -03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// Define generic variables used for later object-specific position test shifts.
|
|
|
|
ROOM_VECTOR tfLocation{}, tcLocation{}, lrfLocation{}, lrcLocation{};
|
|
|
|
int height, ceiling;
|
|
|
|
|
|
|
|
// Parameter definition ends here, now process to actual collision tests...
|
|
|
|
|
2021-10-07 16:45:26 +03:00
|
|
|
// TEST 1: TILT AND NEAREST LEDGE CALCULATION
|
2021-10-07 12:34:16 +03:00
|
|
|
|
|
|
|
auto collResult = GetCollisionResult(x, item->pos.yPos, z, item->roomNumber);
|
|
|
|
coll->TiltX = collResult.TiltX;
|
|
|
|
coll->TiltZ = collResult.TiltZ;
|
2021-10-25 17:09:14 +03:00
|
|
|
coll->NearestLedgeAngle = GetNearestLedgeAngle(item, coll, coll->NearestLedgeDistance);
|
2021-10-07 12:34:16 +03:00
|
|
|
|
2021-10-07 18:03:17 +03:00
|
|
|
// Debug angle and distance
|
2021-10-25 17:09:14 +03:00
|
|
|
// g_Renderer.printDebugMessage("Nearest angle: %d", coll->NearestLedgeAngle);
|
|
|
|
// g_Renderer.printDebugMessage("Nearest dist: %f", coll->NearestLedgeDistance);
|
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// TEST 2: CENTERPOINT PROBE
|
|
|
|
|
|
|
|
collResult = GetCollisionResult(x, y, z, item->roomNumber);
|
|
|
|
auto topRoomNumber = collResult.RoomNumber; // Keep top room number as we need it to re-probe from origin room
|
|
|
|
|
|
|
|
if (playerCollision)
|
|
|
|
{
|
|
|
|
tfLocation = GetRoom(item->location, x, y, z);
|
|
|
|
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
tcLocation = GetRoom(item->location, x, y - item->fallspeed, z);
|
|
|
|
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
|
|
|
}
|
|
|
|
else
|
2021-01-07 11:16:21 -03:00
|
|
|
{
|
2021-10-07 12:34:16 +03:00
|
|
|
height = collResult.Position.Floor;
|
|
|
|
ceiling = GetCeiling(collResult.Block, x, y - item->fallspeed, z);
|
2021-01-07 11:16:21 -03:00
|
|
|
}
|
2021-10-07 12:34:16 +03:00
|
|
|
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
|
|
|
if (ceiling != NO_HEIGHT) ceiling -= y;
|
|
|
|
|
|
|
|
coll->Middle = collResult.Position;
|
|
|
|
coll->Middle.Floor = height;
|
2021-10-10 15:24:28 +03:00
|
|
|
coll->Middle.Ceiling = ceiling;
|
2021-10-07 12:34:16 +03:00
|
|
|
|
|
|
|
// TEST 3: FRONTAL PROBE
|
|
|
|
|
|
|
|
x = xfront + xPos;
|
|
|
|
z = zfront + zPos;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-09 05:01:39 +03:00
|
|
|
g_Renderer.addDebugSphere(Vector3(x, y, z), 32, Vector4(1, 0, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
2021-10-02 21:50:36 +03:00
|
|
|
|
2021-10-01 16:12:02 +03:00
|
|
|
collResult = GetCollisionResult(x, y, z, topRoomNumber);
|
2021-08-22 01:39:14 +03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (playerCollision)
|
|
|
|
{
|
|
|
|
if (resetRoom)
|
|
|
|
{
|
|
|
|
tfLocation = item->location;
|
|
|
|
tcLocation = item->location;
|
|
|
|
topRoomNumber = item->roomNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
tfLocation = GetRoom(tfLocation, x, y, z);
|
|
|
|
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
tcLocation = GetRoom(tcLocation, x, y - item->fallspeed, z);
|
|
|
|
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = collResult.Position.Floor;
|
|
|
|
ceiling = GetCeiling(collResult.Block, x, y - item->fallspeed, z);
|
|
|
|
}
|
|
|
|
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
|
|
|
if (ceiling != NO_HEIGHT) ceiling -= y;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-09-17 22:55:09 +03:00
|
|
|
coll->Front = collResult.Position;
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->Front.Floor = height;
|
2021-10-10 15:24:28 +03:00
|
|
|
coll->Front.Ceiling = ceiling;
|
2021-10-06 23:57:50 +03:00
|
|
|
|
2021-10-01 16:12:02 +03:00
|
|
|
collResult = GetCollisionResult(x + xfront, y, z + zfront, topRoomNumber);
|
2021-02-06 18:33:33 -06:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (playerCollision)
|
|
|
|
{
|
|
|
|
tfLocation = GetRoom(tfLocation, x + xfront, y, z + zfront);
|
|
|
|
height = GetFloorHeight(tfLocation, x + xfront, z + zfront).value_or(NO_HEIGHT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = collResult.Position.Floor;
|
|
|
|
}
|
|
|
|
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (coll->Setup.SlopesAreWalls &&
|
|
|
|
coll->Front.Slope &&
|
|
|
|
coll->Front.Floor < coll->Middle.Floor &&
|
|
|
|
coll->Front.Floor < 0 &&
|
|
|
|
height < coll->Front.Floor)
|
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-10-07 12:34:16 +03:00
|
|
|
else if (coll->Setup.SlopesArePits &&
|
|
|
|
coll->Front.Slope &&
|
|
|
|
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-10-07 12:34:16 +03:00
|
|
|
else if (coll->Setup.DeathFlagIsPit &&
|
|
|
|
coll->Front.Floor > 0 &&
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// TEST 4: MIDDLE-LEFT PROBE
|
|
|
|
|
2021-01-07 11:16:21 -03:00
|
|
|
x = xPos + xleft;
|
|
|
|
z = zPos + zleft;
|
|
|
|
|
2021-10-09 05:01:39 +03:00
|
|
|
g_Renderer.addDebugSphere(Vector3(x, y, z), 32, Vector4(0, 0, 1, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
2021-10-02 21:50:36 +03:00
|
|
|
|
2021-10-01 16:12:02 +03:00
|
|
|
collResult = GetCollisionResult(x, y, z, item->roomNumber);
|
2021-02-06 18:33:33 -06:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (playerCollision)
|
|
|
|
{
|
|
|
|
lrfLocation = GetRoom(item->location, x, y, z);
|
|
|
|
height = GetFloorHeight(lrfLocation, x, z).value_or(NO_HEIGHT);
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
lrcLocation = GetRoom(item->location, x, y - item->fallspeed, z);
|
|
|
|
ceiling = GetCeilingHeight(lrcLocation, x, z).value_or(NO_HEIGHT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = collResult.Position.Floor;
|
|
|
|
ceiling = GetCeiling(collResult.Block, x, y - item->fallspeed, z);
|
|
|
|
}
|
|
|
|
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
|
|
|
if (ceiling != NO_HEIGHT) ceiling -= y;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-09-17 22:55:09 +03:00
|
|
|
coll->MiddleLeft = collResult.Position;
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleLeft.Floor = height;
|
|
|
|
coll->MiddleLeft.Ceiling = ceiling;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (coll->Setup.SlopesAreWalls &&
|
|
|
|
coll->MiddleLeft.Slope &&
|
|
|
|
coll->MiddleLeft.Floor < 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleLeft.Floor = MAX_HEIGHT;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.SlopesArePits &&
|
|
|
|
coll->MiddleLeft.Slope &&
|
|
|
|
coll->MiddleLeft.Floor > 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleLeft.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.DeathFlagIsPit &&
|
|
|
|
coll->MiddleLeft.Floor > 0 &&
|
|
|
|
collResult.BottomBlock->Flags.Death)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleLeft.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TEST 5: FRONT-LEFT PROBE
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-01 16:12:02 +03:00
|
|
|
collResult = GetCollisionResult(x, y, z, topRoomNumber); // We use plain x/z values here, proposed by Choco
|
2021-02-06 18:33:33 -06:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (playerCollision)
|
|
|
|
{
|
|
|
|
tfLocation = GetRoom(tfLocation, x, y, z);
|
|
|
|
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
tcLocation = GetRoom(tcLocation, x, y - item->fallspeed, z);
|
|
|
|
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = collResult.Position.Floor;
|
|
|
|
ceiling = GetCeiling(collResult.Block, x, y - item->fallspeed, z);
|
|
|
|
}
|
|
|
|
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
|
|
|
if (ceiling != NO_HEIGHT) ceiling -= y;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-09-17 22:55:09 +03:00
|
|
|
coll->FrontLeft = collResult.Position;
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontLeft.Floor = height;
|
|
|
|
coll->FrontLeft.Ceiling = ceiling;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (coll->Setup.SlopesAreWalls &&
|
|
|
|
coll->FrontLeft.Slope &&
|
|
|
|
coll->FrontLeft.Floor < 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontLeft.Floor = MAX_HEIGHT;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.SlopesArePits &&
|
|
|
|
coll->FrontLeft.Slope &&
|
|
|
|
coll->FrontLeft.Floor > 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontLeft.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.DeathFlagIsPit &&
|
|
|
|
coll->FrontLeft.Floor > 0 &&
|
|
|
|
collResult.BottomBlock->Flags.Death)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontLeft.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TEST 6: MIDDLE-RIGHT PROBE
|
2021-01-07 11:16:21 -03:00
|
|
|
|
|
|
|
x = xPos + xright;
|
|
|
|
z = zPos + zright;
|
|
|
|
|
2021-10-09 05:01:39 +03:00
|
|
|
g_Renderer.addDebugSphere(Vector3(x, y, z), 32, Vector4(0, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
2021-10-02 21:50:36 +03:00
|
|
|
|
2021-10-01 16:12:02 +03:00
|
|
|
collResult = GetCollisionResult(x, y, z, item->roomNumber);
|
2021-02-06 18:33:33 -06:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (playerCollision)
|
|
|
|
{
|
|
|
|
lrfLocation = GetRoom(item->location, x, y, z);
|
|
|
|
height = GetFloorHeight(lrfLocation, x, z).value_or(NO_HEIGHT);
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
lrcLocation = GetRoom(item->location, x, y - item->fallspeed, z);
|
|
|
|
ceiling = GetCeilingHeight(lrcLocation, x, z).value_or(NO_HEIGHT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = collResult.Position.Floor;
|
|
|
|
ceiling = GetCeiling(collResult.Block, x, y - item->fallspeed, z);
|
|
|
|
}
|
|
|
|
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
|
|
|
if (ceiling != NO_HEIGHT) ceiling -= y;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-09-17 22:55:09 +03:00
|
|
|
coll->MiddleRight = collResult.Position;
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleRight.Floor = height;
|
|
|
|
coll->MiddleRight.Ceiling = ceiling;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (coll->Setup.SlopesAreWalls &&
|
|
|
|
coll->MiddleRight.Slope &&
|
|
|
|
coll->MiddleRight.Floor < 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleRight.Floor = MAX_HEIGHT;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.SlopesArePits &&
|
|
|
|
coll->MiddleRight.Slope &&
|
|
|
|
coll->MiddleRight.Floor > 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleRight.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.DeathFlagIsPit &&
|
|
|
|
coll->MiddleRight.Floor > 0 &&
|
|
|
|
collResult.BottomBlock->Flags.Death)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->MiddleRight.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TEST 7: FRONT-RIGHT PROBE
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-01 16:12:02 +03:00
|
|
|
collResult = GetCollisionResult(x, y, z, topRoomNumber);
|
2021-02-06 18:33:33 -06:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (playerCollision)
|
|
|
|
{
|
|
|
|
tfLocation = GetRoom(tfLocation, x, y, z);
|
|
|
|
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
tcLocation = GetRoom(tcLocation, x, y - item->fallspeed, z);
|
|
|
|
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = collResult.Position.Floor;
|
|
|
|
ceiling = GetCeiling(collResult.Block, x, y - item->fallspeed, z);
|
|
|
|
}
|
|
|
|
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
|
|
|
if (ceiling != NO_HEIGHT) ceiling -= y;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-09-17 22:55:09 +03:00
|
|
|
coll->FrontRight = collResult.Position;
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontRight.Floor = height;
|
|
|
|
coll->FrontRight.Ceiling = ceiling;
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
if (coll->Setup.SlopesAreWalls &&
|
|
|
|
coll->FrontRight.Slope &&
|
|
|
|
coll->FrontRight.Floor < 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontRight.Floor = MAX_HEIGHT;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.SlopesArePits &&
|
|
|
|
coll->FrontRight.Slope &&
|
|
|
|
coll->FrontRight.Floor > 0)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontRight.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
else if (coll->Setup.DeathFlagIsPit &&
|
|
|
|
coll->FrontRight.Floor > 0 &&
|
|
|
|
collResult.BottomBlock->Flags.Death)
|
|
|
|
{
|
2021-09-10 00:18:47 +03:00
|
|
|
coll->FrontRight.Floor = STOP_SIZE;
|
2021-10-07 12:34:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TEST 8: SOLID STATIC MESHES
|
2021-01-07 11:16:21 -03:00
|
|
|
|
2021-09-10 12:14:28 +03:00
|
|
|
CollideSolidStatics(item, coll);
|
2021-09-04 07:59:00 +02:00
|
|
|
|
2021-10-07 12:34:16 +03:00
|
|
|
// Collision tests now end.
|
|
|
|
// Get to calculation of collision side and shifts.
|
|
|
|
|
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-10-07 12:34:16 +03:00
|
|
|
if (coll->Front.Floor > coll->Setup.BadHeightDown ||
|
|
|
|
coll->Front.Floor < coll->Setup.BadHeightUp ||
|
|
|
|
coll->Front.Ceiling > coll->Setup.BadCeilingHeight)
|
2021-01-07 11:16:21 -03:00
|
|
|
{
|
2021-09-20 02:14:44 +03:00
|
|
|
if (coll->Front.HasDiagonalSplit())
|
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-19 17:48:32 +03:00
|
|
|
if (coll->MiddleLeft.Floor > coll->Setup.BadHeightDown ||
|
|
|
|
coll->MiddleLeft.Floor < coll->Setup.BadHeightUp ||
|
2021-09-10 00:20:59 +03:00
|
|
|
coll->MiddleLeft.Ceiling > coll->Setup.BadCeilingHeight)
|
2021-01-07 11:16:21 -03:00
|
|
|
{
|
2021-09-17 22:55:09 +03:00
|
|
|
if (coll->TriangleAtLeft() && !coll->MiddleLeft.Slope)
|
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-17 22:55:09 +03:00
|
|
|
|
|
|
|
if (coll->DiagonalStepAtLeft())
|
|
|
|
{
|
|
|
|
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
|
|
|
|
quarter %= 2;
|
|
|
|
|
2021-09-20 02:14:44 +03:00
|
|
|
if (coll->MiddleLeft.HasFlippedDiagonalSplit())
|
2021-09-17 22:55:09 +03:00
|
|
|
{
|
|
|
|
if (quarter) coll->CollisionType = CT_LEFT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!quarter) coll->CollisionType = CT_LEFT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
coll->CollisionType = CT_LEFT;
|
|
|
|
}
|
|
|
|
|
2021-01-07 11:16:21 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-19 17:48:32 +03:00
|
|
|
if (coll->MiddleRight.Floor > coll->Setup.BadHeightDown ||
|
|
|
|
coll->MiddleRight.Floor < coll->Setup.BadHeightUp ||
|
2021-09-10 00:20:59 +03:00
|
|
|
coll->MiddleRight.Ceiling > coll->Setup.BadCeilingHeight)
|
2021-01-07 11:16:21 -03:00
|
|
|
{
|
2021-09-17 22:55:09 +03:00
|
|
|
if (coll->TriangleAtRight() && !coll->MiddleRight.Slope)
|
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-17 22:55:09 +03:00
|
|
|
|
|
|
|
if (coll->DiagonalStepAtRight())
|
|
|
|
{
|
|
|
|
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
|
|
|
|
quarter %= 2;
|
|
|
|
|
2021-09-20 02:14:44 +03:00
|
|
|
if (coll->MiddleRight.HasFlippedDiagonalSplit())
|
2021-09-17 22:55:09 +03:00
|
|
|
{
|
|
|
|
if (quarter) coll->CollisionType = CT_RIGHT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!quarter) coll->CollisionType = CT_RIGHT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
coll->CollisionType = CT_RIGHT;
|
|
|
|
}
|
|
|
|
|
2021-01-07 11:16:21 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-02 22:40:33 +03:00
|
|
|
void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv) // previously DoProperDetection
|
2021-08-26 12:37:46 +03:00
|
|
|
{
|
|
|
|
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
|
|
|
|
2021-09-10 00:43:26 +03:00
|
|
|
if (item->pos.yPos >= collResult.Position.Floor)
|
2021-08-26 12:37:46 +03:00
|
|
|
{
|
|
|
|
bs = 0;
|
|
|
|
|
2021-09-17 22:55:09 +03:00
|
|
|
if (collResult.Position.Slope && oldCollResult.Position.Floor < collResult.Position.Floor)
|
2021-08-26 12:37:46 +03:00
|
|
|
{
|
|
|
|
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 */
|
|
|
|
|
2021-09-10 00:43:26 +03:00
|
|
|
if (y > (collResult.Position.Floor + 32) && bs == 0 &&
|
2021-08-26 12:37:46 +03:00
|
|
|
(((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;
|
|
|
|
}
|
2021-09-17 22:55:09 +03:00
|
|
|
else if (collResult.Position.Slope) // Hit a steep slope?
|
2021-08-26 12:37:46 +03:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2021-09-10 00:43:26 +03:00
|
|
|
item->pos.yPos = collResult.Position.Floor;
|
2021-08-26 12:37:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
|
2021-09-10 00:43:26 +03:00
|
|
|
if (item->pos.yPos >= oldCollResult.Position.Floor)
|
2021-08-26 12:37:46 +03:00
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
2021-09-10 00:43:26 +03:00
|
|
|
item->pos.yPos = oldCollResult.Position.Floor;
|
2021-08-26 12:37:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// else
|
|
|
|
{
|
|
|
|
/* Bounce off ceiling */
|
|
|
|
collResult = GetCollisionResult(item);
|
|
|
|
|
2021-09-10 00:43:26 +03:00
|
|
|
if (item->pos.yPos < collResult.Position.Ceiling)
|
2021-08-26 12:37:46 +03:00
|
|
|
{
|
2021-09-10 00:43:26 +03:00
|
|
|
if (y < collResult.Position.Ceiling &&
|
2021-08-26 12:37:46 +03:00
|
|
|
(((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
|
2021-09-10 00:43:26 +03:00
|
|
|
item->pos.yPos = collResult.Position.Ceiling;
|
2021-08-26 12:37:46 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-09-02 22:40:33 +03:00
|
|
|
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];
|
|
|
|
|
2021-09-08 14:02:32 +03:00
|
|
|
// 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-10 13:49:45 +03:00
|
|
|
int x = abs(l->pos.xPos - mesh->pos.xPos);
|
|
|
|
int y = abs(l->pos.yPos - mesh->pos.yPos);
|
|
|
|
int z = abs(l->pos.zPos - mesh->pos.zPos);
|
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
|
|
|
{
|
2021-06-04 06:02:19 +02: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;
|
2021-06-04 06:02:19 +02:00
|
|
|
|
2021-09-17 06:01:15 +02:00
|
|
|
int deadlyBits = *((int*)&item->itemFlags[0]);
|
2020-12-21 13:16:29 -03:00
|
|
|
SPHERE* sphere = &CreatureSpheres[0];
|
2021-06-04 06:02:19 +02:00
|
|
|
|
|
|
|
if (item->itemFlags[2] != 0)
|
|
|
|
{
|
|
|
|
collidedBits &= ~1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (collidedBits)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-06-04 06:02:19 +02: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-20 01:11:22 +03:00
|
|
|
if (ItemPushItem(item, l, coll, deadlyBits & 1, 3) && (deadlyBits & 1))
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
l->hitPoints -= item->itemFlags[3];
|
2021-06-04 06:02:19 +02:00
|
|
|
|
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;
|
|
|
|
|
2021-06-04 06:02:19 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-04 06:02:19 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-08-26 15:54:52 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-10-07 01:24:45 +03:00
|
|
|
// Determines vertical surfaces and gets nearest ledge angle.
|
|
|
|
// Allows to eventually use unconstrained vaults and shimmying.
|
|
|
|
|
2021-10-07 17:11:01 +03:00
|
|
|
short GetNearestLedgeAngle(ITEM_INFO* item, COLL_INFO* coll, float& dist)
|
2021-10-06 20:54:15 +03:00
|
|
|
{
|
2021-10-08 14:48:34 +03:00
|
|
|
// Get item bounds and current rotation
|
|
|
|
auto bounds = GetBoundsAccurate(item);
|
2021-10-07 16:10:02 +03:00
|
|
|
auto c = phd_cos(coll->Setup.ForwardAngle);
|
|
|
|
auto s = phd_sin(coll->Setup.ForwardAngle);
|
|
|
|
|
|
|
|
// Origin test position should be slightly in front of origin, because otherwise
|
|
|
|
// misfire may occur near block corners for split angles.
|
2021-10-25 17:09:14 +03:00
|
|
|
auto frontalOffset = coll->Setup.Radius * 0.1f;
|
2021-10-22 13:47:19 +03:00
|
|
|
auto x = item->pos.xPos + frontalOffset * s;
|
|
|
|
auto z = item->pos.zPos + frontalOffset * c;
|
2021-10-07 15:42:49 +03:00
|
|
|
|
2021-10-08 13:58:51 +03:00
|
|
|
// Determine two Y points to test (lower and higher).
|
|
|
|
// 1/10 headroom crop is needed to avoid possible issues with tight diagonal headrooms.
|
2021-10-08 14:48:34 +03:00
|
|
|
int headroom = abs(bounds->Y2 - bounds->Y1) / 20.0f;
|
|
|
|
int yPoints[2] = { item->pos.yPos + bounds->Y1 + headroom,
|
|
|
|
item->pos.yPos + bounds->Y2 - headroom };
|
2021-10-08 13:58:51 +03:00
|
|
|
|
|
|
|
// Prepare test data
|
2021-10-25 17:09:14 +03:00
|
|
|
float finalDistance[2] = { FLT_MAX, FLT_MAX };
|
|
|
|
short finalResult[2] = { 0 };
|
|
|
|
bool hitBridge = false;
|
|
|
|
|
2021-10-08 14:48:34 +03:00
|
|
|
// Do a two-pass surface test for all possible planes in a block.
|
|
|
|
// Two-pass test is needed to resolve different scissor cases with diagonal geometry.
|
2021-10-07 15:42:49 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
for (int h = 0; h < 2; h++)
|
2021-10-08 13:58:51 +03:00
|
|
|
{
|
|
|
|
// Use either bottom or top Y point to test
|
2021-10-25 17:09:14 +03:00
|
|
|
auto y = yPoints[h];
|
2021-10-07 18:03:17 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Prepare test data
|
|
|
|
Ray originRay;
|
|
|
|
Plane closestPlane[3] = { };
|
|
|
|
float closestDistance[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
|
|
|
|
short result[3] = { };
|
2021-10-07 18:03:17 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// If bridge was hit on the first pass, stop checking
|
|
|
|
if (h == 1 && hitBridge)
|
|
|
|
break;
|
2021-10-06 20:54:15 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
for (int p = 0; p < 3; p++)
|
|
|
|
{
|
|
|
|
// Prepare test data
|
|
|
|
float distance = 0.0f;
|
2021-10-07 10:08:25 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Determine horizontal probe coordinates
|
|
|
|
int eX = x;
|
|
|
|
int eZ = z;
|
2021-10-06 23:57:50 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Determine if probe must be shifted (if left or right probe)
|
|
|
|
if (p > 0)
|
|
|
|
{
|
|
|
|
auto s2 = phd_sin(coll->Setup.ForwardAngle + (p == 1 ? ANGLE(90) : ANGLE(-90)));
|
|
|
|
auto c2 = phd_cos(coll->Setup.ForwardAngle + (p == 1 ? ANGLE(90) : ANGLE(-90)));
|
2021-10-06 23:26:46 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Slightly extend width beyond coll radius to hit adjacent blocks for sure
|
|
|
|
eX += s2 * (coll->Setup.Radius * 1.7f);
|
|
|
|
eZ += c2 * (coll->Setup.Radius * 1.7f);
|
|
|
|
}
|
2021-10-06 23:57:50 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Debug probe point
|
|
|
|
// g_Renderer.addDebugSphere(Vector3(eX, y, eZ), 16, Vector4(1, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
2021-10-08 13:58:51 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Determine floor probe offset.
|
|
|
|
// This must be in front of own coll radius so no bridge misfires occur.
|
2021-10-25 18:28:50 +03:00
|
|
|
auto floorProbeOffset = coll->Setup.Radius * 0.5f;
|
|
|
|
auto fpX = eX + floorProbeOffset * s;
|
|
|
|
auto fpZ = eZ + floorProbeOffset * c;
|
|
|
|
|
|
|
|
// Debug probe point
|
|
|
|
// g_Renderer.addDebugSphere(Vector3(fpX, y, fpZ), 16, Vector4(0, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
2021-10-08 13:58:51 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Get true room number and block, based on current Y position
|
|
|
|
auto room = GetRoom(item->location, fpX, y, fpZ).roomNumber;
|
|
|
|
auto block = GetCollisionResult(fpX, y, fpZ, room).Block;
|
2021-10-08 14:48:34 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Get native surface heights
|
|
|
|
auto floorHeight = block->FloorHeight(fpX, fpZ, y);
|
|
|
|
auto ceilingHeight = block->CeilingHeight(fpX, fpZ, y);
|
2021-10-06 23:57:50 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// If ceiling height tests lower than Y value, it means ceiling
|
|
|
|
// ledge is in front and we should use it instead of floor.
|
|
|
|
bool useCeilingLedge = ceilingHeight > y;
|
|
|
|
int height = useCeilingLedge ? ceilingHeight : floorHeight;
|
2021-10-07 18:03:17 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Determine if there is a bridge in front
|
2021-10-25 18:28:50 +03:00
|
|
|
auto bridge = block->InsideBridge(fpX, fpZ, height + 4, false, y == height); // Submerge 1 unit to detect possible bridge
|
2021-10-08 14:48:34 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// We don't need actual corner heights to build planes, so just use normalized value here
|
|
|
|
auto fY = height - 1;
|
|
|
|
auto cY = height + 1;
|
2021-10-08 14:48:34 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Calculate ray
|
|
|
|
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(coll->Setup.ForwardAngle), 0, 0);
|
|
|
|
auto direction = (Matrix::CreateTranslation(Vector3::UnitZ) * mxR).Translation();
|
|
|
|
auto ray = Ray(Vector3(eX, cY, eZ), direction);
|
2021-10-06 20:54:15 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Debug ray direction
|
|
|
|
// g_Renderer.addLine3D(Vector3(eX, y, eZ), Vector3(eX, y, eZ) + direction * 256, Vector4(1, 0, 0, 1));
|
2021-10-06 23:26:46 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Keep origin ray
|
|
|
|
if (p == 0)
|
|
|
|
originRay = Ray(Vector3(eX, cY, eZ), direction);
|
2021-10-06 23:57:50 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
if (bridge >= 0) // Surface is inside bridge
|
2021-10-06 20:54:15 +03:00
|
|
|
{
|
2021-10-25 17:09:14 +03:00
|
|
|
// Get and test DX item coll bounds
|
|
|
|
auto bounds = GetBoundsAccurate(&g_Level.Items[bridge]);
|
|
|
|
auto dxBounds = TO_DX_BBOX(g_Level.Items[bridge].pos, bounds);
|
|
|
|
|
|
|
|
// Decompose bounds into planes
|
|
|
|
Vector3 corners[8];
|
|
|
|
dxBounds.GetCorners(corners);
|
|
|
|
Plane plane[4] =
|
|
|
|
{
|
|
|
|
Plane(corners[2], corners[1], corners[0]),
|
|
|
|
Plane(corners[0], corners[4], corners[3]),
|
|
|
|
Plane(corners[5], corners[6], corners[7]),
|
|
|
|
Plane(corners[6], corners[5], corners[1])
|
|
|
|
};
|
|
|
|
|
|
|
|
// Find closest bridge edge plane
|
|
|
|
for (int i = 0; i < 4; i++)
|
2021-10-08 13:58:51 +03:00
|
|
|
{
|
2021-10-25 17:09:14 +03:00
|
|
|
// No plane intersection, quickly discard
|
|
|
|
if (!ray.Intersects(plane[i], distance))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Process plane intersection only if distance is smaller
|
|
|
|
// than already found minimum
|
|
|
|
if (distance < closestDistance[p])
|
|
|
|
{
|
|
|
|
closestPlane[p] = plane[i];
|
|
|
|
closestDistance[p] = distance;
|
|
|
|
auto normal = closestPlane[p].Normal();
|
|
|
|
result[p] = FROM_RAD(atan2(normal.x, normal.z));
|
|
|
|
hitBridge = true;
|
|
|
|
}
|
2021-10-08 13:58:51 +03:00
|
|
|
}
|
2021-10-06 20:54:15 +03:00
|
|
|
}
|
2021-10-25 17:09:14 +03:00
|
|
|
else // Surface is inside block
|
|
|
|
{
|
|
|
|
// Determine if we should use floor or ceiling split angle based on early tests.
|
|
|
|
auto splitAngle = (useCeilingLedge ? block->CeilingCollision.SplitAngle : block->FloorCollision.SplitAngle);
|
|
|
|
|
|
|
|
// Get horizontal block corner coordinates
|
|
|
|
auto fX = floor(eX / WALL_SIZE) * WALL_SIZE - 1;
|
|
|
|
auto fZ = floor(eZ / WALL_SIZE) * WALL_SIZE - 1;
|
|
|
|
auto cX = fX + WALL_SIZE + 1;
|
|
|
|
auto cZ = fZ + WALL_SIZE + 1;
|
|
|
|
|
|
|
|
// Debug used block
|
|
|
|
// g_Renderer.addDebugSphere(Vector3(round(eX / WALL_SIZE) * WALL_SIZE + 512, y, round(eZ / WALL_SIZE) * WALL_SIZE + 512), 16, Vector4(1, 1, 1, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
|
|
|
|
|
|
|
// Get split angle coordinates
|
|
|
|
auto sX = fX + 1 + WALL_SIZE / 2;
|
|
|
|
auto sZ = fZ + 1 + WALL_SIZE / 2;
|
|
|
|
auto sShiftX = coll->Setup.Radius * sin(splitAngle);
|
|
|
|
auto sShiftZ = coll->Setup.Radius * cos(splitAngle);
|
|
|
|
|
|
|
|
// Get block edge planes + split angle plane
|
|
|
|
Plane plane[5] =
|
|
|
|
{
|
|
|
|
Plane(Vector3(fX, cY, cZ), Vector3(cX, cY, cZ), Vector3(cX, fY, fZ)), // North
|
|
|
|
Plane(Vector3(fX, cY, fZ), Vector3(fX, cY, cZ), Vector3(fX, fY, cZ)), // West
|
|
|
|
Plane(Vector3(cX, fY, fZ), Vector3(cX, cY, fZ), Vector3(fX, cY, fZ)), // South
|
|
|
|
Plane(Vector3(cX, fY, cZ), Vector3(cX, cY, cZ), Vector3(cX, cY, fZ)), // East
|
|
|
|
Plane(Vector3(sX, cY, sZ), Vector3(sX, fY, sZ), Vector3(sX + sShiftX, cY, sZ + sShiftZ)) // Split
|
|
|
|
};
|
|
|
|
|
|
|
|
// If split angle exists, take split plane into account too.
|
|
|
|
auto useSplitAngle = (useCeilingLedge ? block->CeilingIsSplit() : block->FloorIsSplit());
|
|
|
|
|
|
|
|
// Find closest block edge plane
|
|
|
|
for (int i = 0; i < (useSplitAngle ? 5 : 4); i++)
|
|
|
|
{
|
|
|
|
// No plane intersection, quickly discard
|
|
|
|
if (!ray.Intersects(plane[i], distance))
|
|
|
|
continue;
|
2021-10-06 23:26:46 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Process plane intersection only if distance is smaller
|
|
|
|
// than already found minimum
|
|
|
|
if (distance < closestDistance[p])
|
|
|
|
{
|
|
|
|
closestDistance[p] = distance;
|
|
|
|
closestPlane[p] = plane[i];
|
2021-10-07 01:24:45 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Store according rotation.
|
|
|
|
// For block edges (cases 0-3), return ordinary normal values.
|
|
|
|
// For split angle (case 4), return axis perpendicular to split angle (hence + ANGLE(90)) and dependent on
|
|
|
|
// origin sector plane, which determines the direction of edge normal.
|
2021-10-07 17:11:01 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
if (i == 4)
|
|
|
|
{
|
|
|
|
auto usedSectorPlane = useCeilingLedge ? block->SectorPlaneCeiling(eX, eZ) : block->SectorPlane(eX, eZ);
|
|
|
|
result[p] = FROM_RAD(splitAngle) + ANGLE(usedSectorPlane * 180.0f) + ANGLE(90);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto normal = closestPlane[p].Normal();
|
|
|
|
result[p] = FROM_RAD(atan2(normal.x, normal.z)) + ANGLE(180.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-08 13:58:51 +03:00
|
|
|
}
|
2021-10-25 17:09:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compare all 3 probe results and prioritize resulting angle based on
|
|
|
|
// angle occurence. This approach is needed to filter out false detections
|
|
|
|
// on the near-zero thickness edges of diagonal geometry which probes tend to tunnel through.
|
|
|
|
|
|
|
|
std::set<short> angles;
|
2021-10-25 18:28:50 +03:00
|
|
|
int firstEqualAngle = -1;
|
2021-10-25 17:09:14 +03:00
|
|
|
for (int p = 0; p < 3; p++)
|
|
|
|
{
|
|
|
|
// Prioritize ledge angle which was twice recognized
|
|
|
|
if (!angles.insert(result[p]).second)
|
2021-10-08 13:58:51 +03:00
|
|
|
{
|
2021-10-25 18:28:50 +03:00
|
|
|
// Remember distance to the closest plane with same angle (it happens sometimes with bridges)
|
|
|
|
float dist1, dist2;
|
|
|
|
originRay.Intersects(closestPlane[p], dist1);
|
|
|
|
originRay.Intersects(closestPlane[firstEqualAngle], dist2);
|
|
|
|
finalDistance[h] = (dist1 > dist2) ? dist2 : dist1;
|
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
finalResult[h] = result[p];
|
|
|
|
break;
|
2021-10-08 13:58:51 +03:00
|
|
|
}
|
2021-10-25 18:28:50 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
firstEqualAngle = p;
|
|
|
|
}
|
2021-10-07 16:17:50 +03:00
|
|
|
}
|
2021-10-25 17:09:14 +03:00
|
|
|
|
|
|
|
// Store first result in case all 3 results are different (no priority)
|
|
|
|
if (finalDistance[h] == FLT_MAX)
|
|
|
|
{
|
|
|
|
finalDistance[h] = closestDistance[0];
|
|
|
|
finalResult[h] = result[0];
|
|
|
|
}
|
2021-10-06 20:54:15 +03:00
|
|
|
}
|
2021-10-08 13:58:51 +03:00
|
|
|
|
2021-10-25 17:09:14 +03:00
|
|
|
// Return upper probe result in case it returned lower distance or has hit a bridge.
|
2021-10-25 18:28:50 +03:00
|
|
|
auto usedProbe = ((finalDistance[0] < finalDistance[1]) || hitBridge) ? 0 : 1;
|
2021-10-25 17:09:14 +03:00
|
|
|
dist = finalDistance[usedProbe] - (coll->Setup.Radius - frontalOffset);
|
|
|
|
return finalResult[usedProbe];
|
2021-09-04 07:59:00 +02:00
|
|
|
}
|