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)
- Tomo (general coding, bug fixing)
- Troye (general coding, refactoring)
- WolfCheese (general coding)
- Nickelony (general coding)
- JesseG, aka WolfCheese (general coding)
## Testers
- Adngel

View file

@ -9,20 +9,53 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
## Version 1.5 - xxxx-xx-xx
### 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 camera rotating with Lara's hips when climbing out of water.
* Fixed AI for skidoo driver and worker with shotgun TR2 enemies.
* Fixed Ember emitter crashing when ocb is between -1 and -10
* Fixed Electric cleaner and Squishy block not detecting collision with certain block heights.
* Fixed Squishy blocks crashing the level.
* Fixed the path finding zones of Larson and Pierre.
* Fixed the torch flame delay in disappearing when Lara threw or dropped the torch object.
* Fixed camera rotating with the player's hips when climbing out of water.
* Fixed AI for TR2 skidoo driver and worker with shotgun.
* Fixed ember emitter crashing when ocb is between -1 and -10.
* Fixed electric cleaner and squishy block not detecting collision with certain block heights.
* Fixed squishy blocks crashing the level.
* Fixed Larson and Pierre pathfinding.
* Fixed torch flame delay when the player throws or drops a torch.
* Fixed dart emitters failing with antitrigger.
* Fixed homing dart emitter spawning darts continously when player is on its trigger.
* Fixed four blade trap floor and ceiling collision.
* Fixed Joby spikes collision and deformation.
* Fixed sentry gun joint rotation.
* Fixed teeth spikes not triggering the player impale animation.
* Fixed TR4 mine crash with OCB 1 when triggered.
* Fixed cases where Atlantean mutant's bombs cause the game to crash.
* Fixed young hair drawing.
### 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
* 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

View file

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

View file

@ -131,6 +131,10 @@
<td class="summary">Clear an action key.</td>
</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="summary">Get the display position of the cursor in percent.</td>
</tr>
@ -256,6 +260,21 @@
</dd>
<dt>
<a name = "KeyClearAll"></a>
<strong>KeyClearAll()</strong>
</dt>
<dd>
Clear all action keys.
</dd>
<dt>
<a name = "GetMouseDisplayPosition"></a>

View file

@ -173,7 +173,8 @@ LARA_PETROL_MESH
LARA_DIRT_MESH
LARA_CROWBAR_ANIM
LARA_TORCH_ANIM
HAIR
SINGLE_BRAID_HAIR
DUAL_PIGTAIL_HAIR
SNOWMOBILE_LARA_ANIMS
SNOWMOBILE
QUAD_LARA_ANIMS
@ -359,7 +360,7 @@ WASP_MUTANT
SKATEBOARD
SKATEBOARD_KID
WINSTON
ARMY_WINSTON
SEAL_MUTANT
SPRINGBOARD
ROLLING_SPINDLE
DISK_SHOOTER
@ -945,6 +946,7 @@ MESHSWAP_ROMAN_GOD1
MESHSWAP_ROMAN_GOD2
MESHSWAP_MONKEY_MEDIPACK
MESHSWAP_MONKEY_KEY
MESHSWAP_WINSTON_ARMY_OUTFIT
ANIMATING1
ANIMATING2
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.crouchRoll = true
anims.crawlspaceSwandive = true
anims.monkeyAutoJump = false
anims.overhangClimb = false
anims.slideExtended = false
anims.sprintJump = false

View file

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

View file

@ -51,7 +51,8 @@ local strings =
ammo_used = { "Ammo Used" },
antialiasing = { "Antialiasing" },
apply = { "Apply" },
automatic_targeting = { "Automatic Targeting" },
auto_monkey_swing_jump = { "Auto Monkey Jump" },
auto_targeting = { "Auto Targeting" },
back = { "Back" },
cancel = { "Cancel" },
caustics = { "Underwater Caustics" },
@ -79,8 +80,11 @@ local strings =
low = { "Low" },
medium = { "Medium" },
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_smoothing = { "Mouse Smoothing" },
music_volume = { "Music Volume" },
new_game = { "New Game" },
none = { "None" },

View file

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

View file

@ -198,7 +198,7 @@ namespace TEN::Hud
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.
if (_displayPickups.size() < DISPLAY_PICKUP_COUNT_MAX)
@ -224,6 +224,6 @@ namespace TEN::Hud
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:
// Constants
static constexpr auto DISPLAY_PICKUP_COUNT_MAX = 64;
static constexpr auto DISPLAY_PICKUP_COUNT_ARG_DEFAULT = 1;
// Members
std::vector<DisplayPickup> _displayPickups = {};
public:
// 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 Vector3& pos, unsigned int count = DISPLAY_PICKUP_COUNT_ARG_DEFAULT);
@ -63,6 +66,7 @@ namespace TEN::Hud
private:
// Helpers
std::vector<Vector2> GetStackPositions() const;
DisplayPickup& GetNewDisplayPickup();
void ClearInactiveDisplayPickups();

View file

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

View file

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

View file

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

View file

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

View file

@ -15,11 +15,13 @@ namespace TEN::Hud
public:
// Constants
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 SEGMENT_COUNT = 4;
// Members
bool IsActive = false;
bool IsPrimary = false;
@ -41,15 +43,18 @@ namespace TEN::Hud
std::array<SegmentData, SEGMENT_COUNT> PrevSegments = {};
// Getters
float GetScale(float cameraDist) const;
float GetRadius() const;
Vector2 GetPositionOffset(short orientOffset) const;
// Setters
void SetPrimary();
void SetPeripheral();
// Utilities
void Update(const Vector3& targetPos, bool isActive, bool doPulse);
void Draw() const;
@ -67,19 +72,23 @@ namespace TEN::Hud
{
private:
// Members
std::unordered_map<int, CrosshairData> _crosshairs = {}; // Key = item number.
public:
// Utilities
void Update(const ItemInfo& playerItem);
void Draw() const;
void Clear();
private:
// Update helpers
void Update(const std::vector<int>& itemNumbers);
// Object helpers
CrosshairData& GetNewCrosshair(int itemNumber);
void AddCrosshair(int itemNumber, const Vector3& targetPos);
void ClearInactiveCrosshairs();

View file

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

View file

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

View file

@ -260,7 +260,7 @@ void DrawFlare(ItemInfo& laraItem)
SoundEffect(
SFX_TR4_FLARE_IGNITE_DRY,
&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);

View file

@ -22,7 +22,6 @@
#include "Game/savegame.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Renderer/Renderer.h"
#include "Scripting/Include/ScriptInterfaceLevel.h"
#include "Sound/sound.h"
#include "Specific/Input/Input.h"
@ -46,7 +45,6 @@ using namespace TEN::Entities::Player;
using namespace TEN::Gui;
using namespace TEN::Input;
using namespace TEN::Math;
using namespace TEN::Renderer;
// -----------------------------
// 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>
{
@ -293,15 +291,15 @@ static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo&
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);
// 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++)
{
if (SCROLL_WEAPON_TYPES[i] == currentWeaponType)
@ -311,13 +309,13 @@ static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo&
}
}
// Invalid current weapon type; return nullopt.
if (!currentIndex.has_value())
return std::nullopt;
// Invalid current weapon type; return None type.
if (currentIndex == NO_VALUE)
return LaraWeaponType::None;
// Get next valid weapon type in sequence.
unsigned int nextIndex = getNextIndex(*currentIndex);
while (nextIndex != *currentIndex)
int nextIndex = getNextIndex(currentIndex);
while (nextIndex != currentIndex)
{
auto nextWeaponType = SCROLL_WEAPON_TYPES[nextIndex];
if (player.Weapons[(int)nextWeaponType].Present)
@ -326,8 +324,8 @@ static std::optional<LaraWeaponType> GetPlayerScrolledWeaponType(const ItemInfo&
nextIndex = getNextIndex(nextIndex);
}
// No valid weapon type; return nullopt.
return std::nullopt;
// No valid weapon type; return None type.
return LaraWeaponType::None;
}
void HandlePlayerQuickActions(ItemInfo& item)
@ -347,11 +345,9 @@ void HandlePlayerQuickActions(ItemInfo& item)
// Handle weapon scroll request.
if (IsClicked(In::PreviousWeapon) || IsClicked(In::NextWeapon))
{
bool getPrev = IsClicked(In::PreviousWeapon);
auto weaponType = GetPlayerScrolledWeaponType(item, player.Control.Weapon.GunType, getPrev);
if (weaponType.has_value())
player.Control.Weapon.RequestGunType = *weaponType;
auto weaponType = GetPlayerScrolledWeaponType(item, player.Control.Weapon.GunType, IsClicked(In::PreviousWeapon));
if (weaponType != LaraWeaponType::None)
player.Control.Weapon.RequestGunType = weaponType;
}
// Handle weapon requests.
@ -994,7 +990,7 @@ void HandlePlayerAirBubbles(ItemInfo* item)
{
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);
@ -1471,7 +1467,7 @@ void UpdateLaraSubsuitAngles(ItemInfo* item)
auto mul1 = (float)abs(lara->Control.Subsuit.Velocity[0]) / BLOCK(8);
auto mul2 = (float)abs(lara->Control.Subsuit.Velocity[1]) / BLOCK(8);
auto vol = ((mul1 + mul2) * 5.0f) + 0.5f;
SoundEffect(SFX_TR5_VEHICLE_DIVESUIT_ENGINE, &item->Pose, SoundEnvironment::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);
//short deltaAngle = abs((short)(direction - item->Pose.Orientation.y));
//g_Renderer.PrintDebugMessage("%d", slideVelocity);
//PrintDebugMessage("%d", slideVelocity);
//lara->ExtraVelocity.x += slideVelocity;
//lara->ExtraVelocity.y += slideVelocity * phd_sin(steepness);

