Merge branch 'develop' into develop_60fps

# Conflicts:
#	TombEngine/Game/control/control.cpp
#	TombEngine/Game/gui.cpp
#	TombEngine/Renderer/RendererDrawMenu.cpp
#	TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp
This commit is contained in:
MontyTRC89 2024-08-16 10:19:51 +02:00
commit 3a345e9ccb
254 changed files with 6549 additions and 5267 deletions

View file

@ -20,7 +20,8 @@ This is the credit list of **all** the people who contributed to TombEngine in a
- TokyoSU (entity and vehicle decompilation) - TokyoSU (entity and vehicle decompilation)
- Tomo (general coding, bug fixing) - Tomo (general coding, bug fixing)
- Troye (general coding, refactoring) - Troye (general coding, refactoring)
- WolfCheese (general coding) - Nickelony (general coding)
- JesseG, aka WolfCheese (general coding)
## Testers ## Testers
- Adngel - Adngel

View file

@ -9,20 +9,53 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
## Version 1.5 - xxxx-xx-xx ## Version 1.5 - xxxx-xx-xx
### Bug fixes ### Bug fixes
* Fixed original issue with classic switch off trigger wrongly activating some trigger actions. * Fixed original issue with classic switch off trigger incorrectly activating some trigger actions.
* Fixed incorrect diving animation when swandiving from a high place. * Fixed incorrect diving animation when swandiving from a high place.
* Fixed camera rotating with Lara's hips when climbing out of water. * Fixed camera rotating with the player's hips when climbing out of water.
* Fixed AI for skidoo driver and worker with shotgun TR2 enemies. * Fixed AI for TR2 skidoo driver and worker with shotgun.
* Fixed Ember emitter crashing when ocb is between -1 and -10 * 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 electric cleaner and squishy block not detecting collision with certain block heights.
* Fixed Squishy blocks crashing the level. * Fixed squishy blocks crashing the level.
* Fixed the path finding zones of Larson and Pierre. * Fixed Larson and Pierre pathfinding.
* Fixed the torch flame delay in disappearing when Lara threw or dropped the torch object. * 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.
* Fixed cases where Atlantean mutant's bombs cause the game to crash.
* Fixed young hair drawing.
### Features/Amendments ### Features/Amendments
* Changed Rome Hammer to not hurt player whilst deactivated.
* Changed Statue with blade damage, from 20 to 200.
* Enhanced Rolling Spindle detection to avoid them going down through pits.
* Enhanced Sentry Guns, with a new ItemFlags[3], to contain the ID of the inventory item that deactivates the sentry guns ( by default PUZZLE_ITEM5 )
* Enhanced 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 ).
* Enhanced raptor behaviour and handling.
- OCB 0: Classic behaviour
- OCB 1: Can jump up/down up to 4 steps and jump across gaps up to 2 blocks wide.
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Raptor.wad2
* Added TR3 seal mutant.
- OCB 0: Normal enemy behaviour. (TR3 RX-Tech mines level)
- OCB 1: Trap like behaviour. (TR3 Antarctica level)
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Seal_Mutant.wad2
* 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.
* Added option to enable or disable menu option looping.
* Menu scrolling using held inputs will stop at the last option until a new input is made.
* Added the ability to display "Lara's Home" entry in the main menu.
### Lua API changes ### Lua API changes
* Added Inventory.GetUsedItem(), Inventory.SetUsedItem() and Inventory.ClearUsedItem() functions. * Added Inventory.GetUsedItem(), Inventory.SetUsedItem() and Inventory.ClearUsedItem() functions.
* Added Input.KeyClearAll()
* Added Flow.EnableHomeLevel()
* Removed anims.monkeyAutoJump. It is now a player menu configuration.
* Fixed Volume:GetActive() method
## [Version 1.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.1) - 2024-04-21 ## [Version 1.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.1) - 2024-04-21

View file

@ -131,6 +131,10 @@ scripts too.</p>
<td class="summary">Enable or disable level selection in title flyby.</td> <td class="summary">Enable or disable level selection in title flyby.</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="#EnableHomeLevel">EnableHomeLevel(enabled)</a></td>
<td class="summary">Enable or disable Home Level entry in the main menu.</td>
</tr>
<tr>
<td class="name" ><a href="#EnableLoadSave">EnableLoadSave(enabled)</a></td> <td class="name" ><a href="#EnableLoadSave">EnableLoadSave(enabled)</a></td>
<td class="summary">Enable or disable saving and loading of savegames.</td> <td class="summary">Enable or disable saving and loading of savegames.</td>
</tr> </tr>
@ -139,7 +143,7 @@ scripts too.</p>
<table class="function_list"> <table class="function_list">
<tr> <tr>
<td class="name" ><a href="#EnableFlyCheat">EnableFlyCheat(enabled)</a></td> <td class="name" ><a href="#EnableFlyCheat">EnableFlyCheat(enabled)</a></td>
<td class="summary">Enable or disable DOZY mode (fly cheat).</td> <td class="summary">Enable or disable the fly cheat.</td>
</tr> </tr>
<tr> <tr>
<td class="name" ><a href="#EnablePointFilter">EnablePointFilter(enabled)</a></td> <td class="name" ><a href="#EnablePointFilter">EnablePointFilter(enabled)</a></td>
@ -365,11 +369,11 @@ Must be true or false
</dd> </dd>
<dt> <dt>
<a name = "EnableLoadSave"></a> <a name = "EnableHomeLevel"></a>
<strong>EnableLoadSave(enabled)</strong> <strong>EnableHomeLevel(enabled)</strong>
</dt> </dt>
<dd> <dd>
Enable or disable saving and loading of savegames. Enable or disable Home Level entry in the main menu. ()
@ -377,7 +381,29 @@ Must be true or false
<ul> <ul>
<li><span class="parameter">enabled</span> <li><span class="parameter">enabled</span>
<span class="types"><span class="type">bool</span></span> <span class="types"><span class="type">bool</span></span>
true or false. True or false.
</li>
</ul>
</dd>
<dt>
<a name = "EnableLoadSave"></a>
<strong>EnableLoadSave(enabled)</strong>
</dt>
<dd>
Enable or disable saving and loading of savegames. ()
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">enabled</span>
<span class="types"><span class="type">bool</span></span>
True or false.
</li> </li>
</ul> </ul>
@ -395,8 +421,7 @@ Must be true or false
<strong>EnableFlyCheat(enabled)</strong> <strong>EnableFlyCheat(enabled)</strong>
</dt> </dt>
<dd> <dd>
Enable or disable DOZY mode (fly cheat). Enable or disable the fly cheat. ()
Must be true or false
@ -404,7 +429,7 @@ Must be true or false
<ul> <ul>
<li><span class="parameter">enabled</span> <li><span class="parameter">enabled</span>
<span class="types"><span class="type">bool</span></span> <span class="types"><span class="type">bool</span></span>
true or false True or false.
</li> </li>
</ul> </ul>

View file

@ -131,6 +131,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>
@ -256,6 +260,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

@ -173,7 +173,8 @@ LARA_PETROL_MESH
LARA_DIRT_MESH LARA_DIRT_MESH
LARA_CROWBAR_ANIM LARA_CROWBAR_ANIM
LARA_TORCH_ANIM LARA_TORCH_ANIM
HAIR SINGLE_BRAID_HAIR
DUAL_PIGTAIL_HAIR
SNOWMOBILE_LARA_ANIMS SNOWMOBILE_LARA_ANIMS
SNOWMOBILE SNOWMOBILE
QUAD_LARA_ANIMS QUAD_LARA_ANIMS
@ -359,7 +360,7 @@ WASP_MUTANT
SKATEBOARD SKATEBOARD
SKATEBOARD_KID SKATEBOARD_KID
WINSTON WINSTON
ARMY_WINSTON SEAL_MUTANT
SPRINGBOARD SPRINGBOARD
ROLLING_SPINDLE ROLLING_SPINDLE
DISK_SHOOTER DISK_SHOOTER
@ -945,6 +946,7 @@ MESHSWAP_ROMAN_GOD1
MESHSWAP_ROMAN_GOD2 MESHSWAP_ROMAN_GOD2
MESHSWAP_MONKEY_MEDIPACK MESHSWAP_MONKEY_MEDIPACK
MESHSWAP_MONKEY_KEY MESHSWAP_MONKEY_KEY
MESHSWAP_WINSTON_ARMY_OUTFIT
ANIMATING1 ANIMATING1
ANIMATING2 ANIMATING2
ANIMATING3 ANIMATING3

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 TombEngine Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

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

@ -58,7 +58,7 @@ local strings =
-- Level name strings -- Level name strings
lara_home = { "Lara's Home" }, home_level = { "Home Level" },
test_level = { "Test Level" }, test_level = { "Test Level" },
title = { "Title" }, title = { "Title" },
} }

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" },
@ -79,8 +80,11 @@ local strings =
low = { "Low" }, low = { "Low" },
medium = { "Medium" }, medium = { "Medium" },
menu_actions = { "Menu Actions" }, menu_actions = { "Menu Actions" },
menu_option_looping = { "Menu Option Looping" },
menu_option_looping_all_menus = { "All Menus" },
menu_option_looping_disabled = { "Disabled" },
menu_option_looping_save_load_only = { "Save/Load Only" },
mouse_sensitivity = { "Mouse Sensitivity" }, mouse_sensitivity = { "Mouse Sensitivity" },
mouse_smoothing = { "Mouse Smoothing" },
music_volume = { "Music Volume" }, music_volume = { "Music Volume" },
new_game = { "New Game" }, new_game = { "New Game" },
none = { "None" }, none = { "None" },

View file

@ -331,21 +331,21 @@ namespace TEN::Gui
void CombineRevolverLasersight(ItemInfo* item, bool flag) void CombineRevolverLasersight(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
if (flag) if (flag)
{ {
lara->Inventory.HasLasersight = true; player.Inventory.HasLasersight = true;
lara->Weapons[(int)LaraWeaponType::Revolver].HasLasersight = false; player.Weapons[(int)LaraWeaponType::Revolver].HasLasersight = false;
} }
else else
{ {
lara->Inventory.HasLasersight = false; player.Inventory.HasLasersight = false;
lara->Weapons[(int)LaraWeaponType::Revolver].HasLasersight = true; player.Weapons[(int)LaraWeaponType::Revolver].HasLasersight = true;
} }
if (lara->Control.HandStatus != HandStatus::Free && if (player.Control.HandStatus != HandStatus::Free &&
lara->Control.Weapon.GunType == LaraWeaponType::Revolver) player.Control.Weapon.GunType == LaraWeaponType::Revolver)
{ {
UndrawPistolMesh(*item, LaraWeaponType::Revolver, true); UndrawPistolMesh(*item, LaraWeaponType::Revolver, true);
DrawPistolMeshes(*item, LaraWeaponType::Revolver); DrawPistolMeshes(*item, LaraWeaponType::Revolver);
@ -354,21 +354,21 @@ namespace TEN::Gui
void CombineCrossbowLasersight(ItemInfo* item, bool flag) void CombineCrossbowLasersight(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
if (flag) if (flag)
{ {
lara->Inventory.HasLasersight = true; player.Inventory.HasLasersight = true;
lara->Weapons[(int)LaraWeaponType::Crossbow].HasLasersight = false; player.Weapons[(int)LaraWeaponType::Crossbow].HasLasersight = false;
} }
else else
{ {
lara->Inventory.HasLasersight = false; player.Inventory.HasLasersight = false;
lara->Weapons[(int)LaraWeaponType::Crossbow].HasLasersight = true; player.Weapons[(int)LaraWeaponType::Crossbow].HasLasersight = true;
} }
if (lara->Control.HandStatus != HandStatus::Free && if (player.Control.HandStatus != HandStatus::Free &&
lara->Control.Weapon.GunType == LaraWeaponType::Crossbow) player.Control.Weapon.GunType == LaraWeaponType::Crossbow)
{ {
UndrawShotgunMeshes(*item, LaraWeaponType::Crossbow); UndrawShotgunMeshes(*item, LaraWeaponType::Crossbow);
DrawShotgunMeshes(*item, LaraWeaponType::Crossbow); DrawShotgunMeshes(*item, LaraWeaponType::Crossbow);
@ -377,21 +377,21 @@ namespace TEN::Gui
void CombineHKLasersight(ItemInfo* item, bool flag) void CombineHKLasersight(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
if (flag) if (flag)
{ {
lara->Inventory.HasLasersight = true; player.Inventory.HasLasersight = true;
lara->Weapons[(int)LaraWeaponType::HK].HasLasersight = false; player.Weapons[(int)LaraWeaponType::HK].HasLasersight = false;
} }
else else
{ {
lara->Inventory.HasLasersight = false; player.Inventory.HasLasersight = false;
lara->Weapons[(int)LaraWeaponType::HK].HasLasersight = true; player.Weapons[(int)LaraWeaponType::HK].HasLasersight = true;
} }
if (lara->Control.HandStatus != HandStatus::Free && if (player.Control.HandStatus != HandStatus::Free &&
lara->Control.Weapon.GunType == LaraWeaponType::HK) player.Control.Weapon.GunType == LaraWeaponType::HK)
{ {
UndrawShotgunMeshes(*item, LaraWeaponType::HK); UndrawShotgunMeshes(*item, LaraWeaponType::HK);
DrawShotgunMeshes(*item, LaraWeaponType::HK); DrawShotgunMeshes(*item, LaraWeaponType::HK);
@ -400,514 +400,514 @@ namespace TEN::Gui
void CombinePuzzleItem1(ItemInfo* item, bool flag) void CombinePuzzleItem1(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[0] = false; player.Inventory.PuzzlesCombo[0] = false;
lara->Inventory.PuzzlesCombo[1] = false; player.Inventory.PuzzlesCombo[1] = false;
lara->Inventory.Puzzles[0] = true; player.Inventory.Puzzles[0] = true;
} }
void CombinePuzzleItem2(ItemInfo* item, bool flag) void CombinePuzzleItem2(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[2] = false; player.Inventory.PuzzlesCombo[2] = false;
lara->Inventory.PuzzlesCombo[3] = false; player.Inventory.PuzzlesCombo[3] = false;
lara->Inventory.Puzzles[1] = true; player.Inventory.Puzzles[1] = true;
} }
void CombinePuzzleItem3(ItemInfo* item, bool flag) void CombinePuzzleItem3(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[4] = false; player.Inventory.PuzzlesCombo[4] = false;
lara->Inventory.PuzzlesCombo[5] = false; player.Inventory.PuzzlesCombo[5] = false;
lara->Inventory.Puzzles[2] = true; player.Inventory.Puzzles[2] = true;
} }
void CombinePuzzleItem4(ItemInfo* item, bool flag) void CombinePuzzleItem4(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[6] = false; player.Inventory.PuzzlesCombo[6] = false;
lara->Inventory.PuzzlesCombo[7] = false; player.Inventory.PuzzlesCombo[7] = false;
lara->Inventory.Puzzles[3] = true; player.Inventory.Puzzles[3] = true;
} }
void CombinePuzzleItem5(ItemInfo* item, bool flag) void CombinePuzzleItem5(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[8] = false; player.Inventory.PuzzlesCombo[8] = false;
lara->Inventory.PuzzlesCombo[9] = false; player.Inventory.PuzzlesCombo[9] = false;
lara->Inventory.Puzzles[4] = true; player.Inventory.Puzzles[4] = true;
} }
void CombinePuzzleItem6(ItemInfo* item, bool flag) void CombinePuzzleItem6(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[10] = false; player.Inventory.PuzzlesCombo[10] = false;
lara->Inventory.PuzzlesCombo[11] = false; player.Inventory.PuzzlesCombo[11] = false;
lara->Inventory.Puzzles[5] = true; player.Inventory.Puzzles[5] = true;
} }
void CombinePuzzleItem7(ItemInfo* item, bool flag) void CombinePuzzleItem7(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[12] = false; player.Inventory.PuzzlesCombo[12] = false;
lara->Inventory.PuzzlesCombo[13] = false; player.Inventory.PuzzlesCombo[13] = false;
lara->Inventory.Puzzles[6] = true; player.Inventory.Puzzles[6] = true;
} }
void CombinePuzzleItem8(ItemInfo* item, bool flag) void CombinePuzzleItem8(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[14] = false; player.Inventory.PuzzlesCombo[14] = false;
lara->Inventory.PuzzlesCombo[15] = false; player.Inventory.PuzzlesCombo[15] = false;
lara->Inventory.Puzzles[7] = true; player.Inventory.Puzzles[7] = true;
} }
void CombinePuzzleItem9(ItemInfo* item, bool flag) void CombinePuzzleItem9(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[16] = false; player.Inventory.PuzzlesCombo[16] = false;
lara->Inventory.PuzzlesCombo[17] = false; player.Inventory.PuzzlesCombo[17] = false;
lara->Inventory.Puzzles[8] = true; player.Inventory.Puzzles[8] = true;
} }
void CombinePuzzleItem10(ItemInfo* item, bool flag) void CombinePuzzleItem10(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[18] = false; player.Inventory.PuzzlesCombo[18] = false;
lara->Inventory.PuzzlesCombo[19] = false; player.Inventory.PuzzlesCombo[19] = false;
lara->Inventory.Puzzles[9] = true; player.Inventory.Puzzles[9] = true;
} }
void CombinePuzzleItem11(ItemInfo* item, bool flag) void CombinePuzzleItem11(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[20] = false; player.Inventory.PuzzlesCombo[20] = false;
lara->Inventory.PuzzlesCombo[21] = false; player.Inventory.PuzzlesCombo[21] = false;
lara->Inventory.Puzzles[10] = true; player.Inventory.Puzzles[10] = true;
} }
void CombinePuzzleItem12(ItemInfo* item, bool flag) void CombinePuzzleItem12(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[22] = false; player.Inventory.PuzzlesCombo[22] = false;
lara->Inventory.PuzzlesCombo[23] = false; player.Inventory.PuzzlesCombo[23] = false;
lara->Inventory.Puzzles[11] = true; player.Inventory.Puzzles[11] = true;
} }
void CombinePuzzleItem13(ItemInfo* item, bool flag) void CombinePuzzleItem13(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[24] = false; player.Inventory.PuzzlesCombo[24] = false;
lara->Inventory.PuzzlesCombo[25] = false; player.Inventory.PuzzlesCombo[25] = false;
lara->Inventory.Puzzles[12] = true; player.Inventory.Puzzles[12] = true;
} }
void CombinePuzzleItem14(ItemInfo* item, bool flag) void CombinePuzzleItem14(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[26] = false; player.Inventory.PuzzlesCombo[26] = false;
lara->Inventory.PuzzlesCombo[27] = false; player.Inventory.PuzzlesCombo[27] = false;
lara->Inventory.Puzzles[13] = true; player.Inventory.Puzzles[13] = true;
} }
void CombinePuzzleItem15(ItemInfo* item, bool flag) void CombinePuzzleItem15(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[28] = false; player.Inventory.PuzzlesCombo[28] = false;
lara->Inventory.PuzzlesCombo[29] = false; player.Inventory.PuzzlesCombo[29] = false;
lara->Inventory.Puzzles[14] = true; player.Inventory.Puzzles[14] = true;
} }
void CombinePuzzleItem16(ItemInfo* item, bool flag) void CombinePuzzleItem16(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.PuzzlesCombo[30] = false; player.Inventory.PuzzlesCombo[30] = false;
lara->Inventory.PuzzlesCombo[31] = false; player.Inventory.PuzzlesCombo[31] = false;
lara->Inventory.Puzzles[15] = true; player.Inventory.Puzzles[15] = true;
} }
void CombineKeyItem1(ItemInfo* item, bool flag) void CombineKeyItem1(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[0] = true; player.Inventory.Keys[0] = true;
lara->Inventory.KeysCombo[0] = false; player.Inventory.KeysCombo[0] = false;
lara->Inventory.KeysCombo[1] = false; player.Inventory.KeysCombo[1] = false;
} }
void CombineKeyItem2(ItemInfo* item, bool flag) void CombineKeyItem2(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[1] = true; player.Inventory.Keys[1] = true;
lara->Inventory.KeysCombo[2] = false; player.Inventory.KeysCombo[2] = false;
lara->Inventory.KeysCombo[3] = false; player.Inventory.KeysCombo[3] = false;
} }
void CombineKeyItem3(ItemInfo* item, bool flag) void CombineKeyItem3(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[2] = true; player.Inventory.Keys[2] = true;
lara->Inventory.KeysCombo[4] = false; player.Inventory.KeysCombo[4] = false;
lara->Inventory.KeysCombo[5] = false; player.Inventory.KeysCombo[5] = false;
} }
void CombineKeyItem4(ItemInfo* item, bool flag) void CombineKeyItem4(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[3] = true; player.Inventory.Keys[3] = true;
lara->Inventory.KeysCombo[6] = false; player.Inventory.KeysCombo[6] = false;
lara->Inventory.KeysCombo[7] = false; player.Inventory.KeysCombo[7] = false;
} }
void CombineKeyItem5(ItemInfo* item, bool flag) void CombineKeyItem5(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[4] = true; player.Inventory.Keys[4] = true;
lara->Inventory.KeysCombo[8] = false; player.Inventory.KeysCombo[8] = false;
lara->Inventory.KeysCombo[9] = false; player.Inventory.KeysCombo[9] = false;
} }
void CombineKeyItem6(ItemInfo* item, bool flag) void CombineKeyItem6(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[5] = true; player.Inventory.Keys[5] = true;
lara->Inventory.KeysCombo[10] = false; player.Inventory.KeysCombo[10] = false;
lara->Inventory.KeysCombo[11] = false; player.Inventory.KeysCombo[11] = false;
} }
void CombineKeyItem7(ItemInfo* item, bool flag) void CombineKeyItem7(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[6] = true; player.Inventory.Keys[6] = true;
lara->Inventory.KeysCombo[12] = false; player.Inventory.KeysCombo[12] = false;
lara->Inventory.KeysCombo[13] = false; player.Inventory.KeysCombo[13] = false;
} }
void CombineKeyItem8(ItemInfo* item, bool flag) void CombineKeyItem8(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[7] = true; player.Inventory.Keys[7] = true;
lara->Inventory.KeysCombo[14] = false; player.Inventory.KeysCombo[14] = false;
lara->Inventory.KeysCombo[15] = false; player.Inventory.KeysCombo[15] = false;
} }
void CombineKeyItem9(ItemInfo* item, bool flag) void CombineKeyItem9(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[8] = true; player.Inventory.Keys[8] = true;
lara->Inventory.KeysCombo[16] = false; player.Inventory.KeysCombo[16] = false;
lara->Inventory.KeysCombo[17] = false; player.Inventory.KeysCombo[17] = false;
} }
void CombineKeyItem10(ItemInfo* item, bool flag) void CombineKeyItem10(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[9] = true; player.Inventory.Keys[9] = true;
lara->Inventory.KeysCombo[18] = false; player.Inventory.KeysCombo[18] = false;
lara->Inventory.KeysCombo[19] = false; player.Inventory.KeysCombo[19] = false;
} }
void CombineKeyItem11(ItemInfo* item, bool flag) void CombineKeyItem11(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[10] = true; player.Inventory.Keys[10] = true;
lara->Inventory.KeysCombo[20] = false; player.Inventory.KeysCombo[20] = false;
lara->Inventory.KeysCombo[21] = false; player.Inventory.KeysCombo[21] = false;
} }
void CombineKeyItem12(ItemInfo* item, bool flag) void CombineKeyItem12(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[11] = true; player.Inventory.Keys[11] = true;
lara->Inventory.KeysCombo[22] = false; player.Inventory.KeysCombo[22] = false;
lara->Inventory.KeysCombo[23] = false; player.Inventory.KeysCombo[23] = false;
} }
void CombineKeyItem13(ItemInfo* item, bool flag) void CombineKeyItem13(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[12] = true; player.Inventory.Keys[12] = true;
lara->Inventory.KeysCombo[24] = false; player.Inventory.KeysCombo[24] = false;
lara->Inventory.KeysCombo[25] = false; player.Inventory.KeysCombo[25] = false;
} }
void CombineKeyItem14(ItemInfo* item, bool flag) void CombineKeyItem14(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[13] = true; player.Inventory.Keys[13] = true;
lara->Inventory.KeysCombo[26] = false; player.Inventory.KeysCombo[26] = false;
lara->Inventory.KeysCombo[27] = false; player.Inventory.KeysCombo[27] = false;
} }
void CombineKeyItem15(ItemInfo* item, bool flag) void CombineKeyItem15(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[14] = true; player.Inventory.Keys[14] = true;
lara->Inventory.KeysCombo[28] = false; player.Inventory.KeysCombo[28] = false;
lara->Inventory.KeysCombo[29] = false; player.Inventory.KeysCombo[29] = false;
} }
void CombineKeyItem16(ItemInfo* item, bool flag) void CombineKeyItem16(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Keys[15] = true; player.Inventory.Keys[15] = true;
lara->Inventory.KeysCombo[30] = false; player.Inventory.KeysCombo[30] = false;
lara->Inventory.KeysCombo[31] = false; player.Inventory.KeysCombo[31] = false;
} }
void CombinePickupItem1(ItemInfo* item, bool flag) void CombinePickupItem1(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[0] = true; player.Inventory.Pickups[0] = true;
lara->Inventory.PickupsCombo[0] = false; player.Inventory.PickupsCombo[0] = false;
lara->Inventory.PickupsCombo[1] = false; player.Inventory.PickupsCombo[1] = false;
} }
void CombinePickupItem2(ItemInfo* item, bool flag) void CombinePickupItem2(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[1] = true; player.Inventory.Pickups[1] = true;
lara->Inventory.PickupsCombo[2] = false; player.Inventory.PickupsCombo[2] = false;
lara->Inventory.PickupsCombo[3] = false; player.Inventory.PickupsCombo[3] = false;
} }
void CombinePickupItem3(ItemInfo* item, bool flag) void CombinePickupItem3(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[2] = true; player.Inventory.Pickups[2] = true;
lara->Inventory.PickupsCombo[4] = false; player.Inventory.PickupsCombo[4] = false;
lara->Inventory.PickupsCombo[5] = false; player.Inventory.PickupsCombo[5] = false;
} }
void CombinePickupItem4(ItemInfo* item, bool flag) void CombinePickupItem4(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[3] = true; player.Inventory.Pickups[3] = true;
lara->Inventory.PickupsCombo[6] = false; player.Inventory.PickupsCombo[6] = false;
lara->Inventory.PickupsCombo[7] = false; player.Inventory.PickupsCombo[7] = false;
} }
void CombinePickupItem5(ItemInfo* item, bool flag) void CombinePickupItem5(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[4] = true; player.Inventory.Pickups[4] = true;
lara->Inventory.PickupsCombo[8] = false; player.Inventory.PickupsCombo[8] = false;
lara->Inventory.PickupsCombo[9] = false; player.Inventory.PickupsCombo[9] = false;
} }
void CombinePickupItem6(ItemInfo* item, bool flag) void CombinePickupItem6(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[5] = true; player.Inventory.Pickups[5] = true;
lara->Inventory.PickupsCombo[10] = false; player.Inventory.PickupsCombo[10] = false;
lara->Inventory.PickupsCombo[11] = false; player.Inventory.PickupsCombo[11] = false;
} }
void CombinePickupItem7(ItemInfo* item, bool flag) void CombinePickupItem7(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[6] = true; player.Inventory.Pickups[6] = true;
lara->Inventory.PickupsCombo[12] = false; player.Inventory.PickupsCombo[12] = false;
lara->Inventory.PickupsCombo[13] = false; player.Inventory.PickupsCombo[13] = false;
} }
void CombinePickupItem8(ItemInfo* item, bool flag) void CombinePickupItem8(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[7] = true; player.Inventory.Pickups[7] = true;
lara->Inventory.PickupsCombo[14] = false; player.Inventory.PickupsCombo[14] = false;
lara->Inventory.PickupsCombo[15] = false; player.Inventory.PickupsCombo[15] = false;
} }
void CombinePickupItem9(ItemInfo* item, bool flag) void CombinePickupItem9(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[8] = true; player.Inventory.Pickups[8] = true;
lara->Inventory.PickupsCombo[16] = false; player.Inventory.PickupsCombo[16] = false;
lara->Inventory.PickupsCombo[17] = false; player.Inventory.PickupsCombo[17] = false;
} }
void CombinePickupItem10(ItemInfo* item, bool flag) void CombinePickupItem10(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[9] = true; player.Inventory.Pickups[9] = true;
lara->Inventory.PickupsCombo[18] = false; player.Inventory.PickupsCombo[18] = false;
lara->Inventory.PickupsCombo[19] = false; player.Inventory.PickupsCombo[19] = false;
} }
void CombinePickupItem11(ItemInfo* item, bool flag) void CombinePickupItem11(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[10] = true; player.Inventory.Pickups[10] = true;
lara->Inventory.PickupsCombo[20] = false; player.Inventory.PickupsCombo[20] = false;
lara->Inventory.PickupsCombo[21] = false; player.Inventory.PickupsCombo[21] = false;
} }
void CombinePickupItem12(ItemInfo* item, bool flag) void CombinePickupItem12(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[11] = true; player.Inventory.Pickups[11] = true;
lara->Inventory.PickupsCombo[22] = false; player.Inventory.PickupsCombo[22] = false;
lara->Inventory.PickupsCombo[23] = false; player.Inventory.PickupsCombo[23] = false;
} }
void CombinePickupItem13(ItemInfo* item, bool flag) void CombinePickupItem13(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[12] = true; player.Inventory.Pickups[12] = true;
lara->Inventory.PickupsCombo[24] = false; player.Inventory.PickupsCombo[24] = false;
lara->Inventory.PickupsCombo[25] = false; player.Inventory.PickupsCombo[25] = false;
} }
void CombinePickupItem14(ItemInfo* item, bool flag) void CombinePickupItem14(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[13] = true; player.Inventory.Pickups[13] = true;
lara->Inventory.PickupsCombo[26] = false; player.Inventory.PickupsCombo[26] = false;
lara->Inventory.PickupsCombo[27] = false; player.Inventory.PickupsCombo[27] = false;
} }
void CombinePickupItem15(ItemInfo* item, bool flag) void CombinePickupItem15(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[14] = true; player.Inventory.Pickups[14] = true;
lara->Inventory.PickupsCombo[28] = false; player.Inventory.PickupsCombo[28] = false;
lara->Inventory.PickupsCombo[29] = false; player.Inventory.PickupsCombo[29] = false;
} }
void CombinePickupItem16(ItemInfo* item, bool flag) void CombinePickupItem16(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Pickups[15] = true; player.Inventory.Pickups[15] = true;
lara->Inventory.PickupsCombo[30] = false; player.Inventory.PickupsCombo[30] = false;
lara->Inventory.PickupsCombo[31] = false; player.Inventory.PickupsCombo[31] = false;
} }
void CombineExamine1(ItemInfo* item, bool flag) void CombineExamine1(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[0] = true; player.Inventory.Examines[0] = true;
lara->Inventory.ExaminesCombo[0] = false; player.Inventory.ExaminesCombo[0] = false;
lara->Inventory.ExaminesCombo[1] = false; player.Inventory.ExaminesCombo[1] = false;
} }
void CombineExamine2(ItemInfo* item, bool flag) void CombineExamine2(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[1] = true; player.Inventory.Examines[1] = true;
lara->Inventory.ExaminesCombo[2] = false; player.Inventory.ExaminesCombo[2] = false;
lara->Inventory.ExaminesCombo[3] = false; player.Inventory.ExaminesCombo[3] = false;
} }
void CombineExamine3(ItemInfo* item, bool flag) void CombineExamine3(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[2] = true; player.Inventory.Examines[2] = true;
lara->Inventory.ExaminesCombo[4] = false; player.Inventory.ExaminesCombo[4] = false;
lara->Inventory.ExaminesCombo[5] = false; player.Inventory.ExaminesCombo[5] = false;
} }
void CombineExamine4(ItemInfo* item, bool flag) void CombineExamine4(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[3] = true; player.Inventory.Examines[3] = true;
lara->Inventory.ExaminesCombo[6] = false; player.Inventory.ExaminesCombo[6] = false;
lara->Inventory.ExaminesCombo[7] = false; player.Inventory.ExaminesCombo[7] = false;
} }
void CombineExamine5(ItemInfo* item, bool flag) void CombineExamine5(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[4] = true; player.Inventory.Examines[4] = true;
lara->Inventory.ExaminesCombo[8] = false; player.Inventory.ExaminesCombo[8] = false;
lara->Inventory.ExaminesCombo[9] = false; player.Inventory.ExaminesCombo[9] = false;
} }
void CombineExamine6(ItemInfo* item, bool flag) void CombineExamine6(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[5] = true; player.Inventory.Examines[5] = true;
lara->Inventory.ExaminesCombo[10] = false; player.Inventory.ExaminesCombo[10] = false;
lara->Inventory.ExaminesCombo[11] = false; player.Inventory.ExaminesCombo[11] = false;
} }
void CombineExamine7(ItemInfo* item, bool flag) void CombineExamine7(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[6] = true; player.Inventory.Examines[6] = true;
lara->Inventory.ExaminesCombo[12] = false; player.Inventory.ExaminesCombo[12] = false;
lara->Inventory.ExaminesCombo[13] = false; player.Inventory.ExaminesCombo[13] = false;
} }
void CombineExamine8(ItemInfo* item, bool flag) void CombineExamine8(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.Examines[7] = true; player.Inventory.Examines[7] = true;
lara->Inventory.ExaminesCombo[14] = false; player.Inventory.ExaminesCombo[14] = false;
lara->Inventory.ExaminesCombo[15] = false; player.Inventory.ExaminesCombo[15] = false;
} }
void CombineClockWorkBeetle(ItemInfo* item, bool flag) void CombineClockWorkBeetle(ItemInfo* item, bool flag)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Inventory.BeetleComponents |= BEETLECOMP_FLAG_BEETLE; // Get beetle. player.Inventory.BeetleComponents |= BEETLECOMP_FLAG_BEETLE; // Get beetle.
lara->Inventory.BeetleComponents &= BEETLECOMP_FLAG_COMBO_1; // Remove combo 1. player.Inventory.BeetleComponents &= BEETLECOMP_FLAG_COMBO_1; // Remove combo 1.
lara->Inventory.BeetleComponents &= BEETLECOMP_FLAG_COMBO_2; // Remove combo 2. player.Inventory.BeetleComponents &= BEETLECOMP_FLAG_COMBO_2; // Remove combo 2.
} }
} }

