mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
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:
commit
3a345e9ccb
254 changed files with 6549 additions and 5267 deletions
|
@ -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
|
||||
|
|
49
CHANGELOG.md
49
CHANGELOG.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
21
LICENSE
Normal 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.
|
|
@ -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
|
||||
|
|
|
@ -58,7 +58,7 @@ local strings =
|
|||
|
||||
-- Level name strings
|
||||
|
||||
lara_home = { "Lara's Home" },
|
||||
home_level = { "Home Level" },
|
||||
test_level = { "Test Level" },
|
||||
title = { "Title" },
|
||||
}
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -17,6 +17,7 @@ enum class GameStatus
|
|||
{
|
||||
Normal,
|
||||
NewGame,
|
||||
HomeLevel,
|
||||
LoadGame,
|
||||
SaveGame,
|
||||
ExitToTitle,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
111
TombEngine/Math/Utils.cpp
Normal 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
32
TombEngine/Math/Utils.h
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
182
TombEngine/Objects/TR1/Entity/Centaur.cpp
Normal file
182
TombEngine/Objects/TR1/Entity/Centaur.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue