Merge branch 'develop' into sezz_x64

This commit is contained in:
Sezz 2023-05-08 21:41:02 +10:00
commit 54c5e0c70d
504 changed files with 11610 additions and 9032 deletions

1
.gitignore vendored
View file

@ -8,6 +8,7 @@ TombEngine/Legacy Engine Objects
x64/ x64/
packages/ packages/
.vs/ .vs/
.vsconfig
*.dll *.dll
*.dmp *.dmp
*.id0 *.id0

View file

@ -1,21 +1,61 @@
Version 1.0.9
=============
* Fix cold bar triggered in non-water rooms.
* Fix spiky wall speed value and change it via OCB number or Lua (Moveable::SetItemFlags[0]).
* Fix bats emitter crashing the game if little beetle object does not exist in wad.
* Fix gunflash rendering and position for entities.
* Fix snowmobile driver crashing the game.
* Fix knifethrower not throwing knife.
* Fix footprints not being cleared after level change.
* Fix thumbstick camera option sometimes producing jerky camera movements during object interaction.
* Fix inventory input interference when entering inventory via puzzle.
* Fix gamepad still vibrating if Lara was poisoned prior to death.
* Add TR1 cowboy.
* Add TR3 wall mounted blade.
* Add TR3 claw mutant.
* Add removable puzzles from puzzle holes and puzzle dones.
- Employed by setting the trigger type as "Switch" for either puzzle hole or puzzle done.
- Can be mixed with puzzle done and puzzle holes of the same or different type.
* Add reusable keys for key holes.
- Employed by setting the trigger type as "Switch" for key hole.
* Allow key hole animation to be played via OCB number.
- Default OCB 0 will play Lara use key animation.
- Any positive OCB number will play the animation according to the OCB number.
* Add missing gunflash for some entities, also include dynamic light and smoke to all gunflashes.
* Add log reports if title level or other levels don't exist.
* Add better error handling for missing font, sprites or shaders.
* Add "Reset to defaults" entry to controls menu and automatically bind XBOX gamepad profile if connected.
Lua API changes:
* Add class Vec2
* Add function String::SetTranslated()
* Add function Misc::IsStringDisplaying()
* Add the following for use in AddCallback and RemoveCallback:
- PRESTART, POSTSTART
- PREEND, POSTEND
- PRESAVE, POSTSAVE
- PRELOAD, POSTLOAD
Version 1.0.8 Version 1.0.8
============= =============
* Fix bubbles phasing through ceilings. * Fix bubbles phasing through ceilings.
* Fix object camera not clearing at level end. * Fix object camera not clearing at level end.
* Fix double breathing sound effect when coming up for air. * Fix double breath sound effect when coming up for air.
* Fix twisting hair bug. * Fix flickering hair.
* Fix harpoon gun triggering water and dry sounds when shooting and reholstering. * Fix harpoon gun triggering water and dry sounds when shooting and reholstering.
* Fix Z-fighting in inventory rendering. * Fix Z-fighting in inventory rendering.
* Fix transparent objects not displaying correctly in the Inventory. * Fix transparent objects not displaying correctly in the Inventory.
* Fix dozy cheat always giving uzi weapons even if not present in WAD. * Fix dozy cheat always giving uzi weapons even if not present in WAD.
* Fix player getting launched when landing close to an edge. * Fix player getting launched when landing close to an edge.
* Fix player going through trapdoor/bridge while climbing up a climbable wall.
* Fix TR3 Sophia's charge ring drawing below floor. * Fix TR3 Sophia's charge ring drawing below floor.
* Fix TR5 imp collision handling and animations. * Fix TR5 imp collision handling and animations:
- OCB 1 - Climbs up to Lara when triggered. - OCB 1: Climbs up to player when triggered.
- OCB 2 - Starts of playfully rolling on the floor when triggered. - OCB 2: Starts rolling on the floor when triggered.
- OCB 3 - Will throw stones at Lara. - OCB 3: Will throw stones at player.
- Imp is also scared of Lara if she has a lit torch in her hand. - Imp is also scared of of the player if holding a lit torch.
- Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr5_Imp.wad2 - Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr5_Imp.wad2
* Fix and improve wraith tails. * Fix and improve wraith tails.
* Add dedicated WRAITH_TRAP object with enhanced effects. * Add dedicated WRAITH_TRAP object with enhanced effects.
@ -24,22 +64,23 @@ Version 1.0.8
* Add TR1 slamming doors. * Add TR1 slamming doors.
* Add TR3 mutant wasp (AI_MODIFY object won't allow it to land, the wasp will always fly). * Add TR3 mutant wasp (AI_MODIFY object won't allow it to land, the wasp will always fly).
* Add TR3 Corpse * Add TR3 Corpse
- OCB 0: used for coprses targeted by the compsoganathus dinosaur. - OCB 0: used for coprses targeted by the compsognathus dinosaur.
- OCB 1: used for corpses hung in the air to be used as piranha bait. Will fall when shot. - OCB 1: used for corpses hung in the air to be used as piranha bait. Will fall when shot.
- Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr3_Compsognathus_Cadavar.wad2 - Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr3_Compsognathus_Cadavar.wad2
* Add cold exposure bar (employed by setting the "cold" flag in water rooms in Tomb Editor). * Add cold exposure bar (employed by setting the "cold" flag in water rooms in Tomb Editor).
* Add water wakes for vehicles. * Add water wakes for vehicles.
* Allow dynamic segment count of hair object. * Restored light effect nullmeshes (color, electrical, pulse, and strobe):
* Restored light effect nullmeshes (color, electrical, pulse and strobe): - Select the light color as object tint in the OCB menu in Tomb Editor.
- Select the color the light should emit as object tint in OCB menu in Tomb editor.
- ELECTRICAL_LIGHT: - ELECTRICAL_LIGHT:
- OCB 1: Displays mesh of object. - Can have multiple meshes. Add mesh number to OCB to be renderd with the light.
- OCB -1: Hides mesh of object (used for a neon light effect) - OCB + (mesh number): Light behaves like a neon light.
- OCB (mesh number): Light flickers.
* Restored inventory compass. * Restored inventory compass.
* Allow dynamic segment count for hair object.
Lua API changes: Lua API changes:
* Add new function Misc::IsSoundPlaying() * Add function Misc::IsSoundPlaying()
* Add function DisplayString::SetFlags()
Version 1.0.7 Version 1.0.7
============= =============

View file

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

View file

@ -128,6 +128,10 @@ when you need to use screen-space coordinates.</p>
<td class="name" ><a href="#DisplayString:GetPosition">DisplayString:GetPosition()</a></td> <td class="name" ><a href="#DisplayString:GetPosition">DisplayString:GetPosition()</a></td>
<td class="summary">Get the position of the string.</td> <td class="summary">Get the position of the string.</td>
</tr> </tr>
<tr>
<td class="name" ><a href="#DisplayString:SetFlags">DisplayString:SetFlags(table)</a></td>
<td class="summary">Set the display string's flags</td>
</tr>
</table> </table>
<br/> <br/>
@ -250,7 +254,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
<h3>Returns:</h3> <h3>Returns:</h3>
<ol> <ol>
<span class="types"><span class="type">String</span></span> <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
a string a string
</ol> </ol>
@ -272,7 +276,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
<h3>Parameters:</h3> <h3>Parameters:</h3>
<ul> <ul>
<li><span class="parameter">string</span> <li><span class="parameter">string</span>
<span class="types"><span class="type">String</span></span> <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
the new key for the display string the new key for the display string
</li> </li>
</ul> </ul>
@ -333,6 +337,38 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
</dd>
<dt>
<a name = "DisplayString:SetFlags"></a>
<strong>DisplayString:SetFlags(table)</strong>
</dt>
<dd>
Set the display string's flags
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">table</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.6">table</a></span>
the new table with display flags options
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example"><span class="keyword">local</span> varDisplayString = DisplayString(<span class="string">'example string'</span>, <span class="number">0</span>, <span class="number">0</span>, Color(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>), <span class="keyword">false</span>)
varDisplayString:SetFlags({})
varDisplayString:SetFlags({ TEN.Strings.DisplayStringOption.SHADOW })
varDisplayString:SetFlags({ TEN.Strings.DisplayStringOption.CENTER })
varDisplayString:SetFlags({ TEN.Strings.DisplayStringOption.SHADOW, TEN.Strings.DisplayStringOption.CENTER })
<span class="comment">-- When passing a table to a function, you can omit the parentheses
</span>varDisplayString:SetFlags{ TEN.Strings.DisplayStringOption.CENTER }</pre>
</ul>
</dd> </dd>
</dl> </dl>
@ -341,7 +377,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
</div> <!-- id="main" --> </div> <!-- id="main" -->
<div id="about"> <div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i> <i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-03-31 20:44:31 </i> <i style="float:right;">Last updated 2023-04-11 21:28:36 </i>
</div> <!-- id="about" --> </div> <!-- id="about" -->
</div> <!-- id="container" --> </div> <!-- id="container" -->
</body> </body>

View file

@ -0,0 +1,265 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>TombEngine 1.0.8 Lua API</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>TombEngine</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>1 Modules</h2>
<ul class="nowrap">
<li> <a href="../1 modules/Effects.html">Effects</a></li>
<li> <a href="../1 modules/Flow.html">Flow</a></li>
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
<li> <a href="../1 modules/Logic.html">Logic</a></li>
<li> <a href="../1 modules/Misc.html">Misc</a></li>
<li> <a href="../1 modules/Objects.html">Objects</a></li>
<li> <a href="../1 modules/Strings.html">Strings</a></li>
</ul>
<h2>2 Classes</h2>
<ul class="nowrap">
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
</ul>
<h2>3 Primitive Classes</h2>
<ul class="nowrap">
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
<li> <here>Vec2</here></li>
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
</ul>
<h2>4 Enums</h2>
<ul class="nowrap">
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
<li> <a href="../4 enums/Misc.ActionID.html">Misc.ActionID</a></li>
<li> <a href="../4 enums/Misc.CameraType.html">Misc.CameraType</a></li>
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
</ul>
<h2>5 Lua utility modules</h2>
<ul class="nowrap">
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
</ul>
</div>
<div id="content">
<h1>Primitive Class <code>Vec2</code></h1>
<p>Represents a 2D vector.</p>
<p>
</p>
<h2><a href="#Members">Members</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#x">x</a></td>
<td class="summary">(int) x coordinate</td>
</tr>
<tr>
<td class="name" ><a href="#y">y</a></td>
<td class="summary">(int) y coordinate</td>
</tr>
</table>
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#Vec3:GetNormalised">Vec3:GetNormalised(length)</a></td>
<td class="summary">Modify this vector so that it becomes close to the requested length.</td>
</tr>
<tr>
<td class="name" ><a href="#Vec2">Vec2(X, Y)</a></td>
<td class="summary">
</td>
</tr>
<tr>
<td class="name" ><a href="#__tostring">__tostring(Vec2)</a></td>
<td class="summary">Metafunction; use tostring(myVector)</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header "><a name="Members"></a>Members</h2>
<dl class="function">
<dt>
<a name = "x"></a>
<strong>x</strong>
</dt>
<dd>
(int) x coordinate
</dd>
<dt>
<a name = "y"></a>
<strong>y</strong>
</dt>
<dd>
(int) y coordinate
</dd>
</dl>
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "Vec3:GetNormalised"></a>
<strong>Vec3:GetNormalised(length)</strong>
</dt>
<dd>
Modify this vector so that it becomes close to the requested length. </p>
<p>Note that since the engine uses integers instead of floating-point
numbers, this will be less accurate at smaller lengths.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">length</span>
<span class="types"><span class="type">int</span></span>
the new length to set the vector to.
</li>
</ul>
</dd>
<dt>
<a name = "Vec2"></a>
<strong>Vec2(X, Y)</strong>
</dt>
<dd>
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">X</span>
<span class="types"><span class="type">int</span></span>
x coordinate
</li>
<li><span class="parameter">Y</span>
<span class="types"><span class="type">int</span></span>
y coordinate
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
A Vec2 object.
</ol>
</dd>
<dt>
<a name = "__tostring"></a>
<strong>__tostring(Vec2)</strong>
</dt>
<dd>
Metafunction; use tostring(myVector)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">Vec2</span>
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
this Vec2
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
A string showing the x, y, and z values of the Vec2
</ol>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
<i style="float:right;">Last updated 2023-05-02 19:28:45 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View file

@ -3825,7 +3825,7 @@ __Default: empty__</description>
If false or omitted, this will be the string that's displayed.</description> If false or omitted, this will be the string that's displayed.</description>
<returns> <returns>
<return> <return>
<type>String</type> <type>string</type>
<description>a string</description> <description>a string</description>
</return> </return>
</returns> </returns>
@ -3842,7 +3842,7 @@ __Default: empty__</description>
<parameters> <parameters>
<parameter> <parameter>
<name>string</name> <name>string</name>
<type>String</type> <type>string</type>
<description>the new key for the display string</description> <description>the new key for the display string</description>
</parameter> </parameter>
</parameters> </parameters>
@ -3886,6 +3886,20 @@ __Default: empty__</description>
</returns> </returns>
</function> </function>
<function>
<module>Strings.DisplayString</module>
<caller>DisplayString</caller>
<name>SetFlags</name>
<summary>Set the display string's flags</summary>
<parameters>
<parameter>
<name>table</name>
<type>table</type>
<description>the new table with display flags options</description>
</parameter>
</parameters>
</function>
<function> <function>
<module>Strings</module> <module>Strings</module>
<name>ShowString</name> <name>ShowString</name>

View file

@ -277,7 +277,7 @@ Timer = {
--- Get the total time for a timer. --- Get the total time for a timer.
-- This is the amount of time the timer will start with, as well as when starting a new loop -- This is the amount of time the timer will start with, as well as when starting a new loop
-- @function myTimer:GetRemainingTime -- @function myTimer:GetTotalTime
-- @treturn float the timer's total time -- @treturn float the timer's total time
GetTotalTime = function(t) GetTotalTime = function(t)
return LevelVars.Engine.Timer.timers[t.name].totalTime return LevelVars.Engine.Timer.timers[t.name].totalTime

View file

@ -347,8 +347,8 @@ namespace TEN::Gui
if (lara->Control.HandStatus != HandStatus::Free && if (lara->Control.HandStatus != HandStatus::Free &&
lara->Control.Weapon.GunType == LaraWeaponType::Revolver) lara->Control.Weapon.GunType == LaraWeaponType::Revolver)
{ {
UndrawPistolMeshRight(item, LaraWeaponType::Revolver); UndrawPistolMesh(*item, LaraWeaponType::Revolver, true);
DrawPistolMeshes(item, LaraWeaponType::Revolver); DrawPistolMeshes(*item, LaraWeaponType::Revolver);
} }
} }
@ -370,8 +370,8 @@ namespace TEN::Gui
if (lara->Control.HandStatus != HandStatus::Free && if (lara->Control.HandStatus != HandStatus::Free &&
lara->Control.Weapon.GunType == LaraWeaponType::Crossbow) lara->Control.Weapon.GunType == LaraWeaponType::Crossbow)
{ {
UndrawShotgunMeshes(item, LaraWeaponType::Crossbow); UndrawShotgunMeshes(*item, LaraWeaponType::Crossbow);
DrawShotgunMeshes(item, LaraWeaponType::Crossbow); DrawShotgunMeshes(*item, LaraWeaponType::Crossbow);
} }
} }
@ -393,8 +393,8 @@ namespace TEN::Gui
if (lara->Control.HandStatus != HandStatus::Free && if (lara->Control.HandStatus != HandStatus::Free &&
lara->Control.Weapon.GunType == LaraWeaponType::HK) lara->Control.Weapon.GunType == LaraWeaponType::HK)
{ {
UndrawShotgunMeshes(item, LaraWeaponType::HK); UndrawShotgunMeshes(*item, LaraWeaponType::HK);
DrawShotgunMeshes(item, LaraWeaponType::HK); DrawShotgunMeshes(*item, LaraWeaponType::HK);
} }
} }

View file

@ -11,21 +11,21 @@ namespace TEN::Hud
{ {
HudController g_Hud = {}; HudController g_Hud = {};
void HudController::Update(ItemInfo& item) void HudController::Update(const ItemInfo& item)
{ {
this->PickupSummary.Update(); PickupSummary.Update();
this->StatusBars.Update(item); StatusBars.Update(item);
} }
void HudController::Draw(ItemInfo& item) const void HudController::Draw(const ItemInfo& item) const
{ {
this->PickupSummary.Draw(); PickupSummary.Draw();
this->StatusBars.Draw(item); StatusBars.Draw(item);
} }
void HudController::Clear() void HudController::Clear()
{ {
this->PickupSummary.Clear(); PickupSummary.Clear();
this->StatusBars.Clear(); StatusBars.Clear();
} }
} }

View file

@ -9,13 +9,13 @@ namespace TEN::Hud
class HudController class HudController
{ {
public: public:
// Components // Members
StatusBarsController StatusBars = {}; StatusBarsController StatusBars = {};
PickupSummaryController PickupSummary = {}; PickupSummaryController PickupSummary = {};
// Utilities // Utilities
void Update(ItemInfo& item); void Update(const ItemInfo& item);
void Draw(ItemInfo& item) const; void Draw(const ItemInfo& item) const;
void Clear(); void Clear();
}; };

View file

@ -12,22 +12,50 @@
using namespace TEN::Renderer; using namespace TEN::Renderer;
extern TEN::Renderer::RendererHudBar* g_AirBar; extern RendererHudBar* g_AirBar;
extern TEN::Renderer::RendererHudBar* g_ExposureBar; extern RendererHudBar* g_ExposureBar;
extern TEN::Renderer::RendererHudBar* g_HealthBar; extern RendererHudBar* g_HealthBar;
extern TEN::Renderer::RendererHudBar* g_StaminaBar; extern RendererHudBar* g_StaminaBar;
namespace TEN::Hud namespace TEN::Hud
{ {
void StatusBar::Initialize(float value)
{
Value =
TargetValue = std::clamp(value, 0.0f, 1.0f);
}
void StatusBar::Update(float value)
{
constexpr auto LIFE_START_FADING = 0.2f;
constexpr auto LERP_ALPHA = 0.3f;
// Update life.
if (Life > 0.0f)
Life -= 1.0f;
// Update opacity.
float alpha = std::clamp(Life, 0.0f, LIFE_START_FADING) / LIFE_START_FADING;
Opacity = Lerp(0.0f, 1.0f, alpha);
// Update target value.
TargetValue = std::clamp(value, 0.0f, 1.0f);
// Update value.
Value = Lerp(Value, TargetValue, LERP_ALPHA);
if (abs(Value - TargetValue) <= EPSILON)
Value = TargetValue;
}
void StatusBarsController::Initialize(const ItemInfo& item) void StatusBarsController::Initialize(const ItemInfo& item)
{ {
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
// Initialize bar values. // Initialize bar values.
InitializeStatusBar(AirBar, player.Status.Air, LARA_AIR_MAX); AirBar.Initialize(player.Status.Air / LARA_AIR_MAX);
InitializeStatusBar(ExposureBar, player.Status.Exposure, LARA_EXPOSURE_MAX); ExposureBar.Initialize(player.Status.Exposure / LARA_EXPOSURE_MAX);
InitializeStatusBar(HealthBar, item.HitPoints, LARA_HEALTH_MAX); HealthBar.Initialize(item.HitPoints / LARA_HEALTH_MAX);
InitializeStatusBar(StaminaBar, player.Status.Stamina, LARA_STAMINA_MAX); StaminaBar.Initialize(player.Status.Stamina / LARA_STAMINA_MAX);
} }
void StatusBarsController::Update(const ItemInfo& item) void StatusBarsController::Update(const ItemInfo& item)
@ -66,52 +94,26 @@ namespace TEN::Hud
*this = {}; *this = {};
} }
void StatusBarsController::InitializeStatusBar(StatusBar& bar, float statusValue, float statusValueMax)
{
float statusValueNorm = std::clamp(statusValue, 0.0f, statusValueMax);
bar.Value = statusValueNorm / statusValueMax;
}
void StatusBarsController::UpdateStatusBar(StatusBar& bar, float statusValue, float statusValueMax)
{
// Update life.
if (bar.Life > 0.0f)
bar.Life -= 1.0f;
// Update opacity.
float alpha = std::clamp(bar.Life, 0.0f, STATUS_BAR_LIFE_START_FADING) / STATUS_BAR_LIFE_START_FADING;
bar.Opacity = Lerp(0.0f, 1.0f, alpha);
// Update target value.
float statusValueNorm = std::clamp(statusValue, 0.0f, statusValueMax);
bar.TargetValue = statusValueNorm / statusValueMax;
// Update value.
bar.Value = Lerp(bar.Value, bar.TargetValue, STATUS_BAR_VALUE_LERP_ALPHA);
if (abs(bar.Value - bar.TargetValue) <= EPSILON)
bar.Value = bar.TargetValue;
}
void StatusBarsController::UpdateAirBar(const ItemInfo& item) void StatusBarsController::UpdateAirBar(const ItemInfo& item)
{ {
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
// Update generic data. // Update generic data.
UpdateStatusBar(AirBar, player.Status.Air, LARA_AIR_MAX); AirBar.Update(player.Status.Air / LARA_AIR_MAX);
// Update life. // Update life.
if (AirBar.Value != AirBar.TargetValue || if (AirBar.Value != AirBar.TargetValue ||
player.Control.WaterStatus == WaterStatus::Underwater) player.Control.WaterStatus == WaterStatus::Underwater)
{ {
AirBar.Life = round(STATUS_BAR_LIFE_MAX * FPS); AirBar.Life = round(StatusBar::LIFE_MAX * FPS);
} }
// HACK: Special case for UPV as it sets player.Control.WaterStatus to WaterStatus::Dry. // HACK: Special case for UPV as it sets player.Control.WaterStatus to WaterStatus::Dry.
if (player.Vehicle != NO_ITEM) if (player.Context.Vehicle != NO_ITEM)
{ {
const auto& vehicleItem = g_Level.Items[player.Vehicle]; const auto& vehicleItem = g_Level.Items[player.Context.Vehicle];
if (vehicleItem.ObjectNumber == ID_UPV) if (vehicleItem.ObjectNumber == ID_UPV)
AirBar.Life = round(STATUS_BAR_LIFE_MAX * FPS); AirBar.Life = round(StatusBar::LIFE_MAX * FPS);
} }
} }
@ -120,13 +122,13 @@ namespace TEN::Hud
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
// Update generic data. // Update generic data.
UpdateStatusBar(ExposureBar, player.Status.Exposure, LARA_EXPOSURE_MAX); ExposureBar.Update(player.Status.Exposure / LARA_EXPOSURE_MAX);
// Update life. // Update life.
if (ExposureBar.Value != ExposureBar.TargetValue || if (ExposureBar.Value != ExposureBar.TargetValue ||
(TestEnvironment(ENV_FLAG_WATER, item.RoomNumber) && TestEnvironment(ENV_FLAG_COLD, item.RoomNumber))) (TestEnvironment(ENV_FLAG_WATER, item.RoomNumber) && TestEnvironment(ENV_FLAG_COLD, item.RoomNumber)))
{ {
ExposureBar.Life = round(STATUS_BAR_LIFE_MAX * FPS); ExposureBar.Life = round(StatusBar::LIFE_MAX * FPS);
} }
} }
@ -135,7 +137,7 @@ namespace TEN::Hud
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
// Update generic data. // Update generic data.
UpdateStatusBar(HealthBar, item.HitPoints, LARA_HEALTH_MAX); HealthBar.Update(item.HitPoints / LARA_HEALTH_MAX);
// Update life. // Update life.
if (HealthBar.Value != HealthBar.TargetValue || if (HealthBar.Value != HealthBar.TargetValue ||
@ -145,7 +147,7 @@ namespace TEN::Hud
(player.Control.HandStatus == HandStatus::WeaponReady && (player.Control.HandStatus == HandStatus::WeaponReady &&
player.Control.Weapon.GunType != LaraWeaponType::Torch)) // HACK: Exclude torch. player.Control.Weapon.GunType != LaraWeaponType::Torch)) // HACK: Exclude torch.
{ {
HealthBar.Life = round(STATUS_BAR_LIFE_MAX * FPS); HealthBar.Life = round(StatusBar::LIFE_MAX * FPS);
} }
// Special case for weapon undraw. // Special case for weapon undraw.
@ -153,7 +155,7 @@ namespace TEN::Hud
item.HitPoints > LARA_HEALTH_CRITICAL && player.Status.Poison == 0 && item.HitPoints > LARA_HEALTH_CRITICAL && player.Status.Poison == 0 &&
player.Control.HandStatus == HandStatus::WeaponUndraw) player.Control.HandStatus == HandStatus::WeaponUndraw)
{ {
HealthBar.Life = 0;// round(STATUS_BAR_LIFE_START_FADING * FPS); HealthBar.Life = 0.0f;
} }
} }
@ -162,13 +164,13 @@ namespace TEN::Hud
const auto& player = GetLaraInfo(item); const auto& player = GetLaraInfo(item);
// Update generic data. // Update generic data.
UpdateStatusBar(StaminaBar, player.Status.Stamina, LARA_STAMINA_MAX); StaminaBar.Update(player.Status.Stamina / LARA_STAMINA_MAX);
// Update life. // Update life.
if (StaminaBar.Value != StaminaBar.TargetValue || if (StaminaBar.Value != StaminaBar.TargetValue ||
StaminaBar.Value != 1.0f) StaminaBar.Value != 1.0f)
{ {
StaminaBar.Life = round(STATUS_BAR_LIFE_MAX * FPS); StaminaBar.Life = round(StatusBar::LIFE_MAX * FPS);
} }
} }

View file

@ -2,30 +2,28 @@
enum GAME_OBJECT_ID : short; enum GAME_OBJECT_ID : short;
struct ItemInfo; struct ItemInfo;
namespace TEN::Renderer { struct RendererHudBar; }
namespace TEN::Renderer using namespace TEN::Renderer;
{
struct RendererHudBar;
}
namespace TEN::Hud namespace TEN::Hud
{ {
struct StatusBar struct StatusBar
{ {
static constexpr auto LIFE_MAX = 0.75f;
float Value = 0.0f; float Value = 0.0f;
float TargetValue = 0.0f; float TargetValue = 0.0f;
float Life = 0.0f; float Life = 0.0f;
float Opacity = 0.0f; // TODO: Opacity in renderer. float Opacity = 0.0f; // TODO: Opacity in renderer.
void Initialize(float value);
void Update(float value);
}; };
class StatusBarsController class StatusBarsController
{ {
private: private:
// Constants
static constexpr auto STATUS_BAR_LIFE_MAX = 0.75f;
static constexpr auto STATUS_BAR_LIFE_START_FADING = 0.2f;
static constexpr auto STATUS_BAR_VALUE_LERP_ALPHA = 0.3f;
// Members // Members
StatusBar AirBar = {}; StatusBar AirBar = {};
StatusBar ExposureBar = {}; StatusBar ExposureBar = {};
@ -42,18 +40,14 @@ namespace TEN::Hud
void Clear(); void Clear();
private: private:
// Initializer helpers
void InitializeStatusBar(StatusBar& bar, float statusValue, float statusValueMax);
// Update helpers // Update helpers
void UpdateStatusBar(StatusBar& bar, float statusValue, float statusValueMax);
void UpdateAirBar(const ItemInfo& item); void UpdateAirBar(const ItemInfo& item);
void UpdateExposureBar(const ItemInfo& item); void UpdateExposureBar(const ItemInfo& item);
void UpdateHealthBar(const ItemInfo& item); void UpdateHealthBar(const ItemInfo& item);
void UpdateStaminaBar(const ItemInfo& item); void UpdateStaminaBar(const ItemInfo& item);
// Draw helpers // Draw helpers
void DrawStatusBar(float value, float criticalValue, const TEN::Renderer::RendererHudBar& rHudBar, GAME_OBJECT_ID textureID, int frame, bool isPoisoned) const; void DrawStatusBar(float value, float criticalValue, const RendererHudBar& rHudBar, GAME_OBJECT_ID textureID, int frame, bool isPoisoned) const;
void DrawAirBar() const; void DrawAirBar() const;
void DrawExposureBar() const; void DrawExposureBar() const;
void DrawHealthBar(bool isPoisoned) const; void DrawHealthBar(bool isPoisoned) const;

View file

@ -42,7 +42,7 @@
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
using namespace TEN::Effects::Hair; using namespace TEN::Effects::Hair;
using namespace TEN::Effects::Items; using namespace TEN::Effects::Items;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
@ -232,6 +232,22 @@ std::function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1] =
lara_as_crouch_turn_180,//171 lara_as_crouch_turn_180,//171
lara_as_crawl_turn_180,//172 lara_as_crawl_turn_180,//172
lara_as_turn_180,//173 lara_as_turn_180,//173
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_null,
lara_as_use_puzzle,//189
}; };
std::function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1] = std::function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1] =
@ -410,6 +426,22 @@ std::function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1]
lara_col_crouch_turn_180,//171 lara_col_crouch_turn_180,//171
lara_col_crawl_turn_180,//172 lara_col_crawl_turn_180,//172
lara_col_turn_180,//173 lara_col_turn_180,//173
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_void_func,
lara_default_col,//189
}; };
void LaraControl(ItemInfo* item, CollisionInfo* coll) void LaraControl(ItemInfo* item, CollisionInfo* coll)
@ -479,12 +511,12 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
heightFromWater = item->Pose.Position.y - waterHeight; heightFromWater = item->Pose.Position.y - waterHeight;
else else
heightFromWater = NO_HEIGHT; heightFromWater = NO_HEIGHT;
lara->WaterSurfaceDist = -heightFromWater; lara->Context.WaterSurfaceDist = -heightFromWater;
if (lara->Vehicle == NO_ITEM) if (lara->Context.Vehicle == NO_ITEM)
WadeSplash(item, waterHeight, waterDepth); WadeSplash(item, waterHeight, waterDepth);
if (lara->Vehicle == NO_ITEM && lara->ExtraAnim == NO_ITEM) if (lara->Context.Vehicle == NO_ITEM && lara->ExtraAnim == NO_ITEM)
{ {
switch (lara->Control.WaterStatus) switch (lara->Control.WaterStatus)
{ {
@ -534,7 +566,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
item->Pose.Orientation.x = -ANGLE(45.0f); item->Pose.Orientation.x = -ANGLE(45.0f);
} }
ResetLaraFlex(item); ResetPlayerFlex(item);
Splash(item); Splash(item);
} }
} }
@ -591,8 +623,8 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
if (waterDepth == NO_HEIGHT || abs(heightFromWater) >= CLICK(1)) if (waterDepth == NO_HEIGHT || abs(heightFromWater) >= CLICK(1))
{ {
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);
ResetLaraLean(item); ResetPlayerLean(item);
ResetLaraFlex(item); ResetPlayerFlex(item);
item->Animation.IsAirborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity.z = item->Animation.Velocity.y; item->Animation.Velocity.z = item->Animation.Velocity.y;
item->Animation.Velocity.y = 0.0f; item->Animation.Velocity.y = 0.0f;
@ -601,8 +633,8 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
else else
{ {
SetAnimation(item, LA_UNDERWATER_RESURFACE); SetAnimation(item, LA_UNDERWATER_RESURFACE);
ResetLaraLean(item); ResetPlayerLean(item);
ResetLaraFlex(item); ResetPlayerFlex(item);
item->Animation.Velocity.y = 0.0f; item->Animation.Velocity.y = 0.0f;
item->Pose.Position.y = waterHeight; item->Pose.Position.y = waterHeight;
lara->Control.WaterStatus = WaterStatus::TreadWater; lara->Control.WaterStatus = WaterStatus::TreadWater;
@ -614,8 +646,8 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
else else
{ {
SetAnimation(item, LA_UNDERWATER_RESURFACE); SetAnimation(item, LA_UNDERWATER_RESURFACE);
ResetLaraLean(item); ResetPlayerLean(item);
ResetLaraFlex(item); ResetPlayerFlex(item);
item->Animation.Velocity.y = 0.0f; item->Animation.Velocity.y = 0.0f;
item->Pose.Position.y = waterHeight + 1; item->Pose.Position.y = waterHeight + 1;
lara->Control.WaterStatus = WaterStatus::TreadWater; lara->Control.WaterStatus = WaterStatus::TreadWater;
@ -641,8 +673,8 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
lara->Control.WaterStatus = WaterStatus::Wade; lara->Control.WaterStatus = WaterStatus::Wade;
} }
ResetLaraLean(item); ResetPlayerLean(item);
ResetLaraFlex(item); ResetPlayerFlex(item);
item->Animation.Velocity.y = 0.0f; item->Animation.Velocity.y = 0.0f;
} }
@ -656,8 +688,8 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
if (heightFromWater > SWIM_DEPTH && !isSwamp) if (heightFromWater > SWIM_DEPTH && !isSwamp)
{ {
SetAnimation(item, LA_ONWATER_IDLE); SetAnimation(item, LA_ONWATER_IDLE);
ResetLaraLean(item); ResetPlayerLean(item);
ResetLaraFlex(item); ResetPlayerFlex(item);
item->Animation.IsAirborne = false; item->Animation.IsAirborne = false;
item->Animation.Velocity.y = 0.0f; item->Animation.Velocity.y = 0.0f;
item->Pose.Position.y += 1 - heightFromWater; item->Pose.Position.y += 1 - heightFromWater;
@ -702,7 +734,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
{ {
case WaterStatus::Dry: case WaterStatus::Dry:
case WaterStatus::Wade: case WaterStatus::Wade:
if (isSwamp && lara->WaterSurfaceDist < -(LARA_HEIGHT + 8)) // TODO: Find best height. @Sezz 2021.11.10 if (isSwamp && lara->Context.WaterSurfaceDist < -(LARA_HEIGHT + 8)) // TODO: Find best height. @Sezz 2021.11.10
{ {
if (item->HitPoints >= 0) if (item->HitPoints >= 0)
{ {
@ -717,7 +749,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
else if (lara->Status.Air < LARA_AIR_MAX && item->HitPoints >= 0) else if (lara->Status.Air < LARA_AIR_MAX && item->HitPoints >= 0)
{ {
// HACK: Special case for UPV. // HACK: Special case for UPV.
if (lara->Vehicle == NO_ITEM) if (lara->Context.Vehicle == NO_ITEM)
{ {
lara->Status.Air += 10; lara->Status.Air += 10;
if (lara->Status.Air > LARA_AIR_MAX) if (lara->Status.Air > LARA_AIR_MAX)
@ -730,16 +762,14 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
if (lara->Control.WaterStatus == WaterStatus::Dry) if (lara->Control.WaterStatus == WaterStatus::Dry)
{ {
// HACK: Special case for UPV. // HACK: Special case for UPV.
if (lara->Vehicle != NO_ITEM) if (lara->Context.Vehicle != NO_ITEM)
{ {
auto& vehicleItem = g_Level.Items[lara->Vehicle]; auto& vehicleItem = g_Level.Items[lara->Context.Vehicle];
if (vehicleItem.ObjectNumber == ID_UPV) if (vehicleItem.ObjectNumber == ID_UPV)
{ {
auto pointColl = GetCollision(item, 0, 0, CLICK(1)); auto pointColl = GetCollision(item, 0, 0, CLICK(1));
isCold = isCold || TestEnvironment(ENV_FLAG_COLD, pointColl.RoomNumber);
}
}
isCold = isCold || TestEnvironment(ENV_FLAG_COLD, pointColl.RoomNumber);
if (isCold) if (isCold)
{ {
lara->Status.Exposure--; lara->Status.Exposure--;
@ -749,6 +779,8 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
item->HitPoints -= 10; item->HitPoints -= 10;
} }
} }
}
}
else else
{ {
lara->Status.Exposure++; lara->Status.Exposure++;
@ -853,6 +885,7 @@ void LaraAboveWater(ItemInfo* item, CollisionInfo* coll)
coll->Setup.EnableSpasm = true; coll->Setup.EnableSpasm = true;
coll->Setup.OldPosition = item->Pose.Position; coll->Setup.OldPosition = item->Pose.Position;
coll->Setup.PrevAnimObjectID = item->Animation.AnimObjectID;
coll->Setup.OldAnimNumber = item->Animation.AnimNumber; coll->Setup.OldAnimNumber = item->Animation.AnimNumber;
coll->Setup.OldFrameNumber = item->Animation.FrameNumber; coll->Setup.OldFrameNumber = item->Animation.FrameNumber;
coll->Setup.OldState = item->Animation.ActiveState; coll->Setup.OldState = item->Animation.ActiveState;
@ -889,12 +922,12 @@ void LaraAboveWater(ItemInfo* item, CollisionInfo* coll)
DoObjectCollision(item, coll); DoObjectCollision(item, coll);
// Handle Lara collision. // Handle Lara collision.
if (lara->Vehicle == NO_ITEM) if (lara->Context.Vehicle == NO_ITEM)
lara_collision_routines[item->Animation.ActiveState](item, coll); lara_collision_routines[item->Animation.ActiveState](item, coll);
} }
// Handle weapons. // Handle weapons.
HandleWeapon(item); HandleWeapon(*item);
// Handle breath. // Handle breath.
LaraBreath(item); LaraBreath(item);
@ -957,9 +990,9 @@ void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll)
// Reset lean. // Reset lean.
if (!lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT))) if (!lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT)))
ResetLaraLean(item, 8.0f); ResetPlayerLean(item, 1 / 8.0f);
if (lara->WaterCurrentActive && lara->Control.WaterStatus != WaterStatus::FlyCheat) if (lara->Context.WaterCurrentActive && lara->Control.WaterStatus != WaterStatus::FlyCheat)
LaraWaterCurrent(item, coll); LaraWaterCurrent(item, coll);
AnimateItem(item); AnimateItem(item);
@ -967,12 +1000,12 @@ void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll)
DoObjectCollision(item, coll); DoObjectCollision(item, coll);
if (lara->Vehicle == NO_ITEM) if (lara->Context.Vehicle == NO_ITEM)
lara_collision_routines[item->Animation.ActiveState](item, coll); lara_collision_routines[item->Animation.ActiveState](item, coll);
UpdateLaraRoom(item, LARA_RADIUS); UpdateLaraRoom(item, LARA_RADIUS);
HandleWeapon(item); HandleWeapon(*item);
ProcessSectorFlags(item); ProcessSectorFlags(item);
TestTriggers(item, false); TestTriggers(item, false);
@ -1026,7 +1059,7 @@ void LaraUnderwater(ItemInfo* item, CollisionInfo* coll)
UpdateLaraSubsuitAngles(item); UpdateLaraSubsuitAngles(item);
if (!lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT))) if (!lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT)))
ResetLaraLean(item, 8.0f, true, false); ResetPlayerLean(item, 1 / 8.0f, true, false);
if (item->Pose.Orientation.x < -ANGLE(85.0f)) if (item->Pose.Orientation.x < -ANGLE(85.0f))
item->Pose.Orientation.x = -ANGLE(85.0f); item->Pose.Orientation.x = -ANGLE(85.0f);
@ -1048,7 +1081,7 @@ void LaraUnderwater(ItemInfo* item, CollisionInfo* coll)
item->Pose.Orientation.z = -ANGLE(22.0f); item->Pose.Orientation.z = -ANGLE(22.0f);
} }
if (lara->WaterCurrentActive && lara->Control.WaterStatus != WaterStatus::FlyCheat) if (lara->Context.WaterCurrentActive && lara->Control.WaterStatus != WaterStatus::FlyCheat)
LaraWaterCurrent(item, coll); LaraWaterCurrent(item, coll);
AnimateItem(item); AnimateItem(item);
@ -1056,12 +1089,12 @@ void LaraUnderwater(ItemInfo* item, CollisionInfo* coll)
DoObjectCollision(item, coll); DoObjectCollision(item, coll);
if (/*lara->ExtraAnim == -1 &&*/ lara->Vehicle == NO_ITEM) if (/*lara->ExtraAnim == -1 &&*/ lara->Context.Vehicle == NO_ITEM)
lara_collision_routines[item->Animation.ActiveState](item, coll); lara_collision_routines[item->Animation.ActiveState](item, coll);
UpdateLaraRoom(item, 0); UpdateLaraRoom(item, 0);
HandleWeapon(item); HandleWeapon(*item);
ProcessSectorFlags(item); ProcessSectorFlags(item);
TestTriggers(item, false); TestTriggers(item, false);
@ -1082,10 +1115,10 @@ void LaraCheat(ItemInfo* item, CollisionInfo* coll)
if (TrInput & IN_WALK && !(TrInput & IN_LOOK)) if (TrInput & IN_WALK && !(TrInput & IN_LOOK))
{ {
if (TestEnvironment(ENV_FLAG_WATER, item) || (lara->WaterSurfaceDist > 0 && lara->WaterSurfaceDist != NO_HEIGHT)) if (TestEnvironment(ENV_FLAG_WATER, item) || (lara->Context.WaterSurfaceDist > 0 && lara->Context.WaterSurfaceDist != NO_HEIGHT))
{ {
SetAnimation(item, LA_UNDERWATER_IDLE); SetAnimation(item, LA_UNDERWATER_IDLE);
ResetLaraFlex(item); ResetPlayerFlex(item);
lara->Control.WaterStatus = WaterStatus::Underwater; lara->Control.WaterStatus = WaterStatus::Underwater;
} }
else else
@ -1093,11 +1126,11 @@ void LaraCheat(ItemInfo* item, CollisionInfo* coll)
SetAnimation(item, LA_STAND_SOLID); SetAnimation(item, LA_STAND_SOLID);
item->Pose.Orientation.x = 0; item->Pose.Orientation.x = 0;
item->Pose.Orientation.z = 0; item->Pose.Orientation.z = 0;
ResetLaraFlex(item); ResetPlayerFlex(item);
lara->Control.WaterStatus = WaterStatus::Dry; lara->Control.WaterStatus = WaterStatus::Dry;
} }
InitialiseLaraMeshes(item); InitializeLaraMeshes(item);
item->HitPoints = LARA_HEALTH_MAX; item->HitPoints = LARA_HEALTH_MAX;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
} }

View file

@ -13,10 +13,10 @@
#include "Game/Lara/lara_monkey.h" #include "Game/Lara/lara_monkey.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/pickup/pickup.h" #include "Game/pickup/pickup.h"
#include "Game/Setup.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Flow/ScriptInterfaceFlowHandler.h" #include "Flow/ScriptInterfaceFlowHandler.h"
using namespace TEN::Input; using namespace TEN::Input;
@ -73,7 +73,7 @@ void lara_as_controlled(ItemInfo* item, CollisionInfo* coll)
coll->Setup.EnableSpasm = false; coll->Setup.EnableSpasm = false;
Camera.flags = CF_FOLLOW_CENTER; Camera.flags = CF_FOLLOW_CENTER;
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameEnd - 1) if (item->Animation.FrameNumber == GetAnimData(*item).frameEnd - 1)
{ {
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -103,8 +103,8 @@ void lara_as_vault(ItemInfo* item, CollisionInfo* coll)
coll->Setup.EnableObjectPush = false; coll->Setup.EnableObjectPush = false;
coll->Setup.EnableSpasm = false; coll->Setup.EnableSpasm = false;
EaseOutLaraHeight(item, lara->ProjectedFloorHeight - item->Pose.Position.y); EaseOutLaraHeight(item, lara->Context.ProjectedFloorHeight - item->Pose.Position.y);
item->Pose.Orientation.Lerp(lara->TargetOrientation, 0.4f); item->Pose.Orientation.Lerp(lara->Context.TargetOrientation, 0.4f);
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
} }
@ -119,7 +119,7 @@ void lara_as_auto_jump(ItemInfo* item, CollisionInfo* coll)
coll->Setup.EnableObjectPush = false; coll->Setup.EnableObjectPush = false;
coll->Setup.EnableSpasm = false; coll->Setup.EnableSpasm = false;
item->Pose.Orientation.Lerp(lara->TargetOrientation, 0.4f); item->Pose.Orientation.Lerp(lara->Context.TargetOrientation, 0.4f);
} }
// --------------- // ---------------
@ -220,7 +220,7 @@ void lara_col_walk_forward(ItemInfo* item, CollisionInfo* coll)
if (LaraDeflectEdge(item, coll)) if (LaraDeflectEdge(item, coll))
{ {
item->Animation.TargetState = LS_SOFT_SPLAT; item->Animation.TargetState = LS_SOFT_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Animation.ActiveState = LS_SOFT_SPLAT; item->Animation.ActiveState = LS_SOFT_SPLAT;
return; return;
@ -349,13 +349,13 @@ void lara_col_run_forward(ItemInfo* item, CollisionInfo* coll)
if (LaraDeflectEdge(item, coll)) if (LaraDeflectEdge(item, coll))
{ {
ResetLaraLean(item); ResetPlayerLean(item);
if (TestLaraWall(item, OFFSET_RADIUS(coll->Setup.Radius), -CLICK(2.5f)) || if (TestLaraWall(item, OFFSET_RADIUS(coll->Setup.Radius), -CLICK(2.5f)) ||
coll->HitTallObject) coll->HitTallObject)
{ {
item->Animation.TargetState = LS_SPLAT; item->Animation.TargetState = LS_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
Rumble(0.4f, 0.15f); Rumble(0.4f, 0.15f);
@ -365,7 +365,7 @@ void lara_col_run_forward(ItemInfo* item, CollisionInfo* coll)
} }
item->Animation.TargetState = LS_SOFT_SPLAT; item->Animation.TargetState = LS_SOFT_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Animation.ActiveState = LS_SOFT_SPLAT; item->Animation.ActiveState = LS_SOFT_SPLAT;
return; return;
@ -1880,7 +1880,7 @@ void lara_col_step_right(ItemInfo* item, CollisionInfo* coll)
if (LaraDeflectEdge(item, coll)) if (LaraDeflectEdge(item, coll))
{ {
item->Animation.TargetState = LS_SOFT_SPLAT; item->Animation.TargetState = LS_SOFT_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Animation.ActiveState = LS_SOFT_SPLAT; item->Animation.ActiveState = LS_SOFT_SPLAT;
return; return;
@ -1974,7 +1974,7 @@ void lara_col_step_left(ItemInfo* item, CollisionInfo* coll)
if (LaraDeflectEdge(item, coll)) if (LaraDeflectEdge(item, coll))
{ {
item->Animation.TargetState = LS_SOFT_SPLAT; item->Animation.TargetState = LS_SOFT_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Animation.ActiveState = LS_SOFT_SPLAT; item->Animation.ActiveState = LS_SOFT_SPLAT;
return; return;
@ -2227,10 +2227,10 @@ void lara_col_wade_forward(ItemInfo* item, CollisionInfo* coll)
if (LaraDeflectEdge(item, coll)) if (LaraDeflectEdge(item, coll))
{ {
ResetLaraLean(item); ResetPlayerLean(item);
item->Animation.TargetState = LS_SOFT_SPLAT; item->Animation.TargetState = LS_SOFT_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Animation.ActiveState = LS_SOFT_SPLAT; item->Animation.ActiveState = LS_SOFT_SPLAT;
return; return;
@ -2355,13 +2355,13 @@ void lara_col_sprint(ItemInfo* item, CollisionInfo* coll)
if (LaraDeflectEdge(item, coll)) if (LaraDeflectEdge(item, coll))
{ {
ResetLaraLean(item); ResetPlayerLean(item);
if (TestLaraWall(item, OFFSET_RADIUS(coll->Setup.Radius), -CLICK(2.5f)) || if (TestLaraWall(item, OFFSET_RADIUS(coll->Setup.Radius), -CLICK(2.5f)) ||
coll->HitTallObject) coll->HitTallObject)
{ {
item->Animation.TargetState = LS_SPLAT; item->Animation.TargetState = LS_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
Rumble(0.5f, 0.15f); Rumble(0.5f, 0.15f);
@ -2371,7 +2371,7 @@ void lara_col_sprint(ItemInfo* item, CollisionInfo* coll)
} }
item->Animation.TargetState = LS_SOFT_SPLAT; item->Animation.TargetState = LS_SOFT_SPLAT;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Animation.ActiveState = LS_SOFT_SPLAT; item->Animation.ActiveState = LS_SOFT_SPLAT;
return; return;

View file

@ -10,10 +10,10 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Gui; using namespace TEN::Gui;
using namespace TEN::Input; using namespace TEN::Input;
@ -62,7 +62,7 @@ void LaraCheatyBits(ItemInfo* item)
static bool dbFlyCheat = true; static bool dbFlyCheat = true;
if (KeyMap[OIS::KeyCode::KC_O] && dbFlyCheat) if (KeyMap[OIS::KeyCode::KC_O] && dbFlyCheat)
{ {
if (lara->Vehicle == NO_ITEM) if (lara->Context.Vehicle == NO_ITEM)
{ {
LaraCheatGetStuff(item); LaraCheatGetStuff(item);
DelsGiveLaraItemsCheat(item); DelsGiveLaraItemsCheat(item);
@ -77,7 +77,7 @@ void LaraCheatyBits(ItemInfo* item)
item->Pose.Orientation.x = ANGLE(30.0f); item->Pose.Orientation.x = ANGLE(30.0f);
item->HitPoints = LARA_HEALTH_MAX; item->HitPoints = LARA_HEALTH_MAX;
ResetLaraFlex(item); ResetPlayerFlex(item);
lara->Control.WaterStatus = WaterStatus::FlyCheat; lara->Control.WaterStatus = WaterStatus::FlyCheat;
lara->Control.Count.Death = 0; lara->Control.Count.Death = 0;
lara->Status.Air = LARA_AIR_MAX; lara->Status.Air = LARA_AIR_MAX;

View file

@ -10,9 +10,9 @@
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_overhang.h" #include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_tests.h" #include "Game/Lara/lara_tests.h"
#include "Specific/level.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Flow/ScriptInterfaceFlowHandler.h" #include "Specific/level.h"
using namespace TEN::Input; using namespace TEN::Input;
@ -21,7 +21,7 @@ constexpr auto LADDER_TEST_DISTANCE = CLICK(0.5f) - LADDER_TEST_MARGIN;
constexpr auto LADDER_CLIMB_SHIFT = 70; constexpr auto LADDER_CLIMB_SHIFT = 70;
// ----------------------------- // -----------------------------
// LADDER CLIMB // WALL CLIMB
// Control & Collision Functions // Control & Collision Functions
// ----------------------------- // -----------------------------
@ -40,10 +40,10 @@ void lara_as_climb_end(ItemInfo* item, CollisionInfo* coll)
void lara_col_climb_down(ItemInfo* item, CollisionInfo* coll) void lara_col_climb_down(ItemInfo* item, CollisionInfo* coll)
{ {
if (LaraCheckForLetGo(item, coll) || item->Animation.AnimNumber != LA_LADDER_DOWN) if (LaraCheckForLetGo(item, coll) || !TestAnimNumber(*item, LA_LADDER_DOWN))
return; return;
int frame = item->Animation.FrameNumber - g_Level.Anims[LA_LADDER_DOWN].frameBase; int frame = item->Animation.FrameNumber - GetAnimData(item->ObjectNumber, LA_LADDER_DOWN).frameBase;
int yShift = 0; int yShift = 0;
switch (frame) switch (frame)
@ -128,9 +128,9 @@ void lara_as_climb_down(ItemInfo* item, CollisionInfo* coll)
void lara_col_climb_up(ItemInfo* item, CollisionInfo* coll) void lara_col_climb_up(ItemInfo* item, CollisionInfo* coll)
{ {
if (!LaraCheckForLetGo(item, coll) && item->Animation.AnimNumber == LA_LADDER_UP) if (!LaraCheckForLetGo(item, coll) && TestAnimNumber(*item, LA_LADDER_UP))
{ {
int frame = item->Animation.FrameNumber - g_Level.Anims[LA_LADDER_UP].frameBase; int frame = item->Animation.FrameNumber - GetAnimData(item->ObjectNumber, LA_LADDER_UP).frameBase;
int yShift; int yShift;
int resultRight, resultLeft; int resultRight, resultLeft;
int shiftRight, shiftLeft; int shiftRight, shiftLeft;
@ -304,6 +304,10 @@ void lara_col_climb_idle(ItemInfo* item, CollisionInfo* coll)
return; return;
} }
// Added check to avoid climbing through bridges.
if (resultRight == 0 && resultLeft == 0)
return;
if (resultRight >= 0 && resultLeft >= 0) if (resultRight >= 0 && resultLeft >= 0)
{ {
yShift = shiftLeft; yShift = shiftLeft;
@ -321,7 +325,7 @@ void lara_col_climb_idle(ItemInfo* item, CollisionInfo* coll)
yShift = shiftRight; yShift = shiftRight;
} }
// HACK: Prevent climbing inside sloped ceilings. Breaks overhang even more, but that shouldn't matter since we'll be doing it over. @Sezz 2022.05.13 // HACK: Prevent climbing inside sloped ceilings. Breaks overhang even more, but that shouldn't matter since we'll be doing it over. -- Sezz 2022.05.13
int y = item->Pose.Position.y - (coll->Setup.Height + CLICK(0.5f)); int y = item->Pose.Position.y - (coll->Setup.Height + CLICK(0.5f));
auto probe = GetCollision(item, 0, 0, -(coll->Setup.Height + CLICK(0.5f))); auto probe = GetCollision(item, 0, 0, -(coll->Setup.Height + CLICK(0.5f)));
if ((probe.Position.Ceiling - y) < 0) if ((probe.Position.Ceiling - y) < 0)
@ -357,6 +361,7 @@ void lara_as_climb_idle(ItemInfo* item, CollisionInfo* coll)
if (item->Animation.AnimNumber == LA_LADDER_DISMOUNT_LEFT_START) if (item->Animation.AnimNumber == LA_LADDER_DISMOUNT_LEFT_START)
Camera.targetAngle = -ANGLE(60.0f); Camera.targetAngle = -ANGLE(60.0f);
if (item->Animation.AnimNumber == LA_LADDER_DISMOUNT_RIGHT_START) if (item->Animation.AnimNumber == LA_LADDER_DISMOUNT_RIGHT_START)
Camera.targetAngle = ANGLE(60.0f); Camera.targetAngle = ANGLE(60.0f);
@ -627,10 +632,10 @@ int LaraClimbRightCornerTest(ItemInfo* item, CollisionInfo* coll)
if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftExtRightIntTab[angle]) if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftExtRightIntTab[angle])
{ {
lara->NextCornerPos.Position.x = item->Pose.Position.x = x; lara->Context.NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y; lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->NextCornerPos.Position.z = item->Pose.Position.z = z; lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
lara->NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90); lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90);
result = LaraTestClimbPos(item, coll->Setup.Radius, coll->Setup.Radius + LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift); result = LaraTestClimbPos(item, coll->Setup.Radius, coll->Setup.Radius + LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift);
item->ItemFlags[3] = result; item->ItemFlags[3] = result;
@ -668,10 +673,10 @@ int LaraClimbRightCornerTest(ItemInfo* item, CollisionInfo* coll)
if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftIntRightExtTab[angle]) if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftIntRightExtTab[angle])
{ {
lara->NextCornerPos.Position.x = item->Pose.Position.x = x; lara->Context.NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y; lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->NextCornerPos.Position.z = item->Pose.Position.z = z; lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
lara->NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f); lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f);
result = LaraTestClimbPos(item, coll->Setup.Radius, coll->Setup.Radius + LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift); result = LaraTestClimbPos(item, coll->Setup.Radius, coll->Setup.Radius + LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift);
item->ItemFlags[3] = result; item->ItemFlags[3] = result;
@ -716,10 +721,10 @@ int LaraClimbLeftCornerTest(ItemInfo* item, CollisionInfo* coll)
if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftIntRightExtTab[angle]) if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftIntRightExtTab[angle])
{ {
lara->NextCornerPos.Position.x = item->Pose.Position.x = x; lara->Context.NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y; lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->NextCornerPos.Position.z = item->Pose.Position.z = z; lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
lara->NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f); lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f);
result = LaraTestClimbPos(item, coll->Setup.Radius, -coll->Setup.Radius - LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift); result = LaraTestClimbPos(item, coll->Setup.Radius, -coll->Setup.Radius - LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift);
item->ItemFlags[3] = result; item->ItemFlags[3] = result;
@ -756,10 +761,10 @@ int LaraClimbLeftCornerTest(ItemInfo* item, CollisionInfo* coll)
if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftExtRightIntTab[angle]) if (GetClimbFlags(x, item->Pose.Position.y, z, item->RoomNumber) & (short)LeftExtRightIntTab[angle])
{ {
lara->NextCornerPos.Position.x = item->Pose.Position.x = x; lara->Context.NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y; lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->NextCornerPos.Position.z = item->Pose.Position.z = z; lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
lara->NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90.0f); lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y = lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90.0f);
item->ItemFlags[3] = LaraTestClimbPos(item, coll->Setup.Radius, -coll->Setup.Radius - LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift); item->ItemFlags[3] = LaraTestClimbPos(item, coll->Setup.Radius, -coll->Setup.Radius - LADDER_TEST_DISTANCE, -CLICK(2), CLICK(2), &shift);
result = item->ItemFlags[3] != 0; result = item->ItemFlags[3] != 0;
@ -886,47 +891,22 @@ int LaraTestClimb(ItemInfo* item, int xOffset, int yOffset, int zOffset, int xFr
int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* ledge) int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* ledge)
{ {
int y = item->Pose.Position.y - 768; auto probePos = Geometry::TranslatePoint(item->Pose.Position, item->Pose.Orientation.y, front, -CLICK(3), right);
auto probeOffset = Geometry::TranslatePoint(Vector3i::Zero, item->Pose.Orientation.y, 4.0f);
int x, z;
int xFront = 0;
int zFront = 0;
switch (GetQuadrant(item->Pose.Orientation.y))
{
case NORTH:
x = item->Pose.Position.x + right;
z = item->Pose.Position.z + front;
zFront = 4;
break;
case EAST:
x = item->Pose.Position.x + front;
z = item->Pose.Position.z - right;
xFront = 4;
break;
case SOUTH:
x = item->Pose.Position.x - right;
z = item->Pose.Position.z - front;
zFront = -4;
break;
default:
x = item->Pose.Position.x - front;
z = item->Pose.Position.z + right;
xFront = -4;
break;
}
*shift = 0; *shift = 0;
short roomNumber = item->RoomNumber; // Test center.
FloorInfo* floor = GetFloor(x, y, z, &roomNumber); auto pointColl = GetCollision(item);
int ceiling = CLICK(1) - y + GetCeiling(floor, x, y, z); int vPos = item->Pose.Position.y - CLICK(4);
if ((pointColl.Position.Ceiling - vPos) > LADDER_CLIMB_SHIFT)
return 0;
floor = GetFloor(x + xFront, y, z + zFront, &roomNumber); pointColl = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber);
int height = GetFloorHeight(floor, x + xFront, y, z + zFront); int ceiling = (CLICK(1) - probePos.y) + pointColl.Position.Ceiling;
pointColl = GetCollision(probePos.x + probeOffset.x, probePos.y, probePos.z + probeOffset.z, pointColl.RoomNumber);
int height = pointColl.Position.Floor;
if (height == NO_HEIGHT) if (height == NO_HEIGHT)
{ {
@ -934,7 +914,7 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
} }
else else
{ {
height -= y; height -= probePos.y;
*ledge = height; *ledge = height;
} }
@ -955,12 +935,13 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
if (height > 0 && height > *shift) if (height > 0 && height > *shift)
*shift = height; *shift = height;
roomNumber = item->RoomNumber; pointColl = GetCollision(probePos.x, probePos.y + CLICK(2), probePos.z, item->RoomNumber);
GetFloor(x, y + CLICK(2), z, &roomNumber); pointColl = GetCollision(probePos.x + probeOffset.x, probePos.y + CLICK(2), probePos.z + probeOffset.z, pointColl.RoomNumber);
floor = GetFloor(x + xFront, y + CLICK(2), z + zFront, &roomNumber);
ceiling = GetCeiling(floor, x + xFront, y + CLICK(2), z + zFront) - y; ceiling = pointColl.Position.Ceiling - probePos.y;
if (ceiling <= height) if (ceiling <= height)
return 1; return 1;
if (ceiling >= CLICK(2)) if (ceiling >= CLICK(2))
return 1; return 1;
else else
@ -968,13 +949,14 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
} }
else else
{ {
ceiling = GetCeiling(floor, x + xFront, y, z + zFront) - y; ceiling = GetCollision(probePos.x + probeOffset.x, probePos.y, probePos.z + probeOffset.z, pointColl.RoomNumber).Position.Ceiling - probePos.y;
if (ceiling < CLICK(2)) if (ceiling < CLICK(2))
{ {
if ((height - ceiling) <= LARA_HEIGHT) if ((height - ceiling) <= LARA_HEIGHT)
{ {
if ((height - ceiling) < CLICK(2)) if ((height - ceiling) < CLICK(2))
return 0; return 0;
*shift = height; *shift = height;
return -2; return -2;
} }
@ -985,9 +967,11 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
} }
} }
else else
{
return 1; return 1;
} }
} }
}
return -2; return -2;
} }
@ -1002,7 +986,7 @@ bool LaraCheckForLetGo(ItemInfo* item, CollisionInfo* coll)
if (TrInput & IN_ACTION && item->HitPoints > 0 || item->Animation.AnimNumber == LA_ONWATER_TO_LADDER) // Can't let go on this anim if (TrInput & IN_ACTION && item->HitPoints > 0 || item->Animation.AnimNumber == LA_ONWATER_TO_LADDER) // Can't let go on this anim
return false; return false;
ResetLaraFlex(item); ResetPlayerFlex(item);
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);

View file

@ -14,7 +14,7 @@ static ClimbDirectionFlags LeftExtRightIntTab[4] =
}; };
// ----------------------------- // -----------------------------
// LADDER CLIMB // WALL CLIMB
// Control & Collision Functions // Control & Collision Functions
// ----------------------------- // -----------------------------

View file

@ -11,10 +11,10 @@
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_swim.h" #include "Game/Lara/lara_swim.h"
#include "Game/Lara/lara_tests.h" #include "Game/Lara/lara_tests.h"
#include "Game/Setup.h"
#include "Objects/Sink.h" #include "Objects/Sink.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Flow/ScriptInterfaceFlowHandler.h" #include "Flow/ScriptInterfaceFlowHandler.h"
#include "ScriptInterfaceLevel.h" #include "ScriptInterfaceLevel.h"
@ -239,9 +239,10 @@ void LaraCollideStop(ItemInfo* item, CollisionInfo* coll)
case LS_TURN_LEFT_SLOW: case LS_TURN_LEFT_SLOW:
case LS_TURN_RIGHT_FAST: case LS_TURN_RIGHT_FAST:
case LS_TURN_LEFT_FAST: case LS_TURN_LEFT_FAST:
item->Animation.ActiveState = coll->Setup.OldState; item->Animation.AnimObjectID = coll->Setup.PrevAnimObjectID;
item->Animation.AnimNumber = coll->Setup.OldAnimNumber; item->Animation.AnimNumber = coll->Setup.OldAnimNumber;
item->Animation.FrameNumber = coll->Setup.OldFrameNumber; item->Animation.FrameNumber = coll->Setup.OldFrameNumber;
item->Animation.ActiveState = coll->Setup.OldState;
if (TrInput & IN_LEFT) if (TrInput & IN_LEFT)
{ {
@ -288,9 +289,10 @@ void LaraCollideStopCrawl(ItemInfo* item, CollisionInfo* coll)
case LS_CRAWL_IDLE: case LS_CRAWL_IDLE:
case LS_CRAWL_TURN_LEFT: case LS_CRAWL_TURN_LEFT:
case LS_CRAWL_TURN_RIGHT: case LS_CRAWL_TURN_RIGHT:
item->Animation.ActiveState = coll->Setup.OldState; item->Animation.AnimObjectID = coll->Setup.PrevAnimObjectID;
item->Animation.AnimNumber = coll->Setup.OldAnimNumber; item->Animation.AnimNumber = coll->Setup.OldAnimNumber;
item->Animation.FrameNumber = coll->Setup.OldFrameNumber; item->Animation.FrameNumber = coll->Setup.OldFrameNumber;
item->Animation.ActiveState = coll->Setup.OldState;
if (TrInput & IN_LEFT) if (TrInput & IN_LEFT)
item->Animation.TargetState = LS_CRAWL_TURN_LEFT; item->Animation.TargetState = LS_CRAWL_TURN_LEFT;
@ -309,7 +311,7 @@ void LaraCollideStopCrawl(ItemInfo* item, CollisionInfo* coll)
if (item->Animation.AnimNumber != LA_CRAWL_IDLE) if (item->Animation.AnimNumber != LA_CRAWL_IDLE)
{ {
item->Animation.AnimNumber = LA_CRAWL_IDLE; item->Animation.AnimNumber = LA_CRAWL_IDLE;
item->Animation.FrameNumber = GetFrameNumber(item, 0); item->Animation.FrameNumber = GetFrameIndex(item, 0);
} }
break; break;
@ -323,9 +325,10 @@ void LaraCollideStopMonkey(ItemInfo* item, CollisionInfo* coll)
case LS_MONKEY_IDLE: case LS_MONKEY_IDLE:
case LS_MONKEY_TURN_LEFT: case LS_MONKEY_TURN_LEFT:
case LS_MONKEY_TURN_RIGHT: case LS_MONKEY_TURN_RIGHT:
item->Animation.ActiveState = coll->Setup.OldState; item->Animation.AnimObjectID = coll->Setup.PrevAnimObjectID;
item->Animation.AnimNumber = coll->Setup.OldAnimNumber; item->Animation.AnimNumber = coll->Setup.OldAnimNumber;
item->Animation.FrameNumber = coll->Setup.OldFrameNumber; item->Animation.FrameNumber = coll->Setup.OldFrameNumber;
item->Animation.ActiveState = coll->Setup.OldState;
if (TrInput & IN_LEFT) if (TrInput & IN_LEFT)
item->Animation.TargetState = LS_MONKEY_TURN_LEFT; item->Animation.TargetState = LS_MONKEY_TURN_LEFT;
@ -344,7 +347,7 @@ void LaraCollideStopMonkey(ItemInfo* item, CollisionInfo* coll)
if (item->Animation.AnimNumber != LA_MONKEY_IDLE) if (item->Animation.AnimNumber != LA_MONKEY_IDLE)
{ {
item->Animation.AnimNumber = LA_MONKEY_IDLE; item->Animation.AnimNumber = LA_MONKEY_IDLE;
item->Animation.FrameNumber = GetFrameNumber(item, 0); item->Animation.FrameNumber = GetFrameIndex(item, 0);
} }
break; break;
@ -621,13 +624,13 @@ void LaraWaterCurrent(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
if (lara->WaterCurrentActive) if (lara->Context.WaterCurrentActive)
{ {
const auto& sink = g_Level.Sinks[lara->WaterCurrentActive - 1]; const auto& sink = g_Level.Sinks[lara->Context.WaterCurrentActive - 1];
short headingAngle = Geometry::GetOrientToPoint(item->Pose.Position.ToVector3(), sink.Position).y; short headingAngle = Geometry::GetOrientToPoint(item->Pose.Position.ToVector3(), sink.Position).y;
lara->WaterCurrentPull.x += ((sink.Strength * SECTOR(1) * phd_sin(headingAngle)) - lara->WaterCurrentPull.x) / 16; lara->Context.WaterCurrentPull.x += ((sink.Strength * SECTOR(1) * phd_sin(headingAngle)) - lara->Context.WaterCurrentPull.x) / 16;
lara->WaterCurrentPull.z += ((sink.Strength * SECTOR(1) * phd_cos(headingAngle)) - lara->WaterCurrentPull.z) / 16; lara->Context.WaterCurrentPull.z += ((sink.Strength * SECTOR(1) * phd_cos(headingAngle)) - lara->Context.WaterCurrentPull.z) / 16;
item->Pose.Position.y += (sink.Position.y - item->Pose.Position.y) / 16; item->Pose.Position.y += (sink.Position.y - item->Pose.Position.y) / 16;
} }
@ -635,31 +638,31 @@ void LaraWaterCurrent(ItemInfo* item, CollisionInfo* coll)
{ {
int shift = 0; int shift = 0;
if (abs(lara->WaterCurrentPull.x) <= 16) if (abs(lara->Context.WaterCurrentPull.x) <= 16)
shift = (abs(lara->WaterCurrentPull.x) > 8) + 2; shift = (abs(lara->Context.WaterCurrentPull.x) > 8) + 2;
else else
shift = 4; shift = 4;
lara->WaterCurrentPull.x -= lara->WaterCurrentPull.x >> shift; lara->Context.WaterCurrentPull.x -= lara->Context.WaterCurrentPull.x >> shift;
if (abs(lara->WaterCurrentPull.x) < 4) if (abs(lara->Context.WaterCurrentPull.x) < 4)
lara->WaterCurrentPull.x = 0; lara->Context.WaterCurrentPull.x = 0;
if (abs(lara->WaterCurrentPull.z) <= 16) if (abs(lara->Context.WaterCurrentPull.z) <= 16)
shift = (abs(lara->WaterCurrentPull.z) > 8) + 2; shift = (abs(lara->Context.WaterCurrentPull.z) > 8) + 2;
else else
shift = 4; shift = 4;
lara->WaterCurrentPull.z -= lara->WaterCurrentPull.z >> shift; lara->Context.WaterCurrentPull.z -= lara->Context.WaterCurrentPull.z >> shift;
if (abs(lara->WaterCurrentPull.z) < 4) if (abs(lara->Context.WaterCurrentPull.z) < 4)
lara->WaterCurrentPull.z = 0; lara->Context.WaterCurrentPull.z = 0;
if (!lara->WaterCurrentPull.x && !lara->WaterCurrentPull.z) if (!lara->Context.WaterCurrentPull.x && !lara->Context.WaterCurrentPull.z)
return; return;
} }
item->Pose.Position.x += lara->WaterCurrentPull.x / 256; item->Pose.Position.x += lara->Context.WaterCurrentPull.x / 256;
item->Pose.Position.z += lara->WaterCurrentPull.z / 256; item->Pose.Position.z += lara->Context.WaterCurrentPull.z / 256;
lara->WaterCurrentActive = 0; lara->Context.WaterCurrentActive = 0;
coll->Setup.ForwardAngle = phd_atan(item->Pose.Position.z - coll->Setup.OldPosition.z, item->Pose.Position.x - coll->Setup.OldPosition.x); coll->Setup.ForwardAngle = phd_atan(item->Pose.Position.z - coll->Setup.OldPosition.z, item->Pose.Position.x - coll->Setup.OldPosition.x);
coll->Setup.Height = LARA_HEIGHT_CRAWL; coll->Setup.Height = LARA_HEIGHT_CRAWL;

View file

@ -434,7 +434,7 @@ void lara_as_crawl_idle(ItemInfo* item, CollisionInfo* coll)
{ {
item->Animation.TargetState = crawlVaultResult.TargetState; item->Animation.TargetState = crawlVaultResult.TargetState;
lara->Control.TurnRate = 0; lara->Control.TurnRate = 0;
ResetLaraFlex(item); ResetPlayerFlex(item);
return; return;
} }
else if (TestLaraCrawlForward(item, coll)) USE_FEATURE_IF_CPP20([[likely]]) else if (TestLaraCrawlForward(item, coll)) USE_FEATURE_IF_CPP20([[likely]])
@ -864,7 +864,7 @@ void lara_col_crawl_to_hang(ItemInfo* item, CollisionInfo* coll)
Camera.targetAngle = 0; Camera.targetAngle = 0;
Camera.targetDistance = SECTOR(1); Camera.targetDistance = SECTOR(1);
ResetLaraLean(item, 6.0f); ResetPlayerLean(item, 1 / 6.0f);
if (item->Animation.AnimNumber == LA_CRAWL_TO_HANG_END) if (item->Animation.AnimNumber == LA_CRAWL_TO_HANG_END)
{ {

View file

@ -18,6 +18,7 @@
#include "Game/Lara/lara_two_guns.h" #include "Game/Lara/lara_two_guns.h"
#include "Game/misc.h" #include "Game/misc.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/Generic/Object/burning_torch.h" #include "Objects/Generic/Object/burning_torch.h"
#include "Objects/Generic/Object/objects.h" #include "Objects/Generic/Object/objects.h"
@ -28,14 +29,15 @@
#include "Specific/configuration.h" #include "Specific/configuration.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
ItemInfo* LastTargets[MAX_TARGETS]; constexpr auto TARGET_COUNT_MAX = 8;
ItemInfo* TargetList[MAX_TARGETS];
std::array<ItemInfo*, TARGET_COUNT_MAX> LastTargets = {};
std::array<ItemInfo*, TARGET_COUNT_MAX> TargetList = {};
int FlashGrenadeAftershockTimer = 0; int FlashGrenadeAftershockTimer = 0;
@ -88,7 +90,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
650, 650,
SECTOR(8), BLOCK(8),
1, 1,
9, 9,
3, 3,
@ -105,7 +107,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(4.0f), ANGLE(4.0f),
650, 650,
SECTOR(8), BLOCK(8),
21, 21,
16, 16,
3, 3,
@ -122,7 +124,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
650, 650,
SECTOR(8), BLOCK(8),
1, 1,
3, 3,
3, 3,
@ -139,7 +141,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
0, 0,
500, 500,
SECTOR(8), BLOCK(8),
3, 3,
9, 9,
3, 3,
@ -156,7 +158,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(4.0f), ANGLE(4.0f),
500, 500,
SECTOR(12), BLOCK(12),
4, 4,
0, 0,
3, 3,
@ -173,7 +175,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
500, 500,
SECTOR(8), BLOCK(8),
5, 5,
0, 0,
2, 2,
@ -199,7 +201,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
0 0
}, },
// Flare 2 // Torch
{ {
std::pair(EulerAngles(ANGLE(-55.0f), ANGLE(-30.0f), 0), EulerAngles(ANGLE(55.0f), ANGLE(30.0f), 0)), std::pair(EulerAngles(ANGLE(-55.0f), ANGLE(-30.0f), 0), EulerAngles(ANGLE(55.0f), ANGLE(30.0f), 0)),
std::pair(EulerAngles(ANGLE(-55.0f), ANGLE(-30.0f), 0), EulerAngles(ANGLE(55.0f), ANGLE(30.0f), 0)), std::pair(EulerAngles(ANGLE(-55.0f), ANGLE(-30.0f), 0), EulerAngles(ANGLE(55.0f), ANGLE(30.0f), 0)),
@ -207,7 +209,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
400, 400,
SECTOR(8), BLOCK(8),
3, 3,
0, 0,
2, 2,
@ -224,7 +226,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
500, 500,
SECTOR(8), BLOCK(8),
20, 20,
0, 0,
2, 2,
@ -241,7 +243,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
500, 500,
SECTOR(8), BLOCK(8),
6, 6,
0, 0,
2, 2,
@ -258,7 +260,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
500, 500,
SECTOR(8), BLOCK(8),
30, 30,
0, 0,
2, 2,
@ -275,39 +277,39 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f), ANGLE(10.0f),
ANGLE(8.0f), ANGLE(8.0f),
400, 400,
SECTOR(8), BLOCK(8),
3, 3,
0, 0,
0, 2,
0, 0,
SFX_TR4_UZI_FIRE, SFX_TR4_UZI_FIRE,
0 0
} }
}; };
void InitialiseNewWeapon(ItemInfo* laraItem) void InitializeNewWeapon(ItemInfo& laraItem)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
lara.LeftArm.FrameNumber = 0; player.TargetEntity = nullptr;
lara.RightArm.FrameNumber = 0; player.LeftArm.FrameNumber =
lara.LeftArm.Orientation = EulerAngles::Zero; player.RightArm.FrameNumber = 0;
lara.RightArm.Orientation = EulerAngles::Zero; player.LeftArm.Orientation =
lara.TargetEntity = nullptr; player.RightArm.Orientation = EulerAngles::Zero;
lara.LeftArm.Locked = false; player.LeftArm.Locked =
lara.RightArm.Locked = false; player.RightArm.Locked = false;
lara.LeftArm.GunFlash = 0; player.LeftArm.GunFlash =
lara.RightArm.GunFlash = 0; player.RightArm.GunFlash = 0;
switch (lara.Control.Weapon.GunType) switch (player.Control.Weapon.GunType)
{ {
case LaraWeaponType::Pistol: case LaraWeaponType::Pistol:
case LaraWeaponType::Uzi: case LaraWeaponType::Uzi:
lara.RightArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase; player.RightArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase;
lara.LeftArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase; player.LeftArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase;
if (lara.Control.HandStatus != HandStatus::Free) if (player.Control.HandStatus != HandStatus::Free)
DrawPistolMeshes(laraItem, lara.Control.Weapon.GunType); DrawPistolMeshes(laraItem, player.Control.Weapon.GunType);
break; break;
@ -317,26 +319,26 @@ void InitialiseNewWeapon(ItemInfo* laraItem)
case LaraWeaponType::GrenadeLauncher: case LaraWeaponType::GrenadeLauncher:
case LaraWeaponType::HarpoonGun: case LaraWeaponType::HarpoonGun:
case LaraWeaponType::RocketLauncher: case LaraWeaponType::RocketLauncher:
lara.RightArm.FrameBase = Objects[GetWeaponObjectID(lara.Control.Weapon.GunType)].frameBase; player.RightArm.FrameBase = Objects[GetWeaponObjectID(player.Control.Weapon.GunType)].frameBase;
lara.LeftArm.FrameBase = Objects[GetWeaponObjectID(lara.Control.Weapon.GunType)].frameBase; player.LeftArm.FrameBase = Objects[GetWeaponObjectID(player.Control.Weapon.GunType)].frameBase;
if (lara.Control.HandStatus != HandStatus::Free) if (player.Control.HandStatus != HandStatus::Free)
DrawShotgunMeshes(laraItem, lara.Control.Weapon.GunType); DrawShotgunMeshes(laraItem, player.Control.Weapon.GunType);
break; break;
case LaraWeaponType::Flare: case LaraWeaponType::Flare:
lara.RightArm.FrameBase = Objects[ID_FLARE_ANIM].frameBase; player.RightArm.FrameBase = Objects[ID_FLARE_ANIM].frameBase;
lara.LeftArm.FrameBase = Objects[ID_FLARE_ANIM].frameBase; player.LeftArm.FrameBase = Objects[ID_FLARE_ANIM].frameBase;
if (lara.Control.HandStatus != HandStatus::Free) if (player.Control.HandStatus != HandStatus::Free)
DrawFlareMeshes(laraItem); DrawFlareMeshes(laraItem);
break; break;
default: default:
lara.RightArm.FrameBase = g_Level.Anims[laraItem->Animation.AnimNumber].FramePtr; player.RightArm.FrameBase = g_Level.Anims[laraItem.Animation.AnimNumber].FramePtr;
lara.LeftArm.FrameBase = g_Level.Anims[laraItem->Animation.AnimNumber].FramePtr; player.LeftArm.FrameBase = g_Level.Anims[laraItem.Animation.AnimNumber].FramePtr;
break; break;
} }
} }
@ -346,23 +348,23 @@ Ammo& GetAmmo(LaraInfo& lara, LaraWeaponType weaponType)
return lara.Weapons[(int)weaponType].Ammo[(int)lara.Weapons[(int)weaponType].SelectedAmmo]; return lara.Weapons[(int)weaponType].Ammo[(int)lara.Weapons[(int)weaponType].SelectedAmmo];
} }
GameVector GetTargetPoint(ItemInfo* targetEntity) GameVector GetTargetPoint(ItemInfo& targetEntity)
{ {
const auto& bounds = GetBestFrame(*targetEntity).BoundingBox; const auto& bounds = GetBestFrame(targetEntity).BoundingBox;
auto center = Vector3i( auto center = Vector3i(
(bounds.X1 + bounds.X2) / 2, (bounds.X1 + bounds.X2) / 2,
bounds.Y1 + (bounds.GetHeight() / 3), bounds.Y1 + (bounds.GetHeight() / 3),
(bounds.Z1 + bounds.Z2) / 2); (bounds.Z1 + bounds.Z2) / 2);
float sinY = phd_sin(targetEntity->Pose.Orientation.y); float sinY = phd_sin(targetEntity.Pose.Orientation.y);
float cosY = phd_cos(targetEntity->Pose.Orientation.y); float cosY = phd_cos(targetEntity.Pose.Orientation.y);
return GameVector( return GameVector(
targetEntity->Pose.Position.x + ((center.x * cosY) + (center.z * sinY)), targetEntity.Pose.Position.x + ((center.x * cosY) + (center.z * sinY)),
targetEntity->Pose.Position.y + center.y, targetEntity.Pose.Position.y + center.y,
targetEntity->Pose.Position.z + ((center.z * cosY) - (center.x * sinY)), targetEntity.Pose.Position.z + ((center.z * cosY) - (center.x * sinY)),
targetEntity->RoomNumber); targetEntity.RoomNumber);
} }
HolsterSlot GetWeaponHolsterSlot(LaraWeaponType weaponType) HolsterSlot GetWeaponHolsterSlot(LaraWeaponType weaponType)
@ -437,14 +439,14 @@ GAME_OBJECT_ID GetWeaponObjectID(LaraWeaponType weaponType)
} }
} }
GAME_OBJECT_ID GetWeaponObjectMeshID(ItemInfo* laraItem, LaraWeaponType weaponType) GAME_OBJECT_ID GetWeaponObjectMeshID(ItemInfo& laraItem, LaraWeaponType weaponType)
{ {
const auto& lara = *GetLaraInfo(laraItem); const auto& player = *GetLaraInfo(&laraItem);
switch (weaponType) switch (weaponType)
{ {
case LaraWeaponType::Revolver: case LaraWeaponType::Revolver:
return (lara.Weapons[(int)LaraWeaponType::Revolver].HasLasersight ? ID_LARA_REVOLVER_LASER : ID_REVOLVER_ANIM); return (player.Weapons[(int)LaraWeaponType::Revolver].HasLasersight ? ID_LARA_REVOLVER_LASER : ID_REVOLVER_ANIM);
case LaraWeaponType::Uzi: case LaraWeaponType::Uzi:
return ID_UZI_ANIM; return ID_UZI_ANIM;
@ -456,7 +458,7 @@ GAME_OBJECT_ID GetWeaponObjectMeshID(ItemInfo* laraItem, LaraWeaponType weaponTy
return ID_HK_ANIM; return ID_HK_ANIM;
case LaraWeaponType::Crossbow: case LaraWeaponType::Crossbow:
return (lara.Weapons[(int)LaraWeaponType::Crossbow].HasLasersight ? ID_LARA_CROSSBOW_LASER : ID_CROSSBOW_ANIM); return (player.Weapons[(int)LaraWeaponType::Crossbow].HasLasersight ? ID_LARA_CROSSBOW_LASER : ID_CROSSBOW_ANIM);
case LaraWeaponType::GrenadeLauncher: case LaraWeaponType::GrenadeLauncher:
return ID_GRENADE_ANIM; return ID_GRENADE_ANIM;
@ -472,139 +474,143 @@ GAME_OBJECT_ID GetWeaponObjectMeshID(ItemInfo* laraItem, LaraWeaponType weaponTy
} }
} }
void HandleWeapon(ItemInfo* laraItem) void HandleWeapon(ItemInfo& laraItem)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
if (lara.LeftArm.GunFlash > 0) if (player.LeftArm.GunFlash > 0)
--lara.LeftArm.GunFlash; --player.LeftArm.GunFlash;
if (lara.RightArm.GunFlash > 0) if (player.RightArm.GunFlash > 0)
--lara.RightArm.GunFlash; --player.RightArm.GunFlash;
if (lara.RightArm.GunSmoke > 0) if (player.RightArm.GunSmoke > 0)
--lara.RightArm.GunSmoke; --player.RightArm.GunSmoke;
if (lara.LeftArm.GunSmoke > 0) if (player.LeftArm.GunSmoke > 0)
--lara.LeftArm.GunSmoke; --player.LeftArm.GunSmoke;
if (FlashGrenadeAftershockTimer) if (FlashGrenadeAftershockTimer)
FlashGrenadeAftershockTimer--; FlashGrenadeAftershockTimer--;
if (lara.Control.Weapon.GunType == LaraWeaponType::Torch) if (player.Control.Weapon.GunType == LaraWeaponType::Torch)
{ {
DoFlameTorch(); DoFlameTorch();
return; return;
} }
if (laraItem->HitPoints <= 0) if (laraItem.HitPoints <= 0)
{ {
lara.Control.HandStatus = HandStatus::Free; player.Control.HandStatus = HandStatus::Free;
} }
else if (lara.Control.HandStatus == HandStatus::Free) else if (player.Control.HandStatus == HandStatus::Free)
{ {
// Draw weapon. // Draw weapon.
if (IsHeld(In::DrawWeapon)) if (IsHeld(In::DrawWeapon))
{ {
// No weapon - no any actions. // No weapon - no any actions.
if (lara.Control.Weapon.LastGunType != LaraWeaponType::None) if (player.Control.Weapon.LastGunType != LaraWeaponType::None)
lara.Control.Weapon.RequestGunType = lara.Control.Weapon.LastGunType; player.Control.Weapon.RequestGunType = player.Control.Weapon.LastGunType;
} }
// Draw flare. // Draw flare.
else if (IsHeld(In::Flare) && (g_GameFlow->GetLevel(CurrentLevel)->GetLaraType() != LaraType::Young)) else if (IsHeld(In::Flare) && (g_GameFlow->GetLevel(CurrentLevel)->GetLaraType() != LaraType::Young))
{ {
if (lara.Control.Weapon.GunType == LaraWeaponType::Flare) if (player.Control.Weapon.GunType == LaraWeaponType::Flare)
{ {
if (!lara.LeftArm.FrameNumber) if (!player.LeftArm.FrameNumber)
{ {
lara.Control.HandStatus = HandStatus::WeaponUndraw; player.Control.HandStatus = HandStatus::WeaponUndraw;
} }
} }
else if (lara.Inventory.TotalFlares) else if (player.Inventory.TotalFlares)
{ {
if (lara.Inventory.TotalFlares != -1) if (player.Inventory.TotalFlares != -1)
lara.Inventory.TotalFlares--; player.Inventory.TotalFlares--;
lara.Control.Weapon.RequestGunType = LaraWeaponType::Flare; player.Control.Weapon.RequestGunType = LaraWeaponType::Flare;
} }
} }
if ((IsHeld(In::DrawWeapon) && lara.Control.Weapon.LastGunType != LaraWeaponType::None) || if ((IsHeld(In::DrawWeapon) && player.Control.Weapon.LastGunType != LaraWeaponType::None) ||
lara.Control.Weapon.RequestGunType != lara.Control.Weapon.GunType) player.Control.Weapon.RequestGunType != player.Control.Weapon.GunType)
{ {
if (lara.Control.IsLow && if (player.Control.IsLow &&
lara.Control.Weapon.RequestGunType >= LaraWeaponType::Shotgun && player.Control.Weapon.RequestGunType >= LaraWeaponType::Shotgun &&
lara.Control.Weapon.RequestGunType != LaraWeaponType::Flare && player.Control.Weapon.RequestGunType != LaraWeaponType::Flare &&
lara.Control.Weapon.RequestGunType != LaraWeaponType::Torch) player.Control.Weapon.RequestGunType != LaraWeaponType::Torch)
{ {
if (lara.Control.Weapon.GunType == LaraWeaponType::Flare) if (player.Control.Weapon.GunType == LaraWeaponType::Flare)
lara.Control.Weapon.RequestGunType = LaraWeaponType::Flare; player.Control.Weapon.RequestGunType = LaraWeaponType::Flare;
} }
else if (lara.Control.Weapon.RequestGunType == LaraWeaponType::Flare || else if (player.Control.Weapon.RequestGunType == LaraWeaponType::Flare ||
(lara.Vehicle == NO_ITEM && (player.Context.Vehicle == NO_ITEM &&
(lara.Control.Weapon.RequestGunType == LaraWeaponType::HarpoonGun || (player.Control.Weapon.RequestGunType == LaraWeaponType::HarpoonGun ||
lara.Control.WaterStatus == WaterStatus::Dry || player.Control.WaterStatus == WaterStatus::Dry ||
(lara.Control.WaterStatus == WaterStatus::Wade && (player.Control.WaterStatus == WaterStatus::Wade &&
lara.WaterSurfaceDist > -Weapons[(int)lara.Control.Weapon.GunType].GunHeight)))) player.Context.WaterSurfaceDist > -Weapons[(int)player.Control.Weapon.GunType].GunHeight))))
{ {
if (lara.Control.Weapon.GunType == LaraWeaponType::Flare) if (player.Control.Weapon.GunType == LaraWeaponType::Flare)
{ {
CreateFlare(laraItem, ID_FLARE_ITEM, 0); CreateFlare(laraItem, ID_FLARE_ITEM, 0);
UndrawFlareMeshes(laraItem); UndrawFlareMeshes(laraItem);
lara.Flare.ControlLeft = false; player.Flare.ControlLeft = false;
lara.Flare.Life = 0; player.Flare.Life = 0;
} }
lara.Control.Weapon.GunType = lara.Control.Weapon.RequestGunType; player.Control.Weapon.GunType = player.Control.Weapon.RequestGunType;
InitialiseNewWeapon(laraItem); InitializeNewWeapon(laraItem);
lara.RightArm.FrameNumber = 0; player.RightArm.FrameNumber = 0;
lara.LeftArm.FrameNumber = 0; player.LeftArm.FrameNumber = 0;
lara.Control.HandStatus = HandStatus::WeaponDraw; player.Control.HandStatus = HandStatus::WeaponDraw;
} }
else else
{ {
lara.Control.Weapon.LastGunType = lara.Control.Weapon.RequestGunType; player.Control.Weapon.LastGunType = player.Control.Weapon.RequestGunType;
if (lara.Control.Weapon.GunType != LaraWeaponType::Flare) if (player.Control.Weapon.GunType != LaraWeaponType::Flare)
lara.Control.Weapon.GunType = lara.Control.Weapon.RequestGunType; {
player.Control.Weapon.GunType = player.Control.Weapon.RequestGunType;
}
else else
lara.Control.Weapon.RequestGunType = LaraWeaponType::Flare; {
player.Control.Weapon.RequestGunType = LaraWeaponType::Flare;
} }
} }
} }
else if (lara.Control.HandStatus == HandStatus::WeaponReady) }
else if (player.Control.HandStatus == HandStatus::WeaponReady)
{ {
if (IsHeld(In::DrawWeapon) || if (IsHeld(In::DrawWeapon) ||
lara.Control.Weapon.RequestGunType != lara.Control.Weapon.GunType) player.Control.Weapon.RequestGunType != player.Control.Weapon.GunType)
{ {
lara.Control.HandStatus = HandStatus::WeaponUndraw; player.Control.HandStatus = HandStatus::WeaponUndraw;
} }
else if (lara.Control.Weapon.GunType != LaraWeaponType::HarpoonGun && else if (player.Control.Weapon.GunType != LaraWeaponType::HarpoonGun &&
lara.Control.WaterStatus != WaterStatus::Dry && player.Control.WaterStatus != WaterStatus::Dry &&
(lara.Control.WaterStatus != WaterStatus::Wade || (player.Control.WaterStatus != WaterStatus::Wade ||
lara.WaterSurfaceDist < -Weapons[(int)lara.Control.Weapon.GunType].GunHeight)) player.Context.WaterSurfaceDist < -Weapons[(int)player.Control.Weapon.GunType].GunHeight))
{ {
lara.Control.HandStatus = HandStatus::WeaponUndraw; player.Control.HandStatus = HandStatus::WeaponUndraw;
} }
} }
else if (IsHeld(In::Flare) && else if (IsHeld(In::Flare) &&
lara.Control.HandStatus == HandStatus::Busy && player.Control.HandStatus == HandStatus::Busy &&
laraItem->Animation.ActiveState == LS_CRAWL_IDLE && laraItem.Animation.ActiveState == LS_CRAWL_IDLE &&
laraItem->Animation.AnimNumber == LA_CRAWL_IDLE) laraItem.Animation.AnimNumber == LA_CRAWL_IDLE)
{ {
lara.Control.Weapon.RequestGunType = LaraWeaponType::Flare; player.Control.Weapon.RequestGunType = LaraWeaponType::Flare;
} }
switch (lara.Control.HandStatus) switch (player.Control.HandStatus)
{ {
case HandStatus::WeaponDraw: case HandStatus::WeaponDraw:
if (lara.Control.Weapon.GunType != LaraWeaponType::Flare && if (player.Control.Weapon.GunType != LaraWeaponType::Flare &&
lara.Control.Weapon.GunType != LaraWeaponType::None) player.Control.Weapon.GunType != LaraWeaponType::None)
{ {
lara.Control.Weapon.LastGunType = lara.Control.Weapon.GunType; player.Control.Weapon.LastGunType = player.Control.Weapon.GunType;
} }
switch (lara.Control.Weapon.GunType) switch (player.Control.Weapon.GunType)
{ {
case LaraWeaponType::Pistol: case LaraWeaponType::Pistol:
case LaraWeaponType::Revolver: case LaraWeaponType::Revolver:
@ -612,7 +618,7 @@ void HandleWeapon(ItemInfo* laraItem)
if (Camera.type != CameraType::Look && Camera.type != CameraType::Heavy) if (Camera.type != CameraType::Look && Camera.type != CameraType::Heavy)
Camera.type = CameraType::Combat; Camera.type = CameraType::Combat;
DrawPistols(laraItem, lara.Control.Weapon.GunType); DrawPistols(laraItem, player.Control.Weapon.GunType);
break; break;
case LaraWeaponType::Shotgun: case LaraWeaponType::Shotgun:
@ -624,7 +630,7 @@ void HandleWeapon(ItemInfo* laraItem)
if (Camera.type != CameraType::Look && Camera.type != CameraType::Heavy) if (Camera.type != CameraType::Look && Camera.type != CameraType::Heavy)
Camera.type = CameraType::Combat; Camera.type = CameraType::Combat;
DrawShotgun(laraItem, lara.Control.Weapon.GunType); DrawShotgun(laraItem, player.Control.Weapon.GunType);
break; break;
case LaraWeaponType::Flare: case LaraWeaponType::Flare:
@ -632,7 +638,7 @@ void HandleWeapon(ItemInfo* laraItem)
break; break;
default: default:
lara.Control.HandStatus = HandStatus::Free; player.Control.HandStatus = HandStatus::Free;
break; break;
} }
@ -643,14 +649,14 @@ void HandleWeapon(ItemInfo* laraItem)
break; break;
case HandStatus::WeaponUndraw: case HandStatus::WeaponUndraw:
laraItem->Model.MeshIndex[LM_HEAD] = laraItem->Model.BaseMesh + LM_HEAD; laraItem.Model.MeshIndex[LM_HEAD] = laraItem.Model.BaseMesh + LM_HEAD;
switch (lara.Control.Weapon.GunType) switch (player.Control.Weapon.GunType)
{ {
case LaraWeaponType::Pistol: case LaraWeaponType::Pistol:
case LaraWeaponType::Revolver: case LaraWeaponType::Revolver:
case LaraWeaponType::Uzi: case LaraWeaponType::Uzi:
UndrawPistols(laraItem, lara.Control.Weapon.GunType); UndrawPistols(laraItem, player.Control.Weapon.GunType);
break; break;
case LaraWeaponType::Shotgun: case LaraWeaponType::Shotgun:
@ -659,7 +665,7 @@ void HandleWeapon(ItemInfo* laraItem)
case LaraWeaponType::GrenadeLauncher: case LaraWeaponType::GrenadeLauncher:
case LaraWeaponType::RocketLauncher: case LaraWeaponType::RocketLauncher:
case LaraWeaponType::HarpoonGun: case LaraWeaponType::HarpoonGun:
UndrawShotgun(laraItem, lara.Control.Weapon.GunType); UndrawShotgun(laraItem, player.Control.Weapon.GunType);
break; break;
case LaraWeaponType::Flare: case LaraWeaponType::Flare:
@ -674,9 +680,13 @@ void HandleWeapon(ItemInfo* laraItem)
case HandStatus::WeaponReady: case HandStatus::WeaponReady:
if (!IsHeld(In::Action)) if (!IsHeld(In::Action))
laraItem->Model.MeshIndex[LM_HEAD] = laraItem->Model.BaseMesh + LM_HEAD; {
laraItem.Model.MeshIndex[LM_HEAD] = laraItem.Model.BaseMesh + LM_HEAD;
}
else else
laraItem->Model.MeshIndex[LM_HEAD] = Objects[ID_LARA_SCREAM].meshIndex + LM_HEAD; {
laraItem.Model.MeshIndex[LM_HEAD] = Objects[ID_LARA_SCREAM].meshIndex + LM_HEAD;
}
if (Camera.type != CameraType::Look && if (Camera.type != CameraType::Look &&
Camera.type != CameraType::Heavy) Camera.type != CameraType::Heavy)
@ -686,19 +696,19 @@ void HandleWeapon(ItemInfo* laraItem)
if (IsHeld(In::Action) && !LaserSight) if (IsHeld(In::Action) && !LaserSight)
{ {
if (!GetAmmo(lara, lara.Control.Weapon.GunType)) if (!GetAmmo(player, player.Control.Weapon.GunType))
{ {
bool hasPistols = (lara.Weapons[(int)LaraWeaponType::Pistol].Present && Objects[ID_PISTOLS_ITEM].loaded); bool hasPistols = (player.Weapons[(int)LaraWeaponType::Pistol].Present && Objects[ID_PISTOLS_ITEM].loaded);
lara.Control.Weapon.RequestGunType = hasPistols ? LaraWeaponType::Pistol : LaraWeaponType::None; player.Control.Weapon.RequestGunType = hasPistols ? LaraWeaponType::Pistol : LaraWeaponType::None;
return; return;
} }
} }
switch (lara.Control.Weapon.GunType) switch (player.Control.Weapon.GunType)
{ {
case LaraWeaponType::Pistol: case LaraWeaponType::Pistol:
case LaraWeaponType::Uzi: case LaraWeaponType::Uzi:
PistolHandler(laraItem, lara.Control.Weapon.GunType); HandlePistols(laraItem, player.Control.Weapon.GunType);
break; break;
case LaraWeaponType::Shotgun: case LaraWeaponType::Shotgun:
@ -708,8 +718,8 @@ void HandleWeapon(ItemInfo* laraItem)
case LaraWeaponType::RocketLauncher: case LaraWeaponType::RocketLauncher:
case LaraWeaponType::HarpoonGun: case LaraWeaponType::HarpoonGun:
case LaraWeaponType::Revolver: case LaraWeaponType::Revolver:
RifleHandler(laraItem, lara.Control.Weapon.GunType); RifleHandler(laraItem, player.Control.Weapon.GunType);
LasersightWeaponHandler(laraItem, lara.Control.Weapon.GunType); LasersightWeaponHandler(laraItem, player.Control.Weapon.GunType);
break; break;
default: default:
@ -719,43 +729,43 @@ void HandleWeapon(ItemInfo* laraItem)
break; break;
case HandStatus::Free: case HandStatus::Free:
if (lara.Control.Weapon.GunType == LaraWeaponType::Flare) if (player.Control.Weapon.GunType == LaraWeaponType::Flare)
{ {
if (lara.Vehicle != NO_ITEM || TestState(laraItem->Animation.ActiveState, FlarePoseStates)) if (player.Context.Vehicle != NO_ITEM || TestState(laraItem.Animation.ActiveState, FlarePoseStates))
{ {
if (lara.Flare.ControlLeft) if (player.Flare.ControlLeft)
{ {
if (lara.LeftArm.FrameNumber) if (player.LeftArm.FrameNumber)
{ {
if (++lara.LeftArm.FrameNumber == 110) if (++player.LeftArm.FrameNumber == 110)
lara.LeftArm.FrameNumber = 0; player.LeftArm.FrameNumber = 0;
} }
} }
else else
{ {
lara.LeftArm.FrameNumber = 95; player.LeftArm.FrameNumber = 95;
lara.Flare.ControlLeft = true; player.Flare.ControlLeft = true;
} }
} }
else else
{ {
lara.Flare.ControlLeft = false; player.Flare.ControlLeft = false;
} }
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
SetFlareArm(laraItem, lara.LeftArm.FrameNumber); SetFlareArm(laraItem, player.LeftArm.FrameNumber);
} }
break; break;
case HandStatus::Busy: case HandStatus::Busy:
if (lara.Control.Weapon.GunType == LaraWeaponType::Flare) if (player.Control.Weapon.GunType == LaraWeaponType::Flare)
{ {
if (laraItem->Model.MeshIndex[LM_LHAND] == Objects[ID_FLARE_ANIM].meshIndex + LM_LHAND) if (laraItem.Model.MeshIndex[LM_LHAND] == Objects[ID_FLARE_ANIM].meshIndex + LM_LHAND)
{ {
lara.Flare.ControlLeft = (lara.Vehicle != NO_ITEM || TestState(laraItem->Animation.ActiveState, FlarePoseStates)); player.Flare.ControlLeft = (player.Context.Vehicle != NO_ITEM || TestState(laraItem.Animation.ActiveState, FlarePoseStates));
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
SetFlareArm(laraItem, lara.LeftArm.FrameNumber); SetFlareArm(laraItem, player.LeftArm.FrameNumber);
} }
} }
@ -763,18 +773,19 @@ void HandleWeapon(ItemInfo* laraItem)
} }
} }
void AimWeapon(ItemInfo* laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo) void AimWeapon(ItemInfo& laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo)
{ {
const auto& lara = *GetLaraInfo(laraItem); const auto& player = *GetLaraInfo(&laraItem);
auto targetArmOrient = arm.Locked ? lara.TargetArmOrient : EulerAngles::Zero; auto targetArmOrient = arm.Locked ? player.TargetArmOrient : EulerAngles::Zero;
arm.Orientation.InterpolateConstant(targetArmOrient, weaponInfo.AimSpeed); arm.Orientation.InterpolateConstant(targetArmOrient, weaponInfo.AimSpeed);
} }
FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, ItemInfo* laraItem, const EulerAngles& armOrient) // TODO: Include snowmobile gun in GetAmmo(), otherwise the player won't be able to shoot while controlling it. -- TokyoSU 2023.04.21
FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo& targetEntity, ItemInfo& laraItem, const EulerAngles& armOrient)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
auto& ammo = GetAmmo(lara, weaponType); auto& ammo = GetAmmo(player, weaponType);
if (ammo.GetCount() == 0 && !ammo.HasInfinite()) if (ammo.GetCount() == 0 && !ammo.HasInfinite())
return FireWeaponType::NoAmmo; return FireWeaponType::NoAmmo;
@ -789,8 +800,8 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
armOrient.y + (Random::GenerateAngle(0, ANGLE(180.0f)) - ANGLE(90.0f)) * weapon.ShotAccuracy / 65536, armOrient.y + (Random::GenerateAngle(0, ANGLE(180.0f)) - ANGLE(90.0f)) * weapon.ShotAccuracy / 65536,
0); 0);
auto muzzleOffset = GetJointPosition(laraItem, LM_RHAND); auto muzzleOffset = GetJointPosition(&laraItem, LM_RHAND);
auto pos = Vector3i(laraItem->Pose.Position.x, muzzleOffset.y, laraItem->Pose.Position.z); auto pos = Vector3i(laraItem.Pose.Position.x, muzzleOffset.y, laraItem.Pose.Position.z);
// Calculate ray from wobbled orientation. // Calculate ray from wobbled orientation.
auto directionNorm = wobbledArmOrient.ToDirection(); auto directionNorm = wobbledArmOrient.ToDirection();
@ -798,7 +809,7 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
auto target = origin + (directionNorm * weapon.TargetDist); auto target = origin + (directionNorm * weapon.TargetDist);
auto ray = Ray(origin, directionNorm); auto ray = Ray(origin, directionNorm);
int num = GetSpheres(targetEntity, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity); int num = GetSpheres(&targetEntity, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
int bestJointIndex = NO_JOINT; int bestJointIndex = NO_JOINT;
float bestDistance = INFINITY; float bestDistance = INFINITY;
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
@ -815,11 +826,11 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
} }
} }
lara.Control.Weapon.HasFired = true; player.Control.Weapon.HasFired = true;
lara.Control.Weapon.Fired = true; player.Control.Weapon.Fired = true;
auto vOrigin = GameVector(pos); auto vOrigin = GameVector(pos);
short roomNumber = laraItem->RoomNumber; short roomNumber = laraItem.RoomNumber;
GetFloor(pos.x, pos.y, pos.z, &roomNumber); GetFloor(pos.x, pos.y, pos.z, &roomNumber);
vOrigin.RoomNumber = roomNumber; vOrigin.RoomNumber = roomNumber;
@ -838,46 +849,47 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
// NOTE: It seems that entities hit by the player in the normal way must have GetTargetOnLOS return false. // NOTE: It seems that entities hit by the player in the normal way must have GetTargetOnLOS return false.
// It's strange, but this replicates original behaviour until we fully understand what is happening. // It's strange, but this replicates original behaviour until we fully understand what is happening.
if (!GetTargetOnLOS(&vOrigin, &vTarget, false, true)) if (!GetTargetOnLOS(&vOrigin, &vTarget, false, true))
HitTarget(laraItem, targetEntity, &vTarget, weapon.Damage, false, bestJointIndex); HitTarget(&laraItem, &targetEntity, &vTarget, weapon.Damage, false, bestJointIndex);
return FireWeaponType::PossibleHit; return FireWeaponType::PossibleHit;
} }
} }
void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo) void FindNewTarget(ItemInfo& laraItem, const WeaponInfo& weaponInfo)
{ {
if (!g_Configuration.AutoTarget) if (!g_Configuration.AutoTarget)
return; return;
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
if (BinocularRange) if (BinocularRange)
{ {
lara.TargetEntity = nullptr; player.TargetEntity = nullptr;
return; return;
} }
auto origin = GameVector( auto origin = GameVector(
laraItem->Pose.Position.x, laraItem.Pose.Position.x,
GetJointPosition(laraItem, LM_RHAND).y, // Muzzle offset. GetJointPosition(&laraItem, LM_RHAND).y, // Muzzle offset.
laraItem->Pose.Position.z, laraItem.Pose.Position.z,
laraItem->RoomNumber); laraItem.RoomNumber);
ItemInfo* bestEntity = nullptr; ItemInfo* closestEntityPtr = nullptr;
float bestDistance = INFINITY;
short bestYOrient = MAXSHORT; float closestDistance = INFINITY;
unsigned int numTargets = 0; short closestHeadingAngle = MAXSHORT;
unsigned int targetCount = 0;
float maxDistance = weaponInfo.TargetDist; float maxDistance = weaponInfo.TargetDist;
for (auto* activeCreature : ActiveCreatures) for (auto* creaturePtr : ActiveCreatures)
{ {
// Continue loop if no item. // Continue loop if no item.
if (activeCreature->ItemNumber == NO_ITEM) if (creaturePtr->ItemNumber == NO_ITEM)
continue; continue;
auto& item = g_Level.Items[activeCreature->ItemNumber]; auto& item = g_Level.Items[creaturePtr->ItemNumber];
// Check whether creature is alive. // Check if creature is alive.
if (item.HitPoints <= 0) if (item.HitPoints <= 0)
continue; continue;
@ -887,64 +899,64 @@ void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
continue; continue;
// Assess line of sight. // Assess line of sight.
auto target = GetTargetPoint(&item); auto target = GetTargetPoint(item);
if (!LOS(&origin, &target)) if (!LOS(&origin, &target))
continue; continue;
// Assess whether relative orientation falls within weapon's lock constraints. // Assess whether relative orientation falls within weapon's lock constraints.
auto orient = Geometry::GetOrientToPoint(origin.ToVector3(), target.ToVector3()) - (laraItem->Pose.Orientation + lara.ExtraTorsoRot); auto orient = Geometry::GetOrientToPoint(origin.ToVector3(), target.ToVector3()) - (laraItem.Pose.Orientation + player.ExtraTorsoRot);
if (orient.x >= weaponInfo.LockOrientConstraint.first.x && if (orient.x >= weaponInfo.LockOrientConstraint.first.x &&
orient.y >= weaponInfo.LockOrientConstraint.first.y && orient.y >= weaponInfo.LockOrientConstraint.first.y &&
orient.x <= weaponInfo.LockOrientConstraint.second.x && orient.x <= weaponInfo.LockOrientConstraint.second.x &&
orient.y <= weaponInfo.LockOrientConstraint.second.y) orient.y <= weaponInfo.LockOrientConstraint.second.y)
{ {
TargetList[numTargets] = &item; TargetList[targetCount] = &item;
++numTargets; ++targetCount;
if (distance < bestDistance && if (distance < closestDistance &&
abs(orient.y) < (bestYOrient + ANGLE(15.0f))) abs(orient.y) < (closestHeadingAngle + ANGLE(15.0f)))
{ {
bestEntity = &item; closestEntityPtr = &item;
bestDistance = distance; closestDistance = distance;
bestYOrient = abs(orient.y); closestHeadingAngle = abs(orient.y);
} }
} }
} }
TargetList[numTargets] = nullptr; TargetList[targetCount] = nullptr;
if (!TargetList[0]) if (TargetList[0] == nullptr)
{ {
lara.TargetEntity = nullptr; player.TargetEntity = nullptr;
} }
else else
{ {
for (int slot = 0; slot < MAX_TARGETS; ++slot) for (const auto* targetPtr : TargetList)
{ {
if (!TargetList[slot]) if (targetPtr == nullptr)
lara.TargetEntity = nullptr; player.TargetEntity = nullptr;
if (TargetList[slot] == lara.TargetEntity) if (targetPtr == player.TargetEntity)
break; break;
} }
if (lara.Control.HandStatus != HandStatus::Free || IsClicked(In::SwitchTarget)) if (IsClicked(In::SwitchTarget) || player.Control.HandStatus != HandStatus::Free)
{ {
if (lara.TargetEntity == nullptr) if (player.TargetEntity == nullptr)
{ {
lara.TargetEntity = bestEntity; player.TargetEntity = closestEntityPtr;
LastTargets[0] = nullptr; LastTargets[0] = nullptr;
} }
else if (IsClicked(In::SwitchTarget)) else if (IsClicked(In::SwitchTarget))
{ {
lara.TargetEntity = nullptr; player.TargetEntity = nullptr;
bool flag = true; bool flag = true;
for (int match = 0; match < MAX_TARGETS && TargetList[match]; ++match) for (const auto& targetPtr : TargetList)
{ {
bool doLoop = false; bool doLoop = false;
for (int slot = 0; slot < MAX_TARGETS && LastTargets[slot]; ++slot) for (const auto* lastTargetPtr : LastTargets)
{ {
if (LastTargets[slot] == TargetList[match]) if (lastTargetPtr == targetPtr)
{ {
doLoop = true; doLoop = true;
break; break;
@ -953,8 +965,8 @@ void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
if (!doLoop) if (!doLoop)
{ {
lara.TargetEntity = TargetList[match]; player.TargetEntity = targetPtr;
if (lara.TargetEntity) if (player.TargetEntity)
flag = false; flag = false;
break; break;
@ -963,44 +975,44 @@ void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
if (flag) if (flag)
{ {
lara.TargetEntity = bestEntity; player.TargetEntity = closestEntityPtr;
LastTargets[0] = nullptr; LastTargets[0] = nullptr;
} }
} }
} }
} }
if (lara.TargetEntity != LastTargets[0]) if (player.TargetEntity != LastTargets[0])
{ {
for (int slot = 7; slot > 0; --slot) for (int slot = TARGET_COUNT_MAX - 1; slot > 0; --slot)
LastTargets[slot] = LastTargets[slot - 1]; LastTargets[slot] = LastTargets[slot - 1];
LastTargets[0] = lara.TargetEntity; LastTargets[0] = player.TargetEntity;
} }
LaraTargetInfo(laraItem, weaponInfo); LaraTargetInfo(laraItem, weaponInfo);
} }
void LaraTargetInfo(ItemInfo* laraItem, const WeaponInfo& weaponInfo) void LaraTargetInfo(ItemInfo& laraItem, const WeaponInfo& weaponInfo)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
if (lara.TargetEntity == nullptr) if (player.TargetEntity == nullptr)
{ {
lara.RightArm.Locked = false; player.RightArm.Locked = false;
lara.LeftArm.Locked = false; player.LeftArm.Locked = false;
lara.TargetArmOrient = EulerAngles::Zero; player.TargetArmOrient = EulerAngles::Zero;
return; return;
} }
auto origin = GameVector( auto origin = GameVector(
laraItem->Pose.Position.x, laraItem.Pose.Position.x,
GetJointPosition(laraItem, LM_RHAND).y, // Muzzle offset. GetJointPosition(&laraItem, LM_RHAND).y, // Muzzle offset.
laraItem->Pose.Position.z, laraItem.Pose.Position.z,
laraItem->RoomNumber); laraItem.RoomNumber);
auto target = GetTargetPoint(lara.TargetEntity); auto target = GetTargetPoint(*player.TargetEntity);
auto orient = Geometry::GetOrientToPoint(origin.ToVector3(), target.ToVector3()) - laraItem->Pose.Orientation; auto orient = Geometry::GetOrientToPoint(origin.ToVector3(), target.ToVector3()) - laraItem.Pose.Orientation;
if (LOS(&origin, &target)) if (LOS(&origin, &target))
{ {
@ -1009,41 +1021,41 @@ void LaraTargetInfo(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
orient.x <= weaponInfo.LockOrientConstraint.second.x && orient.x <= weaponInfo.LockOrientConstraint.second.x &&
orient.y <= weaponInfo.LockOrientConstraint.second.y) orient.y <= weaponInfo.LockOrientConstraint.second.y)
{ {
lara.RightArm.Locked = true; player.RightArm.Locked = true;
lara.LeftArm.Locked = true; player.LeftArm.Locked = true;
} }
else else
{ {
if (lara.LeftArm.Locked) if (player.LeftArm.Locked)
{ {
if (orient.x < weaponInfo.LeftOrientConstraint.first.x || if (orient.x < weaponInfo.LeftOrientConstraint.first.x ||
orient.y < weaponInfo.LeftOrientConstraint.first.y || orient.y < weaponInfo.LeftOrientConstraint.first.y ||
orient.x > weaponInfo.LeftOrientConstraint.second.x || orient.x > weaponInfo.LeftOrientConstraint.second.x ||
orient.y > weaponInfo.LeftOrientConstraint.second.y) orient.y > weaponInfo.LeftOrientConstraint.second.y)
{ {
lara.LeftArm.Locked = false; player.LeftArm.Locked = false;
} }
} }
if (lara.RightArm.Locked) if (player.RightArm.Locked)
{ {
if (orient.x < weaponInfo.RightOrientConstraint.first.x || if (orient.x < weaponInfo.RightOrientConstraint.first.x ||
orient.y < weaponInfo.RightOrientConstraint.first.y || orient.y < weaponInfo.RightOrientConstraint.first.y ||
orient.x > weaponInfo.RightOrientConstraint.second.x || orient.x > weaponInfo.RightOrientConstraint.second.x ||
orient.y > weaponInfo.RightOrientConstraint.second.y) orient.y > weaponInfo.RightOrientConstraint.second.y)
{ {
lara.RightArm.Locked = false; player.RightArm.Locked = false;
} }
} }
} }
} }
else else
{ {
lara.RightArm.Locked = false; player.RightArm.Locked = false;
lara.LeftArm.Locked = false; player.LeftArm.Locked = false;
} }
lara.TargetArmOrient = orient; player.TargetArmOrient = orient;
} }
void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive, int bestJointIndex) void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive, int bestJointIndex)

View file

@ -5,8 +5,6 @@ class EulerAngles;
struct CollisionInfo; struct CollisionInfo;
struct ItemInfo; struct ItemInfo;
constexpr auto MAX_TARGETS = 8;
enum class FireWeaponType enum class FireWeaponType
{ {
Miss = -1, Miss = -1,
@ -48,20 +46,20 @@ struct WeaponInfo
extern int FlashGrenadeAftershockTimer; extern int FlashGrenadeAftershockTimer;
extern WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons]; extern WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons];
void InitialiseNewWeapon(ItemInfo* laraItem); void InitializeNewWeapon(ItemInfo& laraItem);
Ammo& GetAmmo(LaraInfo& lara, LaraWeaponType weaponType); Ammo& GetAmmo(LaraInfo& lara, LaraWeaponType weaponType);
GameVector GetTargetPoint(ItemInfo* targetEntity); GameVector GetTargetPoint(ItemInfo& targetEntity);
HolsterSlot GetWeaponHolsterSlot(LaraWeaponType weaponType); HolsterSlot GetWeaponHolsterSlot(LaraWeaponType weaponType);
GAME_OBJECT_ID GetWeaponObjectID(LaraWeaponType weaponType); GAME_OBJECT_ID GetWeaponObjectID(LaraWeaponType weaponType);
GAME_OBJECT_ID GetWeaponObjectMeshID(ItemInfo* laraItem, LaraWeaponType weaponType); GAME_OBJECT_ID GetWeaponObjectMeshID(ItemInfo& laraItem, LaraWeaponType weaponType);
void HandleWeapon(ItemInfo* laraItem); void HandleWeapon(ItemInfo& laraItem);
void AimWeapon(ItemInfo* laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo); void AimWeapon(ItemInfo& laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo);
FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, ItemInfo* laraItem, const EulerAngles& armOrient); FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo& targetEntity, ItemInfo& laraItem, const EulerAngles& armOrient);
void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo); void FindNewTarget(ItemInfo& laraItem, const WeaponInfo& weaponInfo);
void LaraTargetInfo(ItemInfo* laraItem, const WeaponInfo& weaponInfo); void LaraTargetInfo(ItemInfo& laraItem, const WeaponInfo& weaponInfo);
void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive, int bestJointIndex = NO_JOINT); void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive, int bestJointIndex = NO_JOINT);
void SmashItem(short itemNumber); void SmashItem(short itemNumber);

View file

@ -4,6 +4,7 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/camera.h" #include "Game/camera.h"
#include "Game/collision/collide_item.h" #include "Game/collision/collide_item.h"
#include "Game/control/control.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/effects/chaffFX.h" #include "Game/effects/chaffFX.h"
#include "Game/items.h" #include "Game/items.h"
@ -11,16 +12,16 @@
#include "Game/Lara/lara_fire.h" #include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_tests.h" #include "Game/Lara/lara_tests.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Math; using namespace TEN::Math;
constexpr auto FLARE_LIFE_MAX = 60 * FPS; constexpr auto FLARE_LIFE_MAX = 60.0f * FPS;
constexpr auto FLARE_LIGHT_COLOR = Vector3(0.8f, 0.42947f, 0.2921f); constexpr auto FLARE_LIGHT_COLOR = Vector3(0.8f, 0.43f, 0.3f);
void FlareControl(short itemNumber) void FlareControl(short itemNumber)
{ {
@ -43,13 +44,13 @@ void FlareControl(short itemNumber)
flareItem.Pose.Orientation.z = 0; flareItem.Pose.Orientation.z = 0;
} }
auto velocity = Vector3i( auto vel = Vector3i(
flareItem.Animation.Velocity.z * phd_sin(flareItem.Pose.Orientation.y), flareItem.Animation.Velocity.z * phd_sin(flareItem.Pose.Orientation.y),
flareItem.Animation.Velocity.y, flareItem.Animation.Velocity.y,
flareItem.Animation.Velocity.z * phd_cos(flareItem.Pose.Orientation.y)); flareItem.Animation.Velocity.z * phd_cos(flareItem.Pose.Orientation.y));
auto prevPos = flareItem.Pose.Position; auto prevPos = flareItem.Pose.Position;
flareItem.Pose.Position += Vector3i(velocity.x, 0, velocity.z); flareItem.Pose.Position += Vector3i(vel.x, 0, vel.z);
if (TestEnvironment(ENV_FLAG_WATER, &flareItem) || if (TestEnvironment(ENV_FLAG_WATER, &flareItem) ||
TestEnvironment(ENV_FLAG_SWAMP, &flareItem)) TestEnvironment(ENV_FLAG_SWAMP, &flareItem))
@ -63,13 +64,14 @@ void FlareControl(short itemNumber)
} }
flareItem.Pose.Position.y += flareItem.Animation.Velocity.y; flareItem.Pose.Position.y += flareItem.Animation.Velocity.y;
DoProjectileDynamics(itemNumber, prevPos.x, prevPos.y, prevPos.z, velocity.x, velocity.y, velocity.z); DoProjectileDynamics(itemNumber, prevPos.x, prevPos.y, prevPos.z, vel.x, vel.y, vel.z);
int& life = flareItem.Data; int& life = flareItem.Data;
life &= 0x7FFF; life &= 0x7FFF;
if (life >= FLARE_LIFE_MAX) if (life >= FLARE_LIFE_MAX)
{ {
if (!flareItem.Animation.Velocity.y && !flareItem.Animation.Velocity.z) if (flareItem.Animation.Velocity.y == 0.0f &&
flareItem.Animation.Velocity.z == 0.0f)
{ {
KillItem(itemNumber); KillItem(itemNumber);
return; return;
@ -83,91 +85,89 @@ void FlareControl(short itemNumber)
if (DoFlareLight(flareItem.Pose.Position, life)) if (DoFlareLight(flareItem.Pose.Position, life))
{ {
TriggerChaffEffects(flareItem, life); TriggerChaffEffects(flareItem, life);
/* Hardcoded code */
life |= 0x8000; life |= 0x8000;
} }
} }
void ReadyFlare(ItemInfo* laraItem) void ReadyFlare(ItemInfo& laraItem)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
lara.Control.HandStatus = HandStatus::Free; player.Control.HandStatus = HandStatus::Free;
lara.LeftArm.Orientation = EulerAngles::Zero; player.LeftArm.Orientation =
lara.RightArm.Orientation = EulerAngles::Zero; player.RightArm.Orientation = EulerAngles::Zero;
lara.LeftArm.Locked = false; player.LeftArm.Locked =
lara.RightArm.Locked = false; player.RightArm.Locked = false;
lara.TargetEntity = nullptr; player.TargetEntity = nullptr;
} }
void UndrawFlareMeshes(ItemInfo* laraItem) void UndrawFlareMeshes(ItemInfo& laraItem)
{ {
laraItem->Model.MeshIndex[LM_LHAND] = laraItem->Model.BaseMesh + LM_LHAND; laraItem.Model.MeshIndex[LM_LHAND] = laraItem.Model.BaseMesh + LM_LHAND;
} }
void DrawFlareMeshes(ItemInfo* laraItem) void DrawFlareMeshes(ItemInfo& laraItem)
{ {
laraItem->Model.MeshIndex[LM_LHAND] = Objects[ID_FLARE_ANIM].meshIndex + LM_LHAND; laraItem.Model.MeshIndex[LM_LHAND] = Objects[ID_FLARE_ANIM].meshIndex + LM_LHAND;
} }
void UndrawFlare(ItemInfo* laraItem) void UndrawFlare(ItemInfo& laraItem)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
int flareFrame = lara.Flare.Frame; int flareFrame = player.Flare.Frame;
int armFrame = lara.LeftArm.FrameNumber; int armFrame = player.LeftArm.FrameNumber;
lara.Flare.ControlLeft = true; player.Flare.ControlLeft = true;
if (laraItem->Animation.TargetState == LS_IDLE && if (laraItem.Animation.TargetState == LS_IDLE &&
lara.Vehicle == NO_ITEM) player.Context.Vehicle == NO_ITEM)
{ {
if (laraItem->Animation.AnimNumber == LA_STAND_IDLE) if (laraItem.Animation.AnimNumber == LA_STAND_IDLE)
{ {
laraItem->Animation.AnimNumber = LA_DISCARD_FLARE; laraItem.Animation.AnimNumber = LA_DISCARD_FLARE;
flareFrame = armFrame + g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; flareFrame = armFrame + g_Level.Anims[laraItem.Animation.AnimNumber].frameBase;
laraItem->Animation.FrameNumber = flareFrame; laraItem.Animation.FrameNumber = flareFrame;
lara.Flare.Frame = flareFrame; player.Flare.Frame = flareFrame;
} }
if (laraItem->Animation.AnimNumber == LA_DISCARD_FLARE) if (laraItem.Animation.AnimNumber == LA_DISCARD_FLARE)
{ {
lara.Flare.ControlLeft = false; player.Flare.ControlLeft = false;
if (flareFrame >= (g_Level.Anims[laraItem->Animation.AnimNumber].frameBase + 31)) // 31 = Last frame. if (flareFrame >= (g_Level.Anims[laraItem.Animation.AnimNumber].frameBase + 31)) // 31 = Last frame.
{ {
lara.Control.Weapon.RequestGunType = lara.Control.Weapon.LastGunType; player.Control.Weapon.RequestGunType = player.Control.Weapon.LastGunType;
lara.Control.Weapon.GunType = lara.Control.Weapon.LastGunType; player.Control.Weapon.GunType = player.Control.Weapon.LastGunType;
lara.Control.HandStatus = HandStatus::Free; player.Control.HandStatus = HandStatus::Free;
InitialiseNewWeapon(laraItem); InitializeNewWeapon(laraItem);
lara.TargetEntity = nullptr; player.TargetEntity = nullptr;
lara.RightArm.Locked = false; player.LeftArm.Locked =
lara.LeftArm.Locked = false; player.RightArm.Locked = false;
SetAnimation(laraItem, LA_STAND_IDLE); SetAnimation(&laraItem, LA_STAND_IDLE);
lara.Flare.Frame = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; player.Flare.Frame = g_Level.Anims[laraItem.Animation.AnimNumber].frameBase;
return; return;
} }
lara.Flare.Frame++; player.Flare.Frame++;
} }
} }
else if (laraItem->Animation.AnimNumber == LA_DISCARD_FLARE) else if (laraItem.Animation.AnimNumber == LA_DISCARD_FLARE)
{ {
SetAnimation(laraItem, LA_STAND_IDLE); SetAnimation(&laraItem, LA_STAND_IDLE);
} }
if (armFrame >= 33 && armFrame < 72) if (armFrame >= 33 && armFrame < 72)
{ {
armFrame = 2; armFrame = 2;
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
} }
else if (!armFrame) else if (!armFrame)
{ {
armFrame = 1; armFrame = 1;
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
} }
else if (armFrame >= 72 && armFrame < 95) else if (armFrame >= 72 && armFrame < 95)
{ {
@ -176,7 +176,7 @@ void UndrawFlare(ItemInfo* laraItem)
if (armFrame == 94) if (armFrame == 94)
{ {
armFrame = 1; armFrame = 1;
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
} }
} }
else if (armFrame >= 1 && armFrame < 33) else if (armFrame >= 1 && armFrame < 33)
@ -187,27 +187,27 @@ void UndrawFlare(ItemInfo* laraItem)
{ {
CreateFlare(laraItem, ID_FLARE_ITEM, true); CreateFlare(laraItem, ID_FLARE_ITEM, true);
UndrawFlareMeshes(laraItem); UndrawFlareMeshes(laraItem);
lara.Flare.Life = 0; player.Flare.Life = 0;
} }
else if (armFrame == 33) else if (armFrame == 33)
{ {
armFrame = 0; armFrame = 0;
lara.Control.Weapon.RequestGunType = lara.Control.Weapon.LastGunType; player.Control.Weapon.RequestGunType = player.Control.Weapon.LastGunType;
lara.Control.Weapon.GunType = lara.Control.Weapon.LastGunType; player.Control.Weapon.GunType = player.Control.Weapon.LastGunType;
lara.Control.HandStatus = HandStatus::Free; player.Control.HandStatus = HandStatus::Free;
InitialiseNewWeapon(laraItem); InitializeNewWeapon(laraItem);
lara.TargetEntity = nullptr; player.TargetEntity = nullptr;
lara.LeftArm.Locked = false; player.LeftArm.Locked =
lara.RightArm.Locked = false; player.RightArm.Locked = false;
lara.Flare.ControlLeft = false; player.Flare.ControlLeft = false;
lara.Flare.Frame = 0; player.Flare.Frame = 0;
} }
else if (armFrame < 21) else if (armFrame < 21)
{ {
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
} }
} }
else if (armFrame >= 95 && armFrame < 110) else if (armFrame >= 95 && armFrame < 110)
@ -217,30 +217,30 @@ void UndrawFlare(ItemInfo* laraItem)
if (armFrame == 110) if (armFrame == 110)
{ {
armFrame = 1; armFrame = 1;
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
} }
} }
lara.LeftArm.FrameNumber = armFrame; player.LeftArm.FrameNumber = armFrame;
SetFlareArm(laraItem, lara.LeftArm.FrameNumber); SetFlareArm(laraItem, player.LeftArm.FrameNumber);
} }
void DrawFlare(ItemInfo* laraItem) void DrawFlare(ItemInfo& laraItem)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
if (laraItem->Animation.ActiveState == LS_PICKUP_FLARE || if (laraItem.Animation.ActiveState == LS_PICKUP_FLARE ||
laraItem->Animation.ActiveState == LS_PICKUP) laraItem.Animation.ActiveState == LS_PICKUP)
{ {
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
lara.Flare.ControlLeft = false; player.Flare.ControlLeft = false;
lara.LeftArm.FrameNumber = 93; player.LeftArm.FrameNumber = 93;
SetFlareArm(laraItem, 93); SetFlareArm(laraItem, 93);
} }
else else
{ {
int armFrame = lara.LeftArm.FrameNumber + 1; int armFrame = player.LeftArm.FrameNumber + 1;
lara.Flare.ControlLeft = true; player.Flare.ControlLeft = true;
if (armFrame < 33 || armFrame > 94) if (armFrame < 33 || armFrame > 94)
{ {
@ -254,11 +254,14 @@ void DrawFlare(ItemInfo* laraItem)
{ {
if (armFrame == 72) if (armFrame == 72)
{ {
SoundEffect(SFX_TR4_FLARE_IGNITE_DRY, &laraItem->Pose, TestEnvironment(ENV_FLAG_WATER, laraItem) ? SoundEnvironment::Water : SoundEnvironment::Land); player.Flare.Life = 1;
lara.Flare.Life = 1; SoundEffect(
SFX_TR4_FLARE_IGNITE_DRY,
&laraItem.Pose,
TestEnvironment(ENV_FLAG_WATER, &laraItem) ? SoundEnvironment::Water : SoundEnvironment::Land);
} }
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
} }
else else
{ {
@ -266,18 +269,18 @@ void DrawFlare(ItemInfo* laraItem)
{ {
ReadyFlare(laraItem); ReadyFlare(laraItem);
armFrame = 0; armFrame = 0;
DoFlareInHand(laraItem, lara.Flare.Life); DoFlareInHand(laraItem, player.Flare.Life);
} }
} }
lara.LeftArm.FrameNumber = armFrame; player.LeftArm.FrameNumber = armFrame;
SetFlareArm(laraItem, armFrame); SetFlareArm(laraItem, armFrame);
} }
} }
void SetFlareArm(ItemInfo* laraItem, int armFrame) void SetFlareArm(ItemInfo& laraItem, int armFrame)
{ {
auto& lara = *GetLaraInfo(laraItem); auto& player = *GetLaraInfo(&laraItem);
int flareAnimNum = Objects[ID_FLARE_ANIM].animIndex; int flareAnimNum = Objects[ID_FLARE_ANIM].animIndex;
if (armFrame >= 95) if (armFrame >= 95)
@ -297,13 +300,13 @@ void SetFlareArm(ItemInfo* laraItem, int armFrame)
flareAnimNum += 1; flareAnimNum += 1;
} }
lara.LeftArm.AnimNumber = flareAnimNum; player.LeftArm.AnimNumber = flareAnimNum;
lara.LeftArm.FrameBase = g_Level.Anims[flareAnimNum].FramePtr; player.LeftArm.FrameBase = g_Level.Anims[flareAnimNum].FramePtr;
} }
void CreateFlare(ItemInfo* laraItem, GAME_OBJECT_ID objectNumber, bool isThrown) void CreateFlare(ItemInfo& laraItem, GAME_OBJECT_ID objectID, bool isThrown)
{ {
const auto& lara = *GetLaraInfo(laraItem); const auto& lara = *GetLaraInfo(&laraItem);
auto itemNumber = CreateItem(); auto itemNumber = CreateItem();
if (itemNumber == NO_ITEM) if (itemNumber == NO_ITEM)
@ -311,36 +314,40 @@ void CreateFlare(ItemInfo* laraItem, GAME_OBJECT_ID objectNumber, bool isThrown)
auto& flareItem = g_Level.Items[itemNumber]; auto& flareItem = g_Level.Items[itemNumber];
flareItem.ObjectNumber = objectNumber; flareItem.ObjectNumber = objectID;
flareItem.RoomNumber = laraItem->RoomNumber; flareItem.RoomNumber = laraItem.RoomNumber;
auto pos = GetJointPosition(laraItem, LM_LHAND, Vector3i(-16, 32, 42)); auto pos = GetJointPosition(&laraItem, LM_LHAND, Vector3i(-16, 32, 42));
flareItem.Pose.Position = pos; flareItem.Pose.Position = pos;
int floorHeight = GetCollision(pos.x, pos.y, pos.z, laraItem->RoomNumber).Position.Floor; int floorHeight = GetCollision(pos.x, pos.y, pos.z, laraItem.RoomNumber).Position.Floor;
auto hasCollided = GetCollidedObjects(&flareItem, 0, true, CollidedItems, CollidedMeshes, true); auto hasCollided = GetCollidedObjects(&flareItem, 0, true, CollidedItems, CollidedMeshes, true);
bool hasLanded = false; bool hasLanded = false;
if (floorHeight < pos.y || hasCollided) if (floorHeight < pos.y || hasCollided)
{ {
hasLanded = true; hasLanded = true;
flareItem.Pose.Position.x = laraItem->Pose.Position.x + 320 * phd_sin(flareItem.Pose.Orientation.y); flareItem.Pose.Position.x = laraItem.Pose.Position.x + 320 * phd_sin(flareItem.Pose.Orientation.y);
flareItem.Pose.Position.z = laraItem->Pose.Position.z + 320 * phd_cos(flareItem.Pose.Orientation.y); flareItem.Pose.Position.z = laraItem.Pose.Position.z + 320 * phd_cos(flareItem.Pose.Orientation.y);
flareItem.Pose.Orientation.y = laraItem->Pose.Orientation.y + ANGLE(180.0f); flareItem.Pose.Orientation.y = laraItem.Pose.Orientation.y + ANGLE(180.0f);
flareItem.RoomNumber = laraItem->RoomNumber; flareItem.RoomNumber = laraItem.RoomNumber;
} }
else else
{ {
if (isThrown) if (isThrown)
flareItem.Pose.Orientation.y = laraItem->Pose.Orientation.y; {
flareItem.Pose.Orientation.y = laraItem.Pose.Orientation.y;
}
else else
flareItem.Pose.Orientation.y = laraItem->Pose.Orientation.y - ANGLE(45.0f); {
flareItem.Pose.Orientation.y = laraItem.Pose.Orientation.y - ANGLE(45.0f);
flareItem.RoomNumber = laraItem->RoomNumber;
} }
InitialiseItem(itemNumber); flareItem.RoomNumber = laraItem.RoomNumber;
}
InitializeItem(itemNumber);
flareItem.Pose.Orientation.x = 0; flareItem.Pose.Orientation.x = 0;
flareItem.Pose.Orientation.z = 0; flareItem.Pose.Orientation.z = 0;
@ -348,19 +355,19 @@ void CreateFlare(ItemInfo* laraItem, GAME_OBJECT_ID objectNumber, bool isThrown)
if (isThrown) if (isThrown)
{ {
flareItem.Animation.Velocity.z = laraItem->Animation.Velocity.z + 50; flareItem.Animation.Velocity.z = laraItem.Animation.Velocity.z + 50;
flareItem.Animation.Velocity.y = laraItem->Animation.Velocity.y - 50; flareItem.Animation.Velocity.y = laraItem.Animation.Velocity.y - 50;
} }
else else
{ {
flareItem.Animation.Velocity.z = laraItem->Animation.Velocity.z + 10; flareItem.Animation.Velocity.z = laraItem.Animation.Velocity.z + 10;
flareItem.Animation.Velocity.y = laraItem->Animation.Velocity.y + 50; flareItem.Animation.Velocity.y = laraItem.Animation.Velocity.y + 50;
} }
if (hasLanded) if (hasLanded)
flareItem.Animation.Velocity.z /= 2; flareItem.Animation.Velocity.z /= 2;
if (objectNumber == ID_FLARE_ITEM) if (objectID == ID_FLARE_ITEM)
{ {
flareItem.Data = (int)0; flareItem.Data = (int)0;
int& life = flareItem.Data; int& life = flareItem.Data;
@ -379,28 +386,21 @@ void CreateFlare(ItemInfo* laraItem, GAME_OBJECT_ID objectNumber, bool isThrown)
flareItem.Status = ITEM_ACTIVE; flareItem.Status = ITEM_ACTIVE;
} }
void DrawFlareInAir(ItemInfo* flareItem) void DoFlareInHand(ItemInfo& laraItem, int flareLife)
{ {
TENLog("DrawFlareInAir() not implemented!", LogLevel::Warning); auto& lara = *GetLaraInfo(&laraItem);
}
void DoFlareInHand(ItemInfo* laraItem, int flareLife) auto pos = GetJointPosition(&laraItem, LM_LHAND, Vector3i(11, 32, 41));
{
auto& lara = *GetLaraInfo(laraItem);
auto pos = GetJointPosition(laraItem, LM_LHAND, Vector3i(11, 32, 41));
if (DoFlareLight(pos, flareLife)) if (DoFlareLight(pos, flareLife))
TriggerChaffEffects(BinocularOn ? 0 : flareLife); TriggerChaffEffects(BinocularOn ? 0 : flareLife);
/* Hardcoded code */
if (lara.Flare.Life >= FLARE_LIFE_MAX) if (lara.Flare.Life >= FLARE_LIFE_MAX)
{ {
// Prevent Lara from intercepting reach/jump states with flare throws. // Prevent player from intercepting reach/jump states with flare throws.
if (laraItem->Animation.IsAirborne || if (laraItem.Animation.IsAirborne ||
laraItem->Animation.TargetState == LS_JUMP_PREPARE || laraItem.Animation.TargetState == LS_JUMP_PREPARE ||
laraItem->Animation.TargetState == LS_JUMP_FORWARD) laraItem.Animation.TargetState == LS_JUMP_FORWARD)
{ {
return; return;
} }
@ -419,53 +419,39 @@ bool DoFlareLight(const Vector3i& pos, int flareLife)
if (flareLife >= FLARE_LIFE_MAX || flareLife == 0) if (flareLife >= FLARE_LIFE_MAX || flareLife == 0)
return false; return false;
float randomFloat = Random::GenerateFloat(); auto sphere = BoundingSphere(pos.ToVector3() - Vector3(0, BLOCK(1 / 8.0f), 0), BLOCK(1 / 16.0f));
auto lightPos = Random::GeneratePointInSphere(sphere);
auto lightPos = pos + Vector3i( bool spawnChaff = false;
randomFloat * 120,
(randomFloat * 120) - CLICK(1),
randomFloat * 120);
bool result = false;
bool isEnding = (flareLife > (FLARE_LIFE_MAX - 90)); bool isEnding = (flareLife > (FLARE_LIFE_MAX - 90));
bool isDying = (flareLife > (FLARE_LIFE_MAX - 5)); bool isDying = (flareLife > (FLARE_LIFE_MAX - 5));
if (isDying) if (isDying)
{ {
int falloff = 6 * (1.0f - (flareLife / FLARE_LIFE_MAX)); int falloff = (1.0f - (flareLife / FLARE_LIFE_MAX)) * 6;
auto color = FLARE_LIGHT_COLOR * 255;
TriggerDynamicLight(lightPos.x, lightPos.y, lightPos.z, falloff, color.x, color.y, color.z);
int r = FLARE_LIGHT_COLOR.x * 255; spawnChaff = Random::TestProbability(9 / 10.0f);
int g = FLARE_LIGHT_COLOR.y * 255;
int b = FLARE_LIGHT_COLOR.z * 255;
TriggerDynamicLight(lightPos.x, lightPos.y, lightPos.z, falloff, r, g, b);
result = (randomFloat < 0.9f);
} }
else if (isEnding) else if (isEnding)
{ {
float multiplier = Random::GenerateFloat(0.05f, 1.0f); float multiplier = Random::GenerateFloat(0.05f, 1.0f);
int falloff = 8 * multiplier; int falloff = multiplier * 8;
auto color = (FLARE_LIGHT_COLOR * multiplier) * 255;
TriggerDynamicLight(lightPos.x, lightPos.y, lightPos.z, falloff, color.x, color.y, color.z);
int r = FLARE_LIGHT_COLOR.x * 255 * multiplier; spawnChaff = Random::TestProbability(2 / 5.0f);
int g = FLARE_LIGHT_COLOR.y * 255 * multiplier;
int b = FLARE_LIGHT_COLOR.z * 255 * multiplier;
TriggerDynamicLight(lightPos.x, lightPos.y, lightPos.z, falloff, r, g, b);
result = (randomFloat < 0.4f);
} }
else else
{ {
float multiplier = Random::GenerateFloat(0.6f, 0.8f); float multiplier = Random::GenerateFloat(0.6f, 0.8f);
int falloff = 8 * (1.0f - (flareLife / FLARE_LIFE_MAX)); int falloff = (1.0f - (flareLife / FLARE_LIFE_MAX)) * 8;
auto color = (FLARE_LIGHT_COLOR * multiplier) * 255;
TriggerDynamicLight(lightPos.x, lightPos.y, lightPos.z, falloff, color.x, color.y, color.z);
int r = FLARE_LIGHT_COLOR.x * 255 * multiplier; spawnChaff = Random::TestProbability(3 / 10.0f);
int g = FLARE_LIGHT_COLOR.y * 255 * multiplier;
int b = FLARE_LIGHT_COLOR.z * 255 * multiplier;
TriggerDynamicLight(lightPos.x, lightPos.y, lightPos.z, falloff, r, g, b);
result = (randomFloat < 0.3f);
} }
return ((isDying || isEnding) ? result : true); return ((isDying || isEnding) ? spawnChaff : true);
} }

View file

@ -1,19 +1,21 @@
#pragma once #pragma once
#include "Game/control/control.h"
enum GAME_OBJECT_ID : short;
class Vector3i;
struct CollisionInfo; struct CollisionInfo;
struct ItemInfo; struct ItemInfo;
class Vector3i;
enum GAME_OBJECT_ID : short;
void FlareControl(short itemNumber); void FlareControl(short itemNumber);
void ReadyFlare(ItemInfo* laraItem); void ReadyFlare(ItemInfo& laraItem);
void UndrawFlareMeshes(ItemInfo* laraItem);
void DrawFlareMeshes(ItemInfo* laraItem); void UndrawFlareMeshes(ItemInfo& laraItem);
void UndrawFlare(ItemInfo* laraItem); void DrawFlareMeshes(ItemInfo& laraItem);
void DrawFlare(ItemInfo* laraItem);
void SetFlareArm(ItemInfo* laraItem, int armFrame); void UndrawFlare(ItemInfo& laraItem);
void CreateFlare(ItemInfo* laraItem, GAME_OBJECT_ID objectNumber, bool isThrown); void DrawFlare(ItemInfo& laraItem);
void DrawFlareInAir(ItemInfo* flareItem);
void DoFlareInHand(ItemInfo* laraItem, int flareLife); void SetFlareArm(ItemInfo& laraItem, int armFrame);
void CreateFlare(ItemInfo& laraItem, GAME_OBJECT_ID objectID, bool isThrown);
void DoFlareInHand(ItemInfo& laraItem, int flareLife);
bool DoFlareLight(const Vector3i& pos, int flareLife); bool DoFlareLight(const Vector3i& pos, int flareLife);

View file

@ -13,13 +13,13 @@
#include "Game/Lara/lara_collide.h" #include "Game/Lara/lara_collide.h"
#include "Game/Lara/lara_fire.h" #include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_tests.h" #include "Game/Lara/lara_tests.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "Scripting/Include/ScriptInterfaceLevel.h" #include "Scripting/Include/ScriptInterfaceLevel.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Objects/TR2/Vehicles/skidoo.h" #include "Objects/TR2/Vehicles/skidoo.h"
#include "Objects/TR3/Vehicles/big_gun.h" #include "Objects/TR3/Vehicles/big_gun.h"
@ -33,7 +33,7 @@
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
using namespace TEN::Effects::Bubble; using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::Drip; using namespace TEN::Effects::Drip;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer; using namespace TEN::Renderer;
@ -71,14 +71,14 @@ void HandleLaraMovementParameters(ItemInfo* item, CollisionInfo* coll)
if ((!lara->Control.IsMoving || (lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT)))) && if ((!lara->Control.IsMoving || (lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT)))) &&
(!lara->Control.IsLow && item->Animation.ActiveState != LS_DEATH)) // HACK: Don't interfere with surface alignment in crouch, crawl, and death states. (!lara->Control.IsLow && item->Animation.ActiveState != LS_DEATH)) // HACK: Don't interfere with surface alignment in crouch, crawl, and death states.
{ {
ResetLaraLean(item, 6.0f); ResetPlayerLean(item, 1 / 6.0f);
} }
// Reset crawl flex. // Reset crawl flex.
if (!(TrInput & IN_LOOK) && coll->Setup.Height > LARA_HEIGHT - LARA_HEADROOM && // HACK if (!(TrInput & IN_LOOK) && coll->Setup.Height > LARA_HEIGHT - LARA_HEADROOM && // HACK
(!item->Animation.Velocity.z || (item->Animation.Velocity.z && !(TrInput & (IN_LEFT | IN_RIGHT))))) (!item->Animation.Velocity.z || (item->Animation.Velocity.z && !(TrInput & (IN_LEFT | IN_RIGHT)))))
{ {
ResetLaraFlex(item, 12.0f); ResetPlayerFlex(item, 0.1f);
} }
// Apply and reset turn rate. // Apply and reset turn rate.
@ -94,20 +94,20 @@ bool HandleLaraVehicle(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
if (lara->Vehicle == NO_ITEM) if (lara->Context.Vehicle == NO_ITEM)
return false; return false;
if (!g_Level.Items[lara->Vehicle].Active) if (!g_Level.Items[lara->Context.Vehicle].Active)
{ {
lara->Vehicle = NO_ITEM; lara->Context.Vehicle = NO_ITEM;
item->Animation.IsAirborne = true; item->Animation.IsAirborne = true;
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);
return false; return false;
} }
TestVolumes(lara->Vehicle); TestVolumes(lara->Context.Vehicle);
switch (g_Level.Items[lara->Vehicle].ObjectNumber) switch (g_Level.Items[lara->Context.Vehicle].ObjectNumber)
{ {
case ID_QUAD: case ID_QUAD:
QuadBikeControl(item, coll); QuadBikeControl(item, coll);
@ -143,7 +143,7 @@ bool HandleLaraVehicle(ItemInfo* item, CollisionInfo* coll)
// Boats are processed like normal items in loop. // Boats are processed like normal items in loop.
default: default:
HandleWeapon(item); HandleWeapon(*item);
} }
return true; return true;
@ -289,7 +289,7 @@ void DoLaraStep(ItemInfo* item, CollisionInfo* coll)
if (TestLaraStepUp(item, coll)) if (TestLaraStepUp(item, coll))
{ {
item->Animation.TargetState = LS_STEP_UP; item->Animation.TargetState = LS_STEP_UP;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Pose.Position.y += coll->Middle.Floor; item->Pose.Position.y += coll->Middle.Floor;
return; return;
@ -298,7 +298,7 @@ void DoLaraStep(ItemInfo* item, CollisionInfo* coll)
else if (TestLaraStepDown(item, coll)) else if (TestLaraStepDown(item, coll))
{ {
item->Animation.TargetState = LS_STEP_DOWN; item->Animation.TargetState = LS_STEP_DOWN;
if (GetStateDispatch(item, g_Level.Anims[item->Animation.AnimNumber])) if (GetStateDispatch(item, GetAnimData(*item)))
{ {
item->Pose.Position.y += coll->Middle.Floor; item->Pose.Position.y += coll->Middle.Floor;
return; return;
@ -755,29 +755,30 @@ void SetLaraVault(ItemInfo* item, CollisionInfo* coll, VaultTestResult vaultResu
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
lara->ProjectedFloorHeight = vaultResult.Height; lara->Context.ProjectedFloorHeight = vaultResult.Height;
lara->Control.HandStatus = vaultResult.SetBusyHands ? HandStatus::Busy : lara->Control.HandStatus; lara->Control.HandStatus = vaultResult.SetBusyHands ? HandStatus::Busy : lara->Control.HandStatus;
lara->Control.TurnRate = 0; lara->Control.TurnRate = 0;
if (vaultResult.SnapToLedge) if (vaultResult.SnapToLedge)
{ {
SnapItemToLedge(item, coll, 0.2f, false); SnapItemToLedge(item, coll, 0.2f, false);
lara->TargetOrientation = EulerAngles(0, coll->NearestLedgeAngle, 0); lara->Context.TargetOrientation = EulerAngles(0, coll->NearestLedgeAngle, 0);
} }
else else
{ {
lara->TargetOrientation = EulerAngles(0, item->Pose.Orientation.y, 0); lara->Context.TargetOrientation = EulerAngles(0, item->Pose.Orientation.y, 0);
} }
if (vaultResult.SetJumpVelocity) if (vaultResult.SetJumpVelocity)
{ {
int height = lara->ProjectedFloorHeight - item->Pose.Position.y; int height = lara->Context.ProjectedFloorHeight - item->Pose.Position.y;
if (height > -CLICK(3.5f)) if (height > -CLICK(3.5f))
height = -CLICK(3.5f); height = -CLICK(3.5f);
else if (height < -CLICK(7.5f)) else if (height < -CLICK(7.5f))
height = -CLICK(7.5f); height = -CLICK(7.5f);
lara->Control.CalculatedJumpVelocity = -3 - sqrt(-9600 - 12 * height); // TODO: Find a better formula for this that won't require the above block. // TODO: Find a better formula for this that won't require the above block.
lara->Context.CalcJumpVelocity = -3 - sqrt(-9600 - 12 * (height));
} }
} }
@ -923,7 +924,7 @@ void SetLaraCornerAnimation(ItemInfo* item, CollisionInfo* coll, bool flip)
item->Animation.Velocity.z = 2; item->Animation.Velocity.z = 2;
item->Animation.Velocity.y = 1; item->Animation.Velocity.y = 1;
item->Pose.Position.y += CLICK(1); item->Pose.Position.y += CLICK(1);
item->Pose.Orientation.y += lara->NextCornerPos.Orientation.y / 2; item->Pose.Orientation.y += lara->Context.NextCornerPos.Orientation.y / 2;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
return; return;
} }
@ -935,9 +936,9 @@ void SetLaraCornerAnimation(ItemInfo* item, CollisionInfo* coll, bool flip)
else else
SetAnimation(item, LA_HANG_IDLE); SetAnimation(item, LA_HANG_IDLE);
item->Pose.Position = lara->NextCornerPos.Position; item->Pose.Position = lara->Context.NextCornerPos.Position;
item->Pose.Orientation.y = lara->NextCornerPos.Orientation.y; item->Pose.Orientation.y = lara->Context.NextCornerPos.Orientation.y;
coll->Setup.OldPosition = lara->NextCornerPos.Position; coll->Setup.OldPosition = lara->Context.NextCornerPos.Position;
} }
} }
@ -958,98 +959,49 @@ void SetLaraVehicle(ItemInfo* item, ItemInfo* vehicle)
if (vehicle == nullptr) if (vehicle == nullptr)
{ {
if (lara->Vehicle != NO_ITEM) if (lara->Context.Vehicle != NO_ITEM)
g_Level.Items[lara->Vehicle].Active = false; g_Level.Items[lara->Context.Vehicle].Active = false;
lara->Vehicle = NO_ITEM; lara->Context.Vehicle = NO_ITEM;
} }
else else
{ {
g_Level.Items[vehicle->Index].Active = true; g_Level.Items[vehicle->Index].Active = true;
lara->Vehicle = vehicle->Index; lara->Context.Vehicle = vehicle->Index;
} }
} }
void ResetLaraLean(ItemInfo* item, float rate, bool resetRoll, bool resetPitch) void ResetPlayerLean(ItemInfo* item, float alpha, bool resetRoll, bool resetPitch)
{ {
if (!rate) if (resetRoll)
{ item->Pose.Orientation.Lerp(EulerAngles(item->Pose.Orientation.x, item->Pose.Orientation.y, 0), alpha);
TENLog(std::string("ResetLaraLean() attempted division by zero!"), LogLevel::Warning);
return;
}
rate = abs(rate);
if (resetPitch) if (resetPitch)
{ item->Pose.Orientation.Lerp(EulerAngles(0, item->Pose.Orientation.y, item->Pose.Orientation.z), alpha);
if (abs(item->Pose.Orientation.x) > ANGLE(0.1f))
item->Pose.Orientation.x += item->Pose.Orientation.x / -rate;
else
item->Pose.Orientation.x = 0;
} }
if (resetRoll) void ResetPlayerFlex(ItemInfo* item, float alpha)
{ {
if (abs(item->Pose.Orientation.z) > ANGLE(0.1f)) auto& player = GetLaraInfo(*item);
item->Pose.Orientation.z += item->Pose.Orientation.z / -rate;
else
item->Pose.Orientation.z = 0;
}
}
void ResetLaraFlex(ItemInfo* item, float rate) player.ExtraHeadRot.Lerp(EulerAngles::Zero, alpha);
{ player.ExtraTorsoRot.Lerp(EulerAngles::Zero, alpha);
auto* lara = GetLaraInfo(item);
if (!rate)
{
TENLog(std::string("ResetLaraFlex() attempted division by zero."), LogLevel::Warning);
return;
}
rate = abs(rate);
// Reset head.
if (abs(lara->ExtraHeadRot.x) > ANGLE(0.1f))
lara->ExtraHeadRot.x += lara->ExtraHeadRot.x / -rate;
else
lara->ExtraHeadRot.x = 0;
if (abs(lara->ExtraHeadRot.y) > ANGLE(0.1f))
lara->ExtraHeadRot.y += lara->ExtraHeadRot.y / -rate;
else
lara->ExtraHeadRot.y = 0;
if (abs(lara->ExtraHeadRot.z) > ANGLE(0.1f))
lara->ExtraHeadRot.z += lara->ExtraHeadRot.z / -rate;
else
lara->ExtraHeadRot.z = 0;
// Reset torso.
if (abs(lara->ExtraTorsoRot.x) > ANGLE(0.1f))
lara->ExtraTorsoRot.x += lara->ExtraTorsoRot.x / -rate;
else
lara->ExtraTorsoRot.x = 0;
if (abs(lara->ExtraTorsoRot.y) > ANGLE(0.1f))
lara->ExtraTorsoRot.y += lara->ExtraTorsoRot.y / -rate;
else
lara->ExtraTorsoRot.y = 0;
if (abs(lara->ExtraTorsoRot.z) > ANGLE(0.1f))
lara->ExtraTorsoRot.z += lara->ExtraTorsoRot.z / -rate;
else
lara->ExtraTorsoRot.z = 0;
} }
void RumbleLaraHealthCondition(ItemInfo* item) void RumbleLaraHealthCondition(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); constexpr auto POWER = 0.2f;
constexpr auto DELAY = 0.1f;
if (item->HitPoints > LARA_HEALTH_CRITICAL && lara->Status.Poison == 0) const auto& player = GetLaraInfo(*item);
if (item->HitPoints > LARA_HEALTH_CRITICAL && player.Status.Poison == 0)
return; return;
bool doPulse = (GlobalCounter & 0x0F) % 0x0F == 1; if (item->HitPoints == 0)
return;
bool doPulse = ((GlobalCounter & 0x0F) % 0x0F == 1);
if (doPulse) if (doPulse)
Rumble(0.2f, 0.1f); Rumble(POWER, DELAY);
} }

View file

@ -56,7 +56,7 @@ void SetLaraCornerAnimation(ItemInfo* item, CollisionInfo* coll, bool flip);
void SetLaraSwimDiveAnimation(ItemInfo* item); void SetLaraSwimDiveAnimation(ItemInfo* item);
void SetLaraVehicle(ItemInfo* item, ItemInfo* vehicle = nullptr); void SetLaraVehicle(ItemInfo* item, ItemInfo* vehicle = nullptr);
void ResetLaraLean(ItemInfo* item, float rate = 1.0f, bool resetRoll = true, bool resetPitch = true); void ResetPlayerLean(ItemInfo* item, float alpha = 1.0f, bool resetRoll = true, bool resetPitch = true);
void ResetLaraFlex(ItemInfo* item, float rate = 1.0f); void ResetPlayerFlex(ItemInfo* item, float alpha = 1.0f);
void RumbleLaraHealthCondition(ItemInfo* item); void RumbleLaraHealthCondition(ItemInfo* item);

View file

@ -7,12 +7,12 @@
#include "Game/Lara/lara_flare.h" #include "Game/Lara/lara_flare.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_tests.h" #include "Game/Lara/lara_tests.h"
#include "Game/Setup.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Hud; using namespace TEN::Hud;
void InitialiseLara(bool restore) void InitializeLara(bool restore)
{ {
if (Lara.ItemNumber == NO_ITEM) if (Lara.ItemNumber == NO_ITEM)
return; return;
@ -39,10 +39,10 @@ void InitialiseLara(bool restore)
Lara.ItemNumber = itemNumber; Lara.ItemNumber = itemNumber;
Lara.HitDirection = -1; Lara.HitDirection = -1;
Lara.Control.Weapon.WeaponItem = NO_ITEM; Lara.Control.Weapon.WeaponItem = NO_ITEM;
Lara.WaterSurfaceDist = 100; Lara.Context.WaterSurfaceDist = 100;
Lara.ExtraAnim = NO_ITEM; Lara.ExtraAnim = NO_ITEM;
Lara.Vehicle = NO_ITEM; Lara.Context.Vehicle = NO_ITEM;
Lara.Location = -1; Lara.Location = -1;
Lara.HighestLocation = -1; Lara.HighestLocation = -1;
Lara.Control.Rope.Ptr = -1; Lara.Control.Rope.Ptr = -1;
@ -50,17 +50,17 @@ void InitialiseLara(bool restore)
Lara.Control.HandStatus = HandStatus::Free; Lara.Control.HandStatus = HandStatus::Free;
if (restore) if (restore)
InitialiseLaraLevelJump(itemNumber, &lBackup); InitializeLaraLevelJump(itemNumber, &lBackup);
else else
InitialiseLaraDefaultInventory(); InitializeLaraDefaultInventory();
InitialiseLaraMeshes(LaraItem); InitializeLaraMeshes(LaraItem);
InitialiseLaraAnims(LaraItem); InitializeLaraAnims(LaraItem);
g_Hud.StatusBars.Initialize(*LaraItem); g_Hud.StatusBars.Initialize(*LaraItem);
} }
void InitialiseLaraMeshes(ItemInfo* item) void InitializeLaraMeshes(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
@ -126,7 +126,7 @@ void InitialiseLaraMeshes(ItemInfo* item)
lara->RightArm.Locked = false; lara->RightArm.Locked = false;
} }
void InitialiseLaraAnims(ItemInfo* item) void InitializeLaraAnims(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
@ -143,13 +143,13 @@ void InitialiseLaraAnims(ItemInfo* item)
} }
} }
void InitialiseLaraLoad(short itemNum) void InitializeLaraLoad(short itemNum)
{ {
Lara.ItemNumber = itemNum; Lara.ItemNumber = itemNum;
LaraItem = &g_Level.Items[itemNum]; LaraItem = &g_Level.Items[itemNum];
} }
void InitialiseLaraLevelJump(short itemNum, LaraInfo* lBackup) void InitializeLaraLevelJump(short itemNum, LaraInfo* lBackup)
{ {
auto* item = &g_Level.Items[itemNum]; auto* item = &g_Level.Items[itemNum];
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
@ -169,10 +169,10 @@ void InitialiseLaraLevelJump(short itemNum, LaraInfo* lBackup)
lara->Control.HandStatus = lBackup->Control.HandStatus; lara->Control.HandStatus = lBackup->Control.HandStatus;
lara->Control.Weapon = lBackup->Control.Weapon; lara->Control.Weapon = lBackup->Control.Weapon;
lara->Flare = lBackup->Flare; lara->Flare = lBackup->Flare;
DrawFlareMeshes(item); DrawFlareMeshes(*item);
} }
void InitialiseLaraDefaultInventory() void InitializeLaraDefaultInventory()
{ {
if (Objects[ID_FLARE_INV_ITEM].loaded) if (Objects[ID_FLARE_INV_ITEM].loaded)
Lara.Inventory.TotalFlares = 3; Lara.Inventory.TotalFlares = 3;

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include "Game/Lara/lara_struct.h" #include "Game/Lara/lara_struct.h"
void InitialiseLara(bool restore); void InitializeLara(bool restore);
void InitialiseLaraMeshes(ItemInfo* item); void InitializeLaraMeshes(ItemInfo* item);
void InitialiseLaraAnims(ItemInfo* item); void InitializeLaraAnims(ItemInfo* item);
void InitialiseLaraLoad(short itemNumber); void InitializeLaraLoad(short itemNumber);
void InitialiseLaraLevelJump(short itemNum, LaraInfo* lBackup); void InitializeLaraLevelJump(short itemNum, LaraInfo* lBackup);
void InitialiseLaraDefaultInventory(); void InitializeLaraDefaultInventory();

View file

@ -10,11 +10,11 @@
#include "Game/Lara/lara_basic.h" #include "Game/Lara/lara_basic.h"
#include "Game/Lara/lara_overhang.h" #include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_slide.h" #include "Game/Lara/lara_slide.h"
#include "Game/Setup.h"
#include "Flow/ScriptInterfaceFlowHandler.h" #include "Flow/ScriptInterfaceFlowHandler.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Input; using namespace TEN::Input;
@ -126,7 +126,7 @@ void lara_col_jump_forward(ItemInfo* item, CollisionInfo* coll)
// Collision: lara_col_freefall() // Collision: lara_col_freefall()
void lara_as_freefall(ItemInfo* item, CollisionInfo* coll) void lara_as_freefall(ItemInfo* item, CollisionInfo* coll)
{ {
item->Animation.Velocity.z = item->Animation.Velocity.z * 0.95f; item->Animation.Velocity.z *= 0.95f;
ModulateLaraTurnRateY(item, 0, 0, 0); ModulateLaraTurnRateY(item, 0, 0, 0);

View file

@ -56,7 +56,7 @@ void lara_as_pickup_flare(ItemInfo* item, CollisionInfo* coll)
Camera.targetElevation = -ANGLE(15.0f); Camera.targetElevation = -ANGLE(15.0f);
Camera.targetDistance = SECTOR(1); Camera.targetDistance = SECTOR(1);
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameEnd - 1) if (item->Animation.FrameNumber == (GetAnimData(*item).frameEnd - 1))
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
} }
@ -102,16 +102,14 @@ void lara_col_turn_switch(ItemInfo* item, CollisionInfo* coll)
{ {
if (item->Animation.AnimNumber == LA_TURNSWITCH_PUSH_COUNTER_CLOCKWISE_CONTINUE) if (item->Animation.AnimNumber == LA_TURNSWITCH_PUSH_COUNTER_CLOCKWISE_CONTINUE)
{ {
SetAnimation(item, LA_TURNSWITCH_PUSH_COUNTER_CLOCKWISE_END);
item->Pose.Orientation.y -= ANGLE(90.0f); item->Pose.Orientation.y -= ANGLE(90.0f);
item->Animation.AnimNumber = LA_TURNSWITCH_PUSH_COUNTER_CLOCKWISE_END;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
} }
if (item->Animation.AnimNumber == LA_TURNSWITCH_PUSH_CLOCKWISE_CONTINUE) if (item->Animation.AnimNumber == LA_TURNSWITCH_PUSH_CLOCKWISE_CONTINUE)
{ {
SetAnimation(item, LA_TURNSWITCH_PUSH_CLOCKWISE_END);
item->Pose.Orientation.y += ANGLE(90.0f); item->Pose.Orientation.y += ANGLE(90.0f);
item->Animation.AnimNumber = LA_TURNSWITCH_PUSH_CLOCKWISE_END;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
} }
} }
} }
@ -151,7 +149,7 @@ void lara_as_use_puzzle(ItemInfo* item, CollisionInfo* coll)
{ {
item->Animation.ActiveState = LS_MISC_CONTROL; item->Animation.ActiveState = LS_MISC_CONTROL;
item->Animation.AnimNumber = item->ItemFlags[0]; item->Animation.AnimNumber = item->ItemFlags[0];
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = GetAnimData(*item).frameBase;
} }
} }
@ -208,7 +206,7 @@ void lara_as_pushable_grab(ItemInfo* item, CollisionInfo* coll)
void lara_as_pulley(ItemInfo* item, CollisionInfo* coll) void lara_as_pulley(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
auto* pulleyItem = &g_Level.Items[lara->InteractedItem]; auto* pulleyItem = &g_Level.Items[lara->Context.InteractedItem];
lara->Control.CanLook = false; lara->Control.CanLook = false;
coll->Setup.EnableSpasm = false; coll->Setup.EnableSpasm = false;
@ -220,7 +218,7 @@ void lara_as_pulley(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
if (item->Animation.AnimNumber == LA_PULLEY_PULL && if (item->Animation.AnimNumber == LA_PULLEY_PULL &&
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 44) item->Animation.FrameNumber == GetAnimData(*item).frameBase + 44)
{ {
if (pulleyItem->TriggerFlags) if (pulleyItem->TriggerFlags)
{ {
@ -250,7 +248,7 @@ void lara_as_pulley(ItemInfo* item, CollisionInfo* coll)
} }
if (item->Animation.AnimNumber == LA_PULLEY_RELEASE && if (item->Animation.AnimNumber == LA_PULLEY_RELEASE &&
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameEnd - 1) item->Animation.FrameNumber == GetAnimData(*item).frameEnd - 1)
{ {
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
} }
@ -284,11 +282,11 @@ void lara_as_horizontal_bar_swing(ItemInfo* item, CollisionInfo* coll)
void lara_as_horizontal_bar_leap(ItemInfo* item, CollisionInfo* coll) void lara_as_horizontal_bar_leap(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
const auto& barItem = g_Level.Items[lara->InteractedItem]; const auto& barItem = g_Level.Items[lara->Context.InteractedItem];
item->Animation.IsAirborne = true; item->Animation.IsAirborne = true;
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) if (item->Animation.FrameNumber == GetAnimData(*item).frameBase)
{ {
int distance = 0; int distance = 0;
if (item->Pose.Orientation.y == barItem.Pose.Orientation.y) if (item->Pose.Orientation.y == barItem.Pose.Orientation.y)
@ -613,7 +611,7 @@ void lara_col_rope_idle(ItemInfo* item, CollisionInfo* coll)
if (TrInput & IN_SPRINT) if (TrInput & IN_SPRINT)
{ {
item->Animation.TargetState = LS_ROPE_SWING; item->Animation.TargetState = LS_ROPE_SWING;
lara->Control.Rope.DFrame = (g_Level.Anims[LA_ROPE_SWING].frameBase + 32) << 8; lara->Control.Rope.DFrame = (GetAnimData(LA_ROPE_SWING).frameBase + 32) << 8;
lara->Control.Rope.Frame = lara->Control.Rope.DFrame; lara->Control.Rope.Frame = lara->Control.Rope.DFrame;
} }
else if (TrInput & IN_FORWARD && lara->Control.Rope.Segment > 4) else if (TrInput & IN_FORWARD && lara->Control.Rope.Segment > 4)
@ -677,7 +675,7 @@ void lara_col_rope_swing(ItemInfo* item, CollisionInfo* coll)
item->Animation.FrameNumber = lara->Control.Rope.Frame >> 8; item->Animation.FrameNumber = lara->Control.Rope.Frame >> 8;
if (!(TrInput & IN_SPRINT) && if (!(TrInput & IN_SPRINT) &&
item->Animation.FrameNumber == g_Level.Anims[LA_ROPE_SWING].frameBase + 32 && item->Animation.FrameNumber == GetAnimData(LA_ROPE_SWING).frameBase + 32 &&
lara->Control.Rope.MaxXBackward < 6750 && lara->Control.Rope.MaxXBackward < 6750 &&
lara->Control.Rope.MaxXForward < 6750) lara->Control.Rope.MaxXForward < 6750)
{ {
@ -690,7 +688,7 @@ void lara_col_rope_swing(ItemInfo* item, CollisionInfo* coll)
if (TrInput & IN_JUMP) if (TrInput & IN_JUMP)
JumpOffRope(item); JumpOffRope(item);
} }
else if (item->Animation.FrameNumber == g_Level.Anims[LA_ROPE_IDLE_TO_SWING].frameBase + 15) else if (item->Animation.FrameNumber == GetAnimData(LA_ROPE_IDLE_TO_SWING).frameBase + 15)
ApplyVelocityToRope(lara->Control.Rope.Segment, item->Pose.Orientation.y, 128); ApplyVelocityToRope(lara->Control.Rope.Segment, item->Pose.Orientation.y, 128);
} }
@ -706,9 +704,9 @@ void lara_as_rope_up(ItemInfo* item, CollisionInfo* coll)
{ {
Camera.targetAngle = ANGLE(30.0f); Camera.targetAngle = ANGLE(30.0f);
if (g_Level.Anims[item->Animation.AnimNumber].frameEnd == item->Animation.FrameNumber) if (GetAnimData(*item).frameEnd == item->Animation.FrameNumber)
{ {
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = GetAnimData(*item).frameBase;
lara->Control.Rope.Segment -= 2; lara->Control.Rope.Segment -= 2;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include "Game/control/control.h"
#include "Math/Math.h"
#include "Specific/clock.h"
enum class LaraWeaponType; enum class LaraWeaponType;
class GameVector;
class Pose;
class Vector3i;
struct ItemInfo; struct ItemInfo;
enum class GrenadeType enum class GrenadeType
@ -26,28 +26,28 @@ enum class ProjectileType
FlashGrenade FlashGrenade
}; };
void AnimateShotgun(ItemInfo* laraItem, LaraWeaponType weaponType); void AnimateShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void ReadyShotgun(ItemInfo* laraItem, LaraWeaponType weaponType); void ReadyShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void FireShotgun(ItemInfo* laraItem); void FireShotgun(ItemInfo& laraItem);
void DrawShotgun(ItemInfo* laraItem, LaraWeaponType weaponType); void DrawShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawShotgun(ItemInfo* laraItem, LaraWeaponType weaponType); void UndrawShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void DrawShotgunMeshes(ItemInfo* laraItem, LaraWeaponType weaponType); void DrawShotgunMeshes(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawShotgunMeshes(ItemInfo* laraItem, LaraWeaponType weaponType); void UndrawShotgunMeshes(ItemInfo& laraItem, LaraWeaponType weaponType);
ItemInfo* FireHarpoon(ItemInfo* laraItem); ItemInfo* FireHarpoon(ItemInfo& laraItem);
void HarpoonBoltControl(short itemNumber); void HarpoonBoltControl(short itemNumber);
void FireGrenade(ItemInfo* laraItem); void FireGrenade(ItemInfo& laraItem);
void GrenadeControl(short itemNumber); void GrenadeControl(short itemNumber);
void FireRocket(ItemInfo* laraItem); void FireRocket(ItemInfo& laraItem);
void RocketControl(short itemNumber); void RocketControl(short itemNumber);
void FireCrossbow(ItemInfo* laraItem, Pose* pos); void FireCrossbow(ItemInfo& laraItem, Pose* pos);
void FireCrossBowFromLaserSight(ItemInfo& laraItem, GameVector* origin, GameVector* target);
void CrossbowBoltControl(short itemNumber); void CrossbowBoltControl(short itemNumber);
void FireCrossBowFromLaserSight(ItemInfo* laraItem, GameVector* origin, GameVector* target);
void FireHK(ItemInfo* laraItem, int mode); void FireHK(ItemInfo& laraItem, int mode);
void RifleHandler(ItemInfo* laraItem, LaraWeaponType weaponType); void RifleHandler(ItemInfo& laraItem, LaraWeaponType weaponType);
void LasersightWeaponHandler(ItemInfo* item, LaraWeaponType weaponType); void LasersightWeaponHandler(ItemInfo& item, LaraWeaponType weaponType);
void DoExplosiveDamage(ItemInfo& emitter, ItemInfo& target, ItemInfo& projectile, int damage); void DoExplosiveDamage(ItemInfo& emitter, ItemInfo& target, ItemInfo& projectile, int damage);
void HandleProjectile(ItemInfo& item, ItemInfo& emitter, const Vector3i& prevPos, ProjectileType type, int damage); void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& prevPos, ProjectileType type, int damage);
void SomeSparkEffect(int x, int y, int z, int count); void SomeSparkEffect(int x, int y, int z, int count);

View file

@ -10,9 +10,9 @@
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_tests.h" #include "Game/Lara/lara_tests.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Setup.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/setup.h"
#include "Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Input; using namespace TEN::Input;
@ -336,7 +336,7 @@ void lara_col_slopeclimb(ItemInfo* item, CollisionInfo* coll)
// Engage shimmy mode if LEFT/LSTEP or RIGHT/RSTEP are pressed. // Engage shimmy mode if LEFT/LSTEP or RIGHT/RSTEP are pressed.
if (TrInput & IN_LEFT || TrInput & IN_RIGHT) if (TrInput & IN_LEFT || TrInput & IN_RIGHT)
{ {
lara->NextCornerPos.Orientation.z = (item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT) ? true : false; // HACK. lara->Context.NextCornerPos.Orientation.z = (item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT) ? true : false; // HACK.
SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_IDLE_2_HANG_LEFT : LA_OVERHANG_IDLE_2_HANG_RIGHT); SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_IDLE_2_HANG_LEFT : LA_OVERHANG_IDLE_2_HANG_RIGHT);
return; return;
} }
@ -500,7 +500,7 @@ void lara_col_slopehang(ItemInfo* item, CollisionInfo* coll)
{ {
// Return to climbing mode. // Return to climbing mode.
if (TrInput & IN_FORWARD || TrInput & IN_BACK) if (TrInput & IN_FORWARD || TrInput & IN_BACK)
SetAnimation(item, lara->NextCornerPos.Orientation.z ? LA_OVERHANG_HANG_2_IDLE_LEFT : LA_OVERHANG_HANG_2_IDLE_RIGHT); // HACK. SetAnimation(item, lara->Context.NextCornerPos.Orientation.z ? LA_OVERHANG_HANG_2_IDLE_LEFT : LA_OVERHANG_HANG_2_IDLE_RIGHT); // HACK.
// Shimmy control. // Shimmy control.
if (TrInput & IN_LEFT || TrInput & IN_RIGHT) if (TrInput & IN_LEFT || TrInput & IN_RIGHT)
@ -641,7 +641,7 @@ void lara_as_slopeclimbup(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION)) if (!(TrInput & IN_ACTION))
{ {
int frame = GetCurrentRelativeFrameNumber(item); int frame = GetFrameNumber(item);
int length = GetFrameCount(item->Animation.AnimNumber); int length = GetFrameCount(item->Animation.AnimNumber);
int dPos = CLICK(1) - (frame * CLICK(1) / length); int dPos = CLICK(1) - (frame * CLICK(1) / length);
@ -671,7 +671,7 @@ void lara_as_slopeclimbdown(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION)) if (!(TrInput & IN_ACTION))
{ {
int frame = GetCurrentRelativeFrameNumber(item); int frame = GetFrameNumber(item);
int length = GetFrameCount(item->Animation.AnimNumber); int length = GetFrameCount(item->Animation.AnimNumber);
int dPos = frame * CLICK(1) / length; int dPos = frame * CLICK(1) / length;
@ -688,7 +688,7 @@ void lara_as_sclimbstart(ItemInfo* item, CollisionInfo* coll)
// Rotating camera effect during monkey to overhead slope transition. // Rotating camera effect during monkey to overhead slope transition.
if (item->Animation.AnimNumber == LA_OVERHANG_MONKEY_SLOPE_CONVEX) if (item->Animation.AnimNumber == LA_OVERHANG_MONKEY_SLOPE_CONVEX)
{ {
int frame = GetCurrentRelativeFrameNumber(item); int frame = GetFrameNumber(item);
int numFrames = GetFrameCount(item->Animation.AnimNumber); int numFrames = GetFrameCount(item->Animation.AnimNumber);
float frac = (frame * 1.5f) / (float)(numFrames); float frac = (frame * 1.5f) / (float)(numFrames);
@ -744,7 +744,7 @@ void lara_as_sclimbstop(ItemInfo* item, CollisionInfo* coll)
// Rotating camera effect during concave slope to monkey transition. // Rotating camera effect during concave slope to monkey transition.
else if (item->Animation.AnimNumber == LA_OVERHANG_SLOPE_MONKEY_CONCAVE) else if (item->Animation.AnimNumber == LA_OVERHANG_SLOPE_MONKEY_CONCAVE)
{ {
int frame = GetCurrentRelativeFrameNumber(item); int frame = GetFrameNumber(item);
int numFrames = GetFrameCount(item->Animation.AnimNumber); int numFrames = GetFrameCount(item->Animation.AnimNumber);
float frac = (frame * 1.25f) / (float)(numFrames); float frac = (frame * 1.25f) / (float)(numFrames);
@ -810,7 +810,7 @@ void lara_as_sclimbend(ItemInfo* item, CollisionInfo* coll)
break; break;
} }
GetLaraInfo(item)->NextCornerPos.Orientation.z = 0; GetLaraInfo(item)->Context.NextCornerPos.Orientation.z = 0;
item->Pose.Orientation.x = 0; item->Pose.Orientation.x = 0;
} }
@ -1050,7 +1050,7 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber); auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber);
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber); auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber);
if (item->Animation.AnimNumber == LA_REACH_TO_MONKEY && !GetFrameNumber(item, 0)) // Manage proper grabbing of monkey slope on forward jump. if (item->Animation.AnimNumber == LA_REACH_TO_MONKEY && !GetFrameIndex(item, 0)) // Manage proper grabbing of monkey slope on forward jump.
{ {
int ceilDist = item->Pose.Position.y - probeNow.Position.Ceiling; int ceilDist = item->Pose.Position.y - probeNow.Position.Ceiling;
@ -1064,7 +1064,7 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0) if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0)
{ {
lara->NextCornerPos.Orientation.z = AlignToGrab(item); lara->Context.NextCornerPos.Orientation.z = AlignToGrab(item);
int ceiling = GetCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).Position.Ceiling; int ceiling = GetCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).Position.Ceiling;
item->Pose.Position.y = ceiling + HEIGHT_ADJUST; item->Pose.Position.y = ceiling + HEIGHT_ADJUST;
@ -1077,7 +1077,7 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
if (TrInput & IN_FORWARD) // Monkey to slope transitions. if (TrInput & IN_FORWARD) // Monkey to slope transitions.
{ {
if (probeNow.BottomBlock->Flags.Monkeyswing && if (probeNow.BottomBlock->Flags.Monkeyswing &&
((item->Animation.AnimNumber == LA_REACH_TO_MONKEY && GetFrameNumber(item, 0) >= 54) || item->Animation.AnimNumber == LA_MONKEY_IDLE)) ((item->Animation.AnimNumber == LA_REACH_TO_MONKEY && GetFrameIndex(item, 0) >= 54) || item->Animation.AnimNumber == LA_MONKEY_IDLE))
{ {
if (abs(OrientDelta(slopeData.GoalOrient, item->Pose.Orientation.y)) <= ANGLE(30.0f) && if (abs(OrientDelta(slopeData.GoalOrient, item->Pose.Orientation.y)) <= ANGLE(30.0f) &&
InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(0.5f))) InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(0.5f)))

View file

@ -134,7 +134,7 @@ void lara_as_slide_back(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
Camera.targetElevation = -ANGLE(45.0f); Camera.targetElevation = -ANGLE(45.0f);
Camera.targetAngle = ANGLE(135.0f); //Camera.targetAngle = ANGLE(135.0f); // TODO: Player setting to swivel camera around. -- Sezz 2023.04.09
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
{ {

View file

@ -4,14 +4,10 @@
using namespace TEN::Math; using namespace TEN::Math;
namespace TEN::Renderer
{
struct RendererMesh;
}
struct CreatureInfo; struct CreatureInfo;
struct FX_INFO; struct FX_INFO;
struct ItemInfo; struct ItemInfo;
namespace TEN::Renderer { struct RendererMesh; };
// Inventory object constants // Inventory object constants
constexpr int NUM_PUZZLES = ID_PUZZLE_ITEM16 - ID_PUZZLE_ITEM1 + 1; constexpr int NUM_PUZZLES = ID_PUZZLE_ITEM16 - ID_PUZZLE_ITEM1 + 1;
@ -212,6 +208,10 @@ enum LaraState
LS_CRAWL_TURN_180 = 172, LS_CRAWL_TURN_180 = 172,
LS_TURN_180 = 173, LS_TURN_180 = 173,
// 174-188 reserved for "true" ladders. -- Sezz 2023.04.16
LS_REMOVE_PUZZLE = 189,
NUM_LARA_STATES NUM_LARA_STATES
}; };
@ -508,7 +508,7 @@ enum LaraAnim
LA_CRAWL_IDLE_TO_CRAWL_BACK = 275, // Crawl > crawl back LA_CRAWL_IDLE_TO_CRAWL_BACK = 275, // Crawl > crawl back
LA_CRAWL_BACK = 276, // Crawl back (looped) LA_CRAWL_BACK = 276, // Crawl back (looped)
LA_CRAWL_BACK_TO_IDLE_RIGHT = 277, // Crawl back > crawl idle, right foot first LA_CRAWL_BACK_TO_IDLE_RIGHT = 277, // Crawl back > crawl idle, right foot first
LA_CRAWL_BACK_TO_IDLE_RIGHT_END = 278, // Unused. LA_REMOVE_PUZZLE = 278, // Remove puzzle item > idle
LA_CRAWL_BACK_TO_IDLE_LEFT = 279, // Crawl back > crawl idle, left foot first LA_CRAWL_BACK_TO_IDLE_LEFT = 279, // Crawl back > crawl idle, left foot first
LA_CRAWL_BACK_TO_IDLE_LEFT_END = 280, // Unused. LA_CRAWL_BACK_TO_IDLE_LEFT_END = 280, // Unused.
LA_CRAWL_TURN_LEFT_TO_IDLE_EARLY = 281, // Crawl rotate left > crawl idle, early opportunity LA_CRAWL_TURN_LEFT_TO_IDLE_EARLY = 281, // Crawl rotate left > crawl idle, early opportunity
@ -815,11 +815,12 @@ enum LaraAnim
LA_LEDGE_JUMP_BACK_START = 567, LA_LEDGE_JUMP_BACK_START = 567,
LA_LEDGE_JUMP_BACK_END = 568, LA_LEDGE_JUMP_BACK_END = 568,
// 569-598 reserved for "true" ladders. -- Sezz 2023.04.16
NUM_LARA_ANIMS NUM_LARA_ANIMS
// TRASHED ANIMS (please reuse slots before going any higher and remove entries from this list as you go): // TRASHED ANIMS (please reuse slots before going any higher and remove entries from this list as you go):
// 102 // 280,
// 273, 274, 278, 280,
// 343, 345, // 343, 345,
// 364, 366, 368, 370, // 364, 366, 368, 370,
}; };
@ -865,6 +866,18 @@ enum LARA_MESHES
NUM_LARA_MESHES NUM_LARA_MESHES
}; };
enum LaraWeaponTypeCarried
{
WTYPE_MISSING = 0,
WTYPE_PRESENT = (1 << 0),
WTYPE_SILENCER = (1 << 1),
WTYPE_LASERSIGHT = (1 << 2),
WTYPE_AMMO_1 = (1 << 3),
WTYPE_AMMO_2 = (1 << 4),
WTYPE_AMMO_3 = (1 << 5),
WTYPE_MASK_AMMO = WTYPE_AMMO_1 | WTYPE_AMMO_2 | WTYPE_AMMO_3,
};
enum class WeaponAmmoType enum class WeaponAmmoType
{ {
Ammo1, Ammo1,
@ -893,18 +906,6 @@ enum class LaraWeaponType
NumWeapons NumWeapons
}; };
enum LaraWeaponTypeCarried
{
WTYPE_MISSING = 0,
WTYPE_PRESENT = (1 << 0),
WTYPE_SILENCER = (1 << 1),
WTYPE_LASERSIGHT = (1 << 2),
WTYPE_AMMO_1 = (1 << 3),
WTYPE_AMMO_2 = (1 << 4),
WTYPE_AMMO_3 = (1 << 5),
WTYPE_MASK_AMMO = WTYPE_AMMO_1 | WTYPE_AMMO_2 | WTYPE_AMMO_3,
};
enum class HolsterSlot enum class HolsterSlot
{ {
Empty = ID_LARA_HOLSTERS, Empty = ID_LARA_HOLSTERS,
@ -919,14 +920,6 @@ enum class HolsterSlot
RocketLauncher = ID_ROCKET_ANIM RocketLauncher = ID_ROCKET_ANIM
}; };
// TODO: Unused.
enum class ClothType
{
None,
Dry,
Wet
};
enum class WaterStatus enum class WaterStatus
{ {
Dry, Dry,
@ -969,8 +962,8 @@ struct Ammo
using CountType = int; using CountType = int;
private: private:
CountType Count; CountType Count = 0;
bool IsInfinite; bool IsInfinite = false;
public: public:
static CountType Clamp(int value) static CountType Clamp(int value)
@ -988,15 +981,15 @@ public:
return Count; return Count;
} }
void SetInfinite(bool infinite) void SetInfinite(bool isInfinite)
{ {
this->IsInfinite = infinite; IsInfinite = isInfinite;
} }
Ammo& operator --() Ammo& operator --()
{ {
assertion(this->Count > 0, "Ammo count is already 0!"); assertion(Count > 0, "Ammo count is already 0.");
--this->Count; --Count;
return *this; return *this;
} }
@ -1009,7 +1002,7 @@ public:
Ammo& operator ++() Ammo& operator ++()
{ {
++this->Count; ++Count;
return *this; return *this;
} }
@ -1022,7 +1015,7 @@ public:
Ammo& operator =(int value) Ammo& operator =(int value)
{ {
this->Count = Clamp(value); Count = Clamp(value);
return *this; return *this;
} }
@ -1031,10 +1024,10 @@ public:
return (Count == Clamp(value)); return (Count == Clamp(value));
} }
Ammo& operator =(Ammo& rhs) Ammo& operator =(Ammo& ammo)
{ {
this->Count = rhs.Count; Count = ammo.Count;
this->IsInfinite = rhs.Count; IsInfinite = ammo.Count;
return *this; return *this;
} }
@ -1055,20 +1048,20 @@ public:
Ammo& operator +=(int value) Ammo& operator +=(int value)
{ {
int temp = Count + value; int temp = Count + value;
this->Count = Clamp(temp); Count = Clamp(temp);
return *this; return *this;
} }
Ammo& operator -=(int value) Ammo& operator -=(int value)
{ {
int temp = Count - value; int temp = Count - value;
this->Count = Clamp(temp); Count = Clamp(temp);
return *this; return *this;
} }
operator bool() operator bool()
{ {
return (IsInfinite || Count > 0); return (IsInfinite || (Count > 0));
} }
}; };
@ -1092,15 +1085,15 @@ struct CarriedWeaponInfo
struct ArmInfo struct ArmInfo
{ {
int AnimNumber; int AnimNumber = 0;
int FrameNumber; int FrameNumber = 0;
int FrameBase; int FrameBase = 0;
EulerAngles Orientation; EulerAngles Orientation = EulerAngles::Zero;
bool Locked = false;
bool Locked; int GunFlash = 0;
int GunFlash; int GunSmoke = 0;
int GunSmoke;
}; };
struct FlareData struct FlareData
@ -1122,29 +1115,29 @@ constexpr int MAX_DIARY_STRINGS_PER_PAGE = 8;
struct DiaryString struct DiaryString
{ {
Vector2i Position; Vector2i Position = Vector2i::Zero;
int StringID; int StringID = 0;
}; };
struct DiaryPage struct DiaryPage
{ {
DiaryString Strings[MAX_DIARY_STRINGS_PER_PAGE]; DiaryString Strings[MAX_DIARY_STRINGS_PER_PAGE] = {};
}; };
struct DiaryInfo struct DiaryInfo
{ {
bool Present; bool Present = false;
DiaryPage Pages[MAX_DIARY_PAGES]; DiaryPage Pages[MAX_DIARY_PAGES] = {};
unsigned int NumPages; unsigned int NumPages = 0;
unsigned int CurrentPage; unsigned int CurrentPage = 0;
}; };
struct LaraInventoryData struct LaraInventoryData
{ {
bool IsBusy; bool IsBusy = false;
bool OldBusy; bool OldBusy = false;
DiaryInfo Diary; DiaryInfo Diary = {};
byte BeetleLife; byte BeetleLife;
int BeetleComponents; // BeetleComponentFlags enum int BeetleComponents; // BeetleComponentFlags enum
@ -1157,29 +1150,29 @@ struct LaraInventoryData
int TotalFlares; int TotalFlares;
unsigned int TotalSecrets; unsigned int TotalSecrets;
bool HasBinoculars; bool HasBinoculars = false;
bool HasCrowbar; bool HasCrowbar = false;
bool HasTorch; bool HasTorch = false;
bool HasLasersight; bool HasLasersight = false;
bool HasSilencer; // TODO: Unused. bool HasSilencer = false; // TODO: Unused.
// TODO: Convert to bools. // TODO: Convert to bools.
int Puzzles[NUM_PUZZLES]; int Puzzles[NUM_PUZZLES] = {};
int Keys[NUM_KEYS]; int Keys[NUM_KEYS] = {};
int Pickups[NUM_PICKUPS]; int Pickups[NUM_PICKUPS] = {};
int Examines[NUM_EXAMINES]; int Examines[NUM_EXAMINES] = {};
int PuzzlesCombo[NUM_PUZZLES * 2]; int PuzzlesCombo[NUM_PUZZLES * 2] = {};
int KeysCombo[NUM_KEYS * 2]; int KeysCombo[NUM_KEYS * 2] = {};
int PickupsCombo[NUM_PICKUPS * 2]; int PickupsCombo[NUM_PICKUPS * 2] = {};
int ExaminesCombo[NUM_EXAMINES * 2]; int ExaminesCombo[NUM_EXAMINES * 2] = {};
}; };
struct LaraCountData struct LaraCountData
{ {
unsigned int Pose; unsigned int Pose = 0;
unsigned int PositionAdjust; unsigned int PositionAdjust = 0;
unsigned int Run; unsigned int Run = 0;
unsigned int Death; unsigned int Death = 0;
}; };
struct WeaponControlData struct WeaponControlData
@ -1204,31 +1197,37 @@ struct WeaponControlData
struct RopeControlData struct RopeControlData
{ {
byte Segment; byte Segment = 0;
byte Direction; byte Direction = 0;
short ArcFront;
short ArcBack; short ArcFront = 0;
short LastX; short ArcBack = 0;
short MaxXForward;
short MaxXBackward; short LastX = 0;
int DFrame; short MaxXForward = 0;
int Frame; short MaxXBackward = 0;
unsigned short FrameRate;
unsigned short Y; int DFrame = 0;
int Ptr; int Frame = 0;
int Offset; unsigned short FrameRate = 0;
int DownVel;
byte Flag; unsigned short Y = 0;
int Count; int Ptr = 0;
int Offset = 0;
int DownVel = 0;
byte Flag = 0;
int Count = 0;
}; };
// TODO: Give tightrope a property for difficulty?
// TODO: Remove old tightrope functionality.
struct TightropeControlData struct TightropeControlData
{ {
#if NEW_TIGHTROPE #if NEW_TIGHTROPE
float Balance; short TightropeItem = 0;
unsigned int TimeOnTightrope; bool CanDismount = false;
bool CanDismount; float Balance = 0.0f;
short TightropeItem; // TODO: Give tightrope a property for difficulty? unsigned int TimeOnTightrope = 0;
#else // !NEW_TIGHTROPE #else // !NEW_TIGHTROPE
unsigned int OnCount; unsigned int OnCount;
byte Off; byte Off;
@ -1238,25 +1237,24 @@ struct TightropeControlData
struct SubsuitControlData struct SubsuitControlData
{ {
short XRot; short XRot = 0;
short DXRot; short DXRot = 0;
int Velocity[2]; int Velocity[2] = {};
int VerticalVelocity; int VerticalVelocity = 0;
// TODO: These appear to be unused. // TODO: These appear to be unused.
short XRotVel; short XRotVel = 0;
unsigned short HitCount; unsigned short HitCount = 0;
}; };
struct LaraControlData struct LaraControlData
{ {
short MoveAngle = 0; short MoveAngle = 0;
short TurnRate = 0; short TurnRate = 0;
int CalculatedJumpVelocity = 0;
JumpDirection JumpDirection = {};
HandStatus HandStatus = {}; HandStatus HandStatus = {};
WaterStatus WaterStatus = {}; WaterStatus WaterStatus = {};
JumpDirection JumpDirection = {};
LaraCountData Count = {}; LaraCountData Count = {};
RopeControlData Rope = {}; RopeControlData Rope = {};
@ -1265,25 +1263,19 @@ struct LaraControlData
WeaponControlData Weapon = {}; WeaponControlData Weapon = {};
bool IsClimbingLadder = false; bool IsClimbingLadder = false;
bool Locked = false; bool Locked = false; // IsLocked
bool IsLow = false; bool IsLow = false;
bool IsMonkeySwinging = false; bool IsMonkeySwinging = false;
bool IsMoving = false; bool IsMoving = false;
bool RunJumpQueued = false; bool RunJumpQueued = false; // IsRunJumpQueued
bool KeepLow = false; bool KeepLow = false; // IsInLowSpace
bool CanClimbLadder = false; bool CanClimbLadder = false;
bool CanLook = false; bool CanLook = false;
bool CanMonkeySwing = false; bool CanMonkeySwing = false;
}; };
struct PlayerEffectData // TODO: Refactor status handling to use floats.
{
std::array<float, NUM_LARA_MESHES> DripNodes = {};
std::array<float, NUM_LARA_MESHES> BubbleNodes = {};
};
// TODO: Make these floats.
struct PlayerStatusData struct PlayerStatusData
{ {
int Air = 0; int Air = 0;
@ -1292,40 +1284,54 @@ struct PlayerStatusData
int Stamina = 0; int Stamina = 0;
}; };
struct PlayerContextData
{
int ProjectedFloorHeight = 0;
float CalcJumpVelocity = 0;
Pose NextCornerPos = Pose::Zero;
EulerAngles TargetOrientation = EulerAngles::Zero;
int WaterSurfaceDist = 0;
short WaterCurrentActive = 0; // Sink number? Often used as bool.
Vector3i WaterCurrentPull = Vector3i::Zero;
int InteractedItem = 0; // Item number.
int Vehicle = 0; // Item number.
};
struct PlayerEffectData
{
std::array<float, NUM_LARA_MESHES> DripNodes = {};
std::array<float, NUM_LARA_MESHES> BubbleNodes = {};
};
struct LaraInfo struct LaraInfo
{ {
int ItemNumber = 0; // TODO: Remove. No longer necessary since ItemInfo already has this. int ItemNumber = 0; // TODO: Remove. No longer necessary since ItemInfo already has it. -- Sezz 2023.04.09
PlayerStatusData Status = {};
LaraControlData Control = {}; LaraControlData Control = {};
LaraInventoryData Inventory; PlayerContextData Context = {};
CarriedWeaponInfo Weapons[(int)LaraWeaponType::NumWeapons]; PlayerStatusData Status = {};
PlayerEffectData Effect = {};
LaraInventoryData Inventory = {};
FlareData Flare = {}; FlareData Flare = {};
TorchData Torch = {}; TorchData Torch = {};
CarriedWeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] = {};
EulerAngles ExtraHeadRot = {}; EulerAngles ExtraHeadRot = {};
EulerAngles ExtraTorsoRot = {}; EulerAngles ExtraTorsoRot = {};
short WaterCurrentActive = 0;
Vector3i WaterCurrentPull = Vector3i::Zero;
ArmInfo LeftArm = {}; ArmInfo LeftArm = {};
ArmInfo RightArm = {}; ArmInfo RightArm = {};
EulerAngles TargetArmOrient = EulerAngles::Zero; EulerAngles TargetArmOrient = EulerAngles::Zero;
ItemInfo* TargetEntity = nullptr; ItemInfo* TargetEntity = nullptr; // TargetEntityPtr. Should use item number instead?
int Vehicle; // TODO: Rewrite and restore spasm effect. Also move to PlayerEffectData?
int ExtraAnim; int HitFrame = 0; // Frame index.
int HitFrame; int HitDirection = 0; // Cardinal direction.
int HitDirection; FX_INFO* SpasmEffect = nullptr; // Not saved.
FX_INFO* SpasmEffect; // Not saved. TODO: Restore this effect.
short InteractedItem; int ExtraAnim = 0; // Item number? Only ever set to NO_ITEM or 1.
int ProjectedFloorHeight;
EulerAngles TargetOrientation;
int WaterSurfaceDist;
Pose NextCornerPos;
PlayerEffectData Effect = {};
signed char Location = 0; signed char Location = 0;
signed char HighestLocation = 0; signed char HighestLocation = 0;

View file

@ -22,7 +22,7 @@
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer; using namespace TEN::Renderer;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using std::vector; using std::vector;
// ----------------------------- // -----------------------------
@ -170,7 +170,7 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll)
} }
else else
{ {
if (((item->Animation.AnimNumber == LA_REACH_TO_HANG && item->Animation.FrameNumber == GetFrameNumber(item, 21)) || item->Animation.AnimNumber == LA_HANG_IDLE) && if (((item->Animation.AnimNumber == LA_REACH_TO_HANG && item->Animation.FrameNumber == GetFrameIndex(item, 21)) || item->Animation.AnimNumber == LA_HANG_IDLE) &&
TestLaraClimbIdle(item, coll)) TestLaraClimbIdle(item, coll))
{ {
item->Animation.TargetState = LS_LADDER_IDLE; item->Animation.TargetState = LS_LADDER_IDLE;
@ -273,7 +273,7 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll)
if (TestLaraMonkeyGrab(item, coll)) if (TestLaraMonkeyGrab(item, coll))
{ {
SetAnimation(item, LA_REACH_TO_MONKEY); SetAnimation(item, LA_REACH_TO_MONKEY);
ResetLaraFlex(item); ResetPlayerFlex(item);
item->Animation.Velocity.z = 0; item->Animation.Velocity.z = 0;
item->Animation.Velocity.y = 0; item->Animation.Velocity.y = 0;
item->Animation.IsAirborne = false; item->Animation.IsAirborne = false;
@ -300,7 +300,7 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll)
if (TestHangSwingIn(item, coll)) if (TestHangSwingIn(item, coll))
{ {
SetAnimation(item, LA_REACH_TO_HANG_OSCILLATE); SetAnimation(item, LA_REACH_TO_HANG_OSCILLATE);
ResetLaraFlex(item); ResetPlayerFlex(item);
} }
else else
SetAnimation(item, LA_REACH_TO_HANG); SetAnimation(item, LA_REACH_TO_HANG);
@ -575,12 +575,12 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
// Store next position // Store next position
item->Pose = cornerResult.RealPositionResult; item->Pose = cornerResult.RealPositionResult;
lara->NextCornerPos.Position = Vector3i( lara->Context.NextCornerPos.Position = Vector3i(
item->Pose.Position.x, item->Pose.Position.x,
GetCollision(item, item->Pose.Orientation.y, coll->Setup.Radius + 16, -(coll->Setup.Height + CLICK(0.5f))).Position.Floor + abs(bounds.Y1), GetCollision(item, item->Pose.Orientation.y, coll->Setup.Radius + 16, -(coll->Setup.Height + CLICK(0.5f))).Position.Floor + abs(bounds.Y1),
item->Pose.Position.z item->Pose.Position.z
); );
lara->NextCornerPos.Orientation.y = item->Pose.Orientation.y; lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
item->Pose = cornerResult.ProbeResult; item->Pose = cornerResult.ProbeResult;
@ -596,9 +596,9 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
if (lara->Control.CanClimbLadder) if (lara->Control.CanClimbLadder)
{ {
auto& angleSet = testAngle > 0 ? LeftExtRightIntTab : LeftIntRightExtTab; auto& angleSet = testAngle > 0 ? LeftExtRightIntTab : LeftIntRightExtTab;
if (GetClimbFlags(lara->NextCornerPos.Position.x, item->Pose.Position.y, lara->NextCornerPos.Position.z, item->RoomNumber) & (short)angleSet[GetQuadrant(item->Pose.Orientation.y)]) if (GetClimbFlags(lara->Context.NextCornerPos.Position.x, item->Pose.Position.y, lara->Context.NextCornerPos.Position.z, item->RoomNumber) & (short)angleSet[GetQuadrant(item->Pose.Orientation.y)])
{ {
lara->NextCornerPos.Position.y = item->Pose.Position.y; // Restore original Y pos for ladder tests because we don't snap to ledge height in such case. lara->Context.NextCornerPos.Position.y = item->Pose.Position.y; // Restore original Y pos for ladder tests because we don't snap to ledge height in such case.
return CornerType::Inner; return CornerType::Inner;
} }
} }
@ -634,10 +634,10 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
// Store next position // Store next position
item->Pose = cornerResult.RealPositionResult; item->Pose = cornerResult.RealPositionResult;
lara->NextCornerPos.Position.x = item->Pose.Position.x; lara->Context.NextCornerPos.Position.x = item->Pose.Position.x;
lara->NextCornerPos.Position.y = GetCollision(item, item->Pose.Orientation.y, coll->Setup.Radius * 1.25f, -(abs(bounds.Y1) + LARA_HEADROOM)).Position.Floor + abs(bounds.Y1); lara->Context.NextCornerPos.Position.y = GetCollision(item, item->Pose.Orientation.y, coll->Setup.Radius * 1.25f, -(abs(bounds.Y1) + LARA_HEADROOM)).Position.Floor + abs(bounds.Y1);
lara->NextCornerPos.Position.z = item->Pose.Position.z; lara->Context.NextCornerPos.Position.z = item->Pose.Position.z;
lara->NextCornerPos.Orientation.y = item->Pose.Orientation.y; lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
item->Pose = cornerResult.ProbeResult; item->Pose = cornerResult.ProbeResult;
@ -653,9 +653,9 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
if (lara->Control.CanClimbLadder) if (lara->Control.CanClimbLadder)
{ {
auto& angleSet = testAngle > 0 ? LeftIntRightExtTab : LeftExtRightIntTab; auto& angleSet = testAngle > 0 ? LeftIntRightExtTab : LeftExtRightIntTab;
if (GetClimbFlags(lara->NextCornerPos.Position.x, item->Pose.Position.y, lara->NextCornerPos.Position.z, item->RoomNumber) & (short)angleSet[GetQuadrant(item->Pose.Orientation.y)]) if (GetClimbFlags(lara->Context.NextCornerPos.Position.x, item->Pose.Position.y, lara->Context.NextCornerPos.Position.z, item->RoomNumber) & (short)angleSet[GetQuadrant(item->Pose.Orientation.y)])
{ {
lara->NextCornerPos.Position.y = item->Pose.Position.y; // Restore original Y pos for ladder tests because we don't snap to ledge height in such case. lara->Context.NextCornerPos.Position.y = item->Pose.Position.y; // Restore original Y pos for ladder tests because we don't snap to ledge height in such case.
return CornerType::Outer; return CornerType::Outer;
} }
} }
@ -1219,7 +1219,7 @@ bool TestLaraPose(ItemInfo* item, CollisionInfo* coll)
lara->Control.HandStatus == HandStatus::Free && // Hands are free. lara->Control.HandStatus == HandStatus::Free && // Hands are free.
(lara->Control.Weapon.GunType != LaraWeaponType::Flare || // Flare is not being handled. (lara->Control.Weapon.GunType != LaraWeaponType::Flare || // Flare is not being handled.
lara->Flare.Life) && lara->Flare.Life) &&
lara->Vehicle == NO_ITEM) // Not in a vehicle. lara->Context.Vehicle == NO_ITEM) // Not in a vehicle.
{ {
return true; return true;
} }
@ -1645,7 +1645,7 @@ bool TestLaraCrouchRoll(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
// Assess water depth. // Assess water depth.
if (lara->WaterSurfaceDist < -CLICK(1)) if (lara->Context.WaterSurfaceDist < -CLICK(1))
return false; return false;
// Assess continuity of path. // Assess continuity of path.
@ -1830,7 +1830,7 @@ VaultTestResult TestLaraVaultTolerance(ItemInfo* item, CollisionInfo* coll, Vaul
auto probeMiddle = GetCollision(item); auto probeMiddle = GetCollision(item);
bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item); bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item);
bool swampTooDeep = testSetup.CheckSwampDepth ? (isSwamp && lara->WaterSurfaceDist < -CLICK(3)) : isSwamp; bool swampTooDeep = testSetup.CheckSwampDepth ? (isSwamp && lara->Context.WaterSurfaceDist < -CLICK(3)) : isSwamp;
int y = isSwamp ? item->Pose.Position.y : probeMiddle.Position.Floor; // HACK: Avoid cheese when in the midst of performing a step. Can be done better. @Sezz 2022.04.08 int y = isSwamp ? item->Pose.Position.y : probeMiddle.Position.Floor; // HACK: Avoid cheese when in the midst of performing a step. Can be done better. @Sezz 2022.04.08
// Check swamp depth (if applicable). // Check swamp depth (if applicable).
@ -2065,7 +2065,7 @@ VaultTestResult TestLaraVault(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION) || lara->Control.HandStatus != HandStatus::Free) if (!(TrInput & IN_ACTION) || lara->Control.HandStatus != HandStatus::Free)
return VaultTestResult{ false }; return VaultTestResult{ false };
if (TestEnvironment(ENV_FLAG_SWAMP, item) && lara->WaterSurfaceDist < -CLICK(3)) if (TestEnvironment(ENV_FLAG_SWAMP, item) && lara->Context.WaterSurfaceDist < -CLICK(3))
return VaultTestResult{ false }; return VaultTestResult{ false };
VaultTestResult vaultResult; VaultTestResult vaultResult;
@ -2154,24 +2154,24 @@ bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION) || !(TrInput & IN_FORWARD) || lara->Control.HandStatus != HandStatus::Free) if (!(TrInput & IN_ACTION) || !(TrInput & IN_FORWARD) || lara->Control.HandStatus != HandStatus::Free)
return false; return false;
if (TestEnvironment(ENV_FLAG_SWAMP, item) && lara->WaterSurfaceDist < -CLICK(3)) if (TestEnvironment(ENV_FLAG_SWAMP, item) && lara->Context.WaterSurfaceDist < -CLICK(3))
return false; return false;
// Auto jump to ladder. // Auto jump to ladder.
auto vaultResult = TestLaraLadderAutoJump(item, coll); auto vaultResult = TestLaraLadderAutoJump(item, coll);
if (vaultResult.Success) if (vaultResult.Success)
{ {
// TODO: Somehow harmonise CalculatedJumpVelocity to work for both ledge and ladder auto jumps, because otherwise there will be a need for an odd workaround in the future. // TODO: Somehow harmonise Context.CalcJumpVelocity to work for both ledge and ladder auto jumps, because otherwise there will be a need for an odd workaround in the future.
lara->Control.CalculatedJumpVelocity = -3 - sqrt(-9600 - 12 * std::max((vaultResult.Height - item->Pose.Position.y + CLICK(0.2f)), -CLICK(7.1f))); lara->Context.CalcJumpVelocity = -3 - sqrt(-9600 - 12 * std::max((vaultResult.Height - item->Pose.Position.y + CLICK(0.2f)), -CLICK(7.1f)));
item->Animation.AnimNumber = LA_STAND_SOLID; item->Animation.AnimNumber = LA_STAND_SOLID;
item->Animation.FrameNumber = GetFrameNumber(item, 0); item->Animation.FrameNumber = GetFrameIndex(item, 0);
item->Animation.TargetState = LS_JUMP_UP; item->Animation.TargetState = LS_JUMP_UP;
item->Animation.ActiveState = LS_IDLE; item->Animation.ActiveState = LS_IDLE;
lara->Control.TurnRate = 0; lara->Control.TurnRate = 0;
ShiftItem(item, coll); ShiftItem(item, coll);
SnapItemToGrid(item, coll); // HACK: until fragile ladder code is refactored, we must exactly snap to grid. SnapItemToGrid(item, coll); // HACK: until fragile ladder code is refactored, we must exactly snap to grid.
lara->TargetOrientation = EulerAngles(0, item->Pose.Orientation.y, 0); lara->Context.TargetOrientation = EulerAngles(0, item->Pose.Orientation.y, 0);
AnimateItem(item); AnimateItem(item);
return true; return true;
@ -2183,7 +2183,7 @@ bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll)
TestLaraClimbIdle(item, coll)) TestLaraClimbIdle(item, coll))
{ {
item->Animation.AnimNumber = LA_STAND_SOLID; item->Animation.AnimNumber = LA_STAND_SOLID;
item->Animation.FrameNumber = GetFrameNumber(item, 0); item->Animation.FrameNumber = GetFrameIndex(item, 0);
item->Animation.TargetState = LS_LADDER_IDLE; item->Animation.TargetState = LS_LADDER_IDLE;
item->Animation.ActiveState = LS_IDLE; item->Animation.ActiveState = LS_IDLE;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;

View file

@ -9,504 +9,417 @@
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_fire.h" #include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_struct.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Math/Random.h" #include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/game_object_ids.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h"
#include "Specific/setup.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h"
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math::Random; using namespace TEN::Math;
struct PistolDef struct WeaponAnimData
{ {
short ObjectNumber; GAME_OBJECT_ID ObjectID = GAME_OBJECT_ID::ID_NO_OBJECT;
int Draw1Anim2;
int Draw1Anim; int Draw1Anim2 = 0;
int Draw2Anim; int Draw1Anim = 0;
int RecoilAnim; int Draw2Anim = 0;
int RecoilAnim = 0;
}; };
PistolDef PistolsTable[4] = static WeaponAnimData GetWeaponAnimData(LaraWeaponType weaponType)
{ {
{ ID_LARA, 0, 0, 0, 0 }, static const auto DEFAULT_WEAPON_ANIM_DATA = WeaponAnimData{ ID_LARA, 0, 0, 0, 0 };
{ ID_PISTOLS_ANIM, 4, 5, 13, 24 }, static const auto ANIM_DATA_MAP = std::unordered_map<LaraWeaponType, WeaponAnimData>
{ ID_REVOLVER_ANIM , 7, 8, 15, 29 }, {
{ ID_UZI_ANIM, 4, 5, 13, 24 } { LaraWeaponType::None, WeaponAnimData{ ID_LARA, 0, 0, 0, 0 } },
{ LaraWeaponType::Pistol, WeaponAnimData{ ID_PISTOLS_ANIM, 4, 5, 13, 24 } },
{ LaraWeaponType::Revolver, WeaponAnimData{ ID_REVOLVER_ANIM , 7, 8, 15, 29 } },
{ LaraWeaponType::Uzi, WeaponAnimData{ ID_UZI_ANIM, 4, 5, 13, 24 } }
}; };
void AnimatePistols(ItemInfo* laraItem, LaraWeaponType weaponType) auto it = ANIM_DATA_MAP.find(weaponType);
{ return ((it != ANIM_DATA_MAP.end()) ? it->second : DEFAULT_WEAPON_ANIM_DATA);
auto* lara = GetLaraInfo(laraItem); }
auto* weapon = &Weapons[(int)weaponType];
auto* p = &PistolsTable[(int)lara->Control.Weapon.GunType];
int fired = false; static Vector3i GetWeaponSmokeRelOffset(LaraWeaponType weaponType, bool isRightWeapon)
if (laraItem->MeshBits.TestAny())
{ {
if (lara->LeftArm.GunSmoke)
{
auto offset = Vector3i::Zero;
switch (weaponType) switch (weaponType)
{ {
case LaraWeaponType::Pistol: case LaraWeaponType::Pistol:
offset = Vector3i(4, 128, 40); return Vector3i(isRightWeapon ? -16 : 4, 128, 40);
break;
case LaraWeaponType::Revolver: case LaraWeaponType::Revolver:
offset = Vector3i(16, 160, 56); return Vector3i(isRightWeapon ? -32 : 16, 160, 56);
break;
case LaraWeaponType::Uzi: case LaraWeaponType::Uzi:
offset = Vector3i(8, 140, 48); return Vector3i(isRightWeapon ? -16 : 8, 140, 48);
break;
default:
return Vector3i::Zero;
}
} }
auto pos = GetJointPosition(laraItem, LM_LHAND, offset); static void SetArmInfo(const ItemInfo& laraItem, ArmInfo& arm, int frame)
TriggerGunSmoke(pos.x, pos.y, pos.z, 0, 0, 0, 0, weaponType, lara->LeftArm.GunSmoke);
}
if (lara->RightArm.GunSmoke)
{ {
auto offset = Vector3i::Zero; const auto& player = GetLaraInfo(laraItem);
switch (weaponType) const auto& weaponAnimData = GetWeaponAnimData(player.Control.Weapon.GunType);
int animBase = Objects[(int)weaponAnimData.ObjectID].animIndex;
if (frame < weaponAnimData.Draw1Anim)
{ {
case LaraWeaponType::Pistol:
offset = Vector3i(-16, 128, 40);
break;
case LaraWeaponType::Revolver:
offset = Vector3i(-32, 160, 56);
break;
case LaraWeaponType::Uzi:
offset = Vector3i(-16, 140, 48);
break;
}
auto pos = GetJointPosition(laraItem, LM_RHAND, offset);
TriggerGunSmoke(pos.x, pos.y, pos.z, 0, 0, 0, 0, weaponType, lara->RightArm.GunSmoke);
}
}
// Shooting action for right arm.
int frameRight = lara->RightArm.FrameNumber; // frame number of DRAW_END?
if ((TrInput & IN_ACTION && !lara->TargetEntity) || lara->RightArm.Locked)
{
// POINT ARMS FORWARD
// at or beyond (2) DRAW_END start frame AND before (0) SHOOT_START end frame...
if ((frameRight >= 0) && (frameRight < p->Draw1Anim2))
{
// ...increment toward (0) SHOOT_START end frame
frameRight++;
}
// at (0) SHOOT_START end frame
else if (frameRight == p->Draw1Anim2)
{
// actually shoot, bang bang
if (TrInput & IN_ACTION)
{
if (weaponType != LaraWeaponType::Revolver)
{
auto rightArmOrient = EulerAngles(
lara->RightArm.Orientation.x,
lara->RightArm.Orientation.y + laraItem->Pose.Orientation.y,
0
);
if (FireWeapon(weaponType, lara->TargetEntity, laraItem, rightArmOrient) != FireWeaponType::NoAmmo)
{
lara->RightArm.GunSmoke = 28;
TriggerGunShell(1, ID_GUNSHELL, weaponType); // Right Hand
lara->RightArm.GunFlash = weapon->FlashTime;
SoundEffect(SFX_TR4_EXPLOSION1, &laraItem->Pose, SoundEnvironment::Land, 0.9f, 0.3f);
SoundEffect(weapon->SampleNum, &laraItem->Pose);
fired = true;
if (weaponType == LaraWeaponType::Uzi)
lara->Control.Weapon.UziRight = true;
Statistics.Game.AmmoUsed++;
}
}
// go to (3) SHOOT_CONTINUE start frame
frameRight = p->RecoilAnim;
}
else if (lara->Control.Weapon.UziRight)
{
SoundEffect(weapon->SampleNum + 1, &laraItem->Pose);
lara->Control.Weapon.UziRight = false;
}
}
// at or beyond (3) SHOOT_CONTINUE start frame
else if (frameRight >= p->RecoilAnim)
{
if (weaponType == LaraWeaponType::Uzi)
{
SoundEffect(weapon->SampleNum, &laraItem->Pose);
lara->Control.Weapon.UziRight = true;
}
// increment toward (3) SHOOT_CONTINUE end frame (finish recoil before allowing to shoot again)
frameRight++;
// at (3) SHOOT_CONTINUE end frame, go to (0) START_SHOOT end frame
if (frameRight == (p->RecoilAnim + weapon->RecoilFrame))
frameRight = p->Draw1Anim2;
}
}
// HAS LET GO OF ACTION
else
{
// let (3) SHOOT_CONTINUE finish
if ((frameRight >= p->RecoilAnim) && (frameRight < p->RecoilAnim + weapon->RecoilFrame))
frameRight++;
// at (3) SHOOT_CONTINUE end frame, go to (0) START_SHOOT end frame
if (frameRight == (p->RecoilAnim + weapon->RecoilFrame))
frameRight = p->Draw1Anim2;
// go back to ready stance
else if ((frameRight > 0) && (frameRight <= p->Draw1Anim2))
frameRight--;
if (lara->Control.Weapon.UziRight)
{
SoundEffect(weapon->SampleNum + 1, &laraItem->Pose);
lara->Control.Weapon.UziRight = false;
}
}
SetArmInfo(laraItem, lara->RightArm, frameRight);
// Shooting for left arm.
int frameLeft = lara->LeftArm.FrameNumber;
if ((TrInput & IN_ACTION && !lara->TargetEntity) || lara->LeftArm.Locked)
{
if ((frameLeft >= 0) && (frameLeft < p->Draw1Anim2))
frameLeft++;
else if (frameLeft == p->Draw1Anim2)
{
if (TrInput & IN_ACTION)
{
auto leftArmOrient = EulerAngles(
lara->LeftArm.Orientation.x,
lara->LeftArm.Orientation.y + laraItem->Pose.Orientation.y,
0
);
if (FireWeapon(weaponType, lara->TargetEntity, laraItem, leftArmOrient) != FireWeaponType::NoAmmo)
{
if (weaponType == LaraWeaponType::Revolver)
{
lara->RightArm.GunSmoke = 28;
lara->RightArm.GunFlash = weapon->FlashTime;
}
else
{
lara->LeftArm.GunSmoke = 28;
TriggerGunShell(0, ID_GUNSHELL, weaponType); // Left hand
lara->LeftArm.GunFlash = weapon->FlashTime;
}
if (!fired)
{
SoundEffect(SFX_TR4_EXPLOSION1, &laraItem->Pose, SoundEnvironment::Land, 0.9f, 0.3f);
SoundEffect(weapon->SampleNum, &laraItem->Pose);
fired = true;
}
if (weaponType == LaraWeaponType::Uzi)
lara->Control.Weapon.UziLeft = true;
Statistics.Game.AmmoUsed++;
}
frameLeft = p->RecoilAnim;
}
else if (lara->Control.Weapon.UziLeft)
{
SoundEffect(weapon->SampleNum + 1, &laraItem->Pose);
lara->Control.Weapon.UziLeft = false;
}
}
else if (frameLeft >= p->RecoilAnim)
{
if (weaponType == LaraWeaponType::Uzi)
{
SoundEffect(weapon->SampleNum, &laraItem->Pose);
lara->Control.Weapon.UziLeft = true;
}
frameLeft++;
if (frameLeft == (p->RecoilAnim + weapon->RecoilFrame))
frameLeft = p->Draw1Anim2;
}
}
else // Havent GOT a LOCK ON..
{
if ((frameLeft >= p->RecoilAnim) && (frameLeft < p->RecoilAnim + weapon->RecoilFrame))
frameLeft++;
if (frameLeft == (p->RecoilAnim + weapon->RecoilFrame))
frameLeft = p->Draw1Anim2;
else if ((frameLeft > 0) && (frameLeft <= p->Draw1Anim2))
frameLeft--;
if (lara->Control.Weapon.UziLeft)
{
SoundEffect(weapon->SampleNum + 1, &laraItem->Pose);
lara->Control.Weapon.UziLeft = false;
}
}
if (fired) // Rumble gamepad only once if any of the hands fired.
{
float power = weaponType == LaraWeaponType::Uzi ? GenerateFloat(0.1f, 0.3f) : 1.0f;
Rumble(power, 0.1f);
}
SetArmInfo(laraItem, lara->LeftArm, frameLeft);
}
void PistolHandler(ItemInfo* laraItem, LaraWeaponType weaponType)
{
auto* lara = GetLaraInfo(laraItem);
auto& weapon = Weapons[(int)weaponType];
FindNewTarget(laraItem, weapon);
if (TrInput & IN_ACTION)
LaraTargetInfo(laraItem, weapon);
AimWeapon(laraItem, lara->LeftArm, weapon);
AimWeapon(laraItem, lara->RightArm, weapon);
if (lara->LeftArm.Locked && !lara->RightArm.Locked)
{
lara->ExtraTorsoRot.x = lara->LeftArm.Orientation.x / 2;
lara->ExtraTorsoRot.y = lara->LeftArm.Orientation.y / 2;
if (Camera.oldType != CameraType::Look)
lara->ExtraHeadRot = lara->ExtraTorsoRot;
}
else if (!lara->LeftArm.Locked && lara->RightArm.Locked)
{
lara->ExtraTorsoRot.x = lara->RightArm.Orientation.x / 2;
lara->ExtraTorsoRot.y = lara->RightArm.Orientation.y / 2;
if (Camera.oldType != CameraType::Look)
lara->ExtraHeadRot = lara->ExtraTorsoRot;
}
else if (lara->LeftArm.Locked && lara->RightArm.Locked)
{
lara->ExtraTorsoRot.x = (lara->LeftArm.Orientation.x + lara->RightArm.Orientation.x) / 4;
lara->ExtraTorsoRot.y = (lara->LeftArm.Orientation.y + lara->RightArm.Orientation.y) / 4;
if (Camera.oldType != CameraType::Look)
lara->ExtraHeadRot = lara->ExtraTorsoRot;
}
AnimatePistols(laraItem, weaponType);
if (lara->LeftArm.GunFlash || lara->RightArm.GunFlash)
{
auto pos = GetJointPosition(laraItem,
(lara->LeftArm.GunFlash != 0) ? LM_LHAND : LM_RHAND,
Vector3i(
(byte)GetRandomControl() - 128,
(GetRandomControl() & 0x7F) - 63,
(byte)GetRandomControl() - 128
));
TriggerDynamicLight(pos.x+GenerateFloat(-128,128), pos.y + GenerateFloat(-128, 128), pos.z + GenerateFloat(-128, 128), GenerateFloat(8,11), (GetRandomControl() & 0x3F) + 192, (GetRandomControl() & 0x1F) + 128, GetRandomControl() & 0x3F);
}
}
void ReadyPistols(ItemInfo* laraItem, LaraWeaponType weaponType)
{
auto* lara = GetLaraInfo(laraItem);
lara->Control.HandStatus = HandStatus::WeaponReady;
lara->LeftArm.Orientation = EulerAngles::Zero;
lara->RightArm.Orientation = EulerAngles::Zero;
lara->LeftArm.FrameNumber = 0;
lara->RightArm.FrameNumber = 0;
lara->TargetEntity = nullptr;
lara->LeftArm.Locked = false;
lara->RightArm.Locked = false;
lara->LeftArm.FrameBase = Objects[GetWeaponObjectID(weaponType)].frameBase;
lara->RightArm.FrameBase = Objects[GetWeaponObjectID(weaponType)].frameBase;
}
void DrawPistols(ItemInfo* laraItem, LaraWeaponType weaponType)
{
auto* lara = GetLaraInfo(laraItem);
auto* p = &PistolsTable[(int)lara->Control.Weapon.GunType];
int frame = lara->LeftArm.FrameNumber + 1;
if (frame < p->Draw1Anim || frame > p->RecoilAnim - 1)
frame = p->Draw1Anim;
else if (frame == p->Draw2Anim)
{
DrawPistolMeshes(laraItem, weaponType);
SoundEffect(SFX_TR4_LARA_DRAW, &laraItem->Pose);
}
else if (frame == p->RecoilAnim - 1)
{
ReadyPistols(laraItem, weaponType);
frame = 0;
}
SetArmInfo(laraItem, lara->RightArm, frame);
SetArmInfo(laraItem, lara->LeftArm, frame);
}
void UndrawPistols(ItemInfo* laraItem, LaraWeaponType weaponType)
{
auto* lara = GetLaraInfo(laraItem);
auto* weapon = &Weapons[(int)weaponType];
auto* p = &PistolsTable[(int)lara->Control.Weapon.GunType];
int frameLeft = lara->LeftArm.FrameNumber;
// Finish recoil anim before reholstering weapon.
if ((frameLeft >= p->RecoilAnim) && (frameLeft < p->RecoilAnim + weapon->RecoilFrame))
frameLeft++;
if (frameLeft == (p->RecoilAnim + weapon->RecoilFrame))
frameLeft = p->Draw1Anim2;
else if (frameLeft > 0 && frameLeft < p->Draw1Anim)
{
lara->LeftArm.Orientation.x -= lara->LeftArm.Orientation.x / frameLeft;
lara->LeftArm.Orientation.y -= lara->LeftArm.Orientation.y / frameLeft;
frameLeft--;
}
else if (frameLeft == 0)
{
lara->LeftArm.Orientation = EulerAngles::Zero;
frameLeft = p->RecoilAnim - 1;
}
else if (frameLeft > p->Draw1Anim && (frameLeft < p->RecoilAnim))
{
frameLeft--;
if (frameLeft == p->Draw2Anim - 1)
{
UndrawPistolMeshLeft(laraItem, weaponType);
SoundEffect(SFX_TR4_LARA_HOLSTER, &laraItem->Pose);
}
}
SetArmInfo(laraItem, lara->LeftArm, frameLeft);
int frameRight = lara->RightArm.FrameNumber;
if ((frameRight >= p->RecoilAnim) && (frameRight < p->RecoilAnim + weapon->RecoilFrame))
frameRight++;
if (frameRight == (p->RecoilAnim + weapon->RecoilFrame))
frameRight = p->Draw1Anim2;
else if (frameRight > 0 && frameRight < p->Draw1Anim)
{
lara->RightArm.Orientation.x -= lara->RightArm.Orientation.x / frameRight;
lara->RightArm.Orientation.y -= lara->RightArm.Orientation.y / frameRight;
frameRight--;
}
else if (frameRight == 0)
{
lara->RightArm.Orientation = EulerAngles::Zero;
frameRight = p->RecoilAnim - 1;
}
else if (frameRight > p->Draw1Anim && (frameRight < p->RecoilAnim))
{
frameRight--;
if (frameRight == p->Draw2Anim - 1)
{
UndrawPistolMeshRight(laraItem, weaponType);
SoundEffect(SFX_TR4_LARA_HOLSTER, &laraItem->Pose);
}
}
SetArmInfo(laraItem, lara->RightArm, frameRight);
if (frameLeft == p->Draw1Anim && frameRight == p->Draw1Anim)
{
lara->Control.HandStatus = HandStatus::Free;
lara->LeftArm.FrameNumber = 0;
lara->RightArm.FrameNumber = 0;
lara->TargetEntity = nullptr;
lara->RightArm.Locked = false;
lara->LeftArm.Locked = false;
}
if (!(TrInput & IN_LOOK))
{
lara->ExtraHeadRot = (lara->LeftArm.Orientation + lara->RightArm.Orientation) / 4;
lara->ExtraTorsoRot = (lara->LeftArm.Orientation + lara->RightArm.Orientation) / 4;
}
}
void SetArmInfo(ItemInfo* laraItem, ArmInfo& arm, int frame)
{
auto* lara = GetLaraInfo(laraItem);
const auto& pistols = PistolsTable[(int)lara->Control.Weapon.GunType];
int animBase = Objects[(int)pistols.ObjectNumber].animIndex;
if (frame < pistols.Draw1Anim)
arm.AnimNumber = animBase; arm.AnimNumber = animBase;
else if (frame < pistols.Draw2Anim) }
else if (frame < weaponAnimData.Draw2Anim)
{
arm.AnimNumber = animBase + 1; arm.AnimNumber = animBase + 1;
else if (frame < pistols.RecoilAnim) }
else if (frame < weaponAnimData.RecoilAnim)
{
arm.AnimNumber = animBase + 2; arm.AnimNumber = animBase + 2;
}
else else
{
arm.AnimNumber = animBase + 3; arm.AnimNumber = animBase + 3;
}
arm.FrameNumber = frame; arm.FrameNumber = frame;
arm.FrameBase = g_Level.Anims[arm.AnimNumber].FramePtr; arm.FrameBase = g_Level.Anims[arm.AnimNumber].FramePtr;
} }
void DrawPistolMeshes(ItemInfo* laraItem, LaraWeaponType weaponType) static void ReadyPistols(ItemInfo& laraItem, LaraWeaponType weaponType)
{ {
auto* lara = GetLaraInfo(laraItem); auto& player = GetLaraInfo(laraItem);
if (weaponType != LaraWeaponType::Revolver) player.Control.HandStatus = HandStatus::WeaponReady;
lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty; player.TargetEntity = nullptr;
player.LeftArm.FrameBase =
lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty; player.RightArm.FrameBase = Objects[GetWeaponObjectID(weaponType)].frameBase;
player.LeftArm.FrameNumber =
laraItem->Model.MeshIndex[LM_RHAND] = Objects[GetWeaponObjectMeshID(laraItem, weaponType)].meshIndex + LM_RHAND; player.RightArm.FrameNumber = 0;
if (weaponType != LaraWeaponType::Revolver) player.LeftArm.Orientation =
laraItem->Model.MeshIndex[LM_LHAND] = Objects[GetWeaponObjectMeshID(laraItem, weaponType)].meshIndex + LM_LHAND; player.RightArm.Orientation = EulerAngles::Zero;
player.LeftArm.Locked =
player.RightArm.Locked = false;
} }
void UndrawPistolMeshRight(ItemInfo* laraItem, LaraWeaponType weaponType) static void AnimateWeapon(ItemInfo& laraItem, LaraWeaponType weaponType, bool& hasFired, bool isRightWeapon)
{ {
auto* lara = GetLaraInfo(laraItem); auto& player = GetLaraInfo(laraItem);
auto& arm = isRightWeapon ? player.RightArm : player.LeftArm;
auto& uziBool = isRightWeapon ? player.Control.Weapon.UziRight : player.Control.Weapon.UziLeft;
const auto& weapon = Weapons[(int)weaponType];
const auto& weaponAnimData = GetWeaponAnimData(player.Control.Weapon.GunType);
laraItem->Model.MeshIndex[LM_RHAND] = laraItem->Model.BaseMesh + LM_RHAND; // Spawn weapon smoke.
if (lara->Weapons[(int)weaponType].Present) if (laraItem.MeshBits.TestAny() && arm.GunSmoke)
lara->Control.Weapon.HolsterInfo.RightHolster = GetWeaponHolsterSlot(weaponType); {
auto relOffset = GetWeaponSmokeRelOffset(weaponType, isRightWeapon);
auto pos = GetJointPosition(&laraItem, isRightWeapon ? LM_RHAND : LM_LHAND, relOffset);
TriggerGunSmoke(pos.x, pos.y, pos.z, 0, 0, 0, 0, weaponType, arm.GunSmoke);
}
int frame = arm.FrameNumber;
// Wind animation forward.
if ((IsHeld(In::Action) && !player.TargetEntity) || arm.Locked)
{
// At or beyond DRAW_END (2) start frame AND before SHOOT_START (0) end frame; increment toward SHOOT_START (0) end frame.
if (frame >= 0 && frame < weaponAnimData.Draw1Anim2)
{
frame++;
}
// At SHOOT_START (0) end frame.
else if (frame == weaponAnimData.Draw1Anim2)
{
// Shoot weapon.
if (IsHeld(In::Action))
{
// HACK: Special case for revolver.
bool canShoot = (weaponType == LaraWeaponType::Revolver) ? isRightWeapon : true;
if (canShoot)
{
// HACK: Revolver, a right weapon, uses the left arm's orientation.
auto armOrient = (weaponType == LaraWeaponType::Revolver) ?
EulerAngles(
player.LeftArm.Orientation.x,
player.LeftArm.Orientation.y + laraItem.Pose.Orientation.y,
0) :
EulerAngles(
arm.Orientation.x,
arm.Orientation.y + laraItem.Pose.Orientation.y,
0);
if (FireWeapon(weaponType, *player.TargetEntity, laraItem, armOrient) != FireWeaponType::NoAmmo)
{
arm.GunSmoke = 28;
TriggerGunShell(isRightWeapon ? true : false, ID_GUNSHELL, weaponType);
arm.GunFlash = weapon.FlashTime;
if (weaponType == LaraWeaponType::Uzi)
uziBool = true;
if (!hasFired)
{
SoundEffect(SFX_TR4_EXPLOSION1, &laraItem.Pose, SoundEnvironment::Land, 0.9f, 0.3f);
SoundEffect(weapon.SampleNum, &laraItem.Pose);
hasFired = true;
}
Statistics.Game.AmmoUsed++;
}
}
// Go to SHOOT_CONTINUE (3) start frame.
frame = weaponAnimData.RecoilAnim;
}
else else
lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty; {
if (uziBool)
{
SoundEffect(weapon.SampleNum + 1, &laraItem.Pose);
uziBool = false;
}
}
}
// At or beyond SHOOT_CONTINUE (3) start frame; increment toward SHOOT_CONTINUE (3) end frame to finish recoil before allowing to shoot again.
else if (frame >= weaponAnimData.RecoilAnim)
{
if (weaponType == LaraWeaponType::Uzi)
{
SoundEffect(weapon.SampleNum, &laraItem.Pose);
uziBool = true;
} }
void UndrawPistolMeshLeft(ItemInfo* laraItem, LaraWeaponType weaponType) frame++;
// At SHOOT_CONTINUE (3) end frame; go to START_SHOOT (0) end frame.
if (frame == (weaponAnimData.RecoilAnim + weapon.RecoilFrame))
frame = weaponAnimData.Draw1Anim2;
}
}
// Wind animation backward.
else
{ {
auto* lara = GetLaraInfo(laraItem); // Let SHOOT_CONTINUE (3) finish.
if (frame >= weaponAnimData.RecoilAnim && frame < (weaponAnimData.RecoilAnim + weapon.RecoilFrame))
frame++;
// At SHOOT_CONTINUE (3) end frame; go to START_SHOOT (0) end frame.
if (frame == (weaponAnimData.RecoilAnim + weapon.RecoilFrame))
{
frame = weaponAnimData.Draw1Anim2;
}
// Go back to "ready" stance.
else if ((frame > 0) && (frame <= weaponAnimData.Draw1Anim2))
{
frame--;
}
if (uziBool)
{
SoundEffect(weapon.SampleNum + 1, &laraItem.Pose);
uziBool = false;
}
}
SetArmInfo(laraItem, arm, frame);
}
static int AnimateWeaponUndraw(ItemInfo& laraItem, LaraWeaponType weaponType, bool isRightWeapon)
{
auto& player = GetLaraInfo(laraItem);
auto& arm = isRightWeapon ? player.RightArm : player.LeftArm;
const auto& weapon = Weapons[(int)weaponType];
const auto& weaponAnimData = GetWeaponAnimData(player.Control.Weapon.GunType);
int frame = arm.FrameNumber;
// Finish recoil anim before reholstering weapon.
if (frame >= weaponAnimData.RecoilAnim && frame < (weaponAnimData.RecoilAnim + weapon.RecoilFrame))
frame++;
if (frame == (weaponAnimData.RecoilAnim + weapon.RecoilFrame))
{
frame = weaponAnimData.Draw1Anim2;
}
else if (frame > 0 && frame < weaponAnimData.Draw1Anim)
{
arm.Orientation -= arm.Orientation / frame;
frame--;
}
else if (frame == 0)
{
arm.Orientation = EulerAngles::Zero;
frame = weaponAnimData.RecoilAnim - 1;
}
else if (frame > weaponAnimData.Draw1Anim && frame < weaponAnimData.RecoilAnim)
{
frame--;
if (frame == (weaponAnimData.Draw2Anim - 1))
{
UndrawPistolMesh(laraItem, weaponType, isRightWeapon);
SoundEffect(SFX_TR4_LARA_HOLSTER, &laraItem.Pose);
}
}
SetArmInfo(laraItem, arm, frame);
return frame;
}
void HandlePistols(ItemInfo& laraItem, LaraWeaponType weaponType)
{
auto& lara = *GetLaraInfo(&laraItem);
auto& weapon = Weapons[(int)weaponType];
FindNewTarget(laraItem, weapon);
if (IsHeld(In::Action))
LaraTargetInfo(laraItem, weapon);
AimWeapon(laraItem, lara.LeftArm, weapon);
AimWeapon(laraItem, lara.RightArm, weapon);
if (lara.LeftArm.Locked && !lara.RightArm.Locked)
{
lara.ExtraTorsoRot = lara.LeftArm.Orientation / 2;
if (Camera.oldType != CameraType::Look)
lara.ExtraHeadRot = lara.ExtraTorsoRot;
}
else if (!lara.LeftArm.Locked && lara.RightArm.Locked)
{
lara.ExtraTorsoRot = lara.RightArm.Orientation / 2;
if (Camera.oldType != CameraType::Look)
lara.ExtraHeadRot = lara.ExtraTorsoRot;
}
else if (lara.LeftArm.Locked && lara.RightArm.Locked)
{
lara.ExtraTorsoRot = (lara.LeftArm.Orientation + lara.RightArm.Orientation) / 4;
if (Camera.oldType != CameraType::Look)
lara.ExtraHeadRot = lara.ExtraTorsoRot;
}
AnimatePistols(laraItem, weaponType);
if (lara.LeftArm.GunFlash || lara.RightArm.GunFlash)
{
auto basePos = GetJointPosition(&laraItem, (lara.LeftArm.GunFlash != 0) ? LM_LHAND : LM_RHAND).ToVector3();
auto sphere = BoundingSphere(basePos, BLOCK(1 / 8.0f));
auto lightPos = Random::GeneratePointInSphere(sphere);
TriggerDynamicLight(
lightPos.x, lightPos.y, lightPos.z,
Random::GenerateFloat(8.0f, 11.0f),
(GetRandomControl() & 0x3F) + 192, (GetRandomControl() & 0x1F) + 128, GetRandomControl() & 0x3F);
}
}
void AnimatePistols(ItemInfo& laraItem, LaraWeaponType weaponType)
{
bool hasFired = false;
AnimateWeapon(laraItem, weaponType, hasFired, true);
AnimateWeapon(laraItem, weaponType, hasFired, false);
// If either weapon has fired, rumble gamepad.
if (hasFired)
{
float power = (weaponType == LaraWeaponType::Uzi) ? Random::GenerateFloat(0.1f, 0.3f) : 1.0f;
Rumble(power, 0.1f);
}
}
void DrawPistols(ItemInfo& laraItem, LaraWeaponType weaponType)
{
auto& player = GetLaraInfo(laraItem);
const auto& weaponAnimData = GetWeaponAnimData(player.Control.Weapon.GunType);
int frame = player.LeftArm.FrameNumber + 1;
if (frame < weaponAnimData.Draw1Anim || frame > (weaponAnimData.RecoilAnim - 1))
{
frame = weaponAnimData.Draw1Anim;
}
else if (frame == weaponAnimData.Draw2Anim)
{
DrawPistolMeshes(laraItem, weaponType);
SoundEffect(SFX_TR4_LARA_DRAW, &laraItem.Pose);
}
else if (frame == (weaponAnimData.RecoilAnim - 1))
{
ReadyPistols(laraItem, weaponType);
frame = 0;
}
SetArmInfo(laraItem, player.RightArm, frame);
SetArmInfo(laraItem, player.LeftArm, frame);
}
void UndrawPistols(ItemInfo& laraItem, LaraWeaponType weaponType)
{
auto& player = GetLaraInfo(laraItem);
const auto& weaponAnimData = GetWeaponAnimData(player.Control.Weapon.GunType);
int frameLeft = AnimateWeaponUndraw(laraItem, weaponType, false);
int frameRight = AnimateWeaponUndraw(laraItem, weaponType, true);
if (frameLeft == weaponAnimData.Draw1Anim &&
frameRight == weaponAnimData.Draw1Anim)
{
player.Control.HandStatus = HandStatus::Free;
player.TargetEntity = nullptr;
player.LeftArm.FrameNumber =
player.RightArm.FrameNumber = 0;
player.LeftArm.Locked =
player.RightArm.Locked = false;
}
if (!IsHeld(In::Look))
{
player.ExtraHeadRot = (player.LeftArm.Orientation + player.RightArm.Orientation) / 4;
player.ExtraTorsoRot = (player.LeftArm.Orientation + player.RightArm.Orientation) / 4;
}
}
void DrawPistolMeshes(ItemInfo& laraItem, LaraWeaponType weaponType)
{
auto& player = GetLaraInfo(laraItem);
if (weaponType != LaraWeaponType::Revolver) if (weaponType != LaraWeaponType::Revolver)
{ player.Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty;
laraItem->Model.MeshIndex[LM_LHAND] = laraItem->Model.BaseMesh + LM_LHAND;
if (lara->Weapons[(int)weaponType].Present) player.Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty;
lara->Control.Weapon.HolsterInfo.LeftHolster = GetWeaponHolsterSlot(weaponType);
laraItem.Model.MeshIndex[LM_RHAND] = Objects[GetWeaponObjectMeshID(laraItem, weaponType)].meshIndex + LM_RHAND;
if (weaponType != LaraWeaponType::Revolver)
laraItem.Model.MeshIndex[LM_LHAND] = Objects[GetWeaponObjectMeshID(laraItem, weaponType)].meshIndex + LM_LHAND;
}
void UndrawPistolMesh(ItemInfo& laraItem, LaraWeaponType weaponType, bool isRightWeapon)
{
auto& player = GetLaraInfo(laraItem);
auto& holster = isRightWeapon ? player.Control.Weapon.HolsterInfo.RightHolster : player.Control.Weapon.HolsterInfo.LeftHolster;
// HACK: Special case for revolver.
if (!isRightWeapon && weaponType == LaraWeaponType::Revolver)
return;
int jointIndex = isRightWeapon ? LM_RHAND : LM_LHAND;
laraItem.Model.MeshIndex[jointIndex] = laraItem.Model.BaseMesh + jointIndex;
if (player.Weapons[(int)weaponType].Present)
{
holster = GetWeaponHolsterSlot(weaponType);
}
else else
lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty; {
holster = HolsterSlot::Empty;
} }
} }

View file

@ -1,12 +1,13 @@
#pragma once #pragma once
#include "Game/Lara/lara_struct.h"
void AnimatePistols(ItemInfo* laraItem, LaraWeaponType weaponType); enum class LaraWeaponType;
void PistolHandler(ItemInfo* laraItem, LaraWeaponType weaponType); struct ItemInfo;
void ReadyPistols(ItemInfo* laraItem, LaraWeaponType weaponType);
void DrawPistols(ItemInfo* laraItem, LaraWeaponType weaponType); void HandlePistols(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawPistols(ItemInfo* laraItem, LaraWeaponType weaponType); void AnimatePistols(ItemInfo& laraItem, LaraWeaponType weaponType);
void SetArmInfo(ItemInfo* laraItem, ArmInfo& arm, int frame);
void DrawPistolMeshes(ItemInfo* laraItem, LaraWeaponType weaponType); void DrawPistols(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawPistolMeshRight(ItemInfo* laraItem, LaraWeaponType weaponType); void DrawPistolMeshes(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawPistolMeshLeft(ItemInfo* laraItem, LaraWeaponType weaponType);
void UndrawPistols(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawPistolMesh(ItemInfo& laraItem, LaraWeaponType weaponType, bool isRightWeapon);

139
TombEngine/Game/Setup.cpp Normal file
View file

@ -0,0 +1,139 @@
#include "framework.h"
#include "Game/Setup.h"
#include "Game/animation.h"
#include "Game/camera.h"
#include "Game/collision/collide_item.h"
#include "Game/control/flipeffect.h"
#include "Game/effects/effects.h"
#include "Game/effects/Hair.h"
#include "Game/effects/tomb4fx.h"
#include "Game/itemdata/creature_info.h"
#include "Game/pickup/pickup.h"
#include "Game/room.h"
#include "Objects/Effects/effect_objects.h"
#include "Objects/Generic/generic_objects.h"
#include "Objects/Generic/Object/objects.h"
#include "Objects/Generic/Object/rope.h"
#include "Objects/Generic/Switches/fullblock_switch.h"
#include "Objects/Generic/Switches/switch.h"
#include "Objects/Generic/Traps/falling_block.h"
#include "Objects/TR1/tr1_objects.h"
#include "Objects/TR2/tr2_objects.h"
#include "Objects/TR3/tr3_objects.h"
#include "Objects/TR4/tr4_objects.h"
#include "Objects/TR5/tr5_objects.h"
#include "Objects/TR4/Entity/tr4_beetle_swarm.h"
#include "Objects/Utils/object_helper.h"
#include "Specific/level.h"
using namespace TEN::Effects::Hair;
using namespace TEN::Entities;
using namespace TEN::Entities::Switches;
ObjectInfo Objects[ID_NUMBER_OBJECTS];
STATIC_INFO StaticObjects[MAX_STATICS];
void InitializeGameFlags()
{
ZeroMemory(FlipMap, MAX_FLIPMAP * sizeof(int));
ZeroMemory(FlipStats, MAX_FLIPMAP * sizeof(int));
FlipEffect = -1;
FlipStatus = 0;
Camera.underwater = false;
}
void InitializeSpecialEffects()
{
memset(&FireSparks, 0, MAX_SPARKS_FIRE * sizeof(FIRE_SPARKS));
memset(&SmokeSparks, 0, MAX_SPARKS_SMOKE * sizeof(SMOKE_SPARKS));
memset(&Gunshells, 0, MAX_GUNSHELL * sizeof(GUNSHELL_STRUCT));
memset(&Gunflashes, 0, (MAX_GUNFLASH * sizeof(GUNFLASH_STRUCT)));
memset(&Blood, 0, MAX_SPARKS_BLOOD * sizeof(BLOOD_STRUCT));
memset(&Splashes, 0, MAX_SPLASHES * sizeof(SPLASH_STRUCT));
memset(&ShockWaves, 0, MAX_SHOCKWAVE * sizeof(SHOCKWAVE_STRUCT));
memset(&Particles, 0, MAX_PARTICLES * sizeof(Particle));
for (int i = 0; i < MAX_PARTICLES; i++)
{
Particles[i].on = false;
Particles[i].dynamic = -1;
}
NextFireSpark = 1;
NextSmokeSpark = 0;
NextGunShell = 0;
NextBlood = 0;
TEN::Entities::TR4::ClearBeetleSwarm();
}
void CustomObjects()
{
}
void InitializeObjects()
{
AllocTR4Objects();
AllocTR5Objects();
ObjectInfo* obj;
for (int i = 0; i < ID_NUMBER_OBJECTS; i++)
{
obj = &Objects[i];
obj->Initialize = nullptr;
obj->collision = nullptr;
obj->control = nullptr;
obj->floor = nullptr;
obj->ceiling = nullptr;
obj->drawRoutine = DrawAnimatingItem;
obj->HitRoutine = DefaultItemHit;
obj->pivotLength = 0;
obj->radius = DEFAULT_RADIUS;
obj->shadowType = ShadowMode::None;
obj->HitPoints = NOT_TARGETABLE;
obj->hitEffect = HitEffect::None;
obj->explodableMeshbits = 0;
obj->intelligent = false;
obj->waterCreature = false;
obj->nonLot = false;
obj->usingDrawAnimatingItem = true;
obj->undead = false;
obj->LotType = LotType::Basic;
obj->meshSwapSlot = NO_ITEM;
obj->isPickup = false;
obj->isPuzzleHole = false;
}
InitializeEffectsObjects();
InitializeGenericObjects(); // Generic objects
InitializeTR1Objects(); // Standard TR1 objects
InitializeTR2Objects(); // Standard TR2 objects
InitializeTR3Objects(); // Standard TR3 objects
InitializeTR4Objects(); // Standard TR4 objects
InitializeTR5Objects(); // Standard TR5 objects
// User defined objects
CustomObjects();
HairEffect.Initialize();
InitializeSpecialEffects();
NumRPickups = 0;
CurrentSequence = 0;
SequenceResults[0][1][2] = 0;
SequenceResults[0][2][1] = 1;
SequenceResults[1][0][2] = 2;
SequenceResults[1][2][0] = 3;
SequenceResults[2][0][1] = 4;
SequenceResults[2][1][0] = 5;
SequenceUsed[0] = 0;
SequenceUsed[1] = 0;
SequenceUsed[2] = 0;
SequenceUsed[3] = 0;
SequenceUsed[4] = 0;
SequenceUsed[5] = 0;
}

View file

@ -1,16 +1,16 @@
#pragma once #pragma once
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Game/control/box.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer11Enums.h" #include "Renderer/Renderer11Enums.h"
#include "Specific/level.h" #include "Specific/level.h"
enum class ZoneType;
struct CollisionInfo; struct CollisionInfo;
struct ItemInfo; struct ItemInfo;
constexpr auto DEFAULT_RADIUS = 10; constexpr auto DEFAULT_RADIUS = 10;
// Custom LOT definition for Creature. Used in InitialiseSlot() in lot.cpp. // Custom LOT definition for Creature. Used in InitializeSlot() in lot.cpp.
enum class LotType enum class LotType
{ {
Skeleton, Skeleton,
@ -23,7 +23,8 @@ enum class LotType
Flyer, Flyer,
Blockable, // For large creatures such as trex and shiva. Blockable, // For large creatures such as trex and shiva.
Spider, // Only 2 block vault allowed. Spider, // Only 2 block vault allowed.
Ape // Only 2 block vault allowed. Ape, // Only 2 block vault allowed.
SnowmobileGun // Only 1 block vault allowed and 4 block drop max.
}; };
enum JointRotationFlags enum JointRotationFlags
@ -52,53 +53,59 @@ enum ShatterType
struct ObjectInfo struct ObjectInfo
{ {
int nmeshes; bool loaded = false; // IsLoaded
int meshIndex;
int boneIndex; int nmeshes; // BoneCount
int frameBase; int meshIndex; // Base index in g_Level.Meshes.
int boneIndex; // Base index in g_Level.Bones.
int animIndex; // Base index in g_Level.Anims.
int frameBase; // Base index in g_Level.Frames.
LotType LotType; LotType LotType;
int animIndex;
short HitPoints;
short pivotLength;
short radius;
ShadowMode shadowType;
short biteOffset;
bool loaded;
bool intelligent;
bool nonLot;
bool waterCreature;
bool usingDrawAnimatingItem;
HitEffect hitEffect; HitEffect hitEffect;
bool undead; ShadowMode shadowType;
bool isPickup;
bool isPuzzleHole;
int meshSwapSlot; int meshSwapSlot;
int pivotLength;
int radius;
int HitPoints;
bool intelligent; // IsIntelligent
bool waterCreature; // IsWaterCreature
bool undead; // IsUndead
bool nonLot; // IsNonLot
bool isPickup; // IsPickup
bool isPuzzleHole; // IsReceptacle
bool usingDrawAnimatingItem;
DWORD explodableMeshbits; DWORD explodableMeshbits;
std::function<void(short itemNumber)> initialise; std::function<void(short itemNumber)> Initialize;
std::function<void(short itemNumber)> control; std::function<void(short itemNumber)> control;
std::function<void(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)> collision;
std::function<void(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex)> HitRoutine;
std::function<void(ItemInfo* item)> drawRoutine;
std::function<std::optional<int>(int itemNumber, int x, int y, int z)> floor; std::function<std::optional<int>(int itemNumber, int x, int y, int z)> floor;
std::function<std::optional<int>(int itemNumber, int x, int y, int z)> ceiling; std::function<std::optional<int>(int itemNumber, int x, int y, int z)> ceiling;
std::function<int(short itemNumber)> floorBorder; std::function<int(short itemNumber)> floorBorder;
std::function<int(short itemNumber)> ceilingBorder; std::function<int(short itemNumber)> ceilingBorder;
std::function<void(ItemInfo* item)> drawRoutine;
std::function<void(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)> collision;
std::function<void(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex)> HitRoutine;
/// <summary> /// <summary>
/// Use ROT_X/Y/Z to allow bones to be rotated with CreatureJoint(). /// ROT_X/Y/Z allows bones to be rotated with CreatureJoint().
/// </summary> /// </summary>
/// <param name="boneID">the mesh id - 1</param> /// <param name="boneNumber:">Mesh number - 1.</param>
/// <param name="flags">can be ROT_X, ROT_Y, ROT_Z or all.</param> /// <param name="flags:">JointRotationFlags enum.</param>
void SetBoneRotationFlags(int boneID, int flags) void SetBoneRotationFlags(int boneNumber, int flags)
{ {
g_Level.Bones[boneIndex + boneID * 4] |= flags; g_Level.Bones[boneIndex + (boneNumber * 4)] |= flags;
} }
/// <summary> /// <summary>
/// Use this to set up a hit effect for the slot based on its value. /// Set up hit effect for object based on its value.
/// </summary> /// </summary>
/// <param name="isAlive">Use this if the object is alive but not intelligent to set up blood effects.</param> /// <param name="isAlive:">Use if object is alive but not intelligent to set up blood effects.</param>
void SetupHitEffect(bool isSolid = false, bool isAlive = false) void SetupHitEffect(bool isSolid = false, bool isAlive = false)
{ {
// Avoid some objects such as ID_SAS_DYING having None. // Avoid some objects such as ID_SAS_DYING having None.
@ -153,6 +160,6 @@ constexpr auto SWAMP_GRAVITY = GRAVITY / 3.0f;
extern ObjectInfo Objects[ID_NUMBER_OBJECTS]; extern ObjectInfo Objects[ID_NUMBER_OBJECTS];
extern STATIC_INFO StaticObjects[MAX_STATICS]; extern STATIC_INFO StaticObjects[MAX_STATICS];
void InitialiseGameFlags(); void InitializeGameFlags();
void InitialiseSpecialEffects(); void InitializeSpecialEffects();
void InitialiseObjects(); void InitializeObjects();

View file

@ -8,12 +8,12 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/Generic/Object/rope.h" #include "Objects/Generic/Object/rope.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Math; using namespace TEN::Math;
@ -74,10 +74,10 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased)
{ {
auto& player = GetLaraInfo(item); auto& player = GetLaraInfo(item);
if (player.Control.CalculatedJumpVelocity != 0) if (player.Context.CalcJumpVelocity != 0)
{ {
item.Animation.Velocity.y = player.Control.CalculatedJumpVelocity; item.Animation.Velocity.y = player.Context.CalcJumpVelocity;
player.Control.CalculatedJumpVelocity = 0; player.Context.CalcJumpVelocity = 0;
} }
} }
} }
@ -121,8 +121,8 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased)
auto& player = GetLaraInfo(item); auto& player = GetLaraInfo(item);
if (playAlways || if (playAlways ||
(playOnLand && (player.WaterSurfaceDist >= -SHALLOW_WATER_DEPTH || player.WaterSurfaceDist == NO_HEIGHT)) || (playOnLand && (player.Context.WaterSurfaceDist >= -SHALLOW_WATER_DEPTH || player.Context.WaterSurfaceDist == NO_HEIGHT)) ||
(playInWater && player.WaterSurfaceDist < -SHALLOW_WATER_DEPTH && player.WaterSurfaceDist != NO_HEIGHT && !TestEnvironment(ENV_FLAG_SWAMP, &item))) (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); SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
} }
@ -216,6 +216,19 @@ void AnimateItem(ItemInfo* item)
{ {
item->Animation.ActiveState = animPtr->ActiveState; item->Animation.ActiveState = animPtr->ActiveState;
} }
// TODO: Theoretically this is better than above block, but it must be checked. -- Sezz 2023.03.31
/*if (item->Animation.ActiveState != animPtr->ActiveState)
{
item->Animation.ActiveState =
item->Animation.TargetState = animPtr->ActiveState;
}
if (!item->IsLara())
{
if (item->Animation.RequiredState == item->Animation.ActiveState)
item->Animation.RequiredState = NO_STATE;
}*/
} }
unsigned int frameCount = GetNonZeroFrameCount(*animPtr); unsigned int frameCount = GetNonZeroFrameCount(*animPtr);
@ -336,19 +349,23 @@ bool HasStateDispatch(ItemInfo* item, int targetState)
bool TestAnimNumber(const ItemInfo& item, int animNumber) bool TestAnimNumber(const ItemInfo& item, int animNumber)
{ {
const auto& object = Objects[item.ObjectNumber]; const auto& object = Objects[item.Animation.AnimObjectID];
return (item.Animation.AnimNumber == (object.animIndex + animNumber)); return (item.Animation.AnimNumber == (object.animIndex + animNumber));
} }
bool TestLastFrame(ItemInfo* item, int animNumber) bool TestLastFrame(ItemInfo* item, int animNumber)
{ {
if (animNumber == NO_ANIM) const auto& object = Objects[item->Animation.AnimObjectID];
animNumber = item->Animation.AnimNumber;
if (item->Animation.AnimNumber != animNumber) if (animNumber == NO_ANIM)
animNumber = item->Animation.AnimNumber - object.animIndex;
// Animation to test doesn't match; return early.
int animIndex = object.animIndex + animNumber;
if (item->Animation.AnimNumber != animIndex)
return false; return false;
const auto& anim = GetAnimData(*item, animNumber); const auto& anim = GetAnimData(object, animNumber);
return (item->Animation.FrameNumber >= anim.frameEnd); return (item->Animation.FrameNumber >= anim.frameEnd);
} }
@ -380,32 +397,49 @@ void TranslateItem(ItemInfo* item, const Vector3& direction, float distance)
item->Pose.Translate(direction, distance); item->Pose.Translate(direction, distance);
} }
void SetAnimation(ItemInfo* item, int animNumber, int frameNumber) void SetAnimation(ItemInfo& item, GAME_OBJECT_ID animObjectID, int animNumber, int frameNumber)
{ {
const auto& object = Objects[item->ObjectNumber]; const auto& animObject = Objects[animObjectID];
int animIndex = object.animIndex + animNumber; int animIndex = animObject.animIndex + animNumber;
// Animation already set; return early. // Animation is missing; return early.
if (item->Animation.AnimNumber == animIndex)
return;
// Animation doesn't exist; return early.
if (animIndex < 0 || animIndex >= g_Level.Anims.size()) if (animIndex < 0 || animIndex >= g_Level.Anims.size())
{ {
TENLog( TENLog(
std::string("Attempted to set nonexistent animation ") + std::to_string(animNumber) + std::string("Attempted to set missing animation ") + std::to_string(animNumber) +
std::string(" for object ") + std::to_string(item->ObjectNumber), (animObjectID == item.ObjectNumber ? std::string() : std::string(" from object ") + GetObjectName(animObjectID)) +
std::string(" for object ") + GetObjectName(item.ObjectNumber),
LogLevel::Warning); LogLevel::Warning);
return; return;
} }
const auto& anim = GetAnimData(*item, animNumber); const auto& anim = GetAnimData(animObject, animNumber);
int frameIndex = anim.frameBase + frameNumber;
item->Animation.AnimNumber = animIndex; // Animation already set; return early.
item->Animation.FrameNumber = anim.frameBase + frameNumber; if (item.Animation.AnimObjectID == animObjectID &&
item->Animation.ActiveState = item.Animation.AnimNumber == animIndex &&
item->Animation.TargetState = anim.ActiveState; item.Animation.FrameNumber == frameIndex)
{
return;
}
item.Animation.AnimObjectID = animObjectID;
item.Animation.AnimNumber = animIndex;
item.Animation.FrameNumber = frameIndex;
item.Animation.ActiveState =
item.Animation.TargetState = anim.ActiveState;
}
void SetAnimation(ItemInfo& item, int animNumber, int frameNumber)
{
SetAnimation(item, item.ObjectNumber, animNumber, frameNumber);
}
void SetAnimation(ItemInfo* item, int animNumber, int frameNumber)
{
SetAnimation(*item, item->ObjectNumber, animNumber, frameNumber);
} }
AnimData& GetAnimData(int animIndex) AnimData& GetAnimData(int animIndex)
@ -413,6 +447,12 @@ AnimData& GetAnimData(int animIndex)
return g_Level.Anims[animIndex]; return g_Level.Anims[animIndex];
} }
AnimData& GetAnimData(GAME_OBJECT_ID objectID, int animNumber)
{
const auto& object = Objects[objectID];
return GetAnimData(object, animNumber);
}
AnimData& GetAnimData(const ObjectInfo& object, int animNumber) AnimData& GetAnimData(const ObjectInfo& object, int animNumber)
{ {
return g_Level.Anims[object.animIndex + animNumber]; return g_Level.Anims[object.animIndex + animNumber];
@ -421,12 +461,140 @@ AnimData& GetAnimData(const ObjectInfo& object, int animNumber)
AnimData& GetAnimData(const ItemInfo& item, int animNumber) AnimData& GetAnimData(const ItemInfo& item, int animNumber)
{ {
if (animNumber == NO_ANIM) if (animNumber == NO_ANIM)
return g_Level.Anims[item.Animation.AnimNumber]; return GetAnimData(item.Animation.AnimNumber);
const auto& object = Objects[item.ObjectNumber]; const auto& object = Objects[item.Animation.AnimObjectID];
return GetAnimData(object, animNumber); return GetAnimData(object, animNumber);
} }
AnimFrameInterpData GetFrameInterpData(const ItemInfo& item)
{
const auto& anim = GetAnimData(item);
// Normalize animation's current frame number into keyframe range.
int frameNumber = GetFrameNumber(item);
float frameNumberNorm = frameNumber / (float)anim.Interpolation;
// Calculate keyframe numbers defining interpolated frame and get pointers to them.
int frame0 = (int)floor(frameNumberNorm);
int frame1 = (int)ceil(frameNumberNorm);
auto* framePtr0 = &g_Level.Frames[anim.FramePtr + frame0];
auto* framePtr1 = &g_Level.Frames[anim.FramePtr + frame1];
// Calculate interpolation alpha between keyframes.
float alpha = (1.0f / anim.Interpolation) * (frameNumber % anim.Interpolation);
// Return frame interpolation data.
return AnimFrameInterpData{ framePtr0, framePtr1, alpha };
}
AnimFrame& GetAnimFrame(const ItemInfo& item, int animNumber, int frameNumber)
{
return *GetFrame(item.ObjectNumber, animNumber, frameNumber);
}
AnimFrame* GetFrame(GAME_OBJECT_ID objectID, int animNumber, int frameNumber)
{
const auto& object = Objects[objectID];
int animIndex = object.animIndex + animNumber;
assertion(animIndex < g_Level.Anims.size(), "GetFrame() attempted to access missing animation.");
const auto& anim = GetAnimData(object, animNumber);
// Get and clamp frame count.
unsigned int frameCount = anim.frameEnd - anim.frameBase;
if (frameNumber > frameCount)
frameNumber = frameCount;
// Interpolate and return frame pointer.
auto* framePtr = &g_Level.Frames[anim.FramePtr];
framePtr += frameNumber / anim.Interpolation;
return framePtr;
}
AnimFrame* GetFirstFrame(GAME_OBJECT_ID objectID, int animNumber)
{
return GetFrame(objectID, animNumber, 0);
}
AnimFrame* GetLastFrame(GAME_OBJECT_ID objectID, int animNumber)
{
return GetFrame(objectID, animNumber, INT_MAX);
}
AnimFrame& GetBestFrame(const ItemInfo& item)
{
auto frameData = GetFrameInterpData(item);
if (frameData.Alpha <= 0.5f)
return *frameData.FramePtr0;
else
return *frameData.FramePtr1;
}
int GetFrameNumber(const ItemInfo& item)
{
const auto& anim = GetAnimData(item);
return (item.Animation.FrameNumber - anim.frameBase);
}
int GetFrameNumber(ItemInfo* item)
{
return GetFrameNumber(*item);
}
int GetAnimNumber(const ItemInfo& item)
{
const auto& object = Objects[item.Animation.AnimObjectID];
return (item.Animation.AnimNumber - object.animIndex);
}
int GetAnimIndex(const ItemInfo& item, int animNumber)
{
const auto& object = Objects[item.Animation.AnimObjectID];
return (object.animIndex + animNumber);
}
int GetFrameIndex(ItemInfo* item, int frameNumber)
{
int animNumber = item->Animation.AnimNumber - Objects[item->Animation.AnimObjectID].animIndex;
return GetFrameIndex(item->Animation.AnimObjectID, animNumber, frameNumber);
}
int GetFrameIndex(GAME_OBJECT_ID objectID, int animNumber, int frameNumber)
{
const auto& object = Objects[objectID];
const auto& anim = GetAnimData(object, animNumber);
return (anim.frameBase + frameNumber);
}
int GetFrameCount(int animIndex)
{
if (animIndex < 0 || g_Level.Anims.size() <= animIndex)
return 0;
const auto& anim = GetAnimData(animIndex);
int end = anim.frameEnd;
int base = anim.frameBase;
return (end - base);
}
int GetNextAnimState(ItemInfo* item)
{
return GetNextAnimState(item->Animation.AnimObjectID, item->Animation.AnimNumber);
}
int GetNextAnimState(int objectID, int animNumber)
{
const auto& object = Objects[objectID];
const auto& anim = GetAnimData(object, animNumber);
const auto& nextAnim = GetAnimData(anim.JumpAnimNum);
return nextAnim.ActiveState;
}
bool GetStateDispatch(ItemInfo* item, const AnimData& anim) bool GetStateDispatch(ItemInfo* item, const AnimData& anim)
{ {
// Active and target states already match; return early. // Active and target states already match; return early.
@ -464,123 +632,6 @@ bool GetStateDispatch(ItemInfo* item, const AnimData& anim)
return false; return false;
} }
AnimFrameInterpData GetFrameInterpData(const ItemInfo& item)
{
const auto& anim = GetAnimData(item);
// Normalize animation's current frame number into keyframe range.
int frameNumber = item.Animation.FrameNumber - anim.frameBase;
float frameNumberNorm = frameNumber / (float)anim.Interpolation;
// Calculate keyframe numbers defining interpolated frame and get pointers to them.
int frame0 = (int)floor(frameNumberNorm);
int frame1 = (int)ceil(frameNumberNorm);
auto* framePtr0 = &g_Level.Frames[anim.FramePtr + frame0];
auto* framePtr1 = &g_Level.Frames[anim.FramePtr + frame1];
// Calculate interpolation alpha between keyframes.
float alpha = (1.0f / anim.Interpolation) * (frameNumber % anim.Interpolation);
// Return frame interpolation data.
return AnimFrameInterpData{ framePtr0, framePtr1, alpha };
}
AnimFrame& GetAnimFrame(const ItemInfo& item, int animNumber, int frameNumber)
{
return *GetFrame(item.ObjectNumber, animNumber, frameNumber);
}
AnimFrame* GetFrame(GAME_OBJECT_ID objectID, int animNumber, int frameNumber)
{
const auto& object = Objects[objectID];
int animIndex = object.animIndex + animNumber;
assertion(animIndex < g_Level.Anims.size(), "GetFrame() attempted to access nonexistent animation.");
const auto& anim = GetAnimData(object, animNumber);
// Get and clamp frame count.
unsigned int frameCount = anim.frameEnd - anim.frameBase;
if (frameNumber > frameCount)
frameNumber = frameCount;
// Interpolate and return frame pointer.
auto* framePtr = &g_Level.Frames[anim.FramePtr];
framePtr += frameNumber / anim.Interpolation;
return framePtr;
}
AnimFrame* GetFirstFrame(GAME_OBJECT_ID objectID, int animNumber)
{
return GetFrame(objectID, animNumber, 0);
}
AnimFrame* GetLastFrame(GAME_OBJECT_ID objectID, int animNumber)
{
return GetFrame(objectID, animNumber, INT_MAX);
}
AnimFrame& GetBestFrame(const ItemInfo& item)
{
auto frameData = GetFrameInterpData(item);
if (frameData.Alpha <= 0.5f)
return *frameData.FramePtr0;
else
return *frameData.FramePtr1;
}
int GetCurrentRelativeFrameNumber(ItemInfo* item)
{
return (item->Animation.FrameNumber - GetFrameNumber(item, 0));
}
// NOTE: Returns g_Level.Anims index.
int GetAnimNumber(ItemInfo& item, int animNumber)
{
const auto& object = Objects[item.ObjectNumber];
return (object.animIndex + animNumber);
}
int GetFrameNumber(ItemInfo* item, int frameToStart)
{
int animNumber = item->Animation.AnimNumber - Objects[item->ObjectNumber].animIndex;
return GetFrameNumber(item->ObjectNumber, animNumber, frameToStart);
}
int GetFrameNumber(int objectID, int animNumber, int frameToStart)
{
const auto& object = Objects[objectID];
const auto& anim = GetAnimData(object, animNumber);
return (anim.frameBase + frameToStart);
}
int GetFrameCount(int animIndex)
{
if (animIndex < 0 || g_Level.Anims.size() <= animIndex)
return 0;
const auto& anim = GetAnimData(animIndex);
int end = anim.frameEnd;
int base = anim.frameBase;
return (end - base);
}
int GetNextAnimState(ItemInfo* item)
{
return GetNextAnimState(item->ObjectNumber, item->Animation.AnimNumber);
}
int GetNextAnimState(int objectID, int animNumber)
{
const auto& object = Objects[objectID];
const auto& anim = GetAnimData(object, animNumber);
const auto& nextAnim = GetAnimData(anim.JumpAnimNum);
return nextAnim.ActiveState;
}
void DrawAnimatingItem(ItemInfo* item) void DrawAnimatingItem(ItemInfo* item)
{ {
// TODO: to refactor // TODO: to refactor
@ -613,6 +664,16 @@ Vector3i GetJointPosition(ItemInfo* item, int jointIndex, const Vector3i& relOff
return GetJointPosition(*item, jointIndex, relOffset); return GetJointPosition(*item, jointIndex, relOffset);
} }
Vector3i GetJointPosition(ItemInfo* item, const CreatureBiteInfo& bite)
{
return GetJointPosition(item, bite.BoneID, bite.Position);
}
Vector3i GetJointPosition(const ItemInfo& item, const CreatureBiteInfo& bite)
{
return GetJointPosition(item, bite.BoneID, bite.Position);
}
Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex) Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex)
{ {
const auto& object = Objects[objectID]; const auto& object = Objects[objectID];

View file

@ -4,13 +4,16 @@
using namespace TEN::Math; using namespace TEN::Math;
enum GAME_OBJECT_ID : short;
class EulerAngles; class EulerAngles;
class Pose; class Pose;
class Vector3i; class Vector3i;
struct CreatureBiteInfo;
struct ItemInfo; struct ItemInfo;
struct ObjectInfo;
// NOTES: // NOTES:
// animNumber: Relative entity animation ID. // animNumber: Relative animation number.
// animIndex: Index of animation in giant g_Level.Anims vector. // animIndex: Index of animation in giant g_Level.Anims vector.
constexpr auto NO_STATE = -1; constexpr auto NO_STATE = -1;
@ -107,17 +110,26 @@ void TranslateItem(ItemInfo* item, const EulerAngles& orient, float distance);
void TranslateItem(ItemInfo* item, const Vector3& direction, float distance); void TranslateItem(ItemInfo* item, const Vector3& direction, float distance);
// Setters // Setters
void SetAnimation(ItemInfo* item, int animNumber, int frameNumber = 0); void SetAnimation(ItemInfo& item, GAME_OBJECT_ID animObjectID, int animNumber, int frameNumber = 0);
void SetAnimation(ItemInfo& item, int animNumber, int frameNumber = 0);
void SetAnimation(ItemInfo* item, int animNumber, int frameNumber = 0); // Deprecated.
// Getters // Getters
AnimData& GetAnimData(int animIndex); AnimData& GetAnimData(int animIndex); // Deprecated.
AnimData& GetAnimData(GAME_OBJECT_ID objectID, int animNumber);
AnimData& GetAnimData(const ObjectInfo& object, int animNumber); AnimData& GetAnimData(const ObjectInfo& object, int animNumber);
AnimData& GetAnimData(const ItemInfo& item, int animNumber = NO_ANIM); AnimData& GetAnimData(const ItemInfo& item, int animNumber = NO_ANIM);
int GetCurrentRelativeFrameNumber(ItemInfo* item);
int GetAnimNumber(ItemInfo& item, int animNumber); int GetAnimNumber(const ItemInfo& item);
int GetFrameNumber(ItemInfo* item, int frameToStart); int GetAnimIndex(const ItemInfo& item, int animNumber);
int GetFrameNumber(int objectID, int animNumber, int frameToStart);
int GetFrameNumber(const ItemInfo& item);
int GetFrameNumber(ItemInfo* item); // Deprecated.
int GetFrameIndex(ItemInfo* item, int frameNumber);
int GetFrameIndex(GAME_OBJECT_ID objectID, int animNumber, int frameNumber);
int GetFrameCount(int animIndex); int GetFrameCount(int animIndex);
int GetNextAnimState(ItemInfo* item); int GetNextAnimState(ItemInfo* item);
int GetNextAnimState(int objectID, int animNumber); int GetNextAnimState(int objectID, int animNumber);
bool GetStateDispatch(ItemInfo* item, const AnimData& anim); bool GetStateDispatch(ItemInfo* item, const AnimData& anim);
@ -134,6 +146,9 @@ void DrawAnimatingItem(ItemInfo* item);
Vector3i GetJointPosition(const ItemInfo& item, int jointIndex, const Vector3i& relOffset = Vector3i::Zero); Vector3i GetJointPosition(const ItemInfo& item, int jointIndex, const Vector3i& relOffset = Vector3i::Zero);
Vector3i GetJointPosition(ItemInfo* item, int jointIndex, const Vector3i& relOffset = Vector3i::Zero); Vector3i GetJointPosition(ItemInfo* item, int jointIndex, const Vector3i& relOffset = Vector3i::Zero);
Vector3i GetJointPosition(ItemInfo* item, const CreatureBiteInfo& bite);
Vector3i GetJointPosition(const ItemInfo& item, const CreatureBiteInfo& bite);
Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex); Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex);
Quaternion GetBoneOrientation(const ItemInfo& item, int boneIndex); Quaternion GetBoneOrientation(const ItemInfo& item, int boneIndex);
float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex); float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex);

View file

@ -13,12 +13,12 @@
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h"
#include "Game/spotcam.h" #include "Game/spotcam.h"
#include "Objects/Generic/Object/burning_torch.h" #include "Objects/Generic/Object/burning_torch.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
@ -117,7 +117,7 @@ inline void RumbleFromBounce()
} }
void InitialiseCamera() void InitializeCamera()
{ {
Camera.shift = LaraItem->Pose.Position.y - SECTOR(1); Camera.shift = LaraItem->Pose.Position.y - SECTOR(1);
@ -545,7 +545,10 @@ void DoThumbstickCamera()
Camera.target.y == OldCam.target.y && Camera.target.y == OldCam.target.y &&
Camera.target.z == OldCam.target.z)) Camera.target.z == OldCam.target.z))
{ {
if (abs(AxisMap[InputAxis::CameraHorizontal]) > EPSILON && abs(Camera.targetAngle) == 0)
Camera.targetAngle = ANGLE(THUMBCAM_VERTICAL_CONSTRAINT_ANGLE * AxisMap[InputAxis::CameraHorizontal]); Camera.targetAngle = ANGLE(THUMBCAM_VERTICAL_CONSTRAINT_ANGLE * AxisMap[InputAxis::CameraHorizontal]);
if (abs(AxisMap[InputAxis::CameraVertical]) > EPSILON)
Camera.targetElevation = ANGLE(-10.0f + (THUMBCAM_HORIZONTAL_CONSTRAINT_ANGLE * AxisMap[InputAxis::CameraVertical])); Camera.targetElevation = ANGLE(-10.0f + (THUMBCAM_HORIZONTAL_CONSTRAINT_ANGLE * AxisMap[InputAxis::CameraVertical]));
} }
} }
@ -1563,7 +1566,7 @@ void LookLeftRight(ItemInfo* item)
} }
if (lara->Control.HandStatus != HandStatus::Busy && if (lara->Control.HandStatus != HandStatus::Busy &&
lara->Vehicle == NO_ITEM && lara->Context.Vehicle == NO_ITEM &&
!lara->LeftArm.Locked && !lara->LeftArm.Locked &&
!lara->RightArm.Locked) !lara->RightArm.Locked)
{ {
@ -1602,7 +1605,7 @@ void LookUpDown(ItemInfo* item)
} }
if (lara->Control.HandStatus != HandStatus::Busy && if (lara->Control.HandStatus != HandStatus::Busy &&
lara->Vehicle == NO_ITEM && lara->Context.Vehicle == NO_ITEM &&
!lara->LeftArm.Locked && !lara->LeftArm.Locked &&
!lara->RightArm.Locked) !lara->RightArm.Locked)
{ {
@ -1634,7 +1637,7 @@ void ResetLook(ItemInfo* item)
if (lara->Control.HandStatus != HandStatus::Busy && if (lara->Control.HandStatus != HandStatus::Busy &&
!lara->LeftArm.Locked && !lara->LeftArm.Locked &&
!lara->RightArm.Locked && !lara->RightArm.Locked &&
lara->Vehicle == NO_ITEM) lara->Context.Vehicle == NO_ITEM)
{ {
lara->ExtraTorsoRot = lara->ExtraHeadRot; lara->ExtraTorsoRot = lara->ExtraHeadRot;
} }
@ -2074,7 +2077,7 @@ void HandleOptics(ItemInfo* item)
AlterFOV(LastFOV); AlterFOV(LastFOV);
Lara.Inventory.IsBusy = false; Lara.Inventory.IsBusy = false;
ResetLaraFlex(LaraItem); ResetPlayerFlex(LaraItem);
TrInput &= ~IN_LOOK; TrInput &= ~IN_LOOK;
} }

View file

@ -82,7 +82,7 @@ extern float CinematicBarsSpeed;
void LookAt(CAMERA_INFO* cam, short roll); void LookAt(CAMERA_INFO* cam, short roll);
void AlterFOV(short value, bool store = true); void AlterFOV(short value, bool store = true);
short GetCurrentFOV(); short GetCurrentFOV();
void InitialiseCamera(); void InitializeCamera();
void MoveCamera(GameVector* ideal, int speed); void MoveCamera(GameVector* ideal, int speed);
void ChaseCamera(ItemInfo* item); void ChaseCamera(ItemInfo* item);
void UpdateCameraElevation(); void UpdateCameraElevation();

View file

@ -14,11 +14,11 @@
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/pickup/pickup.h" #include "Game/pickup/pickup.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "ScriptInterfaceGame.h" #include "ScriptInterfaceGame.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/setup.h"
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer; using namespace TEN::Renderer;
@ -51,6 +51,7 @@ void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionIn
if (item->ItemFlags[2] != 0) if (item->ItemFlags[2] != 0)
collidedBits &= ~1; collidedBits &= ~1;
coll->Setup.EnableObjectPush = item->ItemFlags[4] == 0;
while (collidedBits) while (collidedBits)
{ {

View file

@ -12,7 +12,7 @@
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Math; using namespace TEN::Math;
using namespace TEN::Renderer; using namespace TEN::Renderer;
@ -239,7 +239,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
int xFront, zFront, xRight, zRight, xLeft, zLeft; int xFront, zFront, xRight, zRight, xLeft, zLeft;
// Get nearest 90-degree snapped angle (quadrant). // Get nearest 90-degree snapped angle (quadrant).
auto quadrant = GetQuadrant(coll->Setup.ForwardAngle); int quadrant = GetQuadrant(coll->Setup.ForwardAngle);
// Get side probe offsets depending on quadrant. // Get side probe offsets depending on quadrant.
// If unconstrained mode is specified, don't use quadrant. // If unconstrained mode is specified, don't use quadrant.
@ -322,7 +322,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
// TEST 2: CENTERPOINT PROBE // TEST 2: CENTERPOINT PROBE
collResult = GetCollision(probePos.x, probePos.y, probePos.z, realRoomNumber); collResult = GetCollision(probePos.x, probePos.y, probePos.z, realRoomNumber);
auto topRoomNumber = collResult.RoomNumber; // Keep top room number as we need it to re-probe from origin room. int topRoomNumber = collResult.RoomNumber; // Keep top room number as we need it to re-probe from origin room.
if (doPlayerCollision) if (doPlayerCollision)
{ {
@ -394,7 +394,9 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
height = GetFloorHeight(tfLocation, probePos.x + xFront, probePos.z + zFront).value_or(NO_HEIGHT); height = GetFloorHeight(tfLocation, probePos.x + xFront, probePos.z + zFront).value_or(NO_HEIGHT);
} }
else else
{
height = GetCollision(probePos.x + xFront, probePos.y, probePos.z + zFront, topRoomNumber).Position.Floor; height = GetCollision(probePos.x + xFront, probePos.y, probePos.z + zFront, topRoomNumber).Position.Floor;
}
if (height != NO_HEIGHT) if (height != NO_HEIGHT)
height -= (doPlayerCollision ? entityPos.y : probePos.y); height -= (doPlayerCollision ? entityPos.y : probePos.y);
@ -725,7 +727,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
} }
} }
coll->CollisionType = ((coll->CollisionType == CT_TOP) ? CT_TOP_FRONT : CT_FRONT); coll->CollisionType = (coll->CollisionType == CT_TOP) ? CT_TOP_FRONT : CT_FRONT;
return; return;
} }
@ -773,12 +775,20 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
quarter %= 2; quarter %= 2;
if (coll->MiddleLeft.HasFlippedDiagonalSplit()) if (coll->MiddleLeft.HasFlippedDiagonalSplit())
if (quarter) coll->CollisionType = CT_LEFT; {
else if (quarter)
if (!quarter) coll->CollisionType = CT_LEFT; coll->CollisionType = CT_LEFT;
} }
else else
{
if (!quarter)
coll->CollisionType = CT_LEFT; coll->CollisionType = CT_LEFT;
}
}
else
{
coll->CollisionType = CT_LEFT;
}
return; return;
} }
@ -815,16 +825,24 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
if (coll->DiagonalStepAtRight()) if (coll->DiagonalStepAtRight())
{ {
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90.0f); // NOTE: Different from quadrant! int quarter = unsigned short(coll->Setup.ForwardAngle) / ANGLE(90.0f); // NOTE: Different from quadrant!
quarter %= 2; quarter %= 2;
if (coll->MiddleRight.HasFlippedDiagonalSplit()) if (coll->MiddleRight.HasFlippedDiagonalSplit())
if (quarter) coll->CollisionType = CT_RIGHT; {
else if (quarter)
if (!quarter) coll->CollisionType = CT_RIGHT; coll->CollisionType = CT_RIGHT;
} }
else else
{
if (!quarter)
coll->CollisionType = CT_RIGHT; coll->CollisionType = CT_RIGHT;
}
}
else
{
coll->CollisionType = CT_RIGHT;
}
return; return;
} }

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Math/Math.h" #include "Math/Math.h"
#include "Math/Math.h" #include "Objects/game_object_ids.h"
struct ItemInfo; struct ItemInfo;
struct CollisionInfo; struct CollisionInfo;
@ -87,10 +87,11 @@ struct CollisionSetup
bool EnableSpasm; // Convulse when pushed bool EnableSpasm; // Convulse when pushed
// Preserve old parameters to restore later // Preserve old parameters to restore later
Vector3i OldPosition; Vector3i OldPosition = Vector3i::Zero;
int OldAnimNumber; GAME_OBJECT_ID PrevAnimObjectID = ID_NO_OBJECT;
int OldFrameNumber; int OldAnimNumber = 0;
int OldState; int OldFrameNumber = 0;
int OldState = 0;
}; };
struct CollisionInfo struct CollisionInfo

View file

@ -3,11 +3,11 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Math; using namespace TEN::Math;
int FloorInfo::GetSurfacePlaneIndex(int x, int z, bool isFloor) const int FloorInfo::GetSurfacePlaneIndex(int x, int z, bool isFloor) const
@ -313,7 +313,7 @@ void FloorInfo::RemoveBridge(int itemNumber)
BridgeItemNumbers.erase(itemNumber); BridgeItemNumbers.erase(itemNumber);
} }
namespace TEN::Floordata namespace TEN::Collision::Floordata
{ {
Vector3 GetSurfaceNormal(const Vector2& tilt, bool isFloor) Vector3 GetSurfaceNormal(const Vector2& tilt, bool isFloor)
{ {
@ -756,28 +756,34 @@ namespace TEN::Floordata
auto floor = &GetFloorSide(item.RoomNumber, x, z); auto floor = &GetFloorSide(item.RoomNumber, x, z);
floor->AddBridge(itemNumber); floor->AddBridge(itemNumber);
const auto floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber); if (Objects[item.ObjectNumber].floorBorder != nullptr)
{
int floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber);
while (floorBorder <= floor->GetSurfaceHeight(x, z, false)) while (floorBorder <= floor->GetSurfaceHeight(x, z, false))
{ {
const auto roomAbove = floor->GetRoomNumberAbove(x, z); const auto roomAbove = floor->GetRoomNumberAbove(x, z);
if (!roomAbove) if (!roomAbove.has_value())
break; break;
floor = &GetFloorSide(*roomAbove, x, z); floor = &GetFloorSide(*roomAbove, x, z);
floor->AddBridge(itemNumber); floor->AddBridge(itemNumber);
} }
}
const auto ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber); if (Objects[item.ObjectNumber].ceilingBorder != nullptr)
{
int ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber);
while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true)) while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true))
{ {
const auto roomBelow = floor->GetRoomNumberBelow(x, z); const auto roomBelow = floor->GetRoomNumberBelow(x, z);
if (!roomBelow) if (!roomBelow.has_value())
break; break;
floor = &GetFloorSide(*roomBelow, x, z); floor = &GetFloorSide(*roomBelow, x, z);
floor->AddBridge(itemNumber); floor->AddBridge(itemNumber);
} }
} }
}
void RemoveBridge(int itemNumber, int x, int z) void RemoveBridge(int itemNumber, int x, int z)
{ {
@ -785,31 +791,37 @@ namespace TEN::Floordata
x += item.Pose.Position.x; x += item.Pose.Position.x;
z += item.Pose.Position.z; z += item.Pose.Position.z;
auto floor = &GetFloorSide(item.RoomNumber, x, z); auto* floor = &GetFloorSide(item.RoomNumber, x, z);
floor->RemoveBridge(itemNumber); floor->RemoveBridge(itemNumber);
const auto floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber); if (Objects[item.ObjectNumber].floorBorder != nullptr)
{
int floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber);
while (floorBorder <= floor->GetSurfaceHeight(x, z, false)) while (floorBorder <= floor->GetSurfaceHeight(x, z, false))
{ {
const auto roomAbove = floor->GetRoomNumberAbove(x, z); const auto roomAbove = floor->GetRoomNumberAbove(x, z);
if (!roomAbove) if (!roomAbove.has_value())
break; break;
floor = &GetFloorSide(*roomAbove, x, z); floor = &GetFloorSide(*roomAbove, x, z);
floor->RemoveBridge(itemNumber); floor->RemoveBridge(itemNumber);
} }
}
const auto ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber); if (Objects[item.ObjectNumber].ceilingBorder != nullptr)
{
int ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber);
while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true)) while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true))
{ {
const auto roomBelow = floor->GetRoomNumberBelow(x, z); const auto roomBelow = floor->GetRoomNumberBelow(x, z);
if (!roomBelow) if (!roomBelow.has_value())
break; break;
floor = &GetFloorSide(*roomBelow, x, z); floor = &GetFloorSide(*roomBelow, x, z);
floor->RemoveBridge(itemNumber); floor->RemoveBridge(itemNumber);
} }
} }
}
// New function which gets precise floor/ceiling collision from actual object bounding box. // New function which gets precise floor/ceiling collision from actual object bounding box.
// Animated objects are also supported, although horizontal collision shift is unstable. // Animated objects are also supported, although horizontal collision shift is unstable.
@ -824,17 +836,20 @@ namespace TEN::Floordata
auto bounds = GameBoundingBox(item); auto bounds = GameBoundingBox(item);
auto dxBounds = bounds.ToBoundingOrientedBox(item->Pose); auto dxBounds = bounds.ToBoundingOrientedBox(item->Pose);
Vector3 pos = Vector3(x, y + (bottom ? 4 : -4), z); // Introduce slight vertical margin just in case auto pos = Vector3(x, y + (bottom ? 4 : -4), z); // Introduce slight vertical margin just in case.
static float distance; float distance = 0.0f;
if (dxBounds.Intersects(pos, (bottom ? -Vector3::UnitY : Vector3::UnitY), distance)) if (dxBounds.Intersects(pos, (bottom ? -Vector3::UnitY : Vector3::UnitY), distance))
{
return std::optional{ item->Pose.Position.y + (bottom ? bounds.Y2 : bounds.Y1) }; return std::optional{ item->Pose.Position.y + (bottom ? bounds.Y2 : bounds.Y1) };
}
else else
{
return std::nullopt; return std::nullopt;
} }
}
// Gets bridge min or max height regardless of actual X/Z world position // Gets bridge min or max height regardless of actual X/Z world position.
int GetBridgeBorder(int itemNumber, bool bottom) int GetBridgeBorder(int itemNumber, bool bottom)
{ {
auto item = &g_Level.Items[itemNumber]; auto item = &g_Level.Items[itemNumber];
@ -847,6 +862,7 @@ namespace TEN::Floordata
void UpdateBridgeItem(int itemNumber, bool forceRemoval) void UpdateBridgeItem(int itemNumber, bool forceRemoval)
{ {
auto item = &g_Level.Items[itemNumber]; auto item = &g_Level.Items[itemNumber];
if (!Objects[item->ObjectNumber].loaded) return;
// Force removal if object was killed // Force removal if object was killed
if (item->Flags & IFLAG_KILLED) if (item->Flags & IFLAG_KILLED)
@ -873,10 +889,10 @@ namespace TEN::Floordata
{ {
for (int z = 0; z < room->zSize; z++) for (int z = 0; z < room->zSize; z++)
{ {
auto pX = room->x + (x * BLOCK(1)) + BLOCK(0.5f); float pX = room->x + (x * BLOCK(1)) + BLOCK(0.5f);
auto pZ = room->z + (z * BLOCK(1)) + BLOCK(0.5f); float pZ = room->z + (z * BLOCK(1)) + BLOCK(0.5f);
auto offX = pX - item->Pose.Position.x; float offX = pX - item->Pose.Position.x;
auto offZ = pZ - item->Pose.Position.z; float offZ = pZ - item->Pose.Position.z;
// Clean previous bridge state // Clean previous bridge state
RemoveBridge(itemNumber, offX, offZ); RemoveBridge(itemNumber, offX, offZ);
@ -897,4 +913,15 @@ namespace TEN::Floordata
} }
} }
} }
bool TestMaterial(MaterialType refMaterial, const std::vector<MaterialType>& materialList)
{
for (const auto& material : materialList)
{
if (material == refMaterial)
return true;
}
return false;
}
} }

View file

@ -9,12 +9,16 @@ using namespace TEN::Math;
// Ceiling: Upper surface of a collision block. // Ceiling: Upper surface of a collision block.
// Floor: Lower surface of a collision block. // Floor: Lower surface of a collision block.
// Floordata: Name of the engine's level geometry collision system consisting of rooms and their divisions within a grid. // Floordata: Name of the engine's level geometry collision system consisting of rooms and their divisions within a grid.
// Plane: One of two surface triangles. CHECK: If it's a Vector3, isn't that simply the surface normal? // Plane: One of two surface triangles.
// Portal: Link from one room to another allowing traversal. // Portal: Link from one room to another allowing traversal.
// Room number: Unique numeric index of a room. // Room number: Unique numeric index of a room.
// Surface: Floor or ceiling. // Surface: Floor or ceiling.
// Wall: Inferred from a floor or ceiling with max height. Note that true "walls" do not exist. // Wall: Inferred from a floor or ceiling with max height. Note that true "walls" do not exist.
// Planes are non-standard. Instead of 4 components storing a normal and a distance, floordata uses a Vector3:
// x and y store the 2D direction vector in the xz plane (not normalized).
// z stores the distance.
constexpr auto WALL_PLANE = Vector3(0, 0, -CLICK(127)); constexpr auto WALL_PLANE = Vector3(0, 0, -CLICK(127));
enum class MaterialType enum class MaterialType
@ -154,7 +158,7 @@ class FloorInfo
void RemoveBridge(int itemNumber); void RemoveBridge(int itemNumber);
}; };
namespace TEN::Floordata namespace TEN::Collision::Floordata
{ {
// TODO: Use normals natively. // TODO: Use normals natively.
Vector3 GetSurfaceNormal(const Vector2& tilt, bool isFloor); Vector3 GetSurfaceNormal(const Vector2& tilt, bool isFloor);
@ -183,4 +187,6 @@ namespace TEN::Floordata
std::optional<int> GetBridgeItemIntersect(int itemNumber, int x, int y, int z, bool bottom); std::optional<int> GetBridgeItemIntersect(int itemNumber, int x, int y, int z, bool bottom);
int GetBridgeBorder(int itemNumber, bool bottom); int GetBridgeBorder(int itemNumber, bool bottom);
void UpdateBridgeItem(int itemNumber, bool forceRemoval = false); void UpdateBridgeItem(int itemNumber, bool forceRemoval = false);
bool TestMaterial(MaterialType refMaterial, const std::vector<MaterialType>& materialList);
} }

View file

@ -3,8 +3,8 @@
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Setup.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"

View file

@ -7,6 +7,7 @@
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/control/lot.h" #include "Game/control/lot.h"
#include "Game/effects/smoke.h"
#include "Game/effects/tomb4fx.h" #include "Game/effects/tomb4fx.h"
#include "Game/itemdata/creature_info.h" #include "Game/itemdata/creature_info.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
@ -15,12 +16,14 @@
#include "Game/misc.h" #include "Game/misc.h"
#include "Game/pickup/pickup.h" #include "Game/pickup/pickup.h"
#include "Game/room.h" #include "Game/room.h"
#include "Specific/setup.h" #include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Objects/TR5/Object/tr5_pushableblock.h" #include "Objects/TR5/Object/tr5_pushableblock.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
using namespace TEN::Effects::Smoke;
constexpr auto ESCAPE_DIST = SECTOR(5); constexpr auto ESCAPE_DIST = SECTOR(5);
constexpr auto STALK_DIST = SECTOR(3); constexpr auto STALK_DIST = SECTOR(3);
constexpr auto REACHED_GOAL_RADIUS = 640; constexpr auto REACHED_GOAL_RADIUS = 640;
@ -33,6 +36,8 @@ constexpr auto FEELER_ANGLE = ANGLE(45.0f);
constexpr auto CREATURE_AI_ROTATION_MAX = ANGLE(90.0f); constexpr auto CREATURE_AI_ROTATION_MAX = ANGLE(90.0f);
constexpr auto CREATURE_JOINT_ROTATION_MAX = ANGLE(70.0f); constexpr auto CREATURE_JOINT_ROTATION_MAX = ANGLE(70.0f);
constexpr auto CREATURE_GUN_EFFECT_VERTICAL_OFFSET = 75;
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION #ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
constexpr int HIGH_PRIO_RANGE = 8; constexpr int HIGH_PRIO_RANGE = 8;
constexpr int MEDIUM_PRIO_RANGE = HIGH_PRIO_RANGE + HIGH_PRIO_RANGE * (HIGH_PRIO_RANGE / 6.0f); constexpr int MEDIUM_PRIO_RANGE = HIGH_PRIO_RANGE + HIGH_PRIO_RANGE * (HIGH_PRIO_RANGE / 6.0f);
@ -548,57 +553,44 @@ bool CreaturePathfind(ItemInfo* item, Vector3i prevPos, short angle, short tilt)
return true; return true;
} }
void CreatureKill(ItemInfo* item, int entityKillAnim, int laraExtraKillAnim, int entityKillState, int laraKillState) void CreatureKill(ItemInfo* creatureItem, int creatureAnimNumber, int playerAnimNumber, int creatureState, int playerState)
{ {
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + entityKillAnim; auto& playerItem = *LaraItem;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; auto& player = GetLaraInfo(playerItem);
item->Animation.ActiveState = entityKillState;
LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + laraExtraKillAnim; SetAnimation(*creatureItem, creatureAnimNumber);
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; SetAnimation(playerItem, ID_LARA_EXTRA_ANIMS, playerAnimNumber);
LaraItem->Animation.ActiveState = 0;
LaraItem->Animation.TargetState = laraKillState;
LaraItem->Pose = item->Pose; playerItem.Pose = creatureItem->Pose;
LaraItem->Animation.IsAirborne = false; playerItem.Animation.IsAirborne = false;
LaraItem->Animation.Velocity.z = 0; playerItem.Animation.Velocity = Vector3::Zero;
LaraItem->Animation.Velocity.y = 0;
if (item->RoomNumber != LaraItem->RoomNumber) if (creatureItem->RoomNumber != playerItem.RoomNumber)
ItemNewRoom(Lara.ItemNumber, item->RoomNumber); ItemNewRoom(player.ItemNumber, creatureItem->RoomNumber);
AnimateItem(LaraItem); AnimateItem(&playerItem);
Lara.ExtraAnim = 1; player.ExtraAnim = 1;
Lara.Control.HandStatus = HandStatus::Busy; player.Control.HandStatus = HandStatus::Busy;
Lara.Control.Weapon.GunType = LaraWeaponType::None; player.Control.Weapon.GunType = LaraWeaponType::None;
Lara.HitDirection = -1; player.HitDirection = -1;
Camera.pos.RoomNumber = LaraItem->RoomNumber; Camera.pos.RoomNumber = playerItem.RoomNumber;
Camera.type = CameraType::Chase; Camera.type = CameraType::Chase;
Camera.flags = CF_FOLLOW_CENTER; Camera.flags = CF_FOLLOW_CENTER;
Camera.targetAngle = ANGLE(170.0f); Camera.targetAngle = ANGLE(170.0f);
Camera.targetElevation = -ANGLE(25.0f); Camera.targetElevation = -ANGLE(25.0f);
// TODO: exist in TR5 but just commented in case.
/*
ForcedFixedCamera.x = item->pos.Position.x + (phd_sin(item->pos.Orientation.y) << 13) >> W2V_SHIFT;
ForcedFixedCamera.y = item->pos.Position.y - WALL_SIZE;
ForcedFixedCamera.z = item->pos.Position.z + (phd_cos(item->pos.Orientation.y) << 13) >> W2V_SHIFT;
ForcedFixedCamera.roomNumber = item->roomNumber;
UseForcedFixedCamera = true;
*/
} }
short CreatureEffect2(ItemInfo* item, BiteInfo bite, short velocity, short angle, std::function<CreatureEffectFunction> func) short CreatureEffect2(ItemInfo* item, const CreatureBiteInfo& bite, short velocity, short angle, std::function<CreatureEffectFunction> func)
{ {
auto pos = GetJointPosition(item, bite.meshNum, Vector3i(bite.Position)); auto pos = GetJointPosition(item, bite);
return func(pos.x, pos.y, pos.z, velocity, angle, item->RoomNumber); return func(pos.x, pos.y, pos.z, velocity, angle, item->RoomNumber);
} }
short CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func) short CreatureEffect(ItemInfo* item, const CreatureBiteInfo& bite, std::function<CreatureEffectFunction> func)
{ {
auto pos = GetJointPosition(item, bite.meshNum, Vector3i(bite.Position)); auto pos = GetJointPosition(item, bite);
return func(pos.x, pos.y, pos.z, item->Animation.Velocity.z, item->Pose.Orientation.y, item->RoomNumber); return func(pos.x, pos.y, pos.z, item->Animation.Velocity.z, item->Pose.Orientation.y, item->RoomNumber);
} }
@ -660,7 +652,7 @@ void CreatureFloat(short itemNumber)
if (item->Pose.Position.y <= waterLevel) if (item->Pose.Position.y <= waterLevel)
{ {
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) if (item->Animation.FrameNumber == GetAnimData(*item).frameBase)
{ {
item->Pose.Position.y = waterLevel; item->Pose.Position.y = waterLevel;
item->Collidable = false; item->Collidable = false;
@ -735,26 +727,47 @@ short CreatureTurn(ItemInfo* item, short maxTurn)
return angle; return angle;
} }
bool CreatureAnimation(short itemNumber, short angle, short tilt) static void SpawnCreatureGunEffect(const ItemInfo& item, const CreatureMuzzleFlashInfo& muzzleFlash)
{ {
auto* item = &g_Level.Items[itemNumber]; if (muzzleFlash.Delay == 0)
return;
if (!item->IsCreature()) auto muzzlePos = muzzleFlash.Bite;
auto pos = GetJointPosition(item, muzzlePos);
TriggerDynamicLight(pos.x, pos.y, pos.z, 15, 128, 64, 16);
if (muzzleFlash.UseSmoke)
{
muzzlePos.Position.y -= CREATURE_GUN_EFFECT_VERTICAL_OFFSET;
auto smokePos = GetJointPosition(item, muzzlePos);
SpawnGunSmokeParticles(smokePos.ToVector3(), Vector3::Zero, item.RoomNumber, 1, LaraWeaponType::Pistol, 12);
}
}
bool CreatureAnimation(short itemNumber, short headingAngle, short tiltAngle)
{
auto& item = g_Level.Items[itemNumber];
if (!item.IsCreature())
return false; return false;
auto prevPos = item->Pose.Position; auto& creature = *GetCreatureInfo(&item);
AnimateItem(item); SpawnCreatureGunEffect(item, creature.MuzzleFlash[0]);
ProcessSectorFlags(item); SpawnCreatureGunEffect(item, creature.MuzzleFlash[1]);
CreatureHealth(item);
if (item->Status == ITEM_DEACTIVATED) auto prevPos = item.Pose.Position;
AnimateItem(&item);
ProcessSectorFlags(&item);
CreatureHealth(&item);
if (item.Status == ITEM_DEACTIVATED)
{ {
CreatureDie(itemNumber, false); CreatureDie(itemNumber, false);
return false; return false;
} }
return CreaturePathfind(item, prevPos, angle, tilt); return CreaturePathfind(&item, prevPos, headingAngle, tiltAngle);
} }
void CreatureHealth(ItemInfo* item) void CreatureHealth(ItemInfo* item)
@ -1088,7 +1101,7 @@ bool CreatureActive(short itemNumber)
return true; return true;
} }
void InitialiseCreature(short itemNumber) void InitializeCreature(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
@ -2097,7 +2110,7 @@ void AdjustStopperFlag(ItemInfo* item, int direction)
floor->Stopper = !floor->Stopper; floor->Stopper = !floor->Stopper;
} }
void InitialiseItemBoxData() void InitializeItemBoxData()
{ {
for (int i = 0; i < g_Level.Items.size(); i++) for (int i = 0; i < g_Level.Items.size(); i++)
{ {

View file

@ -2,7 +2,7 @@
#include "Specific/level.h" #include "Specific/level.h"
#include "Math/Math.h" #include "Math/Math.h"
struct BiteInfo; struct CreatureBiteInfo;
struct CreatureInfo; struct CreatureInfo;
struct ItemInfo; struct ItemInfo;
struct LOTInfo; struct LOTInfo;
@ -20,55 +20,6 @@ enum TARGET_TYPE
SECONDARY_TARGET SECONDARY_TARGET
}; };
struct OBJECT_BONES
{
short bone0;
short bone1;
short bone2;
short bone3;
OBJECT_BONES()
{
this->bone0 = 0;
this->bone1 = 0;
this->bone2 = 0;
this->bone3 = 0;
}
OBJECT_BONES(short all)
{
this->bone0 = all;
this->bone1 = all;
this->bone2 = all;
this->bone3 = all;
}
OBJECT_BONES(short angleY, short angleX)
{
this->bone0 = angleY;
this->bone1 = angleX;
this->bone2 = angleY;
this->bone3 = angleX;
}
OBJECT_BONES(short angleY, short angleX, bool total)
{
this->bone0 = angleY;
this->bone1 = angleX;
if (total)
{
this->bone2 = angleY;
this->bone3 = angleX;
}
else
{
this->bone2 = 0;
this->bone3 = 0;
}
}
};
struct AI_INFO struct AI_INFO
{ {
int zoneNumber; int zoneNumber;
@ -99,30 +50,6 @@ struct OVERLAP
int flags; int flags;
}; };
struct BiteInfo
{
Vector3 Position = Vector3::Zero;
int meshNum = 0;
BiteInfo()
{
this->Position = Vector3::Zero;
this->meshNum = 0;
}
BiteInfo(const Vector3& pos, int meshNumber)
{
this->Position = pos;
this->meshNum = meshNumber;
}
BiteInfo(float xPos, float yPos, float zPos, int meshNumber)
{
this->Position = Vector3(xPos, yPos, zPos);
this->meshNum = meshNumber;
}
};
#define CreatureEffectFunction short(int x, int y, int z, short speed, short yRot, short roomNumber) #define CreatureEffectFunction short(int x, int y, int z, short speed, short yRot, short roomNumber)
constexpr auto BOX_BLOCKED = (1 << 14); // unpassable for other enemies, always set for movable blocks & closed doors constexpr auto BOX_BLOCKED = (1 << 14); // unpassable for other enemies, always set for movable blocks & closed doors
@ -164,8 +91,8 @@ short AIGuard(CreatureInfo* creature);
void AlertNearbyGuards(ItemInfo* item); void AlertNearbyGuards(ItemInfo* item);
void AlertAllGuards(short itemNumber); void AlertAllGuards(short itemNumber);
void CreatureKill(ItemInfo* item, int entityKillAnim, int laraExtraKillAnim, int entityKillState, int laraKillState); void CreatureKill(ItemInfo* item, int entityKillAnim, int laraExtraKillAnim, int entityKillState, int laraKillState);
short CreatureEffect2(ItemInfo* item, BiteInfo bite, short velocity, short angle, std::function<CreatureEffectFunction> func); short CreatureEffect2(ItemInfo* item, const CreatureBiteInfo& bite, short velocity, short angle, std::function<CreatureEffectFunction> func);
short CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func); short CreatureEffect(ItemInfo* item, const CreatureBiteInfo& bite, std::function<CreatureEffectFunction> func);
void CreatureUnderwater(ItemInfo* item, int depth); void CreatureUnderwater(ItemInfo* item, int depth);
void CreatureFloat(short itemNumber); void CreatureFloat(short itemNumber);
void CreatureJoint(ItemInfo* item, short joint, short required, short maxAngle = ANGLE(70.0f)); void CreatureJoint(ItemInfo* item, short joint, short required, short maxAngle = ANGLE(70.0f));
@ -180,14 +107,14 @@ void TargetBox(LOTInfo* LOT, int boxNumber);
bool UpdateLOT(LOTInfo* LOT, int expansion); bool UpdateLOT(LOTInfo* LOT, int expansion);
bool SearchLOT(LOTInfo* LOT, int expansion); bool SearchLOT(LOTInfo* LOT, int expansion);
bool CreatureActive(short itemNumber); bool CreatureActive(short itemNumber);
void InitialiseCreature(short itemNumber); void InitializeCreature(short itemNumber);
bool StalkBox(ItemInfo* item, ItemInfo* enemy, int boxNumber); bool StalkBox(ItemInfo* item, ItemInfo* enemy, int boxNumber);
void CreatureAIInfo(ItemInfo* item, AI_INFO* AI); void CreatureAIInfo(ItemInfo* item, AI_INFO* AI);
TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT); TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT);
bool CreatureAnimation(short itemNumber, short angle, short tilt); bool CreatureAnimation(short itemNumber, short headingAngle, short tiltAngle);
void CreatureHealth(ItemInfo* item); void CreatureHealth(ItemInfo* item);
void AdjustStopperFlag(ItemInfo* item, int direction); void AdjustStopperFlag(ItemInfo* item, int direction);
void InitialiseItemBoxData(); void InitializeItemBoxData();
bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType); bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType);

View file

@ -17,7 +17,7 @@
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/effects/Electricity.h" #include "Game/effects/Electricity.h"
#include "Game/effects/explosion.h" #include "Game/effects/explosion.h"
#include "Game/effects/footprint.h" #include "Game/effects/Footprint.h"
#include "Game/effects/Hair.h" #include "Game/effects/Hair.h"
#include "Game/effects/Ripple.h" #include "Game/effects/Ripple.h"
#include "Game/effects/simple_particle.h" #include "Game/effects/simple_particle.h"
@ -36,6 +36,7 @@
#include "Game/pickup/pickup.h" #include "Game/pickup/pickup.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h"
#include "Game/spotcam.h" #include "Game/spotcam.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/Effects/tr4_locusts.h" #include "Objects/Effects/tr4_locusts.h"
@ -54,7 +55,6 @@
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Specific/winmain.h" #include "Specific/winmain.h"
using namespace std::chrono; using namespace std::chrono;
@ -65,7 +65,7 @@ using namespace TEN::Effects::Drip;
using namespace TEN::Effects::Electricity; using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Explosion; using namespace TEN::Effects::Explosion;
using namespace TEN::Effects::Footprints; using namespace TEN::Effects::Footprint;
using namespace TEN::Effects::Hair; using namespace TEN::Effects::Hair;
using namespace TEN::Effects::Ripple; using namespace TEN::Effects::Ripple;
using namespace TEN::Effects::Smoke; using namespace TEN::Effects::Smoke;
@ -74,7 +74,7 @@ using namespace TEN::Effects::Streamer;
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Entities::Switches; using namespace TEN::Entities::Switches;
using namespace TEN::Entities::TR4; using namespace TEN::Entities::TR4;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Hud; using namespace TEN::Hud;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
@ -84,7 +84,7 @@ int GameTimer = 0;
int GlobalCounter = 0; int GlobalCounter = 0;
int Wibble = 0; int Wibble = 0;
bool InitialiseGame; bool InitializeGame;
bool DoTheGame; bool DoTheGame;
bool JustLoaded; bool JustLoaded;
bool ThreadEnded; bool ThreadEnded;
@ -124,7 +124,6 @@ GameStatus ControlPhase(int numFrames)
{ {
auto time1 = std::chrono::high_resolution_clock::now(); auto time1 = std::chrono::high_resolution_clock::now();
auto* level = g_GameFlow->GetLevel(CurrentLevel);
bool isTitle = (CurrentLevel == 0); bool isTitle = (CurrentLevel == 0);
RegeneratePickups(); RegeneratePickups();
@ -271,7 +270,6 @@ unsigned CALLBACK GameMain(void *)
else else
g_Renderer.RenderTitleImage(); g_Renderer.RenderTitleImage();
// Execute the Lua gameflow and play the game. // Execute the Lua gameflow and play the game.
g_GameFlow->DoFlow(); g_GameFlow->DoFlow();
@ -296,17 +294,17 @@ GameStatus DoLevel(int levelIndex, bool loadGame)
// Initialize items, effects, lots, and cameras. // Initialize items, effects, lots, and cameras.
HairEffect.Initialize(); HairEffect.Initialize();
InitialiseFXArray(true); InitializeFXArray(true);
InitialiseCamera(); InitializeCamera();
InitialiseSpotCamSequences(isTitle); InitializeSpotCamSequences(isTitle);
InitialiseItemBoxData(); InitializeItemBoxData();
// Initialize scripting. // Initialize scripting.
InitialiseScripting(levelIndex, loadGame); InitializeScripting(levelIndex, loadGame);
InitialiseNodeScripts(); InitializeNodeScripts();
// Initialize game variables and optionally load game. // Initialize game variables and optionally load game.
InitialiseOrLoadGame(loadGame); InitializeOrLoadGame(loadGame);
// Prepare title menu, if necessary. // Prepare title menu, if necessary.
if (isTitle) if (isTitle)
@ -416,6 +414,7 @@ void CleanUp()
StreamerEffect.Clear(); StreamerEffect.Clear();
ClearUnderwaterBloodParticles(); ClearUnderwaterBloodParticles();
ClearBubbles(); ClearBubbles();
ClearFootprints();
ClearDrips(); ClearDrips();
ClearRipples(); ClearRipples();
DisableSmokeParticles(); DisableSmokeParticles();
@ -438,7 +437,7 @@ void CleanUp()
ClearObjCamera(); ClearObjCamera();
} }
void InitialiseScripting(int levelIndex, bool loadGame) void InitializeScripting(int levelIndex, bool loadGame)
{ {
TENLog("Loading level script...", LogLevel::Info); TENLog("Loading level script...", LogLevel::Info);
@ -465,9 +464,9 @@ void InitialiseScripting(int levelIndex, bool loadGame)
PlaySoundTrack(level->GetAmbientTrack(), SoundTrackType::BGM); PlaySoundTrack(level->GetAmbientTrack(), SoundTrackType::BGM);
} }
void DeInitialiseScripting(int levelIndex) void DeInitializeScripting(int levelIndex, GameStatus reason)
{ {
g_GameScript->OnEnd(); g_GameScript->OnEnd(reason);
g_GameScript->FreeLevelScripts(); g_GameScript->FreeLevelScripts();
g_GameScriptEntities->FreeEntities(); g_GameScriptEntities->FreeEntities();
@ -475,7 +474,7 @@ void DeInitialiseScripting(int levelIndex)
g_GameScript->ResetScripts(true); g_GameScript->ResetScripts(true);
} }
void InitialiseOrLoadGame(bool loadGame) void InitializeOrLoadGame(bool loadGame)
{ {
RequiredStartPos = false; RequiredStartPos = false;
@ -495,7 +494,7 @@ void InitialiseOrLoadGame(bool loadGame)
Camera.target.y = LaraItem->Pose.Position.y; Camera.target.y = LaraItem->Pose.Position.y;
Camera.target.z = LaraItem->Pose.Position.z; Camera.target.z = LaraItem->Pose.Position.z;
InitialiseGame = false; InitializeGame = false;
g_GameFlow->SelectedSaveGame = 0; g_GameFlow->SelectedSaveGame = 0;
g_GameScript->OnLoad(); g_GameScript->OnLoad();
@ -505,12 +504,12 @@ void InitialiseOrLoadGame(bool loadGame)
// If not loading a savegame, clear all info. // If not loading a savegame, clear all info.
Statistics.Level = {}; Statistics.Level = {};
if (InitialiseGame) if (InitializeGame)
{ {
// Clear all game info as well. // Clear all game info as well.
Statistics.Game = {}; Statistics.Game = {};
GameTimer = 0; GameTimer = 0;
InitialiseGame = false; InitializeGame = false;
TENLog("Starting new game.", LogLevel::Info); TENLog("Starting new game.", LogLevel::Info);
} }
@ -563,6 +562,7 @@ GameStatus DoGameLoop(int levelIndex)
else else
{ {
if (result == GameStatus::ExitToTitle || if (result == GameStatus::ExitToTitle ||
result == GameStatus::LaraDead ||
result == GameStatus::LoadGame || result == GameStatus::LoadGame ||
result == GameStatus::LevelComplete) result == GameStatus::LevelComplete)
{ {
@ -574,13 +574,13 @@ GameStatus DoGameLoop(int levelIndex)
Sound_UpdateScene(); Sound_UpdateScene();
} }
EndGameLoop(levelIndex); EndGameLoop(levelIndex, result);
return result; return result;
} }
void EndGameLoop(int levelIndex) void EndGameLoop(int levelIndex, GameStatus reason)
{ {
DeInitialiseScripting(levelIndex); DeInitializeScripting(levelIndex, reason);
StopAllSounds(); StopAllSounds();
StopSoundTracks(); StopSoundTracks();
@ -678,7 +678,7 @@ GameStatus HandleGlobalInputEvents(bool isTitle)
if (Lara.Control.Count.Death > DEATH_NO_INPUT_TIMEOUT || if (Lara.Control.Count.Death > DEATH_NO_INPUT_TIMEOUT ||
Lara.Control.Count.Death > DEATH_INPUT_TIMEOUT && !NoAction()) Lara.Control.Count.Death > DEATH_INPUT_TIMEOUT && !NoAction())
{ {
return GameStatus::ExitToTitle; // Maybe do game over menu like some PSX versions have?? return GameStatus::LaraDead; // Maybe do game over menu like some PSX versions have??
} }
// Has level been completed? // Has level been completed?

View file

@ -50,7 +50,7 @@ extern int RumbleTimer;
extern int GlobalCounter; extern int GlobalCounter;
extern int Wibble; extern int Wibble;
extern bool InitialiseGame; extern bool InitializeGame;
extern bool DoTheGame; extern bool DoTheGame;
extern bool JustLoaded; extern bool JustLoaded;
extern bool ThreadEnded; extern bool ThreadEnded;
@ -77,7 +77,7 @@ int DrawPhase(bool isTitle);
GameStatus ControlPhase(int numFrames); GameStatus ControlPhase(int numFrames);
GameStatus DoLevel(int levelIndex, bool loadGame = false); GameStatus DoLevel(int levelIndex, bool loadGame = false);
GameStatus DoGameLoop(int levelIndex); GameStatus DoGameLoop(int levelIndex);
void EndGameLoop(int levelIndex); void EndGameLoop(int levelIndex, GameStatus reason);
GameStatus HandleMenuCalls(bool isTitle); GameStatus HandleMenuCalls(bool isTitle);
GameStatus HandleGlobalInputEvents(bool isTitle); GameStatus HandleGlobalInputEvents(bool isTitle);
@ -92,8 +92,8 @@ void UpdateShatters();
void CleanUp(); void CleanUp();
void InitialiseOrLoadGame(bool loadGame); void InitializeOrLoadGame(bool loadGame);
void InitialiseScripting(int levelIndex, bool loadGame); void InitializeScripting(int levelIndex, bool loadGame);
void DeInitialiseScripting(int levelIndex); void DeInitializeScripting(int levelIndex);
unsigned CALLBACK GameMain(void*); unsigned CALLBACK GameMain(void*);

View file

@ -7,16 +7,16 @@
#include "Game/effects/Hair.h" #include "Game/effects/Hair.h"
#include "Game/effects/tomb4fx.h" #include "Game/effects/tomb4fx.h"
#include "Game/effects/weather.h" #include "Game/effects/weather.h"
#include "Game/effects/footprint.h" #include "Game/effects/Footprint.h"
#include "Game/effects/debris.h" #include "Game/effects/debris.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_fire.h" #include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/pickup/pickup.h" #include "Game/pickup/pickup.h"
#include "Game/Setup.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Objects/Generic/puzzles_keys.h" #include "Objects/Generic/puzzles_keys.h"
#include "Objects/TR4/Entity/tr4_beetle_swarm.h" #include "Objects/TR4/Entity/tr4_beetle_swarm.h"
#include "Objects/TR5/Emitter/tr5_spider_emitter.h" #include "Objects/TR5/Emitter/tr5_spider_emitter.h"
@ -25,7 +25,7 @@
#include "Objects/Effects/tr4_locusts.h" #include "Objects/Effects/tr4_locusts.h"
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Footprints; using namespace TEN::Effects::Footprint;
using namespace TEN::Effects::Hair; using namespace TEN::Effects::Hair;
int FlipEffect; int FlipEffect;
@ -121,12 +121,12 @@ void Puzzle(ItemInfo* item)
void AddLeftFootprint(ItemInfo* item) void AddLeftFootprint(ItemInfo* item)
{ {
AddFootprint(item, false); SpawnFootprint(*item, false);
} }
void AddRightFootprint(ItemInfo* item) void AddRightFootprint(ItemInfo* item)
{ {
AddFootprint(item, true); SpawnFootprint(*item, true);
} }
void ResetHair(ItemInfo* item) void ResetHair(ItemInfo* item)
@ -155,7 +155,7 @@ void DrawLeftPistol(ItemInfo* item)
if (item->Model.MeshIndex[LM_LHAND] == item->Model.BaseMesh + LM_LHAND) if (item->Model.MeshIndex[LM_LHAND] == item->Model.BaseMesh + LM_LHAND)
{ {
item->Model.MeshIndex[LM_LHAND] = Objects[GetWeaponObjectMeshID(item, LaraWeaponType::Pistol)].meshIndex + LM_LHAND; item->Model.MeshIndex[LM_LHAND] = Objects[GetWeaponObjectMeshID(*item, LaraWeaponType::Pistol)].meshIndex + LM_LHAND;
lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty; lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty;
} }
else else
@ -171,7 +171,7 @@ void DrawRightPistol(ItemInfo* item)
if (item->Model.MeshIndex[LM_RHAND] == item->Model.BaseMesh + LM_RHAND) if (item->Model.MeshIndex[LM_RHAND] == item->Model.BaseMesh + LM_RHAND)
{ {
item->Model.MeshIndex[LM_RHAND] = Objects[GetWeaponObjectMeshID(item, LaraWeaponType::Pistol)].meshIndex + LM_RHAND; item->Model.MeshIndex[LM_RHAND] = Objects[GetWeaponObjectMeshID(*item, LaraWeaponType::Pistol)].meshIndex + LM_RHAND;
lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty; lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty;
} }
else else

View file

@ -8,13 +8,13 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara_fire.h" #include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_one_gun.h" #include "Game/Lara/lara_one_gun.h"
#include "Game/Setup.h"
#include "Objects/Generic/Object/objects.h" #include "Objects/Generic/Object/objects.h"
#include "Objects/Generic/Switches/switch.h" #include "Objects/Generic/Switches/switch.h"
#include "Objects/ScriptInterfaceObjectsHandler.h" #include "Objects/ScriptInterfaceObjectsHandler.h"
#include "ScriptInterfaceGame.h" #include "ScriptInterfaceGame.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/setup.h"
int NumberLosRooms; int NumberLosRooms;
short LosRooms[20]; short LosRooms[20];
@ -252,7 +252,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo
else else
{ {
if (LaserSight && isFiring) if (LaserSight && isFiring)
FireCrossBowFromLaserSight(LaraItem, origin, &target2); FireCrossBowFromLaserSight(*LaraItem, origin, &target2);
} }
} }
@ -263,7 +263,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo
if (Lara.Control.Weapon.GunType == LaraWeaponType::Crossbow) if (Lara.Control.Weapon.GunType == LaraWeaponType::Crossbow)
{ {
if (isFiring && LaserSight) if (isFiring && LaserSight)
FireCrossBowFromLaserSight(LaraItem, origin, &target2); FireCrossBowFromLaserSight(*LaraItem, origin, &target2);
} }
else else
{ {

View file

@ -7,8 +7,8 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/misc.h" #include "Game/misc.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Setup.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#define DEFAULT_FLY_UPDOWN_SPEED 16 #define DEFAULT_FLY_UPDOWN_SPEED 16
#define DEFAULT_SWIM_UPDOWN_SPEED 32 #define DEFAULT_SWIM_UPDOWN_SPEED 32
@ -16,15 +16,15 @@
int SlotsUsed; int SlotsUsed;
std::vector<CreatureInfo*> ActiveCreatures; std::vector<CreatureInfo*> ActiveCreatures;
void InitialiseLOTarray(int itemNumber) void InitializeLOTarray(int itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
auto* creature = GetCreatureInfo(item); auto* creature = GetCreatureInfo(item);
if (!creature->LOT.Initialised) if (!creature->LOT.Initialized)
{ {
creature->LOT.Node = std::vector<BoxNode>(g_Level.Boxes.size(), BoxNode{}); creature->LOT.Node = std::vector<BoxNode>(g_Level.Boxes.size(), BoxNode{});
creature->LOT.Initialised = true; creature->LOT.Initialized = true;
} }
} }
@ -35,7 +35,7 @@ bool EnableEntityAI(short itemNum, bool always, bool makeTarget)
if (item->IsCreature()) if (item->IsCreature())
return true; return true;
InitialiseSlot(itemNum, makeTarget); InitializeSlot(itemNum, makeTarget);
ActiveCreatures.push_back(item->Data); ActiveCreatures.push_back(item->Data);
return item->IsCreature(); return item->IsCreature();
@ -55,14 +55,14 @@ void DisableEntityAI(short itemNumber)
item->Data = nullptr; item->Data = nullptr;
} }
void InitialiseSlot(short itemNumber, bool makeTarget) void InitializeSlot(short itemNumber, bool makeTarget)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
auto* object = &Objects[item->ObjectNumber]; auto* object = &Objects[item->ObjectNumber];
item->Data = CreatureInfo(); item->Data = CreatureInfo();
auto* creature = GetCreatureInfo(item); auto* creature = GetCreatureInfo(item);
InitialiseLOTarray(itemNumber); InitializeLOTarray(itemNumber);
creature->ItemNumber = itemNumber; creature->ItemNumber = itemNumber;
creature->Mood = MoodType::Bored; creature->Mood = MoodType::Bored;
creature->JointRotation[0] = 0; creature->JointRotation[0] = 0;
@ -146,6 +146,12 @@ void InitialiseSlot(short itemNumber, bool makeTarget)
break; break;
case LotType::SnowmobileGun:
creature->LOT.Step = CLICK(1);
creature->LOT.Drop = -BLOCK(1);
creature->LOT.Zone = ZoneType::Human;
break;
// Can climb. // Can climb.
case LotType::Human: case LotType::Human:
creature->LOT.Step = BLOCK(1); creature->LOT.Step = BLOCK(1);

View file

@ -3,9 +3,9 @@
extern std::vector<CreatureInfo*> ActiveCreatures; extern std::vector<CreatureInfo*> ActiveCreatures;
void InitialiseLOTarray(int allocMem); void InitializeLOTarray(int allocMem);
bool EnableEntityAI(short itemNum, bool always, bool makeTarget = true); bool EnableEntityAI(short itemNum, bool always, bool makeTarget = true);
void InitialiseSlot(short itemNum, bool makeTarget); void InitializeSlot(short itemNum, bool makeTarget);
void SetEntityTarget(short itemNum, short target); void SetEntityTarget(short itemNum, short target);
void DisableEntityAI(short itemNumber); void DisableEntityAI(short itemNumber);
void ClearLOT(LOTInfo* LOT); void ClearLOT(LOTInfo* LOT);

View file

@ -15,13 +15,14 @@
#include "Game/Lara/lara_tests.h" #include "Game/Lara/lara_tests.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h"
#include "Game/spotcam.h" #include "Game/spotcam.h"
#include "Objects/Generic/Switches/generic_switch.h" #include "Objects/Generic/Switches/generic_switch.h"
#include "Objects/Generic/puzzles_keys.h"
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Objects/TR3/Vehicles/kayak.h" #include "Objects/TR3/Vehicles/kayak.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Items; using namespace TEN::Effects::Items;
using namespace TEN::Entities::Switches; using namespace TEN::Entities::Switches;
@ -82,7 +83,8 @@ bool GetKeyTrigger(ItemInfo* item)
return true; return true;
} }
int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch) // NOTE: attatchedToSwitch parameter unused.
int GetSwitchTrigger(ItemInfo* item, short* itemNumbersPtr, int attatchedToSwitch)
{ {
auto triggerIndex = GetTriggerIndex(item); auto triggerIndex = GetTriggerIndex(item);
@ -95,14 +97,14 @@ int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch)
return 0; return 0;
trigger += 2; trigger += 2;
short* current = itemNos; short* currentPtr = itemNumbersPtr;
int k = 0; int k = 0;
do do
{ {
if (TRIG_BITS(*trigger) == TO_OBJECT && item != &g_Level.Items[*trigger & VALUE_BITS]) if (TRIG_BITS(*trigger) == TO_OBJECT && item != &g_Level.Items[*trigger & VALUE_BITS])
{ {
current[k] = *trigger & VALUE_BITS; currentPtr[k] = *trigger & VALUE_BITS;
++k; ++k;
} }
@ -121,10 +123,67 @@ int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch)
int SwitchTrigger(short itemNumber, short timer) int SwitchTrigger(short itemNumber, short timer)
{ {
auto& item = g_Level.Items[itemNumber]; auto& item = g_Level.Items[itemNumber];
const auto& player = Lara;
// Handle reusable receptacles.
if (item.ObjectNumber >= ID_PUZZLE_DONE1 && item.ObjectNumber <= ID_PUZZLE_DONE16 &&
item.ItemFlags[1] != 0)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_ACTIVE;
item.ItemFlags[1] = false;
return 1;
}
if (item.ObjectNumber >= ID_PUZZLE_HOLE1 && item.ObjectNumber <= ID_PUZZLE_HOLE16 &&
item.ItemFlags[1] != 0)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_DEACTIVATED;
item.ItemFlags[1] = false;
return 1;
}
if ((item.ObjectNumber >= ID_PUZZLE_DONE1 && item.ObjectNumber <= ID_PUZZLE_DONE16) ||
(item.ObjectNumber >= ID_PUZZLE_HOLE1 && item.ObjectNumber <= ID_PUZZLE_HOLE16))
{
return 0;
}
// Handle reusable receptacles.
if (item.ObjectNumber >= ID_KEY_HOLE1 && item.ObjectNumber <= ID_KEY_HOLE16 &&
item.ItemFlags[1] != 0 &&
(item.ItemFlags[5] == (int)ReusableReceptacleState::Empty || item.ItemFlags[5] == (int)ReusableReceptacleState::None) &&
player.Control.HandStatus != HandStatus::Busy)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_ACTIVE;
item.ItemFlags[5] = (int)ReusableReceptacleState::Done;
item.ItemFlags[1] = false;
return 1;
}
if (item.ObjectNumber >= ID_KEY_HOLE1 && item.ObjectNumber <= ID_KEY_HOLE16 &&
item.ItemFlags[1] != 0 && item.ItemFlags[5] == (int)ReusableReceptacleState::Done &&
player.Control.HandStatus != HandStatus::Busy)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_DEACTIVATED;
item.ItemFlags[5] = (int)ReusableReceptacleState::Empty;
item.ItemFlags[1] = false;
return 1;
}
if (item.ObjectNumber >= ID_KEY_HOLE1 && item.ObjectNumber <= ID_KEY_HOLE16)
return 0;
// Handle switches.
if (item.Status == ITEM_DEACTIVATED) if (item.Status == ITEM_DEACTIVATED)
{ {
if ((!item.Animation.ActiveState && item.ObjectNumber != ID_JUMP_SWITCH || item.Animation.ActiveState == 1 && item.ObjectNumber == ID_JUMP_SWITCH) && if (((item.Animation.ActiveState == 0 && item.ObjectNumber != ID_JUMP_SWITCH) ||
(item.Animation.ActiveState == 1 && item.ObjectNumber == ID_JUMP_SWITCH)) &&
timer > 0) timer > 0)
{ {
item.Timer = timer; item.Timer = timer;
@ -136,13 +195,14 @@ int SwitchTrigger(short itemNumber, short timer)
return 1; return 1;
} }
if (item.TriggerFlags >= 0 || item.Animation.ActiveState) if (item.TriggerFlags >= 0 || item.Animation.ActiveState != 0)
{ {
RemoveActiveItem(itemNumber); RemoveActiveItem(itemNumber);
item.Status = ITEM_NOT_ACTIVE; item.Status = ITEM_NOT_ACTIVE;
if (!item.ItemFlags[0] == 0) if (!item.ItemFlags[0] == 0)
item.Flags |= ONESHOT; item.Flags |= ONESHOT;
return 1; return 1;
} }
else else
@ -151,11 +211,11 @@ int SwitchTrigger(short itemNumber, short timer)
return 1; return 1;
} }
} }
else if (item.Status) else if (item.Status != 0)
{ {
if (item.ObjectNumber == ID_AIRLOCK_SWITCH && if (item.ObjectNumber == ID_AIRLOCK_SWITCH &&
item.Animation.AnimNumber == GetAnimNumber(item, 2) && item.Animation.AnimNumber == GetAnimIndex(item, 2) &&
item.Animation.FrameNumber == GetFrameNumber(&item, 0)) item.Animation.FrameNumber == GetFrameIndex(&item, 0))
{ {
return 1; return 1;
} }
@ -170,15 +230,18 @@ int SwitchTrigger(short itemNumber, short timer)
return 0; return 0;
} }
int KeyTrigger(short itemNum) int KeyTrigger(short itemNumber)
{ {
ItemInfo* item = &g_Level.Items[itemNum]; auto* item = &g_Level.Items[itemNumber];
int oldkey; const auto& player = Lara;
if ((item->Status != ITEM_ACTIVE || Lara.Control.HandStatus == HandStatus::Busy) && (!KeyTriggerActive || Lara.Control.HandStatus != HandStatus::Busy)) if ((item->Status != ITEM_ACTIVE || player.Control.HandStatus == HandStatus::Busy) &&
(!KeyTriggerActive || player.Control.HandStatus != HandStatus::Busy))
{
return -1; return -1;
}
oldkey = KeyTriggerActive; int oldkey = KeyTriggerActive;
if (!oldkey) if (!oldkey)
item->Status = ITEM_DEACTIVATED; item->Status = ITEM_DEACTIVATED;
@ -188,9 +251,9 @@ int KeyTrigger(short itemNum)
return oldkey; return oldkey;
} }
bool PickupTrigger(short itemNum) bool PickupTrigger(short itemNumber)
{ {
ItemInfo* item = &g_Level.Items[itemNum]; auto* item = &g_Level.Items[itemNumber];
if (((item->Flags & IFLAG_CLEAR_BODY) && (item->Flags & IFLAG_KILLED)) || if (((item->Flags & IFLAG_CLEAR_BODY) && (item->Flags & IFLAG_KILLED)) ||
item->Status != ITEM_INVISIBLE || item->Status != ITEM_INVISIBLE ||
@ -200,7 +263,7 @@ bool PickupTrigger(short itemNum)
return false; return false;
} }
KillItem(itemNum); KillItem(itemNumber);
item->Flags |= IFLAG_CLEAR_BODY; item->Flags |= IFLAG_CLEAR_BODY;
return true; return true;
@ -237,6 +300,7 @@ void RefreshCamera(short type, short* data)
} }
else else
targetOk = 0; targetOk = 0;
break; break;
case TO_TARGET: case TO_TARGET:
@ -250,7 +314,7 @@ void RefreshCamera(short type, short* data)
if (Camera.item) if (Camera.item)
if (!targetOk || (targetOk == 2 && Camera.item->LookedAt && Camera.item != Camera.lastItem)) if (!targetOk || (targetOk == 2 && Camera.item->LookedAt && Camera.item != Camera.lastItem))
Camera.item = NULL; Camera.item = nullptr;
if (Camera.number == -1 && Camera.timer > 0) if (Camera.number == -1 && Camera.timer > 0)
Camera.timer = -1; Camera.timer = -1;
@ -498,8 +562,8 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
short targetType = 0; short targetType = 0;
short trigger = 0; short trigger = 0;
ItemInfo* item = NULL; ItemInfo* item = nullptr;
ItemInfo* cameraItem = NULL; ItemInfo* cameraItem = nullptr;
do do
{ {
@ -642,7 +706,7 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
UseSpotCam = true; UseSpotCam = true;
if (LastSpotCamSequence != value) if (LastSpotCamSequence != value)
TrackCameraInit = false; TrackCameraInit = false;
InitialiseSpotCam(value); InitializeSpotCam(value);
} }
} }
} }
@ -653,7 +717,7 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
break; break;
case TO_SINK: case TO_SINK:
Lara.WaterCurrentActive = value + 1; Lara.Context.WaterCurrentActive = value + 1;
break; break;
case TO_FLIPMAP: case TO_FLIPMAP:
@ -761,12 +825,12 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
FlipEffect = newEffect; FlipEffect = newEffect;
} }
void TestTriggers(ItemInfo* item, bool heavy, int heavyFlags) void TestTriggers(ItemInfo* item, bool isHeavy, int heavyFlags)
{ {
auto roomNum = item->RoomNumber; auto roomNumber = item->RoomNumber;
auto floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNum); auto floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, floor, item->Index, heavy, heavyFlags); TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, floor, item->Index, isHeavy, heavyFlags);
} }
void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags) void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags)

View file

@ -67,16 +67,16 @@ extern int TriggerTimer;
extern int KeyTriggerActive; extern int KeyTriggerActive;
bool GetKeyTrigger(ItemInfo* item); bool GetKeyTrigger(ItemInfo* item);
int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch); int GetSwitchTrigger(ItemInfo* item, short* itemNumbersPtr, int attatchedToSwitch);
int SwitchTrigger(short itemNumber, short timer); int SwitchTrigger(short itemNumber, short timer);
int KeyTrigger(short itemNum); int KeyTrigger(short itemNumber);
bool PickupTrigger(short itemNum); bool PickupTrigger(short itemNumber);
void RefreshCamera(short type, short* data); void RefreshCamera(short type, short* data);
int TriggerActive(ItemInfo* item); int TriggerActive(ItemInfo* item);
short* GetTriggerIndex(FloorInfo* floor, int x, int y, int z); short* GetTriggerIndex(FloorInfo* floor, int x, int y, int z);
short* GetTriggerIndex(ItemInfo* item); short* GetTriggerIndex(ItemInfo* item);
void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags = 0); void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags = 0);
void TestTriggers(ItemInfo* item, bool heavy, int heavyFlags = 0); void TestTriggers(ItemInfo* item, bool isHeavy, int heavyFlags = 0);
void ProcessSectorFlags(ItemInfo* item); void ProcessSectorFlags(ItemInfo* item);
void Antitrigger(short const value, short const flags = 0); void Antitrigger(short const value, short const flags = 0);

View file

@ -9,10 +9,10 @@
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "Renderer/Renderer11Enums.h" #include "Renderer/Renderer11Enums.h"
#include "Scripting/Include/ScriptInterfaceGame.h" #include "Scripting/Include/ScriptInterfaceGame.h"
#include "Specific/setup.h"
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
@ -173,7 +173,7 @@ namespace TEN::Control::Volumes
g_Renderer.AddDebugBox(box, Vector4(1.0f, 1.0f, 0.0f, 1.0f), RENDERER_DEBUG_PAGE::LARA_STATS); g_Renderer.AddDebugBox(box, Vector4(1.0f, 1.0f, 0.0f, 1.0f), RENDERER_DEBUG_PAGE::LARA_STATS);
if (item.IsLara() || item.Index == Lara.Vehicle) if (item.IsLara() || item.Index == Lara.Context.Vehicle)
{ {
TestVolumes(item.RoomNumber, box, VolumeActivatorFlags::Player, itemNumber); TestVolumes(item.RoomNumber, box, VolumeActivatorFlags::Player, itemNumber);
} }
@ -187,7 +187,7 @@ namespace TEN::Control::Volumes
} }
} }
void InitialiseNodeScripts() void InitializeNodeScripts()
{ {
static const std::string nodeScriptPath = "Scripts/Engine/NodeCatalogs/"; static const std::string nodeScriptPath = "Scripts/Engine/NodeCatalogs/";
@ -207,10 +207,18 @@ namespace TEN::Control::Volumes
TENLog("Loading node scripts...", LogLevel::Info); TENLog("Loading node scripts...", LogLevel::Info);
std::sort(nodeCatalogs.rbegin(), nodeCatalogs.rend()); std::sort(nodeCatalogs.rbegin(), nodeCatalogs.rend());
if (!nodeCatalogs.empty())
{
for (const auto& file : nodeCatalogs) for (const auto& file : nodeCatalogs)
g_GameScript->ExecuteScriptFile(nodeScriptPath + file); g_GameScript->ExecuteScriptFile(nodeScriptPath + file);
TENLog(std::to_string(nodeCatalogs.size()) + " node catalogs were found and loaded.", LogLevel::Info); TENLog(std::to_string(nodeCatalogs.size()) + " node catalogs were found and loaded.", LogLevel::Info);
}
else
{
TENLog("No node catalogs were found.", LogLevel::Warning);
}
unsigned int nodeCount = 0; unsigned int nodeCount = 0;
for (const auto& set : g_Level.EventSets) for (const auto& set : g_Level.EventSets)

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "Game/control/volumeactivator.h" #include "Game/control/volumeactivator.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "Specific/setup.h"
struct CollisionSetup; struct CollisionSetup;
@ -44,7 +44,7 @@ namespace TEN::Control::Volumes
void TestVolumes(CAMERA_INFO* camera); void TestVolumes(CAMERA_INFO* camera);
void HandleEvent(VolumeEvent& event, VolumeActivator& activator); void HandleEvent(VolumeEvent& event, VolumeActivator& activator);
void InitialiseNodeScripts(); void InitializeNodeScripts();
} }
// TODO: Move into namespace and deal with errors. // TODO: Move into namespace and deal with errors.

View file

@ -3,9 +3,10 @@
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/Utils/object_helper.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/setup.h"
using namespace TEN::Math; using namespace TEN::Math;
@ -17,6 +18,9 @@ namespace TEN::Effects::Blood
void SpawnUnderwaterBlood(const Vector3& pos, int roomNumber, float size) void SpawnUnderwaterBlood(const Vector3& pos, int roomNumber, float size)
{ {
if (!CheckIfSlotExists(ID_DEFAULT_SPRITES, "Blood rendering"))
return;
constexpr auto LIFE_MAX = 8.5f; constexpr auto LIFE_MAX = 8.5f;
constexpr auto LIFE_MIN = 8.0f; constexpr auto LIFE_MIN = 8.0f;
constexpr auto SPAWN_RADIUS = BLOCK(0.25f); constexpr auto SPAWN_RADIUS = BLOCK(0.25f);

View file

@ -3,8 +3,8 @@
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/people.h" #include "Game/people.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/setup.h"
using namespace TEN::Math; using namespace TEN::Math;

View file

@ -2,10 +2,10 @@
#include "Game/effects/Ripple.h" #include "Game/effects/Ripple.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/setup.h"
using namespace TEN::Math; using namespace TEN::Math;

View file

@ -69,7 +69,7 @@ namespace TEN::Effects::Streamer
public: public:
// Members // Members
std::map<int, std::vector<Streamer>> Pools = {}; // Key = tag. std::unordered_map<int, std::vector<Streamer>> Pools = {}; // Key = tag.
// Utilities // Utilities
void AddStreamer(int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color, void AddStreamer(int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
@ -92,7 +92,7 @@ namespace TEN::Effects::Streamer
public: public:
// Members // Members
std::map<int, StreamerModule> Modules = {}; // Key = entity number. std::unordered_map<int, StreamerModule> Modules = {}; // Key = entity number.
// Utilities // Utilities
void Spawn(int entityID, int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color, void Spawn(int entityID, int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,

View file

@ -6,14 +6,14 @@
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/effects/Ripple.h" #include "Game/effects/Ripple.h"
#include "Game/Setup.h"
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Ripple; using namespace TEN::Effects::Ripple;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Math; using namespace TEN::Math;
namespace TEN::Effects::Bubble namespace TEN::Effects::Bubble

View file

@ -10,9 +10,9 @@
#include "Game/effects/tomb4fx.h" #include "Game/effects/tomb4fx.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Renderer/Renderer11Enums.h" #include "Renderer/Renderer11Enums.h"
#include "Sound/sound.h" #include "Sound/sound.h"

View file

@ -2,11 +2,11 @@
#include "Game/effects/debris.h" #include "Game/effects/debris.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/effects/tomb4fx.h"
#include "Game/Setup.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Math/Random.h" #include "Math/Random.h"
#include "Specific/setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include <Game/effects/tomb4fx.h>
using std::vector; using std::vector;
using namespace TEN::Renderer; using namespace TEN::Renderer;

View file

@ -7,14 +7,14 @@
#include "Game/effects/Ripple.h" #include "Game/effects/Ripple.h"
#include "Game/effects/weather.h" #include "Game/effects/weather.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Ripple; using namespace TEN::Effects::Ripple;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Math; using namespace TEN::Math;
namespace TEN::Effects::Drip namespace TEN::Effects::Drip

View file

@ -17,13 +17,13 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Blood; using namespace TEN::Effects::Blood;
using namespace TEN::Effects::Bubble; using namespace TEN::Effects::Bubble;
@ -52,7 +52,7 @@ int SplashCount = 0;
Vector3i NodeVectors[ParticleNodeOffsetIDs::NodeMax]; Vector3i NodeVectors[ParticleNodeOffsetIDs::NodeMax];
NODEOFFSET_INFO NodeOffsets[ParticleNodeOffsetIDs::NodeMax] = NODEOFFSET_INFO NodeOffsets[ParticleNodeOffsetIDs::NodeMax] =
{ {
{ -16, 40, 160, -LM_LHAND, false }, // TR5 offset 0, TODO: This mesh is invalid as it can't be negative. -- TokyoSU 23.02.20 { -16, 40, 160, 13, false }, // TR5 offset 0
{ -16, -8, 160, 0, false }, // TR5 offset 1 { -16, -8, 160, 0, false }, // TR5 offset 1
{ 0, 0, 256, 8, false }, // TR5 offset 2 { 0, 0, 256, 8, false }, // TR5 offset 2
{ 0, 0, 256, 17, false }, // TR5 offset 3 { 0, 0, 256, 17, false }, // TR5 offset 3
@ -741,7 +741,7 @@ void TriggerExplosionBubbles(int x, int y, int z, short roomNumber)
spark->zVel = 0; spark->zVel = 0;
spark->friction = 0; spark->friction = 0;
spark->flags = SP_UNDERWEXP | SP_DEF | SP_SCALE; spark->flags = SP_UNDERWEXP | SP_DEF | SP_SCALE;
spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + 13; spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_BUBBLES;
spark->scalar = 3; spark->scalar = 3;
spark->gravity = 0; spark->gravity = 0;
spark->maxYvel = 0; spark->maxYvel = 0;

View file

@ -4,8 +4,8 @@
#include "Game/effects/effects.h" #include "Game/effects/effects.h"
#include "Game/effects/spark.h" #include "Game/effects/spark.h"
#include "Game/effects/tomb4fx.h" #include "Game/effects/tomb4fx.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/setup.h"
using namespace TEN::Math; using namespace TEN::Math;

View file

@ -1,264 +1,222 @@
#include "framework.h" #include "framework.h"
#include "Game/effects/footprint.h" #include "Game/effects/Footprint.h"
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/collision/floordata.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/effects/effects.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/Utils/object_helper.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Math; using namespace TEN::Math;
namespace TEN::Effects::Footprints namespace TEN::Effects::Footprint
{ {
std::deque<FOOTPRINT_STRUCT> footprints = std::deque<FOOTPRINT_STRUCT>(); const auto FootprintMaterials = std::vector<MaterialType>
bool CheckFootOnFloor(const ItemInfo& item, int mesh, Vector3& outFootprintPosition)
{ {
int x = item.Pose.Position.x; MaterialType::Mud,
int y = item.Pose.Position.y; MaterialType::Snow,
int z = item.Pose.Position.z; MaterialType::Sand,
short roomNumber = item.RoomNumber; MaterialType::Gravel,
MaterialType::Custom1,
MaterialType::Custom2,
MaterialType::Custom3,
MaterialType::Custom4
};
auto floor = GetFloor(x, y, z, &roomNumber); struct FootprintPositionData
auto pos = GetJointPosition(LaraItem, mesh, Vector3i(0, FOOT_HEIGHT_OFFSET, 0)); {
int height = GetFloorHeight(floor, pos.x, pos.y - CLICK(1), pos.z); bool CanSpawn = false;
Vector3 Position = Vector3::Zero;
};
outFootprintPosition.x = pos.x; std::vector<Footprint> Footprints = {};
outFootprintPosition.y = height - 8;
outFootprintPosition.z = pos.z;
return abs(pos.y - height) < 64; static SOUND_EFFECTS GetFootprintSfx(MaterialType material)
{
static const auto SOUND_MAP = std::unordered_map<MaterialType, SOUND_EFFECTS>
{
{ MaterialType::Mud, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_MUD },
{ MaterialType::Snow, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_SNOW },
{ MaterialType::Sand, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_SAND },
{ MaterialType::Gravel, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_GRAVEL },
{ MaterialType::Ice, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_ICE },
{ MaterialType::Water, SOUND_EFFECTS::SFX_TR4_LARA_WET_FEET },
{ MaterialType::Stone, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS },
{ MaterialType::Wood, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_WOOD },
{ MaterialType::Metal, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_METAL },
{ MaterialType::Marble, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_MARBLE },
{ MaterialType::Grass, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_GRASS },
{ MaterialType::Concrete, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS },
{ MaterialType::OldWood, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_WOOD },
{ MaterialType::OldMetal, SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_METAL },
{ MaterialType::Custom1, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_1 },
{ MaterialType::Custom2, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_2 },
{ MaterialType::Custom3, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_3 },
{ MaterialType::Custom4, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_4 },
{ MaterialType::Custom5, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_5 },
{ MaterialType::Custom6, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_6 },
{ MaterialType::Custom7, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_7 },
{ MaterialType::Custom8, SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_8 }
};
auto it = SOUND_MAP.find(material);
return ((it != SOUND_MAP.end()) ? it->second : SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS);
} }
void AddFootprint(ItemInfo* item, bool rightFoot) static std::array<Vector3, Footprint::VERTEX_COUNT> GetFootprintVertexPoints(const ItemInfo& item, const Vector3& pos, const Vector3& normal)
{ {
if (item != LaraItem) constexpr auto SCALE = BLOCK(1 / 16.0f);
return; constexpr auto POINT_0 = Vector3( SCALE, 0.0f, SCALE);
constexpr auto POINT_1 = Vector3(-SCALE, 0.0f, SCALE);
constexpr auto POINT_2 = Vector3(-SCALE, 0.0f, -SCALE);
constexpr auto POINT_3 = Vector3( SCALE, 0.0f, -SCALE);
auto foot = rightFoot ? LM_RFOOT : LM_LFOOT; // Determine rotation matrix.
auto rotMatrix = Geometry::GetRelOrientToNormal(item.Pose.Orientation.y, normal).ToRotationMatrix();
// Don't process actual footprint placement if foot isn't on floor. return std::array<Vector3, Footprint::VERTEX_COUNT>
auto footPos = Vector3();
if (!CheckFootOnFloor(*item, foot, footPos))
return;
// Slightly randomize foot position to avoid patterns.
footPos.x += (GetRandomControl() & 10) - 5;
footPos.z += (GetRandomControl() & 10) - 5;
auto result = GetCollision(footPos.x, footPos.y - STEP_SIZE, footPos.z, item->RoomNumber);
auto floor = result.BottomBlock;
// Don't process material if foot has hit bridge object.
if (result.Position.Bridge >= 0)
return;
auto fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS;
// Choose material for footstep sound
switch (floor->Material)
{ {
case MaterialType::Concrete: pos + Vector3::Transform(POINT_0, rotMatrix),
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS; pos + Vector3::Transform(POINT_1, rotMatrix),
break; pos + Vector3::Transform(POINT_2, rotMatrix),
pos + Vector3::Transform(POINT_3, rotMatrix)
case MaterialType::Grass: };
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_GRASS;
break;
case MaterialType::Gravel:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_GRAVEL;
break;
case MaterialType::Ice:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_ICE;
break;
case MaterialType::Marble:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_MARBLE;
break;
case MaterialType::Metal:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_METAL;
break;
case MaterialType::Mud:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_MUD;
break;
case MaterialType::OldMetal:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_METAL;
break;
case MaterialType::OldWood:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_WOOD;
break;
case MaterialType::Sand:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_SAND;
break;
case MaterialType::Snow:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_SNOW;
break;
case MaterialType::Stone:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS;
break;
case MaterialType::Water:
fx = SOUND_EFFECTS::SFX_TR4_LARA_WET_FEET;
break;
case MaterialType::Wood:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_WOOD;
break;
case MaterialType::Custom1:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_1;
break;
case MaterialType::Custom2:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_2;
break;
case MaterialType::Custom3:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_3;
break;
case MaterialType::Custom4:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_4;
break;
case MaterialType::Custom5:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_5;
break;
case MaterialType::Custom6:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_6;
break;
case MaterialType::Custom7:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_7;
break;
case MaterialType::Custom8:
fx = SOUND_EFFECTS::SFX_CUSTOM_FOOTSTEP_8;
break;
} }
// HACK: Must be here until reference WAD2 is revised. static FootprintPositionData GetFootprintPositionData(const ItemInfo& item, int jointIndex)
if (fx != SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS)
SoundEffect(fx, &item->Pose);
if (floor->Material != MaterialType::Sand &&
floor->Material != MaterialType::Snow &&
floor->Material != MaterialType::Gravel &&
floor->Material != MaterialType::Mud &&
floor->Material != MaterialType::Custom2 &&
floor->Material != MaterialType::Custom1 &&
floor->Material != MaterialType::Custom3 &&
floor->Material != MaterialType::Custom4)
{ {
return; constexpr auto SURFACE_OFFSET = 4;
constexpr auto ABS_FLOOR_BOUND = CLICK(0.25f);
constexpr auto HEIGHT_OFFSET = CLICK(0.25f);
constexpr auto FOOT_OFFSET = Vector3i(0, HEIGHT_OFFSET, 0);
auto footPos = GetJointPosition(item, jointIndex, FOOT_OFFSET);
int floorHeight = GetCollision(footPos.x, footPos.y - CLICK(1), footPos.z, item.RoomNumber).Position.Floor;
bool canSpawn = (abs(footPos.y - floorHeight) < ABS_FLOOR_BOUND);
auto pos = Vector3(footPos.x, floorHeight - SURFACE_OFFSET, footPos.z);
return FootprintPositionData{ canSpawn, pos };
} }
// Calculate footprint tilts. static bool TestFootprintFloor(const ItemInfo& item, const Vector3& pos, const std::array<Vector3, Footprint::VERTEX_COUNT>& vertexPoints)
auto plane = floor->FloorCollision.Planes[floor->GetSurfacePlaneIndex(footPos.x, footPos.z, true)]; {
auto c = phd_cos(item->Pose.Orientation.y + ANGLE(180.0f)); constexpr auto ABS_FLOOR_BOUND = CLICK(0.5f);
auto s = phd_sin(item->Pose.Orientation.y + ANGLE(180.0f));
auto yRot = TO_RAD(item->Pose.Orientation.y);
auto xRot = plane.x * s + plane.y * c;
auto zRot = plane.y * s - plane.x * c;
// Calculate footprint positions. // Get point collision at every vertex point.
auto p0 = Vector3( FOOTPRINT_SIZE, 0, FOOTPRINT_SIZE); auto pointColl0 = GetCollision(vertexPoints[0].x, pos.y - CLICK(1), vertexPoints[0].z, item.RoomNumber);
auto p1 = Vector3(-FOOTPRINT_SIZE, 0, FOOTPRINT_SIZE); auto pointColl1 = GetCollision(vertexPoints[1].x, pos.y - CLICK(1), vertexPoints[1].z, item.RoomNumber);
auto p2 = Vector3(-FOOTPRINT_SIZE, 0, -FOOTPRINT_SIZE); auto pointColl2 = GetCollision(vertexPoints[2].x, pos.y - CLICK(1), vertexPoints[2].z, item.RoomNumber);
auto p3 = Vector3( FOOTPRINT_SIZE, 0, -FOOTPRINT_SIZE); auto pointColl3 = GetCollision(vertexPoints[3].x, pos.y - CLICK(1), vertexPoints[3].z, item.RoomNumber);
auto rot = Matrix::CreateFromYawPitchRoll(yRot, xRot, zRot);
p0 = XMVector3Transform(p0, rot);
p1 = XMVector3Transform(p1, rot);
p2 = XMVector3Transform(p2, rot);
p3 = XMVector3Transform(p3, rot);
p0 += Vector3(footPos.x, footPos.y, footPos.z);
p1 += Vector3(footPos.x, footPos.y, footPos.z);
p2 += Vector3(footPos.x, footPos.y, footPos.z);
p3 += Vector3(footPos.x, footPos.y, footPos.z);
// Get blocks for every footprint corner // Don't spawn footprint if floor heights at vertex points are outside upper/lower floor height bound.
auto c0 = GetCollision(p0.x, footPos.y - STEP_SIZE, p0.z, item->RoomNumber); if ((abs(pointColl0.Position.Floor - pointColl1.Position.Floor) > ABS_FLOOR_BOUND) ||
auto c1 = GetCollision(p1.x, footPos.y - STEP_SIZE, p1.z, item->RoomNumber); (abs(pointColl1.Position.Floor - pointColl2.Position.Floor) > ABS_FLOOR_BOUND) ||
auto c2 = GetCollision(p2.x, footPos.y - STEP_SIZE, p2.z, item->RoomNumber); (abs(pointColl2.Position.Floor - pointColl3.Position.Floor) > ABS_FLOOR_BOUND) ||
auto c3 = GetCollision(p3.x, footPos.y - STEP_SIZE, p3.z, item->RoomNumber); (abs(pointColl3.Position.Floor - pointColl0.Position.Floor) > ABS_FLOOR_BOUND))
{
return false;
}
// Don't process footprint placement if all foot corners aren't on the same tilt level return true;
if ((c0.FloorTilt.x != c1.FloorTilt.x) || (c1.FloorTilt.x != c2.FloorTilt.x) || (c2.FloorTilt.x != c3.FloorTilt.x)) }
return;
if ((c0.FloorTilt.y != c1.FloorTilt.y) || (c1.FloorTilt.y != c2.FloorTilt.y) || (c2.FloorTilt.y != c3.FloorTilt.y)) void SpawnFootprint(bool isRight, const std::array<Vector3, Footprint::VERTEX_COUNT>& vertexPoints)
{
if (!CheckIfSlotExists(ID_MISC_SPRITES, "Footprint rendering"))
return; return;
// Don't process footprint placement if all foot corners aren't on the same height constexpr auto LIFE_MAX = 20.0f;
if ((abs(c0.Position.Floor - c1.Position.Floor) > STEP_SIZE / 2) || constexpr auto LIFE_START_FADING = 10.0f;
(abs(c1.Position.Floor - c2.Position.Floor) > STEP_SIZE / 2) || constexpr auto OPACITY_MAX = 0.5f;
(abs(c2.Position.Floor - c3.Position.Floor) > STEP_SIZE / 2) ||
(abs(c3.Position.Floor - c0.Position.Floor) > STEP_SIZE / 2)) auto& footprint = GetNewEffect(Footprints, Footprint::COUNT_MAX);
footprint.SpriteIndex = Objects[ID_MISC_SPRITES].meshIndex + (1 + (int)isRight);
footprint.IsRight = isRight;
footprint.VertexPoints = vertexPoints;
footprint.Life = std::round(LIFE_MAX * FPS);
footprint.LifeStartFading = std::round(LIFE_START_FADING * FPS);
footprint.Opacity =
footprint.OpacityStart = OPACITY_MAX;
}
void SpawnFootprint(const ItemInfo& item, bool isRight)
{
if (!item.IsLara())
return; return;
// Construct footprint // Don't spawn footprint if foot isn't on floor.
FOOTPRINT_STRUCT footprint = {}; int jointIndex = isRight ? LM_RFOOT : LM_LFOOT;
footprint.Position[0] = p0; auto posData = GetFootprintPositionData(item, jointIndex);
footprint.Position[1] = p1; if (!posData.CanSpawn)
footprint.Position[2] = p2; return;
footprint.Position[3] = p3;
footprint.StartOpacity = 0.5f;
footprint.LifeStartFading = FPS * 10;
footprint.Life = FPS * 20;
footprint.Active = true;
footprint.RightFoot = rightFoot;
// Add footprint // Slightly randomize 2D position.
if (footprints.size() >= MAX_FOOTPRINTS) posData.Position += Vector3(Random::GenerateFloat(-5.0f, 5.0f), 0.0f, Random::GenerateFloat(-5.0f, 5.0f));
footprints.pop_back();
footprints.push_front(footprint); auto pointColl = GetCollision(posData.Position.x, posData.Position.y - CLICK(1), posData.Position.z, item.RoomNumber);
// Don't process material if foot hit bridge object.
// TODO: Handle bridges once bridge collision is less stupid.
if (pointColl.Position.Bridge >= 0)
return;
// Get and emit footstep sound for floor material.
auto sfx = GetFootprintSfx(pointColl.BottomBlock->Material);
if (sfx != SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS) // HACK: Must be here until reference WAD2 is revised.
{
auto pose = item.Pose;
SoundEffect(sfx, &pose);
}
// Check floor material.
if (!TestMaterial(pointColl.BottomBlock->Material, FootprintMaterials))
return;
auto vertexPoints = GetFootprintVertexPoints(item, posData.Position, GetSurfaceNormal(pointColl.FloorTilt, true));
// Test floor continuity.
if (!TestFootprintFloor(item, posData.Position, vertexPoints))
return;
SpawnFootprint(isRight, vertexPoints);
} }
void UpdateFootprints() void UpdateFootprints()
{ {
if (footprints.size() == 0) if (Footprints.empty())
return; return;
int numInvalidFootprints = 0; for (auto& footprint: Footprints)
for (auto i = footprints.begin(); i != footprints.end(); i++)
{ {
FOOTPRINT_STRUCT& footprint = *i; if (footprint.Life <= 0.0f)
footprint.Life--;
if (footprint.Life <= 0)
{
numInvalidFootprints++;
continue; continue;
// Update opacity.
if (footprint.Life <= footprint.LifeStartFading)
{
float alpha = 1.0f - (footprint.Life / footprint.LifeStartFading);
footprint.Opacity = Lerp(footprint.OpacityStart, 0.0f, alpha);
} }
if (footprint.Life > footprint.LifeStartFading) // Update life.
{ footprint.Life -= 1.0f;
footprint.Opacity = footprint.StartOpacity;
}
else
{
float opacity = Lerp(0.0f, footprint.StartOpacity, fmax(0, fmin(1, footprint.Life / (float)footprint.LifeStartFading)));
footprint.Opacity = opacity;
}
} }
for (int i = 0; i < numInvalidFootprints; i++) ClearInactiveEffects(Footprints);
}
void ClearFootprints()
{ {
footprints.pop_back(); Footprints.clear();
}
} }
} }

View file

@ -1,28 +1,30 @@
#pragma once #pragma once
#include <deque>
#include "Math/Math.h"
struct ItemInfo; struct ItemInfo;
namespace TEN::Effects::Footprints namespace TEN::Effects::Footprint
{ {
constexpr size_t MAX_FOOTPRINTS = 20; struct Footprint
constexpr auto FOOTPRINT_SIZE = 64.0f; {
constexpr int FOOT_HEIGHT_OFFSET = 64; static constexpr auto COUNT_MAX = 64;
static constexpr auto VERTEX_COUNT = 4;
struct FOOTPRINT_STRUCT unsigned int SpriteIndex = 0;
{ bool IsRight = false;
Vector3 Position[4];
bool RightFoot; float Life = 0.0f;
int Life; float LifeStartFading = 0.0f;
int LifeStartFading; float Opacity = 0.0f;
float StartOpacity; float OpacityStart = 0.0f;
float Opacity;
bool Active; std::array<Vector3, VERTEX_COUNT> VertexPoints = {};
}; };
extern std::deque<FOOTPRINT_STRUCT> footprints;
bool CheckFootOnFloor(const ItemInfo& item, int mesh, Vector3& outFootprintPosition); extern std::vector<Footprint> Footprints;
void AddFootprint(ItemInfo* item, bool rightFoot);
void SpawnFootprint(bool isRight, const std::array<Vector3, Footprint::VERTEX_COUNT>& vertexPoints);
void SpawnFootprint(const ItemInfo& item, bool isRight);
void UpdateFootprints(); void UpdateFootprints();
void ClearFootprints();
} }

View file

@ -9,10 +9,10 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
@ -81,7 +81,7 @@ namespace TEN::Effects::Hair
// TR3 UPV uses a hack which forces player water status to dry. // TR3 UPV uses a hack which forces player water status to dry.
// Therefore, cannot directly use water status value to determine enrironment. // Therefore, cannot directly use water status value to determine enrironment.
bool isOnLand = (player.Control.WaterStatus == WaterStatus::Dry && bool isOnLand = (player.Control.WaterStatus == WaterStatus::Dry &&
(player.Vehicle == -1 || g_Level.Items[player.Vehicle].ObjectNumber != ID_UPV)); (player.Context.Vehicle == -1 || g_Level.Items[player.Context.Vehicle].ObjectNumber != ID_UPV));
// Handle segment room collision. // Handle segment room collision.
CollideSegmentWithRoom(segment, waterHeight, roomNumber, isOnLand); CollideSegmentWithRoom(segment, waterHeight, roomNumber, isOnLand);
@ -161,7 +161,7 @@ namespace TEN::Effects::Hair
break; break;
} }
int frameBaseIndex = GetAnimData(animNumber).FramePtr; int frameBaseIndex = GetAnimData(item.ObjectNumber, animNumber).FramePtr;
const auto& frame = g_Level.Frames[frameBaseIndex + player.HitFrame]; const auto& frame = g_Level.Frames[frameBaseIndex + player.HitFrame];
return frame.BoundingBox.GetCenter(); return frame.BoundingBox.GetCenter();
} }

View file

@ -95,17 +95,17 @@ namespace TEN::Effects::Items
switch (item->Animation.AnimNumber) switch (item->Animation.AnimNumber)
{ {
case LA_STAND_IDLE: case LA_STAND_IDLE:
if (item->Animation.FrameNumber < GetFrameNumber(ID_LARA, LA_STAND_IDLE, 30)) if (item->Animation.FrameNumber < GetFrameIndex(ID_LARA, LA_STAND_IDLE, 30))
return; return;
break; break;
case LA_CROUCH_IDLE: case LA_CROUCH_IDLE:
if (item->Animation.FrameNumber < GetFrameNumber(ID_LARA, LA_CROUCH_IDLE, 30)) if (item->Animation.FrameNumber < GetFrameIndex(ID_LARA, LA_CROUCH_IDLE, 30))
return; return;
break; break;
case LA_CRAWL_IDLE: case LA_CRAWL_IDLE:
if (item->Animation.FrameNumber < GetFrameNumber(ID_LARA, LA_CRAWL_IDLE, 30)) if (item->Animation.FrameNumber < GetFrameIndex(ID_LARA, LA_CRAWL_IDLE, 30))
return; return;
break; break;

View file

@ -2,8 +2,8 @@
#include "Game/effects/simple_particle.h" #include "Game/effects/simple_particle.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/setup.h"
using namespace TEN::Math; using namespace TEN::Math;

View file

@ -8,9 +8,9 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
using namespace TEN::Math; using namespace TEN::Math;
@ -113,19 +113,24 @@ namespace TEN::Effects::Smoke
//TODO: add additional "Weapon Special" param or something. Currently initial == 2 means Rocket Launcher backwards smoke. //TODO: add additional "Weapon Special" param or something. Currently initial == 2 means Rocket Launcher backwards smoke.
//TODO: Refactor different weapon types out of it //TODO: Refactor different weapon types out of it
void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count) void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count)
{
SpawnGunSmokeParticles(Vector3(x, y, z), Vector3(xv, yv, zv), LaraItem->RoomNumber, initial, weaponType, count);
}
void SpawnGunSmokeParticles(const Vector3& pos, const Vector3& direction, int roomNumber, byte initial, LaraWeaponType weaponType, int count)
{ {
auto& s = GetFreeSmokeParticle(); auto& s = GetFreeSmokeParticle();
s = {}; s = {};
s.active = true; s.active = true;
s.position = Vector3(x, y, z); s.position = pos;
Vector3 direction = Vector3(xv, yv, zv); auto directionNorm = direction;
direction.Normalize(); directionNorm.Normalize();
s.velocity = direction; s.velocity = directionNorm;
s.gravity = -.1f; s.gravity = -.1f;
s.affectedByWind = TestEnvironment(ENV_FLAG_WIND, LaraItem); s.affectedByWind = TestEnvironment(ENV_FLAG_WIND, pos.x, pos.y, pos.z, roomNumber);
s.sourceColor = Vector4(.4f, .4f, .4f, 1); s.sourceColor = Vector4(0.4f, 0.4f, 0.4f, 1);
s.destinationColor = Vector4(0, 0, 0, 0); s.destinationColor = Vector4(0, 0, 0, 0);
if (initial) if (initial)
@ -145,7 +150,7 @@ namespace TEN::Effects::Smoke
float size = Random::GenerateFloat(48, 80); float size = Random::GenerateFloat(48, 80);
s.sourceSize = size * 2; s.sourceSize = size * 2;
s.destinationSize = size * 16; s.destinationSize = size * 16;
s.velocity = Random::GenerateDirectionInCone(direction, 25); s.velocity = Random::GenerateDirectionInCone(directionNorm, 25);
s.velocity *= Random::GenerateFloat(0, 32); s.velocity *= Random::GenerateFloat(0, 32);
} }
else else
@ -153,7 +158,7 @@ namespace TEN::Effects::Smoke
float size = Random::GenerateFloat(48, 80); float size = Random::GenerateFloat(48, 80);
s.sourceSize = size; s.sourceSize = size;
s.destinationSize = size * 8; s.destinationSize = size * 8;
s.velocity = Random::GenerateDirectionInCone(direction, 3); s.velocity = Random::GenerateDirectionInCone(directionNorm, 3);
s.velocity *= Random::GenerateFloat(0, 16); s.velocity *= Random::GenerateFloat(0, 16);
} }
} }
@ -165,7 +170,7 @@ namespace TEN::Effects::Smoke
s.terminalVelocity = 0; s.terminalVelocity = 0;
s.friction = 0.88f; s.friction = 0.88f;
s.life = Random::GenerateFloat(60, 90); s.life = Random::GenerateFloat(60, 90);
s.velocity = Random::GenerateDirectionInCone(direction, 10); s.velocity = Random::GenerateDirectionInCone(directionNorm, 10);
s.velocity *= Random::GenerateFloat(16, 30); s.velocity *= Random::GenerateFloat(16, 30);
} }
} }
@ -184,11 +189,11 @@ namespace TEN::Effects::Smoke
s.velocity *= Random::GenerateFloat(16, 40); s.velocity *= Random::GenerateFloat(16, 40);
} }
s.position = Vector3(x, y, z); s.position = pos;
s.position += Vector3(Random::GenerateFloat(-8, 8), Random::GenerateFloat(-8, 8), Random::GenerateFloat(-8, 8)); s.position += Vector3(Random::GenerateFloat(-8, 8), Random::GenerateFloat(-8, 8), Random::GenerateFloat(-8, 8));
s.angularVelocity = Random::GenerateFloat(-PI_DIV_4, PI_DIV_4); s.angularVelocity = Random::GenerateFloat(-PI_DIV_4, PI_DIV_4);
s.angularDrag = 0.95f; s.angularDrag = 0.95f;
s.room = LaraItem->RoomNumber; s.room = roomNumber;
} }
void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int velocity, int moving) void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int velocity, int moving)

View file

@ -34,6 +34,7 @@ namespace TEN::Effects::Smoke
void DisableSmokeParticles(); void DisableSmokeParticles();
void TriggerFlareSmoke(const Vector3& pos, const Vector3& direction, int life, int room); void TriggerFlareSmoke(const Vector3& pos, const Vector3& direction, int life, int room);
void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count); void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count);
void SpawnGunSmokeParticles(const Vector3& pos, const Vector3& direction, int roomNumber, byte initial, LaraWeaponType weaponType, int count);
void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int velocity, int moving); void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int velocity, int moving);
void TriggerRocketSmoke(int x, int y, int z); void TriggerRocketSmoke(int x, int y, int z);
void TriggerBreathSmoke(long x, long y, long z, short angle); void TriggerBreathSmoke(long x, long y, long z, short angle);

View file

@ -15,18 +15,18 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Bubble; using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::Drip; using namespace TEN::Effects::Drip;
using namespace TEN::Effects::Environment; using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Ripple; using namespace TEN::Effects::Ripple;
using namespace TEN::Effects::Smoke; using namespace TEN::Effects::Smoke;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Math; using namespace TEN::Math;
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
@ -203,14 +203,17 @@ void TriggerGlobalFireFlame()
spark->dSize = spark->size; spark->dSize = spark->size;
} }
void TriggerPilotFlame(int itemNum, int nodeIndex) void TriggerPilotFlame(int itemNumber, int nodeIndex)
{ {
auto* item = &g_Level.Items[itemNum]; auto* item = &g_Level.Items[itemNumber];
int dx = Camera.pos.x - item->Pose.Position.x; int dx = Camera.pos.x - item->Pose.Position.x;
int dz = Camera.pos.z - item->Pose.Position.z; int dz = Camera.pos.z - item->Pose.Position.z;
if (dx < -SECTOR(16) || dx > SECTOR(16) || dz < -SECTOR(16) || dz > SECTOR(16)) if (dx < -BLOCK(16) || dx > BLOCK(16) ||
dz < -BLOCK(16) || dz > BLOCK(16))
{
return; return;
}
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -229,7 +232,7 @@ void TriggerPilotFlame(int itemNum, int nodeIndex)
spark->blendMode = BLEND_MODES::BLENDMODE_ADDITIVE; spark->blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
spark->extras = 0; spark->extras = 0;
spark->dynamic = -1; spark->dynamic = -1;
spark->fxObj = itemNum; spark->fxObj = itemNumber;
spark->x = (GetRandomControl() & 31) - 16; spark->x = (GetRandomControl() & 31) - 16;
spark->y = (GetRandomControl() & 31) - 16; spark->y = (GetRandomControl() & 31) - 16;
@ -291,7 +294,7 @@ Particle* SetupFireSpark()
return spark; return spark;
} }
void AttachAndCreateSpark(Particle* spark, ItemInfo* item, int meshIndex, Vector3i offset, Vector3i speed) void AttachAndCreateSpark(Particle* spark, ItemInfo* item, int meshIndex, Vector3i offset, Vector3i vel)
{ {
auto pos1 = GetJointPosition(item, meshIndex, Vector3i(-4, -30, -4) + offset); auto pos1 = GetJointPosition(item, meshIndex, Vector3i(-4, -30, -4) + offset);
@ -299,7 +302,7 @@ void AttachAndCreateSpark(Particle* spark, ItemInfo* item, int meshIndex, Vector
spark->y = (GetRandomControl() & 0x1F) + pos1.y - 16; spark->y = (GetRandomControl() & 0x1F) + pos1.y - 16;
spark->z = (GetRandomControl() & 0x1F) + pos1.z - 16; spark->z = (GetRandomControl() & 0x1F) + pos1.z - 16;
auto pos2 = GetJointPosition(item, meshIndex, Vector3i(-4, -30, -4) + offset + speed); auto pos2 = GetJointPosition(item, meshIndex, Vector3i(-4, -30, -4) + offset + vel);
int v = (GetRandomControl() & 0x3F) + 192; int v = (GetRandomControl() & 0x3F) + 192;
@ -322,32 +325,41 @@ void AttachAndCreateSpark(Particle* spark, ItemInfo* item, int meshIndex, Vector
spark->on = 1; spark->on = 1;
} }
void ThrowFire(int itemNum, int meshIndex, Vector3i offset, Vector3i speed) void ThrowFire(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel)
{ {
auto* item = &g_Level.Items[itemNum]; auto& item = g_Level.Items[itemNumber];
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
auto* spark = SetupFireSpark(); auto& spark = *SetupFireSpark();
AttachAndCreateSpark(spark, item, meshIndex, offset, speed); AttachAndCreateSpark(&spark, &item, meshIndex, offset, vel);
spark->flags = SP_FIRE | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; spark.flags = SP_FIRE | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
} }
} }
void ThrowPoison(int itemNum, int meshIndex, Vector3i offset, Vector3i speed, Vector3 color) void ThrowFire(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel)
{ {
auto* item = &g_Level.Items[itemNum]; ThrowFire(itemNumber, bite.BoneID, bite.Position, vel);
}
void ThrowPoison(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel, const Vector3& color)
{
auto* item = &g_Level.Items[itemNumber];
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
auto* spark = SetupPoisonSpark(color); auto* spark = SetupPoisonSpark(color);
AttachAndCreateSpark(spark, item, meshIndex, offset, speed); AttachAndCreateSpark(spark, item, meshIndex, offset, vel);
spark->flags = SP_POISON | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; spark->flags = SP_POISON | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
} }
} }
void ThrowPoison(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel, const Vector3& color)
{
ThrowPoison(itemNumber, bite.BoneID, bite.Position, vel, color);
}
void UpdateFireProgress() void UpdateFireProgress()
{ {
TriggerGlobalStaticFlame(); TriggerGlobalStaticFlame();
@ -1072,7 +1084,7 @@ void SomeSparkEffect(int x, int y, int z, int count)
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
spark->on = 1; spark->on = true;
spark->sR = 112; spark->sR = 112;
spark->sG = (GetRandomControl() & 0x1F) + -128; spark->sG = (GetRandomControl() & 0x1F) + -128;
spark->sB = (GetRandomControl() & 0x1F) + -128; spark->sB = (GetRandomControl() & 0x1F) + -128;
@ -1109,9 +1121,9 @@ void TriggerUnderwaterExplosion(ItemInfo* item, int flag)
TriggerExplosionBubbles(x, y, z, item->RoomNumber); TriggerExplosionBubbles(x, y, z, item->RoomNumber);
TriggerExplosionSparks(x, y, z, 2, -1, 1, item->RoomNumber); TriggerExplosionSparks(x, y, z, 2, -1, 1, item->RoomNumber);
int wh = GetWaterHeight(x, y, z, item->RoomNumber); int waterHeight = GetWaterHeight(x, y, z, item->RoomNumber);
if (wh != NO_HEIGHT) if (waterHeight != NO_HEIGHT)
SomeSparkEffect(x, wh, z, 8); SomeSparkEffect(x, waterHeight, z, 8);
} }
else else
{ {
@ -1156,8 +1168,8 @@ void ExplodeVehicle(ItemInfo* laraItem, ItemInfo* vehicle)
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
ExplodingDeath(lara->Vehicle, BODY_EXPLODE | BODY_STONE_SOUND); ExplodingDeath(lara->Context.Vehicle, BODY_EXPLODE | BODY_STONE_SOUND);
KillItem(lara->Vehicle); KillItem(lara->Context.Vehicle);
vehicle->Status = ITEM_DEACTIVATED; vehicle->Status = ITEM_DEACTIVATED;
SoundEffect(SFX_TR4_EXPLOSION1, &laraItem->Pose); SoundEffect(SFX_TR4_EXPLOSION1, &laraItem->Pose);
SoundEffect(SFX_TR4_EXPLOSION2, &laraItem->Pose); SoundEffect(SFX_TR4_EXPLOSION2, &laraItem->Pose);
@ -1424,7 +1436,7 @@ void TriggerExplosionBubble(int x, int y, int z, short roomNumber)
spark->flags = 2058; spark->flags = 2058;
spark->scalar = 3; spark->scalar = 3;
spark->gravity = 0; spark->gravity = 0;
spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + 13; spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_BUBBLES;
spark->maxYvel = 0; spark->maxYvel = 0;
int size = (GetRandomControl() & 7) + 63; int size = (GetRandomControl() & 7) + 63;
spark->sSize = size >> 1; spark->sSize = size >> 1;

View file

@ -5,6 +5,7 @@
#include "Renderer/Renderer11Enums.h" #include "Renderer/Renderer11Enums.h"
enum class LaraWeaponType; enum class LaraWeaponType;
struct CreatureBiteInfo;
struct ItemInfo; struct ItemInfo;
enum BodyPartFlags enum BodyPartFlags
@ -232,9 +233,11 @@ int GetFreeFireSpark();
void TriggerGlobalStaticFlame(); void TriggerGlobalStaticFlame();
void TriggerGlobalFireSmoke(); void TriggerGlobalFireSmoke();
void TriggerGlobalFireFlame(); void TriggerGlobalFireFlame();
void TriggerPilotFlame(int itemNum, int nodeIndex); void TriggerPilotFlame(int itemNumber, int nodeIndex);
void ThrowFire(int itemNum, int meshIndex, Vector3i offset, Vector3i speed); void ThrowFire(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel);
void ThrowPoison(int itemNum, int meshIndex, Vector3i offset, Vector3i speed, Vector3 color); 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 UpdateFireProgress(); void UpdateFireProgress();
void ClearFires(); void ClearFires();
void AddFire(int x, int y, int z, short roomNum, float size, short fade); void AddFire(int x, int y, int z, short roomNum, float size, short fade);

View file

@ -7,9 +7,9 @@
#include "Game/effects/Ripple.h" #include "Game/effects/Ripple.h"
#include "Game/effects/tomb4fx.h" #include "Game/effects/tomb4fx.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h"
#include "Math/Random.h" #include "Math/Random.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/setup.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "ScriptInterfaceLevel.h" #include "ScriptInterfaceLevel.h"

View file

@ -35,6 +35,7 @@ namespace TEN::Gui
constexpr int LINE_HEIGHT = 25; constexpr int LINE_HEIGHT = 25;
constexpr int PHD_CENTER_X = SCREEN_SPACE_RES.x / 2; constexpr int PHD_CENTER_X = SCREEN_SPACE_RES.x / 2;
constexpr int PHD_CENTER_Y = SCREEN_SPACE_RES.y / 2; constexpr int PHD_CENTER_Y = SCREEN_SPACE_RES.y / 2;
constexpr int OBJLIST_SPACING = PHD_CENTER_X / 2;
constexpr int VOLUME_MAX = 100; constexpr int VOLUME_MAX = 100;
@ -86,6 +87,9 @@ namespace TEN::Gui
bool GuiController::GuiIsPulsed(ActionID actionID) const bool GuiController::GuiIsPulsed(ActionID actionID) const
{ {
constexpr auto DELAY = 0.1f;
constexpr auto INITIAL_DELAY = 0.4f;
auto oppositeAction = In::None; auto oppositeAction = In::None;
switch (actionID) switch (actionID)
{ {
@ -107,7 +111,7 @@ namespace TEN::Gui
} }
bool isActionLocked = (oppositeAction == In::None) ? false : IsHeld(oppositeAction); bool isActionLocked = (oppositeAction == In::None) ? false : IsHeld(oppositeAction);
return (IsPulsed(actionID, 0.1f, 0.4f) && !isActionLocked); return (IsPulsed(actionID, DELAY, INITIAL_DELAY) && !isActionLocked);
} }
bool GuiController::GuiIsSelected() const bool GuiController::GuiIsSelected() const
@ -122,16 +126,13 @@ namespace TEN::Gui
bool GuiController::CanSelect() const bool GuiController::CanSelect() const
{ {
// Holding Deselect safely cancels input.
if (IsHeld(In::Deselect)) if (IsHeld(In::Deselect))
return false; return false;
if (GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Deselect) && // Avoid Action release interference when entering inventory.
GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Save) && if (GetActionTimeActive(In::Action) < TimeInMenu)
GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Load) &&
GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Pause))
{
return true; return true;
}
return false; return false;
} }
@ -178,8 +179,12 @@ namespace TEN::Gui
void GuiController::SetInventoryMode(InventoryMode mode) void GuiController::SetInventoryMode(InventoryMode mode)
{ {
if (mode != InvMode)
{
TimeInMenu = 0.0f;
InvMode = mode; InvMode = mode;
} }
}
void GuiController::SetInventoryItemChosen(int number) void GuiController::SetInventoryItemChosen(int number)
{ {
@ -228,6 +233,8 @@ namespace TEN::Gui
static int selectedOptionBackup; static int selectedOptionBackup;
auto inventoryResult = InventoryResult::None; auto inventoryResult = InventoryResult::None;
TimeInMenu++;
// Stuff for credits goes here! // Stuff for credits goes here!
switch (MenuToDisplay) switch (MenuToDisplay)
@ -540,7 +547,7 @@ namespace TEN::Gui
void GuiController::HandleControlSettingsInput(ItemInfo* item, bool fromPauseMenu) void GuiController::HandleControlSettingsInput(ItemInfo* item, bool fromPauseMenu)
{ {
static const int numControlSettingsOptions = KEY_COUNT + 1; static const int numControlSettingsOptions = KEY_COUNT + 2;
OptionCount = numControlSettingsOptions; OptionCount = numControlSettingsOptions;
CurrentSettings.WaitingForKey = false; CurrentSettings.WaitingForKey = false;
@ -554,7 +561,7 @@ namespace TEN::Gui
} }
if (GuiIsSelected() && if (GuiIsSelected() &&
SelectedOption <= (numControlSettingsOptions - 2)) SelectedOption <= (numControlSettingsOptions - 3))
{ {
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always); SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
CurrentSettings.WaitingForKey = true; CurrentSettings.WaitingForKey = true;
@ -635,6 +642,14 @@ namespace TEN::Gui
if (GuiIsSelected()) if (GuiIsSelected())
{ {
// Defaults.
if (SelectedOption == (OptionCount - 2))
{
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
ApplyDefaultBindings();
return;
}
// Apply. // Apply.
if (SelectedOption == (OptionCount - 1)) if (SelectedOption == (OptionCount - 1))
{ {
@ -888,6 +903,7 @@ namespace TEN::Gui
static const int numStatisticsOptions = 0; static const int numStatisticsOptions = 0;
static const int numOptionsOptions = 2; static const int numOptionsOptions = 2;
TimeInMenu++;
UpdateInputActions(item); UpdateInputActions(item);
switch (MenuToDisplay) switch (MenuToDisplay)
@ -946,7 +962,7 @@ namespace TEN::Gui
{ {
if (MenuToDisplay == Menu::Pause) if (MenuToDisplay == Menu::Pause)
{ {
InvMode = InventoryMode::None; SetInventoryMode(InventoryMode::None);
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always); SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
return InventoryResult::None; return InventoryResult::None;
} }
@ -979,7 +995,7 @@ namespace TEN::Gui
break; break;
case PauseMenuOption::ExitToTitle: case PauseMenuOption::ExitToTitle:
InvMode = InventoryMode::None; SetInventoryMode(InventoryMode::None);
return InventoryResult::ExitToTitle; return InventoryResult::ExitToTitle;
break; break;
} }
@ -1593,7 +1609,7 @@ namespace TEN::Gui
Rings[(int)RingTypes::Ammo]->RingActive = false; Rings[(int)RingTypes::Ammo]->RingActive = false;
} }
void GuiController::InitialiseInventory(ItemInfo* item) void GuiController::InitializeInventory(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
@ -1603,14 +1619,22 @@ namespace TEN::Gui
UseItem = false; UseItem = false;
if (lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[0].HasInfinite()) if (lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[0].HasInfinite())
{
Ammo.AmountShotGunAmmo1 = -1; Ammo.AmountShotGunAmmo1 = -1;
}
else else
{
Ammo.AmountShotGunAmmo1 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[0].GetCount() / 6; Ammo.AmountShotGunAmmo1 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[0].GetCount() / 6;
}
if (lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[1].HasInfinite()) if (lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[1].HasInfinite())
{
Ammo.AmountShotGunAmmo2 = -1; Ammo.AmountShotGunAmmo2 = -1;
}
else else
{
Ammo.AmountShotGunAmmo2 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[1].GetCount() / 6; Ammo.AmountShotGunAmmo2 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[1].GetCount() / 6;
}
Ammo.AmountShotGunAmmo1 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo1].HasInfinite() ? -1 : lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo1].GetCount(); Ammo.AmountShotGunAmmo1 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo1].HasInfinite() ? -1 : lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo1].GetCount();
Ammo.AmountShotGunAmmo2 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo2].HasInfinite() ? -1 : lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo2].GetCount(); Ammo.AmountShotGunAmmo2 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo2].HasInfinite() ? -1 : lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[(int)WeaponAmmoType::Ammo2].GetCount();
@ -1633,7 +1657,9 @@ namespace TEN::Gui
if (LastInvItem != NO_ITEM) if (LastInvItem != NO_ITEM)
{ {
if (IsItemInInventory(LastInvItem)) if (IsItemInInventory(LastInvItem))
{
SetupObjectListStartPosition(LastInvItem); SetupObjectListStartPosition(LastInvItem);
}
else else
{ {
if (LastInvItem >= INV_OBJECT_SMALL_WATERSKIN_EMPTY && LastInvItem <= INV_OBJECT_SMALL_WATERSKIN_3L) if (LastInvItem >= INV_OBJECT_SMALL_WATERSKIN_EMPTY && LastInvItem <= INV_OBJECT_SMALL_WATERSKIN_3L)
@ -1820,7 +1846,7 @@ namespace TEN::Gui
ClearAllActions(); ClearAllActions();
ActionMap[(int)In::Flare].Update(1.0f); ActionMap[(int)In::Flare].Update(1.0f);
HandleWeapon(item); HandleWeapon(*item);
ClearAllActions(); ClearAllActions();
} }
@ -2310,20 +2336,20 @@ namespace TEN::Gui
case MenuType::Load: case MenuType::Load:
// fill_up_savegames_array // Maybe not? // fill_up_savegames_array // Maybe not?
InvMode = InventoryMode::Load; SetInventoryMode(InventoryMode::Load);
break; break;
case MenuType::Save: case MenuType::Save:
// fill_up_savegames_array // fill_up_savegames_array
InvMode = InventoryMode::Save; SetInventoryMode(InventoryMode::Save);
break; break;
case MenuType::Examine: case MenuType::Examine:
InvMode = InventoryMode::Examine; SetInventoryMode(InventoryMode::Examine);
break; break;
case MenuType::Statistics: case MenuType::Statistics:
InvMode = InventoryMode::Statistics; SetInventoryMode(InventoryMode::Statistics);
break; break;
case MenuType::Ammo1: case MenuType::Ammo1:
@ -2355,7 +2381,7 @@ namespace TEN::Gui
break; break;
case MenuType::Diary: case MenuType::Diary:
InvMode = InventoryMode::Diary; SetInventoryMode(InventoryMode::Diary);
lara->Inventory.Diary.CurrentPage = 1; lara->Inventory.Diary.CurrentPage = 1;
break; break;
} }
@ -2918,9 +2944,9 @@ namespace TEN::Gui
g_Renderer.DumpGameScene(); g_Renderer.DumpGameScene();
if (resetMode) if (resetMode)
InvMode = InventoryMode::InGame; SetInventoryMode(InventoryMode::InGame);
InitialiseInventory(item); InitializeInventory(item);
Camera.numberFrames = 2; Camera.numberFrames = 2;
bool exitLoop = false; bool exitLoop = false;
@ -2929,10 +2955,10 @@ namespace TEN::Gui
if (ThreadEnded) if (ThreadEnded)
return false; return false;
OBJLIST_SPACING = PHD_CENTER_X / 2; TimeInMenu++;
GameTimer++;
UpdateInputActions(item); UpdateInputActions(item);
GameTimer++;
if (IsClicked(In::Option)) if (IsClicked(In::Option))
{ {
@ -2973,7 +2999,7 @@ namespace TEN::Gui
exitLoop = !resetMode; exitLoop = !resetMode;
if (resetMode) if (resetMode)
InvMode = InventoryMode::InGame; SetInventoryMode(InventoryMode::InGame);
break; break;
@ -2988,7 +3014,7 @@ namespace TEN::Gui
{ {
exitLoop = !resetMode; exitLoop = !resetMode;
if (resetMode) if (resetMode)
InvMode = InventoryMode::InGame; SetInventoryMode(InventoryMode::InGame);
} }
break; break;
@ -3011,30 +3037,30 @@ namespace TEN::Gui
AlterFOV(LastFOV); AlterFOV(LastFOV);
lara->Inventory.IsBusy = lara->Inventory.OldBusy; lara->Inventory.IsBusy = lara->Inventory.OldBusy;
InvMode = InventoryMode::None; SetInventoryMode(InventoryMode::None);
return doLoad; return doLoad;
} }
void GuiController::DoStatisticsMode() void GuiController::DoStatisticsMode()
{ {
InvMode = InventoryMode::Statistics; SetInventoryMode(InventoryMode::Statistics);
if (GuiIsDeselected()) if (GuiIsDeselected())
{ {
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always); SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
InvMode = InventoryMode::InGame; SetInventoryMode(InventoryMode::InGame);
} }
} }
void GuiController::DoExamineMode() void GuiController::DoExamineMode()
{ {
this->InvMode = InventoryMode::Examine; SetInventoryMode(InventoryMode::Examine);
if (GuiIsDeselected()) if (GuiIsDeselected())
{ {
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always); SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
this->InvMode = InventoryMode::None; SetInventoryMode(InventoryMode::None);
} }
} }
@ -3056,7 +3082,7 @@ namespace TEN::Gui
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
this->InvMode = InventoryMode::Diary; SetInventoryMode(InventoryMode::Diary);
if (GuiIsPulsed(In::Right) && if (GuiIsPulsed(In::Right) &&
lara->Inventory.Diary.CurrentPage < lara->Inventory.Diary.NumPages) lara->Inventory.Diary.CurrentPage < lara->Inventory.Diary.NumPages)
@ -3075,7 +3101,7 @@ namespace TEN::Gui
if (GuiIsDeselected()) if (GuiIsDeselected())
{ {
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always); SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
InvMode = InventoryMode::None; SetInventoryMode(InventoryMode::None);
} }
} }

View file

@ -126,6 +126,7 @@ namespace TEN::Gui
int OptionCount; int OptionCount;
int SelectedSaveSlot; int SelectedSaveSlot;
float TimeInMenu = 0.0f;
SettingsData CurrentSettings; SettingsData CurrentSettings;
// Inventory variables // Inventory variables
@ -150,7 +151,6 @@ namespace TEN::Gui
short NormalRingFadeVal; short NormalRingFadeVal;
short NormalRingFadeDir; short NormalRingFadeDir;
unsigned char AmmoActive; unsigned char AmmoActive;
int OBJLIST_SPACING;
MenuOption CurrentOptions[3]; MenuOption CurrentOptions[3];
InventoryMode InvMode; InventoryMode InvMode;
int InventoryItemChosen; int InventoryItemChosen;
@ -199,7 +199,7 @@ namespace TEN::Gui
void HandleOptionsInput(); void HandleOptionsInput();
void BackupOptions(); void BackupOptions();
bool DoObjectsCombine(int objectNumber1, int objectNumber2); bool DoObjectsCombine(int objectNumber1, int objectNumber2);
void InitialiseInventory(ItemInfo* item); void InitializeInventory(ItemInfo* item);
void FillDisplayOptions(); void FillDisplayOptions();
bool IsItemCurrentlyCombinable(int objectNumber); bool IsItemCurrentlyCombinable(int objectNumber);
bool IsItemInInventory(int objectNumber); bool IsItemInInventory(int objectNumber);

View file

@ -0,0 +1,9 @@
#include "framework.h"
#include "Game/itemdata/creature_info.h"
#include "Game/items.h"
bool CreatureInfo::IsTargetAlive()
{
return ((Enemy != nullptr) && (Enemy->HitPoints > 0));
}

View file

@ -45,7 +45,7 @@ struct BoxNode
struct LOTInfo struct LOTInfo
{ {
bool Initialised = false; bool Initialized = false;
std::vector<BoxNode> Node = {}; std::vector<BoxNode> Node = {};
int Head = 0; int Head = 0;
@ -71,6 +71,62 @@ struct LOTInfo
bool CanMonkey = false; bool CanMonkey = false;
}; };
struct CreatureBiteInfo
{
Vector3i Position = Vector3i::Zero; // TODO: Change back to Vector3.
int BoneID = -1;
CreatureBiteInfo() {}
CreatureBiteInfo(const Vector3i& pos, int boneID)
{
Position = pos;
BoneID = boneID;
}
CreatureBiteInfo(int x, int y, int z, int boneID)
{
Position = Vector3i(x, y, z);
BoneID = boneID;
}
};
struct CreatureMuzzleFlashInfo
{
CreatureBiteInfo Bite = {};
int Delay = 0;
bool SwitchToMuzzle2 = false; // Changes muzzle object to ID_GUNFLASH2.
bool ApplyXRotation = true; // Applies X axis rotation for muzzleflash (required for creatures).
bool ApplyZRotation = true; // Applies Y axis rotation for muzzleflash (required for creatures).
bool UseSmoke = true; // Determines if CreatureAnimation calls TriggerGunSmokeParticles().
CreatureMuzzleFlashInfo() {}
CreatureMuzzleFlashInfo(const Vector3i& pos, int boneID, int delay, bool changeToMuzzle2 = false)
{
Bite = CreatureBiteInfo(pos, boneID);
Delay = delay;
SwitchToMuzzle2 = changeToMuzzle2;
}
CreatureMuzzleFlashInfo(const CreatureBiteInfo& bite, int delay, bool changeToMuzzle2 = false)
{
Bite = bite;
Delay = delay;
SwitchToMuzzle2 = changeToMuzzle2;
}
CreatureMuzzleFlashInfo(const CreatureBiteInfo& bite, int delay, bool changeToMuzzle2 = false, bool applyXRot = true, bool applyZRot = true)
{
Bite = bite;
Delay = delay;
SwitchToMuzzle2 = changeToMuzzle2;
ApplyXRotation = applyXRot;
ApplyZRotation = applyZRot;
}
};
struct CreatureInfo struct CreatureInfo
{ {
int ItemNumber = -1; int ItemNumber = -1;
@ -96,11 +152,13 @@ struct CreatureInfo
bool MonkeySwingAhead = false; bool MonkeySwingAhead = false;
bool ReachedGoal = false; bool ReachedGoal = false;
short FiredWeapon = 0; CreatureMuzzleFlashInfo MuzzleFlash[2];
short Tosspad = 0; short Tosspad = 0;
short LocationAI = 0; short LocationAI = 0;
short Flags = 0; short Flags = 0;
bool IsTargetAlive();
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION #ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
CreatureAIPriority Priority = CreatureAIPriority::None; CreatureAIPriority Priority = CreatureAIPriority::None;
size_t FramesSinceLOTUpdate = 0; size_t FramesSinceLOTUpdate = 0;

View file

@ -1,4 +1,4 @@
#include "framework.h" #include "framework.h"
#include "Game/itemdata/itemdata.h" #include "Game/itemdata/itemdata.h"
ITEM_DATA::ITEM_DATA() : data(nullptr) {} ItemData::ItemData() : data(nullptr) {}

View file

@ -20,19 +20,20 @@
#include "Objects/TR4/Vehicles/jeep_info.h" #include "Objects/TR4/Vehicles/jeep_info.h"
#include "Objects/TR4/Vehicles/motorbike_info.h" #include "Objects/TR4/Vehicles/motorbike_info.h"
#include "Objects/TR5/Entity/tr5_laserhead_info.h" #include "Objects/TR5/Entity/tr5_laserhead_info.h"
#include "Objects/TR5/Light/tr5_light_info.h"
#include "Objects/TR5/Object/tr5_pushableblock_info.h" #include "Objects/TR5/Object/tr5_pushableblock_info.h"
template<class... Ts> struct visitor : Ts... { using Ts::operator()...; }; template<class... Ts> struct visitor : Ts... { using Ts::operator()...; };
template<class... Ts> visitor(Ts...)->visitor<Ts...>; // line not needed in C++20... template<class... Ts> visitor(Ts...)->visitor<Ts...>; // TODO: Line not needed in C++20.
using namespace TEN::Entities::TR4;
using namespace TEN::Entities::Creatures::TR5; using namespace TEN::Entities::Creatures::TR5;
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
using namespace TEN::Entities::TR4;
using namespace TEN::Entities::Vehicles; using namespace TEN::Entities::Vehicles;
struct ItemInfo; struct ItemInfo;
class ITEM_DATA class ItemData
{ {
std::variant< std::variant<
std::nullptr_t, std::nullptr_t,
@ -67,16 +68,17 @@ class ITEM_DATA
UPVInfo, UPVInfo,
SpeedboatInfo, SpeedboatInfo,
RubberBoatInfo, RubberBoatInfo,
MinecartInfo MinecartInfo,
ElectricalLightInfo
> data; > data;
public: public:
ITEM_DATA(); ItemData();
template<typename D> template<typename D>
ITEM_DATA(D&& type) : data(std::move(type)) {} ItemData(D&& type) : data(std::move(type)) {}
// conversion operators to keep original syntax! // Conversion operators to keep original syntax.
// TODO: should be removed later and // TODO: Should be removed later and use polymorphism instead.
template<typename T> template<typename T>
operator T* () operator T* ()
{ {
@ -86,7 +88,7 @@ class ITEM_DATA
return &ref; return &ref;
} }
throw std::runtime_error("ITEM_DATA does not hold the requested type!\n The code set the ITEM_DATA to a different type than the type that was attempted to read"); throw std::runtime_error("Attempted to read ItemData as wrong type.");
} }
template<typename T> template<typename T>
@ -98,33 +100,33 @@ class ITEM_DATA
return ref; return ref;
} }
throw std::runtime_error("ITEM_DATA does not hold the requested type!\n The code set the ITEM_DATA to a different type than the type that was attempted to read"); throw std::runtime_error("Attempted to read ItemData as wrong type.");
} }
/* Uncommented, we want to store pointers to global data, too (LaraInfo for example) /* Uncommented, we want to store pointers to global data too (LaraInfo for example).
template<typename T> template<typename T>
ITEM_DATA& operator=(T* newData) ItemData& operator =(T* newData)
{ {
data = *newData; data = *newData;
return *this; return *this;
} }
*/ */
ITEM_DATA& operator=(std::nullptr_t null) ItemData& operator =(std::nullptr_t null)
{ {
data = nullptr; data = nullptr;
return *this; return *this;
} }
template<typename T> template<typename T>
ITEM_DATA& operator=(T& newData) ItemData& operator =(T& newData)
{ {
data = newData; data = newData;
return *this; return *this;
} }
template<typename T> template<typename T>
ITEM_DATA& operator=(T&& newData) ItemData& operator =(T&& newData)
{ {
data = std::move(newData); data = std::move(newData);
return *this; return *this;

View file

@ -10,6 +10,7 @@
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/savegame.h" #include "Game/savegame.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/ScriptInterfaceObjectsHandler.h" #include "Objects/ScriptInterfaceObjectsHandler.h"
#include "Scripting/Include/ScriptInterfaceGame.h" #include "Scripting/Include/ScriptInterfaceGame.h"
@ -17,12 +18,11 @@
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Scripting/Internal/TEN/Objects/ObjectIDs.h" #include "Scripting/Internal/TEN/Objects/ObjectIDs.h"
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
using namespace TEN::Effects::Items; using namespace TEN::Effects::Items;
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math; using namespace TEN::Math;
@ -144,10 +144,10 @@ bool ItemInfo::IsCreature() const
void ItemInfo::ResetModelToDefault() void ItemInfo::ResetModelToDefault()
{ {
this->Model.BaseMesh = Objects[this->ObjectNumber].meshIndex; Model.BaseMesh = Objects[ObjectNumber].meshIndex;
for (int i = 0; i < this->Model.MeshIndex.size(); i++) for (int i = 0; i < Model.MeshIndex.size(); i++)
this->Model.MeshIndex[i] = this->Model.BaseMesh + i; Model.MeshIndex[i] = Model.BaseMesh + i;
} }
bool TestState(int refState, const vector<int>& stateList) bool TestState(int refState, const vector<int>& stateList)
@ -196,7 +196,9 @@ void KillItem(short const itemNumber)
item->Active = false; item->Active = false;
if (NextItemActive == itemNumber) if (NextItemActive == itemNumber)
{
NextItemActive = item->NextActive; NextItemActive = item->NextActive;
}
else else
{ {
short linkNumber; short linkNumber;
@ -213,7 +215,9 @@ void KillItem(short const itemNumber)
if (item->RoomNumber != NO_ROOM) if (item->RoomNumber != NO_ROOM)
{ {
if (g_Level.Rooms[item->RoomNumber].itemNumber == itemNumber) if (g_Level.Rooms[item->RoomNumber].itemNumber == itemNumber)
{
g_Level.Rooms[item->RoomNumber].itemNumber = item->NextItem; g_Level.Rooms[item->RoomNumber].itemNumber = item->NextItem;
}
else else
{ {
short linkNumber; short linkNumber;
@ -229,7 +233,7 @@ void KillItem(short const itemNumber)
} }
if (item == Lara.TargetEntity) if (item == Lara.TargetEntity)
Lara.TargetEntity = NULL; Lara.TargetEntity = nullptr;
if (Objects[item->ObjectNumber].floor != nullptr) if (Objects[item->ObjectNumber].floor != nullptr)
UpdateBridgeItem(itemNumber, true); UpdateBridgeItem(itemNumber, true);
@ -242,9 +246,11 @@ void KillItem(short const itemNumber)
NextItemFree = itemNumber; NextItemFree = itemNumber;
} }
else else
{
item->Flags |= IFLAG_KILLED; item->Flags |= IFLAG_KILLED;
} }
} }
}
void RemoveAllItemsInRoom(short roomNumber, short objectNumber) void RemoveAllItemsInRoom(short roomNumber, short objectNumber)
{ {
@ -422,7 +428,7 @@ short CreateNewEffect(short roomNumber)
return fxNumber; return fxNumber;
} }
void InitialiseFXArray(int allocateMemory) void InitializeFXArray(int allocateMemory)
{ {
NextFxActive = NO_ITEM; NextFxActive = NO_ITEM;
NextFxFree = 0; NextFxFree = 0;
@ -482,19 +488,13 @@ void RemoveActiveItem(short itemNumber, bool killed)
} }
} }
void InitialiseItem(short itemNumber) void InitializeItem(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex; SetAnimation(item, 0);
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.RequiredState = NO_STATE; item->Animation.RequiredState = NO_STATE;
item->Animation.TargetState = g_Level.Anims[item->Animation.AnimNumber].ActiveState; item->Animation.Velocity = Vector3::Zero;
item->Animation.ActiveState = g_Level.Anims[item->Animation.AnimNumber].ActiveState;
item->Animation.Velocity.y = 0;
item->Animation.Velocity.z = 0;
for (int i = 0; i < NUM_ITEM_FLAGS; i++) for (int i = 0; i < NUM_ITEM_FLAGS; i++)
item->ItemFlags[i] = 0; item->ItemFlags[i] = 0;
@ -516,7 +516,9 @@ void InitialiseItem(short itemNumber)
item->MeshBits = 1; item->MeshBits = 1;
} }
else else
{
item->MeshBits = ALL_JOINT_BITS; item->MeshBits = ALL_JOINT_BITS;
}
item->TouchBits = NO_JOINT_BITS; item->TouchBits = NO_JOINT_BITS;
item->AfterDeath = 0; item->AfterDeath = 0;
@ -527,7 +529,9 @@ void InitialiseItem(short itemNumber)
item->Status = ITEM_INVISIBLE; item->Status = ITEM_INVISIBLE;
} }
else if (Objects[item->ObjectNumber].intelligent) else if (Objects[item->ObjectNumber].intelligent)
{
item->Status = ITEM_INVISIBLE; item->Status = ITEM_INVISIBLE;
}
if ((item->Flags & IFLAG_ACTIVATION_MASK) == IFLAG_ACTIVATION_MASK) if ((item->Flags & IFLAG_ACTIVATION_MASK) == IFLAG_ACTIVATION_MASK)
{ {
@ -560,8 +564,8 @@ void InitialiseItem(short itemNumber)
item->Model.MeshIndex.clear(); item->Model.MeshIndex.clear();
} }
if (Objects[item->ObjectNumber].initialise != nullptr) if (Objects[item->ObjectNumber].Initialize != nullptr)
Objects[item->ObjectNumber].initialise(itemNumber); Objects[item->ObjectNumber].Initialize(itemNumber);
} }
short CreateItem() short CreateItem()
@ -576,7 +580,7 @@ short CreateItem()
return itemNumber; return itemNumber;
} }
void InitialiseItemArray(int totalItem) void InitializeItemArray(int totalItem)
{ {
g_Level.Items.clear(); g_Level.Items.clear();
g_Level.Items.resize(totalItem); g_Level.Items.resize(totalItem);
@ -612,7 +616,7 @@ short SpawnItem(ItemInfo* item, GAME_OBJECT_ID objectNumber)
spawn->RoomNumber = item->RoomNumber; spawn->RoomNumber = item->RoomNumber;
memcpy(&spawn->Pose, &item->Pose, sizeof(Pose)); memcpy(&spawn->Pose, &item->Pose, sizeof(Pose));
InitialiseItem(itemNumber); InitializeItem(itemNumber);
spawn->Status = ITEM_NOT_ACTIVE; spawn->Status = ITEM_NOT_ACTIVE;
spawn->Model.Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f); spawn->Model.Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f);

View file

@ -5,14 +5,13 @@
#include "Game/animation.h" #include "Game/animation.h"
#include "Game/itemdata/itemdata.h" #include "Game/itemdata/itemdata.h"
#include "Objects/game_object_ids.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/newtypes.h" #include "Specific/newtypes.h"
#include "Specific/BitField.h" #include "Specific/BitField.h"
using namespace TEN::Utils; using namespace TEN::Utils;
enum GAME_OBJECT_ID : short;
constexpr auto NO_ITEM = -1; constexpr auto NO_ITEM = -1;
constexpr auto NOT_TARGETABLE = -16384; constexpr auto NOT_TARGETABLE = -16384;
@ -66,6 +65,8 @@ enum class EffectType
struct EntityAnimationData struct EntityAnimationData
{ {
GAME_OBJECT_ID AnimObjectID = ID_NO_OBJECT;
int AnimNumber = 0; // g_Level.Anims index. int AnimNumber = 0; // g_Level.Anims index.
int FrameNumber = 0; // g_Level.Frames index. int FrameNumber = 0; // g_Level.Frames index.
int ActiveState = 0; int ActiveState = 0;
@ -106,8 +107,8 @@ struct EntityEffectData
// TODO: We need to find good "default states" for a lot of these. -- squidshire 25/05/2022 // TODO: We need to find good "default states" for a lot of these. -- squidshire 25/05/2022
struct ItemInfo struct ItemInfo
{ {
GAME_OBJECT_ID ObjectNumber; GAME_OBJECT_ID ObjectNumber = ID_NO_OBJECT; // ObjectID
std::string Name; std::string Name = {};
int Status; // ItemStatus enum. int Status; // ItemStatus enum.
bool Active; bool Active;
@ -116,7 +117,7 @@ struct ItemInfo
short NextItem; short NextItem;
short NextActive; short NextActive;
ITEM_DATA Data; ItemData Data;
EntityAnimationData Animation; EntityAnimationData Animation;
EntityCallbackData Callbacks; EntityCallbackData Callbacks;
EntityModelData Model; EntityModelData Model;
@ -178,11 +179,11 @@ short CreateItem();
void RemoveAllItemsInRoom(short roomNumber, short objectNumber); void RemoveAllItemsInRoom(short roomNumber, short objectNumber);
void RemoveActiveItem(short itemNumber, bool killed = true); void RemoveActiveItem(short itemNumber, bool killed = true);
void RemoveDrawnItem(short itemNumber); void RemoveDrawnItem(short itemNumber);
void InitialiseFXArray(int allocateMemory); void InitializeFXArray(int allocateMemory);
short CreateNewEffect(short roomNumber); short CreateNewEffect(short roomNumber);
void KillEffect(short fxNumber); void KillEffect(short fxNumber);
void InitialiseItem(short itemNumber); void InitializeItem(short itemNumber);
void InitialiseItemArray(int totalItems); void InitializeItemArray(int totalItems);
void KillItem(short itemNumber); void KillItem(short itemNumber);
bool UpdateItemRoom(short itemNumber); bool UpdateItemRoom(short itemNumber);
void UpdateAllItems(); void UpdateAllItems();
@ -195,3 +196,4 @@ int FindItem(ItemInfo* item);
void DoDamage(ItemInfo* item, int damage); void DoDamage(ItemInfo* item, int damage);
void DoItemHit(ItemInfo* target, int damage, bool isExplosive, bool allowBurn = true); void DoItemHit(ItemInfo* target, int damage, bool isExplosive, bool allowBurn = true);
void DefaultItemHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex); void DefaultItemHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex);
short SpawnItem(ItemInfo* item, GAME_OBJECT_ID objectNumber);

View file

@ -5,7 +5,7 @@
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/itemdata/creature_info.h" #include "Game/itemdata/creature_info.h"
#include "Game/items.h" #include "Game/items.h"
#include "Specific/setup.h" #include "Game/Setup.h"
#include "Specific/level.h" #include "Specific/level.h"
using std::vector; using std::vector;

View file

@ -10,10 +10,10 @@
#include "Game/effects/Bubble.h" #include "Game/effects/Bubble.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/items.h" #include "Game/items.h"
#include "Game/Setup.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Bubble; using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::Explosion; using namespace TEN::Effects::Explosion;
@ -115,7 +115,7 @@ void ControlMissile(short fxNumber)
EffectNewRoom(fxNumber, pointColl.RoomNumber); EffectNewRoom(fxNumber, pointColl.RoomNumber);
if (fx.objectNumber == ID_KNIFETHROWER_KNIFE) if (fx.objectNumber == ID_KNIFETHROWER_KNIFE)
fx.pos.Orientation.z += ANGLE(3.0f); // Update knife rotation over time. fx.pos.Orientation.z += ANGLE(30.0f); // Update knife rotation over time.
switch (fx.objectNumber) switch (fx.objectNumber)
{ {

View file

@ -12,7 +12,7 @@
#include "Game/misc.h" #include "Game/misc.h"
#include "Sound/sound.h" #include "Sound/sound.h"
bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, int damage) bool ShotLara(ItemInfo* item, AI_INFO* AI, const CreatureBiteInfo& gun, short extraRotation, int damage)
{ {
auto* creature = GetCreatureInfo(item); auto* creature = GetCreatureInfo(item);
auto* enemy = creature->Enemy; auto* enemy = creature->Enemy;
@ -79,13 +79,14 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, in
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber) short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber)
{ {
// TODO: Remove -128 and fix ricochet effect going on floor. -- TokyoSU 2023.04.28
auto pos = GameVector( auto pos = GameVector(
LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF, LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->Floor, LaraItem->Floor - 128,
LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF, LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->RoomNumber); LaraItem->RoomNumber);
Ricochet(Pose(pos.x, pos.y, pos.z)); Ricochet(Pose(pos.ToVector3i()));
return GunShot(x, y, z, velocity, yRot, roomNumber); return GunShot(x, y, z, velocity, yRot, roomNumber);
} }
@ -101,11 +102,11 @@ short GunShot(int x, int y, int z, short velocity, short yRot, short roomNumber)
return -1; return -1;
} }
bool Targetable(ItemInfo* item, AI_INFO* AI) bool Targetable(ItemInfo* item, AI_INFO* ai)
{ {
// Discard it entity is not a creature (only creatures can use Targetable()) // Discard it entity is not a creature (only creatures can use Targetable())
// or if the target is not visible. // or if the target is not visible.
if (!item->IsCreature() || !AI->ahead || AI->distance >= SQUARE(MAX_VISIBILITY_DISTANCE)) if (!item->IsCreature() || !ai->ahead || ai->distance >= SQUARE(MAX_VISIBILITY_DISTANCE))
return false; return false;
auto* creature = GetCreatureInfo(item); auto* creature = GetCreatureInfo(item);
@ -134,9 +135,9 @@ bool Targetable(ItemInfo* item, AI_INFO* AI)
return LOS(&origin, &target); return LOS(&origin, &target);
} }
bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngleInDegrees) bool TargetVisible(ItemInfo* item, AI_INFO* ai, float maxAngleInDegrees)
{ {
if (!item->IsCreature() || AI->distance >= SQUARE(MAX_VISIBILITY_DISTANCE)) if (!item->IsCreature() || ai->distance >= SQUARE(MAX_VISIBILITY_DISTANCE))
return false; return false;
// Check just in case. // Check just in case.
@ -148,7 +149,7 @@ bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngleInDegrees)
if (enemy == nullptr || enemy->HitPoints == 0) if (enemy == nullptr || enemy->HitPoints == 0)
return false; return false;
short angle = AI->angle - creature->JointRotation[2]; short angle = ai->angle - creature->JointRotation[2];
if (angle > ANGLE(-maxAngleInDegrees) && angle < ANGLE(maxAngleInDegrees)) if (angle > ANGLE(-maxAngleInDegrees) && angle < ANGLE(maxAngleInDegrees))
{ {
const auto& bounds = GetBestFrame(*enemy).BoundingBox; const auto& bounds = GetBestFrame(*enemy).BoundingBox;

View file

@ -1,11 +1,13 @@
#pragma once #pragma once
#include "Game/control/box.h" #include "Game/control/box.h"
constexpr auto MAX_VISIBILITY_DISTANCE = SECTOR(8); struct CreatureBiteInfo;
bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, int damage); constexpr auto MAX_VISIBILITY_DISTANCE = BLOCK(8);
bool ShotLara(ItemInfo* item, AI_INFO* AI, const CreatureBiteInfo& gun, short extraRotation, int damage);
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber); short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber);
short GunHit(int x, int y, int z, short velocity, short yRot, short roomNumber); short GunHit(int x, int y, int z, short velocity, short yRot, short roomNumber);
short GunShot(int x, int y, int z, short velocity, short yRot, short roomNumber); short GunShot(int x, int y, int z, short velocity, short yRot, short roomNumber);
bool Targetable(ItemInfo* item, AI_INFO* AI); bool Targetable(ItemInfo* item, AI_INFO* ai);
bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngleInDegrees = 45.0f); bool TargetVisible(ItemInfo* item, AI_INFO* ai, float maxAngleInDegrees = 45.0f);

View file

@ -21,13 +21,13 @@
#include "Game/pickup/pickup_misc_items.h" #include "Game/pickup/pickup_misc_items.h"
#include "Game/pickup/pickup_weapon.h" #include "Game/pickup/pickup_weapon.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/Generic/Object/burning_torch.h" #include "Objects/Generic/Object/burning_torch.h"
#include "Objects/TR4/Object/tr4_clockwork_beetle.h" #include "Objects/TR4/Object/tr4_clockwork_beetle.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/Input/Input.h" #include "Specific/Input/Input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Math/Math.h"
#include "Specific/setup.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h" #include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
using namespace TEN::Entities::Generic; using namespace TEN::Entities::Generic;
@ -273,10 +273,10 @@ void DoPickup(ItemInfo* laraItem)
{ {
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
if (lara->InteractedItem == NO_ITEM) if (lara->Context.InteractedItem == NO_ITEM)
return; return;
short pickupItemNumber = lara->InteractedItem; short pickupItemNumber = lara->Context.InteractedItem;
auto* pickupItem = &g_Level.Items[pickupItemNumber]; auto* pickupItem = &g_Level.Items[pickupItemNumber];
if (!Objects[pickupItem->ObjectNumber].isPickup) if (!Objects[pickupItem->ObjectNumber].isPickup)
@ -292,7 +292,7 @@ void DoPickup(ItemInfo* laraItem)
KillItem(pickupItemNumber); KillItem(pickupItemNumber);
pickupItem->Pose.Orientation = prevOrient; pickupItem->Pose.Orientation = prevOrient;
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
return; return;
} }
else if (pickupItem->ObjectNumber == ID_FLARE_ITEM) else if (pickupItem->ObjectNumber == ID_FLARE_ITEM)
@ -301,25 +301,25 @@ void DoPickup(ItemInfo* laraItem)
{ {
lara->Control.Weapon.RequestGunType = LaraWeaponType::Flare; lara->Control.Weapon.RequestGunType = LaraWeaponType::Flare;
lara->Control.Weapon.GunType = LaraWeaponType::Flare; lara->Control.Weapon.GunType = LaraWeaponType::Flare;
InitialiseNewWeapon(laraItem); InitializeNewWeapon(*laraItem);
lara->Control.HandStatus = HandStatus::Special; lara->Control.HandStatus = HandStatus::Special;
lara->Flare.Life = int(pickupItem->Data) & 0x7FFF; lara->Flare.Life = int(pickupItem->Data) & 0x7FFF;
DrawFlareMeshes(laraItem); DrawFlareMeshes(*laraItem);
KillItem(pickupItemNumber); KillItem(pickupItemNumber);
pickupItem->Pose.Orientation = prevOrient; pickupItem->Pose.Orientation = prevOrient;
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
return; return;
} }
else if (laraItem->Animation.ActiveState == LS_PICKUP_FLARE) else if (laraItem->Animation.ActiveState == LS_PICKUP_FLARE)
{ {
lara->Control.Weapon.RequestGunType = LaraWeaponType::Flare; lara->Control.Weapon.RequestGunType = LaraWeaponType::Flare;
lara->Control.Weapon.GunType = LaraWeaponType::Flare; lara->Control.Weapon.GunType = LaraWeaponType::Flare;
InitialiseNewWeapon(laraItem); InitializeNewWeapon(*laraItem);
lara->Control.HandStatus = HandStatus::Special; lara->Control.HandStatus = HandStatus::Special;
lara->Flare.Life = int(pickupItem->Data) & 0x7FFF; lara->Flare.Life = int(pickupItem->Data) & 0x7FFF;
KillItem(pickupItemNumber); KillItem(pickupItemNumber);
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
return; return;
} }
} }
@ -330,8 +330,8 @@ void DoPickup(ItemInfo* laraItem)
{ {
if (g_GameFlow->IsMassPickupEnabled()) if (g_GameFlow->IsMassPickupEnabled())
{ {
CollectMultiplePickups(lara->InteractedItem); CollectMultiplePickups(lara->Context.InteractedItem);
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
return; return;
} }
@ -348,7 +348,7 @@ void DoPickup(ItemInfo* laraItem)
} }
pickupItem->Pose.Orientation = prevOrient; pickupItem->Pose.Orientation = prevOrient;
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
return; return;
} }
else else
@ -365,8 +365,8 @@ void DoPickup(ItemInfo* laraItem)
{ {
if (g_GameFlow->IsMassPickupEnabled()) if (g_GameFlow->IsMassPickupEnabled())
{ {
CollectMultiplePickups(lara->InteractedItem); CollectMultiplePickups(lara->Context.InteractedItem);
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
return; return;
} }
@ -389,13 +389,13 @@ void DoPickup(ItemInfo* laraItem)
pickupItem->Pose.Orientation = prevOrient; pickupItem->Pose.Orientation = prevOrient;
KillItem(pickupItemNumber); KillItem(pickupItemNumber);
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
return; return;
} }
} }
} }
lara->InteractedItem = NO_ITEM; lara->Context.InteractedItem = NO_ITEM;
} }
void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
@ -430,7 +430,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
item->ObjectNumber != ID_BURNING_TORCH_ITEM && item->ObjectNumber != ID_BURNING_TORCH_ITEM &&
laraItem->Animation.ActiveState == LS_UNDERWATER_IDLE && laraItem->Animation.ActiveState == LS_UNDERWATER_IDLE &&
lara->Control.HandStatus == HandStatus::Free && lara->Control.HandStatus == HandStatus::Free &&
TestLaraPosition(PickUpBoundsUW, item, laraItem) || lara->Control.IsMoving && lara->InteractedItem == itemNumber) TestLaraPosition(PickUpBoundsUW, item, laraItem) || lara->Control.IsMoving && lara->Context.InteractedItem == itemNumber)
{ {
if (TestLaraPosition(PickUpBoundsUW, item, laraItem)) if (TestLaraPosition(PickUpBoundsUW, item, laraItem))
{ {
@ -454,13 +454,13 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
} }
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
} }
else else
{ {
if (lara->Control.IsMoving) if (lara->Control.IsMoving)
{ {
if (lara->InteractedItem == itemNumber) if (lara->Context.InteractedItem == itemNumber)
{ {
lara->Control.IsMoving = false; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -486,7 +486,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{ {
if (!lara->Control.IsMoving) if (!lara->Control.IsMoving)
{ {
if (lara->InteractedItem == itemNumber) if (lara->Context.InteractedItem == itemNumber)
{ {
if (laraItem->Animation.ActiveState != LS_PICKUP && laraItem->Animation.ActiveState != LS_HOLE) if (laraItem->Animation.ActiveState != LS_PICKUP && laraItem->Animation.ActiveState != LS_HOLE)
{ {
@ -501,7 +501,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
} }
} }
if (lara->InteractedItem != itemNumber) if (lara->Context.InteractedItem != itemNumber)
{ {
item->Pose.Orientation = prevOrient; item->Pose.Orientation = prevOrient;
return; return;
@ -519,7 +519,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{ {
if (lara->Control.IsMoving) if (lara->Control.IsMoving)
{ {
if (lara->InteractedItem == itemNumber) if (lara->Context.InteractedItem == itemNumber)
{ {
lara->Control.IsMoving = false; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -536,7 +536,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
flag = true; flag = true;
} }
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
break; break;
// Pick up with crowbar. // Pick up with crowbar.
@ -551,7 +551,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
return; return;
} }
if (lara->InteractedItem == itemNumber) if (lara->Context.InteractedItem == itemNumber)
{ {
lara->Control.IsMoving = false; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -590,7 +590,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
flag = true; flag = true;
} }
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
break; break;
// Pick up from plinth. // Pick up from plinth.
@ -642,7 +642,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
flag = true; flag = true;
} }
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
break; break;
} }
@ -652,7 +652,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
return; return;
} }
if (lara->InteractedItem == itemNumber) if (lara->Context.InteractedItem == itemNumber)
{ {
lara->Control.IsMoving = false; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -679,7 +679,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
flag = true; flag = true;
} }
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
break; break;
// Pick up from ground. // Pick up from ground.
@ -692,7 +692,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
return; return;
} }
if (lara->InteractedItem == itemNumber) if (lara->Context.InteractedItem == itemNumber)
{ {
lara->Control.IsMoving = false; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -716,7 +716,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{ {
laraItem->Animation.AnimNumber = LA_CROUCH_PICKUP_FLARE; laraItem->Animation.AnimNumber = LA_CROUCH_PICKUP_FLARE;
laraItem->Animation.ActiveState = LS_PICKUP_FLARE; laraItem->Animation.ActiveState = LS_PICKUP_FLARE;
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
flag = true; flag = true;
break; break;
} }
@ -741,14 +741,14 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{ {
laraItem->Animation.TargetState = LS_PICKUP; laraItem->Animation.TargetState = LS_PICKUP;
} }
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
break; break;
} }
else else
{ {
if (!MoveLaraPosition(PickUpPosition, item, laraItem)) if (!MoveLaraPosition(PickUpPosition, item, laraItem))
{ {
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
break; break;
} }
@ -756,7 +756,7 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{ {
laraItem->Animation.AnimNumber = LA_PICKUP; laraItem->Animation.AnimNumber = LA_PICKUP;
laraItem->Animation.ActiveState = LS_PICKUP_FLARE; laraItem->Animation.ActiveState = LS_PICKUP_FLARE;
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
flag = true; flag = true;
break; break;
} }
@ -769,14 +769,14 @@ void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
} }
} }
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
flag = true; flag = true;
} }
if (flag) if (flag)
{ {
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
ResetLaraFlex(laraItem); ResetPlayerFlex(laraItem);
lara->Control.IsMoving = false; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
} }
@ -1071,7 +1071,7 @@ GameBoundingBox* FindPlinth(ItemInfo* item)
return &GetBestFrame(g_Level.Items[itemNumber]).BoundingBox; return &GetBestFrame(g_Level.Items[itemNumber]).BoundingBox;
} }
void InitialisePickup(short itemNumber) void InitializePickup(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
@ -1130,7 +1130,7 @@ void InitialisePickup(short itemNumber)
} }
} }
void InitialiseSearchObject(short itemNumber) void InitializeSearchObject(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
if (item->ObjectNumber == ID_SEARCH_OBJECT1) if (item->ObjectNumber == ID_SEARCH_OBJECT1)
@ -1183,7 +1183,7 @@ void SearchObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo*
laraItem->Animation.AnimNumber == LA_STAND_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE &&
lara->Control.HandStatus == HandStatus::Free && lara->Control.HandStatus == HandStatus::Free &&
((item->Status == ITEM_NOT_ACTIVE && item->ObjectNumber != ID_SEARCH_OBJECT4) || !item->ItemFlags[0])) || ((item->Status == ITEM_NOT_ACTIVE && item->ObjectNumber != ID_SEARCH_OBJECT4) || !item->ItemFlags[0])) ||
(lara->Control.IsMoving && lara->InteractedItem == itemNumber)) (lara->Control.IsMoving && lara->Context.InteractedItem == itemNumber))
{ {
auto bounds = GameBoundingBox(item); auto bounds = GameBoundingBox(item);
if (item->ObjectNumber != ID_SEARCH_OBJECT1) if (item->ObjectNumber != ID_SEARCH_OBJECT1)
@ -1205,7 +1205,7 @@ void SearchObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo*
{ {
if (MoveLaraPosition(SOPos, item, laraItem)) if (MoveLaraPosition(SOPos, item, laraItem))
{ {
ResetLaraFlex(laraItem); ResetPlayerFlex(laraItem);
laraItem->Animation.AnimNumber = SearchAnims[objectNumber]; laraItem->Animation.AnimNumber = SearchAnims[objectNumber];
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraItem->Animation.ActiveState = LS_MISC_CONTROL; laraItem->Animation.ActiveState = LS_MISC_CONTROL;
@ -1225,9 +1225,9 @@ void SearchObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo*
AnimateItem(item); AnimateItem(item);
} }
else else
lara->InteractedItem = itemNumber; lara->Context.InteractedItem = itemNumber;
} }
else if (lara->Control.IsMoving && lara->InteractedItem == itemNumber) else if (lara->Control.IsMoving && lara->Context.InteractedItem == itemNumber)
{ {
lara->Control.IsMoving = false; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -1363,7 +1363,7 @@ bool UseSpecialItem(ItemInfo* laraItem)
if (flag == 1) if (flag == 1)
{ {
if (itemIDToUse != ID_WATERSKIN1_3 && itemIDToUse != ID_WATERSKIN2_5 && (lara->WaterSurfaceDist < -SHALLOW_WATER_DEPTH)) if (itemIDToUse != ID_WATERSKIN1_3 && itemIDToUse != ID_WATERSKIN2_5 && (lara->Context.WaterSurfaceDist < -SHALLOW_WATER_DEPTH))
{ {
if (itemIDToUse < ID_WATERSKIN1_3) if (itemIDToUse < ID_WATERSKIN1_3)
lara->Inventory.SmallWaterskin = 4; lara->Inventory.SmallWaterskin = 4;

View file

@ -10,7 +10,7 @@ extern int NumRPickups;
extern short RPickups[16]; extern short RPickups[16];
extern Vector3i OldPickupPos; extern Vector3i OldPickupPos;
void InitialisePickup(short itemNumber); void InitializePickup(short itemNumber);
bool SetInventoryCount(GAME_OBJECT_ID objectID, int count); bool SetInventoryCount(GAME_OBJECT_ID objectID, int count);
void PickedUpObject(GAME_OBJECT_ID objectID, std::optional<int> count = std::nullopt); void PickedUpObject(GAME_OBJECT_ID objectID, std::optional<int> count = std::nullopt);
void RemoveObjectFromInventory(GAME_OBJECT_ID objectID, std::optional<int> count = std::nullopt); void RemoveObjectFromInventory(GAME_OBJECT_ID objectID, std::optional<int> count = std::nullopt);
@ -24,7 +24,7 @@ GameBoundingBox* FindPlinth(ItemInfo* item);
void PickupControl(short itemNumber); void PickupControl(short itemNumber);
void InitialiseSearchObject(short itemNumber); void InitializeSearchObject(short itemNumber);
void SearchObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void SearchObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void SearchObjectControl(short itemNumber); void SearchObjectControl(short itemNumber);
void DoPickup(ItemInfo* laraItem); void DoPickup(ItemInfo* laraItem);

View file

@ -8,7 +8,7 @@
#include "Game/items.h" #include "Game/items.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
using namespace TEN::Floordata; using namespace TEN::Collision::Floordata;
using namespace TEN::Renderer; using namespace TEN::Renderer;
byte FlipStatus = 0; byte FlipStatus = 0;

View file

@ -1,11 +1,43 @@
#pragma once #pragma once
#include "framework.h" #include "framework.h"
#include "Game/collision/floordata.h" #include "Game/collision/floordata.h"
#include "Specific/newtypes.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Specific/newtypes.h"
struct TriggerVolume;
enum class ReverbType; enum class ReverbType;
struct TriggerVolume;
constexpr auto MAX_FLIPMAP = 256;
constexpr auto NUM_ROOMS = 1024;
constexpr auto NO_ROOM = -1;
constexpr auto OUTSIDE_Z = 64;
constexpr auto OUTSIDE_SIZE = 1024;
extern byte FlipStatus;
extern int FlipStats[MAX_FLIPMAP];
extern int FlipMap[MAX_FLIPMAP];
enum RoomEnvFlags
{
ENV_FLAG_WATER = (1 << 0),
ENV_FLAG_SWAMP = (1 << 2),
ENV_FLAG_OUTSIDE = (1 << 3),
ENV_FLAG_DYNAMIC_LIT = (1 << 4),
ENV_FLAG_WIND = (1 << 5),
ENV_FLAG_NOT_NEAR_OUTSIDE = (1 << 6),
ENV_FLAG_NO_LENSFLARE = (1 << 7),
ENV_FLAG_MIST = (1 << 8),
ENV_FLAG_CAUSTICS = (1 << 9),
ENV_FLAG_UNKNOWN3 = (1 << 10),
ENV_FLAG_DAMAGE = (1 << 11),
ENV_FLAG_COLD = (1 << 12)
};
enum StaticMeshFlags : short
{
SM_VISIBLE = 1,
SM_SOLID = 2
};
struct ROOM_VERTEX struct ROOM_VERTEX
{ {
@ -53,44 +85,22 @@ struct MESH_INFO
struct LIGHTINFO struct LIGHTINFO
{ {
int x; // size=0, offset=0 int x;
int y; // size=0, offset=4 int y;
int z; // size=0, offset=8 int z;
unsigned char Type; // size=0, offset=12 unsigned char Type;
unsigned char r; // size=0, offset=13 unsigned char r;
unsigned char g; // size=0, offset=14 unsigned char g;
unsigned char b; // size=0, offset=15 unsigned char b;
short nx; // size=0, offset=16 short nx;
short ny; // size=0, offset=18 short ny;
short nz; // size=0, offset=20 short nz;
short Intensity; // size=0, offset=22 short Intensity;
unsigned char Inner; // size=0, offset=24 unsigned char Inner;
unsigned char Outer; // size=0, offset=25 unsigned char Outer;
short FalloffScale; // size=0, offset=26 short FalloffScale;
short Length; // size=0, offset=28 short Length;
short Cutoff; // size=0, offset=30 short Cutoff;
};
enum RoomEnvFlags
{
ENV_FLAG_WATER = (1 << 0),
ENV_FLAG_SWAMP = (1 << 2),
ENV_FLAG_OUTSIDE = (1 << 3),
ENV_FLAG_DYNAMIC_LIT = (1 << 4),
ENV_FLAG_WIND = (1 << 5),
ENV_FLAG_NOT_NEAR_OUTSIDE = (1 << 6),
ENV_FLAG_NO_LENSFLARE = (1 << 7),
ENV_FLAG_MIST = (1 << 8),
ENV_FLAG_CAUSTICS = (1 << 9),
ENV_FLAG_UNKNOWN3 = (1 << 10),
ENV_FLAG_DAMAGE = (1 << 11),
ENV_FLAG_COLD = (1 << 12)
};
enum StaticMeshFlags : short
{
SM_VISIBLE = 1,
SM_SOLID = 2
}; };
struct ROOM_INFO struct ROOM_INFO
@ -113,36 +123,26 @@ struct ROOM_INFO
short fxNumber; short fxNumber;
bool boundActive; bool boundActive;
std::string name; std::string name = {};
std::vector<std::string> tags; std::vector<std::string> tags = {};
std::vector<FloorInfo> floor; std::vector<FloorInfo> floor = {};
std::vector<ROOM_LIGHT> lights; std::vector<ROOM_LIGHT> lights = {};
std::vector<MESH_INFO> mesh; std::vector<MESH_INFO> mesh = {};
std::vector<TriggerVolume> triggerVolumes; std::vector<TriggerVolume> triggerVolumes = {};
std::vector<Vector3> positions; std::vector<Vector3> positions = {};
std::vector<Vector3> normals; std::vector<Vector3> normals = {};
std::vector<Vector3> colors; std::vector<Vector3> colors = {};
std::vector<Vector3> effects; std::vector<Vector3> effects = {};
std::vector<BUCKET> buckets; std::vector<BUCKET> buckets = {};
std::vector<ROOM_DOOR> doors; std::vector<ROOM_DOOR> doors = {};
std::vector<int> neighbors; // TODO: Move to level struct std::vector<int> neighbors = {};
bool Active(); bool Active();
}; };
constexpr auto MAX_FLIPMAP = 256;
constexpr auto NUM_ROOMS = 1024;
constexpr auto NO_ROOM = -1;
constexpr auto OUTSIDE_Z = 64;
constexpr auto OUTSIDE_SIZE = 1024;
extern byte FlipStatus;
extern int FlipStats[MAX_FLIPMAP];
extern int FlipMap[MAX_FLIPMAP];
void DoFlipMap(short group); void DoFlipMap(short group);
void AddRoomFlipItems(ROOM_INFO* room); void AddRoomFlipItems(ROOM_INFO* room);
void RemoveRoomFlipItems(ROOM_INFO* room); void RemoveRoomFlipItems(ROOM_INFO* room);

View file

@ -18,31 +18,30 @@
#include "Game/misc.h" #include "Game/misc.h"
#include "Game/spotcam.h" #include "Game/spotcam.h"
#include "Game/room.h" #include "Game/room.h"
#include "Game/Setup.h"
#include "Objects/Generic/Object/rope.h" #include "Objects/Generic/Object/rope.h"
#include "Objects/Generic/Switches/fullblock_switch.h" #include "Objects/Generic/Switches/fullblock_switch.h"
#include "Objects/Generic/Traps/traps.h"
#include "Objects/Generic/puzzles_keys.h" #include "Objects/Generic/puzzles_keys.h"
#include "Objects/Sink.h" #include "Objects/Sink.h"
#include "Objects/TR4/Entity/tr4_beetle_swarm.h" #include "Objects/TR4/Entity/tr4_beetle_swarm.h"
#include "Objects/TR5/Emitter/tr5_rats_emitter.h" #include "Objects/TR5/Emitter/tr5_rats_emitter.h"
#include "Objects/TR5/Emitter/tr5_bats_emitter.h" #include "Objects/TR5/Emitter/tr5_bats_emitter.h"
#include "Objects/TR5/Emitter/tr5_spider_emitter.h" #include "Objects/TR5/Emitter/tr5_spider_emitter.h"
#include "Scripting/Include/ScriptInterfaceGame.h"
#include "Scripting/Include/ScriptInterfaceLevel.h"
#include "Scripting/Include/Objects/ScriptInterfaceObjectsHandler.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h"
#include "Specific/savegame/flatbuffers/ten_savegame_generated.h" #include "Specific/savegame/flatbuffers/ten_savegame_generated.h"
#include "ScriptInterfaceLevel.h"
#include "ScriptInterfaceGame.h"
#include "Objects/ScriptInterfaceObjectsHandler.h"
using namespace flatbuffers;
using namespace TEN::Collision::Floordata;
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
using namespace TEN::Entities::Generic;
using namespace TEN::Effects::Items; using namespace TEN::Effects::Items;
using namespace TEN::Entities::Switches; using namespace TEN::Entities::Switches;
using namespace TEN::Entities::TR4; using namespace TEN::Entities::TR4;
using namespace TEN::Entities::Generic;
using namespace TEN::Floordata;
using namespace flatbuffers;
namespace Save = TEN::Save; namespace Save = TEN::Save;
@ -80,7 +79,7 @@ void LoadSavegameInfos()
} }
} }
Pose ToPHD(Save::Position const* src) Pose ToPHD(const Save::Position* src)
{ {
Pose dest; Pose dest;
dest.Position.x = src->x_pos(); dest.Position.x = src->x_pos();
@ -92,9 +91,10 @@ Pose ToPHD(Save::Position const* src)
return dest; return dest;
} }
Save::Position FromPHD(Pose const& src) Save::Position FromPHD(const Pose& src)
{
return Save::Position
{ {
return Save::Position{
src.Position.x, src.Position.x,
src.Position.y, src.Position.y,
src.Position.z, src.Position.z,
@ -104,6 +104,11 @@ Save::Position FromPHD(Pose const& src)
}; };
} }
Save::Vector2 FromVector2(Vector2i vec)
{
return Save::Vector2(vec.x, vec.y);
}
Save::Vector3 FromVector3(Vector3 vec) Save::Vector3 FromVector3(Vector3 vec)
{ {
return Save::Vector3(vec.x, vec.y, vec.z); return Save::Vector3(vec.x, vec.y, vec.z);
@ -126,12 +131,17 @@ Save::Vector4 FromVector4(Vector4 vec)
EulerAngles ToEulerAngles(const Save::Vector3* vec) EulerAngles ToEulerAngles(const Save::Vector3* vec)
{ {
return EulerAngles(short(vec->x()), short(vec->y()), short(vec->z())); return EulerAngles((short)vec->x(), (short)vec->y(), (short)vec->z());
}
Vector2i ToVector2i(const Save::Vector2* vec)
{
return Vector2i((int)vec->x(), (int)vec->y());
} }
Vector3i ToVector3i(const Save::Vector3* vec) Vector3i ToVector3i(const Save::Vector3* vec)
{ {
return Vector3i(int(vec->x()), int(vec->y()), int(vec->z())); return Vector3i((int)vec->x(), (int)vec->y(), (int)vec->z());
} }
Vector3 ToVector3(const Save::Vector3* vec) Vector3 ToVector3(const Save::Vector3* vec)
@ -149,10 +159,10 @@ Vector4 ToVector4(const Save::Vector4* vec)
return Vector4(vec->x(), vec->y(), vec->z(), vec->w()); return Vector4(vec->x(), vec->y(), vec->z(), vec->w());
} }
#define SaveVec(Type, Data, TableBuilder, UnionType) \ #define SaveVec(Type, Data, TableBuilder, UnionType, SaveType, ConversionFunc) \
auto data = std::get<(int)Type>(Data); \ auto data = std::get<(int)Type>(Data); \
TableBuilder vtb{ fbb }; \ TableBuilder vtb{ fbb }; \
Save::Vector3 saveVec = FromVector3(data); \ SaveType saveVec = ConversionFunc(data); \
vtb.add_vec(&saveVec); \ vtb.add_vec(&saveVec); \
auto vecOffset = vtb.Finish(); \ auto vecOffset = vtb.Finish(); \
putDataInVec(UnionType, vecOffset); putDataInVec(UnionType, vecOffset);
@ -389,7 +399,7 @@ bool SaveGame::Save(int slot)
Save::LaraControlDataBuilder control{ fbb }; Save::LaraControlDataBuilder control{ fbb };
control.add_move_angle(Lara.Control.MoveAngle); control.add_move_angle(Lara.Control.MoveAngle);
control.add_turn_rate(Lara.Control.TurnRate); control.add_turn_rate(Lara.Control.TurnRate);
control.add_calculated_jump_velocity(Lara.Control.CalculatedJumpVelocity); control.add_calculated_jump_velocity(Lara.Context.CalcJumpVelocity);
control.add_jump_direction((int)Lara.Control.JumpDirection); control.add_jump_direction((int)Lara.Control.JumpDirection);
control.add_hand_status((int)Lara.Control.HandStatus); control.add_hand_status((int)Lara.Control.HandStatus);
control.add_is_moving(Lara.Control.IsMoving); control.add_is_moving(Lara.Control.IsMoving);
@ -452,7 +462,7 @@ bool SaveGame::Save(int slot)
Save::LaraBuilder lara{ fbb }; Save::LaraBuilder lara{ fbb };
lara.add_control(controlOffset); lara.add_control(controlOffset);
lara.add_next_corner_pose(&FromPHD(Lara.NextCornerPos)); lara.add_next_corner_pose(&FromPHD(Lara.Context.NextCornerPos));
lara.add_effect(effectOffset); lara.add_effect(effectOffset);
lara.add_extra_anim(Lara.ExtraAnim); lara.add_extra_anim(Lara.ExtraAnim);
lara.add_extra_head_rot(&FromVector3(Lara.ExtraHeadRot)); lara.add_extra_head_rot(&FromVector3(Lara.ExtraHeadRot));
@ -461,23 +471,23 @@ bool SaveGame::Save(int slot)
lara.add_highest_location(Lara.HighestLocation); lara.add_highest_location(Lara.HighestLocation);
lara.add_hit_direction(Lara.HitDirection); lara.add_hit_direction(Lara.HitDirection);
lara.add_hit_frame(Lara.HitFrame); lara.add_hit_frame(Lara.HitFrame);
lara.add_interacted_item(Lara.InteractedItem); lara.add_interacted_item(Lara.Context.InteractedItem);
lara.add_inventory(inventoryOffset); lara.add_inventory(inventoryOffset);
lara.add_item_number(Lara.ItemNumber); lara.add_item_number(Lara.ItemNumber);
lara.add_left_arm(leftArmOffset); lara.add_left_arm(leftArmOffset);
lara.add_location(Lara.Location); lara.add_location(Lara.Location);
lara.add_location_pad(Lara.LocationPad); lara.add_location_pad(Lara.LocationPad);
lara.add_projected_floor_height(Lara.ProjectedFloorHeight); lara.add_projected_floor_height(Lara.Context.ProjectedFloorHeight);
lara.add_right_arm(rightArmOffset); lara.add_right_arm(rightArmOffset);
lara.add_status(statusOffset); lara.add_status(statusOffset);
lara.add_target_facing_angle(Lara.TargetOrientation.y); lara.add_target_facing_angle(Lara.Context.TargetOrientation.y);
lara.add_target_arm_angles(laraTargetAnglesOffset); lara.add_target_arm_angles(laraTargetAnglesOffset);
lara.add_target_entity_number(Lara.TargetEntity - g_Level.Items.data()); lara.add_target_entity_number(Lara.TargetEntity - g_Level.Items.data());
lara.add_torch(torchOffset); lara.add_torch(torchOffset);
lara.add_vehicle(Lara.Vehicle); lara.add_vehicle(Lara.Context.Vehicle);
lara.add_water_current_active(Lara.WaterCurrentActive); lara.add_water_current_active(Lara.Context.WaterCurrentActive);
lara.add_water_current_pull(&FromVector3(Lara.WaterCurrentPull)); lara.add_water_current_pull(&FromVector3(Lara.Context.WaterCurrentPull));
lara.add_water_surface_dist(Lara.WaterSurfaceDist); lara.add_water_surface_dist(Lara.Context.WaterSurfaceDist);
lara.add_weapons(carriedWeaponsOffset); lara.add_weapons(carriedWeaponsOffset);
auto laraOffset = lara.Finish(); auto laraOffset = lara.Finish();
@ -527,8 +537,7 @@ bool SaveGame::Save(int slot)
flatbuffers::Offset<Save::Short> shortOffset; flatbuffers::Offset<Save::Short> shortOffset;
flatbuffers::Offset<Save::Int> intOffset; flatbuffers::Offset<Save::Int> intOffset;
if (Objects[itemToSerialize.ObjectNumber].intelligent if (Objects[itemToSerialize.ObjectNumber].intelligent && itemToSerialize.IsCreature())
&& itemToSerialize.Data.is<CreatureInfo>())
{ {
auto creature = GetCreatureInfo(&itemToSerialize); auto creature = GetCreatureInfo(&itemToSerialize);
@ -538,12 +547,10 @@ bool SaveGame::Save(int slot)
auto jointRotationsOffset = fbb.CreateVector(jointRotations); auto jointRotationsOffset = fbb.CreateVector(jointRotations);
Save::CreatureBuilder creatureBuilder{ fbb }; Save::CreatureBuilder creatureBuilder{ fbb };
creatureBuilder.add_alerted(creature->Alerted); creatureBuilder.add_alerted(creature->Alerted);
creatureBuilder.add_can_jump(creature->LOT.CanJump); creatureBuilder.add_can_jump(creature->LOT.CanJump);
creatureBuilder.add_can_monkey(creature->LOT.CanMonkey); creatureBuilder.add_can_monkey(creature->LOT.CanMonkey);
creatureBuilder.add_enemy(creature->Enemy - g_Level.Items.data()); creatureBuilder.add_enemy(creature->Enemy - g_Level.Items.data());
creatureBuilder.add_fired_weapon(creature->FiredWeapon);
creatureBuilder.add_flags(creature->Flags); creatureBuilder.add_flags(creature->Flags);
creatureBuilder.add_friendly(creature->Friendly); creatureBuilder.add_friendly(creature->Friendly);
creatureBuilder.add_head_left(creature->HeadLeft); creatureBuilder.add_head_left(creature->HeadLeft);
@ -555,6 +562,8 @@ bool SaveGame::Save(int slot)
creatureBuilder.add_joint_rotation(jointRotationsOffset); creatureBuilder.add_joint_rotation(jointRotationsOffset);
creatureBuilder.add_jump_ahead(creature->JumpAhead); creatureBuilder.add_jump_ahead(creature->JumpAhead);
creatureBuilder.add_location_ai(creature->LocationAI); creatureBuilder.add_location_ai(creature->LocationAI);
creatureBuilder.add_weapon_delay1(creature->MuzzleFlash[0].Delay);
creatureBuilder.add_weapon_delay2(creature->MuzzleFlash[1].Delay);
creatureBuilder.add_maximum_turn(creature->MaxTurn); creatureBuilder.add_maximum_turn(creature->MaxTurn);
creatureBuilder.add_monkey_swing_ahead(creature->MonkeySwingAhead); creatureBuilder.add_monkey_swing_ahead(creature->MonkeySwingAhead);
creatureBuilder.add_mood((int)creature->Mood); creatureBuilder.add_mood((int)creature->Mood);
@ -1164,16 +1173,24 @@ bool SaveGame::Save(int slot)
{ {
switch (SavedVarType(s.index())) switch (SavedVarType(s.index()))
{ {
case SavedVarType::Vec2:
{
SaveVec(SavedVarType::Vec2, s, Save::vec2TableBuilder, Save::VarUnion::vec2, Save::Vector2, FromVector2);
}
break;
case SavedVarType::Vec3: case SavedVarType::Vec3:
{ {
SaveVec(SavedVarType::Vec3, s, Save::vec3TableBuilder, Save::VarUnion::vec3); SaveVec(SavedVarType::Vec3, s, Save::vec3TableBuilder, Save::VarUnion::vec3, Save::Vector3, FromVector3);
} }
break; break;
case SavedVarType::Rotation: case SavedVarType::Rotation:
{ {
SaveVec(SavedVarType::Rotation, s, Save::rotationTableBuilder, Save::VarUnion::rotation); SaveVec(SavedVarType::Rotation, s, Save::rotationTableBuilder, Save::VarUnion::rotation, Save::Vector3, FromVector3);
} }
break; break;
case SavedVarType::Color: case SavedVarType::Color:
{ {
Save::colorTableBuilder ctb{ fbb }; Save::colorTableBuilder ctb{ fbb };
@ -1183,18 +1200,51 @@ bool SaveGame::Save(int slot)
putDataInVec(Save::VarUnion::color, offset); putDataInVec(Save::VarUnion::color, offset);
} }
break; break;
} }
} }
} }
auto unionVec = fbb.CreateVector(varsVec); auto unionVec = fbb.CreateVector(varsVec);
Save::UnionVecBuilder uvb{ fbb }; Save::UnionVecBuilder uvb{ fbb };
uvb.add_members(unionVec); uvb.add_members(unionVec);
auto unionVecOffset = uvb.Finish(); auto unionVecOffset = uvb.Finish();
std::vector<std::string> callbackVecPreStart;
std::vector<std::string> callbackVecPostStart;
std::vector<std::string> callbackVecPreEnd;
std::vector<std::string> callbackVecPostEnd;
std::vector<std::string> callbackVecPreSave;
std::vector<std::string> callbackVecPostSave;
std::vector<std::string> callbackVecPreLoad;
std::vector<std::string> callbackVecPostLoad;
std::vector<std::string> callbackVecPreControl; std::vector<std::string> callbackVecPreControl;
std::vector<std::string> callbackVecPostControl; std::vector<std::string> callbackVecPostControl;
g_GameScript->GetCallbackStrings(callbackVecPreControl, callbackVecPostControl);
g_GameScript->GetCallbackStrings(
callbackVecPreStart,
callbackVecPostStart,
callbackVecPreEnd,
callbackVecPostEnd,
callbackVecPreSave,
callbackVecPostSave,
callbackVecPreLoad,
callbackVecPostLoad,
callbackVecPreControl,
callbackVecPostControl);
auto stringsCallbackPreStart = fbb.CreateVectorOfStrings(callbackVecPreStart);
auto stringsCallbackPostStart = fbb.CreateVectorOfStrings(callbackVecPostStart);
auto stringsCallbackPreEnd = fbb.CreateVectorOfStrings(callbackVecPreEnd);
auto stringsCallbackPostEnd = fbb.CreateVectorOfStrings(callbackVecPostEnd);
auto stringsCallbackPreSave = fbb.CreateVectorOfStrings(callbackVecPreSave);
auto stringsCallbackPostSave = fbb.CreateVectorOfStrings(callbackVecPostSave);
auto stringsCallbackPreLoad = fbb.CreateVectorOfStrings(callbackVecPreLoad);
auto stringsCallbackPostLoad = fbb.CreateVectorOfStrings(callbackVecPostLoad);
auto stringsCallbackPreControl = fbb.CreateVectorOfStrings(callbackVecPreControl); auto stringsCallbackPreControl = fbb.CreateVectorOfStrings(callbackVecPreControl);
auto stringsCallbackPostControl = fbb.CreateVectorOfStrings(callbackVecPostControl); auto stringsCallbackPostControl = fbb.CreateVectorOfStrings(callbackVecPostControl);
@ -1243,6 +1293,19 @@ bool SaveGame::Save(int slot)
} }
sgb.add_script_vars(unionVecOffset); sgb.add_script_vars(unionVecOffset);
sgb.add_callbacks_pre_start(stringsCallbackPreStart);
sgb.add_callbacks_post_start(stringsCallbackPostStart);
sgb.add_callbacks_pre_end(stringsCallbackPreEnd);
sgb.add_callbacks_post_end(stringsCallbackPostEnd);
sgb.add_callbacks_pre_save(stringsCallbackPreSave);
sgb.add_callbacks_post_save(stringsCallbackPostSave);
sgb.add_callbacks_pre_load(stringsCallbackPreLoad);
sgb.add_callbacks_post_load(stringsCallbackPostLoad);
sgb.add_callbacks_pre_control(stringsCallbackPreControl); sgb.add_callbacks_pre_control(stringsCallbackPreControl);
sgb.add_callbacks_post_control(stringsCallbackPostControl); sgb.add_callbacks_post_control(stringsCallbackPostControl);
@ -1543,20 +1606,25 @@ bool SaveGame::Load(int slot)
creature->Alerted = savedCreature->alerted(); creature->Alerted = savedCreature->alerted();
creature->LOT.CanJump = savedCreature->can_jump(); creature->LOT.CanJump = savedCreature->can_jump();
creature->LOT.CanMonkey = savedCreature->can_monkey(); creature->LOT.CanMonkey = savedCreature->can_monkey();
if (savedCreature->enemy() >= 0) if (savedCreature->enemy() >= 0)
creature->Enemy = &g_Level.Items[savedCreature->enemy()]; creature->Enemy = &g_Level.Items[savedCreature->enemy()];
creature->FiredWeapon = savedCreature->fired_weapon();
creature->Flags = savedCreature->flags(); creature->Flags = savedCreature->flags();
creature->Friendly = savedCreature->friendly(); creature->Friendly = savedCreature->friendly();
creature->HeadLeft = savedCreature->head_left(); creature->HeadLeft = savedCreature->head_left();
creature->HeadRight = savedCreature->head_right(); creature->HeadRight = savedCreature->head_right();
creature->HurtByLara = savedCreature->hurt_by_lara(); creature->HurtByLara = savedCreature->hurt_by_lara();
creature->LocationAI = savedCreature->location_ai(); creature->LocationAI = savedCreature->location_ai();
creature->MuzzleFlash[0].Delay = savedCreature->weapon_delay1();
creature->MuzzleFlash[1].Delay = savedCreature->weapon_delay2();
creature->LOT.IsAmphibious = savedCreature->is_amphibious(); creature->LOT.IsAmphibious = savedCreature->is_amphibious();
creature->LOT.IsJumping = savedCreature->is_jumping(); creature->LOT.IsJumping = savedCreature->is_jumping();
creature->LOT.IsMonkeying = savedCreature->is_monkeying(); creature->LOT.IsMonkeying = savedCreature->is_monkeying();
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
creature->JointRotation[j] = savedCreature->joint_rotation()->Get(j); creature->JointRotation[j] = savedCreature->joint_rotation()->Get(j);
creature->JumpAhead = savedCreature->jump_ahead(); creature->JumpAhead = savedCreature->jump_ahead();
creature->MaxTurn = savedCreature->maximum_turn(); creature->MaxTurn = savedCreature->maximum_turn();
creature->MonkeySwingAhead = savedCreature->monkey_swing_ahead(); creature->MonkeySwingAhead = savedCreature->monkey_swing_ahead();
@ -1813,7 +1881,7 @@ bool SaveGame::Load(int slot)
for (int i = 0; i < Lara.Effect.DripNodes.size(); i++) for (int i = 0; i < Lara.Effect.DripNodes.size(); i++)
Lara.Effect.DripNodes[i] = s->lara()->effect()->drip_nodes()->Get(i); Lara.Effect.DripNodes[i] = s->lara()->effect()->drip_nodes()->Get(i);
Lara.Control.CalculatedJumpVelocity = s->lara()->control()->calculated_jump_velocity(); Lara.Context.CalcJumpVelocity = s->lara()->control()->calculated_jump_velocity();
Lara.Control.CanMonkeySwing = s->lara()->control()->can_monkey_swing(); Lara.Control.CanMonkeySwing = s->lara()->control()->can_monkey_swing();
Lara.Control.CanClimbLadder = s->lara()->control()->is_climbing_ladder(); Lara.Control.CanClimbLadder = s->lara()->control()->is_climbing_ladder();
Lara.Control.Count.Death = s->lara()->control()->count()->death(); Lara.Control.Count.Death = s->lara()->control()->count()->death();
@ -1853,17 +1921,17 @@ bool SaveGame::Load(int slot)
Lara.ExtraTorsoRot.z = s->lara()->extra_torso_rot()->x(); Lara.ExtraTorsoRot.z = s->lara()->extra_torso_rot()->x();
Lara.ExtraTorsoRot.y = s->lara()->extra_torso_rot()->y(); Lara.ExtraTorsoRot.y = s->lara()->extra_torso_rot()->y();
Lara.ExtraTorsoRot.z = s->lara()->extra_torso_rot()->z(); Lara.ExtraTorsoRot.z = s->lara()->extra_torso_rot()->z();
Lara.WaterCurrentActive = s->lara()->water_current_active(); Lara.Context.WaterCurrentActive = s->lara()->water_current_active();
Lara.WaterCurrentPull.x = s->lara()->water_current_pull()->x(); Lara.Context.WaterCurrentPull.x = s->lara()->water_current_pull()->x();
Lara.WaterCurrentPull.y = s->lara()->water_current_pull()->y(); Lara.Context.WaterCurrentPull.y = s->lara()->water_current_pull()->y();
Lara.WaterCurrentPull.z = s->lara()->water_current_pull()->z(); Lara.Context.WaterCurrentPull.z = s->lara()->water_current_pull()->z();
Lara.Flare.Life = s->lara()->flare()->life(); Lara.Flare.Life = s->lara()->flare()->life();
Lara.Flare.ControlLeft = s->lara()->flare()->control_left(); Lara.Flare.ControlLeft = s->lara()->flare()->control_left();
Lara.Flare.Frame = s->lara()->flare()->frame(); Lara.Flare.Frame = s->lara()->flare()->frame();
Lara.HighestLocation = s->lara()->highest_location(); Lara.HighestLocation = s->lara()->highest_location();
Lara.HitDirection = s->lara()->hit_direction(); Lara.HitDirection = s->lara()->hit_direction();
Lara.HitFrame = s->lara()->hit_frame(); Lara.HitFrame = s->lara()->hit_frame();
Lara.InteractedItem = s->lara()->interacted_item(); Lara.Context.InteractedItem = s->lara()->interacted_item();
Lara.Inventory.BeetleComponents = s->lara()->inventory()->beetle_components(); Lara.Inventory.BeetleComponents = s->lara()->inventory()->beetle_components();
Lara.Inventory.BeetleLife = s->lara()->inventory()->beetle_life(); Lara.Inventory.BeetleLife = s->lara()->inventory()->beetle_life();
Lara.Inventory.BigWaterskin = s->lara()->inventory()->big_waterskin(); Lara.Inventory.BigWaterskin = s->lara()->inventory()->big_waterskin();
@ -1888,8 +1956,8 @@ bool SaveGame::Load(int slot)
Lara.LeftArm.Orientation = ToEulerAngles(s->lara()->left_arm()->rotation()); Lara.LeftArm.Orientation = ToEulerAngles(s->lara()->left_arm()->rotation());
Lara.Location = s->lara()->location(); Lara.Location = s->lara()->location();
Lara.LocationPad = s->lara()->location_pad(); Lara.LocationPad = s->lara()->location_pad();
Lara.NextCornerPos = ToPHD(s->lara()->next_corner_pose()); Lara.Context.NextCornerPos = ToPHD(s->lara()->next_corner_pose());
Lara.ProjectedFloorHeight = s->lara()->projected_floor_height(); Lara.Context.ProjectedFloorHeight = s->lara()->projected_floor_height();
Lara.RightArm.AnimNumber = s->lara()->right_arm()->anim_number(); Lara.RightArm.AnimNumber = s->lara()->right_arm()->anim_number();
Lara.RightArm.GunFlash = s->lara()->right_arm()->gun_flash(); Lara.RightArm.GunFlash = s->lara()->right_arm()->gun_flash();
Lara.RightArm.GunSmoke = s->lara()->right_arm()->gun_smoke(); Lara.RightArm.GunSmoke = s->lara()->right_arm()->gun_smoke();
@ -1934,9 +2002,9 @@ bool SaveGame::Load(int slot)
Lara.TargetEntity = (s->lara()->target_entity_number() >= 0 ? &g_Level.Items[s->lara()->target_entity_number()] : nullptr); Lara.TargetEntity = (s->lara()->target_entity_number() >= 0 ? &g_Level.Items[s->lara()->target_entity_number()] : nullptr);
Lara.TargetArmOrient.y = s->lara()->target_arm_angles()->Get(0); Lara.TargetArmOrient.y = s->lara()->target_arm_angles()->Get(0);
Lara.TargetArmOrient.x = s->lara()->target_arm_angles()->Get(1); Lara.TargetArmOrient.x = s->lara()->target_arm_angles()->Get(1);
Lara.TargetOrientation.y = s->lara()->target_facing_angle(); Lara.Context.TargetOrientation.y = s->lara()->target_facing_angle();
Lara.Vehicle = s->lara()->vehicle(); Lara.Context.Vehicle = s->lara()->vehicle();
Lara.WaterSurfaceDist = s->lara()->water_surface_dist(); Lara.Context.WaterSurfaceDist = s->lara()->water_surface_dist();
for (int i = 0; i < s->lara()->weapons()->size(); i++) for (int i = 0; i < s->lara()->weapons()->size(); i++)
{ {
@ -1988,10 +2056,10 @@ bool SaveGame::Load(int slot)
std::vector<SavedVar> loadedVars; std::vector<SavedVar> loadedVars;
auto theVec = s->script_vars(); auto unionVec = s->script_vars();
if (theVec) if (unionVec)
{ {
for (auto const& var : *(theVec->members())) for (const auto& var : *(unionVec->members()))
{ {
if (var->u_type() == Save::VarUnion::num) if (var->u_type() == Save::VarUnion::num)
{ {
@ -2010,24 +2078,29 @@ bool SaveGame::Load(int slot)
auto tab = var->u_as_tab()->keys_vals(); auto tab = var->u_as_tab()->keys_vals();
auto& loadedTab = loadedVars.emplace_back(IndexTable{}); auto& loadedTab = loadedVars.emplace_back(IndexTable{});
for (auto const& p : *tab) for (const auto& pair : *tab)
{ std::get<IndexTable>(loadedTab).push_back(std::make_pair(pair->key(), pair->val()));
std::get<IndexTable>(loadedTab).push_back(std::make_pair(p->key(), p->val()));
} }
else if (var->u_type() == Save::VarUnion::vec2)
{
auto stored = var->u_as_vec2()->vec();
SavedVar var;
var.emplace<(int)SavedVarType::Vec2>(ToVector2i(stored));
loadedVars.push_back(var);
} }
else if (var->u_type() == Save::VarUnion::vec3) else if (var->u_type() == Save::VarUnion::vec3)
{ {
auto stored = var->u_as_vec3()->vec(); auto stored = var->u_as_vec3()->vec();
SavedVar v; SavedVar var;
v.emplace<(int)SavedVarType::Vec3>(ToVector3i(stored)); var.emplace<(int)SavedVarType::Vec3>(ToVector3i(stored));
loadedVars.push_back(v); loadedVars.push_back(var);
} }
else if (var->u_type() == Save::VarUnion::rotation) else if (var->u_type() == Save::VarUnion::rotation)
{ {
auto stored = var->u_as_rotation()->vec(); auto stored = var->u_as_rotation()->vec();
SavedVar v; SavedVar var;
v.emplace<(int)SavedVarType::Rotation>(ToVector3(stored)); var.emplace<(int)SavedVarType::Rotation>(ToVector3(stored));
loadedVars.push_back(v); loadedVars.push_back(var);
} }
else if (var->u_type() == Save::VarUnion::color) else if (var->u_type() == Save::VarUnion::color)
{ {
@ -2037,23 +2110,48 @@ bool SaveGame::Load(int slot)
{ {
loadedVars.push_back(FuncName{var->u_as_funcName()->str()->str()}); loadedVars.push_back(FuncName{var->u_as_funcName()->str()->str()});
} }
} }
} }
g_GameScript->SetVariables(loadedVars); g_GameScript->SetVariables(loadedVars);
std::vector<std::string> callbacksPreControlVec; auto populateCallbackVecs = [&s](auto callbackFunc)
auto callbacksPreControlOffsetVec = s->callbacks_pre_control(); {
for (auto const& s : *callbacksPreControlOffsetVec) auto callbacksVec = std::vector<std::string>{};
callbacksPreControlVec.push_back(s->str()); auto callbacksOffsetVec = std::invoke(callbackFunc, s);
std::vector<std::string> callbacksPostControlVec; for (const auto& e : *callbacksOffsetVec)
auto callbacksPostControlOffsetVec = s->callbacks_post_control(); callbacksVec.push_back(e->str());
for (auto const& s : *callbacksPostControlOffsetVec)
callbacksPostControlVec.push_back(s->str());
g_GameScript->SetCallbackStrings(callbacksPreControlVec, callbacksPostControlVec); return callbacksVec;
};
auto callbacksPreStartVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_start);
auto callbacksPostStartVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_start);
auto callbacksPreEndVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_end);
auto callbacksPostEndVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_end);
auto callbacksPreSaveVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_save);
auto callbacksPostSaveVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_save);
auto callbacksPreLoadVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_load);
auto callbacksPostLoadVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_load);
auto callbacksPreControlVec = populateCallbackVecs(&Save::SaveGame::callbacks_pre_control);
auto callbacksPostControlVec = populateCallbackVecs(&Save::SaveGame::callbacks_post_control);
g_GameScript->SetCallbackStrings(
callbacksPreStartVec,
callbacksPostStartVec,
callbacksPreEndVec,
callbacksPostEndVec,
callbacksPreSaveVec,
callbacksPostSaveVec,
callbacksPreLoadVec,
callbacksPostLoadVec,
callbacksPreControlVec,
callbacksPostControlVec);
return true; return true;
} }

View file

@ -68,7 +68,7 @@ void ClearSpotCamSequences()
SpotCam[i] = {}; SpotCam[i] = {};
} }
void InitialiseSpotCamSequences(bool startFirstSequence) void InitializeSpotCamSequences(bool startFirstSequence)
{ {
TrackCameraInit = false; TrackCameraInit = false;
@ -105,12 +105,12 @@ void InitialiseSpotCamSequences(bool startFirstSequence)
if (startFirstSequence) if (startFirstSequence)
{ {
InitialiseSpotCam(0); InitializeSpotCam(0);
UseSpotCam = true; UseSpotCam = true;
} }
} }
void InitialiseSpotCam(short Sequence) void InitializeSpotCam(short Sequence)
{ {
if (TrackCameraInit != 0 && LastSpotCamSequence == Sequence) if (TrackCameraInit != 0 && LastSpotCamSequence == Sequence)
{ {
@ -125,7 +125,7 @@ void InitialiseSpotCam(short Sequence)
LaraItem->MeshBits = ALL_JOINT_BITS; LaraItem->MeshBits = ALL_JOINT_BITS;
ResetLaraFlex(LaraItem); ResetPlayerFlex(LaraItem);
Camera.bounce = 0; Camera.bounce = 0;

View file

@ -56,7 +56,7 @@ extern bool SpotcamOverlay;
extern bool TrackCameraInit; extern bool TrackCameraInit;
void ClearSpotCamSequences(); void ClearSpotCamSequences();
void InitialiseSpotCamSequences(bool startFirstSequence); void InitializeSpotCamSequences(bool startFirstSequence);
void InitialiseSpotCam(short sequence); void InitializeSpotCam(short sequence);
void CalculateSpotCameras(); void CalculateSpotCameras();
int Spline(int x, int* knots, int nk); int Spline(int x, int* knots, int nk);

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