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/
packages/
.vs/
.vsconfig
*.dll
*.dmp
*.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
=============
* Fix bubbles phasing through ceilings.
* Fix object camera not clearing at level end.
* Fix double breathing sound effect when coming up for air.
* Fix twisting hair bug.
* Fix double breath sound effect when coming up for air.
* Fix flickering hair.
* Fix harpoon gun triggering water and dry sounds when shooting and reholstering.
* Fix Z-fighting in inventory rendering.
* Fix transparent objects not displaying correctly in the Inventory.
* 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 going through trapdoor/bridge while climbing up a climbable wall.
* Fix TR3 Sophia's charge ring drawing below floor.
* Fix TR5 imp collision handling and animations.
- OCB 1 - Climbs up to Lara when triggered.
- OCB 2 - Starts of playfully rolling on the floor when triggered.
- OCB 3 - Will throw stones at Lara.
- Imp is also scared of Lara if she has a lit torch in her hand.
* Fix TR5 imp collision handling and animations:
- OCB 1: Climbs up to player when triggered.
- OCB 2: Starts rolling on the floor when triggered.
- OCB 3: Will throw stones at player.
- 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
* Fix and improve wraith tails.
* Add dedicated WRAITH_TRAP object with enhanced effects.
@ -24,22 +64,23 @@ Version 1.0.8
* Add TR1 slamming doors.
* Add TR3 mutant wasp (AI_MODIFY object won't allow it to land, the wasp will always fly).
* 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.
- 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 water wakes for vehicles.
* Allow dynamic segment count of hair object.
* Restored light effect nullmeshes (color, electrical, pulse and strobe):
- Select the color the light should emit as object tint in OCB menu in Tomb editor.
* Restored light effect nullmeshes (color, electrical, pulse, and strobe):
- Select the light color as object tint in the OCB menu in Tomb Editor.
- ELECTRICAL_LIGHT:
- OCB 1: Displays mesh of object.
- OCB -1: Hides mesh of object (used for a neon light effect)
- Can have multiple meshes. Add mesh number to OCB to be renderd with the light.
- OCB + (mesh number): Light behaves like a neon light.
- OCB (mesh number): Light flickers.
* Restored inventory compass.
* Allow dynamic segment count for hair object.
Lua API changes:
* Add new function Misc::IsSoundPlaying()
* Add function Misc::IsSoundPlaying()
* Add function DisplayString::SetFlags()
Version 1.0.7
=============

View file

@ -12,7 +12,7 @@ new_type("luautil", "5 Lua utility modules", true)
not_luadoc = true
local version = "1.0.8"
local version = "1.0.9"
project = "TombEngine"
title = "TombEngine " .. version .. " Lua API"
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="summary">Get the position of the string.</td>
</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>
<br/>
@ -250,7 +254,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
<h3>Returns:</h3>
<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
</ol>
@ -272,7 +276,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
<h3>Parameters:</h3>
<ul>
<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
</li>
</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>
</dl>
@ -341,7 +377,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
</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-03-31 20:44:31 </i>
<i style="float:right;">Last updated 2023-04-11 21:28:36 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</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>
<returns>
<return>
<type>String</type>
<type>string</type>
<description>a string</description>
</return>
</returns>
@ -3842,7 +3842,7 @@ __Default: empty__</description>
<parameters>
<parameter>
<name>string</name>
<type>String</type>
<type>string</type>
<description>the new key for the display string</description>
</parameter>
</parameters>
@ -3886,6 +3886,20 @@ __Default: empty__</description>
</returns>
</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>
<module>Strings</module>
<name>ShowString</name>

View file

@ -277,7 +277,7 @@ 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
-- @function myTimer:GetRemainingTime
-- @function myTimer:GetTotalTime
-- @treturn float the timer's total time
GetTotalTime = function(t)
return LevelVars.Engine.Timer.timers[t.name].totalTime

View file

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

View file

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

View file

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

View file

@ -12,22 +12,50 @@
using namespace TEN::Renderer;
extern TEN::Renderer::RendererHudBar* g_AirBar;
extern TEN::Renderer::RendererHudBar* g_ExposureBar;
extern TEN::Renderer::RendererHudBar* g_HealthBar;
extern TEN::Renderer::RendererHudBar* g_StaminaBar;
extern RendererHudBar* g_AirBar;
extern RendererHudBar* g_ExposureBar;
extern RendererHudBar* g_HealthBar;
extern RendererHudBar* g_StaminaBar;
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)
{
const auto& player = GetLaraInfo(item);
// Initialize bar values.
InitializeStatusBar(AirBar, player.Status.Air, LARA_AIR_MAX);
InitializeStatusBar(ExposureBar, player.Status.Exposure, LARA_EXPOSURE_MAX);
InitializeStatusBar(HealthBar, item.HitPoints, LARA_HEALTH_MAX);
InitializeStatusBar(StaminaBar, player.Status.Stamina, LARA_STAMINA_MAX);
AirBar.Initialize(player.Status.Air / LARA_AIR_MAX);
ExposureBar.Initialize(player.Status.Exposure / LARA_EXPOSURE_MAX);
HealthBar.Initialize(item.HitPoints / LARA_HEALTH_MAX);
StaminaBar.Initialize(player.Status.Stamina / LARA_STAMINA_MAX);
}
void StatusBarsController::Update(const ItemInfo& item)
@ -66,52 +94,26 @@ namespace TEN::Hud
*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)
{
const auto& player = GetLaraInfo(item);
// Update generic data.
UpdateStatusBar(AirBar, player.Status.Air, LARA_AIR_MAX);
AirBar.Update(player.Status.Air / LARA_AIR_MAX);
// Update life.
if (AirBar.Value != AirBar.TargetValue ||
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.
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)
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);
// Update generic data.
UpdateStatusBar(ExposureBar, player.Status.Exposure, LARA_EXPOSURE_MAX);
ExposureBar.Update(player.Status.Exposure / LARA_EXPOSURE_MAX);
// Update life.
if (ExposureBar.Value != ExposureBar.TargetValue ||
(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);
// Update generic data.
UpdateStatusBar(HealthBar, item.HitPoints, LARA_HEALTH_MAX);
HealthBar.Update(item.HitPoints / LARA_HEALTH_MAX);
// Update life.
if (HealthBar.Value != HealthBar.TargetValue ||
@ -145,7 +147,7 @@ namespace TEN::Hud
(player.Control.HandStatus == HandStatus::WeaponReady &&
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.
@ -153,7 +155,7 @@ namespace TEN::Hud
item.HitPoints > LARA_HEALTH_CRITICAL && player.Status.Poison == 0 &&
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);
// Update generic data.
UpdateStatusBar(StaminaBar, player.Status.Stamina, LARA_STAMINA_MAX);
StaminaBar.Update(player.Status.Stamina / LARA_STAMINA_MAX);
// Update life.
if (StaminaBar.Value != StaminaBar.TargetValue ||
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;
struct ItemInfo;
namespace TEN::Renderer { struct RendererHudBar; }
namespace TEN::Renderer
{
struct RendererHudBar;
}
using namespace TEN::Renderer;
namespace TEN::Hud
{
struct StatusBar
{
static constexpr auto LIFE_MAX = 0.75f;
float Value = 0.0f;
float TargetValue = 0.0f;
float Life = 0.0f;
float Opacity = 0.0f; // TODO: Opacity in renderer.
void Initialize(float value);
void Update(float value);
};
class StatusBarsController
{
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
StatusBar AirBar = {};
StatusBar ExposureBar = {};
@ -42,18 +40,14 @@ namespace TEN::Hud
void Clear();
private:
// Initializer helpers
void InitializeStatusBar(StatusBar& bar, float statusValue, float statusValueMax);
// Update helpers
void UpdateStatusBar(StatusBar& bar, float statusValue, float statusValueMax);
void UpdateAirBar(const ItemInfo& item);
void UpdateExposureBar(const ItemInfo& item);
void UpdateHealthBar(const ItemInfo& item);
void UpdateStaminaBar(const ItemInfo& item);
// Draw helpers
void DrawStatusBar(float value, float criticalValue, const 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 DrawExposureBar() const;
void DrawHealthBar(bool isPoisoned) const;

View file

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

View file

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

View file

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

View file

@ -10,9 +10,9 @@
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_tests.h"
#include "Specific/level.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/Input/Input.h"
#include "Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/level.h"
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;
// -----------------------------
// LADDER CLIMB
// WALL CLIMB
// 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)
{
if (LaraCheckForLetGo(item, coll) || item->Animation.AnimNumber != LA_LADDER_DOWN)
if (LaraCheckForLetGo(item, coll) || !TestAnimNumber(*item, LA_LADDER_DOWN))
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;
switch (frame)
@ -128,9 +128,9 @@ void lara_as_climb_down(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 resultRight, resultLeft;
int shiftRight, shiftLeft;
@ -304,6 +304,10 @@ void lara_col_climb_idle(ItemInfo* item, CollisionInfo* coll)
return;
}
// Added check to avoid climbing through bridges.
if (resultRight == 0 && resultLeft == 0)
return;
if (resultRight >= 0 && resultLeft >= 0)
{
yShift = shiftLeft;
@ -321,7 +325,7 @@ void lara_col_climb_idle(ItemInfo* item, CollisionInfo* coll)
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));
auto probe = GetCollision(item, 0, 0, -(coll->Setup.Height + CLICK(0.5f)));
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)
Camera.targetAngle = -ANGLE(60.0f);
if (item->Animation.AnimNumber == LA_LADDER_DISMOUNT_RIGHT_START)
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])
{
lara->NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y;
lara->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.Position.x = item->Pose.Position.x = x;
lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
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);
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])
{
lara->NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y;
lara->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.Position.x = item->Pose.Position.x = x;
lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
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);
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])
{
lara->NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y;
lara->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.Position.x = item->Pose.Position.x = x;
lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
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);
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])
{
lara->NextCornerPos.Position.x = item->Pose.Position.x = x;
lara->NextCornerPos.Position.y = item->Pose.Position.y;
lara->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.Position.x = item->Pose.Position.x = x;
lara->Context.NextCornerPos.Position.y = item->Pose.Position.y;
lara->Context.NextCornerPos.Position.z = item->Pose.Position.z = z;
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);
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 y = item->Pose.Position.y - 768;
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;
}
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);
*shift = 0;
short roomNumber = item->RoomNumber;
FloorInfo* floor = GetFloor(x, y, z, &roomNumber);
int ceiling = CLICK(1) - y + GetCeiling(floor, x, y, z);
// Test center.
auto pointColl = GetCollision(item);
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);
int height = GetFloorHeight(floor, x + xFront, y, z + zFront);
pointColl = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber);
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)
{
@ -934,7 +914,7 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
}
else
{
height -= y;
height -= probePos.y;
*ledge = height;
}
@ -955,12 +935,13 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
if (height > 0 && height > *shift)
*shift = height;
roomNumber = item->RoomNumber;
GetFloor(x, y + CLICK(2), z, &roomNumber);
floor = GetFloor(x + xFront, y + CLICK(2), z + zFront, &roomNumber);
ceiling = GetCeiling(floor, x + xFront, y + CLICK(2), z + zFront) - y;
pointColl = GetCollision(probePos.x, probePos.y + CLICK(2), probePos.z, item->RoomNumber);
pointColl = GetCollision(probePos.x + probeOffset.x, probePos.y + CLICK(2), probePos.z + probeOffset.z, pointColl.RoomNumber);
ceiling = pointColl.Position.Ceiling - probePos.y;
if (ceiling <= height)
return 1;
return 1;
if (ceiling >= CLICK(2))
return 1;
else
@ -968,13 +949,14 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
}
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 ((height - ceiling) <= LARA_HEIGHT)
{
if ((height - ceiling) < CLICK(2))
return 0;
*shift = height;
return -2;
}
@ -985,7 +967,9 @@ int LaraTestClimbUpPos(ItemInfo* item, int front, int right, int* shift, int* le
}
}
else
{
return 1;
}
}
}
@ -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
return false;
ResetLaraFlex(item);
ResetPlayerFlex(item);
SetAnimation(item, LA_FALL_START);

View file

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

View file

