Dynamic bridge collision (#1137)

* Initial commit

* Don't update bridge offset if not necessary

* Don't stick to bridge when in air

* Update Changes.txt

* Minor simplification

* Simplify

* Update collide_room.h

* Update collide_room.h

* Use real floor height to determine bridge collision

* Detect bridges first to avoid potential issues with item init

* Remove incorrect forward declaration

* Move pickups on dynamic bridges

* Push player away from moving bridges

* Update Changes.txt

* Use actual pickup bounds for collision detection

* Stabilize collision by using both abs Y and bb Y ref points

* Update collide_item.cpp

* Swap function parameters

* Update collide_room.cpp

* Increase movement tolerance

---------

Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Jakub <80340234+Kubsy@users.noreply.github.com>
This commit is contained in:
Lwmte 2023-06-17 12:52:02 +03:00 committed by GitHub
parent ba368be4b7
commit 35304ce84a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 223 additions and 77 deletions

View file

@ -14,6 +14,8 @@ Version 1.1.0
* Add multiple doppelgangers by using the same OCB for the origin nullmesh and doppelganger. * Add multiple doppelgangers by using the same OCB for the origin nullmesh and doppelganger.
* Pause all sounds when entering inventory or pause menu. * Pause all sounds when entering inventory or pause menu.
* Improve deflection against slopes. * Improve deflection against slopes.
* Move and rotate Lara together with dynamic bridge objects.
* Move and rotate activated pickups together with dynamic bridge objects.
* Add TR1 skateboard kid. * Add TR1 skateboard kid.
* Add TR1 Kold. * Add TR1 Kold.

View file

@ -48,6 +48,10 @@ bool LaraDeflectEdge(ItemInfo* item, CollisionInfo* coll)
ShiftItem(item, coll); ShiftItem(item, coll);
item->Pose.Orientation.y -= ANGLE(coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y -= ANGLE(coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE);
} }
else if (coll->LastBridgeItemNumber != NO_ITEM)
{
ShiftItem(item, coll);
}
return false; return false;
} }
@ -202,8 +206,8 @@ bool LaraDeflectEdgeCrawl(ItemInfo* item, CollisionInfo* coll)
bool LaraDeflectEdgeMonkey(ItemInfo* item, CollisionInfo* coll) bool LaraDeflectEdgeMonkey(ItemInfo* item, CollisionInfo* coll)
{ {
// HACK // HACK
if (coll->Shift.y >= 0 && coll->Shift.y <= CLICK(1.25f)) if (coll->Shift.Position.y >= 0 && coll->Shift.Position.y <= CLICK(1.25f))
coll->Shift.y = 0; coll->Shift.Position.y = 0;
if (coll->CollisionType == CT_FRONT || coll->CollisionType == CT_TOP_FRONT || if (coll->CollisionType == CT_FRONT || coll->CollisionType == CT_TOP_FRONT ||
coll->HitTallObject) coll->HitTallObject)

View file

@ -140,8 +140,8 @@ void lara_col_monkey_idle(ItemInfo* item, CollisionInfo* coll)
GetCollisionInfo(coll, item); GetCollisionInfo(coll, item);
// HACK: Prevent ShiftItem() from causing an instantaneous snap, thereby interfering with DoLaraMonkeyStep(), when going down a step. @Sezz 2022.01.28 // HACK: Prevent ShiftItem() from causing an instantaneous snap, thereby interfering with DoLaraMonkeyStep(), when going down a step. @Sezz 2022.01.28
if (coll->Shift.y >= 0 && coll->Shift.y <= CLICK(1.25f)) if (coll->Shift.Position.y >= 0 && coll->Shift.Position.y <= CLICK(1.25f))
coll->Shift.y = 0; coll->Shift.Position.y = 0;
ShiftItem(item, coll); ShiftItem(item, coll);
if (TestLaraMonkeyFall(item, coll)) if (TestLaraMonkeyFall(item, coll))

View file

@ -250,9 +250,9 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll)
else // Death, incorrect ledge or ACTION release else // Death, incorrect ledge or ACTION release
{ {
SetAnimation(item, LA_JUMP_UP, 9); SetAnimation(item, LA_JUMP_UP, 9);
item->Pose.Position.x += coll->Shift.x; item->Pose.Position.x += coll->Shift.Position.x;
item->Pose.Position.y += GameBoundingBox(item).Y2 * 1.8f; item->Pose.Position.y += GameBoundingBox(item).Y2 * 1.8f;
item->Pose.Position.z += coll->Shift.z; item->Pose.Position.z += coll->Shift.Position.z;
item->Animation.IsAirborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity.z = 2; item->Animation.Velocity.z = 2;
item->Animation.Velocity.y = 1; item->Animation.Velocity.y = 1;
@ -472,12 +472,12 @@ bool TestLaraHangOnClimbableWall(ItemInfo* item, CollisionInfo* coll)
{ {
case NORTH: case NORTH:
case SOUTH: case SOUTH:
item->Pose.Position.z += coll2.Shift.z; item->Pose.Position.z += coll2.Shift.Position.z;
break; break;
case EAST: case EAST:
case WEST: case WEST:
item->Pose.Position.x += coll2.Shift.x; item->Pose.Position.x += coll2.Shift.Position.x;
break; break;
default: default:

View file

@ -753,6 +753,54 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool ena
return true; return true;
} }
// Simplified version of ItemPushItem for basic pushes.
bool ItemPushItem(ItemInfo* item, ItemInfo* item2)
{
float sinY = phd_sin(item->Pose.Orientation.y);
float cosY = phd_cos(item->Pose.Orientation.y);
// Get direction vector from item to player.
auto direction = item2->Pose.Position - item->Pose.Position;
// Rotate Lara vector into item frame.
int rx = (direction.x * cosY) - (direction.z * sinY);
int rz = (direction.z * cosY) + (direction.x * sinY);
const auto& bounds = GetBestFrame(*item).BoundingBox;
int minX = bounds.X1;
int maxX = bounds.X2;
int minZ = bounds.Z1;
int maxZ = bounds.Z2;
// Big enemies
if (abs(direction.x) > BLOCK(4.5f) || abs(direction.z) > BLOCK(4.5f) ||
rx <= minX || rx >= maxX ||
rz <= minZ || rz >= maxZ)
{
return false;
}
int left = rx - minX;
int top = maxZ - rz;
int bottom = rz - minZ;
int 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;
item2->Pose.Position.x = item->Pose.Position.x + (rx * cosY) + (rz * sinY);
item2->Pose.Position.z = item->Pose.Position.z + (rz * cosY) - (rx * sinY);
return true;
}
// NOTE: Previously ItemPushLaraStatic(). // NOTE: Previously ItemPushLaraStatic().
bool ItemPushStatic(ItemInfo* item, const MESH_INFO& mesh, CollisionInfo* coll) bool ItemPushStatic(ItemInfo* item, const MESH_INFO& mesh, CollisionInfo* coll)
{ {
@ -828,6 +876,62 @@ bool ItemPushStatic(ItemInfo* item, const MESH_INFO& mesh, CollisionInfo* coll)
return true; return true;
} }
void ItemPushBridge(ItemInfo& item, CollisionInfo& coll)
{
coll.Setup.ForwardAngle = item.Pose.Orientation.y;
coll.Setup.OldPosition = item.Pose.Position;
GetCollisionInfo(&coll, &item);
ShiftItem(&item, &coll);
}
void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, const CollisionResult& collResult)
{
// Store an offset for a bridge item into shifts, if exists.
if (coll.LastBridgeItemNumber == collResult.Position.Bridge && coll.LastBridgeItemNumber != NO_ITEM)
{
auto& bridgeItem = g_Level.Items[collResult.Position.Bridge];
auto deltaPos = bridgeItem.Pose.Position - coll.LastBridgeItemPose.Position;
auto deltaOrient = bridgeItem.Pose.Orientation - coll.LastBridgeItemPose.Orientation;
auto deltaPose = Pose(deltaPos, deltaOrient);
int absDeltaHeight = item.Pose.Position.y - collResult.Position.Floor;
int relDeltaHeight = absDeltaHeight + GameBoundingBox(&item).Y2;
if (deltaPose != Pose::Zero &&
(abs(absDeltaHeight) <= CLICK(1 / 8.0f) || abs(relDeltaHeight) <= CLICK(1 / 8.0f)))
{
const auto& bridgePos = bridgeItem.Pose.Position;
// Calculate offset.
auto relOffset = (item.Pose.Position - bridgePos).ToVector3();
auto rotMatrix = deltaPose.Orientation.ToRotationMatrix();
auto offset = bridgePos.ToVector3() + Vector3::Transform(relOffset, rotMatrix);
deltaPose.Position -= item.Pose.Position - Vector3i(offset);
// Don't update shifts if difference is too big (possibly bridge was teleported or just entered bridge).
if (deltaPose.Position.ToVector3().Length() <= coll.Setup.Radius * 2)
coll.Shift = deltaPose;
}
else if (deltaPos.ToVector3().Length() <= coll.Setup.Radius && relDeltaHeight > 0 &&
(deltaPos != Vector3i::Zero || deltaOrient != EulerAngles::Zero))
{
// Push item away if not directly above bridge, and bridge position was changed.
ItemPushItem(&bridgeItem, &item);
}
coll.LastBridgeItemPose = bridgeItem.Pose;
}
else
{
coll.LastBridgeItemPose = Pose::Zero;
coll.LastBridgeItemNumber = NO_ITEM;
}
coll.LastBridgeItemNumber = collResult.Position.Bridge;
}
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll) void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
{ {
coll->HitTallObject = false; coll->HitTallObject = false;
@ -1057,20 +1161,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
case NORTH: case NORTH:
if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius) if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius)
{ {
coll->Shift.z = rawShift.z; coll->Shift.Position.z = rawShift.z;
coll->Shift.x = ox - x; coll->Shift.Position.x = ox - x;
coll->CollisionType = CT_FRONT; coll->CollisionType = CT_FRONT;
} }
else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius) else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius)
{ {
coll->Shift.x = rawShift.x; coll->Shift.Position.x = rawShift.x;
coll->Shift.z = 0; coll->Shift.Position.z = 0;
coll->CollisionType = CT_LEFT; coll->CollisionType = CT_LEFT;
} }
else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius) else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius)
{ {
coll->Shift.x = rawShift.x; coll->Shift.Position.x = rawShift.x;
coll->Shift.z = 0; coll->Shift.Position.z = 0;
coll->CollisionType = CT_RIGHT; coll->CollisionType = CT_RIGHT;
} }
@ -1079,20 +1183,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
case SOUTH: case SOUTH:
if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius) if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius)
{ {
coll->Shift.z = rawShift.z; coll->Shift.Position.z = rawShift.z;
coll->Shift.x = ox - x; coll->Shift.Position.x = ox - x;
coll->CollisionType = CT_FRONT; coll->CollisionType = CT_FRONT;
} }
else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius) else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius)
{ {
coll->Shift.x = rawShift.x; coll->Shift.Position.x = rawShift.x;
coll->Shift.z = 0; coll->Shift.Position.z = 0;
coll->CollisionType = CT_RIGHT; coll->CollisionType = CT_RIGHT;
} }
else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius) else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius)
{ {
coll->Shift.x = rawShift.x; coll->Shift.Position.x = rawShift.x;
coll->Shift.z = 0; coll->Shift.Position.z = 0;
coll->CollisionType = CT_LEFT; coll->CollisionType = CT_LEFT;
} }
@ -1101,20 +1205,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
case EAST: case EAST:
if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius) if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius)
{ {
coll->Shift.x = rawShift.x; coll->Shift.Position.x = rawShift.x;
coll->Shift.z = oz - z; coll->Shift.Position.z = oz - z;
coll->CollisionType = CT_FRONT; coll->CollisionType = CT_FRONT;
} }
else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius) else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius)
{ {
coll->Shift.z = rawShift.z; coll->Shift.Position.z = rawShift.z;
coll->Shift.x = 0; coll->Shift.Position.x = 0;
coll->CollisionType = CT_RIGHT; coll->CollisionType = CT_RIGHT;
} }
else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius) else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius)
{ {
coll->Shift.z = rawShift.z; coll->Shift.Position.z = rawShift.z;
coll->Shift.x = 0; coll->Shift.Position.x = 0;
coll->CollisionType = CT_LEFT; coll->CollisionType = CT_LEFT;
} }
@ -1123,20 +1227,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
case WEST: case WEST:
if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius) if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius)
{ {
coll->Shift.x = rawShift.x; coll->Shift.Position.x = rawShift.x;
coll->Shift.z = oz - z; coll->Shift.Position.z = oz - z;
coll->CollisionType = CT_FRONT; coll->CollisionType = CT_FRONT;
} }
else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius) else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius)
{ {
coll->Shift.z = rawShift.z; coll->Shift.Position.z = rawShift.z;
coll->Shift.x = 0; coll->Shift.Position.x = 0;
coll->CollisionType = CT_LEFT; coll->CollisionType = CT_LEFT;
} }
else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius) else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius)
{ {
coll->Shift.z = rawShift.z; coll->Shift.Position.z = rawShift.z;
coll->Shift.x = 0; coll->Shift.Position.x = 0;
coll->CollisionType = CT_RIGHT; coll->CollisionType = CT_RIGHT;
} }
@ -1144,15 +1248,15 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
} }
// Determine final shifts orientation/distance. // Determine final shifts orientation/distance.
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - pose.Position.ToVector3(); distance = Vector3(x + coll->Shift.Position.x, y, z + coll->Shift.Position.z) - pose.Position.ToVector3();
sinY = phd_sin(-pose.Orientation.y); sinY = phd_sin(-pose.Orientation.y);
cosY = phd_cos(-pose.Orientation.y); cosY = phd_cos(-pose.Orientation.y);
// Calculate final shifts orientation/distance. // Calculate final shifts orientation/distance.
coll->Shift.x = (round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x) - item->Pose.Position.x; coll->Shift.Position.x = (round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x) - item->Pose.Position.x;
coll->Shift.z = (round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z) - item->Pose.Position.z; coll->Shift.Position.z = (round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z) - item->Pose.Position.z;
if (coll->Shift.x == 0 && coll->Shift.z == 0) if (coll->Shift.Position.x == 0 && coll->Shift.Position.z == 0)
coll->CollisionType = CT_NONE; // Paranoid. coll->CollisionType = CT_NONE; // Paranoid.
// Set splat state flag if item is Lara and bounds are taller than Lara's headroom. // Set splat state flag if item is Lara and bounds are taller than Lara's headroom.
@ -1838,7 +1942,7 @@ void ObjectCollision(const short itemNumber, ItemInfo* laraItem, CollisionInfo*
if (TestCollision(item, laraItem)) if (TestCollision(item, laraItem))
{ {
if (coll->Setup.EnableObjectPush) if (coll->Setup.EnableObjectPush)
ItemPushItem(item, laraItem, coll, false, true); ItemPushItem(item, laraItem, coll, false, 1);
} }
} }
} }