View file

@ -64,7 +64,7 @@ void lara_as_jump_forward(ItemInfo* item, CollisionInfo* coll)
{
DoLaraFallDamage(item);
if (item->HitPoints <= 0) USE_FEATURE_IF_CPP20([[unlikely]])
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else if (IsHeld(In::Forward) && !IsHeld(In::Walk) &&
player.Control.WaterStatus != WaterStatus::Wade)
@ -146,7 +146,7 @@ void lara_as_freefall(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -202,7 +202,7 @@ void lara_as_reach(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -414,7 +414,7 @@ void lara_as_jump_back(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -468,7 +468,7 @@ void lara_as_jump_right(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -523,7 +523,7 @@ void lara_as_jump_left(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -576,7 +576,7 @@ void lara_as_jump_up(ItemInfo* item, CollisionInfo* coll)
{
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -673,7 +673,7 @@ void lara_as_fall_back(ItemInfo* item, CollisionInfo* coll)
if (item->HitPoints <= 0)
item->Animation.TargetState = LS_DEATH;
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -749,7 +749,7 @@ void lara_as_swan_dive(ItemInfo* item, CollisionInfo* coll)
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.
}
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);
@ -823,7 +823,7 @@ void lara_as_freefall_dive(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = LS_DEATH;
Rumble(0.5f, 0.2f);
}
else USE_FEATURE_IF_CPP20([[likely]])
else
item->Animation.TargetState = LS_IDLE;
SetLaraLand(item, coll);

View file

@ -428,7 +428,7 @@ void FireShotgun(ItemInfo& laraItem)
player.RightArm.GunFlash = Weapons[(int)LaraWeaponType::Shotgun].FlashTime;
SoundEffect(SFX_TR4_EXPLOSION1, &laraItem.Pose, TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::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);
Rumble(0.5f, 0.2f);
@ -1563,7 +1563,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
break;
// Run through statics.
for (auto* staticPtr : collObjects.StaticPtrs)
for (auto* staticPtr : collObjects.Statics)
{
hasHit = hasHitNotByEmitter = doShatter = true;
doExplosion = isExplosive;
@ -1584,7 +1584,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
}
// Run through items.
for (auto* itemPtr : collObjects.ItemPtrs)
for (auto* itemPtr : collObjects.Items)
{
// Object was already affected by collision, skip it.
if (std::find(affectedObjects.begin(), affectedObjects.end(), itemPtr->Index) != affectedObjects.end())

View file

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

View file

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

View file

@ -17,7 +17,7 @@
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_monkey.h"
#include "Math/Math.h"
#include "Renderer/Renderer.h"
#include "Specific/configuration.h"
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/trutils.h"
@ -27,7 +27,6 @@ using namespace TEN::Collision::Point;
using namespace TEN::Entities::Player;
using namespace TEN::Input;
using namespace TEN::Math;
using namespace TEN::Renderer;
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))
return false;
//g_Renderer.AddDebugSphere(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 + xl, left, item->pos.Position.z + zl), 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.
// 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 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->Pose.Position = coll->Setup.PrevPosition;
}
else if (waterDepth <= (LARA_HEIGHT - (LARA_HEADROOM / 2)))
else if (pointColl.GetWaterBottomHeight() <= (LARA_HEIGHT - (LARA_HEADROOM / 2)))
{
SetAnimation(item, LA_UNDERWATER_TO_STAND);
ResetPlayerLean(item);
@ -1427,7 +1424,7 @@ std::optional<VaultTestResult> TestLaraLadderMount(ItemInfo* item, CollisionInfo
return std::nullopt;
}
std::optional<VaultTestResult> TestLaraMonkeyAutoJump(ItemInfo* item, CollisionInfo* coll)
std::optional<VaultTestResult> TestLaraAutoMonkeySwingJump(ItemInfo* item, CollisionInfo* coll)
{
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
// Auto jump to monkey swing.
vaultResult = TestLaraMonkeyAutoJump(item, coll);
if (vaultResult.has_value() && g_GameFlow->HasMonkeyAutoJump())
vaultResult = TestLaraAutoMonkeySwingJump(item, coll);
if (vaultResult.has_value() && g_Configuration.EnableAutoMonkeySwingJump)
{
vaultResult->TargetState = LS_AUTO_JUMP;
if (!HasStateDispatch(item, vaultResult->TargetState))
@ -1710,7 +1707,7 @@ CrawlVaultTestResult TestLaraCrawlVault(ItemInfo* item, CollisionInfo* coll)
{
if (IsHeld(In::Crouch) && TestLaraCrawlDownStep(item, coll).Success)
crawlVaultResult.TargetState = LS_CRAWL_STEP_DOWN;
else USE_FEATURE_IF_CPP20([[likely]])
else
crawlVaultResult.TargetState = LS_CRAWL_EXIT_STEP_DOWN;
crawlVaultResult.Success = HasStateDispatch(item, crawlVaultResult.TargetState);
@ -1723,7 +1720,7 @@ CrawlVaultTestResult TestLaraCrawlVault(ItemInfo* item, CollisionInfo* coll)
{
if (IsHeld(In::Walk))
crawlVaultResult.TargetState = LS_CRAWL_EXIT_FLIP;
else USE_FEATURE_IF_CPP20([[likely]])
else
crawlVaultResult.TargetState = LS_CRAWL_EXIT_JUMP;
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 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)
continue;
@ -1803,7 +1800,7 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, fl
auto poleBox = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose);
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))
{

View file

@ -56,7 +56,7 @@ std::optional<VaultTestResult> TestLaraVault3StepsToCrouch(ItemInfo* item, Colli
std::optional<VaultTestResult> TestLaraLedgeAutoJump(ItemInfo* item, CollisionInfo* coll);
std::optional<VaultTestResult> TestLaraLadderAutoJump(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);
bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll);

View file

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

View file

@ -3,6 +3,7 @@
#include "Game/camera.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/Point.h"
#include "Game/control/box.h"
#include "Game/control/flipeffect.h"
#include "Game/items.h"
@ -15,6 +16,7 @@
#include "Sound/sound.h"
#include "Specific/level.h"
using namespace TEN::Collision::Point;
using namespace TEN::Entities::Generic;
using namespace TEN::Math;
using TEN::Renderer::g_Renderer;
@ -112,49 +114,95 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased)
break;
case AnimCommandType::SoundEffect:
if (isFrameBased && item.Animation.FrameNumber == commandDataPtr[0])
{
if (!Objects[item.ObjectNumber].waterCreature)
int frameNumber = commandDataPtr[0];
if (isFrameBased && item.Animation.FrameNumber == frameNumber)
{
bool playInWater = (commandDataPtr[1] & 0x8000) != 0;
bool playOnLand = (commandDataPtr[1] & 0x4000) != 0;
bool playAlways = (playInWater && playOnLand) || (!playInWater && !playOnLand);
// Get sound ID and sound environment flag from packed data.
int soundID = commandDataPtr[1] & 0xFFF; // Exclude last 4 bits for sound ID.
int soundEnvFlag = commandDataPtr[1] & 0xF000; // Keep only last 4 bits for sound environment flag.
if (item.IsLara())
{
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
{
// FAILSAFE.
if (item.RoomNumber == NO_VALUE)
{
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
SoundEffect(soundID, &item.Pose, SoundEnvironment::Always);
commandDataPtr += 2;
break;
}
else if (TestEnvironment(ENV_FLAG_WATER, &item))
// Get required sound environment from flag.
auto requiredSoundEnv = SoundEnvironment::Always;
switch (soundEnvFlag)
{
if (playAlways || (playInWater && TestEnvironment(ENV_FLAG_WATER, Camera.pos.RoomNumber)))
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
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;
}
else if (playAlways || (playOnLand && !TestEnvironment(ENV_FLAG_WATER, Camera.pos.RoomNumber) && !TestEnvironment(ENV_FLAG_SWAMP, Camera.pos.RoomNumber)))
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)
{
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
}
}
}
else
case SoundEnvironment::Always:
soundEnv = SoundEnvironment::Always;
break;
case SoundEnvironment::Land:
if (!isWater && !isSwamp)
soundEnv = SoundEnvironment::Land;
break;
case SoundEnvironment::ShallowWater:
if (isWater)
{
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, TestEnvironment(ENV_FLAG_WATER, &item) ? SoundEnvironment::Water : SoundEnvironment::Land);
// 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;
}
break;
case AnimCommandType::Flipeffect:
@ -508,7 +556,7 @@ const AnimFrame* GetFrame(GAME_OBJECT_ID objectID, int animNumber, int frameNumb
const auto& object = Objects[objectID];
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);

View file

@ -166,7 +166,7 @@ void LookCamera(ItemInfo& item, const CollisionInfo& coll)
bool isInSwamp = TestEnvironment(ENV_FLAG_SWAMP, item.RoomNumber);
auto basePos = Vector3i(
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);
// Define landmarks.
@ -342,7 +342,7 @@ void MoveCamera(GameVector* ideal, int speed)
int y = Camera.pos.y;
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);
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);
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;
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);
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);
Camera.target.RoomNumber = pointColl.GetRoomNumber();
@ -1392,7 +1392,7 @@ bool CheckItemCollideCamera(ItemInfo* item)
static std::vector<int> FillCollideableItemList()
{
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++)
{
@ -1442,7 +1442,7 @@ bool CheckStaticCollideCamera(MESH_INFO* mesh)
std::vector<MESH_INFO*> FillCollideableStaticsList()
{
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)
{
@ -1485,7 +1485,7 @@ void ItemsCollideCamera()
if (TestBoundsCollideCamera(bounds, item->Pose, CAMERA_RADIUS))
ItemPushCamera(&bounds, &item->Pose, RADIUS);
TEN::Renderer::g_Renderer.AddDebugBox(
DrawDebugBox(
bounds.ToBoundingOrientedBox(item->Pose),
Vector4(1.0f, 0.0f, 0.0f, 1.0f), RendererDebugPage::CollisionStats);
}
@ -1508,7 +1508,7 @@ void ItemsCollideCamera()
if (TestBoundsCollideCamera(bounds, mesh->pos, CAMERA_RADIUS))
ItemPushCamera(&bounds, &mesh->pos, RADIUS);
TEN::Renderer::g_Renderer.AddDebugBox(
DrawDebugBox(
bounds.ToBoundingOrientedBox(mesh->pos),
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);
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);
}
_bottomSector = bottomSector;
@ -78,7 +78,7 @@ namespace TEN::Collision::Point
int roomNumber = roomNumberAbove.value_or(topSector->RoomNumber);
auto& room = g_Level.Rooms[roomNumber];
topSector = Room::GetSector(&room, _position.x - room.x, _position.z - room.z);
topSector = Room::GetSector(&room, _position.x - room.Position.x, _position.z - room.Position.z);
roomNumberAbove = topSector->GetNextRoomNumber(_position, false);
}
_topSector = topSector;
@ -177,9 +177,24 @@ namespace TEN::Collision::Point
if (_waterSurfaceHeight.has_value())
return *_waterSurfaceHeight;
// Set water surface height. TODO: Calculate here.
_waterSurfaceHeight = GetWaterSurface(_position.x, _position.y, _position.z, _roomNumber);
auto* room = &g_Level.Rooms[_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;
}
@ -188,20 +203,197 @@ namespace TEN::Collision::Point
if (_waterBottomHeight.has_value())
return *_waterBottomHeight;
// Set water bottom height. TODO: Calculate here.
_waterBottomHeight = GetWaterDepth(_position.x, _position.y, _position.z, _roomNumber);
FloorInfo* sector = nullptr;
auto* room = &g_Level.Rooms[_roomNumber];
short 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);
// 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()
{
if (_waterTopHeight.has_value())
return *_waterTopHeight;
// Set water top height. TODO: Calculate here.
_waterTopHeight = GetWaterHeight(_position.x, _position.y, _position.z, _roomNumber);
FloorInfo* sector = nullptr;
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;
}

