From 35304ce84a33faa9bdec8c1ebe81938f3795f5d1 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 17 Jun 2023 12:52:02 +0300 Subject: [PATCH] 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 Co-authored-by: Jakub <80340234+Kubsy@users.noreply.github.com> --- Documentation/Changes.txt | 2 + TombEngine/Game/Lara/lara_collide.cpp | 8 +- TombEngine/Game/Lara/lara_monkey.cpp | 4 +- TombEngine/Game/Lara/lara_tests.cpp | 8 +- TombEngine/Game/collision/collide_item.cpp | 162 ++++++++++++++---- TombEngine/Game/collision/collide_item.h | 10 +- TombEngine/Game/collision/collide_room.cpp | 44 ++--- TombEngine/Game/collision/collide_room.h | 7 +- TombEngine/Game/itemdata/itemdata.h | 2 + TombEngine/Game/pickup/pickup.cpp | 8 +- .../Objects/Generic/Doors/generic_doors.cpp | 2 +- TombEngine/Objects/TR1/Trap/DamoclesSword.cpp | 2 +- TombEngine/Objects/TR3/Vehicles/upv.cpp | 2 +- .../Objects/TR4/Object/tr4_element_puzzle.cpp | 2 +- TombEngine/Objects/TR4/Object/tr4_scales.cpp | 6 +- TombEngine/Objects/TR4/Trap/tr4_blade.cpp | 2 +- TombEngine/Objects/TR4/Trap/tr4_cog.cpp | 2 +- TombEngine/Objects/TR4/Trap/tr4_stargate.cpp | 2 +- .../TEN/Objects/Moveable/MoveableObject.cpp | 12 +- TombEngine/Specific/level.cpp | 13 +- 20 files changed, 223 insertions(+), 77 deletions(-) diff --git a/Documentation/Changes.txt b/Documentation/Changes.txt index 169aa413f..9644ab7a9 100644 --- a/Documentation/Changes.txt +++ b/Documentation/Changes.txt @@ -14,6 +14,8 @@ Version 1.1.0 * Add multiple doppelgangers by using the same OCB for the origin nullmesh and doppelganger. * Pause all sounds when entering inventory or pause menu. * 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 Kold. diff --git a/TombEngine/Game/Lara/lara_collide.cpp b/TombEngine/Game/Lara/lara_collide.cpp index 928c4c924..378914128 100644 --- a/TombEngine/Game/Lara/lara_collide.cpp +++ b/TombEngine/Game/Lara/lara_collide.cpp @@ -48,6 +48,10 @@ bool LaraDeflectEdge(ItemInfo* item, CollisionInfo* coll) ShiftItem(item, coll); item->Pose.Orientation.y -= ANGLE(coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE); } + else if (coll->LastBridgeItemNumber != NO_ITEM) + { + ShiftItem(item, coll); + } return false; } @@ -202,8 +206,8 @@ bool LaraDeflectEdgeCrawl(ItemInfo* item, CollisionInfo* coll) bool LaraDeflectEdgeMonkey(ItemInfo* item, CollisionInfo* coll) { // HACK - if (coll->Shift.y >= 0 && coll->Shift.y <= CLICK(1.25f)) - coll->Shift.y = 0; + if (coll->Shift.Position.y >= 0 && coll->Shift.Position.y <= CLICK(1.25f)) + coll->Shift.Position.y = 0; if (coll->CollisionType == CT_FRONT || coll->CollisionType == CT_TOP_FRONT || coll->HitTallObject) diff --git a/TombEngine/Game/Lara/lara_monkey.cpp b/TombEngine/Game/Lara/lara_monkey.cpp index 8d6637ef3..f7f453f76 100644 --- a/TombEngine/Game/Lara/lara_monkey.cpp +++ b/TombEngine/Game/Lara/lara_monkey.cpp @@ -140,8 +140,8 @@ void lara_col_monkey_idle(ItemInfo* item, CollisionInfo* coll) GetCollisionInfo(coll, item); // 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)) - coll->Shift.y = 0; + if (coll->Shift.Position.y >= 0 && coll->Shift.Position.y <= CLICK(1.25f)) + coll->Shift.Position.y = 0; ShiftItem(item, coll); if (TestLaraMonkeyFall(item, coll)) diff --git a/TombEngine/Game/Lara/lara_tests.cpp b/TombEngine/Game/Lara/lara_tests.cpp index 8714d9afa..acc0effee 100644 --- a/TombEngine/Game/Lara/lara_tests.cpp +++ b/TombEngine/Game/Lara/lara_tests.cpp @@ -250,9 +250,9 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll) else // Death, incorrect ledge or ACTION release { 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.z += coll->Shift.z; + item->Pose.Position.z += coll->Shift.Position.z; item->Animation.IsAirborne = true; item->Animation.Velocity.z = 2; item->Animation.Velocity.y = 1; @@ -472,12 +472,12 @@ bool TestLaraHangOnClimbableWall(ItemInfo* item, CollisionInfo* coll) { case NORTH: case SOUTH: - item->Pose.Position.z += coll2.Shift.z; + item->Pose.Position.z += coll2.Shift.Position.z; break; case EAST: case WEST: - item->Pose.Position.x += coll2.Shift.x; + item->Pose.Position.x += coll2.Shift.Position.x; break; default: diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 14e5d122e..57354027e 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -753,6 +753,54 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool ena 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(). 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; } +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) { coll->HitTallObject = false; @@ -1057,20 +1161,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& case NORTH: if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius) { - coll->Shift.z = rawShift.z; - coll->Shift.x = ox - x; + coll->Shift.Position.z = rawShift.z; + coll->Shift.Position.x = ox - x; coll->CollisionType = CT_FRONT; } else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius) { - coll->Shift.x = rawShift.x; - coll->Shift.z = 0; + coll->Shift.Position.x = rawShift.x; + coll->Shift.Position.z = 0; coll->CollisionType = CT_LEFT; } else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius) { - coll->Shift.x = rawShift.x; - coll->Shift.z = 0; + coll->Shift.Position.x = rawShift.x; + coll->Shift.Position.z = 0; coll->CollisionType = CT_RIGHT; } @@ -1079,20 +1183,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& case SOUTH: if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius) { - coll->Shift.z = rawShift.z; - coll->Shift.x = ox - x; + coll->Shift.Position.z = rawShift.z; + coll->Shift.Position.x = ox - x; coll->CollisionType = CT_FRONT; } else if (rawShift.x > 0 && rawShift.x <= coll->Setup.Radius) { - coll->Shift.x = rawShift.x; - coll->Shift.z = 0; + coll->Shift.Position.x = rawShift.x; + coll->Shift.Position.z = 0; coll->CollisionType = CT_RIGHT; } else if (rawShift.x < 0 && rawShift.x >= -coll->Setup.Radius) { - coll->Shift.x = rawShift.x; - coll->Shift.z = 0; + coll->Shift.Position.x = rawShift.x; + coll->Shift.Position.z = 0; coll->CollisionType = CT_LEFT; } @@ -1101,20 +1205,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& case EAST: if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius) { - coll->Shift.x = rawShift.x; - coll->Shift.z = oz - z; + coll->Shift.Position.x = rawShift.x; + coll->Shift.Position.z = oz - z; coll->CollisionType = CT_FRONT; } else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius) { - coll->Shift.z = rawShift.z; - coll->Shift.x = 0; + coll->Shift.Position.z = rawShift.z; + coll->Shift.Position.x = 0; coll->CollisionType = CT_RIGHT; } else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius) { - coll->Shift.z = rawShift.z; - coll->Shift.x = 0; + coll->Shift.Position.z = rawShift.z; + coll->Shift.Position.x = 0; coll->CollisionType = CT_LEFT; } @@ -1123,20 +1227,20 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& case WEST: if (rawShift.z > coll->Setup.Radius || rawShift.z < -coll->Setup.Radius) { - coll->Shift.x = rawShift.x; - coll->Shift.z = oz - z; + coll->Shift.Position.x = rawShift.x; + coll->Shift.Position.z = oz - z; coll->CollisionType = CT_FRONT; } else if (rawShift.z > 0 && rawShift.z <= coll->Setup.Radius) { - coll->Shift.z = rawShift.z; - coll->Shift.x = 0; + coll->Shift.Position.z = rawShift.z; + coll->Shift.Position.x = 0; coll->CollisionType = CT_LEFT; } else if (rawShift.z < 0 && rawShift.z >= -coll->Setup.Radius) { - coll->Shift.z = rawShift.z; - coll->Shift.x = 0; + coll->Shift.Position.z = rawShift.z; + coll->Shift.Position.x = 0; coll->CollisionType = CT_RIGHT; } @@ -1144,15 +1248,15 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& } // 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); cosY = phd_cos(-pose.Orientation.y); // Calculate final shifts orientation/distance. - coll->Shift.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.x = (round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x) - item->Pose.Position.x; + 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. // 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 (coll->Setup.EnableObjectPush) - ItemPushItem(item, laraItem, coll, false, true); + ItemPushItem(item, laraItem, coll, false, 1); } } } diff --git a/TombEngine/Game/collision/collide_item.h b/TombEngine/Game/collision/collide_item.h index 78ca817a8..c7e93cc6b 100644 --- a/TombEngine/Game/collision/collide_item.h +++ b/TombEngine/Game/collision/collide_item.h @@ -1,10 +1,9 @@ #pragma once #include "Math/Math.h" -using std::pair; - class FloorInfo; struct CollisionInfo; +struct CollisionResult; struct ItemInfo; struct MESH_INFO; @@ -19,8 +18,8 @@ extern MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS]; struct ObjectCollisionBounds { - GameBoundingBox BoundingBox = GameBoundingBox::Zero; - pair OrientConstraint = {}; + GameBoundingBox BoundingBox = GameBoundingBox::Zero; + std::pair OrientConstraint = {}; }; 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 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* item2); 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); void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll); +void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, const CollisionResult& collResult); void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void ObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); diff --git a/TombEngine/Game/collision/collide_room.cpp b/TombEngine/Game/collision/collide_room.cpp index c3c764e72..39befcdf5 100644 --- a/TombEngine/Game/collision/collide_room.cpp +++ b/TombEngine/Game/collision/collide_room.cpp @@ -18,7 +18,8 @@ using namespace TEN::Renderer; 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; } @@ -227,7 +228,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse // Reset collision parameters. coll->CollisionType = CollisionType::CT_NONE; - coll->Shift = Vector3i::Zero; + coll->Shift = Pose::Zero; // Offset base probe position by provided offset, if any. 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.Ceiling = ceiling; + // Additionally calculate bridge shifts, if present. + CollideBridgeItems(*item, *coll, collResult); + // TEST 3: FRONTAL PROBE probePos.x = entityPos.x + xFront; @@ -698,21 +702,21 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse if (coll->Middle.Floor == NO_HEIGHT) { - coll->Shift = coll->Setup.OldPosition - entityPos; + coll->Shift.Position += coll->Setup.OldPosition - entityPos; coll->CollisionType = CT_FRONT; return; } 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; return; } if (coll->Middle.Ceiling >= 0) { - coll->Shift.y = coll->Middle.Ceiling; + coll->Shift.Position.y += coll->Middle.Ceiling; coll->CollisionType = CT_TOP; } @@ -724,8 +728,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse { if (coll->Front.HasDiagonalSplit()) { - coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; - coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; + coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x; + coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z; } else { @@ -733,14 +737,14 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse { case 0: case 2: - coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; - coll->Shift.z = FindGridShift(entityPos.z + zFront, entityPos.z); + coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x; + coll->Shift.Position.z += FindGridShift(entityPos.z + zFront, entityPos.z); break; case 1: case 3: - coll->Shift.x = FindGridShift(entityPos.x + xFront, entityPos.x); - coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; + coll->Shift.Position.x += FindGridShift(entityPos.x + xFront, entityPos.x); + coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z; break; } @@ -752,7 +756,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse if (coll->Front.Ceiling > coll->Setup.LowerCeilingBound || coll->Front.Ceiling < coll->Setup.UpperCeilingBound) { - coll->Shift = coll->Setup.OldPosition - entityPos; + coll->Shift.Position += coll->Setup.OldPosition - entityPos; coll->CollisionType = CT_TOP_FRONT; 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 TranslateItem(item, coll->Setup.ForwardAngle + ANGLE(8.0f), item->Animation.Velocity.z); - coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; - coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; + coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x; + coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z; } else { @@ -777,12 +781,12 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse { case 0: case 2: - coll->Shift.x = FindGridShift(entityPos.x + xLeft, entityPos.x + xFront); + coll->Shift.Position.x += FindGridShift(entityPos.x + xLeft, entityPos.x + xFront); break; case 1: case 3: - coll->Shift.z = FindGridShift(entityPos.z + zLeft, entityPos.z + zFront); + coll->Shift.Position.z += FindGridShift(entityPos.z + zLeft, entityPos.z + zFront); 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. TranslateItem(item, coll->Setup.ForwardAngle - ANGLE(8.0f), item->Animation.Velocity.z); - coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x; - coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z; + coll->Shift.Position.x += coll->Setup.OldPosition.x - entityPos.x; + coll->Shift.Position.z += coll->Setup.OldPosition.z - entityPos.z; } else { @@ -831,12 +835,12 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse { case 0: case 2: - coll->Shift.x = FindGridShift(entityPos.x + xRight, entityPos.x + xFront); + coll->Shift.Position.x += FindGridShift(entityPos.x + xRight, entityPos.x + xFront); break; case 1: case 3: - coll->Shift.z = FindGridShift(entityPos.z + zRight, entityPos.z + zFront); + coll->Shift.Position.z += FindGridShift(entityPos.z + zRight, entityPos.z + zFront); break; } } diff --git a/TombEngine/Game/collision/collide_room.h b/TombEngine/Game/collision/collide_room.h index bacacc517..62087bd2a 100644 --- a/TombEngine/Game/collision/collide_room.h +++ b/TombEngine/Game/collision/collide_room.h @@ -1,9 +1,9 @@ #pragma once + #include "Math/Math.h" #include "Objects/game_object_ids.h" struct ItemInfo; -struct CollisionInfo; class FloorInfo; struct ROOM_INFO; struct MESH_INFO; @@ -105,13 +105,16 @@ struct CollisionInfo CollisionPosition FrontLeft; CollisionPosition FrontRight; - Vector3i Shift; + Pose Shift = Pose::Zero; CollisionType CollisionType; Vector2 FloorTilt; // x = x, y = z Vector2 CeilingTilt; // x = x, y = z short NearestLedgeAngle; float NearestLedgeDistance; + int LastBridgeItemNumber; + Pose LastBridgeItemPose; + bool HitStatic; bool HitTallObject; diff --git a/TombEngine/Game/itemdata/itemdata.h b/TombEngine/Game/itemdata/itemdata.h index 57a454009..2b3cc6f68 100644 --- a/TombEngine/Game/itemdata/itemdata.h +++ b/TombEngine/Game/itemdata/itemdata.h @@ -4,6 +4,7 @@ #include #include +#include "Game/collision/collide_room.h" #include "Game/itemdata/creature_info.h" #include "Game/itemdata/door_data.h" #include "Game/Lara/lara_struct.h" @@ -56,6 +57,7 @@ class ItemData PushableInfo, ItemInfo*, LaraInfo*, + CollisionInfo, CreatureInfo, WraithInfo, GuardianInfo, diff --git a/TombEngine/Game/pickup/pickup.cpp b/TombEngine/Game/pickup/pickup.cpp index 5775af763..45806ae62 100644 --- a/TombEngine/Game/pickup/pickup.cpp +++ b/TombEngine/Game/pickup/pickup.cpp @@ -958,6 +958,8 @@ void DropPickups(ItemInfo* item) void PickupControl(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; + + ItemPushBridge(*item, *(CollisionInfo*)item->Data); short roomNumber; short triggerFlags = item->TriggerFlags & 0x3F; @@ -1056,9 +1058,13 @@ const GameBoundingBox* FindPlinth(ItemInfo* item) void InitializePickup(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; - 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; if (triggerFlags == 5) { diff --git a/TombEngine/Objects/Generic/Doors/generic_doors.cpp b/TombEngine/Objects/Generic/Doors/generic_doors.cpp index 2676893a0..096f1a457 100644 --- a/TombEngine/Objects/Generic/Doors/generic_doors.cpp +++ b/TombEngine/Objects/Generic/Doors/generic_doors.cpp @@ -253,7 +253,7 @@ namespace TEN::Entities::Doors if (!(doorItem->ObjectNumber >= ID_LIFT_DOORS1 && doorItem->ObjectNumber <= ID_LIFT_DOORS2) || doorItem->ItemFlags[0]) { - ItemPushItem(doorItem, laraItem, coll, FALSE, TRUE); + ItemPushItem(doorItem, laraItem, coll, false, 1); } } } diff --git a/TombEngine/Objects/TR1/Trap/DamoclesSword.cpp b/TombEngine/Objects/TR1/Trap/DamoclesSword.cpp index 6ed41ad2b..b2de4d97e 100644 --- a/TombEngine/Objects/TR1/Trap/DamoclesSword.cpp +++ b/TombEngine/Objects/TR1/Trap/DamoclesSword.cpp @@ -112,7 +112,7 @@ namespace TEN::Entities::Traps::TR1 return; if (coll->Setup.EnableObjectPush) - ItemPushItem(&item, laraItem, coll, false, true); + ItemPushItem(&item, laraItem, coll, false, 1); if (item.Animation.IsAirborne) { diff --git a/TombEngine/Objects/TR3/Vehicles/upv.cpp b/TombEngine/Objects/TR3/Vehicles/upv.cpp index fa57ef8fb..530b8f7a9 100644 --- a/TombEngine/Objects/TR3/Vehicles/upv.cpp +++ b/TombEngine/Objects/TR3/Vehicles/upv.cpp @@ -184,7 +184,7 @@ namespace TEN::Entities::Vehicles { // HACK: Collision in water behaves differently? @Sezz 2022.06.28 if (TestBoundsCollide(UPVItem, laraItem, coll->Setup.Radius) && TestCollision(UPVItem, laraItem)) - ItemPushItem(UPVItem, laraItem, coll, false, false); + ItemPushItem(UPVItem, laraItem, coll, false, 0); } else { diff --git a/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp b/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp index 5c0750b24..60f6e29a9 100644 --- a/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp +++ b/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp @@ -152,7 +152,7 @@ namespace TEN::Entities::TR4 if (TestCollision(item, laraItem)) { if (coll->Setup.EnableObjectPush) - ItemPushItem(item, laraItem, coll, 0, 0); + ItemPushItem(item, laraItem, coll, false, 0); } } } diff --git a/TombEngine/Objects/TR4/Object/tr4_scales.cpp b/TombEngine/Objects/TR4/Object/tr4_scales.cpp index e14cbcc0c..96258fd65 100644 --- a/TombEngine/Objects/TR4/Object/tr4_scales.cpp +++ b/TombEngine/Objects/TR4/Object/tr4_scales.cpp @@ -106,17 +106,17 @@ void ScalesCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) GlobalCollisionBounds.Z1 = -256; GlobalCollisionBounds.Z2 = 384; - ItemPushItem(item, laraItem, coll, 0, 2); + ItemPushItem(item, laraItem, coll, false, 2); GlobalCollisionBounds.X1 = -256; GlobalCollisionBounds.X2 = 256; - ItemPushItem(item, laraItem, coll, 0, 2); + ItemPushItem(item, laraItem, coll, false, 2); GlobalCollisionBounds.X1 = -1280; GlobalCollisionBounds.X2 = -640; - ItemPushItem(item, laraItem, coll, 0, 2); + ItemPushItem(item, laraItem, coll, false, 2); } else { diff --git a/TombEngine/Objects/TR4/Trap/tr4_blade.cpp b/TombEngine/Objects/TR4/Trap/tr4_blade.cpp index 0efe579a0..cb6369593 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_blade.cpp +++ b/TombEngine/Objects/TR4/Trap/tr4_blade.cpp @@ -29,7 +29,7 @@ namespace TEN::Entities::TR4 int dy = 0; int dz = 0; - if (ItemPushItem(bladeItem, laraItem, coll, 1, 1)) + if (ItemPushItem(bladeItem, laraItem, coll, true, 1)) { DoDamage(laraItem, bladeItem->ItemFlags[3]); diff --git a/TombEngine/Objects/TR4/Trap/tr4_cog.cpp b/TombEngine/Objects/TR4/Trap/tr4_cog.cpp index 66b6101a7..e334eb854 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_cog.cpp +++ b/TombEngine/Objects/TR4/Trap/tr4_cog.cpp @@ -57,7 +57,7 @@ namespace TEN::Entities::TR4 DoDamage(laraItem, 10); } else if (coll->Setup.EnableObjectPush) - ItemPushItem(cogItem, laraItem, coll, 0, 0); + ItemPushItem(cogItem, laraItem, coll, false, 0); } } } diff --git a/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp b/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp index d7a43f79f..ff09c5c3e 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp +++ b/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp @@ -57,7 +57,7 @@ namespace TEN::Entities::Traps GlobalCollisionBounds.Z1 = bounds.z; if (TestWithGlobalCollisionBounds(item, laraItem, coll)) - ItemPushItem(item, laraItem, coll, 0, 2); + ItemPushItem(item, laraItem, coll, false, 2); }*/ if (!TestBoundsCollide(item, laraItem, coll->Setup.Radius)) diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp index 666f5a054..81c4d69fd 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp @@ -1,6 +1,7 @@ #include "framework.h" #include "Game/items.h" +#include "Game/collision/floordata.h" #include "Game/control/lot.h" #include "Game/effects/debris.h" #include "Game/effects/item_fx.h" @@ -21,6 +22,7 @@ #include "Scripting/Internal/TEN/Rotation/Rotation.h" #include "Scripting/Internal/TEN/Vec3/Vec3.h" +using namespace TEN::Collision::Floordata; using namespace TEN::Effects::Items; /*** @@ -576,6 +578,10 @@ void Moveable::SetPos(Vec3 const& pos, sol::optional updateRoom) 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 @@ -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.y = ANGLE(rot.y); 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) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 4bab19109..25cfd2f0d 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -187,8 +187,17 @@ void LoadItems() memcpy(&item->StartPose, &item->Pose, sizeof(Pose)); } - for (int i = 0; i < g_Level.NumItems; i++) - InitializeItem(i); + // Initialize all bridges first. + // 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); + } + } } }