View file

@ -1,10 +1,9 @@
#pragma once #pragma once
#include "Math/Math.h" #include "Math/Math.h"
using std::pair;
class FloorInfo; class FloorInfo;
struct CollisionInfo; struct CollisionInfo;
struct CollisionResult;
struct ItemInfo; struct ItemInfo;
struct MESH_INFO; struct MESH_INFO;
@ -19,8 +18,8 @@ extern MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS];
struct ObjectCollisionBounds struct ObjectCollisionBounds
{ {
GameBoundingBox BoundingBox = GameBoundingBox::Zero; GameBoundingBox BoundingBox = GameBoundingBox::Zero;
pair<EulerAngles, EulerAngles> OrientConstraint = {}; std::pair<EulerAngles, EulerAngles> OrientConstraint = {};
}; };
void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
@ -40,10 +39,13 @@ bool Move3DPosTo3DPos(ItemInfo* item, Pose& fromPose, const Pose& toPose, int ve
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius); bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius);
bool TestBoundsCollideStatic(ItemInfo* item, const MESH_INFO& mesh, int radius); bool TestBoundsCollideStatic(ItemInfo* item, const MESH_INFO& mesh, int radius);
bool ItemPushItem(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll, bool enableSpasm, char bigPush); bool ItemPushItem(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll, bool enableSpasm, char bigPush);
bool ItemPushItem(ItemInfo* item, ItemInfo* item2);
bool ItemPushStatic(ItemInfo* laraItem, const MESH_INFO& mesh, CollisionInfo* coll); bool ItemPushStatic(ItemInfo* laraItem, const MESH_INFO& mesh, CollisionInfo* coll);
void ItemPushBridge(ItemInfo& item, CollisionInfo& coll);
bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& pose, CollisionInfo* coll); bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& pose, CollisionInfo* coll);
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll); void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll);
void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, const CollisionResult& collResult);
void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void ObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void ObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);