@ -11,10 +11,10 @@
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_swim.h"
#include "Game/Lara/lara_tests.h"
#include "Game/Setup.h"
#include "Objects/Sink.h"
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/setup.h"
#include "Flow/ScriptInterfaceFlowHandler.h"
#include "ScriptInterfaceLevel.h"
@ -239,9 +239,10 @@ void LaraCollideStop(ItemInfo* item, CollisionInfo* coll)
case LS_TURN_LEFT_SLOW:
case LS_TURN_RIGHT_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.FrameNumber = coll->Setup.OldFrameNumber;
item->Animation.ActiveState = coll->Setup.OldState;
if (TrInput & IN_LEFT)
{
@ -288,9 +289,10 @@ void LaraCollideStopCrawl(ItemInfo* item, CollisionInfo* coll)
case LS_CRAWL_IDLE:
case LS_CRAWL_TURN_LEFT:
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.FrameNumber = coll->Setup.OldFrameNumber;
item->Animation.ActiveState = coll->Setup.OldState;
if (TrInput & IN_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)
{
item->Animation.AnimNumber = LA_CRAWL_IDLE;
item->Animation.FrameNumber = GetFrameNumber(item, 0);
item->Animation.FrameNumber = GetFrameIndex(item, 0);
}
break;
@ -323,9 +325,10 @@ void LaraCollideStopMonkey(ItemInfo* item, CollisionInfo* coll)
case LS_MONKEY_IDLE:
case LS_MONKEY_TURN_LEFT:
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.FrameNumber = coll->Setup.OldFrameNumber;
item->Animation.ActiveState = coll->Setup.OldState;
if (TrInput & IN_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)
{
item->Animation.AnimNumber = LA_MONKEY_IDLE;
item->Animation.FrameNumber = GetFrameNumber(item, 0);
item->Animation.FrameNumber = GetFrameIndex(item, 0);
}
break;
@ -621,13 +624,13 @@ void LaraWaterCurrent(ItemInfo* item, CollisionInfo* coll)
{
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;
lara->WaterCurrentPull.x += ((sink.Strength * SECTOR(1) * phd_sin(headingAngle)) - lara->WaterCurrentPull.x) / 16;
lara->WaterCurrentPull.z += ((sink.Strength * SECTOR(1) * phd_cos(headingAngle)) - lara->WaterCurrentPull.z) / 16;
lara->Context.WaterCurrentPull.x += ((sink.Strength * SECTOR(1) * phd_sin(headingAngle)) - lara->Context.WaterCurrentPull.x) / 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;
}
@ -635,31 +638,31 @@ void LaraWaterCurrent(ItemInfo* item, CollisionInfo* coll)
{
int shift = 0;
if (abs(lara->WaterCurrentPull.x) <= 16)
shift = (abs(lara->WaterCurrentPull.x) > 8) + 2;
if (abs(lara->Context.WaterCurrentPull.x) <= 16)
shift = (abs(lara->Context.WaterCurrentPull.x) > 8) + 2;
else
shift = 4;
lara->WaterCurrentPull.x -= lara->WaterCurrentPull.x >> shift;
lara->Context.WaterCurrentPull.x -= lara->Context.WaterCurrentPull.x >> shift;
if (abs(lara->WaterCurrentPull.x) < 4)
lara->WaterCurrentPull.x = 0;
if (abs(lara->Context.WaterCurrentPull.x) < 4)
lara->Context.WaterCurrentPull.x = 0;
if (abs(lara->WaterCurrentPull.z) <= 16)
shift = (abs(lara->WaterCurrentPull.z) > 8) + 2;
if (abs(lara->Context.WaterCurrentPull.z) <= 16)
shift = (abs(lara->Context.WaterCurrentPull.z) > 8) + 2;
else
shift = 4;
lara->WaterCurrentPull.z -= lara->WaterCurrentPull.z >> shift;
lara->Context.WaterCurrentPull.z -= lara->Context.WaterCurrentPull.z >> shift;
if (abs(lara->WaterCurrentPull.z) < 4)
lara->WaterCurrentPull.z = 0;
if (abs(lara->Context.WaterCurrentPull.z) < 4)
lara->Context.WaterCurrentPull.z = 0;
if (!lara->WaterCurrentPull.x && !lara->WaterCurrentPull.z)
if (!lara->Context.WaterCurrentPull.x && !lara->Context.WaterCurrentPull.z)
return;
}
item->Pose.Position.x += lara->WaterCurrentPull.x / 256;
item->Pose.Position.z += lara->WaterCurrentPull.z / 256;
lara->WaterCurrentActive = 0;
item->Pose.Position.x += lara->Context.WaterCurrentPull.x / 256;
item->Pose.Position.z += lara->Context.WaterCurrentPull.z / 256;
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.Height = LARA_HEIGHT_CRAWL;

View file

@ -434,7 +434,7 @@ void lara_as_crawl_idle(ItemInfo* item, CollisionInfo* coll)
{
item->Animation.TargetState = crawlVaultResult.TargetState;
lara->Control.TurnRate = 0;
ResetLaraFlex(item);
ResetPlayerFlex(item);
return;
}
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.targetDistance = SECTOR(1);
ResetLaraLean(item, 6.0f);
ResetPlayerLean(item, 1 / 6.0f);
if (item->Animation.AnimNumber == LA_CRAWL_TO_HANG_END)
{

View file

@ -18,6 +18,7 @@
#include "Game/Lara/lara_two_guns.h"
#include "Game/misc.h"
#include "Game/savegame.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/Generic/Object/burning_torch.h"
#include "Objects/Generic/Object/objects.h"
@ -28,14 +29,15 @@
#include "Specific/configuration.h"
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Entities::Generic;
using namespace TEN::Input;
using namespace TEN::Math;
ItemInfo* LastTargets[MAX_TARGETS];
ItemInfo* TargetList[MAX_TARGETS];
constexpr auto TARGET_COUNT_MAX = 8;
std::array<ItemInfo*, TARGET_COUNT_MAX> LastTargets = {};
std::array<ItemInfo*, TARGET_COUNT_MAX> TargetList = {};
int FlashGrenadeAftershockTimer = 0;
@ -88,7 +90,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
650,
SECTOR(8),
BLOCK(8),
1,
9,
3,
@ -105,7 +107,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(4.0f),
650,
SECTOR(8),
BLOCK(8),
21,
16,
3,
@ -122,7 +124,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
650,
SECTOR(8),
BLOCK(8),
1,
3,
3,
@ -139,7 +141,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
0,
500,
SECTOR(8),
BLOCK(8),
3,
9,
3,
@ -156,7 +158,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(4.0f),
500,
SECTOR(12),
BLOCK(12),
4,
0,
3,
@ -173,7 +175,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
500,
SECTOR(8),
BLOCK(8),
5,
0,
2,
@ -199,7 +201,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
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)),
@ -207,7 +209,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
400,
SECTOR(8),
BLOCK(8),
3,
0,
2,
@ -224,7 +226,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
500,
SECTOR(8),
BLOCK(8),
20,
0,
2,
@ -241,7 +243,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
500,
SECTOR(8),
BLOCK(8),
6,
0,
2,
@ -258,7 +260,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
500,
SECTOR(8),
BLOCK(8),
30,
0,
2,
@ -275,39 +277,39 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
ANGLE(10.0f),
ANGLE(8.0f),
400,
SECTOR(8),
BLOCK(8),
3,
0,
0,
2,
0,
SFX_TR4_UZI_FIRE,
0
}
};
void InitialiseNewWeapon(ItemInfo* laraItem)
void InitializeNewWeapon(ItemInfo& laraItem)
{
auto& lara = *GetLaraInfo(laraItem);
auto& player = *GetLaraInfo(&laraItem);
lara.LeftArm.FrameNumber = 0;
lara.RightArm.FrameNumber = 0;
lara.LeftArm.Orientation = EulerAngles::Zero;
lara.RightArm.Orientation = EulerAngles::Zero;
lara.TargetEntity = nullptr;
lara.LeftArm.Locked = false;
lara.RightArm.Locked = false;
lara.LeftArm.GunFlash = 0;
lara.RightArm.GunFlash = 0;
player.TargetEntity = nullptr;
player.LeftArm.FrameNumber =
player.RightArm.FrameNumber = 0;
player.LeftArm.Orientation =
player.RightArm.Orientation = EulerAngles::Zero;
player.LeftArm.Locked =
player.RightArm.Locked = false;
player.LeftArm.GunFlash =
player.RightArm.GunFlash = 0;
switch (lara.Control.Weapon.GunType)
switch (player.Control.Weapon.GunType)
{
case LaraWeaponType::Pistol:
case LaraWeaponType::Uzi:
lara.RightArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase;
lara.LeftArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase;
player.RightArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase;
player.LeftArm.FrameBase = Objects[ID_PISTOLS_ANIM].frameBase;
if (lara.Control.HandStatus != HandStatus::Free)
DrawPistolMeshes(laraItem, lara.Control.Weapon.GunType);
if (player.Control.HandStatus != HandStatus::Free)
DrawPistolMeshes(laraItem, player.Control.Weapon.GunType);
break;
@ -317,26 +319,26 @@ void InitialiseNewWeapon(ItemInfo* laraItem)
case LaraWeaponType::GrenadeLauncher:
case LaraWeaponType::HarpoonGun:
case LaraWeaponType::RocketLauncher:
lara.RightArm.FrameBase = Objects[GetWeaponObjectID(lara.Control.Weapon.GunType)].frameBase;
lara.LeftArm.FrameBase = Objects[GetWeaponObjectID(lara.Control.Weapon.GunType)].frameBase;
player.RightArm.FrameBase = Objects[GetWeaponObjectID(player.Control.Weapon.GunType)].frameBase;
player.LeftArm.FrameBase = Objects[GetWeaponObjectID(player.Control.Weapon.GunType)].frameBase;
if (lara.Control.HandStatus != HandStatus::Free)
DrawShotgunMeshes(laraItem, lara.Control.Weapon.GunType);
if (player.Control.HandStatus != HandStatus::Free)
DrawShotgunMeshes(laraItem, player.Control.Weapon.GunType);
break;
case LaraWeaponType::Flare:
lara.RightArm.FrameBase = Objects[ID_FLARE_ANIM].frameBase;
lara.LeftArm.FrameBase = Objects[ID_FLARE_ANIM].frameBase;
player.RightArm.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);
break;
default:
lara.RightArm.FrameBase = g_Level.Anims[laraItem->Animation.AnimNumber].FramePtr;
lara.LeftArm.FrameBase = g_Level.Anims[laraItem->Animation.AnimNumber].FramePtr;
player.RightArm.FrameBase = g_Level.Anims[laraItem.Animation.AnimNumber].FramePtr;
player.LeftArm.FrameBase = g_Level.Anims[laraItem.Animation.AnimNumber].FramePtr;
break;
}
}
@ -346,23 +348,23 @@ Ammo& GetAmmo(LaraInfo& lara, LaraWeaponType weaponType)
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(
(bounds.X1 + bounds.X2) / 2,
bounds.Y1 + (bounds.GetHeight() / 3),
(bounds.Z1 + bounds.Z2) / 2);
float sinY = phd_sin(targetEntity->Pose.Orientation.y);
float cosY = phd_cos(targetEntity->Pose.Orientation.y);
float sinY = phd_sin(targetEntity.Pose.Orientation.y);
float cosY = phd_cos(targetEntity.Pose.Orientation.y);
return GameVector(
targetEntity->Pose.Position.x + ((center.x * cosY) + (center.z * sinY)),
targetEntity->Pose.Position.y + center.y,
targetEntity->Pose.Position.z + ((center.z * cosY) - (center.x * sinY)),
targetEntity->RoomNumber);
targetEntity.Pose.Position.x + ((center.x * cosY) + (center.z * sinY)),
targetEntity.Pose.Position.y + center.y,
targetEntity.Pose.Position.z + ((center.z * cosY) - (center.x * sinY)),
targetEntity.RoomNumber);
}
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)
{
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:
return ID_UZI_ANIM;
@ -456,7 +458,7 @@ GAME_OBJECT_ID GetWeaponObjectMeshID(ItemInfo* laraItem, LaraWeaponType weaponTy
return ID_HK_ANIM;
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:
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)
--lara.LeftArm.GunFlash;
if (player.LeftArm.GunFlash > 0)
--player.LeftArm.GunFlash;
if (lara.RightArm.GunFlash > 0)
--lara.RightArm.GunFlash;
if (player.RightArm.GunFlash > 0)
--player.RightArm.GunFlash;
if (lara.RightArm.GunSmoke > 0)
--lara.RightArm.GunSmoke;
if (player.RightArm.GunSmoke > 0)
--player.RightArm.GunSmoke;
if (lara.LeftArm.GunSmoke > 0)
--lara.LeftArm.GunSmoke;
if (player.LeftArm.GunSmoke > 0)
--player.LeftArm.GunSmoke;
if (FlashGrenadeAftershockTimer)
FlashGrenadeAftershockTimer--;
if (lara.Control.Weapon.GunType == LaraWeaponType::Torch)
if (player.Control.Weapon.GunType == LaraWeaponType::Torch)
{
DoFlameTorch();
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.
if (IsHeld(In::DrawWeapon))
{
// No weapon - no any actions.
if (lara.Control.Weapon.LastGunType != LaraWeaponType::None)
lara.Control.Weapon.RequestGunType = lara.Control.Weapon.LastGunType;
if (player.Control.Weapon.LastGunType != LaraWeaponType::None)
player.Control.Weapon.RequestGunType = player.Control.Weapon.LastGunType;
}
// Draw flare.
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)
lara.Inventory.TotalFlares--;
if (player.Inventory.TotalFlares != -1)
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) ||
lara.Control.Weapon.RequestGunType != lara.Control.Weapon.GunType)
if ((IsHeld(In::DrawWeapon) && player.Control.Weapon.LastGunType != LaraWeaponType::None) ||
player.Control.Weapon.RequestGunType != player.Control.Weapon.GunType)
{
if (lara.Control.IsLow &&
lara.Control.Weapon.RequestGunType >= LaraWeaponType::Shotgun &&
lara.Control.Weapon.RequestGunType != LaraWeaponType::Flare &&
lara.Control.Weapon.RequestGunType != LaraWeaponType::Torch)
if (player.Control.IsLow &&
player.Control.Weapon.RequestGunType >= LaraWeaponType::Shotgun &&
player.Control.Weapon.RequestGunType != LaraWeaponType::Flare &&
player.Control.Weapon.RequestGunType != LaraWeaponType::Torch)
{
if (lara.Control.Weapon.GunType == LaraWeaponType::Flare)
lara.Control.Weapon.RequestGunType = LaraWeaponType::Flare;
if (player.Control.Weapon.GunType == LaraWeaponType::Flare)
player.Control.Weapon.RequestGunType = LaraWeaponType::Flare;
}
else if (lara.Control.Weapon.RequestGunType == LaraWeaponType::Flare ||
(lara.Vehicle == NO_ITEM &&
(lara.Control.Weapon.RequestGunType == LaraWeaponType::HarpoonGun ||
lara.Control.WaterStatus == WaterStatus::Dry ||
(lara.Control.WaterStatus == WaterStatus::Wade &&
lara.WaterSurfaceDist > -Weapons[(int)lara.Control.Weapon.GunType].GunHeight))))
else if (player.Control.Weapon.RequestGunType == LaraWeaponType::Flare ||
(player.Context.Vehicle == NO_ITEM &&
(player.Control.Weapon.RequestGunType == LaraWeaponType::HarpoonGun ||
player.Control.WaterStatus == WaterStatus::Dry ||
(player.Control.WaterStatus == WaterStatus::Wade &&
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);
UndrawFlareMeshes(laraItem);
lara.Flare.ControlLeft = false;
lara.Flare.Life = 0;
player.Flare.ControlLeft = false;
player.Flare.Life = 0;
}
lara.Control.Weapon.GunType = lara.Control.Weapon.RequestGunType;
InitialiseNewWeapon(laraItem);
lara.RightArm.FrameNumber = 0;
lara.LeftArm.FrameNumber = 0;
lara.Control.HandStatus = HandStatus::WeaponDraw;
player.Control.Weapon.GunType = player.Control.Weapon.RequestGunType;
InitializeNewWeapon(laraItem);
player.RightArm.FrameNumber = 0;
player.LeftArm.FrameNumber = 0;
player.Control.HandStatus = HandStatus::WeaponDraw;
}
else
{
lara.Control.Weapon.LastGunType = lara.Control.Weapon.RequestGunType;
player.Control.Weapon.LastGunType = player.Control.Weapon.RequestGunType;
if (lara.Control.Weapon.GunType != LaraWeaponType::Flare)
lara.Control.Weapon.GunType = lara.Control.Weapon.RequestGunType;
if (player.Control.Weapon.GunType != LaraWeaponType::Flare)
{
player.Control.Weapon.GunType = player.Control.Weapon.RequestGunType;
}
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) ||
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 &&
lara.Control.WaterStatus != WaterStatus::Dry &&
(lara.Control.WaterStatus != WaterStatus::Wade ||
lara.WaterSurfaceDist < -Weapons[(int)lara.Control.Weapon.GunType].GunHeight))
else if (player.Control.Weapon.GunType != LaraWeaponType::HarpoonGun &&
player.Control.WaterStatus != WaterStatus::Dry &&
(player.Control.WaterStatus != WaterStatus::Wade ||
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) &&
lara.Control.HandStatus == HandStatus::Busy &&
laraItem->Animation.ActiveState == LS_CRAWL_IDLE &&
laraItem->Animation.AnimNumber == LA_CRAWL_IDLE)
player.Control.HandStatus == HandStatus::Busy &&
laraItem.Animation.ActiveState == LS_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:
if (lara.Control.Weapon.GunType != LaraWeaponType::Flare &&
lara.Control.Weapon.GunType != LaraWeaponType::None)
if (player.Control.Weapon.GunType != LaraWeaponType::Flare &&
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::Revolver:
@ -612,7 +618,7 @@ void HandleWeapon(ItemInfo* laraItem)
if (Camera.type != CameraType::Look && Camera.type != CameraType::Heavy)
Camera.type = CameraType::Combat;
DrawPistols(laraItem, lara.Control.Weapon.GunType);
DrawPistols(laraItem, player.Control.Weapon.GunType);
break;
case LaraWeaponType::Shotgun:
@ -624,7 +630,7 @@ void HandleWeapon(ItemInfo* laraItem)
if (Camera.type != CameraType::Look && Camera.type != CameraType::Heavy)
Camera.type = CameraType::Combat;
DrawShotgun(laraItem, lara.Control.Weapon.GunType);
DrawShotgun(laraItem, player.Control.Weapon.GunType);
break;
case LaraWeaponType::Flare:
@ -632,7 +638,7 @@ void HandleWeapon(ItemInfo* laraItem)
break;
default:
lara.Control.HandStatus = HandStatus::Free;
player.Control.HandStatus = HandStatus::Free;
break;
}
@ -643,14 +649,14 @@ void HandleWeapon(ItemInfo* laraItem)
break;
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::Revolver:
case LaraWeaponType::Uzi:
UndrawPistols(laraItem, lara.Control.Weapon.GunType);
UndrawPistols(laraItem, player.Control.Weapon.GunType);
break;
case LaraWeaponType::Shotgun:
@ -659,7 +665,7 @@ void HandleWeapon(ItemInfo* laraItem)
case LaraWeaponType::GrenadeLauncher:
case LaraWeaponType::RocketLauncher:
case LaraWeaponType::HarpoonGun:
UndrawShotgun(laraItem, lara.Control.Weapon.GunType);
UndrawShotgun(laraItem, player.Control.Weapon.GunType);
break;
case LaraWeaponType::Flare:
@ -674,9 +680,13 @@ void HandleWeapon(ItemInfo* laraItem)
case HandStatus::WeaponReady:
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
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 &&
Camera.type != CameraType::Heavy)
@ -686,19 +696,19 @@ void HandleWeapon(ItemInfo* laraItem)
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);
lara.Control.Weapon.RequestGunType = hasPistols ? LaraWeaponType::Pistol : LaraWeaponType::None;
bool hasPistols = (player.Weapons[(int)LaraWeaponType::Pistol].Present && Objects[ID_PISTOLS_ITEM].loaded);
player.Control.Weapon.RequestGunType = hasPistols ? LaraWeaponType::Pistol : LaraWeaponType::None;
return;
}
}
switch (lara.Control.Weapon.GunType)
switch (player.Control.Weapon.GunType)
{
case LaraWeaponType::Pistol:
case LaraWeaponType::Uzi:
PistolHandler(laraItem, lara.Control.Weapon.GunType);
HandlePistols(laraItem, player.Control.Weapon.GunType);
break;
case LaraWeaponType::Shotgun:
@ -708,8 +718,8 @@ void HandleWeapon(ItemInfo* laraItem)
case LaraWeaponType::RocketLauncher:
case LaraWeaponType::HarpoonGun:
case LaraWeaponType::Revolver:
RifleHandler(laraItem, lara.Control.Weapon.GunType);
LasersightWeaponHandler(laraItem, lara.Control.Weapon.GunType);
RifleHandler(laraItem, player.Control.Weapon.GunType);
LasersightWeaponHandler(laraItem, player.Control.Weapon.GunType);
break;
default:
@ -719,43 +729,43 @@ void HandleWeapon(ItemInfo* laraItem)
break;
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)
lara.LeftArm.FrameNumber = 0;
if (++player.LeftArm.FrameNumber == 110)
player.LeftArm.FrameNumber = 0;
}
}
else
{
lara.LeftArm.FrameNumber = 95;
lara.Flare.ControlLeft = true;
player.LeftArm.FrameNumber = 95;
player.Flare.ControlLeft = true;
}
}
else
{
lara.Flare.ControlLeft = false;
player.Flare.ControlLeft = false;
}
DoFlareInHand(laraItem, lara.Flare.Life);
SetFlareArm(laraItem, lara.LeftArm.FrameNumber);
DoFlareInHand(laraItem, player.Flare.Life);
SetFlareArm(laraItem, player.LeftArm.FrameNumber);
}
break;
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));
DoFlareInHand(laraItem, lara.Flare.Life);
SetFlareArm(laraItem, lara.LeftArm.FrameNumber);
player.Flare.ControlLeft = (player.Context.Vehicle != NO_ITEM || TestState(laraItem.Animation.ActiveState, FlarePoseStates));
DoFlareInHand(laraItem, player.Flare.Life);
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);
}
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& ammo = GetAmmo(lara, weaponType);
auto& player = *GetLaraInfo(&laraItem);
auto& ammo = GetAmmo(player, weaponType);
if (ammo.GetCount() == 0 && !ammo.HasInfinite())
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,
0);
auto muzzleOffset = GetJointPosition(laraItem, LM_RHAND);
auto pos = Vector3i(laraItem->Pose.Position.x, muzzleOffset.y, laraItem->Pose.Position.z);
auto muzzleOffset = GetJointPosition(&laraItem, LM_RHAND);
auto pos = Vector3i(laraItem.Pose.Position.x, muzzleOffset.y, laraItem.Pose.Position.z);
// Calculate ray from wobbled orientation.
auto directionNorm = wobbledArmOrient.ToDirection();
@ -798,7 +809,7 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
auto target = origin + (directionNorm * weapon.TargetDist);
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;
float bestDistance = INFINITY;
for (int i = 0; i < num; i++)
@ -815,11 +826,11 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
}
}
lara.Control.Weapon.HasFired = true;
lara.Control.Weapon.Fired = true;
player.Control.Weapon.HasFired = true;
player.Control.Weapon.Fired = true;
auto vOrigin = GameVector(pos);
short roomNumber = laraItem->RoomNumber;
short roomNumber = laraItem.RoomNumber;
GetFloor(pos.x, pos.y, pos.z, &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.
// It's strange, but this replicates original behaviour until we fully understand what is happening.
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;
}
}
void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
void FindNewTarget(ItemInfo& laraItem, const WeaponInfo& weaponInfo)
{
if (!g_Configuration.AutoTarget)
return;
auto& lara = *GetLaraInfo(laraItem);
auto& player = *GetLaraInfo(&laraItem);
if (BinocularRange)
{
lara.TargetEntity = nullptr;
player.TargetEntity = nullptr;
return;
}
auto origin = GameVector(
laraItem->Pose.Position.x,
GetJointPosition(laraItem, LM_RHAND).y, // Muzzle offset.
laraItem->Pose.Position.z,
laraItem->RoomNumber);
laraItem.Pose.Position.x,
GetJointPosition(&laraItem, LM_RHAND).y, // Muzzle offset.
laraItem.Pose.Position.z,
laraItem.RoomNumber);
ItemInfo* bestEntity = nullptr;
float bestDistance = INFINITY;
short bestYOrient = MAXSHORT;
unsigned int numTargets = 0;
ItemInfo* closestEntityPtr = nullptr;
float closestDistance = INFINITY;
short closestHeadingAngle = MAXSHORT;
unsigned int targetCount = 0;
float maxDistance = weaponInfo.TargetDist;
for (auto* activeCreature : ActiveCreatures)
for (auto* creaturePtr : ActiveCreatures)
{
// Continue loop if no item.
if (activeCreature->ItemNumber == NO_ITEM)
if (creaturePtr->ItemNumber == NO_ITEM)
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)
continue;
@ -887,64 +899,64 @@ void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
continue;
// Assess line of sight.
auto target = GetTargetPoint(&item);
auto target = GetTargetPoint(item);
if (!LOS(&origin, &target))
continue;
// 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 &&
orient.y >= weaponInfo.LockOrientConstraint.first.y &&
orient.x <= weaponInfo.LockOrientConstraint.second.x &&
orient.y <= weaponInfo.LockOrientConstraint.second.y)
{
TargetList[numTargets] = &item;
++numTargets;
TargetList[targetCount] = &item;
++targetCount;
if (distance < bestDistance &&
abs(orient.y) < (bestYOrient + ANGLE(15.0f)))
if (distance < closestDistance &&
abs(orient.y) < (closestHeadingAngle + ANGLE(15.0f)))
{
bestEntity = &item;
bestDistance = distance;
bestYOrient = abs(orient.y);
closestEntityPtr = &item;
closestDistance = distance;
closestHeadingAngle = abs(orient.y);
}
}
}
TargetList[numTargets] = nullptr;
if (!TargetList[0])
TargetList[targetCount] = nullptr;
if (TargetList[0] == nullptr)
{
lara.TargetEntity = nullptr;
player.TargetEntity = nullptr;
}
else
{
for (int slot = 0; slot < MAX_TARGETS; ++slot)
for (const auto* targetPtr : TargetList)
{
if (!TargetList[slot])
lara.TargetEntity = nullptr;
if (targetPtr == nullptr)
player.TargetEntity = nullptr;
if (TargetList[slot] == lara.TargetEntity)
if (targetPtr == player.TargetEntity)
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;
}
else if (IsClicked(In::SwitchTarget))
{
lara.TargetEntity = nullptr;
player.TargetEntity = nullptr;
bool flag = true;
for (int match = 0; match < MAX_TARGETS && TargetList[match]; ++match)
for (const auto& targetPtr : TargetList)
{
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;
break;
@ -953,8 +965,8 @@ void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
if (!doLoop)
{
lara.TargetEntity = TargetList[match];
if (lara.TargetEntity)
player.TargetEntity = targetPtr;
if (player.TargetEntity)
flag = false;
break;
@ -963,44 +975,44 @@ void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
if (flag)
{
lara.TargetEntity = bestEntity;
player.TargetEntity = closestEntityPtr;
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[0] = lara.TargetEntity;
LastTargets[0] = player.TargetEntity;
}
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;
lara.LeftArm.Locked = false;
lara.TargetArmOrient = EulerAngles::Zero;
player.RightArm.Locked = false;
player.LeftArm.Locked = false;
player.TargetArmOrient = EulerAngles::Zero;
return;
}
auto origin = GameVector(
laraItem->Pose.Position.x,
GetJointPosition(laraItem, LM_RHAND).y, // Muzzle offset.
laraItem->Pose.Position.z,
laraItem->RoomNumber);
auto target = GetTargetPoint(lara.TargetEntity);
laraItem.Pose.Position.x,
GetJointPosition(&laraItem, LM_RHAND).y, // Muzzle offset.
laraItem.Pose.Position.z,
laraItem.RoomNumber);
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))
{
@ -1009,41 +1021,41 @@ void LaraTargetInfo(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
orient.x <= weaponInfo.LockOrientConstraint.second.x &&
orient.y <= weaponInfo.LockOrientConstraint.second.y)
{
lara.RightArm.Locked = true;
lara.LeftArm.Locked = true;
player.RightArm.Locked = true;
player.LeftArm.Locked = true;
}
else
{
if (lara.LeftArm.Locked)
if (player.LeftArm.Locked)
{
if (orient.x < weaponInfo.LeftOrientConstraint.first.x ||
orient.y < weaponInfo.LeftOrientConstraint.first.y ||
orient.x > weaponInfo.LeftOrientConstraint.second.x ||
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 ||
orient.y < weaponInfo.RightOrientConstraint.first.y ||
orient.x > weaponInfo.RightOrientConstraint.second.x ||
orient.y > weaponInfo.RightOrientConstraint.second.y)
{
lara.RightArm.Locked = false;
player.RightArm.Locked = false;
}
}
}
}
else
{
lara.RightArm.Locked = false;
lara.LeftArm.Locked = false;
player.RightArm.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)

