From 06a70d364f4b270e11580e6adf9cd2166b538f14 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:19:52 +0100 Subject: [PATCH 001/112] Fixed #1455 --- CHANGELOG.md | 3 ++- TombEngine/Sound/sound.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b72ca25e4..a50609928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,11 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Bug fixes -* Fixed engine performance around multiple stacked bridges. +* Fixed engine performance around bridges. * Fixed engine performance if weather effects are active. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. +* Fixed audio tracks placed in subfolders not restoring after loading savegame. ### Features/Amendments diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index fdd831cbc..5657764b1 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -695,7 +695,7 @@ std::pair GetSoundTrackNameAndPosition(SoundTrackType type) return std::pair(); std::filesystem::path path = track.Track; - return std::pair(path.stem().string(), BASS_ChannelGetPosition(track.Channel, BASS_POS_BYTE)); + return std::pair(path.string(), BASS_ChannelGetPosition(track.Channel, BASS_POS_BYTE)); } static void CALLBACK Sound_FinishOneshotTrack(HSYNC handle, DWORD channel, DWORD data, void* userData) From 506ee0f088ef4067983b0ad6ae75bcc9dcd92e25 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:26:15 +0100 Subject: [PATCH 002/112] Fixed #1459 --- CHANGELOG.md | 1 + TombEngine/Game/control/control.cpp | 4 ++++ TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp | 1 - 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a50609928..08c0b8afa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. * Fixed audio tracks placed in subfolders not restoring after loading savegame. +* Fixed Lara's Home entry not working. ### Features/Amendments diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 43c3bd150..8f34aebd9 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -632,6 +632,10 @@ GameStatus HandleMenuCalls(bool isTitle) case InventoryResult::NewGameSelectedLevel: return GameStatus::NewGame; + case InventoryResult::HomeLevel: + return GameStatus::HomeLevel; + break; + case InventoryResult::LoadGame: return GameStatus::LoadGame; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp index af4c9a948..4b880af1d 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp @@ -701,7 +701,6 @@ bool FlowHandler::DoFlow() case GameStatus::NewGame: // NOTE: 0 reserved for title level and 1 reserved for home level. CurrentLevel = (SelectedLevelForNewGame != 0) ? SelectedLevelForNewGame : (IsHomeLevelEnabled() ? 2 : 1); - RequiredStartPos = 0; SelectedLevelForNewGame = 0; InitializeGame = true; From 50a7fa3b17604eef56c5bbea1204097d1bca9184 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 9 Nov 2024 00:06:40 +0100 Subject: [PATCH 003/112] Return false for IsPointInRoom for inactive flipped rooms --- TombEngine/Game/room.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index 2e8841634..dab20b9ab 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -219,6 +219,9 @@ bool IsPointInRoom(const Vector3i& pos, int roomNumber) { const auto& room = g_Level.Rooms[roomNumber]; + if (!room.Active()) + return false; + if (pos.z >= (room.Position.z + BLOCK(1)) && pos.z <= (room.Position.z + BLOCK(room.ZSize - 1)) && pos.y <= room.BottomHeight && pos.y > room.TopHeight && pos.x >= (room.Position.x + BLOCK(1)) && pos.x <= (room.Position.x + BLOCK(room.XSize - 1))) From ed434aaffea49f8789c3e5018ea38e0ae8906c0a Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 9 Nov 2024 00:13:27 +0100 Subject: [PATCH 004/112] Update room.cpp --- TombEngine/Game/room.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index dab20b9ab..b2f6bf583 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -240,21 +240,20 @@ int FindRoomNumber(const Vector3i& pos, int startRoomNumber, bool onlyNeighbors) for (int neighborRoomNumber : room.NeighborRoomNumbers) { const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber]; - if (neighborRoomNumber != startRoomNumber && neighborRoom.Active() && - IsPointInRoom(pos, neighborRoomNumber)) + if (neighborRoomNumber != startRoomNumber && IsPointInRoom(pos, neighborRoomNumber)) { return neighborRoomNumber; } } } - if (onlyNeighbors) - return startRoomNumber; - - for (int roomNumber = 0; roomNumber < g_Level.Rooms.size(); roomNumber++) + if (!onlyNeighbors) { - if (IsPointInRoom(pos, roomNumber) && g_Level.Rooms[roomNumber].Active()) - return roomNumber; + for (int roomNumber = 0; roomNumber < g_Level.Rooms.size(); roomNumber++) + { + if (IsPointInRoom(pos, roomNumber)) + return roomNumber; + } } return (startRoomNumber != NO_VALUE) ? startRoomNumber : 0; From 718997336bab218ccbdb3d4fea4f0535445316b6 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 9 Nov 2024 08:10:52 +0100 Subject: [PATCH 005/112] Fix git merge error --- TombEngine/Game/control/control.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 8f34aebd9..11c478b3a 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -157,6 +157,9 @@ GameStatus ControlPhase(bool insideMenu) g_GameScript->OnLoop(DELTA_TIME, false); // TODO: Don't use DELTA_TIME constant with high framerate. HandleAllGlobalEvents(EventType::Loop, (Activator)LaraItem->Index); + // Clear last selected item in inventory (must be after on loop event handling, so they can detect that). + g_Gui.CancelInventorySelection(); + // Control lock is processed after handling scripts because builder may want to process input externally while locking player from input. if (!isTitle && Lara.Control.IsLocked) ClearAllActions(); @@ -170,14 +173,6 @@ GameStatus ControlPhase(bool insideMenu) // Smash shatters and clear stopper flags under them. UpdateShatters(); - // Clear last selected item in inventory (must be after on loop event handling, so they can detect that). - g_Gui.CancelInventorySelection(); - - // Control lock is processed after handling scripts because builder may want to - // process input externally while locking player from input. - if (!isTitle && Lara.Control.IsLocked) - ClearAllActions(); - // Update weather. Weather.Update(); From 2b5e1cf4486852c003c3b0280e55fd629881a418 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 9 Nov 2024 08:23:28 +0100 Subject: [PATCH 006/112] Simplify action queue code, fix potential problem with scripted keypresses --- CHANGELOG.md | 1 + TombEngine/Game/control/control.cpp | 10 ++++++++-- TombEngine/Specific/Input/Input.cpp | 6 ------ TombEngine/Specific/Input/Input.h | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08c0b8afa..a28647532 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. * Fixed audio tracks placed in subfolders not restoring after loading savegame. +* Fixed scripted input events not registering on the same game frame. * Fixed Lara's Home entry not working. ### Features/Amendments diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 11c478b3a..14c919c50 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -157,14 +157,20 @@ GameStatus ControlPhase(bool insideMenu) g_GameScript->OnLoop(DELTA_TIME, false); // TODO: Don't use DELTA_TIME constant with high framerate. HandleAllGlobalEvents(EventType::Loop, (Activator)LaraItem->Index); + // Queued input actions are read again after OnLoop, so that remaining control loop can immediately register + // emulated keypresses from the script. + ApplyActionQueue(); + // Clear last selected item in inventory (must be after on loop event handling, so they can detect that). g_Gui.CancelInventorySelection(); - // Control lock is processed after handling scripts because builder may want to process input externally while locking player from input. + // Control lock is processed after handling scripts because builder may want to process input externally + // while locking player from input. if (!isTitle && Lara.Control.IsLocked) ClearAllActions(); - // Item update should happen before camera update, so potential flyby/track camera triggers are processed correctly. + // Item update should happen before camera update, so potential flyby/track camera triggers + // are processed correctly. UpdateAllItems(); UpdateAllEffects(); UpdateLara(LaraItem, isTitle); diff --git a/TombEngine/Specific/Input/Input.cpp b/TombEngine/Specific/Input/Input.cpp index 594992735..6a1779225 100644 --- a/TombEngine/Specific/Input/Input.cpp +++ b/TombEngine/Specific/Input/Input.cpp @@ -278,10 +278,7 @@ namespace TEN::Input break; } } - } - void ClearActionQueue() - { for (auto& queue : ActionQueue) queue = QueueState::None; } @@ -718,10 +715,7 @@ namespace TEN::Input action.Update(Key((int)action.GetID())); if (applyQueue) - { ApplyActionQueue(); - ClearActionQueue(); - } // Additional handling. HandleHotkeyActions(); diff --git a/TombEngine/Specific/Input/Input.h b/TombEngine/Specific/Input/Input.h index 1c25c0099..9789a4da4 100644 --- a/TombEngine/Specific/Input/Input.h +++ b/TombEngine/Specific/Input/Input.h @@ -98,7 +98,6 @@ namespace TEN::Input void DefaultConflict(); void UpdateInputActions(ItemInfo* item, bool applyQueue = false); void ApplyActionQueue(); - void ClearActionQueue(); void ClearAllActions(); void Rumble(float power, float delayInSec = 0.3f, RumbleMode mode = RumbleMode::Both); void StopRumble(); From b5502103c0cf4b2d3c38d97e4497bd8e6fabf8a6 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 9 Nov 2024 18:59:22 +0100 Subject: [PATCH 007/112] Fix lensflare object --- CHANGELOG.md | 1 + TombEngine/Game/collision/collide_item.cpp | 2 +- TombEngine/Objects/Effects/LensFlare.cpp | 2 +- TombEngine/Objects/Effects/effect_objects.cpp | 3 +++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a28647532..430aa94ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed audio tracks placed in subfolders not restoring after loading savegame. * Fixed scripted input events not registering on the same game frame. * Fixed Lara's Home entry not working. +* Fixed Lens Flare object not functioning properly. ### Features/Amendments diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index eaaa9263b..2b906dbab 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -152,7 +152,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, // Ignore items not feasible for collision. if (item.Index == collidingItem.Index || - item.Flags & IFLAG_KILLED || item.MeshBits == NO_JOINT_BITS || + item.Flags & IFLAG_KILLED || item.MeshBits == NO_JOINT_BITS || !item.Collidable || (object.drawRoutine == nullptr && !item.IsLara()) || (object.collision == nullptr && !item.IsLara())) { diff --git a/TombEngine/Objects/Effects/LensFlare.cpp b/TombEngine/Objects/Effects/LensFlare.cpp index 788b64c75..a9932cc65 100644 --- a/TombEngine/Objects/Effects/LensFlare.cpp +++ b/TombEngine/Objects/Effects/LensFlare.cpp @@ -100,7 +100,7 @@ namespace TEN::Entities::Effects auto& item = g_Level.Items[itemNumber]; if (TriggerActive(&item)) - SetupLensFlare(item.Pose.Position.ToVector3(), item.RoomNumber, Color(), false, SPRITE_TYPES::SPR_LENS_FLARE_3); + SetupLensFlare(item.Pose.Position.ToVector3(), item.RoomNumber, item.Model.Color, false, SPRITE_TYPES::SPR_LENS_FLARE_3); } void ClearLensFlares() diff --git a/TombEngine/Objects/Effects/effect_objects.cpp b/TombEngine/Objects/Effects/effect_objects.cpp index 0b6b1f9ec..ef905c561 100644 --- a/TombEngine/Objects/Effects/effect_objects.cpp +++ b/TombEngine/Objects/Effects/effect_objects.cpp @@ -58,5 +58,8 @@ void InitializeEffectsObjects() obj = &Objects[ID_LENS_FLARE]; if (obj->loaded) + { + obj->drawRoutine = nullptr; obj->control = ControlLensFlare; + } } From 908fc612834ed1262b68fee2a26985b05563f12f Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 9 Nov 2024 23:12:29 +0100 Subject: [PATCH 008/112] Fix inventory item cancel --- TombEngine/Game/control/control.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 14c919c50..0c9cdae63 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -161,9 +161,6 @@ GameStatus ControlPhase(bool insideMenu) // emulated keypresses from the script. ApplyActionQueue(); - // Clear last selected item in inventory (must be after on loop event handling, so they can detect that). - g_Gui.CancelInventorySelection(); - // Control lock is processed after handling scripts because builder may want to process input externally // while locking player from input. if (!isTitle && Lara.Control.IsLocked) @@ -179,6 +176,9 @@ GameStatus ControlPhase(bool insideMenu) // Smash shatters and clear stopper flags under them. UpdateShatters(); + // Clear last selected item in inventory (must be after on loop event handling, so they can detect that). + g_Gui.CancelInventorySelection(); + // Update weather. Weather.Update(); From f0577706965a8354eebc6583b5e14f002f7b8de3 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 10 Nov 2024 08:07:18 +0100 Subject: [PATCH 009/112] Fixed incorrect object camera position --- CHANGELOG.md | 1 + TombEngine/Game/camera.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 430aa94ea..ce24af1f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed enemy pickups dropping on death sectors. * Fixed audio tracks placed in subfolders not restoring after loading savegame. * Fixed scripted input events not registering on the same game frame. +* Fixed incorrect object camera position. * Fixed Lara's Home entry not working. * Fixed Lens Flare object not functioning properly. diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index 0c55a0508..e5d0d8101 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -409,7 +409,7 @@ void ObjCamera(ItemInfo* camSlotId, int camMeshId, ItemInfo* targetItem, int tar //get mesh 0 coordinates. auto pos = GetJointPosition(camSlotId, 0, Vector3i::Zero); - auto dest = Vector3(pos.x, pos.y, pos.z); + auto dest = Vector3(pos.x, pos.y, pos.z) + camSlotId->Pose.Position.ToVector3(); GameVector from = GameVector(dest, camSlotId->RoomNumber); Camera.fixedCamera = true; From 6eca0548c6c7408f1f1175e4ccc21abe0bcb53c8 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 10 Nov 2024 17:23:50 +0100 Subject: [PATCH 010/112] Exclude some ToBoundingOrientedBox conversions --- TombEngine/Game/camera.cpp | 27 +++++++++++++++------- TombEngine/Game/collision/collide_item.cpp | 20 ++++++++-------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index e5d0d8101..173534cd9 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -1331,8 +1331,13 @@ void CalculateCamera(const CollisionInfo& coll) bool TestBoundsCollideCamera(const GameBoundingBox& bounds, const Pose& pose, short radius) { - auto sphere = BoundingSphere(Camera.pos.ToVector3(), radius); - return sphere.Intersects(bounds.ToBoundingOrientedBox(pose)); + auto camSphere = BoundingSphere(Camera.pos.ToVector3(), radius); + auto boundsSphere = BoundingSphere(pose.Position.ToVector3(), bounds.GetExtents().Length()); + + if (!camSphere.Intersects(boundsSphere)) + return false; + + return camSphere.Intersects(bounds.ToBoundingOrientedBox(pose)); } float GetParticleDistanceFade(const Vector3i& pos) @@ -1507,9 +1512,12 @@ void ItemsCollideCamera() if (TestBoundsCollideCamera(bounds, item->Pose, CAMERA_RADIUS)) ItemPushCamera(&bounds, &item->Pose, RADIUS); - DrawDebugBox( - bounds.ToBoundingOrientedBox(item->Pose), - Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats); + if (DebugMode) + { + DrawDebugBox( + bounds.ToBoundingOrientedBox(item->Pose), + Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats); + } } // Done. @@ -1530,9 +1538,12 @@ void ItemsCollideCamera() if (TestBoundsCollideCamera(bounds, mesh->pos, CAMERA_RADIUS)) ItemPushCamera(&bounds, &mesh->pos, RADIUS); - DrawDebugBox( - bounds.ToBoundingOrientedBox(mesh->pos), - Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats); + if (DebugMode) + { + DrawDebugBox( + bounds.ToBoundingOrientedBox(mesh->pos), + Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats); + } } // Done. diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 2b906dbab..f94a9bc64 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -27,6 +27,7 @@ using namespace TEN::Collision::Sphere; using namespace TEN::Math; constexpr auto ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD = 6; +constexpr auto COLLIDABLE_BOUNDS_THRESHOLD = 4; // Globals @@ -95,7 +96,6 @@ void GenericSphereBoxCollision(short itemNumber, ItemInfo* playerItem, Collision CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, bool ignorePlayer, float customRadius, ObjectCollectionMode mode) { - constexpr auto EXTENTS_LENGTH_MIN = 2.0f; constexpr auto ROUGH_BOX_HEIGHT_MIN = BLOCK(1 / 8.0f); auto collObjects = CollidedObjectData{}; @@ -117,7 +117,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, convertedBounds.Extents = Vector3(customRadius); // Quickly discard collision if colliding item bounds are below tolerance threshold. - if (collidingSphere.Radius <= EXTENTS_LENGTH_MIN) + if (collidingSphere.Radius <= COLLIDABLE_BOUNDS_THRESHOLD) return collObjects; // Run through neighboring rooms. @@ -172,7 +172,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, auto extents = bounds.GetExtents(); // If item bounding box extents is below tolerance threshold, discard object. - if (extents.Length() <= EXTENTS_LENGTH_MIN) + if (extents.Length() <= COLLIDABLE_BOUNDS_THRESHOLD) continue; // Test rough vertical distance to discard objects not intersecting vertically. @@ -237,10 +237,10 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, continue; // Skip if either bounding box has any zero extent (not a collidable volume). - if (bounds.GetExtents().Length() <= EXTENTS_LENGTH_MIN) + if (bounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) continue; - if (collidingBounds.GetExtents().Length() <= EXTENTS_LENGTH_MIN) + if (collidingBounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) continue; auto box = bounds.ToBoundingOrientedBox(staticObj.pos.Position); @@ -971,13 +971,13 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& { bool result = false; + // Ignore processing null bounds. + if (box.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) + return false; + // Get DX static bounds in global coordinates. auto staticBounds = box.ToBoundingOrientedBox(pose); - // Ignore processing null bounds. - if (Vector3(staticBounds.Extents) == Vector3::Zero) - return false; - // Get local TR bounds and DX item bounds in global coordinates. auto itemBBox = GameBoundingBox(item); auto itemBounds = itemBBox.ToBoundingOrientedBox(item->Pose); @@ -1805,7 +1805,7 @@ void DoObjectCollision(ItemInfo* item, CollisionInfo* coll) if (isPlayer) { - GetLaraInfo(*item).HitDirection = -1; + GetLaraInfo(*item).HitDirection = NO_VALUE; if (item->HitPoints <= 0) return; From 3b252848c59337c4d39bb9663d7f771f121faa1f Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 10 Nov 2024 19:23:17 +0300 Subject: [PATCH 011/112] Displaystring alpha (#1453) * Support for alpha in display strings * Update CHANGELOG.md * Fix alpha * Potentially fix problems with alpha blend --- CHANGELOG.md | 2 ++ TombEngine/Renderer/Renderer.h | 4 ++-- TombEngine/Renderer/RendererDraw.cpp | 3 ++- TombEngine/Renderer/RendererString.cpp | 12 ++++++++---- .../Renderer/Structures/RendererStringToDraw.h | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce24af1f5..3115cfd42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Lua API changes +* Added support for transparency value in DisplayString class. + ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 ### Bug fixes diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index ae794fc92..88b2c443f 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -202,8 +202,8 @@ namespace TEN::Renderer // Text std::unique_ptr _gameFont; std::vector _stringsToDraw; - float _blinkColorValue = 0.0f; - float _blinkTime = 0.0f; + Vector4 _blinkColorValue = Vector4::Zero; + float _blinkTime = 0.0f; float _oldBlinkTime = 0.0f; // Graphics resources diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index f0e73c32a..40dbdc78f 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -1583,7 +1583,8 @@ namespace TEN::Renderer constexpr auto BLINK_TIME_STEP = 0.2f; // Calculate blink increment based on sine wave. - _blinkColorValue = ((sin(_blinkTime) + BLINK_VALUE_MAX) * 0.5f) + BLINK_VALUE_MIN; + float blink = ((sin(_blinkTime) + BLINK_VALUE_MAX) * 0.5f) + BLINK_VALUE_MIN; + _blinkColorValue = Vector4(blink, blink, blink, 1.0f); // Update blink time. _blinkTime += BLINK_TIME_STEP; diff --git a/TombEngine/Renderer/RendererString.cpp b/TombEngine/Renderer/RendererString.cpp index 7ed550a34..ff52f8391 100644 --- a/TombEngine/Renderer/RendererString.cpp +++ b/TombEngine/Renderer/RendererString.cpp @@ -50,7 +50,7 @@ namespace TEN::Renderer rString.Flags = flags; rString.X = 0; rString.Y = 0; - rString.Color = color.ToVector3(); + rString.Color = color; rString.Scale = (uiScale * fontScale) * scale; // Measure string. @@ -89,8 +89,12 @@ namespace TEN::Renderer void Renderer::DrawAllStrings() { - float shadowOffset = 1.5f / (REFERENCE_FONT_SIZE / _gameFont->GetLineSpacing()); + if (_stringsToDraw.empty()) + return; + SetBlendMode(BlendMode::AlphaBlend); + + float shadowOffset = 1.5f / (REFERENCE_FONT_SIZE / _gameFont->GetLineSpacing()); _spriteBatch->Begin(); for (const auto& rString : _stringsToDraw) @@ -101,7 +105,7 @@ namespace TEN::Renderer _gameFont->DrawString( _spriteBatch.get(), rString.String.c_str(), Vector2(rString.X + shadowOffset * rString.Scale, rString.Y + shadowOffset * rString.Scale), - Vector4(0.0f, 0.0f, 0.0f, 1.0f) * ScreenFadeCurrent, + Vector4(0.0f, 0.0f, 0.0f, rString.Color.w) * ScreenFadeCurrent, 0.0f, Vector4::Zero, rString.Scale); } @@ -109,7 +113,7 @@ namespace TEN::Renderer _gameFont->DrawString( _spriteBatch.get(), rString.String.c_str(), Vector2(rString.X, rString.Y), - Vector4(rString.Color.x, rString.Color.y, rString.Color.z, 1.0f) * ScreenFadeCurrent, + (rString.Color * rString.Color.w) * ScreenFadeCurrent, 0.0f, Vector4::Zero, rString.Scale); } diff --git a/TombEngine/Renderer/Structures/RendererStringToDraw.h b/TombEngine/Renderer/Structures/RendererStringToDraw.h index b04aad266..cfe278480 100644 --- a/TombEngine/Renderer/Structures/RendererStringToDraw.h +++ b/TombEngine/Renderer/Structures/RendererStringToDraw.h @@ -11,7 +11,7 @@ namespace TEN::Renderer::Structures float Y; int Flags; std::wstring String; - Vector3 Color; + Vector4 Color; float Scale; }; } From 9494c999e739d654daa65f213789d29ca1d60268 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:04:58 +0100 Subject: [PATCH 012/112] Fixed incorrect camera movement near walls after leaving look mode --- CHANGELOG.md | 1 + TombEngine/Game/camera.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3115cfd42..bdf472101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed audio tracks placed in subfolders not restoring after loading savegame. * Fixed scripted input events not registering on the same game frame. * Fixed incorrect object camera position. +* Fixed incorrect camera movement near walls after leaving look mode. * Fixed Lara's Home entry not working. * Fixed Lens Flare object not functioning properly. diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index 173534cd9..f987412ac 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -1260,8 +1260,8 @@ void CalculateCamera(const CollisionInfo& coll) if (isFixedCamera == Camera.fixedCamera) { Camera.fixedCamera = false; - if (Camera.speed != 1 && - !Lara.Control.Look.IsUsingBinoculars) + + if (Camera.speed != 1 && Camera.oldType != CameraType::Look && !Lara.Control.Look.IsUsingBinoculars) { if (TargetSnaps <= 8) { From d562b9f433859cb23e7f364125d32e2aebb037d8 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:31:36 +0100 Subject: [PATCH 013/112] Less snappy behaviour when exiting look to chase camera --- TombEngine/Game/camera.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index f987412ac..070bac476 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -522,6 +522,13 @@ void ChaseCamera(ItemInfo* item) else if (Camera.actualElevation < ANGLE(-85.0f)) Camera.actualElevation = ANGLE(-85.0f); + // Force item position after exiting look mode to avoid weird movements near walls. + if (Camera.oldType == CameraType::Look) + { + Camera.target.x = item->Pose.Position.x; + Camera.target.z = item->Pose.Position.z; + } + int distance = Camera.targetDistance * phd_cos(Camera.actualElevation); auto pointColl = GetPointCollision(Vector3i(Camera.target.x, Camera.target.y + CLICK(1), Camera.target.z), Camera.target.RoomNumber); @@ -1261,7 +1268,7 @@ void CalculateCamera(const CollisionInfo& coll) { Camera.fixedCamera = false; - if (Camera.speed != 1 && Camera.oldType != CameraType::Look && !Lara.Control.Look.IsUsingBinoculars) + if (Camera.speed != 1 && !Lara.Control.Look.IsUsingBinoculars) { if (TargetSnaps <= 8) { From 6c848dcfaedb4e9e3fbea3b8d3402bcadaae3801 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:50:53 +0100 Subject: [PATCH 014/112] Fixed #1467 --- CHANGELOG.md | 1 + TombEngine/Game/spotcam.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdf472101..b012e9f68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed scripted input events not registering on the same game frame. * Fixed incorrect object camera position. * Fixed incorrect camera movement near walls after leaving look mode. +* Fixed binocular or lasersight camera not switching off correctly after flyby. * Fixed Lara's Home entry not working. * Fixed Lens Flare object not functioning properly. diff --git a/TombEngine/Game/spotcam.cpp b/TombEngine/Game/spotcam.cpp index 15baf58fd..ea78586e8 100644 --- a/TombEngine/Game/spotcam.cpp +++ b/TombEngine/Game/spotcam.cpp @@ -693,13 +693,14 @@ void CalculateSpotCameras() SetCinematicBars(0.0f, SPOTCAM_CINEMATIC_BARS_SPEED); - Camera.DisableInterpolation = true; UseSpotCam = false; - Lara.Control.IsLocked = false; CheckTrigger = false; + Lara.Control.IsLocked = false; + Lara.Control.Look.IsUsingBinoculars = false; Camera.oldType = CameraType::Fixed; Camera.type = CameraType::Chase; Camera.speed = 1; + Camera.DisableInterpolation = true; if (s->flags & SCF_CUT_TO_LARA_CAM) { From abf69f120e6735b276f43b60d94c8aeeb9a3586a Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 09:10:54 +0100 Subject: [PATCH 015/112] Fixed #1468 --- CHANGELOG.md | 1 + TombEngine/Game/savegame.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b012e9f68..1f0c08973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed engine performance if weather effects are active. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. +* Fixed vehicle transfer not happening for levels which were not previously visited. * Fixed audio tracks placed in subfolders not restoring after loading savegame. * Fixed scripted input events not registering on the same game frame. * Fixed incorrect object camera position. diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index 42a877e0e..2757870b0 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -1506,15 +1506,18 @@ void SaveGame::SaveHub(int index) void SaveGame::LoadHub(int index) { - // Don't attempt to load hub data if it doesn't exist, or level is a title level. - if (index == 0 || !IsOnHub(index)) + // Don't attempt to load hub data if level is a title level. + if (index == 0) return; - // Load hub data. - TENLog("Loading hub data for level #" + std::to_string(index), LogLevel::Info); - Parse(Hub[index], true); + if (IsOnHub(index)) + { + // Load hub data. + TENLog("Loading hub data for level #" + std::to_string(index), LogLevel::Info); + Parse(Hub[index], true); + } - // Restore vehicle. + // Restore vehicle (also for cases when no hub data yet exists). InitializePlayerVehicle(*LaraItem); } From 78c0ba3ff054cb4009b36aba220ed02514eea640 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 09:40:34 +0100 Subject: [PATCH 016/112] Add ricochet sound and increase amount of particles --- CHANGELOG.md | 4 +++- TombEngine/Game/control/los.cpp | 11 +++++------ TombEngine/Game/effects/effects.cpp | 9 ++++++--- TombEngine/Game/effects/effects.h | 2 +- TombEngine/Game/items.cpp | 2 +- TombEngine/Objects/TR2/Entity/tr2_spear_guardian.cpp | 2 +- TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp | 2 +- TombEngine/Objects/TR3/Entity/Shiva.cpp | 4 ++-- TombEngine/Objects/TR3/Entity/Winston.cpp | 2 +- TombEngine/Objects/TR4/Entity/tr4_baddy.cpp | 2 +- TombEngine/Objects/TR5/Entity/AutoGun.cpp | 2 +- TombEngine/Objects/TR5/Entity/HeavyGuard.cpp | 2 +- TombEngine/Objects/TR5/Entity/tr5_gunship.cpp | 6 ++---- TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp | 2 +- 14 files changed, 27 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0c08973..df03b1024 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,9 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed Lens Flare object not functioning properly. ### Features/Amendments - + +* Added ricochet sounds and make the effect more prominent. + ### Lua API changes * Added support for transparency value in DisplayString class. diff --git a/TombEngine/Game/control/los.cpp b/TombEngine/Game/control/los.cpp index 003d04b30..c07d5ff74 100644 --- a/TombEngine/Game/control/los.cpp +++ b/TombEngine/Game/control/los.cpp @@ -296,8 +296,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo SoundEffect(GetShatterSound(mesh->staticNumber), (Pose*)mesh); } - TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0); - TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0); + TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y); } else { @@ -312,7 +311,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo ShatterImpactData.impactDirection = dir; ShatterImpactData.impactLocation = ShatterItem.sphere.Center; ShatterObject(&ShatterItem, 0, 128, target2.RoomNumber, 0); - TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0); + TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, false); } else { @@ -348,7 +347,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo { // TR5 if (object->hitEffect == HitEffect::Richochet) - TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0); + TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y); } } else @@ -420,7 +419,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo } } - TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0); + TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y); } } } @@ -447,7 +446,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo target2.z -= (target2.z - origin->z) >> 5; if (isFiring && !result) - TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 8, 0); + TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y); } } diff --git a/TombEngine/Game/effects/effects.cpp b/TombEngine/Game/effects/effects.cpp index c64da1969..fa0bab9c6 100644 --- a/TombEngine/Game/effects/effects.cpp +++ b/TombEngine/Game/effects/effects.cpp @@ -450,9 +450,13 @@ void UpdateSparks() } } -void TriggerRicochetSpark(const GameVector& pos, short angle, int count, int unk) +void TriggerRicochetSpark(const GameVector& pos, short angle, bool sound) { + int count = Random::GenerateInt(3, 8); TriggerRicochetSpark(pos, angle, count); + + if (sound && Random::TestProbability(1 / 3.0f)) + SoundEffect(SFX_TR4_WEAPON_RICOCHET, &Pose(pos.ToVector3i())); } void TriggerCyborgSpark(int x, int y, int z, short xv, short yv, short zv) @@ -1135,8 +1139,7 @@ void Ricochet(Pose& pose) { short angle = Geometry::GetOrientToPoint(pose.Position.ToVector3(), LaraItem->Pose.Position.ToVector3()).y; auto target = GameVector(pose.Position); - TriggerRicochetSpark(target, angle / 16, 3, 0); - SoundEffect(SFX_TR4_WEAPON_RICOCHET, &pose); + TriggerRicochetSpark(target, angle / 16); } void ControlWaterfallMist(short itemNumber) diff --git a/TombEngine/Game/effects/effects.h b/TombEngine/Game/effects/effects.h index aaa5031a9..583d51f82 100644 --- a/TombEngine/Game/effects/effects.h +++ b/TombEngine/Game/effects/effects.h @@ -284,7 +284,7 @@ void SetSpriteSequence(Particle& particle, GAME_OBJECT_ID objectID); void DetatchSpark(int num, SpriteEnumFlag type); void UpdateSparks(); -void TriggerRicochetSpark(const GameVector& pos, short angle, int count, int unk); +void TriggerRicochetSpark(const GameVector& pos, short angle, bool sound = true); void TriggerCyborgSpark(int x, int y, int z, short xv, short yv, short zv); void TriggerExplosionSparks(int x, int y, int z, int extraTrig, int dynamic, int uw, int roomNumber, const Vector3& mainColor = Vector3::Zero, const Vector3& secondColor = Vector3::Zero); void TriggerExplosionSmokeEnd(int x, int y, int z, int uw); diff --git a/TombEngine/Game/items.cpp b/TombEngine/Game/items.cpp index 5f30fac06..19e382ba8 100644 --- a/TombEngine/Game/items.cpp +++ b/TombEngine/Game/items.cpp @@ -893,7 +893,7 @@ void DefaultItemHit(ItemInfo& target, ItemInfo& source, std::optionalstaticNumber), &hitMesh->pos); } - TriggerRicochetSpark(GameVector(hitPos), 2 * GetRandomControl(), 3, 0); - TriggerRicochetSpark(GameVector(hitPos), 2 * GetRandomControl(), 3, 0); + TriggerRicochetSpark(GameVector(hitPos), 2 * GetRandomControl()); } } else diff --git a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp index bdb8dbcfc..3c36ce5c1 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp @@ -882,7 +882,7 @@ namespace TEN::Entities::Creatures::TR5 if (object.hitEffect == HitEffect::Richochet && pos.has_value()) { - TriggerRicochetSpark(*pos, source.Pose.Orientation.y, 3, 0); + TriggerRicochetSpark(*pos, source.Pose.Orientation.y, false); SoundEffect(SFX_TR5_SWORD_GOD_HIT_METAL, &target.Pose); } From 87b9c3caacf188d21d2dbffd52c4afb4ab4db1ba Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:13:48 +0100 Subject: [PATCH 017/112] Fixed #1451 --- CHANGELOG.md | 1 + TombEngine/Objects/TR3/tr3_objects.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df03b1024..f97ae01b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed incorrect camera movement near walls after leaving look mode. * Fixed binocular or lasersight camera not switching off correctly after flyby. * Fixed Lara's Home entry not working. +* Fixed exploding TR3 bosses. * Fixed Lens Flare object not functioning properly. ### Features/Amendments diff --git a/TombEngine/Objects/TR3/tr3_objects.cpp b/TombEngine/Objects/TR3/tr3_objects.cpp index 950c4fb7d..3e9d42952 100644 --- a/TombEngine/Objects/TR3/tr3_objects.cpp +++ b/TombEngine/Objects/TR3/tr3_objects.cpp @@ -73,7 +73,7 @@ static void StartEntity(ObjectInfo* obj) obj->nonLot = true; // NOTE: Doesn't move to reach the player, only throws projectiles. obj->SetBoneRotationFlags(6, ROT_X | ROT_Y); obj->SetBoneRotationFlags(13, ROT_Y); - obj->SetHitEffect(); + obj->SetHitEffect(HitEffect::NonExplosive); } obj = &Objects[ID_TIGER]; @@ -300,7 +300,7 @@ static void StartEntity(ObjectInfo* obj) obj->LotType = LotType::Human; obj->SetBoneRotationFlags(6, ROT_X | ROT_Y); obj->SetBoneRotationFlags(13, ROT_Y); - obj->SetHitEffect(); + obj->SetHitEffect(HitEffect::NonExplosive); } obj = &Objects[ID_CIVVY]; @@ -365,7 +365,7 @@ static void StartEntity(ObjectInfo* obj) obj->pivotLength = 50; obj->SetBoneRotationFlags(4, ROT_Y); // Puna quest object. obj->SetBoneRotationFlags(7, ROT_X | ROT_Y); // Head. - obj->SetHitEffect(); + obj->SetHitEffect(HitEffect::NonExplosive); } obj = &Objects[ID_WASP_MUTANT]; From b79d662b1341786ed64c97aaf87156b1f534cf1d Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:48:59 +0100 Subject: [PATCH 018/112] Fixed bubbles performance --- CHANGELOG.md | 2 +- TombEngine/Game/Lara/lara.h | 2 +- TombEngine/Game/Lara/lara_helpers.cpp | 2 +- TombEngine/Game/effects/bubble.cpp | 24 ++++++++++++++---------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f97ae01b4..5958cd161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Bug fixes * Fixed engine performance around bridges. -* Fixed engine performance if weather effects are active. +* Fixed engine performance if weather or bubble effects are active. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. * Fixed vehicle transfer not happening for levels which were not previously visited. diff --git a/TombEngine/Game/Lara/lara.h b/TombEngine/Game/Lara/lara.h index 5e35047bf..0d426f036 100644 --- a/TombEngine/Game/Lara/lara.h +++ b/TombEngine/Game/Lara/lara.h @@ -82,7 +82,7 @@ constexpr auto LARA_STAMINA_MIN = LARA_STAMINA_MAX / 10; constexpr auto LARA_STAMINA_CRITICAL = LARA_STAMINA_MAX / 2; constexpr auto PLAYER_DRIP_NODE_MAX = 64.0f; -constexpr auto PLAYER_BUBBLE_NODE_MAX = 12.0f; +constexpr auto PLAYER_BUBBLE_NODE_MAX = 8.0f; constexpr auto STEPUP_HEIGHT = (int)CLICK(1.5f); constexpr auto CRAWL_STEPUP_HEIGHT = CLICK(1) - 1; diff --git a/TombEngine/Game/Lara/lara_helpers.cpp b/TombEngine/Game/Lara/lara_helpers.cpp index b307919d2..3efab248d 100644 --- a/TombEngine/Game/Lara/lara_helpers.cpp +++ b/TombEngine/Game/Lara/lara_helpers.cpp @@ -953,7 +953,7 @@ void HandlePlayerWetnessDrips(ItemInfo& item) void HandlePlayerDiveBubbles(ItemInfo& item) { - constexpr auto BUBBLE_COUNT_MULT = 6; + constexpr auto BUBBLE_COUNT_MULT = 3; auto& player = *GetLaraInfo(&item); diff --git a/TombEngine/Game/effects/bubble.cpp b/TombEngine/Game/effects/bubble.cpp index 7dc9f676f..06feb93b7 100644 --- a/TombEngine/Game/effects/bubble.cpp +++ b/TombEngine/Game/effects/bubble.cpp @@ -136,8 +136,9 @@ namespace TEN::Effects::Bubble void UpdateBubbles() { - constexpr auto LIFE_FULL_SCALE = std::max(BUBBLE_LIFE_MAX - 0.25f, 0.0f); - constexpr auto LIFE_START_FADING = std::min(1.0f, BUBBLE_LIFE_MAX); + constexpr auto LIFE_FULL_SCALE = std::max(BUBBLE_LIFE_MAX - 0.25f, 0.0f); + constexpr auto LIFE_START_FADING = std::min(1.0f, BUBBLE_LIFE_MAX); + constexpr int ROOM_UPDATE_INTERVAL = 10; if (Bubbles.empty()) return; @@ -149,19 +150,23 @@ namespace TEN::Effects::Bubble bubble.StoreInterpolationData(); - // Update room number. TODO: Should use GetPointCollision(), but calling it for each bubble is very inefficient. - auto roomVector = RoomVector(bubble.RoomNumber, int(bubble.Position.y - bubble.Gravity)); - int roomNumber = GetRoomVector(roomVector, Vector3i(bubble.Position.x, bubble.Position.y - bubble.Gravity, bubble.Position.z)).RoomNumber; int prevRoomNumber = bubble.RoomNumber; - bubble.RoomNumber = roomNumber; + + int updateOffset = (int)(bubble.Position.x + bubble.Position.y + bubble.Position.z) % ROOM_UPDATE_INTERVAL; + if (GlobalCounter % ROOM_UPDATE_INTERVAL == updateOffset) + { + auto roomVector = RoomVector(bubble.RoomNumber, int(bubble.Position.y - bubble.Gravity)); + auto testPosition = Vector3i(bubble.Position.x, bubble.Position.y - bubble.Gravity, bubble.Position.z); + bubble.RoomNumber = GetRoomVector(roomVector, testPosition).RoomNumber;; + } // Out of water. - if (!TestEnvironment(ENV_FLAG_WATER, roomNumber)) + if (bubble.RoomNumber != prevRoomNumber && !TestEnvironment(ENV_FLAG_WATER, bubble.RoomNumber)) { // Hit water surface; spawn ripple. SpawnRipple( Vector3(bubble.Position.x, g_Level.Rooms[prevRoomNumber].TopHeight, bubble.Position.z), - roomNumber, + bubble.RoomNumber, ((bubble.SizeMax.x + bubble.SizeMax.y) / 2) * 0.5f, (int)RippleFlags::SlowFade); @@ -169,8 +174,7 @@ namespace TEN::Effects::Bubble continue; } // Hit ceiling. NOTE: This is a hacky check. New collision fetching should provide fast info on a need-to-know basis. - else if (bubble.RoomNumber == prevRoomNumber && - bubble.Position.y <= g_Level.Rooms[prevRoomNumber].TopHeight) + else if (bubble.RoomNumber == prevRoomNumber && bubble.Position.y <= g_Level.Rooms[prevRoomNumber].TopHeight) { bubble.Life = 0.0f; continue; From 784f957596a41e8e0b483e2e756300ebc0ca9bdb Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:55:50 +0300 Subject: [PATCH 019/112] Fast reload (#1445) * Initial commit * Fix crash on title * Update level.cpp * Update CHANGELOG.md * Do slight audiotrack fade-ins and fade-outs on leveljumps * Implement hash checks * Fixes * Rename rapid to fast * Fixed flipmaps and bridge reinit * Bypass reinitializing renderer for fast reload * Fix issue when title and last loaded level are the same files * Update CHANGELOG.md * Additional fixes * Wrap savegame loading code into a try-catch * Update CHANGELOG.md * Remove door collision on fast reload, restore stopper flag * Display log message if level file was not found * Update CHANGELOG.md * Clear blocked flag for boxes * Implement fast reload setting * Add defaults to settings * Consistent naming * Smooth level loading audio crossfades * Stop non-ambience soundtracks early * Minor formatting * Update Settings.lua --------- Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com> Co-authored-by: Sezz --- CHANGELOG.md | 6 +- .../doc/2 classes/Flow.Settings.html | 24 + Scripts/Settings.lua | 1 + TombEngine/Game/camera.h | 3 +- TombEngine/Game/control/control.cpp | 8 +- TombEngine/Game/control/control.h | 1 - TombEngine/Game/room.cpp | 96 ++- TombEngine/Game/room.h | 1 + TombEngine/Game/savegame.cpp | 82 ++- TombEngine/Game/savegame.h | 37 +- .../Objects/Generic/Doors/generic_doors.cpp | 2 +- TombEngine/Renderer/RendererCompatibility.cpp | 2 + TombEngine/Renderer/RendererDrawMenu.cpp | 30 +- TombEngine/Renderer/RendererSettings.cpp | 4 +- .../Include/Flow/ScriptInterfaceFlowHandler.h | 3 + .../Scripting/Internal/ReservedScriptNames.h | 1 + .../Internal/TEN/Flow/Settings/Settings.cpp | 28 +- .../Internal/TEN/Flow/Settings/Settings.h | 22 +- TombEngine/Sound/sound.cpp | 13 +- TombEngine/Sound/sound.h | 5 +- TombEngine/Specific/level.cpp | 558 +++++++++++------- TombEngine/Specific/level.h | 4 +- .../flatbuffers/ten_savegame_generated.h | 30 +- .../Specific/savegame/schema/ten_savegame.fbs | 1 + TombEngine/framework.h | 1 + 25 files changed, 605 insertions(+), 358 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5958cd161..0feead175 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ## Version 1.6 - xxxx-xx-xx ### Bug fixes - * Fixed engine performance around bridges. * Fixed engine performance if weather or bubble effects are active. * Fixed snow particles not always melting on the ground. @@ -22,7 +21,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed Lens Flare object not functioning properly. ### Features/Amendments - +* Added fast savegame reloading. * Added ricochet sounds and make the effect more prominent. ### Lua API changes @@ -32,7 +31,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 ### Bug fixes - * Fixed original issue with classic switch off trigger incorrectly activating some trigger actions. * Fixed moveable status after antitriggering. * Fixed leveljump vehicle transfer. @@ -71,7 +69,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed young Lara hair drawing. https://tombengine.com/docs/level-settings/#young_lara ### Features/Amendments - * Added high framerate mode (also known as 60 FPS mode). * Added a customisable global lensflare effect. https://tombengine.com/docs/level-settings/#lensflare * Added a customisable starry sky and meteor effect. https://tombengine.com/docs/level-settings/#stars @@ -100,7 +97,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Removed original limit of 32 active Flame Emitters. ### Lua API changes - * Added Flow.EnableHomeLevel() function. * Added Flow.IsStringPresent() function. * Added Flow.LensFlare() and Flow.Starfield() classes. diff --git a/Documentation/doc/2 classes/Flow.Settings.html b/Documentation/doc/2 classes/Flow.Settings.html index 007e40a45..abe97b44c 100644 --- a/Documentation/doc/2 classes/Flow.Settings.html +++ b/Documentation/doc/2 classes/Flow.Settings.html @@ -114,6 +114,10 @@ errorMode How should the application respond to script errors? + + fastReload + Can game utilize fast reload feature? +
@@ -129,6 +133,7 @@
How should the application respond to script errors? +
Must be one of the following: ErrorMode.TERMINATE - print to the log file and return to the title level when any script error is hit. This is the one you will want to go for if you want to know IMMEDIATELY if something has gone wrong.

@@ -151,6 +156,25 @@ has an unrecoverable error, the game will close. +
+
+ + fastReload +
+
+ Can game utilize fast reload feature? +
+When set to True, game will attempt to perform fast savegame reloading, if current level is the same as +level loaded from the savegame. It will not work if level timestamp or checksum has changed (i.e. level was +updated). If set to False this functionality is turned off. + + + + + + + +
diff --git a/Scripts/Settings.lua b/Scripts/Settings.lua index f50976766..b6d230a98 100644 --- a/Scripts/Settings.lua +++ b/Scripts/Settings.lua @@ -5,6 +5,7 @@ local Flow = TEN.Flow local settings = Flow.Settings.new() settings.errorMode = Flow.ErrorMode.WARN +settings.fastReload = true Flow.SetSettings(settings) local anims = Flow.Animations.new() diff --git a/TombEngine/Game/camera.h b/TombEngine/Game/camera.h index 59db6e4f8..f4f4e6829 100644 --- a/TombEngine/Game/camera.h +++ b/TombEngine/Game/camera.h @@ -1,6 +1,7 @@ #pragma once #include "Game/items.h" #include "Math/Math.h" +#include "Specific/Clock.h" struct CollisionInfo; @@ -67,7 +68,7 @@ enum CAMERA_FLAGS CF_CHASE_OBJECT = 3, }; -constexpr auto FADE_SCREEN_SPEED = 16.0f / 255.0f; +constexpr auto FADE_SCREEN_SPEED = 2.0f / FPS; constexpr auto DEFAULT_FOV = 80.0f; extern CAMERA_INFO Camera; diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 0c9cdae63..aebf8c6e8 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -105,8 +105,6 @@ int RequiredStartPos; int CurrentLevel; int NextLevel; -int SystemNameHash = 0; - bool InItemControlLoop; short ItemNewRoomNo; short ItemNewRooms[MAX_ROOMS]; @@ -508,10 +506,8 @@ void InitializeOrLoadGame(bool loadGame) g_Gui.SetEnterInventory(NO_VALUE); // Restore game? - if (loadGame) + if (loadGame && SaveGame::Load(g_GameFlow->SelectedSaveGame)) { - SaveGame::Load(g_GameFlow->SelectedSaveGame); - InitializeGame = false; g_GameFlow->SelectedSaveGame = 0; @@ -596,7 +592,7 @@ void EndGameLoop(int levelIndex, GameStatus reason) DeInitializeScripting(levelIndex, reason); StopAllSounds(); - StopSoundTracks(); + StopSoundTracks(SOUND_XFADETIME_LEVELJUMP, true); StopRumble(); } diff --git a/TombEngine/Game/control/control.h b/TombEngine/Game/control/control.h index 646808f27..9aa63dd1e 100644 --- a/TombEngine/Game/control/control.h +++ b/TombEngine/Game/control/control.h @@ -64,7 +64,6 @@ extern bool ThreadEnded; extern int RequiredStartPos; extern int CurrentLevel; extern int NextLevel; -extern int SystemNameHash; extern bool InItemControlLoop; extern short ItemNewRoomNo; diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index b2f6bf583..a2cd8564d 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -7,14 +7,16 @@ #include "Game/control/lot.h" #include "Game/control/volume.h" #include "Game/items.h" -#include "Renderer/Renderer.h" #include "Math/Math.h" #include "Objects/game_object_ids.h" +#include "Objects/Generic/Doors/generic_doors.h" +#include "Renderer/Renderer.h" #include "Specific/trutils.h" using namespace TEN::Math; using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Point; +using namespace TEN::Entities::Doors; using namespace TEN::Renderer; using namespace TEN::Utils; @@ -73,6 +75,74 @@ static void RemoveRoomFlipItems(const ROOM_INFO& room) } } +static void FlipRooms(int roomNumber, ROOM_INFO& activeRoom, ROOM_INFO& flippedRoom) +{ + RemoveRoomFlipItems(activeRoom); + + // Swap rooms. + std::swap(activeRoom, flippedRoom); + activeRoom.flippedRoom = flippedRoom.flippedRoom; + flippedRoom.flippedRoom = NO_VALUE; + activeRoom.itemNumber = flippedRoom.itemNumber; + activeRoom.fxNumber = flippedRoom.fxNumber; + + AddRoomFlipItems(activeRoom); + + // Update active room sectors. + for (auto& sector : activeRoom.Sectors) + sector.RoomNumber = roomNumber; + + // Update flipped room sectors. + for (auto& sector : flippedRoom.Sectors) + sector.RoomNumber = activeRoom.flippedRoom; + + // Update renderer data. + g_Renderer.FlipRooms(roomNumber, activeRoom.flippedRoom); +} + +void ResetRoomData() +{ + // Remove all door collisions. + for (const auto& item : g_Level.Items) + { + if (item.ObjectNumber == NO_VALUE || !item.Data.is()) + continue; + + auto& doorItem = g_Level.Items[item.Index]; + auto& door = *(DOOR_DATA*)doorItem.Data; + + if (door.opened) + continue; + + OpenThatDoor(&door.d1, &door); + OpenThatDoor(&door.d2, &door); + OpenThatDoor(&door.d1flip, &door); + OpenThatDoor(&door.d2flip, &door); + door.opened = true; + } + + // Unflip all rooms and remove all bridges and stopper flags. + for (int roomNumber = 0; roomNumber < g_Level.Rooms.size(); roomNumber++) + { + auto& room = g_Level.Rooms[roomNumber]; + if (room.flippedRoom != NO_VALUE && room.flipNumber != NO_VALUE && FlipStats[room.flipNumber]) + { + auto& flippedRoom = g_Level.Rooms[room.flippedRoom]; + FlipRooms(roomNumber, room, flippedRoom); + } + + for (auto& sector : room.Sectors) + { + sector.Stopper = false; + sector.BridgeItemNumbers.clear(); + } + } + + // Make sure no pathfinding boxes are blocked (either by doors or by other door-like objects). + for (int pathfindingBoxID = 0; pathfindingBoxID < g_Level.PathfindingBoxes.size(); pathfindingBoxID++) + g_Level.PathfindingBoxes[pathfindingBoxID].flags &= ~BLOCKED; +} + void DoFlipMap(int group) { if (group >= MAX_FLIPMAP) @@ -87,30 +157,10 @@ void DoFlipMap(int group) auto& room = g_Level.Rooms[roomNumber]; // Handle flipmap. - if (room.flippedRoom >= 0 && room.flipNumber == group) + if (room.flippedRoom != NO_VALUE && room.flipNumber == group) { auto& flippedRoom = g_Level.Rooms[room.flippedRoom]; - - RemoveRoomFlipItems(room); - - // Swap rooms. - std::swap(room, flippedRoom); - room.flippedRoom = flippedRoom.flippedRoom; - flippedRoom.flippedRoom = NO_VALUE; - room.itemNumber = flippedRoom.itemNumber; - room.fxNumber = flippedRoom.fxNumber; - - AddRoomFlipItems(room); - - g_Renderer.FlipRooms(roomNumber, room.flippedRoom); - - // Update active room sectors. - for (auto& sector : room.Sectors) - sector.RoomNumber = roomNumber; - - // Update flipped room sectors. - for (auto& sector : flippedRoom.Sectors) - sector.RoomNumber = room.flippedRoom; + FlipRooms(roomNumber, room, flippedRoom); } } diff --git a/TombEngine/Game/room.h b/TombEngine/Game/room.h index fd228fb2d..673c028e2 100644 --- a/TombEngine/Game/room.h +++ b/TombEngine/Game/room.h @@ -125,6 +125,7 @@ struct ROOM_INFO }; void DoFlipMap(int group); +void ResetRoomData(); bool IsObjectInRoom(int roomNumber, GAME_OBJECT_ID objectID); bool IsPointInRoom(const Vector3i& pos, int roomNumber); int FindRoomNumber(const Vector3i& pos, int startRoomNumber = NO_VALUE, bool onlyNeighbors = false); diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index 2757870b0..4a2c07408 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -247,6 +247,7 @@ const std::vector SaveGame::Build() Save::SaveGameHeaderBuilder sghb{ fbb }; sghb.add_level_name(levelNameOffset); + sghb.add_level_hash(LastLevelHash); auto gameTime = GetGameTime(GameTimer); sghb.add_days(gameTime.Days); @@ -1582,50 +1583,68 @@ bool SaveGame::Save(int slot) bool SaveGame::Load(int slot) { if (!IsSaveGameSlotValid(slot)) + { + TENLog("Savegame slot " + std::to_string(slot) + " is invalid, load is impossible.", LogLevel::Error); return false; + } if (!DoesSaveGameExist(slot)) + { + TENLog("Savegame in slot " + std::to_string(slot) + " does not exist.", LogLevel::Error); return false; + } auto fileName = GetSavegameFilename(slot); TENLog("Loading from savegame: " + fileName, LogLevel::Info); - std::ifstream file; - file.open(fileName, std::ios_base::app | std::ios_base::binary); - - int size; - file.read(reinterpret_cast(&size), sizeof(size)); - - // Read current level save data. - std::vector saveData(size); - file.read(reinterpret_cast(saveData.data()), size); - - // Reset hub data, as it's about to be replaced with saved one. - ResetHub(); - - // Read hub data from savegame. - int hubCount; - file.read(reinterpret_cast(&hubCount), sizeof(hubCount)); - - TENLog("Hub count: " + std::to_string(hubCount), LogLevel::Info); - - for (int i = 0; i < hubCount; i++) + auto file = std::ifstream(); + try { - int index; - file.read(reinterpret_cast(&index), sizeof(index)); + file.open(fileName, std::ios_base::app | std::ios_base::binary); + int size = 0; file.read(reinterpret_cast(&size), sizeof(size)); - std::vector hubBuffer(size); - file.read(reinterpret_cast(hubBuffer.data()), size); - Hub[index] = hubBuffer; + // Read current level save data. + auto saveData = std::vector(size); + file.read(reinterpret_cast(saveData.data()), size); + + // Reset hub data, as it's about to be replaced with saved one. + ResetHub(); + + // Read hub data from savegame. + int hubCount = 0; + file.read(reinterpret_cast(&hubCount), sizeof(hubCount)); + + TENLog("Hub count: " + std::to_string(hubCount), LogLevel::Info); + + for (int i = 0; i < hubCount; i++) + { + int index = 0; + file.read(reinterpret_cast(&index), sizeof(index)); + + file.read(reinterpret_cast(&size), sizeof(size)); + auto hubBuffer = std::vector(size); + file.read(reinterpret_cast(hubBuffer.data()), size); + + Hub[index] = hubBuffer; + } + + file.close(); + + // Load save data for current level. + Parse(saveData, false); + return true; + } + catch (std::exception& ex) + { + TENLog("Error while loading savegame: " + std::string(ex.what()), LogLevel::Error); + + if (file.is_open()) + file.close(); } - file.close(); - - // Load save data for current level. - Parse(saveData, false); - return true; + return false; } static void ParseStatistics(const Save::SaveGame* s, bool isHub) @@ -2049,7 +2068,7 @@ static void ParseEffects(const Save::SaveGame* s) TENAssert(i < (int)SoundTrackType::Count, "Soundtrack type count was changed"); auto track = s->soundtracks()->Get(i); - PlaySoundTrack(track->name()->str(), (SoundTrackType)i, track->position()); + PlaySoundTrack(track->name()->str(), (SoundTrackType)i, track->position(), SOUND_XFADETIME_LEVELJUMP); } // Load fish swarm. @@ -2655,6 +2674,7 @@ bool SaveGame::LoadHeader(int slot, SaveGameHeader* header) header->Level = s->header()->level(); header->LevelName = s->header()->level_name()->str(); + header->LevelHash = s->header()->level_hash(); header->Days = s->header()->days(); header->Hours = s->header()->hours(); header->Minutes = s->header()->minutes(); diff --git a/TombEngine/Game/savegame.h b/TombEngine/Game/savegame.h index 61a8d922e..3b09ac92d 100644 --- a/TombEngine/Game/savegame.h +++ b/TombEngine/Game/savegame.h @@ -11,32 +11,33 @@ constexpr auto SAVEGAME_MAX = 16; struct Stats { - unsigned int Timer; - unsigned int Distance; - unsigned int AmmoHits; - unsigned int AmmoUsed; - unsigned int HealthUsed; - unsigned int Kills; - unsigned int Secrets; + unsigned int Timer = 0; + unsigned int Distance = 0; + unsigned int AmmoHits = 0; + unsigned int AmmoUsed = 0; + unsigned int HealthUsed = 0; + unsigned int Kills = 0; + unsigned int Secrets = 0; }; struct GameStats { - Stats Game; - Stats Level; + Stats Game = {}; + Stats Level = {}; }; struct SaveGameHeader { - std::string LevelName; - int Days; - int Hours; - int Minutes; - int Seconds; - int Level; - int Timer; - int Count; - bool Present; + std::string LevelName = {}; + int LevelHash = 0; + int Days = 0; + int Hours = 0; + int Minutes = 0; + int Seconds = 0; + int Level = 0; + int Timer = 0; + int Count = 0; + bool Present = false; }; class SaveGame diff --git a/TombEngine/Objects/Generic/Doors/generic_doors.cpp b/TombEngine/Objects/Generic/Doors/generic_doors.cpp index 756dfd0c2..583570724 100644 --- a/TombEngine/Objects/Generic/Doors/generic_doors.cpp +++ b/TombEngine/Objects/Generic/Doors/generic_doors.cpp @@ -92,7 +92,7 @@ namespace TEN::Entities::Doors doorData->d1.block = (boxNumber != NO_VALUE && g_Level.PathfindingBoxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE; doorData->d1.data = *doorData->d1.floor; - if (r->flippedRoom != -1) + if (r->flippedRoom != NO_VALUE) { r = &g_Level.Rooms[r->flippedRoom]; doorData->d1flip.floor = GetSector(r, doorItem->Pose.Position.x - r->Position.x + xOffset, doorItem->Pose.Position.z - r->Position.z + zOffset); diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 2bf5f89eb..cd94dd6f3 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -22,6 +22,8 @@ namespace TEN::Renderer bool Renderer::PrepareDataForTheRenderer() { + TENLog("Preparing renderer...", LogLevel::Info); + _lastBlendMode = BlendMode::Unknown; _lastCullMode = CullMode::Unknown; _lastDepthState = DepthState::Unknown; diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index d0316cad4..7db09fa8d 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -1013,10 +1013,6 @@ namespace TEN::Renderer _context->VSSetShader(_vsInventory.Get(), nullptr, 0); _context->PSSetShader(_psInventory.Get(), nullptr, 0); - // Set texture - BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp); - BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp); - if (CurrentLevel == 0) { auto titleMenu = g_Gui.GetMenuToDisplay(); @@ -1047,6 +1043,14 @@ namespace TEN::Renderer } else { + if (g_Gui.GetInventoryMode() == InventoryMode::InGame || + g_Gui.GetInventoryMode() == InventoryMode::Examine) + { + // Set texture. + BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp); + BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp); + } + switch (g_Gui.GetInventoryMode()) { case InventoryMode::Load: @@ -1103,26 +1107,30 @@ namespace TEN::Renderer void Renderer::RenderLoadingScreen(float percentage) { - // Set basic render states + // Set basic render states. SetBlendMode(BlendMode::Opaque); SetCullMode(CullMode::CounterClockwise); do { - // Clear screen + // Clear screen. _context->ClearRenderTargetView(_backBuffer.RenderTargetView.Get(), Colors::Black); _context->ClearDepthStencilView(_backBuffer.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); - // Bind the back buffer + // Bind back buffer. _context->OMSetRenderTargets(1, _backBuffer.RenderTargetView.GetAddressOf(), _backBuffer.DepthStencilView.Get()); _context->RSSetViewports(1, &_viewport); ResetScissor(); - // Draw the full screen background + // Draw fullscreen background. If unavailable, draw last dumped game scene. if (_loadingScreenTexture.Texture) - DrawFullScreenQuad( - _loadingScreenTexture.ShaderResourceView.Get(), - Vector3(ScreenFadeCurrent, ScreenFadeCurrent, ScreenFadeCurrent)); + { + DrawFullScreenQuad(_loadingScreenTexture.ShaderResourceView.Get(), Vector3(ScreenFadeCurrent, ScreenFadeCurrent, ScreenFadeCurrent)); + } + else if (_dumpScreenRenderTarget.Texture) + { + DrawFullScreenQuad(_dumpScreenRenderTarget.ShaderResourceView.Get(), Vector3(ScreenFadeCurrent, ScreenFadeCurrent, ScreenFadeCurrent)); + } if (ScreenFadeCurrent && percentage > 0.0f && percentage < 100.0f) DrawLoadingBar(percentage); diff --git a/TombEngine/Renderer/RendererSettings.cpp b/TombEngine/Renderer/RendererSettings.cpp index cad687213..1f137f9a1 100644 --- a/TombEngine/Renderer/RendererSettings.cpp +++ b/TombEngine/Renderer/RendererSettings.cpp @@ -64,8 +64,10 @@ namespace TEN::Renderer texture = Texture2D(); if (std::filesystem::is_regular_file(path)) + { texture = Texture2D(_device.Get(), path); - else + } + else if (!path.empty()) // Loading default texture without path may be intentional. { std::wstring_convert, wchar_t> converter; TENLog("Texture file not found: " + converter.to_bytes(path), LogLevel::Warning); diff --git a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h index 4a3f67354..c67c32650 100644 --- a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h +++ b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h @@ -1,5 +1,7 @@ #pragma once + #include "Game/control/control.h" +#include "Scripting/Internal/TEN/Flow/Settings/Settings.h" class ScriptInterfaceLevel; @@ -25,6 +27,7 @@ public: virtual ~ScriptInterfaceFlowHandler() = default; virtual void LoadFlowScript() = 0; + virtual Settings* GetSettings() = 0; virtual void SetGameDir(const std::string& assetDir) = 0; virtual std::string GetGameDir() = 0; diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index 42022655e..f4b2df397 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -238,6 +238,7 @@ static constexpr char ScriptReserved_LaraType[] = "LaraType"; static constexpr char ScriptReserved_RotationAxis[] = "RotationAxis"; static constexpr char ScriptReserved_ItemAction[] = "ItemAction"; static constexpr char ScriptReserved_ErrorMode[] = "ErrorMode"; +static constexpr char ScriptReserved_FastReload[] = "FastReload"; static constexpr char ScriptReserved_InventoryItem[] = "InventoryItem"; static constexpr char ScriptReserved_LaraWeaponType[] = "LaraWeaponType"; static constexpr char ScriptReserved_PlayerAmmoType[] = "PlayerAmmoType"; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp b/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp index bcb0e5de2..af8846404 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp @@ -1,19 +1,19 @@ #include "framework.h" -#include "Settings.h" +#include "Scripting/Internal/TEN/Flow/Settings/Settings.h" -/*** -Settings that will be run on game startup. -@tenclass Flow.Settings -@pragma nostrip -*/ +/// Settings that will be run on game startup. +// @tenclass Flow.Settings +// @pragma nostrip -void Settings::Register(sol::table & parent) +void Settings::Register(sol::table& parent) { - parent.new_usertype("Settings", + parent.new_usertype( + "Settings", sol::constructors(), sol::call_constructor, sol::constructors(), /*** How should the application respond to script errors? +
Must be one of the following: `ErrorMode.TERMINATE` - print to the log file and return to the title level when any script error is hit. This is the one you will want to go for if you want to know IMMEDIATELY if something has gone wrong. @@ -31,6 +31,14 @@ has an unrecoverable error, the game will close. @mem errorMode */ - "errorMode", &Settings::ErrorMode - ); + "errorMode", &Settings::ErrorMode, + +/// Can the game utilize the fast reload feature? +//
+// When set to `true`, the game will attempt to perform fast savegame reloading if current level is the same as +// the level loaded from the savegame. It will not work if the level timestamp or checksum has changed +// (i.e. level was updated). If set to `false`, this functionality is turned off. +// +// @mem fastReload + "fastReload", &Settings::FastReload); } diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.h b/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.h index e1988f09f..62dc474a8 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.h +++ b/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.h @@ -1,22 +1,20 @@ #pragma once #include "Scripting/Internal/ScriptAssert.h" -#include -static const std::unordered_map ERROR_MODES { - {"SILENT", ErrorMode::Silent}, - {"WARN", ErrorMode::Warn}, - {"TERMINATE", ErrorMode::Terminate} +namespace sol { class state; } + +static const std::unordered_map ERROR_MODES +{ + { "SILENT", ErrorMode::Silent }, + { "WARN", ErrorMode::Warn }, + { "TERMINATE", ErrorMode::Terminate } }; -namespace sol { - class state; -} - struct Settings { - ErrorMode ErrorMode; + ErrorMode ErrorMode = ErrorMode::Warn; + bool FastReload = true; - static void Register(sol::table & parent); + static void Register(sol::table& parent); }; - diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index 5657764b1..f04695fbc 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -490,7 +490,7 @@ std::optional GetCurrentSubtitle() return std::nullopt; } -void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD position) +void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD position, int forceFadeInTime) { if (!g_Configuration.EnableSound) return; @@ -565,11 +565,11 @@ void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD positio // BGM tracks are crossfaded, and additionally shuffled a bit to make things more natural. // Think everybody are fed up with same start-up sounds of Caves ambience... - if (crossfade && BASS_ChannelIsActive(SoundtrackSlot[(int)SoundTrackType::BGM].Channel)) + if (forceFadeInTime > 0 || (crossfade && BASS_ChannelIsActive(SoundtrackSlot[(int)SoundTrackType::BGM].Channel))) { // Crossfade... BASS_ChannelSetAttribute(stream, BASS_ATTRIB_VOL, 0.0f); - BASS_ChannelSlideAttribute(stream, BASS_ATTRIB_VOL, masterVolume, crossfadeTime); + BASS_ChannelSlideAttribute(stream, BASS_ATTRIB_VOL, masterVolume, (forceFadeInTime > 0) ? forceFadeInTime : crossfadeTime); // Shuffle... // Only activates if no custom position is passed as argument. @@ -659,7 +659,7 @@ void PlaySoundTrack(int index, short mask) PlaySoundTrack(SoundTracks[index].Name, SoundTracks[index].Mode); } -void StopSoundTracks(bool excludeAmbience) +void StopSoundTracks(int fadeoutTime, bool excludeAmbience) { for (int i = 0; i < (int)SoundTrackType::Count; i++) { @@ -667,12 +667,15 @@ void StopSoundTracks(bool excludeAmbience) if (excludeAmbience && type == SoundTrackType::BGM) continue; - StopSoundTrack(type, SOUND_XFADETIME_ONESHOT); + StopSoundTrack(type, fadeoutTime); } } void StopSoundTrack(SoundTrackType mode, int fadeoutTime) { + if (SoundtrackSlot[(int)mode].Channel == NULL) + return; + // Do fadeout. BASS_ChannelSlideAttribute(SoundtrackSlot[(int)mode].Channel, BASS_ATTRIB_VOL | BASS_SLIDE_LOG, -1.0f, fadeoutTime); diff --git a/TombEngine/Sound/sound.h b/TombEngine/Sound/sound.h index eb18997e9..bad970b6c 100644 --- a/TombEngine/Sound/sound.h +++ b/TombEngine/Sound/sound.h @@ -25,6 +25,7 @@ constexpr auto SOUND_MILLISECONDS_IN_SECOND = 1000.0f; constexpr auto SOUND_XFADETIME_BGM = 5000; constexpr auto SOUND_XFADETIME_BGM_START = 1500; constexpr auto SOUND_XFADETIME_ONESHOT = 200; +constexpr auto SOUND_XFADETIME_LEVELJUMP = 400; constexpr auto SOUND_XFADETIME_CUTSOUND = 100; constexpr auto SOUND_XFADETIME_HIJACKSOUND = 50; constexpr auto SOUND_BGM_DAMP_COEFFICIENT = 0.5f; @@ -164,11 +165,11 @@ void SayNo(); void PlaySoundSources(); int GetShatterSound(int shatterID); -void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, QWORD position = 0); +void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, QWORD position = 0, int forceFadeInTime = 0); void PlaySoundTrack(const std::string& trackName, short mask = 0); void PlaySoundTrack(int index, short mask = 0); void StopSoundTrack(SoundTrackType mode, int fadeoutTime); -void StopSoundTracks(bool excludeAmbience = false); +void StopSoundTracks(int fadeoutTime = SOUND_XFADETIME_LEVELJUMP, bool excludeAmbience = false); void ClearSoundTrackMasks(); void PlaySecretTrack(); void EnumerateLegacyTracks(); diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 2d7388c6e..656235b74 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -77,44 +77,54 @@ const std::vector BRIDGE_OBJECT_IDS = ID_BRIDGE_CUSTOM }; -char* LevelDataPtr; +LEVEL g_Level; + std::vector MoveablesIds; std::vector StaticObjectsIds; std::vector SpriteSequencesIds; -LEVEL g_Level; + +char* DataPtr; +char* CurrentDataPtr; + +bool FirstLevel = true; +int SystemNameHash = 0; +int LastLevelHash = 0; + +std::filesystem::file_time_type LastLevelTimestamp; +std::string LastLevelFilePath; unsigned char ReadUInt8() { - unsigned char value = *(unsigned char*)LevelDataPtr; - LevelDataPtr += 1; + unsigned char value = *(unsigned char*)CurrentDataPtr; + CurrentDataPtr += 1; return value; } short ReadInt16() { - short value = *(short*)LevelDataPtr; - LevelDataPtr += 2; + short value = *(short*)CurrentDataPtr; + CurrentDataPtr += 2; return value; } unsigned short ReadUInt16() { - unsigned short value = *(unsigned short*)LevelDataPtr; - LevelDataPtr += 2; + unsigned short value = *(unsigned short*)CurrentDataPtr; + CurrentDataPtr += 2; return value; } int ReadInt32() { - int value = *(int*)LevelDataPtr; - LevelDataPtr += 4; + int value = *(int*)CurrentDataPtr; + CurrentDataPtr += 4; return value; } float ReadFloat() { - float value = *(float*)LevelDataPtr; - LevelDataPtr += 4; + float value = *(float*)CurrentDataPtr; + CurrentDataPtr += 4; return value; } @@ -152,8 +162,8 @@ bool ReadBool() void ReadBytes(void* dest, int count) { - memcpy(dest, LevelDataPtr, count); - LevelDataPtr += count; + memcpy(dest, CurrentDataPtr, count); + CurrentDataPtr += count; } long long ReadLEB128(bool sign) @@ -188,9 +198,9 @@ std::string ReadString() return std::string(); else { - auto newPtr = LevelDataPtr + numBytes; - auto result = std::string(LevelDataPtr, newPtr); - LevelDataPtr = newPtr; + auto newPtr = CurrentDataPtr + numBytes; + auto result = std::string(CurrentDataPtr, newPtr); + CurrentDataPtr = newPtr; return result; } } @@ -205,54 +215,51 @@ void LoadItems() InitializeItemArray(ITEM_COUNT_MAX); - if (g_Level.NumItems > 0) + for (int i = 0; i < g_Level.NumItems; i++) { - for (int i = 0; i < g_Level.NumItems; i++) - { - auto* item = &g_Level.Items[i]; + auto* item = &g_Level.Items[i]; - item->Data = ItemData{}; - item->ObjectNumber = from_underlying(ReadInt16()); - item->RoomNumber = ReadInt16(); - item->Pose.Position.x = ReadInt32(); - item->Pose.Position.y = ReadInt32(); - item->Pose.Position.z = ReadInt32(); - item->Pose.Orientation.y = ReadInt16(); - item->Pose.Orientation.x = ReadInt16(); - item->Pose.Orientation.z = ReadInt16(); - item->Model.Color = ReadVector4(); - item->TriggerFlags = ReadInt16(); - item->Flags = ReadInt16(); - item->Name = ReadString(); + item->Data = ItemData{}; + item->ObjectNumber = from_underlying(ReadInt16()); + item->RoomNumber = ReadInt16(); + item->Pose.Position.x = ReadInt32(); + item->Pose.Position.y = ReadInt32(); + item->Pose.Position.z = ReadInt32(); + item->Pose.Orientation.y = ReadInt16(); + item->Pose.Orientation.x = ReadInt16(); + item->Pose.Orientation.z = ReadInt16(); + item->Model.Color = ReadVector4(); + item->TriggerFlags = ReadInt16(); + item->Flags = ReadInt16(); + item->Name = ReadString(); - g_GameScriptEntities->AddName(item->Name, (short)i); - g_GameScriptEntities->TryAddColliding((short)i); + g_GameScriptEntities->AddName(item->Name, (short)i); + g_GameScriptEntities->TryAddColliding((short)i); - memcpy(&item->StartPose, &item->Pose, sizeof(Pose)); - } + memcpy(&item->StartPose, &item->Pose, sizeof(Pose)); + } - // Initialize items. - for (int i = 0; i <= 1; i++) + // Initialize items. + for (int i = 0; i <= 1; i++) + { + // HACK: Initialize bridges first. Required because other items need final floordata to init properly. + if (i == 0) { - // HACK: Initialize bridges first. Required because other items need final floordata to init properly. - if (i == 0) + for (int j = 0; j < g_Level.NumItems; j++) { - for (int j = 0; j < g_Level.NumItems; j++) - { - const auto& item = g_Level.Items[j]; - if (Contains(BRIDGE_OBJECT_IDS, item.ObjectNumber)) - InitializeItem(j); - } + const auto& item = g_Level.Items[j]; + if (Contains(BRIDGE_OBJECT_IDS, item.ObjectNumber)) + InitializeItem(j); } - // Initialize non-bridge items second. - else if (i == 1) + } + // Initialize non-bridge items second. + else if (i == 1) + { + for (int j = 0; j < g_Level.NumItems; j++) { - for (int j = 0; j < g_Level.NumItems; j++) - { - const auto& item = g_Level.Items[j]; - if (!item.IsBridge()) - InitializeItem(j); - } + const auto& item = g_Level.Items[j]; + if (!item.IsBridge()) + InitializeItem(j); } } } @@ -674,7 +681,95 @@ static Plane ConvertFakePlaneToPlane(const Vector3& fakePlane, bool isFloor) return Plane(normal, dist); } -void ReadRooms() +void LoadDynamicRoomData() +{ + int roomCount = ReadInt32(); + + if (g_Level.Rooms.size() != roomCount) + throw std::exception("Dynamic room data count is inconsistent with room count"); + + for (int i = 0; i < roomCount; i++) + { + auto& room = g_Level.Rooms[i]; + + room.Name = ReadString(); + + int tagCount = ReadInt32(); + room.Tags.resize(0); + room.Tags.reserve(tagCount); + + for (int j = 0; j < tagCount; j++) + room.Tags.push_back(ReadString()); + + room.ambient = ReadVector3(); + + room.flippedRoom = ReadInt32(); + room.flags = ReadInt32(); + room.meshEffect = ReadInt32(); + room.reverbType = (ReverbType)ReadInt32(); + room.flipNumber = ReadInt32(); + + int staticCount = ReadInt32(); + room.mesh.resize(0); + room.mesh.reserve(staticCount); + + for (int j = 0; j < staticCount; j++) + { + auto& mesh = room.mesh.emplace_back(); + + mesh.roomNumber = i; + mesh.pos.Position.x = ReadInt32(); + mesh.pos.Position.y = ReadInt32(); + mesh.pos.Position.z = ReadInt32(); + mesh.pos.Orientation.y = ReadUInt16(); + mesh.pos.Orientation.x = ReadUInt16(); + mesh.pos.Orientation.z = ReadUInt16(); + mesh.scale = ReadFloat(); + mesh.flags = ReadUInt16(); + mesh.color = ReadVector4(); + mesh.staticNumber = ReadUInt16(); + mesh.HitPoints = ReadInt16(); + mesh.Name = ReadString(); + + g_GameScriptEntities->AddName(mesh.Name, mesh); + } + + int triggerVolumeCount = ReadInt32(); + room.TriggerVolumes.resize(0); + room.TriggerVolumes.reserve(triggerVolumeCount); + + for (int j = 0; j < triggerVolumeCount; j++) + { + auto& volume = room.TriggerVolumes.emplace_back(); + + volume.Type = (VolumeType)ReadInt32(); + + auto pos = ReadVector3(); + auto orient = ReadVector4(); + auto scale = ReadVector3(); + + volume.Enabled = ReadBool(); + volume.DetectInAdjacentRooms = ReadBool(); + + volume.Name = ReadString(); + volume.EventSetIndex = ReadInt32(); + + volume.Box = BoundingOrientedBox(pos, scale, orient); + volume.Sphere = BoundingSphere(pos, scale.x); + + volume.StateQueue.reserve(VOLUME_STATE_QUEUE_SIZE); + + g_GameScriptEntities->AddName(volume.Name, volume); + } + + g_GameScriptEntities->AddName(room.Name, room); + + room.itemNumber = NO_VALUE; + room.fxNumber = NO_VALUE; + } +} + +void LoadStaticRoomData() { constexpr auto ILLEGAL_FLOOR_SLOPE_ANGLE = ANGLE(36.0f); constexpr auto ILLEGAL_CEILING_SLOPE_ANGLE = ANGLE(45.0f); @@ -687,12 +782,6 @@ void ReadRooms() { auto& room = g_Level.Rooms.emplace_back(); - room.Name = ReadString(); - - int tagCount = ReadInt32(); - for (int j = 0; j < tagCount; j++) - room.Tags.push_back(ReadString()); - room.Position.x = ReadInt32(); room.Position.y = 0; room.Position.z = ReadInt32(); @@ -824,8 +913,6 @@ void ReadRooms() } } - room.ambient = ReadVector3(); - int lightCount = ReadInt32(); room.lights.reserve(lightCount); for (int j = 0; j < lightCount; j++) @@ -851,67 +938,8 @@ void ReadRooms() room.lights.push_back(light); } - - int staticCount = ReadInt32(); - room.mesh.reserve(staticCount); - for (int j = 0; j < staticCount; j++) - { - auto& mesh = room.mesh.emplace_back(); - mesh.roomNumber = i; - mesh.pos.Position.x = ReadInt32(); - mesh.pos.Position.y = ReadInt32(); - mesh.pos.Position.z = ReadInt32(); - mesh.pos.Orientation.y = ReadUInt16(); - mesh.pos.Orientation.x = ReadUInt16(); - mesh.pos.Orientation.z = ReadUInt16(); - mesh.scale = ReadFloat(); - mesh.flags = ReadUInt16(); - mesh.color = ReadVector4(); - mesh.staticNumber = ReadUInt16(); - mesh.HitPoints = ReadInt16(); - mesh.Name = ReadString(); - - g_GameScriptEntities->AddName(mesh.Name, mesh); - } - - int triggerVolumeCount = ReadInt32(); - room.TriggerVolumes.reserve(triggerVolumeCount); - for (int j = 0; j < triggerVolumeCount; j++) - { - auto& volume = room.TriggerVolumes.emplace_back(); - - volume.Type = (VolumeType)ReadInt32(); - - auto pos = ReadVector3(); - auto orient = ReadVector4(); - auto scale = ReadVector3(); - - volume.Enabled = ReadBool(); - volume.DetectInAdjacentRooms = ReadBool(); - - volume.Name = ReadString(); - volume.EventSetIndex = ReadInt32(); - - volume.Box = BoundingOrientedBox(pos, scale, orient); - volume.Sphere = BoundingSphere(pos, scale.x); - - volume.StateQueue.reserve(VOLUME_STATE_QUEUE_SIZE); - - g_GameScriptEntities->AddName(volume.Name, volume); - } - - room.flippedRoom = ReadInt32(); - room.flags = ReadInt32(); - room.meshEffect = ReadInt32(); - room.reverbType = (ReverbType)ReadInt32(); - room.flipNumber = ReadInt32(); - - room.itemNumber = NO_VALUE; - room.fxNumber = NO_VALUE; room.RoomNumber = i; - - g_GameScriptEntities->AddName(room.Name, room); } } @@ -921,7 +949,7 @@ void LoadRooms() Wibble = 0; - ReadRooms(); + LoadStaticRoomData(); BuildOutsideRoomsTable(); int numFloorData = ReadInt32(); @@ -929,15 +957,39 @@ void LoadRooms() ReadBytes(g_Level.FloorData.data(), numFloorData * sizeof(short)); } -void FreeLevel() +void FreeLevel(bool partial) { - static bool firstLevel = true; - if (firstLevel) + if (FirstLevel) { - firstLevel = false; + FirstLevel = false; return; } + // Should happen before resetting items. + if (partial) + ResetRoomData(); + + g_Level.Items.resize(0); + g_Level.AIObjects.resize(0); + g_Level.Cameras.resize(0); + g_Level.Sinks.resize(0); + g_Level.SoundSources.resize(0); + g_Level.VolumeEventSets.resize(0); + g_Level.GlobalEventSets.resize(0); + g_Level.LoopedEventSetIndices.resize(0); + + g_GameScript->FreeLevelScripts(); + g_GameScriptEntities->FreeEntities(); + + if (partial) + return; + + g_Renderer.FreeRendererData(); + + MoveablesIds.resize(0); + StaticObjectsIds.resize(0); + SpriteSequencesIds.resize(0); + g_Level.RoomTextures.resize(0); g_Level.MoveablesTextures.resize(0); g_Level.StaticsTextures.resize(0); @@ -947,8 +999,6 @@ void FreeLevel() g_Level.Rooms.resize(0); g_Level.Bones.resize(0); g_Level.Meshes.resize(0); - MoveablesIds.resize(0); - SpriteSequencesIds.resize(0); g_Level.PathfindingBoxes.resize(0); g_Level.Overlaps.resize(0); g_Level.Anims.resize(0); @@ -960,14 +1010,6 @@ void FreeLevel() g_Level.SoundDetails.resize(0); g_Level.SoundMap.resize(0); g_Level.FloorData.resize(0); - g_Level.Cameras.resize(0); - g_Level.Sinks.resize(0); - g_Level.SoundSources.resize(0); - g_Level.AIObjects.resize(0); - g_Level.VolumeEventSets.resize(0); - g_Level.GlobalEventSets.resize(0); - g_Level.LoopedEventSetIndices.resize(0); - g_Level.Items.resize(0); for (int i = 0; i < 2; i++) { @@ -975,10 +1017,6 @@ void FreeLevel() g_Level.Zones[j][i].clear(); } - g_Renderer.FreeRendererData(); - g_GameScript->FreeLevelScripts(); - g_GameScriptEntities->FreeEntities(); - FreeSamples(); } @@ -1161,46 +1199,88 @@ bool Decompress(byte* dest, byte* src, unsigned long compressedSize, unsigned lo return false; } -bool LoadLevel(int levelIndex) +bool ReadCompressedBlock(FILE* filePtr, bool skip) { - auto* level = g_GameFlow->GetLevel(levelIndex); + int compressedSize = 0; + int uncompressedSize = 0; - auto assetDir = g_GameFlow->GetGameDir(); - auto levelPath = assetDir + level->FileName; - TENLog("Loading level file: " + levelPath, LogLevel::Info); + ReadFileEx(&uncompressedSize, 1, 4, filePtr); + ReadFileEx(&compressedSize, 1, 4, filePtr); - LevelDataPtr = nullptr; + if (skip) + { + fseek(filePtr, compressedSize, SEEK_CUR); + return false; + } + + auto compressedBuffer = (char*)malloc(compressedSize); + ReadFileEx(compressedBuffer, compressedSize, 1, filePtr); + DataPtr = (char*)malloc(uncompressedSize); + Decompress((byte*)DataPtr, (byte*)compressedBuffer, compressedSize, uncompressedSize); + free(compressedBuffer); + + CurrentDataPtr = DataPtr; + + return true; +} + +void FinalizeBlock() +{ + if (DataPtr == nullptr) + return; + + free(DataPtr); + DataPtr = nullptr; + CurrentDataPtr = nullptr; +} + +void UpdateProgress(float progress, bool skip = false) +{ + if (skip) + return; + + g_Renderer.UpdateProgress(progress); +} + +bool LoadLevel(std::string path, bool partial) +{ FILE* filePtr = nullptr; - char* dataPtr = nullptr; - bool LoadedSuccessfully; - - auto loadingScreenPath = TEN::Utils::ToWString(assetDir + level->LoadScreenFileName); - g_Renderer.SetLoadingScreen(loadingScreenPath); - - SetScreenFadeIn(FADE_SCREEN_SPEED, true); - g_Renderer.UpdateProgress(0); + bool loadedSuccessfully = false; try { - filePtr = FileOpen(levelPath.c_str()); + filePtr = FileOpen(path.c_str()); if (!filePtr) - throw std::exception{ (std::string{ "Unable to read level file: " } + levelPath).c_str() }; + throw std::exception{ (std::string{ "Unable to read level file: " } + path).c_str() }; char header[4]; unsigned char version[4]; - int compressedSize; - int uncompressedSize; - int systemHash; + int systemHash = 0; + int levelHash = 0; // Read file header ReadFileEx(&header, 1, 4, filePtr); ReadFileEx(&version, 1, 4, filePtr); ReadFileEx(&systemHash, 1, 4, filePtr); + ReadFileEx(&levelHash, 1, 4, filePtr); - // Check file header + // Check file header. if (std::string(header) != "TEN") throw std::invalid_argument("Level file header is not valid! Must be TEN. Probably old level version?"); + + // Check level file integrity to allow or disallow fast reload. + if (partial && levelHash != LastLevelHash) + { + TENLog("Level file has changed since the last load; fast reload is not possible.", LogLevel::Warning); + partial = false; + FreeLevel(false); // Erase all precached data. + } + + // Store information about last loaded level file. + LastLevelFilePath = path; + LastLevelHash = levelHash; + LastLevelTimestamp = std::filesystem::last_write_time(path); TENLog("Level compiler version: " + std::to_string(version[0]) + "." + std::to_string(version[1]) + "." + std::to_string(version[2]), LogLevel::Info); @@ -1225,95 +1305,105 @@ bool LoadLevel(int levelIndex) SystemNameHash = 0; } - // Read data sizes - ReadFileEx(&uncompressedSize, 1, 4, filePtr); - ReadFileEx(&compressedSize, 1, 4, filePtr); + if (partial) + { + TENLog("Loading same level. Skipping media and geometry data.", LogLevel::Info); + SetScreenFadeOut(FADE_SCREEN_SPEED * 2, true); + } + else + { + SetScreenFadeIn(FADE_SCREEN_SPEED, true); + } - // The entire level is ZLIB compressed - auto compressedBuffer = (char*)malloc(compressedSize); - dataPtr = (char*)malloc(uncompressedSize); - LevelDataPtr = dataPtr; + UpdateProgress(0); - ReadFileEx(compressedBuffer, compressedSize, 1, filePtr); - Decompress((byte*)LevelDataPtr, (byte*)compressedBuffer, compressedSize, uncompressedSize); + // Media block + if (ReadCompressedBlock(filePtr, partial)) + { + LoadTextures(); + UpdateProgress(30); - // Now the entire level is decompressed, we can close it - free(compressedBuffer); - FileClose(filePtr); - filePtr = nullptr; + LoadSamples(); + UpdateProgress(40); - LoadTextures(); + FinalizeBlock(); + } - g_Renderer.UpdateProgress(20); + // Geometry block + if (ReadCompressedBlock(filePtr, partial)) + { + LoadRooms(); + UpdateProgress(50); - LoadRooms(); - g_Renderer.UpdateProgress(40); + LoadObjects(); + UpdateProgress(60); - LoadObjects(); - g_Renderer.UpdateProgress(50); + LoadSprites(); + LoadBoxes(); + LoadAnimatedTextures(); + UpdateProgress(70); - LoadSprites(); - LoadCameras(); - LoadSoundSources(); - g_Renderer.UpdateProgress(60); + FinalizeBlock(); + } - LoadBoxes(); + // Dynamic data block + if (ReadCompressedBlock(filePtr, false)) + { + LoadDynamicRoomData(); + LoadItems(); + LoadAIObjects(); + LoadCameras(); + LoadSoundSources(); + LoadEventSets(); + UpdateProgress(80, partial); - //InitializeLOTarray(true); - - LoadAnimatedTextures(); - g_Renderer.UpdateProgress(70); - - LoadItems(); - LoadAIObjects(); - - LoadEventSets(); - - LoadSamples(); - g_Renderer.UpdateProgress(80); + FinalizeBlock(); + } TENLog("Initializing level...", LogLevel::Info); - // Initialize the game + // Initialize game. InitializeGameFlags(); InitializeLara(!InitializeGame && CurrentLevel > 0); InitializeNeighborRoomList(); GetCarriedItems(); GetAIPickups(); g_GameScriptEntities->AssignLara(); - g_Renderer.UpdateProgress(90); + UpdateProgress(90, partial); - TENLog("Preparing renderer...", LogLevel::Info); + if (!partial) + { + g_Renderer.PrepareDataForTheRenderer(); + SetScreenFadeOut(FADE_SCREEN_SPEED, true); + StopSoundTracks(SOUND_XFADETIME_BGM_START); + } + else + { + SetScreenFadeIn(FADE_SCREEN_SPEED, true); + StopSoundTracks(SOUND_XFADETIME_LEVELJUMP); + } - g_Renderer.PrepareDataForTheRenderer(); + UpdateProgress(100, partial); TENLog("Level loading complete.", LogLevel::Info); - SetScreenFadeOut(FADE_SCREEN_SPEED, true); - g_Renderer.UpdateProgress(100); - - LoadedSuccessfully = true; + loadedSuccessfully = true; } catch (std::exception& ex) { - if (filePtr) - { - FileClose(filePtr); - filePtr = nullptr; - } + FinalizeBlock(); + StopSoundTracks(SOUND_XFADETIME_LEVELJUMP); TENLog("Error while loading level: " + std::string(ex.what()), LogLevel::Error); - LoadedSuccessfully = false; + loadedSuccessfully = false; SystemNameHash = 0; } - if (dataPtr) - { - free(dataPtr); - dataPtr = LevelDataPtr = nullptr; - } + // Now the entire level is decompressed, we can close it + FileClose(filePtr); + filePtr = nullptr; - return LoadedSuccessfully; + return loadedSuccessfully; } void LoadSamples() @@ -1386,7 +1476,7 @@ void LoadBoxes() int excessiveZoneGroups = numZoneGroups - j + 1; TENLog("Level file contains extra pathfinding data, number of excessive zone groups is " + std::to_string(excessiveZoneGroups) + ". These zone groups will be ignored.", LogLevel::Warning); - LevelDataPtr += numBoxes * sizeof(int); + CurrentDataPtr += numBoxes * sizeof(int); } else { @@ -1406,13 +1496,35 @@ void LoadBoxes() bool LoadLevelFile(int levelIndex) { - TENLog("Loading level file...", LogLevel::Info); + const auto& level = *g_GameFlow->GetLevel(levelIndex); + + auto assetDir = g_GameFlow->GetGameDir(); + auto levelPath = assetDir + level.FileName; + + if (!std::filesystem::is_regular_file(levelPath)) + { + TENLog("Level file not found: " + levelPath, LogLevel::Error); + return false; + } + + TENLog("Loading level file: " + levelPath, LogLevel::Info); + + auto timestamp = std::filesystem::last_write_time(levelPath); + bool fastReload = (g_GameFlow->GetSettings()->FastReload && levelIndex == CurrentLevel && timestamp == LastLevelTimestamp && levelPath == LastLevelFilePath); + + // Dumping game scene right after engine launch is impossible, as no scene exists. + if (!FirstLevel && fastReload) + g_Renderer.DumpGameScene(); + + auto loadingScreenPath = TEN::Utils::ToWString(assetDir + level.LoadScreenFileName); + g_Renderer.SetLoadingScreen(fastReload ? std::wstring{} : loadingScreenPath); BackupLara(); + StopAllSounds(); CleanUp(); - FreeLevel(); + FreeLevel(fastReload); - LevelLoadTask = std::async(std::launch::async, LoadLevel, levelIndex); + LevelLoadTask = std::async(std::launch::async, LoadLevel, levelPath, fastReload); return LevelLoadTask.get(); } diff --git a/TombEngine/Specific/level.h b/TombEngine/Specific/level.h index 1c0c84853..ef6c260f8 100644 --- a/TombEngine/Specific/level.h +++ b/TombEngine/Specific/level.h @@ -140,6 +140,8 @@ extern std::vector MoveablesIds; extern std::vector StaticObjectsIds; extern std::vector SpriteSequencesIds; extern LEVEL g_Level; +extern int SystemNameHash; +extern int LastLevelHash; inline std::future LevelLoadTask; @@ -149,7 +151,7 @@ void FileClose(FILE* ptr); bool Decompress(byte* dest, byte* src, unsigned long compressedSize, unsigned long uncompressedSize); bool LoadLevelFile(int levelIndex); -void FreeLevel(); +void FreeLevel(bool partial); void LoadTextures(); void LoadRooms(); diff --git a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h index 3bbe2dae6..2cb2b7ffc 100644 --- a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h +++ b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h @@ -7017,6 +7017,7 @@ flatbuffers::Offset CreateUnionVec(flatbuffers::FlatBufferBuilder &_fb struct SaveGameHeaderT : public flatbuffers::NativeTable { typedef SaveGameHeader TableType; std::string level_name{}; + int32_t level_hash = 0; int32_t days = 0; int32_t hours = 0; int32_t minutes = 0; @@ -7032,17 +7033,21 @@ struct SaveGameHeader FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct Traits; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_LEVEL_NAME = 4, - VT_DAYS = 6, - VT_HOURS = 8, - VT_MINUTES = 10, - VT_SECONDS = 12, - VT_LEVEL = 14, - VT_TIMER = 16, - VT_COUNT = 18 + VT_LEVEL_HASH = 6, + VT_DAYS = 8, + VT_HOURS = 10, + VT_MINUTES = 12, + VT_SECONDS = 14, + VT_LEVEL = 16, + VT_TIMER = 18, + VT_COUNT = 20 }; const flatbuffers::String *level_name() const { return GetPointer(VT_LEVEL_NAME); } + int32_t level_hash() const { + return GetField(VT_LEVEL_HASH, 0); + } int32_t days() const { return GetField(VT_DAYS, 0); } @@ -7068,6 +7073,7 @@ struct SaveGameHeader FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_LEVEL_NAME) && verifier.VerifyString(level_name()) && + VerifyField(verifier, VT_LEVEL_HASH) && VerifyField(verifier, VT_DAYS) && VerifyField(verifier, VT_HOURS) && VerifyField(verifier, VT_MINUTES) && @@ -7089,6 +7095,9 @@ struct SaveGameHeaderBuilder { void add_level_name(flatbuffers::Offset level_name) { fbb_.AddOffset(SaveGameHeader::VT_LEVEL_NAME, level_name); } + void add_level_hash(int32_t level_hash) { + fbb_.AddElement(SaveGameHeader::VT_LEVEL_HASH, level_hash, 0); + } void add_days(int32_t days) { fbb_.AddElement(SaveGameHeader::VT_DAYS, days, 0); } @@ -7124,6 +7133,7 @@ struct SaveGameHeaderBuilder { inline flatbuffers::Offset CreateSaveGameHeader( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset level_name = 0, + int32_t level_hash = 0, int32_t days = 0, int32_t hours = 0, int32_t minutes = 0, @@ -7139,6 +7149,7 @@ inline flatbuffers::Offset CreateSaveGameHeader( builder_.add_minutes(minutes); builder_.add_hours(hours); builder_.add_days(days); + builder_.add_level_hash(level_hash); builder_.add_level_name(level_name); return builder_.Finish(); } @@ -7151,6 +7162,7 @@ struct SaveGameHeader::Traits { inline flatbuffers::Offset CreateSaveGameHeaderDirect( flatbuffers::FlatBufferBuilder &_fbb, const char *level_name = nullptr, + int32_t level_hash = 0, int32_t days = 0, int32_t hours = 0, int32_t minutes = 0, @@ -7162,6 +7174,7 @@ inline flatbuffers::Offset CreateSaveGameHeaderDirect( return TEN::Save::CreateSaveGameHeader( _fbb, level_name__, + level_hash, days, hours, minutes, @@ -10245,6 +10258,7 @@ inline void SaveGameHeader::UnPackTo(SaveGameHeaderT *_o, const flatbuffers::res (void)_o; (void)_resolver; { auto _e = level_name(); if (_e) _o->level_name = _e->str(); } + { auto _e = level_hash(); _o->level_hash = _e; } { auto _e = days(); _o->days = _e; } { auto _e = hours(); _o->hours = _e; } { auto _e = minutes(); _o->minutes = _e; } @@ -10263,6 +10277,7 @@ inline flatbuffers::Offset CreateSaveGameHeader(flatbuffers::Fla (void)_o; struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const SaveGameHeaderT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; auto _level_name = _o->level_name.empty() ? _fbb.CreateSharedString("") : _fbb.CreateString(_o->level_name); + auto _level_hash = _o->level_hash; auto _days = _o->days; auto _hours = _o->hours; auto _minutes = _o->minutes; @@ -10273,6 +10288,7 @@ inline flatbuffers::Offset CreateSaveGameHeader(flatbuffers::Fla return TEN::Save::CreateSaveGameHeader( _fbb, _level_name, + _level_hash, _days, _hours, _minutes, diff --git a/TombEngine/Specific/savegame/schema/ten_savegame.fbs b/TombEngine/Specific/savegame/schema/ten_savegame.fbs index d7089ad40..634b2c9f8 100644 --- a/TombEngine/Specific/savegame/schema/ten_savegame.fbs +++ b/TombEngine/Specific/savegame/schema/ten_savegame.fbs @@ -504,6 +504,7 @@ table UnionVec { table SaveGameHeader { level_name: string; + level_hash: int32; days: int32; hours: int32; minutes: int32; diff --git a/TombEngine/framework.h b/TombEngine/framework.h index 6740dadd0..663b28f38 100644 --- a/TombEngine/framework.h +++ b/TombEngine/framework.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include From 5825bdee0973e5a0d993d016d165bc3befed1564 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:32:52 +0100 Subject: [PATCH 020/112] Add more clean version management and test build warning --- TombEngine/Renderer/RendererDrawMenu.cpp | 9 +++++++++ TombEngine/Resources.rc | 9 +++++---- TombEngine/TombEngine.vcxproj | 1 + TombEngine/version.h | 20 ++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 TombEngine/version.h diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index 7db09fa8d..a398de129 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -1,6 +1,7 @@ #include "framework.h" #include "Renderer/Renderer.h" +#include "version.h" #include "Game/animation.h" #include "Game/control/control.h" #include "Game/control/volume.h" @@ -1186,6 +1187,14 @@ namespace TEN::Renderer void Renderer::DrawDebugInfo(RenderView& view) { +#ifdef TEST_BUILD + if (CurrentLevel == 0) + { + AddString("TombEngine " + std::string(TEN_VERSION_STRING) + " test build - not for distribution", + Vector2(20, 560), Vector4(1.0f, 0, 0, 0.5f), 0.7f, 0); + } +#endif + if (!DebugMode || CurrentLevel == 0) return; diff --git a/TombEngine/Resources.rc b/TombEngine/Resources.rc index 908593e12..251a6ddff 100644 --- a/TombEngine/Resources.rc +++ b/TombEngine/Resources.rc @@ -3,6 +3,7 @@ #pragma code_page(65001) #include "resource.h" +#include "version.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -26,8 +27,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,5,0,2 - PRODUCTVERSION 1,7,2,0 + FILEVERSION TEN_MAJOR_VERSION,TEN_MINOR_VERSION,TEN_BUILD_NUMBER,TEN_REVISION_NUMBER + PRODUCTVERSION TE_MAJOR_VERSION,TE_MINOR_VERSION,TE_BUILD_NUMBER,TE_REVISION_NUMBER FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,12 +44,12 @@ BEGIN BLOCK "000904b0" BEGIN VALUE "CompanyName", "Tomb Engine Development Community" - VALUE "FileVersion", "1.5.0.2" VALUE "InternalName", "TombEngine.exe" VALUE "LegalCopyright", "Copyright (c) 2024" VALUE "OriginalFilename", "TombEngine.exe" VALUE "ProductName", "Tomb Engine" - VALUE "ProductVersion", "1.7.2.0" + VALUE "FileVersion", TEN_VERSION_STRING + VALUE "ProductVersion", TE_VERSION_STRING END END BLOCK "VarFileInfo" diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index a18d90017..fc3dfca7b 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -957,6 +957,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. + diff --git a/TombEngine/version.h b/TombEngine/version.h new file mode 100644 index 000000000..2601cf754 --- /dev/null +++ b/TombEngine/version.h @@ -0,0 +1,20 @@ +#pragma once + +#define TE_MAJOR_VERSION 1 +#define TE_MINOR_VERSION 7 +#define TE_BUILD_NUMBER 3 +#define TE_REVISION_NUMBER 0 + +#define TEN_MAJOR_VERSION 1 +#define TEN_MINOR_VERSION 6 +#define TEN_BUILD_NUMBER 0 +#define TEN_REVISION_NUMBER 0 + +#define TEST_BUILD 1 + +#define STRINGIFY(x) #x +#define MAKE_VERSION_STRING(major, minor, build, revision) STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(build) "." STRINGIFY(revision) + +// Define the version string using your version components +#define TE_VERSION_STRING MAKE_VERSION_STRING(TE_MAJOR_VERSION, TE_MINOR_VERSION, TE_BUILD_NUMBER, TE_REVISION_NUMBER) +#define TEN_VERSION_STRING MAKE_VERSION_STRING(TEN_MAJOR_VERSION, TEN_MINOR_VERSION, TEN_BUILD_NUMBER, TEN_REVISION_NUMBER) From 8a10fba15670f94d2763024e783f4a61d094cfee Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:04:50 +0100 Subject: [PATCH 021/112] Update version.h --- TombEngine/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TombEngine/version.h b/TombEngine/version.h index 2601cf754..8a6c31706 100644 --- a/TombEngine/version.h +++ b/TombEngine/version.h @@ -6,9 +6,9 @@ #define TE_REVISION_NUMBER 0 #define TEN_MAJOR_VERSION 1 -#define TEN_MINOR_VERSION 6 +#define TEN_MINOR_VERSION 5 #define TEN_BUILD_NUMBER 0 -#define TEN_REVISION_NUMBER 0 +#define TEN_REVISION_NUMBER 3 #define TEST_BUILD 1 From 84ac5652b9c69c4ce0dae7614952fe19ef72bef3 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:35:33 +0100 Subject: [PATCH 022/112] Embed simple title level --- CHANGELOG.md | 1 + TombEngine/Resources.rc | 3 +- TombEngine/{ => Resources}/ten.ico | Bin TombEngine/Specific/level.cpp | 24 ++++++++++++--- TombEngine/Specific/winmain.cpp | 47 ++++++++++++++++++++++++++++- TombEngine/Specific/winmain.h | 5 ++- TombEngine/TombEngine.vcxproj | 7 +++-- TombEngine/resource.h | 1 + 8 files changed, 78 insertions(+), 10 deletions(-) rename TombEngine/{ => Resources}/ten.ico (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0feead175..a04fa08be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Features/Amendments * Added fast savegame reloading. +* Added ability to run engine without title level file. * Added ricochet sounds and make the effect more prominent. ### Lua API changes diff --git a/TombEngine/Resources.rc b/TombEngine/Resources.rc index 251a6ddff..4e49592d5 100644 --- a/TombEngine/Resources.rc +++ b/TombEngine/Resources.rc @@ -65,7 +65,8 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "ten.ico" +IDI_ICON1 ICON "Resources\\ten.ico" +IDR_TITLELEVEL BIN "Resources\\title.ten" ///////////////////////////////////////////////////////////////////////////// diff --git a/TombEngine/ten.ico b/TombEngine/Resources/ten.ico similarity index 100% rename from TombEngine/ten.ico rename to TombEngine/Resources/ten.ico diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 656235b74..d9fbd5a3c 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -28,6 +28,7 @@ #include "Sound/sound.h" #include "Specific/Input/Input.h" #include "Specific/trutils.h" +#include "Specific/winmain.h" using TEN::Renderer::g_Renderer; @@ -1242,7 +1243,7 @@ void UpdateProgress(float progress, bool skip = false) g_Renderer.UpdateProgress(progress); } -bool LoadLevel(std::string path, bool partial) +bool LoadLevel(const std::string& path, bool partial) { FILE* filePtr = nullptr; bool loadedSuccessfully = false; @@ -1494,6 +1495,7 @@ void LoadBoxes() } } + bool LoadLevelFile(int levelIndex) { const auto& level = *g_GameFlow->GetLevel(levelIndex); @@ -1501,10 +1503,20 @@ bool LoadLevelFile(int levelIndex) auto assetDir = g_GameFlow->GetGameDir(); auto levelPath = assetDir + level.FileName; + bool usingEmbeddedLevelFile = false; + if (!std::filesystem::is_regular_file(levelPath)) { - TENLog("Level file not found: " + levelPath, LogLevel::Error); - return false; + if (levelIndex == 0 && GenerateTitleLevel(levelPath)) + { + usingEmbeddedLevelFile = true; + TENLog("Regenerated title level file from embedded data: " + levelPath, LogLevel::Info); + } + else + { + TENLog("Level file not found: " + levelPath, LogLevel::Error); + return false; + } } TENLog("Loading level file: " + levelPath, LogLevel::Info); @@ -1525,8 +1537,12 @@ bool LoadLevelFile(int levelIndex) FreeLevel(fastReload); LevelLoadTask = std::async(std::launch::async, LoadLevel, levelPath, fastReload); + bool result = LevelLoadTask.get(); - return LevelLoadTask.get(); + if (usingEmbeddedLevelFile) + std::filesystem::remove(levelPath); + + return result; } void LoadSprites() diff --git a/TombEngine/Specific/winmain.cpp b/TombEngine/Specific/winmain.cpp index a601d0d36..311beada2 100644 --- a/TombEngine/Specific/winmain.cpp +++ b/TombEngine/Specific/winmain.cpp @@ -3,11 +3,11 @@ #include #include -#include #include #include #include +#include "resource.h" #include "Game/control/control.h" #include "Game/savegame.h" #include "Renderer/Renderer.h" @@ -129,6 +129,50 @@ void DisableDpiAwareness() FreeLibrary(lib); } +bool GenerateTitleLevel(const std::string& levelPath) +{ + // Try to load the embedded resource "data.bin" + HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(IDR_TITLELEVEL), "BIN"); + if (hResource == NULL) + { + TENLog("Embedded title level file not found.", LogLevel::Error); + return false; + } + + // Load the resource into memory + HGLOBAL hGlobal = LoadResource(NULL, hResource); + if (hGlobal == NULL) + { + TENLog("Failed to load embedded title level file.", LogLevel::Error); + return false; + } + + // Lock the resource to get a pointer to the data + void* pData = LockResource(hGlobal); + DWORD dwSize = SizeofResource(NULL, hResource); + + // Write the resource data to the file + try + { + std::ofstream outFile(levelPath, std::ios::binary); + if (!outFile) + throw std::ios_base::failure("Failed to create title level file"); + + outFile.write(reinterpret_cast(pData), dwSize); + if (!outFile) + throw std::ios_base::failure("Failed to write to title level file"); + + outFile.close(); + } + catch (const std::exception& ex) + { + TENLog("Error while regenerating title level file: " + std::string(ex.what()), LogLevel::Error); + return false; + } + + return true; +} + void WinProcMsg() { MSG Msg; @@ -305,6 +349,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine std::to_string(ver[0]) + "." + std::to_string(ver[1]) + "." + std::to_string(ver[2]) + " " + + std::to_string(ver[3]) + " " + #ifdef _WIN64 "(64-bit)" #else diff --git a/TombEngine/Specific/winmain.h b/TombEngine/Specific/winmain.h index 14158adb5..851b13773 100644 --- a/TombEngine/Specific/winmain.h +++ b/TombEngine/Specific/winmain.h @@ -38,6 +38,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine void WinClose(); LRESULT CALLBACK WinAppProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void CALLBACK HandleWmCommand(unsigned short wParam); + Vector2i GetScreenResolution(); std::vector GetAllSupportedScreenResolutions(); -int GetCurrentScreenRefreshRate(); \ No newline at end of file +int GetCurrentScreenRefreshRate(); + +bool GenerateTitleLevel(const std::string& levelPath); \ No newline at end of file diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index fc3dfca7b..b7c821b43 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -1364,6 +1364,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. + @@ -1405,9 +1406,6 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. - - - Document @@ -1634,6 +1632,9 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. true + + + diff --git a/TombEngine/resource.h b/TombEngine/resource.h index a4f1026ea..c59a7f8a9 100644 --- a/TombEngine/resource.h +++ b/TombEngine/resource.h @@ -4,6 +4,7 @@ // #define IDD_SETUP 103 #define IDI_ICON1 108 +#define IDR_TITLELEVEL 109 #define IDC_CHK_ENABLE_SOUND2 1012 #define IDC_GFXADAPTER 1020 #define IDC_GFXADAPTERTXT 1021 From 5f43caf7a28f6637986349d33c8ef5b609a82072 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:49:52 +0100 Subject: [PATCH 023/112] Update version.h --- TombEngine/version.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/TombEngine/version.h b/TombEngine/version.h index 8a6c31706..dc4339854 100644 --- a/TombEngine/version.h +++ b/TombEngine/version.h @@ -12,9 +12,8 @@ #define TEST_BUILD 1 -#define STRINGIFY(x) #x -#define MAKE_VERSION_STRING(major, minor, build, revision) STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(build) "." STRINGIFY(revision) +#define TOSTR(x) #x +#define MAKE_VERSION_STRING(major, minor, build, revision) TOSTR(major) "." TOSTR(minor) "." TOSTR(build) "." TOSTR(revision) -// Define the version string using your version components #define TE_VERSION_STRING MAKE_VERSION_STRING(TE_MAJOR_VERSION, TE_MINOR_VERSION, TE_BUILD_NUMBER, TE_REVISION_NUMBER) #define TEN_VERSION_STRING MAKE_VERSION_STRING(TEN_MAJOR_VERSION, TEN_MINOR_VERSION, TEN_BUILD_NUMBER, TEN_REVISION_NUMBER) From 5db8e9861142f72b43c6ee34283ac434ccba9c00 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:01:49 +0100 Subject: [PATCH 024/112] Restore old condition for look camera as well --- TombEngine/Game/camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index 070bac476..f63704bb5 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -1268,7 +1268,7 @@ void CalculateCamera(const CollisionInfo& coll) { Camera.fixedCamera = false; - if (Camera.speed != 1 && !Lara.Control.Look.IsUsingBinoculars) + if (Camera.speed != 1 && !Lara.Control.Look.IsUsingBinoculars && Camera.oldType != CameraType::Look) { if (TargetSnaps <= 8) { From 1f89c23537723ad179c23a747144def60bd7d8f3 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:11:31 +0100 Subject: [PATCH 025/112] Rename title.ten to title.bin --- TombEngine/Resources.rc | 2 +- TombEngine/Resources/title.bin | Bin 0 -> 105784 bytes TombEngine/TombEngine.vcxproj | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 TombEngine/Resources/title.bin diff --git a/TombEngine/Resources.rc b/TombEngine/Resources.rc index 4e49592d5..10a5632db 100644 --- a/TombEngine/Resources.rc +++ b/TombEngine/Resources.rc @@ -66,7 +66,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_ICON1 ICON "Resources\\ten.ico" -IDR_TITLELEVEL BIN "Resources\\title.ten" +IDR_TITLELEVEL BIN "Resources\\title.bin" ///////////////////////////////////////////////////////////////////////////// diff --git a/TombEngine/Resources/title.bin b/TombEngine/Resources/title.bin new file mode 100644 index 0000000000000000000000000000000000000000..9475b7db030cd203a9c71f36bdfa1694079a1852 GIT binary patch literal 105784 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

4nz6jw;=3aL+M)|HGQECubQ4 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 z

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 TJ6$_JwsEMgU|=}cEBG7$;#C`! literal 0 HcmV?d00001 diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index b7c821b43..c054f00b8 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -1364,7 +1364,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. - + From 890dd5720f84a55c49781800cc8802ead18e74bd Mon Sep 17 00:00:00 2001 From: Sezz Date: Tue, 12 Nov 2024 16:01:38 +1100 Subject: [PATCH 026/112] Improve fly cheat --- TombEngine/Game/Lara/lara_helpers.cpp | 4 +++- TombEngine/Game/animation.cpp | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/TombEngine/Game/Lara/lara_helpers.cpp b/TombEngine/Game/Lara/lara_helpers.cpp index 3efab248d..74bf0ec92 100644 --- a/TombEngine/Game/Lara/lara_helpers.cpp +++ b/TombEngine/Game/Lara/lara_helpers.cpp @@ -895,7 +895,9 @@ void HandlePlayerFlyCheat(ItemInfo& item) { SetAnimation(item, LA_FLY_CHEAT); ResetPlayerFlex(&item); - item.Animation.IsAirborne = false; + item.Animation.Velocity = Vector3::Zero; + item.Animation.IsAirborne = true; + item.Pose.Position.y -= CLICK(0.5f); item.HitPoints = LARA_HEALTH_MAX; player.Control.WaterStatus = WaterStatus::FlyCheat; diff --git a/TombEngine/Game/animation.cpp b/TombEngine/Game/animation.cpp index f29ee8590..ae838853c 100644 --- a/TombEngine/Game/animation.cpp +++ b/TombEngine/Game/animation.cpp @@ -319,10 +319,13 @@ void AnimateItem(ItemInfo* item) } else { - item->Animation.Velocity.y += GetEffectiveGravity(item->Animation.Velocity.y); - item->Animation.Velocity.z += animAccel.z; + if (item->Animation.ActiveState != LS_FLY_CHEAT) + { + item->Animation.Velocity.y += GetEffectiveGravity(item->Animation.Velocity.y); + item->Animation.Velocity.z += animAccel.z; - item->Pose.Position.y += item->Animation.Velocity.y; + item->Pose.Position.y += item->Animation.Velocity.y; + } } } else From d32c1350b7f209f28c380eef31e3499f243d9053 Mon Sep 17 00:00:00 2001 From: Sezz Date: Tue, 12 Nov 2024 16:12:29 +1100 Subject: [PATCH 027/112] Remove pointless loop --- TombEngine/Renderer/RendererHelper.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/TombEngine/Renderer/RendererHelper.cpp b/TombEngine/Renderer/RendererHelper.cpp index 7a2544541..19bc86d7b 100644 --- a/TombEngine/Renderer/RendererHelper.cpp +++ b/TombEngine/Renderer/RendererHelper.cpp @@ -313,9 +313,6 @@ namespace TEN::Renderer auto frameData = GetFrameInterpData(*nativeItem); UpdateAnimation(itemToDraw, moveableObj, frameData, UINT_MAX); - - for (int m = 0; m < obj->nmeshes; m++) - itemToDraw->AnimTransforms[m] = itemToDraw->AnimTransforms[m]; } void Renderer::UpdateItemAnimations(RenderView& view) From 3e605e1dda7cbcdc9316ee2a825ce7585584c9fb Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:07:44 +0100 Subject: [PATCH 028/112] Add safeguard against silent crashes if file format is incorrect --- TombEngine/Specific/level.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index d9fbd5a3c..675c37a9f 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -687,7 +687,7 @@ void LoadDynamicRoomData() int roomCount = ReadInt32(); if (g_Level.Rooms.size() != roomCount) - throw std::exception("Dynamic room data count is inconsistent with room count"); + throw std::exception("Dynamic room data count is inconsistent with room count."); for (int i = 0; i < roomCount; i++) { @@ -1200,6 +1200,21 @@ bool Decompress(byte* dest, byte* src, unsigned long compressedSize, unsigned lo return false; } +long GetRemainingSize(FILE* filePtr) +{ + long current_position = ftell(filePtr); + + if (fseek(filePtr, 0, SEEK_END) != 0) + return NO_VALUE; + + long size = ftell(filePtr); + + if (fseek(filePtr, current_position, SEEK_SET) != 0) + return NO_VALUE; + + return size; +} + bool ReadCompressedBlock(FILE* filePtr, bool skip) { int compressedSize = 0; @@ -1208,6 +1223,11 @@ bool ReadCompressedBlock(FILE* filePtr, bool skip) ReadFileEx(&uncompressedSize, 1, 4, filePtr); ReadFileEx(&compressedSize, 1, 4, filePtr); + // Safeguard against changed file format. + long remainingSize = GetRemainingSize(filePtr); + if (uncompressedSize <= 0 || compressedSize <= 0 || compressedSize > remainingSize) + throw std::exception{ "Data block size is incorrect. Probably old level version?" }; + if (skip) { fseek(filePtr, compressedSize, SEEK_CUR); From 5f884cd84c21f5e7e766dfd08e0a783921920edb Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:16:16 +0100 Subject: [PATCH 029/112] Autocreate /data directory for title level, if it does not exist --- TombEngine/Specific/level.cpp | 12 ++---------- TombEngine/Specific/winmain.cpp | 9 ++++++--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 675c37a9f..c00d47725 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1523,14 +1523,11 @@ bool LoadLevelFile(int levelIndex) auto assetDir = g_GameFlow->GetGameDir(); auto levelPath = assetDir + level.FileName; - bool usingEmbeddedLevelFile = false; - if (!std::filesystem::is_regular_file(levelPath)) { if (levelIndex == 0 && GenerateTitleLevel(levelPath)) { - usingEmbeddedLevelFile = true; - TENLog("Regenerated title level file from embedded data: " + levelPath, LogLevel::Info); + TENLog("Title level file not found, generating dummy level: " + levelPath, LogLevel::Info); } else { @@ -1557,12 +1554,7 @@ bool LoadLevelFile(int levelIndex) FreeLevel(fastReload); LevelLoadTask = std::async(std::launch::async, LoadLevel, levelPath, fastReload); - bool result = LevelLoadTask.get(); - - if (usingEmbeddedLevelFile) - std::filesystem::remove(levelPath); - - return result; + return LevelLoadTask.get(); } void LoadSprites() diff --git a/TombEngine/Specific/winmain.cpp b/TombEngine/Specific/winmain.cpp index 311beada2..bf206367b 100644 --- a/TombEngine/Specific/winmain.cpp +++ b/TombEngine/Specific/winmain.cpp @@ -154,19 +154,22 @@ bool GenerateTitleLevel(const std::string& levelPath) // Write the resource data to the file try { + std::filesystem::path dir = std::filesystem::path(levelPath).parent_path(); + std::filesystem::create_directories(dir); + std::ofstream outFile(levelPath, std::ios::binary); if (!outFile) - throw std::ios_base::failure("Failed to create title level file"); + throw std::ios_base::failure("Failed to create title level file."); outFile.write(reinterpret_cast(pData), dwSize); if (!outFile) - throw std::ios_base::failure("Failed to write to title level file"); + throw std::ios_base::failure("Failed to write to title level file."); outFile.close(); } catch (const std::exception& ex) { - TENLog("Error while regenerating title level file: " + std::string(ex.what()), LogLevel::Error); + TENLog("Error while generating title level file: " + std::string(ex.what()), LogLevel::Error); return false; } From cd30a9e5e85523bb66d1607608f5ab37a0720b71 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Tue, 12 Nov 2024 08:03:22 +0100 Subject: [PATCH 030/112] Reallocate item renderer array according to real moveable count --- TombEngine/Game/effects/effects.cpp | 2 +- TombEngine/Game/effects/effects.h | 4 +-- TombEngine/Game/items.cpp | 4 +-- TombEngine/Game/items.h | 2 +- TombEngine/Renderer/Renderer.cpp | 11 ++----- TombEngine/Renderer/Renderer.h | 7 +++-- TombEngine/Renderer/RendererCompatibility.cpp | 8 +++++ TombEngine/Renderer/RendererEnums.h | 2 -- TombEngine/Renderer/RendererFrame.cpp | 30 +++++++++---------- TombEngine/Renderer/RendererInit.cpp | 10 +++---- TombEngine/Renderer/Structures/RendererItem.h | 4 +-- TombEngine/Specific/level.cpp | 2 +- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/TombEngine/Game/effects/effects.cpp b/TombEngine/Game/effects/effects.cpp index fa0bab9c6..0d0f2efb2 100644 --- a/TombEngine/Game/effects/effects.cpp +++ b/TombEngine/Game/effects/effects.cpp @@ -47,7 +47,7 @@ constexpr int WIBBLE_MAX = UCHAR_MAX - WIBBLE_SPEED + 1; Particle Particles[MAX_PARTICLES]; ParticleDynamic ParticleDynamics[MAX_PARTICLE_DYNAMICS]; -FX_INFO EffectList[NUM_EFFECTS]; +FX_INFO EffectList[MAX_SPAWNED_ITEM_COUNT]; GameBoundingBox DeadlyBounds; SPLASH_SETUP SplashSetup; diff --git a/TombEngine/Game/effects/effects.h b/TombEngine/Game/effects/effects.h index 583d51f82..e980a8d22 100644 --- a/TombEngine/Game/effects/effects.h +++ b/TombEngine/Game/effects/effects.h @@ -1,5 +1,6 @@ #pragma once #include "Math/Math.h" +#include "Game/Items.h" #include "Renderer/RendererEnums.h" enum class LaraWeaponType; @@ -13,7 +14,6 @@ constexpr auto SD_UWEXPLOSION = 2; constexpr auto MAX_NODE = 23; constexpr auto MAX_DYNAMICS = 64; constexpr auto MAX_SPLASHES = 8; -constexpr auto NUM_EFFECTS = 256; constexpr auto MAX_PARTICLES = 1024; constexpr auto MAX_PARTICLE_DYNAMICS = 8; @@ -239,7 +239,7 @@ extern SPLASH_STRUCT Splashes[MAX_SPLASHES]; extern Vector3i NodeVectors[ParticleNodeOffsetIDs::NodeMax]; extern NODEOFFSET_INFO NodeOffsets[ParticleNodeOffsetIDs::NodeMax]; -extern FX_INFO EffectList[NUM_EFFECTS]; +extern FX_INFO EffectList[MAX_SPAWNED_ITEM_COUNT]; template TEffect& GetNewEffect(std::vector& effects, unsigned int countMax) diff --git a/TombEngine/Game/items.cpp b/TombEngine/Game/items.cpp index 19e382ba8..1ea32a99d 100644 --- a/TombEngine/Game/items.cpp +++ b/TombEngine/Game/items.cpp @@ -479,13 +479,13 @@ void InitializeFXArray() NextFxActive = NO_VALUE; NextFxFree = 0; - for (int i = 0; i < NUM_EFFECTS; i++) + for (int i = 0; i < MAX_SPAWNED_ITEM_COUNT; i++) { auto* fx = &EffectList[i]; fx->nextFx = i + 1; } - EffectList[NUM_EFFECTS - 1].nextFx = NO_VALUE; + EffectList[MAX_SPAWNED_ITEM_COUNT - 1].nextFx = NO_VALUE; } void RemoveDrawnItem(short itemNumber) diff --git a/TombEngine/Game/items.h b/TombEngine/Game/items.h index 2d1d7e438..5d209259b 100644 --- a/TombEngine/Game/items.h +++ b/TombEngine/Game/items.h @@ -8,7 +8,7 @@ using namespace TEN::Utils; -constexpr auto ITEM_COUNT_MAX = 1024; +constexpr auto MAX_SPAWNED_ITEM_COUNT = 256; constexpr auto ITEM_FLAG_COUNT = 8; constexpr auto NOT_TARGETABLE = SHRT_MIN / 2; diff --git a/TombEngine/Renderer/Renderer.cpp b/TombEngine/Renderer/Renderer.cpp index e251bc09e..6c5a1a311 100644 --- a/TombEngine/Renderer/Renderer.cpp +++ b/TombEngine/Renderer/Renderer.cpp @@ -34,6 +34,8 @@ namespace TEN::Renderer { _shadowLight = nullptr; + _items.resize(0); + _effects.resize(0); _moveableObjects.resize(0); _staticObjects.resize(0); _sprites.resize(0); @@ -48,15 +50,6 @@ namespace TEN::Renderer for (auto& mesh : _meshes) delete mesh; _meshes.resize(0); - - for (auto& item : _items) - { - item.DisableInterpolation = true; - item.PrevRoomNumber = NO_VALUE; - item.RoomNumber = NO_VALUE; - item.ItemNumber = NO_VALUE; - item.LightsToDraw.clear(); - } } void Renderer::Lock() diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 88b2c443f..47f07698b 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -260,11 +260,12 @@ namespace TEN::Renderer std::vector _staticTextures; std::vector _spritesTextures; - // Preallocated pools of objects for avoiding new/delete + // Preallocated pools of objects for avoiding new/delete. // Items and effects are safe (can't be more than 1024 items in TR), // lights should be oversized (eventually ignore lights more than MAX_LIGHTS) - RendererItem _items[ITEM_COUNT_MAX]; - RendererEffect _effects[ITEM_COUNT_MAX]; + + std::vector _items; + std::vector _effects; // Debug variables int _numDrawCalls = 0; diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index cd94dd6f3..ab1f2cf38 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -35,6 +35,14 @@ namespace TEN::Renderer _meshes.clear(); + int allocatedItemSize = (int)g_Level.Items.size() + MAX_SPAWNED_ITEM_COUNT; + + auto item = RendererItem(); + _items = std::vector(allocatedItemSize, item); + + auto effect = RendererEffect(); + _effects = std::vector(allocatedItemSize, effect); + TENLog("Allocated renderer object memory.", LogLevel::Info); _animatedTextures.resize(g_Level.AnimatedTextures.size()); diff --git a/TombEngine/Renderer/RendererEnums.h b/TombEngine/Renderer/RendererEnums.h index c159c1c42..51b53a81c 100644 --- a/TombEngine/Renderer/RendererEnums.h +++ b/TombEngine/Renderer/RendererEnums.h @@ -58,8 +58,6 @@ constexpr auto SKY_INDICES_COUNT = 6 * SKY_TILES_COUNT * SKY_TILES_COUNT; constexpr auto SKY_TRIANGLES_COUNT = 2 * SKY_TILES_COUNT * SKY_TILES_COUNT; constexpr auto MAX_ROOMS_DRAW = 256; -constexpr auto MAX_STATICS_DRAW = 128; -constexpr auto MAX_EFFECTS_DRAW = 16; constexpr auto MAX_ITEMS_DRAW = 128; constexpr auto MAX_LIGHTS_DRAW = 48; constexpr auto MAX_FOG_BULBS_DRAW = 32; diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index 64d6bea6e..cc013d592 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -868,31 +868,31 @@ namespace TEN::Renderer void Renderer::ResetItems() { - for (int i = 0; i < ITEM_COUNT_MAX; i++) - _items[i].DoneAnimations = false; + for (auto& item : _items) + item.DoneAnimations = false; } void Renderer::SaveOldState() { - for (int i = 0; i < g_Level.Items.size(); i++) + for (auto& item : _items) { - _items[i].PrevPosition = _items[i].Position; - _items[i].PrevWorld = _items[i].World; - _items[i].PrevTranslation = _items[i].Translation; - _items[i].PrevRotation = _items[i].Rotation; - _items[i].PrevScale = _items[i].Scale; + item.PrevPosition = item.Position; + item.PrevWorld = item.World; + item.PrevTranslation = item.Translation; + item.PrevRotation = item.Rotation; + item.PrevScale = item.Scale; for (int j = 0; j < MAX_BONES; j++) - _items[i].PrevAnimTransforms[j] = _items[i].AnimTransforms[j]; + item.PrevAnimTransforms[j] = item.AnimTransforms[j]; } - for (int i = 0; i < ITEM_COUNT_MAX; i++) + for (auto& effect : _effects) { - _effects[i].PrevPosition = _effects[i].Position; - _effects[i].PrevWorld = _effects[i].World; - _effects[i].PrevTranslation = _effects[i].Translation; - _effects[i].PrevRotation = _effects[i].Rotation; - _effects[i].PrevScale = _effects[i].Scale; + effect.PrevPosition = effect.Position; + effect.PrevWorld = effect.World; + effect.PrevTranslation = effect.Translation; + effect.PrevRotation = effect.Rotation; + effect.PrevScale = effect.Scale; } } } diff --git a/TombEngine/Renderer/RendererInit.cpp b/TombEngine/Renderer/RendererInit.cpp index 86fb7ced3..bac7979c3 100644 --- a/TombEngine/Renderer/RendererInit.cpp +++ b/TombEngine/Renderer/RendererInit.cpp @@ -131,11 +131,11 @@ namespace TEN::Renderer _lines3DToDraw = createVector(MAX_LINES_3D); _triangles3DToDraw = createVector(MAX_TRIANGLES_3D); - for (int i = 0; i < ITEM_COUNT_MAX; i++) - { - _items[i].LightsToDraw = createVector(MAX_LIGHTS_PER_ITEM); - _effects[i].LightsToDraw = createVector(MAX_LIGHTS_PER_ITEM); - } + for (auto& item : _items) + item.LightsToDraw = createVector(MAX_LIGHTS_PER_ITEM); + + for (auto& effect : _effects) + effect.LightsToDraw = createVector(MAX_LIGHTS_PER_ITEM); D3D11_BLEND_DESC blendStateDesc{}; blendStateDesc.AlphaToCoverageEnable = false; diff --git a/TombEngine/Renderer/Structures/RendererItem.h b/TombEngine/Renderer/Structures/RendererItem.h index 0924ec4bb..0c9774d69 100644 --- a/TombEngine/Renderer/Structures/RendererItem.h +++ b/TombEngine/Renderer/Structures/RendererItem.h @@ -8,8 +8,8 @@ namespace TEN::Renderer::Structures { struct RendererItem { - int ItemNumber = 0; - int ObjectID = 0; + int ItemNumber = NO_VALUE; + int ObjectID = NO_VALUE; Vector3 Position = Vector3::Zero; int RoomNumber = NO_VALUE; diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index c00d47725..f5ed9e2d4 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -214,7 +214,7 @@ void LoadItems() if (g_Level.NumItems == 0) return; - InitializeItemArray(ITEM_COUNT_MAX); + InitializeItemArray(g_Level.NumItems + MAX_SPAWNED_ITEM_COUNT); for (int i = 0; i < g_Level.NumItems; i++) { From 372afb4e8f556ec55f1d1a7926d7455680d33970 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Tue, 12 Nov 2024 08:06:59 +0100 Subject: [PATCH 031/112] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a04fa08be..e72c980a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Bug fixes * Fixed engine performance around bridges. * Fixed engine performance if weather or bubble effects are active. +* Fixed silent engine crashes if loaded level is corrupted or in incorrect format. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. * Fixed vehicle transfer not happening for levels which were not previously visited. @@ -22,8 +23,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Features/Amendments * Added fast savegame reloading. -* Added ability to run engine without title level file. * Added ricochet sounds and make the effect more prominent. +* Automatically generate title level, if one is missing. ### Lua API changes From 0d8b83cf426ff01840063030f57069e8c355d36c Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:20:28 +0100 Subject: [PATCH 032/112] Fixed #1473 --- TombEngine/Game/control/event.h | 5 ++--- TombEngine/Game/control/volume.cpp | 11 +++-------- TombEngine/Specific/level.cpp | 1 + 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/TombEngine/Game/control/event.h b/TombEngine/Game/control/event.h index 2b712efec..b1bfb6be5 100644 --- a/TombEngine/Game/control/event.h +++ b/TombEngine/Game/control/event.h @@ -6,8 +6,6 @@ struct MESH_INFO; namespace TEN::Control::Volumes { - constexpr auto NO_CALL_COUNTER = -1; - using Activator = std::variant< std::nullptr_t, short, @@ -52,7 +50,8 @@ namespace TEN::Control::Volumes std::string Function = {}; std::string Data = {}; - int CallCounter = NO_CALL_COUNTER; + bool Enabled = true; + int CallCounter = NO_VALUE; }; struct EventSet diff --git a/TombEngine/Game/control/volume.cpp b/TombEngine/Game/control/volume.cpp index 5968dd206..e4ddab38d 100644 --- a/TombEngine/Game/control/volume.cpp +++ b/TombEngine/Game/control/volume.cpp @@ -87,11 +87,11 @@ namespace TEN::Control::Volumes bool HandleEvent(Event& event, Activator& activator) { - if (event.Function.empty() || event.CallCounter == 0 || event.CallCounter < NO_CALL_COUNTER) + if (!event.Enabled || event.CallCounter == 0 || event.Function.empty()) return false; g_GameScript->ExecuteFunction(event.Function, activator, event.Data); - if (event.CallCounter != NO_CALL_COUNTER) + if (event.CallCounter != NO_VALUE) event.CallCounter--; return true; @@ -124,12 +124,7 @@ namespace TEN::Control::Volumes if (eventSet == nullptr) return false; - auto& event = eventSet->Events[(int)eventType]; - bool disabled = eventSet->Events[(int)eventType].CallCounter < NO_CALL_COUNTER; - - // Flip the call counter to indicate that it is currently disabled. - if ((enabled && disabled) || (!enabled && !disabled)) - eventSet->Events[(int)eventType].CallCounter += enabled ? EVENT_STATE_MASK : -EVENT_STATE_MASK; + eventSet->Events[(int)eventType].Enabled = enabled; return true; } diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index f5ed9e2d4..316badb5a 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1123,6 +1123,7 @@ void LoadEvent(EventSet& eventSet) evt.Function = ReadString(); evt.Data = ReadString(); evt.CallCounter = ReadInt32(); + evt.Enabled = ReadBool(); } void LoadEventSets() From 3606f7637b63cd1de7475c11ed1dd97f2ab1fe39 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:24:05 +0100 Subject: [PATCH 033/112] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e72c980a9..f6961947d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Lua API changes * Added support for transparency value in DisplayString class. +* Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 From 4761feb45d38ae53f73c4bcd77a5b2c5baa5d8c7 Mon Sep 17 00:00:00 2001 From: Jakub <80340234+Jakub768@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:27:29 +0000 Subject: [PATCH 034/112] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6961947d..7d1f0abad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Added fast savegame reloading. * Added ricochet sounds and make the effect more prominent. * Automatically generate title level, if one is missing. +* Allow more than 1024 objects in a level. ### Lua API changes From 0ab2e40257f8d986f3599745270e8c7f4a05a730 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:13:38 +0100 Subject: [PATCH 035/112] More correct way of initializing bridges on load game --- TombEngine/Game/collision/floordata.cpp | 11 ++++++----- TombEngine/Game/collision/floordata.h | 9 ++++++++- TombEngine/Game/items.cpp | 2 +- TombEngine/Game/room.cpp | 2 +- TombEngine/Game/savegame.cpp | 7 ++++++- TombEngine/Objects/Generic/Object/generic_bridge.cpp | 2 +- .../Objects/TR5/Object/tr5_twoblockplatform.cpp | 2 +- 7 files changed, 24 insertions(+), 11 deletions(-) diff --git a/TombEngine/Game/collision/floordata.cpp b/TombEngine/Game/collision/floordata.cpp index d35ce9d95..6b9d04e75 100644 --- a/TombEngine/Game/collision/floordata.cpp +++ b/TombEngine/Game/collision/floordata.cpp @@ -879,7 +879,7 @@ namespace TEN::Collision::Floordata } // Updates BridgeItem for all blocks which are enclosed by bridge bounds. - void UpdateBridgeItem(const ItemInfo& item, bool forceRemoval) + void UpdateBridgeItem(const ItemInfo& item, BridgeUpdateType updateType) { constexpr auto SECTOR_EXTENTS = Vector3(BLOCK(0.5f)); @@ -891,7 +891,7 @@ namespace TEN::Collision::Floordata // Force removal if item was killed. if (item.Flags & IFLAG_KILLED) - forceRemoval = true; + updateType = BridgeUpdateType::Remove; // Get bridge OBB. auto bridgeBox = GameBoundingBox(&item).ToBoundingOrientedBox(item.Pose); @@ -919,10 +919,11 @@ namespace TEN::Collision::Floordata float offZ = pZ - item.Pose.Position.z; // Clean previous bridge state. - RemoveBridge(item.Index, offX, offZ); + if (updateType != BridgeUpdateType::Initialize) + RemoveBridge(item.Index, offX, offZ); - // In sweep mode; don't try readding to sector. - if (forceRemoval) + // In removal mode; don't try re-adding to sector. + if (updateType == BridgeUpdateType::Remove) continue; // Sector is outside enclosed AABB space; ignore precise check. diff --git a/TombEngine/Game/collision/floordata.h b/TombEngine/Game/collision/floordata.h index 56dff4dec..81f2018d4 100644 --- a/TombEngine/Game/collision/floordata.h +++ b/TombEngine/Game/collision/floordata.h @@ -55,6 +55,13 @@ enum class ClimbDirectionFlags West = 1 << 11 }; +enum class BridgeUpdateType +{ + Normal, + Initialize, + Remove +}; + // NOTE: Describes vertical room location. class RoomVector { @@ -201,7 +208,7 @@ namespace TEN::Collision::Floordata std::optional GetBridgeItemIntersect(const ItemInfo& item, const Vector3i& pos, bool useBottomHeight); int GetBridgeBorder(const ItemInfo& item, bool isBottom); - void UpdateBridgeItem(const ItemInfo& item, bool forceRemoval = false); + void UpdateBridgeItem(const ItemInfo& item, BridgeUpdateType updateType = BridgeUpdateType::Normal); bool TestMaterial(MaterialType refMaterial, const std::vector& materials); diff --git a/TombEngine/Game/items.cpp b/TombEngine/Game/items.cpp index 1ea32a99d..393e42847 100644 --- a/TombEngine/Game/items.cpp +++ b/TombEngine/Game/items.cpp @@ -276,7 +276,7 @@ void KillItem(short const itemNumber) // AI target generation uses a hack with making a dummy item without ObjectNumber. // Therefore, a check should be done here to prevent access violation. if (item->ObjectNumber != GAME_OBJECT_ID::ID_NO_OBJECT && item->IsBridge()) - UpdateBridgeItem(*item, true); + UpdateBridgeItem(*item, BridgeUpdateType::Remove); GameScriptHandleKilled(itemNumber, true); diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index a2cd8564d..c2e4dc0eb 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -71,7 +71,7 @@ static void RemoveRoomFlipItems(const ROOM_INFO& room) // Clear bridge. if (item.IsBridge()) - UpdateBridgeItem(item, true); + UpdateBridgeItem(item, BridgeUpdateType::Remove); } } diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index 4a2c07408..fc0ac9907 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -2370,6 +2370,10 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode) continue; } + // If object is bridge - remove it from existing sectors. + if (item->IsBridge()) + UpdateBridgeItem(g_Level.Items[i], BridgeUpdateType::Remove); + // Position item->Pose = ToPose(*savedItem->pose()); item->RoomNumber = savedItem->room_number(); @@ -2437,8 +2441,9 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode) item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + savedItem->anim_number(); } + // Re-add bridges at new position. if (item->IsBridge()) - UpdateBridgeItem(g_Level.Items[i]); + UpdateBridgeItem(g_Level.Items[i], BridgeUpdateType::Initialize); // Creature data for intelligent items. if (item->ObjectNumber != ID_LARA && item->Status == ITEM_ACTIVE && obj->intelligent) diff --git a/TombEngine/Objects/Generic/Object/generic_bridge.cpp b/TombEngine/Objects/Generic/Object/generic_bridge.cpp index 81abfa392..c8ccd680f 100644 --- a/TombEngine/Objects/Generic/Object/generic_bridge.cpp +++ b/TombEngine/Objects/Generic/Object/generic_bridge.cpp @@ -104,6 +104,6 @@ namespace TEN::Entities::Generic break; } - UpdateBridgeItem(bridgeItem); + UpdateBridgeItem(bridgeItem, BridgeUpdateType::Initialize); } } diff --git a/TombEngine/Objects/TR5/Object/tr5_twoblockplatform.cpp b/TombEngine/Objects/TR5/Object/tr5_twoblockplatform.cpp index 99bfeee01..2972b8d5a 100644 --- a/TombEngine/Objects/TR5/Object/tr5_twoblockplatform.cpp +++ b/TombEngine/Objects/TR5/Object/tr5_twoblockplatform.cpp @@ -92,7 +92,7 @@ namespace TEN::Entities::Generic if (probe.GetRoomNumber() != item->RoomNumber) { - UpdateBridgeItem(*item, true); + UpdateBridgeItem(*item, BridgeUpdateType::Remove); ItemNewRoom(itemNumber, probe.GetRoomNumber()); UpdateBridgeItem(*item); } From 09e354a33a3ef6225898da99a3a4ff1e07b55a9b Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:14:27 +0100 Subject: [PATCH 036/112] Fixed dart emitter not deactivating --- CHANGELOG.md | 1 + TombEngine/Game/control/trigger.cpp | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d1f0abad..dedafb70c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed binocular or lasersight camera not switching off correctly after flyby. * Fixed Lara's Home entry not working. * Fixed exploding TR3 bosses. +* Fixed the original issue with deactivation of Dart Emitter. * Fixed Lens Flare object not functioning properly. ### Features/Amendments diff --git a/TombEngine/Game/control/trigger.cpp b/TombEngine/Game/control/trigger.cpp index adbe0b74c..584e324e2 100644 --- a/TombEngine/Game/control/trigger.cpp +++ b/TombEngine/Game/control/trigger.cpp @@ -592,14 +592,6 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, Activator activator, bo && (item->Flags & ONESHOT)) break; - if (triggerType != TRIGGER_TYPES::ANTIPAD - && triggerType != TRIGGER_TYPES::ANTITRIGGER - && triggerType != TRIGGER_TYPES::HEAVYANTITRIGGER) - { - if (item->ObjectNumber == ID_DART_EMITTER && item->Active) - break; - } - item->Timer = timer; if (timer != 1) item->Timer = FPS * timer; @@ -609,10 +601,6 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, Activator activator, bo { if (heavyFlags >= 0) { - //if (switchFlag) - //item->Flags |= (flags & CODE_BITS); - //else - item->Flags ^= (flags & CODE_BITS); if (flags & ONESHOT) From 369e1ca96706a34cf70232c1f27fb01da38e6c80 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 13 Nov 2024 22:29:52 +0100 Subject: [PATCH 037/112] Fixed GetTargetOnLOS returning incorrect results with fixed LOS function --- CHANGELOG.md | 1 + TombEngine/Game/Lara/lara_fire.cpp | 5 +++- TombEngine/Game/control/los.cpp | 37 +++++++++++------------------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dedafb70c..1f123820a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed exploding TR3 bosses. * Fixed the original issue with deactivation of Dart Emitter. * Fixed Lens Flare object not functioning properly. +* Fixed Skeleton and Mummy not reacting to shotgun hits. ### Features/Amendments * Added fast savegame reloading. diff --git a/TombEngine/Game/Lara/lara_fire.cpp b/TombEngine/Game/Lara/lara_fire.cpp index 08a97f190..27e0e4d91 100644 --- a/TombEngine/Game/Lara/lara_fire.cpp +++ b/TombEngine/Game/Lara/lara_fire.cpp @@ -874,7 +874,7 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo& targetEntity, Ite } } - if (closestJointIndex < 0) + if (closestJointIndex == NO_VALUE) { auto vTarget = GameVector(target); GetTargetOnLOS(&vOrigin, &vTarget, false, true); @@ -1109,6 +1109,9 @@ void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, i if (targetEntity->IsCreature()) GetCreatureInfo(targetEntity)->HurtByLara = true; + if (object.HitRoutine == nullptr) + return; + if (hitPos != nullptr) { hitPos->RoomNumber = targetEntity->RoomNumber; diff --git a/TombEngine/Game/control/los.cpp b/TombEngine/Game/control/los.cpp index c07d5ff74..87939b760 100644 --- a/TombEngine/Game/control/los.cpp +++ b/TombEngine/Game/control/los.cpp @@ -266,13 +266,14 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo SoundEffect(SFX_TR4_REVOLVER_FIRE, nullptr); } - bool hasHit = false; + bool hitProcessed = false; MESH_INFO* mesh = nullptr; auto vector = Vector3i::Zero; int itemNumber = ObjectOnLOS2(origin, target, &vector, &mesh); + bool hasHit = itemNumber != NO_LOS_ITEM; - if (itemNumber != NO_LOS_ITEM) + if (hasHit) { target2.x = vector.x - ((vector.x - origin->x) >> 5); target2.y = vector.y - ((vector.y - origin->y) >> 5); @@ -294,6 +295,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo ShatterImpactData.impactLocation = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z); ShatterObject(nullptr, mesh, 128, target2.RoomNumber, 0); SoundEffect(GetShatterSound(mesh->staticNumber), (Pose*)mesh); + hitProcessed = true; } TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y); @@ -312,13 +314,14 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo ShatterImpactData.impactLocation = ShatterItem.sphere.Center; ShatterObject(&ShatterItem, 0, 128, target2.RoomNumber, 0); TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, false); + hitProcessed = true; } else { auto* object = &Objects[item->ObjectNumber]; if (drawTarget && (Lara.Control.Weapon.GunType == LaraWeaponType::Revolver || - Lara.Control.Weapon.GunType == LaraWeaponType::HK)) + Lara.Control.Weapon.GunType == LaraWeaponType::HK)) { if (object->intelligent || object->HitRoutine) { @@ -341,7 +344,9 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo } } } + HitTarget(LaraItem, item, &target2, Weapons[(int)Lara.Control.Weapon.GunType].Damage, false, bestJointIndex); + hitProcessed = true; } else { @@ -350,24 +355,10 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y); } } - else + else if (item->ObjectNumber >= ID_SMASH_OBJECT1 && item->ObjectNumber <= ID_SMASH_OBJECT8) { - if (item->ObjectNumber >= ID_SMASH_OBJECT1 && item->ObjectNumber <= ID_SMASH_OBJECT8) - { - SmashObject(itemNumber); - } - else - { - const auto& weapon = Weapons[(int)Lara.Control.Weapon.GunType]; - if (object->HitRoutine != nullptr) - { - object->HitRoutine(*item, *LaraItem, target2, weapon.Damage, false, NO_VALUE); - } - else - { - DefaultItemHit(*item, *LaraItem, target2, weapon.Damage, false, NO_VALUE); - } - } + SmashObject(itemNumber); + hitProcessed = true; } } } @@ -417,6 +408,8 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo item->Status = ITEM_ACTIVE; item->Flags |= IFLAG_ACTIVATION_MASK | 0x40; } + + hitProcessed = true; } TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y); @@ -429,8 +422,6 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo FireCrossBowFromLaserSight(*LaraItem, origin, &target2); } } - - hasHit = true; } else { @@ -459,7 +450,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo LaserSightZ = target2.z; } - return hasHit; + return hitProcessed; } static bool DoRayBox(const GameVector& origin, const GameVector& target, const GameBoundingBox& bounds, From 8a82b0c8917925a743e73ff72608a6667f360164 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:31:29 +0100 Subject: [PATCH 038/112] Fixed DisplayString class not supporting empty lines in multiline strings --- CHANGELOG.md | 1 + TombEngine/Renderer/RendererString.cpp | 9 +++++---- .../Internal/TEN/Strings/StringsHandler.cpp | 2 +- TombEngine/Specific/trutils.cpp | 12 ++++++------ TombEngine/Specific/trutils.h | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f123820a..3a8e25e92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Lua API changes * Added support for transparency value in DisplayString class. +* Fixed DisplayString class not supporting empty lines in multiline strings. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 diff --git a/TombEngine/Renderer/RendererString.cpp b/TombEngine/Renderer/RendererString.cpp index ff52f8391..41bc05c41 100644 --- a/TombEngine/Renderer/RendererString.cpp +++ b/TombEngine/Renderer/RendererString.cpp @@ -40,13 +40,13 @@ namespace TEN::Renderer float fontSpacing = _gameFont->GetLineSpacing(); float fontScale = REFERENCE_FONT_SIZE / fontSpacing; - auto stringLines = SplitString(string); + auto stringLines = SplitString(TEN::Utils::ToWString(string)); float yOffset = 0.0f; for (const auto& line : stringLines) { // Prepare structure for renderer. RendererStringToDraw rString; - rString.String = TEN::Utils::ToWString(line); + rString.String = line; rString.Flags = flags; rString.X = 0; rString.Y = 0; @@ -54,7 +54,8 @@ namespace TEN::Renderer rString.Scale = (uiScale * fontScale) * scale; // Measure string. - auto size = Vector2(_gameFont->MeasureString(rString.String.c_str())) * rString.Scale; + auto size = line.empty() ? Vector2(0, fontSpacing * rString.Scale) : Vector2(_gameFont->MeasureString(line.c_str())) * rString.Scale; + if (flags & (int)PrintStringFlags::Center) { rString.X = (pos.x * factor.x) - (size.x / 2.0f); @@ -66,7 +67,7 @@ namespace TEN::Renderer else { // Calculate indentation to account for string scaling. - auto indent = _gameFont->FindGlyph(line.at(0))->XAdvance * rString.Scale; + auto indent = line.empty() ? 0 : _gameFont->FindGlyph(line.at(0))->XAdvance * rString.Scale; rString.X = pos.x * factor.x + indent; } diff --git a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp index e90ea50b6..3eb6509ea 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp @@ -119,7 +119,7 @@ void StringsHandler::ProcessDisplayStrings(float deltaTime) { if (!endOfLife || str._isInfinite) { - auto cstr = str._isTranslated ? g_GameFlow->GetString(str._key.c_str()) : str._key.c_str(); + auto cstr = str._isTranslated ? g_GameFlow->GetString(str._key.c_str()) : str._key; int flags = 0; if (str._flags[(size_t)DisplayStringOptions::Center]) diff --git a/TombEngine/Specific/trutils.cpp b/TombEngine/Specific/trutils.cpp index 5c2b50972..0ed7885f2 100644 --- a/TombEngine/Specific/trutils.cpp +++ b/TombEngine/Specific/trutils.cpp @@ -132,20 +132,20 @@ namespace TEN::Utils return result; } - std::vector SplitString(const std::string& string) + std::vector SplitString(const std::wstring& string) { - auto strings = std::vector{}; + auto strings = std::vector{}; // Exit early if string is single line. - if (string.find('\n') == std::string::npos) + if (string.find(L'\n') == std::wstring::npos) { strings.push_back(string); return strings; } - std::string::size_type pos = 0; - std::string::size_type prev = 0; - while ((pos = string.find('\n', prev)) != std::string::npos) + std::wstring::size_type pos = 0; + std::wstring::size_type prev = 0; + while ((pos = string.find(L'\n', prev)) != std::string::npos) { strings.push_back(string.substr(prev, pos - prev)); prev = pos + 1; diff --git a/TombEngine/Specific/trutils.h b/TombEngine/Specific/trutils.h index f24acd617..763beb1ec 100644 --- a/TombEngine/Specific/trutils.h +++ b/TombEngine/Specific/trutils.h @@ -11,7 +11,7 @@ namespace TEN::Utils std::string ToString(const wchar_t* wString); std::wstring ToWString(const std::string& string); std::wstring ToWString(const char* cString); - std::vector SplitString(const std::string& string); + std::vector SplitString(const std::wstring& string); // 2D space utilities Vector2 GetAspectCorrect2DPosition(const Vector2& pos); From 4aa1bb81d1bb495385cfef91dffa440a79fae43a Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:57:23 +0100 Subject: [PATCH 039/112] Hide dummy title level from user --- TombEngine/Specific/level.cpp | 19 +++++++++++++++---- TombEngine/Specific/winmain.cpp | 5 +++-- TombEngine/Specific/winmain.h | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 316badb5a..f77c4da61 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1524,11 +1524,16 @@ bool LoadLevelFile(int levelIndex) auto assetDir = g_GameFlow->GetGameDir(); auto levelPath = assetDir + level.FileName; + bool isDummyLevel = false; + if (!std::filesystem::is_regular_file(levelPath)) { - if (levelIndex == 0 && GenerateTitleLevel(levelPath)) + levelPath = assetDir + "dummy.ten"; + + if (levelIndex == 0 && GenerateDummyLevel(levelPath)) { - TENLog("Title level file not found, generating dummy level: " + levelPath, LogLevel::Info); + TENLog("Title level file not found, using dummy level.", LogLevel::Info); + isDummyLevel = true; } else { @@ -1537,7 +1542,8 @@ bool LoadLevelFile(int levelIndex) } } - TENLog("Loading level file: " + levelPath, LogLevel::Info); + if (!isDummyLevel) + TENLog("Loading level file: " + levelPath, LogLevel::Info); auto timestamp = std::filesystem::last_write_time(levelPath); bool fastReload = (g_GameFlow->GetSettings()->FastReload && levelIndex == CurrentLevel && timestamp == LastLevelTimestamp && levelPath == LastLevelFilePath); @@ -1555,7 +1561,12 @@ bool LoadLevelFile(int levelIndex) FreeLevel(fastReload); LevelLoadTask = std::async(std::launch::async, LoadLevel, levelPath, fastReload); - return LevelLoadTask.get(); + bool loadSuccess = LevelLoadTask.get(); + + if (loadSuccess && isDummyLevel) + std::filesystem::remove(levelPath); + + return loadSuccess; } void LoadSprites() diff --git a/TombEngine/Specific/winmain.cpp b/TombEngine/Specific/winmain.cpp index bf206367b..7ed9f2809 100644 --- a/TombEngine/Specific/winmain.cpp +++ b/TombEngine/Specific/winmain.cpp @@ -129,7 +129,7 @@ void DisableDpiAwareness() FreeLibrary(lib); } -bool GenerateTitleLevel(const std::string& levelPath) +bool GenerateDummyLevel(const std::string& levelPath) { // Try to load the embedded resource "data.bin" HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(IDR_TITLELEVEL), "BIN"); @@ -155,7 +155,8 @@ bool GenerateTitleLevel(const std::string& levelPath) try { std::filesystem::path dir = std::filesystem::path(levelPath).parent_path(); - std::filesystem::create_directories(dir); + if (!dir.empty()) + std::filesystem::create_directories(dir); std::ofstream outFile(levelPath, std::ios::binary); if (!outFile) diff --git a/TombEngine/Specific/winmain.h b/TombEngine/Specific/winmain.h index 851b13773..9c89c97cb 100644 --- a/TombEngine/Specific/winmain.h +++ b/TombEngine/Specific/winmain.h @@ -43,4 +43,4 @@ Vector2i GetScreenResolution(); std::vector GetAllSupportedScreenResolutions(); int GetCurrentScreenRefreshRate(); -bool GenerateTitleLevel(const std::string& levelPath); \ No newline at end of file +bool GenerateDummyLevel(const std::string& levelPath); \ No newline at end of file From 5254cc1daf47a6444ba22c6f22b06d7423127a93 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:59:00 +0100 Subject: [PATCH 040/112] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a8e25e92..9149c46b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Bug fixes * Fixed engine performance around bridges. * Fixed engine performance if weather or bubble effects are active. -* Fixed silent engine crashes if loaded level is corrupted or in incorrect format. +* Fixed silent crashes if loaded level is corrupted or in incorrect format. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. * Fixed vehicle transfer not happening for levels which were not previously visited. @@ -26,7 +26,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Features/Amendments * Added fast savegame reloading. * Added ricochet sounds and make the effect more prominent. -* Automatically generate title level, if one is missing. +* Allow to run the engine without title level. * Allow more than 1024 objects in a level. ### Lua API changes From d8b522343fd82e67fdbf77e2bd902f08e9e54ad7 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 14 Nov 2024 00:52:51 +0100 Subject: [PATCH 041/112] Fixed DOZY memory corruption --- CHANGELOG.md | 1 + TombEngine/Game/Lara/lara_helpers.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9149c46b2..10c0d8dba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed engine performance around bridges. * Fixed engine performance if weather or bubble effects are active. * Fixed silent crashes if loaded level is corrupted or in incorrect format. +* Fixed uzi targeting issues after using flycheat. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. * Fixed vehicle transfer not happening for levels which were not previously visited. diff --git a/TombEngine/Game/Lara/lara_helpers.cpp b/TombEngine/Game/Lara/lara_helpers.cpp index 74bf0ec92..5f8a21db2 100644 --- a/TombEngine/Game/Lara/lara_helpers.cpp +++ b/TombEngine/Game/Lara/lara_helpers.cpp @@ -726,7 +726,7 @@ static void GivePlayerItemsCheat(ItemInfo& item) player.Inventory.Puzzles[i] = true; player.Inventory.PuzzlesCombo[2 * i] = false; - player.Inventory.PuzzlesCombo[(92 * i) + 1] = false; + player.Inventory.PuzzlesCombo[(2 * i) + 1] = false; } for (int i = 0; i < 8; ++i) From 8bb955cc088ca250d2fe2e1757ee2375c30bfc99 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 14 Nov 2024 19:31:53 +0100 Subject: [PATCH 042/112] Fix collision callbacks for Lara --- TombEngine/Game/collision/collide_item.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index f94a9bc64..f69326847 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -151,13 +151,12 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, continue; // Ignore items not feasible for collision. - if (item.Index == collidingItem.Index || - item.Flags & IFLAG_KILLED || item.MeshBits == NO_JOINT_BITS || !item.Collidable || - (object.drawRoutine == nullptr && !item.IsLara()) || - (object.collision == nullptr && !item.IsLara())) - { + if (item.Index == collidingItem.Index || item.Flags & IFLAG_KILLED || item.MeshBits == NO_JOINT_BITS) + continue; + + // Ignore non-Lara items without collision or draw routines or with collidable flag unset. + if (!item.IsLara() && (!item.Collidable || object.drawRoutine == nullptr || object.collision == nullptr)) continue; - } // HACK: Ignore UPV and big gun. if ((item.ObjectNumber == ID_UPV || item.ObjectNumber == ID_BIGGUN) && item.HitPoints == 1) From 849e0557da9de797a1ff70c1c550b7f8a0b0e217 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 14 Nov 2024 19:37:46 +0100 Subject: [PATCH 043/112] Reduce load menu timeout on death to 15 game frames --- TombEngine/Game/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/Game/gui.cpp b/TombEngine/Game/gui.cpp index 949634a88..b2c0fd411 100644 --- a/TombEngine/Game/gui.cpp +++ b/TombEngine/Game/gui.cpp @@ -3506,7 +3506,7 @@ namespace TEN::Gui LoadResult GuiController::DoLoad() { - constexpr auto DEATH_NO_INPUT_LOAD_TIMEOUT = 1 * FPS; + constexpr int DEATH_NO_INPUT_LOAD_TIMEOUT = FPS / 2; bool canLoop = g_Configuration.MenuOptionLoopingMode == MenuOptionLoopingMode::SaveLoadOnly || g_Configuration.MenuOptionLoopingMode == MenuOptionLoopingMode::AllMenus; From ee3df1680b8b1c2f5b7ce227fb892574db21a2c5 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:47:56 +0100 Subject: [PATCH 044/112] Fixed incorrect clipping of scaled static meshes --- CHANGELOG.md | 1 + TombEngine/Math/Objects/GameBoundingBox.cpp | 5 +++++ TombEngine/Math/Objects/GameBoundingBox.h | 1 + TombEngine/Renderer/RendererCompatibility.cpp | 2 +- TombEngine/Renderer/RendererFrame.cpp | 4 ++-- TombEngine/Renderer/Structures/RendererStatic.h | 8 +++----- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10c0d8dba..eb81f036e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed engine performance around bridges. * Fixed engine performance if weather or bubble effects are active. * Fixed silent crashes if loaded level is corrupted or in incorrect format. +* Fixed incorrect clipping of scaled static meshes. * Fixed uzi targeting issues after using flycheat. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. diff --git a/TombEngine/Math/Objects/GameBoundingBox.cpp b/TombEngine/Math/Objects/GameBoundingBox.cpp index d69209967..f017cfbee 100644 --- a/TombEngine/Math/Objects/GameBoundingBox.cpp +++ b/TombEngine/Math/Objects/GameBoundingBox.cpp @@ -85,6 +85,11 @@ Z2 = (int)round(boxMax.z); } + BoundingSphere GameBoundingBox::ToLocalBoundingSphere() const + { + return BoundingSphere(GetCenter(), GetExtents().Length()); + } + BoundingOrientedBox GameBoundingBox::ToBoundingOrientedBox(const Pose& pose) const { return ToBoundingOrientedBox(pose.Position.ToVector3(), pose.Orientation.ToQuaternion()); diff --git a/TombEngine/Math/Objects/GameBoundingBox.h b/TombEngine/Math/Objects/GameBoundingBox.h index 17569391d..a457cefc2 100644 --- a/TombEngine/Math/Objects/GameBoundingBox.h +++ b/TombEngine/Math/Objects/GameBoundingBox.h @@ -45,6 +45,7 @@ struct ObjectInfo; // Converters + BoundingSphere ToLocalBoundingSphere() const; BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const; BoundingOrientedBox ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const; diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index ab1f2cf38..8a3cada26 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -273,7 +273,7 @@ namespace TEN::Renderer staticInfo->AmbientLight = r->AmbientLight; staticInfo->Pose = oldMesh->pos; staticInfo->Scale = oldMesh->scale; - staticInfo->OriginalVisibilityBox = StaticObjects[staticInfo->ObjectNumber].visibilityBox; + staticInfo->OriginalSphere = StaticObjects[staticInfo->ObjectNumber].visibilityBox.ToLocalBoundingSphere(); staticInfo->IndexInRoom = l; staticInfo->Update(); diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index cc013d592..3603157bb 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -471,7 +471,7 @@ namespace TEN::Renderer { mesh->ObjectNumber = nativeMesh->staticNumber; mesh->Color = nativeMesh->color; - mesh->OriginalVisibilityBox = StaticObjects[mesh->ObjectNumber].visibilityBox; + mesh->OriginalSphere = StaticObjects[mesh->ObjectNumber].visibilityBox.ToLocalBoundingSphere(); mesh->Pose = nativeMesh->pos; mesh->Scale = nativeMesh->scale; mesh->Update(); @@ -490,7 +490,7 @@ namespace TEN::Renderer if (obj.ObjectMeshes.empty()) continue; - if (!renderView.Camera.Frustum.SphereInFrustum(mesh->VisibilityBox.Center, mesh->VisibilitySphereRadius)) + if (!renderView.Camera.Frustum.SphereInFrustum(mesh->Sphere.Center, mesh->Sphere.Radius)) continue; // Collect the lights diff --git a/TombEngine/Renderer/Structures/RendererStatic.h b/TombEngine/Renderer/Structures/RendererStatic.h index 5e7e4f57c..03ae26f0f 100644 --- a/TombEngine/Renderer/Structures/RendererStatic.h +++ b/TombEngine/Renderer/Structures/RendererStatic.h @@ -22,19 +22,17 @@ namespace TEN::Renderer::Structures std::vector LightsToDraw; std::vector CachedRoomLights; bool CacheLights; - GameBoundingBox OriginalVisibilityBox; - BoundingOrientedBox VisibilityBox; + BoundingSphere OriginalSphere; + BoundingSphere Sphere; float Scale; - float VisibilitySphereRadius; void Update() { World = (Pose.Orientation.ToRotationMatrix() * Matrix::CreateScale(Scale) * Matrix::CreateTranslation(Pose.Position.x, Pose.Position.y, Pose.Position.z)); + Sphere = BoundingSphere(Vector3::Transform(OriginalSphere.Center, World), OriginalSphere.Radius * Scale); CacheLights = true; - VisibilityBox = OriginalVisibilityBox.ToBoundingOrientedBox(Pose); - VisibilitySphereRadius = Vector3(VisibilityBox.Extents).Length() * Scale; } }; } From 43fd5316899bfa2b7b7acf2b5eb79cf7f6faa1de Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 14 Nov 2024 23:47:18 +0100 Subject: [PATCH 045/112] Reorganize game loop to support load cameras --- TombEngine/Game/control/control.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index aebf8c6e8..08e59d0cf 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -223,20 +223,27 @@ GameStatus ControlPhase(bool insideMenu) PlaySoundSources(); Sound_UpdateScene(); - // Handle inventory, pause, load, save screens. + UpdateCamera(); + if (!insideMenu) { + // Handle inventory, pause, load, save screens. auto result = HandleMenuCalls(isTitle); - if (result != GameStatus::Normal) - return result; // Handle global input events. - result = HandleGlobalInputEvents(isTitle); - if (result != GameStatus::Normal) - return result; - } + if (result == GameStatus::Normal) + result = HandleGlobalInputEvents(isTitle); - UpdateCamera(); + if (result != GameStatus::Normal) + { + // Call post-loop callbacks last time and end level. + g_GameScript->OnLoop(DELTA_TIME, true); + g_GameScript->OnEnd(result); + HandleAllGlobalEvents(EventType::End, (Activator)LaraItem->Index); + + return result; + } + } // Post-loop script and event handling. g_GameScript->OnLoop(DELTA_TIME, true); @@ -490,9 +497,6 @@ void InitializeScripting(int levelIndex, LevelLoadType type) void DeInitializeScripting(int levelIndex, GameStatus reason) { - g_GameScript->OnEnd(reason); - HandleAllGlobalEvents(EventType::End, (Activator)LaraItem->Index); - g_GameScript->FreeLevelScripts(); g_GameScriptEntities->FreeEntities(); From 3737613b1a4bac967b7ad9de6b2b6cdb8fb0f4e1 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:16:48 +0100 Subject: [PATCH 046/112] Fixes to previous commit --- TombEngine/Game/control/control.cpp | 32 +++++++++++++++-------------- TombEngine/Specific/level.cpp | 3 ++- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 08e59d0cf..39079f85f 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -223,30 +223,32 @@ GameStatus ControlPhase(bool insideMenu) PlaySoundSources(); Sound_UpdateScene(); - UpdateCamera(); + auto result = GameStatus::Normal; if (!insideMenu) { // Handle inventory, pause, load, save screens. - auto result = HandleMenuCalls(isTitle); + result = HandleMenuCalls(isTitle); // Handle global input events. if (result == GameStatus::Normal) result = HandleGlobalInputEvents(isTitle); - - if (result != GameStatus::Normal) - { - // Call post-loop callbacks last time and end level. - g_GameScript->OnLoop(DELTA_TIME, true); - g_GameScript->OnEnd(result); - HandleAllGlobalEvents(EventType::End, (Activator)LaraItem->Index); - - return result; - } } - // Post-loop script and event handling. - g_GameScript->OnLoop(DELTA_TIME, true); + if (result != GameStatus::Normal) + { + // Call post-loop callbacks last time and end level. + g_GameScript->OnLoop(DELTA_TIME, true); + g_GameScript->OnEnd(result); + HandleAllGlobalEvents(EventType::End, (Activator)LaraItem->Index); + } + else + { + // Post-loop script and event handling. + g_GameScript->OnLoop(DELTA_TIME, true); + } + + UpdateCamera(); // Clear savegame loaded flag. JustLoaded = false; @@ -258,7 +260,7 @@ GameStatus ControlPhase(bool insideMenu) auto time2 = std::chrono::high_resolution_clock::now(); ControlPhaseTime = (std::chrono::duration_cast(time2 - time1)).count() / 1000000; - return GameStatus::Normal; + return result; } unsigned CALLBACK GameMain(void *) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index f77c4da61..1649811bb 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1549,9 +1549,10 @@ bool LoadLevelFile(int levelIndex) bool fastReload = (g_GameFlow->GetSettings()->FastReload && levelIndex == CurrentLevel && timestamp == LastLevelTimestamp && levelPath == LastLevelFilePath); // Dumping game scene right after engine launch is impossible, as no scene exists. - if (!FirstLevel && fastReload) + if (!FirstLevel) g_Renderer.DumpGameScene(); + // If fast reload is in action, don't draw loading screen, and instead draw last game frame. auto loadingScreenPath = TEN::Utils::ToWString(assetDir + level.LoadScreenFileName); g_Renderer.SetLoadingScreen(fastReload ? std::wstring{} : loadingScreenPath); From 74f1d2640e7e08301cb26549e110f23e8b5ae65e Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:25:09 +0100 Subject: [PATCH 047/112] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb81f036e..61183e364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Lua API changes * Added support for transparency value in DisplayString class. +* Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Fixed DisplayString class not supporting empty lines in multiline strings. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. From 03d93bcb2a1f782aa8e7668d35b655c64af4b3f9 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 15 Nov 2024 01:00:57 +0100 Subject: [PATCH 048/112] Prioritize ogg format over mp3, bump version --- TombEngine/Sound/sound.cpp | 2 +- TombEngine/version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index f04695fbc..d2f530215 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -46,7 +46,7 @@ const BASS_BFX_FREEVERB BASS_ReverbTypes[(int)ReverbType::Count] = // Reverb { 1.0f, 0.25f, 0.90f, 1.00f, 1.0f, 0, -1 } // 4 = Pipe }; -const std::string TRACKS_EXTENSIONS[] = {".wav", ".mp3", ".ogg"}; +const std::string TRACKS_EXTENSIONS[] = {".wav", ".ogg", ".mp3" }; const std::string TRACKS_PATH = "Audio/"; static std::string FullAudioDirectory; diff --git a/TombEngine/version.h b/TombEngine/version.h index dc4339854..8ad350120 100644 --- a/TombEngine/version.h +++ b/TombEngine/version.h @@ -8,7 +8,7 @@ #define TEN_MAJOR_VERSION 1 #define TEN_MINOR_VERSION 5 #define TEN_BUILD_NUMBER 0 -#define TEN_REVISION_NUMBER 3 +#define TEN_REVISION_NUMBER 4 #define TEST_BUILD 1 From ee6a661ed04d5a89c35c715ae69bde501db6fbd7 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 15 Nov 2024 01:40:26 +0100 Subject: [PATCH 049/112] Move DumpGameScene call to appropriate place --- TombEngine/Game/control/control.cpp | 2 ++ TombEngine/Specific/level.cpp | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 39079f85f..a86e28330 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -594,6 +594,8 @@ GameStatus DoGameLoop(int levelIndex) void EndGameLoop(int levelIndex, GameStatus reason) { + g_Renderer.DumpGameScene(); // Save last screenshot for loading screen. + SaveGame::SaveHub(levelIndex); DeInitializeScripting(levelIndex, reason); diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 1649811bb..57fef82e2 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1548,10 +1548,6 @@ bool LoadLevelFile(int levelIndex) auto timestamp = std::filesystem::last_write_time(levelPath); bool fastReload = (g_GameFlow->GetSettings()->FastReload && levelIndex == CurrentLevel && timestamp == LastLevelTimestamp && levelPath == LastLevelFilePath); - // Dumping game scene right after engine launch is impossible, as no scene exists. - if (!FirstLevel) - g_Renderer.DumpGameScene(); - // If fast reload is in action, don't draw loading screen, and instead draw last game frame. auto loadingScreenPath = TEN::Utils::ToWString(assetDir + level.LoadScreenFileName); g_Renderer.SetLoadingScreen(fastReload ? std::wstring{} : loadingScreenPath); From 3cb6eec261cabd3b43c229da43ca3e2f424a6756 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:52:36 +0000 Subject: [PATCH 050/112] Add Strings.DisplayString Enum page Also: * bumped up engine version with a developers tag. * Increased margin of the menu to accommodate changes --- Documentation/config.ld | 4 +- .../doc/2 classes/Strings.DisplayString.html | 20 +-- .../4 enums/Strings.DisplayStringOption.html | 159 ++++++++++++++++++ Documentation/doc/index.html | 11 +- Documentation/doc/ldoc.css | 7 +- Documentation/ldoc.css | 7 +- .../Strings/DisplayString/DisplayString.cpp | 8 +- .../DisplayString/DisplayStringOption.h | 22 +++ 8 files changed, 206 insertions(+), 32 deletions(-) create mode 100644 Documentation/doc/4 enums/Strings.DisplayStringOption.html create mode 100644 TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h diff --git a/Documentation/config.ld b/Documentation/config.ld index bd40d633c..a7798f24d 100644 --- a/Documentation/config.ld +++ b/Documentation/config.ld @@ -12,7 +12,7 @@ new_type("luautil", "5 Lua utility modules", true) not_luadoc = true -local version = "1.5" +local version = "1.6 (Developer Version)" project = "TombEngine" title = "TombEngine " .. version .. " Lua API" description = "TombEngine " .. version .. " scripting interface" @@ -34,7 +34,7 @@ If this happens, check __logs/TENLog.txt__ and look for an error message with th Enjoy. -\- _squidshire_ +\- _squidshire and the TombEngine development team._ ]] convert_opt=true diff --git a/Documentation/doc/2 classes/Strings.DisplayString.html b/Documentation/doc/2 classes/Strings.DisplayString.html index c32acc85d..df175324a 100644 --- a/Documentation/doc/2 classes/Strings.DisplayString.html +++ b/Documentation/doc/2 classes/Strings.DisplayString.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • @@ -116,7 +117,7 @@ when you need to use screen-space coordinates.

    Functions

    - + @@ -170,7 +171,7 @@ when you need to use screen-space coordinates.

    - DisplayString(string, Position[, scale][, color][, translated][, flags]) + DisplayString(string, Position[, scale][, color][, translated], table)
    Create a DisplayString. @@ -204,16 +205,9 @@ For use in ShowString and Default: false. (optional) -
  • flags - table - A table of string display options. Can be empty or omitted. The possible values and their effects are:

    -
    TEN.Strings.DisplayStringOption.CENTER: set the horizontal origin point to the center of the string.
    -TEN.Strings.DisplayStringOption.RIGHT: set the horizontal origin point to right of the string.
    -TEN.Strings.DisplayStringOption.SHADOW: give the string a small shadow.
    -TEN.Strings.DisplayStringOption.BLINK: blink the string.
    -
    -

    Default: empty - (optional) +

  • table + DisplayStringOption + Default: None. Please note that Strings are automatically aligned to the LEFT
  • diff --git a/Documentation/doc/4 enums/Strings.DisplayStringOption.html b/Documentation/doc/4 enums/Strings.DisplayStringOption.html new file mode 100644 index 000000000..bcbbf884c --- /dev/null +++ b/Documentation/doc/4 enums/Strings.DisplayStringOption.html @@ -0,0 +1,159 @@ + + + + + TombEngine 1.6 (Developer Version) Lua API + + + + +
    + +
    + +
    +
    +
    + + +
    DisplayString(string, Position[, scale][, color][, translated][, flags])DisplayString(string, Position[, scale][, color][, translated], table) Create a DisplayString.
    + + + + +
    Strings.DisplayStringOptionStrings.DisplayStringOption constants.
    + +
    +
    + + +

    Tables

    + +
    +
    + + Strings.DisplayStringOption +
    +
    + +

    Strings.DisplayStringOption constants.

    +
    CENTER  -- set the horizontal origin point to the center of the string.
    +RIGHT   -- set the horizontal origin point to right of the string.
    +SHADOW  -- gives the string a small drop shadow.
    +BLINK   -- blinks the string
    +
    + + + + + + + + + +
    +
    + + + + +
    +generated by TEN-LDoc (a fork of LDoc 1.4.6) +
    + + + diff --git a/Documentation/doc/index.html b/Documentation/doc/index.html index 814a66e4a..63382222e 100644 --- a/Documentation/doc/index.html +++ b/Documentation/doc/index.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • @@ -102,7 +103,7 @@
    -

    TombEngine 1.5 scripting interface

    +

    TombEngine 1.6 (Developer Version) scripting interface

    Welcome to the TombEngine scripting API. This is a work in progress and some information might be wrong or outdated. Please also note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse.

    At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on the TombEngine website.

    @@ -122,7 +123,7 @@ local door = GetMoveableByName("door_type4_14")

    Enjoy.

    -

    - squidshire

    +

    - squidshire and the TombEngine development team.

    1 Modules

    @@ -311,6 +312,10 @@ local door = GetMoveableByName("door_type4_14") + + + + diff --git a/Documentation/doc/ldoc.css b/Documentation/doc/ldoc.css index 9417d33c0..306dcd9f5 100644 --- a/Documentation/doc/ldoc.css +++ b/Documentation/doc/ldoc.css @@ -141,9 +141,9 @@ table.index td { text-align: left; vertical-align: top; } } #content { - margin-left: 16em; - padding: 1em; - width: 700px; + margin-left: 18em; + padding: 2em; + width: 900px; border-left: 2px solid #cccccc; border-right: 2px solid #cccccc; background-color: #ffffff; @@ -220,7 +220,6 @@ table.function_list td.name { background-color: #f0f0f0; min-width: 250px; } table.function_list td.summary { width: 100%; } ul.nowrap { - overflow:auto; white-space:nowrap; } diff --git a/Documentation/ldoc.css b/Documentation/ldoc.css index 9417d33c0..306dcd9f5 100644 --- a/Documentation/ldoc.css +++ b/Documentation/ldoc.css @@ -141,9 +141,9 @@ table.index td { text-align: left; vertical-align: top; } } #content { - margin-left: 16em; - padding: 1em; - width: 700px; + margin-left: 18em; + padding: 2em; + width: 900px; border-left: 2px solid #cccccc; border-right: 2px solid #cccccc; background-color: #ffffff; @@ -220,7 +220,6 @@ table.function_list td.name { background-color: #f0f0f0; min-width: 250px; } table.function_list td.summary { width: 100%; } ul.nowrap { - overflow:auto; white-space:nowrap; } diff --git a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp index aa3b4ac0f..ab6e5bfd2 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp @@ -47,12 +47,8 @@ For use in @{Strings.ShowString|ShowString} and @{Strings.HideString|HideString} @tparam[opt] Color color the color of the text. __Default: white__ @tparam[opt] bool translated If false or omitted, the input string argument will be displayed. If true, the string argument will be the key of a translated string specified in strings.lua. __Default: false__. -@tparam[opt] table flags A table of string display options. Can be empty or omitted. The possible values and their effects are: - TEN.Strings.DisplayStringOption.CENTER: set the horizontal origin point to the center of the string. - TEN.Strings.DisplayStringOption.RIGHT: set the horizontal origin point to right of the string. - TEN.Strings.DisplayStringOption.SHADOW: give the string a small shadow. - TEN.Strings.DisplayStringOption.BLINK: blink the string. -__Default: empty__ +@tparam Strings.DisplayStringOption table +__Default: None.__ _Please note that Strings are automatically aligned to the LEFT_ @treturn DisplayString A new DisplayString object. */ static std::unique_ptr CreateString(const std::string& key, const Vec2& pos, TypeOrNil scale, TypeOrNil color, diff --git a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h new file mode 100644 index 000000000..dd9674761 --- /dev/null +++ b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +/*** +Constants for Display String Options. +@enum Strings.DisplayStringOption +@pragma nostrip +*/ + +/*** Strings.DisplayStringOption constants. + +@table Strings.DisplayStringOption + CENTER -- set the horizontal origin point to the center of the string. + RIGHT -- set the horizontal origin point to right of the string. + SHADOW -- gives the string a small drop shadow. + BLINK -- blinks the string + + +*/ +/*** + +*/ From eb0390a23ba7cd537f93444e145cd70e27bc8107 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 15 Nov 2024 02:18:01 +0100 Subject: [PATCH 051/112] Fix default looped track fade-in --- CHANGELOG.md | 1 + TombEngine/Game/control/control.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61183e364..2333a502c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed enemy pickups dropping on death sectors. * Fixed vehicle transfer not happening for levels which were not previously visited. * Fixed audio tracks placed in subfolders not restoring after loading savegame. +* Fixed lack of fade-in for looped audio track on level start. * Fixed scripted input events not registering on the same game frame. * Fixed incorrect object camera position. * Fixed incorrect camera movement near walls after leaving look mode. diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index a86e28330..fb81b7dc0 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -494,7 +494,7 @@ void InitializeScripting(int levelIndex, LevelLoadType type) // Play default background music. if (type != LevelLoadType::Load) - PlaySoundTrack(level.GetAmbientTrack(), SoundTrackType::BGM); + PlaySoundTrack(level.GetAmbientTrack(), SoundTrackType::BGM, 0, SOUND_XFADETIME_LEVELJUMP); } void DeInitializeScripting(int levelIndex, GameStatus reason) From c74d1106549d5b26269799d5f9f34bfe9f542ad9 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Fri, 15 Nov 2024 01:30:46 +0000 Subject: [PATCH 052/112] Removed orphaned header file Moved ldoc comments . --- .../TEN/Strings/DisplayString/DisplayString.h | 18 +++++++++++++++ .../DisplayString/DisplayStringOption.h | 22 ------------------- 2 files changed, 18 insertions(+), 22 deletions(-) delete mode 100644 TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h diff --git a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h index 0487411fb..18c78f316 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h +++ b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h @@ -2,6 +2,24 @@ #include "Scripting/Internal/TEN/Color/Color.h" #include "Scripting/Internal/TEN/Vec2/Vec2.h" +/*** +Constants for Display String Options. +@enum Strings.DisplayStringOption +@pragma nostrip +*/ + +/*** Strings.DisplayStringOption constants. + +@table Strings.DisplayStringOption + CENTER -- set the horizontal origin point to the center of the string. + RIGHT -- set the horizontal origin point to right of the string. + SHADOW -- gives the string a small drop shadow. + BLINK -- blinks the string +*/ +/*** + +*/ + enum class DisplayStringOptions { Center, diff --git a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h deleted file mode 100644 index dd9674761..000000000 --- a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayStringOption.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include -#include -/*** -Constants for Display String Options. -@enum Strings.DisplayStringOption -@pragma nostrip -*/ - -/*** Strings.DisplayStringOption constants. - -@table Strings.DisplayStringOption - CENTER -- set the horizontal origin point to the center of the string. - RIGHT -- set the horizontal origin point to right of the string. - SHADOW -- gives the string a small drop shadow. - BLINK -- blinks the string - - -*/ -/*** - -*/ From 2ddd482a80183357ba85291792c7a15e80839616 Mon Sep 17 00:00:00 2001 From: Sezz Date: Fri, 15 Nov 2024 21:03:30 +1100 Subject: [PATCH 053/112] Correct sound conditions --- TombEngine/Game/Lara/lara_flare.cpp | 2 +- TombEngine/Game/Lara/lara_helpers.cpp | 4 ++-- TombEngine/Game/Lara/lara_one_gun.cpp | 2 +- TombEngine/Game/missile.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TombEngine/Game/Lara/lara_flare.cpp b/TombEngine/Game/Lara/lara_flare.cpp index 0cae11806..aae02115e 100644 --- a/TombEngine/Game/Lara/lara_flare.cpp +++ b/TombEngine/Game/Lara/lara_flare.cpp @@ -260,7 +260,7 @@ void DrawFlare(ItemInfo& laraItem) SoundEffect( SFX_TR4_FLARE_IGNITE_DRY, &laraItem.Pose, - TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::ShallowWater : SoundEnvironment::Land); + TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::Underwater : SoundEnvironment::Land); } DoFlareInHand(laraItem, player.Flare.Life); diff --git a/TombEngine/Game/Lara/lara_helpers.cpp b/TombEngine/Game/Lara/lara_helpers.cpp index 5f8a21db2..48fee7e30 100644 --- a/TombEngine/Game/Lara/lara_helpers.cpp +++ b/TombEngine/Game/Lara/lara_helpers.cpp @@ -992,7 +992,7 @@ void HandlePlayerAirBubbles(ItemInfo* item) { constexpr auto BUBBLE_COUNT_MAX = 3; - SoundEffect(SFX_TR4_LARA_BUBBLES, &item->Pose, SoundEnvironment::ShallowWater); + SoundEffect(SFX_TR4_LARA_BUBBLES, &item->Pose, SoundEnvironment::Underwater); const auto& level = *g_GameFlow->GetLevel(CurrentLevel); @@ -1469,7 +1469,7 @@ void UpdateLaraSubsuitAngles(ItemInfo* item) auto mul1 = (float)abs(lara->Control.Subsuit.Velocity[0]) / BLOCK(8); auto mul2 = (float)abs(lara->Control.Subsuit.Velocity[1]) / BLOCK(8); auto vol = ((mul1 + mul2) * 5.0f) + 0.5f; - SoundEffect(SFX_TR5_VEHICLE_DIVESUIT_ENGINE, &item->Pose, SoundEnvironment::ShallowWater, 1.0f + (mul1 + mul2), vol); + SoundEffect(SFX_TR5_VEHICLE_DIVESUIT_ENGINE, &item->Pose, SoundEnvironment::Underwater, 1.0f + (mul1 + mul2), vol); } } diff --git a/TombEngine/Game/Lara/lara_one_gun.cpp b/TombEngine/Game/Lara/lara_one_gun.cpp index 4270b6c9b..85cc23978 100644 --- a/TombEngine/Game/Lara/lara_one_gun.cpp +++ b/TombEngine/Game/Lara/lara_one_gun.cpp @@ -428,7 +428,7 @@ void FireShotgun(ItemInfo& laraItem) player.RightArm.GunFlash = Weapons[(int)LaraWeaponType::Shotgun].FlashTime; - SoundEffect(SFX_TR4_EXPLOSION1, &laraItem.Pose, TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::ShallowWater : SoundEnvironment::Land); + SoundEffect(SFX_TR4_EXPLOSION1, &laraItem.Pose, TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::Underwater : SoundEnvironment::Land); SoundEffect(Weapons[(int)LaraWeaponType::Shotgun].SampleNum, &laraItem.Pose); Rumble(0.5f, 0.2f); diff --git a/TombEngine/Game/missile.cpp b/TombEngine/Game/missile.cpp index f77cc2a4e..991ac5adb 100644 --- a/TombEngine/Game/missile.cpp +++ b/TombEngine/Game/missile.cpp @@ -50,7 +50,7 @@ void ControlMissile(short fxNumber) auto& fx = EffectList[fxNumber]; auto isUnderwater = TestEnvironment(ENV_FLAG_WATER, fx.roomNumber); - auto soundFXType = isUnderwater ? SoundEnvironment::ShallowWater : SoundEnvironment::Land; + auto soundFXType = isUnderwater ? SoundEnvironment::Underwater : SoundEnvironment::Land; if (fx.objectNumber == ID_SCUBA_HARPOON && isUnderwater && fx.pos.Orientation.x > ANGLE(-67.5f)) From 040fd06afe958f1ffec0bae28642cd9d0b125fa9 Mon Sep 17 00:00:00 2001 From: Sezz Date: Fri, 15 Nov 2024 21:13:33 +1100 Subject: [PATCH 054/112] Optimise Vector2i and Vector3i distance methods --- TombEngine/Math/Objects/Vector2i.cpp | 4 ++-- TombEngine/Math/Objects/Vector3i.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TombEngine/Math/Objects/Vector2i.cpp b/TombEngine/Math/Objects/Vector2i.cpp index e8bc8fe61..89b093120 100644 --- a/TombEngine/Math/Objects/Vector2i.cpp +++ b/TombEngine/Math/Objects/Vector2i.cpp @@ -13,12 +13,12 @@ namespace TEN::Math float Vector2i::Distance(const Vector2i& origin, const Vector2i& target) { - return Vector2::Distance(origin.ToVector2(), target.ToVector2()); + return std::sqrt(DistanceSquared(origin, target)); } float Vector2i::DistanceSquared(const Vector2i& origin, const Vector2i& target) { - return Vector2::DistanceSquared(origin.ToVector2(), target.ToVector2()); + return (SQUARE(target.x - origin.x) + SQUARE(target.y - origin.y)); } Vector2 Vector2i::ToVector2() const diff --git a/TombEngine/Math/Objects/Vector3i.cpp b/TombEngine/Math/Objects/Vector3i.cpp index b45e626bc..3b171c9d6 100644 --- a/TombEngine/Math/Objects/Vector3i.cpp +++ b/TombEngine/Math/Objects/Vector3i.cpp @@ -14,12 +14,12 @@ float Vector3i::Distance(const Vector3i& origin, const Vector3i& target) { - return Vector3::Distance(origin.ToVector3(), target.ToVector3()); + return std::sqrt(DistanceSquared(origin, target)); } - + float Vector3i::DistanceSquared(const Vector3i& origin, const Vector3i& target) { - return Vector3::DistanceSquared(origin.ToVector3(), target.ToVector3()); + return (SQUARE(target.x - origin.x) + SQUARE(target.y - origin.y) + SQUARE(target.z - origin.z)); } void Vector3i::Lerp(const Vector3i& target, float alpha) From 8ff1e5ed0db2ef4f6a4ac5343e2ce63f1b0e3e1a Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 00:56:50 +0100 Subject: [PATCH 055/112] Fixed #1480 --- CHANGELOG.md | 6 +-- TombEngine/Game/collision/collide_item.cpp | 58 ++++++--------------- TombEngine/Math/Objects/GameBoundingBox.cpp | 27 ++++++++++ TombEngine/Math/Objects/GameBoundingBox.h | 1 + 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2333a502c..61a4e6824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed engine performance around bridges. * Fixed engine performance if weather or bubble effects are active. * Fixed silent crashes if loaded level is corrupted or in incorrect format. -* Fixed incorrect clipping of scaled static meshes. +* Fixed incorrect clipping of scaled off-centered static meshes. +* Fixed incorrect collision detection for off-centered meshes. * Fixed uzi targeting issues after using flycheat. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. @@ -22,7 +23,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed binocular or lasersight camera not switching off correctly after flyby. * Fixed Lara's Home entry not working. * Fixed exploding TR3 bosses. -* Fixed the original issue with deactivation of Dart Emitter. +* Fixed original issue with deactivation of Dart Emitter. * Fixed Lens Flare object not functioning properly. * Fixed Skeleton and Mummy not reacting to shotgun hits. @@ -33,7 +34,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Allow more than 1024 objects in a level. ### Lua API changes - * Added support for transparency value in DisplayString class. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Fixed DisplayString class not supporting empty lines in multiline strings. diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index f69326847..4e7329f3d 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -105,20 +105,20 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, // Establish parameters of colliding item. const auto& collidingBounds = GetBestFrame(collidingItem).BoundingBox; - auto collidingExtents = collidingBounds.GetExtents(); - auto collidingSphere = BoundingSphere(collidingBounds.GetCenter() + collidingItem.Pose.Position.ToVector3(), collidingExtents.Length()); - auto collidingCircle = Vector3(collidingSphere.Center.x, collidingSphere.Center.z, (customRadius > 0.0f) ? customRadius : std::hypot(collidingExtents.x, collidingExtents.z)); + + // Quickly discard collision if colliding item bounds are below tolerance threshold. + if (!customRadius && collidingBounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) + return collObjects; // Convert bounding box to DX bounds. auto convertedBounds = collidingBounds.ToBoundingOrientedBox(collidingItem.Pose); + // Create conservative AABB for rough tests. + auto& collidingAABB = collidingBounds.ToConservativeBoundingBox(collidingItem.Pose); + // Override extents if specified. if (customRadius > 0.0f) - convertedBounds.Extents = Vector3(customRadius); - - // Quickly discard collision if colliding item bounds are below tolerance threshold. - if (collidingSphere.Radius <= COLLIDABLE_BOUNDS_THRESHOLD) - return collObjects; + collidingAABB = BoundingBox(collidingItem.Pose.Position.ToVector3(), Vector3(customRadius)); // Run through neighboring rooms. const auto& room = g_Level.Rooms[collidingItem.RoomNumber]; @@ -168,27 +168,14 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, continue; const auto& bounds = GetBestFrame(item).BoundingBox; - auto extents = bounds.GetExtents(); // If item bounding box extents is below tolerance threshold, discard object. - if (extents.Length() <= COLLIDABLE_BOUNDS_THRESHOLD) + if (bounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) continue; - // Test rough vertical distance to discard objects not intersecting vertically. - if (((collidingItem.Pose.Position.y + collidingBounds.Y1) - ROUGH_BOX_HEIGHT_MIN) > - ((item.Pose.Position.y + bounds.Y2) + ROUGH_BOX_HEIGHT_MIN)) - { - continue; - } - if (((collidingItem.Pose.Position.y + collidingBounds.Y2) + ROUGH_BOX_HEIGHT_MIN) < - ((item.Pose.Position.y + bounds.Y1) - ROUGH_BOX_HEIGHT_MIN)) - { - continue; - } - - // Test rough circle intersection to discard objects not intersecting horizontally. - auto circle = Vector3(item.Pose.Position.x, item.Pose.Position.z, std::hypot(extents.x, extents.z)); - if (!Geometry::CircleIntersects(circle, collidingCircle)) + // Test conservative AABB intersection. + const auto& AABB = bounds.ToConservativeBoundingBox(item.Pose); + if (!AABB.Intersects(collidingAABB)) continue; auto box = bounds.ToBoundingOrientedBox(item.Pose); @@ -218,28 +205,13 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, const auto& bounds = GetBoundsAccurate(staticObj, false); - // Test rough vertical distance to discard statics not intersecting vertically. - if (((collidingItem.Pose.Position.y + collidingBounds.Y1) - ROUGH_BOX_HEIGHT_MIN) > - ((staticObj.pos.Position.y + bounds.Y2) + ROUGH_BOX_HEIGHT_MIN)) - { - continue; - } - if (((collidingItem.Pose.Position.y + collidingBounds.Y2) + ROUGH_BOX_HEIGHT_MIN) < - ((staticObj.pos.Position.y + bounds.Y1) - ROUGH_BOX_HEIGHT_MIN)) - { - continue; - } - - // Test rough circle intersection to discard statics not intersecting horizontally. - auto circle = Vector3(staticObj.pos.Position.x, staticObj.pos.Position.z, (bounds.GetExtents() * Vector3(1.0f, 0.0f, 1.0f)).Length()); - if (!Geometry::CircleIntersects(circle, collidingCircle)) - continue; - // Skip if either bounding box has any zero extent (not a collidable volume). if (bounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) continue; - if (collidingBounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) + // Test conservative AABB intersection. + const auto& AABB = bounds.ToConservativeBoundingBox(staticObj.pos); + if (!AABB.Intersects(collidingAABB)) continue; auto box = bounds.ToBoundingOrientedBox(staticObj.pos.Position); diff --git a/TombEngine/Math/Objects/GameBoundingBox.cpp b/TombEngine/Math/Objects/GameBoundingBox.cpp index f017cfbee..b764b0a79 100644 --- a/TombEngine/Math/Objects/GameBoundingBox.cpp +++ b/TombEngine/Math/Objects/GameBoundingBox.cpp @@ -85,6 +85,33 @@ Z2 = (int)round(boxMax.z); } + BoundingBox GameBoundingBox::ToConservativeBoundingBox(const Pose& pose) const + { + // Compute extents. + auto extents = GetExtents(); + + // Calculate conservative radius in the XZ plane for Y-axis rotation. + float xzRadius = sqrt(extents.x * extents.x + extents.z * extents.z); + + // Extents remain unchanged along the Y-axis. + auto conservativeExtents = Vector3(xzRadius, extents.y, xzRadius); + + // Manual 2D rotation for Y-axis only. + auto center = GetCenter(); + float cos = phd_cos(pose.Orientation.y); + float sin = phd_sin(pose.Orientation.y); + + // Rotate the centerOffset in the XZ plane (ignore Y rotation for height). + float rotatedX = cos * center.x + sin * center.z; + float rotatedZ = -sin * center.x + cos * center.z; + + // The center of the bounding box is now the position plus the rotated offset. + auto rotatedCenter = pose.Position.ToVector3() + Vector3(rotatedX, center.y, rotatedZ); + + // Build and return the conservative AABB. + return BoundingBox(rotatedCenter, conservativeExtents); + } + BoundingSphere GameBoundingBox::ToLocalBoundingSphere() const { return BoundingSphere(GetCenter(), GetExtents().Length()); diff --git a/TombEngine/Math/Objects/GameBoundingBox.h b/TombEngine/Math/Objects/GameBoundingBox.h index a457cefc2..be28eeabe 100644 --- a/TombEngine/Math/Objects/GameBoundingBox.h +++ b/TombEngine/Math/Objects/GameBoundingBox.h @@ -48,6 +48,7 @@ struct ObjectInfo; BoundingSphere ToLocalBoundingSphere() const; BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const; BoundingOrientedBox ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const; + BoundingBox ToConservativeBoundingBox(const Pose& pose) const; // Operators From f39d2ce250bb80e118966d40f5912c88f3108330 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 02:07:56 +0100 Subject: [PATCH 056/112] Fixed incorrect loading of dummy title file in case of real levels are missing --- TombEngine/Specific/level.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 57fef82e2..ef3b07b9f 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1528,10 +1528,10 @@ bool LoadLevelFile(int levelIndex) if (!std::filesystem::is_regular_file(levelPath)) { - levelPath = assetDir + "dummy.ten"; - - if (levelIndex == 0 && GenerateDummyLevel(levelPath)) + if (levelIndex == 0) { + levelPath = assetDir + "dummy.ten"; + GenerateDummyLevel(levelPath); TENLog("Title level file not found, using dummy level.", LogLevel::Info); isDummyLevel = true; } From e02e1db1baf8ffdf50b229d95367eb850d936363 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 03:15:22 +0100 Subject: [PATCH 057/112] Fixed #1482 --- CHANGELOG.md | 1 + TombEngine/Game/pickup/pickup.cpp | 34 +++++++++++++++---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a4e6824..accf6a5bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed uzi targeting issues after using flycheat. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. +* Fixed Sarcophagus and Search Object pickup triggers. * Fixed vehicle transfer not happening for levels which were not previously visited. * Fixed audio tracks placed in subfolders not restoring after loading savegame. * Fixed lack of fade-in for looped audio track on level start. diff --git a/TombEngine/Game/pickup/pickup.cpp b/TombEngine/Game/pickup/pickup.cpp index 84f6f982d..7eb788e75 100644 --- a/TombEngine/Game/pickup/pickup.cpp +++ b/TombEngine/Game/pickup/pickup.cpp @@ -203,23 +203,6 @@ void RemoveObjectFromInventory(GAME_OBJECT_ID objectID, std::optional count } } -void CollectCarriedItems(ItemInfo* item) -{ - short pickupNumber = item->CarriedItem; - while (pickupNumber != NO_VALUE) - { - auto& pickupItem = g_Level.Items[pickupNumber]; - - PickedUpObject(pickupItem); - g_Hud.PickupSummary.AddDisplayPickup(pickupItem); - KillItem(pickupNumber); - - pickupNumber = pickupItem.CarriedItem; - } - - item->CarriedItem = NO_VALUE; -} - static void HideOrDisablePickup(ItemInfo& pickupItem) { if (pickupItem.TriggerFlags & 0xC0) @@ -234,6 +217,23 @@ static void HideOrDisablePickup(ItemInfo& pickupItem) } } +void CollectCarriedItems(ItemInfo* item) +{ + short pickupNumber = item->CarriedItem; + while (pickupNumber != NO_VALUE) + { + auto& pickupItem = g_Level.Items[pickupNumber]; + + PickedUpObject(pickupItem); + g_Hud.PickupSummary.AddDisplayPickup(pickupItem); + HideOrDisablePickup(pickupItem); + + pickupNumber = pickupItem.CarriedItem; + } + + item->CarriedItem = NO_VALUE; +} + void CollectMultiplePickups(int itemNumber) { auto& firstItem = g_Level.Items[itemNumber]; From 08137298cc8608d4cd5f641f3ffa1ce1b75ef090 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 04:35:20 +0100 Subject: [PATCH 058/112] Fixed #1483 --- TombEngine/Game/room.cpp | 3 +++ TombEngine/Renderer/Renderer.h | 3 ++- TombEngine/Renderer/RendererCompatibility.cpp | 21 ++++++++++++------- TombEngine/Specific/level.cpp | 3 ++- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index c2e4dc0eb..f26e753f6 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -136,6 +136,9 @@ void ResetRoomData() sector.Stopper = false; sector.BridgeItemNumbers.clear(); } + + for (auto& mesh : room.mesh) + mesh.Dirty = true; } // Make sure no pathfinding boxes are blocked (either by doors or by other door-like objects). diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 47f07698b..c88396b82 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -597,7 +597,8 @@ namespace TEN::Renderer void Render(float interpFactor); void RenderTitle(float interpFactor); void Lock(); - bool PrepareDataForTheRenderer(); + void ResetData(); + bool PrepareData(); void UpdateCameraMatrices(CAMERA_INFO* cam, float farView); void RenderSimpleSceneToParaboloid(RenderTarget2D* renderTarget, Vector3 position, int emisphere); void DumpGameScene(); diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 8a3cada26..850938b2b 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -20,7 +20,18 @@ namespace TEN::Renderer { template class VertexBuffer; - bool Renderer::PrepareDataForTheRenderer() + void Renderer::ResetData() + { + int allocatedItemSize = (int)g_Level.Items.size() + MAX_SPAWNED_ITEM_COUNT; + + auto item = RendererItem(); + _items = std::vector(allocatedItemSize, item); + + auto effect = RendererEffect(); + _effects = std::vector(allocatedItemSize, effect); + } + + bool Renderer::PrepareData() { TENLog("Preparing renderer...", LogLevel::Info); @@ -35,13 +46,7 @@ namespace TEN::Renderer _meshes.clear(); - int allocatedItemSize = (int)g_Level.Items.size() + MAX_SPAWNED_ITEM_COUNT; - - auto item = RendererItem(); - _items = std::vector(allocatedItemSize, item); - - auto effect = RendererEffect(); - _effects = std::vector(allocatedItemSize, effect); + ResetData(); TENLog("Allocated renderer object memory.", LogLevel::Info); diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index ef3b07b9f..a9fb98d5d 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1395,12 +1395,13 @@ bool LoadLevel(const std::string& path, bool partial) if (!partial) { - g_Renderer.PrepareDataForTheRenderer(); + g_Renderer.PrepareData(); SetScreenFadeOut(FADE_SCREEN_SPEED, true); StopSoundTracks(SOUND_XFADETIME_BGM_START); } else { + g_Renderer.ResetData(); SetScreenFadeIn(FADE_SCREEN_SPEED, true); StopSoundTracks(SOUND_XFADETIME_LEVELJUMP); } From 3bdb0b367b721e4811fdad85661eae2ff7924296 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 09:58:09 +0100 Subject: [PATCH 059/112] Fixed #1481 --- CHANGELOG.md | 3 ++- .../Scripting/Internal/TEN/Sound/SoundHandler.cpp | 6 ++++-- TombEngine/Sound/sound.cpp | 12 ++++++------ TombEngine/Sound/sound.h | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index accf6a5bb..804d16076 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed Sarcophagus and Search Object pickup triggers. * Fixed vehicle transfer not happening for levels which were not previously visited. * Fixed audio tracks placed in subfolders not restoring after loading savegame. -* Fixed lack of fade-in for looped audio track on level start. +* Fixed initial position and lack of fade-in for looped audio track on level start. * Fixed scripted input events not registering on the same game frame. * Fixed incorrect object camera position. * Fixed incorrect camera movement near walls after leaving look mode. @@ -36,6 +36,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Lua API changes * Added support for transparency value in DisplayString class. +* Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Fixed DisplayString class not supporting empty lines in multiline strings. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. diff --git a/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp b/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp index b9dcf053e..580a0997b 100644 --- a/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp @@ -27,9 +27,11 @@ namespace TEN::Scripting::Sound ///Set and play an ambient track //@function SetAmbientTrack //@tparam string name of track (without file extension) to play - static void SetAmbientTrack(const std::string& trackName) + //@tparam bool specifies whether ambient track should play from the beginning, or crossfade at a random position + static void SetAmbientTrack(const std::string& trackName, TypeOrNil fromTheBeginning) { - PlaySoundTrack(trackName, SoundTrackType::BGM); + std::optional position = USE_IF_HAVE(bool, fromTheBeginning, false) ? std::optional(0) : std::nullopt; + PlaySoundTrack(trackName, SoundTrackType::BGM, position, position.has_value() ? SOUND_XFADETIME_ONESHOT : SOUND_XFADETIME_BGM); } ///Stop any audio tracks currently playing diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index d2f530215..54fdf2d3c 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -490,7 +490,7 @@ std::optional GetCurrentSubtitle() return std::nullopt; } -void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD position, int forceFadeInTime) +void PlaySoundTrack(const std::string& track, SoundTrackType mode, std::optional position, int forceFadeInTime) { if (!g_Configuration.EnableSound) return; @@ -507,8 +507,8 @@ void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD positio { // Same track is incoming with different playhead, set it to a new position. auto stream = SoundtrackSlot[(int)mode].Channel; - if (position && (BASS_ChannelGetLength(stream, BASS_POS_BYTE) > position)) - BASS_ChannelSetPosition(stream, position, BASS_POS_BYTE); + if (position.has_value() && (BASS_ChannelGetLength(stream, BASS_POS_BYTE) > position.value())) + BASS_ChannelSetPosition(stream, position.value(), BASS_POS_BYTE); return; } @@ -573,7 +573,7 @@ void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD positio // Shuffle... // Only activates if no custom position is passed as argument. - if (!position) + if (!position.has_value()) { QWORD newPos = BASS_ChannelGetLength(stream, BASS_POS_BYTE) * (static_cast(GetRandomControl()) / static_cast(RAND_MAX)); BASS_ChannelSetPosition(stream, newPos, BASS_POS_BYTE); @@ -585,8 +585,8 @@ void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD positio BASS_ChannelPlay(stream, false); // Try to restore position, if specified. - if (position && (BASS_ChannelGetLength(stream, BASS_POS_BYTE) > position)) - BASS_ChannelSetPosition(stream, position, BASS_POS_BYTE); + if (position.has_value() && (BASS_ChannelGetLength(stream, BASS_POS_BYTE) > position.value())) + BASS_ChannelSetPosition(stream, position.value(), BASS_POS_BYTE); if (Sound_CheckBASSError("Playing soundtrack '%s'", true, fullTrackName.filename().string().c_str())) return; diff --git a/TombEngine/Sound/sound.h b/TombEngine/Sound/sound.h index bad970b6c..7de2fa084 100644 --- a/TombEngine/Sound/sound.h +++ b/TombEngine/Sound/sound.h @@ -165,7 +165,7 @@ void SayNo(); void PlaySoundSources(); int GetShatterSound(int shatterID); -void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, QWORD position = 0, int forceFadeInTime = 0); +void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, std::optional position = std::nullopt, int forceFadeInTime = 0); void PlaySoundTrack(const std::string& trackName, short mask = 0); void PlaySoundTrack(int index, short mask = 0); void StopSoundTrack(SoundTrackType mode, int fadeoutTime); From 7f4242521af929fe9d6e7cdcd5582a82255a37a1 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:20:28 +0100 Subject: [PATCH 060/112] Fixed crash on leveljump, if collision callbacks were assigned --- CHANGELOG.md | 1 + TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 804d16076..d2a9137b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Fixed DisplayString class not supporting empty lines in multiline strings. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. +* Fixed collision callbacks not properly clearing after leveljump. ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 diff --git a/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.h b/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.h index ef8703b88..0a21ead21 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.h @@ -143,5 +143,7 @@ private: void FreeEntities() override { m_nameMap.clear(); + m_collidingItemsToRemove.clear(); + m_collidingItems.clear(); } }; From 99a4c68417fd97f710f2f9c4af39ef3f4aeb5ad0 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:49:52 +0100 Subject: [PATCH 061/112] Add GetNextLevel and make load game behaviour consistent with scripting API --- CHANGELOG.md | 1 + TombEngine/Game/control/control.cpp | 5 +---- TombEngine/Game/gui.cpp | 2 +- .../Scripting/Internal/ReservedScriptNames.h | 1 + .../Scripting/Internal/TEN/Flow/FlowHandler.cpp | 14 ++++++++++++++ .../Scripting/Internal/TEN/Flow/FlowHandler.h | 1 + 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2a9137b2..992b75207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Allow more than 1024 objects in a level. ### Lua API changes +* Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start. * Added support for transparency value in DisplayString class. * Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index fb81b7dc0..5ad139c51 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -669,11 +669,8 @@ GameStatus HandleMenuCalls(bool isTitle) else if (doLoad && g_GameFlow->IsLoadSaveEnabled() && g_Gui.GetInventoryMode() != InventoryMode::Load) { SaveGame::LoadHeaders(); - g_Gui.SetInventoryMode(InventoryMode::Load); - - if (g_Gui.CallInventory(LaraItem, false)) - gameStatus = GameStatus::LoadGame; + g_Gui.CallInventory(LaraItem, false); } else if (doPause && g_Gui.GetInventoryMode() != InventoryMode::Pause) { diff --git a/TombEngine/Game/gui.cpp b/TombEngine/Game/gui.cpp index b2c0fd411..1fd850f47 100644 --- a/TombEngine/Game/gui.cpp +++ b/TombEngine/Game/gui.cpp @@ -3524,7 +3524,7 @@ namespace TEN::Gui else { SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always); - g_GameFlow->SelectedSaveGame = SelectedSaveSlot; + NextLevel = -(SelectedSaveSlot + 1); return LoadResult::Load; } } diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index f4b2df397..ca2980f7d 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -203,6 +203,7 @@ static constexpr char ScriptReserved_IsTagPresent[] = "IsTagPresent"; static constexpr char ScriptReserved_AddLevel[] = "AddLevel"; static constexpr char ScriptReserved_GetLevel[] = "GetLevel"; static constexpr char ScriptReserved_GetCurrentLevel[] = "GetCurrentLevel"; +static constexpr char ScriptReserved_GetNextLevel[] = "GetNextLevel"; static constexpr char ScriptReserved_SetIntroImagePath[] = "SetIntroImagePath"; static constexpr char ScriptReserved_SetTitleScreenImagePath[] = "SetTitleScreenImagePath"; static constexpr char ScriptReserved_SetFarView[] = "SetFarView"; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp index 4b880af1d..0d15f2d57 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp @@ -125,6 +125,12 @@ have an ID of 0, the second an ID of 1, and so on. */ tableFlow.set_function(ScriptReserved_GetCurrentLevel, &FlowHandler::GetCurrentLevel, this); +/*** Returns the level that is about to load. If no new level is about to load, returns current level. +@function GetNextLevel +@treturn Flow.Level incoming new level or current level, if no new level is loading +*/ + tableFlow.set_function(ScriptReserved_GetNextLevel, &FlowHandler::GetNextLevel, this); + /*** Finishes the current level, with optional level index and start position index provided. If level index is not provided or is zero, jumps to next level. If level index is more than @@ -399,6 +405,14 @@ Level* FlowHandler::GetCurrentLevel() return Levels[CurrentLevel]; } +Level* FlowHandler::GetNextLevel() +{ + if (NextLevel == CurrentLevel) + return Levels[CurrentLevel]; + + return Levels[abs(NextLevel)]; // Negative value indicates incoming savegame. +} + int FlowHandler::GetNumLevels() const { return (int)Levels.size(); diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h index 55092b97d..4369561dd 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h @@ -59,6 +59,7 @@ public: Settings* GetSettings(); Level* GetLevel(int id); Level* GetCurrentLevel(); + Level* GetNextLevel(); int GetLevelNumber(const std::string& flieName); int GetNumLevels() const; void EndLevel(std::optional nextLevel, std::optional startPosIndex); From 2624c5bed276e4f62e70eae7eabe1be07181956b Mon Sep 17 00:00:00 2001 From: Sezz Date: Sat, 16 Nov 2024 22:07:57 +1100 Subject: [PATCH 062/112] Formatting --- Documentation/doc/1 modules/Effects.html | 3 +- Documentation/doc/1 modules/Flow.html | 28 ++++++++++++++- Documentation/doc/1 modules/Input.html | 3 +- Documentation/doc/1 modules/Inventory.html | 3 +- Documentation/doc/1 modules/Logic.html | 3 +- Documentation/doc/1 modules/Objects.html | 3 +- Documentation/doc/1 modules/Sound.html | 11 ++++-- Documentation/doc/1 modules/Strings.html | 3 +- Documentation/doc/1 modules/Util.html | 3 +- Documentation/doc/1 modules/View.html | 3 +- .../doc/2 classes/Flow.Animations.html | 3 +- Documentation/doc/2 classes/Flow.Fog.html | 3 +- .../doc/2 classes/Flow.InventoryItem.html | 3 +- .../doc/2 classes/Flow.LensFlare.html | 3 +- Documentation/doc/2 classes/Flow.Level.html | 3 +- Documentation/doc/2 classes/Flow.Mirror.html | 3 +- .../doc/2 classes/Flow.Settings.html | 15 ++++---- .../doc/2 classes/Flow.SkyLayer.html | 3 +- .../doc/2 classes/Flow.Starfield.html | 3 +- .../doc/2 classes/Objects.AIObject.html | 3 +- .../doc/2 classes/Objects.Camera.html | 3 +- .../doc/2 classes/Objects.LaraObject.html | 3 +- .../doc/2 classes/Objects.Moveable.html | 3 +- Documentation/doc/2 classes/Objects.Room.html | 3 +- Documentation/doc/2 classes/Objects.Sink.html | 3 +- .../doc/2 classes/Objects.SoundSource.html | 3 +- .../doc/2 classes/Objects.Static.html | 3 +- .../doc/2 classes/Objects.Volume.html | 3 +- .../doc/2 classes/View.DisplaySprite.html | 3 +- .../doc/3 primitive classes/Color.html | 3 +- .../doc/3 primitive classes/Rotation.html | 3 +- .../doc/3 primitive classes/Vec2.html | 3 +- .../doc/3 primitive classes/Vec3.html | 3 +- .../doc/4 enums/Effects.BlendID.html | 3 +- .../doc/4 enums/Effects.EffectID.html | 3 +- .../doc/4 enums/Flow.GameStatus.html | 3 +- Documentation/doc/4 enums/Input.ActionID.html | 3 +- .../doc/4 enums/Objects.AmmoType.html | 3 +- .../doc/4 enums/Objects.MoveableStatus.html | 3 +- Documentation/doc/4 enums/Objects.ObjID.html | 3 +- .../doc/4 enums/Objects.RoomFlagID.html | 3 +- .../doc/4 enums/Objects.RoomReverb.html | 3 +- .../doc/4 enums/Sound.SoundTrackType.html | 3 +- Documentation/doc/4 enums/Util.LogLevel.html | 3 +- Documentation/doc/4 enums/View.AlignMode.html | 3 +- .../doc/4 enums/View.CameraType.html | 3 +- .../doc/4 enums/View.PostProcessMode.html | 3 +- Documentation/doc/4 enums/View.ScaleMode.html | 3 +- .../5 lua utility modules/EventSequence.html | 3 +- .../doc/5 lua utility modules/Timer.html | 3 +- TombEngine/Game/collision/collide_item.cpp | 34 ++++++++----------- TombEngine/Game/control/control.cpp | 17 +++++----- TombEngine/Game/gui.cpp | 2 +- TombEngine/Math/Objects/GameBoundingBox.cpp | 26 +++++++------- TombEngine/Math/Objects/GameBoundingBox.h | 6 ++-- .../Internal/TEN/Flow/FlowHandler.cpp | 10 +++--- .../Internal/TEN/Sound/SoundHandler.cpp | 12 +++---- TombEngine/Sound/sound.cpp | 17 ++++++---- TombEngine/Sound/sound.h | 2 +- TombEngine/Specific/level.cpp | 2 +- TombEngine/Specific/winmain.h | 2 +- 61 files changed, 201 insertions(+), 124 deletions(-) diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index ad3ca0b7d..27cd390a2 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/1 modules/Flow.html b/Documentation/doc/1 modules/Flow.html index 4b028009d..089568ddd 100644 --- a/Documentation/doc/1 modules/Flow.html +++ b/Documentation/doc/1 modules/Flow.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • @@ -162,6 +163,10 @@ scripts too.

    + + + + @@ -538,6 +543,27 @@ have an ID of 0, the second an ID of 1, and so on. + +
    + + GetNextLevel() +
    +
    + Returns the level that is about to load. If no new level is about to load, returns current level. + + + + +

    Returns:

    +
      + + Level + incoming new level or current level, if no new level is loading +
    + + + +
    diff --git a/Documentation/doc/1 modules/Input.html b/Documentation/doc/1 modules/Input.html index bf7b07fec..3cfee25d8 100644 --- a/Documentation/doc/1 modules/Input.html +++ b/Documentation/doc/1 modules/Input.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/1 modules/Inventory.html b/Documentation/doc/1 modules/Inventory.html index 32be88d47..15b6c8995 100644 --- a/Documentation/doc/1 modules/Inventory.html +++ b/Documentation/doc/1 modules/Inventory.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/1 modules/Logic.html b/Documentation/doc/1 modules/Logic.html index 500e3d67e..363e56e43 100644 --- a/Documentation/doc/1 modules/Logic.html +++ b/Documentation/doc/1 modules/Logic.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/1 modules/Objects.html b/Documentation/doc/1 modules/Objects.html index e616c0ac0..00de82c8f 100644 --- a/Documentation/doc/1 modules/Objects.html +++ b/Documentation/doc/1 modules/Objects.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/1 modules/Sound.html b/Documentation/doc/1 modules/Sound.html index cc291c4eb..29b5a1eee 100644 --- a/Documentation/doc/1 modules/Sound.html +++ b/Documentation/doc/1 modules/Sound.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • @@ -115,7 +116,7 @@
    - + @@ -187,7 +188,7 @@
    - SetAmbientTrack(name) + SetAmbientTrack(name, specifies)
    Set and play an ambient track @@ -200,6 +201,10 @@ string of track (without file extension) to play +
  • specifies + bool + whether ambient track should play from the beginning, or crossfade at a random position +
  • diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html index e570b6358..f7b0c3421 100644 --- a/Documentation/doc/1 modules/Strings.html +++ b/Documentation/doc/1 modules/Strings.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/1 modules/Util.html b/Documentation/doc/1 modules/Util.html index 319360268..ad9eb794a 100644 --- a/Documentation/doc/1 modules/Util.html +++ b/Documentation/doc/1 modules/Util.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/1 modules/View.html b/Documentation/doc/1 modules/View.html index b201316aa..4938f9ba7 100644 --- a/Documentation/doc/1 modules/View.html +++ b/Documentation/doc/1 modules/View.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.Animations.html b/Documentation/doc/2 classes/Flow.Animations.html index b48bf5fc2..ed8166075 100644 --- a/Documentation/doc/2 classes/Flow.Animations.html +++ b/Documentation/doc/2 classes/Flow.Animations.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.Fog.html b/Documentation/doc/2 classes/Flow.Fog.html index 11a25b76a..323781bc2 100644 --- a/Documentation/doc/2 classes/Flow.Fog.html +++ b/Documentation/doc/2 classes/Flow.Fog.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.InventoryItem.html b/Documentation/doc/2 classes/Flow.InventoryItem.html index a6677c716..b1391bb88 100644 --- a/Documentation/doc/2 classes/Flow.InventoryItem.html +++ b/Documentation/doc/2 classes/Flow.InventoryItem.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.LensFlare.html b/Documentation/doc/2 classes/Flow.LensFlare.html index 0af9dd01b..372fa43d6 100644 --- a/Documentation/doc/2 classes/Flow.LensFlare.html +++ b/Documentation/doc/2 classes/Flow.LensFlare.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html index eaffdc6fb..ee8a2fa5c 100644 --- a/Documentation/doc/2 classes/Flow.Level.html +++ b/Documentation/doc/2 classes/Flow.Level.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.Mirror.html b/Documentation/doc/2 classes/Flow.Mirror.html index b100c6f4b..7a9fbc626 100644 --- a/Documentation/doc/2 classes/Flow.Mirror.html +++ b/Documentation/doc/2 classes/Flow.Mirror.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.Settings.html b/Documentation/doc/2 classes/Flow.Settings.html index abe97b44c..9c54742f8 100644 --- a/Documentation/doc/2 classes/Flow.Settings.html +++ b/Documentation/doc/2 classes/Flow.Settings.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • @@ -116,7 +117,7 @@
    - +
    Sound.SoundTrackType Constants for the type of the audio tracks.
    Strings.DisplayStringOptionConstants for Display String Options.
    Util.LogLevel Constants for LogLevel IDs.Returns the level that the game control is running in that moment.
    GetNextLevel()Returns the level that is about to load.
    EndLevel([index][, startPos]) Finishes the current level, with optional level index and start position index provided.
    Play an audio track
    SetAmbientTrack(name)SetAmbientTrack(name, specifies) Set and play an ambient track
    fastReloadCan game utilize fast reload feature?Can the game utilize the fast reload feature?
    @@ -162,11 +163,11 @@ has an unrecoverable error, the game will close. fastReload
    - Can game utilize fast reload feature? -
    -When set to True, game will attempt to perform fast savegame reloading, if current level is the same as -level loaded from the savegame. It will not work if level timestamp or checksum has changed (i.e. level was -updated). If set to False this functionality is turned off. + Can the game utilize the fast reload feature? +
    + When set to true, the game will attempt to perform fast savegame reloading if current level is the same as + the level loaded from the savegame. It will not work if the level timestamp or checksum has changed + (i.e. level was updated). If set to false, this functionality is turned off. diff --git a/Documentation/doc/2 classes/Flow.SkyLayer.html b/Documentation/doc/2 classes/Flow.SkyLayer.html index 69565445a..755bae5f9 100644 --- a/Documentation/doc/2 classes/Flow.SkyLayer.html +++ b/Documentation/doc/2 classes/Flow.SkyLayer.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Flow.Starfield.html b/Documentation/doc/2 classes/Flow.Starfield.html index 199aa62ed..1fef2f963 100644 --- a/Documentation/doc/2 classes/Flow.Starfield.html +++ b/Documentation/doc/2 classes/Flow.Starfield.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.AIObject.html b/Documentation/doc/2 classes/Objects.AIObject.html index 308b5c37a..adb235784 100644 --- a/Documentation/doc/2 classes/Objects.AIObject.html +++ b/Documentation/doc/2 classes/Objects.AIObject.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.Camera.html b/Documentation/doc/2 classes/Objects.Camera.html index a7ec64c6b..07b63960b 100644 --- a/Documentation/doc/2 classes/Objects.Camera.html +++ b/Documentation/doc/2 classes/Objects.Camera.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.LaraObject.html b/Documentation/doc/2 classes/Objects.LaraObject.html index f153396da..af639b122 100644 --- a/Documentation/doc/2 classes/Objects.LaraObject.html +++ b/Documentation/doc/2 classes/Objects.LaraObject.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.Moveable.html b/Documentation/doc/2 classes/Objects.Moveable.html index fdd8a2559..54b4f76af 100644 --- a/Documentation/doc/2 classes/Objects.Moveable.html +++ b/Documentation/doc/2 classes/Objects.Moveable.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.Room.html b/Documentation/doc/2 classes/Objects.Room.html index 191bdc97d..397634734 100644 --- a/Documentation/doc/2 classes/Objects.Room.html +++ b/Documentation/doc/2 classes/Objects.Room.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.Sink.html b/Documentation/doc/2 classes/Objects.Sink.html index ceb1904e5..18db3c54a 100644 --- a/Documentation/doc/2 classes/Objects.Sink.html +++ b/Documentation/doc/2 classes/Objects.Sink.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.SoundSource.html b/Documentation/doc/2 classes/Objects.SoundSource.html index 78ca5f16c..f50314586 100644 --- a/Documentation/doc/2 classes/Objects.SoundSource.html +++ b/Documentation/doc/2 classes/Objects.SoundSource.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.Static.html b/Documentation/doc/2 classes/Objects.Static.html index 362584c3a..0c7d2299e 100644 --- a/Documentation/doc/2 classes/Objects.Static.html +++ b/Documentation/doc/2 classes/Objects.Static.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/Objects.Volume.html b/Documentation/doc/2 classes/Objects.Volume.html index 3970c11bf..ebbc6ff29 100644 --- a/Documentation/doc/2 classes/Objects.Volume.html +++ b/Documentation/doc/2 classes/Objects.Volume.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/2 classes/View.DisplaySprite.html b/Documentation/doc/2 classes/View.DisplaySprite.html index 97256f8a6..e81bb6ab8 100644 --- a/Documentation/doc/2 classes/View.DisplaySprite.html +++ b/Documentation/doc/2 classes/View.DisplaySprite.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/3 primitive classes/Color.html b/Documentation/doc/3 primitive classes/Color.html index 7021129ac..3c679941e 100644 --- a/Documentation/doc/3 primitive classes/Color.html +++ b/Documentation/doc/3 primitive classes/Color.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html index 79ee6746c..4d4435734 100644 --- a/Documentation/doc/3 primitive classes/Rotation.html +++ b/Documentation/doc/3 primitive classes/Rotation.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/3 primitive classes/Vec2.html b/Documentation/doc/3 primitive classes/Vec2.html index e42f03304..3b4e85b25 100644 --- a/Documentation/doc/3 primitive classes/Vec2.html +++ b/Documentation/doc/3 primitive classes/Vec2.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/3 primitive classes/Vec3.html b/Documentation/doc/3 primitive classes/Vec3.html index 9aeb59a86..c4daea21d 100644 --- a/Documentation/doc/3 primitive classes/Vec3.html +++ b/Documentation/doc/3 primitive classes/Vec3.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Effects.BlendID.html b/Documentation/doc/4 enums/Effects.BlendID.html index d3f8f0c97..a7334fce7 100644 --- a/Documentation/doc/4 enums/Effects.BlendID.html +++ b/Documentation/doc/4 enums/Effects.BlendID.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Effects.EffectID.html b/Documentation/doc/4 enums/Effects.EffectID.html index 9ad52e5b6..082554a40 100644 --- a/Documentation/doc/4 enums/Effects.EffectID.html +++ b/Documentation/doc/4 enums/Effects.EffectID.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Flow.GameStatus.html b/Documentation/doc/4 enums/Flow.GameStatus.html index 1575981e4..08b90c4e7 100644 --- a/Documentation/doc/4 enums/Flow.GameStatus.html +++ b/Documentation/doc/4 enums/Flow.GameStatus.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Input.ActionID.html b/Documentation/doc/4 enums/Input.ActionID.html index 1838d08c1..0ef756bbe 100644 --- a/Documentation/doc/4 enums/Input.ActionID.html +++ b/Documentation/doc/4 enums/Input.ActionID.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Objects.AmmoType.html b/Documentation/doc/4 enums/Objects.AmmoType.html index ab6edf13e..58da03ff1 100644 --- a/Documentation/doc/4 enums/Objects.AmmoType.html +++ b/Documentation/doc/4 enums/Objects.AmmoType.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Objects.MoveableStatus.html b/Documentation/doc/4 enums/Objects.MoveableStatus.html index 51fef38ca..f384a01f7 100644 --- a/Documentation/doc/4 enums/Objects.MoveableStatus.html +++ b/Documentation/doc/4 enums/Objects.MoveableStatus.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index eef909dc7..dc2cf1c90 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Objects.RoomFlagID.html b/Documentation/doc/4 enums/Objects.RoomFlagID.html index f5aec2102..2fe3978cb 100644 --- a/Documentation/doc/4 enums/Objects.RoomFlagID.html +++ b/Documentation/doc/4 enums/Objects.RoomFlagID.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Objects.RoomReverb.html b/Documentation/doc/4 enums/Objects.RoomReverb.html index 8af2f63c1..d34551d76 100644 --- a/Documentation/doc/4 enums/Objects.RoomReverb.html +++ b/Documentation/doc/4 enums/Objects.RoomReverb.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Sound.SoundTrackType.html b/Documentation/doc/4 enums/Sound.SoundTrackType.html index 71391280e..0c6d012f2 100644 --- a/Documentation/doc/4 enums/Sound.SoundTrackType.html +++ b/Documentation/doc/4 enums/Sound.SoundTrackType.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/Util.LogLevel.html b/Documentation/doc/4 enums/Util.LogLevel.html index 9a558d2f2..21f49d494 100644 --- a/Documentation/doc/4 enums/Util.LogLevel.html +++ b/Documentation/doc/4 enums/Util.LogLevel.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/View.AlignMode.html b/Documentation/doc/4 enums/View.AlignMode.html index 067a9b16a..433256659 100644 --- a/Documentation/doc/4 enums/View.AlignMode.html +++ b/Documentation/doc/4 enums/View.AlignMode.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/View.CameraType.html b/Documentation/doc/4 enums/View.CameraType.html index f46afe46e..e602f4836 100644 --- a/Documentation/doc/4 enums/View.CameraType.html +++ b/Documentation/doc/4 enums/View.CameraType.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/View.PostProcessMode.html b/Documentation/doc/4 enums/View.PostProcessMode.html index ae2a247f1..c439d8ab4 100644 --- a/Documentation/doc/4 enums/View.PostProcessMode.html +++ b/Documentation/doc/4 enums/View.PostProcessMode.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/4 enums/View.ScaleMode.html b/Documentation/doc/4 enums/View.ScaleMode.html index e51125c98..4a66eab89 100644 --- a/Documentation/doc/4 enums/View.ScaleMode.html +++ b/Documentation/doc/4 enums/View.ScaleMode.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/5 lua utility modules/EventSequence.html b/Documentation/doc/5 lua utility modules/EventSequence.html index 81b60a585..fc04f3040 100644 --- a/Documentation/doc/5 lua utility modules/EventSequence.html +++ b/Documentation/doc/5 lua utility modules/EventSequence.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/Documentation/doc/5 lua utility modules/Timer.html b/Documentation/doc/5 lua utility modules/Timer.html index 482ccacce..9af877068 100644 --- a/Documentation/doc/5 lua utility modules/Timer.html +++ b/Documentation/doc/5 lua utility modules/Timer.html @@ -3,7 +3,7 @@ - TombEngine 1.5 Lua API + TombEngine 1.6 (Developer Version) Lua API @@ -85,6 +85,7 @@
  • Objects.RoomFlagID
  • Objects.RoomReverb
  • Sound.SoundTrackType
  • +
  • Strings.DisplayStringOption
  • Util.LogLevel
  • View.AlignMode
  • View.CameraType
  • diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 4e7329f3d..c51a5c7d5 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -114,11 +114,11 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, auto convertedBounds = collidingBounds.ToBoundingOrientedBox(collidingItem.Pose); // Create conservative AABB for rough tests. - auto& collidingAABB = collidingBounds.ToConservativeBoundingBox(collidingItem.Pose); + auto collidingAabb = collidingBounds.ToConservativeBoundingBox(collidingItem.Pose); // Override extents if specified. if (customRadius > 0.0f) - collidingAABB = BoundingBox(collidingItem.Pose.Position.ToVector3(), Vector3(customRadius)); + collidingAabb = BoundingBox(collidingItem.Pose.Position.ToVector3(), Vector3(customRadius)); // Run through neighboring rooms. const auto& room = g_Level.Rooms[collidingItem.RoomNumber]; @@ -154,7 +154,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, if (item.Index == collidingItem.Index || item.Flags & IFLAG_KILLED || item.MeshBits == NO_JOINT_BITS) continue; - // Ignore non-Lara items without collision or draw routines or with collidable flag unset. + // Ignore non-collidable non-player. if (!item.IsLara() && (!item.Collidable || object.drawRoutine == nullptr || object.collision == nullptr)) continue; @@ -167,21 +167,19 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, if (dist > COLLISION_CHECK_DISTANCE) continue; - const auto& bounds = GetBestFrame(item).BoundingBox; - // If item bounding box extents is below tolerance threshold, discard object. + const auto& bounds = GetBestFrame(item).BoundingBox; if (bounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) continue; // Test conservative AABB intersection. - const auto& AABB = bounds.ToConservativeBoundingBox(item.Pose); - if (!AABB.Intersects(collidingAABB)) + auto aabb = bounds.ToConservativeBoundingBox(item.Pose); + if (!aabb.Intersects(collidingAabb)) continue; - auto box = bounds.ToBoundingOrientedBox(item.Pose); - - // Test accurate box intersection. - if (box.Intersects(convertedBounds)) + // Test accurate OBB intersection. + auto obb = bounds.ToBoundingOrientedBox(item.Pose); + if (obb.Intersects(convertedBounds)) collObjects.Items.push_back(&item); } while (itemNumber != NO_VALUE); @@ -203,21 +201,19 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, if (dist > COLLISION_CHECK_DISTANCE) continue; - const auto& bounds = GetBoundsAccurate(staticObj, false); - // Skip if either bounding box has any zero extent (not a collidable volume). + const auto& bounds = GetBoundsAccurate(staticObj, false); if (bounds.GetExtents().Length() <= COLLIDABLE_BOUNDS_THRESHOLD) continue; // Test conservative AABB intersection. - const auto& AABB = bounds.ToConservativeBoundingBox(staticObj.pos); - if (!AABB.Intersects(collidingAABB)) + auto aabb = bounds.ToConservativeBoundingBox(staticObj.pos); + if (!aabb.Intersects(collidingAabb)) continue; - auto box = bounds.ToBoundingOrientedBox(staticObj.pos.Position); - - // Test accurate box intersection. - if (box.Intersects(convertedBounds)) + // Test accurate OBB intersection. + auto obb = bounds.ToBoundingOrientedBox(staticObj.pos.Position); + if (obb.Intersects(convertedBounds)) collObjects.Statics.push_back(&staticObj); } } diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 5ad139c51..78baf46fb 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -223,23 +223,23 @@ GameStatus ControlPhase(bool insideMenu) PlaySoundSources(); Sound_UpdateScene(); - auto result = GameStatus::Normal; + auto gameStatus = GameStatus::Normal; if (!insideMenu) { // Handle inventory, pause, load, save screens. - result = HandleMenuCalls(isTitle); + gameStatus = HandleMenuCalls(isTitle); // Handle global input events. - if (result == GameStatus::Normal) - result = HandleGlobalInputEvents(isTitle); + if (gameStatus == GameStatus::Normal) + gameStatus = HandleGlobalInputEvents(isTitle); } - if (result != GameStatus::Normal) + if (gameStatus != GameStatus::Normal) { // Call post-loop callbacks last time and end level. g_GameScript->OnLoop(DELTA_TIME, true); - g_GameScript->OnEnd(result); + g_GameScript->OnEnd(gameStatus); HandleAllGlobalEvents(EventType::End, (Activator)LaraItem->Index); } else @@ -260,7 +260,7 @@ GameStatus ControlPhase(bool insideMenu) auto time2 = std::chrono::high_resolution_clock::now(); ControlPhaseTime = (std::chrono::duration_cast(time2 - time1)).count() / 1000000; - return result; + return gameStatus; } unsigned CALLBACK GameMain(void *) @@ -594,7 +594,8 @@ GameStatus DoGameLoop(int levelIndex) void EndGameLoop(int levelIndex, GameStatus reason) { - g_Renderer.DumpGameScene(); // Save last screenshot for loading screen. + // Save last screenshot for loading screen. + g_Renderer.DumpGameScene(); SaveGame::SaveHub(levelIndex); DeInitializeScripting(levelIndex, reason); diff --git a/TombEngine/Game/gui.cpp b/TombEngine/Game/gui.cpp index 1fd850f47..dccf2a53e 100644 --- a/TombEngine/Game/gui.cpp +++ b/TombEngine/Game/gui.cpp @@ -3506,7 +3506,7 @@ namespace TEN::Gui LoadResult GuiController::DoLoad() { - constexpr int DEATH_NO_INPUT_LOAD_TIMEOUT = FPS / 2; + constexpr auto DEATH_NO_INPUT_LOAD_TIMEOUT = FPS / 2; bool canLoop = g_Configuration.MenuOptionLoopingMode == MenuOptionLoopingMode::SaveLoadOnly || g_Configuration.MenuOptionLoopingMode == MenuOptionLoopingMode::AllMenus; diff --git a/TombEngine/Math/Objects/GameBoundingBox.cpp b/TombEngine/Math/Objects/GameBoundingBox.cpp index b764b0a79..60177a6da 100644 --- a/TombEngine/Math/Objects/GameBoundingBox.cpp +++ b/TombEngine/Math/Objects/GameBoundingBox.cpp @@ -85,15 +85,18 @@ Z2 = (int)round(boxMax.z); } + BoundingSphere GameBoundingBox::ToLocalBoundingSphere() const + { + return BoundingSphere(GetCenter(), GetExtents().Length()); + } + BoundingBox GameBoundingBox::ToConservativeBoundingBox(const Pose& pose) const { - // Compute extents. + // Calculate conservative radius in XZ plane for Y-axis rotation. auto extents = GetExtents(); - - // Calculate conservative radius in the XZ plane for Y-axis rotation. float xzRadius = sqrt(extents.x * extents.x + extents.z * extents.z); - // Extents remain unchanged along the Y-axis. + // Extents remain unchanged along Y-axis. auto conservativeExtents = Vector3(xzRadius, extents.y, xzRadius); // Manual 2D rotation for Y-axis only. @@ -101,22 +104,17 @@ float cos = phd_cos(pose.Orientation.y); float sin = phd_sin(pose.Orientation.y); - // Rotate the centerOffset in the XZ plane (ignore Y rotation for height). - float rotatedX = cos * center.x + sin * center.z; - float rotatedZ = -sin * center.x + cos * center.z; + // Rotate centerOffset in XZ plane (ignore Y rotation for height). + float rotatedX = (cos * center.x) + (sin * center.z); + float rotatedZ = -(sin * center.x) + (cos * center.z); - // The center of the bounding box is now the position plus the rotated offset. + // Box center is now position plus rotated offset. auto rotatedCenter = pose.Position.ToVector3() + Vector3(rotatedX, center.y, rotatedZ); - // Build and return the conservative AABB. + // Build and return conservative AABB. return BoundingBox(rotatedCenter, conservativeExtents); } - BoundingSphere GameBoundingBox::ToLocalBoundingSphere() const - { - return BoundingSphere(GetCenter(), GetExtents().Length()); - } - BoundingOrientedBox GameBoundingBox::ToBoundingOrientedBox(const Pose& pose) const { return ToBoundingOrientedBox(pose.Position.ToVector3(), pose.Orientation.ToQuaternion()); diff --git a/TombEngine/Math/Objects/GameBoundingBox.h b/TombEngine/Math/Objects/GameBoundingBox.h index be28eeabe..cba6910cd 100644 --- a/TombEngine/Math/Objects/GameBoundingBox.h +++ b/TombEngine/Math/Objects/GameBoundingBox.h @@ -45,10 +45,10 @@ struct ObjectInfo; // Converters - BoundingSphere ToLocalBoundingSphere() const; + BoundingSphere ToLocalBoundingSphere() const; + BoundingBox ToConservativeBoundingBox(const Pose& pose) const; // TODO: item.GetAabb() method. BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const; - BoundingOrientedBox ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const; - BoundingBox ToConservativeBoundingBox(const Pose& pose) const; + BoundingOrientedBox ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const; // TODO: item.GetObb() method. // Operators diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp index 0d15f2d57..62a19ecc5 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp @@ -125,10 +125,9 @@ have an ID of 0, the second an ID of 1, and so on. */ tableFlow.set_function(ScriptReserved_GetCurrentLevel, &FlowHandler::GetCurrentLevel, this); -/*** Returns the level that is about to load. If no new level is about to load, returns current level. -@function GetNextLevel -@treturn Flow.Level incoming new level or current level, if no new level is loading -*/ + /// Returns the level that is about to load. If no new level is about to load, returns current level. + // @function GetNextLevel + // @treturn Flow.Level incoming new level or current level, if no new level is loading tableFlow.set_function(ScriptReserved_GetNextLevel, &FlowHandler::GetNextLevel, this); /*** @@ -410,7 +409,8 @@ Level* FlowHandler::GetNextLevel() if (NextLevel == CurrentLevel) return Levels[CurrentLevel]; - return Levels[abs(NextLevel)]; // Negative value indicates incoming savegame. + // NOTE: Negative value indicates incoming savegame. + return Levels[abs(NextLevel)]; } int FlowHandler::GetNumLevels() const diff --git a/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp b/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp index 580a0997b..e0687e19f 100644 --- a/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp @@ -24,14 +24,14 @@ namespace TEN::Scripting::Sound PlaySoundTrack(trackName, playMode); } - ///Set and play an ambient track - //@function SetAmbientTrack - //@tparam string name of track (without file extension) to play - //@tparam bool specifies whether ambient track should play from the beginning, or crossfade at a random position + /// Set and play an ambient track + // @function SetAmbientTrack + // @tparam string name of track (without file extension) to play + // @tparam bool specifies whether ambient track should play from the beginning, or crossfade at a random position static void SetAmbientTrack(const std::string& trackName, TypeOrNil fromTheBeginning) { - std::optional position = USE_IF_HAVE(bool, fromTheBeginning, false) ? std::optional(0) : std::nullopt; - PlaySoundTrack(trackName, SoundTrackType::BGM, position, position.has_value() ? SOUND_XFADETIME_ONESHOT : SOUND_XFADETIME_BGM); + auto pos = USE_IF_HAVE(bool, fromTheBeginning, false) ? std::optional(0) : std::optional(); + PlaySoundTrack(trackName, SoundTrackType::BGM, pos, pos.has_value() ? SOUND_XFADETIME_ONESHOT : SOUND_XFADETIME_BGM); } ///Stop any audio tracks currently playing diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index 54fdf2d3c..49fffcb50 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -490,7 +490,7 @@ std::optional GetCurrentSubtitle() return std::nullopt; } -void PlaySoundTrack(const std::string& track, SoundTrackType mode, std::optional position, int forceFadeInTime) +void PlaySoundTrack(const std::string& track, SoundTrackType mode, std::optional pos, int forceFadeInTime) { if (!g_Configuration.EnableSound) return; @@ -505,10 +505,11 @@ void PlaySoundTrack(const std::string& track, SoundTrackType mode, std::optional bool channelActive = BASS_ChannelIsActive(SoundtrackSlot[(int)mode].Channel); if (channelActive && SoundtrackSlot[(int)mode].Track.compare(track) == 0) { - // Same track is incoming with different playhead, set it to a new position. + // Same track is incoming with different playhead; set it to new position. auto stream = SoundtrackSlot[(int)mode].Channel; - if (position.has_value() && (BASS_ChannelGetLength(stream, BASS_POS_BYTE) > position.value())) - BASS_ChannelSetPosition(stream, position.value(), BASS_POS_BYTE); + if (pos.has_value() && BASS_ChannelGetLength(stream, BASS_POS_BYTE) > pos.value()) + BASS_ChannelSetPosition(stream, pos.value(), BASS_POS_BYTE); + return; } @@ -573,20 +574,22 @@ void PlaySoundTrack(const std::string& track, SoundTrackType mode, std::optional // Shuffle... // Only activates if no custom position is passed as argument. - if (!position.has_value()) + if (!pos.has_value()) { QWORD newPos = BASS_ChannelGetLength(stream, BASS_POS_BYTE) * (static_cast(GetRandomControl()) / static_cast(RAND_MAX)); BASS_ChannelSetPosition(stream, newPos, BASS_POS_BYTE); } } else + { BASS_ChannelSetAttribute(stream, BASS_ATTRIB_VOL, masterVolume); + } BASS_ChannelPlay(stream, false); // Try to restore position, if specified. - if (position.has_value() && (BASS_ChannelGetLength(stream, BASS_POS_BYTE) > position.value())) - BASS_ChannelSetPosition(stream, position.value(), BASS_POS_BYTE); + if (pos.has_value() && BASS_ChannelGetLength(stream, BASS_POS_BYTE) > pos.value()) + BASS_ChannelSetPosition(stream, pos.value(), BASS_POS_BYTE); if (Sound_CheckBASSError("Playing soundtrack '%s'", true, fullTrackName.filename().string().c_str())) return; diff --git a/TombEngine/Sound/sound.h b/TombEngine/Sound/sound.h index 7de2fa084..46133732a 100644 --- a/TombEngine/Sound/sound.h +++ b/TombEngine/Sound/sound.h @@ -165,7 +165,7 @@ void SayNo(); void PlaySoundSources(); int GetShatterSound(int shatterID); -void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, std::optional position = std::nullopt, int forceFadeInTime = 0); +void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, std::optional pos = std::nullopt, int forceFadeInTime = 0); void PlaySoundTrack(const std::string& trackName, short mask = 0); void PlaySoundTrack(int index, short mask = 0); void StopSoundTrack(SoundTrackType mode, int fadeoutTime); diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index a9fb98d5d..5286e2bb0 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1549,7 +1549,7 @@ bool LoadLevelFile(int levelIndex) auto timestamp = std::filesystem::last_write_time(levelPath); bool fastReload = (g_GameFlow->GetSettings()->FastReload && levelIndex == CurrentLevel && timestamp == LastLevelTimestamp && levelPath == LastLevelFilePath); - // If fast reload is in action, don't draw loading screen, and instead draw last game frame. + // If fast reload is in action, draw last game frame instead of loading screen. auto loadingScreenPath = TEN::Utils::ToWString(assetDir + level.LoadScreenFileName); g_Renderer.SetLoadingScreen(fastReload ? std::wstring{} : loadingScreenPath); diff --git a/TombEngine/Specific/winmain.h b/TombEngine/Specific/winmain.h index 9c89c97cb..85a5be48c 100644 --- a/TombEngine/Specific/winmain.h +++ b/TombEngine/Specific/winmain.h @@ -43,4 +43,4 @@ Vector2i GetScreenResolution(); std::vector GetAllSupportedScreenResolutions(); int GetCurrentScreenRefreshRate(); -bool GenerateDummyLevel(const std::string& levelPath); \ No newline at end of file +bool GenerateDummyLevel(const std::string& levelPath); From ef19187f8364cdbd09e7eefe3daaae887b211609 Mon Sep 17 00:00:00 2001 From: Sezz Date: Sat, 16 Nov 2024 22:19:32 +1100 Subject: [PATCH 063/112] Formatting --- TombEngine/Game/camera.cpp | 3 ++- TombEngine/Renderer/RendererDrawMenu.cpp | 2 +- TombEngine/Resources.rc | 6 +++--- TombEngine/Specific/level.cpp | 1 - TombEngine/Specific/winmain.cpp | 26 ++++++++++++------------ TombEngine/TombEngine.vcxproj | 6 +++--- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index f63704bb5..bacba41b5 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -1268,7 +1268,8 @@ void CalculateCamera(const CollisionInfo& coll) { Camera.fixedCamera = false; - if (Camera.speed != 1 && !Lara.Control.Look.IsUsingBinoculars && Camera.oldType != CameraType::Look) + if (Camera.speed != 1 && Camera.oldType != CameraType::Look && + !Lara.Control.Look.IsUsingBinoculars) { if (TargetSnaps <= 8) { diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index a398de129..3b0dd0e6a 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -1,7 +1,6 @@ #include "framework.h" #include "Renderer/Renderer.h" -#include "version.h" #include "Game/animation.h" #include "Game/control/control.h" #include "Game/control/volume.h" @@ -16,6 +15,7 @@ #include "Specific/level.h" #include "Specific/trutils.h" #include "Specific/winmain.h" +#include "Version.h" using namespace TEN::Gui; using namespace TEN::Hud; diff --git a/TombEngine/Resources.rc b/TombEngine/Resources.rc index 10a5632db..0fa0b9c9e 100644 --- a/TombEngine/Resources.rc +++ b/TombEngine/Resources.rc @@ -3,7 +3,7 @@ #pragma code_page(65001) #include "resource.h" -#include "version.h" +#include "Version.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -65,8 +65,8 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "Resources\\ten.ico" -IDR_TITLELEVEL BIN "Resources\\title.bin" +IDI_ICON1 ICON "Resources\\Ten.ico" +IDR_TITLELEVEL BIN "Resources\\Title.bin" ///////////////////////////////////////////////////////////////////////////// diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 5286e2bb0..d5975ddd8 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1517,7 +1517,6 @@ void LoadBoxes() } } - bool LoadLevelFile(int levelIndex) { const auto& level = *g_GameFlow->GetLevel(levelIndex); diff --git a/TombEngine/Specific/winmain.cpp b/TombEngine/Specific/winmain.cpp index 7ed9f2809..d6897aa8a 100644 --- a/TombEngine/Specific/winmain.cpp +++ b/TombEngine/Specific/winmain.cpp @@ -7,10 +7,10 @@ #include #include -#include "resource.h" #include "Game/control/control.h" #include "Game/savegame.h" #include "Renderer/Renderer.h" +#include "resource.h" #include "Sound/sound.h" #include "Specific/level.h" #include "Specific/configuration.h" @@ -131,7 +131,7 @@ void DisableDpiAwareness() bool GenerateDummyLevel(const std::string& levelPath) { - // Try to load the embedded resource "data.bin" + // Try loading embedded resource "data.bin" HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(IDR_TITLELEVEL), "BIN"); if (hResource == NULL) { @@ -139,7 +139,7 @@ bool GenerateDummyLevel(const std::string& levelPath) return false; } - // Load the resource into memory + // Load resource into memory. HGLOBAL hGlobal = LoadResource(NULL, hResource); if (hGlobal == NULL) { @@ -147,18 +147,18 @@ bool GenerateDummyLevel(const std::string& levelPath) return false; } - // Lock the resource to get a pointer to the data + // Lock resource to get data pointer. void* pData = LockResource(hGlobal); DWORD dwSize = SizeofResource(NULL, hResource); - // Write the resource data to the file + // Write resource data to file. try { - std::filesystem::path dir = std::filesystem::path(levelPath).parent_path(); + auto dir = std::filesystem::path(levelPath).parent_path(); if (!dir.empty()) std::filesystem::create_directories(dir); - std::ofstream outFile(levelPath, std::ios::binary); + auto outFile = std::ofstream(levelPath, std::ios::binary); if (!outFile) throw std::ios_base::failure("Failed to create title level file."); @@ -179,18 +179,18 @@ bool GenerateDummyLevel(const std::string& levelPath) void WinProcMsg() { - MSG Msg; + MSG msg; do { - GetMessage(&Msg, 0, 0, 0); - if (!TranslateAccelerator(WindowsHandle, hAccTable, &Msg)) + GetMessage(&msg, 0, 0, 0); + if (!TranslateAccelerator(WindowsHandle, hAccTable, &msg)) { - TranslateMessage(&Msg); - DispatchMessage(&Msg); + TranslateMessage(&msg); + DispatchMessage(&msg); } } - while (!ThreadEnded && Msg.message != WM_QUIT); + while (!ThreadEnded && msg.message != WM_QUIT); } void CALLBACK HandleWmCommand(unsigned short wParam) diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index c054f00b8..d96d876a9 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -957,7 +957,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. - + @@ -1364,7 +1364,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. - + @@ -1633,7 +1633,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. - + From 26adcee7b1840f1c1ef92cd9b6b787daa7653e1f Mon Sep 17 00:00:00 2001 From: Sezz Date: Sun, 17 Nov 2024 03:33:14 +1100 Subject: [PATCH 064/112] Slight floordata optimisation --- TombEngine/Game/collision/floordata.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/TombEngine/Game/collision/floordata.cpp b/TombEngine/Game/collision/floordata.cpp index 6b9d04e75..d38ccf230 100644 --- a/TombEngine/Game/collision/floordata.cpp +++ b/TombEngine/Game/collision/floordata.cpp @@ -40,10 +40,17 @@ int FloorInfo::GetSurfaceTriangleID(int x, int z, bool isFloor) const { constexpr auto TRI_ID_0 = 0; constexpr auto TRI_ID_1 = 1; + + static const auto ROT_MATRIX_0 = Matrix::CreateRotationZ(TO_RAD(SectorSurfaceData::SPLIT_ANGLE_0)); + static const auto ROT_MATRIX_1 = Matrix::CreateRotationZ(TO_RAD(SectorSurfaceData::SPLIT_ANGLE_1)); + // Get matrix. + const auto& rotMatrix = isFloor ? + ((FloorSurface.SplitAngle == SectorSurfaceData::SPLIT_ANGLE_0) ? ROT_MATRIX_0 : ROT_MATRIX_1) : + ((CeilingSurface.SplitAngle == SectorSurfaceData::SPLIT_ANGLE_0) ? ROT_MATRIX_0 : ROT_MATRIX_1); + // Calculate bias. auto sectorPoint = GetSectorPoint(x, z).ToVector2(); - auto rotMatrix = Matrix::CreateRotationZ(TO_RAD(isFloor ? FloorSurface.SplitAngle : CeilingSurface.SplitAngle)); float bias = Vector2::Transform(sectorPoint, rotMatrix).x; // Return triangle ID according to bias. From 540150a41afa2e8cd9f825784ffd107a117bcd2a Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:41:42 +0100 Subject: [PATCH 065/112] Revert "Fixed #1483" This reverts commit 08137298cc8608d4cd5f641f3ffa1ce1b75ef090. --- TombEngine/Game/room.cpp | 3 --- TombEngine/Renderer/Renderer.h | 3 +-- TombEngine/Renderer/RendererCompatibility.cpp | 21 +++++++------------ TombEngine/Specific/level.cpp | 3 +-- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index f26e753f6..c2e4dc0eb 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -136,9 +136,6 @@ void ResetRoomData() sector.Stopper = false; sector.BridgeItemNumbers.clear(); } - - for (auto& mesh : room.mesh) - mesh.Dirty = true; } // Make sure no pathfinding boxes are blocked (either by doors or by other door-like objects). diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index c88396b82..47f07698b 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -597,8 +597,7 @@ namespace TEN::Renderer void Render(float interpFactor); void RenderTitle(float interpFactor); void Lock(); - void ResetData(); - bool PrepareData(); + bool PrepareDataForTheRenderer(); void UpdateCameraMatrices(CAMERA_INFO* cam, float farView); void RenderSimpleSceneToParaboloid(RenderTarget2D* renderTarget, Vector3 position, int emisphere); void DumpGameScene(); diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 850938b2b..8a3cada26 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -20,18 +20,7 @@ namespace TEN::Renderer { template class VertexBuffer; - void Renderer::ResetData() - { - int allocatedItemSize = (int)g_Level.Items.size() + MAX_SPAWNED_ITEM_COUNT; - - auto item = RendererItem(); - _items = std::vector(allocatedItemSize, item); - - auto effect = RendererEffect(); - _effects = std::vector(allocatedItemSize, effect); - } - - bool Renderer::PrepareData() + bool Renderer::PrepareDataForTheRenderer() { TENLog("Preparing renderer...", LogLevel::Info); @@ -46,7 +35,13 @@ namespace TEN::Renderer _meshes.clear(); - ResetData(); + int allocatedItemSize = (int)g_Level.Items.size() + MAX_SPAWNED_ITEM_COUNT; + + auto item = RendererItem(); + _items = std::vector(allocatedItemSize, item); + + auto effect = RendererEffect(); + _effects = std::vector(allocatedItemSize, effect); TENLog("Allocated renderer object memory.", LogLevel::Info); diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index d5975ddd8..b09781821 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -1395,13 +1395,12 @@ bool LoadLevel(const std::string& path, bool partial) if (!partial) { - g_Renderer.PrepareData(); + g_Renderer.PrepareDataForTheRenderer(); SetScreenFadeOut(FADE_SCREEN_SPEED, true); StopSoundTracks(SOUND_XFADETIME_BGM_START); } else { - g_Renderer.ResetData(); SetScreenFadeIn(FADE_SCREEN_SPEED, true); StopSoundTracks(SOUND_XFADETIME_LEVELJUMP); } From 656158874c8b2ffd482a4a182339637011f3eb75 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:42:02 +0100 Subject: [PATCH 066/112] Another attempt at fixing #1483 --- TombEngine/Game/savegame.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index fc0ac9907..695f5dfe0 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -2229,6 +2229,8 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode) room->mesh[number].flags = staticMesh->flags(); room->mesh[number].HitPoints = staticMesh->hit_points(); + + room->mesh[number].Dirty = true; if (!room->mesh[number].flags) { From ec170b8aeeb6ced8a38a25484a90b1a96cd741ab Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:00:43 +0100 Subject: [PATCH 067/112] Fixed pickup trigger for search object 4 as well --- TombEngine/Game/pickup/pickup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/Game/pickup/pickup.cpp b/TombEngine/Game/pickup/pickup.cpp index 7eb788e75..e602a0590 100644 --- a/TombEngine/Game/pickup/pickup.cpp +++ b/TombEngine/Game/pickup/pickup.cpp @@ -1270,7 +1270,7 @@ void SearchObjectControl(short itemNumber) { PickedUpObject(*item2); g_Hud.PickupSummary.AddDisplayPickup(*item2); - KillItem(item->ItemFlags[1]); + HideOrDisablePickup(*item2); } else { From ab979b8487058d238db2fe8667c4903107ff57d9 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 17 Nov 2024 12:43:52 +0100 Subject: [PATCH 068/112] Fix event status not saving --- TombEngine/Game/savegame.cpp | 26 ++++++++++++++++--- .../flatbuffers/ten_savegame_generated.h | 20 +++++++++++++- .../Specific/savegame/schema/ten_savegame.fbs | 1 + 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index 695f5dfe0..18de8ba3a 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -1075,17 +1075,23 @@ const std::vector SaveGame::Build() std::vector> globalEventSets{}; for (int j = 0; j < g_Level.GlobalEventSets.size(); j++) { + std::vector statuses = {}; std::vector callCounters = {}; for (int k = 0; k < g_Level.GlobalEventSets[j].Events.size(); k++) + { + statuses.push_back(g_Level.GlobalEventSets[j].Events[k].Enabled); callCounters.push_back(g_Level.GlobalEventSets[j].Events[k].CallCounter); + } - auto vec = fbb.CreateVector(callCounters); + auto vecStatuses = fbb.CreateVector(statuses); + auto vecCounters = fbb.CreateVector(callCounters); Save::EventSetBuilder eventSet{ fbb }; eventSet.add_index(j); - eventSet.add_call_counters(vec); + eventSet.add_statuses(vecStatuses); + eventSet.add_call_counters(vecCounters); globalEventSets.push_back(eventSet.Finish()); } @@ -1095,17 +1101,23 @@ const std::vector SaveGame::Build() std::vector> volumeEventSets{}; for (int j = 0; j < g_Level.VolumeEventSets.size(); j++) { + std::vector statuses = {}; std::vector callCounters = {}; for (int k = 0; k < g_Level.VolumeEventSets[j].Events.size(); k++) + { + statuses.push_back(g_Level.VolumeEventSets[j].Events[k].Enabled); callCounters.push_back(g_Level.VolumeEventSets[j].Events[k].CallCounter); + } - auto vec = fbb.CreateVector(callCounters); + auto vecStatuses = fbb.CreateVector(statuses); + auto vecCounters = fbb.CreateVector(callCounters); Save::EventSetBuilder eventSet{ fbb }; eventSet.add_index(j); - eventSet.add_call_counters(vec); + eventSet.add_statuses(vecStatuses); + eventSet.add_call_counters(vecCounters); volumeEventSets.push_back(eventSet.Finish()); } @@ -1682,7 +1694,10 @@ static void ParseLua(const Save::SaveGame* s) { auto setSaved = s->volume_event_sets()->Get(i); for (int j = 0; j < setSaved->call_counters()->size(); ++j) + { + g_Level.VolumeEventSets[setSaved->index()].Events[j].Enabled = setSaved->statuses()->Get(j); g_Level.VolumeEventSets[setSaved->index()].Events[j].CallCounter = setSaved->call_counters()->Get(j); + } } } @@ -1692,7 +1707,10 @@ static void ParseLua(const Save::SaveGame* s) { auto setSaved = s->global_event_sets()->Get(i); for (int j = 0; j < setSaved->call_counters()->size(); ++j) + { + g_Level.GlobalEventSets[setSaved->index()].Events[j].Enabled = setSaved->statuses()->Get(j); g_Level.GlobalEventSets[setSaved->index()].Events[j].CallCounter = setSaved->call_counters()->Get(j); + } } } diff --git a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h index 2cb2b7ffc..4a319c1e0 100644 --- a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h +++ b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h @@ -5761,6 +5761,7 @@ flatbuffers::Offset CreatePendulum(flatbuffers::FlatBufferBuilder &_fb struct EventSetT : public flatbuffers::NativeTable { typedef EventSet TableType; int32_t index = 0; + std::vector statuses{}; std::vector call_counters{}; }; @@ -5770,17 +5771,23 @@ struct EventSet FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct Traits; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_INDEX = 4, - VT_CALL_COUNTERS = 6 + VT_STATUSES = 6, + VT_CALL_COUNTERS = 8 }; int32_t index() const { return GetField(VT_INDEX, 0); } + const flatbuffers::Vector *statuses() const { + return GetPointer *>(VT_STATUSES); + } const flatbuffers::Vector *call_counters() const { return GetPointer *>(VT_CALL_COUNTERS); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_INDEX) && + VerifyOffset(verifier, VT_STATUSES) && + verifier.VerifyVector(statuses()) && VerifyOffset(verifier, VT_CALL_COUNTERS) && verifier.VerifyVector(call_counters()) && verifier.EndTable(); @@ -5797,6 +5804,9 @@ struct EventSetBuilder { void add_index(int32_t index) { fbb_.AddElement(EventSet::VT_INDEX, index, 0); } + void add_statuses(flatbuffers::Offset> statuses) { + fbb_.AddOffset(EventSet::VT_STATUSES, statuses); + } void add_call_counters(flatbuffers::Offset> call_counters) { fbb_.AddOffset(EventSet::VT_CALL_COUNTERS, call_counters); } @@ -5814,9 +5824,11 @@ struct EventSetBuilder { inline flatbuffers::Offset CreateEventSet( flatbuffers::FlatBufferBuilder &_fbb, int32_t index = 0, + flatbuffers::Offset> statuses = 0, flatbuffers::Offset> call_counters = 0) { EventSetBuilder builder_(_fbb); builder_.add_call_counters(call_counters); + builder_.add_statuses(statuses); builder_.add_index(index); return builder_.Finish(); } @@ -5829,11 +5841,14 @@ struct EventSet::Traits { inline flatbuffers::Offset CreateEventSetDirect( flatbuffers::FlatBufferBuilder &_fbb, int32_t index = 0, + const std::vector *statuses = nullptr, const std::vector *call_counters = nullptr) { + auto statuses__ = statuses ? _fbb.CreateVector(*statuses) : 0; auto call_counters__ = call_counters ? _fbb.CreateVector(*call_counters) : 0; return TEN::Save::CreateEventSet( _fbb, index, + statuses__, call_counters__); } @@ -9802,6 +9817,7 @@ inline void EventSet::UnPackTo(EventSetT *_o, const flatbuffers::resolver_functi (void)_o; (void)_resolver; { auto _e = index(); _o->index = _e; } + { auto _e = statuses(); if (_e) { _o->statuses.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->statuses[_i] = _e->Get(_i) != 0; } } } { auto _e = call_counters(); if (_e) { _o->call_counters.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->call_counters[_i] = _e->Get(_i); } } } } @@ -9814,10 +9830,12 @@ inline flatbuffers::Offset CreateEventSet(flatbuffers::FlatBufferBuild (void)_o; struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const EventSetT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; auto _index = _o->index; + auto _statuses = _fbb.CreateVector(_o->statuses); auto _call_counters = _fbb.CreateVector(_o->call_counters); return TEN::Save::CreateEventSet( _fbb, _index, + _statuses, _call_counters); } diff --git a/TombEngine/Specific/savegame/schema/ten_savegame.fbs b/TombEngine/Specific/savegame/schema/ten_savegame.fbs index 634b2c9f8..730c2668f 100644 --- a/TombEngine/Specific/savegame/schema/ten_savegame.fbs +++ b/TombEngine/Specific/savegame/schema/ten_savegame.fbs @@ -406,6 +406,7 @@ table Pendulum { table EventSet { index: int32; + statuses: [bool]; call_counters: [int32]; } From c380a74bb0460b6f16812aa7f454527501358761 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 18 Nov 2024 01:23:34 +0100 Subject: [PATCH 069/112] Fixed falling through tilted bridges --- TombEngine/Game/collision/floordata.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/TombEngine/Game/collision/floordata.cpp b/TombEngine/Game/collision/floordata.cpp index d38ccf230..a50f8fa29 100644 --- a/TombEngine/Game/collision/floordata.cpp +++ b/TombEngine/Game/collision/floordata.cpp @@ -230,21 +230,16 @@ int FloorInfo::GetSurfaceHeight(const Vector3i& pos, bool isFloor) const auto bridgeCeilingHeight = bridge.GetCeilingHeight(bridgeItem, pos); // 2.1) Get bridge surface height. - auto bridgeSurfaceHeight = isFloor ? bridge.GetFloorHeight(bridgeItem, pos) : bridge.GetCeilingHeight(bridgeItem, pos); + auto bridgeSurfaceHeight = isFloor ? bridgeFloorHeight : bridgeCeilingHeight; if (!bridgeSurfaceHeight.has_value()) continue; - // Use bridge midpoint to decide whether to return bridge height or room height, in case probe point - // is located within the bridge. Without it, dynamic bridges may fail while Lara is standing on them. - int thickness = bridge.GetCeilingBorder(bridgeItem) - bridge.GetFloorBorder(bridgeItem); - int midpoint = bridgeItem.Pose.Position.y + thickness / 2; - // 2.2) Track closest floor or ceiling height. if (isFloor) { // Test if bridge floor height is closer. - if (midpoint >= pos.y && // Bridge midpoint is below position. + if (*bridgeCeilingHeight >= pos.y && // Bridge midpoint is below position. *bridgeSurfaceHeight < floorHeight && // Bridge floor height is above current closest floor height. *bridgeSurfaceHeight >= ceilingHeight) // Bridge ceiling height is below sector ceiling height. { @@ -254,7 +249,7 @@ int FloorInfo::GetSurfaceHeight(const Vector3i& pos, bool isFloor) const else { // Test if bridge ceiling height is closer. - if (midpoint <= pos.y && // Bridge midpoint is above position. + if (*bridgeFloorHeight <= pos.y && // Bridge midpoint is above position. *bridgeSurfaceHeight > ceilingHeight && // Bridge ceiling height is below current closest ceiling height. *bridgeSurfaceHeight <= floorHeight) // Bridge floor height is above sector floor height. { From b41d591d93003fec9715c05b89e58a1ce1ee7623 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 18 Nov 2024 01:49:42 +0100 Subject: [PATCH 070/112] Fixed stutter during jumps between cameras in a flyby sequence --- CHANGELOG.md | 3 ++- TombEngine/Game/spotcam.cpp | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 992b75207..38ef9f166 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed engine performance if weather or bubble effects are active. * Fixed silent crashes if loaded level is corrupted or in incorrect format. * Fixed incorrect clipping of scaled off-centered static meshes. -* Fixed incorrect collision detection for off-centered meshes. +* Fixed incorrect collision detection for off-centered moveables. +* Fixed stutter during jumps between cameras in a flyby sequence. * Fixed uzi targeting issues after using flycheat. * Fixed snow particles not always melting on the ground. * Fixed enemy pickups dropping on death sectors. diff --git a/TombEngine/Game/spotcam.cpp b/TombEngine/Game/spotcam.cpp index ea78586e8..e39ba51a0 100644 --- a/TombEngine/Game/spotcam.cpp +++ b/TombEngine/Game/spotcam.cpp @@ -659,10 +659,7 @@ void CalculateSpotCameras() SpotcamPaused = 0; if (LastCamera >= CurrentSplineCamera) - { - Camera.DisableInterpolation = true; return; - } if (s->flags & SCF_LOOP_SEQUENCE) { From 68114b03e2ae59b0fb809e8ed42f0e1cec329899 Mon Sep 17 00:00:00 2001 From: MontyTRC89 Date: Mon, 18 Nov 2024 06:25:43 +0100 Subject: [PATCH 071/112] Removed statics limit --- TombEngine/Game/Setup.cpp | 2 +- TombEngine/Game/Setup.h | 2 +- TombEngine/Renderer/Renderer.cpp | 2 +- TombEngine/Renderer/Renderer.h | 2 +- TombEngine/Renderer/RendererCompatibility.cpp | 24 +++++------ TombEngine/Specific/level.cpp | 42 +++++++++---------- TombEngine/Specific/level.h | 1 - 7 files changed, 35 insertions(+), 40 deletions(-) diff --git a/TombEngine/Game/Setup.cpp b/TombEngine/Game/Setup.cpp index 0f5b82874..1819576b9 100644 --- a/TombEngine/Game/Setup.cpp +++ b/TombEngine/Game/Setup.cpp @@ -33,7 +33,7 @@ using namespace TEN::Entities; using namespace TEN::Entities::Switches; ObjectHandler Objects; -StaticInfo StaticObjects[MAX_STATICS]; +std::unordered_map StaticObjects; void ObjectHandler::Initialize() { diff --git a/TombEngine/Game/Setup.h b/TombEngine/Game/Setup.h index 824426e10..4aac5131c 100644 --- a/TombEngine/Game/Setup.h +++ b/TombEngine/Game/Setup.h @@ -137,7 +137,7 @@ struct StaticInfo }; extern ObjectHandler Objects; -extern StaticInfo StaticObjects[MAX_STATICS]; +extern std::unordered_map StaticObjects; void InitializeGameFlags(); void InitializeSpecialEffects(); diff --git a/TombEngine/Renderer/Renderer.cpp b/TombEngine/Renderer/Renderer.cpp index 6c5a1a311..bb54ef41e 100644 --- a/TombEngine/Renderer/Renderer.cpp +++ b/TombEngine/Renderer/Renderer.cpp @@ -37,7 +37,7 @@ namespace TEN::Renderer _items.resize(0); _effects.resize(0); _moveableObjects.resize(0); - _staticObjects.resize(0); + _staticObjects.clear(); _sprites.resize(0); _rooms.resize(0); _roomTextures.resize(0); diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 47f07698b..13e6a36d2 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -248,7 +248,7 @@ namespace TEN::Renderer // Textures, objects and sprites std::vector> _moveableObjects; - std::vector> _staticObjects; + std::unordered_map> _staticObjects; std::vector _sprites; std::vector _spriteSequences; Matrix _laraWorldMatrix; diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 8a3cada26..4c39b927e 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -30,7 +30,6 @@ namespace TEN::Renderer _moveableObjects.resize(ID_NUMBER_OBJECTS); _spriteSequences.resize(ID_NUMBER_OBJECTS); - _staticObjects.resize(MAX_STATICS); _rooms.resize(g_Level.Rooms.size()); _meshes.clear(); @@ -878,11 +877,10 @@ namespace TEN::Renderer totalVertices = 0; totalIndices = 0; - for (int i = 0; i < StaticObjectsIds.size(); i++) + for (auto kv : StaticObjects) { - int objNum = StaticObjectsIds[i]; - StaticInfo* obj = &StaticObjects[objNum]; - MESH* mesh = &g_Level.Meshes[obj->meshNumber]; + StaticInfo& obj = kv.second; + MESH* mesh = &g_Level.Meshes[obj.meshNumber]; for (auto& bucket : mesh->buckets) { @@ -896,20 +894,20 @@ namespace TEN::Renderer lastVertex = 0; lastIndex = 0; - for (int i = 0; i < StaticObjectsIds.size(); i++) + for (auto kv : StaticObjects) { - StaticInfo*obj = &StaticObjects[StaticObjectsIds[i]]; - _staticObjects[StaticObjectsIds[i]] = RendererObject(); - RendererObject &staticObject = *_staticObjects[StaticObjectsIds[i]]; - staticObject.Type = 1; - staticObject.Id = StaticObjectsIds[i]; + StaticInfo& obj = kv.second; - RendererMesh *mesh = GetRendererMeshFromTrMesh(&staticObject, &g_Level.Meshes[obj->meshNumber], 0, false, false, &lastVertex, &lastIndex); + RendererObject staticObject = RendererObject(); + staticObject.Type = 1; + staticObject.Id = kv.first; + + RendererMesh *mesh = GetRendererMeshFromTrMesh(&staticObject, &g_Level.Meshes[obj.meshNumber], 0, false, false, &lastVertex, &lastIndex); staticObject.ObjectMeshes.push_back(mesh); _meshes.push_back(mesh); - _staticObjects[StaticObjectsIds[i]] = staticObject; + _staticObjects[kv.first] = staticObject; } _staticsVertexBuffer = VertexBuffer(_device.Get(), (int)_staticsVertices.size(), _staticsVertices.data()); diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index b09781821..e29285bee 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -81,7 +81,6 @@ const std::vector BRIDGE_OBJECT_IDS = LEVEL g_Level; std::vector MoveablesIds; -std::vector StaticObjectsIds; std::vector SpriteSequencesIds; char* DataPtr; @@ -269,7 +268,7 @@ void LoadItems() void LoadObjects() { Objects.Initialize(); - std::memset(StaticObjects, 0, sizeof(StaticInfo) * MAX_STATICS); + StaticObjects.clear(); int numMeshes = ReadInt32(); TENLog("Num meshes: " + std::to_string(numMeshes), LogLevel::Info); @@ -435,8 +434,6 @@ void LoadObjects() Objects[objNum].boneIndex = ReadInt32(); Objects[objNum].frameBase = ReadInt32(); Objects[objNum].animIndex = ReadInt32(); - - Objects[objNum].loaded = true; } TENLog("Initializing objects...", LogLevel::Info); @@ -457,28 +454,30 @@ void LoadObjects() meshID = 0; } - StaticObjectsIds.push_back(meshID); + StaticInfo staticInfo; - StaticObjects[meshID].meshNumber = (short)ReadInt32(); + staticInfo.meshNumber = (short)ReadInt32(); - StaticObjects[meshID].visibilityBox.X1 = ReadInt16(); - StaticObjects[meshID].visibilityBox.X2 = ReadInt16(); - StaticObjects[meshID].visibilityBox.Y1 = ReadInt16(); - StaticObjects[meshID].visibilityBox.Y2 = ReadInt16(); - StaticObjects[meshID].visibilityBox.Z1 = ReadInt16(); - StaticObjects[meshID].visibilityBox.Z2 = ReadInt16(); + staticInfo.visibilityBox.X1 = ReadInt16(); + staticInfo.visibilityBox.X2 = ReadInt16(); + staticInfo.visibilityBox.Y1 = ReadInt16(); + staticInfo.visibilityBox.Y2 = ReadInt16(); + staticInfo.visibilityBox.Z1 = ReadInt16(); + staticInfo.visibilityBox.Z2 = ReadInt16(); - StaticObjects[meshID].collisionBox.X1 = ReadInt16(); - StaticObjects[meshID].collisionBox.X2 = ReadInt16(); - StaticObjects[meshID].collisionBox.Y1 = ReadInt16(); - StaticObjects[meshID].collisionBox.Y2 = ReadInt16(); - StaticObjects[meshID].collisionBox.Z1 = ReadInt16(); - StaticObjects[meshID].collisionBox.Z2 = ReadInt16(); + staticInfo.collisionBox.X1 = ReadInt16(); + staticInfo.collisionBox.X2 = ReadInt16(); + staticInfo.collisionBox.Y1 = ReadInt16(); + staticInfo.collisionBox.Y2 = ReadInt16(); + staticInfo.collisionBox.Z1 = ReadInt16(); + staticInfo.collisionBox.Z2 = ReadInt16(); - StaticObjects[meshID].flags = (short)ReadInt16(); + staticInfo.flags = (short)ReadInt16(); - StaticObjects[meshID].shatterType = (ShatterType)ReadInt16(); - StaticObjects[meshID].shatterSound = (short)ReadInt16(); + staticInfo.shatterType = (ShatterType)ReadInt16(); + staticInfo.shatterSound = (short)ReadInt16(); + + StaticObjects[meshID] = staticInfo; } } @@ -988,7 +987,6 @@ void FreeLevel(bool partial) g_Renderer.FreeRendererData(); MoveablesIds.resize(0); - StaticObjectsIds.resize(0); SpriteSequencesIds.resize(0); g_Level.RoomTextures.resize(0); diff --git a/TombEngine/Specific/level.h b/TombEngine/Specific/level.h index ef6c260f8..f0a272827 100644 --- a/TombEngine/Specific/level.h +++ b/TombEngine/Specific/level.h @@ -137,7 +137,6 @@ struct LEVEL extern const std::vector BRIDGE_OBJECT_IDS; extern std::vector MoveablesIds; -extern std::vector StaticObjectsIds; extern std::vector SpriteSequencesIds; extern LEVEL g_Level; extern int SystemNameHash; From bb975cda58fd454fdb7ceca062fcdb0fb4ee0748 Mon Sep 17 00:00:00 2001 From: MontyTRC89 Date: Mon, 18 Nov 2024 06:27:39 +0100 Subject: [PATCH 072/112] Fixed changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ef9f166..274943275 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Added ricochet sounds and make the effect more prominent. * Allow to run the engine without title level. * Allow more than 1024 objects in a level. +* Allow more then 1000 static objects in a level ### Lua API changes * Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start. From cfbba4b74126b90005d8a9d0068aaddb489147bc Mon Sep 17 00:00:00 2001 From: Sezz Date: Mon, 18 Nov 2024 17:38:21 +1100 Subject: [PATCH 073/112] Don't create static object copies in loops; readability --- TombEngine/Game/Setup.cpp | 2 +- TombEngine/Game/Setup.h | 2 +- TombEngine/Renderer/Renderer.h | 48 ++++++++++++----- TombEngine/Renderer/RendererCompatibility.cpp | 30 +++++------ TombEngine/Specific/level.cpp | 53 +++++++++---------- 5 files changed, 77 insertions(+), 58 deletions(-) diff --git a/TombEngine/Game/Setup.cpp b/TombEngine/Game/Setup.cpp index 1819576b9..1d0e348c6 100644 --- a/TombEngine/Game/Setup.cpp +++ b/TombEngine/Game/Setup.cpp @@ -32,7 +32,7 @@ using namespace TEN::Effects::Hair; using namespace TEN::Entities; using namespace TEN::Entities::Switches; -ObjectHandler Objects; +ObjectHandler Objects; std::unordered_map StaticObjects; void ObjectHandler::Initialize() diff --git a/TombEngine/Game/Setup.h b/TombEngine/Game/Setup.h index 4aac5131c..284dc5b76 100644 --- a/TombEngine/Game/Setup.h +++ b/TombEngine/Game/Setup.h @@ -136,7 +136,7 @@ struct StaticInfo int shatterSound; }; -extern ObjectHandler Objects; +extern ObjectHandler Objects; extern std::unordered_map StaticObjects; void InitializeGameFlags(); diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 13e6a36d2..82fe8d064 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -90,6 +90,7 @@ namespace TEN::Renderer { private: // Core DX11 objects + ComPtr _device = nullptr; ComPtr _context = nullptr; ComPtr _swapChain = nullptr; @@ -109,6 +110,7 @@ namespace TEN::Renderer Viewport _viewportToolkit; // Render targets + RenderTarget2D _normalsRenderTarget; RenderTarget2D _depthRenderTarget; RenderTarget2D _backBuffer; @@ -123,6 +125,7 @@ namespace TEN::Renderer Texture2DArray _shadowMap; // Shaders + ComPtr _vsRooms; ComPtr _vsRoomsAnimatedTextures; ComPtr _psRooms; @@ -162,6 +165,7 @@ namespace TEN::Renderer ComPtr _psRoomAmbient; // Constant buffers + RenderView _gameCamera; RenderView _oldGameCamera; RenderView _currentGameCamera; @@ -196,10 +200,12 @@ namespace TEN::Renderer ConstantBuffer _cbSMAABuffer; // Primitive batches + std::unique_ptr _spriteBatch; std::unique_ptr> _primitiveBatch; // Text + std::unique_ptr _gameFont; std::vector _stringsToDraw; Vector4 _blinkColorValue = Vector4::Zero; @@ -207,6 +213,7 @@ namespace TEN::Renderer float _oldBlinkTime = 0.0f; // Graphics resources + Texture2D _logo; Texture2D _skyTexture; Texture2D _whiteTexture; @@ -233,32 +240,37 @@ namespace TEN::Renderer std::vector _staticsIndices; // Rooms and collector + std::vector _rooms; bool _invalidateCache; std::vector _visitedRoomsStack; // Lights + std::vector _dynamicLights; RendererLight* _shadowLight; // Lines + std::vector _lines2DToDraw = {}; std::vector _lines3DToDraw = {}; std::vector _triangles3DToDraw = {}; // Textures, objects and sprites - std::vector> _moveableObjects; - std::unordered_map> _staticObjects; - std::vector _sprites; - std::vector _spriteSequences; + + std::vector> _moveableObjects; + std::unordered_map> _staticObjects; // Key = static ID, value = renderer object. + std::vector _sprites; + std::vector _spriteSequences; + std::vector _animatedTextureSets; + std::vector _meshes; + std::vector _roomTextures; + std::vector _animatedTextures; + std::vector _moveablesTextures; + std::vector _staticTextures; + std::vector _spritesTextures; + Matrix _laraWorldMatrix; - std::vector _animatedTextureSets; - std::vector _meshes; - std::vector _roomTextures; - std::vector _animatedTextures; - std::vector _moveablesTextures; - std::vector _staticTextures; - std::vector _spritesTextures; // Preallocated pools of objects for avoiding new/delete. // Items and effects are safe (can't be more than 1024 items in TR), @@ -268,6 +280,7 @@ namespace TEN::Renderer std::vector _effects; // Debug variables + int _numDrawCalls = 0; int _numRoomsDrawCalls = 0; @@ -299,6 +312,7 @@ namespace TEN::Renderer RendererDebugPage _debugPage = RendererDebugPage::None; // Times for debug + int _timeUpdate; int _timeRoomsCollector; int _timeDraw; @@ -307,6 +321,7 @@ namespace TEN::Renderer int _currentCausticsFrame; // Screen settings + int _screenWidth; int _screenHeight; int _refreshRate; @@ -314,9 +329,11 @@ namespace TEN::Renderer float _farView = DEFAULT_FAR_VIEW; // A flag to prevent extra renderer object additions + bool _isLocked = false; // Caching state changes + TextureBase* _lastTexture; BlendMode _lastBlendMode; DepthState _lastDepthState; @@ -327,6 +344,7 @@ namespace TEN::Renderer ComPtr _shadowSampler; // Antialiasing + Texture2D _SMAAAreaTexture; Texture2D _SMAASearchTexture; RenderTarget2D _SMAASceneRenderTarget; @@ -347,7 +365,8 @@ namespace TEN::Renderer ComPtr _vsFXAA; ComPtr _psFXAA; - // Post process + // Post-process + PostProcessMode _postProcessMode = PostProcessMode::None; float _postProcessStrength = 1.0f; Vector3 _postProcessTint = Vector3::One; @@ -365,6 +384,7 @@ namespace TEN::Renderer bool _doingFullscreenPass = false; // SSAO + ComPtr _vsSSAO; ComPtr _psSSAO; ComPtr _psSSAOBlur; @@ -374,20 +394,24 @@ namespace TEN::Renderer std::vector _SSAOKernel; // Special effects + std::vector _causticTextures; // Transparency + fast_vector _sortedPolygonsVertices; fast_vector _sortedPolygonsIndices; VertexBuffer _sortedPolygonsVertexBuffer; IndexBuffer _sortedPolygonsIndexBuffer; // High framerate. + float _interpolationFactor = 0.0f; bool _graphicsSettingsChanged = false; // Private functions + void ApplySMAA(RenderTarget2D* renderTarget, RenderView& view); void ApplyFXAA(RenderTarget2D* renderTarget, RenderView& view); void BindTexture(TextureRegister registerType, TextureBase* texture, SamplerStateRegister samplerType); diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 4c39b927e..2ed6c5ee3 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -877,15 +877,13 @@ namespace TEN::Renderer totalVertices = 0; totalIndices = 0; - for (auto kv : StaticObjects) + for (const auto& [staticID, staticObj] : StaticObjects) { - StaticInfo& obj = kv.second; - MESH* mesh = &g_Level.Meshes[obj.meshNumber]; - - for (auto& bucket : mesh->buckets) + const auto& mesh = g_Level.Meshes[staticObj.meshNumber]; + for (const auto& bucket : mesh.buckets) { - totalVertices += bucket.numQuads * 4 + bucket.numTriangles * 3; - totalIndices += bucket.numQuads * 6 + bucket.numTriangles * 3; + totalVertices += (bucket.numQuads * 4) + (bucket.numTriangles * 3); + totalIndices += (bucket.numQuads * 6) + (bucket.numTriangles * 3); } } @@ -894,20 +892,18 @@ namespace TEN::Renderer lastVertex = 0; lastIndex = 0; - for (auto kv : StaticObjects) + for (const auto& [staticID, staticObj] : StaticObjects) { - StaticInfo& obj = kv.second; + auto newStaticObj = RendererObject(); + newStaticObj.Type = 1; + newStaticObj.Id = staticID; - RendererObject staticObject = RendererObject(); - staticObject.Type = 1; - staticObject.Id = kv.first; + auto& mesh = *GetRendererMeshFromTrMesh(&newStaticObj, &g_Level.Meshes[staticObj.meshNumber], 0, false, false, &lastVertex, &lastIndex); - RendererMesh *mesh = GetRendererMeshFromTrMesh(&staticObject, &g_Level.Meshes[obj.meshNumber], 0, false, false, &lastVertex, &lastIndex); + newStaticObj.ObjectMeshes.push_back(&mesh); + _meshes.push_back(&mesh); - staticObject.ObjectMeshes.push_back(mesh); - _meshes.push_back(mesh); - - _staticObjects[kv.first] = staticObject; + _staticObjects[staticID] = newStaticObj; } _staticsVertexBuffer = VertexBuffer(_device.Get(), (int)_staticsVertices.size(), _staticsVertices.data()); diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index e29285bee..906fa49ea 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -439,45 +439,44 @@ void LoadObjects() TENLog("Initializing objects...", LogLevel::Info); InitializeObjects(); - int numStatics = ReadInt32(); - TENLog("Num statics: " + std::to_string(numStatics), LogLevel::Info); + int staticCount = ReadInt32(); + TENLog("Statics: " + std::to_string(staticCount), LogLevel::Info); - for (int i = 0; i < numStatics; i++) + for (int i = 0; i < staticCount; i++) { - int meshID = ReadInt32(); - - if (meshID >= MAX_STATICS) + int staticID = ReadInt32(); + if (staticID >= MAX_STATICS) { - TENLog("Static with ID " + std::to_string(meshID) + " detected, while maximum is " + std::to_string(MAX_STATICS) + ". " + - "Change static mesh ID in WadTool to a value below maximum.", LogLevel::Warning); + TENLog("Static with ID " + std::to_string(staticID) + " detected, surpassing maximum of " + std::to_string(MAX_STATICS) + ". " + + "Change static object ID in WadTool to a value below the maximum.", LogLevel::Warning); - meshID = 0; + staticID = 0; } - StaticInfo staticInfo; + auto staticObj = StaticInfo{}; - staticInfo.meshNumber = (short)ReadInt32(); + staticObj.meshNumber = ReadInt32(); - staticInfo.visibilityBox.X1 = ReadInt16(); - staticInfo.visibilityBox.X2 = ReadInt16(); - staticInfo.visibilityBox.Y1 = ReadInt16(); - staticInfo.visibilityBox.Y2 = ReadInt16(); - staticInfo.visibilityBox.Z1 = ReadInt16(); - staticInfo.visibilityBox.Z2 = ReadInt16(); + staticObj.visibilityBox.X1 = ReadInt16(); + staticObj.visibilityBox.X2 = ReadInt16(); + staticObj.visibilityBox.Y1 = ReadInt16(); + staticObj.visibilityBox.Y2 = ReadInt16(); + staticObj.visibilityBox.Z1 = ReadInt16(); + staticObj.visibilityBox.Z2 = ReadInt16(); - staticInfo.collisionBox.X1 = ReadInt16(); - staticInfo.collisionBox.X2 = ReadInt16(); - staticInfo.collisionBox.Y1 = ReadInt16(); - staticInfo.collisionBox.Y2 = ReadInt16(); - staticInfo.collisionBox.Z1 = ReadInt16(); - staticInfo.collisionBox.Z2 = ReadInt16(); + staticObj.collisionBox.X1 = ReadInt16(); + staticObj.collisionBox.X2 = ReadInt16(); + staticObj.collisionBox.Y1 = ReadInt16(); + staticObj.collisionBox.Y2 = ReadInt16(); + staticObj.collisionBox.Z1 = ReadInt16(); + staticObj.collisionBox.Z2 = ReadInt16(); - staticInfo.flags = (short)ReadInt16(); + staticObj.flags = ReadInt16(); - staticInfo.shatterType = (ShatterType)ReadInt16(); - staticInfo.shatterSound = (short)ReadInt16(); + staticObj.shatterType = (ShatterType)ReadInt16(); + staticObj.shatterSound = ReadInt16(); - StaticObjects[meshID] = staticInfo; + StaticObjects[staticID] = staticObj; } } From f9c25a18629d592d3f4011cb21fa8d6bc5716425 Mon Sep 17 00:00:00 2001 From: Sezz Date: Mon, 18 Nov 2024 18:58:32 +1100 Subject: [PATCH 074/112] Remove "Num" prefix from level load logs --- TombEngine/Specific/level.cpp | 52 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 906fa49ea..cced51377 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -208,7 +208,7 @@ std::string ReadString() void LoadItems() { g_Level.NumItems = ReadInt32(); - TENLog("Num items: " + std::to_string(g_Level.NumItems), LogLevel::Info); + TENLog("Moveables: " + std::to_string(g_Level.NumItems), LogLevel::Info); if (g_Level.NumItems == 0) return; @@ -271,7 +271,7 @@ void LoadObjects() StaticObjects.clear(); int numMeshes = ReadInt32(); - TENLog("Num meshes: " + std::to_string(numMeshes), LogLevel::Info); + TENLog("Meshes: " + std::to_string(numMeshes), LogLevel::Info); g_Level.Meshes.reserve(numMeshes); for (int i = 0; i < numMeshes; i++) @@ -354,7 +354,7 @@ void LoadObjects() } int numAnimations = ReadInt32(); - TENLog("Num animations: " + std::to_string(numAnimations), LogLevel::Info); + TENLog("Animations: " + std::to_string(numAnimations), LogLevel::Info); g_Level.Anims.resize(numAnimations); for (int i = 0; i < numAnimations; i++) @@ -421,7 +421,7 @@ void LoadObjects() } int numModels = ReadInt32(); - TENLog("Num models: " + std::to_string(numModels), LogLevel::Info); + TENLog("Models: " + std::to_string(numModels), LogLevel::Info); for (int i = 0; i < numModels; i++) { @@ -448,8 +448,8 @@ void LoadObjects() if (staticID >= MAX_STATICS) { TENLog("Static with ID " + std::to_string(staticID) + " detected, surpassing maximum of " + std::to_string(MAX_STATICS) + ". " + - "Change static object ID in WadTool to a value below the maximum.", LogLevel::Warning); - + "Change static object ID in WadTool to a value below the maximum.", LogLevel::Warning); + staticID = 0; } @@ -483,7 +483,7 @@ void LoadObjects() void LoadCameras() { int numCameras = ReadInt32(); - TENLog("Num cameras: " + std::to_string(numCameras), LogLevel::Info); + TENLog("Cameras: " + std::to_string(numCameras), LogLevel::Info); g_Level.Cameras.reserve(numCameras); for (int i = 0; i < numCameras; i++) @@ -508,7 +508,7 @@ void LoadCameras() ReadBytes(SpotCam, NumberSpotcams * sizeof(SPOTCAM)); int numSinks = ReadInt32(); - TENLog("Num sinks: " + std::to_string(numSinks), LogLevel::Info); + TENLog("Sinks: " + std::to_string(numSinks), LogLevel::Info); g_Level.Sinks.reserve(numSinks); for (int i = 0; i < numSinks; i++) @@ -532,7 +532,7 @@ void LoadTextures() int size; int numTextures = ReadInt32(); - TENLog("Num room textures: " + std::to_string(numTextures), LogLevel::Info); + TENLog("Room textures: " + std::to_string(numTextures), LogLevel::Info); g_Level.RoomTextures.reserve(numTextures); for (int i = 0; i < numTextures; i++) @@ -558,7 +558,7 @@ void LoadTextures() } numTextures = ReadInt32(); - TENLog("Num object textures: " + std::to_string(numTextures), LogLevel::Info); + TENLog("Object textures: " + std::to_string(numTextures), LogLevel::Info); g_Level.MoveablesTextures.reserve(numTextures); for (int i = 0; i < numTextures; i++) @@ -584,7 +584,7 @@ void LoadTextures() } numTextures = ReadInt32(); - TENLog("Num static textures: " + std::to_string(numTextures), LogLevel::Info); + TENLog("Static textures: " + std::to_string(numTextures), LogLevel::Info); g_Level.StaticsTextures.reserve(numTextures); for (int i = 0; i < numTextures; i++) @@ -610,7 +610,7 @@ void LoadTextures() } numTextures = ReadInt32(); - TENLog("Num anim textures: " + std::to_string(numTextures), LogLevel::Info); + TENLog("Anim textures: " + std::to_string(numTextures), LogLevel::Info); g_Level.AnimatedTextures.reserve(numTextures); for (int i = 0; i < numTextures; i++) @@ -636,7 +636,7 @@ void LoadTextures() } numTextures = ReadInt32(); - TENLog("Num sprite textures: " + std::to_string(numTextures), LogLevel::Info); + TENLog("Sprite textures: " + std::to_string(numTextures), LogLevel::Info); g_Level.SpritesTextures.reserve(numTextures); for (int i = 0; i < numTextures; i++) @@ -1029,7 +1029,7 @@ size_t ReadFileEx(void* ptr, size_t size, size_t count, FILE* stream) void LoadSoundSources() { int numSoundSources = ReadInt32(); - TENLog("Num sound sources: " + std::to_string(numSoundSources), LogLevel::Info); + TENLog("Sound sources: " + std::to_string(numSoundSources), LogLevel::Info); g_Level.SoundSources.reserve(numSoundSources); for (int i = 0; i < numSoundSources; i++) @@ -1050,7 +1050,7 @@ void LoadSoundSources() void LoadAnimatedTextures() { int numAnimatedTextures = ReadInt32(); - TENLog("Num anim textures: " + std::to_string(numAnimatedTextures), LogLevel::Info); + TENLog("Anim textures: " + std::to_string(numAnimatedTextures), LogLevel::Info); for (int i = 0; i < numAnimatedTextures; i++) { @@ -1080,7 +1080,7 @@ void LoadAnimatedTextures() void LoadAIObjects() { int nAIObjects = ReadInt32(); - TENLog("Num AI objects: " + std::to_string(nAIObjects), LogLevel::Info); + TENLog("AI objects: " + std::to_string(nAIObjects), LogLevel::Info); g_Level.AIObjects.reserve(nAIObjects); for (int i = 0; i < nAIObjects; i++) @@ -1130,7 +1130,7 @@ void LoadEventSets() return; int globalEventSetCount = ReadInt32(); - TENLog("Num global event sets: " + std::to_string(globalEventSetCount), LogLevel::Info); + TENLog("Global event sets: " + std::to_string(globalEventSetCount), LogLevel::Info); for (int i = 0; i < globalEventSetCount; i++) { @@ -1149,7 +1149,7 @@ void LoadEventSets() } int volumeEventSetCount = ReadInt32(); - TENLog("Num volume event sets: " + std::to_string(volumeEventSetCount), LogLevel::Info); + TENLog("Volume event sets: " + std::to_string(volumeEventSetCount), LogLevel::Info); for (int i = 0; i < volumeEventSetCount; i++) { @@ -1442,7 +1442,7 @@ void LoadSamples() return; } - TENLog("Num sample infos: " + std::to_string(numSampleInfos), LogLevel::Info); + TENLog("Sample infos: " + std::to_string(numSampleInfos), LogLevel::Info); g_Level.SoundDetails.resize(numSampleInfos); ReadBytes(g_Level.SoundDetails.data(), numSampleInfos * sizeof(SampleInfo)); @@ -1451,7 +1451,7 @@ void LoadSamples() if (numSamples <= 0) return; - TENLog("Num samples: " + std::to_string(numSamples), LogLevel::Info); + TENLog("Samples: " + std::to_string(numSamples), LogLevel::Info); int uncompressedSize; int compressedSize; @@ -1472,19 +1472,19 @@ void LoadBoxes() { // Read boxes int numBoxes = ReadInt32(); - TENLog("Num boxes: " + std::to_string(numBoxes), LogLevel::Info); + TENLog("Boxes: " + std::to_string(numBoxes), LogLevel::Info); g_Level.PathfindingBoxes.resize(numBoxes); ReadBytes(g_Level.PathfindingBoxes.data(), numBoxes * sizeof(BOX_INFO)); // Read overlaps int numOverlaps = ReadInt32(); - TENLog("Num overlaps: " + std::to_string(numOverlaps), LogLevel::Info); + TENLog("Overlaps: " + std::to_string(numOverlaps), LogLevel::Info); g_Level.Overlaps.resize(numOverlaps); ReadBytes(g_Level.Overlaps.data(), numOverlaps * sizeof(OVERLAP)); // Read zones int numZoneGroups = ReadInt32(); - TENLog("Num zone groups: " + std::to_string(numZoneGroups), LogLevel::Info); + TENLog("Zone groups: " + std::to_string(numZoneGroups), LogLevel::Info); for (int i = 0; i < 2; i++) { @@ -1567,7 +1567,7 @@ void LoadSprites() int numSprites = ReadInt32(); g_Level.Sprites.resize(numSprites); - TENLog("Num sprites: " + std::to_string(numSprites), LogLevel::Info); + TENLog("Sprites: " + std::to_string(numSprites), LogLevel::Info); for (int i = 0; i < numSprites; i++) { @@ -1585,7 +1585,7 @@ void LoadSprites() int numSequences = ReadInt32(); - TENLog("Num sprite sequences: " + std::to_string(numSequences), LogLevel::Info); + TENLog("Sprite sequences: " + std::to_string(numSequences), LogLevel::Info); for (int i = 0; i < numSequences; i++) { @@ -1721,4 +1721,4 @@ void LoadPortal(ROOM_INFO& room) } room.doors.push_back(door); -} \ No newline at end of file +} From 57a151af2a58b9581134ede482790481cf305074 Mon Sep 17 00:00:00 2001 From: Sezz Date: Mon, 18 Nov 2024 19:24:57 +1100 Subject: [PATCH 075/112] "Num" prefix -> "count" suffix --- TombEngine/Specific/level.cpp | 280 +++++++++++++++++----------------- 1 file changed, 142 insertions(+), 138 deletions(-) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index cced51377..93160386b 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -192,13 +192,15 @@ long long ReadLEB128(bool sign) std::string ReadString() { - auto numBytes = ReadLEB128(false); + auto byteCount = ReadLEB128(false); - if (numBytes <= 0) + if (byteCount <= 0) + { return std::string(); + } else { - auto newPtr = CurrentDataPtr + numBytes; + auto newPtr = CurrentDataPtr + byteCount; auto result = std::string(CurrentDataPtr, newPtr); CurrentDataPtr = newPtr; return result; @@ -270,13 +272,13 @@ void LoadObjects() Objects.Initialize(); StaticObjects.clear(); - int numMeshes = ReadInt32(); - TENLog("Meshes: " + std::to_string(numMeshes), LogLevel::Info); + int meshCount = ReadInt32(); + TENLog("Mesh count: " + std::to_string(meshCount), LogLevel::Info); - g_Level.Meshes.reserve(numMeshes); - for (int i = 0; i < numMeshes; i++) + g_Level.Meshes.reserve(meshCount); + for (int i = 0; i < meshCount; i++) { - MESH mesh; + auto mesh = MESH{}; mesh.lightMode = (LightMode)ReadUInt8(); @@ -285,25 +287,25 @@ void LoadObjects() mesh.sphere.Center.z = ReadFloat(); mesh.sphere.Radius = ReadFloat(); - int numVertices = ReadInt32(); + int vertexCount = ReadInt32(); - mesh.positions.resize(numVertices); - ReadBytes(mesh.positions.data(), 12 * numVertices); + mesh.positions.resize(vertexCount); + ReadBytes(mesh.positions.data(), 12 * vertexCount); - mesh.colors.resize(numVertices); - ReadBytes(mesh.colors.data(), 12 * numVertices); + mesh.colors.resize(vertexCount); + ReadBytes(mesh.colors.data(), 12 * vertexCount); - mesh.effects.resize(numVertices); - ReadBytes(mesh.effects.data(), 12 * numVertices); + mesh.effects.resize(vertexCount); + ReadBytes(mesh.effects.data(), 12 * vertexCount); - mesh.bones.resize(numVertices); - ReadBytes(mesh.bones.data(), 4 * numVertices); + mesh.bones.resize(vertexCount); + ReadBytes(mesh.bones.data(), 4 * vertexCount); - int numBuckets = ReadInt32(); - mesh.buckets.reserve(numBuckets); - for (int j = 0; j < numBuckets; j++) + int bucketCount = ReadInt32(); + mesh.buckets.reserve(bucketCount); + for (int j = 0; j < bucketCount; j++) { - BUCKET bucket; + auto bucket = BUCKET{}; bucket.texture = ReadInt32(); bucket.blendMode = (BlendMode)ReadUInt8(); @@ -311,11 +313,11 @@ void LoadObjects() bucket.numQuads = 0; bucket.numTriangles = 0; - int numPolygons = ReadInt32(); - bucket.polygons.reserve(numPolygons); - for (int k = 0; k < numPolygons; k++) + int polyCount = ReadInt32(); + bucket.polygons.reserve(polyCount); + for (int k = 0; k < polyCount; k++) { - POLYGON poly; + auto poly = POLYGON{}; poly.shape = ReadInt32(); poly.animatedSequence = ReadInt32(); @@ -353,11 +355,11 @@ void LoadObjects() g_Level.Meshes.push_back(mesh); } - int numAnimations = ReadInt32(); - TENLog("Animations: " + std::to_string(numAnimations), LogLevel::Info); + int animCount = ReadInt32(); + TENLog("Animation count: " + std::to_string(animCount), LogLevel::Info); - g_Level.Anims.resize(numAnimations); - for (int i = 0; i < numAnimations; i++) + g_Level.Anims.resize(animCount); + for (int i = 0; i < animCount; i++) { auto* anim = &g_Level.Anims[i]; @@ -376,25 +378,25 @@ void LoadObjects() anim->CommandIndex = ReadInt32(); } - int numChanges = ReadInt32(); - g_Level.Changes.resize(numChanges); - ReadBytes(g_Level.Changes.data(), sizeof(StateDispatchData) * numChanges); + int changeCount = ReadInt32(); + g_Level.Changes.resize(changeCount); + ReadBytes(g_Level.Changes.data(), sizeof(StateDispatchData) * changeCount); - int numRanges = ReadInt32(); - g_Level.Ranges.resize(numRanges); - ReadBytes(g_Level.Ranges.data(), sizeof(StateDispatchRangeData) * numRanges); + int rangeCount = ReadInt32(); + g_Level.Ranges.resize(rangeCount); + ReadBytes(g_Level.Ranges.data(), sizeof(StateDispatchRangeData) * rangeCount); - int numCommands = ReadInt32(); - g_Level.Commands.resize(numCommands); - ReadBytes(g_Level.Commands.data(), sizeof(short) * numCommands); + int commandCount = ReadInt32(); + g_Level.Commands.resize(commandCount); + ReadBytes(g_Level.Commands.data(), sizeof(short) * commandCount); - int numBones = ReadInt32(); - g_Level.Bones.resize(numBones); - ReadBytes(g_Level.Bones.data(), 4 * numBones); + int boneCount = ReadInt32(); + g_Level.Bones.resize(boneCount); + ReadBytes(g_Level.Bones.data(), 4 * boneCount); - int numFrames = ReadInt32(); - g_Level.Frames.resize(numFrames); - for (int i = 0; i < numFrames; i++) + int frameCount = ReadInt32(); + g_Level.Frames.resize(frameCount); + for (int i = 0; i < frameCount; i++) { auto* frame = &g_Level.Frames[i]; @@ -408,9 +410,9 @@ void LoadObjects() // NOTE: Braces are necessary to ensure correct value init order. frame->Offset = Vector3{ (float)ReadInt16(), (float)ReadInt16(), (float)ReadInt16() }; - int numAngles = ReadInt16(); - frame->BoneOrientations.resize(numAngles); - for (int j = 0; j < numAngles; j++) + int angleCount = ReadInt16(); + frame->BoneOrientations.resize(angleCount); + for (int j = 0; j < angleCount; j++) { auto* q = &frame->BoneOrientations[j]; q->x = ReadFloat(); @@ -420,10 +422,10 @@ void LoadObjects() } } - int numModels = ReadInt32(); - TENLog("Models: " + std::to_string(numModels), LogLevel::Info); + int modelCount = ReadInt32(); + TENLog("Model count: " + std::to_string(modelCount), LogLevel::Info); - for (int i = 0; i < numModels; i++) + for (int i = 0; i < modelCount; i++) { int objNum = ReadInt32(); MoveablesIds.push_back(objNum); @@ -482,11 +484,11 @@ void LoadObjects() void LoadCameras() { - int numCameras = ReadInt32(); - TENLog("Cameras: " + std::to_string(numCameras), LogLevel::Info); + int cameraCount = ReadInt32(); + TENLog("Camera count: " + std::to_string(cameraCount), LogLevel::Info); - g_Level.Cameras.reserve(numCameras); - for (int i = 0; i < numCameras; i++) + g_Level.Cameras.reserve(cameraCount); + for (int i = 0; i < cameraCount; i++) { auto& camera = g_Level.Cameras.emplace_back(); camera.Index = i; @@ -507,11 +509,11 @@ void LoadCameras() if (NumberSpotcams != 0) ReadBytes(SpotCam, NumberSpotcams * sizeof(SPOTCAM)); - int numSinks = ReadInt32(); - TENLog("Sinks: " + std::to_string(numSinks), LogLevel::Info); + int sinkCount = ReadInt32(); + TENLog("Sink count: " + std::to_string(sinkCount), LogLevel::Info); - g_Level.Sinks.reserve(numSinks); - for (int i = 0; i < numSinks; i++) + g_Level.Sinks.reserve(sinkCount); + for (int i = 0; i < sinkCount; i++) { auto& sink = g_Level.Sinks.emplace_back(); sink.Position.x = ReadInt32(); @@ -531,13 +533,13 @@ void LoadTextures() int size; - int numTextures = ReadInt32(); - TENLog("Room textures: " + std::to_string(numTextures), LogLevel::Info); + int textureCount = ReadInt32(); + TENLog("Room texture count: " + std::to_string(textureCount), LogLevel::Info); - g_Level.RoomTextures.reserve(numTextures); - for (int i = 0; i < numTextures; i++) + g_Level.RoomTextures.reserve(textureCount); + for (int i = 0; i < textureCount; i++) { - TEXTURE texture; + auto texture = TEXTURE{}; texture.width = ReadInt32(); texture.height = ReadInt32(); @@ -557,13 +559,13 @@ void LoadTextures() g_Level.RoomTextures.push_back(texture); } - numTextures = ReadInt32(); - TENLog("Object textures: " + std::to_string(numTextures), LogLevel::Info); + textureCount = ReadInt32(); + TENLog("Object texture count: " + std::to_string(textureCount), LogLevel::Info); - g_Level.MoveablesTextures.reserve(numTextures); - for (int i = 0; i < numTextures; i++) + g_Level.MoveablesTextures.reserve(textureCount); + for (int i = 0; i < textureCount; i++) { - TEXTURE texture; + auto texture = TEXTURE{}; texture.width = ReadInt32(); texture.height = ReadInt32(); @@ -583,13 +585,13 @@ void LoadTextures() g_Level.MoveablesTextures.push_back(texture); } - numTextures = ReadInt32(); - TENLog("Static textures: " + std::to_string(numTextures), LogLevel::Info); + textureCount = ReadInt32(); + TENLog("Static texture count: " + std::to_string(textureCount), LogLevel::Info); - g_Level.StaticsTextures.reserve(numTextures); - for (int i = 0; i < numTextures; i++) + g_Level.StaticsTextures.reserve(textureCount); + for (int i = 0; i < textureCount; i++) { - TEXTURE texture; + auto texture = TEXTURE{}; texture.width = ReadInt32(); texture.height = ReadInt32(); @@ -609,13 +611,13 @@ void LoadTextures() g_Level.StaticsTextures.push_back(texture); } - numTextures = ReadInt32(); - TENLog("Anim textures: " + std::to_string(numTextures), LogLevel::Info); + textureCount = ReadInt32(); + TENLog("Anim texture count: " + std::to_string(textureCount), LogLevel::Info); - g_Level.AnimatedTextures.reserve(numTextures); - for (int i = 0; i < numTextures; i++) + g_Level.AnimatedTextures.reserve(textureCount); + for (int i = 0; i < textureCount; i++) { - TEXTURE texture; + auto texture = TEXTURE{}; texture.width = ReadInt32(); texture.height = ReadInt32(); @@ -635,13 +637,13 @@ void LoadTextures() g_Level.AnimatedTextures.push_back(texture); } - numTextures = ReadInt32(); - TENLog("Sprite textures: " + std::to_string(numTextures), LogLevel::Info); + textureCount = ReadInt32(); + TENLog("Sprite texture count: " + std::to_string(textureCount), LogLevel::Info); - g_Level.SpritesTextures.reserve(numTextures); - for (int i = 0; i < numTextures; i++) + g_Level.SpritesTextures.reserve(textureCount); + for (int i = 0; i < textureCount; i++) { - TEXTURE texture; + auto texture = TEXTURE{}; texture.width = ReadInt32(); texture.height = ReadInt32(); @@ -774,7 +776,7 @@ void LoadStaticRoomData() constexpr auto ILLEGAL_CEILING_SLOPE_ANGLE = ANGLE(45.0f); int roomCount = ReadInt32(); - TENLog("Rooms: " + std::to_string(roomCount), LogLevel::Info); + TENLog("Room count: " + std::to_string(roomCount), LogLevel::Info); g_Level.Rooms.reserve(roomCount); for (int i = 0; i < roomCount; i++) @@ -951,9 +953,9 @@ void LoadRooms() LoadStaticRoomData(); BuildOutsideRoomsTable(); - int numFloorData = ReadInt32(); - g_Level.FloorData.resize(numFloorData); - ReadBytes(g_Level.FloorData.data(), numFloorData * sizeof(short)); + int floordataCount = ReadInt32(); + g_Level.FloorData.resize(floordataCount); + ReadBytes(g_Level.FloorData.data(), floordataCount * sizeof(short)); } void FreeLevel(bool partial) @@ -1028,11 +1030,11 @@ size_t ReadFileEx(void* ptr, size_t size, size_t count, FILE* stream) void LoadSoundSources() { - int numSoundSources = ReadInt32(); - TENLog("Sound sources: " + std::to_string(numSoundSources), LogLevel::Info); + int soundSourceCount = ReadInt32(); + TENLog("Sound source count: " + std::to_string(soundSourceCount), LogLevel::Info); - g_Level.SoundSources.reserve(numSoundSources); - for (int i = 0; i < numSoundSources; i++) + g_Level.SoundSources.reserve(soundSourceCount); + for (int i = 0; i < soundSourceCount; i++) { auto& source = g_Level.SoundSources.emplace_back(SoundSourceInfo{}); @@ -1049,19 +1051,19 @@ void LoadSoundSources() void LoadAnimatedTextures() { - int numAnimatedTextures = ReadInt32(); - TENLog("Anim textures: " + std::to_string(numAnimatedTextures), LogLevel::Info); + int animatedTextureCount = ReadInt32(); + TENLog("Anim texture count: " + std::to_string(animatedTextureCount), LogLevel::Info); - for (int i = 0; i < numAnimatedTextures; i++) + for (int i = 0; i < animatedTextureCount; i++) { - ANIMATED_TEXTURES_SEQUENCE sequence; + auto sequence = ANIMATED_TEXTURES_SEQUENCE{}; sequence.atlas = ReadInt32(); sequence.Fps = ReadInt32(); sequence.numFrames = ReadInt32(); for (int j = 0; j < sequence.numFrames; j++) { - ANIMATED_TEXTURES_FRAME frame; + auto frame = ANIMATED_TEXTURES_FRAME{}; frame.x1 = ReadFloat(); frame.y1 = ReadFloat(); frame.x2 = ReadFloat(); @@ -1079,11 +1081,11 @@ void LoadAnimatedTextures() void LoadAIObjects() { - int nAIObjects = ReadInt32(); - TENLog("AI objects: " + std::to_string(nAIObjects), LogLevel::Info); + int aiObjectCount = ReadInt32(); + TENLog("AI object count: " + std::to_string(aiObjectCount), LogLevel::Info); - g_Level.AIObjects.reserve(nAIObjects); - for (int i = 0; i < nAIObjects; i++) + g_Level.AIObjects.reserve(aiObjectCount); + for (int i = 0; i < aiObjectCount; i++) { auto& obj = g_Level.AIObjects.emplace_back(); @@ -1130,7 +1132,7 @@ void LoadEventSets() return; int globalEventSetCount = ReadInt32(); - TENLog("Global event sets: " + std::to_string(globalEventSetCount), LogLevel::Info); + TENLog("Global event set count: " + std::to_string(globalEventSetCount), LogLevel::Info); for (int i = 0; i < globalEventSetCount; i++) { @@ -1149,7 +1151,7 @@ void LoadEventSets() } int volumeEventSetCount = ReadInt32(); - TENLog("Volume event sets: " + std::to_string(volumeEventSetCount), LogLevel::Info); + TENLog("Volume event set count: " + std::to_string(volumeEventSetCount), LogLevel::Info); for (int i = 0; i < volumeEventSetCount; i++) { @@ -1435,29 +1437,29 @@ void LoadSamples() g_Level.SoundMap.resize(soundMapSize); ReadBytes(g_Level.SoundMap.data(), soundMapSize * sizeof(short)); - int numSampleInfos = ReadInt32(); - if (!numSampleInfos) + int sampleInfoCount = ReadInt32(); + if (!sampleInfoCount) { - TENLog("No samples were found and loaded.", LogLevel::Warning); + TENLog("No samples were found or loaded.", LogLevel::Warning); return; } - TENLog("Sample infos: " + std::to_string(numSampleInfos), LogLevel::Info); + TENLog("Sample info count: " + std::to_string(sampleInfoCount), LogLevel::Info); - g_Level.SoundDetails.resize(numSampleInfos); - ReadBytes(g_Level.SoundDetails.data(), numSampleInfos * sizeof(SampleInfo)); + g_Level.SoundDetails.resize(sampleInfoCount); + ReadBytes(g_Level.SoundDetails.data(), sampleInfoCount * sizeof(SampleInfo)); - int numSamples = ReadInt32(); - if (numSamples <= 0) + int sampleCount = ReadInt32(); + if (sampleCount <= 0) return; - TENLog("Samples: " + std::to_string(numSamples), LogLevel::Info); + TENLog("Sample count: " + std::to_string(sampleCount), LogLevel::Info); - int uncompressedSize; - int compressedSize; + int uncompressedSize = 0; + int compressedSize = 0; char* buffer = (char*)malloc(2 * 1024 * 1024); - for (int i = 0; i < numSamples; i++) + for (int i = 0; i < sampleCount; i++) { uncompressedSize = ReadInt32(); compressedSize = ReadInt32(); @@ -1471,42 +1473,42 @@ void LoadSamples() void LoadBoxes() { // Read boxes - int numBoxes = ReadInt32(); - TENLog("Boxes: " + std::to_string(numBoxes), LogLevel::Info); - g_Level.PathfindingBoxes.resize(numBoxes); - ReadBytes(g_Level.PathfindingBoxes.data(), numBoxes * sizeof(BOX_INFO)); + int boxCount = ReadInt32(); + TENLog("Box count: " + std::to_string(boxCount), LogLevel::Info); + g_Level.PathfindingBoxes.resize(boxCount); + ReadBytes(g_Level.PathfindingBoxes.data(), boxCount * sizeof(BOX_INFO)); // Read overlaps - int numOverlaps = ReadInt32(); - TENLog("Overlaps: " + std::to_string(numOverlaps), LogLevel::Info); - g_Level.Overlaps.resize(numOverlaps); - ReadBytes(g_Level.Overlaps.data(), numOverlaps * sizeof(OVERLAP)); + int overlapCount = ReadInt32(); + TENLog("Overlap count: " + std::to_string(overlapCount), LogLevel::Info); + g_Level.Overlaps.resize(overlapCount); + ReadBytes(g_Level.Overlaps.data(), overlapCount * sizeof(OVERLAP)); // Read zones - int numZoneGroups = ReadInt32(); - TENLog("Zone groups: " + std::to_string(numZoneGroups), LogLevel::Info); + int zoneGroupCount = ReadInt32(); + TENLog("Zone group count: " + std::to_string(zoneGroupCount), LogLevel::Info); for (int i = 0; i < 2; i++) { - for (int j = 0; j < numZoneGroups; j++) + for (int j = 0; j < zoneGroupCount; j++) { if (j >= (int)ZoneType::MaxZone) { - int excessiveZoneGroups = numZoneGroups - j + 1; + int excessiveZoneGroups = zoneGroupCount - j + 1; TENLog("Level file contains extra pathfinding data, number of excessive zone groups is " + std::to_string(excessiveZoneGroups) + ". These zone groups will be ignored.", LogLevel::Warning); - CurrentDataPtr += numBoxes * sizeof(int); + CurrentDataPtr += boxCount * sizeof(int); } else { - g_Level.Zones[j][i].resize(numBoxes); - ReadBytes(g_Level.Zones[j][i].data(), numBoxes * sizeof(int)); + g_Level.Zones[j][i].resize(boxCount); + ReadBytes(g_Level.Zones[j][i].data(), boxCount * sizeof(int)); } } } // By default all blockable boxes are blocked - for (int i = 0; i < numBoxes; i++) + for (int i = 0; i < boxCount; i++) { if (g_Level.PathfindingBoxes[i].flags & BLOCKABLE) g_Level.PathfindingBoxes[i].flags |= BLOCKED; @@ -1564,12 +1566,12 @@ bool LoadLevelFile(int levelIndex) void LoadSprites() { - int numSprites = ReadInt32(); - g_Level.Sprites.resize(numSprites); + int spriteCount = ReadInt32(); + g_Level.Sprites.resize(spriteCount); - TENLog("Sprites: " + std::to_string(numSprites), LogLevel::Info); + TENLog("Sprite count: " + std::to_string(spriteCount), LogLevel::Info); - for (int i = 0; i < numSprites; i++) + for (int i = 0; i < spriteCount; i++) { auto* spr = &g_Level.Sprites[i]; spr->tile = ReadInt32(); @@ -1583,17 +1585,19 @@ void LoadSprites() spr->y4 = ReadFloat(); } - int numSequences = ReadInt32(); + int spriteSeqCount = ReadInt32(); - TENLog("Sprite sequences: " + std::to_string(numSequences), LogLevel::Info); + TENLog("Sprite sequence count: " + std::to_string(spriteSeqCount), LogLevel::Info); - for (int i = 0; i < numSequences; i++) + for (int i = 0; i < spriteSeqCount; i++) { int spriteID = ReadInt32(); short negLength = ReadInt16(); short offset = ReadInt16(); if (spriteID >= ID_NUMBER_OBJECTS) + { StaticObjects[spriteID - ID_NUMBER_OBJECTS].meshNumber = offset; + } else { Objects[spriteID].nmeshes = negLength; @@ -1706,7 +1710,7 @@ void BuildOutsideRoomsTable() void LoadPortal(ROOM_INFO& room) { - ROOM_DOOR door; + auto door = ROOM_DOOR{}; door.room = ReadInt16(); door.normal.x = ReadInt32(); From 231bea0c1e10ca8384f26998216b989a936eab05 Mon Sep 17 00:00:00 2001 From: Sezz Date: Mon, 18 Nov 2024 19:31:28 +1100 Subject: [PATCH 076/112] Remove MAX_STATICS constant --- TombEngine/Game/Setup.h | 2 -- TombEngine/Specific/level.cpp | 11 ++--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/TombEngine/Game/Setup.h b/TombEngine/Game/Setup.h index 284dc5b76..967a1b944 100644 --- a/TombEngine/Game/Setup.h +++ b/TombEngine/Game/Setup.h @@ -12,8 +12,6 @@ constexpr auto DEFAULT_RADIUS = 10; constexpr auto GRAVITY = 6.0f; constexpr auto SWAMP_GRAVITY = GRAVITY / 3.0f; -constexpr auto MAX_STATICS = 1000; - enum JointRotationFlags { ROT_X = 1 << 2, diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 93160386b..b85031596 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -446,17 +446,10 @@ void LoadObjects() for (int i = 0; i < staticCount; i++) { - int staticID = ReadInt32(); - if (staticID >= MAX_STATICS) - { - TENLog("Static with ID " + std::to_string(staticID) + " detected, surpassing maximum of " + std::to_string(MAX_STATICS) + ". " + - "Change static object ID in WadTool to a value below the maximum.", LogLevel::Warning); - - staticID = 0; - } - auto staticObj = StaticInfo{}; + int staticID = ReadInt32(); + staticObj.meshNumber = ReadInt32(); staticObj.visibilityBox.X1 = ReadInt16(); From 483ead320380dea04f0ecba9e878852d6a74774a Mon Sep 17 00:00:00 2001 From: Sezz Date: Mon, 18 Nov 2024 22:12:15 +1100 Subject: [PATCH 077/112] Fix missed illegal -> steep rename --- TombEngine/Game/collision/Point.cpp | 4 ++-- TombEngine/Game/collision/floordata.cpp | 2 +- TombEngine/Game/collision/floordata.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TombEngine/Game/collision/Point.cpp b/TombEngine/Game/collision/Point.cpp index 652421c8c..66806e075 100644 --- a/TombEngine/Game/collision/Point.cpp +++ b/TombEngine/Game/collision/Point.cpp @@ -408,7 +408,7 @@ namespace TEN::Collision::Point short slopeAngle = Geometry::GetSurfaceSlopeAngle(GetFloorNormal()); short steepSlopeAngle = (GetFloorBridgeItemNumber() != NO_VALUE) ? DEFAULT_STEEP_FLOOR_SLOPE_ANGLE : - GetBottomSector().GetSurfaceIllegalSlopeAngle(_position.x, _position.z, true); + GetBottomSector().GetSurfaceSteepSlopeAngle(_position.x, _position.z, true); return (abs(slopeAngle) >= steepSlopeAngle); } @@ -418,7 +418,7 @@ namespace TEN::Collision::Point short slopeAngle = Geometry::GetSurfaceSlopeAngle(GetCeilingNormal(), -Vector3::UnitY); short steepSlopeAngle = (GetCeilingBridgeItemNumber() != NO_VALUE) ? DEFAULT_STEEP_CEILING_SLOPE_ANGLE : - GetTopSector().GetSurfaceIllegalSlopeAngle(_position.x, _position.z, false); + GetTopSector().GetSurfaceSteepSlopeAngle(_position.x, _position.z, false); return (abs(slopeAngle) >= steepSlopeAngle); } diff --git a/TombEngine/Game/collision/floordata.cpp b/TombEngine/Game/collision/floordata.cpp index a50f8fa29..30af7c2c8 100644 --- a/TombEngine/Game/collision/floordata.cpp +++ b/TombEngine/Game/collision/floordata.cpp @@ -83,7 +83,7 @@ Vector3 FloorInfo::GetSurfaceNormal(int x, int z, bool isFloor) const return GetSurfaceNormal(triID, isFloor); } -short FloorInfo::GetSurfaceIllegalSlopeAngle(int x, int z, bool isFloor) const +short FloorInfo::GetSurfaceSteepSlopeAngle(int x, int z, bool isFloor) const { const auto& tri = GetSurfaceTriangle(x, z, isFloor); return tri.SteepSlopeAngle; diff --git a/TombEngine/Game/collision/floordata.h b/TombEngine/Game/collision/floordata.h index 81f2018d4..19d577463 100644 --- a/TombEngine/Game/collision/floordata.h +++ b/TombEngine/Game/collision/floordata.h @@ -161,7 +161,7 @@ public: const SectorSurfaceTriangleData& GetSurfaceTriangle(int x, int z, bool isFloor) const; Vector3 GetSurfaceNormal(int triID, bool isFloor) const; Vector3 GetSurfaceNormal(int x, int z, bool isFloor) const; - short GetSurfaceIllegalSlopeAngle(int x, int z, bool isFloor) const; + short GetSurfaceSteepSlopeAngle(int x, int z, bool isFloor) const; MaterialType GetSurfaceMaterial(int x, int z, bool isFloor) const; std::optional GetNextRoomNumber(int x, int z, bool isBelow) const; From 9ea15571a8e5cb60a1b32dfebfff524eeb69c8cd Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:48:20 +0100 Subject: [PATCH 078/112] Fixed quaternion conversion crash if 2 hair joints share the same offset --- TombEngine/Game/effects/hair.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TombEngine/Game/effects/hair.cpp b/TombEngine/Game/effects/hair.cpp index 32ae15b55..a70545669 100644 --- a/TombEngine/Game/effects/hair.cpp +++ b/TombEngine/Game/effects/hair.cpp @@ -176,6 +176,11 @@ namespace TEN::Effects::Hair // Calculate absolute orientation. auto absDir = target - origin; absDir.Normalize(); + + // Handle cases with zero normal (can happen if 2 hair segments have same offset). + if (absDir == Vector3::Zero) + return Quaternion(); + auto absOrient = Geometry::ConvertDirectionToQuat(absDir); // Calculate relative twist rotation. From 534ac3876a8b3de290b4e5f43b9bad1e4b2ff42c Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:32:35 +0100 Subject: [PATCH 079/112] Fixed occasional crashes if there are static meshes placed within room border walls --- CHANGELOG.md | 1 + TombEngine/Game/control/box.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 274943275..4113d875f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed engine performance around bridges. * Fixed engine performance if weather or bubble effects are active. * Fixed silent crashes if loaded level is corrupted or in incorrect format. +* Fixed occasional crashes if there are static meshes placed within room border walls. * Fixed incorrect clipping of scaled off-centered static meshes. * Fixed incorrect collision detection for off-centered moveables. * Fixed stutter during jumps between cameras in a flyby sequence. diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index 998794061..9f8d05098 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -2187,7 +2187,7 @@ void InitializeItemBoxData() for (const auto& mesh : room.mesh) { long index = ((mesh.pos.Position.z - room.Position.z) / BLOCK(1)) + room.ZSize * ((mesh.pos.Position.x - room.Position.x) / BLOCK(1)); - if (index > room.Sectors.size()) + if (index >= room.Sectors.size()) continue; auto* floor = &room.Sectors[index]; From 545378d967c7bb5eff153a5751b009584c5d064e Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Tue, 19 Nov 2024 01:38:16 +0100 Subject: [PATCH 080/112] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4113d875f..bd8085dcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Added ricochet sounds and make the effect more prominent. * Allow to run the engine without title level. * Allow more than 1024 objects in a level. -* Allow more then 1000 static objects in a level +* Allow more than 1000 static mesh slots in a level. ### Lua API changes * Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start. From d070bc756f44b6f79b32bf8e041b35eb1250bb83 Mon Sep 17 00:00:00 2001 From: Sezz Date: Tue, 19 Nov 2024 15:48:13 +1100 Subject: [PATCH 081/112] Use Quaternion::Identity --- TombEngine/Game/effects/hair.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TombEngine/Game/effects/hair.cpp b/TombEngine/Game/effects/hair.cpp index a70545669..a574ac31c 100644 --- a/TombEngine/Game/effects/hair.cpp +++ b/TombEngine/Game/effects/hair.cpp @@ -177,9 +177,9 @@ namespace TEN::Effects::Hair auto absDir = target - origin; absDir.Normalize(); - // Handle cases with zero normal (can happen if 2 hair segments have same offset). + // FAILSAFE: Handle case with zero normal (can happen if 2 hair segments have same offset). if (absDir == Vector3::Zero) - return Quaternion(); + return Quaternion::Identity; auto absOrient = Geometry::ConvertDirectionToQuat(absDir); From e7fd154fc9d96b63821aa9f2dbc606bcfba344e6 Mon Sep 17 00:00:00 2001 From: MontyTRC89 Date: Wed, 20 Nov 2024 05:59:04 +0100 Subject: [PATCH 082/112] Fixed dual paraboloid scene rendering in prevision of adding better ambient lighting --- TombEngine/Renderer/Renderer.h | 4 ++++ TombEngine/Renderer/RendererDraw.cpp | 6 +++--- TombEngine/Renderer/RendererDrawMenu.cpp | 18 +++++++++++++++++- TombEngine/Renderer/RendererEnums.h | 2 +- TombEngine/Renderer/RendererInit.cpp | 5 ++++- TombEngine/Shaders/RoomAmbient.fx | 24 +++++++++--------------- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 13e6a36d2..d8c509d04 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -373,6 +373,10 @@ namespace TEN::Renderer RenderTarget2D _SSAOBlurredRenderTarget; std::vector _SSAOKernel; + // New ambient light techinque + RenderTarget2D _roomAmbientMapFront; + RenderTarget2D _roomAmbientMapBack; + // Special effects std::vector _causticTextures; diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index 40dbdc78f..b798af960 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -1698,8 +1698,8 @@ namespace TEN::Renderer _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // TODO: Needs improvements before enabling it. - //RenderSimpleSceneToParaboloid(&_roomAmbientMapsCache[ambientMapCacheIndex].Front, LaraItem->Pose.Position.ToVector3(), 1); - //RenderSimpleSceneToParaboloid(&_roomAmbientMapsCache[ambientMapCacheIndex].Back, LaraItem->Pose.Position.ToVector3(), -1); + // RenderSimpleSceneToParaboloid(&_roomAmbientMapFront, LaraItem->Pose.Position.ToVector3(), 1); + // RenderSimpleSceneToParaboloid(&_roomAmbientMapBack, LaraItem->Pose.Position.ToVector3(), -1); // Bind and clear render target. _context->ClearRenderTargetView(_renderTarget.RenderTargetView.Get(), _debugPage == RendererDebugPage::WireframeMode ? Colors::DimGray : Colors::Black); @@ -2057,7 +2057,7 @@ namespace TEN::Renderer // final pixel colors if (Vector3::Distance(room->BoundingBox.Center, LaraItem->Pose.Position.ToVector3()) >= BLOCK(40)) { - continue; + //continue; } cameraConstantBuffer.CameraUnderwater = g_Level.Rooms[_rooms[i].RoomNumber].flags & ENV_FLAG_WATER; diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index 3b0dd0e6a..43d8548ad 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -1203,7 +1203,7 @@ namespace TEN::Renderer const auto& room = g_Level.Rooms[LaraItem->RoomNumber]; float aspectRatio = _screenWidth / (float)_screenHeight; - int thumbWidth = _screenWidth / 6; + int thumbWidth = _screenWidth / 8; auto rect = RECT{}; int thumbY = 0; @@ -1279,6 +1279,22 @@ namespace TEN::Renderer thumbY += thumbWidth / aspectRatio; } + rect.left = _screenWidth - thumbWidth; + rect.top = thumbY; + rect.right = rect.left + thumbWidth; + rect.bottom = rect.top + thumbWidth; + + _spriteBatch->Draw(_roomAmbientMapFront.ShaderResourceView.Get(), rect); + thumbY += thumbWidth; + + rect.left = _screenWidth - thumbWidth; + rect.top = thumbY; + rect.right = rect.left + thumbWidth; + rect.bottom = rect.top + thumbWidth; + + _spriteBatch->Draw(_roomAmbientMapBack.ShaderResourceView.Get(), rect); + thumbY += thumbWidth; + _spriteBatch->End(); break; diff --git a/TombEngine/Renderer/RendererEnums.h b/TombEngine/Renderer/RendererEnums.h index 51b53a81c..194752aab 100644 --- a/TombEngine/Renderer/RendererEnums.h +++ b/TombEngine/Renderer/RendererEnums.h @@ -64,7 +64,7 @@ constexpr auto MAX_FOG_BULBS_DRAW = 32; constexpr auto MAX_SPRITES_DRAW = 512; constexpr auto MAX_LENS_FLARES_DRAW = 8; -constexpr auto ROOM_AMBIENT_MAP_SIZE = 32; +constexpr auto ROOM_AMBIENT_MAP_SIZE = 64; constexpr auto MAX_ROOM_AMBIENT_MAPS = 10; enum class LightType diff --git a/TombEngine/Renderer/RendererInit.cpp b/TombEngine/Renderer/RendererInit.cpp index bac7979c3..44ab3ef55 100644 --- a/TombEngine/Renderer/RendererInit.cpp +++ b/TombEngine/Renderer/RendererInit.cpp @@ -257,7 +257,7 @@ namespace TEN::Renderer //_tempRoomAmbientRenderTarget2 = RenderTarget2D(_device.Get(), ROOM_AMBIENT_MAP_SIZE, ROOM_AMBIENT_MAP_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM, false, DXGI_FORMAT_D24_UNORM_S8_UINT); //_tempRoomAmbientRenderTarget3 = RenderTarget2D(_device.Get(), ROOM_AMBIENT_MAP_SIZE, ROOM_AMBIENT_MAP_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM, false, DXGI_FORMAT_D24_UNORM_S8_UINT); //_tempRoomAmbientRenderTarget4 = RenderTarget2D(_device.Get(), ROOM_AMBIENT_MAP_SIZE, ROOM_AMBIENT_MAP_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM, false, DXGI_FORMAT_D24_UNORM_S8_UINT); - + _SMAAAreaTexture = Texture2D(_device.Get(), AREATEX_WIDTH, AREATEX_HEIGHT, DXGI_FORMAT_R8G8_UNORM, AREATEX_PITCH, areaTexBytes); _SMAASearchTexture = Texture2D(_device.Get(), SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, DXGI_FORMAT_R8_UNORM, SEARCHTEX_PITCH, searchTexBytes); @@ -267,6 +267,9 @@ namespace TEN::Renderer InitializeSpriteQuad(); InitializeSky(); + _roomAmbientMapFront = RenderTarget2D(_device.Get(), ROOM_AMBIENT_MAP_SIZE, ROOM_AMBIENT_MAP_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM, false, DXGI_FORMAT_D32_FLOAT); + _roomAmbientMapBack = RenderTarget2D(_device.Get(), ROOM_AMBIENT_MAP_SIZE, ROOM_AMBIENT_MAP_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM, false, DXGI_FORMAT_D32_FLOAT); + _sortedPolygonsVertices.reserve(MAX_TRANSPARENT_VERTICES); _sortedPolygonsIndices.reserve(MAX_TRANSPARENT_VERTICES); _sortedPolygonsVertexBuffer = VertexBuffer(_device.Get(), MAX_TRANSPARENT_VERTICES, _sortedPolygonsVertices); diff --git a/TombEngine/Shaders/RoomAmbient.fx b/TombEngine/Shaders/RoomAmbient.fx index 0e50c5919..700720920 100644 --- a/TombEngine/Shaders/RoomAmbient.fx +++ b/TombEngine/Shaders/RoomAmbient.fx @@ -6,24 +6,18 @@ #include "./AnimatedTextures.hlsli" #include "./Shadows.hlsli" #include "./ShaderLight.hlsli" +#include "./CBStatic.hlsli" cbuffer RoomBuffer : register(b5) { - float2 CausticsStartUV; - float2 CausticsScale; - float4 AmbientColor; - ShaderLight RoomLights[MAX_LIGHTS_PER_ROOM]; - int NumRoomLights; - int Water; - int Caustics; - int Padding; -}; - -cbuffer SkyBuffer : register(b2) -{ - float4x4 World; - float4 Color; - int ApplyFogBulbs; + int Water; + int Caustics; + int NumRoomLights; + int Padding; + float2 CausticsStartUV; + float2 CausticsScale; + float4 AmbientColor; + ShaderLight RoomLights[MAX_LIGHTS_PER_ROOM]; }; struct PixelShaderInput From 2d0d13c620690fbc757325a9f1edee9dc62399c6 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:18:27 +0100 Subject: [PATCH 083/112] Implement GetCollidable and SetCollidable lua methods for moveables --- CHANGELOG.md | 5 +++-- TombEngine/Game/control/los.cpp | 2 +- .../Scripting/Internal/ReservedScriptNames.h | 2 ++ .../TEN/Objects/Moveable/MoveableObject.cpp | 19 +++++++++++++++++++ .../TEN/Objects/Moveable/MoveableObject.h | 2 ++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd8085dcc..545e00556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Allow more than 1000 static mesh slots in a level. ### Lua API changes +* Added Moveable:GetCollidable() and Moveable:SetCollidable() functions. * Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start. * Added support for transparency value in DisplayString class. * Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning. @@ -120,8 +121,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Added Flow.LensFlare() and Flow.Starfield() classes. * Added Inventory.GetUsedItem(), Inventory.SetUsedItem() and Inventory.ClearUsedItem() functions. * Added Input.KeyClearAll() function. -* Added Moveable.GetJointRotation() and optional 'offset' parameter for Moveable.GetJointPosition(). -* Added Moveable.GetTargetState() function. +* Added Moveable:GetJointRotation() and optional 'offset' parameter for Moveable.GetJointPosition(). +* Added Moveable:GetTargetState() function. * Added Room:GetRoomNumber() function. * Removed anims.monkeyAutoJump. It is now a player menu configuration. * Fixed Volume:GetActive() method. diff --git a/TombEngine/Game/control/los.cpp b/TombEngine/Game/control/los.cpp index 87939b760..733d2f309 100644 --- a/TombEngine/Game/control/los.cpp +++ b/TombEngine/Game/control/los.cpp @@ -592,7 +592,7 @@ int ObjectOnLOS2(GameVector* origin, GameVector* target, Vector3i* vec, MESH_INF if (priorityObjectID != GAME_OBJECT_ID::ID_NO_OBJECT && item.ObjectNumber != priorityObjectID) continue; - if (item.ObjectNumber != ID_LARA && Objects[item.ObjectNumber].collision == nullptr) + if (item.ObjectNumber != ID_LARA && (Objects[item.ObjectNumber].collision == nullptr || !item.Collidable)) continue; if (item.ObjectNumber == ID_LARA && priorityObjectID != ID_LARA) diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index ca2980f7d..54042b164 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -96,6 +96,8 @@ static constexpr char ScriptReserved_New[] = "New"; static constexpr char ScriptReserved_Init[] = "Init"; static constexpr char ScriptReserved_Enable[] = "Enable"; static constexpr char ScriptReserved_Disable[] = "Disable"; +static constexpr char ScriptReserved_GetCollidable[] = "GetCollidable"; +static constexpr char ScriptReserved_SetCollidable[] = "SetCollidable"; static constexpr char ScriptReserved_MakeInvisible[] = "MakeInvisible"; static constexpr char ScriptReserved_SetVisible[] = "SetVisible"; static constexpr char ScriptReserved_Explode[] = "Explode"; diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp index fa72ce12c..7308d8cf2 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp @@ -168,6 +168,9 @@ void Moveable::Register(sol::state& state, sol::table& parent) ScriptReserved_SetVisible, &Moveable::SetVisible, + ScriptReserved_SetCollidable, & Moveable::SetCollidable, + ScriptReserved_GetCollidable, & Moveable::GetCollidable, + /// Explode item. This also kills and disables item. // @function Moveable:Explode ScriptReserved_Explode, &Moveable::Explode, @@ -1166,6 +1169,22 @@ void Moveable::Shatter() KillItem(m_num); } +/// Get the item's collision state. +// @treturn bool item's collision state +// @function Moveable:GetCollidable +bool Moveable::GetCollidable() +{ + return m_item->Collidable; +} + +/// Set the item's collision. +// @bool collidable true if the caller should be collidable, false if no collision should occur. +// @function Moveable:SetCollidable +void Moveable::SetCollidable(bool isCollidable) +{ + m_item->Collidable = isCollidable; +} + /// Make the item invisible. Alias for `Moveable:SetVisible(false)`. // @function Moveable:MakeInvisible void Moveable::MakeInvisible() diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h index dfb795f31..2ad56a48a 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h @@ -118,6 +118,8 @@ public: void DisableItem(); void MakeInvisible(); void SetVisible(bool isVisible); + [[nodiscard]] bool GetCollidable(); + void SetCollidable(bool isCollidable); void Explode(); void Shatter(); From db68eeaca3436af858538edc512ead81567084bd Mon Sep 17 00:00:00 2001 From: Joey Quint Date: Wed, 20 Nov 2024 08:15:26 +0100 Subject: [PATCH 084/112] Expose wind (#1465) * Expose wind * Update EffectsFunctions.cpp --------- Co-authored-by: Sezz --- CHANGELOG.md | 2 ++ Documentation/doc/1 modules/Effects.html | 26 +++++++++++++++++++ .../Scripting/Internal/ReservedScriptNames.h | 1 + .../Internal/TEN/Effects/EffectsFunctions.cpp | 12 +++++++++ 4 files changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 545e00556..e25e64231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. * Fixed collision callbacks not properly clearing after leveljump. +* Added Effects.GetWind() function. + ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 ### Bug fixes diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index 27cd390a2..c7672b1db 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -143,6 +143,10 @@ MakeEarthquake(strength) Make an earthquake + + GetWind() + Get the current wind for the current frame. +
    @@ -498,6 +502,28 @@ +
    +
    + + GetWind() +
    +
    + Get the current wind for the current frame. + This represents the 3D displacement applied by the engine on things like particles affected by wind. + + + + +

    Returns:

    +
      + + Vec3 + The wind. +
    + + + +
    diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index 54042b164..de32a0aaf 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -301,6 +301,7 @@ static constexpr char ScriptReserved_EmitBlood[] = "EmitBlood"; static constexpr char ScriptReserved_EmitFire[] = "EmitFire"; static constexpr char ScriptReserved_MakeExplosion[] = "MakeExplosion"; static constexpr char ScriptReserved_MakeEarthquake[] = "MakeEarthquake"; +static constexpr char ScriptReserved_GetWind[] = "GetWind"; static constexpr char ScriptReserved_Vibrate[] = "Vibrate"; static constexpr char ScriptReserved_FlashScreen[] = "FlashScreen"; static constexpr char ScriptReserved_FadeIn[] = "FadeIn"; diff --git a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp index 9514e38e8..e3404b00e 100644 --- a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp +++ b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp @@ -10,6 +10,7 @@ #include "Game/effects/explosion.h" #include "Game/effects/spark.h" #include "Game/effects/tomb4fx.h" +#include "Game/effects/weather.h" #include "Game/Setup.h" #include "Objects/Utils/object_helper.h" #include "Scripting/Internal/LuaHandler.h" @@ -31,6 +32,7 @@ Functions to generate effects. using namespace TEN::Effects::DisplaySprite; using namespace TEN::Effects::Electricity; +using namespace TEN::Effects::Environment; using namespace TEN::Effects::Explosion; using namespace TEN::Effects::Spark; @@ -308,6 +310,15 @@ namespace TEN::Scripting::Effects Camera.bounce = -str; } + /// Get the wind vector for the current game frame. + // This represents the 3D displacement applied by the engine on things like particles affected by wind. + // @function GetWind() + // @treturn Vec3 Wind vector. + static Vec3 GetWind() + { + return Vec3(Weather.Wind()); + } + void Register(sol::state* state, sol::table& parent) { auto tableEffects = sol::table(state->lua_state(), sol::create); @@ -321,6 +332,7 @@ namespace TEN::Scripting::Effects tableEffects.set_function(ScriptReserved_MakeExplosion, &MakeExplosion); tableEffects.set_function(ScriptReserved_EmitFire, &EmitFire); tableEffects.set_function(ScriptReserved_MakeEarthquake, &Earthquake); + tableEffects.set_function(ScriptReserved_GetWind, &GetWind); auto handler = LuaHandler{ state }; handler.MakeReadOnlyTable(tableEffects, ScriptReserved_BlendID, BLEND_IDS); From f218644703dbf98586de24127b1e3decb4871031 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:21:56 +0100 Subject: [PATCH 085/112] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e25e64231..65ef8c398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Lua API changes * Added Moveable:GetCollidable() and Moveable:SetCollidable() functions. * Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start. +* Added Effects.GetWind() function to get current wind speed vector. * Added support for transparency value in DisplayString class. * Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. @@ -47,8 +48,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. * Fixed collision callbacks not properly clearing after leveljump. -* Added Effects.GetWind() function. - ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 ### Bug fixes From 1de0f275116b7d2f1162457e3bdcda1762483ae5 Mon Sep 17 00:00:00 2001 From: MontyTRC89 Date: Wed, 20 Nov 2024 09:09:23 +0100 Subject: [PATCH 086/112] Switched to LUT for statics --- TombEngine/Game/Lara/lara_one_gun.cpp | 2 +- TombEngine/Game/Setup.cpp | 2 +- TombEngine/Game/Setup.h | 5 +++- TombEngine/Game/collision/collide_item.cpp | 2 +- TombEngine/Game/control/los.cpp | 2 +- TombEngine/Game/effects/debris.cpp | 2 +- TombEngine/Game/pickup/pickup.cpp | 2 +- TombEngine/Game/room.cpp | 4 +-- .../Objects/TR4/Entity/tr4_knight_templar.cpp | 2 +- .../Objects/TR4/Entity/tr4_skeleton.cpp | 2 +- TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp | 2 +- TombEngine/Objects/TR4/Trap/SpikyWall.cpp | 2 +- .../Objects/TR5/Entity/tr5_gladiator.cpp | 2 +- TombEngine/Objects/TR5/Entity/tr5_gunship.cpp | 2 +- .../Objects/TR5/Entity/tr5_roman_statue.cpp | 2 +- TombEngine/Objects/TR5/Trap/tr5_explosion.cpp | 2 +- TombEngine/Renderer/Renderer.h | 7 ++++- TombEngine/Renderer/RendererCompatibility.cpp | 10 +++---- TombEngine/Renderer/RendererDraw.cpp | 6 ++--- TombEngine/Renderer/RendererFrame.cpp | 6 ++--- TombEngine/Sound/sound.cpp | 2 +- TombEngine/Specific/level.cpp | 26 ++++++++++++++++--- TombEngine/Specific/level.h | 2 ++ 23 files changed, 62 insertions(+), 34 deletions(-) diff --git a/TombEngine/Game/Lara/lara_one_gun.cpp b/TombEngine/Game/Lara/lara_one_gun.cpp index 85cc23978..cef264283 100644 --- a/TombEngine/Game/Lara/lara_one_gun.cpp +++ b/TombEngine/Game/Lara/lara_one_gun.cpp @@ -1568,7 +1568,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p hasHit = hasHitNotByEmitter = doShatter = true; doExplosion = isExplosive; - if (StaticObjects[staticPtr->staticNumber].shatterType == ShatterType::None) + if (GetStaticObject(staticPtr->staticNumber).shatterType == ShatterType::None) continue; staticPtr->HitPoints -= damage; diff --git a/TombEngine/Game/Setup.cpp b/TombEngine/Game/Setup.cpp index 1d0e348c6..3b8f770d7 100644 --- a/TombEngine/Game/Setup.cpp +++ b/TombEngine/Game/Setup.cpp @@ -33,7 +33,7 @@ using namespace TEN::Entities; using namespace TEN::Entities::Switches; ObjectHandler Objects; -std::unordered_map StaticObjects; +std::vector StaticObjects; void ObjectHandler::Initialize() { diff --git a/TombEngine/Game/Setup.h b/TombEngine/Game/Setup.h index 967a1b944..e87c471da 100644 --- a/TombEngine/Game/Setup.h +++ b/TombEngine/Game/Setup.h @@ -132,10 +132,13 @@ struct StaticInfo GameBoundingBox collisionBox; ShatterType shatterType; int shatterSound; + int ObjectNumber; }; extern ObjectHandler Objects; -extern std::unordered_map StaticObjects; +extern std::vector StaticObjects; + +#define GetStaticObject(x) StaticObjects[StaticObjectsLUT[x]] void InitializeGameFlags(); void InitializeSpecialEffects(); diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index c51a5c7d5..070aeb390 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -1888,7 +1888,7 @@ void DoObjectCollision(ItemInfo* item, CollisionInfo* coll) // HACK: Shatter statics only by harmful vehicles. if (!isPlayer && !isHarmless && abs(item->Animation.Velocity.z) > VEHICLE_COLLISION_TERMINAL_VELOCITY && - StaticObjects[staticObject.staticNumber].shatterType != ShatterType::None) + GetStaticObject(staticObject.staticNumber).shatterType != ShatterType::None) { SoundEffect(GetShatterSound(staticObject.staticNumber), &staticObject.pos); ShatterObject(nullptr, &staticObject, -128, item->RoomNumber, 0); diff --git a/TombEngine/Game/control/los.cpp b/TombEngine/Game/control/los.cpp index 87939b760..7d3f690d9 100644 --- a/TombEngine/Game/control/los.cpp +++ b/TombEngine/Game/control/los.cpp @@ -287,7 +287,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo { if (itemNumber < 0) { - if (StaticObjects[mesh->staticNumber].shatterType != ShatterType::None) + if (GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) { const auto& weapon = Weapons[(int)Lara.Control.Weapon.GunType]; mesh->HitPoints -= weapon.Damage; diff --git a/TombEngine/Game/effects/debris.cpp b/TombEngine/Game/effects/debris.cpp index 560bc9000..9e1c226c5 100644 --- a/TombEngine/Game/effects/debris.cpp +++ b/TombEngine/Game/effects/debris.cpp @@ -70,7 +70,7 @@ void ShatterObject(SHATTER_ITEM* item, MESH_INFO* mesh, int num, short roomNumbe return; isStatic = true; - meshIndex = StaticObjects[mesh->staticNumber].meshNumber; + meshIndex = GetStaticObject(mesh->staticNumber).meshNumber; yRot = mesh->pos.Orientation.y; pos = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z); scale = mesh->scale; diff --git a/TombEngine/Game/pickup/pickup.cpp b/TombEngine/Game/pickup/pickup.cpp index e602a0590..d0777af21 100644 --- a/TombEngine/Game/pickup/pickup.cpp +++ b/TombEngine/Game/pickup/pickup.cpp @@ -903,7 +903,7 @@ void DropPickups(ItemInfo* item) for (auto* staticPtr : collObjects.Statics) { - auto& object = StaticObjects[staticPtr->staticNumber]; + auto& object = GetStaticObject(staticPtr->staticNumber); auto box = object.collisionBox.ToBoundingOrientedBox(staticPtr->pos); if (box.Intersects(sphere)) diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index c2e4dc0eb..1a1171e08 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -255,11 +255,11 @@ GameBoundingBox& GetBoundsAccurate(const MESH_INFO& mesh, bool getVisibilityBox) if (getVisibilityBox) { - bounds = StaticObjects[mesh.staticNumber].visibilityBox * mesh.scale; + bounds = GetStaticObject(mesh.staticNumber).visibilityBox * mesh.scale; } else { - bounds = StaticObjects[mesh.staticNumber].collisionBox * mesh.scale; + bounds = GetStaticObject(mesh.staticNumber).collisionBox * mesh.scale; } return bounds; diff --git a/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp b/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp index 3ed121e7b..9ea5586aa 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp @@ -194,7 +194,7 @@ namespace TEN::Entities::TR4 { if (abs(pos.x - mesh.pos.Position.x) < BLOCK(1) && abs(pos.z - mesh.pos.Position.z) < BLOCK(1) && - StaticObjects[mesh.staticNumber].shatterType == ShatterType::None) + GetStaticObject(mesh.staticNumber).shatterType == ShatterType::None) { ShatterObject(nullptr, &mesh, -64, LaraItem->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); diff --git a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp index df8eaf7d8..2a1b8d36f 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp @@ -652,7 +652,7 @@ namespace TEN::Entities::TR4 if (abs(pos.x - staticMesh->pos.Position.x) < BLOCK(1) && abs(pos.z - staticMesh->pos.Position.z) < BLOCK(1) && - StaticObjects[staticMesh->staticNumber].shatterType != ShatterType::None) + GetStaticObject(staticMesh->staticNumber).shatterType != ShatterType::None) { ShatterObject(0, staticMesh, -128, LaraItem->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); diff --git a/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp b/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp index e6d194981..fd0d6f5d2 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp @@ -95,7 +95,7 @@ namespace TEN::Entities::TR4 if (((mesh->pos.Position.z / BLOCK(1)) == (z / BLOCK(1))) && ((mesh->pos.Position.x / BLOCK(1)) == (x / BLOCK(1))) && - StaticObjects[mesh->staticNumber].shatterType != ShatterType::None) + GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) { ShatterObject(nullptr, mesh, -64, item->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); diff --git a/TombEngine/Objects/TR4/Trap/SpikyWall.cpp b/TombEngine/Objects/TR4/Trap/SpikyWall.cpp index 225b5efe1..6fed5fbcc 100644 --- a/TombEngine/Objects/TR4/Trap/SpikyWall.cpp +++ b/TombEngine/Objects/TR4/Trap/SpikyWall.cpp @@ -91,7 +91,7 @@ namespace TEN::Entities::Traps abs(pointColl0.GetPosition().z - mesh.pos.Position.z) < BLOCK(1)) || abs(pointColl1.GetPosition().x - mesh.pos.Position.x) < BLOCK(1) && abs(pointColl1.GetPosition().z - mesh.pos.Position.z) < BLOCK(1) && - StaticObjects[mesh.staticNumber].shatterType != ShatterType::None) + GetStaticObject(mesh.staticNumber).shatterType != ShatterType::None) { if (mesh.HitPoints != 0) continue; diff --git a/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp b/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp index cd8759512..b69dd1b03 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp @@ -348,7 +348,7 @@ namespace TEN::Entities::Creatures::TR5 { if (!((pos.x ^ mesh->pos.Position.x) & 0xFFFFFC00)) { - if (StaticObjects[mesh->staticNumber].shatterType != ShatterType::None) + if (GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) { ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0); //SoundEffect(ShatterSounds[gfCurrentLevel - 5][*(v28 + 18)], v28); diff --git a/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp b/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp index 39957dfa2..8e570d7f3 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp @@ -109,7 +109,7 @@ namespace TEN::Entities::Creatures::TR5 if (objOnLos < 0 && GetRandomControl() & 1) { - if (StaticObjects[hitMesh->staticNumber].shatterType != ShatterType::None) + if (GetStaticObject(hitMesh->staticNumber).shatterType != ShatterType::None) { ShatterObject(0, hitMesh, 64, target.RoomNumber, 0); TestTriggers(hitMesh->pos.Position.x, hitMesh->pos.Position.y, hitMesh->pos.Position.z, target.RoomNumber, true); diff --git a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp index 3c36ce5c1..8f427dd2d 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp @@ -558,7 +558,7 @@ namespace TEN::Entities::Creatures::TR5 if (!((mesh->pos.Position.z ^ pos.z) & 0xFFFFFC00) && !((mesh->pos.Position.x ^ pos.x) & 0xFFFFFC00)) { - if (StaticObjects[mesh->staticNumber].shatterType != ShatterType::None) + if (GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) { ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0); SoundEffect(GetShatterSound(mesh->staticNumber), (Pose*)mesh); diff --git a/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp b/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp index b80b0de18..04c5d77f5 100644 --- a/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp +++ b/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp @@ -163,7 +163,7 @@ namespace TEN::Entities::Traps for (auto* staticPtr : collObjects.Statics) { - if (StaticObjects[staticPtr->staticNumber].shatterType != ShatterType::None) + if (GetStaticObject(staticPtr->staticNumber).shatterType != ShatterType::None) { TriggerExplosionSparks(staticPtr->pos.Position.x, staticPtr->pos.Position.y, staticPtr->pos.Position.z, 3, -2, 0, item.RoomNumber); staticPtr->pos.Position.y -= 128; diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index de185cc19..2aac47518 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -259,7 +259,7 @@ namespace TEN::Renderer // Textures, objects and sprites std::vector> _moveableObjects; - std::unordered_map> _staticObjects; // Key = static ID, value = renderer object. + std::vector> _staticObjects; // Key = static ID, value = renderer object. std::vector _sprites; std::vector _spriteSequences; std::vector _animatedTextureSets; @@ -614,6 +614,11 @@ namespace TEN::Renderer blendMode == BlendMode::FastAlphaBlend); } + inline RendererObject& GetStaticRendererObject(short objectNumber) + { + return _staticObjects[StaticObjectsLUT[objectNumber]].value(); + } + public: Renderer(); ~Renderer(); diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 2ed6c5ee3..9fea89120 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -272,7 +272,7 @@ namespace TEN::Renderer staticInfo->AmbientLight = r->AmbientLight; staticInfo->Pose = oldMesh->pos; staticInfo->Scale = oldMesh->scale; - staticInfo->OriginalSphere = StaticObjects[staticInfo->ObjectNumber].visibilityBox.ToLocalBoundingSphere(); + staticInfo->OriginalSphere = GetStaticObject(staticInfo->ObjectNumber).visibilityBox.ToLocalBoundingSphere(); staticInfo->IndexInRoom = l; staticInfo->Update(); @@ -877,7 +877,7 @@ namespace TEN::Renderer totalVertices = 0; totalIndices = 0; - for (const auto& [staticID, staticObj] : StaticObjects) + for (const auto& staticObj : StaticObjects) { const auto& mesh = g_Level.Meshes[staticObj.meshNumber]; for (const auto& bucket : mesh.buckets) @@ -892,18 +892,18 @@ namespace TEN::Renderer lastVertex = 0; lastIndex = 0; - for (const auto& [staticID, staticObj] : StaticObjects) + for (const auto& staticObj : StaticObjects) { auto newStaticObj = RendererObject(); newStaticObj.Type = 1; - newStaticObj.Id = staticID; + newStaticObj.Id = staticObj.ObjectNumber; auto& mesh = *GetRendererMeshFromTrMesh(&newStaticObj, &g_Level.Meshes[staticObj.meshNumber], 0, false, false, &lastVertex, &lastIndex); newStaticObj.ObjectMeshes.push_back(&mesh); _meshes.push_back(&mesh); - _staticObjects[staticID] = newStaticObj; + _staticObjects.push_back(newStaticObj); } _staticsVertexBuffer = VertexBuffer(_device.Get(), (int)_staticsVertices.size(), _staticsVertices.data()); diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index b798af960..0086e1fc2 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -2406,7 +2406,7 @@ namespace TEN::Renderer std::vector statics = it->second; RendererStatic* refStatic = statics[0]; - RendererObject& refStaticObj = *_staticObjects[refStatic->ObjectNumber]; + RendererObject& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber); if (refStaticObj.ObjectMeshes.size() == 0) continue; @@ -2484,7 +2484,7 @@ namespace TEN::Renderer std::vector statics = it->second; RendererStatic* refStatic = statics[0]; - RendererObject& refStaticObj = *_staticObjects[refStatic->ObjectNumber]; + RendererObject& refStaticObj = GetStaticRendererObject(refStatic->ObjectNumber); if (refStaticObj.ObjectMeshes.size() == 0) continue; @@ -3530,7 +3530,7 @@ namespace TEN::Renderer _stStatic.World = objectInfo->Static->World; _stStatic.Color = objectInfo->Static->Color; _stStatic.AmbientLight = objectInfo->Room->AmbientLight; - _stStatic.LightMode = (int)_staticObjects[objectInfo->Static->ObjectNumber]->ObjectMeshes[0]->LightMode; + _stStatic.LightMode = (int)GetStaticRendererObject(objectInfo->Static->ObjectNumber).ObjectMeshes[0]->LightMode; BindStaticLights(objectInfo->Static->LightsToDraw); _cbStatic.UpdateData(_stStatic, _context.Get()); diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index 3603157bb..c5244ef1e 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -471,7 +471,7 @@ namespace TEN::Renderer { mesh->ObjectNumber = nativeMesh->staticNumber; mesh->Color = nativeMesh->color; - mesh->OriginalSphere = StaticObjects[mesh->ObjectNumber].visibilityBox.ToLocalBoundingSphere(); + mesh->OriginalSphere = GetStaticObject(mesh->ObjectNumber).visibilityBox.ToLocalBoundingSphere(); mesh->Pose = nativeMesh->pos; mesh->Scale = nativeMesh->scale; mesh->Update(); @@ -482,10 +482,10 @@ namespace TEN::Renderer if (!(nativeMesh->flags & StaticMeshFlags::SM_VISIBLE)) continue; - if (!_staticObjects[mesh->ObjectNumber].has_value()) + if (!_staticObjects[StaticObjectsLUT[mesh->ObjectNumber]].has_value()) continue; - auto& obj = *_staticObjects[mesh->ObjectNumber]; + auto& obj = GetStaticRendererObject(mesh->ObjectNumber); if (obj.ObjectMeshes.empty()) continue; diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index 49fffcb50..7604eed83 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -1084,7 +1084,7 @@ void PlaySecretTrack() int GetShatterSound(int shatterID) { - auto fxID = StaticObjects[shatterID].shatterSound; + auto fxID = GetStaticObject(shatterID).shatterSound; if (fxID != -1 && fxID < NUM_SFX) return fxID; diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index b85031596..a88807274 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -81,6 +81,8 @@ const std::vector BRIDGE_OBJECT_IDS = LEVEL g_Level; std::vector MoveablesIds; +int* StaticObjectsLUT = nullptr; +int StaticObjectsLUTSize = 0; std::vector SpriteSequencesIds; char* DataPtr; @@ -272,6 +274,13 @@ void LoadObjects() Objects.Initialize(); StaticObjects.clear(); + if (StaticObjectsLUT != nullptr) + { + delete StaticObjectsLUT; + } + StaticObjectsLUTSize = 1000; + StaticObjectsLUT = (int*)malloc(StaticObjectsLUTSize * sizeof(int)); + int meshCount = ReadInt32(); TENLog("Mesh count: " + std::to_string(meshCount), LogLevel::Info); @@ -448,8 +457,7 @@ void LoadObjects() { auto staticObj = StaticInfo{}; - int staticID = ReadInt32(); - + staticObj.ObjectNumber = ReadInt32(); staticObj.meshNumber = ReadInt32(); staticObj.visibilityBox.X1 = ReadInt16(); @@ -471,7 +479,17 @@ void LoadObjects() staticObj.shatterType = (ShatterType)ReadInt16(); staticObj.shatterSound = ReadInt16(); - StaticObjects[staticID] = staticObj; + if (staticObj.ObjectNumber >= StaticObjectsLUTSize) + { + int* LUT = (int*)malloc(staticObj.ObjectNumber * sizeof(int)); + memcpy(LUT, StaticObjectsLUT, StaticObjectsLUTSize * sizeof(int)); + delete StaticObjectsLUT; + StaticObjectsLUT = LUT; + StaticObjectsLUTSize = staticObj.ObjectNumber; + } + + StaticObjectsLUT[staticObj.ObjectNumber] = (int)StaticObjects.size(); + StaticObjects.push_back(staticObj); } } @@ -1589,7 +1607,7 @@ void LoadSprites() short offset = ReadInt16(); if (spriteID >= ID_NUMBER_OBJECTS) { - StaticObjects[spriteID - ID_NUMBER_OBJECTS].meshNumber = offset; + GetStaticObject(spriteID - ID_NUMBER_OBJECTS).meshNumber = offset; } else { diff --git a/TombEngine/Specific/level.h b/TombEngine/Specific/level.h index f0a272827..7942b68d3 100644 --- a/TombEngine/Specific/level.h +++ b/TombEngine/Specific/level.h @@ -137,6 +137,8 @@ struct LEVEL extern const std::vector BRIDGE_OBJECT_IDS; extern std::vector MoveablesIds; +extern int* StaticObjectsLUT; +extern int StaticObjectsLUTSize; extern std::vector SpriteSequencesIds; extern LEVEL g_Level; extern int SystemNameHash; From 88f21a98d4c05c3df0f31a751775b958f116ca73 Mon Sep 17 00:00:00 2001 From: Sezz Date: Wed, 20 Nov 2024 21:51:22 +1100 Subject: [PATCH 087/112] Update CONTRIBUTING.md --- CONTRIBUTING.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d0a82519e..e378ab5bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -370,6 +370,30 @@ Leave them as is. This is good: `auto x = std::vector();` Use `//`-styled comments where possible. Only use `/* */` style in case you are about to temporarily comment certain block for testing purposes or when writing a comment that will serve as the source for generated documentation. +Use a `NOTE: ` prefix in your comment if you want to highlight something particularly noteworthy: +```c +// NOTE: Will not work for bones at ends of hierarchies. +float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex) +{ + const auto& object = Objects[objectID]; + + if (object.nmeshes == boneIndex) + return 0.0f; + + auto nextBoneOffset = GetJointOffset(objectID, boneIndex + 1); + return nextBoneOffset.Length(); +} +``` + +Use a `FAILSAFE: ` prefix in your comment if you want to highlight a particularly quirky solution without an obvious and clear purpose: +```c +if (portalRoomNumber != NO_VALUE && + rayRoomNumber != portalRoomNumber) // FAILSAFE: Prevent infinite loop if room portal leads back to itself. +{ + player.Explode(); +} +``` + ## Branches and pull requests Make sure that epic branches (tens or hundreds of files changed due to renames, namespace wrappings, etc) **are focused on a single feature or task**. Don't jump in to others epic branches with another round of your epic changes. It masks bugs and makes review process very cumbersome. From 91081c9f0d12f96c84dceff0d3f47d0acfce99c3 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:54:39 +0300 Subject: [PATCH 088/112] Freeze event and OnUseItem callbacks (#1463) * Add use item callbacks, add menu boilerplate code * Set up scripting API for game mode * Use word "break" instead of "menu" everywhere * Rudimentary implementation of BreakPhase * More progress * Doc update * Always active lensflare * Update CHANGELOG.md * Fix hair interpolation for spectator mode * Update target highlighter and other HUD elements while in break mode * Fix various problems * Don't try to update camera in spectator mode * Cleanup camera code * Rename break to freeze * Update FlowHandler.cpp * Update Flow.html * Update control.cpp * Rename method * Add displaystring owner to differentiate between freeze modes * Disable triggers and volumes in freeze mode * Update control.cpp * Rename enum * Update CHANGELOG.md * Implement full freeze, correct callback behaviour * Update RendererDrawMenu.cpp * Allow DumpGameScene to do partial pass * Remove hack * Introduce render mode enum to render scene without any postprocess * Tidying * Fix problems with items or renderer not updating before entering freeze mode * Fix rebase errors * Use interpolation factor getter instead of raw value everywhere * Update documentation * Fixed setting freeze mode from OnStart * Fixed freeze mode not working in OnStart event * Revert "Fixed freeze mode not working in OnStart event" This reverts commit 87f6fbdc6d776bb7813133a84c8ba9d0b1171f3b. * Bypass cinematic bars and fades for non-full render modes --------- Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Co-authored-by: Sezz --- CHANGELOG.md | 1 + Documentation/doc/1 modules/Effects.html | 9 +- Documentation/doc/1 modules/Flow.html | 54 +++++ Documentation/doc/1 modules/Input.html | 1 + Documentation/doc/1 modules/Inventory.html | 1 + Documentation/doc/1 modules/Logic.html | 11 +- Documentation/doc/1 modules/Objects.html | 1 + Documentation/doc/1 modules/Sound.html | 1 + Documentation/doc/1 modules/Strings.html | 1 + Documentation/doc/1 modules/Util.html | 1 + Documentation/doc/1 modules/View.html | 1 + .../doc/2 classes/Flow.Animations.html | 1 + Documentation/doc/2 classes/Flow.Fog.html | 1 + .../doc/2 classes/Flow.InventoryItem.html | 1 + .../doc/2 classes/Flow.LensFlare.html | 1 + Documentation/doc/2 classes/Flow.Level.html | 1 + Documentation/doc/2 classes/Flow.Mirror.html | 1 + .../doc/2 classes/Flow.Settings.html | 1 + .../doc/2 classes/Flow.SkyLayer.html | 1 + .../doc/2 classes/Flow.Starfield.html | 1 + .../doc/2 classes/Objects.AIObject.html | 1 + .../doc/2 classes/Objects.Camera.html | 1 + .../doc/2 classes/Objects.LaraObject.html | 1 + .../doc/2 classes/Objects.Moveable.html | 52 +++++ Documentation/doc/2 classes/Objects.Room.html | 1 + Documentation/doc/2 classes/Objects.Sink.html | 1 + .../doc/2 classes/Objects.SoundSource.html | 1 + .../doc/2 classes/Objects.Static.html | 1 + .../doc/2 classes/Objects.Volume.html | 1 + .../doc/2 classes/Strings.DisplayString.html | 1 + .../doc/2 classes/View.DisplaySprite.html | 1 + .../doc/3 primitive classes/Color.html | 1 + .../doc/3 primitive classes/Rotation.html | 1 + .../doc/3 primitive classes/Vec2.html | 1 + .../doc/3 primitive classes/Vec3.html | 1 + .../doc/4 enums/Effects.BlendID.html | 1 + .../doc/4 enums/Effects.EffectID.html | 1 + Documentation/doc/4 enums/Flow.BreakMode.html | 166 ++++++++++++++ .../doc/4 enums/Flow.FreezeMode.html | 167 ++++++++++++++ Documentation/doc/4 enums/Flow.GameMode.html | 165 ++++++++++++++ .../doc/4 enums/Flow.GameStatus.html | 1 + Documentation/doc/4 enums/Input.ActionID.html | 1 + .../doc/4 enums/Objects.AmmoType.html | 1 + .../doc/4 enums/Objects.MoveableStatus.html | 1 + Documentation/doc/4 enums/Objects.ObjID.html | 1 + .../doc/4 enums/Objects.RoomFlagID.html | 1 + .../doc/4 enums/Objects.RoomReverb.html | 1 + .../doc/4 enums/Sound.SoundTrackType.html | 1 + .../4 enums/Strings.DisplayStringOption.html | 1 + Documentation/doc/4 enums/Util.LogLevel.html | 1 + Documentation/doc/4 enums/View.AlignMode.html | 1 + .../doc/4 enums/View.CameraType.html | 1 + .../doc/4 enums/View.PostProcessMode.html | 1 + Documentation/doc/4 enums/View.ScaleMode.html | 1 + .../5 lua utility modules/EventSequence.html | 1 + .../doc/5 lua utility modules/Timer.html | 1 + Documentation/doc/index.html | 5 + TombEngine/Game/Setup.cpp | 1 + TombEngine/Game/Setup.h | 16 +- TombEngine/Game/camera.cpp | 20 +- TombEngine/Game/camera.h | 6 - TombEngine/Game/control/control.cpp | 107 ++++++++- TombEngine/Game/control/control.h | 8 + TombEngine/Game/control/event.h | 1 + TombEngine/Game/control/trigger.cpp | 6 + TombEngine/Game/control/volume.cpp | 3 + TombEngine/Game/gui.cpp | 8 +- TombEngine/Game/items.cpp | 14 +- TombEngine/Game/savegame.cpp | 34 ++- TombEngine/Objects/Effects/effect_objects.cpp | 1 + TombEngine/Renderer/Renderer.h | 9 +- TombEngine/Renderer/RendererDraw.cpp | 87 ++++---- TombEngine/Renderer/RendererDrawEffect.cpp | 206 +++++++++--------- TombEngine/Renderer/RendererDrawMenu.cpp | 48 +++- TombEngine/Renderer/RendererEnums.h | 7 + TombEngine/Renderer/RendererFrame.cpp | 23 +- TombEngine/Renderer/RendererHelper.cpp | 4 +- TombEngine/Renderer/RendererLara.cpp | 6 +- TombEngine/Renderer/RendererPostProcess.cpp | 9 +- .../Include/Flow/ScriptInterfaceFlowHandler.h | 12 +- .../Scripting/Include/ScriptInterfaceGame.h | 13 +- .../Scripting/Internal/ReservedScriptNames.h | 13 +- .../Internal/TEN/Flow/Enums/FreezeModes.h | 33 +++ .../TEN/Flow/{ => Enums}/GameStatuses.h | 0 .../Internal/TEN/Flow/FlowHandler.cpp | 30 ++- .../Scripting/Internal/TEN/Flow/FlowHandler.h | 2 + .../Internal/TEN/Logic/LogicHandler.cpp | 102 +++++++-- .../Internal/TEN/Logic/LogicHandler.h | 50 +++-- .../Strings/DisplayString/DisplayString.cpp | 11 +- .../TEN/Strings/DisplayString/DisplayString.h | 6 +- .../Internal/TEN/Strings/StringsHandler.cpp | 2 +- TombEngine/Specific/clock.cpp | 1 - .../flatbuffers/ten_savegame_generated.h | 86 +++++++- .../Specific/savegame/schema/ten_savegame.fbs | 4 + TombEngine/TombEngine.vcxproj | 3 +- 95 files changed, 1379 insertions(+), 291 deletions(-) create mode 100644 Documentation/doc/4 enums/Flow.BreakMode.html create mode 100644 Documentation/doc/4 enums/Flow.FreezeMode.html create mode 100644 Documentation/doc/4 enums/Flow.GameMode.html create mode 100644 TombEngine/Scripting/Internal/TEN/Flow/Enums/FreezeModes.h rename TombEngine/Scripting/Internal/TEN/Flow/{ => Enums}/GameStatuses.h (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ef8c398..cad5bff65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ### Lua API changes * Added Moveable:GetCollidable() and Moveable:SetCollidable() functions. +* Added Flow.GetFreezeMode() and Flow.SetFreezeMode() functions. * Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start. * Added Effects.GetWind() function to get current wind speed vector. * Added support for transparency value in DisplayString class. diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index c7672b1db..51af05ba6 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -77,6 +77,7 @@
    • Effects.BlendID
    • Effects.EffectID
    • +
    • Flow.FreezeMode
    • Flow.GameStatus
    • Input.ActionID
    • Objects.AmmoType
    • @@ -145,7 +146,7 @@ GetWind() - Get the current wind for the current frame. + Get the wind vector for the current game frame. @@ -508,8 +509,8 @@ GetWind()
      - Get the current wind for the current frame. - This represents the 3D displacement applied by the engine on things like particles affected by wind. + Get the wind vector for the current game frame. + This represents the 3D displacement applied by the engine on things like particles affected by wind.() @@ -518,7 +519,7 @@
        Vec3 - The wind. + Wind vector.
      diff --git a/Documentation/doc/1 modules/Flow.html b/Documentation/doc/1 modules/Flow.html index 089568ddd..4b3b5f8da 100644 --- a/Documentation/doc/1 modules/Flow.html +++ b/Documentation/doc/1 modules/Flow.html @@ -77,6 +77,7 @@
      +
      + + GetFreezeMode() +
      +
      + Get current freeze mode, such as none, full, spectator or player. + + + + +

      Returns:

      +
        + + FreezeMode + the current freeze mode +
      + + + + +
      +
      + + SetFreezeMode(new) +
      +
      + Set current freeze mode, such as none, full, spectator or player.
      +Freeze mode specifies whether game is in normal mode or paused in a particular way to allow +custom menu creation, photo mode or time freeze. + + + +

      Parameters:

      + + + + + +
      diff --git a/Documentation/doc/1 modules/Input.html b/Documentation/doc/1 modules/Input.html index 3cfee25d8..84989a588 100644 --- a/Documentation/doc/1 modules/Input.html +++ b/Documentation/doc/1 modules/Input.html @@ -77,6 +77,7 @@
      • Effects.BlendID
      • Effects.EffectID
      • +
      • Flow.FreezeMode
      • Flow.GameStatus
      • Input.ActionID
      • Objects.AmmoType
      • diff --git a/Documentation/doc/1 modules/Inventory.html b/Documentation/doc/1 modules/Inventory.html index 15b6c8995..edc09052a 100644 --- a/Documentation/doc/1 modules/Inventory.html +++ b/Documentation/doc/1 modules/Inventory.html @@ -77,6 +77,7 @@
        • Effects.BlendID
        • Effects.EffectID
        • +
        • Flow.FreezeMode
        • Flow.GameStatus
        • Input.ActionID
        • Objects.AmmoType
        • diff --git a/Documentation/doc/1 modules/Logic.html b/Documentation/doc/1 modules/Logic.html index 363e56e43..0ec446dba 100644 --- a/Documentation/doc/1 modules/Logic.html +++ b/Documentation/doc/1 modules/Logic.html @@ -77,6 +77,7 @@
          • Effects.BlendID
          • Effects.EffectID
          • +
          • Flow.FreezeMode
          • Flow.GameStatus
          • Input.ActionID
          • Objects.AmmoType
          • @@ -184,6 +185,9 @@ PRESAVE -- will be called immediately before OnSave PRELOAD -- will be called immediately before OnLoad POSTLOAD -- will be called immediately after OnLoad +PREFREEZE -- will be called before entering freeze mode +POSTFREEZE -- will be called immediately after exiting freeze mode + -- These take a LevelEndReason arg, like OnEnd PREEND -- will be called immediately before OnEnd POSTEND -- will be called immediately after OnEnd @@ -191,6 +195,10 @@ PRELOAD -- will be called immediately before OnLoad -- These take functions which accepts a deltaTime argument PRELOOP -- will be called in the beginning of game loop POSTLOOP -- will be called at the end of game loop + +-- These take functions which accepts an objectNumber argument, like OnUseItem +PREUSEITEM -- will be called immediately before OnUseItem +POSTUSEITEM -- will be called immediately after OnUseItem

            The order in which two functions with the same CallbackPoint are called is undefined. i.e. if you register MyFunc and MyFunc2 with PRELOOP, both will be called in the beginning of game loop, but there is no guarantee that MyFunc will be called before MyFunc2, or vice-versa.

            @@ -267,7 +275,8 @@ SAVE START END LOOP -USEITEM +USEITEM +MENU

            Parameters:

            diff --git a/Documentation/doc/1 modules/Objects.html b/Documentation/doc/1 modules/Objects.html index 00de82c8f..e84b3a924 100644 --- a/Documentation/doc/1 modules/Objects.html +++ b/Documentation/doc/1 modules/Objects.html @@ -77,6 +77,7 @@
            • Effects.BlendID
            • Effects.EffectID
            • +
            • Flow.FreezeMode
            • Flow.GameStatus
            • Input.ActionID
            • Objects.AmmoType
            • diff --git a/Documentation/doc/1 modules/Sound.html b/Documentation/doc/1 modules/Sound.html index 29b5a1eee..055fd38e5 100644 --- a/Documentation/doc/1 modules/Sound.html +++ b/Documentation/doc/1 modules/Sound.html @@ -77,6 +77,7 @@
              • Effects.BlendID
              • Effects.EffectID
              • +
              • Flow.FreezeMode
              • Flow.GameStatus
              • Input.ActionID
              • Objects.AmmoType
              • diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html index f7b0c3421..2f6aff820 100644 --- a/Documentation/doc/1 modules/Strings.html +++ b/Documentation/doc/1 modules/Strings.html @@ -77,6 +77,7 @@
                • Effects.BlendID
                • Effects.EffectID
                • +
                • Flow.FreezeMode
                • Flow.GameStatus
                • Input.ActionID
                • Objects.AmmoType
                • diff --git a/Documentation/doc/1 modules/Util.html b/Documentation/doc/1 modules/Util.html index ad9eb794a..ea840a4e8 100644 --- a/Documentation/doc/1 modules/Util.html +++ b/Documentation/doc/1 modules/Util.html @@ -77,6 +77,7 @@
                  • Effects.BlendID
                  • Effects.EffectID
                  • +
                  • Flow.FreezeMode
                  • Flow.GameStatus
                  • Input.ActionID
                  • Objects.AmmoType
                  • diff --git a/Documentation/doc/1 modules/View.html b/Documentation/doc/1 modules/View.html index 4938f9ba7..e5d515495 100644 --- a/Documentation/doc/1 modules/View.html +++ b/Documentation/doc/1 modules/View.html @@ -77,6 +77,7 @@
                    • Effects.BlendID
                    • Effects.EffectID
                    • +
                    • Flow.FreezeMode
                    • Flow.GameStatus
                    • Input.ActionID
                    • Objects.AmmoType
                    • diff --git a/Documentation/doc/2 classes/Flow.Animations.html b/Documentation/doc/2 classes/Flow.Animations.html index ed8166075..55c6bb3cc 100644 --- a/Documentation/doc/2 classes/Flow.Animations.html +++ b/Documentation/doc/2 classes/Flow.Animations.html @@ -77,6 +77,7 @@
                      • Effects.BlendID
                      • Effects.EffectID
                      • +
                      • Flow.FreezeMode
                      • Flow.GameStatus
                      • Input.ActionID
                      • Objects.AmmoType
                      • diff --git a/Documentation/doc/2 classes/Flow.Fog.html b/Documentation/doc/2 classes/Flow.Fog.html index 323781bc2..d47ea0f43 100644 --- a/Documentation/doc/2 classes/Flow.Fog.html +++ b/Documentation/doc/2 classes/Flow.Fog.html @@ -77,6 +77,7 @@
                        • Effects.BlendID
                        • Effects.EffectID
                        • +
                        • Flow.FreezeMode
                        • Flow.GameStatus
                        • Input.ActionID
                        • Objects.AmmoType
                        • diff --git a/Documentation/doc/2 classes/Flow.InventoryItem.html b/Documentation/doc/2 classes/Flow.InventoryItem.html index b1391bb88..3ed30e47b 100644 --- a/Documentation/doc/2 classes/Flow.InventoryItem.html +++ b/Documentation/doc/2 classes/Flow.InventoryItem.html @@ -77,6 +77,7 @@
                          • Effects.BlendID
                          • Effects.EffectID
                          • +
                          • Flow.FreezeMode
                          • Flow.GameStatus
                          • Input.ActionID
                          • Objects.AmmoType
                          • diff --git a/Documentation/doc/2 classes/Flow.LensFlare.html b/Documentation/doc/2 classes/Flow.LensFlare.html index 372fa43d6..5f7146ff3 100644 --- a/Documentation/doc/2 classes/Flow.LensFlare.html +++ b/Documentation/doc/2 classes/Flow.LensFlare.html @@ -77,6 +77,7 @@
                            • Effects.BlendID
                            • Effects.EffectID
                            • +
                            • Flow.FreezeMode
                            • Flow.GameStatus
                            • Input.ActionID
                            • Objects.AmmoType
                            • diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html index ee8a2fa5c..397e7440f 100644 --- a/Documentation/doc/2 classes/Flow.Level.html +++ b/Documentation/doc/2 classes/Flow.Level.html @@ -77,6 +77,7 @@
                              • Effects.BlendID
                              • Effects.EffectID
                              • +
                              • Flow.FreezeMode
                              • Flow.GameStatus
                              • Input.ActionID
                              • Objects.AmmoType
                              • diff --git a/Documentation/doc/2 classes/Flow.Mirror.html b/Documentation/doc/2 classes/Flow.Mirror.html index 7a9fbc626..5e9b99c26 100644 --- a/Documentation/doc/2 classes/Flow.Mirror.html +++ b/Documentation/doc/2 classes/Flow.Mirror.html @@ -77,6 +77,7 @@
                                • Effects.BlendID
                                • Effects.EffectID
                                • +
                                • Flow.FreezeMode
                                • Flow.GameStatus
                                • Input.ActionID
                                • Objects.AmmoType
                                • diff --git a/Documentation/doc/2 classes/Flow.Settings.html b/Documentation/doc/2 classes/Flow.Settings.html index 9c54742f8..55249e222 100644 --- a/Documentation/doc/2 classes/Flow.Settings.html +++ b/Documentation/doc/2 classes/Flow.Settings.html @@ -77,6 +77,7 @@
                                  • Effects.BlendID
                                  • Effects.EffectID
                                  • +
                                  • Flow.FreezeMode
                                  • Flow.GameStatus
                                  • Input.ActionID
                                  • Objects.AmmoType
                                  • diff --git a/Documentation/doc/2 classes/Flow.SkyLayer.html b/Documentation/doc/2 classes/Flow.SkyLayer.html index 755bae5f9..c086a8f7c 100644 --- a/Documentation/doc/2 classes/Flow.SkyLayer.html +++ b/Documentation/doc/2 classes/Flow.SkyLayer.html @@ -77,6 +77,7 @@
                                    • Effects.BlendID
                                    • Effects.EffectID
                                    • +
                                    • Flow.FreezeMode
                                    • Flow.GameStatus
                                    • Input.ActionID
                                    • Objects.AmmoType
                                    • diff --git a/Documentation/doc/2 classes/Flow.Starfield.html b/Documentation/doc/2 classes/Flow.Starfield.html index 1fef2f963..ab054d182 100644 --- a/Documentation/doc/2 classes/Flow.Starfield.html +++ b/Documentation/doc/2 classes/Flow.Starfield.html @@ -77,6 +77,7 @@
                                      • Effects.BlendID
                                      • Effects.EffectID
                                      • +
                                      • Flow.FreezeMode
                                      • Flow.GameStatus
                                      • Input.ActionID
                                      • Objects.AmmoType
                                      • diff --git a/Documentation/doc/2 classes/Objects.AIObject.html b/Documentation/doc/2 classes/Objects.AIObject.html index adb235784..180e6dc02 100644 --- a/Documentation/doc/2 classes/Objects.AIObject.html +++ b/Documentation/doc/2 classes/Objects.AIObject.html @@ -77,6 +77,7 @@
                                        • Effects.BlendID
                                        • Effects.EffectID
                                        • +
                                        • Flow.FreezeMode
                                        • Flow.GameStatus
                                        • Input.ActionID
                                        • Objects.AmmoType
                                        • diff --git a/Documentation/doc/2 classes/Objects.Camera.html b/Documentation/doc/2 classes/Objects.Camera.html index 07b63960b..b7932494b 100644 --- a/Documentation/doc/2 classes/Objects.Camera.html +++ b/Documentation/doc/2 classes/Objects.Camera.html @@ -77,6 +77,7 @@
                                          • Effects.BlendID
                                          • Effects.EffectID
                                          • +
                                          • Flow.FreezeMode
                                          • Flow.GameStatus
                                          • Input.ActionID
                                          • Objects.AmmoType
                                          • diff --git a/Documentation/doc/2 classes/Objects.LaraObject.html b/Documentation/doc/2 classes/Objects.LaraObject.html index af639b122..d7c152e6e 100644 --- a/Documentation/doc/2 classes/Objects.LaraObject.html +++ b/Documentation/doc/2 classes/Objects.LaraObject.html @@ -77,6 +77,7 @@
                                            • Effects.BlendID
                                            • Effects.EffectID
                                            • +
                                            • Flow.FreezeMode
                                            • Flow.GameStatus
                                            • Input.ActionID
                                            • Objects.AmmoType
                                            • diff --git a/Documentation/doc/2 classes/Objects.Moveable.html b/Documentation/doc/2 classes/Objects.Moveable.html index 54b4f76af..0dd793594 100644 --- a/Documentation/doc/2 classes/Objects.Moveable.html +++ b/Documentation/doc/2 classes/Objects.Moveable.html @@ -77,6 +77,7 @@
                                              • Effects.BlendID
                                              • Effects.EffectID
                                              • +
                                              • Flow.FreezeMode
                                              • Flow.GameStatus
                                              • Input.ActionID
                                              • Objects.AmmoType
                                              • @@ -383,6 +384,14 @@ pickups, and Lara herself (see also Disable the item, as if an antitrigger for it had been stepped on (i.e. + Moveable:GetCollidable() + Get the item's collision state. + + + Moveable:SetCollidable(collidable) + Set the item's collision. + + Moveable:MakeInvisible() Make the item invisible. @@ -1967,6 +1976,49 @@ sas:SetPosition(newPos, false) + +
                                                + + Moveable:GetCollidable() +
                                                +
                                                + Get the item's collision state. + + + + +

                                                Returns:

                                                +
                                                  + + bool + item's collision state +
                                                + + + + +
                                                +
                                                + + Moveable:SetCollidable(collidable) +
                                                +
                                                + Set the item's collision. + + + +

                                                Parameters:

                                                +
                                                  +
                                                • collidable + bool + true if the caller should be collidable, false if no collision should occur. +
                                                • +
                                                + + + + +
                                                diff --git a/Documentation/doc/2 classes/Objects.Room.html b/Documentation/doc/2 classes/Objects.Room.html index 397634734..92965751c 100644 --- a/Documentation/doc/2 classes/Objects.Room.html +++ b/Documentation/doc/2 classes/Objects.Room.html @@ -77,6 +77,7 @@
                                                • Effects.BlendID
                                                • Effects.EffectID
                                                • +
                                                • Flow.FreezeMode
                                                • Flow.GameStatus
                                                • Input.ActionID
                                                • Objects.AmmoType
                                                • diff --git a/Documentation/doc/2 classes/Objects.Sink.html b/Documentation/doc/2 classes/Objects.Sink.html index 18db3c54a..93143d6ae 100644 --- a/Documentation/doc/2 classes/Objects.Sink.html +++ b/Documentation/doc/2 classes/Objects.Sink.html @@ -77,6 +77,7 @@
                                                  • Effects.BlendID
                                                  • Effects.EffectID
                                                  • +
                                                  • Flow.FreezeMode
                                                  • Flow.GameStatus
                                                  • Input.ActionID
                                                  • Objects.AmmoType
                                                  • diff --git a/Documentation/doc/2 classes/Objects.SoundSource.html b/Documentation/doc/2 classes/Objects.SoundSource.html index f50314586..14275226d 100644 --- a/Documentation/doc/2 classes/Objects.SoundSource.html +++ b/Documentation/doc/2 classes/Objects.SoundSource.html @@ -77,6 +77,7 @@
                                                    • Effects.BlendID
                                                    • Effects.EffectID
                                                    • +
                                                    • Flow.FreezeMode
                                                    • Flow.GameStatus
                                                    • Input.ActionID
                                                    • Objects.AmmoType
                                                    • diff --git a/Documentation/doc/2 classes/Objects.Static.html b/Documentation/doc/2 classes/Objects.Static.html index 0c7d2299e..f3922a16d 100644 --- a/Documentation/doc/2 classes/Objects.Static.html +++ b/Documentation/doc/2 classes/Objects.Static.html @@ -77,6 +77,7 @@
                                                      • Effects.BlendID
                                                      • Effects.EffectID
                                                      • +
                                                      • Flow.FreezeMode
                                                      • Flow.GameStatus
                                                      • Input.ActionID
                                                      • Objects.AmmoType
                                                      • diff --git a/Documentation/doc/2 classes/Objects.Volume.html b/Documentation/doc/2 classes/Objects.Volume.html index ebbc6ff29..b5bb86cf7 100644 --- a/Documentation/doc/2 classes/Objects.Volume.html +++ b/Documentation/doc/2 classes/Objects.Volume.html @@ -77,6 +77,7 @@
                                                        • Effects.BlendID
                                                        • Effects.EffectID
                                                        • +
                                                        • Flow.FreezeMode
                                                        • Flow.GameStatus
                                                        • Input.ActionID
                                                        • Objects.AmmoType
                                                        • diff --git a/Documentation/doc/2 classes/Strings.DisplayString.html b/Documentation/doc/2 classes/Strings.DisplayString.html index df175324a..772d7020c 100644 --- a/Documentation/doc/2 classes/Strings.DisplayString.html +++ b/Documentation/doc/2 classes/Strings.DisplayString.html @@ -77,6 +77,7 @@
                                                          • Effects.BlendID
                                                          • Effects.EffectID
                                                          • +
                                                          • Flow.FreezeMode
                                                          • Flow.GameStatus
                                                          • Input.ActionID
                                                          • Objects.AmmoType
                                                          • diff --git a/Documentation/doc/2 classes/View.DisplaySprite.html b/Documentation/doc/2 classes/View.DisplaySprite.html index e81bb6ab8..de9964804 100644 --- a/Documentation/doc/2 classes/View.DisplaySprite.html +++ b/Documentation/doc/2 classes/View.DisplaySprite.html @@ -77,6 +77,7 @@
                                                            • Effects.BlendID
                                                            • Effects.EffectID
                                                            • +
                                                            • Flow.FreezeMode
                                                            • Flow.GameStatus
                                                            • Input.ActionID
                                                            • Objects.AmmoType
                                                            • diff --git a/Documentation/doc/3 primitive classes/Color.html b/Documentation/doc/3 primitive classes/Color.html index 3c679941e..f9804e224 100644 --- a/Documentation/doc/3 primitive classes/Color.html +++ b/Documentation/doc/3 primitive classes/Color.html @@ -77,6 +77,7 @@
                                                              • Effects.BlendID
                                                              • Effects.EffectID
                                                              • +
                                                              • Flow.FreezeMode
                                                              • Flow.GameStatus
                                                              • Input.ActionID
                                                              • Objects.AmmoType
                                                              • diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html index 4d4435734..d96fa455f 100644 --- a/Documentation/doc/3 primitive classes/Rotation.html +++ b/Documentation/doc/3 primitive classes/Rotation.html @@ -77,6 +77,7 @@
                                                                • Effects.BlendID
                                                                • Effects.EffectID
                                                                • +
                                                                • Flow.FreezeMode
                                                                • Flow.GameStatus
                                                                • Input.ActionID
                                                                • Objects.AmmoType
                                                                • diff --git a/Documentation/doc/3 primitive classes/Vec2.html b/Documentation/doc/3 primitive classes/Vec2.html index 3b4e85b25..1bf541b4b 100644 --- a/Documentation/doc/3 primitive classes/Vec2.html +++ b/Documentation/doc/3 primitive classes/Vec2.html @@ -77,6 +77,7 @@
                                                                  • Effects.BlendID
                                                                  • Effects.EffectID
                                                                  • +
                                                                  • Flow.FreezeMode
                                                                  • Flow.GameStatus
                                                                  • Input.ActionID
                                                                  • Objects.AmmoType
                                                                  • diff --git a/Documentation/doc/3 primitive classes/Vec3.html b/Documentation/doc/3 primitive classes/Vec3.html index c4daea21d..e13784e67 100644 --- a/Documentation/doc/3 primitive classes/Vec3.html +++ b/Documentation/doc/3 primitive classes/Vec3.html @@ -77,6 +77,7 @@
                                                                    • Effects.BlendID
                                                                    • Effects.EffectID
                                                                    • +
                                                                    • Flow.FreezeMode
                                                                    • Flow.GameStatus
                                                                    • Input.ActionID
                                                                    • Objects.AmmoType
                                                                    • diff --git a/Documentation/doc/4 enums/Effects.BlendID.html b/Documentation/doc/4 enums/Effects.BlendID.html index a7334fce7..666b88507 100644 --- a/Documentation/doc/4 enums/Effects.BlendID.html +++ b/Documentation/doc/4 enums/Effects.BlendID.html @@ -77,6 +77,7 @@
                                                                      • Effects.BlendID
                                                                      • Effects.EffectID
                                                                      • +
                                                                      • Flow.FreezeMode
                                                                      • Flow.GameStatus
                                                                      • Input.ActionID
                                                                      • Objects.AmmoType
                                                                      • diff --git a/Documentation/doc/4 enums/Effects.EffectID.html b/Documentation/doc/4 enums/Effects.EffectID.html index 082554a40..b7b143237 100644 --- a/Documentation/doc/4 enums/Effects.EffectID.html +++ b/Documentation/doc/4 enums/Effects.EffectID.html @@ -77,6 +77,7 @@
                                                                        • Effects.BlendID
                                                                        • Effects.EffectID
                                                                        • +
                                                                        • Flow.FreezeMode
                                                                        • Flow.GameStatus
                                                                        • Input.ActionID
                                                                        • Objects.AmmoType
                                                                        • diff --git a/Documentation/doc/4 enums/Flow.BreakMode.html b/Documentation/doc/4 enums/Flow.BreakMode.html new file mode 100644 index 000000000..db34cabb1 --- /dev/null +++ b/Documentation/doc/4 enums/Flow.BreakMode.html @@ -0,0 +1,166 @@ + + + + + TombEngine 1.5 Lua API + + + + +
                                                                          + +
                                                                          + +
                                                                          +
                                                                          +
                                                                          + + +
                                                                          + + + + + + +
                                                                          + +

                                                                          Enum Flow.BreakMode

                                                                          +

                                                                          Constants for break modes.

                                                                          +

                                                                          + +

                                                                          + + +

                                                                          Flow.BreakMode constants

                                                                          + + + + + +
                                                                          CONSTANT_STRING_HERETable of break modes.
                                                                          + +
                                                                          +
                                                                          + + +

                                                                          Flow.BreakMode constants

                                                                          + +
                                                                          + + + +

                                                                          The following constants are inside Flow.BreakMode.

                                                                          + +
                                                                          NONE
                                                                          +FULL
                                                                          +SPECTATOR
                                                                          +PLAYER
                                                                          +
                                                                          + + +
                                                                          +
                                                                          +
                                                                          + + CONSTANT_STRING_HERE +
                                                                          +
                                                                          + Table of break modes. + + + + + + + + +
                                                                          +
                                                                          + + +
                                                                          +
                                                                          +
                                                                          +generated by TEN-LDoc (a fork of LDoc 1.4.6) +
                                                                          +
                                                                          + + diff --git a/Documentation/doc/4 enums/Flow.FreezeMode.html b/Documentation/doc/4 enums/Flow.FreezeMode.html new file mode 100644 index 000000000..92a7065e5 --- /dev/null +++ b/Documentation/doc/4 enums/Flow.FreezeMode.html @@ -0,0 +1,167 @@ + + + + + TombEngine 1.6 (Developer Version) Lua API + + + + +
                                                                          + +
                                                                          + +
                                                                          +
                                                                          +
                                                                          + + +
                                                                          + + + + + + +
                                                                          + +

                                                                          Enum Flow.FreezeMode

                                                                          +

                                                                          Constants for freeze modes.

                                                                          +

                                                                          + +

                                                                          + + +

                                                                          Flow.FreezeMode constants

                                                                          + + + + + +
                                                                          CONSTANT_STRING_HERETable of freeze modes.
                                                                          + +
                                                                          +
                                                                          + + +

                                                                          Flow.FreezeMode constants

                                                                          + +
                                                                          + + + +

                                                                          The following constants are inside Flow.FreezeMode.

                                                                          + +
                                                                          NONE - Normal in-game operation.
                                                                          +FULL - Game is completely frozen, as in pause or inventory menus.
                                                                          +SPECTATOR - Game is completely frozen, but with ability to control camera.
                                                                          +PLAYER - Game is completely frozen, but with ability to control player. Experimental.
                                                                          +
                                                                          + + +
                                                                          +
                                                                          +
                                                                          + + CONSTANT_STRING_HERE +
                                                                          +
                                                                          + Table of freeze modes. + + + + + + + + +
                                                                          +
                                                                          + + +
                                                                          +
                                                                          +
                                                                          +generated by TEN-LDoc (a fork of LDoc 1.4.6) +
                                                                          +
                                                                          + + diff --git a/Documentation/doc/4 enums/Flow.GameMode.html b/Documentation/doc/4 enums/Flow.GameMode.html new file mode 100644 index 000000000..d6b98b125 --- /dev/null +++ b/Documentation/doc/4 enums/Flow.GameMode.html @@ -0,0 +1,165 @@ + + + + + TombEngine 1.5 Lua API + + + + +
                                                                          + +
                                                                          + +
                                                                          +
                                                                          +
                                                                          + + +
                                                                          + + + + + + +
                                                                          + +

                                                                          Enum Flow.GameMode

                                                                          +

                                                                          Constants for game modes.

                                                                          +

                                                                          + +

                                                                          + + +

                                                                          Flow.GameMode constants

                                                                          + + + + + +
                                                                          CONSTANT_STRING_HERETable of game modes.
                                                                          + +
                                                                          +
                                                                          + + +

                                                                          Flow.GameMode constants

                                                                          + +
                                                                          + + + +

                                                                          The following constants are inside Flow.GameMode.

                                                                          + +
                                                                          NORMAL
                                                                          +FROZEN
                                                                          +MENU
                                                                          +
                                                                          + + +
                                                                          +
                                                                          +
                                                                          + + CONSTANT_STRING_HERE +
                                                                          +
                                                                          + Table of game modes. + + + + + + + + +
                                                                          +
                                                                          + + +
                                                                          +
                                                                          +
                                                                          +generated by TEN-LDoc (a fork of LDoc 1.4.6) +
                                                                          +
                                                                          + + diff --git a/Documentation/doc/4 enums/Flow.GameStatus.html b/Documentation/doc/4 enums/Flow.GameStatus.html index 08b90c4e7..17b807507 100644 --- a/Documentation/doc/4 enums/Flow.GameStatus.html +++ b/Documentation/doc/4 enums/Flow.GameStatus.html @@ -77,6 +77,7 @@
                                                                          • Effects.BlendID
                                                                          • Effects.EffectID
                                                                          • +
                                                                          • Flow.FreezeMode
                                                                          • Flow.GameStatus
                                                                          • Input.ActionID
                                                                          • Objects.AmmoType
                                                                          • diff --git a/Documentation/doc/4 enums/Input.ActionID.html b/Documentation/doc/4 enums/Input.ActionID.html index 0ef756bbe..64728c7b9 100644 --- a/Documentation/doc/4 enums/Input.ActionID.html +++ b/Documentation/doc/4 enums/Input.ActionID.html @@ -77,6 +77,7 @@
                                                                            • Effects.BlendID
                                                                            • Effects.EffectID
                                                                            • +
                                                                            • Flow.FreezeMode
                                                                            • Flow.GameStatus
                                                                            • Input.ActionID
                                                                            • Objects.AmmoType
                                                                            • diff --git a/Documentation/doc/4 enums/Objects.AmmoType.html b/Documentation/doc/4 enums/Objects.AmmoType.html index 58da03ff1..343f9eb6d 100644 --- a/Documentation/doc/4 enums/Objects.AmmoType.html +++ b/Documentation/doc/4 enums/Objects.AmmoType.html @@ -77,6 +77,7 @@
                                                                              • Effects.BlendID
                                                                              • Effects.EffectID
                                                                              • +
                                                                              • Flow.FreezeMode
                                                                              • Flow.GameStatus
                                                                              • Input.ActionID
                                                                              • Objects.AmmoType
                                                                              • diff --git a/Documentation/doc/4 enums/Objects.MoveableStatus.html b/Documentation/doc/4 enums/Objects.MoveableStatus.html index f384a01f7..0f2bcfb6d 100644 --- a/Documentation/doc/4 enums/Objects.MoveableStatus.html +++ b/Documentation/doc/4 enums/Objects.MoveableStatus.html @@ -77,6 +77,7 @@
                                                                                • Effects.BlendID
                                                                                • Effects.EffectID
                                                                                • +
                                                                                • Flow.FreezeMode
                                                                                • Flow.GameStatus
                                                                                • Input.ActionID
                                                                                • Objects.AmmoType
                                                                                • diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index dc2cf1c90..f2c4fbe6a 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -77,6 +77,7 @@
                                                                                  • Effects.BlendID
                                                                                  • Effects.EffectID
                                                                                  • +
                                                                                  • Flow.FreezeMode
                                                                                  • Flow.GameStatus
                                                                                  • Input.ActionID
                                                                                  • Objects.AmmoType
                                                                                  • diff --git a/Documentation/doc/4 enums/Objects.RoomFlagID.html b/Documentation/doc/4 enums/Objects.RoomFlagID.html index 2fe3978cb..558b32baf 100644 --- a/Documentation/doc/4 enums/Objects.RoomFlagID.html +++ b/Documentation/doc/4 enums/Objects.RoomFlagID.html @@ -77,6 +77,7 @@
                                                                                    • Effects.BlendID
                                                                                    • Effects.EffectID
                                                                                    • +
                                                                                    • Flow.FreezeMode
                                                                                    • Flow.GameStatus
                                                                                    • Input.ActionID
                                                                                    • Objects.AmmoType
                                                                                    • diff --git a/Documentation/doc/4 enums/Objects.RoomReverb.html b/Documentation/doc/4 enums/Objects.RoomReverb.html index d34551d76..ede3c289c 100644 --- a/Documentation/doc/4 enums/Objects.RoomReverb.html +++ b/Documentation/doc/4 enums/Objects.RoomReverb.html @@ -77,6 +77,7 @@
                                                                                      • Effects.BlendID
                                                                                      • Effects.EffectID
                                                                                      • +
                                                                                      • Flow.FreezeMode
                                                                                      • Flow.GameStatus
                                                                                      • Input.ActionID
                                                                                      • Objects.AmmoType
                                                                                      • diff --git a/Documentation/doc/4 enums/Sound.SoundTrackType.html b/Documentation/doc/4 enums/Sound.SoundTrackType.html index 0c6d012f2..760b15e1e 100644 --- a/Documentation/doc/4 enums/Sound.SoundTrackType.html +++ b/Documentation/doc/4 enums/Sound.SoundTrackType.html @@ -77,6 +77,7 @@
                                                                                        • Effects.BlendID
                                                                                        • Effects.EffectID
                                                                                        • +
                                                                                        • Flow.FreezeMode
                                                                                        • Flow.GameStatus
                                                                                        • Input.ActionID
                                                                                        • Objects.AmmoType
                                                                                        • diff --git a/Documentation/doc/4 enums/Strings.DisplayStringOption.html b/Documentation/doc/4 enums/Strings.DisplayStringOption.html index bcbbf884c..efbcbb65a 100644 --- a/Documentation/doc/4 enums/Strings.DisplayStringOption.html +++ b/Documentation/doc/4 enums/Strings.DisplayStringOption.html @@ -77,6 +77,7 @@
                                                                                          • Effects.BlendID
                                                                                          • Effects.EffectID
                                                                                          • +
                                                                                          • Flow.FreezeMode
                                                                                          • Flow.GameStatus
                                                                                          • Input.ActionID
                                                                                          • Objects.AmmoType
                                                                                          • diff --git a/Documentation/doc/4 enums/Util.LogLevel.html b/Documentation/doc/4 enums/Util.LogLevel.html index 21f49d494..54f080e45 100644 --- a/Documentation/doc/4 enums/Util.LogLevel.html +++ b/Documentation/doc/4 enums/Util.LogLevel.html @@ -77,6 +77,7 @@
                                                                                            • Effects.BlendID
                                                                                            • Effects.EffectID
                                                                                            • +
                                                                                            • Flow.FreezeMode
                                                                                            • Flow.GameStatus
                                                                                            • Input.ActionID
                                                                                            • Objects.AmmoType
                                                                                            • diff --git a/Documentation/doc/4 enums/View.AlignMode.html b/Documentation/doc/4 enums/View.AlignMode.html index 433256659..79ad956c6 100644 --- a/Documentation/doc/4 enums/View.AlignMode.html +++ b/Documentation/doc/4 enums/View.AlignMode.html @@ -77,6 +77,7 @@
                                                                                              • Effects.BlendID
                                                                                              • Effects.EffectID
                                                                                              • +
                                                                                              • Flow.FreezeMode
                                                                                              • Flow.GameStatus
                                                                                              • Input.ActionID
                                                                                              • Objects.AmmoType
                                                                                              • diff --git a/Documentation/doc/4 enums/View.CameraType.html b/Documentation/doc/4 enums/View.CameraType.html index e602f4836..eb9d9e4ba 100644 --- a/Documentation/doc/4 enums/View.CameraType.html +++ b/Documentation/doc/4 enums/View.CameraType.html @@ -77,6 +77,7 @@
                                                                                                • Effects.BlendID
                                                                                                • Effects.EffectID
                                                                                                • +
                                                                                                • Flow.FreezeMode
                                                                                                • Flow.GameStatus
                                                                                                • Input.ActionID
                                                                                                • Objects.AmmoType
                                                                                                • diff --git a/Documentation/doc/4 enums/View.PostProcessMode.html b/Documentation/doc/4 enums/View.PostProcessMode.html index c439d8ab4..70142411b 100644 --- a/Documentation/doc/4 enums/View.PostProcessMode.html +++ b/Documentation/doc/4 enums/View.PostProcessMode.html @@ -77,6 +77,7 @@
                                                                                                  • Effects.BlendID
                                                                                                  • Effects.EffectID
                                                                                                  • +
                                                                                                  • Flow.FreezeMode
                                                                                                  • Flow.GameStatus
                                                                                                  • Input.ActionID
                                                                                                  • Objects.AmmoType
                                                                                                  • diff --git a/Documentation/doc/4 enums/View.ScaleMode.html b/Documentation/doc/4 enums/View.ScaleMode.html index 4a66eab89..cc0d89c80 100644 --- a/Documentation/doc/4 enums/View.ScaleMode.html +++ b/Documentation/doc/4 enums/View.ScaleMode.html @@ -77,6 +77,7 @@
                                                                                                    • Effects.BlendID
                                                                                                    • Effects.EffectID
                                                                                                    • +
                                                                                                    • Flow.FreezeMode
                                                                                                    • Flow.GameStatus
                                                                                                    • Input.ActionID
                                                                                                    • Objects.AmmoType
                                                                                                    • diff --git a/Documentation/doc/5 lua utility modules/EventSequence.html b/Documentation/doc/5 lua utility modules/EventSequence.html index fc04f3040..263a0fc24 100644 --- a/Documentation/doc/5 lua utility modules/EventSequence.html +++ b/Documentation/doc/5 lua utility modules/EventSequence.html @@ -77,6 +77,7 @@
                                                                                                      • Effects.BlendID
                                                                                                      • Effects.EffectID
                                                                                                      • +
                                                                                                      • Flow.FreezeMode
                                                                                                      • Flow.GameStatus
                                                                                                      • Input.ActionID
                                                                                                      • Objects.AmmoType
                                                                                                      • diff --git a/Documentation/doc/5 lua utility modules/Timer.html b/Documentation/doc/5 lua utility modules/Timer.html index 9af877068..2c4d8d28d 100644 --- a/Documentation/doc/5 lua utility modules/Timer.html +++ b/Documentation/doc/5 lua utility modules/Timer.html @@ -77,6 +77,7 @@
                                                                                                        • Effects.BlendID
                                                                                                        • Effects.EffectID
                                                                                                        • +
                                                                                                        • Flow.FreezeMode
                                                                                                        • Flow.GameStatus
                                                                                                        • Input.ActionID
                                                                                                        • Objects.AmmoType
                                                                                                        • diff --git a/Documentation/doc/index.html b/Documentation/doc/index.html index 63382222e..01ed72090 100644 --- a/Documentation/doc/index.html +++ b/Documentation/doc/index.html @@ -77,6 +77,7 @@
                                                                                                          • Effects.BlendID
                                                                                                          • Effects.EffectID
                                                                                                          • +
                                                                                                          • Flow.FreezeMode
                                                                                                          • Flow.GameStatus
                                                                                                          • Input.ActionID
                                                                                                          • Objects.AmmoType
                                                                                                          • @@ -280,6 +281,10 @@ local door = GetMoveableByName("door_type4_14") Effects.EffectID Constants for effect IDs. + + Flow.FreezeMode + Constants for freeze modes. + Flow.GameStatus Constants for game statuses. diff --git a/TombEngine/Game/Setup.cpp b/TombEngine/Game/Setup.cpp index 3b8f770d7..da461897b 100644 --- a/TombEngine/Game/Setup.cpp +++ b/TombEngine/Game/Setup.cpp @@ -191,6 +191,7 @@ void InitializeObjects() obj->hitEffect = HitEffect::None; obj->explodableMeshbits = 0; obj->intelligent = false; + obj->AlwaysActive = false; obj->waterCreature = false; obj->nonLot = false; obj->usingDrawAnimatingItem = true; diff --git a/TombEngine/Game/Setup.h b/TombEngine/Game/Setup.h index e87c471da..589bde601 100644 --- a/TombEngine/Game/Setup.h +++ b/TombEngine/Game/Setup.h @@ -68,6 +68,7 @@ enum class ShatterType Explode }; +// TODO: All fields to PascalCase. struct ObjectInfo { bool loaded = false; // IsLoaded @@ -87,13 +88,14 @@ struct ObjectInfo int pivotLength; int radius; - int HitPoints; - bool intelligent; // IsIntelligent - bool waterCreature; // IsWaterCreature - bool nonLot; // IsNonLot - bool isPickup; // IsPickup - bool isPuzzleHole; // IsReceptacle - bool usingDrawAnimatingItem; + int HitPoints = 0; + bool AlwaysActive = false; + bool intelligent = false; + bool waterCreature = false; + bool nonLot = false; + bool isPickup = false; + bool isPuzzleHole = false; + bool usingDrawAnimatingItem = false; DWORD explodableMeshbits; diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index bacba41b5..b31f9e9b9 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -49,8 +49,8 @@ struct OLD_CAMERA Vector3i target; }; +bool ItemCameraOn; GameVector LastTarget; - GameVector LastIdeal; GameVector Ideals[5]; OLD_CAMERA OldCam; @@ -60,7 +60,6 @@ GameVector LookCamPosition; GameVector LookCamTarget; Vector3i CamOldPos; CAMERA_INFO Camera; -ObjectCameraInfo ItemCamera; GameVector ForcedFixedCamera; int UseForcedFixedCamera; @@ -403,7 +402,7 @@ void ObjCamera(ItemInfo* camSlotId, int camMeshId, ItemInfo* targetItem, int tar { //camSlotId and targetItem stay the same object until I know how to expand targetItem to another object. //activates code below -> void CalculateCamera(). - ItemCamera.ItemCameraOn = cond; + ItemCameraOn = cond; UpdateCameraElevation(); @@ -420,7 +419,7 @@ void ObjCamera(ItemInfo* camSlotId, int camMeshId, ItemInfo* targetItem, int tar void ClearObjCamera() { - ItemCamera.ItemCameraOn = false; + ItemCameraOn = false; } void MoveObjCamera(GameVector* ideal, ItemInfo* camSlotId, int camMeshId, ItemInfo* targetItem, int targetMeshId) @@ -483,17 +482,10 @@ void MoveObjCamera(GameVector* ideal, ItemInfo* camSlotId, int camMeshId, ItemIn speed = 2; } - //actual movement of the target. + // Actual movement of the target. Camera.target.x += (pos2.x - Camera.target.x) / speed; Camera.target.y += (pos2.y - Camera.target.y) / speed; Camera.target.z += (pos2.z - Camera.target.z) / speed; - - if (ItemCamera.LastAngle != position) - { - ItemCamera.LastAngle = Vector3i(ItemCamera.LastAngle.x = angle.x, - ItemCamera.LastAngle.y = angle.y, - ItemCamera.LastAngle.z = angle.z); - } } void RefreshFixedCamera(short camNumber) @@ -1102,10 +1094,8 @@ void CalculateCamera(const CollisionInfo& coll) return; } - if (ItemCamera.ItemCameraOn) - { + if (ItemCameraOn) return; - } if (UseForcedFixedCamera != 0) { diff --git a/TombEngine/Game/camera.h b/TombEngine/Game/camera.h index f4f4e6829..6a6939c94 100644 --- a/TombEngine/Game/camera.h +++ b/TombEngine/Game/camera.h @@ -54,12 +54,6 @@ struct CAMERA_INFO bool DisableInterpolation = false; }; -struct ObjectCameraInfo -{ - GameVector LastAngle; - bool ItemCameraOn; -}; - enum CAMERA_FLAGS { CF_NONE = 0, diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 78baf46fb..2ba480a49 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -93,13 +93,13 @@ using namespace TEN::Entities::Effects; constexpr auto DEATH_NO_INPUT_TIMEOUT = 10 * FPS; constexpr auto DEATH_INPUT_TIMEOUT = 3 * FPS; -int GameTimer = 0; -int GlobalCounter = 0; +int GameTimer = 0; +int GlobalCounter = 0; -bool InitializeGame; -bool DoTheGame; -bool JustLoaded; -bool ThreadEnded; +bool InitializeGame = false; +bool DoTheGame = false; +bool JustLoaded = false; +bool ThreadEnded = false; int RequiredStartPos; int CurrentLevel; @@ -121,15 +121,19 @@ void DrawPhase(bool isTitle, float interpolationFactor) { g_Renderer.RenderTitle(interpolationFactor); } - else + else if (g_GameFlow->CurrentFreezeMode == FreezeMode::None) { g_Renderer.Render(interpolationFactor); } + else + { + g_Renderer.RenderFreezeMode(interpolationFactor, g_GameFlow->CurrentFreezeMode == FreezeMode::Full); + } g_Renderer.Lock(); } -GameStatus ControlPhase(bool insideMenu) +GameStatus GamePhase(bool insideMenu) { auto time1 = std::chrono::high_resolution_clock::now(); bool isTitle = (CurrentLevel == 0); @@ -263,6 +267,84 @@ GameStatus ControlPhase(bool insideMenu) return gameStatus; } +GameStatus FreezePhase() +{ + // If needed when first entering freeze mode, do initialization. + if (g_GameFlow->LastFreezeMode == FreezeMode::None) + { + // Capture the screen for drawing it as a background. + if (g_GameFlow->LastFreezeMode == FreezeMode::Full) + g_Renderer.DumpGameScene(SceneRenderMode::NoHud); + + StopRumble(); + } + + // Update last freeze mode here, so that items won't update inside freeze loop. + g_GameFlow->LastFreezeMode = g_GameFlow->CurrentFreezeMode; + + g_Renderer.PrepareScene(); + g_Renderer.SaveOldState(); + + ClearLensFlares(); + ClearAllDisplaySprites(); + + SetupInterpolation(); + PrepareCamera(); + + g_GameStringsHandler->ProcessDisplayStrings(DELTA_TIME); + + // Track previous player animation to queue hair update if needed. + int lastAnimNumber = LaraItem->Animation.AnimNumber; + + // Poll controls and call scripting events. + HandleControls(false); + g_GameScript->OnFreeze(); + HandleAllGlobalEvents(EventType::Freeze, (Activator)LaraItem->Index); + + // Partially update scene if not using full freeze mode. + if (g_GameFlow->LastFreezeMode != FreezeMode::Full) + { + if (g_GameFlow->LastFreezeMode == FreezeMode::Player) + UpdateLara(LaraItem, false); + + UpdateAllItems(); + UpdateGlobalLensFlare(); + + UpdateCamera(); + + PlaySoundSources(); + Sound_UpdateScene(); + } + + // HACK: Update player hair if animation was switched in spectator mode. + // Needed for photo mode and other similar functionality. + if (g_GameFlow->LastFreezeMode == FreezeMode::Spectator && + lastAnimNumber != LaraItem->Animation.AnimNumber) + { + lastAnimNumber = LaraItem->Animation.AnimNumber; + for (int i = 0; i < FPS; i++) + HairEffect.Update(*LaraItem); + } + + // Update last freeze mode again, as it may have been changed in a script. + g_GameFlow->LastFreezeMode = g_GameFlow->CurrentFreezeMode; + + return GameStatus::Normal; +} + +GameStatus ControlPhase(bool insideMenu) +{ + // For safety, only allow to break game loop in non-title levels. + if (g_GameFlow->CurrentFreezeMode == FreezeMode::None || CurrentLevel == 0) + { + return GamePhase(insideMenu); + } + else + { + return FreezePhase(); + } +} + unsigned CALLBACK GameMain(void *) { TENLog("Starting GameMain()...", LogLevel::Info); @@ -551,9 +633,9 @@ GameStatus DoGameLoop(int levelIndex) int frameCount = LOOP_FRAME_COUNT; auto& status = g_GameFlow->LastGameStatus; - // Before entering actual game loop, ControlPhase() must be - // called once to sort out various runtime shenanigangs (e.g. hair). - status = ControlPhase(false); + // Before entering actual game loop, GamePhase() must be called once to sort out + // various runtime shenanigangs (e.g. hair or freeze mode initialization). + status = GamePhase(false); g_Synchronizer.Init(); bool legacy30FpsDoneDraw = false; @@ -573,6 +655,9 @@ GameStatus DoGameLoop(int levelIndex) if (status != GameStatus::Normal) break; + if (g_GameFlow->LastFreezeMode != g_GameFlow->CurrentFreezeMode) + continue; + if (!g_Configuration.EnableHighFramerate) { if (!legacy30FpsDoneDraw) diff --git a/TombEngine/Game/control/control.h b/TombEngine/Game/control/control.h index 9aa63dd1e..1a49c42e1 100644 --- a/TombEngine/Game/control/control.h +++ b/TombEngine/Game/control/control.h @@ -26,6 +26,14 @@ enum class GameStatus LevelComplete }; +enum class FreezeMode +{ + None, + Full, + Spectator, + Player +}; + enum class LevelLoadType { New, diff --git a/TombEngine/Game/control/event.h b/TombEngine/Game/control/event.h index b1bfb6be5..67cd532bc 100644 --- a/TombEngine/Game/control/event.h +++ b/TombEngine/Game/control/event.h @@ -40,6 +40,7 @@ namespace TEN::Control::Volumes Start, End, UseItem, + Freeze, Count }; diff --git a/TombEngine/Game/control/trigger.cpp b/TombEngine/Game/control/trigger.cpp index 584e324e2..44e8c9119 100644 --- a/TombEngine/Game/control/trigger.cpp +++ b/TombEngine/Game/control/trigger.cpp @@ -414,6 +414,9 @@ void Trigger(short const value, short const flags) void TestTriggers(int x, int y, int z, FloorInfo* floor, Activator activator, bool heavy, int heavyFlags) { + if (g_GameFlow->CurrentFreezeMode != FreezeMode::None) + return; + bool switchOff = false; bool flipAvailable = false; int flip = NO_VALUE; @@ -855,6 +858,9 @@ void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFl void ProcessSectorFlags(ItemInfo* item) { + if (g_GameFlow->CurrentFreezeMode != FreezeMode::None) + return; + bool isPlayer = item->IsLara(); // HACK: because of L-shaped portal configurations, we need to fetch room number from Location struct for player. diff --git a/TombEngine/Game/control/volume.cpp b/TombEngine/Game/control/volume.cpp index e4ddab38d..0f0188132 100644 --- a/TombEngine/Game/control/volume.cpp +++ b/TombEngine/Game/control/volume.cpp @@ -131,6 +131,9 @@ namespace TEN::Control::Volumes void TestVolumes(short roomNumber, const BoundingOrientedBox& box, ActivatorFlags activatorFlag, Activator activator) { + if (g_GameFlow->CurrentFreezeMode != FreezeMode::None) + return; + if (roomNumber == NO_VALUE) return; diff --git a/TombEngine/Game/gui.cpp b/TombEngine/Game/gui.cpp index dccf2a53e..0979ea6bc 100644 --- a/TombEngine/Game/gui.cpp +++ b/TombEngine/Game/gui.cpp @@ -3168,9 +3168,7 @@ namespace TEN::Gui bool GuiController::CallPause() { - ClearAllDisplaySprites(); - g_Renderer.PrepareScene(); - g_Renderer.DumpGameScene(); + g_Renderer.DumpGameScene(SceneRenderMode::NoHud); PauseAllSounds(SoundPauseMode::Pause); SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always); @@ -3251,9 +3249,7 @@ namespace TEN::Gui player.Inventory.OldBusy = player.Inventory.IsBusy; - ClearAllDisplaySprites(); - g_Renderer.PrepareScene(); - g_Renderer.DumpGameScene(); + g_Renderer.DumpGameScene(SceneRenderMode::NoHud); PauseAllSounds(SoundPauseMode::Inventory); SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always); diff --git a/TombEngine/Game/items.cpp b/TombEngine/Game/items.cpp index 393e42847..75bdbed15 100644 --- a/TombEngine/Game/items.cpp +++ b/TombEngine/Game/items.cpp @@ -763,28 +763,30 @@ void UpdateAllItems() while (itemNumber != NO_VALUE) { auto* item = &g_Level.Items[itemNumber]; - short nextItem = item->NextActive; + itemNumber = item->NextActive; if (!Objects.CheckID(item->ObjectNumber)) continue; + if (g_GameFlow->LastFreezeMode != FreezeMode::None && !Objects[item->ObjectNumber].AlwaysActive) + continue; + if (item->AfterDeath <= ITEM_DEATH_TIMEOUT) { if (Objects[item->ObjectNumber].control) - Objects[item->ObjectNumber].control(itemNumber); + Objects[item->ObjectNumber].control(item->Index); - TestVolumes(itemNumber); + TestVolumes(item->Index); ProcessEffects(item); if (item->AfterDeath > 0 && item->AfterDeath < ITEM_DEATH_TIMEOUT && !(Wibble & 3)) item->AfterDeath++; if (item->AfterDeath == ITEM_DEATH_TIMEOUT) - KillItem(itemNumber); + KillItem(item->Index); } else - KillItem(itemNumber); + KillItem(item->Index); - itemNumber = nextItem; } InItemControlLoop = false; diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index 18de8ba3a..872beacc7 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -1408,6 +1408,12 @@ const std::vector SaveGame::Build() std::vector callbackVecPreLoop; std::vector callbackVecPostLoop; + std::vector callbackVecPreUseItem; + std::vector callbackVecPostUseItem; + + std::vector callbackVecPreFreeze; + std::vector callbackVecPostFreeze; + g_GameScript->GetCallbackStrings( callbackVecPreStart, callbackVecPostStart, @@ -1418,7 +1424,11 @@ const std::vector SaveGame::Build() callbackVecPreLoad, callbackVecPostLoad, callbackVecPreLoop, - callbackVecPostLoop); + callbackVecPostLoop, + callbackVecPreUseItem, + callbackVecPostUseItem, + callbackVecPreFreeze, + callbackVecPostFreeze); auto stringsCallbackPreStart = fbb.CreateVectorOfStrings(callbackVecPreStart); auto stringsCallbackPostStart = fbb.CreateVectorOfStrings(callbackVecPostStart); @@ -1430,6 +1440,10 @@ const std::vector SaveGame::Build() auto stringsCallbackPostLoad = fbb.CreateVectorOfStrings(callbackVecPostLoad); auto stringsCallbackPreLoop = fbb.CreateVectorOfStrings(callbackVecPreLoop); auto stringsCallbackPostLoop = fbb.CreateVectorOfStrings(callbackVecPostLoop); + auto stringsCallbackPreUseItem = fbb.CreateVectorOfStrings(callbackVecPreUseItem); + auto stringsCallbackPostUseItem = fbb.CreateVectorOfStrings(callbackVecPostUseItem); + auto stringsCallbackPreFreeze = fbb.CreateVectorOfStrings(callbackVecPreFreeze); + auto stringsCallbackPostFreeze = fbb.CreateVectorOfStrings(callbackVecPostFreeze); Save::SaveGameBuilder sgb{ fbb }; @@ -1496,6 +1510,12 @@ const std::vector SaveGame::Build() sgb.add_callbacks_pre_loop(stringsCallbackPreLoop); sgb.add_callbacks_post_loop(stringsCallbackPostLoop); + sgb.add_callbacks_pre_useitem(stringsCallbackPreUseItem); + sgb.add_callbacks_post_useitem(stringsCallbackPostUseItem); + + sgb.add_callbacks_pre_freeze(stringsCallbackPreFreeze); + sgb.add_callbacks_post_freeze(stringsCallbackPostFreeze); + auto sg = sgb.Finish(); fbb.Finish(sg); @@ -1816,6 +1836,12 @@ static void ParseLua(const Save::SaveGame* s) auto callbacksPreLoopVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_loop); auto callbacksPostLoopVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_loop); + auto callbacksPreUseItemVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_useitem); + auto callbacksPostUseItemVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_useitem); + + auto callbacksPreFreezeVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_freeze); + auto callbacksPostFreezeVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_freeze); + g_GameScript->SetCallbackStrings( callbacksPreStartVec, callbacksPostStartVec, @@ -1826,7 +1852,11 @@ static void ParseLua(const Save::SaveGame* s) callbacksPreLoadVec, callbacksPostLoadVec, callbacksPreLoopVec, - callbacksPostLoopVec); + callbacksPostLoopVec, + callbacksPreUseItemVec, + callbacksPostUseItemVec, + callbacksPreFreezeVec, + callbacksPostFreezeVec); } static void ParsePlayer(const Save::SaveGame* s) diff --git a/TombEngine/Objects/Effects/effect_objects.cpp b/TombEngine/Objects/Effects/effect_objects.cpp index ef905c561..f157af040 100644 --- a/TombEngine/Objects/Effects/effect_objects.cpp +++ b/TombEngine/Objects/Effects/effect_objects.cpp @@ -61,5 +61,6 @@ void InitializeEffectsObjects() { obj->drawRoutine = nullptr; obj->control = ControlLensFlare; + obj->AlwaysActive = true; } } diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index 2aac47518..bbdd9d64b 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -516,7 +516,7 @@ namespace TEN::Renderer void PrepareStreamers(RenderView& view); void PrepareFootprints(RenderView& view); void DrawLoadingBar(float percent); - void DrawPostprocess(RenderTarget2D* renderTarget, RenderView& view); + void DrawPostprocess(RenderTarget2D* renderTarget, RenderView& view, SceneRenderMode renderMode); void RenderInventoryScene(RenderTarget2D* renderTarget, TextureBase* background, float backgroundFade); void RenderTitleMenu(Menu menu); void RenderPauseMenu(Menu menu); @@ -633,9 +633,9 @@ namespace TEN::Renderer bool PrepareDataForTheRenderer(); void UpdateCameraMatrices(CAMERA_INFO* cam, float farView); void RenderSimpleSceneToParaboloid(RenderTarget2D* renderTarget, Vector3 position, int emisphere); - void DumpGameScene(); + void DumpGameScene(SceneRenderMode renderMode = SceneRenderMode::Full); void RenderInventory(); - void RenderScene(RenderTarget2D* renderTarget, bool doAntialiasing, RenderView& view); + void RenderScene(RenderTarget2D* renderTarget, RenderView& view, SceneRenderMode renderMode = SceneRenderMode::Full); void PrepareScene(); void ClearScene(); void SaveScreenshot(); @@ -647,6 +647,7 @@ namespace TEN::Renderer void FreeRendererData(); void AddDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b); void RenderLoadingScreen(float percentage); + void RenderFreezeMode(float interpFactor, bool staticBackground); void UpdateProgress(float value); void ToggleFullScreen(bool force = false); void SetFullScreen(); @@ -686,7 +687,7 @@ namespace TEN::Renderer void SaveOldState(); float GetFramerateMultiplier() const; - float GetInterpolationFactor() const; + float GetInterpolationFactor(bool forceRawValue = false) const; Vector2i GetScreenResolution() const; int GetScreenRefreshRate() const; std::optional Get2DPosition(const Vector3& pos) const; diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index 0086e1fc2..7e9f5e3be 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -280,7 +280,7 @@ namespace TEN::Renderer auto prevRotMatrix = gunshell->oldPos.Orientation.ToRotationMatrix(); auto prevWorldMatrix = prevRotMatrix * prevTranslation; - worldMatrix = Matrix::Lerp(prevWorldMatrix, worldMatrix, _interpolationFactor); + worldMatrix = Matrix::Lerp(prevWorldMatrix, worldMatrix, GetInterpolationFactor()); _stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].World = worldMatrix; _stInstancedStaticMeshBuffer.StaticMeshes[gunShellCount].Ambient = room.AmbientLight; @@ -346,7 +346,7 @@ namespace TEN::Renderer relPos = Vector3(segment->x >> FP_SHIFT, segment->y >> FP_SHIFT, segment->z >> FP_SHIFT); auto currentOutput = Vector3::Transform(relPos, translationMatrix); - auto absolutePos = Vector3::Lerp(prevOutput, currentOutput, _interpolationFactor); + auto absolutePos = Vector3::Lerp(prevOutput, currentOutput, GetInterpolationFactor()); absolutePoints[i] = absolutePos; } @@ -628,7 +628,7 @@ namespace TEN::Renderer for (auto& poly : bucket.Polygons) { - auto worldMatrix = Matrix::Lerp(fish.PrevTransform, fish.Transform, _interpolationFactor); + auto worldMatrix = Matrix::Lerp(fish.PrevTransform, fish.Transform, GetInterpolationFactor()); auto center = Vector3::Transform(poly.Centre, worldMatrix); float dist = Vector3::Distance(center, view.Camera.WorldPosition); @@ -689,7 +689,7 @@ namespace TEN::Renderer const auto& mesh = *GetMesh(Objects[ID_FISH_EMITTER].meshIndex + fish.MeshIndex); - _stStatic.World = Matrix::Lerp(fish.PrevTransform, fish.Transform, _interpolationFactor); + _stStatic.World = Matrix::Lerp(fish.PrevTransform, fish.Transform, GetInterpolationFactor()); _stStatic.Color = Vector4::One; _stStatic.AmbientLight = _rooms[fish.RoomNumber].AmbientLight; @@ -743,7 +743,7 @@ namespace TEN::Renderer for (int p = 0; p < bucket.Polygons.size(); p++) { - auto transformMatrix = Matrix::Lerp(bat.PrevTransform, bat.Transform, _interpolationFactor); + auto transformMatrix = Matrix::Lerp(bat.PrevTransform, bat.Transform, GetInterpolationFactor()); auto centre = Vector3::Transform(bucket.Polygons[p].Centre, transformMatrix); float dist = (centre - view.Camera.WorldPosition).Length(); @@ -773,7 +773,7 @@ namespace TEN::Renderer { auto& room = _rooms[bat.RoomNumber]; - auto transformMatrix = Matrix::Lerp(bat.PrevTransform, bat.Transform, _interpolationFactor); + auto transformMatrix = Matrix::Lerp(bat.PrevTransform, bat.Transform, GetInterpolationFactor()); _stInstancedStaticMeshBuffer.StaticMeshes[batCount].World = transformMatrix; _stInstancedStaticMeshBuffer.StaticMeshes[batCount].Ambient = room.AmbientLight; @@ -848,7 +848,7 @@ namespace TEN::Renderer if (!beetle.On) continue; - auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, _interpolationFactor); + auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, GetInterpolationFactor()); for (auto& bucket : mesh.Buckets) { @@ -886,7 +886,7 @@ namespace TEN::Renderer { auto& room = _rooms[beetle.RoomNumber]; - auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, _interpolationFactor); + auto transformMatrix = Matrix::Lerp(beetle.PrevTransform, beetle.Transform, GetInterpolationFactor()); _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].World = transformMatrix; _stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Ambient = room.AmbientLight; @@ -977,7 +977,7 @@ namespace TEN::Renderer for (int p = 0; p < bucket.Polygons.size(); p++) { - auto transformMatrix = Matrix::Lerp(locust.PrevTransform, locust.Transform, _interpolationFactor); + auto transformMatrix = Matrix::Lerp(locust.PrevTransform, locust.Transform, GetInterpolationFactor()); auto centre = Vector3::Transform(bucket.Polygons[p].Centre, transformMatrix); float dist = (centre - view.Camera.WorldPosition).Length(); @@ -1039,7 +1039,7 @@ namespace TEN::Renderer auto& mesh = *GetMesh(Objects[ID_LOCUSTS].meshIndex + (-locust.counter & 3)); - _stStatic.World = Matrix::Lerp(locust.PrevTransform, locust.Transform, _interpolationFactor); + _stStatic.World = Matrix::Lerp(locust.PrevTransform, locust.Transform, GetInterpolationFactor()); _stStatic.Color = Vector4::One; _stStatic.AmbientLight = _rooms[locust.roomNumber].AmbientLight; _cbStatic.UpdateData(_stStatic, _context.Get()); @@ -1538,7 +1538,7 @@ namespace TEN::Renderer void Renderer::AddDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b) { - if (_isLocked) + if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None) return; RendererLight dynamicLight = {}; @@ -1569,7 +1569,9 @@ namespace TEN::Renderer void Renderer::PrepareScene() { - _dynamicLights.clear(); + if (g_GameFlow->CurrentFreezeMode == FreezeMode::None) + _dynamicLights.clear(); + _lines2DToDraw.clear(); _lines3DToDraw.clear(); _triangles3DToDraw.clear(); @@ -1605,7 +1607,7 @@ namespace TEN::Renderer ClearShadowMap(); } - void Renderer::RenderScene(RenderTarget2D* renderTarget, bool doAntialiasing, RenderView& view) + void Renderer::RenderScene(RenderTarget2D* renderTarget, RenderView& view, SceneRenderMode renderMode) { using ns = std::chrono::nanoseconds; using get_time = std::chrono::steady_clock; @@ -1850,13 +1852,16 @@ namespace TEN::Renderer ClearDrawPhaseDisplaySprites(); _context->ClearDepthStencilView(_renderTarget.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); - g_Hud.Draw(*LaraItem); - - _doingFullscreenPass = true; - // Apply antialiasing. - if (doAntialiasing) + // HUD must be drawn before post-processing to be antialiased. + if (renderMode == SceneRenderMode::Full) + g_Hud.Draw(*LaraItem); + + if (renderMode != SceneRenderMode::NoPostprocess) { + _doingFullscreenPass = true; + + // Apply antialiasing. switch (g_Configuration.AntialiasingMode) { case AntialiasingMode::None: @@ -1871,30 +1876,33 @@ namespace TEN::Renderer ApplySMAA(&_renderTarget, view); break; } + + // Draw post-process effects (cinematic bars, fade, flash, HDR, tone mapping, etc.). + DrawPostprocess(renderTarget, view, renderMode); + + _doingFullscreenPass = false; + + // Draw binoculars or lasersight overlay. + DrawOverlays(view); + + // Draw 2D debug lines. + DrawLines2D(); } - // Draw post-process effects (cinematic bars, fade, flash, HDR, tone mapping, etc.). - DrawPostprocess(renderTarget, view); + if (renderMode == SceneRenderMode::Full) + { + // Draw display sprites sorted by priority. + CollectDisplaySprites(view); + DrawDisplaySprites(view); - _doingFullscreenPass = false; - - // Draw 2D debug lines. - DrawLines2D(); - - // Draw display sprites sorted by priority. - CollectDisplaySprites(view); - DrawDisplaySprites(view); - - // Draw binoculars or lasersight overlay. - DrawOverlays(view); + DrawDebugInfo(view); + DrawAllStrings(); + } time2 = std::chrono::high_resolution_clock::now(); _timeFrame = (std::chrono::duration_cast(time2 - time1)).count() / 1000000; time1 = time2; - DrawDebugInfo(view); - DrawAllStrings(); - ClearScene(); CalculateFrameRate(); } @@ -2153,10 +2161,9 @@ namespace TEN::Renderer SetBlendMode(BlendMode::Opaque, true);*/ } - void Renderer::DumpGameScene() + void Renderer::DumpGameScene(SceneRenderMode renderMode) { - RenderScene(&_dumpScreenRenderTarget, false, _gameCamera); - + RenderScene(&_dumpScreenRenderTarget, _gameCamera, renderMode); _graphicsSettingsChanged = false; } @@ -2877,7 +2884,7 @@ namespace TEN::Renderer rDrawSprite.Type = SpriteType::CustomBillboard; rDrawSprite.pos = renderView.Camera.WorldPosition + - Vector3::Lerp(meteor.PrevPosition, meteor.Position, _interpolationFactor); + Vector3::Lerp(meteor.PrevPosition, meteor.Position, GetInterpolationFactor()); rDrawSprite.Rotation = 0; rDrawSprite.Scale = 1; rDrawSprite.Width = 2; @@ -2889,7 +2896,7 @@ namespace TEN::Renderer meteor.Color.x, meteor.Color.y, meteor.Color.z, - Lerp(meteor.PrevFade, meteor.Fade, _interpolationFactor)); + Lerp(meteor.PrevFade, meteor.Fade, GetInterpolationFactor())); _stInstancedSpriteBuffer.Sprites[i].IsBillboard = 1; _stInstancedSpriteBuffer.Sprites[i].IsSoftParticle = 0; @@ -3026,7 +3033,7 @@ namespace TEN::Renderer void Renderer::Render(float interpFactor) { InterpolateCamera(interpFactor); - RenderScene(&_backBuffer, true, _gameCamera); + RenderScene(&_backBuffer, _gameCamera); _context->ClearState(); _swapChain->Present(1, 0); diff --git a/TombEngine/Renderer/RendererDrawEffect.cpp b/TombEngine/Renderer/RendererDrawEffect.cpp index bd031cee0..ef7b775fc 100644 --- a/TombEngine/Renderer/RendererDrawEffect.cpp +++ b/TombEngine/Renderer/RendererDrawEffect.cpp @@ -86,22 +86,22 @@ namespace TEN::Renderer { bool isLastSubdivision = (i == (LaserBeamEffect::SUBDIVISION_COUNT - 1)); - auto color = Color::Lerp(beam.OldColor, beam.Color, _interpolationFactor); + auto color = Color::Lerp(beam.OldColor, beam.Color, GetInterpolationFactor()); AddColoredQuad( - Vector3::Lerp(beam.OldVertices[i], beam.Vertices[i], _interpolationFactor), + Vector3::Lerp(beam.OldVertices[i], beam.Vertices[i], GetInterpolationFactor()), Vector3::Lerp( beam.OldVertices[isLastSubdivision ? 0 : (i + 1)], beam.Vertices[isLastSubdivision ? 0 : (i + 1)], - _interpolationFactor), + GetInterpolationFactor()), Vector3::Lerp( beam.OldVertices[LaserBeamEffect::SUBDIVISION_COUNT + (isLastSubdivision ? 0 : (i + 1))], beam.Vertices[LaserBeamEffect::SUBDIVISION_COUNT + (isLastSubdivision ? 0 : (i + 1))], - _interpolationFactor), + GetInterpolationFactor()), Vector3::Lerp( beam.OldVertices[LaserBeamEffect::SUBDIVISION_COUNT + i], beam.Vertices[LaserBeamEffect::SUBDIVISION_COUNT + i], - _interpolationFactor), + GetInterpolationFactor()), color, color, color, color, BlendMode::Additive, view, SpriteRenderType::LaserBeam); } @@ -134,40 +134,40 @@ namespace TEN::Renderer if (segment.Flags & (int)StreamerFlags::FadeLeft) { AddColoredQuad( - Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], _interpolationFactor), - Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], _interpolationFactor), - Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], _interpolationFactor), - Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], _interpolationFactor), + Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), + Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), + Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), + Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), Vector4::Zero, - Vector4::Lerp(segment.PrevColor, segment.Color, _interpolationFactor), - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, _interpolationFactor), + Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), + Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), Vector4::Zero, blendMode, view); } else if (segment.Flags & (int)StreamerFlags::FadeRight) { AddColoredQuad( - Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], _interpolationFactor), - Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], _interpolationFactor), - Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], _interpolationFactor), - Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], _interpolationFactor), - Vector4::Lerp(segment.PrevColor, segment.Color, _interpolationFactor), + Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), + Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), + Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), + Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), + Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), Vector4::Zero, Vector4::Zero, - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, _interpolationFactor), + Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), blendMode, view); } else { AddColoredQuad( - Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], _interpolationFactor), - Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], _interpolationFactor), - Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], _interpolationFactor), - Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], _interpolationFactor), - Vector4::Lerp(segment.PrevColor, segment.Color, _interpolationFactor), - Vector4::Lerp(segment.PrevColor, segment.Color, _interpolationFactor), - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, _interpolationFactor), - Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, _interpolationFactor), + Vector3::Lerp(segment.PrevVertices[0], segment.Vertices[0], GetInterpolationFactor()), + Vector3::Lerp(segment.PrevVertices[1], segment.Vertices[1], GetInterpolationFactor()), + Vector3::Lerp(prevSegment.PrevVertices[1], prevSegment.Vertices[1], GetInterpolationFactor()), + Vector3::Lerp(prevSegment.PrevVertices[0], prevSegment.Vertices[0], GetInterpolationFactor()), + Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), + Vector4::Lerp(segment.PrevColor, segment.Color, GetInterpolationFactor()), + Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), + Vector4::Lerp(prevSegment.PrevColor, prevSegment.Color, GetInterpolationFactor()), blendMode, view); } } @@ -189,13 +189,13 @@ namespace TEN::Renderer if (laser.Life <= 0.0f) continue; - auto color = Vector4::Lerp(laser.PrevColor, laser.Color, _interpolationFactor); - color.w = Lerp(laser.PrevOpacity, laser.Opacity, _interpolationFactor); + auto color = Vector4::Lerp(laser.PrevColor, laser.Color, GetInterpolationFactor()); + color.w = Lerp(laser.PrevOpacity, laser.Opacity, GetInterpolationFactor()); - auto laserTarget = Vector3::Lerp(laser.PrevTarget, laser.Target, _interpolationFactor); + auto laserTarget = Vector3::Lerp(laser.PrevTarget, laser.Target, GetInterpolationFactor()); ElectricityKnots[0] = laserTarget; - ElectricityKnots[1] = Vector3::Lerp(laser.PrevOrigin, laser.Origin, _interpolationFactor); + ElectricityKnots[1] = Vector3::Lerp(laser.PrevOrigin, laser.Origin, GetInterpolationFactor()); for (int j = 0; j < 2; j++) ElectricityKnots[j] -= laserTarget; @@ -241,12 +241,12 @@ namespace TEN::Renderer if (arc.life <= 0) continue; - ElectricityKnots[0] = Vector3::Lerp(arc.PrevPos1, arc.pos1, _interpolationFactor); - ElectricityKnots[1] = Vector3::Lerp(arc.PrevPos1, arc.pos1, _interpolationFactor); - ElectricityKnots[2] = Vector3::Lerp(arc.PrevPos2, arc.pos2, _interpolationFactor); - ElectricityKnots[3] = Vector3::Lerp(arc.PrevPos3, arc.pos3, _interpolationFactor); - ElectricityKnots[4] = Vector3::Lerp(arc.PrevPos4, arc.pos4, _interpolationFactor); - ElectricityKnots[5] = Vector3::Lerp(arc.PrevPos4, arc.pos4, _interpolationFactor); + ElectricityKnots[0] = Vector3::Lerp(arc.PrevPos1, arc.pos1, GetInterpolationFactor()); + ElectricityKnots[1] = Vector3::Lerp(arc.PrevPos1, arc.pos1, GetInterpolationFactor()); + ElectricityKnots[2] = Vector3::Lerp(arc.PrevPos2, arc.pos2, GetInterpolationFactor()); + ElectricityKnots[3] = Vector3::Lerp(arc.PrevPos3, arc.pos3, GetInterpolationFactor()); + ElectricityKnots[4] = Vector3::Lerp(arc.PrevPos4, arc.pos4, GetInterpolationFactor()); + ElectricityKnots[5] = Vector3::Lerp(arc.PrevPos4, arc.pos4, GetInterpolationFactor()); for (int j = 0; j < ElectricityKnots.size(); j++) ElectricityKnots[j] -= LaraItem->Pose.Position.ToVector3(); @@ -300,9 +300,9 @@ namespace TEN::Renderer oldB = (arc.PrevLife * arc.PrevB) / 16; } - r = (byte)Lerp(oldR, r, _interpolationFactor); - g = (byte)Lerp(oldG, g, _interpolationFactor); - b = (byte)Lerp(oldB, b, _interpolationFactor); + r = (byte)Lerp(oldR, r, GetInterpolationFactor()); + g = (byte)Lerp(oldG, g, GetInterpolationFactor()); + b = (byte)Lerp(oldB, b, GetInterpolationFactor()); AddSpriteBillboardConstrained( &_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LIGHTHING], @@ -326,16 +326,16 @@ namespace TEN::Renderer Vector3::Lerp( Vector3(smoke.oldPosition.x, smoke.oldPosition.y, smoke.oldPosition.z), Vector3(smoke.position.x, smoke.position.y, smoke.position.z), - _interpolationFactor), + GetInterpolationFactor()), Vector4::Lerp( Vector4(smoke.oldShade / 255.0f, smoke.oldShade / 255.0f, smoke.oldShade / 255.0f, 1.0f), Vector4(smoke.shade / 255.0f, smoke.shade / 255.0f, smoke.shade / 255.0f, 1.0f), - _interpolationFactor), - TO_RAD(Lerp(smoke.oldRotAng << 4, smoke.rotAng << 4, _interpolationFactor)), - Lerp(smoke.oldScalar, smoke.scalar, _interpolationFactor), + GetInterpolationFactor()), + TO_RAD(Lerp(smoke.oldRotAng << 4, smoke.rotAng << 4, GetInterpolationFactor())), + Lerp(smoke.oldScalar, smoke.scalar, GetInterpolationFactor()), { - Lerp(smoke.oldSize, smoke.size, _interpolationFactor) * 4.0f, - Lerp(smoke.oldSize, smoke.size, _interpolationFactor) * 4.0f + Lerp(smoke.oldSize, smoke.size, GetInterpolationFactor()) * 4.0f, + Lerp(smoke.oldSize, smoke.size, GetInterpolationFactor()) * 4.0f }, BlendMode::Additive, true, view); } @@ -347,7 +347,7 @@ namespace TEN::Renderer { auto oldFade = fire.oldFade == 1 ? 1.0f : (float)(255 - fire.oldFade) / 255.0f; auto fade = fire.fade == 1 ? 1.0f : (float)(255 - fire.fade) / 255.0f; - fade = Lerp(oldFade, fade, _interpolationFactor); + fade = Lerp(oldFade, fade, GetInterpolationFactor()); for (int i = 0; i < MAX_SPARKS_FIRE; i++) { @@ -365,7 +365,7 @@ namespace TEN::Renderer fire.position.x + spark->position.x * fire.size / 2, fire.position.y + spark->position.y * fire.size / 2, fire.position.z + spark->position.z * fire.size / 2), - _interpolationFactor), + GetInterpolationFactor()), Vector4::Lerp( Vector4( spark->oldColor.x / 255.0f * fade, @@ -377,13 +377,13 @@ namespace TEN::Renderer spark->color.y / 255.0f * fade, spark->color.z / 255.0f * fade, 1.0f), - _interpolationFactor), - TO_RAD(Lerp(spark->oldRotAng << 4, spark->rotAng << 4, _interpolationFactor)), - Lerp(spark->oldScalar, spark->scalar, _interpolationFactor), + GetInterpolationFactor()), + TO_RAD(Lerp(spark->oldRotAng << 4, spark->rotAng << 4, GetInterpolationFactor())), + Lerp(spark->oldScalar, spark->scalar, GetInterpolationFactor()), Vector2::Lerp( Vector2(fire.oldSize * spark->oldSize, fire.oldSize * spark->oldSize), Vector2(fire.size * spark->size, fire.size * spark->size), - _interpolationFactor), + GetInterpolationFactor()), BlendMode::Additive, true, view); } } @@ -405,7 +405,7 @@ namespace TEN::Renderer auto pos = Vector3::Lerp( Vector3(particle.PrevX, particle.PrevY, particle.PrevZ), Vector3(particle.x, particle.y, particle.z), - _interpolationFactor); + GetInterpolationFactor()); if (particle.flags & SP_FX) { @@ -421,11 +421,11 @@ namespace TEN::Renderer newEffect.RoomNumber = fx.roomNumber; newEffect.Position = fx.pos.Position.ToVector3(); - newEffect.InterpolatedPosition = Vector3::Lerp(newEffect.PrevPosition, newEffect.Position, _interpolationFactor); - newEffect.InterpolatedTranslation = Matrix::Lerp(newEffect.PrevTranslation, newEffect.Translation, _interpolationFactor); - newEffect.InterpolatedRotation = Matrix::Lerp(newEffect.InterpolatedRotation, newEffect.Rotation, _interpolationFactor); - newEffect.InterpolatedWorld = Matrix::Lerp(newEffect.PrevWorld, newEffect.World, _interpolationFactor); - newEffect.InterpolatedScale = Matrix::Lerp(newEffect.PrevScale, newEffect.Scale, _interpolationFactor); + newEffect.InterpolatedPosition = Vector3::Lerp(newEffect.PrevPosition, newEffect.Position, GetInterpolationFactor()); + newEffect.InterpolatedTranslation = Matrix::Lerp(newEffect.PrevTranslation, newEffect.Translation, GetInterpolationFactor()); + newEffect.InterpolatedRotation = Matrix::Lerp(newEffect.InterpolatedRotation, newEffect.Rotation, GetInterpolationFactor()); + newEffect.InterpolatedWorld = Matrix::Lerp(newEffect.PrevWorld, newEffect.World, GetInterpolationFactor()); + newEffect.InterpolatedScale = Matrix::Lerp(newEffect.PrevScale, newEffect.Scale, GetInterpolationFactor()); pos += newEffect.InterpolatedPosition; @@ -514,7 +514,7 @@ namespace TEN::Renderer auto pos = Vector3::Lerp( Vector3(particle.PrevX, particle.PrevY, particle.PrevZ), Vector3(particle.x, particle.y, particle.z), - _interpolationFactor); + GetInterpolationFactor()); auto axis = Vector3(particle.xVel, particle.yVel, particle.zVel); axis.Normalize(); @@ -567,7 +567,7 @@ namespace TEN::Renderer } } - color = (byte)Lerp(prevColor, color, _interpolationFactor); + color = (byte)Lerp(prevColor, color, GetInterpolationFactor()); float xInner; float zInner; @@ -580,8 +580,8 @@ namespace TEN::Renderer float yInner = splash.y; float yOuter = splash.y - splash.height; - float innerRadius = Lerp(splash.PrevInnerRad, splash.innerRad, _interpolationFactor); - float outerRadius = Lerp(splash.PrevOuterRad, splash.outerRad, _interpolationFactor); + float innerRadius = Lerp(splash.PrevInnerRad, splash.innerRad, GetInterpolationFactor()); + float outerRadius = Lerp(splash.PrevOuterRad, splash.outerRad, GetInterpolationFactor()); for (int i = 0; i < NUM_POINTS; i++) { @@ -630,11 +630,11 @@ namespace TEN::Renderer AddSpriteBillboard( &_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + bubble.SpriteIndex], - Vector3::Lerp(bubble.PrevPosition, bubble.Position, _interpolationFactor), - Vector4::Lerp(bubble.PrevColor, bubble.Color, _interpolationFactor), + Vector3::Lerp(bubble.PrevPosition, bubble.Position, GetInterpolationFactor()), + Vector4::Lerp(bubble.PrevColor, bubble.Color, GetInterpolationFactor()), 0.0f, 1.0f, - Vector2::Lerp(bubble.PrevSize, bubble.Size, _interpolationFactor) / 2, + Vector2::Lerp(bubble.PrevSize, bubble.Size, GetInterpolationFactor()) / 2, BlendMode::Additive, true, view); } } @@ -660,10 +660,10 @@ namespace TEN::Renderer AddSpriteBillboardConstrained( &_sprites[Objects[ID_DRIP_SPRITE].meshIndex], - Vector3::Lerp(drip.PrevPosition, drip.Position, _interpolationFactor), - Vector4::Lerp(drip.PrevColor, drip.Color, _interpolationFactor), - 0.0f, 1.0f, Vector2::Lerp(drip.PrevSize, drip.Size, _interpolationFactor), - BlendMode::Additive, -Vector3::Lerp(prevAxis, axis, _interpolationFactor), false, view); + Vector3::Lerp(drip.PrevPosition, drip.Position, GetInterpolationFactor()), + Vector4::Lerp(drip.PrevColor, drip.Color, GetInterpolationFactor()), + 0.0f, 1.0f, Vector2::Lerp(drip.PrevSize, drip.Size, GetInterpolationFactor()), + BlendMode::Additive, -Vector3::Lerp(prevAxis, axis, GetInterpolationFactor()), false, view); } } @@ -687,9 +687,9 @@ namespace TEN::Renderer AddSpriteBillboardConstrainedLookAt( &_sprites[ripple.SpriteIndex], - Vector3::Lerp(ripple.PrevPosition, ripple.Position, _interpolationFactor), - Vector4::Lerp(oldColor, color, _interpolationFactor), - 0.0f, 1.0f, Vector2(Lerp(ripple.PrevSize, ripple.Size, _interpolationFactor) * 2), + Vector3::Lerp(ripple.PrevPosition, ripple.Position, GetInterpolationFactor()), + Vector4::Lerp(oldColor, color, GetInterpolationFactor()), + 0.0f, 1.0f, Vector2(Lerp(ripple.PrevSize, ripple.Size, GetInterpolationFactor()) * 2), BlendMode::Additive, ripple.Normal, true, view); } } @@ -732,12 +732,12 @@ namespace TEN::Renderer AddSpriteBillboard( &_sprites[uwBlood.SpriteIndex], - Vector3::Lerp(uwBlood.PrevPosition, uwBlood.Position, _interpolationFactor), - Vector4::Lerp(oldColor, color, _interpolationFactor), + Vector3::Lerp(uwBlood.PrevPosition, uwBlood.Position, GetInterpolationFactor()), + Vector4::Lerp(oldColor, color, GetInterpolationFactor()), 0.0f, 1.0f, Vector2( - Lerp(uwBlood.PrevSize, uwBlood.Size, _interpolationFactor), - Lerp(uwBlood.PrevSize, uwBlood.Size, _interpolationFactor)) * 2, + Lerp(uwBlood.PrevSize, uwBlood.Size, GetInterpolationFactor()), + Lerp(uwBlood.PrevSize, uwBlood.Size, GetInterpolationFactor())) * 2, BlendMode::Additive, true, view); } } @@ -772,8 +772,8 @@ namespace TEN::Renderer auto pos = Vector3(shockwave->x, shockwave->y, shockwave->z); - float innerRadius = Lerp(shockwave->oldInnerRad, shockwave->innerRad, _interpolationFactor); - float outerRadius = Lerp(shockwave->oldOuterRad, shockwave->outerRad, _interpolationFactor); + float innerRadius = Lerp(shockwave->oldInnerRad, shockwave->innerRad, GetInterpolationFactor()); + float outerRadius = Lerp(shockwave->oldOuterRad, shockwave->outerRad, GetInterpolationFactor()); // Inner circle if (shockwave->style == (int)ShockwaveStyle::Normal) @@ -934,16 +934,16 @@ namespace TEN::Renderer Vector3::Lerp( Vector3(blood->oldX, blood->oldY, blood->oldZ), Vector3(blood->x, blood->y, blood->z), - _interpolationFactor), + GetInterpolationFactor()), Vector4::Lerp( Vector4(blood->oldShade / 255.0f, blood->oldShade * 0, blood->oldShade * 0, 1.0f), Vector4(blood->shade / 255.0f, blood->shade * 0, blood->shade * 0, 1.0f), - _interpolationFactor), - TO_RAD(Lerp(blood->oldRotAng << 4, blood->rotAng << 4, _interpolationFactor)), + GetInterpolationFactor()), + TO_RAD(Lerp(blood->oldRotAng << 4, blood->rotAng << 4, GetInterpolationFactor())), 1.0f, Vector2( - Lerp(blood->oldSize, blood->size, _interpolationFactor) * 8.0f, - Lerp(blood->oldSize, blood->size, _interpolationFactor) * 8.0f), + Lerp(blood->oldSize, blood->size, GetInterpolationFactor()) * 8.0f, + Lerp(blood->oldSize, blood->size, GetInterpolationFactor()) * 8.0f), BlendMode::Additive, true, view); } } @@ -967,9 +967,9 @@ namespace TEN::Renderer AddSpriteBillboard( &_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_UNDERWATERDUST], - Vector3::Lerp(part.PrevPosition, part.Position, _interpolationFactor), + Vector3::Lerp(part.PrevPosition, part.Position, GetInterpolationFactor()), Color(1.0f, 1.0f, 1.0f, part.Transparency()), - 0.0f, 1.0f, Vector2(Lerp(part.PrevSize, part.Size, _interpolationFactor)), + 0.0f, 1.0f, Vector2(Lerp(part.PrevSize, part.Size, GetInterpolationFactor())), BlendMode::Additive, true, view); break; @@ -981,9 +981,9 @@ namespace TEN::Renderer AddSpriteBillboard( &_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_UNDERWATERDUST], - Vector3::Lerp(part.PrevPosition, part.Position, _interpolationFactor), + Vector3::Lerp(part.PrevPosition, part.Position, GetInterpolationFactor()), Color(1.0f, 1.0f, 1.0f, part.Transparency()), - 0.0f, 1.0f, Vector2(Lerp(part.PrevSize, part.Size, _interpolationFactor)), + 0.0f, 1.0f, Vector2(Lerp(part.PrevSize, part.Size, GetInterpolationFactor())), BlendMode::Additive, true, view); break; @@ -998,10 +998,10 @@ namespace TEN::Renderer AddSpriteBillboardConstrained( &_sprites[Objects[ID_DRIP_SPRITE].meshIndex], - Vector3::Lerp(part.PrevPosition, part.Position, _interpolationFactor), + Vector3::Lerp(part.PrevPosition, part.Position, GetInterpolationFactor()), Color(0.8f, 1.0f, 1.0f, part.Transparency()), 0.0f, 1.0f, - Vector2(RAIN_WIDTH, Lerp(part.PrevSize, part.Size, _interpolationFactor)), + Vector2(RAIN_WIDTH, Lerp(part.PrevSize, part.Size, GetInterpolationFactor())), BlendMode::Additive, -v, true, view); break; @@ -1403,7 +1403,7 @@ namespace TEN::Renderer BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[deb.mesh.tex]), SamplerStateRegister::LinearClamp); } - _stStatic.World = Matrix::Lerp(deb.PrevTransform, deb.Transform, _interpolationFactor); + _stStatic.World = Matrix::Lerp(deb.PrevTransform, deb.Transform, GetInterpolationFactor()); _stStatic.Color = deb.color; _stStatic.AmbientLight = _rooms[deb.roomNumber].AmbientLight; _stStatic.LightMode = (int)deb.lightMode; @@ -1462,13 +1462,13 @@ namespace TEN::Renderer AddSpriteBillboard( &_sprites[Objects[ID_SMOKE_SPRITES].meshIndex + smoke.sprite], - Vector3::Lerp(smoke.PrevPosition, smoke.position, _interpolationFactor), - Vector4::Lerp(smoke.PrevColor, smoke.color, _interpolationFactor), - Lerp(smoke.PrevRotation, smoke.rotation, _interpolationFactor), + Vector3::Lerp(smoke.PrevPosition, smoke.position, GetInterpolationFactor()), + Vector4::Lerp(smoke.PrevColor, smoke.color, GetInterpolationFactor()), + Lerp(smoke.PrevRotation, smoke.rotation, GetInterpolationFactor()), 1.0f, Vector2( - Lerp(smoke.PrevSize, smoke.size, _interpolationFactor), - Lerp(smoke.PrevSize, smoke.size, _interpolationFactor)), + Lerp(smoke.PrevSize, smoke.size, GetInterpolationFactor()), + Lerp(smoke.PrevSize, smoke.size, GetInterpolationFactor())), BlendMode::AlphaBlend, true, view); } } @@ -1493,7 +1493,7 @@ namespace TEN::Renderer s.PrevVelocity.Normalize(prevVelocity); s.velocity.Normalize(velocity); - velocity = Vector3::Lerp(prevVelocity, velocity, _interpolationFactor); + velocity = Vector3::Lerp(prevVelocity, velocity, GetInterpolationFactor()); velocity.Normalize(); float normalizedLife = s.age / s.life; @@ -1502,7 +1502,7 @@ namespace TEN::Renderer AddSpriteBillboardConstrained( &_sprites[Objects[ID_SPARK_SPRITE].meshIndex], - Vector3::Lerp(s.PrevPosition, s.pos, _interpolationFactor), + Vector3::Lerp(s.PrevPosition, s.pos, GetInterpolationFactor()), color, 0, 1, Vector2( @@ -1527,13 +1527,13 @@ namespace TEN::Renderer AddSpriteBillboard( &_sprites[Objects[ID_EXPLOSION_SPRITES].meshIndex + exp.sprite], - Vector3::Lerp(exp.oldPos, exp.pos, _interpolationFactor), - Vector4::Lerp(exp.oldTint, exp.tint, _interpolationFactor), - Lerp(exp.oldRotation, exp.rotation, _interpolationFactor), + Vector3::Lerp(exp.oldPos, exp.pos, GetInterpolationFactor()), + Vector4::Lerp(exp.oldTint, exp.tint, GetInterpolationFactor()), + Lerp(exp.oldRotation, exp.rotation, GetInterpolationFactor()), 1.0f, Vector2( - Lerp(exp.oldSize, exp.size, _interpolationFactor), - Lerp(exp.oldSize, exp.size, _interpolationFactor)), + Lerp(exp.oldSize, exp.size, GetInterpolationFactor()), + Lerp(exp.oldSize, exp.size, GetInterpolationFactor())), BlendMode::Additive, true, view); } } @@ -1552,11 +1552,11 @@ namespace TEN::Renderer AddSpriteBillboard( &_sprites[Objects[part.sequence].meshIndex + part.sprite], - Vector3::Lerp(part.PrevWorldPosition, part.worldPosition, _interpolationFactor), + Vector3::Lerp(part.PrevWorldPosition, part.worldPosition, GetInterpolationFactor()), Color(1.0f, 1.0f, 1.0f), 0, 1.0f, Vector2( - Lerp(part.PrevSize, part.size, _interpolationFactor), - Lerp(part.PrevSize, part.size, _interpolationFactor) / 2), + Lerp(part.PrevSize, part.size, GetInterpolationFactor()), + Lerp(part.PrevSize, part.size, GetInterpolationFactor()) / 2), BlendMode::AlphaBlend, true, view); } } diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index 43d8548ad..26cb17d69 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -746,10 +746,10 @@ namespace TEN::Renderer constexpr auto COUNT_STRING_INF = "Inf"; constexpr auto COUNT_STRING_OFFSET = Vector2(DISPLAY_SPACE_RES.x / 40, 0.0f); - auto pos = Vector2::Lerp(pickup.PrevPosition, pickup.Position, _interpolationFactor); - auto orient = EulerAngles::Lerp(pickup.PrevOrientation, pickup.Orientation, _interpolationFactor); - float scale = Lerp(pickup.PrevScale, pickup.Scale, _interpolationFactor); - float opacity = Lerp(pickup.PrevOpacity, pickup.Opacity, _interpolationFactor); + auto pos = Vector2::Lerp(pickup.PrevPosition, pickup.Position, GetInterpolationFactor()); + auto orient = EulerAngles::Lerp(pickup.PrevOrientation, pickup.Orientation, GetInterpolationFactor()); + float scale = Lerp(pickup.PrevScale, pickup.Scale, GetInterpolationFactor()); + float opacity = Lerp(pickup.PrevOpacity, pickup.Opacity, GetInterpolationFactor()); // Draw display pickup. DrawObjectIn2DSpace(pickup.ObjectID, pos, orient, scale); @@ -1106,6 +1106,46 @@ namespace TEN::Renderer SetTextureOrDefault(_loadingScreenTexture, fileName); } + void Renderer::RenderFreezeMode(float interpFactor, bool staticBackground) + { + if (staticBackground) + { + // Set basic render states. + SetBlendMode(BlendMode::Opaque); + SetCullMode(CullMode::CounterClockwise); + + // Clear screen + _context->ClearRenderTargetView(_backBuffer.RenderTargetView.Get(), Colors::Black); + _context->ClearDepthStencilView(_backBuffer.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); + + // Bind back buffer. + _context->OMSetRenderTargets(1, _backBuffer.RenderTargetView.GetAddressOf(), _backBuffer.DepthStencilView.Get()); + _context->RSSetViewports(1, &_viewport); + ResetScissor(); + + // Draw full screen background. + DrawFullScreenQuad(_dumpScreenRenderTarget.ShaderResourceView.Get(), Vector3::One); + } + else + { + InterpolateCamera(interpFactor); + RenderScene(&_backBuffer, _gameCamera, SceneRenderMode::NoHud); + } + + // TODO: Put 3D object drawing management here (don't forget about interpolation!) + // Draw3DObjectsIn2DSpace(_gameCamera); + + // Draw display sprites sorted by priority. + CollectDisplaySprites(_gameCamera); + DrawDisplaySprites(_gameCamera); + DrawAllStrings(); + + ClearScene(); + + _context->ClearState(); + _swapChain->Present(1, 0); + } + void Renderer::RenderLoadingScreen(float percentage) { // Set basic render states. diff --git a/TombEngine/Renderer/RendererEnums.h b/TombEngine/Renderer/RendererEnums.h index 194752aab..23df7a369 100644 --- a/TombEngine/Renderer/RendererEnums.h +++ b/TombEngine/Renderer/RendererEnums.h @@ -229,6 +229,13 @@ enum class RendererPass RoomAmbient }; +enum class SceneRenderMode +{ + Full, + NoHud, + NoPostprocess +}; + enum class SpriteRenderType { Default, diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index c5244ef1e..cfaade9ce 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -436,13 +436,16 @@ namespace TEN::Renderer newItem->PrevAnimTransforms[j] = newItem->AnimTransforms[j]; } - newItem->InterpolatedPosition = Vector3::Lerp(newItem->PrevPosition, newItem->Position, _interpolationFactor); - newItem->InterpolatedTranslation = Matrix::Lerp(newItem->PrevTranslation, newItem->Translation, _interpolationFactor); - newItem->InterpolatedRotation = Matrix::Lerp(newItem->InterpolatedRotation, newItem->Rotation, _interpolationFactor); - newItem->InterpolatedWorld = Matrix::Lerp(newItem->PrevWorld, newItem->World, _interpolationFactor); + // Force interpolation only for Lara in player freeze mode. + bool forceValue = g_GameFlow->CurrentFreezeMode == FreezeMode::Player && item->ObjectNumber == ID_LARA; + + newItem->InterpolatedPosition = Vector3::Lerp(newItem->PrevPosition, newItem->Position, GetInterpolationFactor(forceValue)); + newItem->InterpolatedTranslation = Matrix::Lerp(newItem->PrevTranslation, newItem->Translation, GetInterpolationFactor(forceValue)); + newItem->InterpolatedRotation = Matrix::Lerp(newItem->InterpolatedRotation, newItem->Rotation, GetInterpolationFactor(forceValue)); + newItem->InterpolatedWorld = Matrix::Lerp(newItem->PrevWorld, newItem->World, GetInterpolationFactor(forceValue)); for (int j = 0; j < MAX_BONES; j++) - newItem->InterpolatedAnimTransforms[j] = Matrix::Lerp(newItem->PrevAnimTransforms[j], newItem->AnimTransforms[j], _interpolationFactor); + newItem->InterpolatedAnimTransforms[j] = Matrix::Lerp(newItem->PrevAnimTransforms[j], newItem->AnimTransforms[j], GetInterpolationFactor(forceValue)); CalculateLightFades(newItem); CollectLightsForItem(newItem); @@ -854,11 +857,11 @@ namespace TEN::Renderer newEffect->PrevScale = newEffect->Scale; } - newEffect->InterpolatedPosition = Vector3::Lerp(newEffect->PrevPosition, newEffect->Position, _interpolationFactor); - newEffect->InterpolatedTranslation = Matrix::Lerp(newEffect->PrevTranslation, newEffect->Translation, _interpolationFactor); - newEffect->InterpolatedRotation = Matrix::Lerp(newEffect->InterpolatedRotation, newEffect->Rotation, _interpolationFactor); - newEffect->InterpolatedWorld = Matrix::Lerp(newEffect->PrevWorld, newEffect->World, _interpolationFactor); - newEffect->InterpolatedScale = Matrix::Lerp(newEffect->PrevScale, newEffect->Scale, _interpolationFactor); + newEffect->InterpolatedPosition = Vector3::Lerp(newEffect->PrevPosition, newEffect->Position, GetInterpolationFactor()); + newEffect->InterpolatedTranslation = Matrix::Lerp(newEffect->PrevTranslation, newEffect->Translation, GetInterpolationFactor()); + newEffect->InterpolatedRotation = Matrix::Lerp(newEffect->InterpolatedRotation, newEffect->Rotation, GetInterpolationFactor()); + newEffect->InterpolatedWorld = Matrix::Lerp(newEffect->PrevWorld, newEffect->World, GetInterpolationFactor()); + newEffect->InterpolatedScale = Matrix::Lerp(newEffect->PrevScale, newEffect->Scale, GetInterpolationFactor()); CollectLightsForEffect(fx->roomNumber, newEffect); diff --git a/TombEngine/Renderer/RendererHelper.cpp b/TombEngine/Renderer/RendererHelper.cpp index 19bc86d7b..2e6a96965 100644 --- a/TombEngine/Renderer/RendererHelper.cpp +++ b/TombEngine/Renderer/RendererHelper.cpp @@ -510,9 +510,9 @@ namespace TEN::Renderer return g_Configuration.EnableHighFramerate ? (g_Renderer.GetScreenRefreshRate() / (float)FPS) : 1.0f; } - float Renderer::GetInterpolationFactor() const + float Renderer::GetInterpolationFactor(bool forceRawValue) const { - return _interpolationFactor; + return (forceRawValue || g_GameFlow->CurrentFreezeMode == FreezeMode::None) ? _interpolationFactor : 0.0f; } Vector2i Renderer::GetScreenResolution() const diff --git a/TombEngine/Renderer/RendererLara.cpp b/TombEngine/Renderer/RendererLara.cpp index b1e29ebe5..75dc4e55b 100644 --- a/TombEngine/Renderer/RendererLara.cpp +++ b/TombEngine/Renderer/RendererLara.cpp @@ -331,6 +331,8 @@ void TEN::Renderer::Renderer::DrawLara(RenderView& view, RendererPass rendererPa void Renderer::DrawLaraHair(RendererItem* itemToDraw, RendererRoom* room, RenderView& view, RendererPass rendererPass) { + bool forceValue = g_GameFlow->CurrentFreezeMode == FreezeMode::Player; + for (int i = 0; i < HairEffect.Units.size(); i++) { const auto& unit = HairEffect.Units[i]; @@ -351,9 +353,9 @@ void Renderer::DrawLaraHair(RendererItem* itemToDraw, RendererRoom* room, Render const auto& segment = unit.Segments[i]; auto worldMatrix = Matrix::CreateFromQuaternion( - Quaternion::Lerp(segment.PrevOrientation, segment.Orientation, _interpolationFactor)) * + Quaternion::Lerp(segment.PrevOrientation, segment.Orientation, GetInterpolationFactor(forceValue))) * Matrix::CreateTranslation( - Vector3::Lerp(segment.PrevPosition, segment.Position, _interpolationFactor)); + Vector3::Lerp(segment.PrevPosition, segment.Position, GetInterpolationFactor(forceValue))); _stItem.BonesMatrices[i + 1] = worldMatrix; _stItem.BoneLightModes[i] = (int)LightMode::Dynamic; diff --git a/TombEngine/Renderer/RendererPostProcess.cpp b/TombEngine/Renderer/RendererPostProcess.cpp index 1c00be4a8..656a6e90b 100644 --- a/TombEngine/Renderer/RendererPostProcess.cpp +++ b/TombEngine/Renderer/RendererPostProcess.cpp @@ -4,7 +4,7 @@ namespace TEN::Renderer { - void Renderer::DrawPostprocess(RenderTarget2D* renderTarget, RenderView& view) + void Renderer::DrawPostprocess(RenderTarget2D* renderTarget, RenderView& view, SceneRenderMode renderMode) { SetBlendMode(BlendMode::Opaque); SetCullMode(CullMode::CounterClockwise); @@ -12,10 +12,13 @@ namespace TEN::Renderer _context->RSSetViewports(1, &view.Viewport); ResetScissor(); + float screenFadeFactor = renderMode == SceneRenderMode::Full ? ScreenFadeCurrent : 1.0f; + float cinematicBarsHeight = renderMode == SceneRenderMode::Full ? Smoothstep(CinematicBarsHeight) * SPOTCAM_CINEMATIC_BARS_HEIGHT : 0.0f; + + _stPostProcessBuffer.ScreenFadeFactor = screenFadeFactor; + _stPostProcessBuffer.CinematicBarsHeight = cinematicBarsHeight; _stPostProcessBuffer.ViewportWidth = _screenWidth; _stPostProcessBuffer.ViewportHeight = _screenHeight; - _stPostProcessBuffer.ScreenFadeFactor = ScreenFadeCurrent; - _stPostProcessBuffer.CinematicBarsHeight = Smoothstep(CinematicBarsHeight) * SPOTCAM_CINEMATIC_BARS_HEIGHT; _stPostProcessBuffer.EffectStrength = _postProcessStrength; _stPostProcessBuffer.Tint = _postProcessTint; _cbPostProcessBuffer.UpdateData(_stPostProcessBuffer, _context.Get()); diff --git a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h index c67c32650..e09b79037 100644 --- a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h +++ b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h @@ -5,17 +5,13 @@ class ScriptInterfaceLevel; -enum class TitleType -{ - Flyby, - Background -}; - class ScriptInterfaceFlowHandler { public: - GameStatus LastGameStatus = GameStatus::Normal; - TitleType TitleType = TitleType::Flyby; + GameStatus LastGameStatus = GameStatus::Normal; + FreezeMode LastFreezeMode = FreezeMode::None; + FreezeMode CurrentFreezeMode = FreezeMode::None; + std::string IntroImagePath = {}; std::string TitleScreenImagePath = {}; diff --git a/TombEngine/Scripting/Include/ScriptInterfaceGame.h b/TombEngine/Scripting/Include/ScriptInterfaceGame.h index 81958aaaf..75510183a 100644 --- a/TombEngine/Scripting/Include/ScriptInterfaceGame.h +++ b/TombEngine/Scripting/Include/ScriptInterfaceGame.h @@ -58,6 +58,7 @@ public: virtual void OnSave() = 0; virtual void OnEnd(GameStatus reason) = 0; virtual void OnUseItem(GAME_OBJECT_ID objectNumber) = 0; + virtual void OnFreeze() = 0; virtual void ShortenTENCalls() = 0; virtual void FreeLevelScripts() = 0; @@ -80,7 +81,11 @@ public: std::vector& preLoad, std::vector& postLoad, std::vector& preLoop, - std::vector& postLoop) const = 0; + std::vector& postLoop, + std::vector& preUseItem, + std::vector& postUseItem, + std::vector& preBreak, + std::vector& postBreak) const = 0; virtual void SetCallbackStrings( const std::vector& preStart, @@ -92,7 +97,11 @@ public: const std::vector& preLoad, const std::vector& postLoad, const std::vector& preLoop, - const std::vector& postLoop) = 0; + const std::vector& postLoop, + const std::vector& preUseItem, + const std::vector& postUseItem, + const std::vector& preBreak, + const std::vector& postBreak) = 0; }; extern ScriptInterfaceGame* g_GameScript; diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index de32a0aaf..dcf278189 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -66,10 +66,14 @@ static constexpr char ScriptReserved_PreSave[] = "PRESAVE"; static constexpr char ScriptReserved_PostSave[] = "POSTSAVE"; static constexpr char ScriptReserved_PreLoad[] = "PRELOAD"; static constexpr char ScriptReserved_PostLoad[] = "POSTLOAD"; +static constexpr char ScriptReserved_PreControlPhase[] = "PRECONTROLPHASE"; // DEPRECATED +static constexpr char ScriptReserved_PostControlPhase[] = "POSTCONTROLPHASE"; // DEPRECATED static constexpr char ScriptReserved_PreLoop[] = "PRELOOP"; static constexpr char ScriptReserved_PostLoop[] = "POSTLOOP"; -static constexpr char ScriptReserved_PreControlPhase[] = "PRECONTROLPHASE"; -static constexpr char ScriptReserved_PostControlPhase[] = "POSTCONTROLPHASE"; +static constexpr char ScriptReserved_PreUseItem[] = "PREUSEITEM"; +static constexpr char ScriptReserved_PostUseItem[] = "POSTUSEITEM"; +static constexpr char ScriptReserved_PreFreeze[] = "PREFREEZE"; +static constexpr char ScriptReserved_PostFreeze[] = "POSTFREEZE"; // Built-in LevelFuncs static constexpr char ScriptReserved_OnStart[] = "OnStart"; @@ -79,6 +83,7 @@ static constexpr char ScriptReserved_OnControlPhase[] = "OnControlPhase"; // DEP static constexpr char ScriptReserved_OnSave[] = "OnSave"; static constexpr char ScriptReserved_OnEnd[] = "OnEnd"; static constexpr char ScriptReserved_OnUseItem[] = "OnUseItem"; +static constexpr char ScriptReserved_OnFreeze[] = "OnFreeze"; // Event types (volume events + global events) static constexpr char ScriptReserved_EventOnEnter[] = "ENTER"; @@ -90,6 +95,7 @@ static constexpr char ScriptReserved_EventOnLoop[] = "LOOP"; static constexpr char ScriptReserved_EventOnSave[] = "SAVE"; static constexpr char ScriptReserved_EventOnEnd[] = "END"; static constexpr char ScriptReserved_EventOnUseItem[] = "USEITEM"; +static constexpr char ScriptReserved_EventOnFreeze[] = "FREEZE"; // Member functions static constexpr char ScriptReserved_New[] = "New"; @@ -213,6 +219,8 @@ static constexpr char ScriptReserved_SetSettings[] = "SetSettings"; static constexpr char ScriptReserved_SetAnimations[] = "SetAnimations"; static constexpr char ScriptReserved_EndLevel[] = "EndLevel"; static constexpr char ScriptReserved_GetGameStatus[] = "GetGameStatus"; +static constexpr char ScriptReserved_SetFreezeMode[] = "SetFreezeMode"; +static constexpr char ScriptReserved_GetFreezeMode[] = "GetFreezeMode"; static constexpr char ScriptReserved_SaveGame[] = "SaveGame"; static constexpr char ScriptReserved_LoadGame[] = "LoadGame"; static constexpr char ScriptReserved_DeleteSaveGame[] = "DeleteSaveGame"; @@ -247,6 +255,7 @@ static constexpr char ScriptReserved_LaraWeaponType[] = "LaraWeaponType"; static constexpr char ScriptReserved_PlayerAmmoType[] = "PlayerAmmoType"; static constexpr char ScriptReserved_HandStatus[] = "HandStatus"; static constexpr char ScriptReserved_GameStatus[] = "GameStatus"; +static constexpr char ScriptReserved_FreezeMode[] = "FreezeMode"; // Functions static constexpr char ScriptReserved_ShowString[] = "ShowString"; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Enums/FreezeModes.h b/TombEngine/Scripting/Internal/TEN/Flow/Enums/FreezeModes.h new file mode 100644 index 000000000..368993ce8 --- /dev/null +++ b/TombEngine/Scripting/Internal/TEN/Flow/Enums/FreezeModes.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Game/control/control.h" + +/*** +Constants for freeze modes. +@enum Flow.FreezeMode +@pragma nostrip +*/ + +/*** Flow.FreezeMode constants. + +The following constants are inside Flow.FreezeMode. + + NONE - Normal in-game operation. + FULL - Game is completely frozen, as in pause or inventory menus. + SPECTATOR - Game is completely frozen, but with ability to control camera. + PLAYER - Game is completely frozen, but with ability to control player. Experimental. + +@section Flow.FreezeMode +*/ + +/*** Table of freeze modes. +@table CONSTANT_STRING_HERE +*/ + +static const auto FREEZE_MODES = std::unordered_map +{ + { "NONE", FreezeMode::None }, + { "FULL", FreezeMode::Full }, + { "SPECTATOR", FreezeMode::Spectator }, + { "PLAYER", FreezeMode::Player } +}; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/GameStatuses.h b/TombEngine/Scripting/Internal/TEN/Flow/Enums/GameStatuses.h similarity index 100% rename from TombEngine/Scripting/Internal/TEN/Flow/GameStatuses.h rename to TombEngine/Scripting/Internal/TEN/Flow/Enums/GameStatuses.h diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp index 62a19ecc5..70bc3f86d 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp @@ -8,7 +8,8 @@ #include "Scripting/Include/Objects/ScriptInterfaceObjectsHandler.h" #include "Scripting/Include/Strings/ScriptInterfaceStringsHandler.h" #include "Scripting/Internal/ReservedScriptNames.h" -#include "Scripting/Internal/TEN/Flow/GameStatuses.h" +#include "Scripting/Internal/TEN/Flow/Enums/FreezeModes.h" +#include "Scripting/Internal/TEN/Flow/Enums/GameStatuses.h" #include "Scripting/Internal/TEN/Flow/InventoryItem/InventoryItem.h" #include "Scripting/Internal/TEN/Logic/LevelFunc.h" #include "Scripting/Internal/TEN/Vec2/Vec2.h" @@ -148,6 +149,22 @@ Get current game status, such as normal game loop, exiting to title, etc. */ tableFlow.set_function(ScriptReserved_GetGameStatus, &FlowHandler::GetGameStatus, this); +/*** +Get current freeze mode, such as none, full, spectator or player. +@function GetFreezeMode +@treturn Flow.FreezeMode the current freeze mode +*/ + tableFlow.set_function(ScriptReserved_GetFreezeMode, &FlowHandler::GetFreezeMode, this); + +/*** +Set current freeze mode, such as none, full, spectator or player. +Freeze mode specifies whether game is in normal mode or paused in a particular way to allow +custom menu creation, photo mode or time freeze. +@function SetFreezeMode +@tparam Flow.FreezeMode new freeze mode to set. +*/ + tableFlow.set_function(ScriptReserved_SetFreezeMode, &FlowHandler::SetFreezeMode, this); + /*** Save the game to a savegame slot. @function SaveGame @@ -286,6 +303,7 @@ Specify which translations in the strings table correspond to which languages. _handler.MakeReadOnlyTable(tableFlow, ScriptReserved_ItemAction, ITEM_MENU_ACTIONS); _handler.MakeReadOnlyTable(tableFlow, ScriptReserved_ErrorMode, ERROR_MODES); _handler.MakeReadOnlyTable(tableFlow, ScriptReserved_GameStatus, GAME_STATUSES); + _handler.MakeReadOnlyTable(tableFlow, ScriptReserved_FreezeMode, FREEZE_MODES); } FlowHandler::~FlowHandler() @@ -480,6 +498,16 @@ GameStatus FlowHandler::GetGameStatus() return this->LastGameStatus; } +FreezeMode FlowHandler::GetFreezeMode() +{ + return this->CurrentFreezeMode; +} + +void FlowHandler::SetFreezeMode(FreezeMode mode) +{ + this->CurrentFreezeMode = mode; +} + void FlowHandler::FlipMap(int group) { DoFlipMap(group); diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h index 4369561dd..06f881412 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h @@ -64,6 +64,8 @@ public: int GetNumLevels() const; void EndLevel(std::optional nextLevel, std::optional startPosIndex); GameStatus GetGameStatus(); + FreezeMode GetFreezeMode(); + void SetFreezeMode(FreezeMode mode); void FlipMap(int group); bool GetFlipMapStatus(std::optional group); void SaveGame(int slot); diff --git a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp index 9b96d12d7..bda6d6213 100644 --- a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp @@ -36,7 +36,11 @@ enum class CallbackPoint PreSave, PostSave, PreEnd, - PostEnd + PostEnd, + PreUseItem, + PostUseItem, + PreFreeze, + PostFreeze }; static const std::unordered_map CALLBACK_POINTS @@ -49,11 +53,14 @@ static const std::unordered_map CALLBACK_POINTS { ScriptReserved_PostLoop, CallbackPoint::PostLoop }, { ScriptReserved_PreControlPhase, CallbackPoint::PreLoop }, // DEPRECATED { ScriptReserved_PostControlPhase, CallbackPoint::PostLoop }, // DEPRECATED - { ScriptReserved_PostSave, CallbackPoint::PostSave }, - { ScriptReserved_PostSave, CallbackPoint::PostSave }, { ScriptReserved_PreSave, CallbackPoint::PreSave }, + { ScriptReserved_PostSave, CallbackPoint::PostSave }, { ScriptReserved_PreEnd, CallbackPoint::PreEnd }, - { ScriptReserved_PostEnd, CallbackPoint::PostEnd } + { ScriptReserved_PostEnd, CallbackPoint::PostEnd }, + { ScriptReserved_PreUseItem, CallbackPoint::PreUseItem }, + { ScriptReserved_PostUseItem, CallbackPoint::PostUseItem }, + { ScriptReserved_PreFreeze, CallbackPoint::PreFreeze }, + { ScriptReserved_PostFreeze, CallbackPoint::PostFreeze } }; static const std::unordered_map EVENT_TYPES @@ -66,7 +73,8 @@ static const std::unordered_map EVENT_TYPES { ScriptReserved_EventOnSave, EventType::Save }, { ScriptReserved_EventOnStart, EventType::Start }, { ScriptReserved_EventOnEnd, EventType::End }, - { ScriptReserved_EventOnUseItem, EventType::UseItem } + { ScriptReserved_EventOnUseItem, EventType::UseItem }, + { ScriptReserved_EventOnFreeze, EventType::Freeze } }; enum class LevelEndReason @@ -190,6 +198,10 @@ LogicHandler::LogicHandler(sol::state* lua, sol::table & parent) : m_handler{ lu m_callbacks.insert(std::make_pair(CallbackPoint::PostSave, &m_callbacksPostSave)); m_callbacks.insert(std::make_pair(CallbackPoint::PreEnd, &m_callbacksPreEnd)); m_callbacks.insert(std::make_pair(CallbackPoint::PostEnd, &m_callbacksPostEnd)); + m_callbacks.insert(std::make_pair(CallbackPoint::PreUseItem, &m_callbacksPreUseItem)); + m_callbacks.insert(std::make_pair(CallbackPoint::PostUseItem, &m_callbacksPostUseItem)); + m_callbacks.insert(std::make_pair(CallbackPoint::PreFreeze, &m_callbacksPreFreeze)); + m_callbacks.insert(std::make_pair(CallbackPoint::PostFreeze, &m_callbacksPostFreeze)); LevelFunc::Register(tableLogic); @@ -221,6 +233,9 @@ Possible values for `point`: PRELOAD -- will be called immediately before OnLoad POSTLOAD -- will be called immediately after OnLoad + PREFREEZE -- will be called before entering freeze mode + POSTFREEZE -- will be called immediately after exiting freeze mode + -- These take a LevelEndReason arg, like OnEnd PREEND -- will be called immediately before OnEnd POSTEND -- will be called immediately after OnEnd @@ -229,6 +244,10 @@ Possible values for `point`: PRELOOP -- will be called in the beginning of game loop POSTLOOP -- will be called at the end of game loop + -- These take functions which accepts an objectNumber argument, like OnUseItem + PREUSEITEM -- will be called immediately before OnUseItem + POSTUSEITEM -- will be called immediately after OnUseItem + The order in which two functions with the same CallbackPoint are called is undefined. i.e. if you register `MyFunc` and `MyFunc2` with `PRELOOP`, both will be called in the beginning of game loop, but there is no guarantee that `MyFunc` will be called before `MyFunc2`, or vice-versa. @@ -295,6 +314,7 @@ Possible event type values: END LOOP USEITEM + MENU @function HandleEvent @tparam string name Name of the event set to find. @@ -460,6 +480,7 @@ void LogicHandler::FreeLevelScripts() m_onSave = sol::nil; m_onEnd = sol::nil; m_onUseItem = sol::nil; + m_onBreak = sol::nil; m_handler.GetState()->collect_garbage(); } @@ -778,7 +799,11 @@ void LogicHandler::GetCallbackStrings( std::vector& preLoad, std::vector& postLoad, std::vector& preLoop, - std::vector& postLoop) const + std::vector& postLoop, + std::vector& preUseItem, + std::vector& postUseItem, + std::vector& preBreak, + std::vector& postBreak) const { auto populateWith = [](std::vector& dest, const std::unordered_set& src) { @@ -800,6 +825,12 @@ void LogicHandler::GetCallbackStrings( populateWith(preLoop, m_callbacksPreLoop); populateWith(postLoop, m_callbacksPostLoop); + + populateWith(preUseItem, m_callbacksPreUseItem); + populateWith(postUseItem, m_callbacksPostUseItem); + + populateWith(preBreak, m_callbacksPreFreeze); + populateWith(postBreak, m_callbacksPostFreeze); } void LogicHandler::SetCallbackStrings( @@ -812,7 +843,11 @@ void LogicHandler::SetCallbackStrings( const std::vector& preLoad, const std::vector& postLoad, const std::vector& preLoop, - const std::vector& postLoop) + const std::vector& postLoop, + const std::vector& preUseItem, + const std::vector& postUseItem, + const std::vector& preBreak, + const std::vector& postBreak) { auto populateWith = [](std::unordered_set& dest, const std::vector& src) { @@ -834,6 +869,12 @@ void LogicHandler::SetCallbackStrings( populateWith(m_callbacksPreLoop, preLoop); populateWith(m_callbacksPostLoop, postLoop); + + populateWith(m_callbacksPreUseItem, preUseItem); + populateWith(m_callbacksPostUseItem, postUseItem); + + populateWith(m_callbacksPreFreeze, preBreak); + populateWith(m_callbacksPostFreeze, postBreak); } template @@ -896,7 +937,7 @@ void LogicHandler::ExecuteString(const std::string& command) // These wind up calling CallLevelFunc, which is where all error checking is. void LogicHandler::ExecuteFunction(const std::string& name, short idOne, short idTwo) { - sol::protected_function func = m_levelFuncs_luaFunctions[name]; + auto func = m_levelFuncs_luaFunctions[name]; func(std::make_unique(idOne), std::make_unique(idTwo)); } @@ -914,28 +955,27 @@ void LogicHandler::ExecuteFunction(const std::string& name, TEN::Control::Volume } } - void LogicHandler::OnStart() { - for (auto& name : m_callbacksPreStart) + for (const auto& name : m_callbacksPreStart) CallLevelFuncByName(name); if (m_onStart.valid()) CallLevelFunc(m_onStart); - for (auto& name : m_callbacksPostStart) + for (const auto& name : m_callbacksPostStart) CallLevelFuncByName(name); } void LogicHandler::OnLoad() { - for (auto& name : m_callbacksPreLoad) + for (const auto& name : m_callbacksPreLoad) CallLevelFuncByName(name); if (m_onLoad.valid()) CallLevelFunc(m_onLoad); - for (auto& name : m_callbacksPostLoad) + for (const auto& name : m_callbacksPostLoad) CallLevelFuncByName(name); } @@ -943,7 +983,7 @@ void LogicHandler::OnLoop(float deltaTime, bool postLoop) { if (!postLoop) { - for (auto& name : m_callbacksPreLoop) + for (const auto& name : m_callbacksPreLoop) CallLevelFuncByName(name, deltaTime); lua_gc(m_handler.GetState()->lua_state(), LUA_GCCOLLECT, 0); @@ -952,27 +992,26 @@ void LogicHandler::OnLoop(float deltaTime, bool postLoop) } else { - for (auto& name : m_callbacksPostLoop) + for (const auto& name : m_callbacksPostLoop) CallLevelFuncByName(name, deltaTime); } } void LogicHandler::OnSave() { - for (auto& name : m_callbacksPreSave) + for (const auto& name : m_callbacksPreSave) CallLevelFuncByName(name); if (m_onSave.valid()) CallLevelFunc(m_onSave); - for (auto& name : m_callbacksPostSave) + for (const auto& name : m_callbacksPostSave) CallLevelFuncByName(name); } void LogicHandler::OnEnd(GameStatus reason) { - auto endReason{LevelEndReason::Other}; - + auto endReason = LevelEndReason::Other; switch (reason) { case GameStatus::LaraDead: @@ -992,20 +1031,38 @@ void LogicHandler::OnEnd(GameStatus reason) break; } - for (auto& name : m_callbacksPreEnd) + for (const auto& name : m_callbacksPreEnd) CallLevelFuncByName(name, endReason); - if(m_onEnd.valid()) + if (m_onEnd.valid()) CallLevelFunc(m_onEnd, endReason); - for (auto& name : m_callbacksPostEnd) + for (const auto& name : m_callbacksPostEnd) CallLevelFuncByName(name, endReason); } void LogicHandler::OnUseItem(GAME_OBJECT_ID objectNumber) { + for (const auto& name : m_callbacksPreUseItem) + CallLevelFuncByName(name, objectNumber); + if (m_onUseItem.valid()) CallLevelFunc(m_onUseItem, objectNumber); + + for (const auto& name : m_callbacksPostUseItem) + CallLevelFuncByName(name, objectNumber); +} + +void LogicHandler::OnFreeze() +{ + for (const auto& name : m_callbacksPreFreeze) + CallLevelFuncByName(name); + + if (m_onBreak.valid()) + CallLevelFunc(m_onBreak); + + for (const auto& name : m_callbacksPostFreeze) + CallLevelFuncByName(name); } /*** Special tables @@ -1152,4 +1209,5 @@ void LogicHandler::InitCallbacks() assignCB(m_onSave, ScriptReserved_OnSave); assignCB(m_onEnd, ScriptReserved_OnEnd); assignCB(m_onUseItem, ScriptReserved_OnUseItem); + assignCB(m_onBreak, ScriptReserved_OnFreeze); } diff --git a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h index c1f4f2108..51fc08a40 100644 --- a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h @@ -51,23 +51,28 @@ private: // "LevelFuncs.MyLevel.CoolFuncs" std::unordered_map> m_levelFuncs_tablesOfNames{}; + std::unordered_set m_callbacksPreStart; + std::unordered_set m_callbacksPostStart; + std::unordered_set m_callbacksPreLoop; + std::unordered_set m_callbacksPostLoop; + std::unordered_set m_callbacksPreLoad; + std::unordered_set m_callbacksPostLoad; + std::unordered_set m_callbacksPreSave; + std::unordered_set m_callbacksPostSave; + std::unordered_set m_callbacksPreEnd; + std::unordered_set m_callbacksPostEnd; + std::unordered_set m_callbacksPreUseItem; + std::unordered_set m_callbacksPostUseItem; + std::unordered_set m_callbacksPreFreeze; + std::unordered_set m_callbacksPostFreeze; + sol::protected_function m_onStart{}; - sol::protected_function m_onLoad{}; sol::protected_function m_onLoop{}; + sol::protected_function m_onLoad{}; sol::protected_function m_onSave{}; sol::protected_function m_onEnd{}; sol::protected_function m_onUseItem{}; - - std::unordered_set m_callbacksPreSave; - std::unordered_set m_callbacksPostSave; - std::unordered_set m_callbacksPreLoad; - std::unordered_set m_callbacksPostLoad; - std::unordered_set m_callbacksPreStart; - std::unordered_set m_callbacksPostStart; - std::unordered_set m_callbacksPreEnd; - std::unordered_set m_callbacksPostEnd; - std::unordered_set m_callbacksPreLoop; - std::unordered_set m_callbacksPostLoop; + sol::protected_function m_onBreak{}; std::unordered_map *> m_callbacks; @@ -148,11 +153,15 @@ public: const std::vector& preEnd, const std::vector& postEnd, const std::vector& preSave, - const std::vector& postSave, - const std::vector& preLoad, - const std::vector& postLoad, - const std::vector& preLoop, - const std::vector& postLoop) override; + const std::vector& postSave, + const std::vector& preLoad, + const std::vector& postLoad, + const std::vector& preLoop, + const std::vector& postLoop, + const std::vector& preUseItem, + const std::vector& postUseItem, + const std::vector& preBreak, + const std::vector& postBreak) override; void GetCallbackStrings(std::vector& preStart, std::vector& postStart, @@ -163,7 +172,11 @@ public: std::vector& preLoad, std::vector& postLoad, std::vector& preLoop, - std::vector& postLoop) const override; + std::vector& postLoop, + std::vector& preUseItem, + std::vector& postUseItem, + std::vector& preBreak, + std::vector& postBreak) const override; void InitCallbacks() override; void OnStart() override; @@ -172,4 +185,5 @@ public: void OnSave() override; void OnEnd(GameStatus reason) override; void OnUseItem(GAME_OBJECT_ID item) override; + void OnFreeze() override; }; diff --git a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp index ab6e5bfd2..eee32ad9d 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp @@ -1,5 +1,6 @@ #include "framework.h" +#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h" #include "Scripting/Internal/ScriptAssert.h" #include "Scripting/Internal/ReservedScriptNames.h" @@ -20,13 +21,14 @@ when you need to use screen-space coordinates. @pragma nostrip */ -UserDisplayString::UserDisplayString(const std::string& key, const Vec2& pos, float scale, D3DCOLOR color, const FlagArray& flags, bool isTranslated) : +UserDisplayString::UserDisplayString(const std::string& key, const Vec2& pos, float scale, D3DCOLOR color, const FlagArray& flags, bool isTranslated, FreezeMode owner) : _key(key), _position(pos), _scale(scale), _color(color), _flags(flags), - _isTranslated(isTranslated) + _isTranslated(isTranslated), + _owner(owner) { } @@ -89,7 +91,10 @@ static std::unique_ptr CreateString(const std::string& key, const if (!IsValidOptionalArg(scale)) ScriptAssertF(false, "Wrong argument type for {}.new \"scale\" argument; must be a float or nil.\n{}", ScriptReserved_DisplayString, getCallStack()); - auto string = UserDisplayString(key, pos, USE_IF_HAVE(float, scale, 1.0f), USE_IF_HAVE(ScriptColor, color, ScriptColor(255, 255, 255)), flagArray, USE_IF_HAVE(bool, isTranslated, false)); + auto string = UserDisplayString(key, pos, USE_IF_HAVE(float, scale, 1.0f), USE_IF_HAVE(ScriptColor, color, ScriptColor(255, 255, 255)), + flagArray, USE_IF_HAVE(bool, isTranslated, false), g_GameFlow->CurrentFreezeMode); + + DisplayString::SetItemCallbackRoutine(id, string); return ptr; } diff --git a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h index 18c78f316..63e6630a8 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h +++ b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.h @@ -1,4 +1,6 @@ #pragma once + +#include "Game/control/control.h" #include "Scripting/Internal/TEN/Color/Color.h" #include "Scripting/Internal/TEN/Vec2/Vec2.h" @@ -60,11 +62,13 @@ private: bool _isTranslated = false; bool _deleteWhenZero = false; + FreezeMode _owner = FreezeMode::None; + // Constructors UserDisplayString() = default; public: - UserDisplayString(const std::string& key, const Vec2& pos, float scale, D3DCOLOR color, const FlagArray& flags, bool isTranslated); + UserDisplayString(const std::string& key, const Vec2& pos, float scale, D3DCOLOR color, const FlagArray& flags, bool isTranslated, FreezeMode owner); }; using DisplayStringID = uintptr_t; diff --git a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp index 3eb6509ea..8e87c312c 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp @@ -117,7 +117,7 @@ void StringsHandler::ProcessDisplayStrings(float deltaTime) } else { - if (!endOfLife || str._isInfinite) + if ((!endOfLife || str._isInfinite) && str._owner == g_GameFlow->CurrentFreezeMode) { auto cstr = str._isTranslated ? g_GameFlow->GetString(str._key.c_str()) : str._key; int flags = 0; diff --git a/TombEngine/Specific/clock.cpp b/TombEngine/Specific/clock.cpp index 854ecd4ec..f2fa0a3d3 100644 --- a/TombEngine/Specific/clock.cpp +++ b/TombEngine/Specific/clock.cpp @@ -69,7 +69,6 @@ float HighFramerateSynchronizer::GetInterpolationFactor() return std::min((float)_controlDelay / (float)CONTROL_FRAME_TIME, 1.0f); } - int TimeSync() { auto ct = LARGE_INTEGER{}; diff --git a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h index 4a319c1e0..37087531a 100644 --- a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h +++ b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h @@ -7380,6 +7380,10 @@ struct SaveGameT : public flatbuffers::NativeTable { std::vector callbacks_post_load{}; std::vector callbacks_pre_loop{}; std::vector callbacks_post_loop{}; + std::vector callbacks_pre_useitem{}; + std::vector callbacks_post_useitem{}; + std::vector callbacks_pre_freeze{}; + std::vector callbacks_post_freeze{}; }; struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -7439,7 +7443,11 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_CALLBACKS_PRE_LOAD = 102, VT_CALLBACKS_POST_LOAD = 104, VT_CALLBACKS_PRE_LOOP = 106, - VT_CALLBACKS_POST_LOOP = 108 + VT_CALLBACKS_POST_LOOP = 108, + VT_CALLBACKS_PRE_USEITEM = 110, + VT_CALLBACKS_POST_USEITEM = 112, + VT_CALLBACKS_PRE_FREEZE = 114, + VT_CALLBACKS_POST_FREEZE = 116 }; const TEN::Save::SaveGameHeader *header() const { return GetPointer(VT_HEADER); @@ -7600,6 +7608,18 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector> *callbacks_post_loop() const { return GetPointer> *>(VT_CALLBACKS_POST_LOOP); } + const flatbuffers::Vector> *callbacks_pre_useitem() const { + return GetPointer> *>(VT_CALLBACKS_PRE_USEITEM); + } + const flatbuffers::Vector> *callbacks_post_useitem() const { + return GetPointer> *>(VT_CALLBACKS_POST_USEITEM); + } + const flatbuffers::Vector> *callbacks_pre_freeze() const { + return GetPointer> *>(VT_CALLBACKS_PRE_FREEZE); + } + const flatbuffers::Vector> *callbacks_post_freeze() const { + return GetPointer> *>(VT_CALLBACKS_POST_FREEZE); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_HEADER) && @@ -7723,6 +7743,18 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyOffset(verifier, VT_CALLBACKS_POST_LOOP) && verifier.VerifyVector(callbacks_post_loop()) && verifier.VerifyVectorOfStrings(callbacks_post_loop()) && + VerifyOffset(verifier, VT_CALLBACKS_PRE_USEITEM) && + verifier.VerifyVector(callbacks_pre_useitem()) && + verifier.VerifyVectorOfStrings(callbacks_pre_useitem()) && + VerifyOffset(verifier, VT_CALLBACKS_POST_USEITEM) && + verifier.VerifyVector(callbacks_post_useitem()) && + verifier.VerifyVectorOfStrings(callbacks_post_useitem()) && + VerifyOffset(verifier, VT_CALLBACKS_PRE_FREEZE) && + verifier.VerifyVector(callbacks_pre_freeze()) && + verifier.VerifyVectorOfStrings(callbacks_pre_freeze()) && + VerifyOffset(verifier, VT_CALLBACKS_POST_FREEZE) && + verifier.VerifyVector(callbacks_post_freeze()) && + verifier.VerifyVectorOfStrings(callbacks_post_freeze()) && verifier.EndTable(); } SaveGameT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; @@ -7893,6 +7925,18 @@ struct SaveGameBuilder { void add_callbacks_post_loop(flatbuffers::Offset>> callbacks_post_loop) { fbb_.AddOffset(SaveGame::VT_CALLBACKS_POST_LOOP, callbacks_post_loop); } + void add_callbacks_pre_useitem(flatbuffers::Offset>> callbacks_pre_useitem) { + fbb_.AddOffset(SaveGame::VT_CALLBACKS_PRE_USEITEM, callbacks_pre_useitem); + } + void add_callbacks_post_useitem(flatbuffers::Offset>> callbacks_post_useitem) { + fbb_.AddOffset(SaveGame::VT_CALLBACKS_POST_USEITEM, callbacks_post_useitem); + } + void add_callbacks_pre_freeze(flatbuffers::Offset>> callbacks_pre_freeze) { + fbb_.AddOffset(SaveGame::VT_CALLBACKS_PRE_FREEZE, callbacks_pre_freeze); + } + void add_callbacks_post_freeze(flatbuffers::Offset>> callbacks_post_freeze) { + fbb_.AddOffset(SaveGame::VT_CALLBACKS_POST_FREEZE, callbacks_post_freeze); + } explicit SaveGameBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -7958,8 +8002,16 @@ inline flatbuffers::Offset CreateSaveGame( flatbuffers::Offset>> callbacks_pre_load = 0, flatbuffers::Offset>> callbacks_post_load = 0, flatbuffers::Offset>> callbacks_pre_loop = 0, - flatbuffers::Offset>> callbacks_post_loop = 0) { + flatbuffers::Offset>> callbacks_post_loop = 0, + flatbuffers::Offset>> callbacks_pre_useitem = 0, + flatbuffers::Offset>> callbacks_post_useitem = 0, + flatbuffers::Offset>> callbacks_pre_freeze = 0, + flatbuffers::Offset>> callbacks_post_freeze = 0) { SaveGameBuilder builder_(_fbb); + builder_.add_callbacks_post_freeze(callbacks_post_freeze); + builder_.add_callbacks_pre_freeze(callbacks_pre_freeze); + builder_.add_callbacks_post_useitem(callbacks_post_useitem); + builder_.add_callbacks_pre_useitem(callbacks_pre_useitem); builder_.add_callbacks_post_loop(callbacks_post_loop); builder_.add_callbacks_pre_loop(callbacks_pre_loop); builder_.add_callbacks_post_load(callbacks_post_load); @@ -8075,7 +8127,11 @@ inline flatbuffers::Offset CreateSaveGameDirect( const std::vector> *callbacks_pre_load = nullptr, const std::vector> *callbacks_post_load = nullptr, const std::vector> *callbacks_pre_loop = nullptr, - const std::vector> *callbacks_post_loop = nullptr) { + const std::vector> *callbacks_post_loop = nullptr, + const std::vector> *callbacks_pre_useitem = nullptr, + const std::vector> *callbacks_post_useitem = nullptr, + const std::vector> *callbacks_pre_freeze = nullptr, + const std::vector> *callbacks_post_freeze = nullptr) { auto rooms__ = rooms ? _fbb.CreateVector>(*rooms) : 0; auto items__ = items ? _fbb.CreateVector>(*items) : 0; auto room_items__ = room_items ? _fbb.CreateVector(*room_items) : 0; @@ -8108,6 +8164,10 @@ inline flatbuffers::Offset CreateSaveGameDirect( auto callbacks_post_load__ = callbacks_post_load ? _fbb.CreateVector>(*callbacks_post_load) : 0; auto callbacks_pre_loop__ = callbacks_pre_loop ? _fbb.CreateVector>(*callbacks_pre_loop) : 0; auto callbacks_post_loop__ = callbacks_post_loop ? _fbb.CreateVector>(*callbacks_post_loop) : 0; + auto callbacks_pre_useitem__ = callbacks_pre_useitem ? _fbb.CreateVector>(*callbacks_pre_useitem) : 0; + auto callbacks_post_useitem__ = callbacks_post_useitem ? _fbb.CreateVector>(*callbacks_post_useitem) : 0; + auto callbacks_pre_freeze__ = callbacks_pre_freeze ? _fbb.CreateVector>(*callbacks_pre_freeze) : 0; + auto callbacks_post_freeze__ = callbacks_post_freeze ? _fbb.CreateVector>(*callbacks_post_freeze) : 0; return TEN::Save::CreateSaveGame( _fbb, header, @@ -8162,7 +8222,11 @@ inline flatbuffers::Offset CreateSaveGameDirect( callbacks_pre_load__, callbacks_post_load__, callbacks_pre_loop__, - callbacks_post_loop__); + callbacks_post_loop__, + callbacks_pre_useitem__, + callbacks_post_useitem__, + callbacks_pre_freeze__, + callbacks_post_freeze__); } flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuilder &_fbb, const SaveGameT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -10422,6 +10486,10 @@ inline void SaveGame::UnPackTo(SaveGameT *_o, const flatbuffers::resolver_functi { auto _e = callbacks_post_load(); if (_e) { _o->callbacks_post_load.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_post_load[_i] = _e->Get(_i)->str(); } } } { auto _e = callbacks_pre_loop(); if (_e) { _o->callbacks_pre_loop.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_pre_loop[_i] = _e->Get(_i)->str(); } } } { auto _e = callbacks_post_loop(); if (_e) { _o->callbacks_post_loop.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_post_loop[_i] = _e->Get(_i)->str(); } } } + { auto _e = callbacks_pre_useitem(); if (_e) { _o->callbacks_pre_useitem.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_pre_useitem[_i] = _e->Get(_i)->str(); } } } + { auto _e = callbacks_post_useitem(); if (_e) { _o->callbacks_post_useitem.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_post_useitem[_i] = _e->Get(_i)->str(); } } } + { auto _e = callbacks_pre_freeze(); if (_e) { _o->callbacks_pre_freeze.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_pre_freeze[_i] = _e->Get(_i)->str(); } } } + { auto _e = callbacks_post_freeze(); if (_e) { _o->callbacks_post_freeze.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_post_freeze[_i] = _e->Get(_i)->str(); } } } } inline flatbuffers::Offset SaveGame::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SaveGameT* _o, const flatbuffers::rehasher_function_t *_rehasher) { @@ -10485,6 +10553,10 @@ inline flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuild auto _callbacks_post_load = _fbb.CreateVectorOfStrings(_o->callbacks_post_load); auto _callbacks_pre_loop = _fbb.CreateVectorOfStrings(_o->callbacks_pre_loop); auto _callbacks_post_loop = _fbb.CreateVectorOfStrings(_o->callbacks_post_loop); + auto _callbacks_pre_useitem = _fbb.CreateVectorOfStrings(_o->callbacks_pre_useitem); + auto _callbacks_post_useitem = _fbb.CreateVectorOfStrings(_o->callbacks_post_useitem); + auto _callbacks_pre_freeze = _fbb.CreateVectorOfStrings(_o->callbacks_pre_freeze); + auto _callbacks_post_freeze = _fbb.CreateVectorOfStrings(_o->callbacks_post_freeze); return TEN::Save::CreateSaveGame( _fbb, _header, @@ -10539,7 +10611,11 @@ inline flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuild _callbacks_pre_load, _callbacks_post_load, _callbacks_pre_loop, - _callbacks_post_loop); + _callbacks_post_loop, + _callbacks_pre_useitem, + _callbacks_post_useitem, + _callbacks_pre_freeze, + _callbacks_post_freeze); } inline bool VerifyVarUnion(flatbuffers::Verifier &verifier, const void *obj, VarUnion type) { diff --git a/TombEngine/Specific/savegame/schema/ten_savegame.fbs b/TombEngine/Specific/savegame/schema/ten_savegame.fbs index 730c2668f..790efaf88 100644 --- a/TombEngine/Specific/savegame/schema/ten_savegame.fbs +++ b/TombEngine/Specific/savegame/schema/ten_savegame.fbs @@ -584,6 +584,10 @@ table SaveGame { callbacks_post_load: [string]; callbacks_pre_loop: [string]; callbacks_post_loop: [string]; + callbacks_pre_useitem: [string]; + callbacks_post_useitem: [string]; + callbacks_pre_freeze: [string]; + callbacks_post_freeze: [string]; } root_type TEN.Save.SaveGame; diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index d96d876a9..c0bf4859d 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -883,7 +883,8 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings. - + + From 3bf00ad18c051789102d57aa66cf478b38476465 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Wed, 20 Nov 2024 22:57:23 +0100 Subject: [PATCH 089/112] Implement StaticHandler class to properly handle LUT --- TombEngine/Game/Lara/lara_one_gun.cpp | 2 +- TombEngine/Game/Setup.cpp | 42 ++++++++++++++++++- TombEngine/Game/Setup.h | 31 +++++++++++--- TombEngine/Game/collision/collide_item.cpp | 2 +- TombEngine/Game/control/los.cpp | 2 +- TombEngine/Game/effects/debris.cpp | 2 +- TombEngine/Game/pickup/pickup.cpp | 2 +- TombEngine/Game/room.cpp | 4 +- .../Objects/TR4/Entity/tr4_knight_templar.cpp | 2 +- .../Objects/TR4/Entity/tr4_skeleton.cpp | 2 +- TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp | 2 +- TombEngine/Objects/TR4/Trap/SpikyWall.cpp | 2 +- .../Objects/TR5/Entity/tr5_gladiator.cpp | 2 +- TombEngine/Objects/TR5/Entity/tr5_gunship.cpp | 2 +- .../Objects/TR5/Entity/tr5_roman_statue.cpp | 2 +- TombEngine/Objects/TR5/Trap/tr5_explosion.cpp | 2 +- TombEngine/Renderer/Renderer.h | 26 ++++++------ TombEngine/Renderer/RendererCompatibility.cpp | 6 +-- TombEngine/Renderer/RendererFrame.cpp | 4 +- TombEngine/Sound/sound.cpp | 4 +- TombEngine/Specific/level.cpp | 25 ++--------- TombEngine/Specific/level.h | 2 - 22 files changed, 102 insertions(+), 68 deletions(-) diff --git a/TombEngine/Game/Lara/lara_one_gun.cpp b/TombEngine/Game/Lara/lara_one_gun.cpp index cef264283..d3771a6cb 100644 --- a/TombEngine/Game/Lara/lara_one_gun.cpp +++ b/TombEngine/Game/Lara/lara_one_gun.cpp @@ -1568,7 +1568,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p hasHit = hasHitNotByEmitter = doShatter = true; doExplosion = isExplosive; - if (GetStaticObject(staticPtr->staticNumber).shatterType == ShatterType::None) + if (Statics[staticPtr->staticNumber].shatterType == ShatterType::None) continue; staticPtr->HitPoints -= damage; diff --git a/TombEngine/Game/Setup.cpp b/TombEngine/Game/Setup.cpp index da461897b..f69f01e5c 100644 --- a/TombEngine/Game/Setup.cpp +++ b/TombEngine/Game/Setup.cpp @@ -32,8 +32,8 @@ using namespace TEN::Effects::Hair; using namespace TEN::Entities; using namespace TEN::Entities::Switches; -ObjectHandler Objects; -std::vector StaticObjects; +ObjectHandler Objects; +StaticHandler Statics; void ObjectHandler::Initialize() { @@ -76,6 +76,44 @@ ObjectInfo& ObjectHandler::GetFirstAvailableObject() return _objects[0]; } +void StaticHandler::Initialize() +{ + _lookupTable.resize(0); + _lookupTable.reserve(_defaultLUTSize); + _statics.resize(0); +} + +int StaticHandler::GetIndex(int staticID) +{ + if (staticID < 0 || staticID >= _lookupTable.size()) + { + TENLog("Attempt to get nonexistent static mesh ID slot index (" + std::to_string(staticID) + ")", LogLevel::Warning); + return _lookupTable.front(); + } + + return _lookupTable[staticID]; +} + +StaticInfo& StaticHandler::operator [](int staticID) +{ + if (staticID < 0) + { + TENLog("Attempt to access illegal static mesh ID slot info", LogLevel::Warning); + return _statics.front(); + } + + if (staticID >= _lookupTable.size()) + _lookupTable.resize(staticID + 1, NO_VALUE); + + if (_lookupTable[staticID] != NO_VALUE) + return _statics[_lookupTable[staticID]]; + + _statics.emplace_back(); + _lookupTable[staticID] = (int)_statics.size() - 1; + + return _statics.back(); +} + // NOTE: JointRotationFlags allows bones to be rotated with CreatureJoint(). void ObjectInfo::SetBoneRotationFlags(int boneID, int flags) { diff --git a/TombEngine/Game/Setup.h b/TombEngine/Game/Setup.h index 589bde601..37445cb14 100644 --- a/TombEngine/Game/Setup.h +++ b/TombEngine/Game/Setup.h @@ -115,15 +115,13 @@ class ObjectHandler { private: ObjectInfo _objects[ID_NUMBER_OBJECTS]; + ObjectInfo& GetFirstAvailableObject(); public: void Initialize(); bool CheckID(GAME_OBJECT_ID objectID, bool isSilent = false); ObjectInfo& operator [](int objectID); - -private: - ObjectInfo& GetFirstAvailableObject(); }; struct StaticInfo @@ -137,10 +135,31 @@ struct StaticInfo int ObjectNumber; }; -extern ObjectHandler Objects; -extern std::vector StaticObjects; +class StaticHandler +{ +private: + static const int _defaultLUTSize = 256; -#define GetStaticObject(x) StaticObjects[StaticObjectsLUT[x]] + std::vector _statics = {}; + std::vector _lookupTable = {}; + +public: + void Initialize(); + int GetIndex(int staticID); + + StaticInfo& operator [](int staticID); + + // Iterators + auto begin() { return _statics.begin(); } // Non-const begin + auto end() { return _statics.end(); } // Non-const end + auto begin() const { return _statics.cbegin(); } // Const begin + auto end() const { return _statics.cend(); } // Const end + auto cbegin() const { return _statics.cbegin(); } // Explicit const begin + auto cend() const { return _statics.cend(); } // Explicit const end +}; + +extern ObjectHandler Objects; +extern StaticHandler Statics; void InitializeGameFlags(); void InitializeSpecialEffects(); diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 070aeb390..1ac90f366 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -1888,7 +1888,7 @@ void DoObjectCollision(ItemInfo* item, CollisionInfo* coll) // HACK: Shatter statics only by harmful vehicles. if (!isPlayer && !isHarmless && abs(item->Animation.Velocity.z) > VEHICLE_COLLISION_TERMINAL_VELOCITY && - GetStaticObject(staticObject.staticNumber).shatterType != ShatterType::None) + Statics[staticObject.staticNumber].shatterType != ShatterType::None) { SoundEffect(GetShatterSound(staticObject.staticNumber), &staticObject.pos); ShatterObject(nullptr, &staticObject, -128, item->RoomNumber, 0); diff --git a/TombEngine/Game/control/los.cpp b/TombEngine/Game/control/los.cpp index 431c385ef..00db3ae6f 100644 --- a/TombEngine/Game/control/los.cpp +++ b/TombEngine/Game/control/los.cpp @@ -287,7 +287,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo { if (itemNumber < 0) { - if (GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) + if (Statics[mesh->staticNumber].shatterType != ShatterType::None) { const auto& weapon = Weapons[(int)Lara.Control.Weapon.GunType]; mesh->HitPoints -= weapon.Damage; diff --git a/TombEngine/Game/effects/debris.cpp b/TombEngine/Game/effects/debris.cpp index 9e1c226c5..d83b51b32 100644 --- a/TombEngine/Game/effects/debris.cpp +++ b/TombEngine/Game/effects/debris.cpp @@ -70,7 +70,7 @@ void ShatterObject(SHATTER_ITEM* item, MESH_INFO* mesh, int num, short roomNumbe return; isStatic = true; - meshIndex = GetStaticObject(mesh->staticNumber).meshNumber; + meshIndex = Statics[mesh->staticNumber].meshNumber; yRot = mesh->pos.Orientation.y; pos = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z); scale = mesh->scale; diff --git a/TombEngine/Game/pickup/pickup.cpp b/TombEngine/Game/pickup/pickup.cpp index d0777af21..b81a1f4d0 100644 --- a/TombEngine/Game/pickup/pickup.cpp +++ b/TombEngine/Game/pickup/pickup.cpp @@ -903,7 +903,7 @@ void DropPickups(ItemInfo* item) for (auto* staticPtr : collObjects.Statics) { - auto& object = GetStaticObject(staticPtr->staticNumber); + auto& object = Statics[staticPtr->staticNumber]; auto box = object.collisionBox.ToBoundingOrientedBox(staticPtr->pos); if (box.Intersects(sphere)) diff --git a/TombEngine/Game/room.cpp b/TombEngine/Game/room.cpp index 1a1171e08..4acf9ee30 100644 --- a/TombEngine/Game/room.cpp +++ b/TombEngine/Game/room.cpp @@ -255,11 +255,11 @@ GameBoundingBox& GetBoundsAccurate(const MESH_INFO& mesh, bool getVisibilityBox) if (getVisibilityBox) { - bounds = GetStaticObject(mesh.staticNumber).visibilityBox * mesh.scale; + bounds = Statics[mesh.staticNumber].visibilityBox * mesh.scale; } else { - bounds = GetStaticObject(mesh.staticNumber).collisionBox * mesh.scale; + bounds = Statics[mesh.staticNumber].collisionBox * mesh.scale; } return bounds; diff --git a/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp b/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp index 9ea5586aa..aa9940f78 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp @@ -194,7 +194,7 @@ namespace TEN::Entities::TR4 { if (abs(pos.x - mesh.pos.Position.x) < BLOCK(1) && abs(pos.z - mesh.pos.Position.z) < BLOCK(1) && - GetStaticObject(mesh.staticNumber).shatterType == ShatterType::None) + Statics[mesh.staticNumber].shatterType == ShatterType::None) { ShatterObject(nullptr, &mesh, -64, LaraItem->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); diff --git a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp index 2a1b8d36f..9438ef83c 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp @@ -652,7 +652,7 @@ namespace TEN::Entities::TR4 if (abs(pos.x - staticMesh->pos.Position.x) < BLOCK(1) && abs(pos.z - staticMesh->pos.Position.z) < BLOCK(1) && - GetStaticObject(staticMesh->staticNumber).shatterType != ShatterType::None) + Statics[staticMesh->staticNumber].shatterType != ShatterType::None) { ShatterObject(0, staticMesh, -128, LaraItem->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); diff --git a/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp b/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp index fd0d6f5d2..3c3c37309 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp @@ -95,7 +95,7 @@ namespace TEN::Entities::TR4 if (((mesh->pos.Position.z / BLOCK(1)) == (z / BLOCK(1))) && ((mesh->pos.Position.x / BLOCK(1)) == (x / BLOCK(1))) && - GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) + Statics[mesh->staticNumber].shatterType != ShatterType::None) { ShatterObject(nullptr, mesh, -64, item->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); diff --git a/TombEngine/Objects/TR4/Trap/SpikyWall.cpp b/TombEngine/Objects/TR4/Trap/SpikyWall.cpp index 6fed5fbcc..fc0a070cd 100644 --- a/TombEngine/Objects/TR4/Trap/SpikyWall.cpp +++ b/TombEngine/Objects/TR4/Trap/SpikyWall.cpp @@ -91,7 +91,7 @@ namespace TEN::Entities::Traps abs(pointColl0.GetPosition().z - mesh.pos.Position.z) < BLOCK(1)) || abs(pointColl1.GetPosition().x - mesh.pos.Position.x) < BLOCK(1) && abs(pointColl1.GetPosition().z - mesh.pos.Position.z) < BLOCK(1) && - GetStaticObject(mesh.staticNumber).shatterType != ShatterType::None) + Statics[mesh.staticNumber].shatterType != ShatterType::None) { if (mesh.HitPoints != 0) continue; diff --git a/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp b/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp index b69dd1b03..28c7b46b1 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp @@ -348,7 +348,7 @@ namespace TEN::Entities::Creatures::TR5 { if (!((pos.x ^ mesh->pos.Position.x) & 0xFFFFFC00)) { - if (GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) + if (Statics[mesh->staticNumber].shatterType != ShatterType::None) { ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0); //SoundEffect(ShatterSounds[gfCurrentLevel - 5][*(v28 + 18)], v28); diff --git a/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp b/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp index 8e570d7f3..193d21cde 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp @@ -109,7 +109,7 @@ namespace TEN::Entities::Creatures::TR5 if (objOnLos < 0 && GetRandomControl() & 1) { - if (GetStaticObject(hitMesh->staticNumber).shatterType != ShatterType::None) + if (Statics[hitMesh->staticNumber].shatterType != ShatterType::None) { ShatterObject(0, hitMesh, 64, target.RoomNumber, 0); TestTriggers(hitMesh->pos.Position.x, hitMesh->pos.Position.y, hitMesh->pos.Position.z, target.RoomNumber, true); diff --git a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp index 8f427dd2d..397276556 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp @@ -558,7 +558,7 @@ namespace TEN::Entities::Creatures::TR5 if (!((mesh->pos.Position.z ^ pos.z) & 0xFFFFFC00) && !((mesh->pos.Position.x ^ pos.x) & 0xFFFFFC00)) { - if (GetStaticObject(mesh->staticNumber).shatterType != ShatterType::None) + if (Statics[mesh->staticNumber].shatterType != ShatterType::None) { ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0); SoundEffect(GetShatterSound(mesh->staticNumber), (Pose*)mesh); diff --git a/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp b/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp index 04c5d77f5..09bf564f0 100644 --- a/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp +++ b/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp @@ -163,7 +163,7 @@ namespace TEN::Entities::Traps for (auto* staticPtr : collObjects.Statics) { - if (GetStaticObject(staticPtr->staticNumber).shatterType != ShatterType::None) + if (Statics[staticPtr->staticNumber].shatterType != ShatterType::None) { TriggerExplosionSparks(staticPtr->pos.Position.x, staticPtr->pos.Position.y, staticPtr->pos.Position.z, 3, -2, 0, item.RoomNumber); staticPtr->pos.Position.y -= 128; diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h index bbdd9d64b..02a5f9a2f 100644 --- a/TombEngine/Renderer/Renderer.h +++ b/TombEngine/Renderer/Renderer.h @@ -6,6 +6,7 @@ #include #include #include + #include "Math/Math.h" #include "Game/control/box.h" #include "Game/items.h" @@ -15,11 +16,13 @@ #include "Game/Hud/PickupSummary.h" #include "Game/effects/effects.h" #include "Game/effects/Electricity.h" +#include "Game/Setup.h" #include "Specific/level.h" #include "Specific/fast_vector.h" +#include "Renderer/Frustum.h" #include "Renderer/RendererEnums.h" -#include "Renderer/Structures/RendererLight.h" #include "Renderer/RenderView.h" +#include "Renderer/Structures/RendererLight.h" #include "Renderer/ConstantBuffers/StaticBuffer.h" #include "Renderer/ConstantBuffers/LightBuffer.h" #include "Renderer/ConstantBuffers/HUDBarBuffer.h" @@ -33,26 +36,23 @@ #include "Renderer/ConstantBuffers/SpriteBuffer.h" #include "Renderer/ConstantBuffers/InstancedStaticBuffer.h" #include "Renderer/ConstantBuffers/InstancedSpriteBuffer.h" -#include "Frustum.h" -#include "Specific/level.h" -#include "ConstantBuffers/ConstantBuffer.h" -#include "Specific/fast_vector.h" -#include "Renderer/ConstantBuffers/InstancedSpriteBuffer.h" +#include "Renderer/ConstantBuffers/ConstantBuffer.h" #include "Renderer/ConstantBuffers/PostProcessBuffer.h" +#include "Renderer/ConstantBuffers/SMAABuffer.h" #include "Renderer/Structures/RendererBone.h" +#include "Renderer/Structures/RendererDoor.h" #include "Renderer/Structures/RendererStringToDraw.h" #include "Renderer/Structures/RendererRoom.h" #include "Renderer/Structures/RendererSprite.h" -#include "Renderer/Graphics/VertexBuffer.h" -#include "Renderer/Structures/RendererDoor.h" -#include "Structures/RendererAnimatedTexture.h" -#include "Structures/RendererAnimatedTextureSet.h" -#include "Structures/RendererRoom.h" +#include "Renderer/Structures/RendererAnimatedTexture.h" +#include "Renderer/Structures/RendererAnimatedTextureSet.h" #include "Renderer/Graphics/Texture2D.h" #include "Renderer/Graphics/IndexBuffer.h" #include "Renderer/Graphics/RenderTarget2D.h" #include "Renderer/Graphics/RenderTargetCube.h" #include "Renderer/Graphics/Texture2DArray.h" +#include "Renderer/Graphics/VertexBuffer.h" +#include "Renderer/Graphics/Vertices/PostProcessVertex.h" #include "Renderer/Structures/RendererItem.h" #include "Renderer/Structures/RendererEffect.h" #include "Renderer/Structures/RendererLine3D.h" @@ -63,9 +63,7 @@ #include "Renderer/Structures/RendererLine2D.h" #include "Renderer/Structures/RendererHudBar.h" #include "Renderer/Structures/RendererRoomAmbientMap.h" -#include "Renderer/ConstantBuffers/SMAABuffer.h" #include "Renderer/Structures/RendererObject.h" -#include "Graphics/Vertices/PostProcessVertex.h" #include "Renderer/Structures/RendererStar.h" enum GAME_OBJECT_ID : short; @@ -616,7 +614,7 @@ namespace TEN::Renderer inline RendererObject& GetStaticRendererObject(short objectNumber) { - return _staticObjects[StaticObjectsLUT[objectNumber]].value(); + return _staticObjects[Statics.GetIndex(objectNumber)].value(); } public: diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp index 9fea89120..5f5be02ef 100644 --- a/TombEngine/Renderer/RendererCompatibility.cpp +++ b/TombEngine/Renderer/RendererCompatibility.cpp @@ -272,7 +272,7 @@ namespace TEN::Renderer staticInfo->AmbientLight = r->AmbientLight; staticInfo->Pose = oldMesh->pos; staticInfo->Scale = oldMesh->scale; - staticInfo->OriginalSphere = GetStaticObject(staticInfo->ObjectNumber).visibilityBox.ToLocalBoundingSphere(); + staticInfo->OriginalSphere = Statics[staticInfo->ObjectNumber].visibilityBox.ToLocalBoundingSphere(); staticInfo->IndexInRoom = l; staticInfo->Update(); @@ -877,7 +877,7 @@ namespace TEN::Renderer totalVertices = 0; totalIndices = 0; - for (const auto& staticObj : StaticObjects) + for (const auto& staticObj : Statics) { const auto& mesh = g_Level.Meshes[staticObj.meshNumber]; for (const auto& bucket : mesh.buckets) @@ -892,7 +892,7 @@ namespace TEN::Renderer lastVertex = 0; lastIndex = 0; - for (const auto& staticObj : StaticObjects) + for (const auto& staticObj : Statics) { auto newStaticObj = RendererObject(); newStaticObj.Type = 1; diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp index cfaade9ce..d40516df1 100644 --- a/TombEngine/Renderer/RendererFrame.cpp +++ b/TombEngine/Renderer/RendererFrame.cpp @@ -474,7 +474,7 @@ namespace TEN::Renderer { mesh->ObjectNumber = nativeMesh->staticNumber; mesh->Color = nativeMesh->color; - mesh->OriginalSphere = GetStaticObject(mesh->ObjectNumber).visibilityBox.ToLocalBoundingSphere(); + mesh->OriginalSphere = Statics[mesh->ObjectNumber].visibilityBox.ToLocalBoundingSphere(); mesh->Pose = nativeMesh->pos; mesh->Scale = nativeMesh->scale; mesh->Update(); @@ -485,7 +485,7 @@ namespace TEN::Renderer if (!(nativeMesh->flags & StaticMeshFlags::SM_VISIBLE)) continue; - if (!_staticObjects[StaticObjectsLUT[mesh->ObjectNumber]].has_value()) + if (!_staticObjects[Statics.GetIndex(mesh->ObjectNumber)].has_value()) continue; auto& obj = GetStaticRendererObject(mesh->ObjectNumber); diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index 7604eed83..36b88718f 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -1084,8 +1084,8 @@ void PlaySecretTrack() int GetShatterSound(int shatterID) { - auto fxID = GetStaticObject(shatterID).shatterSound; - if (fxID != -1 && fxID < NUM_SFX) + auto fxID = Statics[shatterID].shatterSound; + if (fxID != NO_VALUE && fxID < NUM_SFX) return fxID; if (shatterID < 3) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index a88807274..5037f4315 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -81,8 +81,6 @@ const std::vector BRIDGE_OBJECT_IDS = LEVEL g_Level; std::vector MoveablesIds; -int* StaticObjectsLUT = nullptr; -int StaticObjectsLUTSize = 0; std::vector SpriteSequencesIds; char* DataPtr; @@ -272,14 +270,7 @@ void LoadItems() void LoadObjects() { Objects.Initialize(); - StaticObjects.clear(); - - if (StaticObjectsLUT != nullptr) - { - delete StaticObjectsLUT; - } - StaticObjectsLUTSize = 1000; - StaticObjectsLUT = (int*)malloc(StaticObjectsLUTSize * sizeof(int)); + Statics.Initialize(); int meshCount = ReadInt32(); TENLog("Mesh count: " + std::to_string(meshCount), LogLevel::Info); @@ -479,17 +470,7 @@ void LoadObjects() staticObj.shatterType = (ShatterType)ReadInt16(); staticObj.shatterSound = ReadInt16(); - if (staticObj.ObjectNumber >= StaticObjectsLUTSize) - { - int* LUT = (int*)malloc(staticObj.ObjectNumber * sizeof(int)); - memcpy(LUT, StaticObjectsLUT, StaticObjectsLUTSize * sizeof(int)); - delete StaticObjectsLUT; - StaticObjectsLUT = LUT; - StaticObjectsLUTSize = staticObj.ObjectNumber; - } - - StaticObjectsLUT[staticObj.ObjectNumber] = (int)StaticObjects.size(); - StaticObjects.push_back(staticObj); + Statics[staticObj.ObjectNumber] = staticObj; } } @@ -1607,7 +1588,7 @@ void LoadSprites() short offset = ReadInt16(); if (spriteID >= ID_NUMBER_OBJECTS) { - GetStaticObject(spriteID - ID_NUMBER_OBJECTS).meshNumber = offset; + Statics[spriteID - ID_NUMBER_OBJECTS].meshNumber = offset; } else { diff --git a/TombEngine/Specific/level.h b/TombEngine/Specific/level.h index 7942b68d3..f0a272827 100644 --- a/TombEngine/Specific/level.h +++ b/TombEngine/Specific/level.h @@ -137,8 +137,6 @@ struct LEVEL extern const std::vector BRIDGE_OBJECT_IDS; extern std::vector MoveablesIds; -extern int* StaticObjectsLUT; -extern int StaticObjectsLUTSize; extern std::vector SpriteSequencesIds; extern LEVEL g_Level; extern int SystemNameHash; From bdf67b75dcd9abea36a7736e82c669fe40dc9f0b Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 02:02:00 +0300 Subject: [PATCH 090/112] Decoupled sound conditions (#1484) * Convert sound conditions to a new format * Convert animcommands to int * Update flipeffect.cpp * Update winmain.cpp --- TombEngine/Game/animation.cpp | 50 +++++--------------------- TombEngine/Game/control/flipeffect.cpp | 2 +- TombEngine/Specific/level.cpp | 2 +- TombEngine/Specific/level.h | 2 +- TombEngine/Specific/winmain.cpp | 2 +- 5 files changed, 12 insertions(+), 46 deletions(-) diff --git a/TombEngine/Game/animation.cpp b/TombEngine/Game/animation.cpp index ae838853c..9949d097c 100644 --- a/TombEngine/Game/animation.cpp +++ b/TombEngine/Game/animation.cpp @@ -39,7 +39,7 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased) return; // Get command data pointer. - short* commandDataPtr = &g_Level.Commands[anim.CommandIndex]; + int* commandDataPtr = &g_Level.Commands[anim.CommandIndex]; for (int i = anim.NumCommands; i > 0; i--) { @@ -118,45 +118,11 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased) int frameNumber = commandDataPtr[0]; if (isFrameBased && item.Animation.FrameNumber == frameNumber) { - // Get sound ID and sound environment flag from packed data. - int soundID = commandDataPtr[1] & 0xFFF; // Exclude last 4 bits for sound ID. - int soundEnvFlag = commandDataPtr[1] & 0xF000; // Keep only last 4 bits for sound environment flag. + // Get sound ID and sound environment flag. + int soundID = commandDataPtr[1]; + auto requiredSoundEnv = (SoundEnvironment)commandDataPtr[2]; - // FAILSAFE. - if (item.RoomNumber == NO_VALUE) - { - SoundEffect(soundID, &item.Pose, SoundEnvironment::Always); - commandDataPtr += 2; - break; - } - - // Get required sound environment from flag. - auto requiredSoundEnv = SoundEnvironment::Always; - switch (soundEnvFlag) - { - default: - case 0: - requiredSoundEnv = SoundEnvironment::Always; - break; - - case (1 << 14): - requiredSoundEnv = SoundEnvironment::Land; - break; - - case (1 << 15): - requiredSoundEnv = SoundEnvironment::ShallowWater; - break; - - case (1 << 12): - requiredSoundEnv = SoundEnvironment::Swamp; - break; - - case (1 << 13): - requiredSoundEnv = SoundEnvironment::Underwater; - break; - } - - int roomNumberAtPos = GetPointCollision(item).GetRoomNumber(); + int roomNumberAtPos = (item.RoomNumber == NO_VALUE) ? Camera.pos.RoomNumber : GetPointCollision(item).GetRoomNumber(); bool isWater = TestEnvironment(ENV_FLAG_WATER, roomNumberAtPos); bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, roomNumberAtPos); @@ -201,13 +167,13 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased) SoundEffect(soundID, &item.Pose, *soundEnv); } - commandDataPtr += 2; - } + commandDataPtr += 3; break; + } case AnimCommandType::Flipeffect: if (isFrameBased && item.Animation.FrameNumber == commandDataPtr[0]) - DoFlipEffect((commandDataPtr[1] & 0x3FFF), &item); + DoFlipEffect(commandDataPtr[1], &item); commandDataPtr += 2; break; diff --git a/TombEngine/Game/control/flipeffect.cpp b/TombEngine/Game/control/flipeffect.cpp index a47059395..e1b3af5d4 100644 --- a/TombEngine/Game/control/flipeffect.cpp +++ b/TombEngine/Game/control/flipeffect.cpp @@ -346,6 +346,6 @@ void VoidEffect(ItemInfo* item) void DoFlipEffect(int number, ItemInfo* item) { - if (number != -1 && number < NUM_FLIPEFFECTS && effect_routines[number] != nullptr) + if (number != NO_VALUE && number < NUM_FLIPEFFECTS && effect_routines[number] != nullptr) effect_routines[number](item); } diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 5037f4315..33b441f89 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -388,7 +388,7 @@ void LoadObjects() int commandCount = ReadInt32(); g_Level.Commands.resize(commandCount); - ReadBytes(g_Level.Commands.data(), sizeof(short) * commandCount); + ReadBytes(g_Level.Commands.data(), sizeof(int) * commandCount); int boneCount = ReadInt32(); g_Level.Bones.resize(boneCount); diff --git a/TombEngine/Specific/level.h b/TombEngine/Specific/level.h index f0a272827..0c79bba91 100644 --- a/TombEngine/Specific/level.h +++ b/TombEngine/Specific/level.h @@ -99,7 +99,7 @@ struct LEVEL std::vector Frames = {}; std::vector Changes = {}; std::vector Ranges = {}; - std::vector Commands = {}; + std::vector Commands = {}; // Collision data std::vector Rooms = {}; diff --git a/TombEngine/Specific/winmain.cpp b/TombEngine/Specific/winmain.cpp index d6897aa8a..7ed2fc1f9 100644 --- a/TombEngine/Specific/winmain.cpp +++ b/TombEngine/Specific/winmain.cpp @@ -352,7 +352,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine auto windowName = (std::string("Starting TombEngine version ") + std::to_string(ver[0]) + "." + std::to_string(ver[1]) + "." + - std::to_string(ver[2]) + " " + + std::to_string(ver[2]) + "." + std::to_string(ver[3]) + " " + #ifdef _WIN64 "(64-bit)" From 85a456a4f116d5118c11df1dcd32d31b91f632a0 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 00:11:40 +0100 Subject: [PATCH 091/112] Update CHANGELOG.md --- CHANGELOG.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cad5bff65..ef2ef7126 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed Lens Flare object not functioning properly. * Fixed Skeleton and Mummy not reacting to shotgun hits. -### Features/Amendments +### New Features * Added fast savegame reloading. * Added ricochet sounds and make the effect more prominent. * Allow to run the engine without title level. @@ -89,7 +89,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed display sprites and display strings rendering in the inventory background. * Fixed young Lara hair drawing. https://tombengine.com/docs/level-settings/#young_lara -### Features/Amendments +### New Features * Added high framerate mode (also known as 60 FPS mode). * Added a customisable global lensflare effect. https://tombengine.com/docs/level-settings/#lensflare * Added a customisable starry sky and meteor effect. https://tombengine.com/docs/level-settings/#stars @@ -149,7 +149,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed bottom collision for solid static meshes. * Fixed T-Rex's head rotation. -### Features/Amendments +### New Features * Auto-switch to a crawl state if player start position is in a crawlspace. * Allow directional flame emitter (negative OCBs) to be rotated at any angle. * Revise wall spikes: @@ -196,7 +196,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed incorrect light collection in some cases. * Fixed normal mapping for rooms, items, and statics.' -### Features/Amendments +### New Features * Added ambient occlusion (SSAO). * Added new post-process workflow (monochrome, negative, exclusion) with tinting. * Added SMAA antialiasing instead of MSAA. @@ -255,7 +255,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fix camera snap when disengaging the look-around mode. * Fix TR4 mapper not being visible. -### Features/Amendments +### New Features * Improve head-on wall collision. * Overhaul pushables: - Separate climbable and non-climbable pushable object slots. @@ -338,7 +338,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fix incorrect culling for scaled static meshes. * Fix normal mapping. -### Features/Amendments +### New Features * Add ability to save screenshot in the "Screenshots" subfolder by pressing the "Print screen" key. * Implement separate audio track channel for playing voiceovers with subtitles in .srt format. * Don't stop ambience when Lara dies. @@ -386,7 +386,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fix rendering for static meshes with custom blending modes and alpha transparency. * Fix inconsistent multiline string spacing on different display modes. -### Features/Amendments +### New Features * Remove search object 4 hardcoded meshswap activated with a flipmap. * Add TR1 cowboy. * Add TR3 wall mounted blade. @@ -444,7 +444,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): - Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr5_Imp.wad2 * Fix and improve wraith tails. -### Features/Amedments +### New Features/Amedments * Add dedicated WRAITH_TRAP object with enhanced effects. - OCB 0: Effect disabled. - OCB 1: Effect enabled. @@ -493,7 +493,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fix TR3 big gun spawning rocket with 0 life which caused an immediate explosion. * Fix TR3 Tony and add boss effect for him. -### Features/Amendments +### New Features * Add TR3 civvy. * Add TR3 electric cleaner. * Add TR3 Sophia Leigh with following OCBs: @@ -545,7 +545,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fix grenade and rocket launcher lighting. * Fix ceiling trapdoor and floor trapdoor that Lara couldn't open manually. -### Features/Amendments +### New Features * Make enemies drop pickups at first available bounding box corner point, not centerpoint. * Restore original volumetric explosion effects. * Add TR3 lizard and Puna. @@ -597,7 +597,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fix bone rotations of some entities. * Fix Lara's animation for cog switch release. -### Features/Amendments +### New Features * Added new OCB to cog switch object: - Use OCB 0 to have the traditional behaviour. - Use any other OCB to can use the Cog Switch without need of any door linked. @@ -611,7 +611,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ## [Version 1.0.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.2) - 2022-12-16 -### Features/Amendments +### New Features * Add generic assignable effects for moveables - fire, sparks, smoke and laser / electric ignite. * Add ability to burn enemies with FLAME_EMITTER_1 and death blocks. * Add wireframe mode and other visual debug information (switch by F10/F11 debug page scroll hotkeys). @@ -683,7 +683,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ## [Version 1.0.3](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.1) - 2022-11-18 -### Features/Amendments +### New Features * Add ledge jumps (Lara object must be updated with new animations to make it work). * Allow any object slot to be used as a meshswap. * Add OCB 1 for rollingball to make it silent. @@ -751,7 +751,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ## [Version 1.0.2](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6) - 2022-09-16 -### Features/Amendments +### New Features * Fix removing Pistols with TakeItem and SetItemCount. * Allow saving and loading of Vec3s in LevelVars and GameVars. * Support volume triggers made with node editor. @@ -806,7 +806,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): ## [Version 1.0.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.5.2) - 2022-08-16 -### Features +### New Features * Added antialiasing support. * Added static mesh scaling support. * Added free rotation for teeth spikes instead of using OCB codes. From 64b100561ab3a8c30c4309f655e2cfc4a1230bd9 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 02:09:15 +0100 Subject: [PATCH 092/112] Don't pollute DumpGameScene with unrelated flag --- TombEngine/Renderer/RendererDraw.cpp | 1 - TombEngine/Renderer/RendererDrawMenu.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index 7e9f5e3be..b7c9e5456 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -2164,7 +2164,6 @@ namespace TEN::Renderer void Renderer::DumpGameScene(SceneRenderMode renderMode) { RenderScene(&_dumpScreenRenderTarget, _gameCamera, renderMode); - _graphicsSettingsChanged = false; } void Renderer::DrawItems(RenderView& view, RendererPass rendererPass) diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp index 26cb17d69..6e8685634 100644 --- a/TombEngine/Renderer/RendererDrawMenu.cpp +++ b/TombEngine/Renderer/RendererDrawMenu.cpp @@ -1192,6 +1192,7 @@ namespace TEN::Renderer UpdateCameraMatrices(&Camera, BLOCK(g_GameFlow->GetLevel(CurrentLevel)->GetFarView())); Camera.DisableInterpolation = true; DumpGameScene(); + _graphicsSettingsChanged = false; } _context->ClearDepthStencilView(_backBuffer.DepthStencilView.Get(), D3D11_CLEAR_STENCIL | D3D11_CLEAR_DEPTH, 1.0f, 0); From 86cee87ecde334eeca4d59b957ae216ddac7d052 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 02:41:54 +0100 Subject: [PATCH 093/112] Bump version --- TombEngine/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/version.h b/TombEngine/version.h index 8ad350120..f8c2bfd2f 100644 --- a/TombEngine/version.h +++ b/TombEngine/version.h @@ -8,7 +8,7 @@ #define TEN_MAJOR_VERSION 1 #define TEN_MINOR_VERSION 5 #define TEN_BUILD_NUMBER 0 -#define TEN_REVISION_NUMBER 4 +#define TEN_REVISION_NUMBER 5 #define TEST_BUILD 1 From 1267effe08bb4e5e67bc6931a1cfbcd8ba415a9f Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:10:22 +0100 Subject: [PATCH 094/112] Fixed Util.HasLineOfSight not accounting for statics --- CHANGELOG.md | 1 + TombEngine/Scripting/Internal/TEN/Util/Util.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2ef7126..33bcbf3c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. * Fixed DisplayString class not supporting empty lines in multiline strings. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. +* Fixed Util.HasLineOfSight() not taking static meshes into consideration. * Fixed collision callbacks not properly clearing after leveljump. ## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03 diff --git a/TombEngine/Scripting/Internal/TEN/Util/Util.cpp b/TombEngine/Scripting/Internal/TEN/Util/Util.cpp index 0a52f4196..db2890408 100644 --- a/TombEngine/Scripting/Internal/TEN/Util/Util.cpp +++ b/TombEngine/Scripting/Internal/TEN/Util/Util.cpp @@ -41,9 +41,10 @@ namespace TEN::Scripting::Util auto vector0 = posA.ToGameVector(); auto vector1 = posB.ToGameVector(); + MESH_INFO* mesh = nullptr; auto vector = Vector3i::Zero; return (LOS(&vector0, &vector1) && - ObjectOnLOS2(&vector0, &vector1, &vector, nullptr) == NO_LOS_ITEM); + ObjectOnLOS2(&vector0, &vector1, &vector, &mesh) == NO_LOS_ITEM); } ///Calculate the distance between two positions. From 81a8922f1d4bd8aecc5d62f8ceb8e7c664a93348 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:14:19 +0100 Subject: [PATCH 095/112] Fixed global lensflare distance calculations, add object culling, add fade on visibility --- CHANGELOG.md | 2 +- TombEngine/Objects/Effects/LensFlare.cpp | 80 +++++++++++++++++------- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33bcbf3c4..e84e42289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed Lara's Home entry not working. * Fixed exploding TR3 bosses. * Fixed original issue with deactivation of Dart Emitter. -* Fixed Lens Flare object not functioning properly. +* Fixed Lens Flare object not functioning properly and not reacting to moveables and statics in front of it. * Fixed Skeleton and Mummy not reacting to shotgun hits. ### New Features diff --git a/TombEngine/Objects/Effects/LensFlare.cpp b/TombEngine/Objects/Effects/LensFlare.cpp index a9932cc65..260c961ba 100644 --- a/TombEngine/Objects/Effects/LensFlare.cpp +++ b/TombEngine/Objects/Effects/LensFlare.cpp @@ -12,27 +12,54 @@ using namespace TEN::Math; namespace TEN::Entities::Effects { - std::vector LensFlares; + constexpr float MAX_INTENSITY = 1.0f; // Maximum intensity + constexpr float FADE_SPEED = 0.10f; // Speed of fade-in/out per frame + constexpr float SHIMMER_STRENGTH = 0.15f; // Max shimmer amplitude - static void SetupLensFlare(const Vector3& pos, int roomNumber, const Color& color, bool isGlobal, int spriteID) + float GlobalLensFlareIntensity = 0; + + std::vector LensFlares; + + static void UpdateLensFlareIntensity(bool isVisible, float& intensity) { - auto lensFlarePos = Vector3::Zero; + // Target intensity based on visibility. + float targetIntensity = isVisible ? MAX_INTENSITY : 0.0f; + + // Fade-in or fade-out. + if (intensity < targetIntensity) + intensity = std::min(intensity + FADE_SPEED, targetIntensity); + else if (intensity > targetIntensity) + intensity = std::max(intensity - FADE_SPEED, targetIntensity); + } + + static void AdjustLensflarePosition(Vector3& lensFlarePos, const Vector3& targetPos, float divisor, float threshold) + { + // Gradually narrow lensflare vector down to target vector, until threshold is met. + auto delta = Vector3(FLT_MAX); + while (delta.Length() > threshold) + { + delta = lensFlarePos - targetPos; + lensFlarePos -= delta / divisor; + } + } + + static void SetupLensFlare(const Vector3& pos, int roomNumber, const Color& color, float& intensity, int spriteID) + { + auto lensFlarePos = pos; + bool isGlobal = roomNumber == NO_VALUE; + if (isGlobal) { if (TestEnvironment(ENV_FLAG_NO_LENSFLARE, Camera.pos.RoomNumber)) return; - lensFlarePos = pos; - auto delta = (lensFlarePos - Camera.pos.ToVector3()) / 16.0f; - while (abs(delta.x) > BLOCK(200) || abs(delta.y) > BLOCK(200) || abs(delta.z) > BLOCK(200)) - lensFlarePos -= delta; + AdjustLensflarePosition(lensFlarePos, Camera.pos.ToVector3(), 8.0f, BLOCK(256)); + AdjustLensflarePosition(lensFlarePos, Camera.pos.ToVector3(), 4.0f, BLOCK(32)); - delta = (lensFlarePos - Camera.pos.ToVector3()) / 16.0f; - while (abs(delta.x) > BLOCK(32) || abs(delta.y) > BLOCK(32) || abs(delta.z) > BLOCK(32)) - lensFlarePos -= delta; + // FAILSAFE: Break while loop if room can't be found (e.g. camera is in the void). + int narrowingCycleCount = 0; - delta = (lensFlarePos - Camera.pos.ToVector3()) / 16.0f; - for (int i = 0; i < 16; i++) + while (roomNumber == NO_VALUE && narrowingCycleCount < 16) { int foundRoomNumber = IsRoomOutside(lensFlarePos.x, lensFlarePos.y, lensFlarePos.z); if (foundRoomNumber != NO_VALUE) @@ -41,16 +68,15 @@ namespace TEN::Entities::Effects break; } - lensFlarePos -= delta; + AdjustLensflarePosition(lensFlarePos, Camera.pos.ToVector3(), 2.0f, BLOCK(1)); + narrowingCycleCount++; } } else { - float dist = Vector3::Distance(pos, Camera.pos.ToVector3()); + float dist = Vector3::Distance(lensFlarePos, Camera.pos.ToVector3()); if (dist > BLOCK(32)) return; - - lensFlarePos = pos; } bool isVisible = false; @@ -60,18 +86,26 @@ namespace TEN::Entities::Effects { auto origin = Camera.pos; auto target = GameVector(lensFlarePos, roomNumber); - isVisible = LOS(&origin, &target); + + MESH_INFO* mesh = nullptr; + auto tempVector = Vector3i(); + isVisible = LOS(&origin, &target) && ObjectOnLOS2(&origin, &target, &tempVector, &mesh) == NO_LOS_ITEM; } } - if (!isVisible && !isGlobal) + UpdateLensFlareIntensity(isVisible, intensity); + + if (!isVisible && !isGlobal && intensity == 0.0f) return; + float shimmer = Random::GenerateFloat(-SHIMMER_STRENGTH, SHIMMER_STRENGTH); + float finalIntensity = std::clamp(Smoothstep(intensity) + shimmer * intensity, 0.0f, MAX_INTENSITY); + auto lensFlare = LensFlare{}; lensFlare.Position = pos; lensFlare.RoomNumber = roomNumber; lensFlare.IsGlobal = isGlobal; - lensFlare.Color = color; + lensFlare.Color = color * Smoothstep(finalIntensity); lensFlare.SpriteID = spriteID; LensFlares.push_back(lensFlare); @@ -92,7 +126,7 @@ namespace TEN::Entities::Effects auto rotMatrix = orient.ToRotationMatrix(); pos += Vector3::Transform(BASE_POS, rotMatrix); - SetupLensFlare(pos, NO_VALUE, color, true, spriteID); + SetupLensFlare(pos, NO_VALUE, color, GlobalLensFlareIntensity, spriteID); } void ControlLensFlare(int itemNumber) @@ -100,7 +134,11 @@ namespace TEN::Entities::Effects auto& item = g_Level.Items[itemNumber]; if (TriggerActive(&item)) - SetupLensFlare(item.Pose.Position.ToVector3(), item.RoomNumber, item.Model.Color, false, SPRITE_TYPES::SPR_LENS_FLARE_3); + { + float currentIntensity = (float)item.ItemFlags[0] / 100.0f; + SetupLensFlare(item.Pose.Position.ToVector3(), item.RoomNumber, item.Model.Color, currentIntensity, SPRITE_TYPES::SPR_LENS_FLARE_3); + item.ItemFlags[0] = (short)(currentIntensity * 100.0f); + } } void ClearLensFlares() From c484f8aa84956838d54c8137fa753cec9a8f5387 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:55:59 +0100 Subject: [PATCH 096/112] Enhanced lensflare effects, fix ray test, discard flares behind camera --- TombEngine/Objects/Effects/LensFlare.cpp | 26 ++++++++++---- TombEngine/Shaders/PostProcess.fx | 46 ++++++++++++++++-------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/TombEngine/Objects/Effects/LensFlare.cpp b/TombEngine/Objects/Effects/LensFlare.cpp index 260c961ba..92e08d460 100644 --- a/TombEngine/Objects/Effects/LensFlare.cpp +++ b/TombEngine/Objects/Effects/LensFlare.cpp @@ -45,6 +45,16 @@ namespace TEN::Entities::Effects static void SetupLensFlare(const Vector3& pos, int roomNumber, const Color& color, float& intensity, int spriteID) { + auto cameraPos = Camera.pos.ToVector3(); + auto cameraTarget = Camera.target.ToVector3(); + + auto forward = (cameraTarget - cameraPos); + forward.Normalize(); + + // Discard lensflares behind camera. + if (forward.Dot(pos - cameraPos) < 0.0f) + return; + auto lensFlarePos = pos; bool isGlobal = roomNumber == NO_VALUE; @@ -53,8 +63,8 @@ namespace TEN::Entities::Effects if (TestEnvironment(ENV_FLAG_NO_LENSFLARE, Camera.pos.RoomNumber)) return; - AdjustLensflarePosition(lensFlarePos, Camera.pos.ToVector3(), 8.0f, BLOCK(256)); - AdjustLensflarePosition(lensFlarePos, Camera.pos.ToVector3(), 4.0f, BLOCK(32)); + AdjustLensflarePosition(lensFlarePos, cameraPos, 8.0f, BLOCK(256)); + AdjustLensflarePosition(lensFlarePos, cameraPos, 4.0f, BLOCK(32)); // FAILSAFE: Break while loop if room can't be found (e.g. camera is in the void). int narrowingCycleCount = 0; @@ -68,13 +78,13 @@ namespace TEN::Entities::Effects break; } - AdjustLensflarePosition(lensFlarePos, Camera.pos.ToVector3(), 2.0f, BLOCK(1)); + AdjustLensflarePosition(lensFlarePos, cameraPos, 2.0f, BLOCK(32)); narrowingCycleCount++; } } else { - float dist = Vector3::Distance(lensFlarePos, Camera.pos.ToVector3()); + float dist = Vector3::Distance(lensFlarePos, cameraPos); if (dist > BLOCK(32)) return; } @@ -84,12 +94,14 @@ namespace TEN::Entities::Effects { if (TestEnvironment(ENV_FLAG_NOT_NEAR_OUTSIDE, roomNumber) || !isGlobal) { - auto origin = Camera.pos; - auto target = GameVector(lensFlarePos, roomNumber); + auto origin = GameVector(lensFlarePos, roomNumber); + auto target = Camera.pos; MESH_INFO* mesh = nullptr; auto tempVector = Vector3i(); - isVisible = LOS(&origin, &target) && ObjectOnLOS2(&origin, &target, &tempVector, &mesh) == NO_LOS_ITEM; + + isVisible = LOS(&origin, &target) && ObjectOnLOS2(&origin, &target, &tempVector, &mesh) == NO_LOS_ITEM && + ObjectOnLOS2(&origin, &target, &tempVector, nullptr, ID_LARA) == NO_LOS_ITEM; } } diff --git a/TombEngine/Shaders/PostProcess.fx b/TombEngine/Shaders/PostProcess.fx index 3d7b7b786..ffb748a76 100644 --- a/TombEngine/Shaders/PostProcess.fx +++ b/TombEngine/Shaders/PostProcess.fx @@ -93,43 +93,59 @@ float4 PSFinalPass(PixelShaderInput input) : SV_TARGET float3 LensFlare(float2 uv, float2 pos) { - float intensity = 1.5f; - float2 main = uv - pos; - float2 uvd = uv * (length(uv)); + float intensity = 0.5f; + float2 main = uv - pos; + float2 uvd = uv * length(uv); + + // Angular and distance calculations + float ang = atan2(main.y, main.x); float dist = length(main); dist = pow(dist, 0.1f); - float f1 = max(0.01f - pow(length(uv + 1.2f * pos), 1.9f), 0.0f) * 7.0f; + // Sunflare effect with rotation and ray length variation + float f0 = 1.0f / (length(uv - pos) * 35.0f + 1.0f); + f0 = pow(f0, 1.5f); - float f2 = max(1.0f / (1.0f + 32.0f * pow(length(uvd + 0.8f * pos), 2.0f)), 0.0f) * 0.1f; - float f22 = max(1.0f / (1.0f + 32.0f * pow(length(uvd + 0.85f * pos), 2.0f)), 0.0f) * 0.08f; - float f23 = max(1.0f / (1.0f + 32.0f * pow(length(uvd + 0.9f * pos), 2.0f)), 0.0f) * 0.06f; + // Calculate position-based rotation offset + float rotationOffset = dot(pos, float2(0.5f, 0.5f)) * (PI / 2.0f); + // Modify starburst pattern with rotation and variation + f0 += f0 * (sin((ang + rotationOffset + 1.0f / 18.0f) * 8.0f) * 0.2f + dist * 0.1f + 0.2f); + + // Lensflare glow components + float f2 = max(1.0f / (1.0f + 32.0f * pow(length(uvd + 0.8f * pos), 2.0f)), 0.0f) * 0.25f; + float f22 = max(1.0f / (1.0f + 32.0f * pow(length(uvd + 0.85f * pos), 2.0f)), 0.0f) * 0.23f; + float f23 = max(1.0f / (1.0f + 32.0f * pow(length(uvd + 0.9f * pos), 2.0f)), 0.0f) * 0.21f; + + // Circular lens artifacts float2 uvx = lerp(uv, uvd, -0.5f); - float f4 = max(0.01f - pow(length(uvx + 0.4f * pos), 2.4f), 0.0f) * 6.0f; float f42 = max(0.01f - pow(length(uvx + 0.45f * pos), 2.4f), 0.0f) * 5.0f; float f43 = max(0.01f - pow(length(uvx + 0.5f * pos), 2.4f), 0.0f) * 3.0f; + // Smaller lens artifacts uvx = lerp(uv, uvd, -0.4f); - float f5 = max(0.01f - pow(length(uvx + 0.2f * pos), 5.5f), 0.0f) * 2.0f; float f52 = max(0.01f - pow(length(uvx + 0.4f * pos), 5.5f), 0.0f) * 2.0f; float f53 = max(0.01f - pow(length(uvx + 0.6f * pos), 5.5f), 0.0f) * 2.0f; + // Symmetric artifacts uvx = lerp(uv, uvd, -0.5f); - float f6 = max(0.01f - pow(length(uvx - 0.3f * pos), 1.6f), 0.0f) * 6.0f; float f62 = max(0.01f - pow(length(uvx - 0.325f * pos), 1.6f), 0.0f) * 3.0f; float f63 = max(0.01f - pow(length(uvx - 0.35f * pos), 1.6f), 0.0f) * 5.0f; - float3 c = float3(0.0f, 0.0f, 0.0f); + // Sunflare and lensflare outputs + float3 sunflare = float3(f0, f0, f0); + float3 lensflare = float3( + f2 + f4 + f5 + f6, + f22 + f42 + f52 + f62, + f23 + f43 + f53 + f63 + ); - c.r += f2 + f4 + f5 + f6; - c.g += f22 + f42 + f52 + f62; - c.b += f23 + f43 + f53 + f63; - + // Combine the effects and adjust intensity + float3 c = saturate(sunflare) * 0.5f + lensflare; c = c * 1.3f - float3(length(uvd) * 0.05f, length(uvd) * 0.05f, length(uvd) * 0.05f); return c * intensity; From 9e54c8dd0b0e881b3cadcaa83282e5bf589a78dd Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:17:54 +0100 Subject: [PATCH 097/112] Add safeguards in level reader to throw exception if data sizes are inconsistent --- TombEngine/Specific/level.cpp | 102 +++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 33b441f89..101ff3bb7 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -160,6 +160,16 @@ bool ReadBool() return bool(ReadUInt8()); } +int ReadCount(int maxValue = SQUARE(1024)) +{ + int count = ReadInt32(); + + if (count < 0 || count > maxValue) + throw std::exception("Level data block has incorrect size. Level version is probably outdated."); + + return count; +} + void ReadBytes(void* dest, int count) { memcpy(dest, CurrentDataPtr, count); @@ -209,7 +219,7 @@ std::string ReadString() void LoadItems() { - g_Level.NumItems = ReadInt32(); + g_Level.NumItems = ReadCount(); TENLog("Moveables: " + std::to_string(g_Level.NumItems), LogLevel::Info); if (g_Level.NumItems == 0) @@ -272,7 +282,7 @@ void LoadObjects() Objects.Initialize(); Statics.Initialize(); - int meshCount = ReadInt32(); + int meshCount = ReadCount(); TENLog("Mesh count: " + std::to_string(meshCount), LogLevel::Info); g_Level.Meshes.reserve(meshCount); @@ -287,7 +297,7 @@ void LoadObjects() mesh.sphere.Center.z = ReadFloat(); mesh.sphere.Radius = ReadFloat(); - int vertexCount = ReadInt32(); + int vertexCount = ReadCount(CUBE(1024)); mesh.positions.resize(vertexCount); ReadBytes(mesh.positions.data(), 12 * vertexCount); @@ -301,7 +311,7 @@ void LoadObjects() mesh.bones.resize(vertexCount); ReadBytes(mesh.bones.data(), 4 * vertexCount); - int bucketCount = ReadInt32(); + int bucketCount = ReadCount(); mesh.buckets.reserve(bucketCount); for (int j = 0; j < bucketCount; j++) { @@ -313,7 +323,7 @@ void LoadObjects() bucket.numQuads = 0; bucket.numTriangles = 0; - int polyCount = ReadInt32(); + int polyCount = ReadCount(CUBE(1024)); bucket.polygons.reserve(polyCount); for (int k = 0; k < polyCount; k++) { @@ -355,7 +365,7 @@ void LoadObjects() g_Level.Meshes.push_back(mesh); } - int animCount = ReadInt32(); + int animCount = ReadCount(); TENLog("Animation count: " + std::to_string(animCount), LogLevel::Info); g_Level.Anims.resize(animCount); @@ -378,23 +388,23 @@ void LoadObjects() anim->CommandIndex = ReadInt32(); } - int changeCount = ReadInt32(); + int changeCount = ReadCount(); g_Level.Changes.resize(changeCount); ReadBytes(g_Level.Changes.data(), sizeof(StateDispatchData) * changeCount); - int rangeCount = ReadInt32(); + int rangeCount = ReadCount(); g_Level.Ranges.resize(rangeCount); ReadBytes(g_Level.Ranges.data(), sizeof(StateDispatchRangeData) * rangeCount); - int commandCount = ReadInt32(); + int commandCount = ReadCount(); g_Level.Commands.resize(commandCount); ReadBytes(g_Level.Commands.data(), sizeof(int) * commandCount); - int boneCount = ReadInt32(); + int boneCount = ReadCount(); g_Level.Bones.resize(boneCount); ReadBytes(g_Level.Bones.data(), 4 * boneCount); - int frameCount = ReadInt32(); + int frameCount = ReadCount(); g_Level.Frames.resize(frameCount); for (int i = 0; i < frameCount; i++) { @@ -422,7 +432,7 @@ void LoadObjects() } } - int modelCount = ReadInt32(); + int modelCount = ReadCount(); TENLog("Model count: " + std::to_string(modelCount), LogLevel::Info); for (int i = 0; i < modelCount; i++) @@ -441,7 +451,7 @@ void LoadObjects() TENLog("Initializing objects...", LogLevel::Info); InitializeObjects(); - int staticCount = ReadInt32(); + int staticCount = ReadCount(); TENLog("Statics: " + std::to_string(staticCount), LogLevel::Info); for (int i = 0; i < staticCount; i++) @@ -476,7 +486,7 @@ void LoadObjects() void LoadCameras() { - int cameraCount = ReadInt32(); + int cameraCount = ReadCount(); TENLog("Camera count: " + std::to_string(cameraCount), LogLevel::Info); g_Level.Cameras.reserve(cameraCount); @@ -495,13 +505,13 @@ void LoadCameras() g_GameScriptEntities->AddName(camera.Name, camera); } - NumberSpotcams = ReadInt32(); + NumberSpotcams = ReadCount(); // TODO: Read properly! if (NumberSpotcams != 0) ReadBytes(SpotCam, NumberSpotcams * sizeof(SPOTCAM)); - int sinkCount = ReadInt32(); + int sinkCount = ReadCount(); TENLog("Sink count: " + std::to_string(sinkCount), LogLevel::Info); g_Level.Sinks.reserve(sinkCount); @@ -525,7 +535,7 @@ void LoadTextures() int size; - int textureCount = ReadInt32(); + int textureCount = ReadCount(); TENLog("Room texture count: " + std::to_string(textureCount), LogLevel::Info); g_Level.RoomTextures.reserve(textureCount); @@ -551,7 +561,7 @@ void LoadTextures() g_Level.RoomTextures.push_back(texture); } - textureCount = ReadInt32(); + textureCount = ReadCount(); TENLog("Object texture count: " + std::to_string(textureCount), LogLevel::Info); g_Level.MoveablesTextures.reserve(textureCount); @@ -577,7 +587,7 @@ void LoadTextures() g_Level.MoveablesTextures.push_back(texture); } - textureCount = ReadInt32(); + textureCount = ReadCount(); TENLog("Static texture count: " + std::to_string(textureCount), LogLevel::Info); g_Level.StaticsTextures.reserve(textureCount); @@ -603,7 +613,7 @@ void LoadTextures() g_Level.StaticsTextures.push_back(texture); } - textureCount = ReadInt32(); + textureCount = ReadCount(); TENLog("Anim texture count: " + std::to_string(textureCount), LogLevel::Info); g_Level.AnimatedTextures.reserve(textureCount); @@ -629,7 +639,7 @@ void LoadTextures() g_Level.AnimatedTextures.push_back(texture); } - textureCount = ReadInt32(); + textureCount = ReadCount(); TENLog("Sprite texture count: " + std::to_string(textureCount), LogLevel::Info); g_Level.SpritesTextures.reserve(textureCount); @@ -676,7 +686,7 @@ static Plane ConvertFakePlaneToPlane(const Vector3& fakePlane, bool isFloor) void LoadDynamicRoomData() { - int roomCount = ReadInt32(); + int roomCount = ReadCount(); if (g_Level.Rooms.size() != roomCount) throw std::exception("Dynamic room data count is inconsistent with room count."); @@ -687,7 +697,7 @@ void LoadDynamicRoomData() room.Name = ReadString(); - int tagCount = ReadInt32(); + int tagCount = ReadCount(); room.Tags.resize(0); room.Tags.reserve(tagCount); @@ -702,7 +712,7 @@ void LoadDynamicRoomData() room.reverbType = (ReverbType)ReadInt32(); room.flipNumber = ReadInt32(); - int staticCount = ReadInt32(); + int staticCount = ReadCount(); room.mesh.resize(0); room.mesh.reserve(staticCount); @@ -727,7 +737,7 @@ void LoadDynamicRoomData() g_GameScriptEntities->AddName(mesh.Name, mesh); } - int triggerVolumeCount = ReadInt32(); + int triggerVolumeCount = ReadCount(); room.TriggerVolumes.resize(0); room.TriggerVolumes.reserve(triggerVolumeCount); @@ -767,7 +777,7 @@ void LoadStaticRoomData() constexpr auto ILLEGAL_FLOOR_SLOPE_ANGLE = ANGLE(36.0f); constexpr auto ILLEGAL_CEILING_SLOPE_ANGLE = ANGLE(45.0f); - int roomCount = ReadInt32(); + int roomCount = ReadCount(); TENLog("Room count: " + std::to_string(roomCount), LogLevel::Info); g_Level.Rooms.reserve(roomCount); @@ -781,7 +791,7 @@ void LoadStaticRoomData() room.BottomHeight = ReadInt32(); room.TopHeight = ReadInt32(); - int vertexCount = ReadInt32(); + int vertexCount = ReadCount(CUBE(1024)); room.positions.reserve(vertexCount); for (int j = 0; j < vertexCount; j++) @@ -795,7 +805,7 @@ void LoadStaticRoomData() for (int j = 0; j < vertexCount; j++) room.effects.push_back(ReadVector3()); - int bucketCount = ReadInt32(); + int bucketCount = ReadCount(); room.buckets.reserve(bucketCount); for (int j = 0; j < bucketCount; j++) { @@ -807,7 +817,7 @@ void LoadStaticRoomData() bucket.numQuads = 0; bucket.numTriangles = 0; - int polyCount = ReadInt32(); + int polyCount = ReadCount(CUBE(1024)); bucket.polygons.reserve(polyCount); for (int k = 0; k < polyCount; k++) { @@ -847,7 +857,7 @@ void LoadStaticRoomData() room.buckets.push_back(bucket); } - int portalCount = ReadInt32(); + int portalCount = ReadCount(); for (int j = 0; j < portalCount; j++) LoadPortal(room); @@ -906,7 +916,7 @@ void LoadStaticRoomData() } } - int lightCount = ReadInt32(); + int lightCount = ReadCount(); room.lights.reserve(lightCount); for (int j = 0; j < lightCount; j++) { @@ -1022,7 +1032,7 @@ size_t ReadFileEx(void* ptr, size_t size, size_t count, FILE* stream) void LoadSoundSources() { - int soundSourceCount = ReadInt32(); + int soundSourceCount = ReadCount(); TENLog("Sound source count: " + std::to_string(soundSourceCount), LogLevel::Info); g_Level.SoundSources.reserve(soundSourceCount); @@ -1043,7 +1053,7 @@ void LoadSoundSources() void LoadAnimatedTextures() { - int animatedTextureCount = ReadInt32(); + int animatedTextureCount = ReadCount(); TENLog("Anim texture count: " + std::to_string(animatedTextureCount), LogLevel::Info); for (int i = 0; i < animatedTextureCount; i++) @@ -1073,7 +1083,7 @@ void LoadAnimatedTextures() void LoadAIObjects() { - int aiObjectCount = ReadInt32(); + int aiObjectCount = ReadCount(); TENLog("AI object count: " + std::to_string(aiObjectCount), LogLevel::Info); g_Level.AIObjects.reserve(aiObjectCount); @@ -1119,11 +1129,11 @@ void LoadEvent(EventSet& eventSet) void LoadEventSets() { - int eventSetCount = ReadInt32(); + int eventSetCount = ReadCount(); if (eventSetCount == 0) return; - int globalEventSetCount = ReadInt32(); + int globalEventSetCount = ReadCount(); TENLog("Global event set count: " + std::to_string(globalEventSetCount), LogLevel::Info); for (int i = 0; i < globalEventSetCount; i++) @@ -1132,7 +1142,7 @@ void LoadEventSets() eventSet.Name = ReadString(); - int eventCount = ReadInt32(); + int eventCount = ReadCount(); for (int j = 0; j < eventCount; j++) LoadEvent(eventSet); @@ -1142,7 +1152,7 @@ void LoadEventSets() g_Level.LoopedEventSetIndices.push_back(i); } - int volumeEventSetCount = ReadInt32(); + int volumeEventSetCount = ReadCount(); TENLog("Volume event set count: " + std::to_string(volumeEventSetCount), LogLevel::Info); for (int i = 0; i < volumeEventSetCount; i++) @@ -1152,7 +1162,7 @@ void LoadEventSets() eventSet.Name = ReadString(); eventSet.Activators = (ActivatorFlags)ReadInt32(); - int eventCount = ReadInt32(); + int eventCount = ReadCount(); for (int j = 0; j < eventCount; j++) LoadEvent(eventSet); @@ -1429,7 +1439,7 @@ void LoadSamples() g_Level.SoundMap.resize(soundMapSize); ReadBytes(g_Level.SoundMap.data(), soundMapSize * sizeof(short)); - int sampleInfoCount = ReadInt32(); + int sampleInfoCount = ReadCount(); if (!sampleInfoCount) { TENLog("No samples were found or loaded.", LogLevel::Warning); @@ -1441,7 +1451,7 @@ void LoadSamples() g_Level.SoundDetails.resize(sampleInfoCount); ReadBytes(g_Level.SoundDetails.data(), sampleInfoCount * sizeof(SampleInfo)); - int sampleCount = ReadInt32(); + int sampleCount = ReadCount(); if (sampleCount <= 0) return; @@ -1465,19 +1475,19 @@ void LoadSamples() void LoadBoxes() { // Read boxes - int boxCount = ReadInt32(); + int boxCount = ReadCount(CUBE(1024)); TENLog("Box count: " + std::to_string(boxCount), LogLevel::Info); g_Level.PathfindingBoxes.resize(boxCount); ReadBytes(g_Level.PathfindingBoxes.data(), boxCount * sizeof(BOX_INFO)); // Read overlaps - int overlapCount = ReadInt32(); + int overlapCount = ReadCount(CUBE(1024)); TENLog("Overlap count: " + std::to_string(overlapCount), LogLevel::Info); g_Level.Overlaps.resize(overlapCount); ReadBytes(g_Level.Overlaps.data(), overlapCount * sizeof(OVERLAP)); // Read zones - int zoneGroupCount = ReadInt32(); + int zoneGroupCount = ReadCount(CUBE(1024)); TENLog("Zone group count: " + std::to_string(zoneGroupCount), LogLevel::Info); for (int i = 0; i < 2; i++) @@ -1558,7 +1568,7 @@ bool LoadLevelFile(int levelIndex) void LoadSprites() { - int spriteCount = ReadInt32(); + int spriteCount = ReadCount(); g_Level.Sprites.resize(spriteCount); TENLog("Sprite count: " + std::to_string(spriteCount), LogLevel::Info); @@ -1577,7 +1587,7 @@ void LoadSprites() spr->y4 = ReadFloat(); } - int spriteSeqCount = ReadInt32(); + int spriteSeqCount = ReadCount(); TENLog("Sprite sequence count: " + std::to_string(spriteSeqCount), LogLevel::Info); From 1695f4eeb84a1e35d8f68b21acebe2df3e11a057 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:24:12 +0100 Subject: [PATCH 098/112] Update version.h --- TombEngine/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TombEngine/version.h b/TombEngine/version.h index f8c2bfd2f..efc0f13eb 100644 --- a/TombEngine/version.h +++ b/TombEngine/version.h @@ -8,7 +8,7 @@ #define TEN_MAJOR_VERSION 1 #define TEN_MINOR_VERSION 5 #define TEN_BUILD_NUMBER 0 -#define TEN_REVISION_NUMBER 5 +#define TEN_REVISION_NUMBER 6 #define TEST_BUILD 1 From ddfeaf5fa2cc3b3fb43b6c2d2a04611ae71dba8d Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:42:23 +0100 Subject: [PATCH 099/112] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e84e42289..4e427ca78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Fixed Lara's Home entry not working. * Fixed exploding TR3 bosses. * Fixed original issue with deactivation of Dart Emitter. -* Fixed Lens Flare object not functioning properly and not reacting to moveables and statics in front of it. +* Fixed Lens Flare object not functioning properly. +* Fixed lens flares not being occluded by static meshes and moveables. * Fixed Skeleton and Mummy not reacting to shotgun hits. ### New Features From f455dd1c915d77c7ccafe84cb3d015fc6f73fd78 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:11:04 +0100 Subject: [PATCH 100/112] Remove hardcoded ricochet sound condition --- TombEngine/Game/effects/effects.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TombEngine/Game/effects/effects.cpp b/TombEngine/Game/effects/effects.cpp index 0d0f2efb2..9a47a93cb 100644 --- a/TombEngine/Game/effects/effects.cpp +++ b/TombEngine/Game/effects/effects.cpp @@ -454,9 +454,7 @@ void TriggerRicochetSpark(const GameVector& pos, short angle, bool sound) { int count = Random::GenerateInt(3, 8); TriggerRicochetSpark(pos, angle, count); - - if (sound && Random::TestProbability(1 / 3.0f)) - SoundEffect(SFX_TR4_WEAPON_RICOCHET, &Pose(pos.ToVector3i())); + SoundEffect(SFX_TR4_WEAPON_RICOCHET, &Pose(pos.ToVector3i())); } void TriggerCyborgSpark(int x, int y, int z, short xv, short yv, short zv) From 9fad7eec5057aadd0a8a129a201ebf1237811dbb Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:41:18 +0100 Subject: [PATCH 101/112] Fix mistake in documentation --- Documentation/doc/1 modules/Sound.html | 8 ++++---- TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/doc/1 modules/Sound.html b/Documentation/doc/1 modules/Sound.html index 055fd38e5..4ebca6e6e 100644 --- a/Documentation/doc/1 modules/Sound.html +++ b/Documentation/doc/1 modules/Sound.html @@ -117,7 +117,7 @@ Play an audio track - SetAmbientTrack(name, specifies) + SetAmbientTrack(name, fromStart) Set and play an ambient track @@ -189,7 +189,7 @@
                                                                                                            - SetAmbientTrack(name, specifies) + SetAmbientTrack(name, fromStart)
                                                                                                            Set and play an ambient track @@ -202,9 +202,9 @@ string of track (without file extension) to play -
                                                                                                          • specifies +
                                                                                                          • fromStart bool - whether ambient track should play from the beginning, or crossfade at a random position + specifies whether ambient track should play from the start, or crossfade at a random position
                                                                                                          diff --git a/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp b/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp index e0687e19f..7a5402cfb 100644 --- a/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Sound/SoundHandler.cpp @@ -27,7 +27,7 @@ namespace TEN::Scripting::Sound /// Set and play an ambient track // @function SetAmbientTrack // @tparam string name of track (without file extension) to play - // @tparam bool specifies whether ambient track should play from the beginning, or crossfade at a random position + // @tparam bool fromStart specifies whether ambient track should play from the start, or crossfade at a random position static void SetAmbientTrack(const std::string& trackName, TypeOrNil fromTheBeginning) { auto pos = USE_IF_HAVE(bool, fromTheBeginning, false) ? std::optional(0) : std::optional(); From 3a649d612df030f4ec01121af2aa111f08c3e507 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:25:15 +0100 Subject: [PATCH 102/112] Fix issues with lensflare occlusion --- TombEngine/Objects/Effects/LensFlare.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/TombEngine/Objects/Effects/LensFlare.cpp b/TombEngine/Objects/Effects/LensFlare.cpp index 92e08d460..313bce047 100644 --- a/TombEngine/Objects/Effects/LensFlare.cpp +++ b/TombEngine/Objects/Effects/LensFlare.cpp @@ -63,6 +63,7 @@ namespace TEN::Entities::Effects if (TestEnvironment(ENV_FLAG_NO_LENSFLARE, Camera.pos.RoomNumber)) return; + // Gradually move lensflare position to a nearest point within closest room. AdjustLensflarePosition(lensFlarePos, cameraPos, 8.0f, BLOCK(256)); AdjustLensflarePosition(lensFlarePos, cameraPos, 4.0f, BLOCK(32)); @@ -92,24 +93,39 @@ namespace TEN::Entities::Effects bool isVisible = false; if (roomNumber != NO_VALUE) { + // Don't draw lensflares in rooms which aren't outside or adjacent to outside rooms. if (TestEnvironment(ENV_FLAG_NOT_NEAR_OUTSIDE, roomNumber) || !isGlobal) { auto origin = GameVector(lensFlarePos, roomNumber); auto target = Camera.pos; + auto distance = Vector3::Distance(origin.ToVector3(), target.ToVector3()); + + // Check room occlusion. + isVisible = LOS(&origin, &target); MESH_INFO* mesh = nullptr; - auto tempVector = Vector3i(); + auto pointOfContact = Vector3i(); - isVisible = LOS(&origin, &target) && ObjectOnLOS2(&origin, &target, &tempVector, &mesh) == NO_LOS_ITEM && - ObjectOnLOS2(&origin, &target, &tempVector, nullptr, ID_LARA) == NO_LOS_ITEM; + // Check occlusion for all static meshes and moveables but player. + bool collided = isVisible && ObjectOnLOS2(&origin, &target, &pointOfContact, &mesh) != NO_LOS_ITEM; + if (collided && Vector3::Distance(pointOfContact.ToVector3(), origin.ToVector3()) < distance) + isVisible = false; + + // Check occlusion only for player. + collided = isVisible && ObjectOnLOS2(&origin, &target, &pointOfContact, nullptr, ID_LARA) != NO_LOS_ITEM; + if (collided && Vector3::Distance(pointOfContact.ToVector3(), origin.ToVector3()) < distance) + isVisible = false; } } + // Fade in/out lensflares depending on their visibility. UpdateLensFlareIntensity(isVisible, intensity); + // Lensflare is completely invisible. if (!isVisible && !isGlobal && intensity == 0.0f) return; + // Generate slight shimmer. float shimmer = Random::GenerateFloat(-SHIMMER_STRENGTH, SHIMMER_STRENGTH); float finalIntensity = std::clamp(Smoothstep(intensity) + shimmer * intensity, 0.0f, MAX_INTENSITY); From b5fa51b2dc484fc5f370ce7084fd188c6d8af2a2 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:27:42 +0100 Subject: [PATCH 103/112] Recompile docs for 1.6, version update --- Documentation/config.ld | 2 +- Documentation/doc/1 modules/Effects.html | 2 +- Documentation/doc/1 modules/Flow.html | 2 +- Documentation/doc/1 modules/Input.html | 2 +- Documentation/doc/1 modules/Inventory.html | 2 +- Documentation/doc/1 modules/Logic.html | 2 +- Documentation/doc/1 modules/Objects.html | 2 +- Documentation/doc/1 modules/Sound.html | 2 +- Documentation/doc/1 modules/Strings.html | 2 +- Documentation/doc/1 modules/Util.html | 2 +- Documentation/doc/1 modules/View.html | 2 +- Documentation/doc/2 classes/Flow.Animations.html | 2 +- Documentation/doc/2 classes/Flow.Fog.html | 2 +- Documentation/doc/2 classes/Flow.InventoryItem.html | 2 +- Documentation/doc/2 classes/Flow.LensFlare.html | 2 +- Documentation/doc/2 classes/Flow.Level.html | 2 +- Documentation/doc/2 classes/Flow.Mirror.html | 2 +- Documentation/doc/2 classes/Flow.Settings.html | 2 +- Documentation/doc/2 classes/Flow.SkyLayer.html | 2 +- Documentation/doc/2 classes/Flow.Starfield.html | 2 +- Documentation/doc/2 classes/Objects.AIObject.html | 2 +- Documentation/doc/2 classes/Objects.Camera.html | 2 +- Documentation/doc/2 classes/Objects.LaraObject.html | 2 +- Documentation/doc/2 classes/Objects.Moveable.html | 2 +- Documentation/doc/2 classes/Objects.Room.html | 2 +- Documentation/doc/2 classes/Objects.Sink.html | 2 +- Documentation/doc/2 classes/Objects.SoundSource.html | 2 +- Documentation/doc/2 classes/Objects.Static.html | 2 +- Documentation/doc/2 classes/Objects.Volume.html | 2 +- Documentation/doc/2 classes/Strings.DisplayString.html | 2 +- Documentation/doc/2 classes/View.DisplaySprite.html | 2 +- Documentation/doc/3 primitive classes/Color.html | 2 +- Documentation/doc/3 primitive classes/Rotation.html | 2 +- Documentation/doc/3 primitive classes/Vec2.html | 2 +- Documentation/doc/3 primitive classes/Vec3.html | 2 +- Documentation/doc/4 enums/Effects.BlendID.html | 2 +- Documentation/doc/4 enums/Effects.EffectID.html | 2 +- Documentation/doc/4 enums/Flow.FreezeMode.html | 2 +- Documentation/doc/4 enums/Flow.GameStatus.html | 2 +- Documentation/doc/4 enums/Input.ActionID.html | 2 +- Documentation/doc/4 enums/Objects.AmmoType.html | 2 +- Documentation/doc/4 enums/Objects.MoveableStatus.html | 2 +- Documentation/doc/4 enums/Objects.ObjID.html | 2 +- Documentation/doc/4 enums/Objects.RoomFlagID.html | 2 +- Documentation/doc/4 enums/Objects.RoomReverb.html | 2 +- Documentation/doc/4 enums/Sound.SoundTrackType.html | 2 +- Documentation/doc/4 enums/Strings.DisplayStringOption.html | 2 +- Documentation/doc/4 enums/Util.LogLevel.html | 2 +- Documentation/doc/4 enums/View.AlignMode.html | 2 +- Documentation/doc/4 enums/View.CameraType.html | 2 +- Documentation/doc/4 enums/View.PostProcessMode.html | 2 +- Documentation/doc/4 enums/View.ScaleMode.html | 2 +- Documentation/doc/5 lua utility modules/EventSequence.html | 2 +- Documentation/doc/5 lua utility modules/Timer.html | 2 +- Documentation/doc/index.html | 4 ++-- TombEngine/version.h | 6 +++--- 56 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Documentation/config.ld b/Documentation/config.ld index a7798f24d..654c3df26 100644 --- a/Documentation/config.ld +++ b/Documentation/config.ld @@ -12,7 +12,7 @@ new_type("luautil", "5 Lua utility modules", true) not_luadoc = true -local version = "1.6 (Developer Version)" +local version = "1.6" project = "TombEngine" title = "TombEngine " .. version .. " Lua API" description = "TombEngine " .. version .. " scripting interface" diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index 51af05ba6..0e51a9a27 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Flow.html b/Documentation/doc/1 modules/Flow.html index 4b3b5f8da..c673ae000 100644 --- a/Documentation/doc/1 modules/Flow.html +++ b/Documentation/doc/1 modules/Flow.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Input.html b/Documentation/doc/1 modules/Input.html index 84989a588..6f3565a79 100644 --- a/Documentation/doc/1 modules/Input.html +++ b/Documentation/doc/1 modules/Input.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Inventory.html b/Documentation/doc/1 modules/Inventory.html index edc09052a..b1abf76dc 100644 --- a/Documentation/doc/1 modules/Inventory.html +++ b/Documentation/doc/1 modules/Inventory.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Logic.html b/Documentation/doc/1 modules/Logic.html index 0ec446dba..9665b6548 100644 --- a/Documentation/doc/1 modules/Logic.html +++ b/Documentation/doc/1 modules/Logic.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Objects.html b/Documentation/doc/1 modules/Objects.html index e84b3a924..a05ef543a 100644 --- a/Documentation/doc/1 modules/Objects.html +++ b/Documentation/doc/1 modules/Objects.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Sound.html b/Documentation/doc/1 modules/Sound.html index 4ebca6e6e..3902e31b8 100644 --- a/Documentation/doc/1 modules/Sound.html +++ b/Documentation/doc/1 modules/Sound.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html index 2f6aff820..597b921ff 100644 --- a/Documentation/doc/1 modules/Strings.html +++ b/Documentation/doc/1 modules/Strings.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/Util.html b/Documentation/doc/1 modules/Util.html index ea840a4e8..a7f59465f 100644 --- a/Documentation/doc/1 modules/Util.html +++ b/Documentation/doc/1 modules/Util.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/1 modules/View.html b/Documentation/doc/1 modules/View.html index e5d515495..f27895ef3 100644 --- a/Documentation/doc/1 modules/View.html +++ b/Documentation/doc/1 modules/View.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.Animations.html b/Documentation/doc/2 classes/Flow.Animations.html index 55c6bb3cc..5e050ae57 100644 --- a/Documentation/doc/2 classes/Flow.Animations.html +++ b/Documentation/doc/2 classes/Flow.Animations.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.Fog.html b/Documentation/doc/2 classes/Flow.Fog.html index d47ea0f43..393062f23 100644 --- a/Documentation/doc/2 classes/Flow.Fog.html +++ b/Documentation/doc/2 classes/Flow.Fog.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.InventoryItem.html b/Documentation/doc/2 classes/Flow.InventoryItem.html index 3ed30e47b..6e9b4164b 100644 --- a/Documentation/doc/2 classes/Flow.InventoryItem.html +++ b/Documentation/doc/2 classes/Flow.InventoryItem.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.LensFlare.html b/Documentation/doc/2 classes/Flow.LensFlare.html index 5f7146ff3..22c0b4621 100644 --- a/Documentation/doc/2 classes/Flow.LensFlare.html +++ b/Documentation/doc/2 classes/Flow.LensFlare.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html index 397e7440f..7872a908d 100644 --- a/Documentation/doc/2 classes/Flow.Level.html +++ b/Documentation/doc/2 classes/Flow.Level.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.Mirror.html b/Documentation/doc/2 classes/Flow.Mirror.html index 5e9b99c26..98bb6aba9 100644 --- a/Documentation/doc/2 classes/Flow.Mirror.html +++ b/Documentation/doc/2 classes/Flow.Mirror.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.Settings.html b/Documentation/doc/2 classes/Flow.Settings.html index 55249e222..c91812817 100644 --- a/Documentation/doc/2 classes/Flow.Settings.html +++ b/Documentation/doc/2 classes/Flow.Settings.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.SkyLayer.html b/Documentation/doc/2 classes/Flow.SkyLayer.html index c086a8f7c..8a3c3b2b5 100644 --- a/Documentation/doc/2 classes/Flow.SkyLayer.html +++ b/Documentation/doc/2 classes/Flow.SkyLayer.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Flow.Starfield.html b/Documentation/doc/2 classes/Flow.Starfield.html index ab054d182..12525a94f 100644 --- a/Documentation/doc/2 classes/Flow.Starfield.html +++ b/Documentation/doc/2 classes/Flow.Starfield.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.AIObject.html b/Documentation/doc/2 classes/Objects.AIObject.html index 180e6dc02..084d7fea8 100644 --- a/Documentation/doc/2 classes/Objects.AIObject.html +++ b/Documentation/doc/2 classes/Objects.AIObject.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.Camera.html b/Documentation/doc/2 classes/Objects.Camera.html index b7932494b..2bdb9ed5a 100644 --- a/Documentation/doc/2 classes/Objects.Camera.html +++ b/Documentation/doc/2 classes/Objects.Camera.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.LaraObject.html b/Documentation/doc/2 classes/Objects.LaraObject.html index d7c152e6e..51f890f32 100644 --- a/Documentation/doc/2 classes/Objects.LaraObject.html +++ b/Documentation/doc/2 classes/Objects.LaraObject.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.Moveable.html b/Documentation/doc/2 classes/Objects.Moveable.html index 0dd793594..1f08a41eb 100644 --- a/Documentation/doc/2 classes/Objects.Moveable.html +++ b/Documentation/doc/2 classes/Objects.Moveable.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.Room.html b/Documentation/doc/2 classes/Objects.Room.html index 92965751c..e8084443f 100644 --- a/Documentation/doc/2 classes/Objects.Room.html +++ b/Documentation/doc/2 classes/Objects.Room.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.Sink.html b/Documentation/doc/2 classes/Objects.Sink.html index 93143d6ae..e7094070a 100644 --- a/Documentation/doc/2 classes/Objects.Sink.html +++ b/Documentation/doc/2 classes/Objects.Sink.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.SoundSource.html b/Documentation/doc/2 classes/Objects.SoundSource.html index 14275226d..f344ad98d 100644 --- a/Documentation/doc/2 classes/Objects.SoundSource.html +++ b/Documentation/doc/2 classes/Objects.SoundSource.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.Static.html b/Documentation/doc/2 classes/Objects.Static.html index f3922a16d..0a66cb292 100644 --- a/Documentation/doc/2 classes/Objects.Static.html +++ b/Documentation/doc/2 classes/Objects.Static.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Objects.Volume.html b/Documentation/doc/2 classes/Objects.Volume.html index b5bb86cf7..5132490d2 100644 --- a/Documentation/doc/2 classes/Objects.Volume.html +++ b/Documentation/doc/2 classes/Objects.Volume.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/Strings.DisplayString.html b/Documentation/doc/2 classes/Strings.DisplayString.html index 772d7020c..3c99ac6e6 100644 --- a/Documentation/doc/2 classes/Strings.DisplayString.html +++ b/Documentation/doc/2 classes/Strings.DisplayString.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/2 classes/View.DisplaySprite.html b/Documentation/doc/2 classes/View.DisplaySprite.html index de9964804..007df1615 100644 --- a/Documentation/doc/2 classes/View.DisplaySprite.html +++ b/Documentation/doc/2 classes/View.DisplaySprite.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/3 primitive classes/Color.html b/Documentation/doc/3 primitive classes/Color.html index f9804e224..70f66f9cb 100644 --- a/Documentation/doc/3 primitive classes/Color.html +++ b/Documentation/doc/3 primitive classes/Color.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html index d96fa455f..9fffd646f 100644 --- a/Documentation/doc/3 primitive classes/Rotation.html +++ b/Documentation/doc/3 primitive classes/Rotation.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/3 primitive classes/Vec2.html b/Documentation/doc/3 primitive classes/Vec2.html index 1bf541b4b..19e0c140a 100644 --- a/Documentation/doc/3 primitive classes/Vec2.html +++ b/Documentation/doc/3 primitive classes/Vec2.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/3 primitive classes/Vec3.html b/Documentation/doc/3 primitive classes/Vec3.html index e13784e67..0bcf33289 100644 --- a/Documentation/doc/3 primitive classes/Vec3.html +++ b/Documentation/doc/3 primitive classes/Vec3.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Effects.BlendID.html b/Documentation/doc/4 enums/Effects.BlendID.html index 666b88507..98f3570f3 100644 --- a/Documentation/doc/4 enums/Effects.BlendID.html +++ b/Documentation/doc/4 enums/Effects.BlendID.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Effects.EffectID.html b/Documentation/doc/4 enums/Effects.EffectID.html index b7b143237..bfc50b3f9 100644 --- a/Documentation/doc/4 enums/Effects.EffectID.html +++ b/Documentation/doc/4 enums/Effects.EffectID.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Flow.FreezeMode.html b/Documentation/doc/4 enums/Flow.FreezeMode.html index 92a7065e5..e75654056 100644 --- a/Documentation/doc/4 enums/Flow.FreezeMode.html +++ b/Documentation/doc/4 enums/Flow.FreezeMode.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Flow.GameStatus.html b/Documentation/doc/4 enums/Flow.GameStatus.html index 17b807507..7ea8f4369 100644 --- a/Documentation/doc/4 enums/Flow.GameStatus.html +++ b/Documentation/doc/4 enums/Flow.GameStatus.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Input.ActionID.html b/Documentation/doc/4 enums/Input.ActionID.html index 64728c7b9..fefe3d0ca 100644 --- a/Documentation/doc/4 enums/Input.ActionID.html +++ b/Documentation/doc/4 enums/Input.ActionID.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Objects.AmmoType.html b/Documentation/doc/4 enums/Objects.AmmoType.html index 343f9eb6d..c282dc23a 100644 --- a/Documentation/doc/4 enums/Objects.AmmoType.html +++ b/Documentation/doc/4 enums/Objects.AmmoType.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Objects.MoveableStatus.html b/Documentation/doc/4 enums/Objects.MoveableStatus.html index 0f2bcfb6d..23f1929c1 100644 --- a/Documentation/doc/4 enums/Objects.MoveableStatus.html +++ b/Documentation/doc/4 enums/Objects.MoveableStatus.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index f2c4fbe6a..036557551 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Objects.RoomFlagID.html b/Documentation/doc/4 enums/Objects.RoomFlagID.html index 558b32baf..ebf8269f3 100644 --- a/Documentation/doc/4 enums/Objects.RoomFlagID.html +++ b/Documentation/doc/4 enums/Objects.RoomFlagID.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Objects.RoomReverb.html b/Documentation/doc/4 enums/Objects.RoomReverb.html index ede3c289c..c69e1f604 100644 --- a/Documentation/doc/4 enums/Objects.RoomReverb.html +++ b/Documentation/doc/4 enums/Objects.RoomReverb.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Sound.SoundTrackType.html b/Documentation/doc/4 enums/Sound.SoundTrackType.html index 760b15e1e..a5f2b92fa 100644 --- a/Documentation/doc/4 enums/Sound.SoundTrackType.html +++ b/Documentation/doc/4 enums/Sound.SoundTrackType.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Strings.DisplayStringOption.html b/Documentation/doc/4 enums/Strings.DisplayStringOption.html index efbcbb65a..5cfa898a9 100644 --- a/Documentation/doc/4 enums/Strings.DisplayStringOption.html +++ b/Documentation/doc/4 enums/Strings.DisplayStringOption.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/Util.LogLevel.html b/Documentation/doc/4 enums/Util.LogLevel.html index 54f080e45..010d42586 100644 --- a/Documentation/doc/4 enums/Util.LogLevel.html +++ b/Documentation/doc/4 enums/Util.LogLevel.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/View.AlignMode.html b/Documentation/doc/4 enums/View.AlignMode.html index 79ad956c6..6a245e55a 100644 --- a/Documentation/doc/4 enums/View.AlignMode.html +++ b/Documentation/doc/4 enums/View.AlignMode.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/View.CameraType.html b/Documentation/doc/4 enums/View.CameraType.html index eb9d9e4ba..2f990b53e 100644 --- a/Documentation/doc/4 enums/View.CameraType.html +++ b/Documentation/doc/4 enums/View.CameraType.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/View.PostProcessMode.html b/Documentation/doc/4 enums/View.PostProcessMode.html index 70142411b..4dd42d22e 100644 --- a/Documentation/doc/4 enums/View.PostProcessMode.html +++ b/Documentation/doc/4 enums/View.PostProcessMode.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/4 enums/View.ScaleMode.html b/Documentation/doc/4 enums/View.ScaleMode.html index cc0d89c80..d2614d69e 100644 --- a/Documentation/doc/4 enums/View.ScaleMode.html +++ b/Documentation/doc/4 enums/View.ScaleMode.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/5 lua utility modules/EventSequence.html b/Documentation/doc/5 lua utility modules/EventSequence.html index 263a0fc24..bd1db025a 100644 --- a/Documentation/doc/5 lua utility modules/EventSequence.html +++ b/Documentation/doc/5 lua utility modules/EventSequence.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/5 lua utility modules/Timer.html b/Documentation/doc/5 lua utility modules/Timer.html index 2c4d8d28d..e8104edd2 100644 --- a/Documentation/doc/5 lua utility modules/Timer.html +++ b/Documentation/doc/5 lua utility modules/Timer.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API diff --git a/Documentation/doc/index.html b/Documentation/doc/index.html index 01ed72090..1e432e80d 100644 --- a/Documentation/doc/index.html +++ b/Documentation/doc/index.html @@ -3,7 +3,7 @@ - TombEngine 1.6 (Developer Version) Lua API + TombEngine 1.6 Lua API @@ -104,7 +104,7 @@
                                                                                                          -

                                                                                                          TombEngine 1.6 (Developer Version) scripting interface

                                                                                                          +

                                                                                                          TombEngine 1.6 scripting interface

                                                                                                          Welcome to the TombEngine scripting API. This is a work in progress and some information might be wrong or outdated. Please also note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse.

                                                                                                          At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on the TombEngine website.

                                                                                                          diff --git a/TombEngine/version.h b/TombEngine/version.h index efc0f13eb..27241918c 100644 --- a/TombEngine/version.h +++ b/TombEngine/version.h @@ -6,11 +6,11 @@ #define TE_REVISION_NUMBER 0 #define TEN_MAJOR_VERSION 1 -#define TEN_MINOR_VERSION 5 +#define TEN_MINOR_VERSION 6 #define TEN_BUILD_NUMBER 0 -#define TEN_REVISION_NUMBER 6 +#define TEN_REVISION_NUMBER 0 -#define TEST_BUILD 1 +#define TEST_BUILD 0 #define TOSTR(x) #x #define MAKE_VERSION_STRING(major, minor, build, revision) TOSTR(major) "." TOSTR(minor) "." TOSTR(build) "." TOSTR(revision) From 71f6cf5b64b43bf45f6e374db1d4e6f07019ac39 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:28:31 +0100 Subject: [PATCH 104/112] Remove boulder death from special death anims, as it's causing troubles --- TombEngine/Game/camera.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index b31f9e9b9..1a79ecfc8 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -1072,8 +1072,7 @@ static bool CalculateDeathCamera(const ItemInfo& item) return true; // Special death animations. - if (item.Animation.AnimNumber == LA_SPIKE_DEATH || - item.Animation.AnimNumber == LA_BOULDER_DEATH || + if (item.Animation.AnimNumber == LA_SPIKE_DEATH || item.Animation.AnimNumber == LA_TRAIN_OVERBOARD_DEATH) { return true; From 63277cc29d4b06c80de1145b28d8296c59650b7e Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:40:35 +0100 Subject: [PATCH 105/112] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e427ca78..c6e5ad426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor): * Added support for transparency value in DisplayString class. * Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning. * Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry. -* Fixed DisplayString class not supporting empty lines in multiline strings. +* Fixed DisplayString class not supporting some Unicode characters and empty lines in multiline strings. * Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions. * Fixed Util.HasLineOfSight() not taking static meshes into consideration. * Fixed collision callbacks not properly clearing after leveljump. From e093b07c8c3d4961810c13b455145f2b0bcf4ba8 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:24:40 +0100 Subject: [PATCH 106/112] Further optimizations to lensflare code --- TombEngine/Game/room.h | 24 ++++---- TombEngine/Objects/Effects/LensFlare.cpp | 60 ++++++++++--------- .../Objects/TR5/Emitter/tr5_smoke_emitter.cpp | 2 +- TombEngine/Renderer/RendererDraw.cpp | 2 +- .../Internal/TEN/Objects/Room/RoomFlags.h | 2 +- 5 files changed, 46 insertions(+), 44 deletions(-) diff --git a/TombEngine/Game/room.h b/TombEngine/Game/room.h index 673c028e2..eec49f807 100644 --- a/TombEngine/Game/room.h +++ b/TombEngine/Game/room.h @@ -21,18 +21,18 @@ extern int FlipMap[MAX_FLIPMAP]; enum RoomEnvFlags { - ENV_FLAG_WATER = (1 << 0), - ENV_FLAG_SWAMP = (1 << 2), - ENV_FLAG_OUTSIDE = (1 << 3), - ENV_FLAG_DYNAMIC_LIT = (1 << 4), - ENV_FLAG_WIND = (1 << 5), - ENV_FLAG_NOT_NEAR_OUTSIDE = (1 << 6), - ENV_FLAG_NO_LENSFLARE = (1 << 7), - ENV_FLAG_MIST = (1 << 8), - ENV_FLAG_CAUSTICS = (1 << 9), - ENV_FLAG_UNKNOWN3 = (1 << 10), - ENV_FLAG_DAMAGE = (1 << 11), - ENV_FLAG_COLD = (1 << 12) + ENV_FLAG_WATER = (1 << 0), + ENV_FLAG_SWAMP = (1 << 2), + ENV_FLAG_SKYBOX = (1 << 3), + ENV_FLAG_DYNAMIC_LIT = (1 << 4), + ENV_FLAG_WIND = (1 << 5), + ENV_FLAG_NOT_NEAR_SKYBOX = (1 << 6), + ENV_FLAG_NO_LENSFLARE = (1 << 7), + ENV_FLAG_MIST = (1 << 8), + ENV_FLAG_CAUSTICS = (1 << 9), + ENV_FLAG_UNKNOWN3 = (1 << 10), + ENV_FLAG_DAMAGE = (1 << 11), + ENV_FLAG_COLD = (1 << 12) }; enum StaticMeshFlags : short diff --git a/TombEngine/Objects/Effects/LensFlare.cpp b/TombEngine/Objects/Effects/LensFlare.cpp index 313bce047..e6a8c9d95 100644 --- a/TombEngine/Objects/Effects/LensFlare.cpp +++ b/TombEngine/Objects/Effects/LensFlare.cpp @@ -55,14 +55,17 @@ namespace TEN::Entities::Effects if (forward.Dot(pos - cameraPos) < 0.0f) return; - auto lensFlarePos = pos; bool isGlobal = roomNumber == NO_VALUE; + bool isVisible = true; + + // Don't draw global lensflare if camera is in a room with no lensflare flag set. + if (isGlobal && TestEnvironment(ENV_FLAG_NO_LENSFLARE, Camera.pos.RoomNumber)) + isVisible = false; + + auto lensFlarePos = pos; if (isGlobal) { - if (TestEnvironment(ENV_FLAG_NO_LENSFLARE, Camera.pos.RoomNumber)) - return; - // Gradually move lensflare position to a nearest point within closest room. AdjustLensflarePosition(lensFlarePos, cameraPos, 8.0f, BLOCK(256)); AdjustLensflarePosition(lensFlarePos, cameraPos, 4.0f, BLOCK(32)); @@ -82,47 +85,46 @@ namespace TEN::Entities::Effects AdjustLensflarePosition(lensFlarePos, cameraPos, 2.0f, BLOCK(32)); narrowingCycleCount++; } + + // Don't draw global lensflare, if not in room or in rooms where skybox is not visible. + if (roomNumber == NO_VALUE || TestEnvironment(ENV_FLAG_NOT_NEAR_SKYBOX, roomNumber)) + isVisible = false; } else { - float dist = Vector3::Distance(lensFlarePos, cameraPos); - if (dist > BLOCK(32)) - return; + if (Vector3::Distance(lensFlarePos, cameraPos) > BLOCK(64)) + isVisible = false; } - bool isVisible = false; - if (roomNumber != NO_VALUE) + // Do occlusion tests only if lensflare passed the previous checks. + if (isVisible) { - // Don't draw lensflares in rooms which aren't outside or adjacent to outside rooms. - if (TestEnvironment(ENV_FLAG_NOT_NEAR_OUTSIDE, roomNumber) || !isGlobal) - { - auto origin = GameVector(lensFlarePos, roomNumber); - auto target = Camera.pos; - auto distance = Vector3::Distance(origin.ToVector3(), target.ToVector3()); + auto origin = GameVector(lensFlarePos, roomNumber); + auto target = Camera.pos; + auto distance = Vector3::Distance(origin.ToVector3(), target.ToVector3()); - // Check room occlusion. - isVisible = LOS(&origin, &target); + // Check room occlusion. + isVisible = LOS(&origin, &target); - MESH_INFO* mesh = nullptr; - auto pointOfContact = Vector3i(); + MESH_INFO* mesh = nullptr; + auto pointOfContact = Vector3i(); - // Check occlusion for all static meshes and moveables but player. - bool collided = isVisible && ObjectOnLOS2(&origin, &target, &pointOfContact, &mesh) != NO_LOS_ITEM; - if (collided && Vector3::Distance(pointOfContact.ToVector3(), origin.ToVector3()) < distance) - isVisible = false; + // Check occlusion for all static meshes and moveables but player. + bool collided = isVisible && ObjectOnLOS2(&origin, &target, &pointOfContact, &mesh) != NO_LOS_ITEM; + if (collided && Vector3::Distance(pointOfContact.ToVector3(), origin.ToVector3()) < distance) + isVisible = false; - // Check occlusion only for player. - collided = isVisible && ObjectOnLOS2(&origin, &target, &pointOfContact, nullptr, ID_LARA) != NO_LOS_ITEM; - if (collided && Vector3::Distance(pointOfContact.ToVector3(), origin.ToVector3()) < distance) - isVisible = false; - } + // Check occlusion only for player. + collided = isVisible && ObjectOnLOS2(&origin, &target, &pointOfContact, nullptr, ID_LARA) != NO_LOS_ITEM; + if (collided && Vector3::Distance(pointOfContact.ToVector3(), origin.ToVector3()) < distance) + isVisible = false; } // Fade in/out lensflares depending on their visibility. UpdateLensFlareIntensity(isVisible, intensity); // Lensflare is completely invisible. - if (!isVisible && !isGlobal && intensity == 0.0f) + if (!isVisible && intensity == 0.0f) return; // Generate slight shimmer. diff --git a/TombEngine/Objects/TR5/Emitter/tr5_smoke_emitter.cpp b/TombEngine/Objects/TR5/Emitter/tr5_smoke_emitter.cpp index 5a432a1e7..2b54f9584 100644 --- a/TombEngine/Objects/TR5/Emitter/tr5_smoke_emitter.cpp +++ b/TombEngine/Objects/TR5/Emitter/tr5_smoke_emitter.cpp @@ -212,7 +212,7 @@ namespace TEN::Effects::SmokeEmitter part.friction = 3; part.flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; - if (TestEnvironment(RoomEnvFlags::ENV_FLAG_OUTSIDE, item.RoomNumber)) + if (TestEnvironment(RoomEnvFlags::ENV_FLAG_SKYBOX, item.RoomNumber)) part.flags |= SP_WIND; part.rotAng = Random::GenerateInt(0, 4095); diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp index b7c9e5456..019b44ab3 100644 --- a/TombEngine/Renderer/RendererDraw.cpp +++ b/TombEngine/Renderer/RendererDraw.cpp @@ -2730,7 +2730,7 @@ namespace TEN::Renderer for (int k = 0; k < renderView.RoomsToDraw.size(); k++) { const auto& nativeRoom = g_Level.Rooms[renderView.RoomsToDraw[k]->RoomNumber]; - if (nativeRoom.flags & ENV_FLAG_OUTSIDE) + if (nativeRoom.flags & ENV_FLAG_SKYBOX) { anyOutsideRooms = true; break; diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Room/RoomFlags.h b/TombEngine/Scripting/Internal/TEN/Objects/Room/RoomFlags.h index 252b591bf..afebe0f45 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Room/RoomFlags.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/Room/RoomFlags.h @@ -33,7 +33,7 @@ static const std::unordered_map ROOM_FLAG_IDS { { "WATER", RoomEnvFlags::ENV_FLAG_WATER }, { "QUICKSAND", RoomEnvFlags::ENV_FLAG_SWAMP }, - { "SKYBOX", RoomEnvFlags::ENV_FLAG_OUTSIDE }, + { "SKYBOX", RoomEnvFlags::ENV_FLAG_SKYBOX }, { "WIND", RoomEnvFlags::ENV_FLAG_WIND }, { "COLD", RoomEnvFlags::ENV_FLAG_COLD }, { "DAMAGE", RoomEnvFlags::ENV_FLAG_DAMAGE }, From df4a6640209b04fb3cd798e321b6d6b8f6d05019 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:24:51 +0100 Subject: [PATCH 107/112] Remove unused block --- TombEngine/Game/effects/tomb4fx.cpp | 87 ----------------------------- 1 file changed, 87 deletions(-) diff --git a/TombEngine/Game/effects/tomb4fx.cpp b/TombEngine/Game/effects/tomb4fx.cpp index cf83fe7bf..ef50c0418 100644 --- a/TombEngine/Game/effects/tomb4fx.cpp +++ b/TombEngine/Game/effects/tomb4fx.cpp @@ -1474,93 +1474,6 @@ void TriggerExplosionBubble(int x, int y, int z, short roomNumber) } } -/*void TriggerExplosionSmokeEnd(int x, int y, int z, int unk) -{ - auto* spark = GetFreeParticle(); - - spark->on = 1; - if (unk) - { - spark->sR = 0; - spark->sG = 0; - spark->sB = 0; - spark->dR = 192; - spark->dG = 192; - spark->dB = 208; - } - else - { - spark->dR = 64; - spark->sR = 144; - spark->sG = 144; - spark->sB = 144; - spark->dG = 64; - spark->dB = 64; - } - - spark->colFadeSpeed = 8; - spark->fadeToBlack = 64; - spark->life = spark->sLife = (GetRandomControl() & 0x1F) + 96; - - if (unk) - spark->blendMode = BlendMode::Additive; - else - spark->blendMode = 3; - - spark->x = (GetRandomControl() & 0x1F) + x - 16; - spark->y = (GetRandomControl() & 0x1F) + y - 16; - spark->z = (GetRandomControl() & 0x1F) + z - 16; - spark->xVel = ((GetRandomControl() & 0xFFF) - 2048) >> 2; - spark->yVel = (GetRandomControl() & 0xFF) - 128; - spark->zVel = ((GetRandomControl() & 0xFFF) - 2048) >> 2; - - if (unk) - { - spark->friction = 20; - spark->yVel >>= 4; - spark->y += 32; - } - else - spark->friction = 6; - - spark->flags = 538; - spark->rotAng = GetRandomControl() & 0xFFF; - - if (GetRandomControl() & 1) - spark->rotAdd = -((GetRandomControl() & 0xF) + 16); - else - spark->rotAdd = (GetRandomControl() & 0xF) + 16; - spark->scalar = 3; - - if (unk) - { - spark->maxYvel = 0; - spark->gravity = 0; - } - else - { - spark->gravity = -3 - (GetRandomControl() & 3); - spark->maxYvel = -4 - (GetRandomControl() & 3); - } - - int size = (GetRandomControl() & 0x1F) + 128; - spark->dSize = size; - spark->sSize = size >> 2; - spark->size = size >> 2; -} -*/ -/*void DrawLensFlares(ItemInfo* item) -{ - GameVector pos; - - pos.x = item->pos.Position.x; - pos.y = item->pos.Position.y; - pos.z = item->pos.Position.z; - pos.roomNumber = item->roomNumber; - - SetUpLensFlare(0, 0, 0, &pos); -}*/ - void TriggerFenceSparks(int x, int y, int z, int kill, int crane) { auto* spark = GetFreeParticle(); From 546a72c5c8b98d59d5efe6e8f894aba41744b519 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:40:25 +0100 Subject: [PATCH 108/112] Implement OCB for lensflare objects which specifies falloff in sectors --- TombEngine/Objects/Effects/LensFlare.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/TombEngine/Objects/Effects/LensFlare.cpp b/TombEngine/Objects/Effects/LensFlare.cpp index e6a8c9d95..5d88325e7 100644 --- a/TombEngine/Objects/Effects/LensFlare.cpp +++ b/TombEngine/Objects/Effects/LensFlare.cpp @@ -163,12 +163,27 @@ namespace TEN::Entities::Effects { auto& item = g_Level.Items[itemNumber]; - if (TriggerActive(&item)) + if (!TriggerActive(&item)) + return; + + auto color = item.Model.Color; + + // If OCB is set, it specifies distance in blocks, after which flare starts to fadeout. + if (item.TriggerFlags > 0) { - float currentIntensity = (float)item.ItemFlags[0] / 100.0f; - SetupLensFlare(item.Pose.Position.ToVector3(), item.RoomNumber, item.Model.Color, currentIntensity, SPRITE_TYPES::SPR_LENS_FLARE_3); - item.ItemFlags[0] = (short)(currentIntensity * 100.0f); + float falloff = (float)item.TriggerFlags * BLOCK(1); + float distance = Vector3i::Distance(item.Pose.Position, Camera.pos.ToVector3i()); + if (distance > falloff) + { + float fadeMultiplier = std::max((1.0f - ((distance - falloff) / falloff)), 0.0f); + color *= fadeMultiplier; + } } + + // Intensity value can be modified inside lensflare setup function. + float currentIntensity = (float)item.ItemFlags[0] / 100.0f; + SetupLensFlare(item.Pose.Position.ToVector3(), item.RoomNumber, color, currentIntensity, SPRITE_TYPES::SPR_LENS_FLARE_3); + item.ItemFlags[0] = (short)(currentIntensity * 100.0f); } void ClearLensFlares() From a7a28285baa601e5e435bf03efb3c89f578a6529 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:53:29 +0100 Subject: [PATCH 109/112] Move default radius discard to item control function --- TombEngine/Objects/Effects/LensFlare.cpp | 27 ++++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/TombEngine/Objects/Effects/LensFlare.cpp b/TombEngine/Objects/Effects/LensFlare.cpp index 5d88325e7..733e2a27a 100644 --- a/TombEngine/Objects/Effects/LensFlare.cpp +++ b/TombEngine/Objects/Effects/LensFlare.cpp @@ -15,6 +15,7 @@ namespace TEN::Entities::Effects constexpr float MAX_INTENSITY = 1.0f; // Maximum intensity constexpr float FADE_SPEED = 0.10f; // Speed of fade-in/out per frame constexpr float SHIMMER_STRENGTH = 0.15f; // Max shimmer amplitude + constexpr float DEFAULT_FALLOFF_RADIUS = BLOCK(64); float GlobalLensFlareIntensity = 0; @@ -90,11 +91,6 @@ namespace TEN::Entities::Effects if (roomNumber == NO_VALUE || TestEnvironment(ENV_FLAG_NOT_NEAR_SKYBOX, roomNumber)) isVisible = false; } - else - { - if (Vector3::Distance(lensFlarePos, cameraPos) > BLOCK(64)) - isVisible = false; - } // Do occlusion tests only if lensflare passed the previous checks. if (isVisible) @@ -168,16 +164,19 @@ namespace TEN::Entities::Effects auto color = item.Model.Color; - // If OCB is set, it specifies distance in blocks, after which flare starts to fadeout. - if (item.TriggerFlags > 0) + // If OCB is set, it specifies radius in blocks, after which flare starts to fadeout. + float radius = (item.TriggerFlags > 0) ? (float)(item.TriggerFlags * BLOCK(1)) : DEFAULT_FALLOFF_RADIUS; + float distance = Vector3i::Distance(item.Pose.Position, Camera.pos.ToVector3i()); + + if (distance > radius) { - float falloff = (float)item.TriggerFlags * BLOCK(1); - float distance = Vector3i::Distance(item.Pose.Position, Camera.pos.ToVector3i()); - if (distance > falloff) - { - float fadeMultiplier = std::max((1.0f - ((distance - falloff) / falloff)), 0.0f); - color *= fadeMultiplier; - } + float fadeMultiplier = std::max((1.0f - ((distance - radius) / radius)), 0.0f); + + // Discard flare, if it is out of falloff sphere radius. + if (fadeMultiplier <= 0.0f) + return; + + color *= fadeMultiplier; } // Intensity value can be modified inside lensflare setup function. From 9f8ea9e13c558484e980bae2bf52dc93e02bde8d Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 23 Nov 2024 02:40:13 +0100 Subject: [PATCH 110/112] Revert "Changed/Fixed enemy jeep behaviour (#1395)" This reverts commit 267fecb063e6f9b6437eaafb044d3f978ea0ae4b. --- TombEngine/Game/Setup.h | 9 +- TombEngine/Game/collision/collide_item.cpp | 5 +- TombEngine/Game/control/box.cpp | 154 ++-- TombEngine/Game/control/box.h | 17 +- TombEngine/Game/control/lot.cpp | 7 - TombEngine/Objects/TR3/Entity/FishSwarm.cpp | 2 +- TombEngine/Objects/TR3/Entity/SophiaLeigh.cpp | 2 +- TombEngine/Objects/TR4/Entity/tr4_baddy.cpp | 2 +- .../Objects/TR4/Entity/tr4_enemy_jeep.cpp | 692 ++++++++---------- .../Objects/TR4/Entity/tr4_enemy_jeep.h | 3 +- TombEngine/Objects/TR4/tr4_objects.cpp | 8 +- 11 files changed, 387 insertions(+), 514 deletions(-) diff --git a/TombEngine/Game/Setup.h b/TombEngine/Game/Setup.h index 37445cb14..ee0413b56 100644 --- a/TombEngine/Game/Setup.h +++ b/TombEngine/Game/Setup.h @@ -37,11 +37,10 @@ enum class LotType HumanPlusJump, HumanPlusJumpAndMonkey, Flyer, - Blockable, // For large creatures such as trex and shiva. - Spider, // Only 2 block vault allowed. - Ape, // Only 2 block vault allowed. - SnowmobileGun, // Only 1 block vault allowed and 4 block drop max. - EnemyJeep + Blockable, // For large creatures such as trex and shiva. + Spider, // Only 2 block vault allowed. + Ape, // Only 2 block vault allowed. + SnowmobileGun // Only 1 block vault allowed and 4 block drop max. }; enum class HitEffect diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 1ac90f366..7970ec5dc 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -1841,10 +1841,9 @@ void DoObjectCollision(ItemInfo* item, CollisionInfo* coll) if (linkItem.HitPoints <= 0 || linkItem.HitPoints == NOT_TARGETABLE) continue; - if (isHarmless || abs(item->Animation.Velocity.z) < VEHICLE_COLLISION_TERMINAL_VELOCITY || - object.damageType == DamageMode::None) + if (isHarmless || abs(item->Animation.Velocity.z) < VEHICLE_COLLISION_TERMINAL_VELOCITY) { - // If vehicle is harmless, enemy is non-damageable, or speed is too low, push enemy. + // If vehicle is harmless or speed is too low, just push enemy. ItemPushItem(&linkItem, item, coll, false, 0); continue; } diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index 9f8d05098..91ecaf960 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -1297,15 +1297,11 @@ void GetAITarget(CreatureInfo* creature) { auto* enemy = creature->Enemy; - int enemyObjectID = 0; + short enemyObjectNumber; if (enemy) - { - enemyObjectID = enemy->ObjectNumber; - } + enemyObjectNumber = enemy->ObjectNumber; else - { - enemyObjectID = NO_VALUE; - } + enemyObjectNumber = NO_VALUE; auto* item = &g_Level.Items[creature->ItemNumber]; @@ -1333,12 +1329,12 @@ void GetAITarget(CreatureInfo* creature) } else if (!creature->Patrol) { - if (enemyObjectID != ID_AI_PATROL1) - FindAITargetObject(*item, ID_AI_PATROL1); + if (enemyObjectNumber != ID_AI_PATROL1) + FindAITargetObject(creature, ID_AI_PATROL1); } - else if (enemyObjectID != ID_AI_PATROL2) + else if (enemyObjectNumber != ID_AI_PATROL2) { - FindAITargetObject(*item, ID_AI_PATROL2); + FindAITargetObject(creature, ID_AI_PATROL2); } else if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < REACHED_GOAL_RADIUS && abs(enemy->Pose.Position.y - item->Pose.Position.y) < REACHED_GOAL_RADIUS && @@ -1354,8 +1350,8 @@ void GetAITarget(CreatureInfo* creature) // First if was removed probably after TR3 and was it used by monkeys? /*if (!(item->aiBits & MODIFY) && !creature->hurtByLara) creature->enemy = LaraItem; - else*/ if (enemyObjectID != ID_AI_AMBUSH) - FindAITargetObject(*item, ID_AI_AMBUSH); + else*/ if (enemyObjectNumber != ID_AI_AMBUSH) + FindAITargetObject(creature, ID_AI_AMBUSH); /*else if (item->objectNumber == ID_MONKEY) return;*/ else if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < REACHED_GOAL_RADIUS && @@ -1366,7 +1362,6 @@ void GetAITarget(CreatureInfo* creature) creature->ReachedGoal = true; creature->Enemy = LaraItem; item->AIBits &= ~(AMBUSH /* | MODIFY*/); - if (item->AIBits != MODIFY) { item->AIBits |= GUARD; @@ -1386,9 +1381,9 @@ void GetAITarget(CreatureInfo* creature) { item->AIBits &= ~FOLLOW; } - else if (enemyObjectID != ID_AI_FOLLOW) + else if (enemyObjectNumber != ID_AI_FOLLOW) { - FindAITargetObject(*item, ID_AI_FOLLOW); + FindAITargetObject(creature, ID_AI_FOLLOW); } else if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < REACHED_GOAL_RADIUS && abs(enemy->Pose.Position.y - item->Pose.Position.y) < REACHED_GOAL_RADIUS && @@ -1398,18 +1393,17 @@ void GetAITarget(CreatureInfo* creature) item->AIBits &= ~FOLLOW; } } - - /*else if (item->ObjectNumber == ID_MONKEY && item->CarriedItem == NO_VALUE) + /*else if (item->objectNumber == ID_MONKEY && item->carriedItem == NO_VALUE) { - if (item->AIBits != MODIFY) + if (item->aiBits != MODIFY) { - if (enemyObjectID != ID_SMALLMEDI_ITEM) - FindAITargetObject(*item, ID_SMALLMEDI_ITEM); + if (enemyObjectNumber != ID_SMALLMEDI_ITEM) + FindAITargetObject(creature, ID_SMALLMEDI_ITEM); } else { - if (enemyObjectID != ID_KEY_ITEM4) - FindAITargetObject(*item, ID_KEY_ITEM4); + if (enemyObjectNumber != ID_KEY_ITEM4) + FindAITargetObject(creature, ID_KEY_ITEM4); } }*/ } @@ -1438,101 +1432,69 @@ void FindAITarget(CreatureInfo* creature, short objectNumber) } } -void FindAITargetObject(ItemInfo& item, GAME_OBJECT_ID objectID, std::optional ocb, std::optional checkSameZone) +void FindAITargetObject(CreatureInfo* creature, int objectNumber) { - auto& creature = *GetCreatureInfo(&item); + const auto& item = g_Level.Items[creature->ItemNumber]; - auto data = AITargetData{}; - data.CheckDistance = false; - data.CheckOcb = ocb.has_value(); - data.ObjectID = objectID; - data.Ocb = ocb.value_or(item.ItemFlags[3]); - data.CheckSameZone = checkSameZone.value_or(true); - - if (FindAITargetObject(item, data)) - { - *creature.AITarget = data.FoundItem; - creature.Enemy = creature.AITarget; - } + FindAITargetObject(creature, objectNumber, item.ItemFlags[3], true); } -bool FindAITargetObject(ItemInfo& item, AITargetData& data) +void FindAITargetObject(CreatureInfo* creature, int objectNumber, int ocb, bool checkSameZone) { + auto& item = g_Level.Items[creature->ItemNumber]; + if (g_Level.AIObjects.empty()) - return false; + return; - auto& creature = *GetCreatureInfo(&item); + AI_OBJECT* foundObject = nullptr; - const AI_OBJECT* foundAIObject = nullptr; - - for (const auto& aiObject : g_Level.AIObjects) + for (auto& aiObject : g_Level.AIObjects) { - // Check if object IDs match. - if (aiObject.objectNumber != data.ObjectID) - continue; - - // Check if room is valid. - if (aiObject.roomNumber == NO_VALUE) - continue; - - // Check if distance is valid. - if (data.CheckDistance) + if (aiObject.objectNumber == objectNumber && + aiObject.triggerFlags == ocb && + aiObject.roomNumber != NO_VALUE) { - if (Vector3i::Distance(item.Pose.Position, aiObject.pos.Position) > data.DistanceMax) - continue; - } - - // Check if OCBs match (useful for pathfinding). - if (data.CheckOcb) - { - if (aiObject.triggerFlags != data.Ocb) - continue; - } - - // Check if zone IDs match. - if (data.CheckSameZone) - { - int* zone = g_Level.Zones[(int)creature.LOT.Zone][(int)FlipStatus].data(); + int* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data(); auto* room = &g_Level.Rooms[item.RoomNumber]; - // NOTE: Avoid changing box ID of item or AI item so a local variable isn't required when searching for AI object near it. - int boxID = GetSector(room, item.Pose.Position.x - room->Position.x, item.Pose.Position.z - room->Position.z)->PathfindingBoxID; + item.BoxNumber = GetSector(room, item.Pose.Position.x - room->Position.x, item.Pose.Position.z - room->Position.z)->PathfindingBoxID; room = &g_Level.Rooms[aiObject.roomNumber]; - int aiBoxID = GetSector(room, aiObject.pos.Position.x - room->Position.x, aiObject.pos.Position.z - room->Position.z)->PathfindingBoxID; + aiObject.boxNumber = GetSector(room, aiObject.pos.Position.x - room->Position.x, aiObject.pos.Position.z - room->Position.z)->PathfindingBoxID; - // Box is invalid or zones don't match; continue. - if (boxID == NO_VALUE || aiBoxID == NO_VALUE) - continue; + if (item.BoxNumber == NO_VALUE || aiObject.boxNumber == NO_VALUE) + return; - // Zone is invalid; continue. - if (zone[boxID] != zone[aiBoxID]) - continue; + if (checkSameZone && (zone[item.BoxNumber] != zone[aiObject.boxNumber])) + return; + + // Don't check for same zone. Needed for Sophia Leigh. + foundObject = &aiObject; } - - // HACK: Don't check for matching zone. Needed for Sophia Leigh. - foundAIObject = &aiObject; } - if (foundAIObject == nullptr) - return false; + if (foundObject == nullptr) + return; - auto aiItem = ItemInfo{}; - aiItem.ObjectNumber = foundAIObject->objectNumber; - aiItem.RoomNumber = foundAIObject->roomNumber; - aiItem.Pose.Position = foundAIObject->pos.Position; - aiItem.Pose.Orientation.y = foundAIObject->pos.Orientation.y; - aiItem.Flags = foundAIObject->flags; - aiItem.TriggerFlags = foundAIObject->triggerFlags; - aiItem.BoxNumber = foundAIObject->boxNumber; + auto& aiItem = *creature->AITarget; - if (!(aiItem.Flags & IFLAG_TRIGGERED)) + creature->Enemy = &aiItem; + + aiItem.ObjectNumber = foundObject->objectNumber; + aiItem.RoomNumber = foundObject->roomNumber; + aiItem.Pose.Position = foundObject->pos.Position; + aiItem.Pose.Orientation.y = foundObject->pos.Orientation.y; + aiItem.Flags = foundObject->flags; + aiItem.TriggerFlags = foundObject->triggerFlags; + aiItem.BoxNumber = foundObject->boxNumber; + + if (!(creature->AITarget->Flags & ItemFlags::IFLAG_TRIGGERED)) { - aiItem.Pose.Position.x += CLICK(1) * phd_sin(aiItem.Pose.Orientation.y); - aiItem.Pose.Position.z += CLICK(1) * phd_cos(aiItem.Pose.Orientation.y); - } + float sinY = phd_sin(creature->AITarget->Pose.Orientation.y); + float cosY = phd_cos(creature->AITarget->Pose.Orientation.y); - data.FoundItem = aiItem; - return true; + creature->AITarget->Pose.Position.x += CLICK(1) * sinY; + creature->AITarget->Pose.Position.z += CLICK(1) * cosY; + } } int TargetReachable(ItemInfo* item, ItemInfo* enemy) diff --git a/TombEngine/Game/control/box.h b/TombEngine/Game/control/box.h index 1152ec78b..25396411b 100644 --- a/TombEngine/Game/control/box.h +++ b/TombEngine/Game/control/box.h @@ -83,23 +83,10 @@ constexpr auto CLIP_ALL = (CLIP_LEFT | CLIP_RIGHT | CLIP_TOP | CLIP_BOTTOM); constexpr auto CLIP_SECONDARY = 0x10; -struct AITargetData -{ - ItemInfo FoundItem = {}; - GAME_OBJECT_ID ObjectID = GAME_OBJECT_ID::ID_NO_OBJECT; - float DistanceMax = 0.0f; - int Ocb = NO_VALUE; - - bool CheckDistance = false; - bool CheckSameZone = true; - bool CheckOcb = false; -}; - void GetCreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent); void CreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent); -void FindAITargetObject(ItemInfo& item, GAME_OBJECT_ID objectID, std::optional ocb = std::nullopt, std::optional checkSameZone = std::nullopt); -bool FindAITargetObject(ItemInfo& item, AITargetData& data); - +void FindAITargetObject(CreatureInfo* creature, int objectNumber); +void FindAITargetObject(CreatureInfo* creature, int objectNumber, int ocb, bool checkSameZone = true); void GetAITarget(CreatureInfo* creature); int CreatureVault(short itemNumber, short angle, int vault, int shift); bool MoveCreature3DPos(Pose* fromPose, Pose* toPose, int velocity, short angleDif, int angleAdd); diff --git a/TombEngine/Game/control/lot.cpp b/TombEngine/Game/control/lot.cpp index b1168618d..5983a9b57 100644 --- a/TombEngine/Game/control/lot.cpp +++ b/TombEngine/Game/control/lot.cpp @@ -197,13 +197,6 @@ void InitializeSlot(short itemNumber, bool makeTarget) creature->LOT.Drop = -BLOCK(1); creature->LOT.Zone = ZoneType::Human; break; - - case LotType::EnemyJeep: - creature->LOT.Step = BLOCK(4); - creature->LOT.Drop = -BLOCK(4); - creature->LOT.CanJump = true; - creature->LOT.Zone = ZoneType::Human; - break; } ClearLOT(&creature->LOT); diff --git a/TombEngine/Objects/TR3/Entity/FishSwarm.cpp b/TombEngine/Objects/TR3/Entity/FishSwarm.cpp index ae67854f0..7adb8940a 100644 --- a/TombEngine/Objects/TR3/Entity/FishSwarm.cpp +++ b/TombEngine/Objects/TR3/Entity/FishSwarm.cpp @@ -184,7 +184,7 @@ namespace TEN::Entities::Creatures::TR3 // Follow path. if (item.AIBits && !item.ItemFlags[4]) { - FindAITargetObject(item, ID_AI_FOLLOW, item.ItemFlags[3] + item.ItemFlags[2], false); + FindAITargetObject(&creature, ID_AI_FOLLOW, item.ItemFlags[3] + item.ItemFlags[2], false); if (creature.AITarget->TriggerFlags == (item.ItemFlags[3] + item.ItemFlags[2]) && creature.AITarget->ObjectNumber == ID_AI_FOLLOW) diff --git a/TombEngine/Objects/TR3/Entity/SophiaLeigh.cpp b/TombEngine/Objects/TR3/Entity/SophiaLeigh.cpp index d4ae15027..ea752dae8 100644 --- a/TombEngine/Objects/TR3/Entity/SophiaLeigh.cpp +++ b/TombEngine/Objects/TR3/Entity/SophiaLeigh.cpp @@ -289,7 +289,7 @@ namespace TEN::Entities::Creatures::TR3 // Check the previous and next position of AI object to // allow Sophia to go up or down based on enemy's vertical position. - FindAITargetObject(item, ID_AI_X1, creature->LocationAI, false); + FindAITargetObject(creature, ID_AI_X1, creature->LocationAI, false); if (Vector3i::Distance(item.Pose.Position, creature->Enemy->Pose.Position) < SOPHIALEIGH_REACHED_GOAL_RANGE) { diff --git a/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp b/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp index 001337d53..2b7d6bb87 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp @@ -345,7 +345,7 @@ namespace TEN::Entities::TR4 if (item->TriggerFlags % 1000 > 100) { item->ItemFlags[0] = -80; - FindAITargetObject(*item, ID_AI_X1); + FindAITargetObject(creature, ID_AI_X1); } item->TriggerFlags = 1000 * (item->TriggerFlags / 1000); diff --git a/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp b/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp index 3930f6872..98791d0f5 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp @@ -8,441 +8,375 @@ #include "Game/control/lot.h" #include "Game/control/trigger.h" #include "Game/effects/effects.h" -#include "Game/effects/smoke.h" #include "Game/effects/tomb4fx.h" #include "Game/itemdata/creature_info.h" #include "Game/items.h" #include "Game/Lara/lara.h" -#include "Game/Lara/lara_one_gun.h" #include "Game/misc.h" -#include "Game/people.h" #include "Game/Setup.h" #include "Math/Math.h" -#include "Renderer/Renderer.h" #include "Sound/sound.h" #include "Specific/level.h" using namespace TEN::Collision::Point; -using namespace TEN::Effects::Smoke; using namespace TEN::Math; -using namespace TEN::Renderer; - -/// item.ItemFlags[1] = AI_X2 behaviour -/// item.ItemFlags[2] = Wheel rotation -/// item.ItemFlags[3] = Grenade cooldown -/// item.ItemFlags[4] = Behaviour when idle -/// item.ItemFlags[5] = Wait radius namespace TEN::Entities::TR4 { - enum EnemyJeepAnim + void EnemyJeepLaunchGrenade(ItemInfo* item) { - ENEMY_JEEP_ANIM_MOVE_START = 0, - ENEMY_JEEP_ANIM_MOVE_STOP = 1, - ENEMY_JEEP_ANIM_MOVE = 2, - ENEMY_JEEP_ANIM_TURN_LEFT_START = 3, - ENEMY_JEEP_ANIM_TURN_LEFT = 4, - ENEMY_JEEP_ANIM_TURN_LEFT_END = 5, - ENEMY_JEEP_ANIM_FALL_END = 6, - ENEMY_JEEP_ANIM_FALL_2_STEPS = 7, - ENEMY_JEEP_ANIM_JUMP_2_STEP_PIT = 8, - ENEMY_JEEP_ANIM_IDLE = 9, - ENEMY_JEEP_ANIM_TURN_RIGHT_START = 10, - ENEMY_JEEP_ANIM_TURN_RIGHT = 11, - ENEMY_JEEP_ANIM_TURN_RIGHT_END = 12 - }; + short grenadeItemNumber = CreateItem(); - enum EnemyJeepState - { - ENEMY_JEEP_STATE_IDLE = 0, - ENEMY_JEEP_STATE_MOVE = 1, - ENEMY_JEEP_STATE_STOP = 2, - ENEMY_JEEP_STATE_TURN_LEFT = 3, - ENEMY_JEEP_STATE_TURN_RIGHT = 4, - ENEMY_JEEP_STATE_DROP_LAND = 5, - - // States to allow customization. - - ENEMY_JEEP_STATE_DROP = 6, - ENEMY_JEEP_STATE_JUMP_PIT = 7, - ENEMY_JEEP_STATE_CUSTOM_DROP = 8, - ENEMY_JEEP_STATE_CUSTOM_JUMP_PIT = 9 - }; - - enum EnemyJeepOcb - { - EJ_NO_PLAYER_VEHICLE_REQUIRED = 1 // Starts immediately instead of waiting for player to enter vehicle. - }; - - enum EnemyJeepX2Ocb - { - X2_DROP_GRENADE = 1, - X2_DROP = 2, - X2_JUMP_PIT = 3, - // Need ocb 4 + block distance - // Example: 4 + 1024 = 4 block distance + wait behaviour. - X2_WAIT_UNTIL_PLAYER_NEAR = 4, - X2_DISAPPEAR = 5, - X2_ACTIVATE_HEAVY_TRIGGER = 6, - X2_CUSTOM_DROP = 7, // Another drop step for customization. - X2_CUSTOM_JUMP_PIT = 8, // Another jump steps for customization. - }; - - constexpr auto ENEMY_JEEP_GRENADE_VELOCITY = 32.0f; - constexpr auto ENEMY_JEEP_GRENADE_TIMER = 150; - - constexpr auto ENEMY_JEEP_RIGHT_LIGHT_MESHBITS = 15; - constexpr auto ENEMY_JEEP_LEFT_LIGHT_MESHBITS = 17; - constexpr auto ENEMY_JEEP_GRENADE_COOLDOWN_TIME = 15; - constexpr auto ENEMY_JEEP_PLAYER_IS_NEAR = BLOCK(6.0f); - constexpr auto ENEMY_JEEP_NEAR_X1_NODE_DISTANCE = BLOCK(1); - constexpr auto ENEMY_JEEP_NEAR_X2_NODE_DISTANCE = BLOCK(0.3f); - constexpr auto ENEMY_JEEP_PITCH_MAX = 120.0f; - constexpr auto ENEMY_JEEP_PITCH_WHEEL_SPEED_MULTIPLIER = 12.0f; - - constexpr auto ENEMY_JEEP_WHEEL_LEFTRIGHT_TURN_MINIMUM = ANGLE(12.0f); - - constexpr auto ENEMY_JEEP_CENTER_MESH = 11; - - const auto EnemyJeepGrenadeBite = CreatureBiteInfo(Vector3(0.0f, -640.0f, -768.0f), ENEMY_JEEP_CENTER_MESH); - const auto EnemyJeepRightLightBite = CreatureBiteInfo(Vector3(200.0f, -144.0f, -768.0f), ENEMY_JEEP_CENTER_MESH); - const auto EnemyJeepLeftLightBite = CreatureBiteInfo(Vector3(-200.0f, -144.0f, -768.0f), ENEMY_JEEP_CENTER_MESH); - - static void DrawEnemyJeepLightMesh(ItemInfo& item, bool isBraking) - { - if (isBraking) + if (grenadeItemNumber != NO_VALUE) { - item.MeshBits.Set(ENEMY_JEEP_RIGHT_LIGHT_MESHBITS); - item.MeshBits.Set(ENEMY_JEEP_LEFT_LIGHT_MESHBITS); - } - else - { - item.MeshBits.Clear(ENEMY_JEEP_RIGHT_LIGHT_MESHBITS); - item.MeshBits.Clear(ENEMY_JEEP_LEFT_LIGHT_MESHBITS); - } - } + auto* grenadeItem = &g_Level.Items[grenadeItemNumber]; - static void SpawnEnemyJeepBrakeLights(const ItemInfo& item) - { - constexpr auto COLOR = Color(0.25f, 0.0f, 0.0f); - constexpr auto FALLOFF = 0.04f; + grenadeItem->Model.Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f); + grenadeItem->ObjectNumber = ID_GRENADE; + grenadeItem->RoomNumber = item->RoomNumber; - auto leftPos = GetJointPosition(item, EnemyJeepLeftLightBite).ToVector3(); - TriggerDynamicLight(leftPos, COLOR, FALLOFF); + InitializeItem(grenadeItemNumber); - auto rightPos = GetJointPosition(item, EnemyJeepRightLightBite).ToVector3(); - TriggerDynamicLight(rightPos, COLOR, FALLOFF); - } + grenadeItem->Pose.Orientation.x = item->Pose.Orientation.x; + grenadeItem->Pose.Orientation.y = item->Pose.Orientation.y - ANGLE(180.0f); + grenadeItem->Pose.Orientation.z = 0; - static void SpawnEnemyJeepGrenade(ItemInfo& item) - { - int grenadeItemNumber = CreateItem(); - if (grenadeItemNumber == NO_VALUE || item.ItemFlags[3] > 0) - return; + grenadeItem->Pose.Position.x = item->Pose.Position.x + BLOCK(1) * phd_sin(grenadeItem->Pose.Orientation.y); + grenadeItem->Pose.Position.y = item->Pose.Position.y - CLICK(3); + grenadeItem->Pose.Position.z = item->Pose.Position.x + BLOCK(1) * phd_cos(grenadeItem->Pose.Orientation.y); - auto& grenadeItem = g_Level.Items[grenadeItemNumber]; + for (int i = 0; i < 5; i++) + TriggerGunSmoke(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, 0, 0, 0, 1, LaraWeaponType::GrenadeLauncher, 32); - grenadeItem.ObjectNumber = ID_GRENADE; - grenadeItem.RoomNumber = item.RoomNumber; - grenadeItem.Model.Color = Color(0.5f, 0.5f, 0.5f, 1.0f); + if (Random::TestProbability(0.75f)) + grenadeItem->ItemFlags[0] = 1; + else + grenadeItem->ItemFlags[0] = 2; - auto grenadePos = GetJointPosition(item, EnemyJeepGrenadeBite); - auto grenadeposF = Vector3(grenadePos.x, grenadePos.y, grenadePos.z); + grenadeItem->Animation.ActiveState = grenadeItem->Pose.Orientation.x; + grenadeItem->Animation.TargetState = grenadeItem->Pose.Orientation.y; + grenadeItem->Animation.RequiredState = NO_VALUE; + grenadeItem->Animation.Velocity.z = 32; + grenadeItem->Animation.Velocity.y = -32 * phd_sin(grenadeItem->Pose.Orientation.x); + grenadeItem->HitPoints = 120; - grenadeItem.Pose.Orientation = EulerAngles(item.Pose.Orientation.x, item.Pose.Orientation.y + ANGLE(180.0f), 0); - grenadeItem.Pose.Position = grenadePos + Vector3i(BLOCK(0.1f) * phd_sin(grenadeItem.Pose.Orientation.y), 0, BLOCK(0.1f) * phd_cos(grenadeItem.Pose.Orientation.y)); - - InitializeItem(grenadeItemNumber); - - for (int i = 0; i < 9; i++) - SpawnGunSmokeParticles(grenadeposF, Vector3(0, 0, 1), item.RoomNumber, 1, LaraWeaponType::RocketLauncher, 32); - - if (GetRandomControl() & 3) - { - grenadeItem.ItemFlags[0] = (int)ProjectileType::Grenade; - } - else - { - grenadeItem.ItemFlags[0] = (int)ProjectileType::FragGrenade; - } - - grenadeItem.Animation.Velocity.z = ENEMY_JEEP_GRENADE_VELOCITY; - grenadeItem.Animation.Velocity.y = CLICK(1) * phd_sin(grenadeItem.Pose.Orientation.x); - grenadeItem.Animation.ActiveState = grenadeItem.Pose.Orientation.x; - grenadeItem.Animation.TargetState = grenadeItem.Pose.Orientation.y; - grenadeItem.Animation.RequiredState = NO_VALUE; - grenadeItem.HitPoints = ENEMY_JEEP_GRENADE_TIMER; - - item.ItemFlags[3] = ENEMY_JEEP_GRENADE_COOLDOWN_TIME; - - AddActiveItem(grenadeItemNumber); - SoundEffect(SFX_TR4_GRENADEGUN_FIRE, &item.Pose); -; } - - static void RotateTowardTarget(ItemInfo& item, const short angle, short turnRate) - { - if (abs(angle) < turnRate) - { - item.Pose.Orientation.y += angle; - } - else if (angle < 0) - { - item.Pose.Orientation.y -= turnRate; - } - else - { - item.Pose.Orientation.y += turnRate; - } - } - - // Check for X1 and X2 AI object and do a path based on X1 ocb, check behaviour with X2 like throw grenade or stop and wait for X sec... - static void DoNodePath(ItemInfo& item) - { - auto& creature = *GetCreatureInfo(&item); - - // Use it to setup the path - FindAITargetObject(item, ID_AI_X1, creature.LocationAI, false); - if (Vector3i::Distance(item.Pose.Position, creature.Enemy->Pose.Position) <= ENEMY_JEEP_NEAR_X1_NODE_DISTANCE) - creature.LocationAI++; - - // Use it to get behaviour if you arrive on X2 ai without modifing the creature.Enemy. - auto data = AITargetData{}; - data.CheckDistance = true; - data.CheckOcb = false; - data.ObjectID = ID_AI_X2; - data.Ocb = NO_VALUE; - data.CheckSameZone = false; - data.DistanceMax = ENEMY_JEEP_NEAR_X2_NODE_DISTANCE; - - if (FindAITargetObject(item, data)) - { - DrawDebugSphere(data.FoundItem.Pose.Position.ToVector3(), 128.0f, Color(1, 1, 0), RendererDebugPage::WireframeMode, true); - item.ItemFlags[1] = data.FoundItem.TriggerFlags; - } - } - - // Process the AI_X2 ocb and do any required query, like drop grenade or jump pit. - static void ProcessBehaviour(ItemInfo& item) - { - switch (item.ItemFlags[1]) - { - // Drop grenade. - case X2_DROP_GRENADE: - SpawnEnemyJeepGrenade(item); - break; - - // Drop 2 step or more. - case X2_DROP: - item.Animation.TargetState = ENEMY_JEEP_STATE_DROP; - break; - - // Jump 2 step pit. - case X2_JUMP_PIT: - item.Animation.TargetState = ENEMY_JEEP_STATE_JUMP_PIT; - break; - - // Make the entity disappear/kill itself. - case X2_DISAPPEAR: - item.Status = ITEM_INVISIBLE; - item.Flags |= IFLAG_INVISIBLE; - RemoveActiveItem(item.Index); - DisableEntityAI(item.Index); - break; - - // Make the entity start heavy trigger below it. - case X2_ACTIVATE_HEAVY_TRIGGER: - TestTriggers(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, item.RoomNumber, true, 0); - break; - - case X2_CUSTOM_DROP: - item.Animation.TargetState = ENEMY_JEEP_STATE_CUSTOM_DROP; - break; - - case X2_CUSTOM_JUMP_PIT: - item.Animation.TargetState = ENEMY_JEEP_STATE_CUSTOM_JUMP_PIT; - break; - - default: - bool waitBehaviour = (item.ItemFlags[1] & X2_WAIT_UNTIL_PLAYER_NEAR) != 0; - if (waitBehaviour) - { - item.Animation.TargetState = ENEMY_JEEP_STATE_STOP; - item.ItemFlags[4] = 1; - item.ItemFlags[5] = item.ItemFlags[1] - X2_WAIT_UNTIL_PLAYER_NEAR; - } - - break; - } - - item.ItemFlags[1] = 0; // Reset X2 flags to avoid behaviour loop. - } - - static bool IsJeepIdle(int activeState) - { - return (activeState == ENEMY_JEEP_STATE_IDLE || - activeState == ENEMY_JEEP_STATE_STOP); - } - - static bool IsJeepMoving(int activeState) - { - return (activeState == ENEMY_JEEP_STATE_MOVE || - activeState == ENEMY_JEEP_STATE_TURN_LEFT || - activeState == ENEMY_JEEP_STATE_TURN_RIGHT); - } - - static bool IsJeepJumpingOrDropping(int activeState, bool onlyJump = false) - { - if (onlyJump) - { - return (activeState == ENEMY_JEEP_STATE_JUMP_PIT || - activeState == ENEMY_JEEP_STATE_CUSTOM_JUMP_PIT); - } - else - { - return (activeState == ENEMY_JEEP_STATE_JUMP_PIT || - activeState == ENEMY_JEEP_STATE_DROP || - activeState == ENEMY_JEEP_STATE_CUSTOM_DROP || - activeState == ENEMY_JEEP_STATE_CUSTOM_JUMP_PIT); + AddActiveItem(grenadeItemNumber); } } void InitializeEnemyJeep(short itemNumber) { - auto& item = g_Level.Items[itemNumber]; - item.ItemFlags[2] = 0; - item.ItemFlags[3] = 0; - item.ItemFlags[4] = NO_VALUE; - item.ItemFlags[5] = NO_VALUE; + auto* item = &g_Level.Items[itemNumber]; - InitializeCreature(itemNumber); - SetAnimation(item, ENEMY_JEEP_ANIM_IDLE); - DrawEnemyJeepLightMesh(item, true); + item->ItemFlags[0] = -80; + + if (g_Level.NumItems > 0) + { + for (int i = 0; i < g_Level.NumItems; i++) + { + auto* other = &g_Level.Items[i]; + + if (other == item || other->TriggerFlags != item->TriggerFlags) + continue; + + item->ItemFlags[1] = i; + other->ItemFlags[0] = -80; + other->Pose.Position.y = item->Pose.Position.y - BLOCK(1); + } + } } - void ControlEnemyJeep(short itemNumber) + void EnemyJeepControl(short itemNumber) { - if (!CreatureActive(itemNumber)) - return; - - auto& item = g_Level.Items[itemNumber]; - auto& creature = *GetCreatureInfo(&item); - auto& object = Objects[item.ObjectNumber]; - - AI_INFO ai = {}; - - if (item.ItemFlags[3] > 0) - item.ItemFlags[3]--; - item.ItemFlags[3] = std::clamp(item.ItemFlags[3], 0, ENEMY_JEEP_GRENADE_COOLDOWN_TIME); - - if (item.HitPoints <= 0) + if (CreatureActive(itemNumber)) { + auto* item = &g_Level.Items[itemNumber]; + auto* creature = GetCreatureInfo(item); - } - else - { - CreatureAIInfo(&item, &ai); + int x = item->Pose.Position.x; + int y = item->Pose.Position.y; + int z = item->Pose.Position.z; - // Manage light mesh and dynamic light based on state. - if (IsJeepIdle(item.Animation.ActiveState)) + int dx = 682 * phd_sin(item->Pose.Orientation.y); + int dz = 682 * phd_cos(item->Pose.Orientation.y); + + int height1 = GetPointCollision(Vector3i(x - dz, y, z - dx), item->RoomNumber).GetFloorHeight(); + if (abs(item->Pose.Position.y - height1) > CLICK(3)) { - DrawEnemyJeepLightMesh(item, true); - SpawnEnemyJeepBrakeLights(item); + item->Pose.Position.x += dz / 64; + item->Pose.Position.z += dx / 64; + item->Pose.Orientation.y += ANGLE(2.0f); + height1 = y; + } + + int height2 = GetPointCollision(Vector3i(x + dz, y, z - dx), item->RoomNumber).GetFloorHeight(); + if (abs(item->Pose.Position.y - height2) > CLICK(3)) + { + item->Pose.Orientation.y -= ANGLE(2.0f); + item->Pose.Position.x -= dz / 64; + item->Pose.Position.z += dx / 64; + height2 = y; + } + + short zRot = phd_atan(1364, height2 - height1); + + int height3 = GetPointCollision(Vector3i(x + dx, y, z + dz), item->RoomNumber).GetFloorHeight(); + if (abs(y - height3) > CLICK(3)) + height3 = y; + + int height4 = GetPointCollision(Vector3i(x - dx, y, z - dz), item->RoomNumber).GetFloorHeight(); + if (abs(y - height4) > CLICK(3)) + height4 = y; + + short xRot = phd_atan(1364, height4 - height3); + + AI_INFO AI; + CreatureAIInfo(item, &AI); + + creature->Enemy = creature->AITarget; + + auto* target = creature->AITarget; + + dx = LaraItem->Pose.Position.x - item->Pose.Position.x; + dz = LaraItem->Pose.Position.z - item->Pose.Position.z; + short angle = phd_atan(dz, dx) - item->Pose.Orientation.y; + + int distance; + if (dx > BLOCK(31.25f) || dx < -BLOCK(31.25f) || + dz > BLOCK(31.25f) || dz < -BLOCK(31.25f)) + { + distance = INT_MAX; + } + else + distance = pow(dx, 2) + pow(dz, 2); + + auto pos = Vector3i::Zero; + switch (item->Animation.ActiveState) + { + case 0: + case 2: + item->ItemFlags[0] -= 128; + item->MeshBits = -98305; + + pos = GetJointPosition(item, 11, Vector3i(0, -144, -1024)); + TriggerDynamicLight(pos.x, pos.y, pos.z, 10, 64, 0, 0); + + if (item->ItemFlags[0] < 0) + item->ItemFlags[0] = 0; + + if (item->Animation.RequiredState != NO_VALUE) + item->Animation.TargetState = item->Animation.RequiredState; + else if (AI.distance > pow(BLOCK(1), 2) || Lara.Location >= item->ItemFlags[3]) + item->Animation.TargetState = 1; + + break; + + case 1: + item->ItemFlags[0] += 37; + item->MeshBits = 0xFFFDBFFF; + creature->MaxTurn = item->ItemFlags[0] / 16; + + if (item->ItemFlags[0] > 8704) + item->ItemFlags[0] = 8704; + + if (AI.angle <= ANGLE(1.4f)) + { + if (AI.angle < -ANGLE(1.4f)) + item->Animation.TargetState = 3; + } + else + item->Animation.TargetState = 4; + + break; + + case 3: + case 4: + item->Animation.TargetState = 1; + item->ItemFlags[0] += 18; + + if (item->ItemFlags[0] > 8704) + item->ItemFlags[0] = 8704; + + break; + + case 5: + if (item->ItemFlags[0] < 1184) + item->ItemFlags[0] = 1184; + + break; + + default: + break; + } + + if (height3 <= (item->Floor + CLICK(2))) + { + if (height4 > (item->Floor + CLICK(2)) && item->Animation.ActiveState != 5) + { + item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 8; + item->Animation.FrameNumber = GetAnimData(item).frameBase; + item->Animation.ActiveState = 5; + item->Animation.TargetState = 1; + item->ItemFlags[1] = 0; + } } else { - DrawEnemyJeepLightMesh(item, false); - } + creature->LOT.RequiredBox |= 8; - // This jump/drop check need to be there or else the jeep could miss a AI_X1 - // and potentially could break, anyway it's still weird - // to see it going back to valid the missed AI. - if (IsJeepMoving(item.Animation.ActiveState) || - IsJeepJumpingOrDropping(item.Animation.ActiveState)) - { - DoNodePath(item); - } - - if (IsJeepMoving(item.Animation.ActiveState)) - RotateTowardTarget(item, ai.angle, ANGLE(5.0f)); - - switch (item.Animation.ActiveState) - { - case ENEMY_JEEP_STATE_IDLE: - switch (item.ItemFlags[4]) + if (item->ItemFlags[1] > 0) { - // Wait for player to enter a vehicle. - default: - case 0: - if ((item.TriggerFlags & EJ_NO_PLAYER_VEHICLE_REQUIRED) != 0 || - Lara.Context.Vehicle != NO_VALUE) - item.Animation.TargetState = ENEMY_JEEP_STATE_MOVE; - break; + item->ItemFlags[1] -= 8; + item->Pose.Position.y += item->ItemFlags[1] / 64; - // Wait until player is near. - case 1: - if (item.ItemFlags[5] != NO_VALUE && - Vector3i::Distance(LaraItem->Pose.Position, item.Pose.Position) <= item.ItemFlags[5]) + if (item->ItemFlags[1] < 0) + creature->LOT.RequiredBox &= ~8; + } + else + { + item->ItemFlags[1] = 2 * xRot; + creature->LOT.RequiredBox |= 8u; + } + + if (creature->LOT.RequiredBox & 8) + { + item->Animation.TargetState = 1; + creature->MaxTurn = 0; + } + } + + if (AI.distance < pow(BLOCK(1.5f), 2) || item->ItemFlags[3] == -2) + creature->ReachedGoal = true; + + if (creature->ReachedGoal) + { + TestTriggers(target, true); + + if (Lara.Location < item->ItemFlags[3] && item->Animation.ActiveState != 2 && item->Animation.TargetState != 2) + { + item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 1; + item->Animation.FrameNumber = GetAnimData(item).frameBase; + item->Animation.TargetState = 2; + item->Animation.ActiveState = 2; + + if (target->Flags & 4) { - item.Animation.TargetState = ENEMY_JEEP_STATE_MOVE; - item.ItemFlags[4] = NO_VALUE; // Remove state. - item.ItemFlags[5] = NO_VALUE; // Remove radius. + item->Pose = target->Pose; + + if (item->RoomNumber != target->RoomNumber) + ItemNewRoom(itemNumber, target->RoomNumber); + } + } + + if (distance > pow(BLOCK(2), 2) && + distance < pow(BLOCK(10), 2) && + !item->ItemFlags[2] && + (angle < -ANGLE(112.5f) || angle > ANGLE(112.5f))) + { + EnemyJeepLaunchGrenade(item); + item->ItemFlags[2] = 150; + } + + if (target->Flags == 62) + { + item->Status = ITEM_INVISIBLE; + RemoveActiveItem(itemNumber); + DisableEntityAI(itemNumber); + } + + if (Lara.Location >= item->ItemFlags[3] || !(target->Flags & 4)) + { + creature->ReachedGoal = false; + item->ItemFlags[3]++; + + creature->Enemy = nullptr; + AI_OBJECT* aiObject = nullptr; + + for (int i = 0; i < g_Level.AIObjects.size(); i++) + { + aiObject = &g_Level.AIObjects[i]; + + if (g_Level.AIObjects[i].triggerFlags == item->ItemFlags[3] && g_Level.AIObjects[i].roomNumber != NO_VALUE) + { + aiObject = &g_Level.AIObjects[i]; + break; + } } - break; + if (aiObject != nullptr) + { + creature->Enemy = nullptr; + target->ObjectNumber = aiObject->objectNumber; + target->RoomNumber = aiObject->roomNumber; + target->Pose.Position = aiObject->pos.Position; + target->Pose.Orientation.y = aiObject->pos.Orientation.y; + target->Flags = aiObject->flags; + target->TriggerFlags = aiObject->triggerFlags; + target->BoxNumber = aiObject->boxNumber; + + if (!(aiObject->flags & 0x20)) + { + target->Pose.Position.x += CLICK(1) * phd_sin(target->Pose.Orientation.y); + target->Pose.Position.z += CLICK(1) * phd_cos(target->Pose.Orientation.y); + } + } } - - break; - - case ENEMY_JEEP_STATE_MOVE: - if (ai.angle < -ENEMY_JEEP_WHEEL_LEFTRIGHT_TURN_MINIMUM) - { - item.Animation.TargetState = ENEMY_JEEP_STATE_TURN_LEFT; - } - else if (ai.angle > ENEMY_JEEP_WHEEL_LEFTRIGHT_TURN_MINIMUM) - { - item.Animation.TargetState = ENEMY_JEEP_STATE_TURN_RIGHT; - } - - break; - - case ENEMY_JEEP_STATE_TURN_LEFT: - case ENEMY_JEEP_STATE_TURN_RIGHT: - if (abs(ai.angle) <= ENEMY_JEEP_WHEEL_LEFTRIGHT_TURN_MINIMUM) - item.Animation.TargetState = ENEMY_JEEP_STATE_MOVE; - - break; } - } - ProcessBehaviour(item); - CreatureAnimation(itemNumber, 0, 0); + item->ItemFlags[2]--; + if (item->ItemFlags[2] < 0) + item->ItemFlags[2] = 0; - if (IsJeepJumpingOrDropping(item.Animation.ActiveState, true)) - { - // Required, else the entity will go back to previous position (before the jump) - creature.LOT.IsJumping = true; - } - else - { - creature.LOT.IsJumping = false; - AlignEntityToSurface(&item, Vector2(object.radius / 3, (object.radius / 3) * 1.33f), 0.8f); - } + if (abs(xRot - item->Pose.Orientation.x) < ANGLE(1.4f)) + item->Pose.Orientation.x = xRot; + else if (xRot < item->Pose.Orientation.x) + item->Pose.Orientation.x -= ANGLE(1.4f); + else + item->Pose.Orientation.x += ANGLE(1.4f); + + if (abs(zRot - item->Pose.Orientation.z) < ANGLE(1.4f)) + item->Pose.Orientation.z = zRot; + else if (zRot < item->Pose.Orientation.z) + item->Pose.Orientation.z -= ANGLE(1.4f); + else + item->Pose.Orientation.z += ANGLE(1.4f); + + item->ItemFlags[0] += -2 - xRot / 512; + if (item->ItemFlags[0] < 0) + item->ItemFlags[0] = 0; + + dx = item->ItemFlags[0] * phd_sin(-2 - xRot / 512); + dz = item->ItemFlags[0] * phd_cos(-2 - xRot / 512); + + item->Pose.Position.x += dx / 64; + item->Pose.Position.z += dz / 64; - // Didn't use Move there because we need the move sound when jump/drop and rotating left/right. - if (!IsJeepIdle(item.Animation.ActiveState)) - { - float pitch = std::clamp(0.4f + (float)abs(item.Animation.Velocity.z) / (float)ENEMY_JEEP_PITCH_MAX, 0.6f, 1.4f); for (int i = 0; i < 4; i++) - creature.JointRotation[i] -= ANGLE(pitch * ENEMY_JEEP_PITCH_WHEEL_SPEED_MULTIPLIER); + creature->JointRotation[i] -= item->ItemFlags[0]; - SoundEffect(SFX_TR4_VEHICLE_JEEP_MOVING, &item.Pose, SoundEnvironment::Land, pitch, 1.5f); - } - else - { - for (int i = 0; i < 4; i++) - creature.JointRotation[i] = 0; + if (!creature->ReachedGoal) + ClampRotation(item->Pose, AI.angle, item->ItemFlags[0] / 16); - SoundEffect(SFX_TR4_VEHICLE_JEEP_IDLE, &item.Pose); + creature->MaxTurn = 0; + AnimateItem(item); + + auto probe = GetPointCollision(*item); + item->Floor = probe.GetFloorHeight(); + if (item->RoomNumber != probe.GetRoomNumber()) + ItemNewRoom(itemNumber, probe.GetRoomNumber()); + + if (item->Pose.Position.y < item->Floor) + { + item->Animation.IsAirborne = true; + } + else + { + item->Pose.Position.y = item->Floor; + item->Animation.IsAirborne = false; + item->Animation.Velocity.y = 0; + } + + SoundEffect(SFX_TR4_VEHICLE_JEEP_MOVING, &item->Pose, SoundEnvironment::Land, 1.0f + (float)item->ItemFlags[0] / BLOCK(8)); // TODO: Check actual sound! } } } diff --git a/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.h b/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.h index 53e7ad1ae..c2a2d0706 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.h +++ b/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.h @@ -1,9 +1,8 @@ #pragma once - #include "Game/items.h" namespace TEN::Entities::TR4 { void InitializeEnemyJeep(short itemNumber); - void ControlEnemyJeep(short itemNumber); + void EnemyJeepControl(short itemNumber); } diff --git a/TombEngine/Objects/TR4/tr4_objects.cpp b/TombEngine/Objects/TR4/tr4_objects.cpp index 9ce44ab8d..f17c818de 100644 --- a/TombEngine/Objects/TR4/tr4_objects.cpp +++ b/TombEngine/Objects/TR4/tr4_objects.cpp @@ -11,7 +11,7 @@ #include "Specific/level.h" // Creatures -#include "Objects/TR4/Entity/Wraith.h" +#include "Objects/TR4/Entity/Wraith.h" // OFF #include "Objects/TR4/Entity/tr4_enemy_jeep.h" #include "Objects/TR4/Entity/tr4_ahmet.h" // OK #include "Objects/TR4/Entity/tr4_baddy.h" // OK @@ -626,7 +626,7 @@ namespace TEN::Entities if (obj->loaded) { obj->Initialize = InitializeEnemyJeep; - obj->control = ControlEnemyJeep; + obj->control = EnemyJeepControl; obj->collision = CreatureCollision; obj->shadowType = ShadowMode::All; obj->HitPoints = 40; @@ -634,8 +634,8 @@ namespace TEN::Entities obj->radius = 512; obj->intelligent = true; obj->damageType = DamageMode::None; // NOTE: Prevents enemy jeep from being killed with skidoo gun or something like that. - obj->LotType = LotType::EnemyJeep; - obj->SetBoneRotationFlags(8, ROT_X); // Wheel rotation. + obj->LotType = LotType::HumanPlusJumpAndMonkey; + obj->SetBoneRotationFlags(8, ROT_X); obj->SetBoneRotationFlags(9, ROT_X); obj->SetBoneRotationFlags(11, ROT_X); obj->SetBoneRotationFlags(12, ROT_X); From e5e625d78b4fb8d53ef11724c55f2fb65db98ec3 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 23 Nov 2024 14:22:23 +0100 Subject: [PATCH 111/112] Restore ldoc comment --- TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp index ed5fe3145..77517676c 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp @@ -25,7 +25,8 @@ void Level::Register(sol::table& parent) sol::constructors(), sol::call_constructor, sol::constructors(), - // Corresponds to an entry in strings.lua. +/// (string) string key for the level's (localised) name. +// Corresponds to an entry in strings.lua. //@mem nameKey "nameKey", &Level::NameStringKey, From 202c94f02e2e6c57832b2716f06160bb36c73f69 Mon Sep 17 00:00:00 2001 From: Sezz Date: Sun, 24 Nov 2024 21:28:07 +1100 Subject: [PATCH 112/112] Add simple safety checks to RNG --- TombEngine/Math/Random.cpp | 26 +++++++++++++++++++------- TombEngine/Math/Random.h | 6 +++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/TombEngine/Math/Random.cpp b/TombEngine/Math/Random.cpp index 0131624dc..a7459051f 100644 --- a/TombEngine/Math/Random.cpp +++ b/TombEngine/Math/Random.cpp @@ -8,21 +8,33 @@ namespace TEN::Math::Random { - static std::mt19937 Engine; + static auto Generator = std::mt19937(); - int GenerateInt(int low, int high) + int GenerateInt(int min, int max) { - return (Engine() / (Engine.max() / (high - low + 1) + 1) + low); + if (min >= max) + { + TENLog("Attempted to generate integer with minimum value greater than maximum value.", LogLevel::Warning); + return min; + } + + return (((Generator() / (Generator.max()) / (max - min + 1)) + 1) + min); } - float GenerateFloat(float low, float high) + float GenerateFloat(float min, float max) { - return ((high - low) * Engine() / Engine.max() + low); + if (min >= max) + { + TENLog("Attempted to generate float with minimum value greater than maximum value.", LogLevel::Warning); + return min; + } + + return ((((max - min) * Generator()) / Generator.max()) + min); } - short GenerateAngle(short low, short high) + short GenerateAngle(short min, short max) { - return (short)GenerateInt(low, high); + return (short)GenerateInt(min, min); } Vector2 GenerateDirection2D() diff --git a/TombEngine/Math/Random.h b/TombEngine/Math/Random.h index 97b783993..ef197f7c5 100644 --- a/TombEngine/Math/Random.h +++ b/TombEngine/Math/Random.h @@ -6,9 +6,9 @@ namespace TEN::Math::Random { // Value generation - int GenerateInt(int low = 0, int high = SHRT_MAX); - float GenerateFloat(float low = 0.0f, float high = 1.0f); - short GenerateAngle(short low = SHRT_MIN, short high = SHRT_MAX); + int GenerateInt(int min = 0, int max = SHRT_MAX); + float GenerateFloat(float min = 0.0f, float max = 1.0f); + short GenerateAngle(short min = SHRT_MIN, short max = SHRT_MAX); // 2D geometric generation