View file

@ -198,7 +198,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)
@ -224,6 +224,6 @@ namespace TEN::Hud
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

@ -46,14 +46,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);
@ -63,6 +66,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,11 +3,12 @@
#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"
#include "Renderer/Renderer.h"
using namespace TEN::Effects::DisplaySprite; using namespace TEN::Effects::DisplaySprite;
using namespace TEN::Math; using namespace TEN::Math;
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
namespace TEN::Hud namespace TEN::Hud
@ -85,10 +86,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;
@ -27,6 +29,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

@ -359,9 +359,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;
@ -41,15 +43,18 @@ namespace TEN::Hud
std::array<SegmentData, SEGMENT_COUNT> PrevSegments = {}; std::array<SegmentData, SEGMENT_COUNT> PrevSegments = {};
// 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;
@ -67,19 +72,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

@ -336,7 +336,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
if (DebugMode) if (DebugMode)
{ {
DrawNearbyPathfinding(GetPointCollision(*item).GetBottomSector().Box); DrawNearbyPathfinding(GetPointCollision(*item).GetBottomSector().PathfindingBoxID);
DrawNearbySectorFlags(*item); DrawNearbySectorFlags(*item);
} }
} }
@ -642,7 +642,7 @@ void UpdateLara(ItemInfo* item, bool isTitle)
g_Renderer.UpdateLaraAnimations(true); g_Renderer.UpdateLaraAnimations(true);
// Update player effects. // Update player effects.
HairEffect.Update(*item, g_GameFlow->GetLevel(CurrentLevel)->GetLaraType() == LaraType::Young); HairEffect.Update(*item);
HandlePlayerWetnessDrips(*item); HandlePlayerWetnessDrips(*item);
HandlePlayerDiveBubbles(*item); HandlePlayerDiveBubbles(*item);
ProcessEffects(item); ProcessEffects(item);

View file

@ -497,13 +497,11 @@ void LaraSurfaceCollision(ItemInfo* item, CollisionInfo* coll)
} }
auto pointColl = GetPointCollision(*item); auto pointColl = GetPointCollision(*item);
int waterHeight = GetWaterHeight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
if ((pointColl.GetFloorHeight() - 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) else if ((pointColl.GetWaterTopHeight() - item->Pose.Position.y) <= -LARA_HEADROOM)
{ {
SetLaraSwimDiveAnimation(item); SetLaraSwimDiveAnimation(item);
} }

View file

@ -260,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);

View file

@ -22,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"
@ -46,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
@ -278,7 +276,7 @@ void HandlePlayerStatusEffects(ItemInfo& item, WaterStatus waterStatus, PlayerWa
} }
} }
static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo& item, LaraWeaponType currentWeaponType, bool getPrev) static 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>
{ {
@ -293,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)
@ -311,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)
@ -326,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)
@ -347,11 +345,9 @@ void HandlePlayerQuickActions(ItemInfo& 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.
@ -994,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);
@ -1471,7 +1467,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);
} }
} }
@ -1494,7 +1490,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);

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

@ -428,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);
@ -1563,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;
@ -1584,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

@ -1025,7 +1025,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

@ -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/control/control.h" #include "Game/control/control.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara_collide.h" #include "Game/Lara/lara_collide.h"
@ -14,6 +15,7 @@
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
using namespace TEN::Collision::Point;
using namespace TEN::Input; using namespace TEN::Input;
// ----------------------------- // -----------------------------
@ -187,7 +189,7 @@ void lara_col_underwater_death(ItemInfo* item, CollisionInfo* coll)
item->HitPoints = -1; item->HitPoints = -1;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
int waterHeight = GetWaterHeight(item); int waterHeight = GetPointCollision(*item).GetWaterTopHeight();
if (waterHeight < (item->Pose.Position.y - (CLICK(0.4f) - 2)) && if (waterHeight < (item->Pose.Position.y - (CLICK(0.4f) - 2)) &&
waterHeight != NO_HEIGHT) waterHeight != NO_HEIGHT)
{ {

View file

@ -17,7 +17,7 @@
#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"
@ -27,7 +27,6 @@ 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;
// ----------------------------- // -----------------------------
@ -58,8 +57,8 @@ bool TestValidLedge(ItemInfo* item, CollisionInfo* coll, bool ignoreHeadroom, bo
if (frontLeft.GetCeilingHeight() >(item->Pose.Position.y - coll->Setup.Height) || frontRight.GetCeilingHeight() > (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
@ -1088,15 +1087,13 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll)
auto& player = GetLaraInfo(*item); auto& player = GetLaraInfo(*item);
auto pointColl = GetPointCollision(*item); auto pointColl = GetPointCollision(*item);
int waterDepth = GetWaterDepth(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, pointColl.GetRoomNumber());
if (waterDepth == NO_HEIGHT) if (pointColl.GetWaterBottomHeight() == NO_HEIGHT)
{ {
item->Animation.Velocity.y = 0.0f; item->Animation.Velocity.y = 0.0f;
item->Pose.Position = coll->Setup.PrevPosition; item->Pose.Position = coll->Setup.PrevPosition;
} }
else if (pointColl.GetWaterBottomHeight() <= (LARA_HEIGHT - (LARA_HEADROOM / 2)))
else if (waterDepth <= (LARA_HEIGHT - (LARA_HEADROOM / 2)))
{ {
SetAnimation(item, LA_UNDERWATER_TO_STAND); SetAnimation(item, LA_UNDERWATER_TO_STAND);
ResetPlayerLean(item); ResetPlayerLean(item);
@ -1427,7 +1424,7 @@ 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);
@ -1530,8 +1527,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))
@ -1710,7 +1707,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);
@ -1723,7 +1720,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);
@ -1793,9 +1790,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;
@ -1803,7 +1800,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

@ -56,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;
@ -112,49 +114,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:
@ -508,7 +556,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

@ -166,7 +166,7 @@ void LookCamera(ItemInfo& item, const CollisionInfo& coll)
bool isInSwamp = TestEnvironment(ENV_FLAG_SWAMP, item.RoomNumber); bool isInSwamp = TestEnvironment(ENV_FLAG_SWAMP, item.RoomNumber);
auto basePos = Vector3i( auto basePos = Vector3i(
item.Pose.Position.x, item.Pose.Position.x,
isInSwamp ? g_Level.Rooms[item.RoomNumber].maxceiling : item.Pose.Position.y, isInSwamp ? g_Level.Rooms[item.RoomNumber].TopHeight : item.Pose.Position.y,
item.Pose.Position.z); item.Pose.Position.z);
// Define landmarks. // Define landmarks.
@ -342,7 +342,7 @@ void MoveCamera(GameVector* ideal, int speed)
int y = Camera.pos.y; int y = Camera.pos.y;
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].Position.y - CLICK(1);
auto pointColl = GetPointCollision(Vector3i(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 < pointColl.GetCeilingHeight() || if (y < pointColl.GetCeilingHeight() ||
@ -533,7 +533,7 @@ void ChaseCamera(ItemInfo* item)
auto pointColl = GetPointCollision(Vector3i(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, pointColl.GetRoomNumber())) if (TestEnvironment(ENV_FLAG_SWAMP, pointColl.GetRoomNumber()))
Camera.target.y = g_Level.Rooms[pointColl.GetRoomNumber()].maxceiling - CLICK(1); Camera.target.y = g_Level.Rooms[pointColl.GetRoomNumber()].TopHeight - CLICK(1);
int y = Camera.target.y; int y = Camera.target.y;
pointColl = GetPointCollision(Vector3i(Camera.target.x, y, Camera.target.z), Camera.target.RoomNumber); pointColl = GetPointCollision(Vector3i(Camera.target.x, y, Camera.target.z), Camera.target.RoomNumber);
@ -651,7 +651,7 @@ void CombatCamera(ItemInfo* item)
auto pointColl = GetPointCollision(Vector3i(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, pointColl.GetRoomNumber())) if (TestEnvironment(ENV_FLAG_SWAMP, pointColl.GetRoomNumber()))
Camera.target.y = g_Level.Rooms[pointColl.GetRoomNumber()].y - CLICK(1); Camera.target.y = g_Level.Rooms[pointColl.GetRoomNumber()].Position.y - CLICK(1);
pointColl = GetPointCollision(Camera.target.ToVector3i(), Camera.target.RoomNumber); pointColl = GetPointCollision(Camera.target.ToVector3i(), Camera.target.RoomNumber);
Camera.target.RoomNumber = pointColl.GetRoomNumber(); Camera.target.RoomNumber = pointColl.GetRoomNumber();
@ -1392,7 +1392,7 @@ bool CheckItemCollideCamera(ItemInfo* item)
static std::vector<int> FillCollideableItemList() static std::vector<int> FillCollideableItemList()
{ {
auto itemList = std::vector<int>{}; auto itemList = std::vector<int>{};
auto& roomList = g_Level.Rooms[Camera.pos.RoomNumber].neighbors; auto& roomList = g_Level.Rooms[Camera.pos.RoomNumber].NeighborRoomNumbers;
for (short i = 0; i < g_Level.NumItems; i++) for (short i = 0; i < g_Level.NumItems; i++)
{ {
@ -1442,7 +1442,7 @@ bool CheckStaticCollideCamera(MESH_INFO* mesh)
std::vector<MESH_INFO*> FillCollideableStaticsList() std::vector<MESH_INFO*> FillCollideableStaticsList()
{ {
std::vector<MESH_INFO*> staticList; std::vector<MESH_INFO*> staticList;
auto& roomList = g_Level.Rooms[Camera.pos.RoomNumber].neighbors; auto& roomList = g_Level.Rooms[Camera.pos.RoomNumber].NeighborRoomNumbers;
for (int i : roomList) for (int i : roomList)
{ {
@ -1485,7 +1485,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);
} }
@ -1508,7 +1508,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

@ -57,7 +57,7 @@ namespace TEN::Collision::Point
int roomNumber = roomNumberBelow.value_or(bottomSector->RoomNumber); int roomNumber = roomNumberBelow.value_or(bottomSector->RoomNumber);
auto& room = g_Level.Rooms[roomNumber]; auto& room = g_Level.Rooms[roomNumber];
bottomSector = Room::GetSector(&room, _position.x - room.x, _position.z - room.z); bottomSector = Room::GetSector(&room, _position.x - room.Position.x, _position.z - room.Position.z);
roomNumberBelow = bottomSector->GetNextRoomNumber(_position, true); roomNumberBelow = bottomSector->GetNextRoomNumber(_position, true);
} }
_bottomSector = bottomSector; _bottomSector = bottomSector;
@ -78,7 +78,7 @@ namespace TEN::Collision::Point
int roomNumber = roomNumberAbove.value_or(topSector->RoomNumber); int roomNumber = roomNumberAbove.value_or(topSector->RoomNumber);
auto& room = g_Level.Rooms[roomNumber]; auto& room = g_Level.Rooms[roomNumber];
topSector = Room::GetSector(&room, _position.x - room.x, _position.z - room.z); topSector = Room::GetSector(&room, _position.x - room.Position.x, _position.z - room.Position.z);
roomNumberAbove = topSector->GetNextRoomNumber(_position, false); roomNumberAbove = topSector->GetNextRoomNumber(_position, false);
} }
_topSector = topSector; _topSector = topSector;
@ -177,9 +177,24 @@ namespace TEN::Collision::Point
if (_waterSurfaceHeight.has_value()) if (_waterSurfaceHeight.has_value())
return *_waterSurfaceHeight; return *_waterSurfaceHeight;
// Set water surface height. TODO: Calculate here. auto* room = &g_Level.Rooms[_roomNumber];
_waterSurfaceHeight = GetWaterSurface(_position.x, _position.y, _position.z, _roomNumber); const auto* sector = Room::GetSector(room, _position.x - room->Position.x, _position.z - room->Position.z);
// Set water surface height.
bool isBelow = !TestEnvironment(ENV_FLAG_WATER, room);
while (sector->GetNextRoomNumber(_position, isBelow).has_value())
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(_position, isBelow).value_or(sector->RoomNumber)];
if (isBelow == TestEnvironment(ENV_FLAG_WATER, room))
{
_waterSurfaceHeight = sector->GetSurfaceHeight(_position.x, _position.z, isBelow);
return *_waterSurfaceHeight;
}
sector = Room::GetSector(room, _position.x - room->Position.x, _position.z - room->Position.z);
}
_waterSurfaceHeight = NO_HEIGHT;
return *_waterSurfaceHeight; return *_waterSurfaceHeight;
} }
@ -188,10 +203,100 @@ namespace TEN::Collision::Point
if (_waterBottomHeight.has_value()) if (_waterBottomHeight.has_value())
return *_waterBottomHeight; return *_waterBottomHeight;
// Set water bottom height. TODO: Calculate here. FloorInfo* sector = nullptr;
_waterBottomHeight = GetWaterDepth(_position.x, _position.y, _position.z, _roomNumber); auto* room = &g_Level.Rooms[_roomNumber];
short roomNumber = _roomNumber;
return *_waterBottomHeight; int adjoiningRoomNumber = NO_VALUE;
do
{
int x = (_position.x - room->Position.x) / BLOCK(1);
int z = (_position.z - room->Position.z) / BLOCK(1);
if (z <= 0)
{
z = 0;
if (x < 1)
{
x = 1;
}
else if (x > (room->XSize - 2))
{
x = room->XSize - 2;
}
}
else if (z >= (room->ZSize - 1))
{
z = room->ZSize - 1;
if (x < 1)
{
x = 1;
}
else if (x > (room->XSize - 2))
{
x = room->XSize - 2;
}
}
else if (x < 0)
{
x = 0;
}
else if (x >= room->XSize)
{
x = room->XSize - 1;
}
sector = &room->Sectors[z + (x * room->ZSize)];
adjoiningRoomNumber = sector->SidePortalRoomNumber;
if (adjoiningRoomNumber != NO_VALUE)
{
roomNumber = adjoiningRoomNumber;
room = &g_Level.Rooms[adjoiningRoomNumber];
}
}
while (adjoiningRoomNumber != NO_VALUE);
// Set water bottom height.
if (TestEnvironment(ENV_FLAG_WATER, room) || TestEnvironment(ENV_FLAG_SWAMP, room))
{
while (sector->GetNextRoomNumber(_position, false).value_or(NO_VALUE) != NO_VALUE)
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(_position, false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room) && !TestEnvironment(ENV_FLAG_SWAMP, room))
{
int waterHeight = sector->GetSurfaceHeight(_position.x, _position.z, false);
int floorHeight = GetPointCollision(_position, sector->RoomNumber).GetBottomSector().GetSurfaceHeight(_position.x, _position.z, true);
_waterBottomHeight = floorHeight - waterHeight;
return *_waterBottomHeight;
}
sector = Room::GetSector(room, _position.x - room->Position.x, _position.z - room->Position.z);
}
_waterBottomHeight = DEEP_WATER;
return *_waterBottomHeight;
}
else
{
while (sector->GetNextRoomNumber(_position, true).value_or(NO_VALUE) != NO_VALUE)
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(_position, true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room) || TestEnvironment(ENV_FLAG_SWAMP, room))
{
int waterHeight = sector->GetSurfaceHeight(_position.x, _position.z, true);
sector = GetFloor(_position.x, _position.y, _position.z, &roomNumber);
_waterBottomHeight = GetPointCollision(_position, sector->RoomNumber).GetFloorHeight() - waterHeight;
return *_waterBottomHeight;
}
sector = Room::GetSector(room, _position.x - room->Position.x, _position.z - room->Position.z);
}
_waterBottomHeight = NO_HEIGHT;
return *_waterBottomHeight;
}
} }
int PointCollisionData::GetWaterTopHeight() int PointCollisionData::GetWaterTopHeight()
@ -199,9 +304,96 @@ namespace TEN::Collision::Point
if (_waterTopHeight.has_value()) if (_waterTopHeight.has_value())
return *_waterTopHeight; return *_waterTopHeight;
// Set water top height. TODO: Calculate here. FloorInfo* sector = nullptr;
_waterTopHeight = GetWaterHeight(_position.x, _position.y, _position.z, _roomNumber); auto* room = &g_Level.Rooms[_roomNumber];
int roomNumber = _roomNumber;
int adjoiningRoomNumber = NO_VALUE;
do
{
int x = (_position.x - room->Position.x) / BLOCK(1);
int z = (_position.z - room->Position.z) / BLOCK(1);
if (z <= 0)
{
z = 0;
if (x < 1)
{
x = 1;
}
else if (x > (room->XSize - 2))
{
x = room->XSize - 2;
}
}
else if (z >= (room->ZSize - 1))
{
z = room->ZSize - 1;
if (x < 1)
{
x = 1;
}
else if (x > (room->XSize - 2))
{
x = room->XSize - 2;
}
}
else if (x < 0)
{
x = 0;
}
else if (x >= room->XSize)
{
x = room->XSize - 1;
}
sector = &room->Sectors[z + (x * room->ZSize)];
adjoiningRoomNumber = sector->SidePortalRoomNumber;
if (adjoiningRoomNumber != NO_VALUE)
{
roomNumber = adjoiningRoomNumber;
room = &g_Level.Rooms[adjoiningRoomNumber];
}
}
while (adjoiningRoomNumber != NO_VALUE);
if (sector->IsWall(_position.x, _position.z))
{
_waterTopHeight = NO_HEIGHT;
return *_waterTopHeight;
}
if (TestEnvironment(ENV_FLAG_WATER, room) || TestEnvironment(ENV_FLAG_SWAMP, room))
{
while (sector->GetNextRoomNumber(_position, false).has_value())
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(_position, false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room) && !TestEnvironment(ENV_FLAG_SWAMP, room))
break;
sector = Room::GetSector(room, _position.x - room->Position.x, _position.z - room->Position.z);
}
_waterTopHeight = sector->GetSurfaceHeight(_position, false);
return *_waterTopHeight;
}
else if (sector->GetNextRoomNumber(_position, true).has_value())
{
while (sector->GetNextRoomNumber(_position, true).has_value())
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(_position, true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room) || TestEnvironment(ENV_FLAG_SWAMP, room))
break;
sector = Room::GetSector(room, _position.x - room->Position.x, _position.z - room->Position.z);
}
_waterTopHeight = sector->GetSurfaceHeight(_position, true);
return *_waterTopHeight;
}
_waterTopHeight = NO_HEIGHT;
return *_waterTopHeight; return *_waterTopHeight;
} }

View file

@ -14,6 +14,7 @@ namespace TEN::Collision::Point
{ {
private: private:
// Members // Members
Vector3i _position = Vector3i::Zero; Vector3i _position = Vector3i::Zero;
int _roomNumber = 0; int _roomNumber = 0;
@ -34,9 +35,11 @@ namespace TEN::Collision::Point
public: public:
// Constructors // Constructors
PointCollisionData(const Vector3i& pos, int roomNumber); PointCollisionData(const Vector3i& pos, int roomNumber);
// Getters // Getters
Vector3i GetPosition() const; Vector3i GetPosition() const;
int GetRoomNumber() const; int GetRoomNumber() const;
@ -56,6 +59,7 @@ namespace TEN::Collision::Point
int GetWaterTopHeight(); int GetWaterTopHeight();
// Inquirers // Inquirers
bool IsWall(); bool IsWall();
bool IsSteepFloor(); bool IsSteepFloor();
bool IsSteepCeiling(); bool IsSteepCeiling();
@ -70,6 +74,7 @@ namespace TEN::Collision::Point
private: private:
// Helpers // Helpers
Vector3 GetBridgeNormal(bool isFloor); Vector3 GetBridgeNormal(bool isFloor);
}; };

View file

@ -18,14 +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::Floordata;
using namespace TEN::Collision::Point; 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;
@ -125,7 +123,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible,
// Run through neighboring rooms. // Run through neighboring rooms.
const auto& room = g_Level.Rooms[collidingItem.RoomNumber]; const auto& room = g_Level.Rooms[collidingItem.RoomNumber];
for (int roomNumber : room.neighbors) for (int roomNumber : room.NeighborRoomNumbers)
{ {
auto& neighborRoom = g_Level.Rooms[roomNumber]; auto& neighborRoom = g_Level.Rooms[roomNumber];
if (!neighborRoom.Active()) if (!neighborRoom.Active())
@ -204,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);
} }
@ -253,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);
} }
} }
} }
@ -307,9 +305,9 @@ 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].NeighborRoomNumbers)
{ {
if (!g_Level.Rooms[i].Active()) if (!g_Level.Rooms[i].Active())
continue; continue;
@ -706,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;
} }
@ -942,7 +940,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
{ {
coll->HitTallObject = false; coll->HitTallObject = false;
for (auto i : g_Level.Rooms[item->RoomNumber].neighbors) for (auto i : g_Level.Rooms[item->RoomNumber].NeighborRoomNumbers)
{ {
if (!g_Level.Rooms[i].Active()) if (!g_Level.Rooms[i].Active())
continue; continue;
@ -989,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;
@ -1016,7 +1014,7 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
bool intersects = staticBounds.Intersects(collBounds); bool intersects = staticBounds.Intersects(collBounds);
// 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];
@ -1811,7 +1809,7 @@ void DoObjectCollision(ItemInfo* item, CollisionInfo* coll)
return; return;
const auto& room = g_Level.Rooms[item->RoomNumber]; const auto& room = g_Level.Rooms[item->RoomNumber];
for (int neighborRoomNumber : room.neighbors) for (int neighborRoomNumber : room.NeighborRoomNumbers)
{ {
auto& neighborRoom = g_Level.Rooms[neighborRoomNumber]; auto& neighborRoom = g_Level.Rooms[neighborRoomNumber];
if (!neighborRoom.Active()) if (!neighborRoom.Active())

View file

@ -30,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);

View file

@ -11,13 +11,11 @@
#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::Point;
using namespace TEN::Collision::Room; 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)
{ {
@ -299,8 +297,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
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
@ -339,7 +337,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
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);
pointColl = GetPointCollision(probePos, topRoomNumber); pointColl = GetPointCollision(probePos, topRoomNumber);
@ -394,7 +392,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
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);
pointColl = GetPointCollision(probePos, item->RoomNumber); pointColl = GetPointCollision(probePos, item->RoomNumber);
@ -460,7 +458,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
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);
pointColl = GetPointCollision(probePos, item->RoomNumber); pointColl = GetPointCollision(probePos, item->RoomNumber);
@ -807,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.
@ -849,7 +847,7 @@ 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;
@ -865,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)
@ -920,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);
@ -1083,246 +1081,6 @@ int GetDistanceToFloor(int itemNumber, bool precise)
return (minHeight + item.Pose.Position.y - height); return (minHeight + item.Pose.Position.y - height);
} }
int GetWaterSurface(int x, int y, int z, short roomNumber)
{
auto* room = &g_Level.Rooms[roomNumber];
auto* sector = GetSector(room, x - room->x, z - room->z);
if (TestEnvironment(ENV_FLAG_WATER, room))
{
while (sector->GetNextRoomNumber(Vector3i(x, y, z), false).has_value())
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room))
return (sector->GetSurfaceHeight(x, z, false));
sector = GetSector(room, x - room->x, z - room->z);
}
return NO_HEIGHT;
}
else
{
while (sector->GetNextRoomNumber(Vector3i(x, y, z), true).has_value())
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room))
return (sector->GetSurfaceHeight(x, z, true));
sector = GetSector(room, x - room->x, z - room->z);
}
}
return NO_HEIGHT;
}
int GetWaterSurface(ItemInfo* item)
{
return GetWaterSurface(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
}
int GetWaterDepth(int x, int y, int z, short roomNumber)
{
FloorInfo* sector = nullptr;
auto* room = &g_Level.Rooms[roomNumber];
int adjoiningRoomNumber = NO_VALUE;
do
{
int xFloor = (x - room->x) / BLOCK(1);
int zFloor = (z - room->z) / BLOCK(1);
if (zFloor <= 0)
{
zFloor = 0;
if (xFloor < 1)
{
xFloor = 1;
}
else if (xFloor > (room->xSize - 2))
{
xFloor = room->xSize - 2;
}
}
else if (zFloor >= (room->zSize - 1))
{
zFloor = room->zSize - 1;
if (xFloor < 1)
{
xFloor = 1;
}
else if (xFloor > (room->xSize - 2))
{
xFloor = room->xSize - 2;
}
}
else if (xFloor < 0)
{
xFloor = 0;
}
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 (adjoiningRoomNumber != NO_VALUE);
if (TestEnvironment(ENV_FLAG_WATER, room) ||
TestEnvironment(ENV_FLAG_SWAMP, room))
{
while (sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(NO_VALUE) != NO_VALUE)
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room) &&
!TestEnvironment(ENV_FLAG_SWAMP, room))
{
int waterHeight = sector->GetSurfaceHeight(x, z, false);
int floorHeight = GetPointCollision(Vector3i(x, y, z), sector->RoomNumber).GetBottomSector().GetSurfaceHeight(x, z, true);
return (floorHeight - waterHeight);
}
sector = GetSector(room, x - room->x, z - room->z);
}
return DEEP_WATER;
}
else
{
while (sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(NO_VALUE) != NO_VALUE)
{
room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room) ||
TestEnvironment(ENV_FLAG_SWAMP, room))
{
int waterHeight = sector->GetSurfaceHeight(x, z, true);
sector = GetFloor(x, y, z, &roomNumber);
return (GetFloorHeight(sector, x, y, z) - waterHeight);
}
sector = GetSector(room, x - room->x, z - room->z);
}
return NO_HEIGHT;
}
}
int GetWaterDepth(ItemInfo* item)
{
return GetWaterDepth(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
}
int GetWaterHeight(int x, int y, int z, short roomNumber)
{
FloorInfo* sector = nullptr;
auto* room = &g_Level.Rooms[roomNumber];
int adjoiningRoomNumber = NO_VALUE;
do
{
int xBlock = (x - room->x) / BLOCK(1);
int zBlock = (z - room->z) / BLOCK(1);
if (zBlock <= 0)
{
zBlock = 0;
if (xBlock < 1)
{
xBlock = 1;
}
else if (xBlock > (room->xSize - 2))
{
xBlock = room->xSize - 2;
}
}
else if (zBlock >= (room->zSize - 1))
{
zBlock = room->zSize - 1;
if (xBlock < 1)
{
xBlock = 1;
}
else if (xBlock > (room->xSize - 2))
{
xBlock = room->xSize - 2;
}
}
else if (xBlock < 0)
{
xBlock = 0;
}
else if (xBlock >= room->xSize)
{
xBlock = room->xSize - 1;
}
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;
if (TestEnvironment(ENV_FLAG_WATER, room) ||
TestEnvironment(ENV_FLAG_SWAMP, room))
{
while (sector->GetNextRoomNumber(Vector3i(x, y, z), false).has_value())
{
auto* room = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), false).value_or(sector->RoomNumber)];
if (!TestEnvironment(ENV_FLAG_WATER, room) &&
!TestEnvironment(ENV_FLAG_SWAMP, room))
{
break;
}
sector = GetSector(room, x - room->x, z - room->z);
}
return sector->GetSurfaceHeight(Vector3i(x, y, z), false);
}
else if (sector->GetNextRoomNumber(Vector3i(x, y, z), true).has_value())
{
while (sector->GetNextRoomNumber(Vector3i(x, y, z), true).has_value())
{
auto* room2 = &g_Level.Rooms[sector->GetNextRoomNumber(Vector3i(x, y, z), true).value_or(sector->RoomNumber)];
if (TestEnvironment(ENV_FLAG_WATER, room2) ||
TestEnvironment(ENV_FLAG_SWAMP, room2))
{
break;
}
sector = GetSector(room2, x - room2->x, z - room2->z);
}
return sector->GetSurfaceHeight(Vector3i(x, y, z), true);
}
return NO_HEIGHT;
}
int GetWaterHeight(ItemInfo* item)
{
return GetWaterHeight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
}
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, GetPointCollision(Vector3i(x, y, z), roomNumber).GetRoomNumber()); return TestEnvironment(environmentType, GetPointCollision(Vector3i(x, y, z), roomNumber).GetRoomNumber());
@ -1343,7 +1101,7 @@ bool TestEnvironment(RoomEnvFlags environmentType, int roomNumber)
return TestEnvironment(environmentType, &g_Level.Rooms[roomNumber]); return TestEnvironment(environmentType, &g_Level.Rooms[roomNumber]);
} }
bool TestEnvironment(RoomEnvFlags environmentType, ROOM_INFO* room) bool TestEnvironment(RoomEnvFlags environmentType, const ROOM_INFO* room)
{ {
return TestEnvironmentFlags(environmentType, room->flags); return TestEnvironmentFlags(environmentType, room->flags);
} }

View file