View file

@ -5,8 +5,6 @@ class EulerAngles;
struct CollisionInfo;
struct ItemInfo;
constexpr auto MAX_TARGETS = 8;
enum class FireWeaponType
{
Miss = -1,
@ -48,20 +46,20 @@ struct WeaponInfo
extern int FlashGrenadeAftershockTimer;
extern WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons];
void InitialiseNewWeapon(ItemInfo* laraItem);
void InitializeNewWeapon(ItemInfo& laraItem);
Ammo& GetAmmo(LaraInfo& lara, LaraWeaponType weaponType);
GameVector GetTargetPoint(ItemInfo* targetEntity);
GameVector GetTargetPoint(ItemInfo& targetEntity);
HolsterSlot GetWeaponHolsterSlot(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 AimWeapon(ItemInfo* laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo);
FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, ItemInfo* laraItem, const EulerAngles& armOrient);
void HandleWeapon(ItemInfo& laraItem);
void AimWeapon(ItemInfo& laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo);
FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo& targetEntity, ItemInfo& laraItem, const EulerAngles& armOrient);
void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo);
void LaraTargetInfo(ItemInfo* laraItem, const WeaponInfo& weaponInfo);
void FindNewTarget(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 SmashItem(short itemNumber);

View file

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

View file

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

View file

@ -13,13 +13,13 @@
#include "Game/Lara/lara_collide.h"
#include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_tests.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Renderer/Renderer11.h"
#include "Scripting/Include/ScriptInterfaceLevel.h"
#include "Sound/sound.h"
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/setup.h"
#include "Objects/TR2/Vehicles/skidoo.h"
#include "Objects/TR3/Vehicles/big_gun.h"
@ -33,7 +33,7 @@
using namespace TEN::Control::Volumes;
using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::Drip;
using namespace TEN::Floordata;
using namespace TEN::Collision::Floordata;
using namespace TEN::Input;
using namespace TEN::Math;
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)))) &&
(!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.
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)))))
{
ResetLaraFlex(item, 12.0f);
ResetPlayerFlex(item, 0.1f);
}
// Apply and reset turn rate.
@ -94,20 +94,20 @@ bool HandleLaraVehicle(ItemInfo* item, CollisionInfo* coll)
{
auto* lara = GetLaraInfo(item);
if (lara->Vehicle == NO_ITEM)
if (lara->Context.Vehicle == NO_ITEM)
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;
SetAnimation(item, LA_FALL_START);
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:
QuadBikeControl(item, coll);
@ -143,7 +143,7 @@ bool HandleLaraVehicle(ItemInfo* item, CollisionInfo* coll)
// Boats are processed like normal items in loop.
default:
HandleWeapon(item);
HandleWeapon(*item);
}
return true;
@ -289,7 +289,7 @@ void DoLaraStep(ItemInfo* item, CollisionInfo* coll)
if (TestLaraStepUp(item, coll))
{
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;
return;
@ -298,7 +298,7 @@ void DoLaraStep(ItemInfo* item, CollisionInfo* coll)
else if (TestLaraStepDown(item, coll))
{
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;
return;
@ -755,29 +755,30 @@ void SetLaraVault(ItemInfo* item, CollisionInfo* coll, VaultTestResult vaultResu
{
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.TurnRate = 0;
if (vaultResult.SnapToLedge)
{
SnapItemToLedge(item, coll, 0.2f, false);
lara->TargetOrientation = EulerAngles(0, coll->NearestLedgeAngle, 0);
lara->Context.TargetOrientation = EulerAngles(0, coll->NearestLedgeAngle, 0);
}
else
{
lara->TargetOrientation = EulerAngles(0, item->Pose.Orientation.y, 0);
lara->Context.TargetOrientation = EulerAngles(0, item->Pose.Orientation.y, 0);
}
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))
height = -CLICK(3.5f);
else if (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.y = 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;
return;
}
@ -935,9 +936,9 @@ void SetLaraCornerAnimation(ItemInfo* item, CollisionInfo* coll, bool flip)
else
SetAnimation(item, LA_HANG_IDLE);
item->Pose.Position = lara->NextCornerPos.Position;
item->Pose.Orientation.y = lara->NextCornerPos.Orientation.y;
coll->Setup.OldPosition = lara->NextCornerPos.Position;
item->Pose.Position = lara->Context.NextCornerPos.Position;
item->Pose.Orientation.y = lara->Context.NextCornerPos.Orientation.y;
coll->Setup.OldPosition = lara->Context.NextCornerPos.Position;
}
}
@ -958,98 +959,49 @@ void SetLaraVehicle(ItemInfo* item, ItemInfo* vehicle)
if (vehicle == nullptr)
{
if (lara->Vehicle != NO_ITEM)
g_Level.Items[lara->Vehicle].Active = false;
if (lara->Context.Vehicle != NO_ITEM)
g_Level.Items[lara->Context.Vehicle].Active = false;
lara->Vehicle = NO_ITEM;
lara->Context.Vehicle = NO_ITEM;
}
else
{
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)
{
TENLog(std::string("ResetLaraLean() attempted division by zero!"), LogLevel::Warning);
return;
}
rate = abs(rate);
if (resetRoll)
item->Pose.Orientation.Lerp(EulerAngles(item->Pose.Orientation.x, item->Pose.Orientation.y, 0), alpha);
if (resetPitch)
{
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)
{
if (abs(item->Pose.Orientation.z) > ANGLE(0.1f))
item->Pose.Orientation.z += item->Pose.Orientation.z / -rate;
else
item->Pose.Orientation.z = 0;
}
item->Pose.Orientation.Lerp(EulerAngles(0, item->Pose.Orientation.y, item->Pose.Orientation.z), alpha);
}
void ResetLaraFlex(ItemInfo* item, float rate)
void ResetPlayerFlex(ItemInfo* item, float alpha)
{
auto* lara = GetLaraInfo(item);
auto& player = 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;
player.ExtraHeadRot.Lerp(EulerAngles::Zero, alpha);
player.ExtraTorsoRot.Lerp(EulerAngles::Zero, alpha);
}
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;
bool doPulse = (GlobalCounter & 0x0F) % 0x0F == 1;
if (item->HitPoints == 0)
return;
bool doPulse = ((GlobalCounter & 0x0F) % 0x0F == 1);
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 SetLaraVehicle(ItemInfo* item, ItemInfo* vehicle = nullptr);
void ResetLaraLean(ItemInfo* item, float rate = 1.0f, bool resetRoll = true, bool resetPitch = true);
void ResetLaraFlex(ItemInfo* item, float rate = 1.0f);
void ResetPlayerLean(ItemInfo* item, float alpha = 1.0f, bool resetRoll = true, bool resetPitch = true);
void ResetPlayerFlex(ItemInfo* item, float alpha = 1.0f);
void RumbleLaraHealthCondition(ItemInfo* item);

View file

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

View file

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

View file

@ -10,11 +10,11 @@
#include "Game/Lara/lara_basic.h"
#include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_slide.h"
#include "Game/Setup.h"
#include "Flow/ScriptInterfaceFlowHandler.h"
#include "Sound/sound.h"
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Input;
@ -126,7 +126,7 @@ void lara_col_jump_forward(ItemInfo* item, CollisionInfo* coll)
// Collision: lara_col_freefall()
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);

View file

@ -56,7 +56,7 @@ void lara_as_pickup_flare(ItemInfo* item, CollisionInfo* coll)
Camera.targetElevation = -ANGLE(15.0f);
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;
}
@ -102,16 +102,14 @@ void lara_col_turn_switch(ItemInfo* item, CollisionInfo* coll)
{
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->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)
{
SetAnimation(item, LA_TURNSWITCH_PUSH_CLOCKWISE_END);
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.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)
{
auto* lara = GetLaraInfo(item);
auto* pulleyItem = &g_Level.Items[lara->InteractedItem];
auto* pulleyItem = &g_Level.Items[lara->Context.InteractedItem];
lara->Control.CanLook = false;
coll->Setup.EnableSpasm = false;
@ -220,7 +218,7 @@ void lara_as_pulley(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = LS_IDLE;
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)
{
@ -250,7 +248,7 @@ void lara_as_pulley(ItemInfo* item, CollisionInfo* coll)
}
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;
}
@ -284,11 +282,11 @@ void lara_as_horizontal_bar_swing(ItemInfo* item, CollisionInfo* coll)
void lara_as_horizontal_bar_leap(ItemInfo* item, CollisionInfo* coll)
{
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;
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
if (item->Animation.FrameNumber == GetAnimData(*item).frameBase)
{
int distance = 0;
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)
{
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;
}
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;
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.MaxXForward < 6750)
{
@ -690,7 +688,7 @@ void lara_col_rope_swing(ItemInfo* item, CollisionInfo* coll)
if (TrInput & IN_JUMP)
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);
}
@ -706,9 +704,9 @@ void lara_as_rope_up(ItemInfo* item, CollisionInfo* coll)
{
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;
}

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
#pragma once
#include "Game/control/control.h"
#include "Math/Math.h"
#include "Specific/clock.h"
enum class LaraWeaponType;
class GameVector;
class Pose;
class Vector3i;
struct ItemInfo;
enum class GrenadeType
@ -26,28 +26,28 @@ enum class ProjectileType
FlashGrenade
};
void AnimateShotgun(ItemInfo* laraItem, LaraWeaponType weaponType);
void ReadyShotgun(ItemInfo* laraItem, LaraWeaponType weaponType);
void FireShotgun(ItemInfo* laraItem);
void DrawShotgun(ItemInfo* laraItem, LaraWeaponType weaponType);
void UndrawShotgun(ItemInfo* laraItem, LaraWeaponType weaponType);
void DrawShotgunMeshes(ItemInfo* laraItem, LaraWeaponType weaponType);
void UndrawShotgunMeshes(ItemInfo* laraItem, LaraWeaponType weaponType);
void AnimateShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void ReadyShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void FireShotgun(ItemInfo& laraItem);
void DrawShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawShotgun(ItemInfo& laraItem, LaraWeaponType weaponType);
void DrawShotgunMeshes(ItemInfo& laraItem, LaraWeaponType weaponType);
void UndrawShotgunMeshes(ItemInfo& laraItem, LaraWeaponType weaponType);
ItemInfo* FireHarpoon(ItemInfo* laraItem);
ItemInfo* FireHarpoon(ItemInfo& laraItem);
void HarpoonBoltControl(short itemNumber);
void FireGrenade(ItemInfo* laraItem);
void FireGrenade(ItemInfo& laraItem);
void GrenadeControl(short itemNumber);
void FireRocket(ItemInfo* laraItem);
void FireRocket(ItemInfo& laraItem);
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 FireCrossBowFromLaserSight(ItemInfo* laraItem, GameVector* origin, GameVector* target);
void FireHK(ItemInfo* laraItem, int mode);
void RifleHandler(ItemInfo* laraItem, LaraWeaponType weaponType);
void LasersightWeaponHandler(ItemInfo* item, LaraWeaponType weaponType);
void FireHK(ItemInfo& laraItem, int mode);
void RifleHandler(ItemInfo& laraItem, LaraWeaponType weaponType);
void LasersightWeaponHandler(ItemInfo& item, LaraWeaponType weaponType);
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);