View file

@ -18,7 +18,8 @@ using namespace TEN::Renderer;
void ShiftItem(ItemInfo* item, CollisionInfo* coll) void ShiftItem(ItemInfo* item, CollisionInfo* coll)
{ {
item->Pose.Position += coll->Shift; item->Pose.Position += coll->Shift.Position;
item->Pose.Orientation += coll->Shift.Orientation;
coll->Shift = Vector3i::Zero; coll->Shift = Vector3i::Zero;
} }
@ -227,7 +228,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
// Reset collision parameters. // Reset collision parameters.
coll->CollisionType = CollisionType::CT_NONE; coll->CollisionType = CollisionType::CT_NONE;
coll->Shift = Vector3i::Zero; coll->Shift = Pose::Zero;
// Offset base probe position by provided offset, if any. // Offset base probe position by provided offset, if any.
auto entityPos = item->Pose.Position + offset; auto entityPos = item->Pose.Position + offset;
@ -348,6 +349,9 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
coll->Middle.Floor = height; coll->Middle.Floor = height;
coll->Middle.Ceiling = ceiling; coll->Middle.Ceiling = ceiling;
// Additionally calculate bridge shifts, if present.
CollideBridgeItems(*item, *coll, collResult);
// TEST 3: FRONTAL PROBE // TEST 3: FRONTAL PROBE
probePos.x = entityPos.x + xFront; probePos.x = entityPos.x + xFront;
@ -698,21 +702,21 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (coll->Middle.Floor == NO_HEIGHT) if (coll->Middle.Floor == NO_HEIGHT)
{ {
coll->Shift = coll->Setup.OldPosition - entityPos; coll->Shift.Position += coll->Setup.OldPosition - entityPos;
coll->CollisionType = CT_FRONT; coll->CollisionType = CT_FRONT;
return; return;
} }
if (coll->Middle.Floor - coll->Middle.Ceiling <= 0) if (coll->Middle.Floor - coll->Middle.Ceiling <= 0)
{ {
coll->Shift = coll->Setup.OldPosition - entityPos; coll->Shift.Position += coll->Setup.OldPosition - entityPos;
coll->CollisionType = CT_CLAMP; coll->CollisionType = CT_CLAMP;
return; return;
} }
if (coll->Middle.Ceiling >= 0) if (coll->Middle.Ceiling >= 0)
{ {
coll->Shift.y = coll->Middle.Ceiling; coll->Shift.Position.y += coll->Middle.Ceiling;
coll->CollisionType = CT_TOP; coll->CollisionType = CT_TOP;
} }
@ -724,8 +728,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
{ {
if (coll->Front.HasDiagonalSplit()) if (coll->Front.HasDiagonalSplit())
{ {
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x;
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z;
} }
else else
{ {
@ -733,14 +737,14 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
{ {
case 0: case 0:
case 2: case 2:
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x;
coll->Shift.z = FindGridShift(entityPos.z + zFront, entityPos.z); coll->Shift.Position.z += FindGridShift(entityPos.z + zFront, entityPos.z);
break; break;
case 1: case 1:
case 3: case 3:
coll->Shift.x = FindGridShift(entityPos.x + xFront, entityPos.x); coll->Shift.Position.x += FindGridShift(entityPos.x + xFront, entityPos.x);
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z;
break; break;
} }
@ -752,7 +756,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (coll->Front.Ceiling > coll->Setup.LowerCeilingBound || if (coll->Front.Ceiling > coll->Setup.LowerCeilingBound ||
coll->Front.Ceiling < coll->Setup.UpperCeilingBound) coll->Front.Ceiling < coll->Setup.UpperCeilingBound)
{ {
coll->Shift = coll->Setup.OldPosition - entityPos; coll->Shift.Position += coll->Setup.OldPosition - entityPos;
coll->CollisionType = CT_TOP_FRONT; coll->CollisionType = CT_TOP_FRONT;
return; return;
} }
@ -768,8 +772,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
// HACK: Force slight push-out to the left side to avoid stucking // HACK: Force slight push-out to the left side to avoid stucking
TranslateItem(item, coll->Setup.ForwardAngle + ANGLE(8.0f), item->Animation.Velocity.z); TranslateItem(item, coll->Setup.ForwardAngle + ANGLE(8.0f), item->Animation.Velocity.z);
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x;
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z;
} }
else else
{ {
@ -777,12 +781,12 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
{ {
case 0: case 0:
case 2: case 2:
coll->Shift.x = FindGridShift(entityPos.x + xLeft, entityPos.x + xFront); coll->Shift.Position.x += FindGridShift(entityPos.x + xLeft, entityPos.x + xFront);
break; break;
case 1: case 1:
case 3: case 3:
coll->Shift.z = FindGridShift(entityPos.z + zLeft, entityPos.z + zFront); coll->Shift.Position.z += FindGridShift(entityPos.z + zLeft, entityPos.z + zFront);
break; break;
} }
} }
@ -822,8 +826,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
// HACK: Force slight push out to the right side to avoid getting stuck. // HACK: Force slight push out to the right side to avoid getting stuck.
TranslateItem(item, coll->Setup.ForwardAngle - ANGLE(8.0f), item->Animation.Velocity.z); TranslateItem(item, coll->Setup.ForwardAngle - ANGLE(8.0f), item->Animation.Velocity.z);
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x;
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z;
} }
else else
{ {
@ -831,12 +835,12 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
{ {
case 0: case 0:
case 2: case 2:
coll->Shift.x = FindGridShift(entityPos.x + xRight, entityPos.x + xFront); coll->Shift.Position.x += FindGridShift(entityPos.x + xRight, entityPos.x + xFront);
break; break;
case 1: case 1:
case 3: case 3:
coll->Shift.z = FindGridShift(entityPos.z + zRight, entityPos.z + zFront); coll->Shift.Position.z += FindGridShift(entityPos.z + zRight, entityPos.z + zFront);
break; break;
} }
} }

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/game_object_ids.h" #include "Objects/game_object_ids.h"
struct ItemInfo; struct ItemInfo;
struct CollisionInfo;
class FloorInfo; class FloorInfo;
struct ROOM_INFO; struct ROOM_INFO;
struct MESH_INFO; struct MESH_INFO;
@ -105,13 +105,16 @@ struct CollisionInfo
CollisionPosition FrontLeft; CollisionPosition FrontLeft;
CollisionPosition FrontRight; CollisionPosition FrontRight;
Vector3i Shift; Pose Shift = Pose::Zero;
CollisionType CollisionType; CollisionType CollisionType;
Vector2 FloorTilt; // x = x, y = z Vector2 FloorTilt; // x = x, y = z
Vector2 CeilingTilt; // x = x, y = z Vector2 CeilingTilt; // x = x, y = z
short NearestLedgeAngle; short NearestLedgeAngle;
float NearestLedgeDistance; float NearestLedgeDistance;
int LastBridgeItemNumber;
Pose LastBridgeItemPose;
bool HitStatic; bool HitStatic;
bool HitTallObject; bool HitTallObject;

