Merge branch 'develop' into sezz_fx_info_branch_port

This commit is contained in:
Sezz 2024-07-25 18:33:20 +10:00
commit 1f32ff025d
342 changed files with 7417 additions and 11663 deletions

109
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View file

@ -0,0 +1,109 @@
name: Bug report
description: Create a report to help us improve
title: "[ bug Report ]"
labels: Awaiting Triage
body:
- type: markdown
attributes:
value: |
Please follow this document in order to report a bug
- type: dropdown
attributes:
label: TombEngine version
description: |
Please select the TombEngine Version from the dropdown list.
options:
- v1.4
- v1.3
- v1.2
- v1.1
validations:
required: true
- type: dropdown
attributes:
label: Tomb Editor version
description: |
Please select the Tomb Editor version used from the dropdown list.
options:
- v1.7.1
- v1.7.0
- v1.6.9
- v1.6.8
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: 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
**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: 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: Screenshots
description: |
Please check this box if you have provided screenshots or any other media for this issue
**note** If you do not provide screenshots or anything else, it may be hard for devs to investigate
provide a link to your screenshot here or simply drag and drop your screenshot into this textbox
placeholder: |
A description of any additional content here.
validations:
required: true
- type: textarea
attributes:
label: Additional Content
description: |
Add any other context about the problem here.
* 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

17
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,17 @@
blank_issues_enabled: false
# Disable blank issue
blank_issues_enabled: false
# Bug Report Form
name: Bug Report
description: File a bug report.
title: "[Bug]: "
labels: [ "Awaiting Triage" ]
# Initial Message
body:
type: markdown
attributes:
value: Thanks for taking the time to fill out this bug report! Please use the steps below to log the report for the development team.

View file

@ -0,0 +1,34 @@
name: Feature Request
description: Suggest an idea for TombEngine
title: "[ Feature Request ] "
labels: Awaiting Triage
body:
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
placeholder: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
placeholder: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
attributes:
label: Additional context
placeholder: Add any other context or screenshots about the feature request here. (as a link to screenshot or drop the screenshot into this textbox)
validations:
required: false

52
AUTHORS.md Normal file
View file

@ -0,0 +1,52 @@
# Credit List
This is the credit list of **all** the people who contributed to TombEngine in any shape or form.
## Developers
- MontyTRC (Project Leader)
- Gancian (general coding)
- Krystian (general coding)
- Kubsy (Some cleanups and fixes)
- l.m. (general coding, Lua enhancements, bug fixing)
- Lwmte (sound refactoring, general coding, code cleanups, bug fixing)
- Moooonyeah (Jumanji) (entity decompilation)
- Raildex (renderer refactoring, particle coding, general coding)
- RicardoLuis0 (general coding)
- Sezz (player state refactoring, general coding, code cleanups, bug fixing, assets)
- Squidshire (Hispidence) (Lua implementation, bug fixing)
- Stranger1992 (sound asset refactoring and organisation, assets)
- TokyoSU (entity and vehicle decompilation)
- Tomo (general coding, bug fixing)
- Troye (general coding, refactoring)
- WolfCheese (general coding)
## Testers
- Adngel
- Caesum
- Dustie
- GeckoKid
- JoeyQuint
- Kamillos
- Kubsy
- LGG_PRODUCTION
- Lore
- RemRem
- Stranger1992
- WolfCheese
## Assets and Miscellaneous
- Geckokid (Sprites and test level creator).
### Animations
- SrDanielPonces (Diagonal shimmy transitions, backwards monkey swinging)
- Krystian (Flexibility crawlspace, slope climbing animations)
- Sezz (Additional Animations for player state refactoring)
- Naotheia (Underwater puzzle placement, crouch 180° turn, crawl 180° turn, water surface 180° turn)
- JoeyQuint (Standing 180° turn, monkey swing 180° turn)
### TombEngine Marketing
- Kubsy (Twitter and forum posts)
- Stranger1992 (This website, Facebook, Instagram, Youtube and Twitch.

View file

@ -1,16 +1,70 @@
Version 1.4 # Changelog
===========
Here you will find the full changelog of TEN's releases from Version 1.0 and up
The dates are in European standard format where date is presented as **YYYY-MM-DD**
TombEngine releases are located in this repository (alongside with Tomb Editor): https://github.com/TombEngine/TombEditorReleases
## Version 1.5 - xxxx-xx-xx
### Bug fixes
* Fixed original issue with classic switch off trigger incorrectly activating some trigger actions.
* Fixed incorrect diving animation when swandiving from a high place.
* Fixed camera rotating with the player's hips when climbing out of water.
* Fixed AI for TR2 skidoo driver and worker with shotgun.
* Fixed ember emitter crashing when ocb is between -1 and -10.
* Fixed electric cleaner and squishy block not detecting collision with certain block heights.
* Fixed squishy blocks crashing the level.
* Fixed Larson and Pierre pathfinding.
* Fixed torch flame delay when the player throws or drops a torch.
* Fixed dart emitters failing with antitrigger.
* Fixed homing dart emitter spawning darts continously when player is on its trigger.
* Fixed four blade trap floor and ceiling collision.
* Fixed Joby spikes collision and deformation.
* Fixed sentry gun joint rotation.
* Fixed teeth spikes not triggering the player impale animation.
* Fixed TR4 mine crash with OCB 1 when triggered.
### Features/Amendments
* Changed Rome Hammer to not hurt player whilst deactivated.
* Changed Statue with blade damage, from 20 to 200.
* Enhaced Rolling Spindle detection to avoid them going down through pits.
* Enhaced Sentry Guns, with a new ItemFlags[3], to contain the ID of the inventory item that deactivates the sentry guns ( by default PUZZLE_ITEM5 )
* Enhaced Dart Emitter, with a new ItemFlags[0], to contain the number of frames between shots ( by default 32 in dart emitter, and 24 in homing dar emitter ).
* Add new sound conditions: quicksand and Underwater.
- Quicksand - sound effect plays when a moveable is in quicksand.
- Underwater - sound plays when the camera is submerged.
* Changed Water sound condition to ShallowWater.
### Lua API changes
* Added Inventory.GetUsedItem(), Inventory.SetUsedItem() and Inventory.ClearUsedItem() functions.
* Added Input.KeyClearAll()
* Removed anims.monkeyAutoJump. It is now a player menu configuration.
## [Version 1.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.1) - 2024-04-21
### Bug Fixes
* Fixed drawing of display sprites in title level. * Fixed drawing of display sprites in title level.
* Fixed drawing of smoke sprites and various other sprites. * Fixed drawing of smoke sprites and various other sprites.
* Fixed drawing of transparent surfaces when debris are present in scene.
* Fixed player holster state and current vehicle not preserved correctly on level jump. * Fixed player holster state and current vehicle not preserved correctly on level jump.
* Fixed diving when swimming over sinks.
* Fixed fire item effect not extinguishing in water.
* Fixed fade-in and fade-out effects not canceling correctly when next level is loaded. * Fixed fade-in and fade-out effects not canceling correctly when next level is loaded.
* Fixed shadows still being visible after shattering a moveable. * Fixed shadows still being visible after shattering a moveable.
* Fixed FOV interpolation at the end of the flyby sequence. * Fixed FOV interpolation at the end of the flyby sequence.
* Fixed sounds resuming in pause mode while switching between apps. * Fixed sounds resuming in pause mode while switching between apps.
* Fixed slide directions. * Fixed slide directions.
* Fixed occasional collision warnings in a log when teeth spikes object was activated. * Fixed occasional collision warnings in a log when teeth spikes object was activated.
* Fixed climbable pushables collision during continuous pulling action.
* Fixed collision for solid static meshes with zero collision box.
* Fixed bottom collision for solid static meshes.
* Fixed T-Rex's head rotation.
### Features/Amendments
* Auto-switch to a crawl state if player start position is in a crawlspace. * 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: * Revise wall spikes:
- Wall spikes now stop when they touch a pushable, another spike wall or a normal wall. - Wall spikes now stop when they touch a pushable, another spike wall or a normal wall.
- Wall spikes will shatter any shatter in its path. - Wall spikes will shatter any shatter in its path.
@ -23,16 +77,17 @@ Version 1.4
* Added TR3 Winston (requires updated TEN .wad2 on TombEngine.com). * Added TR3 Winston (requires updated TEN .wad2 on TombEngine.com).
* Added TR4 squishy blocks (requires updated TEN .wad2 on TombEngine.com). * Added TR4 squishy blocks (requires updated TEN .wad2 on TombEngine.com).
Lua API changes: ### Lua API changes
* Added resetHub flag to Flow.Level, which allows to reset hub data. * Added resetHub flag to Flow.Level, which allows to reset hub data.
* Added Flow.GetFlipMapStatus() function to get current flipmap status. * Added Flow.GetFlipMapStatus() function to get current flipmap status.
* Added Moveable:GetMeshCount() function to get number of moveable meshes. * Added Moveable:GetMeshCount() function to get number of moveable meshes.
* Added timeout parameter for Moveable:Enable() function.
* Added Static:GetHP() and Static:SetHP() functions to change shatterable static mesh hit points. * Added Static:GetHP() and Static:SetHP() functions to change shatterable static mesh hit points.
* Fixed Moveable:SetOnCollidedWithObject() callback. * Fixed Moveable:SetOnCollidedWithObject() callback.
Version 1.3 ## [Version 1.3](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7) - 2024-01-06
===========
### Bug Fixes
* Fixed crash if title logo is removed from Textures folder. * Fixed crash if title logo is removed from Textures folder.
* Fixed crash if unknown player state ID is encountered. * Fixed crash if unknown player state ID is encountered.
* Fixed bug with OCB 2 on pushables, and some other pushable bugs. * Fixed bug with OCB 2 on pushables, and some other pushable bugs.
@ -52,7 +107,9 @@ Version 1.3
* Fixed big static objects affected wrongly by dynamic lights. * Fixed big static objects affected wrongly by dynamic lights.
* Fixed legacy trigger leveljumps ignoring provided level index. * Fixed legacy trigger leveljumps ignoring provided level index.
* Fixed incorrect light collection in some cases. * Fixed incorrect light collection in some cases.
* Fixed normal mapping for rooms, items, and statics. * Fixed normal mapping for rooms, items, and statics.'
### Features/Amendments
* Added ambient occlusion (SSAO). * Added ambient occlusion (SSAO).
* Added new post-process workflow (monochrome, negative, exclusion) with tinting. * Added new post-process workflow (monochrome, negative, exclusion) with tinting.
* Added SMAA antialiasing instead of MSAA. * Added SMAA antialiasing instead of MSAA.
@ -76,7 +133,7 @@ Version 1.3
- OCB 0: Wolf starts in walking animation, ready to chase Lara. - OCB 0: Wolf starts in walking animation, ready to chase Lara.
- OCB 1: Wolf starts in sleeping animation. - OCB 1: Wolf starts in sleeping animation.
Lua API changes: ### Lua API changes
* Added Lara:GetInteractedMoveable() which returns currently interacted moveable by Lara. * Added Lara:GetInteractedMoveable() which returns currently interacted moveable by Lara.
* Added Moveable:SetStatus() to set the current status of the moveable. * Added Moveable:SetStatus() to set the current status of the moveable.
* Added Room:GetColor() to get room's ambient light color. * Added Room:GetColor() to get room's ambient light color.
@ -84,9 +141,9 @@ Lua API changes:
* Added View.GetCameraPosition(), View.GetCameraTarget() and View.GetCameraRoom() functions. * Added View.GetCameraPosition(), View.GetCameraTarget() and View.GetCameraRoom() functions.
* Added View.SetPostProcessMode(), View.SetPostProcessStrength() and View.SetPostProcessTint() functions. * Added View.SetPostProcessMode(), View.SetPostProcessStrength() and View.SetPostProcessTint() functions.
Version 1.2 ## [Version 1.2](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.9) - 2023-11-11
===========
### Bug Fixes
* Fix burning torch not working properly if there are more than 256 objects in a level. * Fix burning torch not working properly if there are more than 256 objects in a level.
* Fix grenade and rocket projectiles smoke offset in certain directions. * Fix grenade and rocket projectiles smoke offset in certain directions.
* Fix projectiles flying through animating objects. * Fix projectiles flying through animating objects.
@ -110,30 +167,32 @@ Version 1.2
* Fix non-elevated combat camera. * Fix non-elevated combat camera.
* Fix camera snap when disengaging the look-around mode. * Fix camera snap when disengaging the look-around mode.
* Fix TR4 mapper not being visible. * Fix TR4 mapper not being visible.
### Features/Amendments
* Improve head-on wall collision. * Improve head-on wall collision.
* Overhaul pushables: * Overhaul pushables:
- Separate climbable and non-climbable pushable object slots. - Separate climbable and non-climbable pushable object slots.
- Add new pushable OCB to manipulate pushable properties. - Add new pushable OCB to manipulate pushable properties.
- Add new animations for pushing pushables off edgees (TR1-3 and TR4-5 versions). - Add new animations for pushing pushables off edgees (TR1-3 and TR4-5 versions).
- Fix pushables not working with raising blocks. - Fix pushables not working with raising blocks.
- Fix miscellaneous pushable bugs. - Fix miscellaneous pushable bugs.
* Overhaul look-around feature: * Overhaul look-around feature:
- Allow for more consistent and wider viewing angles while crawling, crouching, and hanging. - Allow for more consistent and wider viewing angles while crawling, crouching, and hanging.
- Improve look camera movement and control. - Improve look camera movement and control.
- Re-enable looking while performing up jump, backward jump, or backward crawl. - Re-enable looking while performing up jump, backward jump, or backward crawl.
- Add functionality to rotate slowly when holding Walk while using binoculars or lasersight. - Add functionality to rotate slowly when holding Walk while using binoculars or lasersight.
* Add target highlighter system with toggle in Sound and Gameplay settings. * Add target highlighter system with toggle in Sound and Gameplay settings.
* Add sprint slide state 191. * Add sprint slide state 191.
* Add swinging blade. * Add swinging blade.
* Add crumbling platform and add new OCBs for behaviour: * Add crumbling platform and add new OCBs for behaviour:
- OCB 0: Default behaviour. When the player steps on the platform, it will shake and crumble after 1.2 seconds. - OCB 0: Default behaviour. When the player steps on the platform, it will shake and crumble after 1.2 seconds.
- OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB. - OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB.
- A positive value results in activation via player collision. - A positive value results in activation via player collision.
- A negative value requires a trigger to activate. - A negative value requires a trigger to activate.
* Add basic mouse input handling. Allows for binding of mouse inputs in control settings. * Add basic mouse input handling. Allows for binding of mouse inputs in control settings.
* Add settings for Mouse Sensitivity and Mouse Smoothing (not used in-game yet). * Add settings for Mouse Sensitivity and Mouse Smoothing (not used in-game yet).
Lua API changes: ### Lua API changes
* Split and organize functions in `Misc` namespace to appropriate new namespaces. * Split and organize functions in `Misc` namespace to appropriate new namespaces.
* Make Vec2 and Vec3 objects float-based instead of integer-based. * Make Vec2 and Vec3 objects float-based instead of integer-based.
* Add DisplaySprite object. * Add DisplaySprite object.
@ -159,9 +218,9 @@ Lua API changes:
* Add DisplayString::SetScale() function to resize text. * Add DisplayString::SetScale() function to resize text.
* Add DisplayString::GetScale() function to get text scale. * Add DisplayString::GetScale() function to get text scale.
Version 1.1.0 ## [Version 1.1.0](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.8) - 2023-07-29
==============
### Bug Fixes
* Fix enemies shooting Lara through static meshes and moveables. * Fix enemies shooting Lara through static meshes and moveables.
* Fix skeletons and mummies not being affected by explosive weapons. * Fix skeletons and mummies not being affected by explosive weapons.
* Fix crash on loading if static meshes with IDs above maximum are present. * Fix crash on loading if static meshes with IDs above maximum are present.
@ -191,6 +250,8 @@ Version 1.1.0
* Fix room collector freezing game on some occasions. * Fix room collector freezing game on some occasions.
* Fix incorrect culling for scaled static meshes. * Fix incorrect culling for scaled static meshes.
* Fix normal mapping. * Fix normal mapping.
### Features/Amendments
* Add ability to save screenshot in the "Screenshots" subfolder by pressing the "Print screen" key. * 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. * Implement separate audio track channel for playing voiceovers with subtitles in .srt format.
* Don't stop ambience when Lara dies. * Don't stop ambience when Lara dies.
@ -208,15 +269,15 @@ Version 1.1.0
* Add TR1 skateboard kid. * Add TR1 skateboard kid.
* Add TR1 Kold. * Add TR1 Kold.
Lua API changes: ### Lua API changes
* Add soundtrack functions: * Add soundtrack functions:
- Misc::GetAudioTrackLoudness() for getting current loudness of a given track type. - Misc::GetAudioTrackLoudness() for getting current loudness of a given track type.
- Misc::IsAudioTrackPlaying() for checking if a given track type is playing. - Misc::IsAudioTrackPlaying() for checking if a given track type is playing.
- Misc::GetCurrentSubtitle() for getting current subtitle string for the voice track. - Misc::GetCurrentSubtitle() for getting current subtitle string for the voice track.
Version 1.0.9 ## [Version 1.0.9](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.7) - 2023-06-03
=============
### Bug fixes
* Fix cold bar triggered in non-water rooms. * Fix cold bar triggered in non-water rooms.
* Fix spiky wall speed value and change it via OCB number or Lua (Moveable::SetItemFlags[0]). * Fix spiky wall speed value and change it via OCB number or Lua (Moveable::SetItemFlags[0]).
* Fix bats emitter crashing the game if little beetle object does not exist in wad. * Fix bats emitter crashing the game if little beetle object does not exist in wad.
@ -237,6 +298,8 @@ Version 1.0.9
* Fix grenade firing angle. * Fix grenade firing angle.
* Fix rendering for static meshes with custom blending modes and alpha transparency. * Fix rendering for static meshes with custom blending modes and alpha transparency.
* Fix inconsistent multiline string spacing on different display modes. * Fix inconsistent multiline string spacing on different display modes.
### Features/Amendments
* Remove search object 4 hardcoded meshswap activated with a flipmap. * Remove search object 4 hardcoded meshswap activated with a flipmap.
* Add TR1 cowboy. * Add TR1 cowboy.
* Add TR3 wall mounted blade. * Add TR3 wall mounted blade.
@ -262,7 +325,7 @@ Version 1.0.9
* Add "Reset to defaults" entry to controls menu and automatically bind XBOX gamepad profile if connected. * Add "Reset to defaults" entry to controls menu and automatically bind XBOX gamepad profile if connected.
* Add 64-bit executable and place both 32-bit and 64-bit versions into /Bin subdirectory. * Add 64-bit executable and place both 32-bit and 64-bit versions into /Bin subdirectory.
Lua API changes: ### Lua API changes
* Add Vec2 class. * Add Vec2 class.
* Add function String::SetTranslated(). * Add function String::SetTranslated().
* Add function Misc::IsStringDisplaying(). * Add function Misc::IsStringDisplaying().
@ -272,9 +335,9 @@ Lua API changes:
- PRESAVE, POSTSAVE - PRESAVE, POSTSAVE
- PRELOAD, POSTLOAD - PRELOAD, POSTLOAD
Version 1.0.8 ## [Version 1.0.8](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.6) - 2023-04-10
=============
### Bug fixes
* Fix bubbles phasing through ceilings. * Fix bubbles phasing through ceilings.
* Fix object camera not clearing at level end. * Fix object camera not clearing at level end.
* Fix double breath sound effect when coming up for air. * Fix double breath sound effect when coming up for air.
@ -293,6 +356,8 @@ Version 1.0.8
- Imp is also scared of of the player if holding a lit torch. - Imp is also scared of of the player if holding a lit torch.
- Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr5_Imp.wad2 - 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. * Fix and improve wraith tails.
### Features/Amedments
* Add dedicated WRAITH_TRAP object with enhanced effects. * Add dedicated WRAITH_TRAP object with enhanced effects.
- OCB 0: Effect disabled. - OCB 0: Effect disabled.
- OCB 1: Effect enabled. - OCB 1: Effect enabled.
@ -313,13 +378,13 @@ Version 1.0.8
* Restored inventory compass. * Restored inventory compass.
* Allow dynamic segment count for hair object. * Allow dynamic segment count for hair object.
Lua API changes: ### Lua API changes
* Add function Misc::IsSoundPlaying() * Add function Misc::IsSoundPlaying()
* Add function DisplayString::SetFlags() * Add function DisplayString::SetFlags()
Version 1.0.7 ## [Version 1.0.7](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.5) - 2023-02-26
=============
### Bug fixes
* Fix spark particles not being cleared on level reload. * Fix spark particles not being cleared on level reload.
* Fix visible but inactive enemies (e.g. Shiva or Xian guardians) taking damage. * Fix visible but inactive enemies (e.g. Shiva or Xian guardians) taking damage.
* Fix blockable LOT type enemies (e.g. T-Rex and Shiva) not being able to step up 1 click or drop 2 clicks. * Fix blockable LOT type enemies (e.g. T-Rex and Shiva) not being able to step up 1 click or drop 2 clicks.
@ -340,6 +405,8 @@ Version 1.0.7
- Killing move for spear used wrong value. - Killing move for spear used wrong value.
* Fix TR3 big gun spawning rocket with 0 life which caused an immediate explosion. * Fix TR3 big gun spawning rocket with 0 life which caused an immediate explosion.
* Fix TR3 Tony and add boss effect for him. * Fix TR3 Tony and add boss effect for him.
### Features/Amendments
* Add TR3 civvy. * Add TR3 civvy.
* Add TR3 electric cleaner. * Add TR3 electric cleaner.
* Add TR3 Sophia Leigh with following OCBs: * Add TR3 Sophia Leigh with following OCBs:
@ -357,14 +424,14 @@ Version 1.0.7
* Prevent Lara from drawing weapons during parallel bar swinging. * Prevent Lara from drawing weapons during parallel bar swinging.
* Further renderer performance optimizations and bugfixes. * Further renderer performance optimizations and bugfixes.
Lua API changes: ### Lua API changes
* Fix Camera:SetPosition not updating camera position when it is played simultaneously. * Fix Camera:SetPosition not updating camera position when it is played simultaneously.
* Add Moveable:GetAirborne and Moveable:SetAirborne. * Add Moveable:GetAirborne and Moveable:SetAirborne.
* Add Moveable:GetLocationAI and Moveable:SetLocationAI. * Add Moveable:GetLocationAI and Moveable:SetLocationAI.
Version 1.0.6 ## [Version 1.0.6](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.4) - 2023-01-29
=============
### Bug FIxes
* Fix major pathfinding bug which could have caused lots of issues with enemy behaviour. * Fix major pathfinding bug which could have caused lots of issues with enemy behaviour.
* Fix potential random crashes due to incorrect rendering behaviour. * Fix potential random crashes due to incorrect rendering behaviour.
* Fix savegame crash for disabled enemies with partially set activation mask. * Fix savegame crash for disabled enemies with partially set activation mask.
@ -390,6 +457,8 @@ Version 1.0.6
* Fix grenade launcher super ammo emitting too many fragments. * Fix grenade launcher super ammo emitting too many fragments.
* Fix grenade and rocket launcher lighting. * Fix grenade and rocket launcher lighting.
* Fix ceiling trapdoor and floor trapdoor that Lara couldn't open manually. * Fix ceiling trapdoor and floor trapdoor that Lara couldn't open manually.
### Features/Amendments
* Make enemies drop pickups at first available bounding box corner point, not centerpoint. * Make enemies drop pickups at first available bounding box corner point, not centerpoint.
* Restore original volumetric explosion effects. * Restore original volumetric explosion effects.
* Add TR3 lizard and Puna. * Add TR3 lizard and Puna.
@ -401,7 +470,7 @@ Version 1.0.6
* Lua Moveable functions Enable and Disable now correctly trigger and antitrigger the moveable. * Lua Moveable functions Enable and Disable now correctly trigger and antitrigger the moveable.
* Improve level loading speed a lot. * Improve level loading speed a lot.
Lua API changes: ### Lua API changes
* Moveable:SetVisible has been added. MakeInvisible is now an alias for SetVisible(false). * Moveable:SetVisible has been added. MakeInvisible is now an alias for SetVisible(false).
* Moveable:MeshIsVisible is now GetMeshVisible. * Moveable:MeshIsVisible is now GetMeshVisible.
* Moveable:SetMeshVisible has been added to replace ShowMesh/HideMesh. * Moveable:SetMeshVisible has been added to replace ShowMesh/HideMesh.
@ -413,9 +482,9 @@ Lua API changes:
* Add new function Misc::GetCameraType() * Add new function Misc::GetCameraType()
* Add new functions Moveable:GetAirborne() and Moveable:SetAirborne(bool input) * Add new functions Moveable:GetAirborne() and Moveable:SetAirborne(bool input)
Version 1.0.5 ## [Version 1.0.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.3) - 2022-12-30
=============
### Bug fixes
* Fix combined items not existing in inventory upon game reload. * Fix combined items not existing in inventory upon game reload.
* Fix classic rollingball not behaving properly in rooms beyond the distance of 32 blocks. * Fix classic rollingball not behaving properly in rooms beyond the distance of 32 blocks.
* Fix rollingball not killing Lara under certain movement angles. * Fix rollingball not killing Lara under certain movement angles.
@ -440,6 +509,8 @@ Version 1.0.5
* Fix rare crash when smash item is inside a wall and add warning log for the scenario. * Fix rare crash when smash item is inside a wall and add warning log for the scenario.
* Fix bone rotations of some entities. * Fix bone rotations of some entities.
* Fix Lara's animation for cog switch release. * Fix Lara's animation for cog switch release.
### Features/Amendments
* Added new OCB to cog switch object: * Added new OCB to cog switch object:
- Use OCB 0 to have the traditional behaviour. - Use OCB 0 to have the traditional behaviour.
- Use any other OCB to can use the Cog Switch without need of any door linked. - Use any other OCB to can use the Cog Switch without need of any door linked.
@ -448,12 +519,12 @@ Version 1.0.5
* Draw real mesh for darts. * Draw real mesh for darts.
* Added warning log when one slot requires another slot which is missing. * Added warning log when one slot requires another slot which is missing.
Lua API changes: ### Lua API changes
* Add new Room class and several methods for it. * Add new Room class and several methods for it.
Version 1.0.4 ## [Version 1.0.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.2) - 2022-12-16
=============
### Features/Amendments
* Add generic assignable effects for moveables - fire, sparks, smoke and laser / electric ignite. * 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 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). * Add wireframe mode and other visual debug information (switch by F10/F11 debug page scroll hotkeys).
@ -475,6 +546,8 @@ Version 1.0.4
* SAS enhancements: * SAS enhancements:
- Fix grenade shooting. - Fix grenade shooting.
- Fix AI_MODIFY and AI_GUARD behaviour. - Fix AI_MODIFY and AI_GUARD behaviour.
### Bug fixes
* Fix choppy camera movement in several cases. * Fix choppy camera movement in several cases.
* Fix Lara's vertical position when shimmying around steep slope corners. * Fix Lara's vertical position when shimmying around steep slope corners.
* Fix legacy pickup triggers not working in certain cases. * Fix legacy pickup triggers not working in certain cases.
@ -503,7 +576,7 @@ Version 1.0.4
* Fix current soundtrack fading into silence if incoming one doesn't exist. * Fix current soundtrack fading into silence if incoming one doesn't exist.
* Fix crash if there is an attempt to display a string with missing characters. * Fix crash if there is an attempt to display a string with missing characters.
Lua API changes: ### Lua API changes
* Add new Volume class and several methods for it. * Add new Volume class and several methods for it.
* Add new Moveable functions: GetEffect, SetEffect and SetCustomEffect (for colored fire). * Add new Moveable functions: GetEffect, SetEffect and SetCustomEffect (for colored fire).
* Add new Lara functions: GetTarget, GetVehicle and TorchIsLit. * Add new Lara functions: GetTarget, GetVehicle and TorchIsLit.
@ -521,9 +594,9 @@ Lua API changes:
* Fix Rotation class using integers under the hood which prevented using fractional rotation values. * Fix Rotation class using integers under the hood which prevented using fractional rotation values.
* Fix distance tests failing on a very high distances. * Fix distance tests failing on a very high distances.
Version 1.0.3 ## [Version 1.0.3](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.1) - 2022-11-18
=============
### Features/Amendments
* Add ledge jumps (Lara object must be updated with new animations to make it work). * 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. * Allow any object slot to be used as a meshswap.
* Add OCB 1 for rollingball to make it silent. * Add OCB 1 for rollingball to make it silent.
@ -536,6 +609,8 @@ Version 1.0.3
* Improve game and inventory input handling. * Improve game and inventory input handling.
* Adjust sprint jump timing. * Adjust sprint jump timing.
* Backport DAMOCLES_SWORD from TR1. * Backport DAMOCLES_SWORD from TR1.
### Bug fixes
* Fix going into inventory and load/save dialogs during fade-ins and fade-outs. * Fix going into inventory and load/save dialogs during fade-ins and fade-outs.
* Fix savegames not preserving save number and game timer. * Fix savegames not preserving save number and game timer.
* Fix dodgy weapon lock angle constraints. * Fix dodgy weapon lock angle constraints.
@ -567,7 +642,7 @@ Version 1.0.3
* Fix SAS_DRAG_BLOKE object interaction. * Fix SAS_DRAG_BLOKE object interaction.
* Fix KILLER_STATUE not triggering. * Fix KILLER_STATUE not triggering.
Lua API changes: ### Lua API changes
* A new class has been added, LaraObject, for Lara-specific functions. The built-in "Lara" variable now uses this class. * A new class has been added, LaraObject, for Lara-specific functions. The built-in "Lara" variable now uses this class.
* Add functions for Lara object: * Add functions for Lara object:
- GetPoison / SetPoison - GetPoison / SetPoison
@ -587,9 +662,9 @@ Lua API changes:
* Add SetTotalSecretCount option to gameflow script to set overall amount of secrets. * Add SetTotalSecretCount option to gameflow script to set overall amount of secrets.
* Raised the maximum value on Moveable.SetHP to 32767 (its internal numeric maximum). * Raised the maximum value on Moveable.SetHP to 32767 (its internal numeric maximum).
Version 1.0.2 ## [Version 1.0.2](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6) - 2022-09-16
=============
### Features/Amendments
* Fix removing Pistols with TakeItem and SetItemCount. * Fix removing Pistols with TakeItem and SetItemCount.
* Allow saving and loading of Vec3s in LevelVars and GameVars. * Allow saving and loading of Vec3s in LevelVars and GameVars.
* Support volume triggers made with node editor. * Support volume triggers made with node editor.
@ -613,7 +688,8 @@ Version 1.0.2
- 5 for valve turn - 5 for valve turn
- 6 for hole switch - 6 for hole switch
- any other OCBs play corresponding switch on anim or OCB+1 switch off anim. - any other OCBs play corresponding switch on anim or OCB+1 switch off anim.
### Bug fixes
* Fix incorrect pole mounting. * Fix incorrect pole mounting.
* Fix zeroed forward velocity upon landing. * Fix zeroed forward velocity upon landing.
* Fix incorrect behaviour when falling on statics from the top after monkeyswing. * Fix incorrect behaviour when falling on statics from the top after monkeyswing.
@ -627,7 +703,7 @@ Version 1.0.2
* Fix stargate blades needlessly pushing the player around while hardly doing any damage. * Fix stargate blades needlessly pushing the player around while hardly doing any damage.
* Fix weapon hotkeys and add missing crossbow hotkey. * Fix weapon hotkeys and add missing crossbow hotkey.
Lua API changes: ### Lua API changes
* Util.ShortenTENCalls no longer needs to be called; it is now automatic for both level scripts and Gameflow.lua. * Util.ShortenTENCalls no longer needs to be called; it is now automatic for both level scripts and Gameflow.lua.
* Flow.InvID has been removed; any function taking a pickup (e.g. GiveItem) now takes an Objects.ObjID instead. * Flow.InvID has been removed; any function taking a pickup (e.g. GiveItem) now takes an Objects.ObjID instead.
* Add Enable, Disable, GetActive, Get/SetSolid functions for static meshes. * Add Enable, Disable, GetActive, Get/SetSolid functions for static meshes.
@ -641,12 +717,14 @@ Lua API changes:
* Rework GiveItem, TakeItem, and SetItemCount (e.g. SetItemCount with a value of -1 can give infinite ammo/consumables). * Rework GiveItem, TakeItem, and SetItemCount (e.g. SetItemCount with a value of -1 can give infinite ammo/consumables).
Version 1.0.1 ## [Version 1.0.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.5.2) - 2022-08-16
=============
### Features
* Added antialiasing support. * Added antialiasing support.
* Added static mesh scaling support. * Added static mesh scaling support.
* Added free rotation for teeth spikes instead of using OCB codes. * Added free rotation for teeth spikes instead of using OCB codes.
### Bug fixes
* Fix some issues with shimmying between diagonal ledges and walls. * Fix some issues with shimmying between diagonal ledges and walls.
* Fix rope transparency. * Fix rope transparency.
* Fix objects disappearing under certain angles at the edges of the screen. * Fix objects disappearing under certain angles at the edges of the screen.
@ -664,6 +742,8 @@ Version 1.0.1
* Fix falling through twoblock platform on room number change. * Fix falling through twoblock platform on room number change.
* Fix falling block breaking too early if placed on a vertical portal. * Fix falling block breaking too early if placed on a vertical portal.
* Fix crashes when loading image files are missing. * Fix crashes when loading image files are missing.
### Amendments
* Disable trigger check for puzzle holes. * Disable trigger check for puzzle holes.
* Clear locusts and other swarm enemies on level reload. * Clear locusts and other swarm enemies on level reload.
* Enhance cobra AI and fix targeting. * Enhance cobra AI and fix targeting.
@ -679,7 +759,6 @@ Version 1.0.1
* EventSequence.lua has been added and documented. * EventSequence.lua has been added and documented.
Version 1.0 ## [Version 1.0](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.5.1) - 2022-08-06
===========
First beta release. First beta release.

82
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,82 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
TombEngine Discord Server.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

377
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,377 @@
What you will read under has been agreed by the current active team in order to keep a good flow of work and proper management. You are welcome to put suggestions in the comments in order to discuss it with the TEN team.
# Contributor General rules
**1. Developer**
- If you wish to contribute to the TEN project, you must read and follow the coding convention used by the team, available here: https://github.com/MontyTRC89/TombEngine/blob/master/CONTRIBUTING.md#coding-conventions
- Do not work directly on master nor any branch which may be developed by several members. Create your branch, and name it with a clear description of the feature/set of features you are working on. You may regularly merge/rebase master or the active development branch with yours.
- Be respectful to other developers. Disagreements may occur on code style, changes, and should be discussed directly with the person who pushed or merged the commit/branch. If you are unsure of what some parts of the code means, or why a decision was made, contact the team as a whole so they can be explained to you.
- Do not make a pull request until your branch and features are extensively tested by the QA team. You are invited to test your feature set as much as you can too.
- If you are allowed to merge PRs, do not do such until code has been properly reviewed, preferably by more than one person.
- Merge PRs using **squash and merge** mode to preserve main branch history in original state. For squash merge commit, leave list of commits in commit description (GitHub does it automatically in both desktop client and web interface).
- Do not revert active development branch to a previous commit unless you are absolutely sure there will be no regressions. Do not revert parts of code to a previous state for the same reasons. In both of those cases, the entire team needs to be aware of it and agree on the decision.
- There are **two key branches**: `master` and `develop`. `master` is to be used for releases, and nothing else. `develop` is based on `master` and is used for active development, **Every PR should use it as its base**. To keep in sync, for release period they are to be merged in the following order: `develop` -> `master`.
**2. Tester**
- Be respectful with the developer you are testing the feature of.
- New features or bug fixes should be tested with several use cases, if not all possible use cases you can think of, of this feature. This avoids going back to it in the future because of missed test cases.
- Pay attention to any regressions. Developer may have touched part of the code that could break other linked or unlinked features. Features or bug fixes can't be considered done if there are any regression elsewhere.
## Issues handling
**1. General**
- Preferably, write one issue for one specific bug. List of bugs should stay rare, unless they are closely related to each other.
- Follow the progress of features/fixes on the project board.
- Do not assign a dev to an issue. They will do so themselves
- Carefully choose the labels you add to the issue
**2. Developer**
- Assign yourself to an issue you want to work on available in the To do column of the project board. Add this feature to the "In progress" column. Make the team know you are working on it both by adding a comment to it and via Discord, on the strategy channel of Tomb Engine.
- Regularly update the status of the task by adding comments. If you are stuck, let other people know if they can help.
- Do not pass an issue as done unless fixes or features are fully tested and merged with active dev branch
**3. Tester**
- Give the Developer as much detail as you can about the bug you're writing for, and an easy way to reproduce it.
- Update and comment the issue with the tests you've done and your findings after fixes.
# Coding Conventions
## General Rules
* **Do not silently comment out any lines or change operators or conditional statements**, even for testing purposes. Should you comment out certain code, also leave a comment beginning with `TODO` prefix, and date-author signature, like this:
``` c
// TODO: This code caused NPCs to run into walls, so it is disabled for now. -- Lwmte, 21.08.19
// GetBoundsAccurate(blah);
```
* **Do not modify existing code without a clear understanding of what it does.** Even if you think you are fixing bugs or restoring original behavior by reverting the code, consult the TombEngine discord before committing any changes. It is possible you may ruin the work of other people since the original Core Design codebase is very fragile and it is very easy to falsely identify code as bugged.
* **Do not introduce quick hacks to fix issues**. Hacks like `if (room == 314)` or `if (direction == WEST)` should be left in the history of bad Core Design coding practices.
* If code remains unimplemented or you need to visit another part of the code for a considerable amount of time to implement missing functionality for that first part of code, **leave a comment** prefixed `FIXME` and with a date-author signature, describing missing functionality that you are about to add:
``` c
// FIXME: I need to precisely calculate floor height, but for now let's keep original. -- Lwmte, 21.08.19
foo = GetFloor(blah);
```
* Make sure that **any new code is warning-free** before committing. Exceptions to this guideline include cases where fixing warnings would require you to make many changes outside of the new code, where the warning originates from a 3rd-party library, or where fixing the warning would make the code significantly less readable or performant.
* Avoid using magic numbers. Use constants or enums instead.
* Use **American English**.
## Parenthesis and new lines
Please do not write like this:
``` c
if (blah) {
foo;
foo_foo;
} else {
bar;
bar_bar; }
```
Write like this instead:
``` c
if (blah)
{
foo;
foo_foo;
}
else
{
bar;
bar_bar;
}
```
However, if you have only one line enclosed (i. e. only `foo`), and there are no following `else` or `else if` conditions, you may omit brackets and do it like this:
``` c
if (blah)
foo;
```
For one-line if statements, if the condition itself is multi-line, brackets should be added anyway to maintain readability:
```
if (item.RoomNumber == 123 || IsHeld(In::Action) &&
LaraHasEatenSandwich == SANDWICH_BACON)
{
CurrentLevel = 4;
}
```
Same rule applies to loops.
Avoid multiple nested conditional statements. Where possible, do an early exit from the code using the opposite statement instead of enclosing the statement body in an extra set of brackets. For example, instead of this:
``` c
if (blah)
{
if (foo)
bar;
}
```
Write this:
``` c
if (!blah || !foo)
return;
bar;
```
## Spacing
Don't condense arguments, loop statements, or any other code which requires splitting with commas/semicolons or operators like this:
``` c
void SomeLoop(int blah,int blah2)
{
for(int i=0;i<4;i++)
{
bar =blah;
foo= blah2;
}
}
```
Instead, separate everything with spaces **on the both sides** of operator, and **after** the comma or semicolon, like this:
``` c
void SomeLoop(int blah, int blah2)
{
for (int i = 0; i < 4; i++)
{
bar = blah;
foo = blah2;
}
}
```
The same rule applies to any code body, not only function declarations or loop statements.
## Tabulation and indents
When you have numerous assignment operations in a row, and/or their names are somewhat of equal length, and their data types are similar, align "left" and "right" parts of the assignment using tabs most of the way and spaces the rest of the way, like this:
``` c
float foo = 1.0;
float bar_bar = 1.0;
float foo_o_o = 1.0;
```
Or:
``` c
bar = foo-foo-foo;
foo_o = bar;
bar_r_r = foo;
```
In case you have pointers defined along with "normal" variables, the asterisk symbol must be placed instead of the last tab's space symbol (this also applies for class declarations and/or implementations), like this:
``` c
bar = foo_foo;
*foo = &bar_bar;
```
Of course, if one's left part is way longer than another one's left part, there's no need for such alignment, so you can leave it like this:
``` c
*foo_foo = &bar_foo;
bar->foo_foo_foo.blah_blah_bar_foo = 1.0;
foo_bar = 1.0;
```
In a switch case, each case body must be one tab further case's own label. Like this:
``` c
switch (blah)
{
case foo:
foo - foo - foo;
foo - foo - foo - foo;
break;
case bar:
bar - bar;
bar - bar - bar;
break;
}
```
If you need to enclose a particular case into a bracket block, put the body one tab further:
``` c
switch (blah)
{
case foo:
{
float bar;
bar = foo - foo - foo;
foo - foo - foo - foo = bar;
break;
}
case bar:
bar - bar;
bar - bar - bar;
break;
}
```
## Code splitting
If a code block becomes too large, it is recommended to split it into several "sub-blocks" with empty lines, taking each sub-block's meaning into account, like this:
``` c
foo = foo_foo + foo_foo_foo;
if (foo)
foo - foo - foo - foo - foo;
foo - foo = foo;
bar = (bar - bar > bar) ? (bar - bar) : 0;
bar - bar - bar = bar - bar - bar;
```
Conditional statements, if there are many, should be grouped according to their meaning. That is, if you are doing early exit from the function because of different conditions, group them as such:
``` c
if (coll.Floor > CLICK(1) && coll.Ceiling < CLICK(3) && bounds.Y1 > WALL_SIZE)
return false;
if (enemy.health <= 0 || lara.health <= 0 || collidedItems[0] == nullptr)
return false;
```
However, if there are few conditional statements, you can group them together. The rule of thumb is if there are more than three conditional statements and two of them are of a different kind, split them. This variant is allowed:
``` c
if (coll.Floor > CLICK(2) && coll.Ceiling < CLICK(4) || lara.health <= 0)
return false;
```
Sometimes IDA decompiled output generates "pascal-styled" or "basic-styled" variable declarations at the beginning of the function or code block. Like this:
``` c
int foo, bar = 0;
float blah;
...
blah = foo;
foo = bar;
...
```
**Please**, get rid of this style everywhere you see it and declare variables in the place narrowest to its actual usage, like this:
``` c
int foo = foo;
float blah = bar;
...
```
Let's cite [Google Style Guide](https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Local_Variables) here:
> Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
## Naming
* Use `auto` where possible and if the original type name is longer than `auto`. E.g. there is no point in changing `bool enabled = true` to `auto enabled = true`. Remember that C++ `auto` **is not similar to C#** `var`, and for referencing existing value with `auto`, you should add `&` to it in the end, e.g. `auto& item = g_Level.Items[itemIndex];` Also, for underlying pointer types, please write `auto*` instead of `auto`, even if it seems redundant.
* Avoid using Hungarian notation for function names. Currently, inconsistent notation indicates an unrefactored coding style, because the original code used inconsistent naming, like `SOUND_Stop()`, `have_i_got_object()` or `gar_SuperpackYXZ()`. These should eventually be eradicated along the course of code restyling. For new function names, `PascalCase` should be used.
* For global struct or class members or methods, `PascalCase` should be used. For local variable names, `camelCase` should be used. For global variables, which are temporary until full refactoring occurs, an exclusive case of Hungarian notation with the `g_` prefix (e.g. `g_Foo`) is permitted.
* Functions designed to take an enum argument should take the enum type itself instead of an int or short. `PassInfo(InfoEnum type)` is more readable and error-proof than `PassInfo(int type)`.
* Functions designed to return boolean value (0 or 1) should define return type as bool. Please don't use `int` return type and don't write `return 0` or `return 1` in bool return functions, use `return false` or `return true`.
* Use the following convention for Pointers and References: `Type* somePointer` / `Type& someReference`. Do not write this: `Type * somePointer` / `Type & someReference` or `Type *somePointer` / `Type &someReference`. Pointers and references are distinct types, hence why the notation for them should have the token on the side of the type.
* Avoid unscoped enum types. For scoped `enum class` types, use `PascalCase` without including enum prefix:
``` c
enum class WeatherType
{
None,
Rain,
Snow,
Cats,
Dogs
};
```
`ENUM_ALL_CAPS` primarily indicates old C-styled Core notation. For C-styled (unscoped) enum values themselves, `ALL_CAPS` may be used for now, along with enum prefix:
``` c
enum LaraWeaponType
{
WEAPON_NONE,
WEAPON_PISTOLS,
WEAPON_REVOLVER
};
```
## Data types
Avoid using `_t` data types, such as `uint8_t`, `int8_t`, `int16_t` etc. Use `unsigned char`, `signed char`, `short` etc. instead. If new variables or fields are introduced, prefer longer and more contemporary data types over deprecated ones. That is, if integer value is used, use `int` instead of `char` or `short`. If potentially fractional value is used (such as coordinates which are eventually multiplied or divided or transformed otherwise), prefer `float` over `int`.
For legacy functions and code paths, preserving original data types may be necessary. Special case are angle values - original games used weird `signed short` angle convention. So extra caution must be taken when writing code which operates on native TR angles, and it should always be kept in variables of `signed short` data type.
Prefer using references over pointers, both in function body and as arguments. When using references or pointers, prefix with `const` for read-only safety if the variable is not being written to.
## Casting
Prefer using C-styled casting instead of C++-styled casting where it is safe. While using it, avoid separating casting operator and variable with space:
`bar = (int)foo;`
For expressions, you can enclose expression into brackets to cast it:
`bar = int(foo + blah * 4);`
Using C++-styled casting is allowed when C-styled casting provides undefined or unacceptable behaviour.
## Includes
For header files from project itself, always use includes with quotes, not with brackets. Also avoid using Windows-specific `\` backslash symbols to specify subpaths, only use normal `/` slashes. Also please include full path to a header file and order includes alphabetically:
```c
#include "Game/effects/lightning.h"
#include "Specific/phd_math.h"
```
Includes with brackets are only allowed when using external libraries:
```c
#include <algorithm>
#include "Game/collision/collide.h"
```
## Namespaces
Don't shorten `std` namespace types and methods by using `using` directive. This is bad: `auto x = vector<int>();`
Leave them as is. This is good: `auto x = std::vector<int>();`
## Comments
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.
## 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.
Avoid making new branches based on unapproved epic PRs which are in the process of review. It may render your work useless if parent epic branch is unapproved and scrapped.

View file

@ -4,4 +4,5 @@ set LDOC_DIR=.\compiler\ldoc
set LUA_PATH=.\compiler\?.lua set LUA_PATH=.\compiler\?.lua
set LUA_CPATH=.\compiler\?.dll set LUA_CPATH=.\compiler\?.dll
.\compiler\lua.exe %LDOC_DIR%\\ldoc.lua %* .\compiler\lua.exe %LDOC_DIR%\\ldoc.lua %*
del output.xml
exit /b %ERRORLEVEL% exit /b %ERRORLEVEL%

View file

@ -12,7 +12,7 @@ new_type("luautil", "5 Lua utility modules", true)
not_luadoc = true not_luadoc = true
local version = "1.3" local version = "1.4"
project = "TombEngine" project = "TombEngine"
title = "TombEngine " .. version .. " Lua API" title = "TombEngine " .. version .. " Lua API"
description = "TombEngine " .. version .. " scripting interface" description = "TombEngine " .. version .. " scripting interface"

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>
@ -129,6 +129,10 @@
<td class="summary">Clear an action key.</td> <td class="summary">Clear an action key.</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="#KeyClearAll">KeyClearAll()</a></td>
<td class="summary">Clear all action keys.</td>
</tr>
<tr>
<td class="name" ><a href="#GetMouseDisplayPosition">GetMouseDisplayPosition()</a></td> <td class="name" ><a href="#GetMouseDisplayPosition">GetMouseDisplayPosition()</a></td>
<td class="summary">Get the display position of the cursor in percent.</td> <td class="summary">Get the display position of the cursor in percent.</td>
</tr> </tr>
@ -254,6 +258,21 @@
</dd>
<dt>
<a name = "KeyClearAll"></a>
<strong>KeyClearAll()</strong>
</dt>
<dd>
Clear all action keys.
</dd> </dd>
<dt> <dt>
<a name = "GetMouseDisplayPosition"></a> <a name = "GetMouseDisplayPosition"></a>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>
@ -124,6 +124,18 @@
<td class="name" ><a href="#SetItemCount">SetItemCount(objectID, count)</a></td> <td class="name" ><a href="#SetItemCount">SetItemCount(objectID, count)</a></td>
<td class="summary">Set the amount of an item in the player's inventory.</td> <td class="summary">Set the amount of an item in the player's inventory.</td>
</tr> </tr>
<tr>
<td class="name" ><a href="#GetUsedItem">GetUsedItem()</a></td>
<td class="summary">Get last item used in the player's inventory.</td>
</tr>
<tr>
<td class="name" ><a href="#SetUsedItem">SetUsedItem(objectID)</a></td>
<td class="summary">Set last item used in the player's inventory.</td>
</tr>
<tr>
<td class="name" ><a href="#ClearUsedItem">ClearUsedItem()</a></td>
<td class="summary">Clear last item used in the player's inventory.</td>
</tr>
</table> </table>
<br/> <br/>
@ -245,6 +257,70 @@
</dd>
<dt>
<a name = "GetUsedItem"></a>
<strong>GetUsedItem()</strong>
</dt>
<dd>
Get last item used in the player's inventory.
This value will be valid only for a single frame after exiting inventory, after which Lara says "No".
Therefore, this function must be preferably used either in OnLoop or OnUseItem events.
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
Last item used in the inventory.
</ol>
</dd>
<dt>
<a name = "SetUsedItem"></a>
<strong>SetUsedItem(objectID)</strong>
</dt>
<dd>
Set last item used in the player's inventory.
You will be able to specify only objects which already exist in the inventory.
Will only be valid for the next frame. If not processed by the game, Lara will say "No".
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">objectID</span>
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
Object ID of the item to select from inventory.
</li>
</ul>
</dd>
<dt>
<a name = "ClearUsedItem"></a>
<strong>ClearUsedItem()</strong>
</dt>
<dd>
Clear last item used in the player's inventory.
When this function is used in OnUseItem level function, it allows to override existing item functionality.
For items without existing functionality, this function is needed to avoid Lara saying "No" after using it.
</dd> </dd>
</dl> </dl>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>
@ -263,7 +263,8 @@ LOAD
SAVE SAVE
START START
END END
LOOP</pre> LOOP
USEITEM</pre>
<h3>Parameters:</h3> <h3>Parameters:</h3>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>
@ -109,7 +109,7 @@
<h2><a href="#Functions">Functions</a></h2> <h2><a href="#Functions">Functions</a></h2>
<table class="function_list"> <table class="function_list">
<tr> <tr>
<td class="name" ><a href="#ShowString">ShowString(str, time)</a></td> <td class="name" ><a href="#ShowString">ShowString(str, time, autoDelete)</a></td>
<td class="summary">Show some text on-screen.</td> <td class="summary">Show some text on-screen.</td>
</tr> </tr>
<tr> <tr>
@ -131,7 +131,7 @@
<dl class="function"> <dl class="function">
<dt> <dt>
<a name = "ShowString"></a> <a name = "ShowString"></a>
<strong>ShowString(str, time)</strong> <strong>ShowString(str, time, autoDelete)</strong>
</dt> </dt>
<dd> <dd>
Show some text on-screen. Show some text on-screen.
@ -151,6 +151,13 @@ If not given, the string will have an "infinite" life, and will show
until <a href="../1 modules/Strings.html#HideString">HideString</a> is called or until the level is finished. until <a href="../1 modules/Strings.html#HideString">HideString</a> is called or until the level is finished.
Default: nil (i.e. infinite) Default: nil (i.e. infinite)
</li> </li>
<li><span class="parameter">autoDelete</span>
<span class="types"><span class="type">bool</span></span>
should be string automatically deleted after timeout is reached.
If not given, the string will remain allocated even after timeout is reached, and can be
shown again without re-initialization.
Default: false
</li>
</ul> </ul>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>
@ -364,7 +364,7 @@ pickups, and Lara herself (see also <a href="../2 classes/Objects.LaraObject.htm
Use this to bring back original unswapped mesh</td> Use this to bring back original unswapped mesh</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="#Moveable:Enable">Moveable:Enable()</a></td> <td class="name" ><a href="#Moveable:Enable">Moveable:Enable(timeout)</a></td>
<td class="summary">Enable the item, as if a trigger for it had been stepped on.</td> <td class="summary">Enable the item, as if a trigger for it had been stepped on.</td>
</tr> </tr>
<tr> <tr>
@ -1861,13 +1861,20 @@ sas:SetPosition(newPos, <span class="keyword">false</span>)</pre>
</dd> </dd>
<dt> <dt>
<a name = "Moveable:Enable"></a> <a name = "Moveable:Enable"></a>
<strong>Moveable:Enable()</strong> <strong>Moveable:Enable(timeout)</strong>
</dt> </dt>
<dd> <dd>
Enable the item, as if a trigger for it had been stepped on. Enable the item, as if a trigger for it had been stepped on.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">timeout</span>
<span class="types"><span class="type">float</span></span>
time (in seconds) after which moveable automatically disables (optional).
</li>
</ul>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>
@ -100,27 +100,27 @@
<div id="content"> <div id="content">
<h1>Primitive Class <code>Color</code></h1> <h1>Primitive Class <code>Color</code></h1>
<p>An RGBA or RGB color.</p> <p>Represents an RGBA or RGB color.</p>
<p>Components are specified in bytes; all values are clamped to [0, 255].</p> <p> Components are specified in bytes. All values are clamped to the range [0, 255].</p>
<h2><a href="#Members">Members</a></h2> <h2><a href="#Members">Members</a></h2>
<table class="function_list"> <table class="function_list">
<tr> <tr>
<td class="name" ><a href="#r">r</a></td> <td class="name" ><a href="#r">r</a></td>
<td class="summary">(int) red component</td> <td class="summary">(int) Red component.</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="#g">g</a></td> <td class="name" ><a href="#g">g</a></td>
<td class="summary">(int) green component</td> <td class="summary">(int) Green component.</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="#b">b</a></td> <td class="name" ><a href="#b">b</a></td>
<td class="summary">(int) blue component</td> <td class="summary">(int) Blue component.</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="#a">a</a></td> <td class="name" ><a href="#a">a</a></td>
<td class="summary">(int) alpha component (255 = opaque, 0 = invisible)</td> <td class="summary">(int) Alpha component (0 = invisible, 255 = opaque).</td>
</tr> </tr>
</table> </table>
<h2><a href="#Functions">Functions</a></h2> <h2><a href="#Functions">Functions</a></h2>
@ -129,12 +129,6 @@
<td class="name" ><a href="#Color">Color(R, G, B)</a></td> <td class="name" ><a href="#Color">Color(R, G, B)</a></td>
<td class="summary"> <td class="summary">
</td>
</tr>
<tr>
<td class="name" ><a href="#Color">Color(R, G, B, A)</a></td>
<td class="summary">
</td> </td>
</tr> </tr>
<tr> <tr>
@ -157,7 +151,7 @@
<strong>r</strong> <strong>r</strong>
</dt> </dt>
<dd> <dd>
(int) red component (int) Red component.
@ -172,7 +166,7 @@
<strong>g</strong> <strong>g</strong>
</dt> </dt>
<dd> <dd>
(int) green component (int) Green component.
@ -187,7 +181,7 @@
<strong>b</strong> <strong>b</strong>
</dt> </dt>
<dd> <dd>
(int) blue component (int) Blue component.
@ -202,7 +196,7 @@
<strong>a</strong> <strong>a</strong>
</dt> </dt>
<dd> <dd>
(int) alpha component (255 = opaque, 0 = invisible) (int) Alpha component (0 = invisible, 255 = opaque).
@ -253,48 +247,6 @@
</dd>
<dt>
<a name = "Color"></a>
<strong>Color(R, G, B, A)</strong>
</dt>
<dd>
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">R</span>
<span class="types"><span class="type">int</span></span>
red component
</li>
<li><span class="parameter">G</span>
<span class="types"><span class="type">int</span></span>
green component
</li>
<li><span class="parameter">B</span>
<span class="types"><span class="type">int</span></span>
blue component
</li>
<li><span class="parameter">A</span>
<span class="types"><span class="type">int</span></span>
alpha component (255 is opaque, 0 is invisible)
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
A new Color object.
</ol>
</dd> </dd>
<dt> <dt>
<a name = "__tostring"></a> <a name = "__tostring"></a>
@ -311,7 +263,7 @@
<ul> <ul>
<li><span class="parameter">color</span> <li><span class="parameter">color</span>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span> <span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
this color This color.
</li> </li>
</ul> </ul>
@ -319,7 +271,7 @@
<ol> <ol>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
A string showing the r, g, b, and a values of the color A string representing the r, g, b, and a values of the color.
</ol> </ol>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" /> <link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head> </head>
<body> <body>

View file

@ -3,7 +3,7 @@
<html> <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head> <head>
<title>TombEngine 1.3 Lua API</title> <title>TombEngine 1.4 Lua API</title>
<link rel="stylesheet" href="ldoc.css" type="text/css" /> <link rel="stylesheet" href="ldoc.css" type="text/css" />
</head> </head>
<body> <body>
@ -100,7 +100,7 @@
<div id="content"> <div id="content">
<h2>TombEngine 1.3 scripting interface</h2> <h2>TombEngine 1.4 scripting interface</h2>
<p>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.</p> <p>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.</p>
<p>At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on <a href="https://www.tombengine.com">the TombEngine website</a>.</p> <p>At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on <a href="https://www.tombengine.com">the TombEngine website</a>.</p>
@ -244,7 +244,7 @@ local door = GetMoveableByName("door_type4_14")
<table class="module_list"> <table class="module_list">
<tr> <tr>
<td class="name" ><a href="3 primitive classes/Color.html">Color</a></td> <td class="name" ><a href="3 primitive classes/Color.html">Color</a></td>
<td class="summary">An RGBA or RGB color.</td> <td class="summary">Represents an RGBA or RGB color.</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="3 primitive classes/Rotation.html">Rotation</a></td> <td class="name" ><a href="3 primitive classes/Rotation.html">Rotation</a></td>

File diff suppressed because it is too large Load diff

10
IMPORTANT_LINKS.md Normal file
View file

@ -0,0 +1,10 @@
# Important Links
Here you will find other essential sources that TombEngine has.
- TombEngine website: https://tombengine.com/
- TombEngine Organization repository: https://github.com/TombEngine
- TombEngine LUA API webpage source: https://github.com/TombEngine/TombEngine.github.io
- TombEngine Assets used on the tombEngine website: https://github.com/TombEngine/Resources
- TombEngine releases: https://github.com/TombEngine/TombEditorReleases
- TombEditor repository: https://github.com/MontyTRC89/Tomb-Editor (A Tomb Raider Level Editor to build custom levels. Tomb Editor should be used to develop levels for TombEngine)

View file

@ -1,10 +1,8 @@
# TombEngine # TombEngine
In the year 2000, Core Design granted us a great gift: their TR4-based Level Editor, which allowed people to create custom levels. It was, unfortunately, quite limited, hence why over the decades it was upgraded massively with fan projects such as Tomb Raider Engine Patcher (TREP) and Tomb Raider Next Generation (TRNG). ![Logo](https://github.com/MontyTRC89/TombEngine/assets/80340234/f22c9ca9-7159-467f-b8ad-7bb32274a278)
- TREP was a tool which allowed modification of the executable to expand certain limits and implement new features.
- TRNG built upon TREP and provided many new tools, including a scripting language, expanding even more limits with its own .DLL.
Unfortunately, TRNG's toolset is poorly documented and not user-friendly; the program remains closed-source to this day and is in all practicality an abandonware. As a direct consequence, no one is able to fix the countless well-known bugs and issues extant in TRNG, rendering implementation of new features is impossible without an in-depth knowledge of C++ plugin creation and a solid understanding of the classic Tomb Raider engine's many idiosyncrasies. In the year 2000, Core Design granted us a great gift: their TR4-based Level Editor, which allowed people to create custom levels. It was, unfortunately, quite limited, hence why over the decades it was upgraded massively with fan patcher projects such as Tomb Raider Engine Patcher (TREP) and Tomb Raider Next Generation (TRNG).
TombEngine (TEN) is a new, open-source engine which aims to abolish all limits, fix bugs from the original games, introduce new features while refining old ones, and provide for a refined, user-friendly level creation process. Current support includes: TombEngine (TEN) is a new, open-source engine which aims to abolish all limits, fix bugs from the original games, introduce new features while refining old ones, and provide for a refined, user-friendly level creation process. Current support includes:
- Lua (as the native scripting language) - Lua (as the native scripting language)
@ -20,7 +18,7 @@ Tomb Engine should be used in conjuction with Tomb Editor. Tomb Editor is also o
# Compiling TombEngine # Compiling TombEngine
To compile TEN, ensure you have installed: To compile TEN, ensure you have installed:
- Microsoft Visual Studio - Microsoft Visual Studio
- TombEditor (if you would like to create and test levels) - Tomb Editor (if you would like to create and test levels)
Steps: Steps:
1) Clone the repository to your GitHub Desktop 1) Clone the repository to your GitHub Desktop
@ -40,54 +38,3 @@ Visual Studio may also warn about NuGet packages. To fix:
# Disclaimer # Disclaimer
We do not and have never worked for Core Design, Eidos Interactive, or Square Enix. This is a hobby project. Tomb Raider is a registered trademark of Square Enix; TombEngine is not be sold. The code is open-source to encourage contributions and to be used for study purposes. We are not responsible for illegal uses of this source code. This source code is released as-is and continues to be maintained by non-paid contributors in their free time. We do not and have never worked for Core Design, Eidos Interactive, or Square Enix. This is a hobby project. Tomb Raider is a registered trademark of Square Enix; TombEngine is not be sold. The code is open-source to encourage contributions and to be used for study purposes. We are not responsible for illegal uses of this source code. This source code is released as-is and continues to be maintained by non-paid contributors in their free time.
# Credit List
## Developers
- MontyTRC (Project Leader)
- Gancian (general coding)
- Krystian (general coding)
- Kubsy (Some cleanups and fixes)
- l.m. (general coding, Lua enhancements, bug fixing)
- Lwmte (sound refactoring, general coding, code cleanups, bug fixing)
- Moooonyeah (Jumanji) (entity decompilation)
- Raildex (renderer refactoring, particle coding, general coding)
- RicardoLuis0 (general coding)
- Sezz (player state refactoring, general coding, code cleanups, bug fixing, assets)
- Squidshire (Hispidence) (Lua implementation, bug fixing)
- Stranger1992 (sound asset refactoring and organisation, assets)
- TokyoSU (entity and vehicle decompilation)
- Tomo (general coding, bug fixing)
- Troye (general coding, refactoring)
- WolfCheese (general coding)
## Testers
- Adngel
- Caesum
- Dustie
- GeckoKid
- JoeyQuint
- Kamillos
- Kubsy
- LGG_PRODUCTION
- Lore
- RemRem
- Stranger1992
- WolfCheese
## Assets and Miscellaneous
- Geckokid (Sprites and test level creator).
### Animations
- SrDanielPonces (Diagonal shimmy transitions, backwards monkey swinging)
- Krystian (Flexibility crawlspace, slope climbing animations)
- Sezz (Additional Animations for player state refactoring)
- Naotheia (Underwater puzzle placement, crouch 180° turn, crawl 180° turn, water surface 180° turn)
- JoeyQuint (Standing 180° turn, monkey swing 180° turn)
### TombEngine Marketing
- Kubsy (Twitter and forum posts)
- Stranger1992 (This website, Facebook, Instagram, Youtube and Twitch.

View file

@ -11,7 +11,6 @@ local anims = Flow.Animations.new()
anims.crawlExtended = true anims.crawlExtended = true
anims.crouchRoll = true anims.crouchRoll = true
anims.crawlspaceSwandive = true anims.crawlspaceSwandive = true
anims.monkeyAutoJump = false
anims.overhangClimb = false anims.overhangClimb = false
anims.slideExtended = false anims.slideExtended = false
anims.sprintJump = false anims.sprintJump = false

View file

@ -51,7 +51,8 @@ local strings =
ammo_used = { "Ammo Used" }, ammo_used = { "Ammo Used" },
antialiasing = { "Antialiasing" }, antialiasing = { "Antialiasing" },
apply = { "Apply" }, apply = { "Apply" },
automatic_targeting = { "Automatic Targeting" }, auto_monkey_swing_jump = { "Auto Monkey Jump" },
auto_targeting = { "Auto Targeting" },
back = { "Back" }, back = { "Back" },
cancel = { "Cancel" }, cancel = { "Cancel" },
caustics = { "Underwater Caustics" }, caustics = { "Underwater Caustics" },

BIN
TEN logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -196,7 +196,7 @@ namespace TEN::Hud
DisplayPickup& PickupSummaryController::GetNewDisplayPickup() DisplayPickup& PickupSummaryController::GetNewDisplayPickup()
{ {
assertion(_displayPickups.size() <= DISPLAY_PICKUP_COUNT_MAX, "Display pickup overflow."); TENAssert(_displayPickups.size() <= DISPLAY_PICKUP_COUNT_MAX, "Display pickup overflow.");
// Add and return new display pickup. // Add and return new display pickup.
if (_displayPickups.size() < DISPLAY_PICKUP_COUNT_MAX) if (_displayPickups.size() < DISPLAY_PICKUP_COUNT_MAX)
@ -213,12 +213,15 @@ namespace TEN::Hud
_displayPickups.erase( _displayPickups.erase(
std::remove_if( std::remove_if(
_displayPickups.begin(), _displayPickups.end(), _displayPickups.begin(), _displayPickups.end(),
[](const DisplayPickup& pickup) { return ((pickup.Life <= 0.0f) && pickup.IsOffscreen()); }), [](const DisplayPickup& pickup)
{
return ((pickup.Life <= 0.0f) && pickup.IsOffscreen());
}),
_displayPickups.end()); _displayPickups.end());
} }
void PickupSummaryController::DrawDebug() const void PickupSummaryController::DrawDebug() const
{ {
g_Renderer.PrintDebugMessage("Display pickups in summary: %d", _displayPickups.size()); PrintDebugMessage("Display pickups in summary: %d", _displayPickups.size());
} }
} }

View file

@ -33,14 +33,17 @@ namespace TEN::Hud
{ {
private: private:
// Constants // Constants
static constexpr auto DISPLAY_PICKUP_COUNT_MAX = 64; static constexpr auto DISPLAY_PICKUP_COUNT_MAX = 64;
static constexpr auto DISPLAY_PICKUP_COUNT_ARG_DEFAULT = 1; static constexpr auto DISPLAY_PICKUP_COUNT_ARG_DEFAULT = 1;
// Members // Members
std::vector<DisplayPickup> _displayPickups = {}; std::vector<DisplayPickup> _displayPickups = {};
public: public:
// Utilities // Utilities
void AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector2& origin, unsigned int count = DISPLAY_PICKUP_COUNT_ARG_DEFAULT); void AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector2& origin, unsigned int count = DISPLAY_PICKUP_COUNT_ARG_DEFAULT);
void AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector3& pos, unsigned int count = DISPLAY_PICKUP_COUNT_ARG_DEFAULT); void AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector3& pos, unsigned int count = DISPLAY_PICKUP_COUNT_ARG_DEFAULT);
@ -50,6 +53,7 @@ namespace TEN::Hud
private: private:
// Helpers // Helpers
std::vector<Vector2> GetStackPositions() const; std::vector<Vector2> GetStackPositions() const;
DisplayPickup& GetNewDisplayPickup(); DisplayPickup& GetNewDisplayPickup();
void ClearInactiveDisplayPickups(); void ClearInactiveDisplayPickups();

View file

@ -3,12 +3,10 @@
#include "Game/effects/DisplaySprite.h" #include "Game/effects/DisplaySprite.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer.h"
#include "Specific/clock.h" #include "Specific/clock.h"
using namespace TEN::Effects::DisplaySprite; using namespace TEN::Effects::DisplaySprite;
using namespace TEN::Math; using namespace TEN::Math;
using TEN::Renderer::g_Renderer;
namespace TEN::Hud namespace TEN::Hud
{ {
@ -80,10 +78,10 @@ namespace TEN::Hud
void SpeedometerController::DrawDebug() const void SpeedometerController::DrawDebug() const
{ {
g_Renderer.PrintDebugMessage("SPEEDOMETER DEBUG"); PrintDebugMessage("SPEEDOMETER DEBUG");
g_Renderer.PrintDebugMessage("Value: %.3f", _value); PrintDebugMessage("Value: %.3f", _value);
g_Renderer.PrintDebugMessage("Pointer angle: %.3f", _pointerAngle); PrintDebugMessage("Pointer angle: %.3f", _pointerAngle);
g_Renderer.PrintDebugMessage("Opacity: %.3f", _opacity); PrintDebugMessage("Opacity: %.3f", _opacity);
g_Renderer.PrintDebugMessage("Life: %.3f", _life / FPS); PrintDebugMessage("Life: %.3f", _life / FPS);
} }
} }

View file

@ -6,9 +6,11 @@ namespace TEN::Hud
{ {
private: private:
// Constants // Constants
static constexpr auto LIFE_MAX = 0.75f; static constexpr auto LIFE_MAX = 0.75f;
// Members // Members
bool _hasValueUpdated = false; bool _hasValueUpdated = false;
float _value = 0.0f; float _value = 0.0f;
@ -18,6 +20,7 @@ namespace TEN::Hud
public: public:
// Utilities // Utilities
void UpdateValue(float value); void UpdateValue(float value);
void Update(); void Update();

View file

@ -26,6 +26,7 @@ namespace TEN::Hud
{ {
private: private:
// Members // Members
StatusBar _airBar = {}; StatusBar _airBar = {};
StatusBar _exposureBar = {}; StatusBar _exposureBar = {};
StatusBar _healthBar = {}; StatusBar _healthBar = {};
@ -35,6 +36,7 @@ namespace TEN::Hud
public: public:
// Utilities // Utilities
void Initialize(const ItemInfo& item); void Initialize(const ItemInfo& item);
void Update(const ItemInfo& item); void Update(const ItemInfo& item);
void Draw(const ItemInfo& item) const; void Draw(const ItemInfo& item) const;
@ -42,12 +44,14 @@ namespace TEN::Hud
private: private:
// Update helpers // Update helpers
void UpdateAirBar(const ItemInfo& item); void UpdateAirBar(const ItemInfo& item);
void UpdateExposureBar(const ItemInfo& item); void UpdateExposureBar(const ItemInfo& item);
void UpdateHealthBar(const ItemInfo& item); void UpdateHealthBar(const ItemInfo& item);
void UpdateStaminaBar(const ItemInfo& item); void UpdateStaminaBar(const ItemInfo& item);
// Draw helpers // Draw helpers
void DrawStatusBar(float value, float criticalValue, const RendererHudBar& rHudBar, GAME_OBJECT_ID textureID, int frame, bool isPoisoned) const; void DrawStatusBar(float value, float criticalValue, const RendererHudBar& rHudBar, GAME_OBJECT_ID textureID, int frame, bool isPoisoned) const;
void DrawAirBar() const; void DrawAirBar() const;
void DrawExposureBar() const; void DrawExposureBar() const;

View file

@ -350,9 +350,9 @@ namespace TEN::Hud
for (const auto& [itemNumber, crosshair] : _crosshairs) for (const auto& [itemNumber, crosshair] : _crosshairs)
crosshair.IsPrimary ? primaryCount++ : peripheralCount++; crosshair.IsPrimary ? primaryCount++ : peripheralCount++;
g_Renderer.PrintDebugMessage("TARGET HIGHLIGHTER DEBUG"); PrintDebugMessage("TARGET HIGHLIGHTER DEBUG");
g_Renderer.PrintDebugMessage(g_Configuration.EnableTargetHighlighter ? "Enabled" : "Disabled"); PrintDebugMessage(g_Configuration.EnableTargetHighlighter ? "Enabled" : "Disabled");
g_Renderer.PrintDebugMessage("Primary crosshairs: %d", primaryCount); PrintDebugMessage("Primary crosshairs: %d", primaryCount);
g_Renderer.PrintDebugMessage("Peripheral crosshairs: %d", peripheralCount); PrintDebugMessage("Peripheral crosshairs: %d", peripheralCount);
} }
} }

View file

@ -15,11 +15,13 @@ namespace TEN::Hud
public: public:
// Constants // Constants
static constexpr auto COLOR_RED = Color(1.0f, 0.2f, 0.2f); static constexpr auto COLOR_RED = Color(1.0f, 0.2f, 0.2f);
static constexpr auto COLOR_GRAY = Color(0.7f, 0.7f, 0.7f, 0.7f); static constexpr auto COLOR_GRAY = Color(0.7f, 0.7f, 0.7f, 0.7f);
static constexpr auto SEGMENT_COUNT = 4; static constexpr auto SEGMENT_COUNT = 4;
// Members // Members
bool IsActive = false; bool IsActive = false;
bool IsPrimary = false; bool IsPrimary = false;
@ -35,15 +37,18 @@ namespace TEN::Hud
std::array<SegmentData, SEGMENT_COUNT> Segments = {}; std::array<SegmentData, SEGMENT_COUNT> Segments = {};
// Getters // Getters
float GetScale(float cameraDist) const; float GetScale(float cameraDist) const;
float GetRadius() const; float GetRadius() const;
Vector2 GetPositionOffset(short orientOffset) const; Vector2 GetPositionOffset(short orientOffset) const;
// Setters // Setters
void SetPrimary(); void SetPrimary();
void SetPeripheral(); void SetPeripheral();
// Utilities // Utilities
void Update(const Vector3& targetPos, bool isActive, bool doPulse); void Update(const Vector3& targetPos, bool isActive, bool doPulse);
void Draw() const; void Draw() const;
}; };
@ -52,19 +57,23 @@ namespace TEN::Hud
{ {
private: private:
// Members // Members
std::unordered_map<int, CrosshairData> _crosshairs = {}; // Key = item number. std::unordered_map<int, CrosshairData> _crosshairs = {}; // Key = item number.
public: public:
// Utilities // Utilities
void Update(const ItemInfo& playerItem); void Update(const ItemInfo& playerItem);
void Draw() const; void Draw() const;
void Clear(); void Clear();
private: private:
// Update helpers // Update helpers
void Update(const std::vector<int>& itemNumbers); void Update(const std::vector<int>& itemNumbers);
// Object helpers // Object helpers
CrosshairData& GetNewCrosshair(int itemNumber); CrosshairData& GetNewCrosshair(int itemNumber);
void AddCrosshair(int itemNumber, const Vector3& targetPos); void AddCrosshair(int itemNumber, const Vector3& targetPos);
void ClearInactiveCrosshairs(); void ClearInactiveCrosshairs();

View file

@ -3,6 +3,7 @@
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/Point.h"
#include "Game/collision/floordata.h" #include "Game/collision/floordata.h"
#include "Game/control/los.h" #include "Game/control/los.h"
#include "Game/items.h" #include "Game/items.h"
@ -14,6 +15,7 @@
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Input; using namespace TEN::Input;
namespace TEN::Entities::Player namespace TEN::Entities::Player
@ -35,8 +37,8 @@ namespace TEN::Entities::Player
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, 0, 0, -coll.Setup.Height / 2); // NOTE: Height offset required for correct bridge collision. auto pointColl = GetPointCollision(item, 0, 0, -coll.Setup.Height / 2); // NOTE: Height offset required for correct bridge collision.
int relFloorHeight = pointColl.Position.Floor - item.Pose.Position.y; int relFloorHeight = pointColl.GetFloorHeight() - item.Pose.Position.y;
// 1) Test if player is already aligned with floor. // 1) Test if player is already aligned with floor.
if (relFloorHeight == 0) if (relFloorHeight == 0)
@ -61,8 +63,8 @@ namespace TEN::Entities::Player
constexpr auto UPPER_FLOOR_BOUND_DOWN = CLICK(0.75f); constexpr auto UPPER_FLOOR_BOUND_DOWN = CLICK(0.75f);
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, 0, 0, -coll.Setup.Height / 2); // NOTE: Height offset required for correct bridge collision. auto pointColl = GetPointCollision(item, 0, 0, -coll.Setup.Height / 2); // NOTE: Height offset required for correct bridge collision.
int relFloorHeight = pointColl.Position.Floor - item.Pose.Position.y; int relFloorHeight = pointColl.GetFloorHeight() - item.Pose.Position.y;
// Determine appropriate floor bounds. // Determine appropriate floor bounds.
int lowerFloorBound = isGoingUp ? LOWER_FLOOR_BOUND_UP : LOWER_FLOOR_BOUND_DOWN; int lowerFloorBound = isGoingUp ? LOWER_FLOOR_BOUND_UP : LOWER_FLOOR_BOUND_DOWN;
@ -167,30 +169,30 @@ namespace TEN::Entities::Player
int playerHeight = isCrawling ? LARA_HEIGHT_CRAWL : coll.Setup.Height; int playerHeight = isCrawling ? LARA_HEIGHT_CRAWL : coll.Setup.Height;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, setup.HeadingAngle, OFFSET_RADIUS(playerRadius), -playerHeight); auto pointColl = GetPointCollision(item, setup.HeadingAngle, OFFSET_RADIUS(playerRadius), -playerHeight);
int vPos = item.Pose.Position.y; int vPos = item.Pose.Position.y;
int vPosTop = vPos - playerHeight; int vPosTop = vPos - playerHeight;
// Calculate slope aspect delta angle. // Calculate slope aspect delta angle.
short aspectAngle = Geometry::GetSurfaceAspectAngle(pointColl.FloorNormal); short aspectAngle = Geometry::GetSurfaceAspectAngle(pointColl.GetFloorNormal());
short aspectAngleDelta = Geometry::GetShortestAngle(setup.HeadingAngle, aspectAngle); short aspectAngleDelta = Geometry::GetShortestAngle(setup.HeadingAngle, aspectAngle);
// 1) Check for slippery slope below floor (if applicable). // 1) Check for illegal slope below floor (if applicable).
if (setup.TestSlipperySlopeBelow && if (setup.TestSteepFloorBelow &&
(pointColl.Position.FloorSlope && abs(aspectAngleDelta) <= SLOPE_ASPECT_ANGLE_DELTA_MAX)) (pointColl.IsSteepFloor() && abs(aspectAngleDelta) <= SLOPE_ASPECT_ANGLE_DELTA_MAX))
{ {
return false; return false;
} }
// 1) Check for slippery slope above floor (if applicable). // 1) Check for illegal slope above floor (if applicable).
if (setup.TestSlipperySlopeAbove && if (setup.TestSteepFloorAbove &&
(pointColl.Position.FloorSlope && abs(aspectAngleDelta) >= SLOPE_ASPECT_ANGLE_DELTA_MAX)) (pointColl.IsSteepFloor() && abs(aspectAngleDelta) >= SLOPE_ASPECT_ANGLE_DELTA_MAX))
{ {
return false; return false;
} }
// 3) Check for death floor (if applicable). // 3) Check for death floor (if applicable).
if (setup.TestDeathFloor && pointColl.Block->Flags.Death) if (setup.TestDeathFloor && pointColl.GetSector().Flags.Death && pointColl.GetFloorBridgeItemNumber() == NO_VALUE)
return false; return false;
// LOS setup at upper floor bound. // LOS setup at upper floor bound.
@ -200,9 +202,9 @@ namespace TEN::Entities::Player
item.Pose.Position.z, item.Pose.Position.z,
item.RoomNumber); item.RoomNumber);
auto target0 = GameVector( auto target0 = GameVector(
pointColl.Coordinates.x, pointColl.GetPosition().x,
(vPos + setup.UpperFloorBound) - 1, (vPos + setup.UpperFloorBound) - 1,
pointColl.Coordinates.z, pointColl.GetPosition().z,
item.RoomNumber); item.RoomNumber);
// LOS setup at lowest ceiling bound (player height). // LOS setup at lowest ceiling bound (player height).
@ -212,9 +214,9 @@ namespace TEN::Entities::Player
item.Pose.Position.z, item.Pose.Position.z,
item.RoomNumber); item.RoomNumber);
auto target1 = GameVector( auto target1 = GameVector(
pointColl.Coordinates.x, pointColl.GetPosition().x,
vPosTop + 1, vPosTop + 1,
pointColl.Coordinates.z, pointColl.GetPosition().z,
item.RoomNumber); item.RoomNumber);
// Calculate LOS direction. // Calculate LOS direction.
@ -232,9 +234,9 @@ namespace TEN::Entities::Player
if (!LOS(&origin0, &target0) || !LOS(&origin1, &target1)) if (!LOS(&origin0, &target0) || !LOS(&origin1, &target1))
return false; return false;
int relFloorHeight = pointColl.Position.Floor - vPos; int relFloorHeight = pointColl.GetFloorHeight() - vPos;
int relCeilHeight = pointColl.Position.Ceiling - vPos; int relCeilHeight = pointColl.GetCeilingHeight() - vPos;
int floorToCeilHeight = abs(pointColl.Position.Ceiling - pointColl.Position.Floor); int floorToCeilHeight = abs(pointColl.GetCeilingHeight() - pointColl.GetFloorHeight());
// 6) Assess point collision. // 6) Assess point collision.
if (relFloorHeight <= setup.LowerFloorBound && // Floor height is above lower floor bound. if (relFloorHeight <= setup.LowerFloorBound && // Floor height is above lower floor bound.
@ -400,12 +402,12 @@ namespace TEN::Entities::Player
return false; return false;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, 0, 0, -coll.Setup.Height / 2); // NOTE: Offset required for correct bridge collision. auto pointColl = GetPointCollision(item, 0, 0, -coll.Setup.Height / 2); // NOTE: Offset required for correct bridge collision.
int relFloorHeight = pointColl.Position.Floor - item.Pose.Position.y; int relFloorHeight = pointColl.GetFloorHeight() - item.Pose.Position.y;
// 2) Assess point collision. // 2) Assess point collision.
if (abs(relFloorHeight) <= ABS_FLOOR_BOUND && // Floor height is within upper/lower floor bounds. if (abs(relFloorHeight) <= ABS_FLOOR_BOUND && // Floor height is within upper/lower floor bounds.
pointColl.Position.FloorSlope) // Floor is a slippery slope. pointColl.IsSteepFloor()) // Floor is a slippery slope.
{ {
return true; return true;
} }
@ -432,8 +434,8 @@ namespace TEN::Entities::Player
float radius = TestState(item.Animation.ActiveState, CROUCH_STATES) ? LARA_RADIUS_CRAWL : LARA_RADIUS; float radius = TestState(item.Animation.ActiveState, CROUCH_STATES) ? LARA_RADIUS_CRAWL : LARA_RADIUS;
// Get center point collision. // Get center point collision.
auto pointCollCenter = GetCollision(&item, 0, 0.0f, -LARA_HEIGHT / 2); auto pointCollCenter = GetPointCollision(item, 0, 0.0f, -LARA_HEIGHT / 2);
int floorToCeilHeightCenter = abs(pointCollCenter.Position.Ceiling - pointCollCenter.Position.Floor); int floorToCeilHeightCenter = abs(pointCollCenter.GetCeilingHeight() - pointCollCenter.GetFloorHeight());
// Assess center point collision. // Assess center point collision.
if (floorToCeilHeightCenter < LARA_HEIGHT || // Floor-to-ceiling height isn't too wide. if (floorToCeilHeightCenter < LARA_HEIGHT || // Floor-to-ceiling height isn't too wide.
@ -445,9 +447,9 @@ namespace TEN::Entities::Player
// TODO: Check whether < or <= and > or >=. // TODO: Check whether < or <= and > or >=.
// Get front point collision. // Get front point collision.
auto pointCollFront = GetCollision(&item, item.Pose.Orientation.y, radius, -coll.Setup.Height); auto pointCollFront = GetPointCollision(item, item.Pose.Orientation.y, radius, -coll.Setup.Height);
int floorToCeilHeightFront = abs(pointCollFront.Position.Ceiling - pointCollFront.Position.Floor); int floorToCeilHeightFront = abs(pointCollFront.GetCeilingHeight() - pointCollFront.GetFloorHeight());
int relFloorHeightFront = abs(pointCollFront.Position.Floor - pointCollCenter.Position.Floor); int relFloorHeightFront = abs(pointCollFront.GetFloorHeight() - pointCollCenter.GetFloorHeight());
// Assess front point collision. // Assess front point collision.
if (relFloorHeightFront <= CRAWL_STEPUP_HEIGHT && // Floor is within upper/lower floor bounds. if (relFloorHeightFront <= CRAWL_STEPUP_HEIGHT && // Floor is within upper/lower floor bounds.
@ -458,9 +460,9 @@ namespace TEN::Entities::Player
} }
// Get back point collision. // Get back point collision.
auto pointCollBack = GetCollision(&item, item.Pose.Orientation.y, -radius, -coll.Setup.Height); auto pointCollBack = GetPointCollision(item, item.Pose.Orientation.y, -radius, -coll.Setup.Height);
int floorToCeilHeightBack = abs(pointCollBack.Position.Ceiling - pointCollBack.Position.Floor); int floorToCeilHeightBack = abs(pointCollBack.GetCeilingHeight() - pointCollBack.GetFloorHeight());
int relFloorHeightBack = abs(pointCollBack.Position.Floor - pointCollCenter.Position.Floor); int relFloorHeightBack = abs(pointCollBack.GetFloorHeight() - pointCollCenter.GetFloorHeight());
// Assess back point collision. // Assess back point collision.
if (relFloorHeightBack <= CRAWL_STEPUP_HEIGHT && // Floor is within upper/lower floor bounds. if (relFloorHeightBack <= CRAWL_STEPUP_HEIGHT && // Floor is within upper/lower floor bounds.
@ -526,22 +528,22 @@ namespace TEN::Entities::Player
// TODO: Extend point collision struct to also find water depths. // TODO: Extend point collision struct to also find water depths.
float dist = 0.0f; float dist = 0.0f;
auto pointColl0 = GetCollision(&item); auto pointColl0 = GetPointCollision(item);
// 3) Test continuity of path. // 3) Test continuity of path.
while (dist < PROBE_DIST_MAX) while (dist < PROBE_DIST_MAX)
{ {
// Get point collision. // Get point collision.
dist += STEP_DIST; dist += STEP_DIST;
auto pointColl1 = GetCollision(&item, item.Pose.Orientation.y, dist, -LARA_HEIGHT_CRAWL); auto pointColl1 = GetPointCollision(item, item.Pose.Orientation.y, dist, -LARA_HEIGHT_CRAWL);
int floorHeightDelta = abs(pointColl0.Position.Floor - pointColl1.Position.Floor); int floorHeightDelta = abs(pointColl0.GetFloorHeight() - pointColl1.GetFloorHeight());
int floorToCeilHeight = abs(pointColl1.Position.Ceiling - pointColl1.Position.Floor); int floorToCeilHeight = abs(pointColl1.GetCeilingHeight() - pointColl1.GetFloorHeight());
// Assess point collision. // Assess point collision.
if (floorHeightDelta > FLOOR_BOUND || // Avoid floor height delta beyond crawl stepup threshold. if (floorHeightDelta > FLOOR_BOUND || // Avoid floor height delta beyond crawl stepup threshold.
floorToCeilHeight <= FLOOR_TO_CEIL_HEIGHT_MAX || // Avoid narrow spaces. floorToCeilHeight <= FLOOR_TO_CEIL_HEIGHT_MAX || // Avoid narrow spaces.
pointColl1.Position.FloorSlope) // Avoid slippery floor slopes. pointColl1.IsSteepFloor()) // Avoid slippery floor slopes.
{ {
return false; return false;
} }
@ -580,8 +582,8 @@ namespace TEN::Entities::Player
constexpr auto UPPER_CEIL_BOUND = -MONKEY_STEPUP_HEIGHT; constexpr auto UPPER_CEIL_BOUND = -MONKEY_STEPUP_HEIGHT;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item); auto pointColl = GetPointCollision(item);
int relCeilHeight = pointColl.Position.Ceiling - (item.Pose.Position.y - LARA_HEIGHT_MONKEY); int relCeilHeight = pointColl.GetCeilingHeight() - (item.Pose.Position.y - LARA_HEIGHT_MONKEY);
// Assess point collision. // Assess point collision.
if (relCeilHeight <= LOWER_CEIL_BOUND && // Ceiling height is above lower ceiling bound. if (relCeilHeight <= LOWER_CEIL_BOUND && // Ceiling height is above lower ceiling bound.
@ -604,14 +606,14 @@ namespace TEN::Entities::Player
return true; return true;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item); auto pointColl = GetPointCollision(item);
// 2) Test for slippery ceiling slope and check if overhang climb is disabled. // 2) Test for slippery ceiling slope and check if overhang climb is disabled.
if (pointColl.Position.CeilingSlope && !g_GameFlow->HasOverhangClimb()) if (pointColl.IsSteepCeiling() && !g_GameFlow->HasOverhangClimb())
return true; return true;
// 3) Assess point collision. // 3) Assess point collision.
int relCeilHeight = pointColl.Position.Ceiling - (item.Pose.Position.y - LARA_HEIGHT_MONKEY); int relCeilHeight = pointColl.GetCeilingHeight() - (item.Pose.Position.y - LARA_HEIGHT_MONKEY);
if (abs(relCeilHeight) > ABS_CEIL_BOUND) // Ceiling height is within lower/upper ceiling bound. if (abs(relCeilHeight) > ABS_CEIL_BOUND) // Ceiling height is within lower/upper ceiling bound.
return true; return true;
@ -630,9 +632,9 @@ namespace TEN::Entities::Player
return false; return false;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item); auto pointColl = GetPointCollision(item);
int relCeilHeight = pointColl.Position.Ceiling - (item.Pose.Position.y - LARA_HEIGHT_MONKEY); int relCeilHeight = pointColl.GetCeilingHeight() - (item.Pose.Position.y - LARA_HEIGHT_MONKEY);
int floorToCeilHeight = abs(pointColl.Position.Ceiling - pointColl.Position.Floor); int floorToCeilHeight = abs(pointColl.GetCeilingHeight() - pointColl.GetFloorHeight());
// 2) Assess collision with ceiling. // 2) Assess collision with ceiling.
if (relCeilHeight < 0 && if (relCeilHeight < 0 &&
@ -658,14 +660,14 @@ namespace TEN::Entities::Player
constexpr auto PLAYER_HEIGHT = LARA_HEIGHT_MONKEY; constexpr auto PLAYER_HEIGHT = LARA_HEIGHT_MONKEY;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, setup.HeadingAngle, OFFSET_RADIUS(coll.Setup.Radius)); auto pointColl = GetPointCollision(item, setup.HeadingAngle, OFFSET_RADIUS(coll.Setup.Radius));
// 1) Test if ceiling is monkey swing. // 1) Test if ceiling is monkey swing.
if (!pointColl.BottomBlock->Flags.Monkeyswing) if (!pointColl.GetBottomSector().Flags.Monkeyswing)
return false; return false;
// 2) Test for ceiling slippery slope. // 2) Test for illegal ceiling.
if (pointColl.Position.CeilingSlope) if (pointColl.IsSteepCeiling())
return false; return false;
int vPos = item.Pose.Position.y; int vPos = item.Pose.Position.y;
@ -678,9 +680,9 @@ namespace TEN::Entities::Player
item.Pose.Position.z, item.Pose.Position.z,
item.RoomNumber); item.RoomNumber);
auto target0 = GameVector( auto target0 = GameVector(
pointColl.Coordinates.x, pointColl.GetPosition().x,
vPos - 1, vPos - 1,
pointColl.Coordinates.z, pointColl.GetPosition().z,
item.RoomNumber); item.RoomNumber);
// Raycast setup at lower ceiling bound. // Raycast setup at lower ceiling bound.
@ -690,9 +692,9 @@ namespace TEN::Entities::Player
item.Pose.Position.z, item.Pose.Position.z,
item.RoomNumber); item.RoomNumber);
auto target1 = GameVector( auto target1 = GameVector(
pointColl.Coordinates.x, pointColl.GetPosition().x,
(vPosTop + setup.LowerCeilingBound) + 1, (vPosTop + setup.LowerCeilingBound) + 1,
pointColl.Coordinates.z, pointColl.GetPosition().z,
item.RoomNumber); item.RoomNumber);
// Prepare data for static object LOS. // Prepare data for static object LOS.
@ -712,9 +714,9 @@ namespace TEN::Entities::Player
// TODO: Assess static object geometry ray collision. // TODO: Assess static object geometry ray collision.
int relFloorHeight = pointColl.Position.Floor - vPos; int relFloorHeight = pointColl.GetFloorHeight() - vPos;
int relCeilHeight = pointColl.Position.Ceiling - vPosTop; int relCeilHeight = pointColl.GetCeilingHeight() - vPosTop;
int floorToCeilHeight = abs(pointColl.Position.Ceiling - pointColl.Position.Floor); int floorToCeilHeight = abs(pointColl.GetCeilingHeight() - pointColl.GetFloorHeight());
// 4) Assess point collision. // 4) Assess point collision.
if (relFloorHeight > 0 && // Floor is within highest floor bound (player base). if (relFloorHeight > 0 && // Floor is within highest floor bound (player base).
@ -782,8 +784,8 @@ namespace TEN::Entities::Player
return false; return false;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, 0, 0, -coll.Setup.Height / 2); auto pointColl = GetPointCollision(item, 0, 0, -coll.Setup.Height / 2);
int relFloorHeight = pointColl.Position.Floor - item.Pose.Position.y; int relFloorHeight = pointColl.GetFloorHeight() - item.Pose.Position.y;
// 2) Assess point collision. // 2) Assess point collision.
if (relFloorHeight > UPPER_FLOOR_BOUND) // Floor height is below upper floor bound. if (relFloorHeight > UPPER_FLOOR_BOUND) // Floor height is below upper floor bound.
@ -805,11 +807,11 @@ namespace TEN::Entities::Player
return true; return true;
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item); auto pointColl = GetPointCollision(item);
int vPos = item.Pose.Position.y; int vPos = item.Pose.Position.y;
// 3) Assess point collision. // 3) Assess point collision.
if ((pointColl.Position.Floor - vPos) <= projVerticalVel) // Floor height is above projected vertical position. if ((pointColl.GetFloorHeight() - vPos) <= projVerticalVel) // Floor height is above projected vertical position.
return true; return true;
return false; return false;
@ -848,9 +850,9 @@ namespace TEN::Entities::Player
return false;*/ return false;*/
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, setup.HeadingAngle, setup.Distance, -coll.Setup.Height); auto pointColl = GetPointCollision(item, setup.HeadingAngle, setup.Distance, -coll.Setup.Height);
int relFloorHeight = pointColl.Position.Floor - item.Pose.Position.y; int relFloorHeight = pointColl.GetFloorHeight() - item.Pose.Position.y;
int relCeilHeight = pointColl.Position.Ceiling - item.Pose.Position.y; int relCeilHeight = pointColl.GetCeilingHeight() - item.Pose.Position.y;
// 4) Assess point collision. // 4) Assess point collision.
if (relFloorHeight >= -STEPUP_HEIGHT && // Floor is within highest floor bound. if (relFloorHeight >= -STEPUP_HEIGHT && // Floor is within highest floor bound.
@ -923,11 +925,11 @@ namespace TEN::Entities::Player
return IsRunJumpQueueableState(item.Animation.TargetState); return IsRunJumpQueueableState(item.Animation.TargetState);
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item, item.Pose.Orientation.y, BLOCK(1), -coll.Setup.Height); auto pointColl = GetPointCollision(item, item.Pose.Orientation.y, BLOCK(1), -coll.Setup.Height);
int lowerCeilingBound = (LOWER_CEIL_BOUND_BASE - coll.Setup.Height); int lowerCeilingBound = (LOWER_CEIL_BOUND_BASE - coll.Setup.Height);
int relFloorHeight = pointColl.Position.Floor - item.Pose.Position.y; int relFloorHeight = pointColl.GetFloorHeight() - item.Pose.Position.y;
int relCeilHeight = pointColl.Position.Ceiling - item.Pose.Position.y; int relCeilHeight = pointColl.GetCeilingHeight() - item.Pose.Position.y;
// 2) Assess point collision for possible running jump ahead. // 2) Assess point collision for possible running jump ahead.
if (relCeilHeight < lowerCeilingBound || // Ceiling height is above lower ceiling bound. if (relCeilHeight < lowerCeilingBound || // Ceiling height is above lower ceiling bound.
@ -993,7 +995,7 @@ namespace TEN::Entities::Player
// TODO: Broken on diagonal slides? // TODO: Broken on diagonal slides?
auto pointColl = GetCollision(&item); auto pointColl = GetPointCollision(item);
//short aspectAngle = GetLaraSlideHeadingAngle(item, coll); //short aspectAngle = GetLaraSlideHeadingAngle(item, coll);
//short slopeAngle = Geometry::GetSurfaceSlopeAngle(GetSurfaceNormal(pointColl.FloorTilt, true)); //short slopeAngle = Geometry::GetSurfaceSlopeAngle(GetSurfaceNormal(pointColl.FloorTilt, true));
@ -1002,8 +1004,8 @@ namespace TEN::Entities::Player
bool CanCrawlspaceDive(const ItemInfo& item, const CollisionInfo& coll) bool CanCrawlspaceDive(const ItemInfo& item, const CollisionInfo& coll)
{ {
auto pointColl = GetCollision(&item, coll.Setup.ForwardAngle, coll.Setup.Radius, -coll.Setup.Height); auto pointColl = GetPointCollision(item, coll.Setup.ForwardAngle, coll.Setup.Radius, -coll.Setup.Height);
return (abs(pointColl.Position.Ceiling - pointColl.Position.Floor) < LARA_HEIGHT || IsInLowSpace(item, coll)); return (abs(pointColl.GetCeilingHeight() - pointColl.GetFloorHeight()) < LARA_HEIGHT || IsInLowSpace(item, coll));
} }
bool CanPerformLedgeJump(const ItemInfo& item, const CollisionInfo& coll) bool CanPerformLedgeJump(const ItemInfo& item, const CollisionInfo& coll)
@ -1031,8 +1033,8 @@ namespace TEN::Entities::Player
// TODO: Assess static object geometry ray collision. // TODO: Assess static object geometry ray collision.
// Get point collision. // Get point collision.
auto pointColl = GetCollision(&item); auto pointColl = GetPointCollision(item);
int relCeilHeight = pointColl.Position.Ceiling - (item.Pose.Position.y - LARA_HEIGHT_STRETCH); int relCeilHeight = pointColl.GetCeilingHeight() - (item.Pose.Position.y - LARA_HEIGHT_STRETCH);
// 3) Assess point collision. // 3) Assess point collision.
if (relCeilHeight >= -coll.Setup.Height) // Ceiling height is below upper ceiling bound. if (relCeilHeight >= -coll.Setup.Height) // Ceiling height is below upper ceiling bound.
@ -1045,10 +1047,10 @@ namespace TEN::Entities::Player
{ {
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
auto pointColl = GetCollision(&item); auto pointColl = GetPointCollision(item);
if (player.Control.Tightrope.CanDismount && // Dismount is allowed. if (player.Control.Tightrope.CanDismount && // Dismount is allowed.
pointColl.Position.Floor == item.Pose.Position.y) // Floor is level with player. pointColl.GetFloorHeight() == item.Pose.Position.y) // Floor is level with player.
{ {
return true; return true;
} }

View file

@ -8,9 +8,9 @@ namespace TEN::Entities::Player
int LowerFloorBound = 0; int LowerFloorBound = 0;
int UpperFloorBound = 0; int UpperFloorBound = 0;
bool TestSlipperySlopeBelow = true; bool TestSteepFloorBelow = true;
bool TestSlipperySlopeAbove = true; bool TestSteepFloorAbove = true;
bool TestDeathFloor = true; bool TestDeathFloor = true;
}; };
struct MonkeySwingMovementSetupData struct MonkeySwingMovementSetupData

View file

@ -24,6 +24,7 @@
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/collision/floordata.h" #include "Game/collision/floordata.h"
#include "Game/collision/Point.h"
#include "Game/control/flipeffect.h" #include "Game/control/flipeffect.h"
#include "Game/control/volume.h" #include "Game/control/volume.h"
#include "Game/effects/Hair.h" #include "Game/effects/Hair.h"
@ -47,6 +48,7 @@
#include "Specific/winmain.h" #include "Specific/winmain.h"
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
using namespace TEN::Effects::Hair; using namespace TEN::Effects::Hair;
using namespace TEN::Effects::Items; using namespace TEN::Effects::Items;
@ -152,7 +154,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
} }
else if (item->Animation.ActiveState == LS_FREEFALL_DIVE) else if (item->Animation.ActiveState == LS_FREEFALL_DIVE)
{ {
SetAnimation(item, LA_SWANDIVE_DIVE); SetAnimation(item, LA_SWANDIVE_FREEFALL_DIVE);
item->Animation.Velocity.y /= 2; item->Animation.Velocity.y /= 2;
item->Pose.Orientation.x = ANGLE(-85.0f); item->Pose.Orientation.x = ANGLE(-85.0f);
player.Control.HandStatus = HandStatus::Free; player.Control.HandStatus = HandStatus::Free;
@ -207,7 +209,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
// pre-TR5 bug where player would keep submerged until root mesh was above water level. // pre-TR5 bug where player would keep submerged until root mesh was above water level.
isWaterOnHeadspace = TestEnvironment( isWaterOnHeadspace = TestEnvironment(
ENV_FLAG_WATER, item->Pose.Position.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z, ENV_FLAG_WATER, item->Pose.Position.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z,
GetCollision(item->Pose.Position.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z, item->RoomNumber).RoomNumber); GetPointCollision(*item, 0, 0, -CLICK(1)).GetRoomNumber());
if (water.WaterDepth == NO_HEIGHT || abs(water.HeightFromWater) >= CLICK(1) || isWaterOnHeadspace || if (water.WaterDepth == NO_HEIGHT || abs(water.HeightFromWater) >= CLICK(1) || isWaterOnHeadspace ||
item->Animation.AnimNumber == LA_UNDERWATER_RESURFACE || item->Animation.AnimNumber == LA_ONWATER_DIVE) item->Animation.AnimNumber == LA_UNDERWATER_RESURFACE || item->Animation.AnimNumber == LA_ONWATER_DIVE)
@ -333,7 +335,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
if (DebugMode) if (DebugMode)
{ {
DrawNearbyPathfinding(GetCollision(item).BottomBlock->Box); DrawNearbyPathfinding(GetPointCollision(*item).GetBottomSector().PathfindingBoxID);
DrawNearbySectorFlags(*item); DrawNearbySectorFlags(*item);
} }
} }
@ -634,12 +636,6 @@ void UpdateLara(ItemInfo* item, bool isTitle)
if (isTitle) if (isTitle)
ActionMap = actionMap; ActionMap = actionMap;
if (g_Gui.GetInventoryItemChosen() != NO_VALUE)
{
g_Gui.SetInventoryItemChosen(NO_VALUE);
SayNo();
}
// Update player animations. // Update player animations.
g_Renderer.UpdateLaraAnimations(true); g_Renderer.UpdateLaraAnimations(true);

View file

@ -3,6 +3,7 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/Point.h"
#include "Game/collision/sphere.h" #include "Game/collision/sphere.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/items.h" #include "Game/items.h"
@ -14,6 +15,7 @@
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Collision::Point;
using namespace TEN::Input; using namespace TEN::Input;
constexpr auto LADDER_TEST_MARGIN = 8; constexpr auto LADDER_TEST_MARGIN = 8;
@ -327,8 +329,8 @@ void lara_col_climb_idle(ItemInfo* item, CollisionInfo* coll)
// HACK: Prevent climbing inside sloped ceilings. Breaks overhang even more, but that shouldn't matter since we'll be doing it over. -- Sezz 2022.05.13 // HACK: Prevent climbing inside sloped ceilings. Breaks overhang even more, but that shouldn't matter since we'll be doing it over. -- Sezz 2022.05.13
int y = item->Pose.Position.y - (coll->Setup.Height + CLICK(0.5f)); int y = item->Pose.Position.y - (coll->Setup.Height + CLICK(0.5f));
auto probe = GetCollision(item, 0, 0, -(coll->Setup.Height + CLICK(0.5f))); auto probe = GetPointCollision(*item, 0, 0, -(coll->Setup.Height + CLICK(0.5f)));
if ((probe.Position.Ceiling - y) < 0) if ((probe.GetCeilingHeight() - y) < 0)
{ {
item->Animation.TargetState = LS_LADDER_UP; item->Animation.TargetState = LS_LADDER_UP;
item->Pose.Position.y += yShift; item->Pose.Position.y += yShift;
@ -416,7 +418,7 @@ void lara_as_climb_stepoff_right(ItemInfo* item, CollisionInfo* coll)
short GetClimbFlags(int x, int y, int z, short roomNumber) short GetClimbFlags(int x, int y, int z, short roomNumber)
{ {
return GetClimbFlags(GetCollision(x, y, z, roomNumber).BottomBlock); return GetClimbFlags(&GetPointCollision(Vector3i(x, y, z), roomNumber).GetBottomSector());
} }
short GetClimbFlags(FloorInfo* floor) short GetClimbFlags(FloorInfo* floor)
@ -787,13 +789,13 @@ int LaraTestClimb(ItemInfo* item, int xOffset, int yOffset, int zOffset, int xFr
int y = item->Pose.Position.y + yOffset; int y = item->Pose.Position.y + yOffset;
int z = item->Pose.Position.z + zOffset; int z = item->Pose.Position.z + zOffset;
auto probeUp = GetCollision(x, y - CLICK(0.5f), z, item->RoomNumber); auto probeUp = GetPointCollision(Vector3i(x, y - CLICK(0.5f), z), item->RoomNumber);
auto probeDown = GetCollision(x, y, z, item->RoomNumber); auto probeDown = GetPointCollision(Vector3i(x, y, z), item->RoomNumber);
if (!lara->Control.CanClimbLadder && !TestLaraNearClimbableWall(item, probeDown.BottomBlock)) if (!lara->Control.CanClimbLadder && !TestLaraNearClimbableWall(item, &probeDown.GetBottomSector()))
return 0; return 0;
int height = probeUp.Position.Floor; int height = probeUp.GetFloorHeight();
if (height == NO_HEIGHT) if (height == NO_HEIGHT)
return 0; return 0;
@ -805,7 +807,7 @@ int LaraTestClimb(ItemInfo* item, int xOffset, int yOffset, int zOffset, int xFr
if (height < 0) if (height < 0)
*shift = height; *shift = height;
int ceiling = probeDown.Position.Ceiling - y; int ceiling = probeDown.GetCeilingHeight() - y;
if (ceiling > LADDER_CLIMB_SHIFT) if (ceiling > LADDER_CLIMB_SHIFT)
return 0; return 0;
@ -822,8 +824,8 @@ int LaraTestClimb(ItemInfo* item, int xOffset, int yOffset, int zOffset, int xFr
int dz = zFront + z; int dz = zFront + z;
int dx = xFront + x; int dx = xFront + x;
auto probeFront = GetCollision(dx, y, dz, item->RoomNumber); auto probeFront = GetPointCollision(Vector3i(dx, y, dz), item->RoomNumber);
height = probeFront.Position.Floor; height = probeFront.GetFloorHeight();
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
height -= y; height -= y;
@ -839,9 +841,9 @@ int LaraTestClimb(ItemInfo* item, int xOffset, int yOffset, int zOffset, int xFr
*shift = height; *shift = height;
} }
auto probeTop = GetCollision(x, y + itemHeight, z, item->RoomNumber); auto probeTop = GetPointCollision(Vector3i(x, y + itemHeight, z), item->RoomNumber);
auto probeTopFront = GetCollision(dx, y + itemHeight, dz, probeTop.RoomNumber); auto probeTopFront = GetPointCollision(Vector3i(dx, y + itemHeight, dz), probeTop.GetRoomNumber());
ceiling = probeTopFront.Position.Ceiling; ceiling = probeTopFront.GetCeilingHeight();
if (ceiling == NO_HEIGHT) if (ceiling == NO_HEIGHT)
return 1; return 1;
@ -862,7 +864,7 @@ int LaraTestClimb(ItemInfo* item, int xOffset, int yOffset, int zOffset, int xFr
return 1; return 1;
} }
ceiling = probeFront.Position.Ceiling - y; ceiling = probeFront.GetCeilingHeight() - y;
if (ceiling >= CLICK(2)) if (ceiling >= CLICK(2))
return 1; return 1;
@ -895,16 +897,16 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
*shift = 0; *shift = 0;
// Test center. // Test center.
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(*item);
int vPos = item->Pose.Position.y - CLICK(4); int vPos = item->Pose.Position.y - CLICK(4);
if ((pointColl.Position.Ceiling - vPos) > LADDER_CLIMB_SHIFT) if ((pointColl.GetCeilingHeight() - vPos) > LADDER_CLIMB_SHIFT)
return 0; return 0;
pointColl = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber); pointColl = GetPointCollision(probePos, item->RoomNumber);
int ceiling = (CLICK(1) - probePos.y) + pointColl.Position.Ceiling; int ceiling = (CLICK(1) - probePos.y) + pointColl.GetCeilingHeight();
pointColl = GetCollision(probePos.x + probeOffset.x, probePos.y, probePos.z + probeOffset.z, pointColl.RoomNumber); pointColl = GetPointCollision(Vector3i(probePos.x + probeOffset.x, probePos.y, probePos.z + probeOffset.z), pointColl.GetRoomNumber());
int height = pointColl.Position.Floor; int height = pointColl.GetFloorHeight();
if (height == NO_HEIGHT) if (height == NO_HEIGHT)
{ {
@ -933,10 +935,10 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
if (height > 0 && height > *shift) if (height > 0 && height > *shift)
*shift = height; *shift = height;
pointColl = GetCollision(probePos.x, probePos.y + CLICK(2), probePos.z, item->RoomNumber); pointColl = GetPointCollision(Vector3i(probePos.x, probePos.y + CLICK(2), probePos.z), item->RoomNumber);
pointColl = GetCollision(probePos.x + probeOffset.x, probePos.y + CLICK(2), probePos.z + probeOffset.z, pointColl.RoomNumber); pointColl = GetPointCollision(Vector3i(probePos.x + probeOffset.x, probePos.y + CLICK(2), probePos.z + probeOffset.z), pointColl.GetRoomNumber());
ceiling = pointColl.Position.Ceiling - probePos.y; ceiling = pointColl.GetCeilingHeight() - probePos.y;
if (ceiling <= height) if (ceiling <= height)
return 1; return 1;
@ -947,7 +949,7 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
} }
else else
{ {
ceiling = GetCollision(probePos.x + probeOffset.x, probePos.y, probePos.z + probeOffset.z, pointColl.RoomNumber).Position.Ceiling - probePos.y; ceiling = GetPointCollision(Vector3i(probePos.x + probeOffset.x, probePos.y, probePos.z + probeOffset.z), pointColl.GetRoomNumber()).GetCeilingHeight() - probePos.y;
if (ceiling < CLICK(2)) if (ceiling < CLICK(2))
{ {
if ((height - ceiling) <= LARA_HEIGHT) if ((height - ceiling) <= LARA_HEIGHT)

View file

@ -4,6 +4,7 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/collision/Point.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/items.h" #include "Game/items.h"
@ -19,9 +20,15 @@
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Scripting/Include/ScriptInterfaceLevel.h" #include "Scripting/Include/ScriptInterfaceLevel.h"
using namespace TEN::Collision::Point;
using namespace TEN::Entities::Player; using namespace TEN::Entities::Player;
using namespace TEN::Input; using namespace TEN::Input;
constexpr auto DEFLECT_STRAIGHT_ANGLE = ANGLE(5.0f);
constexpr auto DEFLECT_DIAGONAL_ANGLE = ANGLE(12.0f);
constexpr auto DEFLECT_STRAIGHT_ANGLE_CRAWL = ANGLE(2.0f);
constexpr auto DEFLECT_DIAGONAL_ANGLE_CRAWL = ANGLE(5.0f);
// ----------------------------- // -----------------------------
// COLLISION TEST FUNCTIONS // COLLISION TEST FUNCTIONS
// For State Control & Collision // For State Control & Collision
@ -43,12 +50,12 @@ bool LaraDeflectEdge(ItemInfo* item, CollisionInfo* coll)
if (coll->CollisionType == CollisionType::Left) if (coll->CollisionType == CollisionType::Left)
{ {
ShiftItem(item, coll); ShiftItem(item, coll);
item->Pose.Orientation.y += ANGLE(coll->DiagonalStepAtLeft() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y += coll->DiagonalStepAtLeft() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE;
} }
else if (coll->CollisionType == CollisionType::Right) else if (coll->CollisionType == CollisionType::Right)
{ {
ShiftItem(item, coll); ShiftItem(item, coll);
item->Pose.Orientation.y -= ANGLE(coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y -= coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE;
} }
else if (coll->LastBridgeItemNumber != NO_VALUE) else if (coll->LastBridgeItemNumber != NO_VALUE)
{ {
@ -118,11 +125,11 @@ bool LaraDeflectEdgeJump(ItemInfo* item, CollisionInfo* coll)
switch (coll->CollisionType) switch (coll->CollisionType)
{ {
case CollisionType::Left: case CollisionType::Left:
item->Pose.Orientation.y += ANGLE(DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y += DEFLECT_STRAIGHT_ANGLE;
break; break;
case CollisionType::Right: case CollisionType::Right:
item->Pose.Orientation.y -= ANGLE(DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y -= DEFLECT_STRAIGHT_ANGLE;
break; break;
case CollisionType::Top: case CollisionType::Top:
@ -154,11 +161,11 @@ void LaraSlideEdgeJump(ItemInfo* item, CollisionInfo* coll)
switch (coll->CollisionType) switch (coll->CollisionType)
{ {
case CollisionType::Left: case CollisionType::Left:
item->Pose.Orientation.y += ANGLE(DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y += DEFLECT_STRAIGHT_ANGLE;
break; break;
case CollisionType::Right: case CollisionType::Right:
item->Pose.Orientation.y -= ANGLE(DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y -= DEFLECT_STRAIGHT_ANGLE;
break; break;
case CollisionType::Top: case CollisionType::Top:
@ -196,12 +203,12 @@ bool LaraDeflectEdgeCrawl(ItemInfo* item, CollisionInfo* coll)
if (coll->CollisionType == CollisionType::Left) if (coll->CollisionType == CollisionType::Left)
{ {
ShiftItem(item, coll); ShiftItem(item, coll);
item->Pose.Orientation.y += ANGLE(coll->DiagonalStepAtLeft() ? DEFLECT_DIAGONAL_ANGLE_CRAWL : DEFLECT_STRAIGHT_ANGLE_CRAWL); item->Pose.Orientation.y += coll->DiagonalStepAtLeft() ? DEFLECT_DIAGONAL_ANGLE_CRAWL : DEFLECT_STRAIGHT_ANGLE_CRAWL;
} }
else if (coll->CollisionType == CollisionType::Right) else if (coll->CollisionType == CollisionType::Right)
{ {
ShiftItem(item, coll); ShiftItem(item, coll);
item->Pose.Orientation.y -= ANGLE(coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE_CRAWL : DEFLECT_STRAIGHT_ANGLE_CRAWL); item->Pose.Orientation.y -= coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE_CRAWL : DEFLECT_STRAIGHT_ANGLE_CRAWL;
} }
return false; return false;
@ -227,12 +234,12 @@ bool LaraDeflectEdgeMonkey(ItemInfo* item, CollisionInfo* coll)
if (coll->CollisionType == CollisionType::Left) if (coll->CollisionType == CollisionType::Left)
{ {
ShiftItem(item, coll); ShiftItem(item, coll);
item->Pose.Orientation.y += ANGLE(coll->DiagonalStepAtLeft() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y += coll->DiagonalStepAtLeft() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE;
} }
else if (coll->CollisionType == CollisionType::Right) else if (coll->CollisionType == CollisionType::Right)
{ {
ShiftItem(item, coll); ShiftItem(item, coll);
item->Pose.Orientation.y -= ANGLE(coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE); item->Pose.Orientation.y -= coll->DiagonalStepAtRight() ? DEFLECT_DIAGONAL_ANGLE : DEFLECT_STRAIGHT_ANGLE;
} }
return false; return false;
@ -489,11 +496,17 @@ void LaraSurfaceCollision(ItemInfo* item, CollisionInfo* coll)
item->Pose.Orientation.y -= ANGLE(5.0f); item->Pose.Orientation.y -= ANGLE(5.0f);
} }
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(*item);
int waterHeight = GetWaterHeight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber); int waterHeight = GetWaterHeight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
if ((pointColl.Position.Floor - item->Pose.Position.y) < SWIM_WATER_DEPTH) if ((pointColl.GetFloorHeight() - item->Pose.Position.y) < SWIM_WATER_DEPTH)
{
TestPlayerWaterStepOut(item, coll); TestPlayerWaterStepOut(item, coll);
}
else if ((waterHeight - item->Pose.Position.y) <= -LARA_HEADROOM)
{
SetLaraSwimDiveAnimation(item);
}
} }
void LaraSwimCollision(ItemInfo* item, CollisionInfo* coll) void LaraSwimCollision(ItemInfo* item, CollisionInfo* coll)

View file

@ -3,11 +3,6 @@
struct ItemInfo; struct ItemInfo;
struct CollisionInfo; struct CollisionInfo;
constexpr auto DEFLECT_STRAIGHT_ANGLE = 5.0f;
constexpr auto DEFLECT_DIAGONAL_ANGLE = 12.0f;
constexpr auto DEFLECT_STRAIGHT_ANGLE_CRAWL = 2.0f;
constexpr auto DEFLECT_DIAGONAL_ANGLE_CRAWL = 5.0f;
// ----------------------------- // -----------------------------
// COLLISION TEST FUNCTIONS // COLLISION TEST FUNCTIONS
// For State Control & Collision // For State Control & Collision

View file

@ -4,6 +4,7 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/collision/Point.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/effects/chaffFX.h" #include "Game/effects/chaffFX.h"
@ -18,6 +19,7 @@
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Collision::Point;
using namespace TEN::Math; using namespace TEN::Math;
constexpr auto FLARE_LIFE_MAX = 60.0f * FPS; constexpr auto FLARE_LIFE_MAX = 60.0f * FPS;
@ -258,7 +260,7 @@ void DrawFlare(ItemInfo& laraItem)
SoundEffect( SoundEffect(
SFX_TR4_FLARE_IGNITE_DRY, SFX_TR4_FLARE_IGNITE_DRY,
&laraItem.Pose, &laraItem.Pose,
TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::Water : SoundEnvironment::Land); TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::ShallowWater : SoundEnvironment::Land);
} }
DoFlareInHand(laraItem, player.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
@ -323,7 +325,7 @@ void CreateFlare(ItemInfo& laraItem, GAME_OBJECT_ID objectID, bool isThrown)
flareItem.Pose.Position = pos; flareItem.Pose.Position = pos;
int floorHeight = GetCollision(pos.x, pos.y, pos.z, laraItem.RoomNumber).Position.Floor; int floorHeight = GetPointCollision(pos, laraItem.RoomNumber).GetFloorHeight();
auto isCollided = !GetCollidedObjects(flareItem, true, true).IsEmpty(); auto isCollided = !GetCollidedObjects(flareItem, true, true).IsEmpty();
bool hasLanded = false; bool hasLanded = false;

View file

@ -7,6 +7,7 @@
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/floordata.h" #include "Game/collision/floordata.h"
#include "Game/collision/Point.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/control/volume.h" #include "Game/control/volume.h"
#include "Game/items.h" #include "Game/items.h"
@ -21,7 +22,6 @@
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h" #include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer.h"
#include "Scripting/Include/ScriptInterfaceLevel.h" #include "Scripting/Include/ScriptInterfaceLevel.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
@ -37,6 +37,7 @@
#include "Objects/TR4/Vehicles/motorbike.h" #include "Objects/TR4/Vehicles/motorbike.h"
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
using namespace TEN::Effects::Bubble; using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::Drip; using namespace TEN::Effects::Drip;
@ -44,7 +45,6 @@ using namespace TEN::Entities::Player;
using namespace TEN::Gui; using namespace TEN::Gui;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer;
// ----------------------------- // -----------------------------
// HELPER FUNCTIONS // HELPER FUNCTIONS
@ -160,9 +160,9 @@ void HandlePlayerStatusEffects(ItemInfo& item, WaterStatus waterStatus, PlayerWa
const auto& vehicleItem = g_Level.Items[player.Context.Vehicle]; const auto& vehicleItem = g_Level.Items[player.Context.Vehicle];
if (vehicleItem.ObjectNumber == ID_UPV) if (vehicleItem.ObjectNumber == ID_UPV)
{ {
auto pointColl = GetCollision(&item, 0, 0, CLICK(1)); auto pointColl = GetPointCollision(item, 0, 0, CLICK(1));
water.IsCold = (water.IsCold || TestEnvironment(ENV_FLAG_COLD, pointColl.RoomNumber)); water.IsCold = (water.IsCold || TestEnvironment(ENV_FLAG_COLD, pointColl.GetRoomNumber()));
if (water.IsCold) if (water.IsCold)
{ {
player.Status.Exposure--; player.Status.Exposure--;
@ -276,50 +276,7 @@ void HandlePlayerStatusEffects(ItemInfo& item, WaterStatus waterStatus, PlayerWa
} }
} }
static void UsePlayerMedipack(ItemInfo& item) static LaraWeaponType GetPlayerScrolledWeaponType(const ItemInfo& item, LaraWeaponType currentWeaponType, bool getPrev)
{
auto& player = GetLaraInfo(item);
// Can't use medipack; return early.
if (item.HitPoints <= 0 ||
(item.HitPoints >= LARA_HEALTH_MAX && player.Status.Poison == 0))
{
return;
}
bool hasUsedMedipack = false;
if (IsClicked(In::SmallMedipack) &&
player.Inventory.TotalSmallMedipacks != 0)
{
hasUsedMedipack = true;
item.HitPoints += LARA_HEALTH_MAX / 2;
if (item.HitPoints > LARA_HEALTH_MAX)
item.HitPoints = LARA_HEALTH_MAX;
if (player.Inventory.TotalSmallMedipacks != -1)
player.Inventory.TotalSmallMedipacks--;
}
else if (IsClicked(In::LargeMedipack) &&
player.Inventory.TotalLargeMedipacks != 0)
{
hasUsedMedipack = true;
item.HitPoints = LARA_HEALTH_MAX;
if (player.Inventory.TotalLargeMedipacks != -1)
player.Inventory.TotalLargeMedipacks--;
}
if (hasUsedMedipack)
{
player.Status.Poison = 0;
SaveGame::Statistics.Game.HealthUsed++;
SoundEffect(SFX_TR4_MENU_MEDI, nullptr, SoundEnvironment::Always);
}
}
static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo& item, LaraWeaponType currentWeaponType, bool getPrev)
{ {
static const auto SCROLL_WEAPON_TYPES = std::vector<LaraWeaponType> static const auto SCROLL_WEAPON_TYPES = std::vector<LaraWeaponType>
{ {
@ -334,15 +291,15 @@ static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo&
LaraWeaponType::RocketLauncher LaraWeaponType::RocketLauncher
}; };
auto getNextIndex = [getPrev](unsigned int index) auto getNextIndex = [getPrev](int index)
{ {
return (index + (getPrev ? ((unsigned int)SCROLL_WEAPON_TYPES.size() - 1) : 1)) % (unsigned int)SCROLL_WEAPON_TYPES.size(); return (index + (getPrev ? ((int)SCROLL_WEAPON_TYPES.size() - 1) : 1)) % (int)SCROLL_WEAPON_TYPES.size();
}; };
auto& player = GetLaraInfo(item); auto& player = GetLaraInfo(item);
// Get vector index for current weapon type. // Get vector index for current weapon type.
auto currentIndex = std::optional<unsigned int>(std::nullopt); auto currentIndex = NO_VALUE;
for (int i = 0; i < SCROLL_WEAPON_TYPES.size(); i++) for (int i = 0; i < SCROLL_WEAPON_TYPES.size(); i++)
{ {
if (SCROLL_WEAPON_TYPES[i] == currentWeaponType) if (SCROLL_WEAPON_TYPES[i] == currentWeaponType)
@ -352,13 +309,13 @@ static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo&
} }
} }
// Invalid current weapon type; return nullopt. // Invalid current weapon type; return None type.
if (!currentIndex.has_value()) if (currentIndex == NO_VALUE)
return std::nullopt; return LaraWeaponType::None;
// Get next valid weapon type in sequence. // Get next valid weapon type in sequence.
unsigned int nextIndex = getNextIndex(*currentIndex); int nextIndex = getNextIndex(currentIndex);
while (nextIndex != *currentIndex) while (nextIndex != currentIndex)
{ {
auto nextWeaponType = SCROLL_WEAPON_TYPES[nextIndex]; auto nextWeaponType = SCROLL_WEAPON_TYPES[nextIndex];
if (player.Weapons[(int)nextWeaponType].Present) if (player.Weapons[(int)nextWeaponType].Present)
@ -367,8 +324,8 @@ static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo&
nextIndex = getNextIndex(nextIndex); nextIndex = getNextIndex(nextIndex);
} }
// No valid weapon type; return nullopt. // No valid weapon type; return None type.
return std::nullopt; return LaraWeaponType::None;
} }
void HandlePlayerQuickActions(ItemInfo& item) void HandlePlayerQuickActions(ItemInfo& item)
@ -376,17 +333,21 @@ void HandlePlayerQuickActions(ItemInfo& item)
auto& player = GetLaraInfo(item); auto& player = GetLaraInfo(item);
// Handle medipacks. // Handle medipacks.
if (IsClicked(In::SmallMedipack) || IsClicked(In::LargeMedipack)) if (IsClicked(In::SmallMedipack))
UsePlayerMedipack(item); {
g_Gui.UseItem(item, GAME_OBJECT_ID::ID_SMALLMEDI_ITEM);
}
else if (IsClicked(In::LargeMedipack))
{
g_Gui.UseItem(item, GAME_OBJECT_ID::ID_BIGMEDI_ITEM);
}
// Handle weapon scroll request. // Handle weapon scroll request.
if (IsClicked(In::PreviousWeapon) || IsClicked(In::NextWeapon)) if (IsClicked(In::PreviousWeapon) || IsClicked(In::NextWeapon))
{ {
bool getPrev = IsClicked(In::PreviousWeapon); auto weaponType = GetPlayerScrolledWeaponType(item, player.Control.Weapon.GunType, IsClicked(In::PreviousWeapon));
auto weaponType = GetPlayerScrolledWeaponType(item, player.Control.Weapon.GunType, getPrev); if (weaponType != LaraWeaponType::None)
player.Control.Weapon.RequestGunType = weaponType;
if (weaponType.has_value())
player.Control.Weapon.RequestGunType = *weaponType;
} }
// Handle weapon requests. // Handle weapon requests.
@ -419,7 +380,7 @@ void HandlePlayerQuickActions(ItemInfo& item)
// TODO: 10th possible weapon, probably grapple gun. // TODO: 10th possible weapon, probably grapple gun.
/*if (IsClicked(In::Weapon10) && player.Weapons[(int)LaraWeaponType::].Present) /*if (IsClicked(In::Weapon10) && player.Weapons[(int)LaraWeaponType::].Present)
player.Control.Weapon.RequestGunType = LaraWeaponType::;*/ player.Control.Weapon.RequestGunType = LaraWeaponType::;*/
} }
bool CanPlayerLookAround(const ItemInfo& item) bool CanPlayerLookAround(const ItemInfo& item)
@ -1029,7 +990,7 @@ void HandlePlayerAirBubbles(ItemInfo* item)
{ {
constexpr auto BUBBLE_COUNT_MAX = 3; constexpr auto BUBBLE_COUNT_MAX = 3;
SoundEffect(SFX_TR4_LARA_BUBBLES, &item->Pose, SoundEnvironment::Water); SoundEffect(SFX_TR4_LARA_BUBBLES, &item->Pose, SoundEnvironment::ShallowWater);
const auto& level = *g_GameFlow->GetLevel(CurrentLevel); const auto& level = *g_GameFlow->GetLevel(CurrentLevel);
@ -1250,20 +1211,14 @@ LaraInfo*& GetLaraInfo(ItemInfo* item)
PlayerWaterData GetPlayerWaterData(ItemInfo& item) PlayerWaterData GetPlayerWaterData(ItemInfo& item)
{ {
bool isWater = TestEnvironment(ENV_FLAG_WATER, &item); auto pointColl = GetPointCollision(item);
bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, &item); int heightFromWater = (pointColl.GetWaterTopHeight() == NO_HEIGHT) ?
bool isCold = TestEnvironment(ENV_FLAG_COLD, &item); NO_HEIGHT : (std::min(item.Pose.Position.y, pointColl.GetFloorHeight()) - pointColl.GetWaterTopHeight());
int waterDepth = GetWaterDepth(&item);
int waterHeight = GetWaterHeight(&item);
auto pointColl = GetCollision(item);
int heightFromWater = (waterHeight == NO_HEIGHT) ? NO_HEIGHT : (std::min(item.Pose.Position.y, pointColl.Position.Floor) - waterHeight);
return PlayerWaterData return PlayerWaterData
{ {
isWater, isSwamp, isCold, TestEnvironment(ENV_FLAG_WATER, &item), TestEnvironment(ENV_FLAG_SWAMP, &item), TestEnvironment(ENV_FLAG_COLD, &item),
waterDepth, waterHeight, heightFromWater pointColl.GetWaterBottomHeight(), pointColl.GetWaterTopHeight(), heightFromWater
}; };
} }
@ -1322,20 +1277,20 @@ static short GetLegacySlideHeadingAngle(const Vector3& floorNormal)
short GetPlayerSlideHeadingAngle(ItemInfo* item, CollisionInfo* coll) short GetPlayerSlideHeadingAngle(ItemInfo* item, CollisionInfo* coll)
{ {
short headingAngle = coll->Setup.ForwardAngle; short headingAngle = coll->Setup.ForwardAngle;
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(*item);
// Ground is flat. // Ground is flat.
if (pointColl.FloorTilt == Vector2::Zero) if (pointColl.GetFloorNormal() == -Vector3::UnitY)
return coll->Setup.ForwardAngle; return coll->Setup.ForwardAngle;
// Return slide heading angle. // Return slide heading angle.
if (g_GameFlow->HasSlideExtended()) if (g_GameFlow->HasSlideExtended())
{ {
return Geometry::GetSurfaceAspectAngle(pointColl.FloorNormal); return Geometry::GetSurfaceAspectAngle(pointColl.GetFloorNormal());
} }
else else
{ {
return GetLegacySlideHeadingAngle(pointColl.FloorNormal); return GetLegacySlideHeadingAngle(pointColl.GetFloorNormal());
} }
} }
@ -1510,7 +1465,7 @@ void UpdateLaraSubsuitAngles(ItemInfo* item)
auto mul1 = (float)abs(lara->Control.Subsuit.Velocity[0]) / BLOCK(8); auto mul1 = (float)abs(lara->Control.Subsuit.Velocity[0]) / BLOCK(8);
auto mul2 = (float)abs(lara->Control.Subsuit.Velocity[1]) / BLOCK(8); auto mul2 = (float)abs(lara->Control.Subsuit.Velocity[1]) / BLOCK(8);
auto vol = ((mul1 + mul2) * 5.0f) + 0.5f; auto vol = ((mul1 + mul2) * 5.0f) + 0.5f;
SoundEffect(SFX_TR5_VEHICLE_DIVESUIT_ENGINE, &item->Pose, SoundEnvironment::Water, 1.0f + (mul1 + mul2), vol); SoundEffect(SFX_TR5_VEHICLE_DIVESUIT_ENGINE, &item->Pose, SoundEnvironment::ShallowWater, 1.0f + (mul1 + mul2), vol);
} }
} }
@ -1524,7 +1479,7 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
if (g_GameFlow->HasSlideExtended()) if (g_GameFlow->HasSlideExtended())
{ {
auto probe = GetCollision(item); auto probe = GetPointCollision(*item);
short minSlideAngle = ANGLE(33.75f); short minSlideAngle = ANGLE(33.75f);
//short steepness = Geometry::GetSurfaceSlopeAngle(probe.FloorTilt); //short steepness = Geometry::GetSurfaceSlopeAngle(probe.FloorTilt);
//short direction = Geometry::GetSurfaceAspectAngle(probe.FloorTilt); //short direction = Geometry::GetSurfaceAspectAngle(probe.FloorTilt);
@ -1533,7 +1488,7 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
//int slideVelocity = std::min<int>(minVelocity + 10 * (steepness * velocityMultiplier), LARA_TERMINAL_VELOCITY); //int slideVelocity = std::min<int>(minVelocity + 10 * (steepness * velocityMultiplier), LARA_TERMINAL_VELOCITY);
//short deltaAngle = abs((short)(direction - item->Pose.Orientation.y)); //short deltaAngle = abs((short)(direction - item->Pose.Orientation.y));
//g_Renderer.PrintDebugMessage("%d", slideVelocity); //PrintDebugMessage("%d", slideVelocity);
//lara->ExtraVelocity.x += slideVelocity; //lara->ExtraVelocity.x += slideVelocity;
//lara->ExtraVelocity.y += slideVelocity * phd_sin(steepness); //lara->ExtraVelocity.y += slideVelocity * phd_sin(steepness);
@ -1545,7 +1500,7 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
void AlignLaraToSurface(ItemInfo* item, float alpha) void AlignLaraToSurface(ItemInfo* item, float alpha)
{ {
// Determine relative orientation adhering to floor normal. // Determine relative orientation adhering to floor normal.
auto floorNormal = GetCollision(item).FloorNormal; auto floorNormal = GetPointCollision(*item).GetFloorNormal();
auto orient = Geometry::GetRelOrientToNormal(item->Pose.Orientation.y, floorNormal); auto orient = Geometry::GetRelOrientToNormal(item->Pose.Orientation.y, floorNormal);
// Apply extra rotation according to alpha. // Apply extra rotation according to alpha.

View file

@ -1,6 +1,7 @@
#include "framework.h" #include "framework.h"
#include "Game/Lara/lara_initialise.h" #include "Game/Lara/lara_initialise.h"
#include "Game/collision/Point.h"
#include "Game/Hud/Hud.h" #include "Game/Hud/Hud.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
@ -18,6 +19,7 @@
#include "Objects/TR4/Vehicles/motorbike.h" #include "Objects/TR4/Vehicles/motorbike.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Collision::Point;
using namespace TEN::Entities::Player; using namespace TEN::Entities::Player;
using namespace TEN::Hud; using namespace TEN::Hud;
@ -131,8 +133,8 @@ void InitializeLaraAnims(ItemInfo* item)
player.Control.WaterStatus = WaterStatus::Dry; player.Control.WaterStatus = WaterStatus::Dry;
// Allow player to start in crawl idle anim if start position is too low. // Allow player to start in crawl idle anim if start position is too low.
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(*item);
if (abs(pointColl.Position.Ceiling - pointColl.Position.Floor) < LARA_HEIGHT) if (abs(pointColl.GetCeilingHeight() - pointColl.GetFloorHeight()) < LARA_HEIGHT)
{ {
SetAnimation(item, LA_CRAWL_IDLE); SetAnimation(item, LA_CRAWL_IDLE);
player.Control.IsLow = player.Control.IsLow =

View file

@ -64,7 +64,7 @@ void lara_as_jump_forward(ItemInfo* item, CollisionInfo* coll)
{ {
DoLaraFallDamage(item); DoLaraFallDamage(item);
if (item->HitPoints <= 0) USE_FEATURE_IF_CPP20([[unlikely]]) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else if (IsHeld(In::Forward) && !IsHeld(In::Walk) && else if (IsHeld(In::Forward) && !IsHeld(In::Walk) &&
player.Control.WaterStatus != WaterStatus::Wade) player.Control.WaterStatus != WaterStatus::Wade)
@ -146,7 +146,7 @@ void lara_as_freefall(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -202,7 +202,7 @@ void lara_as_reach(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -414,7 +414,7 @@ void lara_as_jump_back(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -468,7 +468,7 @@ void lara_as_jump_right(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -523,7 +523,7 @@ void lara_as_jump_left(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -576,7 +576,7 @@ void lara_as_jump_up(ItemInfo* item, CollisionInfo* coll)
{ {
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -673,7 +673,7 @@ void lara_as_fall_back(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -749,7 +749,7 @@ void lara_as_swan_dive(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = LS_CROUCH_IDLE; item->Animation.TargetState = LS_CROUCH_IDLE;
TranslateItem(item, coll->Setup.ForwardAngle, CLICK(0.5f)); // HACK: Move forward to avoid standing up or falling out on an edge. TranslateItem(item, coll->Setup.ForwardAngle, CLICK(0.5f)); // HACK: Move forward to avoid standing up or falling out on an edge.
} }
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);
@ -823,7 +823,7 @@ void lara_as_freefall_dive(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
Rumble(0.5f, 0.2f); Rumble(0.5f, 0.2f);
} }
else USE_FEATURE_IF_CPP20([[likely]]) else
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll); SetLaraLand(item, coll);

View file

@ -4,6 +4,7 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/collision/Point.h"
#include "Game/control/box.h" #include "Game/control/box.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/control/los.h" #include "Game/control/los.h"
@ -31,6 +32,7 @@
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Collision::Point;
using namespace TEN::Effects::Bubble; using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::Drip; using namespace TEN::Effects::Drip;
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
@ -426,7 +428,7 @@ void FireShotgun(ItemInfo& laraItem)
player.RightArm.GunFlash = Weapons[(int)LaraWeaponType::Shotgun].FlashTime; player.RightArm.GunFlash = Weapons[(int)LaraWeaponType::Shotgun].FlashTime;
SoundEffect(SFX_TR4_EXPLOSION1, &laraItem.Pose, TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::Water : SoundEnvironment::Land); SoundEffect(SFX_TR4_EXPLOSION1, &laraItem.Pose, TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::ShallowWater : SoundEnvironment::Land);
SoundEffect(Weapons[(int)LaraWeaponType::Shotgun].SampleNum, &laraItem.Pose); SoundEffect(Weapons[(int)LaraWeaponType::Shotgun].SampleNum, &laraItem.Pose);
Rumble(0.5f, 0.2f); Rumble(0.5f, 0.2f);
@ -600,7 +602,7 @@ bool FireHarpoon(ItemInfo& laraItem, const std::optional<Pose>& pose)
auto jointPos = GetJointPosition(&laraItem, LM_RHAND, Vector3i(-2, 373, 77)); auto jointPos = GetJointPosition(&laraItem, LM_RHAND, Vector3i(-2, 373, 77));
harpoonItem.RoomNumber = laraItem.RoomNumber; harpoonItem.RoomNumber = laraItem.RoomNumber;
int floorHeight = GetCollision(jointPos.x, jointPos.y, jointPos.z, harpoonItem.RoomNumber).Position.Floor; int floorHeight = GetPointCollision(jointPos, harpoonItem.RoomNumber).GetFloorHeight();
if (floorHeight >= jointPos.y) if (floorHeight >= jointPos.y)
{ {
harpoonItem.Pose.Position = jointPos; harpoonItem.Pose.Position = jointPos;
@ -693,7 +695,7 @@ void FireGrenade(ItemInfo& laraItem)
grenadeItem.Pose.Position = jointPos; grenadeItem.Pose.Position = jointPos;
auto smokePos = jointPos; auto smokePos = jointPos;
int floorHeight = GetCollision(jointPos.x, jointPos.y, jointPos.z, grenadeItem.RoomNumber).Position.Floor; int floorHeight = GetPointCollision(jointPos, grenadeItem.RoomNumber).GetFloorHeight();
if (floorHeight < jointPos.y) if (floorHeight < jointPos.y)
{ {
grenadeItem.Pose.Position.x = laraItem.Pose.Position.x; grenadeItem.Pose.Position.x = laraItem.Pose.Position.x;
@ -1027,9 +1029,11 @@ void FireCrossbow(ItemInfo& laraItem, const std::optional<Pose>& pose)
boltItem.RoomNumber = laraItem.RoomNumber; boltItem.RoomNumber = laraItem.RoomNumber;
int floorHeight = GetCollision(jointPos.x, jointPos.y, jointPos.z, boltItem.RoomNumber).Position.Floor; int floorHeight = GetPointCollision(jointPos, boltItem.RoomNumber).GetFloorHeight();
if (floorHeight >= jointPos.y) if (floorHeight >= jointPos.y)
{
boltItem.Pose.Position = jointPos; boltItem.Pose.Position = jointPos;
}
else else
{ {
boltItem.Pose.Position = Vector3i(laraItem.Pose.Position.x, jointPos.y, laraItem.Pose.Position.z); boltItem.Pose.Position = Vector3i(laraItem.Pose.Position.x, jointPos.y, laraItem.Pose.Position.z);
@ -1418,20 +1422,20 @@ bool EmitFromProjectile(ItemInfo& projectile, ProjectileType type)
return true; return true;
} }
bool TestProjectileNewRoom(ItemInfo& item, const CollisionResult& coll) bool TestProjectileNewRoom(ItemInfo& item, PointCollisionData& pointColl)
{ {
// Check if projectile changed room. // Check if projectile changed room.
if (item.RoomNumber == coll.RoomNumber) if (item.RoomNumber == pointColl.GetRoomNumber())
return false; return false;
// If currently in water and previously on land, spawn ripple. // If currently in water and previously on land, spawn ripple.
if (TestEnvironment(ENV_FLAG_WATER, item.RoomNumber) != TestEnvironment(ENV_FLAG_WATER, coll.RoomNumber)) if (TestEnvironment(ENV_FLAG_WATER, item.RoomNumber) != TestEnvironment(ENV_FLAG_WATER, pointColl.GetRoomNumber()))
{ {
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
int floorDiff = abs(coll.Position.Floor - item.Pose.Position.y); int floorDiff = abs(pointColl.GetFloorHeight() - item.Pose.Position.y);
int ceilingDiff = abs(coll.Position.Ceiling - item.Pose.Position.y); int ceilingDiff = abs(pointColl.GetCeilingHeight() - item.Pose.Position.y);
int yPoint = (floorDiff > ceilingDiff) ? coll.Position.Ceiling : coll.Position.Floor; int yPoint = (floorDiff > ceilingDiff) ? pointColl.GetCeilingHeight() : pointColl.GetFloorHeight();
if (player.Control.Weapon.GunType != LaraWeaponType::GrenadeLauncher && player.Control.Weapon.GunType != LaraWeaponType::RocketLauncher) if (player.Control.Weapon.GunType != LaraWeaponType::GrenadeLauncher && player.Control.Weapon.GunType != LaraWeaponType::RocketLauncher)
{ {
@ -1447,7 +1451,7 @@ bool TestProjectileNewRoom(ItemInfo& item, const CollisionResult& coll)
} }
} }
ItemNewRoom(item.Index, coll.RoomNumber); ItemNewRoom(item.Index, pointColl.GetRoomNumber());
return true; return true;
} }
@ -1475,7 +1479,7 @@ void ExplodeProjectile(ItemInfo& item, const Vector3i& prevPos)
void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& prevPos, ProjectileType type, int damage) void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& prevPos, ProjectileType type, int damage)
{ {
auto pointColl = GetCollision(&projectile); auto pointColl = GetPointCollision(projectile);
bool hasHit = false; bool hasHit = false;
bool hasHitNotByEmitter = false; bool hasHitNotByEmitter = false;
@ -1485,8 +1489,8 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
// For non-grenade projectiles, check for room collision. // For non-grenade projectiles, check for room collision.
if (type < ProjectileType::Grenade) if (type < ProjectileType::Grenade)
{ {
if (pointColl.Position.Floor < projectile.Pose.Position.y || if (pointColl.GetFloorHeight() < projectile.Pose.Position.y ||
pointColl.Position.Ceiling > projectile.Pose.Position.y) pointColl.GetCeilingHeight() > projectile.Pose.Position.y)
{ {
hasHit = hasHitNotByEmitter = true; hasHit = hasHitNotByEmitter = true;
} }
@ -1559,7 +1563,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
break; break;
// Run through statics. // Run through statics.
for (auto* staticPtr : collObjects.StaticPtrs) for (auto* staticPtr : collObjects.Statics)
{ {
hasHit = hasHitNotByEmitter = doShatter = true; hasHit = hasHitNotByEmitter = doShatter = true;
doExplosion = isExplosive; doExplosion = isExplosive;
@ -1580,7 +1584,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
} }
// Run through items. // Run through items.
for (auto* itemPtr : collObjects.ItemPtrs) for (auto* itemPtr : collObjects.Items)
{ {
// Object was already affected by collision, skip it. // Object was already affected by collision, skip it.
if (std::find(affectedObjects.begin(), affectedObjects.end(), itemPtr->Index) != affectedObjects.end()) if (std::find(affectedObjects.begin(), affectedObjects.end(), itemPtr->Index) != affectedObjects.end())

View file

@ -39,6 +39,7 @@ void HarpoonBoltControl(short itemNumber);
void FireGrenade(ItemInfo& laraItem); void FireGrenade(ItemInfo& laraItem);
void GrenadeControl(short itemNumber); void GrenadeControl(short itemNumber);
void FireRocket(ItemInfo& laraItem); void FireRocket(ItemInfo& laraItem);
void FireRocket(ItemInfo& laraItem);
void RocketControl(short itemNumber); void RocketControl(short itemNumber);
void FireCrossbow(ItemInfo& laraItem, const std::optional<Pose>& pose = std::nullopt); void FireCrossbow(ItemInfo& laraItem, const std::optional<Pose>& pose = std::nullopt);
void FireCrossBowFromLaserSight(ItemInfo& laraItem, GameVector* origin, GameVector* target); void FireCrossBowFromLaserSight(ItemInfo& laraItem, GameVector* origin, GameVector* target);

View file

@ -2,8 +2,9 @@
#include "Game/Lara/lara_overhang.h" #include "Game/Lara/lara_overhang.h"
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/floordata.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/floordata.h"
#include "Game/collision/Point.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_climb.h" #include "Game/Lara/lara_climb.h"
@ -16,6 +17,8 @@
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Input; using namespace TEN::Input;
@ -324,14 +327,14 @@ void lara_col_slopeclimb(ItemInfo* item, CollisionInfo* coll)
auto up = Vector3i(item->Pose.Position.x - slopeData.Offset.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z - slopeData.Offset.z); auto up = Vector3i(item->Pose.Position.x - slopeData.Offset.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z - slopeData.Offset.z);
auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z); auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber); auto probeNow = GetPointCollision(now, item->RoomNumber);
auto probeUp = GetCollision(up.x, up.y, up.z, item->RoomNumber); auto probeUp = GetPointCollision(up, item->RoomNumber);
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber); auto probeDown = GetPointCollision(down, item->RoomNumber);
if (item->Animation.AnimNumber == LA_OVERHANG_LADDER_SLOPE_CONCAVE) if (item->Animation.AnimNumber == LA_OVERHANG_LADDER_SLOPE_CONCAVE)
return; return;
item->Pose.Position.y = probeNow.Position.Ceiling + HEIGHT_ADJUST; item->Pose.Position.y = probeNow.GetCeilingHeight() + HEIGHT_ADJUST;
// Drop down if action not pressed. // Drop down if action not pressed.
if (!IsHeld(In::Action)) if (!IsHeld(In::Action))
@ -351,13 +354,13 @@ void lara_col_slopeclimb(ItemInfo* item, CollisionInfo* coll)
if (IsHeld(In::Forward)) if (IsHeld(In::Forward))
{ {
// Test for ledge over slope. // Test for ledge over slope.
short tempRoom = probeUp.Block->GetNextRoomNumber(up.x, up.z, false).value_or(NO_VALUE); short tempRoom = probeUp.GetSector().GetNextRoomNumber(up.x, up.z, false).value_or(NO_VALUE);
if (tempRoom != NO_VALUE) if (tempRoom != NO_VALUE)
{ {
auto probeLedge = GetCollision(now.x, now.y - CLICK(3), now.z, tempRoom); auto probeLedge = GetPointCollision(Vector3i(now.x, now.y - CLICK(3), now.z), tempRoom);
if ((probeLedge.Position.Floor - probeLedge.Position.Ceiling) >= CLICK(3) && if ((probeLedge.GetFloorHeight() - probeLedge.GetCeilingHeight()) >= CLICK(3) &&
abs((item->Pose.Position.y - (CLICK(2.5f) + 48)) - probeLedge.Position.Floor) < 64) abs((item->Pose.Position.y - (CLICK(2.5f) + 48)) - probeLedge.GetFloorHeight()) < 64)
{ {
AlignToEdge(item, FORWARD_ALIGNMENT); AlignToEdge(item, FORWARD_ALIGNMENT);
SetAnimation(item, LA_OVERHANG_LEDGE_VAULT_START); // Ledge climb-up from slope. SetAnimation(item, LA_OVERHANG_LEDGE_VAULT_START); // Ledge climb-up from slope.
@ -365,32 +368,32 @@ void lara_col_slopeclimb(ItemInfo* item, CollisionInfo* coll)
} }
// Test for slope to overhead ladder transition (convex). // Test for slope to overhead ladder transition (convex).
if (GetClimbFlags(probeUp.BottomBlock) & slopeData.ClimbOrient && if (GetClimbFlags(&probeUp.GetBottomSector()) & slopeData.ClimbOrient &&
InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, CLICK(3), CLICK(4))) InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, CLICK(3), CLICK(4)))
{ {
if (GetCollision(probeUp.Block, up.x, up.y, up.z).Position.Ceiling - item->Pose.Position.y <= (BLOCK(1.5f) - 80)) // Check if a wall is actually there. //if (GetPointCollision(probeUp.Block, up.x, up.y, up.z).GetCeilingHeight() - item->Pose.Position.y <= (BLOCK(1.5f) - 80)) // Check if a wall is actually there.
{ //{
AlignToEdge(item, FORWARD_ALIGNMENT); // AlignToEdge(item, FORWARD_ALIGNMENT);
SetAnimation(item, LA_OVERHANG_SLOPE_LADDER_CONVEX_START); // SetAnimation(item, LA_OVERHANG_SLOPE_LADDER_CONVEX_START);
} //}
} }
// Test for monkey at next position. // Test for monkey at next position.
if (probeUp.BottomBlock->Flags.Monkeyswing) if (probeUp.GetBottomSector().Flags.Monkeyswing)
{ {
int yDelta = probeUp.Position.Ceiling - probeNow.Position.Ceiling; int yDelta = probeUp.GetCeilingHeight() - probeNow.GetCeilingHeight();
int height; // Height variable for bridge ceiling functions. int height; // Height variable for bridge ceiling functions.
// Test for upwards slope to climb. // Test for upwards slope to climb.
short bridge = FindBridge(4, item->Pose.Orientation.y, up, &height, -CLICK(2.5f), -CLICK(1.5f)); short bridge = FindBridge(4, item->Pose.Orientation.y, up, &height, -CLICK(2.5f), -CLICK(1.5f));
if (yDelta >= -CLICK(1.25f) && yDelta <= -CLICK(0.75f) && (SlopeCheck(probeUp.CeilingTilt, slopeData.Goal) || bridge >= 0)) if (yDelta >= -CLICK(1.25f) && yDelta <= -CLICK(0.75f) && (SlopeCheck(GetSurfaceTilt(probeUp.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) || bridge >= 0))
{ {
// Do one more check for wall/ceiling step 2 * offX / Z further to avoid lara sinking her head in wall/step. // Do one more check for wall/ceiling step 2 * offX / Z further to avoid lara sinking her head in wall/step.
auto probeWall = GetCollision((up.x - slopeData.Offset.x), (up.y - CLICK(1)), (up.z - slopeData.Offset.z), item->RoomNumber); auto probeWall = GetPointCollision(Vector3i((up.x - slopeData.Offset.x), (up.y - CLICK(1)), (up.z - slopeData.Offset.z)), item->RoomNumber);
if (!probeWall.Block->IsWall((up.x - slopeData.Offset.x), (up.z - slopeData.Offset.z)) && if (!probeWall.GetSector().IsWall((up.x - slopeData.Offset.x), (up.z - slopeData.Offset.z)) &&
(probeNow.Position.Ceiling - probeWall.Position.Ceiling) > CLICK(0.5f)) // No wall or downward ceiling step. (probeNow.GetCeilingHeight() - probeWall.GetCeilingHeight()) > CLICK(0.5f)) // No wall or downward ceiling step.
{ {
TranslateItem(item, 0, -CLICK(1), -CLICK(1)); TranslateItem(item, 0, -CLICK(1), -CLICK(1));
SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_CLIMB_UP_LEFT : LA_OVERHANG_CLIMB_UP_RIGHT); SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_CLIMB_UP_LEFT : LA_OVERHANG_CLIMB_UP_RIGHT);
@ -405,7 +408,7 @@ void lara_col_slopeclimb(ItemInfo* item, CollisionInfo* coll)
// HACK: because of the different calculations of bridge height in TR4 and TEN, we need to lower yDiff tolerance to 0.9f. // HACK: because of the different calculations of bridge height in TR4 and TEN, we need to lower yDiff tolerance to 0.9f.
if (yDelta > -CLICK(0.9f) && yDelta <= -CLICK(0.5f) && if (yDelta > -CLICK(0.9f) && yDelta <= -CLICK(0.5f) &&
((abs(probeUp.CeilingTilt.x) <= 2 && abs(probeUp.CeilingTilt.y) <= 2) || bridge >= 0)) ((abs(GetSurfaceTilt(probeUp.GetCeilingNormal(), false).ToVector2().x) <= 2 && abs(GetSurfaceTilt(probeUp.GetCeilingNormal(), false).ToVector2().y) <= 2) || bridge >= 0))
{ {
SetAnimation(item, LA_OVERHANG_SLOPE_MONKEY_CONCAVE); // Slope to overhead monkey transition (concave). SetAnimation(item, LA_OVERHANG_SLOPE_MONKEY_CONCAVE); // Slope to overhead monkey transition (concave).
} }
@ -413,30 +416,30 @@ void lara_col_slopeclimb(ItemInfo* item, CollisionInfo* coll)
} }
else if (IsHeld(In::Back)) else if (IsHeld(In::Back))
{ {
if ((GetClimbFlags(GetCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).BottomBlock) & slopeData.ClimbOrient) && //if ((GetClimbFlags(GetPointCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).BottomBlock) & slopeData.ClimbOrient) &&
InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(1))) // InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(1)))
{ //{
AlignToEdge(item, BACKWARD_ALIGNMENT); // AlignToEdge(item, BACKWARD_ALIGNMENT);
SetAnimation(item, LA_OVERHANG_SLOPE_LADDER_CONCAVE); // Slope to underlying ladder transition (concave). // SetAnimation(item, LA_OVERHANG_SLOPE_LADDER_CONCAVE); // Slope to underlying ladder transition (concave).
return; // return;
} //}
if (probeDown.BottomBlock->Flags.Monkeyswing) if (probeDown.GetBottomSector().Flags.Monkeyswing)
{ {
int height; int height;
int yDiff = probeDown.Position.Ceiling - probeNow.Position.Ceiling; int yDiff = probeDown.GetCeilingHeight() - probeNow.GetCeilingHeight();
// Test for flat monkey (abs(slope) < 2). // Test for flat monkey (abs(slope) < 2).
short bridge = FindBridge(0, slopeData.GoalOrient, down, &height, -CLICK(3), -CLICK(2)); short bridge = FindBridge(0, slopeData.GoalOrient, down, &height, -CLICK(3), -CLICK(2));
if (bridge < 0) if (bridge < 0)
bridge = FindBridge(1, slopeData.GoalOrient, down, &height, -CLICK(3), -CLICK(2)); bridge = FindBridge(1, slopeData.GoalOrient, down, &height, -CLICK(3), -CLICK(2));
if ((abs(yDiff) < CLICK(1) && abs(probeDown.CeilingTilt.x) <= 2 && abs(probeDown.CeilingTilt.y) <= 2) || bridge >= 0) if ((abs(yDiff) < CLICK(1) && abs(GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2().x) <= 2 && abs(GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2().y) <= 2) || bridge >= 0)
SetAnimation(item, LA_OVERHANG_SLOPE_MONKEY_CONVEX); // Force slope to underlying monkey transition (convex) SetAnimation(item, LA_OVERHANG_SLOPE_MONKEY_CONVEX); // Force slope to underlying monkey transition (convex)
// Test for downward slope to climb. // Test for downward slope to climb.
bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(2.5f), -CLICK(1.5f)); bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(2.5f), -CLICK(1.5f));
if (yDiff >= CLICK(0.75f) && yDiff <= CLICK(1.25f) && (SlopeCheck(probeDown.CeilingTilt, slopeData.Goal) || bridge >= 0)) if (yDiff >= CLICK(0.75f) && yDiff <= CLICK(1.25f) && (SlopeCheck(GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) || bridge >= 0))
{ {
SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_CLIMB_DOWN_LEFT : LA_OVERHANG_CLIMB_DOWN_RIGHT); SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_CLIMB_DOWN_LEFT : LA_OVERHANG_CLIMB_DOWN_RIGHT);
return; return;
@ -491,9 +494,9 @@ void lara_col_slopehang(ItemInfo* item, CollisionInfo* coll)
auto now = item->Pose.Position; auto now = item->Pose.Position;
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber); auto probeNow = GetPointCollision(now, item->RoomNumber);
item->Pose.Position.y = probeNow.Position.Ceiling + HEIGHT_ADJUST; item->Pose.Position.y = probeNow.GetCeilingHeight() + HEIGHT_ADJUST;
// Drop down if action not pressed. // Drop down if action not pressed.
if (!IsHeld(In::Action)) if (!IsHeld(In::Action))
@ -528,16 +531,16 @@ void lara_col_slopehang(ItemInfo* item, CollisionInfo* coll)
direction = ANGLE(90.0f); direction = ANGLE(90.0f);
} }
auto probeShimmy = GetCollision(shimmy.x, shimmy.y, shimmy.z, item->RoomNumber); auto probeShimmy = GetPointCollision(shimmy, item->RoomNumber);
if (probeShimmy.BottomBlock->Flags.Monkeyswing) if (probeShimmy.GetBottomSector().Flags.Monkeyswing)
{ {
int yDiff = probeShimmy.Position.Ceiling - probeNow.Position.Ceiling; int yDiff = probeShimmy.GetCeilingHeight() - probeNow.GetCeilingHeight();
int height; int height;
short bridge = FindBridge(4, slopeData.GoalOrient, shimmy, &height, -CLICK(2.5f), -CLICK(1.5f)); short bridge = FindBridge(4, slopeData.GoalOrient, shimmy, &height, -CLICK(2.5f), -CLICK(1.5f));
if ((SlopeCheck(probeShimmy.CeilingTilt, slopeData.Goal) && abs(yDiff) < 64) || bridge >= 0) if ((SlopeCheck(GetSurfaceTilt(probeShimmy.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) && abs(yDiff) < 64) || bridge >= 0)
SetAnimation(item, direction < 0 ? LA_OVERHANG_SHIMMY_LEFT : LA_OVERHANG_SHIMMY_RIGHT); SetAnimation(item, direction < 0 ? LA_OVERHANG_SHIMMY_LEFT : LA_OVERHANG_SHIMMY_RIGHT);
} }
} }
@ -567,9 +570,9 @@ void lara_col_slopeshimmy(ItemInfo* item, CollisionInfo* coll)
auto now = item->Pose.Position; auto now = item->Pose.Position;
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber); auto probeNow = GetPointCollision(now, item->RoomNumber);
item->Pose.Position.y = probeNow.Position.Ceiling + HEIGHT_ADJUST; item->Pose.Position.y = probeNow.GetCeilingHeight() + HEIGHT_ADJUST;
auto shimmy = item->Pose.Position; auto shimmy = item->Pose.Position;
if (item->Animation.AnimNumber == LA_OVERHANG_SHIMMY_LEFT) if (item->Animation.AnimNumber == LA_OVERHANG_SHIMMY_LEFT)
@ -583,17 +586,17 @@ void lara_col_slopeshimmy(ItemInfo* item, CollisionInfo* coll)
shimmy.z -= slopeData.Offset.x / 2; shimmy.z -= slopeData.Offset.x / 2;
} }
auto probeShimmy = GetCollision(shimmy.x, shimmy.y, shimmy.z, item->RoomNumber); auto probeShimmy = GetPointCollision(shimmy, item->RoomNumber);
bool cancelShimmy = true; bool cancelShimmy = true;
if (probeShimmy.BottomBlock->Flags.Monkeyswing) if (probeShimmy.GetBottomSector().Flags.Monkeyswing)
{ {
int yDiff = probeShimmy.Position.Ceiling - probeNow.Position.Ceiling; int yDiff = probeShimmy.GetCeilingHeight() - probeNow.GetCeilingHeight();
int height; int height;
short bridge = FindBridge(4, slopeData.GoalOrient, shimmy, &height, -CLICK(2.5f), -CLICK(1.5f)); short bridge = FindBridge(4, slopeData.GoalOrient, shimmy, &height, -CLICK(2.5f), -CLICK(1.5f));
if ((SlopeCheck(probeShimmy.CeilingTilt, slopeData.Goal) && abs(yDiff) < 64) || bridge >= 0) if ((SlopeCheck(GetSurfaceTilt(probeShimmy.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) && abs(yDiff) < 64) || bridge >= 0)
cancelShimmy = false; cancelShimmy = false;
} }
@ -836,16 +839,16 @@ void SlopeHangExtra(ItemInfo* item, CollisionInfo* coll)
auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z); auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber); auto probeDown = GetPointCollision(down, item->RoomNumber);
int ceilDist = item->Pose.Position.y - probeDown.Position.Ceiling; int ceilDist = item->Pose.Position.y - probeDown.GetCeilingHeight();
if (item->Animation.TargetState == LS_LADDER_IDLE) // Prevent going from hang to climb mode if slope is under ladder. if (item->Animation.TargetState == LS_LADDER_IDLE) // Prevent going from hang to climb mode if slope is under ladder.
{ {
if (ceilDist >= CLICK(1) && ceilDist < CLICK(2)) if (ceilDist >= CLICK(1) && ceilDist < CLICK(2))
{ {
if ((probeDown.CeilingTilt.x / 3) == (slopeData.Goal.x / 3) || if ((GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2().x / 3) == (slopeData.Goal.x / 3) ||
(probeDown.CeilingTilt.y / 3) == (slopeData.Goal.y / 3)) (GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2().y / 3) == (slopeData.Goal.y / 3))
{ {
item->Animation.TargetState = LS_HANG; item->Animation.TargetState = LS_HANG;
if (IsHeld(In::Forward)) if (IsHeld(In::Forward))
@ -861,8 +864,8 @@ void SlopeHangExtra(ItemInfo* item, CollisionInfo* coll)
{ {
if (ceilDist < CLICK(1)) if (ceilDist < CLICK(1))
{ {
if ((probeDown.CeilingTilt.x / 3) == (goal.x / 3) || if ((GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2().x / 3) == (goal.x / 3) ||
(probeDown.CeilingTilt.z / 3) == (goal.y / 3)) (GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2().z / 3) == (goal.y / 3))
{ {
SetAnimation(item, LA_REACH_TO_HANG, 21); SetAnimation(item, LA_REACH_TO_HANG, 21);
} }
@ -881,19 +884,19 @@ void SlopeReachExtra(ItemInfo* item, CollisionInfo* coll)
auto now = item->Pose.Position; auto now = item->Pose.Position;
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber); auto probeNow = GetPointCollision(now, item->RoomNumber);
int ceilDist = item->Pose.Position.y - probeNow.Position.Ceiling; int ceilDist = item->Pose.Position.y - probeNow.GetCeilingHeight();
if (probeNow.BottomBlock->Flags.Monkeyswing && ceilDist <= CLICK(3.5f)) if (probeNow.GetBottomSector().Flags.Monkeyswing && ceilDist <= CLICK(3.5f))
{ {
int height; int height;
short bridge = FindBridge(4, slopeData.GoalOrient, now, &height, -CLICK(4), -CLICK(2.5f)); short bridge = FindBridge(4, slopeData.GoalOrient, now, &height, -CLICK(4), -CLICK(2.5f));
if (abs(probeNow.CeilingTilt.x) > 2 || abs(probeNow.CeilingTilt.y) > 2 || bridge >= 0) if (abs(GetSurfaceTilt(probeNow.GetCeilingNormal(), false).ToVector2().x) > 2 || abs(GetSurfaceTilt(probeNow.GetCeilingNormal(), false).ToVector2().y) > 2 || bridge >= 0)
{ {
bool disableGrab = true; bool disableGrab = true;
if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0) if (SlopeCheck(GetSurfaceTilt(probeNow.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) || bridge >= 0)
{ {
if (abs(OrientDelta(item->Pose.Orientation.y, slopeData.GoalOrient)) < ANGLE(33.75f)) if (abs(OrientDelta(item->Pose.Orientation.y, slopeData.GoalOrient)) < ANGLE(33.75f))
disableGrab = false; disableGrab = false;
@ -916,17 +919,17 @@ void SlopeClimbExtra(ItemInfo* item, CollisionInfo* coll)
auto now = item->Pose.Position; auto now = item->Pose.Position;
auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z); auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber); auto probeNow = GetPointCollision(now, item->RoomNumber);
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber); auto probeDown = GetPointCollision(down, item->RoomNumber);
// Block for ladder to overhead slope transition. // Block for ladder to overhead slope transition.
if (item->Animation.AnimNumber == LA_LADDER_IDLE) if (item->Animation.AnimNumber == LA_LADDER_IDLE)
{ {
if (IsHeld(In::Forward)) if (IsHeld(In::Forward))
{ {
int ceilDist = probeNow.Position.Ceiling - item->Pose.Position.y; int ceilDist = probeNow.GetCeilingHeight() - item->Pose.Position.y;
if (probeNow.BottomBlock->Flags.Monkeyswing && ceilDist >= -CLICK(4) && ceilDist <= -CLICK(3)) if (probeNow.GetBottomSector().Flags.Monkeyswing && ceilDist >= -CLICK(4) && ceilDist <= -CLICK(3))
{ {
short facing = item->Pose.Orientation.y + ANGLE(45.0f); short facing = item->Pose.Orientation.y + ANGLE(45.0f);
facing &= ANGLE(270.0f); facing &= ANGLE(270.0f);
@ -934,9 +937,9 @@ void SlopeClimbExtra(ItemInfo* item, CollisionInfo* coll)
int height; int height;
short bridge = FindBridge(4, facing, now, &height, -CLICK(4), -CLICK(3)); short bridge = FindBridge(4, facing, now, &height, -CLICK(4), -CLICK(3));
if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0) if (SlopeCheck(GetSurfaceTilt(probeNow.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) || bridge >= 0)
{ {
item->Pose.Position.y = probeNow.Position.Ceiling + 900; item->Pose.Position.y = probeNow.GetCeilingHeight() + 900;
SetAnimation(item, LA_OVERHANG_LADDER_SLOPE_CONCAVE); // Ladder to overhead slope transition (concave). SetAnimation(item, LA_OVERHANG_LADDER_SLOPE_CONCAVE); // Ladder to overhead slope transition (concave).
} }
} }
@ -944,9 +947,9 @@ void SlopeClimbExtra(ItemInfo* item, CollisionInfo* coll)
if (IsHeld(In::Back)) if (IsHeld(In::Back))
{ {
int ceilDist = probeDown.Position.Ceiling - item->Pose.Position.y; int ceilDist = probeDown.GetCeilingHeight() - item->Pose.Position.y;
if (probeDown.BottomBlock->Flags.Monkeyswing && ceilDist >= 0 && ceilDist <= CLICK(1)) if (probeDown.GetBottomSector().Flags.Monkeyswing && ceilDist >= 0 && ceilDist <= CLICK(1))
{ {
short facing = item->Pose.Orientation.y + ANGLE(45.0f); short facing = item->Pose.Orientation.y + ANGLE(45.0f);
facing &= ANGLE(270.0f); facing &= ANGLE(270.0f);
@ -954,9 +957,9 @@ void SlopeClimbExtra(ItemInfo* item, CollisionInfo* coll)
int height; int height;
short bridge = FindBridge(4, facing, down, &height, -CLICK(0.5f), -CLICK(0.25f)); short bridge = FindBridge(4, facing, down, &height, -CLICK(0.5f), -CLICK(0.25f));
if (SlopeCheck(probeDown.CeilingTilt, slopeData.Goal) || bridge >= 0) if (SlopeCheck(GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) || bridge >= 0)
{ {
item->Pose.Position.y = probeDown.Position.Ceiling - 156; item->Pose.Position.y = probeDown.GetCeilingHeight() - 156;
SetAnimation(item, LA_OVERHANG_LADDER_SLOPE_CONVEX); // Ladder to underlying slope transition (convex). SetAnimation(item, LA_OVERHANG_LADDER_SLOPE_CONVEX); // Ladder to underlying slope transition (convex).
} }
} }
@ -967,12 +970,12 @@ void SlopeClimbExtra(ItemInfo* item, CollisionInfo* coll)
// Extends LS_LADDER_IDLE (56) // Extends LS_LADDER_IDLE (56)
bool LadderMonkeyExtra(ItemInfo* item, CollisionInfo* coll) bool LadderMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
{ {
auto probe = GetCollision(item); auto probe = GetPointCollision(*item);
if (probe.Position.CeilingSlope) if (probe.IsSteepCeiling())
return false; return false;
if (probe.BottomBlock->Flags.Monkeyswing && (item->Pose.Position.y - coll->Setup.Height - CLICK(0.5f) <= probe.Position.Ceiling)) if (probe.GetBottomSector().Flags.Monkeyswing && (item->Pose.Position.y - coll->Setup.Height - CLICK(0.5f) <= probe.GetCeilingHeight()))
{ {
item->Animation.TargetState = LS_MONKEY_IDLE; item->Animation.TargetState = LS_MONKEY_IDLE;
return true; return true;
@ -991,15 +994,15 @@ void SlopeClimbDownExtra(ItemInfo* item, CollisionInfo* coll)
auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z); auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber); auto probeDown = GetPointCollision(down, item->RoomNumber);
if (item->Animation.AnimNumber == LA_LADDER_DOWN) // Make Lara stop before underlying slope ceiling at correct height. if (item->Animation.AnimNumber == LA_LADDER_DOWN) // Make Lara stop before underlying slope ceiling at correct height.
{ {
if (IsHeld(In::Back)) if (IsHeld(In::Back))
{ {
int ceilDist = probeDown.Position.Ceiling - item->Pose.Position.y; int ceilDist = probeDown.GetCeilingHeight() - item->Pose.Position.y;
if (probeDown.BottomBlock->Flags.Monkeyswing && ceilDist >= 0 && ceilDist <= CLICK(1)) if (probeDown.GetBottomSector().Flags.Monkeyswing && ceilDist >= 0 && ceilDist <= CLICK(1))
{ {
short facing = item->Pose.Orientation.y + ANGLE(45.0f); short facing = item->Pose.Orientation.y + ANGLE(45.0f);
facing &= ANGLE(270.0f); facing &= ANGLE(270.0f);
@ -1007,9 +1010,9 @@ void SlopeClimbDownExtra(ItemInfo* item, CollisionInfo* coll)
int height; int height;
short bridge = FindBridge(4, facing, down, &height, -CLICK(0.5f), -CLICK(0.25f)); short bridge = FindBridge(4, facing, down, &height, -CLICK(0.5f), -CLICK(0.25f));
if (SlopeCheck(probeDown.CeilingTilt, slopeData.Goal) || bridge >= 0) if (SlopeCheck(GetSurfaceTilt(probeDown.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) || bridge >= 0)
{ {
item->Pose.Position.y = probeDown.Position.Ceiling - 156; item->Pose.Position.y = probeDown.GetCeilingHeight() - 156;
item->Animation.TargetState = LS_LADDER_IDLE; item->Animation.TargetState = LS_LADDER_IDLE;
} }
} }
@ -1054,14 +1057,14 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
auto now = item->Pose.Position; auto now = item->Pose.Position;
auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z); auto down = Vector3i(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber); auto probeNow = GetPointCollision(now, item->RoomNumber);
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber); auto probeDown = GetPointCollision(down, item->RoomNumber);
if (item->Animation.AnimNumber == LA_REACH_TO_MONKEY && !GetFrameIndex(item, 0)) // Manage proper grabbing of monkey slope on forward jump. if (item->Animation.AnimNumber == LA_REACH_TO_MONKEY && !GetFrameIndex(item, 0)) // Manage proper grabbing of monkey slope on forward jump.
{ {
int ceilDist = item->Pose.Position.y - probeNow.Position.Ceiling; int ceilDist = item->Pose.Position.y - probeNow.GetCeilingHeight();
if (probeNow.BottomBlock->Flags.Monkeyswing && ceilDist <= CLICK(3.5f)) if (probeNow.GetBottomSector().Flags.Monkeyswing && ceilDist <= CLICK(3.5f))
{ {
short facing = item->Pose.Orientation.y + ANGLE(45.0f); short facing = item->Pose.Orientation.y + ANGLE(45.0f);
facing &= 0xC000; facing &= 0xC000;
@ -1069,12 +1072,12 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
int height; int height;
short bridge = FindBridge(4, facing, now, &height, -CLICK(3.5f), -CLICK(2.5f)); short bridge = FindBridge(4, facing, now, &height, -CLICK(3.5f), -CLICK(2.5f));
if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0) if (SlopeCheck(GetSurfaceTilt(probeNow.GetCeilingNormal(), false).ToVector2(), slopeData.Goal) || bridge >= 0)
{ {
lara->Context.NextCornerPos.Orientation.z = AlignToGrab(item); lara->Context.NextCornerPos.Orientation.z = AlignToGrab(item);
int ceiling = GetCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).Position.Ceiling; /*int ceiling = GetPointCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).GetCeilingHeight();
item->Pose.Position.y = ceiling + HEIGHT_ADJUST; item->Pose.Position.y = ceiling + HEIGHT_ADJUST;*/
SetAnimation(item, LA_OVERHANG_HANG_SWING); SetAnimation(item, LA_OVERHANG_HANG_SWING);
} }
@ -1083,16 +1086,16 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
if (IsHeld(In::Forward)) // Monkey to slope transitions. if (IsHeld(In::Forward)) // Monkey to slope transitions.
{ {
if (probeNow.BottomBlock->Flags.Monkeyswing && if (probeNow.GetBottomSector().Flags.Monkeyswing &&
((item->Animation.AnimNumber == LA_REACH_TO_MONKEY && GetFrameIndex(item, 0) >= 54) || item->Animation.AnimNumber == LA_MONKEY_IDLE)) ((item->Animation.AnimNumber == LA_REACH_TO_MONKEY && GetFrameIndex(item, 0) >= 54) || item->Animation.AnimNumber == LA_MONKEY_IDLE))
{ {
if (abs(OrientDelta(slopeData.GoalOrient, item->Pose.Orientation.y)) <= ANGLE(30.0f) && if (abs(OrientDelta(slopeData.GoalOrient, item->Pose.Orientation.y)) <= ANGLE(30.0f) &&
InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(0.5f))) InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(0.5f)))
{ {
if (probeDown.BottomBlock->Flags.Monkeyswing) /*if (probeDown.BottomBlock->Flags.Monkeyswing)
{ {
int ceiling = GetCollision(probeDown.Block, down.x, now.y, down.z).Position.Ceiling; int ceiling = GetPointCollision(probeDown.Block, down.x, now.y, down.z).GetCeilingHeight();
int yDiff = ceiling - probeNow.Position.Ceiling; int yDiff = ceiling - probeNow.GetCeilingHeight();
int height; int height;
short bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(7) >> 1, -CLICK(5) >> 1); short bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(7) >> 1, -CLICK(5) >> 1);
@ -1113,7 +1116,7 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
return; return;
//item->Pose.Position.y = ceiling + 914; //item->Pose.Position.y = ceiling + 914;
} }
} }*/
} }
} }
@ -1122,19 +1125,19 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
// Additional overhang ladder tests. // Additional overhang ladder tests.
int y = item->Pose.Position.y - coll->Setup.Height; int y = item->Pose.Position.y - coll->Setup.Height;
auto probe = GetCollision(down.x, item->Pose.Position.y - coll->Setup.Height, down.z, item->RoomNumber); auto probe = GetPointCollision(Vector3i(down.x, item->Pose.Position.y - coll->Setup.Height, down.z), item->RoomNumber);
if (probe.BottomBlock->Flags.IsWallClimbable(GetClimbDirectionFlags(item->Pose.Orientation.y + ANGLE(180.0f))) && if (probe.GetBottomSector().Flags.IsWallClimbable(GetClimbDirectionFlags(item->Pose.Orientation.y + ANGLE(180.0f))) &&
probe.Position.Floor >= (item->Pose.Position.y - CLICK(1)) && probe.GetFloorHeight() >= (item->Pose.Position.y - CLICK(1)) &&
probe.Position.Ceiling <= (y - CLICK(1))) probe.GetCeilingHeight() <= (y - CLICK(1)))
{ {
// Primary checks succeeded, now do C-shaped secondary probing. // Primary checks succeeded, now do C-shaped secondary probing.
probe = GetCollision(down.x, y, down.z, probe.RoomNumber); probe = GetPointCollision(Vector3i(down.x, y, down.z), probe.GetRoomNumber());
probe = GetCollision(down.x, y - CLICK(2), down.z, probe.RoomNumber); probe = GetPointCollision(Vector3i(down.x, y - CLICK(2), down.z), probe.GetRoomNumber());
probe = GetCollision(now.x, y - CLICK(2), now.z, probe.RoomNumber); probe = GetPointCollision(Vector3i(now.x, y - CLICK(2), now.z), probe.GetRoomNumber());
if (probe.Position.Floor <= (y - CLICK(1)) || if (probe.GetFloorHeight() <= (y - CLICK(1)) ||
probe.Position.Ceiling >= (y - CLICK(1))) probe.GetCeilingHeight() >= (y - CLICK(1)))
{ {
if (item->Animation.TargetState != LS_LADDER_IDLE) if (item->Animation.TargetState != LS_LADDER_IDLE)
{ {

View file

@ -1024,7 +1024,7 @@ public:
Ammo& operator --() Ammo& operator --()
{ {
assertion(Count > 0, "Ammo count is already 0."); TENAssert(Count > 0, "Ammo count is already 0.");
--Count; --Count;
return *this; return *this;
} }

View file

@ -273,6 +273,5 @@ void lara_as_surface_climb_out(ItemInfo* item, CollisionInfo* coll)
player.Control.Look.Mode = LookMode::None; player.Control.Look.Mode = LookMode::None;
coll->Setup.EnableObjectPush = false; coll->Setup.EnableObjectPush = false;
coll->Setup.EnableSpasm = false; coll->Setup.EnableSpasm = false;
Camera.flags = CF_FOLLOW_CENTER; Camera.flags = CF_FOLLOW_CENTER; // Forces the camera to follow Lara instead of snapping.
Camera.laraNode = LM_HIPS; // Forces the camera to follow Lara instead of snapping.
} }

View file

@ -5,6 +5,7 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/Point.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/control/los.h" #include "Game/control/los.h"
#include "Game/items.h" #include "Game/items.h"
@ -16,16 +17,16 @@
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_monkey.h" #include "Game/Lara/lara_monkey.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer.h" #include "Specific/configuration.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/trutils.h" #include "Specific/trutils.h"
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Entities::Player; using namespace TEN::Entities::Player;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer;
using namespace TEN::Utils; using namespace TEN::Utils;
// ----------------------------- // -----------------------------
@ -46,18 +47,18 @@ bool TestValidLedge(ItemInfo* item, CollisionInfo* coll, bool ignoreHeadroom, bo
int y = item->Pose.Position.y - coll->Setup.Height; int y = item->Pose.Position.y - coll->Setup.Height;
// Get frontal collision data // Get frontal collision data
auto frontLeft = GetCollision(item->Pose.Position.x + xl, y, item->Pose.Position.z + zl, GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber); auto frontLeft = GetPointCollision(Vector3i(item->Pose.Position.x + xl, y, item->Pose.Position.z + zl), GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber);
auto frontRight = GetCollision(item->Pose.Position.x + xr, y, item->Pose.Position.z + zr, GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber); auto frontRight = GetPointCollision(Vector3i(item->Pose.Position.x + xr, y, item->Pose.Position.z + zr), GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber);
// If any of the frontal collision results intersects item bounds, return false, because there is material intersection. // If any of the frontal collision results intersects item bounds, return false, because there is material intersection.
// This check helps to filter out cases when Lara is formally facing corner but ledge check returns true because probe distance is fixed. // This check helps to filter out cases when Lara is formally facing corner but ledge check returns true because probe distance is fixed.
if (frontLeft.Position.Floor < (item->Pose.Position.y - CLICK(0.5f)) || frontRight.Position.Floor < (item->Pose.Position.y - CLICK(0.5f))) if (frontLeft.GetFloorHeight() < (item->Pose.Position.y - CLICK(0.5f)) || frontRight.GetFloorHeight() < (item->Pose.Position.y - CLICK(0.5f)))
return false; return false;
if (frontLeft.Position.Ceiling > (item->Pose.Position.y - coll->Setup.Height) || frontRight.Position.Ceiling > (item->Pose.Position.y - coll->Setup.Height)) if (frontLeft.GetCeilingHeight() >(item->Pose.Position.y - coll->Setup.Height) || frontRight.GetCeilingHeight() > (item->Pose.Position.y - coll->Setup.Height))
return false; return false;
//g_Renderer.AddDebugSphere(Vector3(item->pos.Position.x + xl, left, item->pos.Position.z + zl), 64, Vector4::One, RendererDebugPage::CollisionStats); //DrawDebugSphere(Vector3(item->pos.Position.x + xl, left, item->pos.Position.z + zl), 64, Vector4::One, RendererDebugPage::CollisionStats);
//g_Renderer.AddDebugSphere(Vector3(item->pos.Position.x + xr, right, item->pos.Position.z + zr), 64, Vector4::One, RendererDebugPage::CollisionStats); //DrawDebugSphere(Vector3(item->pos.Position.x + xr, right, item->pos.Position.z + zr), 64, Vector4::One, RendererDebugPage::CollisionStats);
// Determine ledge probe embed offset. // Determine ledge probe embed offset.
// We use 0.2f radius extents here for two purposes. First - we can't guarantee that shifts weren't already applied // We use 0.2f radius extents here for two purposes. First - we can't guarantee that shifts weren't already applied
@ -66,8 +67,8 @@ bool TestValidLedge(ItemInfo* item, CollisionInfo* coll, bool ignoreHeadroom, bo
int zf = phd_cos(coll->NearestLedgeAngle) * (coll->Setup.Radius * 1.2f); int zf = phd_cos(coll->NearestLedgeAngle) * (coll->Setup.Radius * 1.2f);
// Get floor heights at both points // Get floor heights at both points
auto left = GetCollision(item->Pose.Position.x + xf + xl, y, item->Pose.Position.z + zf + zl, GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber).Position.Floor; auto left = GetPointCollision(Vector3i(item->Pose.Position.x + xf + xl, y, item->Pose.Position.z + zf + zl), GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber).GetFloorHeight();
auto right = GetCollision(item->Pose.Position.x + xf + xr, y, item->Pose.Position.z + zf + zr, GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber).Position.Floor; auto right = GetPointCollision(Vector3i(item->Pose.Position.x + xf + xr, y, item->Pose.Position.z + zf + zr), GetRoomVector(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).RoomNumber).GetFloorHeight();
// If specified, limit vertical search zone only to nearest height // If specified, limit vertical search zone only to nearest height
if (heightLimit && (abs(left - y) > CLICK(0.5f) || abs(right - y) > CLICK(0.5f))) if (heightLimit && (abs(left - y) > CLICK(0.5f) || abs(right - y) > CLICK(0.5f)))
@ -214,7 +215,7 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll)
z += testShift.y; z += testShift.y;
} }
if (TestLaraNearClimbableWall(item, GetCollision(x, item->Pose.Position.y, z, item->RoomNumber).BottomBlock)) if (TestLaraNearClimbableWall(item, &GetPointCollision(Vector3i(x, item->Pose.Position.y, z), item->RoomNumber).GetBottomSector()))
{ {
if (!TestLaraHangOnClimbableWall(item, coll)) if (!TestLaraHangOnClimbableWall(item, coll))
verticalShift = 0; // Ignore vertical shift if ladder is encountered next block verticalShift = 0; // Ignore vertical shift if ladder is encountered next block
@ -450,7 +451,7 @@ bool TestLaraClimbIdle(ItemInfo* item, CollisionInfo* coll)
bool TestLaraNearClimbableWall(ItemInfo* item, FloorInfo* floor) bool TestLaraNearClimbableWall(ItemInfo* item, FloorInfo* floor)
{ {
if (floor == nullptr) if (floor == nullptr)
floor = GetCollision(item).BottomBlock; floor = &GetPointCollision(*item).GetBottomSector();
return ((256 << (GetQuadrant(item->Pose.Orientation.y))) & GetClimbFlags(floor)); return ((256 << (GetQuadrant(item->Pose.Orientation.y))) & GetClimbFlags(floor));
} }
@ -523,7 +524,7 @@ bool TestLaraValidHangPosition(ItemInfo* item, CollisionInfo* coll)
// Get incoming ledge height and own Lara's upper bound. // Get incoming ledge height and own Lara's upper bound.
// First one will be negative while first one is positive. // First one will be negative while first one is positive.
// Difference between two indicates difference in height between ledges. // Difference between two indicates difference in height between ledges.
auto frontFloor = GetCollision(item, lara->Control.MoveAngle, coll->Setup.Radius + CLICK(0.5f), -LARA_HEIGHT).Position.Floor; auto frontFloor = GetPointCollision(*item, lara->Control.MoveAngle, coll->Setup.Radius + CLICK(0.5f), -LARA_HEIGHT).GetFloorHeight();
auto laraUpperBound = item->Pose.Position.y - coll->Setup.Height; auto laraUpperBound = item->Pose.Position.y - coll->Setup.Height;
// If difference is above 1/2 click, return false (ledge is out of reach). // If difference is above 1/2 click, return false (ledge is out of reach).
@ -582,7 +583,7 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
item->Pose = cornerResult.RealPositionResult; item->Pose = cornerResult.RealPositionResult;
lara->Context.NextCornerPos.Position = Vector3i( lara->Context.NextCornerPos.Position = Vector3i(
item->Pose.Position.x, item->Pose.Position.x,
GetCollision(item, item->Pose.Orientation.y, coll->Setup.Radius + 16, -(coll->Setup.Height + CLICK(0.5f))).Position.Floor + abs(bounds.Y1), GetPointCollision(*item, item->Pose.Orientation.y, coll->Setup.Radius + 16, -(coll->Setup.Height + CLICK(0.5f))).GetFloorHeight() + abs(bounds.Y1),
item->Pose.Position.z item->Pose.Position.z
); );
lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y; lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y;
@ -640,7 +641,7 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
// Store next position // Store next position
item->Pose = cornerResult.RealPositionResult; item->Pose = cornerResult.RealPositionResult;
lara->Context.NextCornerPos.Position.x = item->Pose.Position.x; lara->Context.NextCornerPos.Position.x = item->Pose.Position.x;
lara->Context.NextCornerPos.Position.y = GetCollision(item, item->Pose.Orientation.y, coll->Setup.Radius * 1.25f, -(abs(bounds.Y1) + LARA_HEADROOM)).Position.Floor + abs(bounds.Y1); lara->Context.NextCornerPos.Position.y = GetPointCollision(*item, item->Pose.Orientation.y, coll->Setup.Radius * 1.25f, -(abs(bounds.Y1) + LARA_HEADROOM)).GetFloorHeight() + abs(bounds.Y1);
lara->Context.NextCornerPos.Position.z = item->Pose.Position.z; lara->Context.NextCornerPos.Position.z = item->Pose.Position.z;
lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y; lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
@ -745,11 +746,11 @@ bool TestHangSwingIn(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
int y = item->Pose.Position.y; int y = item->Pose.Position.y;
auto probe = GetCollision(item, item->Pose.Orientation.y, OFFSET_RADIUS(coll->Setup.Radius)); auto probe = GetPointCollision(*item, item->Pose.Orientation.y, OFFSET_RADIUS(coll->Setup.Radius));
if ((probe.Position.Floor - y) > 0 && if ((probe.GetFloorHeight() - y) > 0 &&
(probe.Position.Ceiling - y) < -CLICK(1.6f) && (probe.GetCeilingHeight() - y) < -CLICK(1.6f) &&
probe.Position.Floor != NO_HEIGHT) probe.GetFloorHeight() != NO_HEIGHT)
{ {
return true; return true;
} }
@ -852,32 +853,22 @@ bool LaraPositionOnLOS(ItemInfo* item, short angle, int distance)
int LaraFloorFront(ItemInfo* item, short angle, int distance) int LaraFloorFront(ItemInfo* item, short angle, int distance)
{ {
return LaraCollisionFront(item, angle, distance).Position.Floor; auto pointColl = GetPointCollision(*item, angle, distance, -LARA_HEIGHT);
if (pointColl.GetFloorHeight() == NO_HEIGHT)
return pointColl.GetFloorHeight();
return (pointColl.GetFloorHeight() - item->Pose.Position.y);
} }
int LaraCeilingFront(ItemInfo* item, short angle, int distance, int height) int LaraCeilingFront(ItemInfo* item, short angle, int distance, int height)
{ {
return LaraCeilingCollisionFront(item, angle, distance, height).Position.Ceiling; auto pointColl = GetPointCollision(*item, angle, distance, -height);
}
CollisionResult LaraCollisionFront(ItemInfo* item, short angle, int distance) if (pointColl.GetCeilingHeight() == NO_HEIGHT)
{ return pointColl.GetCeilingHeight();
auto probe = GetCollision(item, angle, distance, -LARA_HEIGHT);
if (probe.Position.Floor != NO_HEIGHT) return ((pointColl.GetCeilingHeight() + height) - item->Pose.Position.y);
probe.Position.Floor -= item->Pose.Position.y;
return probe;
}
CollisionResult LaraCeilingCollisionFront(ItemInfo* item, short angle, int distance, int height)
{
auto probe = GetCollision(item, angle, distance, -height);
if (probe.Position.Ceiling != NO_HEIGHT)
probe.Position.Ceiling += height - item->Pose.Position.y;
return probe;
} }
bool TestPlayerWaterStepOut(ItemInfo* item, CollisionInfo* coll) bool TestPlayerWaterStepOut(ItemInfo* item, CollisionInfo* coll)
@ -885,17 +876,17 @@ bool TestPlayerWaterStepOut(ItemInfo* item, CollisionInfo* coll)
auto& player = GetLaraInfo(*item); auto& player = GetLaraInfo(*item);
// Get point collision. // Get point collision.
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(*item);
int vPos = item->Pose.Position.y; int vPos = item->Pose.Position.y;
if (coll->CollisionType == CollisionType::Front || if (coll->CollisionType == CollisionType::Front ||
pointColl.Position.FloorSlope || pointColl.IsSteepFloor() ||
(pointColl.Position.Floor - vPos) <= 0) (pointColl.GetFloorHeight() - vPos) <= 0)
{ {
return false; return false;
} }
if ((pointColl.Position.Floor - vPos) >= -CLICK(0.5f)) if ((pointColl.GetFloorHeight() - vPos) >= -CLICK(0.5f))
{ {
SetAnimation(item, LA_STAND_IDLE); SetAnimation(item, LA_STAND_IDLE);
} }
@ -905,7 +896,7 @@ bool TestPlayerWaterStepOut(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
} }
item->Pose.Position.y = pointColl.Position.Floor; item->Pose.Position.y = pointColl.GetFloorHeight();
UpdateLaraRoom(item, -(STEPUP_HEIGHT - 3)); UpdateLaraRoom(item, -(STEPUP_HEIGHT - 3));
ResetPlayerLean(item); ResetPlayerLean(item);
@ -961,8 +952,8 @@ bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll)
if (coll->HitStatic) if (coll->HitStatic)
return false; return false;
auto probe = GetCollision(item, coll->Setup.ForwardAngle, CLICK(2), -CLICK(1)); auto probe = GetPointCollision(*item, coll->Setup.ForwardAngle, CLICK(2), -CLICK(1));
int headroom = probe.Position.Floor - probe.Position.Ceiling; int headroom = probe.GetFloorHeight() - probe.GetCeilingHeight();
if (frontFloor <= -CLICK(1)) if (frontFloor <= -CLICK(1))
{ {
@ -1095,8 +1086,8 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll)
{ {
auto& player = GetLaraInfo(*item); auto& player = GetLaraInfo(*item);
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(*item);
int waterDepth = GetWaterDepth(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, pointColl.RoomNumber); int waterDepth = GetWaterDepth(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, pointColl.GetRoomNumber());
if (waterDepth == NO_HEIGHT) if (waterDepth == NO_HEIGHT)
{ {
@ -1109,7 +1100,7 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll)
SetAnimation(item, LA_UNDERWATER_TO_STAND); SetAnimation(item, LA_UNDERWATER_TO_STAND);
ResetPlayerLean(item); ResetPlayerLean(item);
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
item->Pose.Position.y = pointColl.Position.Floor; item->Pose.Position.y = pointColl.GetFloorHeight();
item->Animation.IsAirborne = false; item->Animation.IsAirborne = false;
item->Animation.Velocity.y = 0.0f; item->Animation.Velocity.y = 0.0f;
item->Animation.Velocity.z = 0.0f; item->Animation.Velocity.z = 0.0f;
@ -1202,12 +1193,12 @@ std::optional<VaultTestResult> TestLaraVaultTolerance(ItemInfo* item, CollisionI
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
int distance = OFFSET_RADIUS(coll->Setup.Radius); int distance = OFFSET_RADIUS(coll->Setup.Radius);
auto probeFront = GetCollision(item, coll->NearestLedgeAngle, distance, -coll->Setup.Height); auto probeFront = GetPointCollision(*item, coll->NearestLedgeAngle, distance, -coll->Setup.Height);
auto probeMiddle = GetCollision(item); auto probeMiddle = GetPointCollision(*item);
bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item); bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item);
bool swampTooDeep = testSetup.CheckSwampDepth ? (isSwamp && lara->Context.WaterSurfaceDist < -CLICK(3)) : isSwamp; bool swampTooDeep = testSetup.CheckSwampDepth ? (isSwamp && lara->Context.WaterSurfaceDist < -CLICK(3)) : isSwamp;
int y = isSwamp ? item->Pose.Position.y : probeMiddle.Position.Floor; // HACK: Avoid cheese when in the midst of performing a step. Can be done better. @Sezz 2022.04.08 int y = isSwamp ? item->Pose.Position.y : probeMiddle.GetFloorHeight(); // HACK: Avoid cheese when in the midst of performing a step. Can be done better. @Sezz 2022.04.08
// Check swamp depth (if applicable). // Check swamp depth (if applicable).
if (swampTooDeep) if (swampTooDeep)
@ -1220,27 +1211,27 @@ std::optional<VaultTestResult> TestLaraVaultTolerance(ItemInfo* item, CollisionI
// Raise y position of point/room probe by increments of CLICK(0.5f) to find potential vault ledge. // Raise y position of point/room probe by increments of CLICK(0.5f) to find potential vault ledge.
int yOffset = testSetup.LowerFloorBound; int yOffset = testSetup.LowerFloorBound;
while (((probeFront.Position.Ceiling - y) > -coll->Setup.Height || // Ceiling is below Lara's height... while (((probeFront.GetCeilingHeight() - y) > -coll->Setup.Height || // Ceiling is below Lara's height...
abs(probeFront.Position.Ceiling - probeFront.Position.Floor) <= testSetup.ClampMin || // OR clamp is too small abs(probeFront.GetCeilingHeight() - probeFront.GetFloorHeight()) <= testSetup.ClampMin || // OR clamp is too small
abs(probeFront.Position.Ceiling - probeFront.Position.Floor) > testSetup.ClampMax) && // OR clamp is too large (future-proofing; not possible right now). abs(probeFront.GetCeilingHeight() - probeFront.GetFloorHeight()) > testSetup.ClampMax) && // OR clamp is too large (future-proofing; not possible right now).
yOffset > (testSetup.UpperFloorBound - coll->Setup.Height)) // Offset is not too high. yOffset > (testSetup.UpperFloorBound - coll->Setup.Height)) // Offset is not too high.
{ {
probeFront = GetCollision(item, coll->NearestLedgeAngle, distance, yOffset); probeFront = GetPointCollision(*item, coll->NearestLedgeAngle, distance, yOffset);
yOffset -= std::max<int>(CLICK(0.5f), testSetup.ClampMin); yOffset -= std::max<int>(CLICK(0.5f), testSetup.ClampMin);
} }
// Discard walls. // Discard walls.
if (probeFront.Position.Floor == NO_HEIGHT) if (probeFront.GetFloorHeight() == NO_HEIGHT)
return std::nullopt; return std::nullopt;
// Assess point/room collision. // Assess point/room collision.
if ((probeFront.Position.Floor - y) < testSetup.LowerFloorBound && // Within lower floor bound. if ((probeFront.GetFloorHeight() - y) < testSetup.LowerFloorBound && // Within lower floor bound.
(probeFront.Position.Floor - y) >= testSetup.UpperFloorBound && // Within upper floor bound. (probeFront.GetFloorHeight() - y) >= testSetup.UpperFloorBound && // Within upper floor bound.
abs(probeFront.Position.Ceiling - probeFront.Position.Floor) > testSetup.ClampMin && // Within clamp min. abs(probeFront.GetCeilingHeight() - probeFront.GetFloorHeight()) > testSetup.ClampMin && // Within clamp min.
abs(probeFront.Position.Ceiling - probeFront.Position.Floor) <= testSetup.ClampMax && // Within clamp max. abs(probeFront.GetCeilingHeight() - probeFront.GetFloorHeight()) <= testSetup.ClampMax && // Within clamp max.
abs(probeMiddle.Position.Ceiling - probeFront.Position.Floor) >= testSetup.GapMin) // Gap is optically permissive. abs(probeMiddle.GetCeilingHeight() - probeFront.GetFloorHeight()) >= testSetup.GapMin) // Gap is optically permissive.
{ {
return VaultTestResult{ probeFront.Position.Floor }; return VaultTestResult{ probeFront.GetFloorHeight() };
} }
return std::nullopt; return std::nullopt;
@ -1390,20 +1381,20 @@ std::optional<VaultTestResult> TestLaraLadderAutoJump(ItemInfo* item, CollisionI
int y = item->Pose.Position.y; int y = item->Pose.Position.y;
int distance = OFFSET_RADIUS(coll->Setup.Radius); int distance = OFFSET_RADIUS(coll->Setup.Radius);
auto probeFront = GetCollision(item, coll->NearestLedgeAngle, distance, -coll->Setup.Height); auto probeFront = GetPointCollision(*item, coll->NearestLedgeAngle, distance, -coll->Setup.Height);
auto probeMiddle = GetCollision(item); auto probeMiddle = GetPointCollision(*item);
// Check ledge angle. // Check ledge angle.
if (!TestValidLedgeAngle(item, coll)) if (!TestValidLedgeAngle(item, coll))
return std::nullopt; return std::nullopt;
if (lara->Control.CanClimbLadder && // Ladder sector flag set. if (lara->Control.CanClimbLadder && // Ladder sector flag set.
(probeMiddle.Position.Ceiling - y) <= -CLICK(6.5f) && // Within lowest middle ceiling bound. (Synced with TestLaraLadderMount()) (probeMiddle.GetCeilingHeight() - y) <= -CLICK(6.5f) && // Within lowest middle ceiling bound. (Synced with TestLaraLadderMount())
((probeFront.Position.Floor - y) <= -CLICK(6.5f) || // Floor height is appropriate, OR ((probeFront.GetFloorHeight() - y) <= -CLICK(6.5f) || // Floor height is appropriate, OR
(probeFront.Position.Ceiling - y) > -CLICK(6.5f)) && // Ceiling height is appropriate. (Synced with TestLaraLadderMount()) (probeFront.GetCeilingHeight() - y) > -CLICK(6.5f)) && // Ceiling height is appropriate. (Synced with TestLaraLadderMount())
coll->NearestLedgeDistance <= coll->Setup.Radius) // Appropriate distance from wall. coll->NearestLedgeDistance <= coll->Setup.Radius) // Appropriate distance from wall.
{ {
return VaultTestResult{ probeMiddle.Position.Ceiling, false, true, true }; return VaultTestResult{ probeMiddle.GetCeilingHeight(), false, true, true };
} }
return std::nullopt; return std::nullopt;
@ -1415,18 +1406,18 @@ std::optional<VaultTestResult> TestLaraLadderMount(ItemInfo* item, CollisionInfo
int y = item->Pose.Position.y; int y = item->Pose.Position.y;
int distance = OFFSET_RADIUS(coll->Setup.Radius); int distance = OFFSET_RADIUS(coll->Setup.Radius);
auto probeFront = GetCollision(item, coll->NearestLedgeAngle, distance, -coll->Setup.Height); auto probeFront = GetPointCollision(*item, coll->NearestLedgeAngle, distance, -coll->Setup.Height);
auto probeMiddle = GetCollision(item); auto probeMiddle = GetPointCollision(*item);
// Check ledge angle. // Check ledge angle.
if (!TestValidLedgeAngle(item, coll)) if (!TestValidLedgeAngle(item, coll))
return std::nullopt; return std::nullopt;
if (lara->Control.CanClimbLadder && // Ladder sector flag set. if (lara->Control.CanClimbLadder && // Ladder sector flag set.
(probeMiddle.Position.Ceiling - y) <= -CLICK(4.5f) && // Within lower middle ceiling bound. (probeMiddle.GetCeilingHeight() - y) <= -CLICK(4.5f) && // Within lower middle ceiling bound.
(probeMiddle.Position.Ceiling - y) > -CLICK(6.5f) && // Within upper middle ceiling bound. (probeMiddle.GetCeilingHeight() - y) > -CLICK(6.5f) && // Within upper middle ceiling bound.
(probeMiddle.Position.Floor - y) > -CLICK(6.5f) && // Within upper middle floor bound. (Synced with TestLaraAutoJump()) (probeMiddle.GetFloorHeight() - y) > -CLICK(6.5f) && // Within upper middle floor bound. (Synced with TestLaraAutoJump())
(probeFront.Position.Ceiling - y) <= -CLICK(4.5f) && // Within lowest front ceiling bound. (probeFront.GetCeilingHeight() - y) <= -CLICK(4.5f) && // Within lowest front ceiling bound.
coll->NearestLedgeDistance <= coll->Setup.Radius) // Appropriate distance from wall. coll->NearestLedgeDistance <= coll->Setup.Radius) // Appropriate distance from wall.
{ {
return VaultTestResult{ NO_HEIGHT, true, true, false }; return VaultTestResult{ NO_HEIGHT, true, true, false };
@ -1435,18 +1426,18 @@ std::optional<VaultTestResult> TestLaraLadderMount(ItemInfo* item, CollisionInfo
return std::nullopt; return std::nullopt;
} }
std::optional<VaultTestResult> TestLaraMonkeyAutoJump(ItemInfo* item, CollisionInfo* coll) std::optional<VaultTestResult> TestLaraAutoMonkeySwingJump(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
int y = item->Pose.Position.y; int y = item->Pose.Position.y;
auto probe = GetCollision(item); auto probe = GetPointCollision(*item);
if (lara->Control.CanMonkeySwing && // Monkey swing sector flag set. if (lara->Control.CanMonkeySwing && // Monkey swing sector flag set.
(probe.Position.Ceiling - y) < -LARA_HEIGHT_MONKEY && // Within lower ceiling bound. (probe.GetCeilingHeight() - y) < -LARA_HEIGHT_MONKEY && // Within lower ceiling bound.
(probe.Position.Ceiling - y) >= -CLICK(7)) // Within upper ceiling bound. (probe.GetCeilingHeight() - y) >= -CLICK(7)) // Within upper ceiling bound.
{ {
return VaultTestResult{ probe.Position.Ceiling, false, false, true }; return VaultTestResult{ probe.GetCeilingHeight(), false, false, true };
} }
return std::nullopt; return std::nullopt;
@ -1538,8 +1529,8 @@ std::optional<VaultTestResult> TestLaraVault(ItemInfo* item, CollisionInfo* coll
// In this case, they fail due to a reliance on ShiftItem(). @Sezz 2021.02.05 // In this case, they fail due to a reliance on ShiftItem(). @Sezz 2021.02.05
// Auto jump to monkey swing. // Auto jump to monkey swing.
vaultResult = TestLaraMonkeyAutoJump(item, coll); vaultResult = TestLaraAutoMonkeySwingJump(item, coll);
if (vaultResult.has_value() && g_GameFlow->HasMonkeyAutoJump()) if (vaultResult.has_value() && g_Configuration.EnableAutoMonkeySwingJump)
{ {
vaultResult->TargetState = LS_AUTO_JUMP; vaultResult->TargetState = LS_AUTO_JUMP;
if (!HasStateDispatch(item, vaultResult->TargetState)) if (!HasStateDispatch(item, vaultResult->TargetState))
@ -1606,15 +1597,15 @@ bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll)
CrawlVaultTestResult TestLaraCrawlVaultTolerance(ItemInfo* item, CollisionInfo* coll, CrawlVaultTestSetup testSetup) CrawlVaultTestResult TestLaraCrawlVaultTolerance(ItemInfo* item, CollisionInfo* coll, CrawlVaultTestSetup testSetup)
{ {
int y = item->Pose.Position.y; int y = item->Pose.Position.y;
auto probeA = GetCollision(item, item->Pose.Orientation.y, testSetup.CrossDist, -LARA_HEIGHT_CRAWL); // Crossing. auto probeA = GetPointCollision(*item, item->Pose.Orientation.y, testSetup.CrossDist, -LARA_HEIGHT_CRAWL); // Crossing.
auto probeB = GetCollision(item, item->Pose.Orientation.y, testSetup.DestDist, -LARA_HEIGHT_CRAWL); // Approximate destination. auto probeB = GetPointCollision(*item, item->Pose.Orientation.y, testSetup.DestDist, -LARA_HEIGHT_CRAWL); // Approximate destination.
auto probeMiddle = GetCollision(item); auto probeMiddle = GetPointCollision(*item);
bool isSlope = testSetup.CheckSlope ? probeB.Position.FloorSlope : false; bool isSlope = testSetup.CheckSlope ? probeB.IsSteepFloor() : false;
bool isDeath = testSetup.CheckDeath ? probeB.Block->Flags.Death : false; bool isDeath = testSetup.CheckDeath ? probeB.GetSector().Flags.Death : false;
// Discard walls. // Discard walls.
if (probeA.Position.Floor == NO_HEIGHT || probeB.Position.Floor == NO_HEIGHT) if (probeA.GetFloorHeight() == NO_HEIGHT || probeB.GetFloorHeight() == NO_HEIGHT)
return CrawlVaultTestResult{ false }; return CrawlVaultTestResult{ false };
// Check for slope or death sector (if applicable). // Check for slope or death sector (if applicable).
@ -1622,14 +1613,14 @@ CrawlVaultTestResult TestLaraCrawlVaultTolerance(ItemInfo* item, CollisionInfo*
return CrawlVaultTestResult{ false }; return CrawlVaultTestResult{ false };
// Assess point/room collision. // Assess point/room collision.
if ((probeA.Position.Floor - y) <= testSetup.LowerFloorBound && // Within lower floor bound. if ((probeA.GetFloorHeight() - y) <= testSetup.LowerFloorBound && // Within lower floor bound.
(probeA.Position.Floor - y) >= testSetup.UpperFloorBound && // Within upper floor bound. (probeA.GetFloorHeight() - y) >= testSetup.UpperFloorBound && // Within upper floor bound.
abs(probeA.Position.Ceiling - probeA.Position.Floor) > testSetup.ClampMin && // Crossing clamp limit. abs(probeA.GetCeilingHeight() - probeA.GetFloorHeight()) > testSetup.ClampMin && // Crossing clamp limit.
abs(probeB.Position.Ceiling - probeB.Position.Floor) > testSetup.ClampMin && // Destination clamp limit. abs(probeB.GetCeilingHeight() - probeB.GetFloorHeight()) > testSetup.ClampMin && // Destination clamp limit.
abs(probeMiddle.Position.Ceiling - probeA.Position.Floor) >= testSetup.GapMin && // Gap is optically permissive (going up). abs(probeMiddle.GetCeilingHeight() - probeA.GetFloorHeight()) >= testSetup.GapMin && // Gap is optically permissive (going up).
abs(probeA.Position.Ceiling - probeMiddle.Position.Floor) >= testSetup.GapMin && // Gap is optically permissive (going down). abs(probeA.GetCeilingHeight() - probeMiddle.GetFloorHeight()) >= testSetup.GapMin && // Gap is optically permissive (going down).
abs(probeA.Position.Floor - probeB.Position.Floor) <= testSetup.FloorBound && // Crossing/destination floor height difference suggests continuous crawl surface. abs(probeA.GetFloorHeight() - probeB.GetFloorHeight()) <= testSetup.FloorBound && // Crossing/destination floor height difference suggests continuous crawl surface.
(probeA.Position.Ceiling - y) < -testSetup.GapMin) // Ceiling height is permissive. (probeA.GetCeilingHeight() - y) < -testSetup.GapMin) // Ceiling height is permissive.
{ {
return CrawlVaultTestResult{ true }; return CrawlVaultTestResult{ true };
} }
@ -1718,7 +1709,7 @@ CrawlVaultTestResult TestLaraCrawlVault(ItemInfo* item, CollisionInfo* coll)
{ {
if (IsHeld(In::Crouch) && TestLaraCrawlDownStep(item, coll).Success) if (IsHeld(In::Crouch) && TestLaraCrawlDownStep(item, coll).Success)
crawlVaultResult.TargetState = LS_CRAWL_STEP_DOWN; crawlVaultResult.TargetState = LS_CRAWL_STEP_DOWN;
else USE_FEATURE_IF_CPP20([[likely]]) else
crawlVaultResult.TargetState = LS_CRAWL_EXIT_STEP_DOWN; crawlVaultResult.TargetState = LS_CRAWL_EXIT_STEP_DOWN;
crawlVaultResult.Success = HasStateDispatch(item, crawlVaultResult.TargetState); crawlVaultResult.Success = HasStateDispatch(item, crawlVaultResult.TargetState);
@ -1731,7 +1722,7 @@ CrawlVaultTestResult TestLaraCrawlVault(ItemInfo* item, CollisionInfo* coll)
{ {
if (IsHeld(In::Walk)) if (IsHeld(In::Walk))
crawlVaultResult.TargetState = LS_CRAWL_EXIT_FLIP; crawlVaultResult.TargetState = LS_CRAWL_EXIT_FLIP;
else USE_FEATURE_IF_CPP20([[likely]]) else
crawlVaultResult.TargetState = LS_CRAWL_EXIT_JUMP; crawlVaultResult.TargetState = LS_CRAWL_EXIT_JUMP;
crawlVaultResult.Success = HasStateDispatch(item, crawlVaultResult.TargetState); crawlVaultResult.Success = HasStateDispatch(item, crawlVaultResult.TargetState);
@ -1763,14 +1754,14 @@ bool TestLaraCrawlToHang(ItemInfo* item, CollisionInfo* coll)
{ {
int y = item->Pose.Position.y; int y = item->Pose.Position.y;
int distance = CLICK(1.2f); int distance = CLICK(1.2f);
auto probe = GetCollision(item, item->Pose.Orientation.y + ANGLE(180.0f), distance, -LARA_HEIGHT_CRAWL); auto probe = GetPointCollision(*item, item->Pose.Orientation.y + ANGLE(180.0f), distance, -LARA_HEIGHT_CRAWL);
bool objectCollided = TestLaraObjectCollision(item, item->Pose.Orientation.y + ANGLE(180.0f), CLICK(1.2f), -LARA_HEIGHT_CRAWL); bool objectCollided = TestLaraObjectCollision(item, item->Pose.Orientation.y + ANGLE(180.0f), CLICK(1.2f), -LARA_HEIGHT_CRAWL);
if (!objectCollided && // No obstruction. if (!objectCollided && // No obstruction.
(probe.Position.Floor - y) >= LARA_HEIGHT_STRETCH && // Highest floor bound. (probe.GetFloorHeight() - y) >= LARA_HEIGHT_STRETCH && // Highest floor bound.
(probe.Position.Ceiling - y) <= -CLICK(0.75f) && // Gap is optically permissive. (probe.GetCeilingHeight() - y) <= -CLICK(0.75f) && // Gap is optically permissive.
probe.Position.Floor != NO_HEIGHT) probe.GetFloorHeight() != NO_HEIGHT)
{ {
return true; return true;
} }
@ -1801,9 +1792,9 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, fl
auto sphere = BoundingSphere(spherePos, poleProbeCollRadius); auto sphere = BoundingSphere(spherePos, poleProbeCollRadius);
auto offsetSphere = BoundingSphere(spherePos + sphereOffset2D, poleProbeCollRadius); auto offsetSphere = BoundingSphere(spherePos + sphereOffset2D, poleProbeCollRadius);
//g_Renderer.AddDebugSphere(sphere.Center, 16.0f, Vector4(1, 0, 0, 1), RendererDebugPage::CollisionStats); //DrawDebugSphere(sphere.Center, 16.0f, Vector4(1, 0, 0, 1), RendererDebugPage::CollisionStats);
for (const auto* itemPtr : collObjects.ItemPtrs) for (const auto* itemPtr : collObjects.Items)
{ {
if (itemPtr->ObjectNumber != ID_POLEROPE) if (itemPtr->ObjectNumber != ID_POLEROPE)
continue; continue;
@ -1811,7 +1802,7 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, fl
auto poleBox = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose); auto poleBox = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose);
poleBox.Extents = poleBox.Extents + Vector3(coll->Setup.Radius, 0.0f, coll->Setup.Radius); poleBox.Extents = poleBox.Extents + Vector3(coll->Setup.Radius, 0.0f, coll->Setup.Radius);
//g_Renderer.AddDebugBox(poleBox, Vector4(0, 0, 1, 1), RendererDebugPage::CollisionStats); //DrawDebugBox(poleBox, Vector4(0, 0, 1, 1), RendererDebugPage::CollisionStats);
if (poleBox.Intersects(sphere) || poleBox.Intersects(offsetSphere)) if (poleBox.Intersects(sphere) || poleBox.Intersects(offsetSphere))
{ {

View file

@ -34,8 +34,6 @@ bool TestLaraFacingCorner(const ItemInfo* item, short headingAngle, float dist);
bool LaraPositionOnLOS(ItemInfo* item, short angle, int distance); bool LaraPositionOnLOS(ItemInfo* item, short angle, int distance);
int LaraFloorFront(ItemInfo* item, short angle, int distance); int LaraFloorFront(ItemInfo* item, short angle, int distance);
int LaraCeilingFront(ItemInfo* item, short angle, int distance, int height); int LaraCeilingFront(ItemInfo* item, short angle, int distance, int height);
CollisionResult LaraCollisionFront(ItemInfo* item, short angle, int distance);
CollisionResult LaraCeilingCollisionFront(ItemInfo* item, short angle, int distance, int height);
bool TestPlayerWaterStepOut(ItemInfo* item, CollisionInfo* coll); bool TestPlayerWaterStepOut(ItemInfo* item, CollisionInfo* coll);
bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll); bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll);
@ -58,7 +56,7 @@ std::optional<VaultTestResult> TestLaraVault3StepsToCrouch(ItemInfo* item, Colli
std::optional<VaultTestResult> TestLaraLedgeAutoJump(ItemInfo* item, CollisionInfo* coll); std::optional<VaultTestResult> TestLaraLedgeAutoJump(ItemInfo* item, CollisionInfo* coll);
std::optional<VaultTestResult> TestLaraLadderAutoJump(ItemInfo* item, CollisionInfo* coll); std::optional<VaultTestResult> TestLaraLadderAutoJump(ItemInfo* item, CollisionInfo* coll);
std::optional<VaultTestResult> TestLaraLadderMount(ItemInfo* item, CollisionInfo* coll); std::optional<VaultTestResult> TestLaraLadderMount(ItemInfo* item, CollisionInfo* coll);
std::optional<VaultTestResult> TestLaraMonkeyAutoJump(ItemInfo* item, CollisionInfo* coll); std::optional<VaultTestResult> TestLaraAutoMonkeySwingJump(ItemInfo* item, CollisionInfo* coll);
std::optional<VaultTestResult> TestLaraVault(ItemInfo* item, CollisionInfo* coll); std::optional<VaultTestResult> TestLaraVault(ItemInfo* item, CollisionInfo* coll);
bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll); bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll);

View file

@ -127,7 +127,7 @@ void InitializeGameFlags()
ZeroMemory(FlipMap, MAX_FLIPMAP * sizeof(int)); ZeroMemory(FlipMap, MAX_FLIPMAP * sizeof(int));
ZeroMemory(FlipStats, MAX_FLIPMAP * sizeof(bool)); ZeroMemory(FlipStats, MAX_FLIPMAP * sizeof(bool));
FlipEffect = -1; FlipEffect = NO_VALUE;
FlipStatus = false; FlipStatus = false;
Camera.underwater = false; Camera.underwater = false;
} }

View file

@ -3,6 +3,7 @@
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/Point.h"
#include "Game/control/box.h" #include "Game/control/box.h"
#include "Game/control/flipeffect.h" #include "Game/control/flipeffect.h"
#include "Game/items.h" #include "Game/items.h"
@ -15,6 +16,7 @@
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Collision::Point;
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Math; using namespace TEN::Math;
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
@ -110,49 +112,95 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased)
break; break;
case AnimCommandType::SoundEffect: case AnimCommandType::SoundEffect:
if (isFrameBased && item.Animation.FrameNumber == commandDataPtr[0]) {
int frameNumber = commandDataPtr[0];
if (isFrameBased && item.Animation.FrameNumber == frameNumber)
{ {
if (!Objects[item.ObjectNumber].waterCreature) // Get sound ID and sound environment flag from packed data.
{ int soundID = commandDataPtr[1] & 0xFFF; // Exclude last 4 bits for sound ID.
bool playInWater = (commandDataPtr[1] & 0x8000) != 0; int soundEnvFlag = commandDataPtr[1] & 0xF000; // Keep only last 4 bits for sound environment flag.
bool playOnLand = (commandDataPtr[1] & 0x4000) != 0;
bool playAlways = (playInWater && playOnLand) || (!playInWater && !playOnLand);
if (item.IsLara()) // FAILSAFE.
{ if (item.RoomNumber == NO_VALUE)
auto& player = GetLaraInfo(item);
if (playAlways ||
(playOnLand && (player.Context.WaterSurfaceDist >= -SHALLOW_WATER_DEPTH || player.Context.WaterSurfaceDist == NO_HEIGHT)) ||
(playInWater && player.Context.WaterSurfaceDist < -SHALLOW_WATER_DEPTH && player.Context.WaterSurfaceDist != NO_HEIGHT && !TestEnvironment(ENV_FLAG_SWAMP, &item)))
{
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
}
}
else
{
if (item.RoomNumber == NO_VALUE)
{
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
}
else if (TestEnvironment(ENV_FLAG_WATER, &item))
{
if (playAlways || (playInWater && TestEnvironment(ENV_FLAG_WATER, Camera.pos.RoomNumber)))
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
}
else if (playAlways || (playOnLand && !TestEnvironment(ENV_FLAG_WATER, Camera.pos.RoomNumber) && !TestEnvironment(ENV_FLAG_SWAMP, Camera.pos.RoomNumber)))
{
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
}
}
}
else
{ {
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, TestEnvironment(ENV_FLAG_WATER, &item) ? SoundEnvironment::Water : SoundEnvironment::Land); 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();
bool isWater = TestEnvironment(ENV_FLAG_WATER, roomNumberAtPos);
bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, roomNumberAtPos);
// Get sound environment for sound effect.
auto soundEnv = std::optional<SoundEnvironment>();
switch (requiredSoundEnv)
{
case SoundEnvironment::Always:
soundEnv = SoundEnvironment::Always;
break;
case SoundEnvironment::Land:
if (!isWater && !isSwamp)
soundEnv = SoundEnvironment::Land;
break;
case SoundEnvironment::ShallowWater:
if (isWater)
{
// HACK: Must update assets before removing this exception for water creatures.
const auto& object = Objects[item.ObjectNumber];
soundEnv = object.waterCreature ? SoundEnvironment::Underwater : SoundEnvironment::ShallowWater;
}
break;
case SoundEnvironment::Swamp:
if (isSwamp)
soundEnv = SoundEnvironment::Swamp;
break;
case SoundEnvironment::Underwater:
if (isWater || isSwamp)
soundEnv = SoundEnvironment::Underwater;
break;
}
if (soundEnv.has_value())
SoundEffect(soundID, &item.Pose, *soundEnv);
} }
commandDataPtr += 2; commandDataPtr += 2;
}
break; break;
case AnimCommandType::Flipeffect: case AnimCommandType::Flipeffect:
@ -506,7 +554,7 @@ const AnimFrame* GetFrame(GAME_OBJECT_ID objectID, int animNumber, int frameNumb
const auto& object = Objects[objectID]; const auto& object = Objects[objectID];
int animIndex = object.animIndex + animNumber; int animIndex = object.animIndex + animNumber;
assertion(animIndex < g_Level.Anims.size(), "GetFrame() attempted to access missing animation."); TENAssert(animIndex < g_Level.Anims.size(), "GetFrame() attempted to access missing animation.");
const auto& anim = GetAnimData(object, animNumber); const auto& anim = GetAnimData(object, animNumber);

View file

@ -3,6 +3,7 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/Point.h"
#include "Game/control/los.h" #include "Game/control/los.h"
#include "Game/effects/debris.h" #include "Game/effects/debris.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
@ -21,12 +22,13 @@
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
using TEN::Renderer::g_Renderer;
using namespace TEN::Collision::Point;
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
using TEN::Renderer::g_Renderer;
constexpr auto PARTICLE_FADE_THRESHOLD = BLOCK(14); constexpr auto PARTICLE_FADE_THRESHOLD = BLOCK(14);
constexpr auto COLL_CHECK_THRESHOLD = BLOCK(4); constexpr auto COLL_CHECK_THRESHOLD = BLOCK(4);
@ -132,8 +134,8 @@ static int GetLookCameraVerticalOffset(const ItemInfo& item, const CollisionInfo
} }
// Get floor-to-ceiling height. // Get floor-to-ceiling height.
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(item);
int floorToCeilHeight = abs(pointColl.Position.Ceiling - pointColl.Position.Floor); int floorToCeilHeight = abs(pointColl.GetCeilingHeight() - pointColl.GetFloorHeight());
// Return appropriate vertical offset. // Return appropriate vertical offset.
return -((verticalOffset < floorToCeilHeight) ? verticalOffset : floorToCeilHeight); return -((verticalOffset < floorToCeilHeight) ? verticalOffset : floorToCeilHeight);
@ -173,8 +175,8 @@ void LookCamera(ItemInfo& item, const CollisionInfo& coll)
auto lookAtPos = Geometry::TranslatePoint(pivotPos, orient, LOOK_AT_DIST); auto lookAtPos = Geometry::TranslatePoint(pivotPos, orient, LOOK_AT_DIST);
// Determine best position. // Determine best position.
auto origin = GameVector(pivotPos, GetCollision(&item, item.Pose.Orientation.y, pivotOffset.z, pivotOffset.y).RoomNumber); auto origin = GameVector(pivotPos, GetPointCollision(item, item.Pose.Orientation.y, pivotOffset.z, pivotOffset.y).GetRoomNumber());
auto target = GameVector(idealPos, GetCollision(origin.ToVector3i(), origin.RoomNumber, orient, idealDist).RoomNumber); auto target = GameVector(idealPos, GetPointCollision(origin.ToVector3i(), origin.RoomNumber, orient.ToDirection(), idealDist).GetRoomNumber());
// Handle room and object collisions. // Handle room and object collisions.
LOSAndReturnTarget(&origin, &target, 0); LOSAndReturnTarget(&origin, &target, 0);
@ -343,9 +345,9 @@ void MoveCamera(GameVector* ideal, int speed)
if (TestEnvironment(ENV_FLAG_SWAMP, Camera.pos.RoomNumber)) if (TestEnvironment(ENV_FLAG_SWAMP, Camera.pos.RoomNumber))
y = g_Level.Rooms[Camera.pos.RoomNumber].y - CLICK(1); y = g_Level.Rooms[Camera.pos.RoomNumber].y - CLICK(1);
auto probe = GetCollision(Camera.pos.x, y, Camera.pos.z, Camera.pos.RoomNumber); auto pointColl = GetPointCollision(Vector3i(Camera.pos.x, y, Camera.pos.z), Camera.pos.RoomNumber);
if (y < probe.Position.Ceiling || if (y < pointColl.GetCeilingHeight() ||
y > probe.Position.Floor) y > pointColl.GetFloorHeight())
{ {
LOSAndReturnTarget(&Camera.target, &Camera.pos, 0); LOSAndReturnTarget(&Camera.target, &Camera.pos, 0);
@ -364,41 +366,41 @@ void MoveCamera(GameVector* ideal, int speed)
} }
} }
probe = GetCollision(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.RoomNumber); pointColl = GetPointCollision(Camera.pos.ToVector3i(), Camera.pos.RoomNumber);
int buffer = CLICK(1) - 1; int buffer = CLICK(1) - 1;
if ((Camera.pos.y - buffer) < probe.Position.Ceiling && if ((Camera.pos.y - buffer) < pointColl.GetCeilingHeight() &&
(Camera.pos.y + buffer) > probe.Position.Floor && (Camera.pos.y + buffer) > pointColl.GetFloorHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
Camera.pos.y = (probe.Position.Floor + probe.Position.Ceiling) / 2; Camera.pos.y = (pointColl.GetFloorHeight() + pointColl.GetCeilingHeight()) / 2;
} }
else if ((Camera.pos.y + buffer) > probe.Position.Floor && else if ((Camera.pos.y + buffer) > pointColl.GetFloorHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
Camera.pos.y = probe.Position.Floor - buffer; Camera.pos.y = pointColl.GetFloorHeight() - buffer;
} }
else if ((Camera.pos.y - buffer) < probe.Position.Ceiling && else if ((Camera.pos.y - buffer) < pointColl.GetCeilingHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
Camera.pos.y = probe.Position.Ceiling + buffer; Camera.pos.y = pointColl.GetCeilingHeight() + buffer;
} }
else if (probe.Position.Ceiling >= probe.Position.Floor || else if (pointColl.GetCeilingHeight() >= pointColl.GetFloorHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT) pointColl.GetCeilingHeight() == NO_HEIGHT)
{ {
Camera.pos = *ideal; Camera.pos = *ideal;
} }
ItemsCollideCamera(); ItemsCollideCamera();
Camera.pos.RoomNumber = GetCollision(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.RoomNumber).RoomNumber; Camera.pos.RoomNumber = GetPointCollision(Camera.pos.ToVector3i(), Camera.pos.RoomNumber).GetRoomNumber();
LookAt(&Camera, 0); LookAt(&Camera, 0);
UpdateMikePos(*LaraItem); UpdateMikePos(*LaraItem);
Camera.oldType = Camera.type; Camera.oldType = Camera.type;
@ -465,7 +467,7 @@ void MoveObjCamera(GameVector* ideal, ItemInfo* camSlotId, int camMeshId, ItemIn
} }
Camera.pos += (ideal->ToVector3i() - Camera.pos.ToVector3i()) / speed; Camera.pos += (ideal->ToVector3i() - Camera.pos.ToVector3i()) / speed;
Camera.pos.RoomNumber = GetCollision(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.RoomNumber).RoomNumber; Camera.pos.RoomNumber = GetPointCollision(Camera.pos.ToVector3i(), Camera.pos.RoomNumber).GetRoomNumber();
LookAt(&Camera, 0); LookAt(&Camera, 0);
auto angle = Camera.target.ToVector3i() - Camera.pos.ToVector3i(); auto angle = Camera.target.ToVector3i() - Camera.pos.ToVector3i();
@ -529,21 +531,23 @@ void ChaseCamera(ItemInfo* item)
int distance = Camera.targetDistance * phd_cos(Camera.actualElevation); int distance = Camera.targetDistance * phd_cos(Camera.actualElevation);
auto probe = GetCollision(Camera.target.x, Camera.target.y + CLICK(1), Camera.target.z, Camera.target.RoomNumber); auto pointColl = GetPointCollision(Vector3i(Camera.target.x, Camera.target.y + CLICK(1), Camera.target.z), Camera.target.RoomNumber);
if (TestEnvironment(ENV_FLAG_SWAMP, probe.RoomNumber)) if (TestEnvironment(ENV_FLAG_SWAMP, pointColl.GetRoomNumber()))
Camera.target.y = g_Level.Rooms[probe.RoomNumber].maxceiling - CLICK(1); Camera.target.y = g_Level.Rooms[pointColl.GetRoomNumber()].maxceiling - CLICK(1);
int y = Camera.target.y; int y = Camera.target.y;
probe = GetCollision(Camera.target.x, y, Camera.target.z, Camera.target.RoomNumber); pointColl = GetPointCollision(Vector3i(Camera.target.x, y, Camera.target.z), Camera.target.RoomNumber);
if (((y < probe.Position.Ceiling || probe.Position.Floor < y) || probe.Position.Floor <= probe.Position.Ceiling) || if (((y < pointColl.GetCeilingHeight() || pointColl.GetFloorHeight() < y) || pointColl.GetFloorHeight() <= pointColl.GetCeilingHeight()) ||
(probe.Position.Floor == NO_HEIGHT || probe.Position.Ceiling == NO_HEIGHT)) (pointColl.GetFloorHeight() == NO_HEIGHT || pointColl.GetCeilingHeight() == NO_HEIGHT))
{ {
TargetSnaps++; TargetSnaps++;
Camera.target = LastTarget; Camera.target = LastTarget;
} }
else else
{
TargetSnaps = 0; TargetSnaps = 0;
}
for (int i = 0; i < maxSwivelSteps; i++) for (int i = 0; i < maxSwivelSteps; i++)
Ideals[i].y = Camera.target.y + (Camera.targetDistance * phd_sin(Camera.actualElevation)); Ideals[i].y = Camera.target.y + (Camera.targetDistance * phd_sin(Camera.actualElevation));
@ -646,43 +650,43 @@ void CombatCamera(ItemInfo* item)
Camera.targetElevation = player.ExtraHeadRot.x + player.ExtraTorsoRot.x + item->Pose.Orientation.x - ANGLE(15.0f); Camera.targetElevation = player.ExtraHeadRot.x + player.ExtraTorsoRot.x + item->Pose.Orientation.x - ANGLE(15.0f);
} }
auto probe = GetCollision(Camera.target.x, Camera.target.y + CLICK(1), Camera.target.z, Camera.target.RoomNumber); auto pointColl = GetPointCollision(Vector3i(Camera.target.x, Camera.target.y + CLICK(1), Camera.target.z), Camera.target.RoomNumber);
if (TestEnvironment(ENV_FLAG_SWAMP, probe.RoomNumber)) if (TestEnvironment(ENV_FLAG_SWAMP, pointColl.GetRoomNumber()))
Camera.target.y = g_Level.Rooms[probe.RoomNumber].y - CLICK(1); Camera.target.y = g_Level.Rooms[pointColl.GetRoomNumber()].y - CLICK(1);
probe = GetCollision(Camera.target.x, Camera.target.y, Camera.target.z, Camera.target.RoomNumber); pointColl = GetPointCollision(Camera.target.ToVector3i(), Camera.target.RoomNumber);
Camera.target.RoomNumber = probe.RoomNumber; Camera.target.RoomNumber = pointColl.GetRoomNumber();
int buffer = CLICK(0.25f); int buffer = CLICK(0.25f);
if ((probe.Position.Ceiling + buffer) > (probe.Position.Floor - buffer) && if ((pointColl.GetCeilingHeight() + buffer) > (pointColl.GetFloorHeight() - buffer) &&
probe.Position.Floor != NO_HEIGHT && pointColl.GetFloorHeight() != NO_HEIGHT &&
probe.Position.Ceiling != NO_HEIGHT) pointColl.GetCeilingHeight() != NO_HEIGHT)
{ {
Camera.target.y = (probe.Position.Ceiling + probe.Position.Floor) / 2; Camera.target.y = (pointColl.GetCeilingHeight() + pointColl.GetFloorHeight()) / 2;
Camera.targetElevation = 0; Camera.targetElevation = 0;
} }
else if (Camera.target.y > (probe.Position.Floor - buffer) && else if (Camera.target.y > (pointColl.GetFloorHeight() - buffer) &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
Camera.target.y = probe.Position.Floor - buffer; Camera.target.y = pointColl.GetFloorHeight() - buffer;
Camera.targetElevation = 0; Camera.targetElevation = 0;
} }
else if (Camera.target.y < (probe.Position.Ceiling + buffer) && else if (Camera.target.y < (pointColl.GetCeilingHeight() + buffer) &&
probe.Position.Ceiling != NO_HEIGHT) pointColl.GetCeilingHeight() != NO_HEIGHT)
{ {
Camera.target.y = probe.Position.Ceiling + buffer; Camera.target.y = pointColl.GetCeilingHeight() + buffer;
Camera.targetElevation = 0; Camera.targetElevation = 0;
} }
int y = Camera.target.y; int y = Camera.target.y;
probe = GetCollision(Camera.target.x, y, Camera.target.z, Camera.target.RoomNumber); pointColl = GetPointCollision(Vector3i(Camera.target.x, y, Camera.target.z), Camera.target.RoomNumber);
Camera.target.RoomNumber = probe.RoomNumber; Camera.target.RoomNumber = pointColl.GetRoomNumber();
if (y < probe.Position.Ceiling || if (y < pointColl.GetCeilingHeight() ||
y > probe.Position.Floor || y > pointColl.GetFloorHeight() ||
probe.Position.Ceiling >= probe.Position.Floor || pointColl.GetCeilingHeight() >= pointColl.GetFloorHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT) pointColl.GetCeilingHeight() == NO_HEIGHT)
{ {
TargetSnaps++; TargetSnaps++;
Camera.target = LastTarget; Camera.target = LastTarget;
@ -768,116 +772,114 @@ bool CameraCollisionBounds(GameVector* ideal, int push, bool yFirst)
int y = ideal->y; int y = ideal->y;
int z = ideal->z; int z = ideal->z;
CollisionResult probe = {}; auto pointColl = GetPointCollision(Vector3i(x, y, z), ideal->RoomNumber);
if (yFirst) if (yFirst)
{ {
probe = GetCollision(x, y, z, ideal->RoomNumber);
int buffer = CLICK(1) - 1; int buffer = CLICK(1) - 1;
if ((y - buffer) < probe.Position.Ceiling && if ((y - buffer) < pointColl.GetCeilingHeight() &&
(y + buffer) > probe.Position.Floor && (y + buffer) > pointColl.GetFloorHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
y = (probe.Position.Floor + probe.Position.Ceiling) / 2; y = (pointColl.GetFloorHeight() + pointColl.GetCeilingHeight()) / 2;
} }
else if ((y + buffer) > probe.Position.Floor && else if ((y + buffer) > pointColl.GetFloorHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
y = probe.Position.Floor - buffer; y = pointColl.GetFloorHeight() - buffer;
} }
else if ((y - buffer) < probe.Position.Ceiling && else if ((y - buffer) < pointColl.GetCeilingHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
y = probe.Position.Ceiling + buffer; y = pointColl.GetCeilingHeight() + buffer;
} }
} }
probe = GetCollision(x - push, y, z, ideal->RoomNumber); pointColl = GetPointCollision(Vector3i(x - push, y, z), ideal->RoomNumber);
if (y > probe.Position.Floor || if (y > pointColl.GetFloorHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT || pointColl.GetCeilingHeight() == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor || pointColl.GetCeilingHeight() >= pointColl.GetFloorHeight() ||
y < probe.Position.Ceiling) y < pointColl.GetCeilingHeight())
{ {
x = (x & (~1023)) + push; x = (x & (~1023)) + push;
} }
probe = GetCollision(x, y, z - push, ideal->RoomNumber); pointColl = GetPointCollision(Vector3i(x, y, z - push), ideal->RoomNumber);
if (y > probe.Position.Floor || if (y > pointColl.GetFloorHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT || pointColl.GetCeilingHeight() == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor || pointColl.GetCeilingHeight() >= pointColl.GetFloorHeight() ||
y < probe.Position.Ceiling) y < pointColl.GetCeilingHeight())
{ {
z = (z & (~1023)) + push; z = (z & (~1023)) + push;
} }
probe = GetCollision(x + push, y, z, ideal->RoomNumber); pointColl = GetPointCollision(Vector3i(x + push, y, z), ideal->RoomNumber);
if (y > probe.Position.Floor || if (y > pointColl.GetFloorHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT || pointColl.GetCeilingHeight() == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor || pointColl.GetCeilingHeight() >= pointColl.GetFloorHeight() ||
y < probe.Position.Ceiling) y < pointColl.GetCeilingHeight())
{ {
x = (x | 1023) - push; x = (x | 1023) - push;
} }
probe = GetCollision(x, y, z + push, ideal->RoomNumber); pointColl = GetPointCollision(Vector3i(x, y, z + push), ideal->RoomNumber);
if (y > probe.Position.Floor || if (y > pointColl.GetFloorHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT || pointColl.GetCeilingHeight() == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor || pointColl.GetCeilingHeight() >= pointColl.GetFloorHeight() ||
y < probe.Position.Ceiling) y < pointColl.GetCeilingHeight())
{ {
z = (z | 1023) - push; z = (z | 1023) - push;
} }
if (!yFirst) if (!yFirst)
{ {
probe = GetCollision(x, y, z, ideal->RoomNumber); pointColl = GetPointCollision(Vector3i(x, y, z), ideal->RoomNumber);
int buffer = CLICK(1) - 1; int buffer = CLICK(1) - 1;
if ((y - buffer) < probe.Position.Ceiling && if ((y - buffer) < pointColl.GetCeilingHeight() &&
(y + buffer) > probe.Position.Floor && (y + buffer) > pointColl.GetFloorHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
y = (probe.Position.Floor + probe.Position.Ceiling) / 2; y = (pointColl.GetFloorHeight() + pointColl.GetCeilingHeight()) / 2;
} }
else if ((y + buffer) > probe.Position.Floor && else if ((y + buffer) > pointColl.GetFloorHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
y = probe.Position.Floor - buffer; y = pointColl.GetFloorHeight() - buffer;
} }
else if ((y - buffer) < probe.Position.Ceiling && else if ((y - buffer) < pointColl.GetCeilingHeight() &&
probe.Position.Ceiling < probe.Position.Floor && pointColl.GetCeilingHeight() < pointColl.GetFloorHeight() &&
probe.Position.Ceiling != NO_HEIGHT && pointColl.GetCeilingHeight() != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT) pointColl.GetFloorHeight() != NO_HEIGHT)
{ {
y = probe.Position.Ceiling + buffer; y = pointColl.GetCeilingHeight() + buffer;
} }
} }
probe = GetCollision(x, y, z, ideal->RoomNumber); pointColl = GetPointCollision(Vector3i(x, y, z), ideal->RoomNumber);
if (y > probe.Position.Floor || if (y > pointColl.GetFloorHeight() ||
y < probe.Position.Ceiling || y < pointColl.GetCeilingHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT || pointColl.GetCeilingHeight() == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor) pointColl.GetCeilingHeight() >= pointColl.GetFloorHeight())
{ {
return true; return true;
} }
ideal->RoomNumber = probe.RoomNumber; ideal->RoomNumber = pointColl.GetRoomNumber();
ideal->x = x; ideal->x = x;
ideal->y = y; ideal->y = y;
ideal->z = z; ideal->z = z;
@ -961,20 +963,20 @@ void BinocularCamera(ItemInfo* item)
int y = item->Pose.Position.y - CLICK(2); int y = item->Pose.Position.y - CLICK(2);
int z = item->Pose.Position.z; int z = item->Pose.Position.z;
auto probe = GetCollision(x, y, z, item->RoomNumber); auto pointColl = GetPointCollision(Vector3i(x, y, z), item->RoomNumber);
if (probe.Position.Ceiling <= (y - CLICK(1))) if (pointColl.GetCeilingHeight() <= (y - CLICK(1)))
{ {
y -= CLICK(1); y -= CLICK(1);
} }
else else
{ {
y = probe.Position.Ceiling + CLICK(0.25f); y = pointColl.GetCeilingHeight() + CLICK(0.25f);
} }
Camera.pos.x = x; Camera.pos.x = x;
Camera.pos.y = y; Camera.pos.y = y;
Camera.pos.z = z; Camera.pos.z = z;
Camera.pos.RoomNumber = probe.RoomNumber; Camera.pos.RoomNumber = pointColl.GetRoomNumber();
float l = BLOCK(20.25f) * phd_cos(player.Control.Look.Orientation.x); float l = BLOCK(20.25f) * phd_cos(player.Control.Look.Orientation.x);
float tx = x + l * phd_sin(item->Pose.Orientation.y + player.Control.Look.Orientation.y); float tx = x + l * phd_sin(item->Pose.Orientation.y + player.Control.Look.Orientation.y);
@ -1014,7 +1016,7 @@ void BinocularCamera(ItemInfo* item)
} }
} }
Camera.target.RoomNumber = GetCollision(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.target.RoomNumber).RoomNumber; Camera.target.RoomNumber = GetPointCollision(Camera.pos.ToVector3i(), Camera.target.RoomNumber).GetRoomNumber();
LookAt(&Camera, 0); LookAt(&Camera, 0);
UpdateMikePos(*item); UpdateMikePos(*item);
Camera.oldType = Camera.type; Camera.oldType = Camera.type;
@ -1050,12 +1052,12 @@ void ConfirmCameraTargetPos()
} }
int y = Camera.target.y; int y = Camera.target.y;
auto probe = GetCollision(Camera.target.x, y, Camera.target.z, Camera.target.RoomNumber); auto pointColl = GetPointCollision(Vector3i(Camera.target.x, y, Camera.target.z), Camera.target.RoomNumber);
if (y < probe.Position.Ceiling || if (y < pointColl.GetCeilingHeight() ||
probe.Position.Floor < y || pointColl.GetFloorHeight() < y ||
probe.Position.Floor <= probe.Position.Ceiling || pointColl.GetFloorHeight() <= pointColl.GetCeilingHeight() ||
probe.Position.Floor == NO_HEIGHT || pointColl.GetFloorHeight() == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT) pointColl.GetCeilingHeight() == NO_HEIGHT)
{ {
Camera.target.x = pos.x; Camera.target.x = pos.x;
Camera.target.y = pos.y; Camera.target.y = pos.y;
@ -1266,7 +1268,7 @@ void CalculateCamera(const CollisionInfo& coll)
Camera.speed = 1; Camera.speed = 1;
} }
Camera.target.RoomNumber = GetCollision(x, y, z, Camera.target.RoomNumber).RoomNumber; Camera.target.RoomNumber = GetPointCollision(Vector3i(x, y, z), Camera.target.RoomNumber).GetRoomNumber();
if (abs(LastTarget.x - Camera.target.x) < 4 && if (abs(LastTarget.x - Camera.target.x) < 4 &&
abs(LastTarget.y - Camera.target.y) < 4 && abs(LastTarget.y - Camera.target.y) < 4 &&
@ -1359,9 +1361,9 @@ void ItemPushCamera(GameBoundingBox* bounds, Pose* pos, short radius)
Camera.pos.x = pos->Position.x + ((x * cosY) + (z * sinY)); Camera.pos.x = pos->Position.x + ((x * cosY) + (z * sinY));
Camera.pos.z = pos->Position.z + ((z * cosY) - (x * sinY)); Camera.pos.z = pos->Position.z + ((z * cosY) - (x * sinY));
auto pointColl = GetCollision(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.RoomNumber); auto pointColl = GetPointCollision(Camera.pos.ToVector3i(), Camera.pos.RoomNumber);
if (pointColl.Position.Floor == NO_HEIGHT || Camera.pos.y > pointColl.Position.Floor || Camera.pos.y < pointColl.Position.Ceiling) if (pointColl.GetFloorHeight() == NO_HEIGHT || Camera.pos.y > pointColl.GetFloorHeight() || Camera.pos.y < pointColl.GetCeilingHeight())
Camera.pos = GameVector(CamOldPos, pointColl.RoomNumber); Camera.pos = GameVector(CamOldPos, pointColl.GetRoomNumber());
} }
bool CheckItemCollideCamera(ItemInfo* item) bool CheckItemCollideCamera(ItemInfo* item)
@ -1484,7 +1486,7 @@ void ItemsCollideCamera()
if (TestBoundsCollideCamera(bounds, item->Pose, CAMERA_RADIUS)) if (TestBoundsCollideCamera(bounds, item->Pose, CAMERA_RADIUS))
ItemPushCamera(&bounds, &item->Pose, RADIUS); ItemPushCamera(&bounds, &item->Pose, RADIUS);
TEN::Renderer::g_Renderer.AddDebugBox( DrawDebugBox(
bounds.ToBoundingOrientedBox(item->Pose), bounds.ToBoundingOrientedBox(item->Pose),
Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats); Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats);
} }
@ -1507,7 +1509,7 @@ void ItemsCollideCamera()
if (TestBoundsCollideCamera(bounds, mesh->pos, CAMERA_RADIUS)) if (TestBoundsCollideCamera(bounds, mesh->pos, CAMERA_RADIUS))
ItemPushCamera(&bounds, &mesh->pos, RADIUS); ItemPushCamera(&bounds, &mesh->pos, RADIUS);
TEN::Renderer::g_Renderer.AddDebugBox( DrawDebugBox(
bounds.ToBoundingOrientedBox(mesh->pos), bounds.ToBoundingOrientedBox(mesh->pos),
Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats); Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats);
} }

View file

@ -0,0 +1,401 @@
#include "framework.h"
#include "Game/collision/Point.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/floordata.h"
#include "Game/items.h"
#include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/game_object_ids.h"
#include "Specific/level.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Room;
using namespace TEN::Math;
namespace TEN::Collision::Point
{
PointCollisionData::PointCollisionData(const Vector3i& pos, int roomNumber)
{
_position = pos;
_roomNumber = roomNumber;
}
Vector3i PointCollisionData::GetPosition() const
{
return _position;
}
int PointCollisionData::GetRoomNumber() const
{
return _roomNumber;
}
FloorInfo& PointCollisionData::GetSector()
{
if (_sector != nullptr)
return *_sector;
// Set current sector.
short probeRoomNumber = _roomNumber;
_sector = GetFloor(_position.x, _position.y, _position.z, &probeRoomNumber);
return *_sector;
}
FloorInfo& PointCollisionData::GetBottomSector()
{
if (_bottomSector != nullptr)
return *_bottomSector;
// Set bottom sector.
auto* bottomSector = &GetSector();
auto roomNumberBelow = bottomSector->GetNextRoomNumber(_position, true);
while (roomNumberBelow.has_value())
{
int roomNumber = roomNumberBelow.value_or(bottomSector->RoomNumber);
auto& room = g_Level.Rooms[roomNumber];
bottomSector = Room::GetSector(&room, _position.x - room.x, _position.z - room.z);
roomNumberBelow = bottomSector->GetNextRoomNumber(_position, true);
}
_bottomSector = bottomSector;
return *_bottomSector;
}
FloorInfo& PointCollisionData::GetTopSector()
{
if (_topSector != nullptr)
return *_topSector;
// Set top sector.
auto* topSector = &GetSector();
auto roomNumberAbove = topSector->GetNextRoomNumber(_position, false);
while (roomNumberAbove.has_value())
{
int roomNumber = roomNumberAbove.value_or(topSector->RoomNumber);
auto& room = g_Level.Rooms[roomNumber];
topSector = Room::GetSector(&room, _position.x - room.x, _position.z - room.z);
roomNumberAbove = topSector->GetNextRoomNumber(_position, false);
}
_topSector = topSector;
return *_topSector;
}
int PointCollisionData::GetFloorHeight()
{
if (_floorHeight.has_value())
return *_floorHeight;
// Set floor height.
auto location = RoomVector(GetSector().RoomNumber, _position.y);
_floorHeight = Floordata::GetSurfaceHeight(location, _position.x, _position.z, true).value_or(NO_HEIGHT);
return *_floorHeight;
}
int PointCollisionData::GetCeilingHeight()
{
if (_ceilingHeight.has_value())
return *_ceilingHeight;
// Set ceiling height.
auto location = RoomVector(GetSector().RoomNumber, _position.y);
_ceilingHeight = Floordata::GetSurfaceHeight(location, _position.x, _position.z, false).value_or(NO_HEIGHT);
return *_ceilingHeight;
}
Vector3 PointCollisionData::GetFloorNormal()
{
if (_floorNormal.has_value())
return *_floorNormal;
// Set floor normal.
if (GetFloorBridgeItemNumber() != NO_VALUE)
{
_floorNormal = GetBridgeNormal(true);
}
else
{
_floorNormal = GetBottomSector().GetSurfaceNormal(_position.x, _position.z, true);
}
return *_floorNormal;
}
Vector3 PointCollisionData::GetCeilingNormal()
{
if (_ceilingNormal.has_value())
return *_ceilingNormal;
// Set ceiling normal.
if (GetCeilingBridgeItemNumber() != NO_VALUE)
{
_ceilingNormal = GetBridgeNormal(false);
}
else
{
_ceilingNormal = GetTopSector().GetSurfaceNormal(_position.x, _position.z, false);
}
return *_ceilingNormal;
}
int PointCollisionData::GetFloorBridgeItemNumber()
{
if (_floorBridgeItemNumber.has_value())
return *_floorBridgeItemNumber;
// Set floor bridge item number.
int floorHeight = GetFloorHeight();
auto pos = Vector3i(_position.x, floorHeight, _position.z);
_floorBridgeItemNumber = GetBottomSector().GetInsideBridgeItemNumber(pos, true, false);
return *_floorBridgeItemNumber;
}
int PointCollisionData::GetCeilingBridgeItemNumber()
{
if (_ceilingBridgeItemNumber.has_value())
return *_ceilingBridgeItemNumber;
// Set ceiling bridge item number.
int ceilingHeight = GetCeilingHeight();
auto pos = Vector3i(_position.x, ceilingHeight, _position.z);
_ceilingBridgeItemNumber = GetTopSector().GetInsideBridgeItemNumber(pos, false, true);
return *_ceilingBridgeItemNumber;
}
int PointCollisionData::GetWaterSurfaceHeight()
{
if (_waterSurfaceHeight.has_value())
return *_waterSurfaceHeight;
// Set water surface height. TODO: Calculate here.
_waterSurfaceHeight = GetWaterSurface(_position.x, _position.y, _position.z, _roomNumber);
return *_waterSurfaceHeight;
}
int PointCollisionData::GetWaterBottomHeight()
{
if (_waterBottomHeight.has_value())
return *_waterBottomHeight;
// Set water bottom height. TODO: Calculate here.
_waterBottomHeight = GetWaterDepth(_position.x, _position.y, _position.z, _roomNumber);
return *_waterBottomHeight;
}
int PointCollisionData::GetWaterTopHeight()
{
if (_waterTopHeight.has_value())
return *_waterTopHeight;
// Set water top height. TODO: Calculate here.
_waterTopHeight = GetWaterHeight(_position.x, _position.y, _position.z, _roomNumber);
return *_waterTopHeight;
}
bool PointCollisionData::IsWall()
{
return (GetFloorHeight() == NO_HEIGHT || GetCeilingHeight() == NO_HEIGHT ||
GetFloorHeight() <= GetCeilingHeight());
}
bool PointCollisionData::IsSteepFloor()
{
short slopeAngle = Geometry::GetSurfaceSlopeAngle(GetFloorNormal());
short steepSlopeAngle = (GetFloorBridgeItemNumber() != NO_VALUE) ?
DEFAULT_STEEP_FLOOR_SLOPE_ANGLE :
GetBottomSector().GetSurfaceIllegalSlopeAngle(_position.x, _position.z, true);
return (abs(slopeAngle) >= steepSlopeAngle);
}
bool PointCollisionData::IsSteepCeiling()
{
short slopeAngle = Geometry::GetSurfaceSlopeAngle(GetCeilingNormal(), -Vector3::UnitY);
short steepSlopeAngle = (GetCeilingBridgeItemNumber() != NO_VALUE) ?
DEFAULT_STEEP_CEILING_SLOPE_ANGLE :
GetTopSector().GetSurfaceIllegalSlopeAngle(_position.x, _position.z, false);
return (abs(slopeAngle) >= steepSlopeAngle);
}
bool PointCollisionData::IsDiagonalFloorStep()
{
return GetBottomSector().IsSurfaceDiagonalStep(true);
}
bool PointCollisionData::IsDiagonalCeilingStep()
{
return GetTopSector().IsSurfaceDiagonalStep(false);
}
bool PointCollisionData::IsDiagonalFloorSplit()
{
float splitAngle = GetBottomSector().FloorSurface.SplitAngle;
return (splitAngle == SectorSurfaceData::SPLIT_ANGLE_0 || splitAngle == SectorSurfaceData::SPLIT_ANGLE_1);
}
bool PointCollisionData::IsDiagonalCeilingSplit()
{
float splitAngle = GetTopSector().CeilingSurface.SplitAngle;
return (splitAngle == SectorSurfaceData::SPLIT_ANGLE_0 || splitAngle == SectorSurfaceData::SPLIT_ANGLE_1);
}
bool PointCollisionData::IsFlippedDiagonalFloorSplit()
{
float splitAngle = GetBottomSector().FloorSurface.SplitAngle;
return (IsDiagonalFloorStep() && splitAngle == SectorSurfaceData::SPLIT_ANGLE_1);
}
bool PointCollisionData::IsFlippedDiagonalCeilingSplit()
{
float splitAngle = GetTopSector().CeilingSurface.SplitAngle;
return (IsDiagonalCeilingStep() && splitAngle == SectorSurfaceData::SPLIT_ANGLE_1);
}
bool PointCollisionData::TestEnvironmentFlag(RoomEnvFlags envFlag)
{
const auto& room = g_Level.Rooms[_roomNumber];
return ((room.flags & envFlag) == envFlag);
}
// HACK.
Vector3 PointCollisionData::GetBridgeNormal(bool isFloor)
{
constexpr auto ANGLE_STEP = ANGLE(45.0f / 4);
int bridgeItemNumber = isFloor ? GetFloorBridgeItemNumber() : GetCeilingBridgeItemNumber();
const auto& bridgeItem = g_Level.Items[bridgeItemNumber];
auto orient = bridgeItem.Pose.Orientation;
switch (bridgeItem.ObjectNumber)
{
default:
case ID_BRIDGE_FLAT:
break;
case ID_BRIDGE_TILT1:
orient.z -= ANGLE_STEP;
break;
case ID_BRIDGE_TILT2:
orient.z -= ANGLE_STEP * 2;
break;
case ID_BRIDGE_TILT3:
orient.z -= ANGLE_STEP * 3;
break;
case ID_BRIDGE_TILT4:
orient.z -= ANGLE_STEP * 4;
break;
}
int sign = isFloor ? -1 : 1;
return Vector3::Transform(Vector3::UnitY * sign, orient.ToRotationMatrix());
}
static int GetProbeRoomNumber(const Vector3i& pos, const RoomVector& location, const Vector3i& probePos)
{
// Conduct L-shaped room traversal.
short probeRoomNumber = GetRoomVector(location, Vector3i(pos.x, probePos.y, pos.z)).RoomNumber;
GetFloor(probePos.x, probePos.y, probePos.z, &probeRoomNumber);
return probeRoomNumber;
}
static RoomVector GetLocation(const Vector3i& pos, int roomNumber)
{
short tempRoomNumber = roomNumber;
const auto& sector = *GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber);
return RoomVector(sector.RoomNumber, pos.y);
}
static RoomVector GetLocation(const ItemInfo& item)
{
// TODO: Find cleaner solution. Manually constructing a player "location" can result in stumbles when climbing onto thin platforms.
// May have to do with player's room number being updated at half-height? -- Sezz 2022.06.14
if (item.IsLara())
return item.Location;
short tempRoomNumber = item.RoomNumber;
const auto& sector = *GetFloor(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, &tempRoomNumber);
return RoomVector(sector.RoomNumber, item.Pose.Position.y);
}
PointCollisionData GetPointCollision(const Vector3i& pos, int roomNumber)
{
// HACK: Ensure room number is correct if position extends to another room.
// Accounts for some calls to this function which directly pass offset position instead of using dedicated probe overloads.
GetFloor(pos.x, pos.y, pos.z, (short*)&roomNumber);
return PointCollisionData(pos, roomNumber);
}
PointCollisionData GetPointCollision(const Vector3i& pos, int roomNumber, const Vector3& dir, float dist)
{
// Get "location".
auto location = GetLocation(pos, roomNumber);
// Calculate probe position.
auto probePos = Geometry::TranslatePoint(pos, dir, dist);
short probeRoomNumber = GetProbeRoomNumber(pos, location, probePos);
return PointCollisionData(probePos, probeRoomNumber);
}
PointCollisionData GetPointCollision(const Vector3i& pos, int roomNumber, short headingAngle, float forward, float down, float right, const Vector3& axis)
{
// Get "location".
auto location = GetLocation(pos, roomNumber);
// Calculate probe position.
auto probePos = Geometry::TranslatePoint(pos, headingAngle, forward, down, right, axis);
short probeRoomNumber = GetProbeRoomNumber(pos, location, probePos);
return PointCollisionData(probePos, probeRoomNumber);
}
PointCollisionData GetPointCollision(const ItemInfo& item)
{
return GetPointCollision(item.Pose.Position, item.RoomNumber);
}
PointCollisionData GetPointCollision(const ItemInfo& item, const Vector3& dir, float dist)
{
// Get "location".
auto location = GetLocation(item);
// Calculate probe position.
auto probePos = Geometry::TranslatePoint(item.Pose.Position, dir, dist);
short probeRoomNumber = GetProbeRoomNumber(item.Pose.Position, location, probePos);
return PointCollisionData(probePos, probeRoomNumber);
}
PointCollisionData GetPointCollision(const ItemInfo& item, short headingAngle, float forward, float down, float right, const Vector3& axis)
{
// Get "location".
auto location = GetLocation(item);
// Calculate probe position.
auto probePos = Geometry::TranslatePoint(item.Pose.Position, headingAngle, forward, down, right, axis);
short probeRoomNumber = GetProbeRoomNumber(item.Pose.Position, location, probePos);
return PointCollisionData(probePos, probeRoomNumber);
}
}

View file

@ -0,0 +1,90 @@
#pragma once
#include "Game/collision/collide_room.h"
#include "Math/Math.h"
enum RoomEnvFlags;
class FloorInfo;
struct ItemInfo;
using namespace TEN::Math;
namespace TEN::Collision::Point
{
class PointCollisionData
{
private:
// Members
Vector3i _position = Vector3i::Zero;
int _roomNumber = 0;
FloorInfo* _sector = nullptr;
FloorInfo* _bottomSector = nullptr;
FloorInfo* _topSector = nullptr;
std::optional<int> _floorHeight = std::nullopt;
std::optional<int> _ceilingHeight = std::nullopt;
std::optional<Vector3> _floorNormal = std::nullopt;
std::optional<Vector3> _ceilingNormal = std::nullopt;
std::optional<int> _floorBridgeItemNumber = std::nullopt;
std::optional<int> _ceilingBridgeItemNumber = std::nullopt;
std::optional<int> _waterSurfaceHeight = std::nullopt;
std::optional<int> _waterBottomHeight = std::nullopt;
std::optional<int> _waterTopHeight = std::nullopt;
public:
// Constructors
PointCollisionData(const Vector3i& pos, int roomNumber);
// Getters
Vector3i GetPosition() const;
int GetRoomNumber() const;
FloorInfo& GetSector();
FloorInfo& GetBottomSector();
FloorInfo& GetTopSector();
int GetFloorHeight();
int GetCeilingHeight();
Vector3 GetFloorNormal();
Vector3 GetCeilingNormal();
int GetFloorBridgeItemNumber();
int GetCeilingBridgeItemNumber();
int GetWaterSurfaceHeight();
int GetWaterBottomHeight();
int GetWaterTopHeight();
// Inquirers
bool IsWall();
bool IsSteepFloor();
bool IsSteepCeiling();
bool IsDiagonalFloorStep();
bool IsDiagonalCeilingStep();
bool IsDiagonalFloorSplit();
bool IsDiagonalCeilingSplit();
bool IsFlippedDiagonalFloorSplit();
bool IsFlippedDiagonalCeilingSplit();
bool TestEnvironmentFlag(RoomEnvFlags envFlag);
private:
// Helpers
Vector3 GetBridgeNormal(bool isFloor);
};
PointCollisionData GetPointCollision(const Vector3i& pos, int roomNumber);
PointCollisionData GetPointCollision(const Vector3i& pos, int roomNumber, const Vector3& dir, float dist);
PointCollisionData GetPointCollision(const Vector3i& pos, int roomNumber, short headingAngle, float forward, float down = 0.0f, float right = 0.0f,
const Vector3& axis = Vector3::UnitY);
PointCollisionData GetPointCollision(const ItemInfo& item);
PointCollisionData GetPointCollision(const ItemInfo& item, const Vector3& dir, float dist);
PointCollisionData GetPointCollision(const ItemInfo& item, short headingAngle, float forward, float down = 0.0f, float right = 0.0f,
const Vector3& axis = Vector3::UnitY);
}

View file

@ -4,6 +4,8 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/control/los.h" #include "Game/control/los.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/floordata.h"
#include "Game/collision/Point.h"
#include "Game/collision/sphere.h" #include "Game/collision/sphere.h"
#include "Game/effects/debris.h" #include "Game/effects/debris.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
@ -16,12 +18,12 @@
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h" #include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer.h"
#include "Scripting/Include/ScriptInterfaceGame.h" #include "Scripting/Include/ScriptInterfaceGame.h"
#include "Sound/sound.h" #include "Sound/sound.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer;
constexpr auto ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD = 6; constexpr auto ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD = 6;
@ -200,7 +202,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible,
// Test accurate box intersection. // Test accurate box intersection.
if (box0.Intersects(box1)) if (box0.Intersects(box1))
collObjects.ItemPtrs.push_back(&item); collObjects.Items.push_back(&item);
} }
while (itemNumber != NO_VALUE); while (itemNumber != NO_VALUE);
} }
@ -249,7 +251,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible,
// Test accurate box intersection. // Test accurate box intersection.
if (box0.Intersects(box1)) if (box0.Intersects(box1))
collObjects.StaticPtrs.push_back(&staticObj); collObjects.Statics.push_back(&staticObj);
} }
} }
} }
@ -303,7 +305,7 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(coll->Setup.ForwardAngle), 0.0f, 0.0f); auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(coll->Setup.ForwardAngle), 0.0f, 0.0f);
auto direction = (Matrix::CreateTranslation(Vector3::UnitZ) * mxR).Translation(); auto direction = (Matrix::CreateTranslation(Vector3::UnitZ) * mxR).Translation();
// g_Renderer.AddDebugSphere(origin, 16, Vector4::One, RendererDebugPage::CollisionStats); // DrawDebugSphere(origin, 16, Vector4::One, RendererDebugPage::CollisionStats);
for (auto i : g_Level.Rooms[item->RoomNumber].neighbors) for (auto i : g_Level.Rooms[item->RoomNumber].neighbors)
{ {
@ -400,7 +402,7 @@ bool AlignLaraPosition(const Vector3i& offset, ItemInfo* item, ItemInfo* laraIte
auto pos = Vector3::Transform(offset.ToVector3(), rotMatrix); auto pos = Vector3::Transform(offset.ToVector3(), rotMatrix);
auto target = item->Pose.Position.ToVector3() + pos; auto target = item->Pose.Position.ToVector3() + pos;
int height = GetCollision(target.x, target.y, target.z, laraItem->RoomNumber).Position.Floor; int height = GetPointCollision(target, laraItem->RoomNumber).GetFloorHeight();
if ((laraItem->Pose.Position.y - height) <= CLICK(2)) if ((laraItem->Pose.Position.y - height) <= CLICK(2))
{ {
laraItem->Pose.Position = Vector3i(target); laraItem->Pose.Position = Vector3i(target);
@ -429,7 +431,7 @@ bool MoveLaraPosition(const Vector3i& offset, ItemInfo* item, ItemInfo* laraItem
else else
{ {
// Prevent picking up items which can result in so called "flare pickup bug" // Prevent picking up items which can result in so called "flare pickup bug"
int height = GetCollision(target.Position.x, target.Position.y, target.Position.z, laraItem->RoomNumber).Position.Floor; int height = GetPointCollision(target.Position, laraItem->RoomNumber).GetFloorHeight();
if (abs(height - laraItem->Pose.Position.y) <= CLICK(2)) if (abs(height - laraItem->Pose.Position.y) <= CLICK(2))
return Move3DPosTo3DPos(laraItem, laraItem->Pose, target, LARA_ALIGN_VELOCITY, ANGLE(2.0f)); return Move3DPosTo3DPos(laraItem, laraItem->Pose, target, LARA_ALIGN_VELOCITY, ANGLE(2.0f));
} }
@ -702,7 +704,7 @@ bool ItemPushItem(ItemInfo* item0, ItemInfo* item1, CollisionInfo* coll, bool en
item1->Pose.Position.Lerp(item0->Pose.Position + newDeltaPos, SOFT_PUSH_LERP_ALPHA); item1->Pose.Position.Lerp(item0->Pose.Position + newDeltaPos, SOFT_PUSH_LERP_ALPHA);
} }
// Snap to new position. // Snap to new position.
else else if (coll->Setup.EnableObjectPush)
{ {
item1->Pose.Position = item0->Pose.Position + newDeltaPos; item1->Pose.Position = item0->Pose.Position + newDeltaPos;
} }
@ -885,22 +887,19 @@ void ItemPushBridge(ItemInfo& item, CollisionInfo& coll)
ShiftItem(&item, &coll); ShiftItem(&item, &coll);
} }
void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, const CollisionResult& collResult) void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, PointCollisionData& pointColl)
{ {
// Store an offset for a bridge item into shifts, if exists. // Store offset for bridge item into shifts if it exists.
if (coll.LastBridgeItemNumber == collResult.Position.Bridge && coll.LastBridgeItemNumber != NO_VALUE) if (coll.LastBridgeItemNumber == pointColl.GetFloorBridgeItemNumber() && coll.LastBridgeItemNumber != NO_VALUE)
{ {
auto& bridgeItem = g_Level.Items[collResult.Position.Bridge]; auto& bridgeItem = g_Level.Items[pointColl.GetFloorBridgeItemNumber()];
auto deltaPos = bridgeItem.Pose.Position - coll.LastBridgeItemPose.Position; auto deltaPos = bridgeItem.Pose.Position - coll.LastBridgeItemPose.Position;
auto deltaOrient = bridgeItem.Pose.Orientation - coll.LastBridgeItemPose.Orientation; auto deltaOrient = bridgeItem.Pose.Orientation - coll.LastBridgeItemPose.Orientation;
auto deltaPose = Pose(deltaPos, deltaOrient); auto deltaPose = Pose(deltaPos, deltaOrient);
int absDeltaHeight = item.Pose.Position.y - collResult.Position.Floor; // Item is grounded and bridge position changed; set shift.
int relDeltaHeight = absDeltaHeight + GameBoundingBox(&item).Y2; if (deltaPose != Pose::Zero && !item.Animation.IsAirborne)
if (deltaPose != Pose::Zero &&
(abs(absDeltaHeight) <= CLICK(1 / 8.0f) || abs(relDeltaHeight) <= CLICK(1 / 8.0f)))
{ {
const auto& bridgePos = bridgeItem.Pose.Position; const auto& bridgePos = bridgeItem.Pose.Position;
@ -910,15 +909,19 @@ void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, const CollisionResu
auto offset = bridgePos.ToVector3() + Vector3::Transform(relOffset, rotMatrix); auto offset = bridgePos.ToVector3() + Vector3::Transform(relOffset, rotMatrix);
deltaPose.Position -= item.Pose.Position - Vector3i(offset); deltaPose.Position -= item.Pose.Position - Vector3i(offset);
// Don't update shifts if difference is too big (possibly bridge was teleported or just entered bridge). // Don't update shifts if difference is too big (bridge was possibly teleported or just entered).
if (deltaPose.Position.ToVector3().Length() <= coll.Setup.Radius * 2) if (Vector2(deltaPose.Position.x, deltaPose.Position.z).Length() <= (coll.Setup.Radius * 2))
{
deltaPose.Orientation = EulerAngles(0, deltaPose.Orientation.y, 0);
coll.Shift = deltaPose; coll.Shift = deltaPose;
}
} }
else if (deltaPos.ToVector3().Length() <= coll.Setup.Radius && relDeltaHeight > 0 && // Push item.
(deltaPos != Vector3i::Zero || deltaOrient != EulerAngles::Identity)) else if (TestBoundsCollide(&bridgeItem, &item, coll.Setup.Radius) &&
Vector2(deltaPose.Position.x, deltaPose.Position.z).Length() <= coll.Setup.Radius &&
(deltaPos != Vector3i::Zero || deltaOrient != EulerAngles::Identity))
{ {
// Push item away if not directly above bridge, and bridge position was changed.
ItemPushItem(&bridgeItem, &item); ItemPushItem(&bridgeItem, &item);
} }
@ -930,7 +933,7 @@ void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, const CollisionResu
coll.LastBridgeItemNumber = NO_VALUE; coll.LastBridgeItemNumber = NO_VALUE;
} }
coll.LastBridgeItemNumber = collResult.Position.Bridge; coll.LastBridgeItemNumber = pointColl.GetFloorBridgeItemNumber();
} }
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll) void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
@ -965,6 +968,10 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
// Get DX static bounds in global coordinates. // Get DX static bounds in global coordinates.
auto staticBounds = box.ToBoundingOrientedBox(pose); 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. // Get local TR bounds and DX item bounds in global coordinates.
auto itemBBox = GameBoundingBox(item); auto itemBBox = GameBoundingBox(item);
auto itemBounds = itemBBox.ToBoundingOrientedBox(item->Pose); auto itemBounds = itemBBox.ToBoundingOrientedBox(item->Pose);
@ -980,7 +987,7 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
itemBounds.Extents = itemBounds.Extents - Vector3(BLOCK(1)); itemBounds.Extents = itemBounds.Extents - Vector3(BLOCK(1));
// Draw static bounds. // Draw static bounds.
g_Renderer.AddDebugBox(staticBounds, Vector4(1, 0.3f, 0, 1), RendererDebugPage::CollisionStats); DrawDebugBox(staticBounds, Vector4(1, 0.3f, 0, 1), RendererDebugPage::CollisionStats);
// Calculate horizontal item collision bounds according to radius. // Calculate horizontal item collision bounds according to radius.
GameBoundingBox collBox; GameBoundingBox collBox;
@ -1006,12 +1013,8 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
auto collBounds = collBox.ToBoundingOrientedBox(Pose(item->Pose.Position)); auto collBounds = collBox.ToBoundingOrientedBox(Pose(item->Pose.Position));
bool intersects = staticBounds.Intersects(collBounds); bool intersects = staticBounds.Intersects(collBounds);
// Check if previous item horizontal position intersects bounds.
auto prevCollBounds = collBox.ToBoundingOrientedBox(Pose(coll->Setup.PrevPosition));
bool prevHorIntersects = staticBounds.Intersects(prevCollBounds);
// Draw item coll bounds. // Draw item coll bounds.
g_Renderer.AddDebugBox(collBounds, intersects ? Vector4(1, 0, 0, 1) : Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats); DrawDebugBox(collBounds, intersects ? Vector4(1, 0, 0, 1) : Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats);
// Decompose static bounds into top/bottom plane vertices. // Decompose static bounds into top/bottom plane vertices.
Vector3 corners[8]; Vector3 corners[8];
@ -1068,7 +1071,7 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
auto distanceToVerticalPlane = height / 2 - yPoint; auto distanceToVerticalPlane = height / 2 - yPoint;
// Correct position according to top/bottom bounds, if collided and plane is nearby. // Correct position according to top/bottom bounds, if collided and plane is nearby.
if (intersects && prevHorIntersects && minDistance < height) if (intersects && minDistance < height)
{ {
if (bottom) if (bottom)
{ {
@ -1272,39 +1275,42 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
auto prevPointProbe = GetCollision(x, y, z, item->RoomNumber); auto prevPointColl = GetPointCollision(Vector3i(x, y, z), item->RoomNumber);
auto pointProbe = GetCollision(item); auto pointColl = GetPointCollision(*item);
// TODO: Use floor normal directly.
auto floorTilt = GetSurfaceTilt(pointColl.GetFloorNormal(), true);
auto bounds = GameBoundingBox(item); auto bounds = GameBoundingBox(item);
int radius = bounds.GetHeight(); int radius = bounds.GetHeight();
item->Pose.Position.y += radius; item->Pose.Position.y += radius;
if (item->Pose.Position.y >= pointProbe.Position.Floor) if (item->Pose.Position.y >= pointColl.GetFloorHeight())
{ {
int bs = 0; int bs = 0;
if (pointProbe.Position.FloorSlope && prevPointProbe.Position.Floor < pointProbe.Position.Floor) if (pointColl.IsSteepFloor() && prevPointColl.GetFloorHeight() < pointColl.GetFloorHeight())
{ {
int yAngle = (long)((unsigned short)item->Pose.Orientation.y); int yAngle = (long)((unsigned short)item->Pose.Orientation.y);
if (pointProbe.FloorTilt.x < 0) if (floorTilt.x < 0)
{ {
if (yAngle >= ANGLE(180.0f)) if (yAngle >= ANGLE(180.0f))
bs = 1; bs = 1;
} }
else if (pointProbe.FloorTilt.x > 0) else if (floorTilt.x > 0)
{ {
if (yAngle <= ANGLE(180.0f)) if (yAngle <= ANGLE(180.0f))
bs = 1; bs = 1;
} }
if (pointProbe.FloorTilt.y < 0) if (floorTilt.y < 0)
{ {
if (yAngle >= ANGLE(90.0f) && yAngle <= ANGLE(270.0f)) if (yAngle >= ANGLE(90.0f) && yAngle <= ANGLE(270.0f))
bs = 1; bs = 1;
} }
else if (pointProbe.FloorTilt.y > 0) else if (floorTilt.y > 0)
{ {
if (yAngle <= ANGLE(90.0f) || yAngle >= ANGLE(270.0f)) if (yAngle <= ANGLE(90.0f) || yAngle >= ANGLE(270.0f))
bs = 1; bs = 1;
@ -1313,7 +1319,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
// If last position of item was also below this floor height, we've hit a wall, else we've hit a floor. // If last position of item was also below this floor height, we've hit a wall, else we've hit a floor.
if (y > (pointProbe.Position.Floor + 32) && bs == 0 && if (y > (pointColl.GetFloorHeight() + 32) && bs == 0 &&
(((x / BLOCK(1)) != (item->Pose.Position.x / BLOCK(1))) || (((x / BLOCK(1)) != (item->Pose.Position.x / BLOCK(1))) ||
((z / BLOCK(1)) != (item->Pose.Position.z / BLOCK(1))))) ((z / BLOCK(1)) != (item->Pose.Position.z / BLOCK(1)))))
{ {
@ -1353,14 +1359,14 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
item->Pose.Position.z = z; item->Pose.Position.z = z;
} }
// Hit a steep slope? // Hit a steep slope?
else if (pointProbe.Position.FloorSlope) else if (pointColl.IsSteepFloor())
{ {
// Need to know which direction the slope is. // Need to know which direction the slope is.
item->Animation.Velocity.z -= (item->Animation.Velocity.z / 4); item->Animation.Velocity.z -= (item->Animation.Velocity.z / 4);
// Hit angle = ANGLE(90.0f) // Hit angle = ANGLE(90.0f)
if (pointProbe.FloorTilt.x < 0 && ((abs(pointProbe.FloorTilt.x)) - (abs(pointProbe.FloorTilt.y)) >= 2)) if (floorTilt.x < 0 && ((abs(floorTilt.x)) - (abs(floorTilt.y)) >= 2))
{ {
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(180.0f)) if (((unsigned short)item->Pose.Orientation.y) > ANGLE(180.0f))
{ {
@ -1372,7 +1378,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z -= pointProbe.FloorTilt.x * 2; item->Animation.Velocity.z -= floorTilt.x * 2;
if ((unsigned short)item->Pose.Orientation.y > ANGLE(90.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(270.0f)) if ((unsigned short)item->Pose.Orientation.y > ANGLE(90.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(270.0f))
{ {
item->Pose.Orientation.y -= ANGLE(22.5f); item->Pose.Orientation.y -= ANGLE(22.5f);
@ -1394,7 +1400,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
// Hit angle = ANGLE(270.0f) // Hit angle = ANGLE(270.0f)
else if (pointProbe.FloorTilt.x > 0 && ((abs(pointProbe.FloorTilt.x)) - (abs(pointProbe.FloorTilt.y)) >= 2)) else if (floorTilt.x > 0 && ((abs(floorTilt.x)) - (abs(floorTilt.y)) >= 2))
{ {
if (((unsigned short)item->Pose.Orientation.y) < ANGLE(180.0f)) if (((unsigned short)item->Pose.Orientation.y) < ANGLE(180.0f))
{ {
@ -1406,7 +1412,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z += pointProbe.FloorTilt.x * 2; item->Animation.Velocity.z += floorTilt.x * 2;
if ((unsigned short)item->Pose.Orientation.y > ANGLE(270.0f) || (unsigned short)item->Pose.Orientation.y < ANGLE(90.0f)) if ((unsigned short)item->Pose.Orientation.y > ANGLE(270.0f) || (unsigned short)item->Pose.Orientation.y < ANGLE(90.0f))
{ {
item->Pose.Orientation.y -= ANGLE(22.5f); item->Pose.Orientation.y -= ANGLE(22.5f);
@ -1428,7 +1434,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
// Hit angle = 0 // Hit angle = 0
else if (pointProbe.FloorTilt.y < 0 && ((abs(pointProbe.FloorTilt.y)) - (abs(pointProbe.FloorTilt.x)) >= 2)) else if (floorTilt.y < 0 && ((abs(floorTilt.y)) - (abs(floorTilt.x)) >= 2))
{ {
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(90.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(270.0f)) if (((unsigned short)item->Pose.Orientation.y) > ANGLE(90.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(270.0f))
{ {
@ -1440,7 +1446,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z -= pointProbe.FloorTilt.y * 2; item->Animation.Velocity.z -= floorTilt.y * 2;
if ((unsigned short)item->Pose.Orientation.y < ANGLE(180.0f)) if ((unsigned short)item->Pose.Orientation.y < ANGLE(180.0f))
{ {
@ -1463,7 +1469,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
// Hit angle = ANGLE(180.0f) // Hit angle = ANGLE(180.0f)
else if (pointProbe.FloorTilt.y > 0 && ((abs(pointProbe.FloorTilt.y)) - (abs(pointProbe.FloorTilt.x)) >= 2)) else if (floorTilt.y > 0 && ((abs(floorTilt.y)) - (abs(floorTilt.x)) >= 2))
{ {
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(270.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(90.0f)) if (((unsigned short)item->Pose.Orientation.y) > ANGLE(270.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(90.0f))
{ {
@ -1475,7 +1481,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z += pointProbe.FloorTilt.y * 2; item->Animation.Velocity.z += floorTilt.y * 2;
if ((unsigned short)item->Pose.Orientation.y > ANGLE(180.0f)) if ((unsigned short)item->Pose.Orientation.y > ANGLE(180.0f))
{ {
@ -1497,7 +1503,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
item->Animation.Velocity.y = 0; item->Animation.Velocity.y = 0;
} }
} }
else if (pointProbe.FloorTilt.x < 0 && pointProbe.FloorTilt.y < 0) // Hit angle = 0x2000 else if (floorTilt.x < 0 && floorTilt.y < 0) // Hit angle = 0x2000
{ {
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(135.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(315.0f)) if (((unsigned short)item->Pose.Orientation.y) > ANGLE(135.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(315.0f))
{ {
@ -1509,7 +1515,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z += -pointProbe.FloorTilt.x + -pointProbe.FloorTilt.y; item->Animation.Velocity.z += -floorTilt.x + -floorTilt.y;
if ((unsigned short)item->Pose.Orientation.y > ANGLE(45.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(225.0f)) if ((unsigned short)item->Pose.Orientation.y > ANGLE(45.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(225.0f))
{ {
item->Pose.Orientation.y -= ANGLE(22.5f); item->Pose.Orientation.y -= ANGLE(22.5f);
@ -1531,7 +1537,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
// Hit angle = ANGLE(135.0f) // Hit angle = ANGLE(135.0f)
else if (pointProbe.FloorTilt.x < 0 && pointProbe.FloorTilt.y > 0) else if (floorTilt.x < 0 && floorTilt.y > 0)
{ {
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(225.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(45.0f)) if (((unsigned short)item->Pose.Orientation.y) > ANGLE(225.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(45.0f))
{ {
@ -1543,7 +1549,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z += (-pointProbe.FloorTilt.x) + pointProbe.FloorTilt.y; item->Animation.Velocity.z += (-floorTilt.x) + floorTilt.y;
if ((unsigned short)item->Pose.Orientation.y < ANGLE(315.0f) && (unsigned short)item->Pose.Orientation.y > ANGLE(135.0f)) if ((unsigned short)item->Pose.Orientation.y < ANGLE(315.0f) && (unsigned short)item->Pose.Orientation.y > ANGLE(135.0f))
{ {
item->Pose.Orientation.y -= ANGLE(22.5f); item->Pose.Orientation.y -= ANGLE(22.5f);
@ -1565,7 +1571,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
// Hit angle = ANGLE(225.5f) // Hit angle = ANGLE(225.5f)
else if (pointProbe.FloorTilt.x > 0 && pointProbe.FloorTilt.y > 0) else if (floorTilt.x > 0 && floorTilt.y > 0)
{ {
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(315.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(135.0f)) if (((unsigned short)item->Pose.Orientation.y) > ANGLE(315.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(135.0f))
{ {
@ -1577,7 +1583,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z += pointProbe.FloorTilt.x + pointProbe.FloorTilt.y; item->Animation.Velocity.z += floorTilt.x + floorTilt.y;
if ((unsigned short)item->Pose.Orientation.y < ANGLE(45.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(225.5f)) if ((unsigned short)item->Pose.Orientation.y < ANGLE(45.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(225.5f))
{ {
item->Pose.Orientation.y -= ANGLE(22.5f); item->Pose.Orientation.y -= ANGLE(22.5f);
@ -1599,7 +1605,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
// Hit angle = ANGLE(315.0f) // Hit angle = ANGLE(315.0f)
else if (pointProbe.FloorTilt.x > 0 && pointProbe.FloorTilt.y < 0) else if (floorTilt.x > 0 && floorTilt.y < 0)
{ {
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(45.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(225.5f)) if (((unsigned short)item->Pose.Orientation.y) > ANGLE(45.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(225.5f))
{ {
@ -1611,7 +1617,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (item->Animation.Velocity.z < 32) if (item->Animation.Velocity.z < 32)
{ {
item->Animation.Velocity.z += pointProbe.FloorTilt.x + (-pointProbe.FloorTilt.y); item->Animation.Velocity.z += floorTilt.x + (-floorTilt.y);
if ((unsigned short)item->Pose.Orientation.y < ANGLE(135.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(315.0f)) if ((unsigned short)item->Pose.Orientation.y < ANGLE(135.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(315.0f))
{ {
item->Pose.Orientation.y -= ANGLE(22.5f); item->Pose.Orientation.y -= ANGLE(22.5f);
@ -1672,7 +1678,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
item->Pose.Position.y = pointProbe.Position.Floor; item->Pose.Position.y = pointColl.GetFloorHeight();
} }
} }
// Check for on top of object. // Check for on top of object.
@ -1680,8 +1686,8 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
{ {
if (yv >= 0) if (yv >= 0)
{ {
prevPointProbe = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber); prevPointColl = GetPointCollision(Vector3i(item->Pose.Position.x, y, item->Pose.Position.z), item->RoomNumber);
pointProbe = GetCollision(item); pointColl = GetPointCollision(*item);
// Bounce off floor. // Bounce off floor.
@ -1689,7 +1695,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
// was always set to 0 by GetHeight() function which was called before the check. // was always set to 0 by GetHeight() function which was called before the check.
// Possibly a mistake or unfinished feature by Core? -- Lwmte, 27.08.21 // Possibly a mistake or unfinished feature by Core? -- Lwmte, 27.08.21
if (item->Pose.Position.y >= prevPointProbe.Position.Floor) if (item->Pose.Position.y >= prevPointColl.GetFloorHeight())
{ {
// Hit the floor; bounce and slow down. // Hit the floor; bounce and slow down.
if (item->Animation.Velocity.y > 0) if (item->Animation.Velocity.y > 0)
@ -1723,17 +1729,17 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
item->Pose.Position.y = prevPointProbe.Position.Floor; item->Pose.Position.y = prevPointColl.GetFloorHeight();
} }
} }
// else // else
{ {
// Bounce off ceiling. // Bounce off ceiling.
pointProbe = GetCollision(item); pointColl = GetPointCollision(*item);
if (item->Pose.Position.y < pointProbe.Position.Ceiling) if (item->Pose.Position.y < pointColl.GetCeilingHeight())
{ {
if (y < pointProbe.Position.Ceiling && if (y < pointColl.GetCeilingHeight() &&
(((x / BLOCK(1)) != (item->Pose.Position.x / BLOCK(1))) || (((x / BLOCK(1)) != (item->Pose.Position.x / BLOCK(1))) ||
((z / BLOCK(1)) != (item->Pose.Position.z / BLOCK(1))))) ((z / BLOCK(1)) != (item->Pose.Position.z / BLOCK(1)))))
{ {
@ -1762,7 +1768,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
item->Pose.Position.z = z; item->Pose.Position.z = z;
} }
else else
item->Pose.Position.y = pointProbe.Position.Ceiling; item->Pose.Position.y = pointColl.GetCeilingHeight();
if (item->Animation.Velocity.y < 0) if (item->Animation.Velocity.y < 0)
item->Animation.Velocity.y = -item->Animation.Velocity.y; item->Animation.Velocity.y = -item->Animation.Velocity.y;
@ -1770,14 +1776,14 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
} }
} }
pointProbe = GetCollision(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber); pointColl = GetPointCollision(*item);
if (pointProbe.RoomNumber != item->RoomNumber) if (pointColl.GetRoomNumber() != item->RoomNumber)
{ {
if (item->ObjectNumber == ID_GRENADE && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, pointProbe.RoomNumber)) if (item->ObjectNumber == ID_GRENADE && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, pointColl.GetRoomNumber()))
Splash(item); Splash(item);
ItemNewRoom(itemNumber, pointProbe.RoomNumber); ItemNewRoom(itemNumber, pointColl.GetRoomNumber());
} }
item->Pose.Position.y -= radius; item->Pose.Position.y -= radius;

View file

@ -1,6 +1,9 @@
#pragma once #pragma once
#include "Game/collision/Point.h"
#include "Math/Math.h" #include "Math/Math.h"
using namespace TEN::Collision::Point;
class FloorInfo; class FloorInfo;
struct CollisionInfo; struct CollisionInfo;
struct CollisionResult; struct CollisionResult;
@ -27,10 +30,10 @@ struct ObjectCollisionBounds
struct CollidedObjectData struct CollidedObjectData
{ {
std::vector<ItemInfo*> ItemPtrs = {}; std::vector<ItemInfo*> Items = {};
std::vector<MESH_INFO*> StaticPtrs = {}; std::vector<MESH_INFO*> Statics = {};
bool IsEmpty() const { return (ItemPtrs.empty() && StaticPtrs.empty()); }; bool IsEmpty() const { return (Items.empty() && Statics.empty()); };
}; };
void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
@ -56,7 +59,7 @@ void ItemPushBridge(ItemInfo& item, CollisionInfo& coll);
bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& pose, CollisionInfo* coll); bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose& pose, CollisionInfo* coll);
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll); void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll);
void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, const CollisionResult& collResult); void CollideBridgeItems(ItemInfo& item, CollisionInfo& coll, PointCollisionData& pointColl);
void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void ObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void ObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);

View file

@ -4,17 +4,18 @@
#include "Game/control/box.h" #include "Game/control/box.h"
#include "Game/control/los.h" #include "Game/control/los.h"
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/collision/Point.h"
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/room.h" #include "Game/room.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Renderer/Renderer.h"
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Collision::Room;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer;
void ShiftItem(ItemInfo* item, CollisionInfo* coll) void ShiftItem(ItemInfo* item, CollisionInfo* coll)
{ {
@ -97,12 +98,12 @@ bool TestItemRoomCollisionAABB(ItemInfo* item)
auto test = [item](short x, short y, short z, bool floor) auto test = [item](short x, short y, short z, bool floor)
{ {
auto collPos = GetCollision(x, y, z, item->RoomNumber).Position; auto pointColl = GetPointCollision(Vector3i(x, y, z), item->RoomNumber);
if (floor) if (floor)
return (y > collPos.Floor); return (y > pointColl.GetFloorHeight());
else else
return (y < collPos.Ceiling); return (y < pointColl.GetCeilingHeight());
}; };
bool collided = bool collided =
@ -118,155 +119,26 @@ bool TestItemRoomCollisionAABB(ItemInfo* item)
return collided; return collided;
} }
// Overload used to quickly get point collision parameters at a given item's position. static CollisionPositionData GetCollisionPosition(PointCollisionData& pointColl)
CollisionResult GetCollision(const ItemInfo& item)
{ {
short newRoomNumber = item.RoomNumber; auto collPos = CollisionPositionData{};
auto floor = GetFloor(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, &newRoomNumber); collPos.Floor = pointColl.GetFloorHeight();
auto probe = GetCollision(floor, item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z); collPos.Ceiling = pointColl.GetCeilingHeight();
collPos.Bridge = pointColl.GetFloorBridgeItemNumber();
collPos.SplitAngle = pointColl.GetBottomSector().FloorSurface.SplitAngle;
collPos.FloorSlope = pointColl.IsSteepFloor();
collPos.CeilingSlope = pointColl.IsSteepCeiling();
collPos.DiagonalStep = pointColl.IsDiagonalFloorStep();
probe.RoomNumber = newRoomNumber; return collPos;
return probe;
} }
// Deprecated. static void SetSectorAttribs(CollisionPositionData& sectorAttribs, const CollisionSetupData& collSetup, PointCollisionData& pointColl,
CollisionResult GetCollision(const ItemInfo* item)
{
return GetCollision(*item);
}
// Overload used to probe point collision parameters from a given item's position.
CollisionResult GetCollision(const ItemInfo* item, short headingAngle, float forward, float down, float right)
{
short tempRoomNumber = item->RoomNumber;
// TODO: Find cleaner solution. Constructing a Location for Lara on the spot can result in a stumble when climbing onto thin platforms. -- Sezz 2022.06.14
auto location = item->IsLara() ?
item->Location :
RoomVector(GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &tempRoomNumber)->RoomNumber, item->Pose.Position.y);
auto point = Geometry::TranslatePoint(item->Pose.Position, headingAngle, forward, down, right);
int adjacentRoomNumber = GetRoomVector(location, Vector3i(item->Pose.Position.x, point.y, item->Pose.Position.z)).RoomNumber;
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
}
// Overload used to probe point collision parameters from a given position.
CollisionResult GetCollision(const Vector3i& pos, int roomNumber, short headingAngle, float forward, float down, float right)
{
short tempRoomNumber = roomNumber;
auto location = RoomVector(GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->RoomNumber, pos.y);
auto point = Geometry::TranslatePoint(pos, headingAngle, forward, down, right);
int adjacentRoomNumber = GetRoomVector(location, Vector3i(pos.x, point.y, pos.z)).RoomNumber;
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
}
CollisionResult GetCollision(const Vector3i& pos, int roomNumber, const EulerAngles& orient, float dist)
{
auto point = Geometry::TranslatePoint(pos, orient, dist);
short tempRoomNumber = roomNumber;
auto location = RoomVector(GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->RoomNumber, pos.y);
int adjacentRoomNumber = GetRoomVector(location, Vector3i(pos.x, point.y, pos.z)).RoomNumber;
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
}
CollisionResult GetCollision(const Vector3i& pos, int roomNumber, const Vector3& dir, float dist)
{
auto point = Geometry::TranslatePoint(pos, dir, dist);
short tempRoomNumber = roomNumber;
auto location = RoomVector(GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->RoomNumber, pos.y);
int adjacentRoomNumber = GetRoomVector(location, Vector3i(pos.x, point.y, pos.z)).RoomNumber;
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
}
// Overload used as universal wrapper across collisional code replacing
// triads of roomNumber-GetFloor()-GetFloorHeight() calls.
// Advantage is that it does NOT modify incoming roomNumber argument,
// instead storing one modified by GetFloor() within a returned CollisionResult struct.
// This way, no external variables are modified as output arguments.
CollisionResult GetCollision(const Vector3i& pos, int roomNumber)
{
return GetCollision(pos.x, pos.y, pos.z, roomNumber);
}
// Deprecated.
CollisionResult GetCollision(int x, int y, int z, short roomNumber)
{
auto room = roomNumber;
auto floor = GetFloor(x, y, z, &room);
auto result = GetCollision(floor, x, y, z);
result.RoomNumber = room;
return result;
}
// NOTE: To be used only when absolutely necessary.
CollisionResult GetCollision(const GameVector& pos)
{
return GetCollision(pos.x, pos.y, pos.z, pos.RoomNumber);
}
// A reworked legacy GetFloorHeight() function which writes data
// into a special CollisionResult struct instead of global variables.
// It writes for both floor and ceiling heights at the same coordinates, meaning it should be used
// in place of successive GetFloorHeight() and GetCeilingHeight() calls to increase readability.
CollisionResult GetCollision(FloorInfo* floor, int x, int y, int z)
{
auto result = CollisionResult{};
// Record coordinates.
result.Coordinates = Vector3i(x, y, z);
// Return provided collision block into result as itself.
result.Block = floor;
// Floor and ceiling heights are borrowed directly from floordata.
result.Position.Floor = GetSurfaceHeight(RoomVector(floor->RoomNumber, y), x, z, true).value_or(NO_HEIGHT);
result.Position.Ceiling = GetSurfaceHeight(RoomVector(floor->RoomNumber, y), x, z, false).value_or(NO_HEIGHT);
// Probe bottom collision block through portals.
while (floor->GetNextRoomNumber(Vector3i(x, y, z), true).has_value())
{
auto* room = &g_Level.Rooms[floor->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(floor->RoomNumber)];
floor = GetSector(room, x - room->x, z - room->z);
}
// Return probed bottom collision block into result.
result.BottomBlock = floor;
// Get surface noramls.
result.FloorNormal = floor->GetSurfaceNormal(x, z, true);
result.CeilingNormal = floor->GetSurfaceNormal(x, z, false);
// Backport surface normals to tilts.
result.FloorTilt = GetSurfaceTilt(result.FloorNormal, true).ToVector2();
result.CeilingTilt = GetSurfaceTilt(result.CeilingNormal, false).ToVector2();
// Split, bridge and slope data.
result.Position.DiagonalStep = floor->IsSurfaceDiagonalStep(true);
result.Position.SplitAngle = TO_RAD(floor->FloorSurface.SplitAngle);
result.Position.Bridge = result.BottomBlock->GetInsideBridgeItemNumber(Vector3i(x, result.Position.Floor, z), true, false);
result.Position.FloorSlope = result.Position.Bridge < 0 && Geometry::GetSurfaceSlopeAngle(result.FloorNormal) >=
result.BottomBlock->GetSurfaceIllegalSlopeAngle(x, z, true);
result.Position.CeilingSlope = Geometry::GetSurfaceSlopeAngle(result.CeilingNormal, -Vector3::UnitY) >=
result.BottomBlock->GetSurfaceIllegalSlopeAngle(x, z, false); // TODO: Fix on bridges placed beneath ceiling slopes. @Sezz 2022.01.29
return result;
}
void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, bool resetRoom)
{
GetCollisionInfo(coll, item, Vector3i::Zero, resetRoom);
}
static void SetSectorAttribs(CollisionPosition& sectorAttribs, const CollisionSetup& collSetup, const CollisionResult& pointColl,
const Vector3i& probePos, int realRoomNumber) const Vector3i& probePos, int realRoomNumber)
{ {
constexpr auto ASPECT_ANGLE_DELTA_MAX = ANGLE(90.0f); constexpr auto ASPECT_ANGLE_DELTA_MAX = ANGLE(90.0f);
auto floorNormal = pointColl.FloorNormal; auto floorNormal = pointColl.GetFloorNormal();
short aspectAngle = Geometry::GetSurfaceAspectAngle(floorNormal); short aspectAngle = Geometry::GetSurfaceAspectAngle(floorNormal);
short aspectAngleDelta = Geometry::GetShortestAngle(collSetup.ForwardAngle, aspectAngle); short aspectAngleDelta = Geometry::GetShortestAngle(collSetup.ForwardAngle, aspectAngle);
@ -293,14 +165,14 @@ static void SetSectorAttribs(CollisionPosition& sectorAttribs, const CollisionSe
} }
else if (collSetup.BlockDeathFloorDown && else if (collSetup.BlockDeathFloorDown &&
sectorAttribs.Floor >= CLICK(0.5f) && sectorAttribs.Floor >= CLICK(0.5f) &&
pointColl.BottomBlock->Flags.Death) pointColl.GetBottomSector().Flags.Death)
{ {
sectorAttribs.Floor = MAX_HEIGHT; sectorAttribs.Floor = MAX_HEIGHT;
} }
else if (collSetup.BlockMonkeySwingEdge) else if (collSetup.BlockMonkeySwingEdge)
{ {
auto pointColl = GetCollision(probePos.x, probePos.y + collSetup.Height, probePos.z, realRoomNumber); auto pointColl = GetPointCollision(probePos, realRoomNumber, Vector3::UnitY, collSetup.Height);
if (!pointColl.BottomBlock->Flags.Monkeyswing) if (!pointColl.GetBottomSector().Flags.Monkeyswing)
sectorAttribs.Floor = MAX_HEIGHT; sectorAttribs.Floor = MAX_HEIGHT;
} }
} }
@ -407,8 +279,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
// HACK: when using SetPosition animcommand, item->RoomNumber does not immediately // HACK: when using SetPosition animcommand, item->RoomNumber does not immediately
// update, but only at the end of control loop. This may cause bugs when Lara is // update, but only at the end of control loop. This may cause bugs when Lara is
// climbing or vaulting ledges under slopes. Using Location->roomNumber solves // climbing or vaulting ledges under slopes. Using Location->RoomNumber solves
// these bugs, as it is updated immediately. But since Location->roomNumber is ONLY // these bugs, as it is updated immediately. But since Location->RoomNumber is ONLY
// updated for Lara, we can't use it for all objects for now. In future, we should // updated for Lara, we can't use it for all objects for now. In future, we should
// either update Location field for all objects or use this value as it is now. // either update Location field for all objects or use this value as it is now.
@ -416,22 +288,22 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
// TEST 1: TILT AND NEAREST LEDGE CALCULATION // TEST 1: TILT AND NEAREST LEDGE CALCULATION
auto collResult = GetCollision(probePos.x, item->Pose.Position.y, probePos.z, realRoomNumber); auto pointColl = GetPointCollision(Vector3i(probePos.x, item->Pose.Position.y, probePos.z), realRoomNumber);
coll->FloorNormal = collResult.FloorNormal; coll->FloorNormal = pointColl.GetFloorNormal();
coll->CeilingNormal = collResult.CeilingNormal; coll->CeilingNormal = pointColl.GetCeilingNormal();
coll->FloorTilt = collResult.FloorTilt; coll->FloorTilt = GetSurfaceTilt(pointColl.GetFloorNormal(), true).ToVector2();
coll->CeilingTilt = collResult.CeilingTilt; coll->CeilingTilt = GetSurfaceTilt(pointColl.GetCeilingNormal(), false).ToVector2();
coll->NearestLedgeAngle = GetNearestLedgeAngle(item, coll, coll->NearestLedgeDistance); coll->NearestLedgeAngle = GetNearestLedgeAngle(item, coll, coll->NearestLedgeDistance);
// Debug angle and distance // Debug angle and distance
// g_Renderer.PrintDebugMessage("Nearest angle: %d", coll->NearestLedgeAngle); // PrintDebugMessage("Nearest angle: %d", coll->NearestLedgeAngle);
// g_Renderer.PrintDebugMessage("Nearest dist: %f", coll->NearestLedgeDistance); // PrintDebugMessage("Nearest dist: %f", coll->NearestLedgeDistance);
// TEST 2: CENTERPOINT PROBE // TEST 2: CENTERPOINT PROBE
collResult = GetCollision(probePos.x, probePos.y, probePos.z, realRoomNumber); pointColl = GetPointCollision(probePos, realRoomNumber);
int topRoomNumber = collResult.RoomNumber; // Keep top room number as we need it to re-probe from origin room. int topRoomNumber = pointColl.GetRoomNumber(); // Keep top room number as we need it to re-probe from origin room.
if (doPlayerCollision) if (doPlayerCollision)
{ {
@ -443,8 +315,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
else else
{ {
height = collResult.Position.Floor; height = pointColl.GetFloorHeight();
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z); ceiling = GetCeiling(&pointColl.GetSector(), probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
} }
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
@ -453,21 +325,21 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (ceiling != NO_HEIGHT) if (ceiling != NO_HEIGHT)
ceiling -= probePos.y; ceiling -= probePos.y;
coll->Middle = collResult.Position; coll->Middle = GetCollisionPosition(pointColl);
coll->Middle.Floor = height; coll->Middle.Floor = height;
coll->Middle.Ceiling = ceiling; coll->Middle.Ceiling = ceiling;
// Additionally calculate bridge shifts, if present. // Additionally calculate bridge shifts if present.
CollideBridgeItems(*item, *coll, collResult); CollideBridgeItems(*item, *coll, pointColl);
// TEST 3: FRONTAL PROBE // TEST 3: FRONTAL PROBE
probePos.x = entityPos.x + xFront; probePos.x = entityPos.x + xFront;
probePos.z = entityPos.z + zFront; probePos.z = entityPos.z + zFront;
g_Renderer.AddDebugSphere(probePos.ToVector3(), 32, Vector4(1, 0, 0, 1), RendererDebugPage::CollisionStats); DrawDebugSphere(probePos.ToVector3(), 32, Vector4(1, 0, 0, 1), RendererDebugPage::CollisionStats);
collResult = GetCollision(probePos.x, probePos.y, probePos.z, topRoomNumber); pointColl = GetPointCollision(probePos, topRoomNumber);
if (doPlayerCollision) if (doPlayerCollision)
{ {
@ -486,8 +358,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
else else
{ {
height = collResult.Position.Floor; height = pointColl.GetFloorHeight();
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z); ceiling = GetCeiling(&pointColl.GetSector(), probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
} }
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
@ -496,7 +368,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (ceiling != NO_HEIGHT) if (ceiling != NO_HEIGHT)
ceiling -= probePos.y; ceiling -= probePos.y;
coll->Front = collResult.Position; coll->Front = GetCollisionPosition(pointColl);
coll->Front.Floor = height; coll->Front.Floor = height;
coll->Front.Ceiling = ceiling; coll->Front.Ceiling = ceiling;
@ -507,22 +379,22 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
else else
{ {
height = GetCollision(probePos.x + xFront, probePos.y, probePos.z + zFront, topRoomNumber).Position.Floor; height = GetPointCollision(Vector3i(probePos.x + xFront, probePos.y, probePos.z + zFront), topRoomNumber).GetFloorHeight();
} }
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
height -= (doPlayerCollision ? entityPos.y : probePos.y); height -= (doPlayerCollision ? entityPos.y : probePos.y);
SetSectorAttribs(coll->Front, coll->Setup, collResult, probePos, realRoomNumber); SetSectorAttribs(coll->Front, coll->Setup, pointColl, probePos, realRoomNumber);
// TEST 4: MIDDLE-LEFT PROBE // TEST 4: MIDDLE-LEFT PROBE
probePos.x = entityPos.x + xLeft; probePos.x = entityPos.x + xLeft;
probePos.z = entityPos.z + zLeft; probePos.z = entityPos.z + zLeft;
g_Renderer.AddDebugSphere(probePos.ToVector3(), 32, Vector4(0, 0, 1, 1), RendererDebugPage::CollisionStats); DrawDebugSphere(probePos.ToVector3(), 32, Vector4(0, 0, 1, 1), RendererDebugPage::CollisionStats);
collResult = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber); pointColl = GetPointCollision(probePos, item->RoomNumber);
if (doPlayerCollision) if (doPlayerCollision)
{ {
@ -534,8 +406,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
else else
{ {
height = collResult.Position.Floor; height = pointColl.GetFloorHeight();
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z); ceiling = GetCeiling(&pointColl.GetSector(), probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
} }
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
@ -544,15 +416,16 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (ceiling != NO_HEIGHT) if (ceiling != NO_HEIGHT)
ceiling -= probePos.y; ceiling -= probePos.y;
coll->MiddleLeft = collResult.Position; coll->MiddleLeft = GetCollisionPosition(pointColl);
coll->MiddleLeft.Floor = height; coll->MiddleLeft.Floor = height;
coll->MiddleLeft.Ceiling = ceiling; coll->MiddleLeft.Ceiling = ceiling;
SetSectorAttribs(coll->MiddleLeft, coll->Setup, collResult, probePos, realRoomNumber); SetSectorAttribs(coll->MiddleLeft, coll->Setup, pointColl, probePos, realRoomNumber);
// TEST 5: FRONT-LEFT PROBE // TEST 5: FRONT-LEFT PROBE
collResult = GetCollision(probePos.x, probePos.y, probePos.z, topRoomNumber); // Use plain X/Z values here as proposed by Choco. // Use plain X/Z values.
pointColl = GetPointCollision(probePos, topRoomNumber);
if (doPlayerCollision) if (doPlayerCollision)
{ {
@ -564,8 +437,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
else else
{ {
height = collResult.Position.Floor; height = pointColl.GetFloorHeight();
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z); ceiling = GetCeiling(&pointColl.GetSector(), probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
} }
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
@ -574,20 +447,20 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (ceiling != NO_HEIGHT) if (ceiling != NO_HEIGHT)
ceiling -= probePos.y; ceiling -= probePos.y;
coll->FrontLeft = collResult.Position; coll->FrontLeft = GetCollisionPosition(pointColl);
coll->FrontLeft.Floor = height; coll->FrontLeft.Floor = height;
coll->FrontLeft.Ceiling = ceiling; coll->FrontLeft.Ceiling = ceiling;
SetSectorAttribs(coll->FrontLeft, coll->Setup, collResult, probePos, realRoomNumber); SetSectorAttribs(coll->FrontLeft, coll->Setup, pointColl, probePos, realRoomNumber);
// TEST 6: MIDDLE-RIGHT PROBE // TEST 6: MIDDLE-RIGHT PROBE
probePos.x = entityPos.x + xRight; probePos.x = entityPos.x + xRight;
probePos.z = entityPos.z + zRight; probePos.z = entityPos.z + zRight;
g_Renderer.AddDebugSphere(probePos.ToVector3(), 32, Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats); DrawDebugSphere(probePos.ToVector3(), 32, Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats);
collResult = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber); pointColl = GetPointCollision(probePos, item->RoomNumber);
if (doPlayerCollision) if (doPlayerCollision)
{ {
@ -599,8 +472,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
else else
{ {
height = collResult.Position.Floor; height = pointColl.GetFloorHeight();
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z); ceiling = GetCeiling(&pointColl.GetSector(), probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
} }
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
@ -609,15 +482,15 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (ceiling != NO_HEIGHT) if (ceiling != NO_HEIGHT)
ceiling -= probePos.y; ceiling -= probePos.y;
coll->MiddleRight = collResult.Position; coll->MiddleRight = GetCollisionPosition(pointColl);
coll->MiddleRight.Floor = height; coll->MiddleRight.Floor = height;
coll->MiddleRight.Ceiling = ceiling; coll->MiddleRight.Ceiling = ceiling;
SetSectorAttribs(coll->MiddleRight, coll->Setup, collResult, probePos, realRoomNumber); SetSectorAttribs(coll->MiddleRight, coll->Setup, pointColl, probePos, realRoomNumber);
// TEST 7: FRONT-RIGHT PROBE // TEST 7: FRONT-RIGHT PROBE
collResult = GetCollision(probePos.x, probePos.y, probePos.z, topRoomNumber); pointColl = GetPointCollision(probePos, topRoomNumber);
if (doPlayerCollision) if (doPlayerCollision)
{ {
@ -629,8 +502,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
else else
{ {
height = collResult.Position.Floor; height = pointColl.GetFloorHeight();
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z); ceiling = GetCeiling(&pointColl.GetSector(), probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
} }
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
@ -639,11 +512,11 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (ceiling != NO_HEIGHT) if (ceiling != NO_HEIGHT)
ceiling -= probePos.y; ceiling -= probePos.y;
coll->FrontRight = collResult.Position; coll->FrontRight = GetCollisionPosition(pointColl);
coll->FrontRight.Floor = height; coll->FrontRight.Floor = height;
coll->FrontRight.Ceiling = ceiling; coll->FrontRight.Ceiling = ceiling;
SetSectorAttribs(coll->FrontRight, coll->Setup, collResult, probePos, realRoomNumber); SetSectorAttribs(coll->FrontRight, coll->Setup, pointColl, probePos, realRoomNumber);
// TEST 8: SOLID STATIC MESHES // TEST 8: SOLID STATIC MESHES
@ -818,16 +691,21 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
} }
void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, bool resetRoom)
{
GetCollisionInfo(coll, item, Vector3i::Zero, resetRoom);
}
void AlignEntityToSurface(ItemInfo* item, const Vector2& ellipse, float alpha, short constraintAngle) void AlignEntityToSurface(ItemInfo* item, const Vector2& ellipse, float alpha, short constraintAngle)
{ {
// Reduce ellipse axis lengths for stability. // Reduce ellipse axis lengths for stability.
auto reducedEllipse = ellipse * 0.75f; auto reducedEllipse = ellipse * 0.75f;
// Probe heights at points around entity. // Probe heights at points around entity.
int frontHeight = GetCollision(item, item->Pose.Orientation.y, reducedEllipse.y).Position.Floor; int frontHeight = GetPointCollision(*item, item->Pose.Orientation.y, reducedEllipse.y).GetFloorHeight();
int backHeight = GetCollision(item, item->Pose.Orientation.y + ANGLE(180.0f), reducedEllipse.y).Position.Floor; int backHeight = GetPointCollision(*item, item->Pose.Orientation.y + ANGLE(180.0f), reducedEllipse.y).GetFloorHeight();
int leftHeight = GetCollision(item, item->Pose.Orientation.y - ANGLE(90.0f), reducedEllipse.x).Position.Floor; int leftHeight = GetPointCollision(*item, item->Pose.Orientation.y - ANGLE(90.0f), reducedEllipse.x).GetFloorHeight();
int rightHeight = GetCollision(item, item->Pose.Orientation.y + ANGLE(90.0f), reducedEllipse.x).Position.Floor; int rightHeight = GetPointCollision(*item, item->Pose.Orientation.y + ANGLE(90.0f), reducedEllipse.x).GetFloorHeight();
// Calculate height deltas. // Calculate height deltas.
int forwardHeightDelta = backHeight - frontHeight; int forwardHeightDelta = backHeight - frontHeight;
@ -927,7 +805,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
} }
// Debug probe point // Debug probe point
// g_Renderer.AddDebugSphere(Vector3(eX, y, eZ), 16, Vector4(1, 1, 0, 1), RendererDebugPage::CollisionStats); // DrawDebugSphere(Vector3(eX, y, eZ), 16, Vector4(1, 1, 0, 1), RendererDebugPage::CollisionStats);
// Determine front floor probe offset. // Determine front floor probe offset.
// It is needed to identify if there is bridge or ceiling split in front. // It is needed to identify if there is bridge or ceiling split in front.
@ -943,11 +821,11 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
// Get front floor block // Get front floor block
auto room = GetRoomVector(item->Location, Vector3i(ffpX, y, ffpZ)).RoomNumber; auto room = GetRoomVector(item->Location, Vector3i(ffpX, y, ffpZ)).RoomNumber;
auto block = GetCollision(ffpX, y, ffpZ, room).Block; auto sector = &GetPointCollision(Vector3i(ffpX, y, ffpZ), room).GetSector();
// Get front floor surface heights // Get front floor surface heights
auto floorHeight = GetSurfaceHeight(RoomVector(block->RoomNumber, y), ffpX, ffpZ, true).value_or(NO_HEIGHT); auto floorHeight = GetSurfaceHeight(RoomVector(sector->RoomNumber, y), ffpX, ffpZ, true).value_or(NO_HEIGHT);
auto ceilingHeight = GetSurfaceHeight(RoomVector(block->RoomNumber, y), ffpX, ffpZ, false).value_or(NO_HEIGHT); auto ceilingHeight = GetSurfaceHeight(RoomVector(sector->RoomNumber, y), ffpX, ffpZ, false).value_or(NO_HEIGHT);
// If probe landed inside wall (i.e. both floor/ceiling heights are NO_HEIGHT), make a fake // If probe landed inside wall (i.e. both floor/ceiling heights are NO_HEIGHT), make a fake
// ledge for algorithm to further succeed. // ledge for algorithm to further succeed.
@ -960,7 +838,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
int height = useCeilingLedge ? ceilingHeight : floorHeight; int height = useCeilingLedge ? ceilingHeight : floorHeight;
// Determine if there is a bridge in front. // Determine if there is a bridge in front.
auto bridge = block->GetInsideBridgeItemNumber(Vector3i(ffpX, height, ffpZ), true, y == height); auto bridge = sector->GetInsideBridgeItemNumber(Vector3i(ffpX, height, ffpZ), true, y == height);
// Determine floor probe offset. // Determine floor probe offset.
// This must be slightly in front of own coll radius so no bridge misfires occur. // This must be slightly in front of own coll radius so no bridge misfires occur.
@ -969,11 +847,11 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
auto fpZ = eZ + floorProbeOffset * cosForwardAngle; auto fpZ = eZ + floorProbeOffset * cosForwardAngle;
// Debug probe point. // Debug probe point.
// g_Renderer.AddDebugSphere(Vector3(fpX, y, fpZ), 16, Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats); // DrawDebugSphere(Vector3(fpX, y, fpZ), 16, Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats);
// Get true room number and block, based on derived height // Get true room number and block, based on derived height
room = GetRoomVector(item->Location, Vector3i(fpX, height, fpZ)).RoomNumber; room = GetRoomVector(item->Location, Vector3i(fpX, height, fpZ)).RoomNumber;
block = GetCollision(fpX, height, fpZ, room).Block; sector = &GetPointCollision(Vector3i(fpX, height, fpZ), room).GetSector();
// We don't need actual corner heights to build planes, so just use normalized value here. // We don't need actual corner heights to build planes, so just use normalized value here.
auto fY = height - 1; auto fY = height - 1;
@ -985,7 +863,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
auto ray = Ray(Vector3(eX, cY, eZ), direction); auto ray = Ray(Vector3(eX, cY, eZ), direction);
// Debug ray direction. // Debug ray direction.
// g_Renderer.AddDebugLine(Vector3(eX, y, eZ), Vector3(eX, y, eZ) + direction * 256, Vector4(1, 0, 0, 1)); // DrawDebugLine(Vector3(eX, y, eZ), Vector3(eX, y, eZ) + direction * 256, Vector4(1, 0, 0, 1));
// Keep origin ray to calculate true centerpoint distance to ledge later. // Keep origin ray to calculate true centerpoint distance to ledge later.
if (p == 0) if (p == 0)
@ -1031,7 +909,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
else else
{ {
// Determine if we should use floor or ceiling split angle based on early tests. // Determine if we should use floor or ceiling split angle based on early tests.
auto splitAngle = (useCeilingLedge ? TO_RAD(block->CeilingSurface.SplitAngle) : TO_RAD(block->FloorSurface.SplitAngle)); auto splitAngle = (useCeilingLedge ? TO_RAD(sector->CeilingSurface.SplitAngle) : TO_RAD(sector->FloorSurface.SplitAngle));
// Get horizontal block corner coordinates. // Get horizontal block corner coordinates.
auto fX = floor(eX / BLOCK(1)) * BLOCK(1) - 1; auto fX = floor(eX / BLOCK(1)) * BLOCK(1) - 1;
@ -1040,7 +918,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
auto cZ = fZ + BLOCK(1) + 1; auto cZ = fZ + BLOCK(1) + 1;
// Debug used block // Debug used block
// g_Renderer.AddDebugSphere(Vector3(round(eX / BLOCK(1)) * BLOCK(1) + BLOCK(0.5f), y, round(eZ / BLOCK(1)) * BLOCK(1) + BLOCK(0.5f)), 16, Vector4::One, RendererDebugPage::CollisionStats); // DrawDebugSphere(Vector3(round(eX / BLOCK(1)) * BLOCK(1) + BLOCK(0.5f), y, round(eZ / BLOCK(1)) * BLOCK(1) + BLOCK(0.5f)), 16, Vector4::One, RendererDebugPage::CollisionStats);
// Get split angle coordinates. // Get split angle coordinates.
auto sX = fX + 1 + BLOCK(0.5f); auto sX = fX + 1 + BLOCK(0.5f);
@ -1059,7 +937,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
}; };
// If split angle exists, take split plane into account too. // If split angle exists, take split plane into account too.
auto useSplitAngle = (useCeilingLedge ? block->IsSurfaceSplit(false) : block->IsSurfaceSplit(true)); auto useSplitAngle = (useCeilingLedge ? sector->IsSurfaceSplit(false) : sector->IsSurfaceSplit(true));
// Find closest block edge plane. // Find closest block edge plane.
for (int i = 0; i < (useSplitAngle ? 5 : 4); i++) for (int i = 0; i < (useSplitAngle ? 5 : 4); i++)
@ -1086,7 +964,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
if (i == 4) if (i == 4)
{ {
auto usedSectorPlane = useCeilingLedge ? block->GetSurfaceTriangleID(eX, eZ, false) : block->GetSurfaceTriangleID(eX, eZ, true); auto usedSectorPlane = useCeilingLedge ? sector->GetSurfaceTriangleID(eX, eZ, false) : sector->GetSurfaceTriangleID(eX, eZ, true);
result[p] = FROM_RAD(splitAngle) + ANGLE(usedSectorPlane * 180.0f) + ANGLE(90.0f); result[p] = FROM_RAD(splitAngle) + ANGLE(usedSectorPlane * 180.0f) + ANGLE(90.0f);
} }
else else
@ -1171,7 +1049,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
FloorInfo* GetFloor(int x, int y, int z, short* roomNumber) FloorInfo* GetFloor(int x, int y, int z, short* roomNumber)
{ {
const auto location = GetRoomVector(RoomVector(*roomNumber, y), Vector3i(x, y, z)); auto location = GetRoomVector(RoomVector(*roomNumber, y), Vector3i(x, y, z));
*roomNumber = location.RoomNumber; *roomNumber = location.RoomNumber;
return &GetFloor(*roomNumber, x, z); return &GetFloor(*roomNumber, x, z);
} }
@ -1188,48 +1066,48 @@ int GetCeiling(FloorInfo* floor, int x, int y, int z)
int GetDistanceToFloor(int itemNumber, bool precise) int GetDistanceToFloor(int itemNumber, bool precise)
{ {
auto* item = &g_Level.Items[itemNumber]; const auto& item = g_Level.Items[itemNumber];
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(item);
// HACK: Remove item from bridge objects temporarily. // HACK: Remove item from bridge objects temporarily.
pointColl.Block->RemoveBridge(itemNumber); pointColl.GetSector().RemoveBridge(itemNumber);
int height = GetFloorHeight(pointColl.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z); int height = GetFloorHeight(&pointColl.GetSector(), item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z);
pointColl.Block->AddBridge(itemNumber); pointColl.GetSector().AddBridge(itemNumber);
auto bounds = GameBoundingBox(item); auto bounds = GameBoundingBox(&item);
int minHeight = precise ? bounds.Y2 : 0; int minHeight = precise ? bounds.Y2 : 0;
return (minHeight + item->Pose.Position.y - height); return (minHeight + item.Pose.Position.y - height);
} }
int GetWaterSurface(int x, int y, int z, short roomNumber) int GetWaterSurface(int x, int y, int z, short roomNumber)
{ {
auto* room = &g_Level.Rooms[roomNumber]; auto* room = &g_Level.Rooms[roomNumber];
FloorInfo* floor = GetSector(room, x - room->x, z - room->z); auto* sector = GetSector(room, x - room->x, z - room->z);
if (TestEnvironment(ENV_FLAG_WATER, room)) if (TestEnvironment(ENV_FLAG_WATER, room))
{ {
while (floor->GetNextRoomNumber(Vector3i(x, y, z), false).has_value()) while (sector->GetNextRoomNumber(Vector3i(x, y, z), false).has_value())
{ {
room = &g_Level.Rooms[floor->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(floor->RoomNumber)]; room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room)) if (!TestEnvironment(ENV_FLAG_WATER, room))
return (floor->GetSurfaceHeight(x, z, false)); return (sector->GetSurfaceHeight(x, z, false));
floor = GetSector(room, x - room->x, z - room->z); sector = GetSector(room, x - room->x, z - room->z);
} }
return NO_HEIGHT; return NO_HEIGHT;
} }
else else
{ {
while (floor->GetNextRoomNumber(Vector3i(x, y, z), true).has_value()) while (sector->GetNextRoomNumber(Vector3i(x, y, z), true).has_value())
{ {
room = &g_Level.Rooms[floor->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(floor->RoomNumber)]; room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room)) if (TestEnvironment(ENV_FLAG_WATER, room))
return (floor->GetSurfaceHeight(x, z, true)); return (sector->GetSurfaceHeight(x, z, true));
floor = GetSector(room, x - room->x, z - room->z); sector = GetSector(room, x - room->x, z - room->z);
} }
} }
@ -1243,81 +1121,93 @@ int GetWaterSurface(ItemInfo* item)
int GetWaterDepth(int x, int y, int z, short roomNumber) int GetWaterDepth(int x, int y, int z, short roomNumber)
{ {
FloorInfo* floor; FloorInfo* sector = nullptr;
auto* room = &g_Level.Rooms[roomNumber]; auto* room = &g_Level.Rooms[roomNumber];
short roomIndex = NO_VALUE; int adjoiningRoomNumber = NO_VALUE;
do do
{ {
int zFloor = (z - room->z) / BLOCK(1);
int xFloor = (x - room->x) / BLOCK(1); int xFloor = (x - room->x) / BLOCK(1);
int zFloor = (z - room->z) / BLOCK(1);
if (zFloor <= 0) if (zFloor <= 0)
{ {
zFloor = 0; zFloor = 0;
if (xFloor < 1) if (xFloor < 1)
{
xFloor = 1; xFloor = 1;
else if (xFloor > room->xSize - 2) }
else if (xFloor > (room->xSize - 2))
{
xFloor = room->xSize - 2; xFloor = room->xSize - 2;
}
} }
else if (zFloor >= room->zSize - 1) else if (zFloor >= (room->zSize - 1))
{ {
zFloor = room->zSize - 1; zFloor = room->zSize - 1;
if (xFloor < 1) if (xFloor < 1)
{
xFloor = 1; xFloor = 1;
else if (xFloor > room->xSize - 2) }
else if (xFloor > (room->xSize - 2))
{
xFloor = room->xSize - 2; xFloor = room->xSize - 2;
}
} }
else if (xFloor < 0) else if (xFloor < 0)
xFloor = 0;
else if (xFloor >= room->xSize)
xFloor = room->xSize - 1;
floor = &room->floor[zFloor + xFloor * room->zSize];
roomIndex = floor->SidePortalRoomNumber;
if (roomIndex != NO_VALUE)
{ {
roomNumber = roomIndex; xFloor = 0;
room = &g_Level.Rooms[roomIndex]; }
else if (xFloor >= room->xSize)
{
xFloor = room->xSize - 1;
}
sector = &room->floor[zFloor + (xFloor * room->zSize)];
adjoiningRoomNumber = sector->SidePortalRoomNumber;
if (adjoiningRoomNumber != NO_VALUE)
{
roomNumber = adjoiningRoomNumber;
room = &g_Level.Rooms[adjoiningRoomNumber];
} }
} }
while (roomIndex != NO_VALUE); while (adjoiningRoomNumber != NO_VALUE);
if (TestEnvironment(ENV_FLAG_WATER, room) || if (TestEnvironment(ENV_FLAG_WATER, room) ||
TestEnvironment(ENV_FLAG_SWAMP, room)) TestEnvironment(ENV_FLAG_SWAMP, room))
{ {
while (floor->GetNextRoomNumber(Vector3i(x, y, z), false).has_value()) while (sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(NO_VALUE) != NO_VALUE)
{ {
room = &g_Level.Rooms[floor->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(floor->RoomNumber)]; room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room) && if (!TestEnvironment(ENV_FLAG_WATER, room) &&
!TestEnvironment(ENV_FLAG_SWAMP, room)) !TestEnvironment(ENV_FLAG_SWAMP, room))
{ {
int waterHeight = floor->GetSurfaceHeight(x, z, false); int waterHeight = sector->GetSurfaceHeight(x, z, false);
int floorHeight = GetCollision(floor, x, y, z).BottomBlock->GetSurfaceHeight(x, z, true); int floorHeight = GetPointCollision(Vector3i(x, y, z), sector->RoomNumber).GetBottomSector().GetSurfaceHeight(x, z, true);
return (floorHeight - waterHeight); return (floorHeight - waterHeight);
} }
floor = GetSector(room, x - room->x, z - room->z); sector = GetSector(room, x - room->x, z - room->z);
} }
return DEEP_WATER; return DEEP_WATER;
} }
else else
{ {
while (floor->GetNextRoomNumber(Vector3i(x, y, z), true).has_value()) while (sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(NO_VALUE) != NO_VALUE)
{ {
room = &g_Level.Rooms[floor->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(floor->RoomNumber)]; room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room) || if (TestEnvironment(ENV_FLAG_WATER, room) ||
TestEnvironment(ENV_FLAG_SWAMP, room)) TestEnvironment(ENV_FLAG_SWAMP, room))
{ {
int waterHeight = floor->GetSurfaceHeight(x, z, true); int waterHeight = sector->GetSurfaceHeight(x, z, true);
floor = GetFloor(x, y, z, &roomNumber); sector = GetFloor(x, y, z, &roomNumber);
return (GetFloorHeight(floor, x, y, z) - waterHeight); return (GetFloorHeight(sector, x, y, z) - waterHeight);
} }
floor = GetSector(room, x - room->x, z - room->z); sector = GetSector(room, x - room->x, z - room->z);
} }
return NO_HEIGHT; return NO_HEIGHT;
@ -1331,10 +1221,10 @@ int GetWaterDepth(ItemInfo* item)
int GetWaterHeight(int x, int y, int z, short roomNumber) int GetWaterHeight(int x, int y, int z, short roomNumber)
{ {
FloorInfo* sector = nullptr;
auto* room = &g_Level.Rooms[roomNumber]; auto* room = &g_Level.Rooms[roomNumber];
FloorInfo* floor;
short adjoiningRoom = NO_VALUE; int adjoiningRoomNumber = NO_VALUE;
do do
{ {
int xBlock = (x - room->x) / BLOCK(1); int xBlock = (x - room->x) / BLOCK(1);
@ -1344,66 +1234,83 @@ int GetWaterHeight(int x, int y, int z, short roomNumber)
{ {
zBlock = 0; zBlock = 0;
if (xBlock < 1) if (xBlock < 1)
{
xBlock = 1; xBlock = 1;
else if (xBlock > room->xSize - 2) }
else if (xBlock > (room->xSize - 2))
{
xBlock = room->xSize - 2; xBlock = room->xSize - 2;
}
} }
else if (zBlock >= room->zSize - 1) else if (zBlock >= (room->zSize - 1))
{ {
zBlock = room->zSize - 1; zBlock = room->zSize - 1;
if (xBlock < 1) if (xBlock < 1)
{
xBlock = 1; xBlock = 1;
else if (xBlock > room->xSize - 2) }
else if (xBlock > (room->xSize - 2))
{
xBlock = room->xSize - 2; xBlock = room->xSize - 2;
}
} }
else if (xBlock < 0) else if (xBlock < 0)
xBlock = 0;
else if (xBlock >= room->xSize)
xBlock = room->xSize - 1;
floor = &room->floor[zBlock + xBlock * room->zSize];
adjoiningRoom = floor->SidePortalRoomNumber;
if (adjoiningRoom != NO_VALUE)
{ {
roomNumber = adjoiningRoom; xBlock = 0;
room = &g_Level.Rooms[adjoiningRoom]; }
else if (xBlock >= room->xSize)
{
xBlock = room->xSize - 1;
} }
} while (adjoiningRoom != NO_VALUE);
if (floor->IsWall(x, z)) sector = &room->floor[zBlock + (xBlock * room->zSize)];
adjoiningRoomNumber = sector->SidePortalRoomNumber;
if (adjoiningRoomNumber != NO_VALUE)
{
roomNumber = adjoiningRoomNumber;
room = &g_Level.Rooms[adjoiningRoomNumber];
}
}
while (adjoiningRoomNumber != NO_VALUE);
if (sector->IsWall(x, z))
return NO_HEIGHT; return NO_HEIGHT;
if (TestEnvironment(ENV_FLAG_WATER, room) || if (TestEnvironment(ENV_FLAG_WATER, room) ||
TestEnvironment(ENV_FLAG_SWAMP, room)) TestEnvironment(ENV_FLAG_SWAMP, room))
{ {
while (floor->GetNextRoomNumber(Vector3i(x, y, z), false).has_value()) while (sector->GetNextRoomNumber(Vector3i(x, y, z), false).has_value())
{ {
auto* room = &g_Level.Rooms[floor->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(floor->RoomNumber)]; auto* room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room) && if (!TestEnvironment(ENV_FLAG_WATER, room) &&
!TestEnvironment(ENV_FLAG_SWAMP, room)) !TestEnvironment(ENV_FLAG_SWAMP, room))
{
break; break;
}
floor = GetSector(room, x - room->x, z - room->z); sector = GetSector(room, x - room->x, z - room->z);
} }
return GetCollision(floor, x, y, z).Block->GetSurfaceHeight(Vector3i(x, y, z), false); return sector->GetSurfaceHeight(Vector3i(x, y, z), false);
} }
else if (floor->GetNextRoomNumber(Vector3i(x, y, z), true).has_value()) else if (sector->GetNextRoomNumber(Vector3i(x, y, z), true).has_value())
{ {
while (floor->GetNextRoomNumber(Vector3i(x, y, z), true).has_value()) while (sector->GetNextRoomNumber(Vector3i(x, y, z), true).has_value())
{ {
auto* room2 = &g_Level.Rooms[floor->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(floor->RoomNumber)]; auto* room2 = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room2) || if (TestEnvironment(ENV_FLAG_WATER, room2) ||
TestEnvironment(ENV_FLAG_SWAMP, room2)) TestEnvironment(ENV_FLAG_SWAMP, room2))
{
break; break;
}
floor = GetSector(room2, x - room2->x, z - room2->z); sector = GetSector(room2, x - room2->x, z - room2->z);
} }
return GetCollision(floor, x, y, z).Block->GetSurfaceHeight(Vector3i(x, y, z), true); return sector->GetSurfaceHeight(Vector3i(x, y, z), true);
} }
return NO_HEIGHT; return NO_HEIGHT;
@ -1416,12 +1323,12 @@ int GetWaterHeight(ItemInfo* item)
bool TestEnvironment(RoomEnvFlags environmentType, int x, int y, int z, int roomNumber) bool TestEnvironment(RoomEnvFlags environmentType, int x, int y, int z, int roomNumber)
{ {
return TestEnvironment(environmentType, GetCollision(x, y, z, roomNumber).RoomNumber); return TestEnvironment(environmentType, GetPointCollision(Vector3i(x, y, z), roomNumber).GetRoomNumber());
} }
bool TestEnvironment(RoomEnvFlags environmentType, Vector3i pos, int roomNumber) bool TestEnvironment(RoomEnvFlags environmentType, const Vector3i& pos, int roomNumber)
{ {
return TestEnvironment(environmentType, GetCollision(pos.x, pos.y, pos.z, roomNumber).RoomNumber); return TestEnvironment(environmentType, GetPointCollision(pos, roomNumber).GetRoomNumber());
} }
bool TestEnvironment(RoomEnvFlags environmentType, const ItemInfo* item) bool TestEnvironment(RoomEnvFlags environmentType, const ItemInfo* item)

View file

@ -1,18 +1,24 @@
#pragma once #pragma once
#include "Game/collision/floordata.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/game_object_ids.h" #include "Objects/game_object_ids.h"
struct ItemInfo;
class FloorInfo;
struct ROOM_INFO;
struct MESH_INFO;
enum RoomEnvFlags; enum RoomEnvFlags;
class FloorInfo;
struct ItemInfo;
struct MESH_INFO;
struct ROOM_INFO;
using namespace TEN::Collision::Floordata;
using namespace TEN::Math;
constexpr auto NO_LOWER_BOUND = -NO_HEIGHT; // Used by coll->Setup.LowerFloorBound. constexpr auto NO_LOWER_BOUND = -NO_HEIGHT; // Used by coll->Setup.LowerFloorBound.
constexpr auto NO_UPPER_BOUND = NO_HEIGHT; // Used by coll->Setup.UpperFloorBound. constexpr auto NO_UPPER_BOUND = NO_HEIGHT; // Used by coll->Setup.UpperFloorBound.
constexpr auto COLLISION_CHECK_DISTANCE = BLOCK(8); constexpr auto COLLISION_CHECK_DISTANCE = BLOCK(8);
constexpr auto DEFAULT_STEEP_FLOOR_SLOPE_ANGLE = ANGLE(36.0f);
constexpr auto DEFAULT_STEEP_CEILING_SLOPE_ANGLE = ANGLE(45.0f);
enum class CollisionType enum class CollisionType
{ {
None, None,
@ -38,58 +44,46 @@ enum class CornerType
Outer Outer
}; };
struct CollisionPosition struct CollisionPositionData
{ {
int Floor; int Floor = 0;
int Ceiling; int Ceiling = 0;
int Bridge; int Bridge = 0;
float SplitAngle; short SplitAngle = 0;
bool FloorSlope; bool FloorSlope = false;
bool CeilingSlope; bool CeilingSlope = false;
bool DiagonalStep; bool DiagonalStep = false;
bool HasDiagonalSplit() { return ((SplitAngle == (45.0f * RADIAN)) || (SplitAngle == (135.0f * RADIAN))); } bool HasDiagonalSplit() { return ((SplitAngle == SectorSurfaceData::SPLIT_ANGLE_0) || (SplitAngle == SectorSurfaceData::SPLIT_ANGLE_1)); }
bool HasFlippedDiagonalSplit() { return (HasDiagonalSplit() && (SplitAngle != (45.0f * RADIAN))); } bool HasFlippedDiagonalSplit() { return (HasDiagonalSplit() && (SplitAngle != SectorSurfaceData::SPLIT_ANGLE_0)); }
}; };
struct CollisionResult struct CollisionSetupData
{ {
Vector3i Coordinates; // Collider parameters
int RoomNumber; CollisionProbeMode Mode = CollisionProbeMode::Quadrants;
int Radius = 0;
int Height = 0;
short ForwardAngle = 0;
FloorInfo* Block; // Borderline step heights
FloorInfo* BottomBlock; int LowerFloorBound = 0;
int UpperFloorBound = 0;
int LowerCeilingBound = 0;
int UpperCeilingBound = 0;
CollisionPosition Position; // Blocker flags
Vector2 FloorTilt; // x = x, y = z bool BlockFloorSlopeUp = false;
Vector2 CeilingTilt; // x = x, y = z bool BlockFloorSlopeDown = false;
bool BlockCeilingSlope = false;
Vector3 FloorNormal; bool BlockDeathFloorDown = false;
Vector3 CeilingNormal; bool BlockMonkeySwingEdge = false;
};
struct CollisionSetup
{
CollisionProbeMode Mode; // Probe rotation mode
int Radius; // Collision bounds horizontal size
int Height; // Collision bounds vertical size
short ForwardAngle; // Forward angle direction
int LowerFloorBound; // Borderline floor step-up height
int UpperFloorBound; // Borderline floor step-down height
int LowerCeilingBound; // Borderline ceiling step-up height
int UpperCeilingBound; // Borderline ceiling step-down height
bool BlockFloorSlopeUp; // Treat steep slopes as walls
bool BlockFloorSlopeDown; // Treat steep slopes as pits
bool BlockCeilingSlope; // Treat steep slopes on ceilings as walls
bool BlockDeathFloorDown; // Treat death sectors as pits
bool BlockMonkeySwingEdge; // Treat non-monkey sectors as walls
bool EnableObjectPush; // Can be pushed by objects // Inquirers
bool EnableSpasm; // Convulse when pushed bool EnableObjectPush = false;
bool EnableSpasm = false;
// Preserve previous parameters to restore later. // Previous parameters
Vector3i PrevPosition = Vector3i::Zero; Vector3i PrevPosition = Vector3i::Zero;
GAME_OBJECT_ID PrevAnimObjectID = ID_NO_OBJECT; GAME_OBJECT_ID PrevAnimObjectID = ID_NO_OBJECT;
int PrevAnimNumber = 0; int PrevAnimNumber = 0;
@ -99,51 +93,42 @@ struct CollisionSetup
struct CollisionInfo struct CollisionInfo
{ {
CollisionSetup Setup; // In parameters CollisionSetupData Setup = {};
CollisionPosition Middle; CollisionPositionData Middle = {};
CollisionPosition MiddleLeft; CollisionPositionData MiddleLeft = {};
CollisionPosition MiddleRight; CollisionPositionData MiddleRight = {};
CollisionPosition Front; CollisionPositionData Front = {};
CollisionPosition FrontLeft; CollisionPositionData FrontLeft = {};
CollisionPosition FrontRight; CollisionPositionData FrontRight = {};
Pose Shift = Pose::Zero; CollisionType CollisionType = CollisionType::None;
CollisionType CollisionType; Pose Shift = Pose::Zero;
Vector3 FloorNormal;
Vector3 CeilingNormal;
Vector2 FloorTilt; // x = x, y = z
Vector2 CeilingTilt; // x = x, y = z
short NearestLedgeAngle;
float NearestLedgeDistance;
int LastBridgeItemNumber; Vector3 FloorNormal = Vector3::Zero;
Pose LastBridgeItemPose; Vector3 CeilingNormal = Vector3::Zero;
Vector2 FloorTilt = Vector2::Zero; // NOTE: Deprecated.
Vector2 CeilingTilt = Vector2::Zero; // NOTE: Deprecated.
short NearestLedgeAngle = 0;
float NearestLedgeDistance = 0.0f;
bool HitStatic; int LastBridgeItemNumber = 0;
bool HitTallObject; Pose LastBridgeItemPose = Pose::Zero;
bool TriangleAtRight() { return ((MiddleRight.SplitAngle != 0.0f) && (MiddleRight.SplitAngle == Middle.SplitAngle)); } bool HitStatic = false;
bool TriangleAtLeft() { return ((MiddleLeft.SplitAngle != 0.0f) && (MiddleLeft.SplitAngle == Middle.SplitAngle)); } bool HitTallObject = false;
// Inquirers.
bool TriangleAtRight() { return ((MiddleRight.SplitAngle != 0) && (MiddleRight.SplitAngle == Middle.SplitAngle)); }
bool TriangleAtLeft() { return ((MiddleLeft.SplitAngle != 0) && (MiddleLeft.SplitAngle == Middle.SplitAngle)); }
bool DiagonalStepAtRight() { return (MiddleRight.DiagonalStep && TriangleAtRight() && (NearestLedgeAngle % ANGLE(90.0f))); } bool DiagonalStepAtRight() { return (MiddleRight.DiagonalStep && TriangleAtRight() && (NearestLedgeAngle % ANGLE(90.0f))); }
bool DiagonalStepAtLeft() { return (MiddleLeft.DiagonalStep && TriangleAtLeft() && (NearestLedgeAngle % ANGLE(90.0f))); } bool DiagonalStepAtLeft() { return (MiddleLeft.DiagonalStep && TriangleAtLeft() && (NearestLedgeAngle % ANGLE(90.0f))); }
}; };
[[nodiscard]] bool TestItemRoomCollisionAABB(ItemInfo* item); [[nodiscard]] bool TestItemRoomCollisionAABB(ItemInfo* item);
CollisionResult GetCollision(const ItemInfo& item);
CollisionResult GetCollision(const ItemInfo* item);
CollisionResult GetCollision(const ItemInfo* item, short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
CollisionResult GetCollision(const Vector3i& pos, int roomNumber, short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
CollisionResult GetCollision(const Vector3i& pos, int roomNumber, const EulerAngles& orient, float dist);
CollisionResult GetCollision(const Vector3i& pos, int roomNumber, const Vector3& dir, float dist);
CollisionResult GetCollision(const Vector3i& pos, int roomNumber);
CollisionResult GetCollision(int x, int y, int z, short roomNumber);
CollisionResult GetCollision(const GameVector& pos);
CollisionResult GetCollision(FloorInfo* floor, int x, int y, int z);
void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offset, bool resetRoom = false);
void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, bool resetRoom = false); void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, bool resetRoom = false);
void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offset, bool resetRoom = false);
int GetQuadrant(short angle); int GetQuadrant(short angle);
short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance); short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance);
@ -167,10 +152,10 @@ void SnapItemToGrid(ItemInfo* item, CollisionInfo* coll);
void AlignEntityToSurface(ItemInfo* item, const Vector2& ellipse, float alpha = 0.75f, short constraintAngle = ANGLE(70.0f)); void AlignEntityToSurface(ItemInfo* item, const Vector2& ellipse, float alpha = 0.75f, short constraintAngle = ANGLE(70.0f));
// TODO: Deprecated.
bool TestEnvironment(RoomEnvFlags environmentType, int x, int y, int z, int roomNumber); bool TestEnvironment(RoomEnvFlags environmentType, int x, int y, int z, int roomNumber);
bool TestEnvironment(RoomEnvFlags environmentType, Vector3i pos, int roomNumber); bool TestEnvironment(RoomEnvFlags environmentType, const Vector3i& pos, int roomNumber);
bool TestEnvironment(RoomEnvFlags environmentType, const ItemInfo* item); bool TestEnvironment(RoomEnvFlags environmentType, const ItemInfo* item);
bool TestEnvironment(RoomEnvFlags environmentType, int roomNumber); bool TestEnvironment(RoomEnvFlags environmentType, int roomNumber);
bool TestEnvironment(RoomEnvFlags environmentType, ROOM_INFO* room); bool TestEnvironment(RoomEnvFlags environmentType, ROOM_INFO* room);
bool TestEnvironmentFlags(RoomEnvFlags environmentType, int flags); bool TestEnvironmentFlags(RoomEnvFlags environmentType, int flags);

View file

@ -1,6 +1,8 @@
#include "framework.h" #include "framework.h"
#include "Game/collision/floordata.h" #include "Game/collision/floordata.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/Point.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h" #include "Game/Setup.h"
@ -11,6 +13,7 @@
#include "Specific/trutils.h" #include "Specific/trutils.h"
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Utils; using namespace TEN::Utils;
@ -59,7 +62,7 @@ Vector3 FloorInfo::GetSurfaceNormal(int x, int z, bool isFloor) const
short FloorInfo::GetSurfaceIllegalSlopeAngle(int x, int z, bool isFloor) const short FloorInfo::GetSurfaceIllegalSlopeAngle(int x, int z, bool isFloor) const
{ {
const auto& tri = GetSurfaceTriangle(x, z, isFloor); const auto& tri = GetSurfaceTriangle(x, z, isFloor);
return tri.IllegalSlopeAngle; return tri.SteepSlopeAngle;
} }
MaterialType FloorInfo::GetSurfaceMaterial(int x, int z, bool isFloor) const MaterialType FloorInfo::GetSurfaceMaterial(int x, int z, bool isFloor) const
@ -421,22 +424,22 @@ namespace TEN::Collision::Floordata
return roomGridCoords; return roomGridCoords;
} }
std::vector<FloorInfo*> GetNeighborSectorPtrs(const Vector3i& pos, int roomNumber, unsigned int searchDepth) std::vector<FloorInfo*> GetNeighborSectors(const Vector3i& pos, int roomNumber, unsigned int searchDepth)
{ {
auto sectorPtrs = std::vector<FloorInfo*>{}; auto sectors = std::vector<FloorInfo*>{};
// Run through neighbor rooms. // Run through neighbor rooms.
auto& room = g_Level.Rooms[roomNumber]; auto& room = g_Level.Rooms[roomNumber];
for (int neighborRoomNumber : room.neighbors) for (int neighborRoomNumber : room.neighbors)
{ {
// Collect neighbor sector pointers. // Collect neighbor sectors.
auto roomGridCoords = GetNeighborRoomGridCoords(pos, neighborRoomNumber, searchDepth); auto roomGridCoords = GetNeighborRoomGridCoords(pos, neighborRoomNumber, searchDepth);
for (const auto& roomGridCoord : roomGridCoords) for (const auto& roomGridCoord : roomGridCoords)
sectorPtrs.push_back(&GetFloor(neighborRoomNumber, roomGridCoord)); sectors.push_back(&GetFloor(neighborRoomNumber, roomGridCoord));
} }
// Return neighbor sector pointers. // Return neighbor sectors.
return sectorPtrs; return sectors;
} }
FloorInfo& GetFloor(int roomNumber, const Vector2i& roomGridCoord) FloorInfo& GetFloor(int roomNumber, const Vector2i& roomGridCoord)
@ -455,89 +458,96 @@ namespace TEN::Collision::Floordata
FloorInfo& GetFarthestSector(int roomNumber, int x, int z, bool isBottom) FloorInfo& GetFarthestSector(int roomNumber, int x, int z, bool isBottom)
{ {
auto* sectorPtr = &GetSideSector(roomNumber, x, z); auto* sector = &GetSideSector(roomNumber, x, z);
// Find bottom or top sector. // Find bottom or top sector.
bool isWall = sectorPtr->IsWall(x, z); bool isWall = sector->IsWall(x, z);
while (isWall) while (isWall)
{ {
auto nextRoomNumber = sectorPtr->GetNextRoomNumber(x, z, isBottom); auto nextRoomNumber = sector->GetNextRoomNumber(x, z, isBottom);
if (!nextRoomNumber.has_value()) if (!nextRoomNumber.has_value())
break; break;
// TODO: Check. // TODO: Check.
sectorPtr = &GetSideSector(*nextRoomNumber, x, z); sector = &GetSideSector(*nextRoomNumber, x, z);
isWall = sectorPtr->IsWall(x, z); isWall = sector->IsWall(x, z);
} }
return *sectorPtr; return *sector;
} }
FloorInfo& GetSideSector(int roomNumber, int x, int z) FloorInfo& GetSideSector(int roomNumber, int x, int z)
{ {
auto* sectorPtr = &GetFloor(roomNumber, x, z); auto* sector = &GetFloor(roomNumber, x, z);
// Find side sector. // Find side sector.
auto sideRoomNumber = sectorPtr->GetSideRoomNumber(); auto sideRoomNumber = sector->GetSideRoomNumber();
while (sideRoomNumber.has_value()) while (sideRoomNumber.has_value())
{ {
sectorPtr = &GetFloor(*sideRoomNumber, x, z); sector = &GetFloor(*sideRoomNumber, x, z);
sideRoomNumber = sectorPtr->GetSideRoomNumber(); sideRoomNumber = sector->GetSideRoomNumber();
} }
return *sectorPtr; return *sector;
} }
static std::optional<FarthestHeightData> GetFarthestHeightData(FloorInfo& currentSector, Vector3i pos, bool isBottom) static std::optional<FarthestHeightData> GetFarthestHeightData(FloorInfo& currentSector, Vector3i pos, bool isBottom)
{ {
// Find bottom or top height while bridge exists(?). // Find bottom or top height while bridge exists(?).
auto* sectorPtr = &currentSector; auto* sector = &currentSector;
do do
{ {
// Set vertical position to lowest bridge ceiling height or highest bridge floor height. // Set vertical position to lowest bridge ceiling height or highest bridge floor height.
pos.y = sectorPtr->GetBridgeSurfaceHeight(pos, !isBottom); pos.y = sector->GetBridgeSurfaceHeight(pos, !isBottom);
// Find sector at lowest bridge floor height or highest bridge ceiling height. // Find sector at lowest bridge floor height or highest bridge ceiling height.
while (isBottom ? while (isBottom ?
(pos.y >= sectorPtr->GetSurfaceHeight(pos.x, pos.z, true)) : (pos.y >= sector->GetSurfaceHeight(pos.x, pos.z, true)) :
(pos.y <= sectorPtr->GetSurfaceHeight(pos.x, pos.z, false))) (pos.y <= sector->GetSurfaceHeight(pos.x, pos.z, false)))
{ {
auto nextRoomNumber = sectorPtr->GetNextRoomNumber(pos.x, pos.z, isBottom); auto nextRoomNumber = sector->GetNextRoomNumber(pos.x, pos.z, isBottom);
if (!nextRoomNumber.has_value()) if (!nextRoomNumber.has_value())
return std::nullopt; return std::nullopt;
sectorPtr = &GetSideSector(*nextRoomNumber, pos.x, pos.z); sector = &GetSideSector(*nextRoomNumber, pos.x, pos.z);
} }
} }
while (sectorPtr->GetInsideBridgeItemNumber(pos, isBottom, !isBottom) != NO_VALUE); while (sector->GetInsideBridgeItemNumber(pos, isBottom, !isBottom) != NO_VALUE);
return FarthestHeightData{ *sectorPtr, pos.y }; return FarthestHeightData{ *sector, pos.y };
} }
std::optional<int> GetSurfaceHeight(const RoomVector& location, int x, int z, bool isFloor) std::optional<int> GetSurfaceHeight(const RoomVector& location, int x, int z, bool isFloor)
{ {
auto* sectorPtr = &GetSideSector(location.RoomNumber, x, z); enum class Polarity
{
None,
Floor,
Ceiling
};
auto* sector = &GetSideSector(location.RoomNumber, x, z);
auto pos = Vector3i(x, location.Height, z); auto pos = Vector3i(x, location.Height, z);
int polarity = 0; auto polarity = Polarity::None;
if (sectorPtr->IsWall(x, z)) if (sector->IsWall(x, z))
{ {
sectorPtr = &GetFarthestSector(location.RoomNumber, x, z, !isFloor); sector = &GetFarthestSector(location.RoomNumber, x, z, !isFloor);
if (!sectorPtr->IsWall(x, z)) if (!sector->IsWall(x, z))
{ {
pos.y = sectorPtr->GetSurfaceHeight(x, z, isFloor); pos.y = sector->GetSurfaceHeight(x, z, isFloor);
polarity = isFloor ? -1 : 1; polarity = isFloor ? Polarity::Floor : Polarity::Ceiling;
} }
else else
{ {
sectorPtr = &GetFarthestSector(location.RoomNumber, x, z, isFloor); sector = &GetFarthestSector(location.RoomNumber, x, z, isFloor);
if (!sectorPtr->IsWall(x, z)) if (!sector->IsWall(x, z))
{ {
pos.y = sectorPtr->GetSurfaceHeight(x, z, !isFloor); pos.y = sector->GetSurfaceHeight(x, z, !isFloor);
polarity = isFloor ? 1 : -1; polarity = isFloor ? Polarity::Ceiling : Polarity::Floor;
} }
else else
{ {
@ -546,81 +556,81 @@ namespace TEN::Collision::Floordata
} }
} }
int floorHeight = sectorPtr->GetSurfaceHeight(pos, true); int floorHeight = sector->GetSurfaceHeight(pos, true);
int ceilingHeight = sectorPtr->GetSurfaceHeight(pos, false); int ceilingHeight = sector->GetSurfaceHeight(pos, false);
pos.y = std::clamp(pos.y, std::min(floorHeight, ceilingHeight), std::max(floorHeight, ceilingHeight)); pos.y = std::clamp(pos.y, std::min(floorHeight, ceilingHeight), std::max(floorHeight, ceilingHeight));
bool testFloorBorder = (pos.y == ceilingHeight); bool testFloorBorder = (pos.y == ceilingHeight);
bool testCeilBorder = (pos.y == floorHeight); bool testCeilBorder = (pos.y == floorHeight);
int insideBridgeItemNumber = sectorPtr->GetInsideBridgeItemNumber(pos, testFloorBorder, testCeilBorder); int insideBridgeItemNumber = sector->GetInsideBridgeItemNumber(pos, testFloorBorder, testCeilBorder);
if (insideBridgeItemNumber != NO_VALUE) if (insideBridgeItemNumber != NO_VALUE)
{ {
if (isFloor ? (polarity <= 0) : (polarity >= 0)) if (polarity == Polarity::None || (isFloor ? (polarity == Polarity::Floor) : (polarity == Polarity::Ceiling)))
{ {
auto heightData = GetFarthestHeightData(*sectorPtr, pos, !isFloor); auto heightData = GetFarthestHeightData(*sector, pos, !isFloor);
if (heightData.has_value()) if (heightData.has_value())
return heightData->Height; return heightData->Height;
} }
if (isFloor ? (polarity >= 0) : (polarity <= 0)) if (polarity == Polarity::None || (isFloor ? (polarity == Polarity::Ceiling) : (polarity == Polarity::Floor)))
{ {
auto heightData = GetFarthestHeightData(*sectorPtr, pos, isFloor); auto heightData = GetFarthestHeightData(*sector, pos, isFloor);
if (!heightData.has_value()) if (!heightData.has_value())
return std::nullopt; return std::nullopt;
sectorPtr = &heightData->Sector; sector = &heightData->Sector;
pos.y = heightData->Height; pos.y = heightData->Height;
} }
} }
if (isFloor ? (polarity >= 0) : (polarity <= 0)) if (polarity == Polarity::None || (isFloor ? (polarity == Polarity::Ceiling) : (polarity == Polarity::Floor)))
{ {
auto nextRoomNumber = sectorPtr->GetNextRoomNumber(pos, isFloor); auto nextRoomNumber = sector->GetNextRoomNumber(pos, isFloor);
while (nextRoomNumber.has_value()) while (nextRoomNumber.has_value())
{ {
sectorPtr = &GetSideSector(*nextRoomNumber, x, z); sector = &GetSideSector(*nextRoomNumber, x, z);
nextRoomNumber = sectorPtr->GetNextRoomNumber(pos, isFloor); nextRoomNumber = sector->GetNextRoomNumber(pos, isFloor);
} }
} }
return sectorPtr->GetSurfaceHeight(pos, isFloor); return sector->GetSurfaceHeight(pos, isFloor);
} }
static std::optional<RoomVector> GetFarthestRoomVector(RoomVector location, const Vector3i& pos, bool isBottom) static std::optional<RoomVector> GetFarthestRoomVector(RoomVector location, const Vector3i& pos, bool isBottom)
{ {
auto* sectorPtr = &GetSideSector(location.RoomNumber, pos.x, pos.z); auto* sector = &GetSideSector(location.RoomNumber, pos.x, pos.z);
location.RoomNumber = sectorPtr->RoomNumber; location.RoomNumber = sector->RoomNumber;
if (sectorPtr->IsWall(pos.x, pos.z)) if (sector->IsWall(pos.x, pos.z))
{ {
sectorPtr = &GetFarthestSector(location.RoomNumber, pos.x, pos.z, isBottom); sector = &GetFarthestSector(location.RoomNumber, pos.x, pos.z, isBottom);
location.RoomNumber = sectorPtr->RoomNumber; location.RoomNumber = sector->RoomNumber;
if (sectorPtr->IsWall(pos.x, pos.z)) if (sector->IsWall(pos.x, pos.z))
return std::nullopt; return std::nullopt;
location.Height = sectorPtr->GetSurfaceHeight(pos.x, pos.z, !isBottom); location.Height = sector->GetSurfaceHeight(pos.x, pos.z, !isBottom);
} }
int floorHeight = sectorPtr->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), true); int floorHeight = sector->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), true);
int ceilingHeight = sectorPtr->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), false); int ceilingHeight = sector->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), false);
location.Height = std::clamp(location.Height, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight)); location.Height = std::clamp(location.Height, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
bool testFloorBorder = (location.Height == ceilingHeight); bool testFloorBorder = (location.Height == ceilingHeight);
bool testCeilBorder = (location.Height == floorHeight); bool testCeilBorder = (location.Height == floorHeight);
int insideBridgeItemNumber = sectorPtr->GetInsideBridgeItemNumber(Vector3i(pos.x, location.Height, pos.z), testFloorBorder, testCeilBorder); int insideBridgeItemNumber = sector->GetInsideBridgeItemNumber(Vector3i(pos.x, location.Height, pos.z), testFloorBorder, testCeilBorder);
if (insideBridgeItemNumber != NO_VALUE) if (insideBridgeItemNumber != NO_VALUE)
{ {
auto heightData = GetFarthestHeightData(*sectorPtr, Vector3i(pos.x, location.Height, pos.z), isBottom); auto heightData = GetFarthestHeightData(*sector, Vector3i(pos.x, location.Height, pos.z), isBottom);
if (!heightData.has_value()) if (!heightData.has_value())
return std::nullopt; return std::nullopt;
sectorPtr = &heightData->Sector; sector = &heightData->Sector;
location.RoomNumber = sectorPtr->RoomNumber; location.RoomNumber = sector->RoomNumber;
location.Height = heightData->Height; location.Height = heightData->Height;
} }
@ -630,19 +640,19 @@ namespace TEN::Collision::Floordata
{ {
if (!isFirstSector) if (!isFirstSector)
{ {
sectorPtr = &GetSideSector(*nextRoomNumber, pos.x, pos.z); sector = &GetSideSector(*nextRoomNumber, pos.x, pos.z);
location.RoomNumber = sectorPtr->RoomNumber; location.RoomNumber = sector->RoomNumber;
location.Height = sectorPtr->GetSurfaceHeight(pos.x, pos.z, !isBottom); location.Height = sector->GetSurfaceHeight(pos.x, pos.z, !isBottom);
} }
isFirstSector = false; isFirstSector = false;
if (isBottom) if (isBottom)
{ {
ceilingHeight = sectorPtr->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), false); ceilingHeight = sector->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), false);
if (pos.y < ceilingHeight && sectorPtr->GetNextRoomNumber(Vector3i(pos.x, location.Height, pos.z), false)) if (pos.y < ceilingHeight && sector->GetNextRoomNumber(Vector3i(pos.x, location.Height, pos.z), false))
return std::nullopt; return std::nullopt;
floorHeight = sectorPtr->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), true); floorHeight = sector->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), true);
if (pos.y <= floorHeight) if (pos.y <= floorHeight)
{ {
location.Height = std::max(pos.y, ceilingHeight); location.Height = std::max(pos.y, ceilingHeight);
@ -651,11 +661,11 @@ namespace TEN::Collision::Floordata
} }
else else
{ {
floorHeight = sectorPtr->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), true); floorHeight = sector->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), true);
if (pos.y > floorHeight && sectorPtr->GetNextRoomNumber(Vector3i(pos.x, location.Height, pos.z), true)) if (pos.y > floorHeight && sector->GetNextRoomNumber(Vector3i(pos.x, location.Height, pos.z), true))
return std::nullopt; return std::nullopt;
ceilingHeight = sectorPtr->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), false); ceilingHeight = sector->GetSurfaceHeight(Vector3i(pos.x, location.Height, pos.z), false);
if (pos.y >= ceilingHeight) if (pos.y >= ceilingHeight)
{ {
location.Height = std::min(pos.y, floorHeight); location.Height = std::min(pos.y, floorHeight);
@ -663,7 +673,7 @@ namespace TEN::Collision::Floordata
} }
} }
nextRoomNumber = sectorPtr->GetNextRoomNumber(Vector3i(pos.x, location.Height, pos.z), isBottom); nextRoomNumber = sector->GetNextRoomNumber(Vector3i(pos.x, location.Height, pos.z), isBottom);
} }
return std::nullopt; return std::nullopt;
@ -693,34 +703,34 @@ namespace TEN::Collision::Floordata
x += bridgeItem.Pose.Position.x; x += bridgeItem.Pose.Position.x;
z += bridgeItem.Pose.Position.z; z += bridgeItem.Pose.Position.z;
auto* sectorPtr = &GetSideSector(bridgeItem.RoomNumber, x, z); auto* sector = &GetSideSector(bridgeItem.RoomNumber, x, z);
sectorPtr->AddBridge(itemNumber); sector->AddBridge(itemNumber);
if (bridge.GetFloorBorder != nullptr) if (bridge.GetFloorBorder != nullptr)
{ {
int floorBorder = bridge.GetFloorBorder(bridgeItem); int floorBorder = bridge.GetFloorBorder(bridgeItem);
while (floorBorder <= sectorPtr->GetSurfaceHeight(x, z, false)) while (floorBorder <= sector->GetSurfaceHeight(x, z, false))
{ {
auto roomNumberAbove = sectorPtr->GetNextRoomNumber(x, z, false); auto roomNumberAbove = sector->GetNextRoomNumber(x, z, false);
if (!roomNumberAbove.has_value()) if (!roomNumberAbove.has_value())
break; break;
sectorPtr = &GetSideSector(*roomNumberAbove, x, z); sector = &GetSideSector(*roomNumberAbove, x, z);
sectorPtr->AddBridge(itemNumber); sector->AddBridge(itemNumber);
} }
} }
if (bridge.GetCeilingBorder != nullptr) if (bridge.GetCeilingBorder != nullptr)
{ {
int ceilingBorder = bridge.GetCeilingBorder(bridgeItem); int ceilingBorder = bridge.GetCeilingBorder(bridgeItem);
while (ceilingBorder >= sectorPtr->GetSurfaceHeight(x, z, true)) while (ceilingBorder >= sector->GetSurfaceHeight(x, z, true))
{ {
auto roomNumberBelow = sectorPtr->GetNextRoomNumber(x, z, true); auto roomNumberBelow = sector->GetNextRoomNumber(x, z, true);
if (!roomNumberBelow.has_value()) if (!roomNumberBelow.has_value())
break; break;
sectorPtr = &GetSideSector(*roomNumberBelow, x, z); sector = &GetSideSector(*roomNumberBelow, x, z);
sectorPtr->AddBridge(itemNumber); sector->AddBridge(itemNumber);
} }
} }
} }
@ -736,34 +746,34 @@ namespace TEN::Collision::Floordata
x += bridgeItem.Pose.Position.x; x += bridgeItem.Pose.Position.x;
z += bridgeItem.Pose.Position.z; z += bridgeItem.Pose.Position.z;
auto* sectorPtr = &GetSideSector(bridgeItem.RoomNumber, x, z); auto* sector = &GetSideSector(bridgeItem.RoomNumber, x, z);
sectorPtr->RemoveBridge(itemNumber); sector->RemoveBridge(itemNumber);
if (bridge.GetFloorBorder != nullptr) if (bridge.GetFloorBorder != nullptr)
{ {
int floorBorder = bridge.GetFloorBorder(bridgeItem); int floorBorder = bridge.GetFloorBorder(bridgeItem);
while (floorBorder <= sectorPtr->GetSurfaceHeight(x, z, false)) while (floorBorder <= sector->GetSurfaceHeight(x, z, false))
{ {
auto roomNumberAbove = sectorPtr->GetNextRoomNumber(x, z, false); auto roomNumberAbove = sector->GetNextRoomNumber(x, z, false);
if (!roomNumberAbove.has_value()) if (!roomNumberAbove.has_value())
break; break;
sectorPtr = &GetSideSector(*roomNumberAbove, x, z); sector = &GetSideSector(*roomNumberAbove, x, z);
sectorPtr->RemoveBridge(itemNumber); sector->RemoveBridge(itemNumber);
} }
} }
if (bridge.GetCeilingBorder != nullptr) if (bridge.GetCeilingBorder != nullptr)
{ {
int ceilingBorder = bridge.GetCeilingBorder(bridgeItem); int ceilingBorder = bridge.GetCeilingBorder(bridgeItem);
while (ceilingBorder >= sectorPtr->GetSurfaceHeight(x, z, true)) while (ceilingBorder >= sector->GetSurfaceHeight(x, z, true))
{ {
auto roomNumberBelow = sectorPtr->GetNextRoomNumber(x, z, true); auto roomNumberBelow = sector->GetNextRoomNumber(x, z, true);
if (!roomNumberBelow.has_value()) if (!roomNumberBelow.has_value())
break; break;
sectorPtr = &GetSideSector(*roomNumberBelow, x, z); sector = &GetSideSector(*roomNumberBelow, x, z);
sectorPtr->RemoveBridge(itemNumber); sector->RemoveBridge(itemNumber);
} }
} }
} }
@ -776,8 +786,7 @@ namespace TEN::Collision::Floordata
{ {
constexpr auto VERTICAL_MARGIN = 4; constexpr auto VERTICAL_MARGIN = 4;
auto bounds = GameBoundingBox(&item); auto box = GameBoundingBox(&item).ToBoundingOrientedBox(item.Pose);
auto box = bounds.ToBoundingOrientedBox(item.Pose);
auto origin = Vector3(pos.x, pos.y + (useBottomHeight ? VERTICAL_MARGIN : -VERTICAL_MARGIN), pos.z); auto origin = Vector3(pos.x, pos.y + (useBottomHeight ? VERTICAL_MARGIN : -VERTICAL_MARGIN), pos.z);
auto dir = useBottomHeight ? -Vector3::UnitY : Vector3::UnitY; auto dir = useBottomHeight ? -Vector3::UnitY : Vector3::UnitY;
@ -785,7 +794,7 @@ namespace TEN::Collision::Floordata
// Ray intersects box; return bridge box height. // Ray intersects box; return bridge box height.
float dist = 0.0f; float dist = 0.0f;
if (box.Intersects(origin, dir, dist)) if (box.Intersects(origin, dir, dist))
return (item.Pose.Position.y + (useBottomHeight ? bounds.Y2 : bounds.Y1)); return Geometry::TranslatePoint(origin, dir, dist).y;
return std::nullopt; return std::nullopt;
} }
@ -880,7 +889,7 @@ namespace TEN::Collision::Floordata
if (labelPos2D.has_value()) if (labelPos2D.has_value())
{ {
*labelPos2D += Vector2(0.0f, verticalOffset); *labelPos2D += Vector2(0.0f, verticalOffset);
g_Renderer.AddDebugString(string, *labelPos2D, color, LABEL_SCALE, 0, RendererDebugPage::CollisionStats); DrawDebugString(string, *labelPos2D, color, LABEL_SCALE, RendererDebugPage::CollisionStats);
} }
} }
@ -897,7 +906,7 @@ namespace TEN::Collision::Floordata
constexpr auto MINECART_STOP_COLOR = Vector4(0.4f, 1.0f, 1.0f, 1.0f); constexpr auto MINECART_STOP_COLOR = Vector4(0.4f, 1.0f, 1.0f, 1.0f);
// Get point collision. // Get point collision.
auto pointColl = GetCollision(item); auto pointColl = GetPointCollision(item);
auto pos = item.Pose.Position.ToVector3(); auto pos = item.Pose.Position.ToVector3();
// Run through neighboring rooms. // Run through neighboring rooms.
@ -913,50 +922,50 @@ namespace TEN::Collision::Floordata
pos.x = BLOCK(roomGridCoord.x) + neighborRoom.x; pos.x = BLOCK(roomGridCoord.x) + neighborRoom.x;
pos.z = BLOCK(roomGridCoord.y) + neighborRoom.z; pos.z = BLOCK(roomGridCoord.y) + neighborRoom.z;
pointColl = GetCollision(pos, neighborRoomNumber); pointColl = GetPointCollision(pos, neighborRoomNumber);
pos.y = pointColl.Position.Floor; pos.y = pointColl.GetFloorHeight();
float verticalOffset = STRING_SPACING; float verticalOffset = STRING_SPACING;
// Stopper // Stopper
if (pointColl.Block->Stopper) if (pointColl.GetSector().Stopper)
{ {
DrawSectorFlagLabel(pos, "Stopper", STOPPER_COLOR, verticalOffset); DrawSectorFlagLabel(pos, "Stopper", STOPPER_COLOR, verticalOffset);
verticalOffset += STRING_SPACING; verticalOffset += STRING_SPACING;
} }
// Death // Death
if (pointColl.Block->Flags.Death) if (pointColl.GetSector().Flags.Death)
{ {
DrawSectorFlagLabel(pos, "Death", DEATH_COLOR, verticalOffset); DrawSectorFlagLabel(pos, "Death", DEATH_COLOR, verticalOffset);
verticalOffset += STRING_SPACING; verticalOffset += STRING_SPACING;
} }
// Monkey Swing // Monkey Swing
if (pointColl.Block->Flags.Monkeyswing) if (pointColl.GetSector().Flags.Monkeyswing)
{ {
DrawSectorFlagLabel(pos, "Monkey Swing", MONKEY_SWING_COLOR, verticalOffset); DrawSectorFlagLabel(pos, "Monkey Swing", MONKEY_SWING_COLOR, verticalOffset);
verticalOffset += STRING_SPACING; verticalOffset += STRING_SPACING;
} }
// Beetle / Minecart Right // Beetle / Minecart Right
if (pointColl.Block->Flags.MarkBeetle) if (pointColl.GetSector().Flags.MarkBeetle)
{ {
auto labelString = std::string("Beetle") + (!pointColl.Block->Flags.MinecartStop() ? " / Minecart Right" : ""); auto labelString = std::string("Beetle") + (!pointColl.GetSector().Flags.MinecartStop() ? " / Minecart Right" : "");
DrawSectorFlagLabel(pos, labelString, BEETLE_MINECART_RIGHT_COLOR, verticalOffset); DrawSectorFlagLabel(pos, labelString, BEETLE_MINECART_RIGHT_COLOR, verticalOffset);
verticalOffset += STRING_SPACING; verticalOffset += STRING_SPACING;
} }
// Activator / Minecart Left // Activator / Minecart Left
if (pointColl.Block->Flags.MarkTriggerer) if (pointColl.GetSector().Flags.MarkTriggerer)
{ {
auto labelString = std::string("Activator") + (!pointColl.Block->Flags.MinecartStop() ? " / Minecart Left" : ""); auto labelString = std::string("Activator") + (!pointColl.GetSector().Flags.MinecartStop() ? " / Minecart Left" : "");
DrawSectorFlagLabel(pos, labelString, ACTIVATOR_MINECART_LEFT_COLOR, verticalOffset); DrawSectorFlagLabel(pos, labelString, ACTIVATOR_MINECART_LEFT_COLOR, verticalOffset);
verticalOffset += STRING_SPACING; verticalOffset += STRING_SPACING;
} }
// Minecart Stop // Minecart Stop
if (pointColl.Block->Flags.MinecartStop()) if (pointColl.GetSector().Flags.MinecartStop())
{ {
DrawSectorFlagLabel(pos, "Minecart Stop", MINECART_STOP_COLOR, verticalOffset); DrawSectorFlagLabel(pos, "Minecart Stop", MINECART_STOP_COLOR, verticalOffset);
verticalOffset += STRING_SPACING; verticalOffset += STRING_SPACING;

View file

@ -49,10 +49,10 @@ enum class MaterialType
enum class ClimbDirectionFlags enum class ClimbDirectionFlags
{ {
North = (1 << 8), North = 1 << 8,
East = (1 << 9), East = 1 << 9,
South = (1 << 10), South = 1 << 10,
West = (1 << 11) West = 1 << 11
}; };
// NOTE: Describes vertical room location. // NOTE: Describes vertical room location.
@ -74,10 +74,10 @@ public:
struct SectorSurfaceTriangleData struct SectorSurfaceTriangleData
{ {
Plane Plane = {}; Plane Plane = {};
int PortalRoomNumber = 0; int PortalRoomNumber = 0;
short IllegalSlopeAngle = 0; short SteepSlopeAngle = 0;
MaterialType Material = MaterialType::Stone; MaterialType Material = MaterialType::Stone;
}; };
struct SectorSurfaceData struct SectorSurfaceData
@ -135,17 +135,19 @@ struct SectorFlagData
class FloorInfo class FloorInfo
{ {
public: public:
// Components // Members
int RoomNumber = 0; Vector2i Position = Vector2i::Zero;
int SidePortalRoomNumber = 0; int RoomNumber = 0;
SectorSurfaceData FloorSurface = {}; SectorSurfaceData FloorSurface = {};
SectorSurfaceData CeilingSurface = {}; SectorSurfaceData CeilingSurface = {};
std::set<int> BridgeItemNumbers = {}; SectorFlagData Flags = {};
SectorFlagData Flags = {};
int Box = 0; std::set<int> BridgeItemNumbers = {};
int TriggerIndex = 0; int SidePortalRoomNumber = 0;
bool Stopper = true;
int PathfindingBoxID = 0;
int TriggerIndex = 0;
bool Stopper = true;
// Getters // Getters
int GetSurfaceTriangleID(int x, int z, bool isFloor) const; int GetSurfaceTriangleID(int x, int z, bool isFloor) const;
@ -184,7 +186,7 @@ namespace TEN::Collision::Floordata
Vector2i GetSectorPoint(int x, int z); Vector2i GetSectorPoint(int x, int z);
Vector2i GetRoomGridCoord(int roomNumber, int x, int z, bool clampToBounds = true); Vector2i GetRoomGridCoord(int roomNumber, int x, int z, bool clampToBounds = true);
std::vector<Vector2i> GetNeighborRoomGridCoords(const Vector3i& pos, int roomNumber, unsigned int searchDepth); std::vector<Vector2i> GetNeighborRoomGridCoords(const Vector3i& pos, int roomNumber, unsigned int searchDepth);
std::vector<FloorInfo*> GetNeighborSectorPtrs(const Vector3i& pos, int roomNumber, unsigned int searchDepth); std::vector<FloorInfo*> GetNeighborSectors(const Vector3i& pos, int roomNumber, unsigned int searchDepth);
FloorInfo& GetFloor(int roomNumber, const Vector2i& roomGridCoord); FloorInfo& GetFloor(int roomNumber, const Vector2i& roomGridCoord);
FloorInfo& GetFloor(int roomNumber, int x, int z); FloorInfo& GetFloor(int roomNumber, int x, int z);

Some files were not shown because too many files have changed in this diff Show more