@ -137,13 +137,6 @@ int GetFloorHeight(FloorInfo* floor, int x, int y, int z);
int GetCeiling(FloorInfo* floor, int x, int y, int z); int GetCeiling(FloorInfo* floor, int x, int y, int z);
int GetDistanceToFloor(int itemNumber, bool precise = true); int GetDistanceToFloor(int itemNumber, bool precise = true);
int GetWaterSurface(int x, int y, int z, short roomNumber);
int GetWaterSurface(ItemInfo* item);
int GetWaterDepth(int x, int y, int z, short roomNumber);
int GetWaterDepth(ItemInfo* item);
int GetWaterHeight(int x, int y, int z, short roomNumber);
int GetWaterHeight(ItemInfo* item);
int FindGridShift(int x, int z); int FindGridShift(int x, int z);
void ShiftItem(ItemInfo* item, CollisionInfo* coll); void ShiftItem(ItemInfo* item, CollisionInfo* coll);
void SnapItemToLedge(ItemInfo* item, CollisionInfo* coll, float offsetMultiplier = 0.0f, bool snapToAngle = true); void SnapItemToLedge(ItemInfo* item, CollisionInfo* coll, float offsetMultiplier = 0.0f, bool snapToAngle = true);
@ -157,5 +150,5 @@ bool TestEnvironment(RoomEnvFlags environmentType, int x, int y, int z, int room
bool TestEnvironment(RoomEnvFlags environmentType, const 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, const ROOM_INFO* room);
bool TestEnvironmentFlags(RoomEnvFlags environmentType, int flags); bool TestEnvironmentFlags(RoomEnvFlags environmentType, int flags);

View file

@ -368,17 +368,17 @@ namespace TEN::Collision::Floordata
const auto& room = g_Level.Rooms[roomNumber]; const auto& room = g_Level.Rooms[roomNumber];
// Calculate room grid coord. // Calculate room grid coord.
auto roomGridCoord = Vector2i((x - room.x) / BLOCK(1), (z - room.z) / BLOCK(1)); auto roomGridCoord = Vector2i((x - room.Position.x) / BLOCK(1), (z - room.Position.z) / BLOCK(1));
if (x < room.x) if (x < room.Position.x)
roomGridCoord.x -= 1; roomGridCoord.x -= 1;
if (z < room.z) if (z < room.Position.z)
roomGridCoord.y -= 1; roomGridCoord.y -= 1;
// Clamp room grid coord to room bounds (if applicable). // Clamp room grid coord to room bounds (if applicable).
if (clampToBounds) if (clampToBounds)
{ {
roomGridCoord.x = std::clamp(roomGridCoord.x, 0, room.xSize - 1); roomGridCoord.x = std::clamp(roomGridCoord.x, 0, room.XSize - 1);
roomGridCoord.y = std::clamp(roomGridCoord.y, 0, room.zSize - 1); roomGridCoord.y = std::clamp(roomGridCoord.y, 0, room.ZSize - 1);
} }
return roomGridCoord; return roomGridCoord;
@ -397,8 +397,8 @@ namespace TEN::Collision::Floordata
const auto& room = g_Level.Rooms[roomNumber]; const auto& room = g_Level.Rooms[roomNumber];
// Search area out of range; return empty vector. // Search area out of range; return empty vector.
if (xMax <= 0 || xMin >= (room.xSize - 1) || if (xMax <= 0 || xMin >= (room.XSize - 1) ||
xMax <= 0 || xMin >= (room.xSize - 1)) xMax <= 0 || xMin >= (room.XSize - 1))
{ {
return {}; return {};
} }
@ -408,13 +408,13 @@ namespace TEN::Collision::Floordata
for (int x = xMin; x <= xMax; x++) for (int x = xMin; x <= xMax; x++)
{ {
// Test if out of room X range. // Test if out of room X range.
if (x <= 0 || x >= (room.xSize - 1)) if (x <= 0 || x >= (room.XSize - 1))
continue; continue;
for (int z = zMin; z <= zMax; z++) for (int z = zMin; z <= zMax; z++)
{ {
// Test if out of room Z range. // Test if out of room Z range.
if (z <= 0 || z >= (room.zSize - 1)) if (z <= 0 || z >= (room.ZSize - 1))
continue; continue;
roomGridCoords.push_back(Vector2i(x, z)); roomGridCoords.push_back(Vector2i(x, z));
@ -430,7 +430,7 @@ namespace TEN::Collision::Floordata
// 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.NeighborRoomNumbers)
{ {
// Collect neighbor sectors. // Collect neighbor sectors.
auto roomGridCoords = GetNeighborRoomGridCoords(pos, neighborRoomNumber, searchDepth); auto roomGridCoords = GetNeighborRoomGridCoords(pos, neighborRoomNumber, searchDepth);
@ -446,8 +446,8 @@ namespace TEN::Collision::Floordata
{ {
auto& room = g_Level.Rooms[roomNumber]; auto& room = g_Level.Rooms[roomNumber];
int sectorID = (room.zSize * roomGridCoord.x) + roomGridCoord.y; int sectorID = (room.ZSize * roomGridCoord.x) + roomGridCoord.y;
return room.floor[sectorID]; return room.Sectors[sectorID];
} }
FloorInfo& GetFloor(int roomNumber, int x, int z) FloorInfo& GetFloor(int roomNumber, int x, int z)
@ -831,18 +831,18 @@ namespace TEN::Collision::Floordata
const auto& room = g_Level.Rooms[item.RoomNumber]; const auto& room = g_Level.Rooms[item.RoomNumber];
// Get projected AABB min and max of bridge OBB. // Get projected AABB min and max of bridge OBB.
float xMin = floor((std::min(std::min(std::min(corners[0].x, corners[1].x), corners[4].x), corners[5].x) - room.x) / BLOCK(1)); float xMin = floor((std::min(std::min(std::min(corners[0].x, corners[1].x), corners[4].x), corners[5].x) - room.Position.x) / BLOCK(1));
float zMin = floor((std::min(std::min(std::min(corners[0].z, corners[1].z), corners[4].z), corners[5].z) - room.z) / BLOCK(1)); float zMin = floor((std::min(std::min(std::min(corners[0].z, corners[1].z), corners[4].z), corners[5].z) - room.Position.z) / BLOCK(1));
float xMax = ceil((std::max(std::max(std::max(corners[0].x, corners[1].x), corners[4].x), corners[5].x) - room.x) / BLOCK(1)); float xMax = ceil((std::max(std::max(std::max(corners[0].x, corners[1].x), corners[4].x), corners[5].x) - room.Position.x) / BLOCK(1));
float zMax = ceil((std::max(std::max(std::max(corners[0].z, corners[1].z), corners[4].z), corners[5].z) - room.z) / BLOCK(1)); float zMax = ceil((std::max(std::max(std::max(corners[0].z, corners[1].z), corners[4].z), corners[5].z) - room.Position.z) / BLOCK(1));
// Run through sectors enclosed in projected bridge AABB. // Run through sectors enclosed in projected bridge AABB.
for (int x = 0; x < room.xSize; x++) for (int x = 0; x < room.XSize; x++)
{ {
for (int z = 0; z < room.zSize; z++) for (int z = 0; z < room.ZSize; z++)
{ {
float pX = (room.x + BLOCK(x)) + BLOCK(0.5f); float pX = (room.Position.x + BLOCK(x)) + BLOCK(0.5f);
float pZ = (room.z + BLOCK(z)) + BLOCK(0.5f); float pZ = (room.Position.z + BLOCK(z)) + BLOCK(0.5f);
float offX = pX - item.Pose.Position.x; float offX = pX - item.Pose.Position.x;
float offZ = pZ - item.Pose.Position.z; float offZ = pZ - item.Pose.Position.z;
@ -889,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);
} }
} }
@ -911,7 +911,7 @@ namespace TEN::Collision::Floordata
// Run through neighboring rooms. // Run through neighboring rooms.
const auto& room = g_Level.Rooms[item.RoomNumber]; const auto& room = g_Level.Rooms[item.RoomNumber];
for (int neighborRoomNumber : room.neighbors) for (int neighborRoomNumber : room.NeighborRoomNumbers)
{ {
const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber]; const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber];
@ -919,8 +919,8 @@ namespace TEN::Collision::Floordata
auto roomGridCoords = GetNeighborRoomGridCoords(item.Pose.Position, neighborRoomNumber, SECTOR_SEARCH_DEPTH); auto roomGridCoords = GetNeighborRoomGridCoords(item.Pose.Position, neighborRoomNumber, SECTOR_SEARCH_DEPTH);
for (const auto& roomGridCoord : roomGridCoords) for (const auto& roomGridCoord : roomGridCoords)
{ {
pos.x = BLOCK(roomGridCoord.x) + neighborRoom.x; pos.x = BLOCK(roomGridCoord.x) + neighborRoom.Position.x;
pos.z = BLOCK(roomGridCoord.y) + neighborRoom.z; pos.z = BLOCK(roomGridCoord.y) + neighborRoom.Position.z;
pointColl = GetPointCollision(pos, neighborRoomNumber); pointColl = GetPointCollision(pos, neighborRoomNumber);
pos.y = pointColl.GetFloorHeight(); pos.y = pointColl.GetFloorHeight();

View file

@ -136,16 +136,18 @@ class FloorInfo
{ {
public: public:
// Members // 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;

View file

@ -21,7 +21,6 @@
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Objects/Generic/Object/Pushable/PushableObject.h" #include "Objects/Generic/Object/Pushable/PushableObject.h"
#include "Renderer/Renderer.h"
using namespace TEN::Collision::Point; using namespace TEN::Collision::Point;
using namespace TEN::Collision::Room; using namespace TEN::Collision::Room;
@ -55,7 +54,7 @@ void DrawBox(int boxIndex, Vector3 color)
if (boxIndex == NO_VALUE) if (boxIndex == NO_VALUE)
return; return;
auto& currBox = g_Level.Boxes[boxIndex]; auto& currBox = g_Level.PathfindingBoxes[boxIndex];
float x = ((float)currBox.left + (float)(currBox.right - currBox.left) / 2.0f) * 1024.0f; float x = ((float)currBox.left + (float)(currBox.right - currBox.left) / 2.0f) * 1024.0f;
auto y = currBox.height - CLICK(1); auto y = currBox.height - CLICK(1);
@ -69,7 +68,7 @@ void DrawBox(int boxIndex, Vector3 color)
for (int i = 0; i <= 10; i++) for (int i = 0; i <= 10; i++)
{ {
dBox.Extents = extents + Vector3(i); dBox.Extents = extents + Vector3(i);
TEN::Renderer::g_Renderer.AddDebugBox(dBox, Vector4(color.x, color.y, color.z, 1), RendererDebugPage::PathfindingStats); DrawDebugBox(dBox, Vector4(color.x, color.y, color.z, 1), RendererDebugPage::PathfindingStats);
} }
} }
@ -78,7 +77,7 @@ void DrawNearbyPathfinding(int boxIndex)
if (boxIndex == NO_VALUE) if (boxIndex == NO_VALUE)
return; return;
auto& currBox = g_Level.Boxes[boxIndex]; auto& currBox = g_Level.PathfindingBoxes[boxIndex];
auto index = currBox.overlapIndex; auto index = currBox.overlapIndex;
// Grey flag box. // Grey flag box.
@ -155,13 +154,13 @@ bool SameZone(CreatureInfo* creature, ItemInfo* target)
auto* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data(); auto* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data();
auto& roomSource = g_Level.Rooms[item.RoomNumber]; auto& roomSource = g_Level.Rooms[item.RoomNumber];
auto& boxSource = GetSector(&roomSource, item.Pose.Position.x - roomSource.x, item.Pose.Position.z - roomSource.z)->Box; auto& boxSource = GetSector(&roomSource, item.Pose.Position.x - roomSource.Position.x, item.Pose.Position.z - roomSource.Position.z)->PathfindingBoxID;
if (boxSource == NO_VALUE) if (boxSource == NO_VALUE)
return false; return false;
item.BoxNumber = boxSource; item.BoxNumber = boxSource;
auto& roomTarget = g_Level.Rooms[target->RoomNumber]; auto& roomTarget = g_Level.Rooms[target->RoomNumber];
auto& boxTarget = GetSector(&roomTarget, target->Pose.Position.x - roomTarget.x, target->Pose.Position.z - roomTarget.z)->Box; auto& boxTarget = GetSector(&roomTarget, target->Pose.Position.x - roomTarget.Position.x, target->Pose.Position.z - roomTarget.Position.z)->PathfindingBoxID;
if (boxTarget == NO_VALUE) if (boxTarget == NO_VALUE)
return false; return false;
target->BoxNumber = boxTarget; target->BoxNumber = boxTarget;
@ -257,7 +256,7 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt)
int boxHeight; int boxHeight;
if (item->BoxNumber != NO_VALUE) if (item->BoxNumber != NO_VALUE)
boxHeight = g_Level.Boxes[item->BoxNumber].height; boxHeight = g_Level.PathfindingBoxes[item->BoxNumber].height;
else else
boxHeight = item->Floor; boxHeight = item->Floor;
@ -267,31 +266,31 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt)
GetFloor(prevPos.x, y, prevPos.z, &roomNumber); GetFloor(prevPos.x, y, prevPos.z, &roomNumber);
auto* floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber); auto* floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber);
if (floor->Box == NO_VALUE) if (floor->PathfindingBoxID == NO_VALUE)
return false; return false;
int height = g_Level.Boxes[floor->Box].height; int height = g_Level.PathfindingBoxes[floor->PathfindingBoxID].height;
int nextHeight = 0; int nextHeight = 0;
int nextBox; int nextBox;
if (!Objects[item->ObjectNumber].nonLot) if (!Objects[item->ObjectNumber].nonLot)
{ {
nextBox = LOT->Node[floor->Box].exitBox; nextBox = LOT->Node[floor->PathfindingBoxID].exitBox;
} }
else else
{ {
floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
height = g_Level.Boxes[floor->Box].height; height = g_Level.PathfindingBoxes[floor->PathfindingBoxID].height;
nextBox = floor->Box; nextBox = floor->PathfindingBoxID;
} }
if (nextBox == NO_VALUE) if (nextBox == NO_VALUE)
nextHeight = height; nextHeight = height;
else else
nextHeight = g_Level.Boxes[nextBox].height; nextHeight = g_Level.PathfindingBoxes[nextBox].height;
if (floor->Box == NO_VALUE || !LOT->IsJumping && if (floor->PathfindingBoxID == NO_VALUE || !LOT->IsJumping &&
(LOT->Fly == NO_FLYING && item->BoxNumber != NO_VALUE && zone[item->BoxNumber] != zone[floor->Box] || (LOT->Fly == NO_FLYING && item->BoxNumber != NO_VALUE && zone[item->BoxNumber] != zone[floor->PathfindingBoxID] ||
boxHeight - height > LOT->Step || boxHeight - height > LOT->Step ||
boxHeight - height < LOT->Drop)) boxHeight - height < LOT->Drop))
{ {
@ -311,22 +310,22 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt)
item->Pose.Position.z = prevPos.z | WALL_MASK; item->Pose.Position.z = prevPos.z | WALL_MASK;
floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber); floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber);
height = g_Level.Boxes[floor->Box].height; height = g_Level.PathfindingBoxes[floor->PathfindingBoxID].height;
if (!Objects[item->ObjectNumber].nonLot) if (!Objects[item->ObjectNumber].nonLot)
{ {
nextBox = LOT->Node[floor->Box].exitBox; nextBox = LOT->Node[floor->PathfindingBoxID].exitBox;
} }
else else
{ {
floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
height = g_Level.Boxes[floor->Box].height; height = g_Level.PathfindingBoxes[floor->PathfindingBoxID].height;
nextBox = floor->Box; nextBox = floor->PathfindingBoxID;
} }
if (nextBox == NO_VALUE) if (nextBox == NO_VALUE)
nextHeight = height; nextHeight = height;
else else
nextHeight = g_Level.Boxes[nextBox].height; nextHeight = g_Level.PathfindingBoxes[nextBox].height;
} }
int x = item->Pose.Position.x; int x = item->Pose.Position.x;
@ -614,7 +613,7 @@ void CreatureUnderwater(ItemInfo* item, int depth)
} }
else else
{ {
waterHeight = GetWaterHeight(item); waterHeight = GetPointCollision(*item).GetWaterTopHeight();
} }
int y = waterHeight + waterLevel; int y = waterHeight + waterLevel;
@ -648,7 +647,7 @@ void CreatureFloat(short itemNumber)
item->Pose.Orientation.x = 0; item->Pose.Orientation.x = 0;
int y = item->Pose.Position.y; int y = item->Pose.Position.y;
int waterLevel = GetWaterHeight(item); int waterLevel = GetPointCollision(*item).GetWaterTopHeight();
if (waterLevel == NO_HEIGHT) if (waterLevel == NO_HEIGHT)
return; return;
@ -795,7 +794,7 @@ void CreatureHealth(ItemInfo* item)
TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, &g_Level.Rooms[item->RoomNumber])) TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, &g_Level.Rooms[item->RoomNumber]))
{ {
auto bounds = GameBoundingBox(item); auto bounds = GameBoundingBox(item);
auto height = item->Pose.Position.y - GetWaterHeight(item); auto height = item->Pose.Position.y - GetPointCollision(*item).GetWaterTopHeight();
if (abs(bounds.Y1 + bounds.Y2) < height) if (abs(bounds.Y1 + bounds.Y2) < height)
DoDamage(item, INT_MAX); DoDamage(item, INT_MAX);
@ -857,13 +856,13 @@ void CreatureDie(int itemNumber, bool doExplosion, int flags)
bool BadFloor(int x, int y, int z, int boxHeight, int nextHeight, short roomNumber, LOTInfo* LOT) bool BadFloor(int x, int y, int z, int boxHeight, int nextHeight, short roomNumber, LOTInfo* LOT)
{ {
auto* floor = GetFloor(x, y, z, &roomNumber); auto* floor = GetFloor(x, y, z, &roomNumber);
if (floor->Box == NO_VALUE) if (floor->PathfindingBoxID == NO_VALUE)
return true; return true;
if (LOT->IsJumping) if (LOT->IsJumping)
return false; return false;
auto* box = &g_Level.Boxes[floor->Box]; auto* box = &g_Level.PathfindingBoxes[floor->PathfindingBoxID];
if (box->flags & LOT->BlockMask) if (box->flags & LOT->BlockMask)
return true; return true;
@ -928,7 +927,7 @@ bool ValidBox(ItemInfo* item, short zoneNumber, short boxNumber)
if (creature.LOT.Fly == NO_FLYING && zone[boxNumber] != zoneNumber) if (creature.LOT.Fly == NO_FLYING && zone[boxNumber] != zoneNumber)
return false; return false;
const auto& box = g_Level.Boxes[boxNumber]; const auto& box = g_Level.PathfindingBoxes[boxNumber];
if (creature.LOT.BlockMask & box.flags) if (creature.LOT.BlockMask & box.flags)
return false; return false;
@ -948,7 +947,7 @@ bool EscapeBox(ItemInfo* item, ItemInfo* enemy, int boxNumber)
if (boxNumber == NO_VALUE) if (boxNumber == NO_VALUE)
return false; return false;
const auto& box = g_Level.Boxes[boxNumber]; const auto& box = g_Level.PathfindingBoxes[boxNumber];
int x = ((box.top + box.bottom) * BLOCK(0.5f)) - enemy->Pose.Position.x; int x = ((box.top + box.bottom) * BLOCK(0.5f)) - enemy->Pose.Position.x;
int z = ((box.left + box.right) * BLOCK(0.5f)) - enemy->Pose.Position.z; int z = ((box.left + box.right) * BLOCK(0.5f)) - enemy->Pose.Position.z;
@ -966,7 +965,7 @@ void TargetBox(LOTInfo* LOT, int boxNumber)
{ {
if (boxNumber == NO_VALUE) if (boxNumber == NO_VALUE)
return; return;
auto* box = &g_Level.Boxes[boxNumber]; auto* box = &g_Level.PathfindingBoxes[boxNumber];
// Maximize target precision. DO NOT change bracket precedence! // Maximize target precision. DO NOT change bracket precedence!
LOT->Target.x = (int)((box->top * BLOCK(1)) + (float)GetRandomControl() * (((float)(box->bottom - box->top) - 1.0f) / 32.0f) + CLICK(2.0f)); LOT->Target.x = (int)((box->top * BLOCK(1)) + (float)GetRandomControl() * (((float)(box->bottom - box->top) - 1.0f) / 32.0f) + CLICK(2.0f));
@ -1016,7 +1015,7 @@ bool SearchLOT(LOTInfo* LOT, int depth)
return false; return false;
} }
auto* box = &g_Level.Boxes[LOT->Head]; auto* box = &g_Level.PathfindingBoxes[LOT->Head];
auto* node = &LOT->Node[LOT->Head]; auto* node = &LOT->Node[LOT->Head];
int index = box->overlapIndex; int index = box->overlapIndex;
@ -1034,7 +1033,7 @@ bool SearchLOT(LOTInfo* LOT, int depth)
if (LOT->Fly == NO_FLYING && searchZone != zone[boxNumber]) if (LOT->Fly == NO_FLYING && searchZone != zone[boxNumber])
continue; continue;
int delta = g_Level.Boxes[boxNumber].height - box->height; int delta = g_Level.PathfindingBoxes[boxNumber].height - box->height;
if ((delta > LOT->Step || delta < LOT->Drop) && (!(flags & BOX_MONKEY) || !LOT->CanMonkey)) if ((delta > LOT->Step || delta < LOT->Drop) && (!(flags & BOX_MONKEY) || !LOT->CanMonkey))
continue; continue;
@ -1057,7 +1056,7 @@ bool SearchLOT(LOTInfo* LOT, int depth)
if ((node->searchNumber & SEARCH_NUMBER) == (expand->searchNumber & SEARCH_NUMBER) && !(expand->searchNumber & BLOCKED_SEARCH)) if ((node->searchNumber & SEARCH_NUMBER) == (expand->searchNumber & SEARCH_NUMBER) && !(expand->searchNumber & BLOCKED_SEARCH))
continue; continue;
if (g_Level.Boxes[boxNumber].flags & LOT->BlockMask) if (g_Level.PathfindingBoxes[boxNumber].flags & LOT->BlockMask)
{ {
expand->searchNumber = node->searchNumber | BLOCKED_SEARCH; expand->searchNumber = node->searchNumber | BLOCKED_SEARCH;
} }
@ -1145,7 +1144,7 @@ bool StalkBox(ItemInfo* item, ItemInfo* enemy, int boxNumber)
{ {
if (enemy == nullptr || boxNumber == NO_VALUE) if (enemy == nullptr || boxNumber == NO_VALUE)
return false; return false;
auto* box = &g_Level.Boxes[boxNumber]; auto* box = &g_Level.PathfindingBoxes[boxNumber];
int xRange = STALK_DIST + ((box->bottom - box->top) * BLOCK(1)); int xRange = STALK_DIST + ((box->bottom - box->top) * BLOCK(1));
int zRange = STALK_DIST + ((box->right - box->left) * BLOCK(1)); int zRange = STALK_DIST + ((box->right - box->left) * BLOCK(1));
@ -1461,9 +1460,9 @@ void FindAITargetObject(CreatureInfo* creature, int objectNumber, int ocb, bool
int* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data(); int* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data();
auto* room = &g_Level.Rooms[item.RoomNumber]; auto* room = &g_Level.Rooms[item.RoomNumber];
item.BoxNumber = GetSector(room, item.Pose.Position.x - room->x, item.Pose.Position.z - room->z)->Box; item.BoxNumber = GetSector(room, item.Pose.Position.x - room->Position.x, item.Pose.Position.z - room->Position.z)->PathfindingBoxID;
room = &g_Level.Rooms[aiObject.roomNumber]; room = &g_Level.Rooms[aiObject.roomNumber];
aiObject.boxNumber = GetSector(room, aiObject.pos.Position.x - room->x, aiObject.pos.Position.z - room->z)->Box; aiObject.boxNumber = GetSector(room, aiObject.pos.Position.x - room->Position.x, aiObject.pos.Position.z - room->Position.z)->PathfindingBoxID;
if (item.BoxNumber == NO_VALUE || aiObject.boxNumber == NO_VALUE) if (item.BoxNumber == NO_VALUE || aiObject.boxNumber == NO_VALUE)
return; return;
@ -1505,7 +1504,7 @@ int TargetReachable(ItemInfo* item, ItemInfo* enemy)
{ {
const auto& creature = *GetCreatureInfo(item); const auto& creature = *GetCreatureInfo(item);
auto& room = g_Level.Rooms[enemy->RoomNumber]; auto& room = g_Level.Rooms[enemy->RoomNumber];
auto* floor = GetSector(&room, enemy->Pose.Position.x - room.x, enemy->Pose.Position.z - room.z); auto* floor = GetSector(&room, enemy->Pose.Position.x - room.Position.x, enemy->Pose.Position.z - room.Position.z);
// NEW: Only update enemy box number if it is actually reachable by the enemy. // NEW: Only update enemy box number if it is actually reachable by the enemy.
// This prevents enemies from running to the player and attacking nothing when they are hanging or shimmying. -- Lwmte, 27.06.22 // This prevents enemies from running to the player and attacking nothing when they are hanging or shimmying. -- Lwmte, 27.06.22
@ -1524,7 +1523,7 @@ int TargetReachable(ItemInfo* item, ItemInfo* enemy)
isReachable = abs(enemy->Pose.Position.y - pointColl.GetFloorHeight()) < bounds.GetHeight(); isReachable = abs(enemy->Pose.Position.y - pointColl.GetFloorHeight()) < bounds.GetHeight();
} }
return (isReachable ? floor->Box : NO_VALUE); return (isReachable ? floor->PathfindingBoxID : NO_VALUE);
} }
void CreatureAIInfo(ItemInfo* item, AI_INFO* AI) void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
@ -1546,7 +1545,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
auto* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data(); auto* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data();
auto* room = &g_Level.Rooms[item->RoomNumber]; auto* room = &g_Level.Rooms[item->RoomNumber];
item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box; item->BoxNumber = GetSector(room, item->Pose.Position.x - room->Position.x, item->Pose.Position.z - room->Position.z)->PathfindingBoxID;
AI->zoneNumber = zone[item->BoxNumber]; AI->zoneNumber = zone[item->BoxNumber];
enemy->BoxNumber = TargetReachable(item, enemy); enemy->BoxNumber = TargetReachable(item, enemy);
@ -1554,7 +1553,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
if (!object->nonLot) if (!object->nonLot)
{ {
if (enemy->BoxNumber != NO_VALUE && g_Level.Boxes[enemy->BoxNumber].flags & creature->LOT.BlockMask) if (enemy->BoxNumber != NO_VALUE && g_Level.PathfindingBoxes[enemy->BoxNumber].flags & creature->LOT.BlockMask)
{ {
AI->enemyZone |= BLOCKED; AI->enemyZone |= BLOCKED;
} }
@ -1754,7 +1753,7 @@ void CreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent)
int endBox = LOT->Node[item->BoxNumber].exitBox; int endBox = LOT->Node[item->BoxNumber].exitBox;
if (endBox != NO_VALUE) if (endBox != NO_VALUE)
{ {
int overlapIndex = g_Level.Boxes[item->BoxNumber].overlapIndex; int overlapIndex = g_Level.PathfindingBoxes[item->BoxNumber].overlapIndex;
int nextBox = 0; int nextBox = 0;
int flags = 0; int flags = 0;
@ -1901,7 +1900,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
if (boxNumber == NO_VALUE) if (boxNumber == NO_VALUE)
return TARGET_TYPE::NO_TARGET; return TARGET_TYPE::NO_TARGET;
auto* box = &g_Level.Boxes[boxNumber]; auto* box = &g_Level.PathfindingBoxes[boxNumber];
int boxLeft = ((int)box->left * BLOCK(1)); int boxLeft = ((int)box->left * BLOCK(1));
int boxRight = ((int)box->right * BLOCK(1)) - 1; int boxRight = ((int)box->right * BLOCK(1)) - 1;
@ -1915,7 +1914,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
do do
{ {
box = &g_Level.Boxes[boxNumber]; box = &g_Level.PathfindingBoxes[boxNumber];
if (LOT->Fly != NO_FLYING) if (LOT->Fly != NO_FLYING)
{ {
@ -2096,7 +2095,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
} }
boxNumber = LOT->Node[boxNumber].exitBox; boxNumber = LOT->Node[boxNumber].exitBox;
if (boxNumber != NO_VALUE && (g_Level.Boxes[boxNumber].flags & LOT->BlockMask)) if (boxNumber != NO_VALUE && (g_Level.PathfindingBoxes[boxNumber].flags & LOT->BlockMask))
break; break;
} while (boxNumber != NO_VALUE); } while (boxNumber != NO_VALUE);
@ -2130,14 +2129,14 @@ void AdjustStopperFlag(ItemInfo* item, int direction)
int z = item->Pose.Position.z; int z = item->Pose.Position.z;
auto* room = &g_Level.Rooms[item->RoomNumber]; auto* room = &g_Level.Rooms[item->RoomNumber];
auto* floor = GetSector(room, x - room->x, z - room->z); auto* floor = GetSector(room, x - room->Position.x, z - room->Position.z);
floor->Stopper = !floor->Stopper; floor->Stopper = !floor->Stopper;
x = item->Pose.Position.x + BLOCK(1) * phd_sin(direction); x = item->Pose.Position.x + BLOCK(1) * phd_sin(direction);
z = item->Pose.Position.z + BLOCK(1) * phd_cos(direction); z = item->Pose.Position.z + BLOCK(1) * phd_cos(direction);
room = &g_Level.Rooms[GetPointCollision(Vector3i(x, item->Pose.Position.y, z), item->RoomNumber).GetRoomNumber()]; room = &g_Level.Rooms[GetPointCollision(Vector3i(x, item->Pose.Position.y, z), item->RoomNumber).GetRoomNumber()];
floor = GetSector(room, x - room->x, z - room->z); floor = GetSector(room, x - room->Position.x, z - room->Position.z);
floor->Stopper = !floor->Stopper; floor->Stopper = !floor->Stopper;
} }
@ -2152,15 +2151,15 @@ void InitializeItemBoxData()
{ {
for (const auto& mesh : room.mesh) for (const auto& mesh : room.mesh)
{ {
long index = ((mesh.pos.Position.z - room.z) / BLOCK(1)) + room.zSize * ((mesh.pos.Position.x - room.x) / BLOCK(1)); long index = ((mesh.pos.Position.z - room.Position.z) / BLOCK(1)) + room.ZSize * ((mesh.pos.Position.x - room.Position.x) / BLOCK(1));
if (index > room.floor.size()) if (index > room.Sectors.size())
continue; continue;
auto* floor = &room.floor[index]; auto* floor = &room.Sectors[index];
if (floor->Box == NO_VALUE) if (floor->PathfindingBoxID == NO_VALUE)
continue; continue;
if (!(g_Level.Boxes[floor->Box].flags & BLOCKED)) if (!(g_Level.PathfindingBoxes[floor->PathfindingBoxID].flags & BLOCKED))
{ {
int floorHeight = floor->GetSurfaceHeight(mesh.pos.Position.x, mesh.pos.Position.z, true); int floorHeight = floor->GetSurfaceHeight(mesh.pos.Position.x, mesh.pos.Position.z, true);
const auto& bBox = GetBoundsAccurate(mesh, false); const auto& bBox = GetBoundsAccurate(mesh, false);
@ -2185,19 +2184,7 @@ bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType)
if (creature.Enemy == nullptr) if (creature.Enemy == nullptr)
return false; return false;
float stepDist = 0.0f; float stepDist = BLOCK(0.92f);
switch (jumpDistType)
{
default:
case JumpDistance::Block1:
stepDist = BLOCK(0.51f);
break;
case JumpDistance::Block2:
stepDist = BLOCK(0.76f);
break;
}
int vPos = item.Pose.Position.y; int vPos = item.Pose.Position.y;
auto pointCollA = GetPointCollision(item, item.Pose.Orientation.y, stepDist); auto pointCollA = GetPointCollision(item, item.Pose.Orientation.y, stepDist);
auto pointCollB = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 2); auto pointCollB = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 2);

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "Specific/level.h" #include "Specific/level.h"
#include "Math/Math.h" #include "Math/Math.h"
@ -33,12 +34,14 @@ struct AI_INFO
short enemyFacing; short enemyFacing;
}; };
// TODO: Use DX BoundingBox class to store AABB.
struct BOX_INFO struct BOX_INFO
{ {
unsigned int left; unsigned int left;
unsigned int right; unsigned int right;
unsigned int top; unsigned int top;
unsigned int bottom; unsigned int bottom;
int height; int height;
int overlapIndex; int overlapIndex;
int flags; int flags;

View file

@ -77,8 +77,10 @@ using namespace TEN::Effects::Ripple;
using namespace TEN::Effects::Smoke; using namespace TEN::Effects::Smoke;
using namespace TEN::Effects::Spark; using namespace TEN::Effects::Spark;
using namespace TEN::Effects::Streamer; using namespace TEN::Effects::Streamer;
using namespace TEN::Entities::Creatures::TR3;
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Entities::Switches; using namespace TEN::Entities::Switches;
using namespace TEN::Entities::Traps;
using namespace TEN::Entities::TR4; using namespace TEN::Entities::TR4;
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
@ -86,7 +88,6 @@ using namespace TEN::Hud;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer; using namespace TEN::Renderer;
using namespace TEN::Traps::TR5;
using namespace TEN::Entities::Creatures::TR3; using namespace TEN::Entities::Creatures::TR3;
using namespace TEN::Entities::Effects; using namespace TEN::Entities::Effects;
@ -412,6 +413,7 @@ void KillMoveEffects()
ItemNewRoomNo = 0; ItemNewRoomNo = 0;
} }
// NOTE: No one should use this ever again.
int GetRandomControl() int GetRandomControl()
{ {
return Random::GenerateInt(); return Random::GenerateInt();

View file

@ -17,6 +17,7 @@ enum class GameStatus
{ {
Normal, Normal,
NewGame, NewGame,
HomeLevel,
LoadGame, LoadGame,
SaveGame, SaveGame,
ExitToTitle, ExitToTitle,

View file

@ -94,21 +94,17 @@ void ClearSwarmEnemies(ItemInfo* item)
void FlashOrange(ItemInfo* item) void FlashOrange(ItemInfo* item)
{ {
FlipEffect = -1; FlipEffect = NO_VALUE;
Weather.Flash(255, 128, 0, 0.03f); Weather.Flash(255, 128, 0, 0.03f);
} }
void MeshSwapToPour(ItemInfo* item) void MeshSwapToPour(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item);
item->Model.MeshIndex[LM_LHAND] = Objects[item->ItemFlags[2]].meshIndex + LM_LHAND; item->Model.MeshIndex[LM_LHAND] = Objects[item->ItemFlags[2]].meshIndex + LM_LHAND;
} }
void MeshSwapFromPour(ItemInfo* item) void MeshSwapFromPour(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item);
item->Model.MeshIndex[LM_LHAND] = item->Model.BaseMesh + LM_LHAND; item->Model.MeshIndex[LM_LHAND] = item->Model.BaseMesh + LM_LHAND;
} }
@ -149,60 +145,60 @@ void InvisibilityOn(ItemInfo* item)
void SetFog(ItemInfo* item) void SetFog(ItemInfo* item)
{ {
FlipEffect = -1; FlipEffect = NO_VALUE;
} }
void DrawLeftPistol(ItemInfo* item) void DrawLeftPistol(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
if (item->Model.MeshIndex[LM_LHAND] == item->Model.BaseMesh + LM_LHAND) if (item->Model.MeshIndex[LM_LHAND] == item->Model.BaseMesh + LM_LHAND)
{ {
item->Model.MeshIndex[LM_LHAND] = Objects[GetWeaponObjectMeshID(*item, LaraWeaponType::Pistol)].meshIndex + LM_LHAND; item->Model.MeshIndex[LM_LHAND] = Objects[GetWeaponObjectMeshID(*item, LaraWeaponType::Pistol)].meshIndex + LM_LHAND;
lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty; player.Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty;
} }
else else
{ {
item->Model.MeshIndex[LM_LHAND] = item->Model.BaseMesh + LM_LHAND; item->Model.MeshIndex[LM_LHAND] = item->Model.BaseMesh + LM_LHAND;
lara->Control.Weapon.HolsterInfo.LeftHolster = GetWeaponHolsterSlot(LaraWeaponType::Pistol); player.Control.Weapon.HolsterInfo.LeftHolster = GetWeaponHolsterSlot(LaraWeaponType::Pistol);
} }
} }
void DrawRightPistol(ItemInfo* item) void DrawRightPistol(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
if (item->Model.MeshIndex[LM_RHAND] == item->Model.BaseMesh + LM_RHAND) if (item->Model.MeshIndex[LM_RHAND] == item->Model.BaseMesh + LM_RHAND)
{ {
item->Model.MeshIndex[LM_RHAND] = Objects[GetWeaponObjectMeshID(*item, LaraWeaponType::Pistol)].meshIndex + LM_RHAND; item->Model.MeshIndex[LM_RHAND] = Objects[GetWeaponObjectMeshID(*item, LaraWeaponType::Pistol)].meshIndex + LM_RHAND;
lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty; player.Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty;
} }
else else
{ {
item->Model.MeshIndex[LM_RHAND] = item->Model.BaseMesh + LM_RHAND; item->Model.MeshIndex[LM_RHAND] = item->Model.BaseMesh + LM_RHAND;
lara->Control.Weapon.HolsterInfo.RightHolster = GetWeaponHolsterSlot(LaraWeaponType::Pistol); player.Control.Weapon.HolsterInfo.RightHolster = GetWeaponHolsterSlot(LaraWeaponType::Pistol);
} }
} }
void ShootLeftGun(ItemInfo* item) void ShootLeftGun(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->LeftArm.GunFlash = 3; player.LeftArm.GunFlash = 3;
} }
void ShootRightGun(ItemInfo* item) void ShootRightGun(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->RightArm.GunFlash = 3; player.RightArm.GunFlash = 3;
} }
void LaraHandsFree(ItemInfo* item) void LaraHandsFree(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto& player = GetLaraInfo(*item);
lara->Control.HandStatus = HandStatus::Free; player.Control.HandStatus = HandStatus::Free;
} }
void KillActiveBaddys(ItemInfo* item) void KillActiveBaddys(ItemInfo* item)
@ -231,14 +227,14 @@ void KillActiveBaddys(ItemInfo* item)
} while (itemNumber != NO_VALUE); } while (itemNumber != NO_VALUE);
} }
FlipEffect = -1; FlipEffect = NO_VALUE;
} }
void LaraLocationPad(ItemInfo* item) void LaraLocationPad(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
FlipEffect = -1; FlipEffect = NO_VALUE;
lara->Location = TriggerTimer; lara->Location = TriggerTimer;
lara->LocationPad = TriggerTimer; lara->LocationPad = TriggerTimer;
@ -248,7 +244,7 @@ void LaraLocation(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
FlipEffect = -1; FlipEffect = NO_VALUE;
lara->Location = TriggerTimer; lara->Location = TriggerTimer;
if (lara->HighestLocation < TriggerTimer) if (lara->HighestLocation < TriggerTimer)
@ -259,17 +255,19 @@ void ExplosionFX(ItemInfo* item)
{ {
SoundEffect(SFX_TR4_EXPLOSION1, nullptr); SoundEffect(SFX_TR4_EXPLOSION1, nullptr);
Camera.bounce = -75; Camera.bounce = -75;
FlipEffect = -1; FlipEffect = NO_VALUE;
} }
void SwapCrowbar(ItemInfo* item) void SwapCrowbar(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item);
if (item->Model.MeshIndex[LM_RHAND] == item->Model.BaseMesh + LM_RHAND) if (item->Model.MeshIndex[LM_RHAND] == item->Model.BaseMesh + LM_RHAND)
{
item->Model.MeshIndex[LM_RHAND] = Objects[ID_LARA_CROWBAR_ANIM].meshIndex + LM_RHAND; item->Model.MeshIndex[LM_RHAND] = Objects[ID_LARA_CROWBAR_ANIM].meshIndex + LM_RHAND;
}
else else
{
item->Model.MeshIndex[LM_RHAND] = item->Model.BaseMesh + LM_RHAND; item->Model.MeshIndex[LM_RHAND] = item->Model.BaseMesh + LM_RHAND;
}
} }
void ActivateKey(ItemInfo* item) void ActivateKey(ItemInfo* item)
@ -285,7 +283,7 @@ void ActivateCamera(ItemInfo* item)
void PoseidonSFX(ItemInfo* item) void PoseidonSFX(ItemInfo* item)
{ {
SoundEffect(SFX_TR4_WATER_FLUSHES, nullptr); SoundEffect(SFX_TR4_WATER_FLUSHES, nullptr);
FlipEffect = -1; FlipEffect = NO_VALUE;
} }
void RubbleFX(ItemInfo* item) void RubbleFX(ItemInfo* item)
@ -303,13 +301,13 @@ void RubbleFX(ItemInfo* item)
else else
Camera.bounce = -150; Camera.bounce = -150;
FlipEffect = -1; FlipEffect = NO_VALUE;
} }
void PlaySoundEffect(ItemInfo* item) void PlaySoundEffect(ItemInfo* item)
{ {
SoundEffect(TriggerTimer, nullptr); SoundEffect(TriggerTimer, nullptr);
FlipEffect = -1; FlipEffect = NO_VALUE;
} }
void FloorShake(ItemInfo* item) void FloorShake(ItemInfo* item)

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "Game/items.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/items.h"
#define EffectFunction void(ItemInfo* item) #define EffectFunction void(ItemInfo* item)
@ -10,35 +11,35 @@ extern int FlipEffect;
extern std::function<EffectFunction> effect_routines[]; extern std::function<EffectFunction> effect_routines[];
void AddLeftFootprint(ItemInfo* item); void AddLeftFootprint(ItemInfo* item); // TODO: To anim command.
void AddRightFootprint(ItemInfo* item); void AddRightFootprint(ItemInfo* item); // TODO: To anim command.
void VoidEffect(ItemInfo* item); void VoidEffect(ItemInfo* item);
void FinishLevel(ItemInfo* item); void FinishLevel(ItemInfo* item);
void Turn180(ItemInfo* item); void Turn180(ItemInfo* item); // TODO: To anim command.
void FloorShake(ItemInfo* item); void FloorShake(ItemInfo* item);
void PlaySoundEffect(ItemInfo* item); void PlaySoundEffect(ItemInfo* item);
void RubbleFX(ItemInfo* item); void RubbleFX(ItemInfo* item);
void PoseidonSFX(ItemInfo* item); void PoseidonSFX(ItemInfo* item);
void ActivateCamera(ItemInfo* item); void ActivateCamera(ItemInfo* item);
void ActivateKey(ItemInfo* item); void ActivateKey(ItemInfo* item);
void SwapCrowbar(ItemInfo* item); void SwapCrowbar(ItemInfo* item); // TODO: To anim command.
void ExplosionFX(ItemInfo* item); void ExplosionFX(ItemInfo* item);
void LaraLocation(ItemInfo* item); void LaraLocation(ItemInfo* item);
void LaraLocationPad(ItemInfo* item); void LaraLocationPad(ItemInfo* item);
void KillActiveBaddys(ItemInfo* item); void KillActiveBaddys(ItemInfo* item);
void LaraHandsFree(ItemInfo* item); void LaraHandsFree(ItemInfo* item); // TODO: To anim command.
void ShootRightGun(ItemInfo* item); void ShootRightGun(ItemInfo* item); // TODO: To anim command.
void ShootLeftGun(ItemInfo* item); void ShootLeftGun(ItemInfo* item); // TODO: To anim command.
void SetFog(ItemInfo* item); void SetFog(ItemInfo* item);
void InvisibilityOn(ItemInfo* item); void InvisibilityOn(ItemInfo* item);
void InvisibilityOff(ItemInfo* item); void InvisibilityOff(ItemInfo* item);
void ResetHair(ItemInfo* item); void ResetHair(ItemInfo* item);
void Pickup(ItemInfo* item); void Pickup(ItemInfo* item); // TODO: To anim command.
void Puzzle(ItemInfo* item); void Puzzle(ItemInfo* item); // TODO: To anim command.
void DrawRightPistol(ItemInfo* item); void DrawRightPistol(ItemInfo* item); // TODO: To anim command.
void DrawLeftPistol(ItemInfo* item); void DrawLeftPistol(ItemInfo* item); // TODO: To anim command.
void MeshSwapToPour(ItemInfo* item); void MeshSwapToPour(ItemInfo* item); // TODO: To anim command.
void MeshSwapFromPour(ItemInfo* item); void MeshSwapFromPour(ItemInfo* item); // TODO: To anim command.
void FlashOrange(ItemInfo* item); void FlashOrange(ItemInfo* item);
void ClearSwarmEnemies(ItemInfo* item); void ClearSwarmEnemies(ItemInfo* item);