View file

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

View file

@ -18,14 +18,12 @@
#include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Renderer/Renderer.h"
#include "Scripting/Include/ScriptInterfaceGame.h"
#include "Sound/sound.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Math;
using namespace TEN::Renderer;
constexpr auto ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD = 6;
@ -125,7 +123,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible,
// Run through neighboring rooms.
const auto& room = g_Level.Rooms[collidingItem.RoomNumber];
for (int roomNumber : room.neighbors)
for (int roomNumber : room.NeighborRoomNumbers)
{
auto& neighborRoom = g_Level.Rooms[roomNumber];
if (!neighborRoom.Active())
@ -204,7 +202,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible,
// Test accurate box intersection.
if (box0.Intersects(box1))
collObjects.ItemPtrs.push_back(&item);
collObjects.Items.push_back(&item);
}
while (itemNumber != NO_VALUE);
}
@ -253,7 +251,7 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible,
// Test accurate box intersection.
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 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())
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);
}
// Snap to new position.
else
else if (coll->Setup.EnableObjectPush)
{
item1->Pose.Position = item0->Pose.Position + newDeltaPos;
}
@ -942,7 +940,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
{
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())
continue;
@ -989,7 +987,7 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
itemBounds.Extents = itemBounds.Extents - Vector3(BLOCK(1));
// 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.
GameBoundingBox collBox;
@ -1016,7 +1014,7 @@ bool CollideSolidBounds(ItemInfo* item, const GameBoundingBox& box, const Pose&
bool intersects = staticBounds.Intersects(collBounds);
// 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.
Vector3 corners[8];
@ -1811,7 +1809,7 @@ void DoObjectCollision(ItemInfo* item, CollisionInfo* coll)
return;
const auto& room = g_Level.Rooms[item->RoomNumber];
for (int neighborRoomNumber : room.neighbors)
for (int neighborRoomNumber : room.NeighborRoomNumbers)
{
auto& neighborRoom = g_Level.Rooms[neighborRoomNumber];
if (!neighborRoom.Active())

View file

@ -30,10 +30,10 @@ struct ObjectCollisionBounds
struct CollidedObjectData
{
std::vector<ItemInfo*> ItemPtrs = {};
std::vector<MESH_INFO*> StaticPtrs = {};
std::vector<ItemInfo*> Items = {};
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);

View file

@ -11,13 +11,11 @@
#include "Game/room.h"
#include "Math/Math.h"
#include "Sound/sound.h"
#include "Renderer/Renderer.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Collision::Room;
using namespace TEN::Math;
using namespace TEN::Renderer;
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);
// Debug angle and distance
// g_Renderer.PrintDebugMessage("Nearest angle: %d", coll->NearestLedgeAngle);
// g_Renderer.PrintDebugMessage("Nearest dist: %f", coll->NearestLedgeDistance);
// PrintDebugMessage("Nearest angle: %d", coll->NearestLedgeAngle);
// PrintDebugMessage("Nearest dist: %f", coll->NearestLedgeDistance);
// TEST 2: CENTERPOINT PROBE
@ -339,7 +337,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
probePos.x = entityPos.x + xFront;
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);
@ -394,7 +392,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
probePos.x = entityPos.x + xLeft;
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);
@ -460,7 +458,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
probePos.x = entityPos.x + xRight;
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);
@ -807,7 +805,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
}
// 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.
// 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;
// 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
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);
// 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.
if (p == 0)
@ -920,7 +918,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
auto cZ = fZ + BLOCK(1) + 1;
// 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.
auto sX = fX + 1 + BLOCK(0.5f);
@ -1083,246 +1081,6 @@ int GetDistanceToFloor(int itemNumber, bool precise)
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)
{
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]);
}
bool TestEnvironment(RoomEnvFlags environmentType, ROOM_INFO* room)
bool TestEnvironment(RoomEnvFlags environmentType, const ROOM_INFO* room)
{
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 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);
void ShiftItem(ItemInfo* item, CollisionInfo* coll);
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 ItemInfo* item);
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);

View file

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

View file