View file

@ -4,6 +4,7 @@
#include <stdexcept> #include <stdexcept>
#include <variant> #include <variant>
#include "Game/collision/collide_room.h"
#include "Game/itemdata/creature_info.h" #include "Game/itemdata/creature_info.h"
#include "Game/itemdata/door_data.h" #include "Game/itemdata/door_data.h"
#include "Game/Lara/lara_struct.h" #include "Game/Lara/lara_struct.h"
@ -56,6 +57,7 @@ class ItemData
PushableInfo, PushableInfo,
ItemInfo*, ItemInfo*,
LaraInfo*, LaraInfo*,
CollisionInfo,
CreatureInfo, CreatureInfo,
WraithInfo, WraithInfo,
GuardianInfo, GuardianInfo,

View file

@ -958,6 +958,8 @@ void DropPickups(ItemInfo* item)
void PickupControl(short itemNumber) void PickupControl(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
ItemPushBridge(*item, *(CollisionInfo*)item->Data);
short roomNumber; short roomNumber;
short triggerFlags = item->TriggerFlags & 0x3F; short triggerFlags = item->TriggerFlags & 0x3F;
@ -1056,9 +1058,13 @@ const GameBoundingBox* FindPlinth(ItemInfo* item)
void InitializePickup(short itemNumber) void InitializePickup(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
auto bounds = GameBoundingBox(item); auto bounds = GameBoundingBox(item);
item->Data = CollisionInfo();
auto* coll = (CollisionInfo*)item->Data;
coll->Setup.Radius = std::max(bounds.GetWidth(), bounds.GetDepth());
coll->Setup.Height = bounds.GetHeight();
short triggerFlags = item->TriggerFlags & 0x3F; short triggerFlags = item->TriggerFlags & 0x3F;
if (triggerFlags == 5) if (triggerFlags == 5)
{ {

View file

@ -253,7 +253,7 @@ namespace TEN::Entities::Doors
if (!(doorItem->ObjectNumber >= ID_LIFT_DOORS1 && if (!(doorItem->ObjectNumber >= ID_LIFT_DOORS1 &&
doorItem->ObjectNumber <= ID_LIFT_DOORS2) || doorItem->ItemFlags[0]) doorItem->ObjectNumber <= ID_LIFT_DOORS2) || doorItem->ItemFlags[0])
{ {
ItemPushItem(doorItem, laraItem, coll, FALSE, TRUE); ItemPushItem(doorItem, laraItem, coll, false, 1);
} }
} }
} }