View file

@ -10,9 +10,9 @@
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_tests.h"
#include "Game/items.h"
#include "Game/Setup.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/Input/Input.h"
#include "Specific/setup.h"
#include "Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/level.h"
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.
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);
return;
}
@ -500,7 +500,7 @@ void lara_col_slopehang(ItemInfo* item, CollisionInfo* coll)
{
// Return to climbing mode.
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.
if (TrInput & IN_LEFT || TrInput & IN_RIGHT)
@ -641,7 +641,7 @@ void lara_as_slopeclimbup(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION))
{
int frame = GetCurrentRelativeFrameNumber(item);
int frame = GetFrameNumber(item);
int length = GetFrameCount(item->Animation.AnimNumber);
int dPos = CLICK(1) - (frame * CLICK(1) / length);
@ -671,7 +671,7 @@ void lara_as_slopeclimbdown(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION))
{
int frame = GetCurrentRelativeFrameNumber(item);
int frame = GetFrameNumber(item);
int length = GetFrameCount(item->Animation.AnimNumber);
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.
if (item->Animation.AnimNumber == LA_OVERHANG_MONKEY_SLOPE_CONVEX)
{
int frame = GetCurrentRelativeFrameNumber(item);
int frame = GetFrameNumber(item);
int numFrames = GetFrameCount(item->Animation.AnimNumber);
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.
else if (item->Animation.AnimNumber == LA_OVERHANG_SLOPE_MONKEY_CONCAVE)
{
int frame = GetCurrentRelativeFrameNumber(item);
int frame = GetFrameNumber(item);
int numFrames = GetFrameCount(item->Animation.AnimNumber);
float frac = (frame * 1.25f) / (float)(numFrames);
@ -810,7 +810,7 @@ void lara_as_sclimbend(ItemInfo* item, CollisionInfo* coll)
break;
}
GetLaraInfo(item)->NextCornerPos.Orientation.z = 0;
GetLaraInfo(item)->Context.NextCornerPos.Orientation.z = 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 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;
@ -1064,7 +1064,7 @@ void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
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;
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 (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) &&
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);
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)
{

View file

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

View file

@ -22,7 +22,7 @@
using namespace TEN::Input;
using namespace TEN::Math;
using namespace TEN::Renderer;
using namespace TEN::Floordata;
using namespace TEN::Collision::Floordata;
using std::vector;
// -----------------------------
@ -170,7 +170,7 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll)
}
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))
{
item->Animation.TargetState = LS_LADDER_IDLE;
@ -273,7 +273,7 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll)
if (TestLaraMonkeyGrab(item, coll))
{
SetAnimation(item, LA_REACH_TO_MONKEY);
ResetLaraFlex(item);
ResetPlayerFlex(item);
item->Animation.Velocity.z = 0;
item->Animation.Velocity.y = 0;
item->Animation.IsAirborne = false;
@ -300,7 +300,7 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll)
if (TestHangSwingIn(item, coll))
{
SetAnimation(item, LA_REACH_TO_HANG_OSCILLATE);
ResetLaraFlex(item);
ResetPlayerFlex(item);
}
else
SetAnimation(item, LA_REACH_TO_HANG);
@ -575,12 +575,12 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
// Store next position
item->Pose = cornerResult.RealPositionResult;
lara->NextCornerPos.Position = Vector3i(
lara->Context.NextCornerPos.Position = Vector3i(
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),
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;
item->Pose = cornerResult.ProbeResult;
@ -596,9 +596,9 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
if (lara->Control.CanClimbLadder)
{
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;
}
}
@ -634,10 +634,10 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
// Store next position
item->Pose = cornerResult.RealPositionResult;
lara->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->NextCornerPos.Position.z = item->Pose.Position.z;
lara->NextCornerPos.Orientation.y = item->Pose.Orientation.y;
lara->Context.NextCornerPos.Position.x = item->Pose.Position.x;
lara->Context.NextCornerPos.Position.y = GetCollision(item, item->Pose.Orientation.y, coll->Setup.Radius * 1.25f, -(abs(bounds.Y1) + LARA_HEADROOM)).Position.Floor + abs(bounds.Y1);
lara->Context.NextCornerPos.Position.z = item->Pose.Position.z;
lara->Context.NextCornerPos.Orientation.y = item->Pose.Orientation.y;
lara->Control.MoveAngle = item->Pose.Orientation.y;
item->Pose = cornerResult.ProbeResult;
@ -653,9 +653,9 @@ CornerType TestLaraHangCorner(ItemInfo* item, CollisionInfo* coll, float testAng
if (lara->Control.CanClimbLadder)
{
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;
}
}
@ -1219,7 +1219,7 @@ bool TestLaraPose(ItemInfo* item, CollisionInfo* coll)
lara->Control.HandStatus == HandStatus::Free && // Hands are free.
(lara->Control.Weapon.GunType != LaraWeaponType::Flare || // Flare is not being handled.
lara->Flare.Life) &&
lara->Vehicle == NO_ITEM) // Not in a vehicle.
lara->Context.Vehicle == NO_ITEM) // Not in a vehicle.
{
return true;
}
@ -1645,7 +1645,7 @@ bool TestLaraCrouchRoll(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item);
// Assess water depth.
if (lara->WaterSurfaceDist < -CLICK(1))
if (lara->Context.WaterSurfaceDist < -CLICK(1))
return false;
// Assess continuity of path.
@ -1830,7 +1830,7 @@ VaultTestResult TestLaraVaultTolerance(ItemInfo* item, CollisionInfo* coll, Vaul
auto probeMiddle = GetCollision(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
// Check swamp depth (if applicable).
@ -2065,7 +2065,7 @@ VaultTestResult TestLaraVault(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION) || lara->Control.HandStatus != HandStatus::Free)
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 };
VaultTestResult vaultResult;
@ -2154,24 +2154,24 @@ bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION) || !(TrInput & IN_FORWARD) || lara->Control.HandStatus != HandStatus::Free)
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;
// Auto jump to ladder.
auto vaultResult = TestLaraLadderAutoJump(item, coll);
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.
lara->Control.CalculatedJumpVelocity = -3 - sqrt(-9600 - 12 * std::max((vaultResult.Height - item->Pose.Position.y + CLICK(0.2f)), -CLICK(7.1f)));
// 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->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.FrameNumber = GetFrameNumber(item, 0);
item->Animation.FrameNumber = GetFrameIndex(item, 0);
item->Animation.TargetState = LS_JUMP_UP;
item->Animation.ActiveState = LS_IDLE;
lara->Control.TurnRate = 0;
ShiftItem(item, coll);
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);
return true;
@ -2183,7 +2183,7 @@ bool TestAndDoLaraLadderClimb(ItemInfo* item, CollisionInfo* coll)
TestLaraClimbIdle(item, coll))
{
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.ActiveState = LS_IDLE;
lara->Control.HandStatus = HandStatus::Busy;

View file

@ -9,504 +9,417 @@
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_struct.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 "Specific/level.h"
#include "Specific/setup.h"
#include "Specific/Input/Input.h"
#include "Specific/level.h"
using namespace TEN::Input;
using namespace TEN::Math::Random;
using namespace TEN::Math;
struct PistolDef
struct WeaponAnimData
{
short ObjectNumber;
int Draw1Anim2;
int Draw1Anim;
int Draw2Anim;
int RecoilAnim;
GAME_OBJECT_ID ObjectID = GAME_OBJECT_ID::ID_NO_OBJECT;
int Draw1Anim2 = 0;
int Draw1Anim = 0;
int Draw2Anim = 0;
int RecoilAnim = 0;
};
PistolDef PistolsTable[4] =
static WeaponAnimData GetWeaponAnimData(LaraWeaponType weaponType)
{
{ ID_LARA, 0, 0, 0, 0 },
{ ID_PISTOLS_ANIM, 4, 5, 13, 24 },
{ ID_REVOLVER_ANIM , 7, 8, 15, 29 },
{ ID_UZI_ANIM, 4, 5, 13, 24 }
};
void AnimatePistols(ItemInfo* laraItem, LaraWeaponType weaponType)
{
auto* lara = GetLaraInfo(laraItem);
auto* weapon = &Weapons[(int)weaponType];
auto* p = &PistolsTable[(int)lara->Control.Weapon.GunType];
int fired = false;
if (laraItem->MeshBits.TestAny())
static const auto DEFAULT_WEAPON_ANIM_DATA = WeaponAnimData{ ID_LARA, 0, 0, 0, 0 };
static const auto ANIM_DATA_MAP = std::unordered_map<LaraWeaponType, WeaponAnimData>
{
if (lara->LeftArm.GunSmoke)
{
auto offset = Vector3i::Zero;
switch (weaponType)
{
case LaraWeaponType::Pistol:
offset = Vector3i(4, 128, 40);
break;
{ 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 } }
};
case LaraWeaponType::Revolver:
offset = Vector3i(16, 160, 56);
break;
case LaraWeaponType::Uzi:
offset = Vector3i(8, 140, 48);
break;
}
auto pos = GetJointPosition(laraItem, LM_LHAND, offset);
TriggerGunSmoke(pos.x, pos.y, pos.z, 0, 0, 0, 0, weaponType, lara->LeftArm.GunSmoke);
}
if (lara->RightArm.GunSmoke)
{
auto offset = Vector3i::Zero;
switch (weaponType)
{
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);
auto it = ANIM_DATA_MAP.find(weaponType);
return ((it != ANIM_DATA_MAP.end()) ? it->second : DEFAULT_WEAPON_ANIM_DATA);
}
void PistolHandler(ItemInfo* laraItem, LaraWeaponType weaponType)
static Vector3i GetWeaponSmokeRelOffset(LaraWeaponType weaponType, bool isRightWeapon)
{
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)
switch (weaponType)
{
lara->ExtraTorsoRot.x = lara->LeftArm.Orientation.x / 2;
lara->ExtraTorsoRot.y = lara->LeftArm.Orientation.y / 2;
case LaraWeaponType::Pistol:
return Vector3i(isRightWeapon ? -16 : 4, 128, 40);
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;
case LaraWeaponType::Revolver:
return Vector3i(isRightWeapon ? -32 : 16, 160, 56);
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;
case LaraWeaponType::Uzi:
return Vector3i(isRightWeapon ? -16 : 8, 140, 48);
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);
default:
return Vector3i::Zero;
}
}
void ReadyPistols(ItemInfo* laraItem, LaraWeaponType weaponType)
static void SetArmInfo(const ItemInfo& laraItem, ArmInfo& arm, int frame)
{
auto* lara = GetLaraInfo(laraItem);
const auto& player = GetLaraInfo(laraItem);
const auto& weaponAnimData = GetWeaponAnimData(player.Control.Weapon.GunType);
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;
}
int animBase = Objects[(int)weaponAnimData.ObjectID].animIndex;
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)
if (frame < weaponAnimData.Draw1Anim)
{
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;
else if (frame < pistols.Draw2Anim)
}
else if (frame < weaponAnimData.Draw2Anim)
{
arm.AnimNumber = animBase + 1;
else if (frame < pistols.RecoilAnim)
}
else if (frame < weaponAnimData.RecoilAnim)
{
arm.AnimNumber = animBase + 2;
}
else
{
arm.AnimNumber = animBase + 3;
}
arm.FrameNumber = frame;
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)
lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty;
lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty;
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;
player.Control.HandStatus = HandStatus::WeaponReady;
player.TargetEntity = nullptr;
player.LeftArm.FrameBase =
player.RightArm.FrameBase = Objects[GetWeaponObjectID(weaponType)].frameBase;
player.LeftArm.FrameNumber =
player.RightArm.FrameNumber = 0;
player.LeftArm.Orientation =
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;
if (lara->Weapons[(int)weaponType].Present)
lara->Control.Weapon.HolsterInfo.RightHolster = GetWeaponHolsterSlot(weaponType);
else
lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty;
}
void UndrawPistolMeshLeft(ItemInfo* laraItem, LaraWeaponType weaponType)
{
auto* lara = GetLaraInfo(laraItem);
if (weaponType != LaraWeaponType::Revolver)
// Spawn weapon smoke.
if (laraItem.MeshBits.TestAny() && arm.GunSmoke)
{
laraItem->Model.MeshIndex[LM_LHAND] = laraItem->Model.BaseMesh + LM_LHAND;
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);
}
if (lara->Weapons[(int)weaponType].Present)
lara->Control.Weapon.HolsterInfo.LeftHolster = GetWeaponHolsterSlot(weaponType);
else
lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty;
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
{
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;
}
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
{
// 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)
player.Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty;
player.Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty;
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
{
holster = HolsterSlot::Empty;
}
}

View file