@ -136,14 +136,16 @@ class FloorInfo
{
public:
// Members
Vector2i Position = Vector2i::Zero;
int RoomNumber = 0;
int SidePortalRoomNumber = 0;
SectorSurfaceData FloorSurface = {};
SectorSurfaceData CeilingSurface = {};
std::set<int> BridgeItemNumbers = {};
SectorFlagData Flags = {};
int Box = 0;
std::set<int> BridgeItemNumbers = {};
int SidePortalRoomNumber = 0;
int PathfindingBoxID = 0;
int TriggerIndex = 0;
bool Stopper = true;

View file

@ -21,7 +21,6 @@
#include "Math/Math.h"
#include "Objects/objectslist.h"
#include "Objects/Generic/Object/Pushable/PushableObject.h"
#include "Renderer/Renderer.h"
using namespace TEN::Collision::Point;
using namespace TEN::Collision::Room;
@ -55,7 +54,7 @@ void DrawBox(int boxIndex, Vector3 color)
if (boxIndex == NO_VALUE)
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;
auto y = currBox.height - CLICK(1);
@ -69,7 +68,7 @@ void DrawBox(int boxIndex, Vector3 color)
for (int i = 0; i <= 10; 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)
return;
auto& currBox = g_Level.Boxes[boxIndex];
auto& currBox = g_Level.PathfindingBoxes[boxIndex];
auto index = currBox.overlapIndex;
// 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& 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)
return false;
item.BoxNumber = boxSource;
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)
return false;
target->BoxNumber = boxTarget;
@ -257,7 +256,7 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt)
int boxHeight;
if (item->BoxNumber != NO_VALUE)
boxHeight = g_Level.Boxes[item->BoxNumber].height;
boxHeight = g_Level.PathfindingBoxes[item->BoxNumber].height;
else
boxHeight = item->Floor;
@ -267,31 +266,31 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt)
GetFloor(prevPos.x, y, prevPos.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;
int height = g_Level.Boxes[floor->Box].height;
int height = g_Level.PathfindingBoxes[floor->PathfindingBoxID].height;
int nextHeight = 0;
int nextBox;
if (!Objects[item->ObjectNumber].nonLot)
{
nextBox = LOT->Node[floor->Box].exitBox;
nextBox = LOT->Node[floor->PathfindingBoxID].exitBox;
}
else
{
floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
height = g_Level.Boxes[floor->Box].height;
nextBox = floor->Box;
height = g_Level.PathfindingBoxes[floor->PathfindingBoxID].height;
nextBox = floor->PathfindingBoxID;
}
if (nextBox == NO_VALUE)
nextHeight = height;
else
nextHeight = g_Level.Boxes[nextBox].height;
nextHeight = g_Level.PathfindingBoxes[nextBox].height;
if (floor->Box == NO_VALUE || !LOT->IsJumping &&
(LOT->Fly == NO_FLYING && item->BoxNumber != NO_VALUE && zone[item->BoxNumber] != zone[floor->Box] ||
if (floor->PathfindingBoxID == NO_VALUE || !LOT->IsJumping &&
(LOT->Fly == NO_FLYING && item->BoxNumber != NO_VALUE && zone[item->BoxNumber] != zone[floor->PathfindingBoxID] ||
boxHeight - height > LOT->Step ||
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;
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)
{
nextBox = LOT->Node[floor->Box].exitBox;
nextBox = LOT->Node[floor->PathfindingBoxID].exitBox;
}
else
{
floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
height = g_Level.Boxes[floor->Box].height;
nextBox = floor->Box;
height = g_Level.PathfindingBoxes[floor->PathfindingBoxID].height;
nextBox = floor->PathfindingBoxID;
}
if (nextBox == NO_VALUE)
nextHeight = height;
else
nextHeight = g_Level.Boxes[nextBox].height;
nextHeight = g_Level.PathfindingBoxes[nextBox].height;
}
int x = item->Pose.Position.x;
@ -614,7 +613,7 @@ void CreatureUnderwater(ItemInfo* item, int depth)
}
else
{
waterHeight = GetWaterHeight(item);
waterHeight = GetPointCollision(*item).GetWaterTopHeight();
}
int y = waterHeight + waterLevel;
@ -648,7 +647,7 @@ void CreatureFloat(short itemNumber)
item->Pose.Orientation.x = 0;
int y = item->Pose.Position.y;
int waterLevel = GetWaterHeight(item);
int waterLevel = GetPointCollision(*item).GetWaterTopHeight();
if (waterLevel == NO_HEIGHT)
return;
@ -795,7 +794,7 @@ void CreatureHealth(ItemInfo* item)
TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, &g_Level.Rooms[item->RoomNumber]))
{
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)
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)
{
auto* floor = GetFloor(x, y, z, &roomNumber);
if (floor->Box == NO_VALUE)
if (floor->PathfindingBoxID == NO_VALUE)
return true;
if (LOT->IsJumping)
return false;
auto* box = &g_Level.Boxes[floor->Box];
auto* box = &g_Level.PathfindingBoxes[floor->PathfindingBoxID];
if (box->flags & LOT->BlockMask)
return true;
@ -928,7 +927,7 @@ bool ValidBox(ItemInfo* item, short zoneNumber, short boxNumber)
if (creature.LOT.Fly == NO_FLYING && zone[boxNumber] != zoneNumber)
return false;
const auto& box = g_Level.Boxes[boxNumber];
const auto& box = g_Level.PathfindingBoxes[boxNumber];
if (creature.LOT.BlockMask & box.flags)
return false;
@ -948,7 +947,7 @@ bool EscapeBox(ItemInfo* item, ItemInfo* enemy, int boxNumber)
if (boxNumber == NO_VALUE)
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 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)
return;
auto* box = &g_Level.Boxes[boxNumber];
auto* box = &g_Level.PathfindingBoxes[boxNumber];
// 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));
@ -1016,7 +1015,7 @@ bool SearchLOT(LOTInfo* LOT, int depth)
return false;
}
auto* box = &g_Level.Boxes[LOT->Head];
auto* box = &g_Level.PathfindingBoxes[LOT->Head];
auto* node = &LOT->Node[LOT->Head];
int index = box->overlapIndex;
@ -1034,7 +1033,7 @@ bool SearchLOT(LOTInfo* LOT, int depth)
if (LOT->Fly == NO_FLYING && searchZone != zone[boxNumber])
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))
continue;
@ -1057,7 +1056,7 @@ bool SearchLOT(LOTInfo* LOT, int depth)
if ((node->searchNumber & SEARCH_NUMBER) == (expand->searchNumber & SEARCH_NUMBER) && !(expand->searchNumber & BLOCKED_SEARCH))
continue;
if (g_Level.Boxes[boxNumber].flags & LOT->BlockMask)
if (g_Level.PathfindingBoxes[boxNumber].flags & LOT->BlockMask)
{
expand->searchNumber = node->searchNumber | BLOCKED_SEARCH;
}
@ -1145,7 +1144,7 @@ bool StalkBox(ItemInfo* item, ItemInfo* enemy, int boxNumber)
{
if (enemy == nullptr || boxNumber == NO_VALUE)
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 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();
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];
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)
return;
@ -1505,7 +1504,7 @@ int TargetReachable(ItemInfo* item, ItemInfo* enemy)
{
const auto& creature = *GetCreatureInfo(item);
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.
// 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();
}
return (isReachable ? floor->Box : NO_VALUE);
return (isReachable ? floor->PathfindingBoxID : NO_VALUE);
}
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* 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];
enemy->BoxNumber = TargetReachable(item, enemy);
@ -1554,7 +1553,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
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;
}
@ -1754,7 +1753,7 @@ void CreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent)
int endBox = LOT->Node[item->BoxNumber].exitBox;
if (endBox != NO_VALUE)
{
int overlapIndex = g_Level.Boxes[item->BoxNumber].overlapIndex;
int overlapIndex = g_Level.PathfindingBoxes[item->BoxNumber].overlapIndex;
int nextBox = 0;
int flags = 0;
@ -1901,7 +1900,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
if (boxNumber == NO_VALUE)
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 boxRight = ((int)box->right * BLOCK(1)) - 1;
@ -1915,7 +1914,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
do
{
box = &g_Level.Boxes[boxNumber];
box = &g_Level.PathfindingBoxes[boxNumber];
if (LOT->Fly != NO_FLYING)
{
@ -2096,7 +2095,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
}
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;
} while (boxNumber != NO_VALUE);
@ -2130,14 +2129,14 @@ void AdjustStopperFlag(ItemInfo* item, int direction)
int z = item->Pose.Position.z;
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;
x = item->Pose.Position.x + BLOCK(1) * phd_sin(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()];
floor = GetSector(room, x - room->x, z - room->z);
floor = GetSector(room, x - room->Position.x, z - room->Position.z);
floor->Stopper = !floor->Stopper;
}
@ -2152,15 +2151,15 @@ void InitializeItemBoxData()
{
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));
if (index > room.floor.size())
long index = ((mesh.pos.Position.z - room.Position.z) / BLOCK(1)) + room.ZSize * ((mesh.pos.Position.x - room.Position.x) / BLOCK(1));
if (index > room.Sectors.size())
continue;
auto* floor = &room.floor[index];
if (floor->Box == NO_VALUE)
auto* floor = &room.Sectors[index];
if (floor->PathfindingBoxID == NO_VALUE)
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);
const auto& bBox = GetBoundsAccurate(mesh, false);
@ -2185,19 +2184,7 @@ bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType)
if (creature.Enemy == nullptr)
return false;
float stepDist = 0.0f;
switch (jumpDistType)
{
default:
case JumpDistance::Block1:
stepDist = BLOCK(0.51f);
break;
case JumpDistance::Block2:
stepDist = BLOCK(0.76f);
break;
}
float stepDist = BLOCK(0.92f);
int vPos = item.Pose.Position.y;
auto pointCollA = GetPointCollision(item, item.Pose.Orientation.y, stepDist);
auto pointCollB = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 2);

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#pragma once
#include "Game/items.h"
#include "Game/control/control.h"
#include "Game/items.h"
#define EffectFunction void(ItemInfo* item)
@ -10,35 +11,35 @@ extern int FlipEffect;
extern std::function<EffectFunction> effect_routines[];
void AddLeftFootprint(ItemInfo* item);
void AddRightFootprint(ItemInfo* item);
void AddLeftFootprint(ItemInfo* item); // TODO: To anim command.
void AddRightFootprint(ItemInfo* item); // TODO: To anim command.
void VoidEffect(ItemInfo* item);
void FinishLevel(ItemInfo* item);
void Turn180(ItemInfo* item);
void Turn180(ItemInfo* item); // TODO: To anim command.
void FloorShake(ItemInfo* item);
void PlaySoundEffect(ItemInfo* item);
void RubbleFX(ItemInfo* item);
void PoseidonSFX(ItemInfo* item);
void ActivateCamera(ItemInfo* item);
void ActivateKey(ItemInfo* item);
void SwapCrowbar(ItemInfo* item);
void SwapCrowbar(ItemInfo* item); // TODO: To anim command.
void ExplosionFX(ItemInfo* item);
void LaraLocation(ItemInfo* item);
void LaraLocationPad(ItemInfo* item);
void KillActiveBaddys(ItemInfo* item);
void LaraHandsFree(ItemInfo* item);
void ShootRightGun(ItemInfo* item);
void ShootLeftGun(ItemInfo* item);
void LaraHandsFree(ItemInfo* item); // TODO: To anim command.
void ShootRightGun(ItemInfo* item); // TODO: To anim command.
void ShootLeftGun(ItemInfo* item); // TODO: To anim command.
void SetFog(ItemInfo* item);
void InvisibilityOn(ItemInfo* item);
void InvisibilityOff(ItemInfo* item);
void ResetHair(ItemInfo* item);
void Pickup(ItemInfo* item);
void Puzzle(ItemInfo* item);
void DrawRightPistol(ItemInfo* item);
void DrawLeftPistol(ItemInfo* item);
void MeshSwapToPour(ItemInfo* item);
void MeshSwapFromPour(ItemInfo* item);
void Pickup(ItemInfo* item); // TODO: To anim command.
void Puzzle(ItemInfo* item); // TODO: To anim command.
void DrawRightPistol(ItemInfo* item); // TODO: To anim command.
void DrawLeftPistol(ItemInfo* item); // TODO: To anim command.
void MeshSwapToPour(ItemInfo* item); // TODO: To anim command.
void MeshSwapFromPour(ItemInfo* item); // TODO: To anim command.
void FlashOrange(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.
const auto& room = g_Level.Rooms[roomNumber];
for (int neighborRoomNumber : room.neighbors)
for (int neighborRoomNumber : room.NeighborRoomNumbers)
{
// Get neighbor room.
const auto& neighborRoom = g_Level.Rooms[neighborRoomNumber];

View file

@ -25,7 +25,7 @@ void InitializeLOTarray(int itemNumber)
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;
}
}
@ -240,14 +240,14 @@ void CreateZone(ItemInfo* item)
auto* creature = GetCreatureInfo(item);
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)
{
auto* node = creature->LOT.Node.data();
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++;
@ -265,7 +265,7 @@ void CreateZone(ItemInfo* item)
auto* node = creature->LOT.Node.data();
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)
{

View file

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

View file

@ -1,38 +1,52 @@
#include "framework.h"
#include "Game/debug/debug.h"
#include "Game/Debug/Debug.h"
#include <chrono>
#include <spdlog.h>
#include <spdlog/sinks/basic_file_sink.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
{
static auto StartTime = std::chrono::high_resolution_clock::time_point{};
void InitTENLog(const std::string& logDirContainingDir)
{
// "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);
std::shared_ptr<spdlog::logger> logger;
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 });
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 = {};
void ShutdownTENLog()
{
spdlog::shutdown();
}
if (lastString == str && !allowSpam)
void TENLog(const std::string_view& msg, LogLevel level, LogConfig config, bool allowSpam)
{
static auto prevString = std::string();
if (prevString == msg && !allowSpam)
return;
if constexpr (!DebugBuild)
{
if (LogConfig::Debug == config)
if (config == LogConfig::Debug)
return;
}
@ -40,22 +54,106 @@ void TENLog(std::string_view str, LogLevel level, LogConfig config, bool allowSp
switch (level)
{
case LogLevel::Error:
logger->error(str);
logger->error(msg);
break;
case LogLevel::Warning:
logger->warn(str);
logger->warn(msg);
break;
case LogLevel::Info:
logger->info(str);
logger->info(msg);
break;
}
logger->flush();
lastString = std::string(str);
}
prevString = std::string(msg);
}
void ShutdownTENLog()
{
spdlog::shutdown();
void StartDebugTimer()
{
StartTime = std::chrono::high_resolution_clock::now();
}
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);
}
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 DrawDebug2DLine(const Vector2& origin, const Vector2& target, const Color& color, RendererDebugPage page)
{
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
#if _DEBUG
constexpr bool DebugBuild = true;
#else
constexpr bool DebugBuild = false;
#endif
#include <iostream>
#include <stdexcept>
#include <string_view>
#include <iostream>
enum class LogLevel
#include "Renderer/RendererEnums.h"
namespace TEN::Debug
{
#if _DEBUG
constexpr bool DebugBuild = true;
#else
constexpr bool DebugBuild = false;
#endif
enum class LogLevel
{
Error,
Warning,
Info
};
};
enum class LogConfig
{
enum class LogConfig
{
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:
class TENScriptException : public std::runtime_error
{
public:
using std::runtime_error::runtime_error;
};
};
inline void assertion(const bool& expr, const char* msg)
{
// 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)
{
if (!expr)
if (!cond)
{
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.
if (Color.w > 0.0f)
Color.w = InterpolateCos(0.0f, OpacityMax, Life / LifeMax);
Color.w = EaseInOutSine(0.0f, OpacityMax, Life / LifeMax);
// TODO: Not working.
// Update orientation.
@ -68,7 +68,7 @@ namespace TEN::Effects::Streamer
float lifeMax = std::min(round(life * FPS), (float)SEGMENT_COUNT_MAX);
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.Color = Vector4(color.x, color.y, color.z, opacityMax);
@ -101,7 +101,7 @@ namespace TEN::Effects::Streamer
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.
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,
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.
if (Pools.size() == POOL_COUNT_MAX && !Pools.count(tag))
@ -152,7 +152,7 @@ namespace TEN::Effects::Streamer
Streamer& StreamerModule::GetStreamer(int 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.
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,
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.
if (Modules.size() == MODULE_COUNT_MAX && !Modules.count(itemNumber))

View file

@ -160,7 +160,7 @@ namespace TEN::Effects::Bubble
{
// Hit water surface; spawn ripple.
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,
((bubble.SizeMax.x + bubble.SizeMax.y) / 2) * 0.5f,
(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.
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;
continue;

View file

@ -132,9 +132,8 @@ namespace TEN::Effects::Drip
// Spawn ripple on surface only.
if (!TestEnvironment(ENV_FLAG_WATER, prevRoomNumber))
{
float waterHeight = GetWaterHeight(drip.Position.x, drip.Position.y, drip.Position.z, drip.RoomNumber);
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(),
Random::GenerateFloat(RIPPLE_SIZE_WATER_MIN, RIPPLE_SIZE_WATER_MAX),
(int)RippleFlags::SlowFade | (int)RippleFlags::LowOpacity);

View file

@ -1312,7 +1312,7 @@ void Splash(ItemInfo* item)
if (!TestEnvironment(ENV_FLAG_WATER, probedRoomNumber))
return;
int waterHeight = GetWaterHeight(item);
int waterHeight = GetPointCollision(*item).GetWaterTopHeight();
SplashSetup.x = item->Pose.Position.x;
SplashSetup.y = waterHeight - 1;
@ -1944,7 +1944,7 @@ void ProcessEffects(ItemInfo* item)
if (item->Effect.Type != EffectType::Sparks && item->Effect.Type != EffectType::Smoke)
{
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);
if (waterHeight != NO_HEIGHT && itemLevel > waterHeight)

View file

@ -246,7 +246,7 @@ extern FX_INFO EffectList[NUM_EFFECTS];
template <typename TEffect>
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.
if (effects.size() < countMax)

View file

@ -23,7 +23,7 @@ namespace TEN::Effects::Hair
{
HairEffectController HairEffect = {};
void HairUnit::Update(const ItemInfo& item, int hairUnitIndex)
void HairUnit::Update(const ItemInfo& item, int hairUnitID)
{
for (auto& segment : Segments)
segment.StoreInterpolationData();
@ -37,15 +37,14 @@ namespace TEN::Effects::Hair
g_Renderer.GetBoneMatrix(item.Index, LM_HEAD, &worldMatrix);
// Apply base offset to world matrix.
auto relOffset = GetRelBaseOffset(hairUnitIndex, isYoung);
auto relOffset = GetRelBaseOffset(hairUnitID, isYoung);
worldMatrix = Matrix::CreateTranslation(relOffset) * worldMatrix;
// Use player's head bone orientation as base.
auto baseOrient = Geometry::ConvertDirectionToQuat(-Geometry::ConvertQuatToDirection(GetBoneOrientation(item, LM_HEAD)));
// Set position of base segment.
auto basePos = worldMatrix.Translation();
Segments[0].Position = basePos;
Segments[0].Position = worldMatrix.Translation();
if (!IsInitialized)
{
@ -56,7 +55,7 @@ namespace TEN::Effects::Hair
auto& nextSegment = Segments[i + 1];
// NOTE: Joint offset determines segment length.
auto jointOffset = GetJointOffset(ID_HAIR, i);
auto jointOffset = GetJointOffset(ObjectID, i);
worldMatrix = Matrix::CreateTranslation(segment.Position);
worldMatrix = Matrix::CreateFromQuaternion(segment.Orientation) * worldMatrix;
@ -72,7 +71,7 @@ namespace TEN::Effects::Hair
// Get water height.
auto pos = item.Pose.Position + Vector3i(GetWaterProbeOffset(item));
int roomNumber = item.RoomNumber;
int waterHeight = GetWaterHeight(pos.x, pos.y, pos.z, roomNumber);
int waterHeight = GetPointCollision(pos, roomNumber).GetWaterTopHeight();
// Get collision spheres.
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.
// Therefore, cannot directly use water status value to determine enrironment.
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);
// Handle segment sphere collision.
CollideSegmentWithSpheres(segment, spheres);
// Calculate orientation.
@ -102,8 +99,8 @@ namespace TEN::Effects::Hair
worldMatrix = Matrix::CreateFromQuaternion(prevSegment.Orientation) * worldMatrix;
auto jointOffset = (i == (Segments.size() - 1)) ?
GetJointOffset(ID_HAIR, (i - 1) - 1) :
GetJointOffset(ID_HAIR, (i - 1));
GetJointOffset(ObjectID, (i - 1) - 1) :
GetJointOffset(ObjectID, (i - 1));
worldMatrix = Matrix::CreateTranslation(jointOffset) * worldMatrix;
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;
if (isYoung)
{
switch (hairUnitIndex)
switch (hairUnitID)
{
// Left pigtail offset.
case 0:
relOffset = Vector3(-52.0f, -48.0f, -50.0f);
relOffset = Vector3(-48.0f, -48.0f, -50.0f);
break;
// Right pigtail offset.
case 1:
relOffset = Vector3(44.0f, -48.0f, -50.0f);
relOffset = Vector3(48.0f, -48.0f, -50.0f);
break;
}
}
@ -178,13 +175,13 @@ namespace TEN::Effects::Hair
Quaternion HairUnit::GetSegmentOrientation(const Vector3& origin, const Vector3& target, const Quaternion& baseOrient)
{
// Calculate absolute orientation.
auto absDirection = target - origin;
absDirection.Normalize();
auto absOrient = Geometry::ConvertDirectionToQuat(absDirection);
auto absDir = target - origin;
absDir.Normalize();
auto absOrient = Geometry::ConvertDirectionToQuat(absDir);
// Calculate relative twist rotation.
// 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();
// Return ideal orientation.
@ -201,21 +198,21 @@ namespace TEN::Effects::Hair
spheres.reserve(SPHERE_COUNT);
// Hips sphere.
auto* meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_HIPS]];
auto pos = GetJointPosition(item, LM_HIPS, Vector3i(meshPtr->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, meshPtr->sphere.Radius));
const auto* mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_HIPS]];
auto pos = GetJointPosition(item, LM_HIPS, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, mesh->sphere.Radius));
// Torso sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_TORSO]];
pos = GetJointPosition(item, LM_TORSO, Vector3i(meshPtr->sphere.Center) + TORSO_SPHERE_OFFSET).ToVector3();
spheres.push_back(BoundingSphere(pos, meshPtr->sphere.Radius));
mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_TORSO]];
pos = GetJointPosition(item, LM_TORSO, Vector3i(mesh->sphere.Center) + TORSO_SPHERE_OFFSET).ToVector3();
spheres.push_back(BoundingSphere(pos, mesh->sphere.Radius));
if (isYoung)
spheres.back().Radius = spheres.back().Radius - ((spheres.back().Radius / 4) + (spheres.back().Radius / 8));
// Head sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_HEAD]];
pos = GetJointPosition(item, LM_HEAD, Vector3i(meshPtr->sphere.Center) + HEAD_SPHERE_OFFSET).ToVector3();
spheres.push_back(BoundingSphere(pos, meshPtr->sphere.Radius));
mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_HEAD]];
pos = GetJointPosition(item, LM_HEAD, Vector3i(mesh->sphere.Center) + HEAD_SPHERE_OFFSET).ToVector3();
spheres.push_back(BoundingSphere(pos, mesh->sphere.Radius));
// Neck sphere.
spheres.push_back(BoundingSphere(
@ -223,30 +220,30 @@ namespace TEN::Effects::Hair
isYoung ? 0.0f : (spheres[2].Radius * 0.75f)));
// Left arm sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_LINARM]];
pos = GetJointPosition(item, LM_LINARM, Vector3i(meshPtr->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, (meshPtr->sphere.Radius / 3) * 4));
mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_LINARM]];
pos = GetJointPosition(item, LM_LINARM, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, (mesh->sphere.Radius / 3) * 4));
// Right arm sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_RINARM]];
pos = GetJointPosition(item, LM_RINARM, Vector3i(meshPtr->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, (meshPtr->sphere.Radius / 3) * 4));
mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_RINARM]];
pos = GetJointPosition(item, LM_RINARM, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(BoundingSphere(pos, (mesh->sphere.Radius / 3) * 4));
// Left holster sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_LTHIGH]];
pos = GetJointPosition(item, LM_LTHIGH, Vector3i(meshPtr->sphere.Center)).ToVector3();
mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_LTHIGH]];
pos = GetJointPosition(item, LM_LTHIGH, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(
BoundingSphere(
pos + ((spheres[0].Center - pos) / 2),
meshPtr->sphere.Radius));
mesh->sphere.Radius));
// Right holster sphere.
meshPtr = &g_Level.Meshes[item.Model.MeshIndex[LM_RTHIGH]];
pos = GetJointPosition(item, LM_RTHIGH, Vector3i(meshPtr->sphere.Center)).ToVector3();
mesh = &g_Level.Meshes[item.Model.MeshIndex[LM_RTHIGH]];
pos = GetJointPosition(item, LM_RTHIGH, Vector3i(mesh->sphere.Center)).ToVector3();
spheres.push_back(
BoundingSphere(
pos + ((spheres[0].Center - pos) / 2),
meshPtr->sphere.Radius));
mesh->sphere.Radius));
if (isYoung)
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)
{
constexpr auto VELOCITY_COEFF = 0.75f;
constexpr auto VEL_COEFF = 0.75f;
auto pointColl = GetPointCollision(segment.Position, roomNumber);
int floorHeight = pointColl.GetFloorHeight();
Segments[0].Velocity = segment.Position;
segment.Position += segment.Velocity * VELOCITY_COEFF;
segment.Position += segment.Velocity * VEL_COEFF;
// Land collision.
if (isOnLand)
@ -280,7 +276,7 @@ namespace TEN::Effects::Hair
segment.Position.y = waterHeight;
}
// 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;
}
@ -292,9 +288,9 @@ namespace TEN::Effects::Hair
{
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)
{
auto direction = segment.Position - sphere.Center;
auto dir = segment.Position - sphere.Center;
float distance = Vector3::Distance(segment.Position, sphere.Center);
if (distance < sphere.Radius)
float dist = Vector3::Distance(segment.Position, sphere.Center);
if (dist < sphere.Radius)
{
// Avoid division by zero.
if (distance == 0.0f)
distance = 1.0f;
if (dist == 0.0f)
dist = 1.0f;
// Push segment away from sphere.
segment.Position = sphere.Center + (direction * (sphere.Radius / distance));
segment.Position = sphere.Center + (dir * (sphere.Radius / dist));
}
}
}
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);
// Initialize hair units.
bool isHead = true;
for (auto& unit : Units)
for (int i = 0; i < Units.size(); i++)
{
unit.IsEnabled = (!isHead || isYoung);
unit.IsInitialized = false;
auto& unit = Units[i];
unsigned int segmentCount = Objects[ID_HAIR].nmeshes + 1;
unit.Segments.resize(segmentCount);
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.ObjectID = objectID;
unit.Segments.resize(object.nmeshes + 1);
// Initialize segments.
for (auto& segment : unit.Segments)
{
segment.Position = GetJointOffset(ID_HAIR, 0);
segment.Position = GetJointOffset(objectID, 0);
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++)
{
Units[i].Update(item, i);
auto& unit = Units[i];
if (!unit.IsEnabled)
continue;
if (isYoung && i == 1)
Units[i].Update(item, i);
unit.Update(item, i);
}
}
}