View file

@ -784,7 +784,7 @@ std::optional<Vector3> GetStaticObjectLos(const Vector3& origin, int roomNumber,
{ {
// Run through neighbor rooms. // Run through neighbor rooms.
const auto& room = g_Level.Rooms[roomNumber]; const auto& room = g_Level.Rooms[roomNumber];
for (int neighborRoomNumber : room.neighbors) for (int neighborRoomNumber : room.NeighborRoomNumbers)
{ {
// Get neighbor room. // Get neighbor room.
const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber]; const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber];

View file

@ -25,7 +25,7 @@ void InitializeLOTarray(int itemNumber)
if (!creature->LOT.Initialized) if (!creature->LOT.Initialized)
{ {
creature->LOT.Node = std::vector<BoxNode>(g_Level.Boxes.size(), BoxNode{}); creature->LOT.Node = std::vector<BoxNode>(g_Level.PathfindingBoxes.size(), BoxNode{});
creature->LOT.Initialized = true; creature->LOT.Initialized = true;
} }
} }
@ -240,14 +240,14 @@ void CreateZone(ItemInfo* item)
auto* creature = GetCreatureInfo(item); auto* creature = GetCreatureInfo(item);
auto* room = &g_Level.Rooms[item->RoomNumber]; auto* room = &g_Level.Rooms[item->RoomNumber];
item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box; item->BoxNumber = GetSector(room, item->Pose.Position.x - room->Position.x, item->Pose.Position.z - room->Position.z)->PathfindingBoxID;
if (creature->LOT.Fly) if (creature->LOT.Fly)
{ {
auto* node = creature->LOT.Node.data(); auto* node = creature->LOT.Node.data();
creature->LOT.ZoneCount = 0; creature->LOT.ZoneCount = 0;
for (int i = 0; i < g_Level.Boxes.size(); i++) for (int i = 0; i < g_Level.PathfindingBoxes.size(); i++)
{ {
node->boxNumber = i; node->boxNumber = i;
node++; node++;
@ -265,7 +265,7 @@ void CreateZone(ItemInfo* item)
auto* node = creature->LOT.Node.data(); auto* node = creature->LOT.Node.data();
creature->LOT.ZoneCount = 0; creature->LOT.ZoneCount = 0;
for (int i = 0; i < g_Level.Boxes.size(); i++) for (int i = 0; i < g_Level.PathfindingBoxes.size(); i++)
{ {
if (*zone == zoneNumber || *flippedZone == flippedZoneNumber) if (*zone == zoneNumber || *flippedZone == flippedZoneNumber)
{ {

View file

@ -10,12 +10,8 @@
#include "Game/room.h" #include "Game/room.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h" #include "Game/Setup.h"
#include "Renderer/Renderer.h"
#include "Renderer/RendererEnums.h"
#include "Scripting/Include/ScriptInterfaceGame.h" #include "Scripting/Include/ScriptInterfaceGame.h"
using TEN::Renderer::g_Renderer;
namespace TEN::Control::Volumes namespace TEN::Control::Volumes
{ {
constexpr auto CAM_SIZE = 32; constexpr auto CAM_SIZE = 32;
@ -30,7 +26,7 @@ namespace TEN::Control::Volumes
case VolumeType::Box: case VolumeType::Box:
if (roomNumber == Camera.pos.RoomNumber) if (roomNumber == Camera.pos.RoomNumber)
{ {
g_Renderer.AddDebugBox(volume.Box, DrawDebugBox(volume.Box,
Vector4(color, 0.0f, color, 1.0f), RendererDebugPage::CollisionStats); Vector4(color, 0.0f, color, 1.0f), RendererDebugPage::CollisionStats);
} }
return volume.Box.Intersects(box); return volume.Box.Intersects(box);
@ -38,7 +34,7 @@ namespace TEN::Control::Volumes
case VolumeType::Sphere: case VolumeType::Sphere:
if (roomNumber == Camera.pos.RoomNumber) if (roomNumber == Camera.pos.RoomNumber)
{ {
g_Renderer.AddDebugSphere(volume.Sphere.Center, volume.Sphere.Radius, DrawDebugSphere(volume.Sphere.Center, volume.Sphere.Radius,
Vector4(color, 0.0f, color, 1.0f), RendererDebugPage::CollisionStats); Vector4(color, 0.0f, color, 1.0f), RendererDebugPage::CollisionStats);
} }
return volume.Sphere.Intersects(box); return volume.Sphere.Intersects(box);
@ -143,14 +139,14 @@ namespace TEN::Control::Volumes
if (roomNumber == NO_VALUE) if (roomNumber == NO_VALUE)
return; return;
for (int currentRoomIndex : g_Level.Rooms[roomNumber].neighbors) for (int currentRoomIndex : g_Level.Rooms[roomNumber].NeighborRoomNumbers)
{ {
auto& room = g_Level.Rooms[currentRoomIndex]; auto& room = g_Level.Rooms[currentRoomIndex];
if (!room.Active()) if (!room.Active())
continue; continue;
for (auto& volume : room.triggerVolumes) for (auto& volume : room.TriggerVolumes)
{ {
if (!volume.Enabled) if (!volume.Enabled)
continue; continue;
@ -252,7 +248,7 @@ namespace TEN::Control::Volumes
auto box = (coll != nullptr) ? auto box = (coll != nullptr) ?
ConstructRoughBox(item, *coll) : GameBoundingBox(&item).ToBoundingOrientedBox(item.Pose); ConstructRoughBox(item, *coll) : GameBoundingBox(&item).ToBoundingOrientedBox(item.Pose);
g_Renderer.AddDebugBox(box, Vector4(1.0f, 1.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats); DrawDebugBox(box, Vector4(1.0f, 1.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats);
if (item.IsLara() || item.Index == Lara.Context.Vehicle) if (item.IsLara() || item.Index == Lara.Context.Vehicle)
{ {

View file

@ -1,61 +1,159 @@
#include "framework.h" #include "framework.h"
#include "Game/debug/debug.h" #include "Game/Debug/Debug.h"
#include <chrono>
#include <spdlog.h> #include <spdlog.h>
#include <spdlog/sinks/basic_file_sink.h> #include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/stdout_color_sinks.h>
#include <stdarg.h>
void InitTENLog(const std::string& logDirContainingDir) #include "Renderer/Renderer.h"
using TEN::Renderer::g_Renderer;
namespace TEN::Debug
{ {
// "true" means create new log file each time game is run. static auto StartTime = std::chrono::high_resolution_clock::time_point{};
auto logPath = logDirContainingDir + "Logs/TENLog.txt";
auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath, true);
std::shared_ptr<spdlog::logger> logger; void InitTENLog(const std::string& logDirContainingDir)
// Set file and console log targets.
auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
logger = std::make_shared<spdlog::logger>(std::string{ "multi_sink" }, spdlog::sinks_init_list{ fileSink, consoleSink });
spdlog::initialize_logger(logger);
logger->set_level(spdlog::level::info);
logger->flush_on(spdlog::level::info);
logger->set_pattern("[%Y-%b-%d %T] [%^%l%$] %v");
}
void TENLog(std::string_view str, LogLevel level, LogConfig config, bool allowSpam)
{
static std::string lastString = {};
if (lastString == str && !allowSpam)
return;
if constexpr (!DebugBuild)
{ {
if (LogConfig::Debug == config) // "true" means create new log file each time game is run.
auto logPath = logDirContainingDir + "Logs/TENLog.txt";
auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath, true);
auto logger = std::shared_ptr<spdlog::logger>();
// Set file and console log targets.
auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
logger = std::make_shared<spdlog::logger>(std::string("multi_sink"), spdlog::sinks_init_list{ fileSink, consoleSink });
spdlog::initialize_logger(logger);
logger->set_level(spdlog::level::info);
logger->flush_on(spdlog::level::info);
logger->set_pattern("[%Y-%b-%d %T] [%^%l%$] %v");
}
void ShutdownTENLog()
{
spdlog::shutdown();
}
void TENLog(const std::string_view& msg, LogLevel level, LogConfig config, bool allowSpam)
{
static auto prevString = std::string();
if (prevString == msg && !allowSpam)
return; return;
if constexpr (!DebugBuild)
{
if (config == LogConfig::Debug)
return;
}
auto logger = spdlog::get("multi_sink");
switch (level)
{
case LogLevel::Error:
logger->error(msg);
break;
case LogLevel::Warning:
logger->warn(msg);
break;
case LogLevel::Info:
logger->info(msg);
break;
}
logger->flush();
prevString = std::string(msg);
} }
auto logger = spdlog::get("multi_sink"); void StartDebugTimer()
switch (level)
{ {
case LogLevel::Error: StartTime = std::chrono::high_resolution_clock::now();
logger->error(str);
break;
case LogLevel::Warning:
logger->warn(str);
break;
case LogLevel::Info:
logger->info(str);
break;
} }
logger->flush(); void EndDebugTimer()
{
auto endTime = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endTime - StartTime);
PrintDebugMessage("Execution (microseconds): %d", duration);
}
lastString = std::string(str); void PrintDebugMessage(LPCSTR msg, ...)
} {
auto args = va_list{};
va_start(args, msg);
g_Renderer.PrintDebugMessage(msg, args);
va_end(args);
}
void DrawDebugString(const std::string& string, const Vector2& pos, const Color& color, float scale, RendererDebugPage page)
{
g_Renderer.AddDebugString(string, pos, color, scale, page);
}
void ShutdownTENLog() void DrawDebug2DLine(const Vector2& origin, const Vector2& target, const Color& color, RendererDebugPage page)
{ {
spdlog::shutdown(); g_Renderer.AddLine2D(origin, target, color, page);
}
void DrawDebugLine(const Vector3& origin, const Vector3& target, const Color& color, RendererDebugPage page)
{
g_Renderer.AddDebugLine(origin, target, color, page);
}
void DrawDebugTriangle(const Vector3& vertex0, const Vector3& vertex1, const Vector3& vertex2, const Color& color, RendererDebugPage page)
{
g_Renderer.AddDebugTriangle(vertex0, vertex1, vertex2, color, page);
}
void DrawDebugTarget(const Vector3& center, const Quaternion& orient, float radius, const Color& color, RendererDebugPage page)
{
g_Renderer.AddDebugTarget(center, orient, radius, color, page);
}
void DrawDebugBox(const std::array<Vector3, BOX_VERTEX_COUNT>& corners, const Color& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugBox(corners, color, page, isWireframe);
}
void DrawDebugBox(const Vector3& min, const Vector3& max, const Color& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugBox(min, max, color, page, isWireframe);
}
void DrawDebugBox(const BoundingOrientedBox& box, const Color& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugBox(box, color, page, isWireframe);
}
void DrawDebugBox(const BoundingBox& box, const Color& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugBox(box, color, page, isWireframe);
}
void DrawDebugCone(const Vector3& center, const Quaternion& orient, float radius, float length, const Vector4& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugCone(center, orient, radius, length, color, page, isWireframe);
}
void DrawDebugCylinder(const Vector3& center, const Quaternion& orient, float radius, float length, const Color& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugCylinder(center, orient, radius, length, color, page, isWireframe);
}
void DrawDebugSphere(const Vector3& center, float radius, const Color& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugSphere(center, radius, color, page, isWireframe);
}
void DrawDebugSphere(const BoundingSphere& sphere, const Color& color, RendererDebugPage page, bool isWireframe)
{
g_Renderer.AddDebugSphere(sphere, color, page, isWireframe);
}
} }

View file

@ -1,45 +1,75 @@
#pragma once #pragma once
#if _DEBUG
constexpr bool DebugBuild = true;
#else
constexpr bool DebugBuild = false;
#endif
#include <iostream>
#include <stdexcept> #include <stdexcept>
#include <string_view> #include <string_view>
#include <iostream>
enum class LogLevel #include "Renderer/RendererEnums.h"
namespace TEN::Debug
{ {
Error, #if _DEBUG
Warning, constexpr bool DebugBuild = true;
Info #else
}; constexpr bool DebugBuild = false;
#endif
enum class LogConfig enum class LogLevel
{
Debug,
All
};
void TENLog(std::string_view str, LogLevel level = LogLevel::Info, LogConfig config = LogConfig::All, bool allowSpam = false);
void ShutdownTENLog();
void InitTENLog(const std::string& logDirContainingDir);
class TENScriptException : public std::runtime_error
{
public:
using std::runtime_error::runtime_error;
};
inline void assertion(const bool& expr, const char* msg)
{
if constexpr (DebugBuild)
{ {
if (!expr) Error,
Warning,
Info
};
enum class LogConfig
{
Debug,
All
};
class TENScriptException : public std::runtime_error
{
public:
using std::runtime_error::runtime_error;
};
// Logs
void InitTENLog(const std::string& logDirContainingDir);
void ShutdownTENLog();
void TENLog(const std::string_view& msg, LogLevel level = LogLevel::Info, LogConfig config = LogConfig::All, bool allowSpam = false);
inline void TENAssert(const bool& cond, const std::string& msg)
{
if constexpr (DebugBuild)
{ {
TENLog(msg, LogLevel::Error); if (!cond)
throw std::runtime_error(msg); {
TENLog(msg, LogLevel::Error);
throw std::runtime_error(msg);
}
} }
} };
};
// Timers
void StartDebugTimer();
void EndDebugTimer();
// Objects
void PrintDebugMessage(LPCSTR msg, ...);
void DrawDebugString(const std::string& string, const Vector2& pos, const Color& color, float scale, RendererDebugPage page = RendererDebugPage::None);
void DrawDebug2DLine(const Vector2& origin, const Vector2& target, const Color& color, RendererDebugPage page = RendererDebugPage::None);
void DrawDebugLine(const Vector3& origin, const Vector3& target, const Color& color, RendererDebugPage page = RendererDebugPage::None);
void DrawDebugTriangle(const Vector3& vertex0, const Vector3& vertex1, const Vector3& vertex2, const Color& color, RendererDebugPage page = RendererDebugPage::None);
void DrawDebugTarget(const Vector3& center, const Quaternion& orient, float radius, const Color& color, RendererDebugPage page = RendererDebugPage::None);
void DrawDebugBox(const std::array<Vector3, BOX_VERTEX_COUNT>& corners, const Color& color, RendererDebugPage page = RendererDebugPage::None, bool isWireframe = true);
void DrawDebugBox(const Vector3& min, const Vector3& max, const Color& color, RendererDebugPage page = RendererDebugPage::None, bool isWireframe = true);
void DrawDebugBox(const BoundingOrientedBox& box, const Color& color, RendererDebugPage page = RendererDebugPage::None, bool isWireframe = true);
void DrawDebugBox(const BoundingBox& box, const Color& color, RendererDebugPage page = RendererDebugPage::None, bool isWireframe = true);
void DrawDebugCone(const Vector3& center, const Quaternion& orient, float radius, float length, const Vector4& color, RendererDebugPage page, bool isWireframe = true);
void DrawDebugCylinder(const Vector3& center, const Quaternion& orient, float radius, float length, const Color& color, RendererDebugPage page = RendererDebugPage::None, bool isWireframe = true);
void DrawDebugSphere(const Vector3& center, float radius, const Color& color, RendererDebugPage page = RendererDebugPage::None, bool isWireframe = true);
void DrawDebugSphere(const BoundingSphere& sphere, const Color& color, RendererDebugPage page = RendererDebugPage::None, bool isWireframe = true);
}

View file

@ -25,7 +25,7 @@ namespace TEN::Effects::Streamer
// Update opacity. // Update opacity.
if (Color.w > 0.0f) if (Color.w > 0.0f)
Color.w = InterpolateCos(0.0f, OpacityMax, Life / LifeMax); Color.w = EaseInOutSine(0.0f, OpacityMax, Life / LifeMax);
// TODO: Not working. // TODO: Not working.
// Update orientation. // Update orientation.
@ -68,7 +68,7 @@ namespace TEN::Effects::Streamer
float lifeMax = std::min(round(life * FPS), (float)SEGMENT_COUNT_MAX); float lifeMax = std::min(round(life * FPS), (float)SEGMENT_COUNT_MAX);
float alpha = (segmentCount / lifeMax) * FADE_IN_COEFF; float alpha = (segmentCount / lifeMax) * FADE_IN_COEFF;
float opacityMax = InterpolateCos(0.0f, color.w, alpha); float opacityMax = EaseInOutSine(0.0f, color.w, alpha);
segment.Orientation = AxisAngle(dir, orient); segment.Orientation = AxisAngle(dir, orient);
segment.Color = Vector4(color.x, color.y, color.z, opacityMax); segment.Color = Vector4(color.x, color.y, color.z, opacityMax);
@ -101,7 +101,7 @@ namespace TEN::Effects::Streamer
Streamer::StreamerSegment& Streamer::GetNewSegment() Streamer::StreamerSegment& Streamer::GetNewSegment()
{ {
assertion(Segments.size() <= SEGMENT_COUNT_MAX, "Streamer segment count overflow."); TENAssert(Segments.size() <= SEGMENT_COUNT_MAX, "Streamer segment count overflow.");
// Clear oldest segment if vector is full. // Clear oldest segment if vector is full.
if (Segments.size() == SEGMENT_COUNT_MAX) if (Segments.size() == SEGMENT_COUNT_MAX)
@ -114,7 +114,7 @@ namespace TEN::Effects::Streamer
void StreamerModule::AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color, void StreamerModule::AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
float width, float life, float vel, float scaleRate, short rot, int flags) float width, float life, float vel, float scaleRate, short rot, int flags)
{ {
assertion(Pools.size() <= POOL_COUNT_MAX, "Streamer pool count overflow."); TENAssert(Pools.size() <= POOL_COUNT_MAX, "Streamer pool count overflow.");
// Return early if pool map is full and element with tag key doesn't already exist. // Return early if pool map is full and element with tag key doesn't already exist.
if (Pools.size() == POOL_COUNT_MAX && !Pools.count(tag)) if (Pools.size() == POOL_COUNT_MAX && !Pools.count(tag))
@ -152,7 +152,7 @@ namespace TEN::Effects::Streamer
Streamer& StreamerModule::GetStreamer(int tag) Streamer& StreamerModule::GetStreamer(int tag)
{ {
auto& pool = GetPool(tag); auto& pool = GetPool(tag);
assertion(pool.size() <= STREAMER_COUNT_MAX, "Streamer pool size overflow."); TENAssert(pool.size() <= STREAMER_COUNT_MAX, "Streamer pool size overflow.");
// Return most recent streamer iteration if it exists and is unbroken. // Return most recent streamer iteration if it exists and is unbroken.
if (!pool.empty()) if (!pool.empty())
@ -202,7 +202,7 @@ namespace TEN::Effects::Streamer
void StreamerEffectController::Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& direction, short orient, const Vector4& color, void StreamerEffectController::Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& direction, short orient, const Vector4& color,
float width, float life, float vel, float scaleRate, short rot, int flags) float width, float life, float vel, float scaleRate, short rot, int flags)
{ {
assertion(Modules.size() <= MODULE_COUNT_MAX, "Streamer module count overflow."); TENAssert(Modules.size() <= MODULE_COUNT_MAX, "Streamer module count overflow.");
// Return early if module map is full and element with itemNumber key doesn't already exist. // Return early if module map is full and element with itemNumber key doesn't already exist.
if (Modules.size() == MODULE_COUNT_MAX && !Modules.count(itemNumber)) if (Modules.size() == MODULE_COUNT_MAX && !Modules.count(itemNumber))

View file

@ -160,7 +160,7 @@ namespace TEN::Effects::Bubble
{ {
// Hit water surface; spawn ripple. // Hit water surface; spawn ripple.
SpawnRipple( SpawnRipple(
Vector3(bubble.Position.x, g_Level.Rooms[prevRoomNumber].maxceiling, bubble.Position.z), Vector3(bubble.Position.x, g_Level.Rooms[prevRoomNumber].TopHeight, bubble.Position.z),
roomNumber, roomNumber,
((bubble.SizeMax.x + bubble.SizeMax.y) / 2) * 0.5f, ((bubble.SizeMax.x + bubble.SizeMax.y) / 2) * 0.5f,
(int)RippleFlags::SlowFade); (int)RippleFlags::SlowFade);
@ -170,7 +170,7 @@ namespace TEN::Effects::Bubble
} }
// Hit ceiling. NOTE: This is a hacky check. New collision fetching should provide fast info on a need-to-know basis. // Hit ceiling. NOTE: This is a hacky check. New collision fetching should provide fast info on a need-to-know basis.
else if (bubble.RoomNumber == prevRoomNumber && else if (bubble.RoomNumber == prevRoomNumber &&
bubble.Position.y <= g_Level.Rooms[prevRoomNumber].maxceiling) bubble.Position.y <= g_Level.Rooms[prevRoomNumber].TopHeight)
{ {
bubble.Life = 0.0f; bubble.Life = 0.0f;
continue; continue;

View file

@ -132,9 +132,8 @@ namespace TEN::Effects::Drip
// Spawn ripple on surface only. // Spawn ripple on surface only.
if (!TestEnvironment(ENV_FLAG_WATER, prevRoomNumber)) if (!TestEnvironment(ENV_FLAG_WATER, prevRoomNumber))
{ {
float waterHeight = GetWaterHeight(drip.Position.x, drip.Position.y, drip.Position.z, drip.RoomNumber);
SpawnRipple( SpawnRipple(
Vector3(drip.Position.x, waterHeight - RIPPLE_HEIGHT_OFFSET, drip.Position.z), Vector3(drip.Position.x, pointColl.GetWaterTopHeight() - RIPPLE_HEIGHT_OFFSET, drip.Position.z),
pointColl.GetRoomNumber(), pointColl.GetRoomNumber(),
Random::GenerateFloat(RIPPLE_SIZE_WATER_MIN, RIPPLE_SIZE_WATER_MAX), Random::GenerateFloat(RIPPLE_SIZE_WATER_MIN, RIPPLE_SIZE_WATER_MAX),
(int)RippleFlags::SlowFade | (int)RippleFlags::LowOpacity); (int)RippleFlags::SlowFade | (int)RippleFlags::LowOpacity);

View file

@ -1312,7 +1312,7 @@ void Splash(ItemInfo* item)
if (!TestEnvironment(ENV_FLAG_WATER, probedRoomNumber)) if (!TestEnvironment(ENV_FLAG_WATER, probedRoomNumber))
return; return;
int waterHeight = GetWaterHeight(item); int waterHeight = GetPointCollision(*item).GetWaterTopHeight();
SplashSetup.x = item->Pose.Position.x; SplashSetup.x = item->Pose.Position.x;
SplashSetup.y = waterHeight - 1; SplashSetup.y = waterHeight - 1;
@ -1944,7 +1944,7 @@ void ProcessEffects(ItemInfo* item)
if (item->Effect.Type != EffectType::Sparks && item->Effect.Type != EffectType::Smoke) if (item->Effect.Type != EffectType::Sparks && item->Effect.Type != EffectType::Smoke)
{ {
const auto& bounds = GameBoundingBox(item); const auto& bounds = GameBoundingBox(item);
int waterHeight = GetWaterHeight(item); int waterHeight = GetPointCollision(*item).GetWaterTopHeight();
int itemLevel = item->Pose.Position.y + bounds.Y2 - (bounds.GetHeight() / 3); int itemLevel = item->Pose.Position.y + bounds.Y2 - (bounds.GetHeight() / 3);
if (waterHeight != NO_HEIGHT && itemLevel > waterHeight) if (waterHeight != NO_HEIGHT && itemLevel > waterHeight)

View file

@ -246,7 +246,7 @@ extern FX_INFO EffectList[NUM_EFFECTS];
template <typename TEffect> template <typename TEffect>
TEffect& GetNewEffect(std::vector<TEffect>& effects, unsigned int countMax) TEffect& GetNewEffect(std::vector<TEffect>& effects, unsigned int countMax)
{ {
assertion(effects.size() <= countMax, "Too many particle effects."); TENAssert(effects.size() <= countMax, "Too many particle effects.");
// Add and return new effect. // Add and return new effect.
if (effects.size() < countMax) if (effects.size() < countMax)

View file

@ -23,7 +23,7 @@ namespace TEN::Effects::Hair
{ {
HairEffectController HairEffect = {}; HairEffectController HairEffect = {};
void HairUnit::Update(const ItemInfo& item, int hairUnitIndex) void HairUnit::Update(const ItemInfo& item, int hairUnitID)
{ {
for (auto& segment : Segments) for (auto& segment : Segments)
segment.StoreInterpolationData(); segment.StoreInterpolationData();
@ -37,15 +37,14 @@ namespace TEN::Effects::Hair
g_Renderer.GetBoneMatrix(item.Index, LM_HEAD, &worldMatrix); g_Renderer.GetBoneMatrix(item.Index, LM_HEAD, &worldMatrix);
// Apply base offset to world matrix. // Apply base offset to world matrix.
auto relOffset = GetRelBaseOffset(hairUnitIndex, isYoung); auto relOffset = GetRelBaseOffset(hairUnitID, isYoung);
worldMatrix = Matrix::CreateTranslation(relOffset) * worldMatrix; worldMatrix = Matrix::CreateTranslation(relOffset) * worldMatrix;
// Use player's head bone orientation as base. // Use player's head bone orientation as base.
auto baseOrient = Geometry::ConvertDirectionToQuat(-Geometry::ConvertQuatToDirection(GetBoneOrientation(item, LM_HEAD))); auto baseOrient = Geometry::ConvertDirectionToQuat(-Geometry::ConvertQuatToDirection(GetBoneOrientation(item, LM_HEAD)));
// Set position of base segment. // Set position of base segment.
auto basePos = worldMatrix.Translation(); Segments[0].Position = worldMatrix.Translation();
Segments[0].Position = basePos;
if (!IsInitialized) if (!IsInitialized)
{ {
@ -56,7 +55,7 @@ namespace TEN::Effects::Hair
auto& nextSegment = Segments[i + 1]; auto& nextSegment = Segments[i + 1];
// NOTE: Joint offset determines segment length. // NOTE: Joint offset determines segment length.
auto jointOffset = GetJointOffset(ID_HAIR, i); auto jointOffset = GetJointOffset(ObjectID, i);
worldMatrix = Matrix::CreateTranslation(segment.Position); worldMatrix = Matrix::CreateTranslation(segment.Position);
worldMatrix = Matrix::CreateFromQuaternion(segment.Orientation) * worldMatrix; worldMatrix = Matrix::CreateFromQuaternion(segment.Orientation) * worldMatrix;
@ -72,7 +71,7 @@ namespace TEN::Effects::Hair
// Get water height. // Get water height.
auto pos = item.Pose.Position + Vector3i(GetWaterProbeOffset(item)); auto pos = item.Pose.Position + Vector3i(GetWaterProbeOffset(item));
int roomNumber = item.RoomNumber; int roomNumber = item.RoomNumber;
int waterHeight = GetWaterHeight(pos.x, pos.y, pos.z, roomNumber); int waterHeight = GetPointCollision(pos, roomNumber).GetWaterTopHeight();
// Get collision spheres. // Get collision spheres.
auto spheres = GetSpheres(item, isYoung); auto spheres = GetSpheres(item, isYoung);
@ -86,12 +85,10 @@ namespace TEN::Effects::Hair
// TR3 UPV uses a hack which forces player water status to dry. // TR3 UPV uses a hack which forces player water status to dry.
// Therefore, cannot directly use water status value to determine enrironment. // Therefore, cannot directly use water status value to determine enrironment.
bool isOnLand = (player.Control.WaterStatus == WaterStatus::Dry && bool isOnLand = (player.Control.WaterStatus == WaterStatus::Dry &&
(player.Context.Vehicle == -1 || g_Level.Items[player.Context.Vehicle].ObjectNumber != ID_UPV)); (player.Context.Vehicle == NO_VALUE || g_Level.Items[player.Context.Vehicle].ObjectNumber != ID_UPV));
// Handle segment room collision. // Handle segment collision.
CollideSegmentWithRoom(segment, waterHeight, roomNumber, isOnLand); CollideSegmentWithRoom(segment, waterHeight, roomNumber, isOnLand);
// Handle segment sphere collision.
CollideSegmentWithSpheres(segment, spheres); CollideSegmentWithSpheres(segment, spheres);
// Calculate orientation. // Calculate orientation.
@ -102,8 +99,8 @@ namespace TEN::Effects::Hair
worldMatrix = Matrix::CreateFromQuaternion(prevSegment.Orientation) * worldMatrix; worldMatrix = Matrix::CreateFromQuaternion(prevSegment.Orientation) * worldMatrix;
auto jointOffset = (i == (Segments.size() - 1)) ? auto jointOffset = (i == (Segments.size() - 1)) ?
GetJointOffset(ID_HAIR, (i - 1) - 1) : GetJointOffset(ObjectID, (i - 1) - 1) :
GetJointOffset(ID_HAIR, (i - 1)); GetJointOffset(ObjectID, (i - 1));
worldMatrix = Matrix::CreateTranslation(jointOffset) * worldMatrix; worldMatrix = Matrix::CreateTranslation(jointOffset) * worldMatrix;
segment.Position = worldMatrix.Translation(); segment.Position = worldMatrix.Translation();
@ -112,21 +109,21 @@ namespace TEN::Effects::Hair
} }
} }
Vector3 HairUnit::GetRelBaseOffset(int hairUnitIndex, bool isYoung) Vector3 HairUnit::GetRelBaseOffset(int hairUnitID, bool isYoung)
{ {
auto relOffset = Vector3::Zero; auto relOffset = Vector3::Zero;
if (isYoung) if (isYoung)
{ {
switch (hairUnitIndex) switch (hairUnitID)
{ {
// Left pigtail offset. // Left pigtail offset.
case 0: case 0:
relOffset = Vector3(-52.0f, -48.0f, -50.0f); relOffset = Vector3(-48.0f, -48.0f, -50.0f);
break; break;
// Right pigtail offset. // Right pigtail offset.
case 1: case 1:
relOffset = Vector3(44.0f, -48.0f, -50.0f); relOffset = Vector3(48.0f, -48.0f, -50.0f);
break; break;
} }
} }
@ -178,13 +175,13 @@ namespace TEN::Effects::Hair
Quaternion HairUnit::GetSegmentOrientation(const Vector3& origin, const Vector3& target, const Quaternion& baseOrient) Quaternion HairUnit::GetSegmentOrientation(const Vector3& origin, const Vector3& target, const Quaternion& baseOrient)
{ {
// Calculate absolute orientation. // Calculate absolute orientation.
auto absDirection = target - origin; auto absDir = target - origin;
absDirection.Normalize(); absDir.Normalize();
auto absOrient = Geometry::ConvertDirectionToQuat(absDirection); auto absOrient = Geometry::ConvertDirectionToQuat(absDir);
// Calculate relative twist rotation. // Calculate relative twist rotation.
// TODO: Find accurate twist angle based on relation between absOrient and baseOrient. // TODO: Find accurate twist angle based on relation between absOrient and baseOrient.
auto twistAxisAngle = AxisAngle(absDirection, EulerAngles(baseOrient).y); auto twistAxisAngle = AxisAngle(absDir, EulerAngles(baseOrient).y);
auto twistRot = twistAxisAngle.ToQuaternion(); auto twistRot = twistAxisAngle.ToQuaternion();
// Return ideal orientation. // Return ideal orientation.
@ -201,21 +198,21 @@ namespace TEN::Effects::Hair
spheres.reserve(SPHERE_COUNT); spheres.reserve(SPHERE_COUNT);
// Hips sphere. // Hips sphere.
auto* meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_HIPS]]; const auto* mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_HIPS]];
auto pos = GetJointPosition(item, LM_HIPS, Vector3i(meshPtr->sphere.Center)).ToVector3(); auto pos = GetJointPosition(item, LM_HIPS, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, meshPtr->sphere.Radius)); spheres.push_back(BoundingSphere(pos, mesh->sphere.Radius));
// Torso sphere. // Torso sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_TORSO]]; mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_TORSO]];
pos = GetJointPosition(item, LM_TORSO, Vector3i(meshPtr->sphere.Center) + TORSO_SPHERE_OFFSET).ToVector3(); pos = GetJointPosition(item, LM_TORSO, Vector3i(mesh->sphere.Center) + TORSO_SPHERE_OFFSET).ToVector3();
spheres.push_back(BoundingSphere(pos, meshPtr->sphere.Radius)); spheres.push_back(BoundingSphere(pos, mesh->sphere.Radius));
if (isYoung) if (isYoung)
spheres.back().Radius = spheres.back().Radius - ((spheres.back().Radius / 4) + (spheres.back().Radius / 8)); spheres.back().Radius = spheres.back().Radius - ((spheres.back().Radius / 4) + (spheres.back().Radius / 8));
// Head sphere. // Head sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_HEAD]]; mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_HEAD]];
pos = GetJointPosition(item, LM_HEAD, Vector3i(meshPtr->sphere.Center) + HEAD_SPHERE_OFFSET).ToVector3(); pos = GetJointPosition(item, LM_HEAD, Vector3i(mesh->sphere.Center) + HEAD_SPHERE_OFFSET).ToVector3();
spheres.push_back(BoundingSphere(pos, meshPtr->sphere.Radius)); spheres.push_back(BoundingSphere(pos, mesh->sphere.Radius));
// Neck sphere. // Neck sphere.
spheres.push_back(BoundingSphere( spheres.push_back(BoundingSphere(
@ -223,30 +220,30 @@ namespace TEN::Effects::Hair
isYoung ? 0.0f : (spheres[2].Radius * 0.75f))); isYoung ? 0.0f : (spheres[2].Radius * 0.75f)));
// Left arm sphere. // Left arm sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_LINARM]]; mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_LINARM]];
pos = GetJointPosition(item, LM_LINARM, Vector3i(meshPtr->sphere.Center)).ToVector3(); pos = GetJointPosition(item, LM_LINARM, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, (meshPtr->sphere.Radius / 3) * 4)); spheres.push_back(BoundingSphere(pos, (mesh->sphere.Radius / 3) * 4));
// Right arm sphere. // Right arm sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_RINARM]]; mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_RINARM]];
pos = GetJointPosition(item, LM_RINARM, Vector3i(meshPtr->sphere.Center)).ToVector3(); pos = GetJointPosition(item, LM_RINARM, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, (meshPtr->sphere.Radius / 3) * 4)); spheres.push_back(BoundingSphere(pos, (mesh->sphere.Radius / 3) * 4));
// Left holster sphere. // Left holster sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_LTHIGH]]; mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_LTHIGH]];
pos = GetJointPosition(item, LM_LTHIGH, Vector3i(meshPtr->sphere.Center)).ToVector3(); pos = GetJointPosition(item, LM_LTHIGH, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back( spheres.push_back(
BoundingSphere( BoundingSphere(
pos + ((spheres[0].Center - pos) / 2), pos + ((spheres[0].Center - pos) / 2),
meshPtr->sphere.Radius)); mesh->sphere.Radius));
// Right holster sphere. // Right holster sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_RTHIGH]]; mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_RTHIGH]];
pos = GetJointPosition(item, LM_RTHIGH, Vector3i(meshPtr->sphere.Center)).ToVector3(); pos = GetJointPosition(item, LM_RTHIGH, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back( spheres.push_back(
BoundingSphere( BoundingSphere(
pos + ((spheres[0].Center - pos) / 2), pos + ((spheres[0].Center - pos) / 2),
meshPtr->sphere.Radius)); mesh->sphere.Radius));
if (isYoung) if (isYoung)
spheres[1].Center = (spheres[1].Center + spheres[2].Center) / 2; spheres[1].Center = (spheres[1].Center + spheres[2].Center) / 2;
@ -256,13 +253,12 @@ namespace TEN::Effects::Hair
void HairUnit::CollideSegmentWithRoom(HairSegment& segment, int waterHeight, int roomNumber, bool isOnLand) void HairUnit::CollideSegmentWithRoom(HairSegment& segment, int waterHeight, int roomNumber, bool isOnLand)
{ {
constexpr auto VELOCITY_COEFF = 0.75f; constexpr auto VEL_COEFF = 0.75f;
auto pointColl = GetPointCollision(segment.Position, roomNumber); auto pointColl = GetPointCollision(segment.Position, roomNumber);
int floorHeight = pointColl.GetFloorHeight();
Segments[0].Velocity = segment.Position; Segments[0].Velocity = segment.Position;
segment.Position += segment.Velocity * VELOCITY_COEFF; segment.Position += segment.Velocity * VEL_COEFF;
// Land collision. // Land collision.
if (isOnLand) if (isOnLand)
@ -280,7 +276,7 @@ namespace TEN::Effects::Hair
segment.Position.y = waterHeight; segment.Position.y = waterHeight;
} }
// Avoid clipping through floor. // Avoid clipping through floor.
else if (floorHeight > Segments[0].Position.y && segment.Position.y > floorHeight) else if (pointColl.GetFloorHeight() > Segments[0].Position.y && segment.Position.y > pointColl.GetFloorHeight())
{ {
segment.Position = Segments[0].Velocity; segment.Position = Segments[0].Velocity;
} }
@ -292,9 +288,9 @@ namespace TEN::Effects::Hair
{ {
segment.Position.y = waterHeight; segment.Position.y = waterHeight;
} }
else if (segment.Position.y > floorHeight) else if (segment.Position.y > pointColl.GetFloorHeight())
{ {
segment.Position.y = floorHeight; segment.Position.y = pointColl.GetFloorHeight();
} }
} }
} }
@ -303,57 +299,59 @@ namespace TEN::Effects::Hair
{ {
for (const auto& sphere : spheres) for (const auto& sphere : spheres)
{ {
auto direction = segment.Position - sphere.Center; auto dir = segment.Position - sphere.Center;
float distance = Vector3::Distance(segment.Position, sphere.Center); float dist = Vector3::Distance(segment.Position, sphere.Center);
if (distance < sphere.Radius) if (dist < sphere.Radius)
{ {
// Avoid division by zero. // Avoid division by zero.
if (distance == 0.0f) if (dist == 0.0f)
distance = 1.0f; dist = 1.0f;
// Push segment away from sphere. // Push segment away from sphere.
segment.Position = sphere.Center + (direction * (sphere.Radius / distance)); segment.Position = sphere.Center + (dir * (sphere.Radius / dist));
} }
} }
} }
void HairEffectController::Initialize() void HairEffectController::Initialize()
{ {
constexpr auto ORIENT_DEFAULT = EulerAngles(ANGLE(-90.0f), 0, 0); constexpr auto DEFAULT_ORIENT = EulerAngles(ANGLE(-90.0f), 0, 0);
bool isYoung = (g_GameFlow->GetLevel(CurrentLevel)->GetLaraType() == LaraType::Young); bool isYoung = (g_GameFlow->GetLevel(CurrentLevel)->GetLaraType() == LaraType::Young);
// Initialize hair units. // Initialize hair units.
bool isHead = true; for (int i = 0; i < Units.size(); i++)
for (auto& unit : Units)
{ {
unit.IsEnabled = (!isHead || isYoung); auto& unit = Units[i];
auto objectID = (i == 0) ? ID_SINGLE_BRAID_HAIR : ID_DUAL_PIGTAIL_HAIR;
const auto& object = Objects[objectID];
unit.IsEnabled = (object.loaded && (i == 0 || (i == 1 && isYoung)));
unit.IsInitialized = false; unit.IsInitialized = false;
unit.ObjectID = objectID;
unsigned int segmentCount = Objects[ID_HAIR].nmeshes + 1; unit.Segments.resize(object.nmeshes + 1);
unit.Segments.resize(segmentCount);
// Initialize segments. // Initialize segments.
for (auto& segment : unit.Segments) for (auto& segment : unit.Segments)
{ {
segment.Position = GetJointOffset(ID_HAIR, 0); segment.Position = GetJointOffset(objectID, 0);
segment.Velocity = Vector3::Zero; segment.Velocity = Vector3::Zero;
segment.Orientation = ORIENT_DEFAULT.ToQuaternion(); segment.Orientation = DEFAULT_ORIENT.ToQuaternion();
} }
isHead = false;
} }
} }
void HairEffectController::Update(ItemInfo& item, bool isYoung) void HairEffectController::Update(ItemInfo& item)
{ {
for (int i = 0; i < Units.size(); i++) for (int i = 0; i < Units.size(); i++)
{ {
Units[i].Update(item, i); auto& unit = Units[i];
if (!unit.IsEnabled)
continue;
if (isYoung && i == 1) unit.Update(item, i);
Units[i].Update(item, i);
} }
} }
} }

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "Objects/game_object_ids.h"
struct ItemInfo; struct ItemInfo;
namespace TEN::Effects::Hair namespace TEN::Effects::Hair
@ -8,6 +10,7 @@ namespace TEN::Effects::Hair
{ {
private: private:
// Constants // Constants
static constexpr auto HAIR_GRAVITY = 10.0f; static constexpr auto HAIR_GRAVITY = 10.0f;
struct HairSegment struct HairSegment
@ -28,16 +31,21 @@ namespace TEN::Effects::Hair
public: public:
// Members // Members
bool IsEnabled = false; bool IsEnabled = false;
bool IsInitialized = false; bool IsInitialized = false;
GAME_OBJECT_ID ObjectID = GAME_OBJECT_ID::ID_NO_OBJECT;
std::vector<HairSegment> Segments = {}; std::vector<HairSegment> Segments = {};
// Utilities // Utilities
void Update(const ItemInfo& item, int hairUnitIndex);
void Update(const ItemInfo& item, int hairUnitID);
private: private:
// Helpers // Helpers
Vector3 GetRelBaseOffset(int hairUnitIndex, bool isYoung);
Vector3 GetRelBaseOffset(int hairUnitID, bool isYoung);
Vector3 GetWaterProbeOffset(const ItemInfo& item); Vector3 GetWaterProbeOffset(const ItemInfo& item);
Quaternion GetSegmentOrientation(const Vector3& origin, const Vector3& target, const Quaternion& baseOrient); Quaternion GetSegmentOrientation(const Vector3& origin, const Vector3& target, const Quaternion& baseOrient);
std::vector<BoundingSphere> GetSpheres(const ItemInfo& item, bool isYoung); std::vector<BoundingSphere> GetSpheres(const ItemInfo& item, bool isYoung);
@ -50,15 +58,18 @@ namespace TEN::Effects::Hair
{ {
private: private:
// Constants // Constants
static constexpr auto UNIT_COUNT_MAX = 2; static constexpr auto UNIT_COUNT_MAX = 2;
public: public:
// Members // Members
std::array<HairUnit, UNIT_COUNT_MAX> Units = {}; std::array<HairUnit, UNIT_COUNT_MAX> Units = {};
// Utilities // Utilities
void Initialize(); void Initialize();
void Update(ItemInfo& item, bool isYoung); void Update(ItemInfo& item);
}; };
extern HairEffectController HairEffect; extern HairEffectController HairEffect;

View file

@ -5,6 +5,7 @@
#include "Game/animation.h" #include "Game/animation.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/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/effects/Bubble.h" #include "Game/effects/Bubble.h"
#include "Game/effects/debris.h" #include "Game/effects/debris.h"
@ -27,6 +28,7 @@ using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Ripple; using namespace TEN::Effects::Ripple;
using namespace TEN::Effects::Smoke; using namespace TEN::Effects::Smoke;
using namespace TEN::Collision::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Math; using namespace TEN::Math;
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
@ -264,30 +266,23 @@ void TriggerPilotFlame(int itemNumber, int nodeIndex)
spark->dSize = size; spark->dSize = size;
} }
Particle* SetupPoisonSpark(Vector3 color) static Particle& SetupPoisonParticle(const Color& colorStart, const Color& colorEnd)
{ {
auto* spark = GetFreeParticle(); auto& part = *GetFreeParticle();
part.sR = std::clamp<unsigned char>(colorStart.x * UCHAR_MAX, 0, UCHAR_MAX);
part.sG = std::clamp<unsigned char>(colorStart.y * UCHAR_MAX, 0, UCHAR_MAX);
part.sB = std::clamp<unsigned char>(colorStart.z * UCHAR_MAX, 0, UCHAR_MAX);
part.dR = std::clamp<unsigned char>(colorEnd.x * UCHAR_MAX, 0, UCHAR_MAX);
part.dG = std::clamp<unsigned char>(colorEnd.y * UCHAR_MAX, 0, UCHAR_MAX);
part.dB = std::clamp<unsigned char>(colorEnd.z * UCHAR_MAX, 0, UCHAR_MAX);
part.colFadeSpeed = 14;
part.fadeToBlack = 8;
part.blendMode = BlendMode::Screen;
bool rMax = color.x > color.y && color.x > color.z; return part;
bool gMax = color.y > color.x && color.y > color.z;
bool bMax = color.z > color.x && color.z > color.y;
char seed = (GetRandomControl() & 0x1F) + 220;
spark->sR = (rMax ? seed : 255) * (color.x * 0.4);
spark->sG = (gMax ? seed : 255) * (color.y * 0.4);
spark->sB = (bMax ? seed : 255) * (color.z * 0.4);
spark->dR = 255 * color.x;
spark->dG = 255 * color.y;
spark->dB = 255 * color.z;
spark->colFadeSpeed = 14;
spark->fadeToBlack = 8;
spark->blendMode = BlendMode::Screen;
return spark;
} }
Particle* SetupFireSpark() static Particle* SetupFireSpark()
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -304,19 +299,20 @@ Particle* SetupFireSpark()
return spark; return spark;
} }
void AttachAndCreateSpark(Particle* spark, ItemInfo* item, int meshIndex, Vector3i offset, Vector3i vel) static void AttachAndCreateSpark(Particle* spark, const ItemInfo* item, int meshID, Vector3i offset, Vector3i vel, int spriteID = 0)
{ {
auto pos1 = GetJointPosition(item, meshIndex, Vector3i(-4, -30, -4) + offset); auto pos1 = GetJointPosition(*item, meshID, Vector3i(-4, -30, -4) + offset);
spark->x = (GetRandomControl() & 0x1F) + pos1.x - 16; spark->x = (GetRandomControl() & 0x1F) + pos1.x - 16;
spark->y = (GetRandomControl() & 0x1F) + pos1.y - 16; spark->y = (GetRandomControl() & 0x1F) + pos1.y - 16;
spark->z = (GetRandomControl() & 0x1F) + pos1.z - 16; spark->z = (GetRandomControl() & 0x1F) + pos1.z - 16;
auto pos2 = GetJointPosition(item, meshIndex, Vector3i(-4, -30, -4) + offset + vel); auto pos2 = GetJointPosition(*item, meshID, Vector3i(-4, -30, -4) + offset + vel);
int v = (GetRandomControl() & 0x3F) + 192; int v = (GetRandomControl() & 0x3F) + 192;
spark->life = spark->sLife = v / 6; spark->life = spark->sLife = v / 6;
spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + spriteID;
spark->xVel = v * (pos2.x - pos1.x) / 10; spark->xVel = v * (pos2.x - pos1.x) / 10;
spark->yVel = v * (pos2.y - pos1.y) / 10; spark->yVel = v * (pos2.y - pos1.y) / 10;
@ -335,39 +331,39 @@ void AttachAndCreateSpark(Particle* spark, ItemInfo* item, int meshIndex, Vector
spark->on = 1; spark->on = 1;
} }
void ThrowFire(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel) void ThrowFire(int itemNumber, int meshID, const Vector3i& offset, const Vector3i& vel, int spriteID)
{ {
auto& item = g_Level.Items[itemNumber]; auto& item = g_Level.Items[itemNumber];
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
auto& spark = *SetupFireSpark(); auto& part = *SetupFireSpark();
AttachAndCreateSpark(&spark, &item, meshIndex, offset, vel); AttachAndCreateSpark(&part, &item, meshID, offset, vel, spriteID);
spark.flags = SP_FIRE | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; part.flags = SP_FIRE | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
} }
} }
void ThrowFire(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel) void ThrowFire(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel, int spriteID)
{ {
ThrowFire(itemNumber, bite.BoneID, bite.Position, vel); ThrowFire(itemNumber, bite.BoneID, bite.Position, vel, spriteID);
} }
void ThrowPoison(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel, const Vector3& color) void ThrowPoison(const ItemInfo& item, int boneID, const Vector3& offset, const Vector3& vel, const Color& colorStart, const Color& colorEnd, int spriteID)
{ {
auto* item = &g_Level.Items[itemNumber]; constexpr auto COUNT = 2;
for (int i = 0; i < 2; i++) for (int i = 0; i < COUNT; i++)
{ {
auto* spark = SetupPoisonSpark(color); auto& part = SetupPoisonParticle(colorStart, colorEnd);
AttachAndCreateSpark(spark, item, meshIndex, offset, vel); AttachAndCreateSpark(&part, &item, boneID, offset, vel, spriteID);
spark->flags = SP_POISON | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; part.flags = SP_POISON | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
} }
} }
void ThrowPoison(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel, const Vector3& color) void ThrowPoison(const ItemInfo& item, const CreatureBiteInfo& bite, const Vector3& vel, const Color& colorStart, const Color& colorEnd, int spriteID)
{ {
ThrowPoison(itemNumber, bite.BoneID, bite.Position, vel, color); ThrowPoison(item, bite.BoneID, bite.Position, vel, colorStart, colorEnd, spriteID);
} }
void UpdateFireProgress() void UpdateFireProgress()
@ -1016,10 +1012,10 @@ void UpdateGunShells()
!TestEnvironment(ENV_FLAG_WATER, prevRoomNumber)) !TestEnvironment(ENV_FLAG_WATER, prevRoomNumber))
{ {
SpawnSplashDrips(Vector3(gunshell->pos.Position.x, g_Level.Rooms[gunshell->roomNumber].maxceiling, gunshell->pos.Position.z), gunshell->roomNumber, 3, true); SpawnSplashDrips(Vector3(gunshell->pos.Position.x, g_Level.Rooms[gunshell->roomNumber].TopHeight, gunshell->pos.Position.z), gunshell->roomNumber, 3, true);
//AddWaterSparks(gs->pos.Position.x, g_Level.Rooms[gs->roomNumber].maxceiling, gs->pos.Position.z, 8); //AddWaterSparks(gs->pos.Position.x, g_Level.Rooms[gs->roomNumber].maxceiling, gs->pos.Position.z, 8);
SpawnRipple( SpawnRipple(
Vector3(gunshell->pos.Position.x, g_Level.Rooms[gunshell->roomNumber].maxceiling, gunshell->pos.Position.z), Vector3(gunshell->pos.Position.x, g_Level.Rooms[gunshell->roomNumber].TopHeight, gunshell->pos.Position.z),
gunshell->roomNumber, gunshell->roomNumber,
Random::GenerateFloat(8.0f, 12.0f), Random::GenerateFloat(8.0f, 12.0f),
(int)RippleFlags::SlowFade); (int)RippleFlags::SlowFade);
@ -1146,7 +1142,7 @@ void TriggerUnderwaterExplosion(ItemInfo* item, int flag)
TriggerExplosionBubbles(x, y, z, item->RoomNumber); TriggerExplosionBubbles(x, y, z, item->RoomNumber);
TriggerExplosionSparks(x, y, z, 2, -1, 1, item->RoomNumber); TriggerExplosionSparks(x, y, z, 2, -1, 1, item->RoomNumber);
int waterHeight = GetWaterHeight(x, y, z, item->RoomNumber); int waterHeight = GetPointCollision(Vector3i(x, y, z), item->RoomNumber).GetWaterTopHeight();
if (waterHeight != NO_HEIGHT) if (waterHeight != NO_HEIGHT)
SomeSparkEffect(x, waterHeight, z, 8); SomeSparkEffect(x, waterHeight, z, 8);
} }
@ -1158,7 +1154,7 @@ void TriggerUnderwaterExplosion(ItemInfo* item, int flag)
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
TriggerExplosionSparks(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, 2, -1, 1, item->RoomNumber); TriggerExplosionSparks(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, 2, -1, 1, item->RoomNumber);
int waterHeight = GetWaterHeight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber); int waterHeight = GetPointCollision(*item).GetWaterTopHeight();
if (waterHeight != NO_HEIGHT) if (waterHeight != NO_HEIGHT)
{ {
int dy = item->Pose.Position.y - waterHeight; int dy = item->Pose.Position.y - waterHeight;

View file

@ -315,10 +315,10 @@ void TriggerGlobalStaticFlame();
void TriggerGlobalFireSmoke(); void TriggerGlobalFireSmoke();
void TriggerGlobalFireFlame(); void TriggerGlobalFireFlame();
void TriggerPilotFlame(int itemNumber, int nodeIndex); void TriggerPilotFlame(int itemNumber, int nodeIndex);
void ThrowFire(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel); void ThrowFire(int itemNumber, int meshID, const Vector3i& offset, const Vector3i& vel, int spriteID = 0);
void ThrowFire(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel); void ThrowFire(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel, int spriteID = 0);
void ThrowPoison(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel, const Vector3& color); void ThrowPoison(const ItemInfo& item, int boneID, const Vector3& offset, const Vector3& vel, const Color& colorStart, const Color& colorEnd, int spriteID = 0);
void ThrowPoison(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel, const Vector3& color); void ThrowPoison(const ItemInfo& item, const CreatureBiteInfo& bite, const Vector3& vel, const Color& colorStart, const Color& colorEnd, int spriteID = 0);
void UpdateFireProgress(); void UpdateFireProgress();
void ClearFires(); void ClearFires();
void AddFire(int x, int y, int z, short roomNum, float size, short fade); void AddFire(int x, int y, int z, short roomNum, float size, short fade);

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,7 @@ namespace TEN::Gui
None, None,
UseItem, UseItem,
NewGame, NewGame,
HomeLevel,
LoadGame, LoadGame,
SaveGame, SaveGame,
ExitGame, ExitGame,
@ -187,6 +188,7 @@ namespace TEN::Gui
void UseItem(ItemInfo& item, int objectNumber); void UseItem(ItemInfo& item, int objectNumber);
// Getters // Getters
const InventoryRing& GetRing(RingTypes ringType); const InventoryRing& GetRing(RingTypes ringType);
int GetSelectedOption(); int GetSelectedOption();
Menu GetMenuToDisplay(); Menu GetMenuToDisplay();
@ -196,8 +198,10 @@ namespace TEN::Gui
int GetLastInventoryItem(); int GetLastInventoryItem();
SettingsData& GetCurrentSettings(); SettingsData& GetCurrentSettings();
int GetLoadSaveSelection(); int GetLoadSaveSelection();
int GetLoopedSelectedOption(int selectedOption, int optionCount, bool canLoop);
// Setters // Setters
void SetSelectedOption(int menu); void SetSelectedOption(int menu);
void SetMenuToDisplay(Menu menu); void SetMenuToDisplay(Menu menu);
void SetInventoryMode(InventoryMode mode); void SetInventoryMode(InventoryMode mode);

View file

@ -83,12 +83,6 @@ struct CreatureBiteInfo
Position = pos; Position = pos;
BoneID = boneID; BoneID = boneID;
} }
CreatureBiteInfo(float x, float y, float z, int boneID)
{
Position = Vector3(x, y, z);
BoneID = boneID;
}
}; };
struct CreatureMuzzleFlashInfo struct CreatureMuzzleFlashInfo

View file

@ -584,9 +584,9 @@ void InitializeItem(short itemNumber)
item->NextItem = room->itemNumber; item->NextItem = room->itemNumber;
room->itemNumber = itemNumber; room->itemNumber = itemNumber;
FloorInfo* floor = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z); FloorInfo* floor = GetSector(room, item->Pose.Position.x - room->Position.x, item->Pose.Position.z - room->Position.z);
item->Floor = floor->GetSurfaceHeight(item->Pose.Position.x, item->Pose.Position.z, true); item->Floor = floor->GetSurfaceHeight(item->Pose.Position.x, item->Pose.Position.z, true);
item->BoxNumber = floor->Box; item->BoxNumber = floor->PathfindingBoxID;
item->ResetModelToDefault(); item->ResetModelToDefault();

View file

@ -8,34 +8,36 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Setup.h" #include "Game/Setup.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/trutils.h"
using namespace TEN::Collision::Point; using namespace TEN::Collision::Point;
using namespace TEN::Utils;
CreatureInfo* GetCreatureInfo(ItemInfo* item) CreatureInfo* GetCreatureInfo(ItemInfo* item)
{ {
return (CreatureInfo*)item->Data; return (CreatureInfo*)item->Data;
} }
void TargetNearestEntity(ItemInfo* item, CreatureInfo* creature) void TargetNearestEntity(ItemInfo* item, CreatureInfo* creature, const std::vector<GAME_OBJECT_ID>& keyObjectIds, bool ignoreKeyObjectIds)
{ {
float nearestDistance = INFINITY; float closestDistSqr = INFINITY;
for (int itemNumber = 0; itemNumber < g_Level.NumItems; itemNumber++)
for (int i = 0; i < g_Level.NumItems; i++)
{ {
auto* targetEntity = &g_Level.Items[i]; auto* targetItem = &g_Level.Items[itemNumber];
if (targetItem == nullptr || targetItem->Index == item->Index)
if (targetEntity == nullptr)
continue; continue;
if (targetEntity != item && // Ignore or specifically target key object IDs.
targetEntity->HitPoints > 0 && if (ignoreKeyObjectIds ? Contains(keyObjectIds, targetItem->ObjectNumber) : !Contains(keyObjectIds, targetItem->ObjectNumber))
targetEntity->Status != ITEM_INVISIBLE) continue;
if (targetItem != item && targetItem->HitPoints > 0 && targetItem->Status != ITEM_INVISIBLE)
{ {
float distance = Vector3i::Distance(item->Pose.Position, targetEntity->Pose.Position); float distSqr = Vector3i::DistanceSquared(item->Pose.Position, targetItem->Pose.Position);
if (distance < nearestDistance) if (distSqr < closestDistSqr)
{ {
creature->Enemy = targetEntity; creature->Enemy = targetItem;
nearestDistance = distance; closestDistSqr = distSqr;
} }
} }
} }
@ -132,13 +134,13 @@ bool IsNextSectorValid(const ItemInfo& item, const Vector3& dir, float dist, boo
} }
// Check for inaccessible sector. // Check for inaccessible sector.
if (pointColl.GetSector().Box == NO_VALUE) if (pointColl.GetSector().PathfindingBoxID == NO_VALUE)
return false; return false;
// Check for blocked grey box. // Check for blocked grey box.
if (g_Level.Boxes[pointColl.GetSector().Box].flags & BLOCKABLE) if (g_Level.PathfindingBoxes[pointColl.GetSector().PathfindingBoxID].flags & BLOCKABLE)
{ {
if (g_Level.Boxes[pointColl.GetSector().Box].flags & BLOCKED) if (g_Level.PathfindingBoxes[pointColl.GetSector().PathfindingBoxID].flags & BLOCKED)
return false; return false;
} }

View file

@ -18,5 +18,5 @@ enum LaraMeshMask
}; };
CreatureInfo* GetCreatureInfo(ItemInfo* item); CreatureInfo* GetCreatureInfo(ItemInfo* item);
void TargetNearestEntity(ItemInfo* item, CreatureInfo* creature); void TargetNearestEntity(ItemInfo* item, CreatureInfo* creature, const std::vector<GAME_OBJECT_ID>& keyObjectIds = {}, bool ignoreKeyObjectIds = true);
bool IsNextSectorValid(const ItemInfo& item, const Vector3& dir, float dist, bool canFloat); bool IsNextSectorValid(const ItemInfo& item, const Vector3& dir, float dist, bool canFloat);

View file

@ -50,7 +50,7 @@ void ControlMissile(short fxNumber)
auto& fx = EffectList[fxNumber]; auto& fx = EffectList[fxNumber];
auto isUnderwater = TestEnvironment(ENV_FLAG_WATER, fx.roomNumber); auto isUnderwater = TestEnvironment(ENV_FLAG_WATER, fx.roomNumber);
auto soundFXType = isUnderwater ? SoundEnvironment::Water : SoundEnvironment::Land; auto soundFXType = isUnderwater ? SoundEnvironment::ShallowWater : SoundEnvironment::Land;
if (fx.objectNumber == ID_SCUBA_HARPOON && isUnderwater && if (fx.objectNumber == ID_SCUBA_HARPOON && isUnderwater &&
fx.pos.Orientation.x > ANGLE(-67.5f)) fx.pos.Orientation.x > ANGLE(-67.5f))
@ -66,6 +66,7 @@ void ControlMissile(short fxNumber)
// Check whether something was hit. // Check whether something was hit.
if (fx.pos.Position.y >= pointColl.GetFloorHeight() || if (fx.pos.Position.y >= pointColl.GetFloorHeight() ||
fx.pos.Position.y <= pointColl.GetCeilingHeight() || fx.pos.Position.y <= pointColl.GetCeilingHeight() ||
pointColl.IsWall() ||
hasHitPlayer) hasHitPlayer)
{ {
if (fx.objectNumber == ID_KNIFETHROWER_KNIFE || if (fx.objectNumber == ID_KNIFETHROWER_KNIFE ||
@ -111,6 +112,7 @@ void ControlMissile(short fxNumber)
} }
KillEffect(fxNumber); KillEffect(fxNumber);
return;
} }
if (pointColl.GetRoomNumber() != fx.roomNumber) if (pointColl.GetRoomNumber() != fx.roomNumber)

View file

@ -169,8 +169,8 @@ bool TargetVisible(ItemInfo* item, AI_INFO* ai, float maxAngleInDegrees)
auto target = GameVector( auto target = GameVector(
enemy->Pose.Position.x, enemy->Pose.Position.x,
enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4), enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4),
enemy->Pose.Position.z, enemy->Pose.Position.z);
enemy->RoomNumber); // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022
return LOS(&origin, &target); return LOS(&origin, &target);
} }

View file

@ -234,8 +234,8 @@ void CollectMultiplePickups(int itemNumber)
auto& firstItem = g_Level.Items[itemNumber]; auto& firstItem = g_Level.Items[itemNumber];
auto collObjects = GetCollidedObjects(firstItem, true, true, LARA_RADIUS, ObjectCollectionMode::Items); auto collObjects = GetCollidedObjects(firstItem, true, true, LARA_RADIUS, ObjectCollectionMode::Items);
collObjects.ItemPtrs.push_back(&firstItem); collObjects.Items.push_back(&firstItem);
for (auto* itemPtr : collObjects.ItemPtrs) for (auto* itemPtr : collObjects.Items)
{ {
if (!Objects[itemPtr->ObjectNumber].isPickup) if (!Objects[itemPtr->ObjectNumber].isPickup)
continue; continue;
@ -886,7 +886,7 @@ void DropPickups(ItemInfo* item)
// Iterate through all found items and statics around, and determine if dummy sphere // Iterate through all found items and statics around, and determine if dummy sphere
// intersects any of those. If so, try other corner. // intersects any of those. If so, try other corner.
for (const auto* itemPtr : collObjects.ItemPtrs) for (const auto* itemPtr : collObjects.Items)
{ {
auto box = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose); auto box = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose);
if (box.Intersects(sphere)) if (box.Intersects(sphere))
@ -896,7 +896,7 @@ void DropPickups(ItemInfo* item)
} }
} }
for (auto* staticPtr : collObjects.StaticPtrs) for (auto* staticPtr : collObjects.Statics)
{ {
auto& object = StaticObjects[staticPtr->staticNumber]; auto& object = StaticObjects[staticPtr->staticNumber];

View file

@ -32,8 +32,8 @@ bool ROOM_INFO::Active() const
// Since engine swaps whole room memory block but substitutes flippedRoom, // Since engine swaps whole room memory block but substitutes flippedRoom,
// must check both original room number and flippedRoom equality, // must check both original room number and flippedRoom equality,
// as well as NO_VALUE if checking non-flipped rooms. // as well as NO_VALUE if checking non-flipped rooms.
return (!FlipStats[flipNumber] && flippedRoom != index && flippedRoom != NO_VALUE) || return (!FlipStats[flipNumber] && flippedRoom != RoomNumber && flippedRoom != NO_VALUE) ||
( FlipStats[flipNumber] && flippedRoom == index); ( FlipStats[flipNumber] && flippedRoom == RoomNumber);
} }
static void AddRoomFlipItems(const ROOM_INFO& room) static void AddRoomFlipItems(const ROOM_INFO& room)
@ -105,11 +105,11 @@ void DoFlipMap(int group)
g_Renderer.FlipRooms(roomNumber, room.flippedRoom); g_Renderer.FlipRooms(roomNumber, room.flippedRoom);
// Update active room sectors. // Update active room sectors.
for (auto& sector : room.floor) for (auto& sector : room.Sectors)
sector.RoomNumber = roomNumber; sector.RoomNumber = roomNumber;
// Update flipped room sectors. // Update flipped room sectors.
for (auto& sector : flippedRoom.floor) for (auto& sector : flippedRoom.Sectors)
sector.RoomNumber = room.flippedRoom; sector.RoomNumber = room.flippedRoom;
} }
} }
@ -158,9 +158,9 @@ int IsRoomOutside(int x, int y, int z)
int roomNumber = OutsideRoomTable[xTable][zTable][i]; int roomNumber = OutsideRoomTable[xTable][zTable][i];
const auto& room = g_Level.Rooms[roomNumber]; const auto& room = g_Level.Rooms[roomNumber];
if ((x > (room.x + BLOCK(1)) && x < (room.x + (room.xSize - 1) * BLOCK(1))) && if ((x > (room.Position.x + BLOCK(1)) && x < (room.Position.x + (room.XSize - 1) * BLOCK(1))) &&
(y > room.maxceiling && y < room.minfloor) && (y > room.TopHeight && y < room.BottomHeight) &&
(z > (room.z + BLOCK(1)) && z < (room.z + (room.zSize - 1) * BLOCK(1)))) (z > (room.Position.z + BLOCK(1)) && z < (room.Position.z + (room.ZSize - 1) * BLOCK(1))))
{ {
auto pointColl = GetPointCollision(Vector3i(x, y, z), roomNumber); auto pointColl = GetPointCollision(Vector3i(x, y, z), roomNumber);
@ -188,14 +188,14 @@ namespace TEN::Collision::Room
// TODO: Can use floordata's GetRoomGridCoord()? // TODO: Can use floordata's GetRoomGridCoord()?
FloorInfo* GetSector(ROOM_INFO* room, int x, int z) FloorInfo* GetSector(ROOM_INFO* room, int x, int z)
{ {
int sectorX = std::clamp(x / BLOCK(1), 0, room->xSize - 1); int sectorX = std::clamp(x / BLOCK(1), 0, room->XSize - 1);
int sectorZ = std::clamp(z / BLOCK(1), 0, room->zSize - 1); int sectorZ = std::clamp(z / BLOCK(1), 0, room->ZSize - 1);
int sectorID = sectorZ + (sectorX * room->zSize); int sectorID = sectorZ + (sectorX * room->ZSize);
if (sectorID > room->floor.size()) if (sectorID > room->Sectors.size())
return nullptr; return nullptr;
return &room->floor[sectorID]; return &room->Sectors[sectorID];
} }
} }
@ -219,9 +219,9 @@ bool IsPointInRoom(const Vector3i& pos, int roomNumber)
{ {
const auto& room = g_Level.Rooms[roomNumber]; const auto& room = g_Level.Rooms[roomNumber];
if (pos.z >= (room.z + BLOCK(1)) && pos.z <= (room.z + BLOCK(room.zSize - 1)) && if (pos.z >= (room.Position.z + BLOCK(1)) && pos.z <= (room.Position.z + BLOCK(room.ZSize - 1)) &&
pos.y <= room.minfloor && pos.y > room.maxceiling && pos.y <= room.BottomHeight && pos.y > room.TopHeight &&
pos.x >= (room.x + BLOCK(1)) && pos.x <= (room.x + BLOCK(room.xSize - 1))) pos.x >= (room.Position.x + BLOCK(1)) && pos.x <= (room.Position.x + BLOCK(room.XSize - 1)))
{ {
return true; return true;
} }
@ -234,7 +234,7 @@ int FindRoomNumber(const Vector3i& pos, int startRoomNumber)
if (startRoomNumber != NO_VALUE && startRoomNumber < g_Level.Rooms.size()) if (startRoomNumber != NO_VALUE && startRoomNumber < g_Level.Rooms.size())
{ {
const auto& room = g_Level.Rooms[startRoomNumber]; const auto& room = g_Level.Rooms[startRoomNumber];
for (int neighborRoomNumber : room.neighbors) for (int neighborRoomNumber : room.NeighborRoomNumbers)
{ {
const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber]; const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber];
if (neighborRoomNumber != startRoomNumber && neighborRoom.Active() && if (neighborRoomNumber != startRoomNumber && neighborRoom.Active() &&
@ -258,15 +258,15 @@ Vector3i GetRoomCenter(int roomNumber)
{ {
const auto& room = g_Level.Rooms[roomNumber]; const auto& room = g_Level.Rooms[roomNumber];
int halfLength = BLOCK(room.xSize) / 2; int halfLength = BLOCK(room.XSize) / 2;
int halfDepth = BLOCK(room.zSize) / 2; int halfDepth = BLOCK(room.ZSize) / 2;
int halfHeight = (room.maxceiling - room.minfloor) / 2; int halfHeight = (room.TopHeight - room.BottomHeight) / 2;
// Calculate and return center. // Calculate and return center.
return Vector3i( return Vector3i(
room.x + halfLength, room.Position.x + halfLength,
room.minfloor + halfHeight, room.BottomHeight + halfHeight,
room.z + halfDepth); room.Position.z + halfDepth);
} }
static std::vector<int> GetNeighborRoomNumbers(int roomNumber, unsigned int searchDepth, std::vector<int>& visitedRoomNumbers = std::vector<int>{}) static std::vector<int> GetNeighborRoomNumbers(int roomNumber, unsigned int searchDepth, std::vector<int>& visitedRoomNumbers = std::vector<int>{})
@ -316,7 +316,7 @@ void InitializeNeighborRoomList()
for (int roomNumber = 0; roomNumber < g_Level.Rooms.size(); roomNumber++) for (int roomNumber = 0; roomNumber < g_Level.Rooms.size(); roomNumber++)
{ {
auto& room = g_Level.Rooms[roomNumber]; auto& room = g_Level.Rooms[roomNumber];
room.neighbors = GetNeighborRoomNumbers(roomNumber, NEIGHBOR_ROOM_SEARCH_DEPTH); room.NeighborRoomNumbers = GetNeighborRoomNumbers(roomNumber, NEIGHBOR_ROOM_SEARCH_DEPTH);
} }
// Add flipped variations of itself. // Add flipped variations of itself.
@ -326,11 +326,11 @@ void InitializeNeighborRoomList()
if (room.flippedRoom == NO_VALUE) if (room.flippedRoom == NO_VALUE)
continue; continue;
if (!Contains(room.neighbors, room.flippedRoom)) if (!Contains(room.NeighborRoomNumbers, room.flippedRoom))
room.neighbors.push_back(room.flippedRoom); room.NeighborRoomNumbers.push_back(room.flippedRoom);
auto& flippedRoom = g_Level.Rooms[room.flippedRoom]; auto& flippedRoom = g_Level.Rooms[room.flippedRoom];
if (!Contains(flippedRoom.neighbors, roomNumber)) if (!Contains(flippedRoom.NeighborRoomNumbers, roomNumber))
flippedRoom.neighbors.push_back(roomNumber); flippedRoom.NeighborRoomNumbers.push_back(roomNumber);
} }
} }

View file

@ -87,31 +87,32 @@ struct MESH_INFO
struct ROOM_INFO struct ROOM_INFO
{ {
int index; int RoomNumber = 0;
int x; std::string Name = {};
int y; std::vector<std::string> Tags = {};
int z;
int minfloor; Vector3i Position = Vector3i::Zero;
int maxceiling; int BottomHeight = 0;
int xSize; int TopHeight = 0;
int zSize; int XSize = 0;
int ZSize = 0;
Vector3 ambient; Vector3 ambient;
int flippedRoom;
int flags; int flags;
int meshEffect; int meshEffect;
ReverbType reverbType; ReverbType reverbType;
int flippedRoom;
int flipNumber; int flipNumber;
short itemNumber; short itemNumber;
short fxNumber; short fxNumber;
bool boundActive; bool boundActive;
std::string name = {}; std::vector<int> NeighborRoomNumbers = {};
std::vector<std::string> tags = {};
std::vector<FloorInfo> floor = {}; std::vector<FloorInfo> Sectors = {};
std::vector<ROOM_LIGHT> lights = {}; std::vector<ROOM_LIGHT> lights = {};
std::vector<MESH_INFO> mesh = {}; std::vector<MESH_INFO> mesh = {}; // Statics
std::vector<TriggerVolume> triggerVolumes = {}; std::vector<TriggerVolume> TriggerVolumes = {};
std::vector<Vector3> positions = {}; std::vector<Vector3> positions = {};
std::vector<Vector3> normals = {}; std::vector<Vector3> normals = {};
@ -120,8 +121,6 @@ struct ROOM_INFO
std::vector<BUCKET> buckets = {}; std::vector<BUCKET> buckets = {};
std::vector<ROOM_DOOR> doors = {}; std::vector<ROOM_DOOR> doors = {};
std::vector<int> neighbors = {};
bool Active() const; bool Active() const;
}; };

View file

@ -544,11 +544,11 @@ const std::vector<byte> SaveGame::Build()
std::vector<flatbuffers::Offset<Save::Room>> rooms; std::vector<flatbuffers::Offset<Save::Room>> rooms;
for (auto& room : g_Level.Rooms) for (auto& room : g_Level.Rooms)
{ {
auto nameOffset = fbb.CreateString(room.name); auto nameOffset = fbb.CreateString(room.Name);
Save::RoomBuilder serializedInfo{ fbb }; Save::RoomBuilder serializedInfo{ fbb };
serializedInfo.add_name(nameOffset); serializedInfo.add_name(nameOffset);
serializedInfo.add_index(room.index); serializedInfo.add_index(room.RoomNumber);
serializedInfo.add_reverb_type((int)room.reverbType); serializedInfo.add_reverb_type((int)room.reverbType);
serializedInfo.add_flags(room.flags); serializedInfo.add_flags(room.flags);
auto serializedInfoOffset = serializedInfo.Finish(); auto serializedInfoOffset = serializedInfo.Finish();
@ -991,14 +991,14 @@ const std::vector<byte> SaveGame::Build()
staticMesh.add_flags(room->mesh[j].flags); staticMesh.add_flags(room->mesh[j].flags);
staticMesh.add_hit_points(room->mesh[j].HitPoints); staticMesh.add_hit_points(room->mesh[j].HitPoints);
staticMesh.add_room_number(room->index); staticMesh.add_room_number(room->RoomNumber);
staticMesh.add_number(j); staticMesh.add_number(j);
staticMeshes.push_back(staticMesh.Finish()); staticMeshes.push_back(staticMesh.Finish());
} }
for (int j = 0; j < room->triggerVolumes.size(); j++) for (int j = 0; j < room->TriggerVolumes.size(); j++)
{ {
auto& currVolume = room->triggerVolumes[j]; auto& currVolume = room->TriggerVolumes[j];
std::vector<flatbuffers::Offset<Save::VolumeState>> queue; std::vector<flatbuffers::Offset<Save::VolumeState>> queue;
for (int k = 0; k < currVolume.StateQueue.size(); k++) for (int k = 0; k < currVolume.StateQueue.size(); k++)
@ -1022,7 +1022,7 @@ const std::vector<byte> SaveGame::Build()
auto nameOffset = fbb.CreateString(currVolume.Name); auto nameOffset = fbb.CreateString(currVolume.Name);
Save::VolumeBuilder volume{ fbb }; Save::VolumeBuilder volume{ fbb };
volume.add_room_number(room->index); volume.add_room_number(room->RoomNumber);
volume.add_number(j); volume.add_number(j);
volume.add_name(nameOffset); volume.add_name(nameOffset);
volume.add_enabled(currVolume.Enabled); volume.add_enabled(currVolume.Enabled);
@ -2010,7 +2010,7 @@ static void ParseEffects(const Save::SaveGame* s)
// Restore soundtracks. // Restore soundtracks.
for (int i = 0; i < s->soundtracks()->size(); i++) for (int i = 0; i < s->soundtracks()->size(); i++)
{ {
assertion(i < (int)SoundTrackType::Count, "Soundtrack type count was changed"); TENAssert(i < (int)SoundTrackType::Count, "Soundtrack type count was changed");
auto track = s->soundtracks()->Get(i); auto track = s->soundtracks()->Get(i);
PlaySoundTrack(track->name()->str(), (SoundTrackType)i, track->position()); PlaySoundTrack(track->name()->str(), (SoundTrackType)i, track->position());
@ -2155,7 +2155,7 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode)
for (int i = 0; i < s->rooms()->size(); i++) for (int i = 0; i < s->rooms()->size(); i++)
{ {
auto room = s->rooms()->Get(i); auto room = s->rooms()->Get(i);
g_Level.Rooms[room->index()].name = room->name()->str(); g_Level.Rooms[room->index()].Name = room->name()->str();
g_Level.Rooms[room->index()].flags = room->flags(); g_Level.Rooms[room->index()].flags = room->flags();
g_Level.Rooms[room->index()].reverbType = (ReverbType)room->reverb_type(); g_Level.Rooms[room->index()].reverbType = (ReverbType)room->reverb_type();
} }
@ -2191,18 +2191,18 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode)
auto room = &g_Level.Rooms[volume->room_number()]; auto room = &g_Level.Rooms[volume->room_number()];
int number = volume->number(); int number = volume->number();
room->triggerVolumes[number].Enabled = volume->enabled(); room->TriggerVolumes[number].Enabled = volume->enabled();
room->triggerVolumes[number].Name = volume->name()->str(); room->TriggerVolumes[number].Name = volume->name()->str();
room->triggerVolumes[number].Box.Center = room->TriggerVolumes[number].Box.Center =
room->triggerVolumes[number].Sphere.Center = ToVector3(volume->position()); room->TriggerVolumes[number].Sphere.Center = ToVector3(volume->position());
room->triggerVolumes[number].Box.Orientation = ToVector4(volume->rotation()); room->TriggerVolumes[number].Box.Orientation = ToVector4(volume->rotation());
room->triggerVolumes[number].Box.Extents = ToVector3(volume->scale()); room->TriggerVolumes[number].Box.Extents = ToVector3(volume->scale());
room->triggerVolumes[number].Sphere.Radius = room->triggerVolumes[number].Box.Extents.x; room->TriggerVolumes[number].Sphere.Radius = room->TriggerVolumes[number].Box.Extents.x;
for (int j = 0; j < volume->queue()->size(); j++) for (int j = 0; j < volume->queue()->size(); j++)
{ {
auto state = volume->queue()->Get(j); auto state = volume->queue()->Get(j);
room->triggerVolumes[number].StateQueue.push_back( room->TriggerVolumes[number].StateQueue.push_back(
VolumeState VolumeState
{ {
(VolumeStateStatus)state->status(), (VolumeStateStatus)state->status(),
@ -2228,7 +2228,7 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode)
// Restore action queue. // Restore action queue.
for (int i = 0; i < s->action_queue()->size(); i++) for (int i = 0; i < s->action_queue()->size(); i++)
{ {
assertion(i < ActionQueue.size(), "Action queue size was changed"); TENAssert(i < ActionQueue.size(), "Action queue size was changed");
ActionQueue[i] = (QueueState)s->action_queue()->Get(i); ActionQueue[i] = (QueueState)s->action_queue()->Get(i);
} }

View file

@ -4,6 +4,7 @@
//namespace TEN::Math //namespace TEN::Math
//{ //{
// Math constants // Math constants
constexpr auto PI = 3.14159265358979323846264338327950288419716939937510f; constexpr auto PI = 3.14159265358979323846264338327950288419716939937510f;
constexpr auto PI_MUL_2 = PI * 2; constexpr auto PI_MUL_2 = PI * 2;
constexpr auto PI_DIV_2 = PI / 2; constexpr auto PI_DIV_2 = PI / 2;
@ -15,7 +16,14 @@
constexpr auto SQUARE = [](auto x) { return (x * x); }; constexpr auto SQUARE = [](auto x) { return (x * x); };
constexpr auto CUBE = [](auto x) { return (x * x * x); }; constexpr auto CUBE = [](auto x) { return (x * x * x); };
// Geometry constants
constexpr auto BOX_VERTEX_COUNT = 8;
constexpr auto BOX_EDGE_COUNT = 12;
constexpr auto BOX_FACE_COUNT = 6;
// World constants // World constants
constexpr auto BLOCK_UNIT = 1024; constexpr auto BLOCK_UNIT = 1024;
constexpr auto NO_HEIGHT = INT_MIN + UCHAR_MAX; constexpr auto NO_HEIGHT = INT_MIN + UCHAR_MAX;
constexpr auto MAX_HEIGHT = INT_MIN + 1; // NOTE: +1 prevents issues with sign change. constexpr auto MAX_HEIGHT = INT_MIN + 1; // NOTE: +1 prevents issues with sign change.

View file

@ -8,6 +8,7 @@ class Vector3i;
namespace TEN::Math::Geometry namespace TEN::Math::Geometry
{ {
// Integer-based point translation // Integer-based point translation
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f, const Vector3& axis = Vector3::UnitY); Vector3i TranslatePoint(const Vector3i& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f, const Vector3& axis = Vector3::UnitY);
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, const Vector3i& relOffset, const Vector3& axis = Vector3::UnitY); Vector3i TranslatePoint(const Vector3i& point, short headingAngle, const Vector3i& relOffset, const Vector3& axis = Vector3::UnitY);
Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, const Vector3i& relOffset); Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, const Vector3i& relOffset);
@ -16,6 +17,7 @@ namespace TEN::Math::Geometry
Vector3i TranslatePoint(const Vector3i& point, const Vector3& dir, float dist); Vector3i TranslatePoint(const Vector3i& point, const Vector3& dir, float dist);
// Float-based point translation // Float-based point translation
Vector3 TranslatePoint(const Vector3& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f, const Vector3& axis = Vector3::UnitY); Vector3 TranslatePoint(const Vector3& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f, const Vector3& axis = Vector3::UnitY);
Vector3 TranslatePoint(const Vector3& point, short headingAngle, const Vector3& relOffset, const Vector3& axis = Vector3::UnitY); Vector3 TranslatePoint(const Vector3& point, short headingAngle, const Vector3& relOffset, const Vector3& axis = Vector3::UnitY);
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, const Vector3& relOffset); Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, const Vector3& relOffset);
@ -24,15 +26,18 @@ namespace TEN::Math::Geometry
Vector3 TranslatePoint(const Vector3& point, const Vector3& dir, float dist); Vector3 TranslatePoint(const Vector3& point, const Vector3& dir, float dist);
// Rotation // Rotation
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rot); Vector3 RotatePoint(const Vector3& point, const EulerAngles& rot);
Vector3 RotatePoint(const Vector3& point, const AxisAngle& rot); Vector3 RotatePoint(const Vector3& point, const AxisAngle& rot);
// Angle getters // Angle getters
short GetShortestAngle(short fromAngle, short toAngle); short GetShortestAngle(short fromAngle, short toAngle);
short GetSurfaceSlopeAngle(const Vector3& normal, const Vector3& axis = Vector3::UnitY); short GetSurfaceSlopeAngle(const Vector3& normal, const Vector3& axis = Vector3::UnitY);
short GetSurfaceAspectAngle(const Vector3& normal, const Vector3& axis = Vector3::UnitY); short GetSurfaceAspectAngle(const Vector3& normal, const Vector3& axis = Vector3::UnitY);
// Misc. getters // Misc. getters
float GetDistanceToLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1); float GetDistanceToLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
Vector3 GetClosestPointOnLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1); Vector3 GetClosestPointOnLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
Vector3 GetClosestPointOnLinePerp(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1, const Vector3& axis = Vector3::UnitY); Vector3 GetClosestPointOnLinePerp(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1, const Vector3& axis = Vector3::UnitY);
@ -41,10 +46,12 @@ namespace TEN::Math::Geometry
BoundingBox GetBoundingBox(const std::vector<Vector3>& points); BoundingBox GetBoundingBox(const std::vector<Vector3>& points);
// Converters // Converters
Quaternion ConvertDirectionToQuat(const Vector3& dir); Quaternion ConvertDirectionToQuat(const Vector3& dir);
Vector3 ConvertQuatToDirection(const Quaternion& quat); Vector3 ConvertQuatToDirection(const Quaternion& quat);
// Point relation inquirers // Point relation inquirers
bool IsPointInFront(const Pose& pose, const Vector3& target); bool IsPointInFront(const Pose& pose, const Vector3& target);
bool IsPointInFront(const Vector3& origin, const Vector3& target, const EulerAngles& orient); bool IsPointInFront(const Vector3& origin, const Vector3& target, const EulerAngles& orient);
bool IsPointInFront(const Vector3& origin, const Vector3& target, const Vector3& refPoint); bool IsPointInFront(const Vector3& origin, const Vector3& target, const Vector3& refPoint);
@ -56,5 +63,6 @@ namespace TEN::Math::Geometry
bool IsPointInSphere(const Vector3& point, const BoundingSphere& sphere); bool IsPointInSphere(const Vector3& point, const BoundingSphere& sphere);
// Intersection inquirers // Intersection inquirers
bool CircleIntersects(const Vector3& circle0, const Vector3& circle1); bool CircleIntersects(const Vector3& circle0, const Vector3& circle1);
} }

View file

@ -1,58 +0,0 @@
#include "framework.h"
#include "Math/Interpolation.h"
#include "Math/Constants.h"
namespace TEN::Math
{
float Lerp(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
return (((1.0f - alpha) * value0) + (alpha * value1));
}
float InterpolateCos(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
return Lerp(value0, value1, (1 - cos(alpha * PI)) * 0.5f);
}
float InterpolateCubic(float value0, float value1, float value2, float value3, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
float p = (value3 - value2) - (value0 - value1);
float q = (value0 - value1) - p;
float r = value2 - value0;
float s = value1;
float x = alpha;
float xSquared = SQUARE(x);
return ((p * xSquared * x) + (q * xSquared) + (r * x) + s);
}
float Smoothstep(float alpha)
{
return Smoothstep(0.0f, 1.0f, alpha);
}
float Smoothstep(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, value0, value1);
// Don't process if input value is same as one of the values.
if (alpha == value0)
{
return value0;
}
else if (alpha == value1)
{
return value1;
}
// Scale, bias, and saturate alpha to [0, 1] range.
alpha = std::clamp((alpha - value0) / (value1 - value0), 0.0f, 1.0f);
// Evaluate polynomial.
return (CUBE(alpha) * (alpha * (alpha * 6 - 15) + 10));
}
}

View file

@ -1,10 +0,0 @@
#pragma once
namespace TEN::Math
{
float Lerp(float value0, float value1, float alpha);
float InterpolateCos(float value0, float value1, float alpha);
float InterpolateCubic(float value0, float value1, float value2, float value3, float alpha);
float Smoothstep(float alpha);
float Smoothstep(float value0, float value1, float alpha);
}

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "Math/Constants.h" #include "Math/Constants.h"
constexpr auto FP_SHIFT = 16; constexpr auto FP_SHIFT = 16;
@ -8,14 +9,19 @@ constexpr auto PREDICTIVE_SCALE_FACTOR = 14;
constexpr auto SHORTS_TO_1_DEGREE = 65536.0f / 360.0f; constexpr auto SHORTS_TO_1_DEGREE = 65536.0f / 360.0f;
constexpr auto DEGREES_TO_1_SHORT = 360.0f / 65536.0f; constexpr auto DEGREES_TO_1_SHORT = 360.0f / 65536.0f;
constexpr float ROUND(float value)
{
return ((value > 0.0f) ? int(value + 0.5f) : int(value - 0.5f));
}
constexpr short ANGLE(float degrees) constexpr short ANGLE(float degrees)
{ {
return short(degrees * SHORTS_TO_1_DEGREE); return (short)ROUND(degrees * SHORTS_TO_1_DEGREE);
} }
constexpr short FROM_RAD(float radians) constexpr short FROM_RAD(float radians)
{ {
return short((radians / RADIAN) * SHORTS_TO_1_DEGREE); return (short)ROUND((radians / RADIAN) * SHORTS_TO_1_DEGREE);
} }
constexpr float TO_DEGREES(short shortAngle) constexpr float TO_DEGREES(short shortAngle)

View file

@ -1,35 +0,0 @@
#include "framework.h"
#include <cmath>
#include "Math/Math.h"
namespace TEN::Math
{
float Luma(const Vector3& color)
{
constexpr auto RED_COEFF = 0.2126f;
constexpr auto GREEN_COEFF = 0.7152f;
constexpr auto BLUE_COEFF = 0.0722f;
// Use Rec.709 trichromat formula to get perceptive luma value.
return float((color.x * RED_COEFF) + (color.y * GREEN_COEFF) + (color.z * BLUE_COEFF));
}
Vector3 Screen(const Vector3& ambient, const Vector3& tint)
{
float luma = Luma(tint);
auto multiplicative = ambient * tint;
auto additive = ambient + tint;
return Vector3(
Lerp(multiplicative.x, additive.x, luma),
Lerp(multiplicative.y, additive.y, luma),
Lerp(multiplicative.z, additive.z, luma));
}
Vector4 Screen(const Vector4& ambient, const Vector4& tint)
{
auto result = Screen(Vector3(ambient), Vector3(tint));
return Vector4(result.x, result.y, result.z, ambient.w * tint.w);
}
}

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "Math/Constants.h" #include "Math/Constants.h"
#include "Math/Geometry.h" #include "Math/Geometry.h"
#include "Math/Interpolation.h"
#include "Math/Legacy.h" #include "Math/Legacy.h"
#include "Math/Objects/EulerAngles.h" #include "Math/Objects/EulerAngles.h"
#include "Math/Objects/GameBoundingBox.h" #include "Math/Objects/GameBoundingBox.h"
@ -11,13 +10,4 @@
#include "Math/Objects/Vector3i.h" #include "Math/Objects/Vector3i.h"
#include "Math/Random.h" #include "Math/Random.h"
#include "Math/Solvers.h" #include "Math/Solvers.h"
#include "Math/Utils.h"
namespace TEN::Math
{
constexpr inline auto OFFSET_RADIUS = [](auto x) { return ((x * SQRT_2) + 4); };
constexpr inline auto MESH_BITS = [](auto x) { return (1 << x); };
float Luma(const Vector3& color);
Vector3 Screen(const Vector3& ambient, const Vector3& tint);
Vector4 Screen(const Vector4& ambient, const Vector4& tint);
}

View file

@ -8,14 +8,17 @@ class EulerAngles;
{ {
private: private:
// Members // Members
Vector3 _axis = Vector3::Backward; Vector3 _axis = Vector3::Backward;
short _angle = 0; short _angle = 0;
public: public:
// Constants // Constants
static const AxisAngle Identity; static const AxisAngle Identity;
// Constructors // Constructors
AxisAngle() {}; AxisAngle() {};
AxisAngle(const Vector3& axis, short angle); AxisAngle(const Vector3& axis, short angle);
AxisAngle(const EulerAngles& eulers); AxisAngle(const EulerAngles& eulers);
@ -23,24 +26,29 @@ class EulerAngles;
AxisAngle(const Matrix& rotMatrix); AxisAngle(const Matrix& rotMatrix);
// Getters // Getters
Vector3 GetAxis() const; Vector3 GetAxis() const;
short GetAngle() const; short GetAngle() const;
// Setters // Setters
void SetAxis(const Vector3& axis); void SetAxis(const Vector3& axis);
void SetAngle(short angle); void SetAngle(short angle);
// Utilities // Utilities
void Slerp(const AxisAngle& axisAngleTo, float alpha); void Slerp(const AxisAngle& axisAngleTo, float alpha);
static AxisAngle Slerp(const AxisAngle& axisAngleFrom, const AxisAngle& axisAngleTo, float alpha); static AxisAngle Slerp(const AxisAngle& axisAngleFrom, const AxisAngle& axisAngleTo, float alpha);
// Converters // Converters
Vector3 ToDirection() const; Vector3 ToDirection() const;
EulerAngles ToEulerAngles() const; EulerAngles ToEulerAngles() const;
Quaternion ToQuaternion() const; Quaternion ToQuaternion() const;
Matrix ToRotationMatrix() const; Matrix ToRotationMatrix() const;
// Operators // Operators
bool operator ==(const AxisAngle& axisAngle) const; bool operator ==(const AxisAngle& axisAngle) const;
bool operator !=(const AxisAngle& axisAngle) const; bool operator !=(const AxisAngle& axisAngle) const;
AxisAngle& operator =(const AxisAngle& axisAngle); AxisAngle& operator =(const AxisAngle& axisAngle);

View file

@ -7,14 +7,17 @@
{ {
public: public:
// Members (CONVENTION: X = Pitch, Y = Yaw, Z = Roll) // Members (CONVENTION: X = Pitch, Y = Yaw, Z = Roll)
short x = 0; short x = 0;
short y = 0; short y = 0;
short z = 0; short z = 0;
// Constants // Constants
static const EulerAngles Identity; static const EulerAngles Identity;
// Constructors // Constructors
constexpr EulerAngles() {}; constexpr EulerAngles() {};
constexpr EulerAngles(short x, short y, short z) { this->x = x; this->y = y; this->z = z; }; constexpr EulerAngles(short x, short y, short z) { this->x = x; this->y = y; this->z = z; };
EulerAngles(const Vector3& dir); EulerAngles(const Vector3& dir);
@ -23,6 +26,7 @@
EulerAngles(const Matrix& rotMatrix); EulerAngles(const Matrix& rotMatrix);
// Utilities // Utilities
static bool Compare(const EulerAngles& eulers0, const EulerAngles& eulers1, short epsilon = 3); static bool Compare(const EulerAngles& eulers0, const EulerAngles& eulers1, short epsilon = 3);
void Lerp(const EulerAngles& eulersTo, float alpha, short epsilon = 3); void Lerp(const EulerAngles& eulersTo, float alpha, short epsilon = 3);
static EulerAngles Lerp(const EulerAngles& eulersFrom, const EulerAngles& eulersTo, float alpha, short epsilon = 3); static EulerAngles Lerp(const EulerAngles& eulersFrom, const EulerAngles& eulersTo, float alpha, short epsilon = 3);
@ -32,12 +36,14 @@
static EulerAngles InterpolateConstant(const EulerAngles& eulersFrom, const EulerAngles& eulerTo, short angularVel); static EulerAngles InterpolateConstant(const EulerAngles& eulersFrom, const EulerAngles& eulerTo, short angularVel);
// Converters // Converters
Vector3 ToDirection() const; Vector3 ToDirection() const;
AxisAngle ToAxisAngle() const; AxisAngle ToAxisAngle() const;
Quaternion ToQuaternion() const; Quaternion ToQuaternion() const;
Matrix ToRotationMatrix() const; Matrix ToRotationMatrix() const;
// Operators // Operators
bool operator ==(const EulerAngles& eulers) const; bool operator ==(const EulerAngles& eulers) const;
bool operator !=(const EulerAngles& eulers) const; bool operator !=(const EulerAngles& eulers) const;
EulerAngles& operator =(const EulerAngles& eulers); EulerAngles& operator =(const EulerAngles& eulers);
@ -54,6 +60,7 @@
private: private:
// Temporary. Will be integrated into eventual Angle class. // Temporary. Will be integrated into eventual Angle class.
static float ClampAlpha(float alpha); static float ClampAlpha(float alpha);
static bool Compare(short angle0, short angle1, short epsilon = 3); static bool Compare(short angle0, short angle1, short epsilon = 3);
static short Lerp(short angleFrom, short angleTo, float alpha, short epsilon = 3); static short Lerp(short angleFrom, short angleTo, float alpha, short epsilon = 3);

View file

@ -12,6 +12,7 @@ struct ObjectInfo;
{ {
public: public:
// Members // Members
int X1 = 0; int X1 = 0;
int X2 = 0; int X2 = 0;
int Y1 = 0; int Y1 = 0;
@ -20,15 +21,18 @@ struct ObjectInfo;
int Z2 = 0; int Z2 = 0;
// Constants // Constants
static const GameBoundingBox Zero; static const GameBoundingBox Zero;
// Constructors // Constructors
GameBoundingBox() {}; GameBoundingBox() {};
GameBoundingBox(float x1, float x2, float y1, float y2, float z1, float z2); GameBoundingBox(float x1, float x2, float y1, float y2, float z1, float z2);
GameBoundingBox(GAME_OBJECT_ID objectID, int animNumber = 0, int frameNumber = 0); GameBoundingBox(GAME_OBJECT_ID objectID, int animNumber = 0, int frameNumber = 0);
GameBoundingBox(const ItemInfo* item); GameBoundingBox(const ItemInfo* item);
// Getters // Getters
int GetWidth() const; int GetWidth() const;
int GetHeight() const; int GetHeight() const;
int GetDepth() const; int GetDepth() const;
@ -36,13 +40,16 @@ struct ObjectInfo;
Vector3 GetExtents() const; Vector3 GetExtents() const;
// Utilities // Utilities
void Rotate(const EulerAngles& rot); void Rotate(const EulerAngles& rot);
// Converters // Converters
BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const; BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const;
BoundingOrientedBox ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const; BoundingOrientedBox ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const;
// Operators // Operators
GameBoundingBox operator +(const GameBoundingBox& bounds) const; GameBoundingBox operator +(const GameBoundingBox& bounds) const;
GameBoundingBox operator +(const Pose& pose) const; GameBoundingBox operator +(const Pose& pose) const;
GameBoundingBox operator -(const GameBoundingBox& bounds) const; GameBoundingBox operator -(const GameBoundingBox& bounds) const;

View file

@ -8,6 +8,7 @@ class Vector3i;
{ {
public: public:
// Members // Members
int x = 0; int x = 0;
int y = 0; int y = 0;
int z = 0; int z = 0;
@ -15,9 +16,11 @@ class Vector3i;
int BoxNumber = 0; // Unused. int BoxNumber = 0; // Unused.
// Constants // Constants
static const GameVector Zero; static const GameVector Zero;
// Constructors // Constructors
GameVector(); GameVector();
GameVector(const Vector3i& pos); GameVector(const Vector3i& pos);
GameVector(const Vector3i& pos, short roomNumber); GameVector(const Vector3i& pos, short roomNumber);
@ -25,10 +28,12 @@ class Vector3i;
GameVector(int xPos, int yPos, int zPos, short roomNumber); GameVector(int xPos, int yPos, int zPos, short roomNumber);
// Converters // Converters
Vector3 ToVector3() const; Vector3 ToVector3() const;
Vector3i ToVector3i() const; Vector3i ToVector3i() const;
// Operators // Operators
bool operator ==(const GameVector& vector) const; bool operator ==(const GameVector& vector) const;
bool operator !=(const GameVector& vector) const; bool operator !=(const GameVector& vector) const;
GameVector& operator =(const GameVector& vector); GameVector& operator =(const GameVector& vector);

View file

@ -8,13 +8,16 @@
{ {
public: public:
// Members // Members
Vector3i Position = Vector3i::Zero; Vector3i Position = Vector3i::Zero;
EulerAngles Orientation = EulerAngles::Identity; EulerAngles Orientation = EulerAngles::Identity;
// Constants // Constants
static const Pose Zero; static const Pose Zero;
// Constructors // Constructors
Pose(); Pose();
Pose(const Vector3i& pos); Pose(const Vector3i& pos);
Pose(int xPos, int yPos, int zPos); Pose(int xPos, int yPos, int zPos);
@ -26,11 +29,13 @@
Pose(int xPos, int yPos, int zPos, short xOrient, short yOrient, short zOrient); Pose(int xPos, int yPos, int zPos, short xOrient, short yOrient, short zOrient);
// Utilities // Utilities
void Translate(short headingAngle, float forward, float down = 0.0f, float right = 0.0f); void Translate(short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
void Translate(const EulerAngles& orient, float dist); void Translate(const EulerAngles& orient, float dist);
void Translate(const Vector3& dir, float dist); void Translate(const Vector3& dir, float dist);
// Operators // Operators
bool operator ==(const Pose& pose) const; bool operator ==(const Pose& pose) const;
bool operator !=(const Pose& pose) const; bool operator !=(const Pose& pose) const;
}; };

View file

@ -6,25 +6,31 @@ namespace TEN::Math
{ {
public: public:
// Members // Members
int x = 0; int x = 0;
int y = 0; int y = 0;
// Constants // Constants
static const Vector2i Zero; static const Vector2i Zero;
// Constructors // Constructors
constexpr Vector2i() {}; constexpr Vector2i() {};
constexpr Vector2i(int x, int y) { this->x = x; this->y = y; }; constexpr Vector2i(int x, int y) { this->x = x; this->y = y; };
Vector2i(const Vector2& vector); Vector2i(const Vector2& vector);
// Utilities // Utilities
static float Distance(const Vector2i& origin, const Vector2i& target); static float Distance(const Vector2i& origin, const Vector2i& target);
static float DistanceSquared(const Vector2i& origin, const Vector2i& target); static float DistanceSquared(const Vector2i& origin, const Vector2i& target);
// Converters // Converters
Vector2 ToVector2() const; Vector2 ToVector2() const;
// Operators // Operators
bool operator ==(const Vector2i& vector) const; bool operator ==(const Vector2i& vector) const;
bool operator !=(const Vector2i& vector) const; bool operator !=(const Vector2i& vector) const;
Vector2i& operator =(const Vector2i& vector); Vector2i& operator =(const Vector2i& vector);

View file

@ -6,28 +6,34 @@
{ {
public: public:
// Members // Members
int x = 0; int x = 0;
int y = 0; int y = 0;
int z = 0; int z = 0;
// Constants // Constants
static const Vector3i Zero; static const Vector3i Zero;
// Constructors // Constructors
constexpr Vector3i() {}; constexpr Vector3i() {};
constexpr Vector3i(int x, int y, int z) { this->x = x; this->y = y; this->z = z; }; constexpr Vector3i(int x, int y, int z) { this->x = x; this->y = y; this->z = z; };
Vector3i(const Vector3& vector); Vector3i(const Vector3& vector);
// Utilities // Utilities
static float Distance(const Vector3i& origin, const Vector3i& target); static float Distance(const Vector3i& origin, const Vector3i& target);
static float DistanceSquared(const Vector3i& origin, const Vector3i& target); static float DistanceSquared(const Vector3i& origin, const Vector3i& target);
void Lerp(const Vector3i& target, float alpha); void Lerp(const Vector3i& target, float alpha);
static Vector3i Lerp(const Vector3i& origin, const Vector3i& target, float alpha); static Vector3i Lerp(const Vector3i& origin, const Vector3i& target, float alpha);
// Converters // Converters
Vector3 ToVector3() const; Vector3 ToVector3() const;
// Operators // Operators
bool operator ==(const Vector3i& vector) const; bool operator ==(const Vector3i& vector) const;
bool operator !=(const Vector3i& vector) const; bool operator !=(const Vector3i& vector) const;
Vector3i& operator =(const Vector3i& vector); Vector3i& operator =(const Vector3i& vector);

View file

@ -5,16 +5,19 @@ class EulerAngles;
namespace TEN::Math::Random namespace TEN::Math::Random
{ {
// Value generation // Value generation
int GenerateInt(int low = 0, int high = SHRT_MAX); int GenerateInt(int low = 0, int high = SHRT_MAX);
float GenerateFloat(float low = 0.0f, float high = 1.0f); float GenerateFloat(float low = 0.0f, float high = 1.0f);
short GenerateAngle(short low = SHRT_MIN, short high = SHRT_MAX); short GenerateAngle(short low = SHRT_MIN, short high = SHRT_MAX);
// 2D geometric generation // 2D geometric generation
Vector2 GenerateDirection2D(); Vector2 GenerateDirection2D();
Vector2 GeneratePoint2DInSquare(const Vector2& pos, short orient, float apothem); Vector2 GeneratePoint2DInSquare(const Vector2& pos, short orient, float apothem);
Vector2 GeneratePoint2DInCircle(const Vector2& pos, float radius); Vector2 GeneratePoint2DInCircle(const Vector2& pos, float radius);
// 3D geometric generation // 3D geometric generation
Vector3 GenerateDirection(); Vector3 GenerateDirection();
Vector3 GenerateDirectionInCone(const Vector3& dir, float semiangleInDeg); Vector3 GenerateDirectionInCone(const Vector3& dir, float semiangleInDeg);
Vector3 GeneratePointInBox(const BoundingOrientedBox& box); Vector3 GeneratePointInBox(const BoundingOrientedBox& box);
@ -22,5 +25,7 @@ namespace TEN::Math::Random
Vector3 GeneratePointOnSphere(const BoundingSphere& sphere); Vector3 GeneratePointOnSphere(const BoundingSphere& sphere);
Vector3 GeneratePointInSpheroid(const Vector3& center, const EulerAngles& orient, const Vector3& semiMajorAxis); Vector3 GeneratePointInSpheroid(const Vector3& center, const EulerAngles& orient, const Vector3& semiMajorAxis);
// Probability
bool TestProbability(float prob); bool TestProbability(float prob);
} }

View file

@ -56,7 +56,7 @@ namespace TEN::Math::Solvers
float a = flipXY ? (scaledTarget.y - origin.y) : (scaledTarget.x - origin.x); float a = flipXY ? (scaledTarget.y - origin.y) : (scaledTarget.x - origin.x);
float b = flipXY ? (scaledTarget.x - origin.x) : (scaledTarget.y - origin.y); float b = flipXY ? (scaledTarget.x - origin.x) : (scaledTarget.y - origin.y);
assertion(abs(a) >= EPSILON, "SolveIK2D() failed."); TENAssert(abs(a) >= EPSILON, "SolveIK2D() failed.");
float m = ((SQUARE(length0) - SQUARE(length1)) + (SQUARE(a) + SQUARE(b))) / (2.0f * a); float m = ((SQUARE(length0) - SQUARE(length1)) + (SQUARE(a) + SQUARE(b))) / (2.0f * a);
float n = b / a; float n = b / a;

111
TombEngine/Math/Utils.cpp Normal file
View file

@ -0,0 +1,111 @@
#include "framework.h"
#include "Math/Utils.h"
#include "Math/Constants.h"
namespace TEN::Math
{
float FloorToStep(float value, float step)
{
return (floor(value / step) * step);
}
float CeilToStep(float value, float step)
{
return (ceil(value / step) * step);
}
float RoundToStep(float value, float step)
{
return (round(value / step) * step);
}
float Remap(float value, float min0, float max0, float min1, float max1)
{
float alpha = (value - min0) / (max0 - min0);
return Lerp(min1, max1, alpha);
}
float Lerp(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
return (((1.0f - alpha) * value0) + (alpha * value1));
}
float Smoothstep(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, value0, value1);
// Scale, bias, and saturate alpha to [0, 1] range.
alpha = std::clamp((alpha - value0) / (value1 - value0), 0.0f, 1.0f);
// Evaluate polynomial.
return (CUBE(alpha) * (alpha * ((alpha * 6) - 15.0f) + 10.0f));
}
float Smoothstep(float alpha)
{
return Smoothstep(0.0f, 1.0f, alpha);
}
float EaseInSine(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
return Lerp(value0, value1, 1.0f - cos((alpha * PI) / 2));
}
float EaseInSine(float alpha)
{
return EaseInSine(0.0f, 1.0f, alpha);
}
float EaseOutSine(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
return Lerp(value0, value1, sin((alpha * PI) / 2));
}
float EaseOutSine(float alpha)
{
return EaseOutSine(0.0f, 1.0f, alpha);
}
float EaseInOutSine(float value0, float value1, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
return Lerp(value0, value1, (1.0f - cos(alpha * PI)) / 2);
}
float EaseInOutSine(float alpha)
{
return EaseInOutSine(0.0f, 1.0f, alpha);
}
float Luma(const Vector3& color)
{
constexpr auto RED_COEFF = 0.2126f;
constexpr auto GREEN_COEFF = 0.7152f;
constexpr auto BLUE_COEFF = 0.0722f;
// Use Rec.709 trichromat formula to get perceptive luma value.
return float((color.x * RED_COEFF) + (color.y * GREEN_COEFF) + (color.z * BLUE_COEFF));
}
Vector3 Screen(const Vector3& ambient, const Vector3& tint)
{
float luma = Luma(tint);
auto multiplicative = ambient * tint;
auto additive = ambient + tint;
return Vector3(
Lerp(multiplicative.x, additive.x, luma),
Lerp(multiplicative.y, additive.y, luma),
Lerp(multiplicative.z, additive.z, luma));
}
Vector4 Screen(const Vector4& ambient, const Vector4& tint)
{
auto result = Screen(Vector3(ambient), Vector3(tint));
return Vector4(result.x, result.y, result.z, ambient.w * tint.w);
}
}

32
TombEngine/Math/Utils.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
namespace TEN::Math
{
constexpr inline auto OFFSET_RADIUS = [](auto x) { return ((x * SQRT_2) + 4); };
constexpr inline auto MESH_BITS = [](auto x) { return (1 << x); };
// Value manipulation
float FloorToStep(float value, float step);
float CeilToStep(float value, float step);
float RoundToStep(float value, float step);
float Remap(float value, float min0, float max0, float min1, float max1);
// Interpolation
float Lerp(float value0, float value1, float alpha);
float Smoothstep(float value0, float value1, float alpha);
float Smoothstep(float alpha);
float EaseInSine(float value0, float value1, float alpha);
float EaseInSine(float alpha);
float EaseOutSine(float value0, float value1, float alpha);
float EaseOutSine(float alpha);
float EaseInOutSine(float value0, float value1, float alpha);
float EaseInOutSine(float alpha);
// Color
float Luma(const Vector3& color);
Vector3 Screen(const Vector3& ambient, const Vector3& tint);
Vector4 Screen(const Vector4& ambient, const Vector4& tint);
}

View file

@ -65,7 +65,7 @@ namespace TEN::Entities::Effects
void BurnNearbyItems(ItemInfo* item, int radius) void BurnNearbyItems(ItemInfo* item, int radius)
{ {
auto collObjects = GetCollidedObjects(*item, true, false, radius, ObjectCollectionMode::Items); auto collObjects = GetCollidedObjects(*item, true, false, radius, ObjectCollectionMode::Items);
for (auto* itemPtr : collObjects.ItemPtrs) for (auto* itemPtr : collObjects.Items)
{ {
if (TestEnvironment(ENV_FLAG_WATER, itemPtr->RoomNumber)) if (TestEnvironment(ENV_FLAG_WATER, itemPtr->RoomNumber))
continue; continue;

View file

@ -180,7 +180,7 @@ void ElectricityWiresControl(short itemNumber)
return; return;
auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(2), ObjectCollectionMode::Items); auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(2), ObjectCollectionMode::Items);
for (auto* itemPtr : collObjects.ItemPtrs) for (auto* itemPtr : collObjects.Items)
{ {
const auto& object = Objects[itemPtr->ObjectNumber]; const auto& object = Objects[itemPtr->ObjectNumber];

View file

@ -77,35 +77,35 @@ namespace TEN::Entities::Doors
xOffset = BLOCK(1); xOffset = BLOCK(1);
auto* r = &g_Level.Rooms[doorItem->RoomNumber]; auto* r = &g_Level.Rooms[doorItem->RoomNumber];
doorData->d1.floor = GetSector(r, doorItem->Pose.Position.x - r->x + xOffset, doorItem->Pose.Position.z - r->z + zOffset); doorData->d1.floor = GetSector(r, doorItem->Pose.Position.x - r->Position.x + xOffset, doorItem->Pose.Position.z - r->Position.z + zOffset);
auto roomNumber = doorData->d1.floor->SidePortalRoomNumber; auto roomNumber = doorData->d1.floor->SidePortalRoomNumber;
if (roomNumber == NO_VALUE) if (roomNumber == NO_VALUE)
boxNumber = doorData->d1.floor->Box; boxNumber = doorData->d1.floor->PathfindingBoxID;
else else
{ {
auto* b = &g_Level.Rooms[roomNumber]; auto* b = &g_Level.Rooms[roomNumber];
boxNumber = GetSector(b, doorItem->Pose.Position.x - b->x + xOffset, doorItem->Pose.Position.z - b->z + zOffset)->Box; boxNumber = GetSector(b, doorItem->Pose.Position.x - b->Position.x + xOffset, doorItem->Pose.Position.z - b->Position.z + zOffset)->PathfindingBoxID;
} }
doorData->d1.block = (boxNumber != NO_VALUE && g_Level.Boxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE; doorData->d1.block = (boxNumber != NO_VALUE && g_Level.PathfindingBoxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE;
doorData->d1.data = *doorData->d1.floor; doorData->d1.data = *doorData->d1.floor;
if (r->flippedRoom != -1) if (r->flippedRoom != -1)
{ {
r = &g_Level.Rooms[r->flippedRoom]; r = &g_Level.Rooms[r->flippedRoom];
doorData->d1flip.floor = GetSector(r, doorItem->Pose.Position.x - r->x + xOffset, doorItem->Pose.Position.z - r->z + zOffset); doorData->d1flip.floor = GetSector(r, doorItem->Pose.Position.x - r->Position.x + xOffset, doorItem->Pose.Position.z - r->Position.z + zOffset);
roomNumber = doorData->d1flip.floor->SidePortalRoomNumber; roomNumber = doorData->d1flip.floor->SidePortalRoomNumber;
if (roomNumber == NO_VALUE) if (roomNumber == NO_VALUE)
boxNumber = doorData->d1flip.floor->Box; boxNumber = doorData->d1flip.floor->PathfindingBoxID;
else else
{ {
auto* b = &g_Level.Rooms[roomNumber]; auto* b = &g_Level.Rooms[roomNumber];
boxNumber = GetSector(b, doorItem->Pose.Position.x - b->x + xOffset, doorItem->Pose.Position.z - b->z + zOffset)->Box; boxNumber = GetSector(b, doorItem->Pose.Position.x - b->Position.x + xOffset, doorItem->Pose.Position.z - b->Position.z + zOffset)->PathfindingBoxID;
} }
doorData->d1flip.block = (boxNumber != NO_VALUE && g_Level.Boxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE; doorData->d1flip.block = (boxNumber != NO_VALUE && g_Level.PathfindingBoxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE;
doorData->d1flip.data = *doorData->d1flip.floor; doorData->d1flip.data = *doorData->d1flip.floor;
} }
else else
@ -124,35 +124,35 @@ namespace TEN::Entities::Doors
else else
{ {
r = &g_Level.Rooms[twoRoom]; r = &g_Level.Rooms[twoRoom];
doorData->d2.floor = GetSector(r, doorItem->Pose.Position.x - r->x, doorItem->Pose.Position.z - r->z); doorData->d2.floor = GetSector(r, doorItem->Pose.Position.x - r->Position.x, doorItem->Pose.Position.z - r->Position.z);
roomNumber = doorData->d2.floor->SidePortalRoomNumber; roomNumber = doorData->d2.floor->SidePortalRoomNumber;
if (roomNumber == NO_VALUE) if (roomNumber == NO_VALUE)
boxNumber = doorData->d2.floor->Box; boxNumber = doorData->d2.floor->PathfindingBoxID;
else else
{ {
auto* b = &g_Level.Rooms[roomNumber]; auto* b = &g_Level.Rooms[roomNumber];
boxNumber = GetSector(b, doorItem->Pose.Position.x - b->x, doorItem->Pose.Position.z - b->z)->Box; boxNumber = GetSector(b, doorItem->Pose.Position.x - b->Position.x, doorItem->Pose.Position.z - b->Position.z)->PathfindingBoxID;
} }
doorData->d2.block = (boxNumber != NO_VALUE && g_Level.Boxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE; doorData->d2.block = (boxNumber != NO_VALUE && g_Level.PathfindingBoxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE;
doorData->d2.data = *doorData->d2.floor; doorData->d2.data = *doorData->d2.floor;
if (r->flippedRoom != -1) if (r->flippedRoom != -1)
{ {
r = &g_Level.Rooms[r->flippedRoom]; r = &g_Level.Rooms[r->flippedRoom];
doorData->d2flip.floor = GetSector(r, doorItem->Pose.Position.x - r->x, doorItem->Pose.Position.z - r->z); doorData->d2flip.floor = GetSector(r, doorItem->Pose.Position.x - r->Position.x, doorItem->Pose.Position.z - r->Position.z);
roomNumber = doorData->d2flip.floor->SidePortalRoomNumber; roomNumber = doorData->d2flip.floor->SidePortalRoomNumber;
if (roomNumber == NO_VALUE) if (roomNumber == NO_VALUE)
boxNumber = doorData->d2flip.floor->Box; boxNumber = doorData->d2flip.floor->PathfindingBoxID;
else else
{ {
auto* b = &g_Level.Rooms[roomNumber]; auto* b = &g_Level.Rooms[roomNumber];
boxNumber = GetSector(b, doorItem->Pose.Position.x - b->x, doorItem->Pose.Position.z - b->z)->Box; boxNumber = GetSector(b, doorItem->Pose.Position.x - b->Position.x, doorItem->Pose.Position.z - b->Position.z)->PathfindingBoxID;
} }
doorData->d2flip.block = (boxNumber != NO_VALUE && g_Level.Boxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE; doorData->d2flip.block = (boxNumber != NO_VALUE && g_Level.PathfindingBoxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE;
doorData->d2flip.data = *doorData->d2flip.floor; doorData->d2flip.data = *doorData->d2flip.floor;
} }
else else
@ -401,7 +401,7 @@ namespace TEN::Entities::Doors
short boxIndex = doorPos->block; short boxIndex = doorPos->block;
if (boxIndex != NO_VALUE) if (boxIndex != NO_VALUE)
{ {
g_Level.Boxes[boxIndex].flags &= ~BLOCKED; g_Level.PathfindingBoxes[boxIndex].flags &= ~BLOCKED;
for (auto& currentCreature : ActiveCreatures) for (auto& currentCreature : ActiveCreatures)
currentCreature->LOT.TargetBox = NO_VALUE; currentCreature->LOT.TargetBox = NO_VALUE;
} }
@ -416,7 +416,7 @@ namespace TEN::Entities::Doors
if (floor) if (floor)
{ {
floor->Box = NO_VALUE; floor->PathfindingBoxID = NO_VALUE;
floor->TriggerIndex = 0; floor->TriggerIndex = 0;
// FIXME: HACK!!!!!!! // FIXME: HACK!!!!!!!
@ -435,7 +435,7 @@ namespace TEN::Entities::Doors
short boxIndex = doorPos->block; short boxIndex = doorPos->block;
if (boxIndex != NO_VALUE) if (boxIndex != NO_VALUE)
{ {
g_Level.Boxes[boxIndex].flags |= BLOCKED; g_Level.PathfindingBoxes[boxIndex].flags |= BLOCKED;
for (auto& currentCreature : ActiveCreatures) for (auto& currentCreature : ActiveCreatures)
currentCreature->LOT.TargetBox = NO_VALUE; currentCreature->LOT.TargetBox = NO_VALUE;

View file

@ -120,10 +120,10 @@ namespace TEN::Entities::Generic
auto collObjects = GetCollidedObjects(pushableItem, true, true); auto collObjects = GetCollidedObjects(pushableItem, true, true);
pushableItem.Pose.Position = prevPos; pushableItem.Pose.Position = prevPos;
if (!collObjects.StaticPtrs.empty()) if (!collObjects.Statics.empty())
return false; return false;
for (const auto* itemPtr : collObjects.ItemPtrs) for (const auto* itemPtr : collObjects.Items)
{ {
const auto& object = Objects[itemPtr->ObjectNumber]; const auto& object = Objects[itemPtr->ObjectNumber];
@ -191,10 +191,10 @@ namespace TEN::Entities::Generic
auto collObjects = GetCollidedObjects(*LaraItem, true, true); auto collObjects = GetCollidedObjects(*LaraItem, true, true);
LaraItem->Pose.Position = prevPos; LaraItem->Pose.Position = prevPos;
if (!collObjects.StaticPtrs.empty()) if (!collObjects.Statics.empty())
return false; return false;
for (const auto* itemPtr : collObjects.ItemPtrs) for (const auto* itemPtr : collObjects.Items)
{ {
const auto& object = Objects[itemPtr->ObjectNumber]; const auto& object = Objects[itemPtr->ObjectNumber];
@ -315,7 +315,7 @@ namespace TEN::Entities::Generic
waterHeight = pointColl.GetWaterSurfaceHeight(); waterHeight = pointColl.GetWaterSurfaceHeight();
if (waterHeight == NO_HEIGHT && TestEnvironment(ENV_FLAG_SWAMP, item.RoomNumber)) if (waterHeight == NO_HEIGHT && TestEnvironment(ENV_FLAG_SWAMP, item.RoomNumber))
waterHeight = g_Level.Rooms[item.RoomNumber].maxceiling; waterHeight = g_Level.Rooms[item.RoomNumber].TopHeight;
AddPushableBridge(item); AddPushableBridge(item);
} }
@ -324,7 +324,7 @@ namespace TEN::Entities::Generic
waterHeight = pointColl.GetWaterSurfaceHeight(); waterHeight = pointColl.GetWaterSurfaceHeight();
if (waterHeight == NO_HEIGHT && TestEnvironment(ENV_FLAG_SWAMP, item.RoomNumber)) if (waterHeight == NO_HEIGHT && TestEnvironment(ENV_FLAG_SWAMP, item.RoomNumber))
waterHeight = g_Level.Rooms[item.RoomNumber].maxceiling; waterHeight = g_Level.Rooms[item.RoomNumber].TopHeight;
} }
auto pushableColl = PushableCollisionData{}; auto pushableColl = PushableCollisionData{};

View file

@ -367,6 +367,20 @@ namespace TEN::Entities::Generic
} }
} }
// TODO: Remove.
static float InterpolateCubic(float value0, float value1, float value2, float value3, float alpha)
{
alpha = std::clamp(alpha, 0.0f, 1.0f);
float p = (value3 - value2) - (value0 - value1);
float q = (value0 - value1) - p;
float r = value2 - value0;
float s = value1;
float x = alpha;
float xSquared = SQUARE(x);
return ((p * xSquared * x) + (q * xSquared) + (r * x) + s);
}
static void HandleEdgeSlipState(ItemInfo& pushableItem) static void HandleEdgeSlipState(ItemInfo& pushableItem)
{ {
constexpr auto LEAN_ANGLE_MAX = ANGLE(40.0f); constexpr auto LEAN_ANGLE_MAX = ANGLE(40.0f);

View file

@ -261,19 +261,19 @@ namespace TEN::Entities::Generic
if (!collObjects.IsEmpty()) if (!collObjects.IsEmpty())
{ {
LaraCollision.Setup.EnableObjectPush = true; LaraCollision.Setup.EnableObjectPush = true;
if (!collObjects.ItemPtrs.empty()) if (!collObjects.Items.empty())
{ {
const auto& object = Objects[collObjects.ItemPtrs.front()->ObjectNumber]; const auto& object = Objects[collObjects.Items.front()->ObjectNumber];
if (!object.intelligent && if (!object.intelligent &&
!collObjects.ItemPtrs.front()->IsLara()) !collObjects.Items.front()->IsLara())
{ {
ObjectCollision(collObjects.ItemPtrs.front()->Index, item, &LaraCollision); ObjectCollision(collObjects.Items.front()->Index, item, &LaraCollision);
} }
} }
else if (!collObjects.StaticPtrs.empty()) else if (!collObjects.Statics.empty())
{ {
ItemPushStatic(item, *collObjects.StaticPtrs.front(), &LaraCollision); ItemPushStatic(item, *collObjects.Statics.front(), &LaraCollision);
} }
item->Animation.Velocity.z = -int(item->Animation.Velocity.z / 1.5f); item->Animation.Velocity.z = -int(item->Animation.Velocity.z / 1.5f);

View file

@ -187,8 +187,8 @@ namespace TEN::Entities::Generic
ForcedFixedCamera.x = trapDoorItem->Pose.Position.x - phd_sin(trapDoorItem->Pose.Orientation.y) * 2048; ForcedFixedCamera.x = trapDoorItem->Pose.Position.x - phd_sin(trapDoorItem->Pose.Orientation.y) * 2048;
ForcedFixedCamera.y = trapDoorItem->Pose.Position.y - 2048; ForcedFixedCamera.y = trapDoorItem->Pose.Position.y - 2048;
if (ForcedFixedCamera.y < g_Level.Rooms[trapDoorItem->RoomNumber].maxceiling) if (ForcedFixedCamera.y < g_Level.Rooms[trapDoorItem->RoomNumber].TopHeight)
ForcedFixedCamera.y = g_Level.Rooms[trapDoorItem->RoomNumber].maxceiling; ForcedFixedCamera.y = g_Level.Rooms[trapDoorItem->RoomNumber].TopHeight;
ForcedFixedCamera.z = trapDoorItem->Pose.Position.z - phd_cos(trapDoorItem->Pose.Orientation.y) * 2048; ForcedFixedCamera.z = trapDoorItem->Pose.Position.z - phd_cos(trapDoorItem->Pose.Orientation.y) * 2048;
ForcedFixedCamera.RoomNumber = trapDoorItem->RoomNumber; ForcedFixedCamera.RoomNumber = trapDoorItem->RoomNumber;

View file

@ -236,13 +236,31 @@ void InitializeAnimating(short itemNumber)
void AnimatingControl(short itemNumber) void AnimatingControl(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto& item = g_Level.Items[itemNumber];
if (!TriggerActive(item)) if (TriggerActive(&item))
return; {
item.Status = ITEM_ACTIVE;
AnimateItem(&item);
item->Status = ITEM_ACTIVE; if (item.TriggerFlags == 666) //OCB used for the helicopter animating in the Train level.
AnimateItem(item); {
auto pos = GetJointPosition(item, 0);
SoundEffect(SFX_TR4_HELICOPTER_LOOP, (Pose*)&pos);
if (item.Animation.FrameNumber == GetAnimData(item).frameEnd)
{
item.Flags &= 0xC1;
RemoveActiveItem(itemNumber);
item.Status = ITEM_NOT_ACTIVE;
}
}
}
else if (item.TriggerFlags == 2) //Make the animating dissapear when anti-triggered.
{
RemoveActiveItem(itemNumber);
item.Status |= ITEM_INVISIBLE;
}
// TODO: ID_SHOOT_SWITCH2 is probably the bell in Trajan Markets, use Lua for that. // TODO: ID_SHOOT_SWITCH2 is probably the bell in Trajan Markets, use Lua for that.
/*if (item->frameNumber >= g_Level.Anims[item->animNumber].frameEnd) /*if (item->frameNumber >= g_Level.Anims[item->animNumber].frameEnd)

View file

@ -9,188 +9,225 @@
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h" #include "Specific/level.h"
// NOTES:
// ItemFlags[0]: Delay between darts in frame time.
// ItemFlags[1]: Timer in frame time.
namespace TEN::Entities::Traps namespace TEN::Entities::Traps
{ {
constexpr auto DART_DEFAULT_DAMAGE = 25; constexpr auto DART_DEFAULT_HARM_DAMAGE = 25;
constexpr auto DART_DEFAULT_VELOCITY = BLOCK(0.25f);
constexpr auto DART_DEFAULT_DELAY = 32;
constexpr auto DART_DEFAULT_HOMING_DELAY = 24;
void DartControl(short itemNumber) void InitializeDartEmitter(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto& item = g_Level.Items[itemNumber];
if (item->TouchBits.TestAny()) auto& delay = item.ItemFlags[0];
if (item.ObjectNumber == ID_HOMING_DART_EMITTER)
{ {
if (item->TriggerFlags < 0) if (delay == 0)
delay = DART_DEFAULT_HOMING_DELAY;
}
else
{
if (delay == 0)
delay = DART_DEFAULT_DELAY;
}
}
void ControlDart(short itemNumber)
{
auto& item = g_Level.Items[itemNumber];
if (item.TouchBits.TestAny())
{
if (item.TriggerFlags < 0)
Lara.Status.Poison += 1; Lara.Status.Poison += 1;
DoDamage(LaraItem, item->TriggerFlags ? abs(item->TriggerFlags) : DART_DEFAULT_DAMAGE); DoDamage(LaraItem, item.TriggerFlags ? abs(item.TriggerFlags) : DART_DEFAULT_HARM_DAMAGE);
DoBloodSplat(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, (GetRandomControl() & 3) + 4, LaraItem->Pose.Orientation.y, LaraItem->RoomNumber); DoBloodSplat(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, (GetRandomControl() & 3) + 4, LaraItem->Pose.Orientation.y, LaraItem->RoomNumber);
KillItem(itemNumber); KillItem(itemNumber);
} }
else else
{ {
int oldX = item->Pose.Position.x; auto prevPos = item.Pose.Position;
int oldZ = item->Pose.Position.z;
int velocity = item->Animation.Velocity.z * phd_cos(item->Pose.Orientation.x); float vel = item.Animation.Velocity.z * phd_cos(item.Pose.Orientation.x);
item->Pose.Position.x += velocity * phd_sin(item->Pose.Orientation.y); item.Pose.Position.x += vel * phd_sin(item.Pose.Orientation.y);
item->Pose.Position.y -= item->Animation.Velocity.z * phd_sin(item->Pose.Orientation.x); item.Pose.Position.y -= item.Animation.Velocity.z * phd_sin(item.Pose.Orientation.x);
item->Pose.Position.z += velocity * phd_cos(item->Pose.Orientation.y); item.Pose.Position.z += vel * phd_cos(item.Pose.Orientation.y);
short roomNumber = item->RoomNumber; short roomNumber = item.RoomNumber;
FloorInfo* floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); auto* floor = GetFloor(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, &roomNumber);
if (item->RoomNumber != roomNumber) if (item.RoomNumber != roomNumber)
ItemNewRoom(itemNumber, roomNumber); ItemNewRoom(itemNumber, roomNumber);
int height = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z); int height = GetFloorHeight(floor, item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z);
item->Floor = height; item.Floor = height;
if (item->Pose.Position.y >= height) if (item.Pose.Position.y >= height)
{ {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
TriggerDartSmoke(oldX, item->Pose.Position.y, oldZ, 0, 0, true); SpawnDartSmoke(Vector3(prevPos.x, item.Pose.Position.y, prevPos.z), Vector3::Zero, true);
KillItem(itemNumber); KillItem(itemNumber);
} }
} }
} }
void DartEmitterControl(short itemNumber) void ControlDartEmitter(short itemNumber)
{ {
ItemInfo* item = &g_Level.Items[itemNumber]; auto& item = g_Level.Items[itemNumber];
if (item->Active) if (TriggerActive(&item))
{ {
if (item->Timer > 0) if (item.Active)
{ {
item->Timer--; auto& delay = item.ItemFlags[0];
auto& timer = item.ItemFlags[1];
if (timer > 0)
{
timer--;
return;
}
else
{
timer = delay;
}
}
int dartItemNumber = CreateItem();
if (dartItemNumber == NO_VALUE)
return; return;
}
else
{
item->Timer = 24;
}
}
int dartItemNumber = CreateItem(); auto& dartItem = g_Level.Items[dartItemNumber];
if (dartItemNumber == NO_VALUE) dartItem.ObjectNumber = ID_DARTS;
return; dartItem.Pose.Position = item.Pose.Position + Vector3i(0, -CLICK(0.9f), 0);
dartItem.Pose.Orientation = item.Pose.Orientation + EulerAngles(0, ANGLE(180.0f), 0);
dartItem.RoomNumber = item.RoomNumber;
ItemInfo* dartItem = &g_Level.Items[dartItemNumber]; InitializeItem(dartItemNumber);
dartItem->ObjectNumber = ID_DARTS; dartItem.Animation.Velocity.z = DART_DEFAULT_VELOCITY;
dartItem->RoomNumber = item->RoomNumber; dartItem.TriggerFlags = item.TriggerFlags;
dartItem.Model.Color = item.Model.Color;
dartItem->Pose.Position.x = item->Pose.Position.x; for (int i = 0; i < 4; i++)
dartItem->Pose.Position.y = item->Pose.Position.y - CLICK(0.9); SpawnDartSmoke(dartItem.Pose.Position.ToVector3(), Vector3::Zero, false);
dartItem->Pose.Position.z = item->Pose.Position.z;
InitializeItem(dartItemNumber); AddActiveItem(dartItemNumber);
dartItem.Status = ITEM_ACTIVE;
dartItem->Pose.Orientation.x = item->Pose.Orientation.x - ANGLE(180.0f); SoundEffect(SFX_TR4_DART_SPIT, &dartItem.Pose);
dartItem->Pose.Orientation.y = item->Pose.Orientation.y;
dartItem->Pose.Orientation.z = item->Pose.Orientation.z;
dartItem->Animation.Velocity.z = BLOCK(0.25f);
dartItem->TriggerFlags = item->TriggerFlags;
dartItem->Model.Color = item->Model.Color;
for (int i = 0; i < 4; i++)
TriggerDartSmoke(dartItem->Pose.Position.x, dartItem->Pose.Position.y, dartItem->Pose.Position.z, 0, 0, false);
AddActiveItem(dartItemNumber);
dartItem->Status = ITEM_ACTIVE;
SoundEffect(SFX_TR4_DART_SPIT, &dartItem->Pose);
}
void TriggerDartSmoke(int x, int y, int z, int xv, int zv, bool hit)
{
int dx = LaraItem->Pose.Position.x - x;
int dz = LaraItem->Pose.Position.z - z;
if (dx < -16384 || dx > 16384 || dz < -16384 || dz > 16384)
return;
auto* spark = GetFreeParticle();
spark->on = true;
spark->sR = 16;
spark->sG = 8;
spark->sB = 4;
spark->dR = 64;
spark->dG = 48;
spark->dB = 32;
spark->colFadeSpeed = 8;
spark->fadeToBlack = 4;
spark->blendMode = BlendMode::Additive;
spark->life = spark->sLife = (GetRandomControl() & 3) + 32;
spark->x = x + ((GetRandomControl() & 31) - 16);
spark->y = y + ((GetRandomControl() & 31) - 16);
spark->z = z + ((GetRandomControl() & 31) - 16);
if (hit)
{
spark->xVel = -xv + ((GetRandomControl() & 255) - 128);
spark->yVel = -(GetRandomControl() & 3) - 4;
spark->zVel = -zv + ((GetRandomControl() & 255) - 128);
spark->friction = 3;
} }
else else
{ {
if (xv) item.Status = ITEM_NOT_ACTIVE;
spark->xVel = -xv; RemoveActiveItem(itemNumber, false);
else item.Active = false;
spark->xVel = ((GetRandomControl() & 255) - 128); }
}
spark->yVel = -(GetRandomControl() & 3) - 4; void SpawnDartSmoke(const Vector3& pos, const Vector3& vel, bool isHit)
if (zv) {
spark->zVel = -zv; auto& part = *GetFreeParticle();
else
spark->zVel = ((GetRandomControl() & 255) - 128);
spark->friction = 3; part.on = true;
part.sR = 16;
part.sG = 8;
part.sB = 4;
part.dR = 64;
part.dG = 48;
part.dB = 32;
part.colFadeSpeed = 8;
part.fadeToBlack = 4;
part.blendMode = BlendMode::Additive;
part.life = part.sLife = (GetRandomControl() & 3) + 32;
part.x = pos.x + ((GetRandomControl() & 31) - 16);
part.y = pos.y + ((GetRandomControl() & 31) - 16);
part.z = pos.z + ((GetRandomControl() & 31) - 16);
if (isHit)
{
part.xVel = -vel.x + ((GetRandomControl() & 255) - 128);
part.yVel = -(GetRandomControl() & 3) - 4;
part.zVel = -vel.z + ((GetRandomControl() & 255) - 128);
part.friction = 3;
}
else
{
if (vel.x != 0.0f)
{
part.xVel = -vel.x;
}
else
{
part.xVel = ((GetRandomControl() & 255) - 128);
}
part.yVel = -(GetRandomControl() & 3) - 4;
if (vel.z != 0.0f)
{
part.zVel = -vel.z;
}
else
{
part.zVel = ((GetRandomControl() & 255) - 128);
}
part.friction = 3;
} }
spark->friction = 3; part.friction = 3;
if (GetRandomControl() & 1) if (GetRandomControl() & 1)
{ {
spark->flags = SP_EXPDEF | SP_ROTATE | SP_DEF | SP_SCALE; part.flags = SP_EXPDEF | SP_ROTATE | SP_DEF | SP_SCALE;
spark->rotAng = GetRandomControl() & 0xFFF; part.rotAng = GetRandomControl() & 0xFFF;
if (GetRandomControl() & 1) if (GetRandomControl() & 1)
spark->rotAdd = -16 - (GetRandomControl() & 0xF); {
part.rotAdd = -16 - (GetRandomControl() & 0xF);
}
else else
spark->rotAdd = (GetRandomControl() & 0xF) + 16; {
part.rotAdd = (GetRandomControl() & 0xF) + 16;
}
} }
else else
{ {
spark->flags = SP_EXPDEF | SP_DEF | SP_SCALE; part.flags = SP_EXPDEF | SP_DEF | SP_SCALE;
} }
spark->scalar = 1; part.scalar = 1;
int size = (GetRandomControl() & 63) + 72; int size = (GetRandomControl() & 63) + 72;
if (hit) if (isHit)
{ {
size >>= 1; size /= 2;
spark->size = spark->sSize = size >> 2; part.size =
spark->gravity = spark->maxYvel = 0; part.sSize = size *= 4;
part.gravity = part.maxYvel = 0;
} }
else else
{ {
spark->size = spark->sSize = size >> 4; part.size = part.sSize = size >> 4;
spark->gravity = -(GetRandomControl() & 3) - 4; part.gravity = -(GetRandomControl() & 3) - 4;
spark->maxYvel = -(GetRandomControl() & 3) - 4; part.maxYvel = -(GetRandomControl() & 3) - 4;
} }
spark->dSize = size; part.dSize = size;
} }
} }

View file

@ -2,7 +2,9 @@
namespace TEN::Entities::Traps namespace TEN::Entities::Traps
{ {
void DartControl(short itemNumber); void InitializeDartEmitter(short itemNumber);
void DartEmitterControl(short itemNumber); void ControlDart(short itemNumber);
void TriggerDartSmoke(int x, int y, int z, int xv, int zv, bool hit); void ControlDartEmitter(short itemNumber);
void SpawnDartSmoke(const Vector3& pos, const Vector3& vel, bool isHit);
} }

View file

@ -347,14 +347,15 @@ void StartTraps(ObjectInfo* object)
if (object->loaded) if (object->loaded)
{ {
object->collision = ObjectCollision; object->collision = ObjectCollision;
object->control = DartControl; object->control = ControlDart;
object->shadowType = ShadowMode::All; object->shadowType = ShadowMode::All;
} }
object = &Objects[ID_DART_EMITTER]; object = &Objects[ID_DART_EMITTER];
if (object->loaded) if (object->loaded)
{ {
object->control = DartEmitterControl; object->Initialize = InitializeDartEmitter;
object->control = ControlDartEmitter;
object->drawRoutine = nullptr; object->drawRoutine = nullptr;
object->usingDrawAnimatingItem = false; object->usingDrawAnimatingItem = false;
} }
@ -362,7 +363,8 @@ void StartTraps(ObjectInfo* object)
object = &Objects[ID_HOMING_DART_EMITTER]; object = &Objects[ID_HOMING_DART_EMITTER];
if (object->loaded) if (object->loaded)
{ {
object->control = DartEmitterControl; object->Initialize = InitializeDartEmitter;
object->control = ControlDartEmitter;
object->drawRoutine = nullptr; object->drawRoutine = nullptr;
object->usingDrawAnimatingItem = false; object->usingDrawAnimatingItem = false;
} }

View file

@ -0,0 +1,182 @@
#include "framework.h"
#include "Objects/TR1/Entity/Centaur.h"
#include "Game/animation.h"
#include "Game/control/box.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h"
#include "Game/effects/effects.h"
#include "Game/effects/tomb4fx.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_one_gun.h"
#include "Game/misc.h"
#include "Game/missile.h"
#include "Game/people.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Sound/sound.h"
#include "Specific/level.h"
using namespace TEN::Math;
namespace TEN::Entities::Creatures::TR1
{
constexpr auto CENTAUR_REAR_DAMAGE = 200;
constexpr auto CENTAUR_REAR_RANGE = SQUARE(BLOCK(3 / 2.0f));
constexpr auto CENTAUR_REAR_CHANCE = 1 / 340.0f;
constexpr auto CENTAUR_BOMB_VELOCITY = CLICK(1);
constexpr auto CENTAUR_TURN_RATE_MAX = ANGLE(4.0f);
const auto CentaurRocketBite = CreatureBiteInfo(Vector3(11, 415, 41), 13);
const auto CentaurRearBite = CreatureBiteInfo(Vector3(50, 30, 0), 5);
const auto CentaurAttackJoints = std::vector<unsigned int>{ 0, 3, 4, 7, 8, 16, 17 };
enum CentaurState
{
// No state 0.
CENTAUR_STATE_IDLE = 1,
CENTAUR_PROJECTILE_ATTACK = 2,
CENTAUR_STATE_RUN_FORWARD = 3,
CENTAUR_STATE_AIM = 4,
CENTAUR_STATE_DEATH = 5,
CENTAUR_STATE_WARNING = 6
};
// TODO
enum CentaurAnim
{
CENTAUR_ANIM_DEATH = 8,
};
void ControlCentaur(short itemNumber)
{
if (!CreatureActive(itemNumber))
return;
auto& item = g_Level.Items[itemNumber];
auto& creature = *GetCreatureInfo(&item);
short headingAngle = 0;
auto headOrient = EulerAngles::Identity;
auto torsoOrient = EulerAngles::Identity;
if (item.HitPoints <= 0)
{
if (item.Animation.ActiveState != CENTAUR_STATE_DEATH)
SetAnimation(item, CENTAUR_ANIM_DEATH);
}
else
{
AI_INFO ai;
CreatureAIInfo(&item, &ai);
if (ai.ahead)
{
headOrient.x = ai.xAngle;
headOrient.y = ai.angle;
torsoOrient.x = ai.xAngle / 2;
torsoOrient.y = ai.angle / 2;
}
GetCreatureMood(&item, &ai, true);
CreatureMood(&item, &ai, true);
headingAngle = CreatureTurn(&item, CENTAUR_TURN_RATE_MAX);
switch (item.Animation.ActiveState)
{
case CENTAUR_STATE_IDLE:
if (item.Animation.RequiredState != NO_VALUE)
{
item.Animation.TargetState = item.Animation.RequiredState;
}
else if (ai.bite && ai.distance < CENTAUR_REAR_RANGE)
{
item.Animation.TargetState = CENTAUR_STATE_RUN_FORWARD;
}
else if (Targetable(&item, &ai))
{
item.Animation.TargetState = CENTAUR_STATE_AIM;
}
else
{
item.Animation.TargetState = CENTAUR_STATE_RUN_FORWARD;
}
break;
case CENTAUR_STATE_RUN_FORWARD:
torsoOrient = EulerAngles::Identity;
if (ai.bite && ai.distance < CENTAUR_REAR_RANGE)
{
item.Animation.TargetState = CENTAUR_STATE_IDLE;
item.Animation.RequiredState = CENTAUR_STATE_WARNING;
}
else if (Targetable(&item, &ai))
{
item.Animation.TargetState = CENTAUR_STATE_IDLE;
item.Animation.RequiredState = CENTAUR_STATE_AIM;
}
else if (Random::TestProbability(CENTAUR_REAR_CHANCE))
{
item.Animation.TargetState = CENTAUR_STATE_IDLE;
item.Animation.RequiredState = CENTAUR_STATE_WARNING;
}
break;
case CENTAUR_STATE_AIM:
if (item.Animation.RequiredState != NO_VALUE)
{
item.Animation.TargetState = item.Animation.RequiredState;
}
else if (Targetable(&item, &ai))
{
item.Animation.TargetState = CENTAUR_PROJECTILE_ATTACK;
}
else
{
item.Animation.TargetState = CENTAUR_STATE_IDLE;
}
break;
case CENTAUR_PROJECTILE_ATTACK:
if (item.Animation.RequiredState == NO_VALUE)
{
item.Animation.RequiredState = CENTAUR_STATE_AIM;
CreatureEffect2(&item, CentaurRocketBite, CENTAUR_BOMB_VELOCITY, headOrient.y, BombGun);
}
break;
case CENTAUR_STATE_WARNING:
if (item.Animation.RequiredState == NO_VALUE &&
item.TouchBits.Test(CentaurAttackJoints))
{
DoDamage(creature.Enemy, CENTAUR_REAR_DAMAGE);
CreatureEffect(&item, CentaurRearBite, DoBloodSplat);
item.Animation.RequiredState = CENTAUR_STATE_IDLE;
}
break;
}
}
CreatureJoint(&item, 0, headOrient.y);
CreatureJoint(&item, 1, -headOrient.x);
CreatureJoint(&item, 2, torsoOrient.y);
CreatureJoint(&item, 3, -torsoOrient.x);
CreatureAnimation(itemNumber, headingAngle, 0);
if (item.Status == ITEM_DEACTIVATED)
{
SoundEffect(SFX_TR1_ATLANTEAN_DEATH, &item.Pose);
ExplodingDeath(itemNumber, BODY_DO_EXPLOSION);
KillItem(itemNumber);
item.Status = ITEM_DEACTIVATED;
}
}
}

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