View file

@ -112,7 +112,7 @@ namespace TEN::Entities::Traps::TR1
return; return;
if (coll->Setup.EnableObjectPush) if (coll->Setup.EnableObjectPush)
ItemPushItem(&item, laraItem, coll, false, true); ItemPushItem(&item, laraItem, coll, false, 1);
if (item.Animation.IsAirborne) if (item.Animation.IsAirborne)
{ {

View file

@ -184,7 +184,7 @@ namespace TEN::Entities::Vehicles
{ {
// HACK: Collision in water behaves differently? @Sezz 2022.06.28 // HACK: Collision in water behaves differently? @Sezz 2022.06.28
if (TestBoundsCollide(UPVItem, laraItem, coll->Setup.Radius) && TestCollision(UPVItem, laraItem)) if (TestBoundsCollide(UPVItem, laraItem, coll->Setup.Radius) && TestCollision(UPVItem, laraItem))
ItemPushItem(UPVItem, laraItem, coll, false, false); ItemPushItem(UPVItem, laraItem, coll, false, 0);
} }
else else
{ {

View file

@ -152,7 +152,7 @@ namespace TEN::Entities::TR4
if (TestCollision(item, laraItem)) if (TestCollision(item, laraItem))
{ {
if (coll->Setup.EnableObjectPush) if (coll->Setup.EnableObjectPush)
ItemPushItem(item, laraItem, coll, 0, 0); ItemPushItem(item, laraItem, coll, false, 0);
} }
} }
} }