View file

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

View file

@ -5,6 +5,7 @@
#include "Game/animation.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/floordata.h"
#include "Game/collision/Point.h"
#include "Game/effects/effects.h"
#include "Game/effects/Bubble.h"
#include "Game/effects/debris.h"
@ -27,6 +28,7 @@ using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Ripple;
using namespace TEN::Effects::Smoke;
using namespace TEN::Collision::Floordata;
using namespace TEN::Collision::Point;
using namespace TEN::Math;
using TEN::Renderer::g_Renderer;
@ -264,30 +266,23 @@ void TriggerPilotFlame(int itemNumber, int nodeIndex)
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;
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;
return part;
}
Particle* SetupFireSpark()
static Particle* SetupFireSpark()
{
auto* spark = GetFreeParticle();
@ -304,19 +299,20 @@ Particle* SetupFireSpark()
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->y = (GetRandomControl() & 0x1F) + pos1.y - 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;
spark->life = spark->sLife = v / 6;
spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + spriteID;
spark->xVel = v * (pos2.x - pos1.x) / 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;
}
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];
for (int i = 0; i < 3; i++)
{
auto& spark = *SetupFireSpark();
AttachAndCreateSpark(&spark, &item, meshIndex, offset, vel);
auto& part = *SetupFireSpark();
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);
AttachAndCreateSpark(spark, item, meshIndex, offset, vel);
spark->flags = SP_POISON | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
auto& part = SetupPoisonParticle(colorStart, colorEnd);
AttachAndCreateSpark(&part, &item, boneID, offset, vel, spriteID);
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()
@ -1016,10 +1012,10 @@ void UpdateGunShells()
!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);
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,
Random::GenerateFloat(8.0f, 12.0f),
(int)RippleFlags::SlowFade);
@ -1146,7 +1142,7 @@ void TriggerUnderwaterExplosion(ItemInfo* item, int flag)
TriggerExplosionBubbles(x, y, z, 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)
SomeSparkEffect(x, waterHeight, z, 8);
}
@ -1158,7 +1154,7 @@ void TriggerUnderwaterExplosion(ItemInfo* item, int flag)
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);
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)
{
int dy = item->Pose.Position.y - waterHeight;

View file

@ -315,10 +315,10 @@ void TriggerGlobalStaticFlame();
void TriggerGlobalFireSmoke();
void TriggerGlobalFireFlame();
void TriggerPilotFlame(int itemNumber, int nodeIndex);
void ThrowFire(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel);
void ThrowFire(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel);
void ThrowPoison(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel, const Vector3& color);
void ThrowPoison(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel, const Vector3& color);
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, int spriteID = 0);
void ThrowPoison(const ItemInfo& item, int boneID, const Vector3& offset, const Vector3& vel, const Color& colorStart, const Color& colorEnd, int spriteID = 0);
void ThrowPoison(const ItemInfo& item, const CreatureBiteInfo& bite, const Vector3& vel, const Color& colorStart, const Color& colorEnd, int spriteID = 0);
void UpdateFireProgress();
void ClearFires();
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,
UseItem,
NewGame,
HomeLevel,
LoadGame,
SaveGame,
ExitGame,
@ -187,6 +188,7 @@ namespace TEN::Gui
void UseItem(ItemInfo& item, int objectNumber);
// Getters
const InventoryRing& GetRing(RingTypes ringType);
int GetSelectedOption();
Menu GetMenuToDisplay();
@ -196,8 +198,10 @@ namespace TEN::Gui
int GetLastInventoryItem();
SettingsData& GetCurrentSettings();
int GetLoadSaveSelection();
int GetLoopedSelectedOption(int selectedOption, int optionCount, bool canLoop);
// Setters
void SetSelectedOption(int menu);
void SetMenuToDisplay(Menu menu);
void SetInventoryMode(InventoryMode mode);