@ -1,12 +1,13 @@
#pragma once
#include "Game/Lara/lara_struct.h"
void AnimatePistols(ItemInfo* laraItem, LaraWeaponType weaponType);
void PistolHandler(ItemInfo* laraItem, LaraWeaponType weaponType);
void ReadyPistols(ItemInfo* laraItem, LaraWeaponType weaponType);
void DrawPistols(ItemInfo* laraItem, LaraWeaponType weaponType);
void UndrawPistols(ItemInfo* laraItem, LaraWeaponType weaponType);
void SetArmInfo(ItemInfo* laraItem, ArmInfo& arm, int frame);
void DrawPistolMeshes(ItemInfo* laraItem, LaraWeaponType weaponType);
void UndrawPistolMeshRight(ItemInfo* laraItem, LaraWeaponType weaponType);
void UndrawPistolMeshLeft(ItemInfo* laraItem, LaraWeaponType weaponType);
enum class LaraWeaponType;
struct ItemInfo;
void HandlePistols(ItemInfo& laraItem, LaraWeaponType weaponType);
void AnimatePistols(ItemInfo& laraItem, LaraWeaponType weaponType);
void DrawPistols(ItemInfo& laraItem, LaraWeaponType weaponType);
void DrawPistolMeshes(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
#include "Objects/objectslist.h"
#include "Game/control/box.h"
#include "Math/Math.h"
#include "Renderer/Renderer11Enums.h"
#include "Specific/level.h"
enum class ZoneType;
struct CollisionInfo;
struct ItemInfo;
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
{
Skeleton,
@ -23,7 +23,8 @@ enum class LotType
Flyer,
Blockable, // For large creatures such as trex and shiva.
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
@ -52,53 +53,59 @@ enum ShatterType
struct ObjectInfo
{
int nmeshes;
int meshIndex;
int boneIndex;
int frameBase;
bool loaded = false; // IsLoaded
int nmeshes; // BoneCount
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;
int animIndex;
short HitPoints;
short pivotLength;
short radius;
ShadowMode shadowType;
short biteOffset;
bool loaded;
bool intelligent;
bool nonLot;
bool waterCreature;
bool usingDrawAnimatingItem;
HitEffect hitEffect;
bool undead;
bool isPickup;
bool isPuzzleHole;
ShadowMode shadowType;
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;
std::function<void(short itemNumber)> initialise;
std::function<void(short itemNumber)> control;
std::function<void(short itemNumber)> Initialize;
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)> ceiling;
std::function<int(short itemNumber)> floorBorder;
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;
std::function<int(short itemNumber)> floorBorder;
std::function<int(short itemNumber)> ceilingBorder;
/// <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>
/// <param name="boneID">the mesh id - 1</param>
/// <param name="flags">can be ROT_X, ROT_Y, ROT_Z or all.</param>
void SetBoneRotationFlags(int boneID, int flags)
/// <param name="boneNumber:">Mesh number - 1.</param>
/// <param name="flags:">JointRotationFlags enum.</param>
void SetBoneRotationFlags(int boneNumber, int flags)
{
g_Level.Bones[boneIndex + boneID * 4] |= flags;
g_Level.Bones[boneIndex + (boneNumber * 4)] |= flags;
}
/// <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>
/// <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)
{
// 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 STATIC_INFO StaticObjects[MAX_STATICS];
void InitialiseGameFlags();
void InitialiseSpecialEffects();
void InitialiseObjects();
void InitializeGameFlags();
void InitializeSpecialEffects();
void InitializeObjects();

View file

@ -8,12 +8,12 @@
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/Generic/Object/rope.h"
#include "Renderer/Renderer11.h"
#include "Sound/sound.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Entities::Generic;
using namespace TEN::Math;
@ -74,10 +74,10 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased)
{
auto& player = GetLaraInfo(item);
if (player.Control.CalculatedJumpVelocity != 0)
if (player.Context.CalcJumpVelocity != 0)
{
item.Animation.Velocity.y = player.Control.CalculatedJumpVelocity;
player.Control.CalculatedJumpVelocity = 0;
item.Animation.Velocity.y = player.Context.CalcJumpVelocity;
player.Context.CalcJumpVelocity = 0;
}
}
}
@ -121,8 +121,8 @@ static void PerformAnimCommands(ItemInfo& item, bool isFrameBased)
auto& player = GetLaraInfo(item);
if (playAlways ||
(playOnLand && (player.WaterSurfaceDist >= -SHALLOW_WATER_DEPTH || player.WaterSurfaceDist == NO_HEIGHT)) ||
(playInWater && player.WaterSurfaceDist < -SHALLOW_WATER_DEPTH && player.WaterSurfaceDist != NO_HEIGHT && !TestEnvironment(ENV_FLAG_SWAMP, &item)))
(playOnLand && (player.Context.WaterSurfaceDist >= -SHALLOW_WATER_DEPTH || player.Context.WaterSurfaceDist == NO_HEIGHT)) ||
(playInWater && player.Context.WaterSurfaceDist < -SHALLOW_WATER_DEPTH && player.Context.WaterSurfaceDist != NO_HEIGHT && !TestEnvironment(ENV_FLAG_SWAMP, &item)))
{
SoundEffect(commandDataPtr[1] & 0x3FFF, &item.Pose, SoundEnvironment::Always);
}
@ -216,6 +216,19 @@ void AnimateItem(ItemInfo* item)
{
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);
@ -336,19 +349,23 @@ bool HasStateDispatch(ItemInfo* item, int targetState)
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));
}
bool TestLastFrame(ItemInfo* item, int animNumber)
{
if (animNumber == NO_ANIM)
animNumber = item->Animation.AnimNumber;
const auto& object = Objects[item->Animation.AnimObjectID];
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;
const auto& anim = GetAnimData(*item, animNumber);
const auto& anim = GetAnimData(object, animNumber);
return (item->Animation.FrameNumber >= anim.frameEnd);
}
@ -380,32 +397,49 @@ void TranslateItem(ItemInfo* item, const Vector3& direction, float 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];
int animIndex = object.animIndex + animNumber;
const auto& animObject = Objects[animObjectID];
int animIndex = animObject.animIndex + animNumber;
// Animation already set; return early.
if (item->Animation.AnimNumber == animIndex)
return;
// Animation doesn't exist; return early.
// Animation is missing; return early.
if (animIndex < 0 || animIndex >= g_Level.Anims.size())
{
TENLog(
std::string("Attempted to set nonexistent animation ") + std::to_string(animNumber) +
std::string(" for object ") + std::to_string(item->ObjectNumber),
std::string("Attempted to set missing animation ") + std::to_string(animNumber) +
(animObjectID == item.ObjectNumber ? std::string() : std::string(" from object ") + GetObjectName(animObjectID)) +
std::string(" for object ") + GetObjectName(item.ObjectNumber),
LogLevel::Warning);
return;
}
const auto& anim = GetAnimData(*item, animNumber);
item->Animation.AnimNumber = animIndex;
item->Animation.FrameNumber = anim.frameBase + frameNumber;
item->Animation.ActiveState =
item->Animation.TargetState = anim.ActiveState;
const auto& anim = GetAnimData(animObject, animNumber);
int frameIndex = anim.frameBase + frameNumber;
// Animation already set; return early.
if (item.Animation.AnimObjectID == animObjectID &&
item.Animation.AnimNumber == animIndex &&
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)
@ -413,6 +447,12 @@ AnimData& GetAnimData(int 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)
{
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)
{
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);
}
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)
{
// Active and target states already match; return early.
@ -464,123 +632,6 @@ bool GetStateDispatch(ItemInfo* item, const AnimData& anim)
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)
{
// TODO: to refactor
@ -613,6 +664,16 @@ Vector3i GetJointPosition(ItemInfo* item, int jointIndex, const Vector3i& relOff
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)
{
const auto& object = Objects[objectID];

View file

@ -4,13 +4,16 @@
using namespace TEN::Math;
enum GAME_OBJECT_ID : short;
class EulerAngles;
class Pose;
class Vector3i;
struct CreatureBiteInfo;
struct ItemInfo;
struct ObjectInfo;
// NOTES:
// animNumber: Relative entity animation ID.
// animNumber: Relative animation number.
// animIndex: Index of animation in giant g_Level.Anims vector.
constexpr auto NO_STATE = -1;
@ -107,20 +110,29 @@ void TranslateItem(ItemInfo* item, const EulerAngles& orient, float distance);
void TranslateItem(ItemInfo* item, const Vector3& direction, float distance);
// 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
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 ItemInfo& item, int animNumber = NO_ANIM);
int GetCurrentRelativeFrameNumber(ItemInfo* item);
int GetAnimNumber(ItemInfo& item, int animNumber);
int GetFrameNumber(ItemInfo* item, int frameToStart);
int GetFrameNumber(int objectID, int animNumber, int frameToStart);
int GetFrameCount(int animIndex);
int GetNextAnimState(ItemInfo* item);
int GetNextAnimState(int objectID, int animNumber);
bool GetStateDispatch(ItemInfo* item, const AnimData& anim);
int GetAnimNumber(const ItemInfo& item);
int GetAnimIndex(const ItemInfo& item, int animNumber);
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 GetNextAnimState(ItemInfo* item);
int GetNextAnimState(int objectID, int animNumber);
bool GetStateDispatch(ItemInfo* item, const AnimData& anim);
AnimFrameInterpData GetFrameInterpData(const ItemInfo& item);
AnimFrame& GetAnimFrame(const ItemInfo& item, int animNumber, int frameNumber);
@ -134,6 +146,9 @@ void DrawAnimatingItem(ItemInfo* item);
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, const CreatureBiteInfo& bite);
Vector3i GetJointPosition(const ItemInfo& item, const CreatureBiteInfo& bite);
Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex);
Quaternion GetBoneOrientation(const ItemInfo& item, int boneIndex);
float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex);

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@
#include "Sound/sound.h"
#include "Renderer/Renderer11.h"
using namespace TEN::Floordata;
using namespace TEN::Collision::Floordata;
using namespace TEN::Math;
using namespace TEN::Renderer;
@ -239,7 +239,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
int xFront, zFront, xRight, zRight, xLeft, zLeft;
// 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.
// 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
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)
{
@ -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);
}
else
{
height = GetCollision(probePos.x + xFront, probePos.y, probePos.z + zFront, topRoomNumber).Position.Floor;
}
if (height != NO_HEIGHT)
height -= (doPlayerCollision ? entityPos.y : probePos.y);
@ -408,19 +410,19 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
coll->Front.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockFloorSlopeDown &&
coll->Front.FloorSlope &&
coll->Front.Floor > coll->Middle.Floor)
coll->Front.FloorSlope &&
coll->Front.Floor > coll->Middle.Floor)
{
coll->Front.Floor = STOP_SIZE;
}
else if (coll->Setup.BlockCeilingSlope &&
coll->Front.CeilingSlope)
coll->Front.CeilingSlope)
{
coll->Front.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockDeathFloorDown &&
coll->Front.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
coll->Front.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
{
coll->Front.Floor = STOP_SIZE;
}
@ -471,19 +473,19 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
coll->MiddleLeft.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockFloorSlopeDown &&
coll->MiddleLeft.FloorSlope &&
coll->MiddleLeft.Floor > 0)
coll->MiddleLeft.FloorSlope &&
coll->MiddleLeft.Floor > 0)
{
coll->MiddleLeft.Floor = STOP_SIZE;
}
else if (coll->Setup.BlockCeilingSlope &&
coll->MiddleLeft.CeilingSlope)
coll->MiddleLeft.CeilingSlope)
{
coll->MiddleLeft.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockDeathFloorDown &&
coll->MiddleLeft.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
coll->MiddleLeft.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
{
coll->MiddleLeft.Floor = STOP_SIZE;
}
@ -528,19 +530,19 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
coll->FrontLeft.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockFloorSlopeDown &&
coll->FrontLeft.FloorSlope &&
coll->FrontLeft.Floor > 0)
coll->FrontLeft.FloorSlope &&
coll->FrontLeft.Floor > 0)
{
coll->FrontLeft.Floor = STOP_SIZE;
}
else if (coll->Setup.BlockCeilingSlope &&
coll->FrontLeft.CeilingSlope)
coll->FrontLeft.CeilingSlope)
{
coll->FrontLeft.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockDeathFloorDown &&
coll->FrontLeft.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
coll->FrontLeft.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
{
coll->FrontLeft.Floor = STOP_SIZE;
}
@ -590,19 +592,19 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
coll->MiddleRight.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockFloorSlopeDown &&
coll->MiddleRight.FloorSlope &&
coll->MiddleRight.Floor > 0)
coll->MiddleRight.FloorSlope &&
coll->MiddleRight.Floor > 0)
{
coll->MiddleRight.Floor = STOP_SIZE;
}
else if (coll->Setup.BlockCeilingSlope &&
coll->MiddleRight.CeilingSlope)
coll->MiddleRight.CeilingSlope)
{
coll->MiddleRight.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockDeathFloorDown &&
coll->MiddleRight.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
coll->MiddleRight.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
{
coll->MiddleRight.Floor = STOP_SIZE;
}
@ -647,19 +649,19 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
coll->FrontRight.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockFloorSlopeDown &&
coll->FrontRight.FloorSlope &&
coll->FrontRight.Floor > 0)
coll->FrontRight.FloorSlope &&
coll->FrontRight.Floor > 0)
{
coll->FrontRight.Floor = STOP_SIZE;
}
else if (coll->Setup.BlockCeilingSlope &&
coll->FrontRight.CeilingSlope)
coll->FrontRight.CeilingSlope)
{
coll->FrontRight.Floor = MAX_HEIGHT;
}
else if (coll->Setup.BlockDeathFloorDown &&
coll->FrontRight.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
coll->FrontRight.Floor >= CLICK(0.5f) &&
collResult.BottomBlock->Flags.Death)
{
coll->FrontRight.Floor = STOP_SIZE;
}
@ -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;
}
@ -773,12 +775,20 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
quarter %= 2;
if (coll->MiddleLeft.HasFlippedDiagonalSplit())
if (quarter) coll->CollisionType = CT_LEFT;
{
if (quarter)
coll->CollisionType = CT_LEFT;
}
else
if (!quarter) coll->CollisionType = CT_LEFT;
{
if (!quarter)
coll->CollisionType = CT_LEFT;
}
}
else
{
coll->CollisionType = CT_LEFT;
}
return;
}
@ -815,16 +825,24 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
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;
if (coll->MiddleRight.HasFlippedDiagonalSplit())
if (quarter) coll->CollisionType = CT_RIGHT;
{
if (quarter)
coll->CollisionType = CT_RIGHT;
}
else
if (!quarter) coll->CollisionType = CT_RIGHT;
{
if (!quarter)
coll->CollisionType = CT_RIGHT;
}
}
else
{
coll->CollisionType = CT_RIGHT;
}
return;
}

View file

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

View file

