Vertical pole fix (#707)

* Partially revise vertical pole detection
* Refine vertical pole mount logic
* Cleanup
* Cleanup
* Partially fix grabbing vertical poles while airborne
* Dump a maths function from another branch
* Use const argument
* Fix wrong sphere collisions with vertical poles in edge cases
* Cleanup
* Update vector struct methods
* Fix Lara alignment on narrow distances
* Revert removal of static keyword; update IsPointInFront()

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
This commit is contained in:
Sezz 2022-08-30 23:59:19 +10:00 committed by GitHub
parent 2b0e019963
commit b8fd52554d
12 changed files with 330 additions and 251 deletions

View file

@ -50,7 +50,7 @@ constexpr int LARA_RADIUS = 100;
constexpr int LARA_RADIUS_CRAWL = 200;
constexpr int LARA_RADIUS_UNDERWATER = 300;
constexpr int LARA_RADIUS_DEATH = 400;
constexpr int LARA_VELOCITY = 12; // TODO: Float.
constexpr int LARA_ALIGN_VELOCITY = 12; // TODO: Float.
constexpr auto LARA_FREEFALL_VELOCITY = 131.0f;
constexpr auto LARA_DAMAGE_VELOCITY = 141.0f;

View file

@ -562,9 +562,9 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
void AlignLaraToSurface(ItemInfo* item, float alpha)
{
auto probe = GetCollision(item);
short aspectAngle = GetSurfaceAspectAngle(probe.FloorTilt);
short steepnessAngle = std::min(GetSurfaceSteepnessAngle(probe.FloorTilt), ANGLE(70.0f));
auto floorTilt = GetCollision(item).FloorTilt;
short aspectAngle = GetSurfaceAspectAngle(floorTilt);
short steepnessAngle = std::min(GetSurfaceSteepnessAngle(floorTilt), ANGLE(70.0f));
short deltaAngle = GetShortestAngularDistance(item->Pose.Orientation.y, aspectAngle);
float sinDeltaAngle = phd_sin(deltaAngle);

View file

@ -1113,22 +1113,22 @@ void GetTightropeFallOff(ItemInfo* item, int regularity)
#endif
// TODO: Organise all of this properly. -- Sezz 2022.07.28
bool CheckLaraState(LaraState state, std::vector<LaraState> stateList)
bool CheckLaraState(LaraState referenceState, const std::vector<LaraState>& stateList)
{
for (auto listedState : stateList)
for (auto& state : stateList)
{
if (state == listedState)
if (state == referenceState)
return true;
}
return false;
}
bool CheckLaraWeaponType(LaraWeaponType weaponType, std::vector<LaraWeaponType> weaponTypeList)
bool CheckLaraWeaponType(LaraWeaponType referenceWeaponType, const std::vector<LaraWeaponType>& weaponTypeList)
{
for (auto listedWeaponType : weaponTypeList)
for (auto& weaponType : weaponTypeList)
{
if (weaponType == listedWeaponType)
if (weaponType == referenceWeaponType)
return true;
}
@ -2519,25 +2519,33 @@ bool TestLaraTightropeDismount(ItemInfo* item, CollisionInfo* coll)
return false;
}
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool up, float offset)
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, float offset)
{
static constexpr auto poleProbeCollRadius = 16.0f;
bool atLeastOnePoleCollided = false;
if (GetCollidedObjects(item, SECTOR(1), true, CollidedItems, nullptr, 0) && CollidedItems[0])
if (GetCollidedObjects(item, SECTOR(1), true, CollidedItems, nullptr, false) &&
CollidedItems[0] != nullptr)
{
auto laraBox = TO_DX_BBOX(item->Pose, GetBoundsAccurate(item));
// HACK: because Core implemented upward pole movement as SetPosition command, we can't precisely
// HACK: Because Core implemented upward pole movement as a SetPosition command, we can't precisely
// check her position. So we add a fixed height offset.
auto sphere = BoundingSphere(laraBox.Center + Vector3(0, (laraBox.Extents.y + poleProbeCollRadius + offset) * (up ? -1 : 1), 0), poleProbeCollRadius);
// Offset a sphere when jumping toward pole.
auto sphereOffset2D = Vector3::Zero;
sphereOffset2D = TranslateVector(sphereOffset2D, item->Pose.Orientation.y, coll->Setup.Radius + item->Animation.Velocity.z);
auto spherePos = laraBox.Center + Vector3(0.0f, (laraBox.Extents.y + poleProbeCollRadius + offset) * (goingUp ? -1 : 1), 0.0f);
auto sphere = BoundingSphere(spherePos, poleProbeCollRadius);
auto offsetSphere = BoundingSphere(spherePos + sphereOffset2D, poleProbeCollRadius);
//g_Renderer.AddDebugSphere(sphere.Center, 16.0f, Vector4(1, 0, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
int i = 0;
while (CollidedItems[i] != NULL)
while (CollidedItems[i] != nullptr)
{
auto*& object = CollidedItems[i];
i++;
@ -2546,11 +2554,11 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool up, float o
continue;
auto poleBox = TO_DX_BBOX(object->Pose, GetBoundsAccurate(object));
poleBox.Extents = poleBox.Extents + Vector3(coll->Setup.Radius, 0, coll->Setup.Radius);
poleBox.Extents = poleBox.Extents + Vector3(coll->Setup.Radius, 0.0f, coll->Setup.Radius);
//g_Renderer.AddDebugBox(poleBox, Vector4(0, 0, 1, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
if (poleBox.Intersects(sphere))
if (poleBox.Intersects(sphere) || poleBox.Intersects(offsetSphere))
{
atLeastOnePoleCollided = true;
break;

View file

@ -46,7 +46,8 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll);
void GetTightropeFallOff(ItemInfo* item, int regularity);
#endif
bool CheckLaraState(LaraState state, std::vector<LaraState> stateList);
bool CheckLaraState(LaraState referenceState, const std::vector<LaraState>& stateList);
bool CheckLaraWeaponType(LaraWeaponType referenceWeaponType, const std::vector<LaraWeaponType>& weaponTypeList);
bool IsStandingWeapon(ItemInfo* item, LaraWeaponType weaponType);
bool IsVaultState(LaraState state);
bool IsJumpState(LaraState state);
@ -123,6 +124,6 @@ bool TestLaraCrawlspaceDive(ItemInfo* item, CollisionInfo* coll);
bool TestLaraTightropeDismount(ItemInfo* item, CollisionInfo* coll);
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool up, float offset = 0.0f);
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, float offset = 0.0f);
bool TestLaraPoleUp(ItemInfo* item, CollisionInfo* coll);
bool TestLaraPoleDown(ItemInfo* item, CollisionInfo* coll);

View file

@ -3,22 +3,23 @@
#include "Game/animation.h"
#include "Game/control/los.h"
#include "Game/collision/sphere.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/sphere.h"
#include "Game/effects/debris.h"
#include "Game/effects/effects.h"
#include "Game/effects/simple_particle.h"
#include "Game/effects/tomb4fx.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/items.h"
#include "Game/effects/effects.h"
#include "Game/effects/debris.h"
#include "Game/effects/tomb4fx.h"
#include "Game/effects/simple_particle.h"
#include "Game/pickup/pickup.h"
#include "Game/room.h"
#include "Renderer/Renderer11.h"
#include "Sound/sound.h"
#include "Specific/trmath.h"
#include "Specific/prng.h"
#include "ScriptInterfaceGame.h"
#include "Sound/sound.h"
#include "Specific/prng.h"
#include "Specific/setup.h"
#include "Specific/trmath.h"
using namespace TEN::Math::Random;
using namespace TEN::Renderer;
@ -27,6 +28,8 @@ BOUNDING_BOX GlobalCollisionBounds;
ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS];
MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS];
constexpr auto ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD = 6;
void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{
auto* item = &g_Level.Items[itemNumber];
@ -38,11 +41,11 @@ void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionIn
int collidedBits = TestCollision(item, laraItem);
if (collidedBits)
{
short oldYOrient = item->Pose.Orientation.y;
short prevYOrient = item->Pose.Orientation.y;
item->Pose.Orientation.y = 0;
GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
item->Pose.Orientation.y = oldYOrient;
item->Pose.Orientation.y = prevYOrient;
int deadlyBits = *((int*)&item->ItemFlags[0]);
auto* sphere = &CreatureSpheres[0];
@ -117,21 +120,22 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
continue;
if (collidingItem->Pose.Position.y + radius + CLICK(0.5f) < mesh->pos.Position.y + bbox->Y1)
if ((collidingItem->Pose.Position.y + radius + CLICK(0.5f)) < (mesh->pos.Position.y + bbox->Y1))
continue;
if (collidingItem->Pose.Position.y > mesh->pos.Position.y + bbox->Y2)
if (collidingItem->Pose.Position.y > (mesh->pos.Position.y + bbox->Y2))
continue;
float s = phd_sin(mesh->pos.Orientation.y);
float c = phd_cos(mesh->pos.Orientation.y);
float rx = (collidingItem->Pose.Position.x - mesh->pos.Position.x) * c - s * (collidingItem->Pose.Position.z - mesh->pos.Position.z);
float rz = (collidingItem->Pose.Position.z - mesh->pos.Position.z) * c + s * (collidingItem->Pose.Position.x - mesh->pos.Position.x);
float sinY = phd_sin(mesh->pos.Orientation.y);
float cosY = phd_cos(mesh->pos.Orientation.y);
if (radius + rx + CLICK(0.5f) < bbox->X1 || rx - radius - CLICK(0.5f) > bbox->X2)
float rx = ((collidingItem->Pose.Position.x - mesh->pos.Position.x) * cosY) - ((collidingItem->Pose.Position.z - mesh->pos.Position.z) * sinY);
float rz = ((collidingItem->Pose.Position.z - mesh->pos.Position.z) * cosY) + ((collidingItem->Pose.Position.x - mesh->pos.Position.x) * sinY);
if ((radius + rx + CLICK(0.5f) < bbox->X1) || (rx - radius - CLICK(0.5f) > bbox->X2))
continue;
if (radius + rz + CLICK(0.5f) < bbox->Z1 || rz - radius - CLICK(0.5f) > bbox->Z2)
if ((radius + rz + CLICK(0.5f) < bbox->Z1) || (rz - radius - CLICK(0.5f) > bbox->Z2))
continue;
collidedMeshes[numMeshes++] = mesh;
@ -159,8 +163,8 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
item->ObjectNumber == ID_LARA && ignoreLara ||
item->Flags & 0x8000 ||
item->MeshBits == NO_JOINT_BITS ||
(Objects[item->ObjectNumber].drawRoutine == NULL && item->ObjectNumber != ID_LARA) ||
(Objects[item->ObjectNumber].collision == NULL && item->ObjectNumber != ID_LARA) ||
(Objects[item->ObjectNumber].drawRoutine == nullptr && item->ObjectNumber != ID_LARA) ||
(Objects[item->ObjectNumber].collision == nullptr && item->ObjectNumber != ID_LARA) ||
onlyVisible && item->Status == ITEM_INVISIBLE ||
item->ObjectNumber == ID_BURNING_FLOOR)
{
@ -190,14 +194,14 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
if (dx >= -SECTOR(2) && dx <= SECTOR(2) &&
dy >= -SECTOR(2) && dy <= SECTOR(2) &&
dz >= -SECTOR(2) && dz <= SECTOR(2) &&
collidingItem->Pose.Position.y + radius + CLICK(0.5f) >= item->Pose.Position.y + framePtr->boundingBox.Y1 &&
collidingItem->Pose.Position.y - radius - CLICK(0.5f) <= item->Pose.Position.y + framePtr->boundingBox.Y2)
(collidingItem->Pose.Position.y + radius + CLICK(0.5f)) >= (item->Pose.Position.y + framePtr->boundingBox.Y1) &&
(collidingItem->Pose.Position.y - radius - CLICK(0.5f)) <= (item->Pose.Position.y + framePtr->boundingBox.Y2))
{
float s = phd_sin(item->Pose.Orientation.y);
float c = phd_cos(item->Pose.Orientation.y);
float sinY = phd_sin(item->Pose.Orientation.y);
float cosY = phd_cos(item->Pose.Orientation.y);
int rx = dx * c - s * dz;
int rz = dz * c + s * dx;
int rx = (dx * cosY) - (dz * sinY);
int rz = (dz * cosY) + (dx * sinY);
if (item->ObjectNumber == ID_TURN_SWITCH)
{
@ -207,36 +211,39 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
framePtr->boundingBox.Z1 = CLICK(1);
}
if (radius + rx + CLICK(0.5f) >= framePtr->boundingBox.X1 && rx - radius - CLICK(0.5f) <= framePtr->boundingBox.X2)
if ((radius + rx + CLICK(0.5f)) >= framePtr->boundingBox.X1 &&
(rx - radius - CLICK(0.5f)) <= framePtr->boundingBox.X2)
{
if (radius + rz + CLICK(0.5f) >= framePtr->boundingBox.Z1 && rz - radius - CLICK(0.5f) <= framePtr->boundingBox.Z2)
if ((radius + rz + CLICK(0.5f)) >= framePtr->boundingBox.Z1 &&
(rz - radius - CLICK(0.5f)) <= framePtr->boundingBox.Z2)
{
collidedItems[numItems++] = item;
}
}
else
{
if (collidingItem->Pose.Position.y + radius + 128 >= item->Pose.Position.y + framePtr->boundingBox.Y1 &&
collidingItem->Pose.Position.y - radius - 128 <= item->Pose.Position.y + framePtr->boundingBox.Y2)
if ((collidingItem->Pose.Position.y + radius + CLICK(0.5f)) >= (item->Pose.Position.y + framePtr->boundingBox.Y1) &&
(collidingItem->Pose.Position.y - radius - CLICK(0.5f)) <= (item->Pose.Position.y + framePtr->boundingBox.Y2))
{
float s = phd_sin(item->Pose.Orientation.y);
float c = phd_cos(item->Pose.Orientation.y);
float sinY = phd_sin(item->Pose.Orientation.y);
float cosY = phd_cos(item->Pose.Orientation.y);
int rx = dx * c - s * dz;
int rz = dz * c + s * dx;
int rx = (dx * cosY) - (dz * sinY);
int rz = (dz * cosY) + (dx * sinY);
if (item->ObjectNumber == ID_TURN_SWITCH)
{
framePtr->boundingBox.X1 = -256;
framePtr->boundingBox.X2 = 256;
framePtr->boundingBox.Z1 = -256;
framePtr->boundingBox.Z1 = 256;
framePtr->boundingBox.X1 = -CLICK(1);
framePtr->boundingBox.X2 = CLICK(1);
framePtr->boundingBox.Z1 = -CLICK(1);
framePtr->boundingBox.Z1 = CLICK(1);
}
if (radius + rx + 128 >= framePtr->boundingBox.X1 && rx - radius - 128 <= framePtr->boundingBox.X2)
if ((radius + rx + CLICK(0.5f)) >= framePtr->boundingBox.X1 &&
(rx - radius - CLICK(0.5f)) <= framePtr->boundingBox.X2)
{
if (radius + rz + 128 >= framePtr->boundingBox.Z1 && rz - radius - 128 <= framePtr->boundingBox.Z2)
if ((radius + rz + CLICK(0.5f)) >= framePtr->boundingBox.Z1 &&
(rz - radius - CLICK(0.5f)) <= framePtr->boundingBox.Z2)
{
collidedItems[numItems++] = item;
@ -249,10 +256,11 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
}
itemNumber = item->NextItem;
} while (itemNumber != NO_ITEM);
}
while (itemNumber != NO_ITEM);
}
collidedItems[numItems] = NULL;
collidedItems[numItems] = nullptr;
}
}
@ -269,20 +277,22 @@ bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, Collision
if (item->Pose.Position.y + GlobalCollisionBounds.Y1 >= framePtr->boundingBox.Y2)
return false;
float s = phd_sin(item->Pose.Orientation.y);
float c = phd_cos(item->Pose.Orientation.y);
float sinY = phd_sin(item->Pose.Orientation.y);
float cosY = phd_cos(item->Pose.Orientation.y);
int dx = laraItem->Pose.Position.x - item->Pose.Position.x;
int dz = laraItem->Pose.Position.z - item->Pose.Position.z;
int x = c * dx - s * dz;
int z = c * dz + s * dx;
int x = (dx * cosY) - (dz * sinY);
int z = (dz * cosY) + (dx * sinY);
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)
{
return false;
}
return true;
}
@ -294,15 +304,15 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
for (int i = 0; i < 3; i++)
{
auto s = (i != 1) ? phd_sin(coll->Setup.ForwardAngle + ANGLE((i * 90) - 90)) : 0;
auto c = (i != 1) ? phd_cos(coll->Setup.ForwardAngle + ANGLE((i * 90) - 90)) : 0;
auto sinForwardAngle = (i != 1) ? (phd_sin(coll->Setup.ForwardAngle + ANGLE((i * 90.0f) - 90.0f))) : 0;
auto cosForwardAngle = (i != 1) ? (phd_cos(coll->Setup.ForwardAngle + ANGLE((i * 90.0f) - 90.0f))) : 0;
auto x = item->Pose.Position.x + (s * (coll->Setup.Radius));
auto y = item->Pose.Position.y - height - STEP_SIZE;
auto z = item->Pose.Position.z + (c * (coll->Setup.Radius));
auto x = item->Pose.Position.x + (sinForwardAngle * (coll->Setup.Radius));
auto y = item->Pose.Position.y - height - CLICK(1);
auto z = item->Pose.Position.z + (cosForwardAngle * (coll->Setup.Radius));
auto origin = Vector3(x, y, z);
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(coll->Setup.ForwardAngle), 0, 0);
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(coll->Setup.ForwardAngle), 0.0f, 0.0f);
auto direction = (Matrix::CreateTranslation(Vector3::UnitZ) * mxR).Translation();
// g_Renderer.AddDebugSphere(origin, 16, Vector4::One, RENDERER_DEBUG_PAGE::DIMENSION_STATS);
@ -312,10 +322,10 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
short itemNumber = g_Level.Rooms[i].itemNumber;
while (itemNumber != NO_ITEM)
{
auto item2 = &g_Level.Items[itemNumber];
auto obj = &Objects[item2->ObjectNumber];
auto* item2 = &g_Level.Items[itemNumber];
auto* object = &Objects[item2->ObjectNumber];
if (obj->isPickup || obj->collision == nullptr || !item2->Collidable || item2->Status == ITEM_INVISIBLE)
if (object->isPickup || object->collision == nullptr || !item2->Collidable || item2->Status == ITEM_INVISIBLE)
{
itemNumber = item2->NextItem;
continue;
@ -338,7 +348,7 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
for (int j = 0; j < g_Level.Rooms[i].mesh.size(); j++)
{
auto mesh = &g_Level.Rooms[i].mesh[j];
auto* mesh = &g_Level.Rooms[i].mesh[j];
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
continue;
@ -346,9 +356,9 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
if (phd_Distance(&item->Pose, &mesh->pos) < COLLISION_CHECK_DISTANCE)
{
auto box = TO_DX_BBOX(mesh->pos, GetBoundsAccurate(mesh, false));
float dist;
float distance;
if (box.Intersects(origin, direction, dist) && dist < coll->Setup.Radius * 2)
if (box.Intersects(origin, direction, distance) && distance < coll->Setup.Radius * 2)
{
coll->HitStatic = true;
return;
@ -361,29 +371,29 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ItemInfo* item, ItemInfo* laraItem)
{
auto rotRel = laraItem->Pose.Orientation - item->Pose.Orientation;
auto deltaOrient = laraItem->Pose.Orientation - item->Pose.Orientation;
if (rotRel.x < bounds->rotX1)
if (deltaOrient.x < bounds->rotX1)
return false;
if (rotRel.x > bounds->rotX2)
if (deltaOrient.x > bounds->rotX2)
return false;
if (rotRel.y < bounds->rotY1)
if (deltaOrient.y < bounds->rotY1)
return false;
if (rotRel.y > bounds->rotY2)
if (deltaOrient.y > bounds->rotY2)
return false;
if (rotRel.z < bounds->rotZ1)
if (deltaOrient.z < bounds->rotZ1)
return false;
if (rotRel.z > bounds->rotZ2)
if (deltaOrient.z > bounds->rotZ2)
return false;
auto pos = (laraItem->Pose.Position - item->Pose.Position).ToVector3();
Matrix matrix = Matrix::CreateFromYawPitchRoll(
auto matrix = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->Pose.Orientation.y),
TO_RAD(item->Pose.Orientation.x),
TO_RAD(item->Pose.Orientation.z)
@ -401,34 +411,35 @@ bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ItemInfo* item, ItemInfo*
if (pos.x < bounds->boundingBox.X1 || pos.x > bounds->boundingBox.X2 ||
pos.y < bounds->boundingBox.Y1 || pos.y > bounds->boundingBox.Y2 ||
pos.z < bounds->boundingBox.Z1 || pos.z > bounds->boundingBox.Z2)
{
return false;
}
return true;
}
bool AlignLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
{
auto* lara = GetLaraInfo(laraItem);
laraItem->Pose.Orientation = item->Pose.Orientation;
Matrix matrix = Matrix::CreateFromYawPitchRoll(
auto matrix = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->Pose.Orientation.y),
TO_RAD(item->Pose.Orientation.x),
TO_RAD(item->Pose.Orientation.z)
);
Vector3 pos = Vector3::Transform(Vector3(vec->x, vec->y, vec->z), matrix);
Vector3 newPos = item->Pose.Position.ToVector3() + pos;
auto pos = Vector3::Transform(vec->ToVector3(), matrix);
auto newPos = item->Pose.Position.ToVector3() + pos;
int height = GetCollision(newPos.x, newPos.y, newPos.z, laraItem->RoomNumber).Position.Floor;
if ((laraItem->Pose.Position.y - height) <= CLICK(2))
{
laraItem->Pose.Position.x = newPos.x;
laraItem->Pose.Position.y = newPos.y;
laraItem->Pose.Position.z = newPos.z;
laraItem->Pose.Position = Vector3Int(newPos);
return true;
}
auto* lara = GetLaraInfo(laraItem);
if (lara->Control.IsMoving)
{
lara->Control.IsMoving = false;
@ -442,9 +453,8 @@ bool MoveLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
{
auto* lara = GetLaraInfo(laraItem);
auto dest = PHD_3DPOS(item->Pose.Orientation);
Vector3 pos = Vector3(vec->x, vec->y, vec->z);
auto target = PHD_3DPOS(item->Pose.Orientation);
auto pos = vec->ToVector3();
Matrix matrix = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->Pose.Orientation.y),
@ -453,24 +463,23 @@ bool MoveLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
);
pos = Vector3::Transform(pos, matrix);
target.Position = item->Pose.Position + Vector3Int(pos);
dest.Position.x = item->Pose.Position.x + pos.x;
dest.Position.y = item->Pose.Position.y + pos.y;
dest.Position.z = item->Pose.Position.z + pos.z;
if (!Objects[item->ObjectNumber].isPickup)
return Move3DPosTo3DPos(&laraItem->Pose, &target, LARA_ALIGN_VELOCITY, ANGLE(2.0f));
if (item->ObjectNumber != ID_FLARE_ITEM && item->ObjectNumber != ID_BURNING_TORCH_ITEM)
return Move3DPosTo3DPos(&laraItem->Pose, &dest, LARA_VELOCITY, ANGLE(2.0f));
// Prevent picking up items which can result in so called "flare pickup bug"
int height = GetCollision(dest.Position.x, dest.Position.y, dest.Position.z, laraItem->RoomNumber).Position.Floor;
int height = GetCollision(target.Position.x, target.Position.y, target.Position.z, laraItem->RoomNumber).Position.Floor;
if (abs(height - laraItem->Pose.Position.y) <= CLICK(2))
{
auto direction = dest.Position - laraItem->Pose.Position;
auto direction = target.Position - laraItem->Pose.Position;
float distance = sqrt(pow(direction.x, 2) + pow(direction.y, 2) + pow(direction.z, 2));
float distance = sqrt(SQUARE(direction.x) + SQUARE(direction.y) + SQUARE(direction.z));
if (distance < CLICK(0.5f))
return true;
return Move3DPosTo3DPos(&laraItem->Pose, &dest, LARA_VELOCITY, ANGLE(2.0f));
return Move3DPosTo3DPos(&laraItem->Pose, &target, LARA_ALIGN_VELOCITY, ANGLE(2.0f));
}
if (lara->Control.IsMoving)
@ -484,20 +493,21 @@ bool MoveLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
static bool ItemCollide(int value, int radius)
{
return value >= -radius && value <= radius;
return (value >= -radius && value <= radius);
}
static bool ItemInRange(int x, int z, int radius)
{
return (pow(x, 2) + pow(z, 2)) <= pow(radius, 2);
return ((SQUARE(x) + SQUARE(z)) <= SQUARE(radius));
}
bool ItemNearLara(PHD_3DPOS* pos, int radius)
{
GameVector target;
target.x = pos->Position.x - LaraItem->Pose.Position.x;
target.y = pos->Position.y - LaraItem->Pose.Position.y;
target.z = pos->Position.z - LaraItem->Pose.Position.z;
auto target = GameVector(
pos->Position.x - LaraItem->Pose.Position.x,
pos->Position.y - LaraItem->Pose.Position.y,
pos->Position.z - LaraItem->Pose.Position.z
);
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
return false;
@ -535,22 +545,24 @@ bool ItemNearTarget(PHD_3DPOS* src, ItemInfo* target, int radius)
return false;
}
bool Move3DPosTo3DPos(PHD_3DPOS* src, PHD_3DPOS* dest, int velocity, short angleAdd)
bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short angleAdd)
{
auto direction = dest->Position - src->Position;
int distance = sqrt(pow(direction.x, 2) + pow(direction.y, 2) + pow(direction.z, 2));
auto direction = target->Position - origin->Position;
int distance = sqrt(SQUARE(direction.x) + SQUARE(direction.y) + SQUARE(direction.z));
if (velocity < distance)
src->Position += direction * velocity / distance;
origin->Position += direction * velocity / distance;
else
src->Position = dest->Position;
origin->Position = target->Position;
if (!Lara.Control.IsMoving)
{
if (Lara.Control.WaterStatus != WaterStatus::Underwater)
bool shouldAnimate = (distance - velocity) > (velocity * ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD);
if (shouldAnimate && Lara.Control.WaterStatus != WaterStatus::Underwater)
{
int angle = mGetAngle(dest->Position.x, dest->Position.z, src->Position.x, src->Position.z);
int direction = (GetQuadrant(angle) - GetQuadrant(dest->Orientation.y)) & 3;
int angle = mGetAngle(target->Position.x, target->Position.z, origin->Position.x, origin->Position.z);
int direction = (GetQuadrant(angle) - GetQuadrant(target->Orientation.y)) & 3;
switch (direction)
{
@ -581,42 +593,42 @@ bool Move3DPosTo3DPos(PHD_3DPOS* src, PHD_3DPOS* dest, int velocity, short angle
Lara.Control.Count.PositionAdjust = 0;
}
short deltaAngle = dest->Orientation.x - src->Orientation.x;
short deltaAngle = target->Orientation.x - origin->Orientation.x;
if (deltaAngle > angleAdd)
src->Orientation.x += angleAdd;
origin->Orientation.x += angleAdd;
else if (deltaAngle < -angleAdd)
src->Orientation.x -= angleAdd;
origin->Orientation.x -= angleAdd;
else
src->Orientation.x = dest->Orientation.x;
origin->Orientation.x = target->Orientation.x;
deltaAngle = dest->Orientation.y - src->Orientation.y;
deltaAngle = target->Orientation.y - origin->Orientation.y;
if (deltaAngle > angleAdd)
src->Orientation.y += angleAdd;
origin->Orientation.y += angleAdd;
else if (deltaAngle < -angleAdd)
src->Orientation.y -= angleAdd;
origin->Orientation.y -= angleAdd;
else
src->Orientation.y = dest->Orientation.y;
origin->Orientation.y = target->Orientation.y;
deltaAngle = dest->Orientation.z - src->Orientation.z;
deltaAngle = target->Orientation.z - origin->Orientation.z;
if (deltaAngle > angleAdd)
src->Orientation.z += angleAdd;
origin->Orientation.z += angleAdd;
else if (deltaAngle < -angleAdd)
src->Orientation.z -= angleAdd;
origin->Orientation.z -= angleAdd;
else
src->Orientation.z = dest->Orientation.z;
origin->Orientation.z = target->Orientation.z;
return (src->Position == dest->Position && src->Orientation == dest->Orientation);
return (origin->Position == target->Position && origin->Orientation == target->Orientation);
}
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
{
auto bounds = (BOUNDING_BOX*)GetBestFrame(item);
auto laraBounds = (BOUNDING_BOX*)GetBestFrame(laraItem);
auto* bounds = (BOUNDING_BOX*)GetBestFrame(item);
auto* laraBounds = (BOUNDING_BOX*)GetBestFrame(laraItem);
if (item->Pose.Position.y + bounds->Y2 <= laraItem->Pose.Position.y + laraBounds->Y1)
if ((item->Pose.Position.y + bounds->Y2) <= (laraItem->Pose.Position.y + laraBounds->Y1))
return false;
if (item->Pose.Position.y + bounds->Y1 >= laraItem->Pose.Position.y + laraBounds->Y2)
if ((item->Pose.Position.y + bounds->Y1) >= (laraItem->Pose.Position.y + laraBounds->Y2))
return false;
float sinY = phd_sin(item->Pose.Orientation.y);
@ -671,19 +683,20 @@ bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius)
return false;
}
bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spasmEnabled, char bigPush) // previously ItemPushLara
// NOTE: Previously ItemPushLara().
bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spasmEnabled, char bigPush)
{
// Get item's rotation
// Get item's rotation.
float sinY = phd_sin(item->Pose.Orientation.y);
float cosY = phd_cos(item->Pose.Orientation.y);
// Get vector from item to Lara
// Get vector from item to Lara.
int dx = item2->Pose.Position.x - item->Pose.Position.x;
int dz = item2->Pose.Position.z - item->Pose.Position.z;
// Rotate Lara vector into item frame
int rx = cosY * dx - sinY * dz;
int rz = cosY * dz + sinY * dx;
// Rotate Lara vector into item frame.
int rx = (dx * cosY) - (dz * sinY);
int rz = (dz * cosY) + (dx * sinY);
BOUNDING_BOX* bounds;
if (bigPush & 2)
@ -731,13 +744,13 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
auto* lara = item2->IsLara() ? GetLaraInfo(item2) : nullptr;
if (lara != nullptr && spasmEnabled && bounds->Y2 - bounds->Y1 > CLICK(1))
if (lara != nullptr && spasmEnabled && (bounds->Y2 - bounds->Y1) > CLICK(1))
{
rx = (bounds->X1 + bounds->X2) / 2;
rz = (bounds->Z1 + bounds->Z2) / 2;
dx -= cosY * rx + sinY * rz;
dz -= cosY * rz - sinY * rx;
dx -= (rx * cosY) + (rz * sinY);
dz -= (rz * cosY) - (rx * sinY);
lara->HitDirection = (item2->Pose.Orientation.y - phd_atan(dz, dz) - ANGLE(135.0f)) / ANGLE(90.0f);
DoDamage(item2, 0); // Dummy hurt call. Only for ooh sound!
@ -862,7 +875,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
{
for (int j = 0; j < g_Level.Rooms[i].mesh.size(); j++)
{
auto mesh = &g_Level.Rooms[i].mesh[j];
auto* mesh = &g_Level.Rooms[i].mesh[j];
// Only process meshes which are visible and solid
if ((mesh->flags & StaticMeshFlags::SM_VISIBLE) && (mesh->flags & StaticMeshFlags::SM_SOLID))
@ -911,7 +924,7 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
// 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)
if (TestEnvironment(ENV_FLAG_WATER, item))
{
collBox.Y1 = itemBBox->Y1;
collBox.Y2 = itemBBox->Y2;
@ -1022,13 +1035,13 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
// Determine identity rotation/distance
auto distance = Vector3(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
auto c = phd_cos(pos.Orientation.y);
auto s = phd_sin(pos.Orientation.y);
auto sinY = phd_sin(pos.Orientation.y);
auto cosY = phd_cos(pos.Orientation.y);
// Rotate item to collision bounds identity
auto x = round(distance.x * c - distance.z * s) + pos.Position.x;
auto x = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
auto y = item->Pose.Position.y;
auto z = round(distance.x * s + distance.z * c) + pos.Position.z;
auto z = round(distance.x * sinY + distance.z * cosY) + pos.Position.z;
// Determine identity static collision bounds
auto XMin = pos.Position.x + box->X1;
@ -1074,8 +1087,8 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
// Rotate previous collision position to identity
distance = Vector3(coll->Setup.OldPosition.x, coll->Setup.OldPosition.y, coll->Setup.OldPosition.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
auto ox = round(distance.x * c - distance.z * s) + pos.Position.x;
auto oz = round(distance.x * s + distance.z * c) + pos.Position.z;
auto ox = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
auto oz = round(distance.x * sinY + distance.z * cosY) + pos.Position.z;
// Calculate collisison type based on identity rotation
switch (GetQuadrant(coll->Setup.ForwardAngle - pos.Orientation.y))
@ -1171,12 +1184,12 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
// Determine final shifts rotation/distance
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
c = phd_cos(-pos.Orientation.y);
s = phd_sin(-pos.Orientation.y);
sinY = phd_sin(-pos.Orientation.y);
cosY = phd_cos(-pos.Orientation.y);
// Calculate final shifts rotation/distance
coll->Shift.x = (round(distance.x * c - distance.z * s) + pos.Position.x) - item->Pose.Position.x;
coll->Shift.z = (round(distance.x * s + distance.z * c) + pos.Position.z) - item->Pose.Position.z;
coll->Shift.x = (round(distance.x * cosY - distance.z * sinY) + pos.Position.x) - item->Pose.Position.x;
coll->Shift.z = (round(distance.x * sinY + distance.z * cosY) + pos.Position.z) - item->Pose.Position.z;
if (coll->Shift.x == 0 && coll->Shift.z == 0)
coll->CollisionType = CT_NONE; // Paranoid
@ -1188,13 +1201,12 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
return true;
}
void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv) // previously DoProperDetection
// NOTE: Previously DoProperDetection().
void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv)
{
int bs, yAngle;
auto* item = &g_Level.Items[itemNumber];
auto oldCollResult = GetCollision(x, y, z, item->RoomNumber);
auto prevCollResult = GetCollision(x, y, z, item->RoomNumber);
auto collResult = GetCollision(item);
auto* bounds = GetBoundsAccurate(item);
@ -1204,11 +1216,12 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
if (item->Pose.Position.y >= collResult.Position.Floor)
{
bs = 0;
int bs = 0;
if (collResult.Position.FloorSlope && oldCollResult.Position.Floor < collResult.Position.Floor)
if (collResult.Position.FloorSlope && prevCollResult.Position.Floor < collResult.Position.Floor)
{
yAngle = (long)((unsigned short)item->Pose.Orientation.y);
int yAngle = (long)((unsigned short)item->Pose.Orientation.y);
if (collResult.FloorTilt.x < 0)
{
if (yAngle >= ANGLE(180.0f))
@ -1601,7 +1614,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{
if (yv >= 0)
{
oldCollResult = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
prevCollResult = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
collResult = GetCollision(item);
// Bounce off floor.
@ -1610,7 +1623,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
// was always set to 0 by GetHeight() function which was called before the check.
// Possibly a mistake or unfinished feature by Core? -- Lwmte, 27.08.21
if (item->Pose.Position.y >= oldCollResult.Position.Floor)
if (item->Pose.Position.y >= prevCollResult.Position.Floor)
{
// Hit the floor; bounce and slow down.
if (item->Animation.Velocity.y > 0)
@ -1644,7 +1657,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
}
}
item->Pose.Position.y = oldCollResult.Position.Floor;
item->Pose.Position.y = prevCollResult.Position.Floor;
}
}
// else
@ -1709,10 +1722,10 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
laraItem->HitStatus = false;
coll->HitStatic = false;
bool playerCollision = laraItem->IsLara();
bool harmless = !playerCollision && (laraItem->Data.is<KayakInfo>() || laraItem->Data.is<UPVInfo>());
bool doPlayerCollision = laraItem->IsLara();
bool harmless = !doPlayerCollision && (laraItem->Data.is<KayakInfo>() || laraItem->Data.is<UPVInfo>());
if (playerCollision)
if (doPlayerCollision)
{
GetLaraInfo(laraItem)->HitDirection = -1;
@ -1751,7 +1764,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
if (phd_Distance(&item->Pose, &laraItem->Pose) >= COLLISION_CHECK_DISTANCE)
continue;
if (playerCollision)
if (doPlayerCollision)
{
// Objects' own collision routines were almost universally written only for
// managing collisions with Lara and nothing else. Until all of these routines
@ -1810,7 +1823,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
// For Lara, solid static mesh collisions are directly managed by GetCollisionInfo,
// so we bypass them here to avoid interference.
if (playerCollision && (mesh->flags & StaticMeshFlags::SM_SOLID))
if (doPlayerCollision && (mesh->flags & StaticMeshFlags::SM_SOLID))
continue;
if (phd_Distance(&mesh->pos, &laraItem->Pose) >= COLLISION_CHECK_DISTANCE)
@ -1822,7 +1835,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
coll->HitStatic = true;
// HACK: Shatter statics only by non-harmless vehicles.
if (!playerCollision &&
if (!doPlayerCollision &&
!harmless && abs(laraItem->Animation.Velocity.z) > VEHICLE_COLLISION_TERMINAL_VELOCITY &&
StaticObjects[mesh->staticNumber].shatterType != SHT_NONE)
{
@ -1840,7 +1853,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
}
}
if (playerCollision)
if (doPlayerCollision)
{
auto* lara = GetLaraInfo(laraItem);
if (lara->HitDirection == -1)
@ -1880,14 +1893,12 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
if (!TestCollision(item, laraItem))
return;
bool playerCollision = laraItem->IsLara();
bool waterPlayerCollision = playerCollision && GetLaraInfo(laraItem)->Control.WaterStatus >= WaterStatus::TreadWater;
bool doPlayerCollision = laraItem->IsLara();
bool waterPlayerCollision = doPlayerCollision && GetLaraInfo(laraItem)->Control.WaterStatus >= WaterStatus::TreadWater;
if (waterPlayerCollision || coll->Setup.EnableObjectPush)
{
ItemPushItem(item, laraItem, coll, coll->Setup.EnableSpasm, 0);
}
else if (playerCollision && coll->Setup.EnableSpasm)
else if (doPlayerCollision && coll->Setup.EnableSpasm)
{
int x = laraItem->Pose.Position.x - item->Pose.Position.x;
int z = laraItem->Pose.Position.z - item->Pose.Position.z;
@ -1901,10 +1912,10 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
if (frame->boundingBox.Height() > STEP_SIZE)
{
int angle = (laraItem->Pose.Orientation.y - phd_atan(z - cosY * rx - sinY * rz, x - cosY * rx + sinY * rz) - ANGLE(135.0f)) / ANGLE(90.0f);
auto* lara = GetLaraInfo(laraItem);
int angle = (laraItem->Pose.Orientation.y - phd_atan(z - cosY * rx - sinY * rz, x - cosY * rx + sinY * rz) - ANGLE(135.0f)) / ANGLE(90.0f);
lara->HitDirection = (short)angle;
// TODO: check if a second Lara.hitFrame++; is required there !

View file

@ -333,7 +333,7 @@ int ObjectOnLOS2(GameVector* start, GameVector* end, Vector3Int* vec, MESH_INFO*
if ((priorityObject != GAME_OBJECT_ID::ID_NO_OBJECT) && (item->ObjectNumber != priorityObject))
continue;
if ((item->ObjectNumber != ID_LARA) && (Objects[item->ObjectNumber].collision == NULL))
if ((item->ObjectNumber != ID_LARA) && (Objects[item->ObjectNumber].collision == nullptr))
continue;
if ((item->ObjectNumber == ID_LARA) && (priorityObject != ID_LARA))

View file

@ -1,122 +1,157 @@
#include "framework.h"
#include "Specific/level.h"
#include "Game/control/control.h"
#include "Objects/Generic/Object/polerope.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/sphere.h"
#include "Game/control/box.h"
#include "Game/items.h"
#include "Game/control/control.h"
#include "Game/control/lot.h"
#include "Specific/input.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_struct.h"
#include "Game/Lara/lara_tests.h"
#include "Game/Lara/lara.h"
#include "Specific/input.h"
#include "Specific/level.h"
#include "Specific/trmath.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/sphere.h"
using namespace TEN::Input;
using std::vector;
namespace TEN::Entities::Generic
{
Vector3Int PolePos = { 0, 0, -208 };
Vector3Int PolePosR = { 0, 0, 0 };
OBJECT_COLLISION_BOUNDS PoleBounds =
const vector<LaraState> VPoleMountedStates =
{
-256, 256,
LS_POLE_IDLE,
LS_POLE_UP,
LS_POLE_DOWN,
LS_POLE_TURN_CLOCKWISE,
LS_POLE_TURN_COUNTER_CLOCKWISE
};
const vector<LaraState> VPoleGroundedMountStates =
{
LS_IDLE,
LS_TURN_LEFT_SLOW,
LS_TURN_RIGHT_SLOW,
LS_TURN_LEFT_FAST,
LS_TURN_RIGHT_FAST,
LS_WALK_FORWARD,
LS_RUN_FORWARD
};
const vector<LaraState> VPoleAirborneMountStates =
{
LS_REACH,
LS_JUMP_UP
};
// TODO: These might be interfering with the SetPosition command. -- Sezz 2022.08.29
auto VPolePos = Vector3Int(0, 0, -208);
auto VPolePosR = Vector3Int::Zero;
OBJECT_COLLISION_BOUNDS VPoleBounds =
{
-CLICK(1), CLICK(1),
0, 0,
-512, 512,
-ANGLE(10.0f), ANGLE(10.0f),
-ANGLE(30.0f), ANGLE(30.0f),
-ANGLE(10.0f), ANGLE(10.0f)
-CLICK(2), CLICK(2),
ANGLE(-10.0f), ANGLE(10.0f),
ANGLE(-30.0f), ANGLE(30.0f),
ANGLE(-10.0f), ANGLE(10.0f)
};
void PoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{
auto* laraInfo = GetLaraInfo(laraItem);
auto* poleItem = &g_Level.Items[itemNumber];
auto* lara = GetLaraInfo(laraItem);
bool isLara = !poleItem->IsLara();
bool isFacingPole = IsPointInFront(laraItem->Pose, poleItem->Pose.Position.ToVector3());
if (TrInput & IN_ACTION && isLara &&
laraInfo->Control.HandStatus == HandStatus::Free &&
laraItem->Animation.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_IDLE || laraInfo->Control.IsMoving &&
laraInfo->InteractedItem == itemNumber)
// Mount while grounded.
if (TrInput & IN_ACTION && isFacingPole &&
CheckLaraState((LaraState)laraItem->Animation.ActiveState, VPoleGroundedMountStates) &&
lara->Control.HandStatus == HandStatus::Free ||
(lara->Control.IsMoving && lara->InteractedItem == itemNumber))
{
short rot = poleItem->Pose.Orientation.y;
// Temporarily reorient pole.
short yOrient = poleItem->Pose.Orientation.y;
poleItem->Pose.Orientation.y = laraItem->Pose.Orientation.y;
if (TestLaraPosition(&PoleBounds, poleItem, laraItem))
if (TestLaraPosition(&VPoleBounds, poleItem, laraItem))
{
if (MoveLaraPosition(&PolePos, poleItem, laraItem))
if (MoveLaraPosition(&VPolePos, poleItem, laraItem))
{
laraItem->Animation.AnimNumber = LA_STAND_TO_POLE;
laraItem->Animation.ActiveState = LS_POLE_IDLE;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraInfo->Control.IsMoving = false;
laraInfo->Control.HandStatus = HandStatus::Busy;
SetAnimation(laraItem, LA_STAND_TO_POLE);
lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Busy;
}
else
laraInfo->InteractedItem = itemNumber;
lara->InteractedItem = itemNumber;
poleItem->Pose.Orientation.y = rot;
poleItem->Pose.Orientation.y = yOrient;
}
else
{
if (laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber)
if (lara->Control.IsMoving && lara->InteractedItem == itemNumber)
{
laraInfo->Control.IsMoving = false;
laraInfo->Control.HandStatus = HandStatus::Free;
lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free;
}
poleItem->Pose.Orientation.y = rot;
poleItem->Pose.Orientation.y = yOrient;
}
return;
}
else if (TrInput & IN_ACTION && isLara &&
laraInfo->Control.HandStatus == HandStatus::Free &&
laraItem->Animation.IsAirborne &&
laraItem->Animation.Velocity.y > (int)laraInfo->Control.HandStatus && // ?????
laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP)
// Mount while airborne.
if (TrInput & IN_ACTION && isFacingPole &&
CheckLaraState((LaraState)laraItem->Animation.ActiveState, VPoleAirborneMountStates) &&
laraItem->Animation.IsAirborne &&
laraItem->Animation.Velocity.y > 0.0f &&
lara->Control.HandStatus == HandStatus::Free)
{
if (TestBoundsCollide(poleItem, laraItem, 100) &&
TestLaraPoleCollision(laraItem, coll, true, -CLICK(1)) &&
TestLaraPoleCollision(laraItem, coll, false))
// Test sphere collision.
if (!TestLaraPoleCollision(laraItem, coll, true, -CLICK(1)) || !TestLaraPoleCollision(laraItem, coll, false))
return;
// Test bounds collision.
if (TestBoundsCollide(poleItem, laraItem, LARA_RADIUS + (int)round(abs(laraItem->Animation.Velocity.z))))
{
if (TestCollision(poleItem, laraItem))
{
short rot = poleItem->Pose.Orientation.y;
// Temporarily reorient pole.
short yOrient = poleItem->Pose.Orientation.y;
poleItem->Pose.Orientation.y = laraItem->Pose.Orientation.y;
// Reaching.
if (laraItem->Animation.ActiveState == LS_REACH)
{
PolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 10;
AlignLaraPosition(&PolePosR, poleItem, laraItem);
laraItem->Animation.AnimNumber = LA_REACH_TO_POLE;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
VPolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 10;
AlignLaraPosition(&VPolePosR, poleItem, laraItem);
SetAnimation(laraItem, LA_REACH_TO_POLE);
}
// Jumping up.
else
{
PolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 66;
AlignLaraPosition(&PolePosR, poleItem, laraItem);
laraItem->Animation.AnimNumber = LA_JUMP_UP_TO_POLE;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
VPolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 66;
AlignLaraPosition(&VPolePosR, poleItem, laraItem);
SetAnimation(laraItem, LA_JUMP_UP_TO_POLE);
}
laraItem->Animation.ActiveState = LS_POLE_IDLE;
laraItem->Animation.Velocity.y = 0;
laraItem->Animation.IsAirborne = false;
laraInfo->Control.HandStatus = HandStatus::Busy;
poleItem->Pose.Orientation.y = rot;
laraItem->Animation.Velocity.y = 0.0f;
lara->Control.HandStatus = HandStatus::Busy;
poleItem->Pose.Orientation.y = yOrient;
}
}
return;
}
else
// Player is not interacting with vertical pole; do regular object collision.
if (!CheckLaraState((LaraState)laraItem->Animation.ActiveState, VPoleMountedStates) &&
laraItem->Animation.ActiveState != LS_JUMP_BACK)
{
if (!isLara || ((laraItem->Animation.ActiveState < LS_POLE_IDLE ||
laraItem->Animation.ActiveState > LS_POLE_TURN_COUNTER_CLOCKWISE) &&
laraItem->Animation.ActiveState != LS_JUMP_BACK))
{
ObjectCollision(itemNumber, laraItem, coll);
}
ObjectCollision(itemNumber, laraItem, coll);
}
}
}

View file

@ -1,7 +1,7 @@
#pragma once
struct ItemInfo;
struct CollisionInfo;
struct ItemInfo;
namespace TEN::Entities::Generic
{

View file

@ -461,6 +461,7 @@ void StartTraps()
object->saveFlags = true;
object->savePosition = true;
object->usingDrawAnimatingItem = true;
object->isPickup = true;
}
}

View file

@ -48,7 +48,7 @@ struct Vector3Int
this->z = int(v.z);
}
Vector3 ToVector3()
Vector3 ToVector3() const
{
return Vector3(x, y, z);
}
@ -147,7 +147,7 @@ struct Vector3Shrt
this->z = z;
}
Vector3 ToVector3()
Vector3 ToVector3() const
{
return Vector3(x, y, z);
}

View file

@ -399,3 +399,24 @@ Vector3Int TranslateVector(Vector3Int& vector, Vector3& direction, float distanc
(int)round(newVector.z)
);
}
bool IsPointInFront(const PHD_3DPOS& pose, const Vector3& target)
{
return IsPointInFront(pose.Position.ToVector3(), target, pose.Orientation);
}
bool IsPointInFront(const Vector3& origin, const Vector3& target, const Vector3Shrt& orient)
{
float sinY = phd_sin(orient.y);
float cosY = phd_cos(orient.y);
// The heading angle (Y only) direction vector: X = +sinY, Y = 0, Z = +cosY
auto headingDirection = Vector3(sinY, 0.0f, cosY);
auto targetDirection = target - origin;
float dot = headingDirection.Dot(targetDirection);
if (dot > 0.0f)
return true;
return false;
}

View file

@ -82,3 +82,5 @@ Vector3 TranslateVector(Vector3& vector, Vector3Shrt& orient, float distance);
Vector3Int TranslateVector(Vector3Int& vector, Vector3Shrt& orient, float distance);
Vector3 TranslateVector(Vector3& vector, Vector3& direction, float distance);
Vector3Int TranslateVector(Vector3Int& vector, Vector3& direction, float distance);
bool IsPointInFront(const PHD_3DPOS& pose, const Vector3& target);
bool IsPointInFront(const Vector3& origin, const Vector3& target, const Vector3Shrt& orient);