From f8c0f1afda78ef63af18850734006b1ba6c91a87 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:30:01 +0100 Subject: [PATCH 01/22] Update bug_report.yaml Cleaned up the bug report and streamlined the process of creating it. --- .github/ISSUE_TEMPLATE/bug_report.yaml | 155 +++++++++++-------------- 1 file changed, 70 insertions(+), 85 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index e123eab748..87d8f72a88 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,99 +1,84 @@ name: Bug report -description: Create a report to help us understand and diagnose your issue. Your contribution is welcomed and valued! It is highly recommended that you are using the latest version before submitting a bug report. -title: "[ bug Report ]" -labels: Awaiting Triage +description: Create a report to help us understand and triage your issue. +title: "" +labels: + - Awaiting Triage body: + - type: markdown + attributes: + value: | + ## Bug Report -- type: markdown - attributes: - value: | - Please follow this document in order to report a bug. It is highly recommended that you are using the latest version before submitting a bug report. + Please follow this document carefully to report a bug. -- type: dropdown - attributes: - label: TombEngine version - description: | - Please select the TombEngine Version from the dropdown list. - options: - - Development Build - - v1.8.0 (latest version) - - v1.7.1 - - v1.7.0 - - v1.5 - validations: - required: true + > **Important**: It is highly recommended that you use the latest version before submitting a bug report. -- type: dropdown - attributes: - label: Tomb Editor version - description: | - Please select the Tomb Editor version used from the dropdown list. - options: - - Development Build - - v1.8.0 (latest version) - - v1.7.2 - - v1.7.1 - - v1.7.0 - validations: - required: true + - type: dropdown + attributes: + label: TombEngine Version + description: | + Please select the TombEngine version you are using. + options: + - v1.8.1 (latest release) + - v1.8.0 + validations: + required: true -- type: textarea - attributes: - label: Describe the bug - description: | - Please provide A clear and concise description of what the bug is. - placeholder: | - Your bug report here. - validations: - required: true + - type: checkboxes + attributes: + label: Development Version + description: Are you submitting this report from a development build that has not been officially released? + options: + - label: Yes + - label: No + validations: + required: true -- type: textarea - attributes: - label: To Reproduce - description: | - To reproduce the behaviour, please provide detailed steps for the development team to follow. This can be done through screenshots or a written guide + - type: textarea + attributes: + label: Describe the Bug + description: | + Please provide a clear and concise description of what the issue is. + placeholder: | + Your bug report here. + validations: + required: true - **If the bug cannot be reproduced, and if the issue is not adequately explained, it will be closed without further investigation** - placeholder: | - Provide detailed reproducible steps here. - validations: - required: true + - type: textarea + attributes: + label: To Reproduce + description: | + Please provide detailed steps to reproduce the issue. + + **Note**: If the bug cannot be reproduced or the issue is not clearly explained, it may be closed without further investigation. + placeholder: | + Step-by-step reproduction instructions here. + validations: + required: true -- type: textarea - attributes: - label: Expected Behaviour - description: | - A clear and concise description of what you expected to happen. - placeholder: | - A description of what should happen here. - validations: - required: true + - type: textarea + attributes: + label: Expected Behaviour + description: | + What did you expect to happen? -- type: textarea - attributes: - label: Additional Content - description: | - Add any other context about the problem here. + **Note**: If the bug cannot be reproduced or the issue is not clearly explained, it may be closed without further investigation. + placeholder: | + A description of what should happen here. + validations: + required: true - * Are you testing an build of a TombEngine that has not yet been released? If so please give some context. - * Did you get any asset from the TombEngine website that has presented a bug? - placeholder: | - A description of any additional content here. - validations: - required: false - -- type: textarea - attributes: - label: Minimal reproduction project - description: | - **Please upload a .zip file containing your level and all assets needed to compile the level and a cut-down version of your level where the bug presents itself** - The project can be uploaded as a zip file (10 mb max) or provide a link from google drive, dropbox etc. - **Note** if you do not provide this, your issue may be rejected - placeholder: | - Download link to your project - validations: - required: true - + - type: textarea + attributes: + label: Minimal Reproduction Project + description: | + Please upload a .zip file (10 MB max) containing your level and all assets needed to compile the level, including a minimal version where the bug occurs. + Alternatively, provide a download link from a cloud storage service (e.g., Google Drive, Dropbox). + > **Important**: If you do not provide a minimal reproduction project, your issue may be rejected. + placeholder: | + Download link to your project or attach a .zip file. + validations: + required: true From 05949c1cb2ddd91dca0dd05d6c816c362e16f141 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:31:36 +0100 Subject: [PATCH 02/22] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 87d8f72a88..b4df0b7948 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,6 +1,5 @@ name: Bug report description: Create a report to help us understand and triage your issue. -title: "" labels: - Awaiting Triage @@ -30,8 +29,8 @@ body: label: Development Version description: Are you submitting this report from a development build that has not been officially released? options: - - label: Yes - - label: No + - label: "I am using an unofficial development version." + - label: "I am using an official release." validations: required: true From cd4c9b4645167c381c1755a9a8402fde5febad19 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:33:01 +0100 Subject: [PATCH 03/22] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index b4df0b7948..cbf6c1e59e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -15,7 +15,7 @@ body: - type: dropdown attributes: - label: TombEngine Version + label: Tomb Engine Version description: | Please select the TombEngine version you are using. options: From 94e095fb5342451c14d799c4d4540888201409dd Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Sun, 1 Jun 2025 15:55:25 +0100 Subject: [PATCH 04/22] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index cbf6c1e59e..592411413b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -19,7 +19,8 @@ body: description: | Please select the TombEngine version you are using. options: - - v1.8.1 (latest release) + - v1.9.0 (Pre-release) + - v1.8.1 (latest public release) - v1.8.0 validations: required: true @@ -31,6 +32,7 @@ body: options: - label: "I am using an unofficial development version." - label: "I am using an official release." + - label: "I am using an official pre-release." validations: required: true From 37001e5be9aec936abb065f5e165191b8bc3c744 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Sat, 14 Jun 2025 07:57:44 +0100 Subject: [PATCH 05/22] Update README.md Added reference to skinning support. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f699f1622..76a8ebd63f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ - *Lua* as the native scripting language. - Many objects from the original series (1-5). - Support for high framerate, antialiasing, mipmapping, and SSAO. +- Full skinning support for all objects - Full diagonal geometry support. - Uncapped map size. - A streamlined player control scheme. @@ -38,4 +39,4 @@ Once done, you should be able to build a level with *Tomb Editor* and run it in Contributions are welcome. If you would like to participate in development to any degree, whether that be through suggestions, bug reports, or code, join our [Discord server](https://discord.gg/h5tUYFmres). # Disclaimer -Tomb Engine uses modified MIT license for non-commercial use only. For more information, see [license](https://github.com/TombEngine/TombEngine?tab=License-1-ov-file#readme). Tomb Engine is unaffiliated with the Crystal Dynamics group of companies or Embracer Group AB. *Tomb Raider* is a trademark of the Crystal Dynamics group of companies. Tomb Engine team is not responsible for illegal use of this source code and built binaries alone or in combination with third-party assets or components. This source code is released as-is and continues to be maintained by non-paid contributors in their free time. \ No newline at end of file +Tomb Engine uses modified MIT license for non-commercial use only. For more information, see [license](https://github.com/TombEngine/TombEngine?tab=License-1-ov-file#readme). Tomb Engine is unaffiliated with the Crystal Dynamics group of companies or Embracer Group AB. *Tomb Raider* is a trademark of the Crystal Dynamics group of companies. Tomb Engine team is not responsible for illegal use of this source code and built binaries alone or in combination with third-party assets or components. This source code is released as-is and continues to be maintained by non-paid contributors in their free time. From ca30a69814e6f4731b727a31b9941f034e5a9556 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Wed, 18 Jun 2025 18:45:28 +0100 Subject: [PATCH 06/22] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 592411413b..333c429a24 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -19,9 +19,9 @@ body: description: | Please select the TombEngine version you are using. options: - - v1.9.0 (Pre-release) - - v1.8.1 (latest public release) - - v1.8.0 + - Development build + - v1.9.0 (latest public release) + - v1.8.1 validations: required: true From d268f6659de17bf56a8b278005e77b0002f3d2af Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 22 Jun 2025 11:09:39 +0300 Subject: [PATCH 07/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76a8ebd63f..88b45dd54d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - *Lua* as the native scripting language. - Many objects from the original series (1-5). - Support for high framerate, antialiasing, mipmapping, and SSAO. -- Full skinning support for all objects +- Full skinning support for all objects. - Full diagonal geometry support. - Uncapped map size. - A streamlined player control scheme. From 6aea42463613a7d62493914745586386dfe1d8c4 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Sun, 17 Aug 2025 12:55:52 +0100 Subject: [PATCH 08/22] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 333c429a24..755bfb370a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -20,8 +20,8 @@ body: Please select the TombEngine version you are using. options: - Development build - - v1.9.0 (latest public release) - - v1.8.1 + - v1.9.2 (latest public release) + - v1.9.1 validations: required: true From 50ea441f83673245a610ede060b366df1d9e1fe4 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Tue, 11 Nov 2025 21:45:38 +0000 Subject: [PATCH 09/22] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 755bfb370a..90c42f4091 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -20,8 +20,8 @@ body: Please select the TombEngine version you are using. options: - Development build - - v1.9.2 (latest public release) - - v1.9.1 + - v1.10.0 (latest public release) + - v1.9.2 validations: required: true From 8e09754f1404068d4d5ab9ca8448e0602cac7532 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Sun, 23 Nov 2025 22:40:09 +0000 Subject: [PATCH 10/22] Updated Bug Report form for 1.10.1 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 90c42f4091..8c6effd1d7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -20,8 +20,8 @@ body: Please select the TombEngine version you are using. options: - Development build - - v1.10.0 (latest public release) - - v1.9.2 + - v1.10.1 (latest public release) + - v1.10.0 validations: required: true From 308a42c1f989fdfe669492615819452b48b77572 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 15 Mar 2026 12:04:17 +0100 Subject: [PATCH 11/22] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 8c6effd1d7..7bd666036e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -20,8 +20,8 @@ body: Please select the TombEngine version you are using. options: - Development build - - v1.10.1 (latest public release) - - v1.10.0 + - v1.11 (latest public release) + - v1.10.1 validations: required: true From 0954dbe7908e0d8a103af6b72d32cd776f3a2ec7 Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:15:59 +0000 Subject: [PATCH 12/22] Add cross-repo dependency check workflow This workflow checks for cross-repo dependencies in pull requests and sets the commit status accordingly. This is for merging branches in the Tomb Editor and Tomb Engine repo that require both sides to be merged before the changes are completed. --- .github/workflows/cross-repo-dependency.yml | 78 +++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/cross-repo-dependency.yml diff --git a/.github/workflows/cross-repo-dependency.yml b/.github/workflows/cross-repo-dependency.yml new file mode 100644 index 0000000000..a5bea6db84 --- /dev/null +++ b/.github/workflows/cross-repo-dependency.yml @@ -0,0 +1,78 @@ +name: Cross repo dependency + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + pull_request_review: + types: [submitted] + +jobs: + check-linked-pr: + runs-on: ubuntu-latest + steps: + - name: Check for dependency and set status + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const body = pr.body || ""; + + // Look for: Depends on: org/repo#123 + const match = body.match(/Depends on:\s+([\w-]+)\/([\w-]+)#(\d+)/); + + const owner = context.repo.owner; + const repo = context.repo.repo; + const sha = pr.head.sha; + + if (!match) { + // No dependency → pass + await github.rest.repos.createCommitStatus({ + owner, + repo, + sha, + state: "success", + context: "cross-repo-dependency", + description: "No cross-repo dependency" + }); + return; + } + + const depOwner = match[1]; + const depRepo = match[2]; + const depNumber = Number(match[3]); + + // Get linked PR + const linked = await github.rest.pulls.get({ + owner: depOwner, + repo: depRepo, + pull_number: depNumber + }); + + // Get reviews to see if it has at least one APPROVED + const reviews = await github.rest.pulls.listReviews({ + owner: depOwner, + repo: depRepo, + pull_number: depNumber + }); + + const approved = reviews.data.some(r => r.state === "APPROVED"); + + // mergeable can be null while GitHub is still calculating; treat that as not ready + const mergeable = linked.data.mergeable === true; + + let state = "pending"; + let description = "Waiting for linked PR to be approved and mergeable"; + + if (approved && mergeable) { + state = "success"; + description = `Linked PR ${depOwner}/${depRepo}#${depNumber} is approved and mergeable`; + } + + await github.rest.repos.createCommitStatus({ + owner, + repo, + sha, + state, + context: "cross-repo-dependency", + description + }); From 25b81d825a566248f3d2f29fb42d199b6fdd07bd Mon Sep 17 00:00:00 2001 From: Stranger1992 <84292688+Stranger1992@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:30:41 +0000 Subject: [PATCH 13/22] Refactor dependency check workflow steps Refactor cross-repo dependency check to improve clarity and functionality. --- .github/workflows/cross-repo-dependency.yml | 79 ++++++++++++--------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/.github/workflows/cross-repo-dependency.yml b/.github/workflows/cross-repo-dependency.yml index a5bea6db84..afad434580 100644 --- a/.github/workflows/cross-repo-dependency.yml +++ b/.github/workflows/cross-repo-dependency.yml @@ -10,68 +10,77 @@ jobs: check-linked-pr: runs-on: ubuntu-latest steps: - - name: Check for dependency and set status + - name: Check for dependency + id: parse uses: actions/github-script@v7 with: script: | - const pr = context.payload.pull_request; - const body = pr.body || ""; - - // Look for: Depends on: org/repo#123 + const body = context.payload.pull_request.body || ""; const match = body.match(/Depends on:\s+([\w-]+)\/([\w-]+)#(\d+)/); - const owner = context.repo.owner; - const repo = context.repo.repo; - const sha = pr.head.sha; - if (!match) { - // No dependency → pass - await github.rest.repos.createCommitStatus({ - owner, - repo, - sha, - state: "success", - context: "cross-repo-dependency", - description: "No cross-repo dependency" - }); + // No dependency → do NOT create a status check + core.setOutput("skip", "true"); return; } - const depOwner = match[1]; - const depRepo = match[2]; - const depNumber = Number(match[3]); + core.setOutput("skip", "false"); + core.setOutput("depOwner", match[1]); + core.setOutput("depRepo", match[2]); + core.setOutput("depNumber", match[3]); + + - name: Exit early if no dependency + if: steps.parse.outputs.skip == 'true' + run: echo "No dependency found — skipping." + + - name: Check linked PR status + if: steps.parse.outputs.skip == 'false' + id: check + uses: actions/github-script@v7 + with: + script: | + const owner = steps.parse.outputs.depOwner; + const repo = steps.parse.outputs.depRepo; + const number = Number(steps.parse.outputs.depNumber); - // Get linked PR const linked = await github.rest.pulls.get({ - owner: depOwner, - repo: depRepo, - pull_number: depNumber + owner, + repo, + pull_number: number }); - // Get reviews to see if it has at least one APPROVED const reviews = await github.rest.pulls.listReviews({ - owner: depOwner, - repo: depRepo, - pull_number: depNumber + owner, + repo, + pull_number: number }); const approved = reviews.data.some(r => r.state === "APPROVED"); - - // mergeable can be null while GitHub is still calculating; treat that as not ready const mergeable = linked.data.mergeable === true; + core.setOutput("approved", approved); + core.setOutput("mergeable", mergeable); + + - name: Set dependency status + if: steps.parse.outputs.skip == 'false' + uses: actions/github-script@v7 + with: + script: | + const approved = steps.check.outputs.approved === 'true'; + const mergeable = steps.check.outputs.mergeable === 'true'; + let state = "pending"; let description = "Waiting for linked PR to be approved and mergeable"; if (approved && mergeable) { state = "success"; - description = `Linked PR ${depOwner}/${depRepo}#${depNumber} is approved and mergeable`; + description = "Linked PR is approved and mergeable"; } await github.rest.repos.createCommitStatus({ - owner, - repo, - sha, + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.payload.pull_request.head.sha, state, context: "cross-repo-dependency", description From 2eafefef341b3e50362de1372f79e3df5331893b Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:25:23 +0300 Subject: [PATCH 14/22] add Smooth step-up ascent --- TombEngine/Game/control/box.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index eff00df64b..816dc24016 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -1086,9 +1086,15 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt) floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z); - // Snap to floor or smoothly descend. + // Snap to floor, smoothly ascend steps, or smoothly descend. if (item->Pose.Position.y > item->Floor) - item->Pose.Position.y = item->Floor; + { + int heightDiff = item->Pose.Position.y - item->Floor; + if (heightDiff > CLICK(0.25f) && heightDiff <= LOT->Step) + item->Pose.Position.y -= CLICK(0.25f); // Smooth step-up ascent. + else + item->Pose.Position.y = item->Floor; + } else if (item->Floor - item->Pose.Position.y > CLICK(0.25f)) item->Pose.Position.y += CLICK(0.25f); else if (item->Pose.Position.y < item->Floor) From 63da0e21ffc23dc7c1b08cb483dae544a5159132 Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:22:15 +0300 Subject: [PATCH 15/22] Enhance floor height adjustment and vault trigger logic Added logic to update floor height and prevent item pushing through room geometry when nudged by another creature. Adjusted pivot offset for land creatures to improve vault trigger detection for correct ascend interpolation. This Pivot Offset used only for intersections between pathfinding boxes. --- TombEngine/Game/control/box.cpp | 57 +++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index 816dc24016..7825245c28 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -903,7 +903,16 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt) else item->Pose.Orientation.y += BIFF_AVOID_TURN; - return true; + // Update floor height to prevent sinking through geometry when nudged by another creature. + if (LOT->Fly == NO_FLYING) + { + floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); + item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z); + if (item->Pose.Position.y > item->Floor) + \titem->Pose.Position.y = item->Floor; + } + + \treturn true; } // VERTICAL MOVEMENT: @@ -1086,15 +1095,12 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt) floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z); - // Snap to floor, smoothly ascend steps, or smoothly descend. - if (item->Pose.Position.y > item->Floor) - { - int heightDiff = item->Pose.Position.y - item->Floor; - if (heightDiff > CLICK(0.25f) && heightDiff <= LOT->Step) - item->Pose.Position.y -= CLICK(0.25f); // Smooth step-up ascent. - else - item->Pose.Position.y = item->Floor; - } + // Snap to floor or smoothly ascend/descend. + int heightDiff = item->Pose.Position.y - item->Floor; + if (heightDiff > CLICK(0.25f) && heightDiff <= CLICK(1)) + item->Pose.Position.y -= CLICK(0.25f); + else if (item->Pose.Position.y > item->Floor) + item->Pose.Position.y = item->Floor; else if (item->Floor - item->Pose.Position.y > CLICK(0.25f)) item->Pose.Position.y += CLICK(0.25f); else if (item->Pose.Position.y < item->Floor) @@ -2150,6 +2156,31 @@ int CreatureVault(short itemNumber, short angle, int vault, int shift) auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); + // Apply a forward pivot offset when stepping up so the vault triggers when the + // creature's front half enters the next sector rather than its body centre. + int pivotOffsetX = 0; + int pivotOffsetZ = 0; + if (item->BoxNumber != NO_VALUE) + { + int nextBox = creature->LOT.Node[item->BoxNumber].exitBox; + if (nextBox != NO_VALUE && + g_Level.PathfindingBoxes[nextBox].height < g_Level.PathfindingBoxes[item->BoxNumber].height) + { + int stepHeight = g_Level.PathfindingBoxes[item->BoxNumber].height - g_Level.PathfindingBoxes[nextBox].height; + if (stepHeight <= CLICK(1)) + { + int forwardExtent = GameBoundingBox(item->ObjectNumber, item->Animation.AnimNumber, item->Animation.FrameNumber).Z2 / 2; + if (forwardExtent > 0) + { + pivotOffsetX = (int)(phd_sin(item->Pose.Orientation.y) * forwardExtent); + pivotOffsetZ = (int)(phd_cos(item->Pose.Orientation.y) * forwardExtent); + item->Pose.Position.x += pivotOffsetX; + item->Pose.Position.z += pivotOffsetZ; + } + } + } + } + int xBlock = item->Pose.Position.x / BLOCK(1); int zBlock = item->Pose.Position.z / BLOCK(1); int y = item->Pose.Position.y; @@ -2175,6 +2206,8 @@ int CreatureVault(short itemNumber, short angle, int vault, int shift) } else if (item->Pose.Position.y > (y - CLICK(1.5f))) { + item->Pose.Position.x -= pivotOffsetX; + item->Pose.Position.z -= pivotOffsetZ; return 0; } else if (item->Pose.Position.y > (y - CLICK(2.5f))) @@ -2197,7 +2230,11 @@ int CreatureVault(short itemNumber, short angle, int vault, int shift) if (zBlock == newZblock) { if (xBlock == newXblock) + { + item->Pose.Position.x -= pivotOffsetX; + item->Pose.Position.z -= pivotOffsetZ; return 0; + } if (xBlock < newXblock) { From 998af1a4b0b759672975fdcda0510cae840b18fc Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Tue, 7 Apr 2026 01:47:51 +0300 Subject: [PATCH 16/22] Fix indentation in box.cpp --- TombEngine/Game/control/box.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index 7825245c28..9945165664 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -909,10 +909,10 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt) floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z); if (item->Pose.Position.y > item->Floor) - \titem->Pose.Position.y = item->Floor; + item->Pose.Position.y = item->Floor; } - \treturn true; + return true; } // VERTICAL MOVEMENT: From 08a80efedd00c013e56c9cb8413f842c64db58b5 Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Thu, 14 May 2026 23:12:53 +0300 Subject: [PATCH 17/22] Implement monkey pickup target logic and mesh swapping Added functions to handle monkey pickup targets and mesh swapping. --- TombEngine/Objects/TR3/Entity/tr3_monkey.cpp | 125 ++++++++++++------- 1 file changed, 78 insertions(+), 47 deletions(-) diff --git a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp index c008530a9b..32a21d16fa 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp @@ -20,6 +20,11 @@ namespace TEN::Entities::Creatures::TR3 // TODO: Work out damage constants. constexpr auto MONKEY_SWIPE_ATTACK_PLAYER_DAMAGE = 40; constexpr auto MONKEY_SWIPE_ATTACK_CREATURE_DAMAGE = 20; + constexpr auto MONKEY_PICKUP_FRAME = 12; + constexpr auto MONKEY_MESH_NORMAL = ALL_JOINT_BITS; + constexpr auto MONKEY_MESH_MEDIPACK = 0xFFFFFEFF; + constexpr auto MONKEY_MESH_KEY = 0xFFFF6E6F; + constexpr auto MONKEY_MESH_KEY_EMPTY = 0xFFFF6F6F; // TODO: Range constants. @@ -85,6 +90,64 @@ namespace TEN::Entities::Creatures::TR3 MONKEY_ANIM_WALK_FORWARD_TO_IDLE = 30 }; + bool IsMonkeyPickupTarget(ItemInfo* target, GAME_OBJECT_ID objectNumber, CreatureInfo* creature) + { + if (target->ObjectNumber != objectNumber) + return false; + + if (target->RoomNumber == NO_VALUE || target->AIBits) + return false; + + if (target->Status == ITEM_INVISIBLE || target->Flags & IFLAG_CLEAR_BODY) + return false; + + return SameZone(creature, target); + } + + void UpdateMonkeyPickupTarget(ItemInfo* item, CreatureInfo* creature) + { + if (item->CarriedItem != NO_VALUE) + return; + + auto targetObjectNumber = (item->AIBits == MODIFY) ? ID_KEY_ITEM4 : ID_SMALLMEDI_ITEM; + + if (creature->Enemy && IsMonkeyPickupTarget(creature->Enemy, targetObjectNumber, creature)) + return; + + for (int i = 0; i < g_Level.NumItems; i++) + { + auto* target = &g_Level.Items[i]; + + if (IsMonkeyPickupTarget(target, targetObjectNumber, creature)) + { + creature->Enemy = target; + return; + } + } + } + + void ApplyMonkeyMeshSwap(ItemInfo* item, unsigned int swapMask) + { + auto meshSwapObjectNumber = (item->AIBits == MODIFY) ? + ID_MESHSWAP_MONKEY_KEY : + ID_MESHSWAP_MONKEY_MEDIPACK; + + const auto& meshSwapObject = Objects[meshSwapObjectNumber]; + if (!meshSwapObject.loaded) + { + item->ResetModelToDefault(); + return; + } + + for (int i = 0; i < item->Model.MeshIndex.size(); i++) + { + if (swapMask & (1 << i)) + item->Model.MeshIndex[i] = item->Model.BaseMesh + i; + else + item->Model.MeshIndex[i] = meshSwapObject.meshIndex + i; + } + } + void InitializeMonkey(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; @@ -110,6 +173,7 @@ namespace TEN::Entities::Creatures::TR3 { if (item->Animation.ActiveState != MONKEY_STATE_DEATH) { + item->ResetModelToDefault(); SetAnimation(item, MONKEY_ANIM_DEATH); item->MeshBits = ALL_JOINT_BITS; } @@ -121,50 +185,16 @@ namespace TEN::Entities::Creatures::TR3 if (creature->HurtByLara) creature->Enemy = LaraItem; else - { - creature->Enemy = nullptr; - int minDistance = INT_MAX; - - for (auto creatureIndex : ActiveCreatures) - { - auto* currentCreature = GetCreatureInfo(&g_Level.Items[creatureIndex]); - - if (currentCreature->ItemNumber == NO_VALUE || currentCreature->ItemNumber == itemNumber) - continue; - - auto* target = &g_Level.Items[currentCreature->ItemNumber]; - if (target->ObjectNumber == ID_LARA || target->ObjectNumber == ID_MONKEY) - continue; - - if (target->ObjectNumber == ID_SMALLMEDI_ITEM) - { - int x = target->Pose.Position.x - item->Pose.Position.x; - int z = target->Pose.Position.z - item->Pose.Position.z; - int distance = pow(x, 2) + pow(z, 2); + UpdateMonkeyPickupTarget(item, creature); - if (distance < minDistance) - { - creature->Enemy = target; - minDistance = distance; - } - } - } - } + auto swapMask = MONKEY_MESH_NORMAL; + if (item->AIBits == MODIFY) + swapMask = (item->CarriedItem != NO_VALUE) ? MONKEY_MESH_KEY : MONKEY_MESH_KEY_EMPTY; + else if (item->CarriedItem != NO_VALUE) + swapMask = MONKEY_MESH_MEDIPACK; - if (item->AIBits != MODIFY) - { - if (item->CarriedItem != NO_VALUE) - item->MeshBits = 0xFFFFFEFF; - else - item->MeshBits = ALL_JOINT_BITS; - } - else - { - if (item->CarriedItem != NO_VALUE) - item->MeshBits = 0xFFFF6E6F; - else - item->MeshBits = 0xFFFF6F6F; - } + item->MeshBits = ALL_JOINT_BITS; + ApplyMonkeyMeshSwap(item, swapMask); AI_INFO AI; CreatureAIInfo(item, &AI); @@ -186,7 +216,7 @@ namespace TEN::Entities::Creatures::TR3 int dx = LaraItem->Pose.Position.x - item->Pose.Position.x; int dz = LaraItem->Pose.Position.z - item->Pose.Position.z; - laraAI.angle = phd_atan(dz, dz) - item->Pose.Orientation.y; + laraAI.angle = phd_atan(dz, dx) - item->Pose.Orientation.y; laraAI.distance = pow(dx, 2) + pow(dz, 2); } @@ -328,7 +358,7 @@ namespace TEN::Entities::Creatures::TR3 else if (AI.bite && AI.distance < pow(682, 2)) item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; else if (AI.distance < pow(682, 2) && - !creature->Enemy->IsLara() && creature->Enemy != nullptr && + creature->Enemy != nullptr && !creature->Enemy->IsLara() && creature->Enemy->ObjectNumber != ID_AI_PATROL1 && creature->Enemy->ObjectNumber != ID_AI_PATROL2 && abs(item->Pose.Position.y - creature->Enemy->Pose.Position.y) < CLICK(1)) @@ -349,11 +379,11 @@ namespace TEN::Entities::Creatures::TR3 break; else if ((creature->Enemy->ObjectNumber == ID_SMALLMEDI_ITEM || creature->Enemy->ObjectNumber == ID_KEY_ITEM4) && - item->Animation.FrameNumber == 12) + item->Animation.FrameNumber == MONKEY_PICKUP_FRAME) { if (creature->Enemy->RoomNumber == NO_VALUE || creature->Enemy->Status == ITEM_INVISIBLE || - creature->Enemy->Flags & -32768) + creature->Enemy->Flags & IFLAG_CLEAR_BODY) { creature->Enemy = nullptr; } @@ -386,7 +416,8 @@ namespace TEN::Entities::Creatures::TR3 } } else if (creature->Enemy->ObjectNumber == ID_AI_AMBUSH && - item->Animation.FrameNumber == 12) + item->Animation.FrameNumber == MONKEY_PICKUP_FRAME && + item->CarriedItem != NO_VALUE) { item->AIBits = 0; From 087ec06d9974323286fcaa3a3856d328b6a2c66c Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Thu, 14 May 2026 23:22:00 +0300 Subject: [PATCH 18/22] Refactor enemy target selection logic for Monkey Refactor enemy target selection logic to include item checks. --- TombEngine/Game/control/box.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index eff00df64b..aeb3fb49bc 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -2281,7 +2281,8 @@ void GetAITarget(CreatureInfo* creature) { FindAITargetObject(creature, ID_AI_AMBUSH); } - else if (Vector3i::Distance(enemy->Pose.Position, item->Pose.Position) < REACHED_GOAL_RADIUS) + else if (item->ObjectNumber != ID_MONKEY && + Vector3i::Distance(enemy->Pose.Position, item->Pose.Position) < REACHED_GOAL_RADIUS) { TestTriggers(enemy, true); creature->ReachedGoal = true; @@ -2361,8 +2362,12 @@ void FindAITargetObject(CreatureInfo* creature, int objectNumber, int ocb, bool for (auto& aiObject : g_Level.AIObjects) { + bool matchingTriggerFlags = (aiObject.triggerFlags == ocb); + if (item.ObjectNumber == ID_MONKEY && objectNumber == ID_AI_AMBUSH) + matchingTriggerFlags = true; + if (aiObject.objectNumber == objectNumber && - aiObject.triggerFlags == ocb && + matchingTriggerFlags && aiObject.roomNumber != NO_VALUE) { int* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data(); From e4570495d5ee3041fc421f5a3f47ebcbef562d9b Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Fri, 15 May 2026 15:47:45 +0300 Subject: [PATCH 19/22] Add LotType as Human to TR3_Monkey object --- TombEngine/Objects/TR3/tr3_objects.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/TombEngine/Objects/TR3/tr3_objects.cpp b/TombEngine/Objects/TR3/tr3_objects.cpp index 99ea154209..fc971d0ac9 100644 --- a/TombEngine/Objects/TR3/tr3_objects.cpp +++ b/TombEngine/Objects/TR3/tr3_objects.cpp @@ -230,6 +230,7 @@ static void StartEntity(ObjectInfo* obj) obj->HitPoints = 8; obj->radius = 102; obj->intelligent = true; + obj->LotType = LotType::Human; obj->pivotLength = 0; obj->SetBoneRotationFlags(0, ROT_X | ROT_Y); obj->SetBoneRotationFlags(7, ROT_Y); From e715ce0af51d0fa9e02d1cf6e3537e2124497a3e Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Fri, 15 May 2026 21:05:13 +0300 Subject: [PATCH 20/22] Add function to check monkey pickup box match --- TombEngine/Objects/TR3/Entity/tr3_monkey.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp index 32a21d16fa..ca519c0c70 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp @@ -104,6 +104,15 @@ namespace TEN::Entities::Creatures::TR3 return SameZone(creature, target); } + bool IsMonkeyPickupInSameBox(ItemInfo* item, CreatureInfo* creature) + { + auto* enemy = creature->Enemy; + if (enemy == nullptr) + return false; + + return item->BoxNumber == enemy->BoxNumber; + } + void UpdateMonkeyPickupTarget(ItemInfo* item, CreatureInfo* creature) { if (item->CarriedItem != NO_VALUE) @@ -461,9 +470,11 @@ namespace TEN::Entities::Creatures::TR3 if (Random::TestProbability(1 / 128.0f)) item->Animation.TargetState = MONKEY_STATE_SIT; } + else if (IsMonkeyPickupInSameBox(item, creature)) + item->Animation.TargetState = MONKEY_STATE_IDLE; else if (AI.bite && AI.distance < pow(682, 2)) item->Animation.TargetState = MONKEY_STATE_IDLE; - + break; case MONKEY_STATE_RUN_FORWARD: From c08e36a14a675a85995ff858e1ee007560b761c9 Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Sat, 16 May 2026 21:55:47 +0300 Subject: [PATCH 21/22] Revert box.cpp changes --- TombEngine/Game/control/box.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index aeb3fb49bc..eff00df64b 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -2281,8 +2281,7 @@ void GetAITarget(CreatureInfo* creature) { FindAITargetObject(creature, ID_AI_AMBUSH); } - else if (item->ObjectNumber != ID_MONKEY && - Vector3i::Distance(enemy->Pose.Position, item->Pose.Position) < REACHED_GOAL_RADIUS) + else if (Vector3i::Distance(enemy->Pose.Position, item->Pose.Position) < REACHED_GOAL_RADIUS) { TestTriggers(enemy, true); creature->ReachedGoal = true; @@ -2362,12 +2361,8 @@ void FindAITargetObject(CreatureInfo* creature, int objectNumber, int ocb, bool for (auto& aiObject : g_Level.AIObjects) { - bool matchingTriggerFlags = (aiObject.triggerFlags == ocb); - if (item.ObjectNumber == ID_MONKEY && objectNumber == ID_AI_AMBUSH) - matchingTriggerFlags = true; - if (aiObject.objectNumber == objectNumber && - matchingTriggerFlags && + aiObject.triggerFlags == ocb && aiObject.roomNumber != NO_VALUE) { int* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data(); From 23ef115fe198a54abe2f5c33ea7f79fab0817555 Mon Sep 17 00:00:00 2001 From: AlexxxQ <152021684+AlexxxQ@users.noreply.github.com> Date: Sat, 16 May 2026 22:00:22 +0300 Subject: [PATCH 22/22] Add ambush AI behavior for monkey entity --- TombEngine/Objects/TR3/Entity/tr3_monkey.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp index ca519c0c70..41612aecb3 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp @@ -189,7 +189,15 @@ namespace TEN::Entities::Creatures::TR3 } else { - GetAITarget(creature); + if (item->AIBits & AMBUSH) + { + if (creature->Enemy == nullptr || creature->Enemy->ObjectNumber != ID_AI_AMBUSH) + FindAITargetObject(creature, ID_AI_AMBUSH, 0, true); + } + else + { + GetAITarget(creature); + } if (creature->HurtByLara) creature->Enemy = LaraItem;