@ -3,11 +3,11 @@
#include "Game/items.h"
#include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Floordata;
using namespace TEN::Collision::Floordata;
using namespace TEN::Math;
int FloorInfo::GetSurfacePlaneIndex(int x, int z, bool isFloor) const
@ -313,7 +313,7 @@ void FloorInfo::RemoveBridge(int itemNumber)
BridgeItemNumbers.erase(itemNumber);
}
namespace TEN::Floordata
namespace TEN::Collision::Floordata
{
Vector3 GetSurfaceNormal(const Vector2& tilt, bool isFloor)
{
@ -756,26 +756,32 @@ namespace TEN::Floordata
auto floor = &GetFloorSide(item.RoomNumber, x, z);
floor->AddBridge(itemNumber);
const auto floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber);
while (floorBorder <= floor->GetSurfaceHeight(x, z, false))
if (Objects[item.ObjectNumber].floorBorder != nullptr)
{
const auto roomAbove = floor->GetRoomNumberAbove(x, z);
if (!roomAbove)
break;
int floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber);
while (floorBorder <= floor->GetSurfaceHeight(x, z, false))
{
const auto roomAbove = floor->GetRoomNumberAbove(x, z);
if (!roomAbove.has_value())
break;
floor = &GetFloorSide(*roomAbove, x, z);
floor->AddBridge(itemNumber);
floor = &GetFloorSide(*roomAbove, x, z);
floor->AddBridge(itemNumber);
}
}
const auto ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber);
while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true))
if (Objects[item.ObjectNumber].ceilingBorder != nullptr)
{
const auto roomBelow = floor->GetRoomNumberBelow(x, z);
if (!roomBelow)
break;
int ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber);
while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true))
{
const auto roomBelow = floor->GetRoomNumberBelow(x, z);
if (!roomBelow.has_value())
break;
floor = &GetFloorSide(*roomBelow, x, z);
floor->AddBridge(itemNumber);
floor = &GetFloorSide(*roomBelow, x, z);
floor->AddBridge(itemNumber);
}
}
}
@ -785,29 +791,35 @@ namespace TEN::Floordata
x += item.Pose.Position.x;
z += item.Pose.Position.z;
auto floor = &GetFloorSide(item.RoomNumber, x, z);
auto* floor = &GetFloorSide(item.RoomNumber, x, z);
floor->RemoveBridge(itemNumber);
const auto floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber);
while (floorBorder <= floor->GetSurfaceHeight(x, z, false))
if (Objects[item.ObjectNumber].floorBorder != nullptr)
{
const auto roomAbove = floor->GetRoomNumberAbove(x, z);
if (!roomAbove)
break;
int floorBorder = Objects[item.ObjectNumber].floorBorder(itemNumber);
while (floorBorder <= floor->GetSurfaceHeight(x, z, false))
{
const auto roomAbove = floor->GetRoomNumberAbove(x, z);
if (!roomAbove.has_value())
break;
floor = &GetFloorSide(*roomAbove, x, z);
floor->RemoveBridge(itemNumber);
floor = &GetFloorSide(*roomAbove, x, z);
floor->RemoveBridge(itemNumber);
}
}
const auto ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber);
while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true))
if (Objects[item.ObjectNumber].ceilingBorder != nullptr)
{
const auto roomBelow = floor->GetRoomNumberBelow(x, z);
if (!roomBelow)
break;
int ceilingBorder = Objects[item.ObjectNumber].ceilingBorder(itemNumber);
while (ceilingBorder >= floor->GetSurfaceHeight(x, z, true))
{
const auto roomBelow = floor->GetRoomNumberBelow(x, z);
if (!roomBelow.has_value())
break;
floor = &GetFloorSide(*roomBelow, x, z);
floor->RemoveBridge(itemNumber);
floor = &GetFloorSide(*roomBelow, x, z);
floor->RemoveBridge(itemNumber);
}
}
}
@ -824,17 +836,20 @@ namespace TEN::Floordata
auto bounds = GameBoundingBox(item);
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))
{
return std::optional{ item->Pose.Position.y + (bottom ? bounds.Y2 : bounds.Y1) };
}
else
{
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)
{
auto item = &g_Level.Items[itemNumber];
@ -847,6 +862,7 @@ namespace TEN::Floordata
void UpdateBridgeItem(int itemNumber, bool forceRemoval)
{
auto item = &g_Level.Items[itemNumber];
if (!Objects[item->ObjectNumber].loaded) return;
// Force removal if object was killed
if (item->Flags & IFLAG_KILLED)
@ -873,10 +889,10 @@ namespace TEN::Floordata
{
for (int z = 0; z < room->zSize; z++)
{
auto pX = room->x + (x * BLOCK(1)) + BLOCK(0.5f);
auto pZ = room->z + (z * BLOCK(1)) + BLOCK(0.5f);
auto offX = pX - item->Pose.Position.x;
auto offZ = pZ - item->Pose.Position.z;
float pX = room->x + (x * BLOCK(1)) + BLOCK(0.5f);
float pZ = room->z + (z * BLOCK(1)) + BLOCK(0.5f);
float offX = pX - item->Pose.Position.x;
float offZ = pZ - item->Pose.Position.z;
// Clean previous bridge state
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.
// 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.
// 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.
// Room number: Unique numeric index of a room.
// Surface: Floor or ceiling.
// 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));
enum class MaterialType
@ -154,7 +158,7 @@ class FloorInfo
void RemoveBridge(int itemNumber);
};
namespace TEN::Floordata
namespace TEN::Collision::Floordata
{
// TODO: Use normals natively.
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);
int GetBridgeBorder(int itemNumber, bool bottom);
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/items.h"
#include "Game/Setup.h"
#include "Specific/level.h"
#include "Specific/setup.h"
#include "Math/Math.h"
#include "Renderer/Renderer11.h"

View file

@ -7,6 +7,7 @@
#include "Game/collision/collide_room.h"
#include "Game/control/control.h"
#include "Game/control/lot.h"
#include "Game/effects/smoke.h"
#include "Game/effects/tomb4fx.h"
#include "Game/itemdata/creature_info.h"
#include "Game/Lara/lara.h"
@ -15,12 +16,14 @@
#include "Game/misc.h"
#include "Game/pickup/pickup.h"
#include "Game/room.h"
#include "Specific/setup.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/objectslist.h"
#include "Objects/TR5/Object/tr5_pushableblock.h"
#include "Renderer/Renderer11.h"
using namespace TEN::Effects::Smoke;
constexpr auto ESCAPE_DIST = SECTOR(5);
constexpr auto STALK_DIST = SECTOR(3);
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_JOINT_ROTATION_MAX = ANGLE(70.0f);
constexpr auto CREATURE_GUN_EFFECT_VERTICAL_OFFSET = 75;
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
constexpr int HIGH_PRIO_RANGE = 8;
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;
}
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;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = entityKillState;
auto& playerItem = *LaraItem;
auto& player = GetLaraInfo(playerItem);
LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + laraExtraKillAnim;
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
LaraItem->Animation.ActiveState = 0;
LaraItem->Animation.TargetState = laraKillState;
SetAnimation(*creatureItem, creatureAnimNumber);
SetAnimation(playerItem, ID_LARA_EXTRA_ANIMS, playerAnimNumber);
LaraItem->Pose = item->Pose;
LaraItem->Animation.IsAirborne = false;
LaraItem->Animation.Velocity.z = 0;
LaraItem->Animation.Velocity.y = 0;
playerItem.Pose = creatureItem->Pose;
playerItem.Animation.IsAirborne = false;
playerItem.Animation.Velocity = Vector3::Zero;
if (item->RoomNumber != LaraItem->RoomNumber)
ItemNewRoom(Lara.ItemNumber, item->RoomNumber);
if (creatureItem->RoomNumber != playerItem.RoomNumber)
ItemNewRoom(player.ItemNumber, creatureItem->RoomNumber);
AnimateItem(LaraItem);
AnimateItem(&playerItem);
Lara.ExtraAnim = 1;
Lara.Control.HandStatus = HandStatus::Busy;
Lara.Control.Weapon.GunType = LaraWeaponType::None;
Lara.HitDirection = -1;
player.ExtraAnim = 1;
player.Control.HandStatus = HandStatus::Busy;
player.Control.Weapon.GunType = LaraWeaponType::None;
player.HitDirection = -1;
Camera.pos.RoomNumber = LaraItem->RoomNumber;
Camera.pos.RoomNumber = playerItem.RoomNumber;
Camera.type = CameraType::Chase;
Camera.flags = CF_FOLLOW_CENTER;
Camera.targetAngle = ANGLE(170.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);
}
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);
}
@ -660,7 +652,7 @@ void CreatureFloat(short itemNumber)
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->Collidable = false;
@ -735,26 +727,47 @@ short CreatureTurn(ItemInfo* item, short maxTurn)
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;
auto prevPos = item->Pose.Position;
auto& creature = *GetCreatureInfo(&item);
AnimateItem(item);
ProcessSectorFlags(item);
CreatureHealth(item);
SpawnCreatureGunEffect(item, creature.MuzzleFlash[0]);
SpawnCreatureGunEffect(item, creature.MuzzleFlash[1]);
if (item->Status == ITEM_DEACTIVATED)
auto prevPos = item.Pose.Position;
AnimateItem(&item);
ProcessSectorFlags(&item);
CreatureHealth(&item);
if (item.Status == ITEM_DEACTIVATED)
{
CreatureDie(itemNumber, false);
return false;
}
return CreaturePathfind(item, prevPos, angle, tilt);
return CreaturePathfind(&item, prevPos, headingAngle, tiltAngle);
}
void CreatureHealth(ItemInfo* item)
@ -1088,7 +1101,7 @@ bool CreatureActive(short itemNumber)
return true;
}
void InitialiseCreature(short itemNumber)
void InitializeCreature(short itemNumber)
{
auto* item = &g_Level.Items[itemNumber];
@ -2097,7 +2110,7 @@ void AdjustStopperFlag(ItemInfo* item, int direction)
floor->Stopper = !floor->Stopper;
}
void InitialiseItemBoxData()
void InitializeItemBoxData()
{
for (int i = 0; i < g_Level.Items.size(); i++)
{

View file

@ -2,7 +2,7 @@
#include "Specific/level.h"
#include "Math/Math.h"
struct BiteInfo;
struct CreatureBiteInfo;
struct CreatureInfo;
struct ItemInfo;
struct LOTInfo;
@ -20,55 +20,6 @@ enum TARGET_TYPE
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
{
int zoneNumber;
@ -99,30 +50,6 @@ struct OVERLAP
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)
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 AlertAllGuards(short itemNumber);
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 CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func);
short CreatureEffect2(ItemInfo* item, const CreatureBiteInfo& bite, short velocity, short angle, std::function<CreatureEffectFunction> func);
short CreatureEffect(ItemInfo* item, const CreatureBiteInfo& bite, std::function<CreatureEffectFunction> func);
void CreatureUnderwater(ItemInfo* item, int depth);
void CreatureFloat(short itemNumber);
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 SearchLOT(LOTInfo* LOT, int expansion);
bool CreatureActive(short itemNumber);
void InitialiseCreature(short itemNumber);
void InitializeCreature(short itemNumber);
bool StalkBox(ItemInfo* item, ItemInfo* enemy, int boxNumber);
void CreatureAIInfo(ItemInfo* item, AI_INFO* AI);
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 AdjustStopperFlag(ItemInfo* item, int direction);
void InitialiseItemBoxData();
void InitializeItemBoxData();
bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,9 +3,9 @@
extern std::vector<CreatureInfo*> ActiveCreatures;
void InitialiseLOTarray(int allocMem);
void InitializeLOTarray(int allocMem);
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 DisableEntityAI(short itemNumber);
void ClearLOT(LOTInfo* LOT);

View file

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

View file

@ -67,16 +67,16 @@ extern int TriggerTimer;
extern int KeyTriggerActive;
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 KeyTrigger(short itemNum);
bool PickupTrigger(short itemNum);
int KeyTrigger(short itemNumber);
bool PickupTrigger(short itemNumber);
void RefreshCamera(short type, short* data);
int TriggerActive(ItemInfo* item);
short* GetTriggerIndex(FloorInfo* floor, int x, int y, int z);
short* GetTriggerIndex(ItemInfo* item);
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 Antitrigger(short const value, short const flags = 0);

View file

@ -9,10 +9,10 @@
#include "Game/Lara/lara.h"
#include "Game/room.h"
#include "Game/savegame.h"
#include "Game/Setup.h"
#include "Renderer/Renderer11.h"
#include "Renderer/Renderer11Enums.h"
#include "Scripting/Include/ScriptInterfaceGame.h"
#include "Specific/setup.h"
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);
if (item.IsLara() || item.Index == Lara.Vehicle)
if (item.IsLara() || item.Index == Lara.Context.Vehicle)
{
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/";
@ -207,10 +207,18 @@ namespace TEN::Control::Volumes
TENLog("Loading node scripts...", LogLevel::Info);
std::sort(nodeCatalogs.rbegin(), nodeCatalogs.rend());
for (const auto& file : nodeCatalogs)
g_GameScript->ExecuteScriptFile(nodeScriptPath + file);
TENLog(std::to_string(nodeCatalogs.size()) + " node catalogs were found and loaded.", LogLevel::Info);
if (!nodeCatalogs.empty())
{
for (const auto& file : nodeCatalogs)
g_GameScript->ExecuteScriptFile(nodeScriptPath + file);
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;
for (const auto& set : g_Level.EventSets)

View file

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

View file

@ -42,7 +42,7 @@ namespace TEN::Control::Volumes
struct VolumeEventSet
{
std::string Name = {};
std::string Name = {};
VolumeActivatorFlags Activators = VolumeActivatorFlags::None;
VolumeEvent OnEnter = {};

View file

@ -3,9 +3,10 @@
#include "Game/collision/collide_room.h"
#include "Game/effects/effects.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/Utils/object_helper.h"
#include "Specific/clock.h"
#include "Specific/setup.h"
using namespace TEN::Math;
@ -17,6 +18,9 @@ namespace TEN::Effects::Blood
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_MIN = 8.0f;
constexpr auto SPAWN_RADIUS = BLOCK(0.25f);

View file

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

View file

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

View file

@ -69,7 +69,7 @@ namespace TEN::Effects::Streamer
public:
// Members
std::map<int, std::vector<Streamer>> Pools = {}; // Key = tag.
std::unordered_map<int, std::vector<Streamer>> Pools = {}; // Key = tag.
// Utilities
void AddStreamer(int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
@ -92,7 +92,7 @@ namespace TEN::Effects::Streamer
public:
// Members
std::map<int, StreamerModule> Modules = {}; // Key = entity number.
std::unordered_map<int, StreamerModule> Modules = {}; // Key = entity number.
// Utilities
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/effects/effects.h"
#include "Game/effects/Ripple.h"
#include "Game/Setup.h"
#include "Objects/objectslist.h"
#include "Math/Math.h"
#include "Specific/clock.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Ripple;
using namespace TEN::Floordata;
using namespace TEN::Collision::Floordata;
using namespace TEN::Math;
namespace TEN::Effects::Bubble

View file

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

View file

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

View file

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

View file

@ -17,13 +17,13 @@
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/objectslist.h"
#include "Renderer/Renderer11.h"
#include "Sound/sound.h"
#include "Specific/clock.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Blood;
using namespace TEN::Effects::Bubble;
@ -52,7 +52,7 @@ int SplashCount = 0;
Vector3i NodeVectors[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
{ 0, 0, 256, 8, false }, // TR5 offset 2
{ 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->friction = 0;
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->gravity = 0;
spark->maxYvel = 0;

View file

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

View file

@ -1,264 +1,222 @@
#include "framework.h"
#include "Game/effects/footprint.h"
#include "Game/effects/Footprint.h"
#include "Game/animation.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/floordata.h"
#include "Game/control/control.h"
#include "Game/effects/effects.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/Utils/object_helper.h"
#include "Sound/sound.h"
#include "Specific/clock.h"
#include "Specific/level.h"
using namespace TEN::Collision::Floordata;
using namespace TEN::Math;
namespace TEN::Effects::Footprints
namespace TEN::Effects::Footprint
{
std::deque<FOOTPRINT_STRUCT> footprints = std::deque<FOOTPRINT_STRUCT>();
bool CheckFootOnFloor(const ItemInfo& item, int mesh, Vector3& outFootprintPosition)
const auto FootprintMaterials = std::vector<MaterialType>
{
int x = item.Pose.Position.x;
int y = item.Pose.Position.y;
int z = item.Pose.Position.z;
short roomNumber = item.RoomNumber;
MaterialType::Mud,
MaterialType::Snow,
MaterialType::Sand,
MaterialType::Gravel,
MaterialType::Custom1,
MaterialType::Custom2,
MaterialType::Custom3,
MaterialType::Custom4
};
auto floor = GetFloor(x, y, z, &roomNumber);
auto pos = GetJointPosition(LaraItem, mesh, Vector3i(0, FOOT_HEIGHT_OFFSET, 0));
int height = GetFloorHeight(floor, pos.x, pos.y - CLICK(1), pos.z);
struct FootprintPositionData
{
bool CanSpawn = false;
Vector3 Position = Vector3::Zero;
};
outFootprintPosition.x = pos.x;
outFootprintPosition.y = height - 8;
outFootprintPosition.z = pos.z;
std::vector<Footprint> Footprints = {};
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)
return;
constexpr auto SCALE = BLOCK(1 / 16.0f);
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.
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)
return std::array<Vector3, Footprint::VERTEX_COUNT>
{
case MaterialType::Concrete:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS;
break;
pos + Vector3::Transform(POINT_0, rotMatrix),
pos + Vector3::Transform(POINT_1, rotMatrix),
pos + Vector3::Transform(POINT_2, rotMatrix),
pos + Vector3::Transform(POINT_3, rotMatrix)
};
}
case MaterialType::Grass:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_GRASS;
break;
static FootprintPositionData GetFootprintPositionData(const ItemInfo& item, int jointIndex)
{
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);
case MaterialType::Gravel:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_GRAVEL;
break;
auto footPos = GetJointPosition(item, jointIndex, FOOT_OFFSET);
int floorHeight = GetCollision(footPos.x, footPos.y - CLICK(1), footPos.z, item.RoomNumber).Position.Floor;
case MaterialType::Ice:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_ICE;
break;
bool canSpawn = (abs(footPos.y - floorHeight) < ABS_FLOOR_BOUND);
auto pos = Vector3(footPos.x, floorHeight - SURFACE_OFFSET, footPos.z);
return FootprintPositionData{ canSpawn, pos };
}
case MaterialType::Marble:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_MARBLE;
break;
static bool TestFootprintFloor(const ItemInfo& item, const Vector3& pos, const std::array<Vector3, Footprint::VERTEX_COUNT>& vertexPoints)
{
constexpr auto ABS_FLOOR_BOUND = CLICK(0.5f);
case MaterialType::Metal:
fx = SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS_METAL;
break;
// Get point collision at every vertex point.
auto pointColl0 = GetCollision(vertexPoints[0].x, pos.y - CLICK(1), vertexPoints[0].z, item.RoomNumber);
auto pointColl1 = GetCollision(vertexPoints[1].x, pos.y - CLICK(1), vertexPoints[1].z, item.RoomNumber);
auto pointColl2 = GetCollision(vertexPoints[2].x, pos.y - CLICK(1), vertexPoints[2].z, item.RoomNumber);
auto pointColl3 = GetCollision(vertexPoints[3].x, pos.y - CLICK(1), vertexPoints[3].z, item.RoomNumber);
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;
// Don't spawn footprint if floor heights at vertex points are outside upper/lower floor height bound.
if ((abs(pointColl0.Position.Floor - pointColl1.Position.Floor) > ABS_FLOOR_BOUND) ||
(abs(pointColl1.Position.Floor - pointColl2.Position.Floor) > ABS_FLOOR_BOUND) ||
(abs(pointColl2.Position.Floor - pointColl3.Position.Floor) > ABS_FLOOR_BOUND) ||
(abs(pointColl3.Position.Floor - pointColl0.Position.Floor) > ABS_FLOOR_BOUND))
{
return false;
}
// HACK: Must be here until reference WAD2 is revised.
if (fx != SOUND_EFFECTS::SFX_TR4_LARA_FOOTSTEPS)
SoundEffect(fx, &item->Pose);
return true;
}
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)
{
void SpawnFootprint(bool isRight, const std::array<Vector3, Footprint::VERTEX_COUNT>& vertexPoints)
{
if (!CheckIfSlotExists(ID_MISC_SPRITES, "Footprint rendering"))
return;
constexpr auto LIFE_MAX = 20.0f;
constexpr auto LIFE_START_FADING = 10.0f;
constexpr auto OPACITY_MAX = 0.5f;
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;
// Don't spawn footprint if foot isn't on floor.
int jointIndex = isRight ? LM_RFOOT : LM_LFOOT;
auto posData = GetFootprintPositionData(item, jointIndex);
if (!posData.CanSpawn)
return;
// Slightly randomize 2D position.
posData.Position += Vector3(Random::GenerateFloat(-5.0f, 5.0f), 0.0f, Random::GenerateFloat(-5.0f, 5.0f));
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);
}
// Calculate footprint tilts.
auto plane = floor->FloorCollision.Planes[floor->GetSurfacePlaneIndex(footPos.x, footPos.z, true)];
auto c = phd_cos(item->Pose.Orientation.y + ANGLE(180.0f));
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.
auto p0 = Vector3( FOOTPRINT_SIZE, 0, FOOTPRINT_SIZE);
auto p1 = Vector3(-FOOTPRINT_SIZE, 0, FOOTPRINT_SIZE);
auto p2 = Vector3(-FOOTPRINT_SIZE, 0, -FOOTPRINT_SIZE);
auto p3 = Vector3( FOOTPRINT_SIZE, 0, -FOOTPRINT_SIZE);
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
auto c0 = GetCollision(p0.x, footPos.y - STEP_SIZE, p0.z, item->RoomNumber);
auto c1 = GetCollision(p1.x, footPos.y - STEP_SIZE, p1.z, item->RoomNumber);
auto c2 = GetCollision(p2.x, footPos.y - STEP_SIZE, p2.z, item->RoomNumber);
auto c3 = GetCollision(p3.x, footPos.y - STEP_SIZE, p3.z, item->RoomNumber);
// Don't process footprint placement if all foot corners aren't on the same tilt level
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))
// Check floor material.
if (!TestMaterial(pointColl.BottomBlock->Material, FootprintMaterials))
return;
// Don't process footprint placement if all foot corners aren't on the same height
if ((abs(c0.Position.Floor - c1.Position.Floor) > STEP_SIZE / 2) ||
(abs(c1.Position.Floor - c2.Position.Floor) > STEP_SIZE / 2) ||
(abs(c2.Position.Floor - c3.Position.Floor) > STEP_SIZE / 2) ||
(abs(c3.Position.Floor - c0.Position.Floor) > STEP_SIZE / 2))
auto vertexPoints = GetFootprintVertexPoints(item, posData.Position, GetSurfaceNormal(pointColl.FloorTilt, true));
// Test floor continuity.
if (!TestFootprintFloor(item, posData.Position, vertexPoints))
return;
// Construct footprint
FOOTPRINT_STRUCT footprint = {};
footprint.Position[0] = p0;
footprint.Position[1] = p1;
footprint.Position[2] = p2;
footprint.Position[3] = p3;
footprint.StartOpacity = 0.5f;
footprint.LifeStartFading = FPS * 10;
footprint.Life = FPS * 20;
footprint.Active = true;
footprint.RightFoot = rightFoot;
// Add footprint
if (footprints.size() >= MAX_FOOTPRINTS)
footprints.pop_back();
footprints.push_front(footprint);
SpawnFootprint(isRight, vertexPoints);
}
void UpdateFootprints()
{
if (footprints.size() == 0)
if (Footprints.empty())
return;
int numInvalidFootprints = 0;
for (auto i = footprints.begin(); i != footprints.end(); i++)
for (auto& footprint: Footprints)
{
FOOTPRINT_STRUCT& footprint = *i;
footprint.Life--;
if (footprint.Life <= 0)
{
numInvalidFootprints++;
if (footprint.Life <= 0.0f)
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)
{
footprint.Opacity = footprint.StartOpacity;
}
else
{
float opacity = Lerp(0.0f, footprint.StartOpacity, fmax(0, fmin(1, footprint.Life / (float)footprint.LifeStartFading)));
footprint.Opacity = opacity;
}
// Update life.
footprint.Life -= 1.0f;
}
for (int i = 0; i < numInvalidFootprints; i++)
{
footprints.pop_back();
}
ClearInactiveEffects(Footprints);
}
void ClearFootprints()
{
Footprints.clear();
}
}

View file

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

View file