View file

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

View file

@ -584,9 +584,9 @@ void InitializeItem(short itemNumber)
item->NextItem = room->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->BoxNumber = floor->Box;
item->BoxNumber = floor->PathfindingBoxID;
item->ResetModelToDefault();

View file

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

View file

@ -18,5 +18,5 @@ enum LaraMeshMask
};
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);

View file

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

View file

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

View file

@ -234,8 +234,8 @@ void CollectMultiplePickups(int itemNumber)
auto& firstItem = g_Level.Items[itemNumber];
auto collObjects = GetCollidedObjects(firstItem, true, true, LARA_RADIUS, ObjectCollectionMode::Items);
collObjects.ItemPtrs.push_back(&firstItem);
for (auto* itemPtr : collObjects.ItemPtrs)
collObjects.Items.push_back(&firstItem);
for (auto* itemPtr : collObjects.Items)
{
if (!Objects[itemPtr->ObjectNumber].isPickup)
continue;
@ -886,7 +886,7 @@ void DropPickups(ItemInfo* item)
// Iterate through all found items and statics around, and determine if dummy sphere
// 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);
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];

View file

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

View file

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

View file

@ -544,11 +544,11 @@ const std::vector<byte> SaveGame::Build()
std::vector<flatbuffers::Offset<Save::Room>> rooms;
for (auto& room : g_Level.Rooms)
{
auto nameOffset = fbb.CreateString(room.name);
auto nameOffset = fbb.CreateString(room.Name);
Save::RoomBuilder serializedInfo{ fbb };
serializedInfo.add_name(nameOffset);
serializedInfo.add_index(room.index);
serializedInfo.add_index(room.RoomNumber);
serializedInfo.add_reverb_type((int)room.reverbType);
serializedInfo.add_flags(room.flags);
auto serializedInfoOffset = serializedInfo.Finish();
@ -991,14 +991,14 @@ const std::vector<byte> SaveGame::Build()
staticMesh.add_flags(room->mesh[j].flags);
staticMesh.add_hit_points(room->mesh[j].HitPoints);
staticMesh.add_room_number(room->index);
staticMesh.add_room_number(room->RoomNumber);
staticMesh.add_number(j);
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;
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);
Save::VolumeBuilder volume{ fbb };
volume.add_room_number(room->index);
volume.add_room_number(room->RoomNumber);
volume.add_number(j);
volume.add_name(nameOffset);
volume.add_enabled(currVolume.Enabled);
@ -2010,7 +2010,7 @@ static void ParseEffects(const Save::SaveGame* s)
// Restore soundtracks.
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);
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++)
{
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()].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()];
int number = volume->number();
room->triggerVolumes[number].Enabled = volume->enabled();
room->triggerVolumes[number].Name = volume->name()->str();
room->triggerVolumes[number].Box.Center =
room->triggerVolumes[number].Sphere.Center = ToVector3(volume->position());
room->triggerVolumes[number].Box.Orientation = ToVector4(volume->rotation());
room->triggerVolumes[number].Box.Extents = ToVector3(volume->scale());
room->triggerVolumes[number].Sphere.Radius = room->triggerVolumes[number].Box.Extents.x;
room->TriggerVolumes[number].Enabled = volume->enabled();
room->TriggerVolumes[number].Name = volume->name()->str();
room->TriggerVolumes[number].Box.Center =
room->TriggerVolumes[number].Sphere.Center = ToVector3(volume->position());
room->TriggerVolumes[number].Box.Orientation = ToVector4(volume->rotation());
room->TriggerVolumes[number].Box.Extents = ToVector3(volume->scale());
room->TriggerVolumes[number].Sphere.Radius = room->TriggerVolumes[number].Box.Extents.x;
for (int j = 0; j < volume->queue()->size(); j++)
{
auto state = volume->queue()->Get(j);
room->triggerVolumes[number].StateQueue.push_back(
room->TriggerVolumes[number].StateQueue.push_back(
VolumeState
{
(VolumeStateStatus)state->status(),
@ -2228,7 +2228,7 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode)
// Restore action queue.
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);
}

View file

@ -4,6 +4,7 @@
//namespace TEN::Math
//{
// Math constants
constexpr auto PI = 3.14159265358979323846264338327950288419716939937510f;
constexpr auto PI_MUL_2 = PI * 2;
constexpr auto PI_DIV_2 = PI / 2;
@ -15,7 +16,14 @@
constexpr auto SQUARE = [](auto x) { return (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
constexpr auto BLOCK_UNIT = 1024;
constexpr auto NO_HEIGHT = INT_MIN + UCHAR_MAX;
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
{
// 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, const Vector3i& relOffset, const Vector3& axis = Vector3::UnitY);
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);
// 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, const Vector3& relOffset, const Vector3& axis = Vector3::UnitY);
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);
// Rotation
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rot);
Vector3 RotatePoint(const Vector3& point, const AxisAngle& rot);
// Angle getters
short GetShortestAngle(short fromAngle, short toAngle);
short GetSurfaceSlopeAngle(const Vector3& normal, const Vector3& axis = Vector3::UnitY);
short GetSurfaceAspectAngle(const Vector3& normal, const Vector3& axis = Vector3::UnitY);
// Misc. getters
float GetDistanceToLine(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);
@ -41,10 +46,12 @@ namespace TEN::Math::Geometry
BoundingBox GetBoundingBox(const std::vector<Vector3>& points);
// Converters
Quaternion ConvertDirectionToQuat(const Vector3& dir);
Vector3 ConvertQuatToDirection(const Quaternion& quat);
// Point relation inquirers
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 Vector3& refPoint);
@ -56,5 +63,6 @@ namespace TEN::Math::Geometry
bool IsPointInSphere(const Vector3& point, const BoundingSphere& sphere);
// Intersection inquirers
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
#include "Math/Constants.h"
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 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)
{
return short(degrees * SHORTS_TO_1_DEGREE);
return (short)ROUND(degrees * SHORTS_TO_1_DEGREE);
}
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)

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
#include "Math/Constants.h"
#include "Math/Geometry.h"
#include "Math/Interpolation.h"
#include "Math/Legacy.h"
#include "Math/Objects/EulerAngles.h"
#include "Math/Objects/GameBoundingBox.h"
@ -11,13 +10,4 @@
#include "Math/Objects/Vector3i.h"
#include "Math/Random.h"
#include "Math/Solvers.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);
}
#include "Math/Utils.h"