View file

@ -106,17 +106,17 @@ void ScalesCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
GlobalCollisionBounds.Z1 = -256; GlobalCollisionBounds.Z1 = -256;
GlobalCollisionBounds.Z2 = 384; GlobalCollisionBounds.Z2 = 384;
ItemPushItem(item, laraItem, coll, 0, 2); ItemPushItem(item, laraItem, coll, false, 2);
GlobalCollisionBounds.X1 = -256; GlobalCollisionBounds.X1 = -256;
GlobalCollisionBounds.X2 = 256; GlobalCollisionBounds.X2 = 256;
ItemPushItem(item, laraItem, coll, 0, 2); ItemPushItem(item, laraItem, coll, false, 2);
GlobalCollisionBounds.X1 = -1280; GlobalCollisionBounds.X1 = -1280;
GlobalCollisionBounds.X2 = -640; GlobalCollisionBounds.X2 = -640;
ItemPushItem(item, laraItem, coll, 0, 2); ItemPushItem(item, laraItem, coll, false, 2);
} }
else else
{ {

View file

@ -29,7 +29,7 @@ namespace TEN::Entities::TR4
int dy = 0; int dy = 0;
int dz = 0; int dz = 0;
if (ItemPushItem(bladeItem, laraItem, coll, 1, 1)) if (ItemPushItem(bladeItem, laraItem, coll, true, 1))
{ {
DoDamage(laraItem, bladeItem->ItemFlags[3]); DoDamage(laraItem, bladeItem->ItemFlags[3]);

View file

@ -57,7 +57,7 @@ namespace TEN::Entities::TR4
DoDamage(laraItem, 10); DoDamage(laraItem, 10);
} }
else if (coll->Setup.EnableObjectPush) else if (coll->Setup.EnableObjectPush)
ItemPushItem(cogItem, laraItem, coll, 0, 0); ItemPushItem(cogItem, laraItem, coll, false, 0);
} }
} }
} }