@ -9,10 +9,10 @@
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Setup.h"
#include "Renderer/Renderer11.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Environment;
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.
// Therefore, cannot directly use water status value to determine enrironment.
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.
CollideSegmentWithRoom(segment, waterHeight, roomNumber, isOnLand);
@ -161,7 +161,7 @@ namespace TEN::Effects::Hair
break;
}
int frameBaseIndex = GetAnimData(animNumber).FramePtr;
int frameBaseIndex = GetAnimData(item.ObjectNumber, animNumber).FramePtr;
const auto& frame = g_Level.Frames[frameBaseIndex + player.HitFrame];
return frame.BoundingBox.GetCenter();
}

View file

@ -95,17 +95,17 @@ namespace TEN::Effects::Items
switch (item->Animation.AnimNumber)
{
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;
break;
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;
break;
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;
break;

View file

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

View file

@ -8,9 +8,9 @@
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/room.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Environment;
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: 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)
{
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();
s = {};
s.active = true;
s.position = Vector3(x, y, z);
s.position = pos;
Vector3 direction = Vector3(xv, yv, zv);
direction.Normalize();
auto directionNorm = direction;
directionNorm.Normalize();
s.velocity = direction;
s.velocity = directionNorm;
s.gravity = -.1f;
s.affectedByWind = TestEnvironment(ENV_FLAG_WIND, LaraItem);
s.sourceColor = Vector4(.4f, .4f, .4f, 1);
s.affectedByWind = TestEnvironment(ENV_FLAG_WIND, pos.x, pos.y, pos.z, roomNumber);
s.sourceColor = Vector4(0.4f, 0.4f, 0.4f, 1);
s.destinationColor = Vector4(0, 0, 0, 0);
if (initial)
@ -135,7 +140,7 @@ namespace TEN::Effects::Smoke
float size = Random::GenerateFloat(48, 80);
s.sourceSize = size * 2;
s.destinationSize = size * 8;
s.sourceColor = {0.75,0.75,1,1};
s.sourceColor = { 0.75, 0.75, 1, 1 };
s.terminalVelocity = 0;
s.friction = 0.82f;
s.life = Random::GenerateFloat(60, 90);
@ -145,7 +150,7 @@ namespace TEN::Effects::Smoke
float size = Random::GenerateFloat(48, 80);
s.sourceSize = size * 2;
s.destinationSize = size * 16;
s.velocity = Random::GenerateDirectionInCone(direction, 25);
s.velocity = Random::GenerateDirectionInCone(directionNorm, 25);
s.velocity *= Random::GenerateFloat(0, 32);
}
else
@ -153,7 +158,7 @@ namespace TEN::Effects::Smoke
float size = Random::GenerateFloat(48, 80);
s.sourceSize = size;
s.destinationSize = size * 8;
s.velocity = Random::GenerateDirectionInCone(direction, 3);
s.velocity = Random::GenerateDirectionInCone(directionNorm, 3);
s.velocity *= Random::GenerateFloat(0, 16);
}
}
@ -165,7 +170,7 @@ namespace TEN::Effects::Smoke
s.terminalVelocity = 0;
s.friction = 0.88f;
s.life = Random::GenerateFloat(60, 90);
s.velocity = Random::GenerateDirectionInCone(direction, 10);
s.velocity = Random::GenerateDirectionInCone(directionNorm, 10);
s.velocity *= Random::GenerateFloat(16, 30);
}
}
@ -184,11 +189,11 @@ namespace TEN::Effects::Smoke
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.angularVelocity = Random::GenerateFloat(-PI_DIV_4, PI_DIV_4);
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)

View file

@ -34,6 +34,7 @@ namespace TEN::Effects::Smoke
void DisableSmokeParticles();
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 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 TriggerRocketSmoke(int x, int y, int z);
void TriggerBreathSmoke(long x, long y, long z, short angle);

View file

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

View file

@ -5,6 +5,7 @@
#include "Renderer/Renderer11Enums.h"
enum class LaraWeaponType;
struct CreatureBiteInfo;
struct ItemInfo;
enum BodyPartFlags
@ -232,9 +233,11 @@ int GetFreeFireSpark();
void TriggerGlobalStaticFlame();
void TriggerGlobalFireSmoke();
void TriggerGlobalFireFlame();
void TriggerPilotFlame(int itemNum, int nodeIndex);
void ThrowFire(int itemNum, int meshIndex, Vector3i offset, Vector3i speed);
void ThrowPoison(int itemNum, int meshIndex, Vector3i offset, Vector3i speed, Vector3 color);
void TriggerPilotFlame(int itemNumber, int nodeIndex);
void ThrowFire(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel);
void ThrowFire(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel);
void ThrowPoison(int itemNumber, int meshIndex, const Vector3i& offset, const Vector3i& vel, const Vector3& color);
void ThrowPoison(int itemNumber, const CreatureBiteInfo& bite, const Vector3i& vel, const Vector3& color);
void UpdateFireProgress();
void ClearFires();
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/tomb4fx.h"
#include "Game/savegame.h"
#include "Game/Setup.h"
#include "Math/Random.h"
#include "Sound/sound.h"
#include "Specific/setup.h"
#include "Specific/level.h"
#include "ScriptInterfaceLevel.h"

View file