View file

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

View file

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

View file

@ -12,6 +12,7 @@ struct ObjectInfo;
{
public:
// Members
int X1 = 0;
int X2 = 0;
int Y1 = 0;
@ -20,15 +21,18 @@ struct ObjectInfo;
int Z2 = 0;
// Constants
static const GameBoundingBox Zero;
// Constructors
GameBoundingBox() {};
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(const ItemInfo* item);
// Getters
int GetWidth() const;
int GetHeight() const;
int GetDepth() const;
@ -36,13 +40,16 @@ struct ObjectInfo;
Vector3 GetExtents() const;
// Utilities
void Rotate(const EulerAngles& rot);
// Converters
BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const;
BoundingOrientedBox ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const;
// Operators
GameBoundingBox operator +(const GameBoundingBox& bounds) const;
GameBoundingBox operator +(const Pose& pose) const;
GameBoundingBox operator -(const GameBoundingBox& bounds) const;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -180,7 +180,7 @@ void ElectricityWiresControl(short itemNumber)
return;
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];

View file

@ -77,35 +77,35 @@ namespace TEN::Entities::Doors
xOffset = BLOCK(1);
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;
if (roomNumber == NO_VALUE)
boxNumber = doorData->d1.floor->Box;
boxNumber = doorData->d1.floor->PathfindingBoxID;
else
{
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;
if (r->flippedRoom != -1)
{
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;
if (roomNumber == NO_VALUE)
boxNumber = doorData->d1flip.floor->Box;
boxNumber = doorData->d1flip.floor->PathfindingBoxID;
else
{
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;
}
else
@ -124,35 +124,35 @@ namespace TEN::Entities::Doors
else
{
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;
if (roomNumber == NO_VALUE)
boxNumber = doorData->d2.floor->Box;
boxNumber = doorData->d2.floor->PathfindingBoxID;
else
{
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;
if (r->flippedRoom != -1)
{
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;
if (roomNumber == NO_VALUE)
boxNumber = doorData->d2flip.floor->Box;
boxNumber = doorData->d2flip.floor->PathfindingBoxID;
else
{
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;
}
else
@ -401,7 +401,7 @@ namespace TEN::Entities::Doors
short boxIndex = doorPos->block;
if (boxIndex != NO_VALUE)
{
g_Level.Boxes[boxIndex].flags &= ~BLOCKED;
g_Level.PathfindingBoxes[boxIndex].flags &= ~BLOCKED;
for (auto& currentCreature : ActiveCreatures)
currentCreature->LOT.TargetBox = NO_VALUE;
}
@ -416,7 +416,7 @@ namespace TEN::Entities::Doors
if (floor)
{
floor->Box = NO_VALUE;
floor->PathfindingBoxID = NO_VALUE;
floor->TriggerIndex = 0;
// FIXME: HACK!!!!!!!
@ -435,7 +435,7 @@ namespace TEN::Entities::Doors
short boxIndex = doorPos->block;
if (boxIndex != NO_VALUE)
{
g_Level.Boxes[boxIndex].flags |= BLOCKED;
g_Level.PathfindingBoxes[boxIndex].flags |= BLOCKED;
for (auto& currentCreature : ActiveCreatures)
currentCreature->LOT.TargetBox = NO_VALUE;

View file

@ -120,10 +120,10 @@ namespace TEN::Entities::Generic
auto collObjects = GetCollidedObjects(pushableItem, true, true);
pushableItem.Pose.Position = prevPos;
if (!collObjects.StaticPtrs.empty())
if (!collObjects.Statics.empty())
return false;
for (const auto* itemPtr : collObjects.ItemPtrs)
for (const auto* itemPtr : collObjects.Items)
{
const auto& object = Objects[itemPtr->ObjectNumber];
@ -191,10 +191,10 @@ namespace TEN::Entities::Generic
auto collObjects = GetCollidedObjects(*LaraItem, true, true);
LaraItem->Pose.Position = prevPos;
if (!collObjects.StaticPtrs.empty())
if (!collObjects.Statics.empty())
return false;
for (const auto* itemPtr : collObjects.ItemPtrs)
for (const auto* itemPtr : collObjects.Items)
{
const auto& object = Objects[itemPtr->ObjectNumber];
@ -315,7 +315,7 @@ namespace TEN::Entities::Generic
waterHeight = pointColl.GetWaterSurfaceHeight();
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);
}
@ -324,7 +324,7 @@ namespace TEN::Entities::Generic
waterHeight = pointColl.GetWaterSurfaceHeight();
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{};

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)
{
constexpr auto LEAN_ANGLE_MAX = ANGLE(40.0f);

View file

@ -261,19 +261,19 @@ namespace TEN::Entities::Generic
if (!collObjects.IsEmpty())
{
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 &&
!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);

View file

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

View file

@ -236,13 +236,31 @@ void InitializeAnimating(short itemNumber)
void AnimatingControl(short itemNumber)
{
auto* item = &g_Level.Items[itemNumber];
auto& item = g_Level.Items[itemNumber];
if (!TriggerActive(item))
return;
if (TriggerActive(&item))
{
item.Status = ITEM_ACTIVE;
AnimateItem(&item);
item->Status = ITEM_ACTIVE;
AnimateItem(item);
if (item.TriggerFlags == 666) //OCB used for the helicopter animating in the Train level.
{
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.
/*if (item->frameNumber >= g_Level.Anims[item->animNumber].frameEnd)

View file

@ -9,67 +9,96 @@
#include "Sound/sound.h"
#include "Specific/level.h"
// NOTES:
// ItemFlags[0]: Delay between darts in frame time.
// ItemFlags[1]: Timer in frame time.
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;
DoDamage(LaraItem, item->TriggerFlags ? abs(item->TriggerFlags) : DART_DEFAULT_DAMAGE);
DoBloodSplat(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, (GetRandomControl() & 3) + 4, LaraItem->Pose.Orientation.y, LaraItem->RoomNumber);
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);
KillItem(itemNumber);
}
else
{
int oldX = item->Pose.Position.x;
int oldZ = item->Pose.Position.z;
auto prevPos = item.Pose.Position;
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.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.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.z += vel * phd_cos(item.Pose.Orientation.y);
short roomNumber = item->RoomNumber;
FloorInfo* floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
short roomNumber = item.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);
int height = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z);
item->Floor = height;
int height = GetFloorHeight(floor, item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z);
item.Floor = height;
if (item->Pose.Position.y >= height)
if (item.Pose.Position.y >= height)
{
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);
}
}
}
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
{
item->Timer = 24;
timer = delay;
}
}
@ -77,120 +106,128 @@ namespace TEN::Entities::Traps
if (dartItemNumber == NO_VALUE)
return;
ItemInfo* dartItem = &g_Level.Items[dartItemNumber];
dartItem->ObjectNumber = ID_DARTS;
dartItem->RoomNumber = item->RoomNumber;
dartItem->Pose.Position.x = item->Pose.Position.x;
dartItem->Pose.Position.y = item->Pose.Position.y - CLICK(0.9);
dartItem->Pose.Position.z = item->Pose.Position.z;
auto& dartItem = g_Level.Items[dartItemNumber];
dartItem.ObjectNumber = ID_DARTS;
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;
InitializeItem(dartItemNumber);
dartItem->Pose.Orientation.x = item->Pose.Orientation.x - ANGLE(180.0f);
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;
dartItem.Animation.Velocity.z = DART_DEFAULT_VELOCITY;
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);
SpawnDartSmoke(dartItem.Pose.Position.ToVector3(), Vector3::Zero, false);
AddActiveItem(dartItemNumber);
dartItem->Status = ITEM_ACTIVE;
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;
SoundEffect(SFX_TR4_DART_SPIT, &dartItem.Pose);
}
else
{
if (xv)
spark->xVel = -xv;
else
spark->xVel = ((GetRandomControl() & 255) - 128);
spark->yVel = -(GetRandomControl() & 3) - 4;
if (zv)
spark->zVel = -zv;
else
spark->zVel = ((GetRandomControl() & 255) - 128);
spark->friction = 3;
item.Status = ITEM_NOT_ACTIVE;
RemoveActiveItem(itemNumber, false);
item.Active = false;
}
}
spark->friction = 3;
void SpawnDartSmoke(const Vector3& pos, const Vector3& vel, bool isHit)
{
auto& part = *GetFreeParticle();
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;
}
part.friction = 3;
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)
spark->rotAdd = -16 - (GetRandomControl() & 0xF);
else
spark->rotAdd = (GetRandomControl() & 0xF) + 16;
{
part.rotAdd = -16 - (GetRandomControl() & 0xF);
}
else
{
spark->flags = SP_EXPDEF | SP_DEF | SP_SCALE;
part.rotAdd = (GetRandomControl() & 0xF) + 16;
}
}
else
{
part.flags = SP_EXPDEF | SP_DEF | SP_SCALE;
}
spark->scalar = 1;
part.scalar = 1;
int size = (GetRandomControl() & 63) + 72;
if (hit)
if (isHit)
{
size >>= 1;
spark->size = spark->sSize = size >> 2;
spark->gravity = spark->maxYvel = 0;
size /= 2;
part.size =
part.sSize = size *= 4;
part.gravity = part.maxYvel = 0;
}
else
{
spark->size = spark->sSize = size >> 4;
spark->gravity = -(GetRandomControl() & 3) - 4;
spark->maxYvel = -(GetRandomControl() & 3) - 4;
part.size = part.sSize = size >> 4;
part.gravity = -(GetRandomControl() & 3) - 4;
part.maxYvel = -(GetRandomControl() & 3) - 4;
}
spark->dSize = size;
part.dSize = size;
}
}

View file

@ -2,7 +2,9 @@
namespace TEN::Entities::Traps
{
void DartControl(short itemNumber);
void DartEmitterControl(short itemNumber);
void TriggerDartSmoke(int x, int y, int z, int xv, int zv, bool hit);
void InitializeDartEmitter(short itemNumber);
void ControlDart(short itemNumber);
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)
{
object->collision = ObjectCollision;
object->control = DartControl;
object->control = ControlDart;
object->shadowType = ShadowMode::All;
}
object = &Objects[ID_DART_EMITTER];
if (object->loaded)
{
object->control = DartEmitterControl;
object->Initialize = InitializeDartEmitter;
object->control = ControlDartEmitter;
object->drawRoutine = nullptr;
object->usingDrawAnimatingItem = false;
}
@ -362,7 +363,8 @@ void StartTraps(ObjectInfo* object)
object = &Objects[ID_HOMING_DART_EMITTER];
if (object->loaded)
{
object->control = DartEmitterControl;
object->Initialize = InitializeDartEmitter;
object->control = ControlDartEmitter;
object->drawRoutine = nullptr;
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