View file

@ -57,7 +57,7 @@ namespace TEN::Entities::Traps
GlobalCollisionBounds.Z1 = bounds.z; GlobalCollisionBounds.Z1 = bounds.z;
if (TestWithGlobalCollisionBounds(item, laraItem, coll)) if (TestWithGlobalCollisionBounds(item, laraItem, coll))
ItemPushItem(item, laraItem, coll, 0, 2); ItemPushItem(item, laraItem, coll, false, 2);
}*/ }*/
if (!TestBoundsCollide(item, laraItem, coll->Setup.Radius)) if (!TestBoundsCollide(item, laraItem, coll->Setup.Radius))

View file

@ -1,6 +1,7 @@
#include "framework.h" #include "framework.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/collision/floordata.h"
#include "Game/control/lot.h" #include "Game/control/lot.h"
#include "Game/effects/debris.h" #include "Game/effects/debris.h"
#include "Game/effects/item_fx.h" #include "Game/effects/item_fx.h"
@ -21,6 +22,7 @@
#include "Scripting/Internal/TEN/Rotation/Rotation.h" #include "Scripting/Internal/TEN/Rotation/Rotation.h"
#include "Scripting/Internal/TEN/Vec3/Vec3.h" #include "Scripting/Internal/TEN/Vec3/Vec3.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Effects::Items; using namespace TEN::Effects::Items;
/*** /***
@ -576,6 +578,10 @@ void Moveable::SetPos(Vec3 const& pos, sol::optional<bool> updateRoom)
SetRoomNumber(potentialNewRoom); SetRoomNumber(potentialNewRoom);
} }
} }
const auto& object = Objects[m_item->ObjectNumber];
if (object.floor != nullptr || object.ceiling != nullptr)
UpdateBridgeItem((int)m_item->Index);
} }
Vec3 Moveable::GetJointPos(int jointIndex) const Vec3 Moveable::GetJointPos(int jointIndex) const
@ -598,11 +604,15 @@ Rotation Moveable::GetRot() const
}; };
} }
void Moveable::SetRot(Rotation const& rot) void Moveable::SetRot(const Rotation& rot)
{ {
m_item->Pose.Orientation.x = ANGLE(rot.x); m_item->Pose.Orientation.x = ANGLE(rot.x);
m_item->Pose.Orientation.y = ANGLE(rot.y); m_item->Pose.Orientation.y = ANGLE(rot.y);
m_item->Pose.Orientation.z = ANGLE(rot.z); m_item->Pose.Orientation.z = ANGLE(rot.z);
const auto& object = Objects[m_item->ObjectNumber];
if (object.floor != nullptr || object.ceiling != nullptr)
UpdateBridgeItem(m_item->Index);
} }
/// Get current HP (hit points/health points) /// Get current HP (hit points/health points)

View file

@ -187,8 +187,17 @@ void LoadItems()
memcpy(&item->StartPose, &item->Pose, sizeof(Pose)); memcpy(&item->StartPose, &item->Pose, sizeof(Pose));
} }
for (int i = 0; i < g_Level.NumItems; i++) // Initialize all bridges first.
InitializeItem(i); // It is needed because some other items need final floor height to init properly.
for (int isFloor = 0; isFloor <= 1; isFloor++)
{
for (int i = 0; i < g_Level.NumItems; i++)
{
if ((Objects[g_Level.Items[i].ObjectNumber].floor == nullptr) == (bool)isFloor)
InitializeItem(i);
}
}
} }
} }