@ -35,6 +35,7 @@ namespace TEN::Gui
constexpr int LINE_HEIGHT = 25;
constexpr int PHD_CENTER_X = SCREEN_SPACE_RES.x / 2;
constexpr int PHD_CENTER_Y = SCREEN_SPACE_RES.y / 2;
constexpr int OBJLIST_SPACING = PHD_CENTER_X / 2;
constexpr int VOLUME_MAX = 100;
@ -86,6 +87,9 @@ namespace TEN::Gui
bool GuiController::GuiIsPulsed(ActionID actionID) const
{
constexpr auto DELAY = 0.1f;
constexpr auto INITIAL_DELAY = 0.4f;
auto oppositeAction = In::None;
switch (actionID)
{
@ -107,7 +111,7 @@ namespace TEN::Gui
}
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
@ -122,16 +126,13 @@ namespace TEN::Gui
bool GuiController::CanSelect() const
{
// Holding Deselect safely cancels input.
if (IsHeld(In::Deselect))
return false;
if (GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Deselect) &&
GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Save) &&
GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Load) &&
GetActionTimeActive(In::Action) <= GetActionTimeInactive(In::Pause))
{
// Avoid Action release interference when entering inventory.
if (GetActionTimeActive(In::Action) < TimeInMenu)
return true;
}
return false;
}
@ -178,7 +179,11 @@ namespace TEN::Gui
void GuiController::SetInventoryMode(InventoryMode mode)
{
InvMode = mode;
if (mode != InvMode)
{
TimeInMenu = 0.0f;
InvMode = mode;
}
}
void GuiController::SetInventoryItemChosen(int number)
@ -228,6 +233,8 @@ namespace TEN::Gui
static int selectedOptionBackup;
auto inventoryResult = InventoryResult::None;
TimeInMenu++;
// Stuff for credits goes here!
switch (MenuToDisplay)
@ -281,8 +288,8 @@ namespace TEN::Gui
}
}
else if (MenuToDisplay == Menu::Title ||
MenuToDisplay == Menu::SelectLevel ||
MenuToDisplay == Menu::Options)
MenuToDisplay == Menu::SelectLevel ||
MenuToDisplay == Menu::Options)
{
if (GuiIsPulsed(In::Forward))
{
@ -540,7 +547,7 @@ namespace TEN::Gui
void GuiController::HandleControlSettingsInput(ItemInfo* item, bool fromPauseMenu)
{
static const int numControlSettingsOptions = KEY_COUNT + 1;
static const int numControlSettingsOptions = KEY_COUNT + 2;
OptionCount = numControlSettingsOptions;
CurrentSettings.WaitingForKey = false;
@ -554,7 +561,7 @@ namespace TEN::Gui
}
if (GuiIsSelected() &&
SelectedOption <= (numControlSettingsOptions - 2))
SelectedOption <= (numControlSettingsOptions - 3))
{
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
CurrentSettings.WaitingForKey = true;
@ -635,6 +642,14 @@ namespace TEN::Gui
if (GuiIsSelected())
{
// Defaults.
if (SelectedOption == (OptionCount - 2))
{
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
ApplyDefaultBindings();
return;
}
// Apply.
if (SelectedOption == (OptionCount - 1))
{
@ -888,6 +903,7 @@ namespace TEN::Gui
static const int numStatisticsOptions = 0;
static const int numOptionsOptions = 2;
TimeInMenu++;
UpdateInputActions(item);
switch (MenuToDisplay)
@ -946,7 +962,7 @@ namespace TEN::Gui
{
if (MenuToDisplay == Menu::Pause)
{
InvMode = InventoryMode::None;
SetInventoryMode(InventoryMode::None);
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
return InventoryResult::None;
}
@ -979,7 +995,7 @@ namespace TEN::Gui
break;
case PauseMenuOption::ExitToTitle:
InvMode = InventoryMode::None;
SetInventoryMode(InventoryMode::None);
return InventoryResult::ExitToTitle;
break;
}
@ -1593,7 +1609,7 @@ namespace TEN::Gui
Rings[(int)RingTypes::Ammo]->RingActive = false;
}
void GuiController::InitialiseInventory(ItemInfo* item)
void GuiController::InitializeInventory(ItemInfo* item)
{
auto* lara = GetLaraInfo(item);
@ -1603,14 +1619,22 @@ namespace TEN::Gui
UseItem = false;
if (lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[0].HasInfinite())
{
Ammo.AmountShotGunAmmo1 = -1;
}
else
{
Ammo.AmountShotGunAmmo1 = lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[0].GetCount() / 6;
}
if (lara->Weapons[(int)LaraWeaponType::Shotgun].Ammo[1].HasInfinite())
{
Ammo.AmountShotGunAmmo2 = -1;
}
else
{
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.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 (IsItemInInventory(LastInvItem))
{
SetupObjectListStartPosition(LastInvItem);
}
else
{
if (LastInvItem >= INV_OBJECT_SMALL_WATERSKIN_EMPTY && LastInvItem <= INV_OBJECT_SMALL_WATERSKIN_3L)
@ -1820,7 +1846,7 @@ namespace TEN::Gui
ClearAllActions();
ActionMap[(int)In::Flare].Update(1.0f);
HandleWeapon(item);
HandleWeapon(*item);
ClearAllActions();
}
@ -2310,20 +2336,20 @@ namespace TEN::Gui
case MenuType::Load:
// fill_up_savegames_array // Maybe not?
InvMode = InventoryMode::Load;
SetInventoryMode(InventoryMode::Load);
break;
case MenuType::Save:
// fill_up_savegames_array
InvMode = InventoryMode::Save;
SetInventoryMode(InventoryMode::Save);
break;
case MenuType::Examine:
InvMode = InventoryMode::Examine;
SetInventoryMode(InventoryMode::Examine);
break;
case MenuType::Statistics:
InvMode = InventoryMode::Statistics;
SetInventoryMode(InventoryMode::Statistics);
break;
case MenuType::Ammo1:
@ -2355,7 +2381,7 @@ namespace TEN::Gui
break;
case MenuType::Diary:
InvMode = InventoryMode::Diary;
SetInventoryMode(InventoryMode::Diary);
lara->Inventory.Diary.CurrentPage = 1;
break;
}
@ -2918,9 +2944,9 @@ namespace TEN::Gui
g_Renderer.DumpGameScene();
if (resetMode)
InvMode = InventoryMode::InGame;
SetInventoryMode(InventoryMode::InGame);
InitialiseInventory(item);
InitializeInventory(item);
Camera.numberFrames = 2;
bool exitLoop = false;
@ -2929,10 +2955,10 @@ namespace TEN::Gui
if (ThreadEnded)
return false;
OBJLIST_SPACING = PHD_CENTER_X / 2;
TimeInMenu++;
GameTimer++;
UpdateInputActions(item);
GameTimer++;
if (IsClicked(In::Option))
{
@ -2973,7 +2999,7 @@ namespace TEN::Gui
exitLoop = !resetMode;
if (resetMode)
InvMode = InventoryMode::InGame;
SetInventoryMode(InventoryMode::InGame);
break;
@ -2988,7 +3014,7 @@ namespace TEN::Gui
{
exitLoop = !resetMode;
if (resetMode)
InvMode = InventoryMode::InGame;
SetInventoryMode(InventoryMode::InGame);
}
break;
@ -3011,30 +3037,30 @@ namespace TEN::Gui
AlterFOV(LastFOV);
lara->Inventory.IsBusy = lara->Inventory.OldBusy;
InvMode = InventoryMode::None;
SetInventoryMode(InventoryMode::None);
return doLoad;
}
void GuiController::DoStatisticsMode()
{
InvMode = InventoryMode::Statistics;
SetInventoryMode(InventoryMode::Statistics);
if (GuiIsDeselected())
{
SoundEffect(SFX_TR4_MENU_SELECT, nullptr, SoundEnvironment::Always);
InvMode = InventoryMode::InGame;
SetInventoryMode(InventoryMode::InGame);
}
}
void GuiController::DoExamineMode()
{
this->InvMode = InventoryMode::Examine;
SetInventoryMode(InventoryMode::Examine);
if (GuiIsDeselected())
{
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);
this->InvMode = InventoryMode::Diary;
SetInventoryMode(InventoryMode::Diary);
if (GuiIsPulsed(In::Right) &&
lara->Inventory.Diary.CurrentPage < lara->Inventory.Diary.NumPages)
@ -3075,7 +3101,7 @@ namespace TEN::Gui
if (GuiIsDeselected())
{
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 SelectedSaveSlot;
float TimeInMenu = 0.0f;
SettingsData CurrentSettings;
// Inventory variables
@ -150,7 +151,6 @@ namespace TEN::Gui
short NormalRingFadeVal;
short NormalRingFadeDir;
unsigned char AmmoActive;
int OBJLIST_SPACING;
MenuOption CurrentOptions[3];
InventoryMode InvMode;
int InventoryItemChosen;
@ -199,7 +199,7 @@ namespace TEN::Gui
void HandleOptionsInput();
void BackupOptions();
bool DoObjectsCombine(int objectNumber1, int objectNumber2);
void InitialiseInventory(ItemInfo* item);
void InitializeInventory(ItemInfo* item);
void FillDisplayOptions();
bool IsItemCurrentlyCombinable(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
{
bool Initialised = false;
bool Initialized = false;
std::vector<BoxNode> Node = {};
int Head = 0;
@ -71,6 +71,62 @@ struct LOTInfo
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
{
int ItemNumber = -1;
@ -96,11 +152,13 @@ struct CreatureInfo
bool MonkeySwingAhead = false;
bool ReachedGoal = false;
short FiredWeapon = 0;
CreatureMuzzleFlashInfo MuzzleFlash[2];
short Tosspad = 0;
short LocationAI = 0;
short Flags = 0;
bool IsTargetAlive();
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
CreatureAIPriority Priority = CreatureAIPriority::None;
size_t FramesSinceLOTUpdate = 0;

View file

@ -1,4 +1,4 @@
#include "framework.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/motorbike_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"
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::Generic;
using namespace TEN::Entities::TR4;
using namespace TEN::Entities::Vehicles;
struct ItemInfo;
class ITEM_DATA
class ItemData
{
std::variant<
std::nullptr_t,
@ -67,16 +68,17 @@ class ITEM_DATA
UPVInfo,
SpeedboatInfo,
RubberBoatInfo,
MinecartInfo
MinecartInfo,
ElectricalLightInfo
> data;
public:
ITEM_DATA();
ItemData();
template<typename D>
ITEM_DATA(D&& type) : data(std::move(type)) {}
ItemData(D&& type) : data(std::move(type)) {}
// conversion operators to keep original syntax!
// TODO: should be removed later and
// Conversion operators to keep original syntax.
// TODO: Should be removed later and use polymorphism instead.
template<typename T>
operator T* ()
{
@ -86,7 +88,7 @@ class ITEM_DATA
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>
@ -98,33 +100,33 @@ class ITEM_DATA
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>
ITEM_DATA& operator=(T* newData)
ItemData& operator =(T* newData)
{
data = *newData;
return *this;
}
*/
ITEM_DATA& operator=(std::nullptr_t null)
ItemData& operator =(std::nullptr_t null)
{
data = nullptr;
return *this;
}
template<typename T>
ITEM_DATA& operator=(T& newData)
ItemData& operator =(T& newData)
{
data = newData;
return *this;
}
template<typename T>
ITEM_DATA& operator=(T&& newData)
ItemData& operator =(T&& newData)
{
data = std::move(newData);
return *this;
@ -139,12 +141,12 @@ class ITEM_DATA
void apply(Funcs&&... funcs)
{
std::visit(
visitor
visitor
{
[](auto const&) {},
std::forward<Funcs>(funcs)...
},
data);
data);
}
template<typename T>

View file

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

View file

@ -5,14 +5,13 @@
#include "Game/animation.h"
#include "Game/itemdata/itemdata.h"
#include "Objects/game_object_ids.h"
#include "Math/Math.h"
#include "Specific/newtypes.h"
#include "Specific/BitField.h"
using namespace TEN::Utils;
enum GAME_OBJECT_ID : short;
constexpr auto NO_ITEM = -1;
constexpr auto NOT_TARGETABLE = -16384;
@ -66,6 +65,8 @@ enum class EffectType
struct EntityAnimationData
{
GAME_OBJECT_ID AnimObjectID = ID_NO_OBJECT;
int AnimNumber = 0; // g_Level.Anims index.
int FrameNumber = 0; // g_Level.Frames index.
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
struct ItemInfo
{
GAME_OBJECT_ID ObjectNumber;
std::string Name;
GAME_OBJECT_ID ObjectNumber = ID_NO_OBJECT; // ObjectID
std::string Name = {};
int Status; // ItemStatus enum.
bool Active;
@ -116,7 +117,7 @@ struct ItemInfo
short NextItem;
short NextActive;
ITEM_DATA Data;
ItemData Data;
EntityAnimationData Animation;
EntityCallbackData Callbacks;
EntityModelData Model;
@ -178,11 +179,11 @@ short CreateItem();
void RemoveAllItemsInRoom(short roomNumber, short objectNumber);
void RemoveActiveItem(short itemNumber, bool killed = true);
void RemoveDrawnItem(short itemNumber);
void InitialiseFXArray(int allocateMemory);
void InitializeFXArray(int allocateMemory);
short CreateNewEffect(short roomNumber);
void KillEffect(short fxNumber);
void InitialiseItem(short itemNumber);
void InitialiseItemArray(int totalItems);
void InitializeItem(short itemNumber);
void InitializeItemArray(int totalItems);
void KillItem(short itemNumber);
bool UpdateItemRoom(short itemNumber);
void UpdateAllItems();
@ -195,3 +196,4 @@ int FindItem(ItemInfo* item);
void DoDamage(ItemInfo* item, int damage);
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);
short SpawnItem(ItemInfo* item, GAME_OBJECT_ID objectNumber);

View file

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

View file

@ -10,10 +10,10 @@
#include "Game/effects/Bubble.h"
#include "Game/Lara/lara.h"
#include "Game/items.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Sound/sound.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::Explosion;
@ -115,7 +115,7 @@ void ControlMissile(short fxNumber)
EffectNewRoom(fxNumber, pointColl.RoomNumber);
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)
{

View file

@ -12,7 +12,7 @@
#include "Game/misc.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* 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)
{
// TODO: Remove -128 and fix ricochet effect going on floor. -- TokyoSU 2023.04.28
auto pos = GameVector(
LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->Floor,
LaraItem->Floor - 128,
LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->RoomNumber);
Ricochet(Pose(pos.x, pos.y, pos.z));
Ricochet(Pose(pos.ToVector3i()));
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;
}
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())
// 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;
auto* creature = GetCreatureInfo(item);
@ -134,9 +135,9 @@ bool Targetable(ItemInfo* item, AI_INFO* AI)
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;
// Check just in case.
@ -148,7 +149,7 @@ bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngleInDegrees)
if (enemy == nullptr || enemy->HitPoints == 0)
return false;
short angle = AI->angle - creature->JointRotation[2];
short angle = ai->angle - creature->JointRotation[2];
if (angle > ANGLE(-maxAngleInDegrees) && angle < ANGLE(maxAngleInDegrees))
{
const auto& bounds = GetBestFrame(*enemy).BoundingBox;

View file

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

View file

@ -10,7 +10,7 @@ extern int NumRPickups;
extern short RPickups[16];
extern Vector3i OldPickupPos;
void InitialisePickup(short itemNumber);
void InitializePickup(short itemNumber);
bool SetInventoryCount(GAME_OBJECT_ID objectID, int count);
void PickedUpObject(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 InitialiseSearchObject(short itemNumber);
void InitializeSearchObject(short itemNumber);
void SearchObjectCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void SearchObjectControl(short itemNumber);
void DoPickup(ItemInfo* laraItem);

View file

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

View file

@ -1,11 +1,43 @@
#pragma once
#include "framework.h"
#include "Game/collision/floordata.h"
#include "Specific/newtypes.h"
#include "Math/Math.h"
#include "Specific/newtypes.h"
struct TriggerVolume;
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
{
@ -13,8 +45,8 @@ struct ROOM_VERTEX
Vector3 normal;
Vector2 textureCoordinates;
Vector3 color;
int effects;
int index;
int effects;
int index;
};
struct ROOM_DOOR
@ -53,44 +85,22 @@ struct MESH_INFO
struct LIGHTINFO
{
int x; // size=0, offset=0
int y; // size=0, offset=4
int z; // size=0, offset=8
unsigned char Type; // size=0, offset=12
unsigned char r; // size=0, offset=13
unsigned char g; // size=0, offset=14
unsigned char b; // size=0, offset=15
short nx; // size=0, offset=16
short ny; // size=0, offset=18
short nz; // size=0, offset=20
short Intensity; // size=0, offset=22
unsigned char Inner; // size=0, offset=24
unsigned char Outer; // size=0, offset=25
short FalloffScale; // size=0, offset=26
short Length; // size=0, offset=28
short Cutoff; // size=0, offset=30
};
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
int x;
int y;
int z;
unsigned char Type;
unsigned char r;
unsigned char g;
unsigned char b;
short nx;
short ny;
short nz;
short Intensity;
unsigned char Inner;
unsigned char Outer;
short FalloffScale;
short Length;
short Cutoff;
};
struct ROOM_INFO
@ -113,36 +123,26 @@ struct ROOM_INFO
short fxNumber;
bool boundActive;
std::string name;
std::vector<std::string> tags;
std::string name = {};
std::vector<std::string> tags = {};
std::vector<FloorInfo> floor;
std::vector<ROOM_LIGHT> lights;
std::vector<MESH_INFO> mesh;
std::vector<TriggerVolume> triggerVolumes;
std::vector<FloorInfo> floor = {};
std::vector<ROOM_LIGHT> lights = {};
std::vector<MESH_INFO> mesh = {};
std::vector<TriggerVolume> triggerVolumes = {};
std::vector<Vector3> positions;
std::vector<Vector3> normals;
std::vector<Vector3> colors;
std::vector<Vector3> effects;
std::vector<BUCKET> buckets;
std::vector<ROOM_DOOR> doors;
std::vector<Vector3> positions = {};
std::vector<Vector3> normals = {};
std::vector<Vector3> colors = {};
std::vector<Vector3> effects = {};
std::vector<BUCKET> buckets = {};
std::vector<ROOM_DOOR> doors = {};
std::vector<int> neighbors; // TODO: Move to level struct
std::vector<int> neighbors = {};
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 AddRoomFlipItems(ROOM_INFO* room);
void RemoveRoomFlipItems(ROOM_INFO* room);

View file

@ -18,31 +18,30 @@
#include "Game/misc.h"
#include "Game/spotcam.h"
#include "Game/room.h"
#include "Game/Setup.h"
#include "Objects/Generic/Object/rope.h"
#include "Objects/Generic/Switches/fullblock_switch.h"
#include "Objects/Generic/Traps/traps.h"
#include "Objects/Generic/puzzles_keys.h"
#include "Objects/Sink.h"
#include "Objects/TR4/Entity/tr4_beetle_swarm.h"
#include "Objects/TR5/Emitter/tr5_rats_emitter.h"
#include "Objects/TR5/Emitter/tr5_bats_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 "Specific/clock.h"
#include "Specific/level.h"
#include "Specific/setup.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::Entities::Generic;
using namespace TEN::Effects::Items;
using namespace TEN::Entities::Switches;
using namespace TEN::Entities::TR4;
using namespace TEN::Entities::Generic;
using namespace TEN::Floordata;
using namespace flatbuffers;
namespace Save = TEN::Save;
@ -80,7 +79,7 @@ void LoadSavegameInfos()
}
}
Pose ToPHD(Save::Position const* src)
Pose ToPHD(const Save::Position* src)
{
Pose dest;
dest.Position.x = src->x_pos();
@ -92,9 +91,10 @@ Pose ToPHD(Save::Position const* src)
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.y,
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)
{
return Save::Vector3(vec.x, vec.y, vec.z);
@ -126,12 +131,17 @@ Save::Vector4 FromVector4(Vector4 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)
{
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)
@ -149,10 +159,10 @@ Vector4 ToVector4(const Save::Vector4* vec)
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); \
TableBuilder vtb{ fbb }; \
Save::Vector3 saveVec = FromVector3(data); \
SaveType saveVec = ConversionFunc(data); \
vtb.add_vec(&saveVec); \
auto vecOffset = vtb.Finish(); \
putDataInVec(UnionType, vecOffset);
@ -389,7 +399,7 @@ bool SaveGame::Save(int slot)
Save::LaraControlDataBuilder control{ fbb };
control.add_move_angle(Lara.Control.MoveAngle);
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_hand_status((int)Lara.Control.HandStatus);
control.add_is_moving(Lara.Control.IsMoving);
@ -452,7 +462,7 @@ bool SaveGame::Save(int slot)
Save::LaraBuilder lara{ fbb };
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_extra_anim(Lara.ExtraAnim);
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_hit_direction(Lara.HitDirection);
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_item_number(Lara.ItemNumber);
lara.add_left_arm(leftArmOffset);
lara.add_location(Lara.Location);
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_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_entity_number(Lara.TargetEntity - g_Level.Items.data());
lara.add_torch(torchOffset);
lara.add_vehicle(Lara.Vehicle);
lara.add_water_current_active(Lara.WaterCurrentActive);
lara.add_water_current_pull(&FromVector3(Lara.WaterCurrentPull));
lara.add_water_surface_dist(Lara.WaterSurfaceDist);
lara.add_vehicle(Lara.Context.Vehicle);
lara.add_water_current_active(Lara.Context.WaterCurrentActive);
lara.add_water_current_pull(&FromVector3(Lara.Context.WaterCurrentPull));
lara.add_water_surface_dist(Lara.Context.WaterSurfaceDist);
lara.add_weapons(carriedWeaponsOffset);
auto laraOffset = lara.Finish();
@ -527,8 +537,7 @@ bool SaveGame::Save(int slot)
flatbuffers::Offset<Save::Short> shortOffset;
flatbuffers::Offset<Save::Int> intOffset;
if (Objects[itemToSerialize.ObjectNumber].intelligent
&& itemToSerialize.Data.is<CreatureInfo>())
if (Objects[itemToSerialize.ObjectNumber].intelligent && itemToSerialize.IsCreature())
{
auto creature = GetCreatureInfo(&itemToSerialize);
@ -538,12 +547,10 @@ bool SaveGame::Save(int slot)
auto jointRotationsOffset = fbb.CreateVector(jointRotations);
Save::CreatureBuilder creatureBuilder{ fbb };
creatureBuilder.add_alerted(creature->Alerted);
creatureBuilder.add_can_jump(creature->LOT.CanJump);
creatureBuilder.add_can_monkey(creature->LOT.CanMonkey);
creatureBuilder.add_enemy(creature->Enemy - g_Level.Items.data());
creatureBuilder.add_fired_weapon(creature->FiredWeapon);
creatureBuilder.add_flags(creature->Flags);
creatureBuilder.add_friendly(creature->Friendly);
creatureBuilder.add_head_left(creature->HeadLeft);
@ -555,6 +562,8 @@ bool SaveGame::Save(int slot)
creatureBuilder.add_joint_rotation(jointRotationsOffset);
creatureBuilder.add_jump_ahead(creature->JumpAhead);
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_monkey_swing_ahead(creature->MonkeySwingAhead);
creatureBuilder.add_mood((int)creature->Mood);
@ -1164,16 +1173,24 @@ bool SaveGame::Save(int slot)
{
switch (SavedVarType(s.index()))
{
case SavedVarType::Vec2:
{
SaveVec(SavedVarType::Vec2, s, Save::vec2TableBuilder, Save::VarUnion::vec2, Save::Vector2, FromVector2);
}
break;
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;
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;
case SavedVarType::Color:
{
Save::colorTableBuilder ctb{ fbb };
@ -1183,18 +1200,51 @@ bool SaveGame::Save(int slot)
putDataInVec(Save::VarUnion::color, offset);
}
break;
}
}
}
auto unionVec = fbb.CreateVector(varsVec);
Save::UnionVecBuilder uvb{ fbb };
uvb.add_members(unionVec);
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> 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 stringsCallbackPostControl = fbb.CreateVectorOfStrings(callbackVecPostControl);
@ -1243,6 +1293,19 @@ bool SaveGame::Save(int slot)
}
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_post_control(stringsCallbackPostControl);
@ -1543,20 +1606,25 @@ bool SaveGame::Load(int slot)
creature->Alerted = savedCreature->alerted();
creature->LOT.CanJump = savedCreature->can_jump();
creature->LOT.CanMonkey = savedCreature->can_monkey();
if (savedCreature->enemy() >= 0)
creature->Enemy = &g_Level.Items[savedCreature->enemy()];
creature->FiredWeapon = savedCreature->fired_weapon();
creature->Flags = savedCreature->flags();
creature->Friendly = savedCreature->friendly();
creature->HeadLeft = savedCreature->head_left();
creature->HeadRight = savedCreature->head_right();
creature->HurtByLara = savedCreature->hurt_by_lara();
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.IsJumping = savedCreature->is_jumping();
creature->LOT.IsMonkeying = savedCreature->is_monkeying();
for (int j = 0; j < 4; j++)
creature->JointRotation[j] = savedCreature->joint_rotation()->Get(j);
creature->JumpAhead = savedCreature->jump_ahead();
creature->MaxTurn = savedCreature->maximum_turn();
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++)
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.CanClimbLadder = s->lara()->control()->is_climbing_ladder();
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.y = s->lara()->extra_torso_rot()->y();
Lara.ExtraTorsoRot.z = s->lara()->extra_torso_rot()->z();
Lara.WaterCurrentActive = s->lara()->water_current_active();
Lara.WaterCurrentPull.x = s->lara()->water_current_pull()->x();
Lara.WaterCurrentPull.y = s->lara()->water_current_pull()->y();
Lara.WaterCurrentPull.z = s->lara()->water_current_pull()->z();
Lara.Context.WaterCurrentActive = s->lara()->water_current_active();
Lara.Context.WaterCurrentPull.x = s->lara()->water_current_pull()->x();
Lara.Context.WaterCurrentPull.y = s->lara()->water_current_pull()->y();
Lara.Context.WaterCurrentPull.z = s->lara()->water_current_pull()->z();
Lara.Flare.Life = s->lara()->flare()->life();
Lara.Flare.ControlLeft = s->lara()->flare()->control_left();
Lara.Flare.Frame = s->lara()->flare()->frame();
Lara.HighestLocation = s->lara()->highest_location();
Lara.HitDirection = s->lara()->hit_direction();
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.BeetleLife = s->lara()->inventory()->beetle_life();
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.Location = s->lara()->location();
Lara.LocationPad = s->lara()->location_pad();
Lara.NextCornerPos = ToPHD(s->lara()->next_corner_pose());
Lara.ProjectedFloorHeight = s->lara()->projected_floor_height();
Lara.Context.NextCornerPos = ToPHD(s->lara()->next_corner_pose());
Lara.Context.ProjectedFloorHeight = s->lara()->projected_floor_height();
Lara.RightArm.AnimNumber = s->lara()->right_arm()->anim_number();
Lara.RightArm.GunFlash = s->lara()->right_arm()->gun_flash();
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.TargetArmOrient.y = s->lara()->target_arm_angles()->Get(0);
Lara.TargetArmOrient.x = s->lara()->target_arm_angles()->Get(1);
Lara.TargetOrientation.y = s->lara()->target_facing_angle();
Lara.Vehicle = s->lara()->vehicle();
Lara.WaterSurfaceDist = s->lara()->water_surface_dist();
Lara.Context.TargetOrientation.y = s->lara()->target_facing_angle();
Lara.Context.Vehicle = s->lara()->vehicle();
Lara.Context.WaterSurfaceDist = s->lara()->water_surface_dist();
for (int i = 0; i < s->lara()->weapons()->size(); i++)
{
@ -1988,10 +2056,10 @@ bool SaveGame::Load(int slot)
std::vector<SavedVar> loadedVars;
auto theVec = s->script_vars();
if (theVec)
auto unionVec = s->script_vars();
if (unionVec)
{
for (auto const& var : *(theVec->members()))
for (const auto& var : *(unionVec->members()))
{
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& loadedTab = loadedVars.emplace_back(IndexTable{});
for (auto const& p : *tab)
{
std::get<IndexTable>(loadedTab).push_back(std::make_pair(p->key(), p->val()));
}
for (const auto& pair : *tab)
std::get<IndexTable>(loadedTab).push_back(std::make_pair(pair->key(), pair->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)
{
auto stored = var->u_as_vec3()->vec();
SavedVar v;
v.emplace<(int)SavedVarType::Vec3>(ToVector3i(stored));
loadedVars.push_back(v);
SavedVar var;
var.emplace<(int)SavedVarType::Vec3>(ToVector3i(stored));
loadedVars.push_back(var);
}
else if (var->u_type() == Save::VarUnion::rotation)
{
auto stored = var->u_as_rotation()->vec();
SavedVar v;
v.emplace<(int)SavedVarType::Rotation>(ToVector3(stored));
loadedVars.push_back(v);
SavedVar var;
var.emplace<(int)SavedVarType::Rotation>(ToVector3(stored));
loadedVars.push_back(var);
}
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()});
}
}
}
g_GameScript->SetVariables(loadedVars);
std::vector<std::string> callbacksPreControlVec;
auto callbacksPreControlOffsetVec = s->callbacks_pre_control();
for (auto const& s : *callbacksPreControlOffsetVec)
callbacksPreControlVec.push_back(s->str());
auto populateCallbackVecs = [&s](auto callbackFunc)
{
auto callbacksVec = std::vector<std::string>{};
auto callbacksOffsetVec = std::invoke(callbackFunc, s);
std::vector<std::string> callbacksPostControlVec;
auto callbacksPostControlOffsetVec = s->callbacks_post_control();
for (auto const& s : *callbacksPostControlOffsetVec)
callbacksPostControlVec.push_back(s->str());
for (const auto& e : *callbacksOffsetVec)
callbacksVec.push_back(e->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;
}

View file

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

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