Merge branch 'develop' into voncroy_reworked

This commit is contained in:
TokyoSU 2024-10-26 11:05:54 +02:00
commit c9ec3150ec
77 changed files with 916 additions and 774 deletions

View file

@ -1,6 +1,6 @@
# Changelog
Here you will find the full changelog of TEN's releases from Version 1.0 and up
Here you will find the full changelog of TEN's releases from Version 1.0 and up
The dates are in European standard format where date is presented as **YYYY-MM-DD**
@ -10,6 +10,16 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
### Bug fixes
* Fixed original issue with classic switch off trigger incorrectly activating some trigger actions.
* Fixed moveable status after antitriggering.
* Fixed leveljump vehicle transfer.
* Fixed weapons not properly hitting enemies.
* Fixed laserhead teleporting Lara and making her invisible on death.
* Fixed sarcophagus pick-ups.
* Fixed issue with Lara not rotating together with bridges while picking up items.
* Fixed ghost collision with moveables with zero bounds.
* Fixed several binocular bugs.
* Fixed incorrect climbing out of water on bridge objects.
* Fixed faulty death sectors.
* Fixed incorrect diving animation when swandiving from a high place.
* Fixed camera rotating with the player's hips when climbing out of water.
* Fixed AI for TR2 skidoo driver and worker with shotgun.
@ -26,36 +36,45 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* Fixed teeth spikes not triggering the player impale animation.
* Fixed TR4 mine crash with OCB 1 when triggered.
* Fixed cases where Atlantean mutant's bombs cause the game to crash.
* Fixed young hair drawing.
* Fixed young Lara hair drawing. https://tombengine.com/docs/level-settings/#young_lara
### Features/Amendments
* Changed Rome Hammer to not hurt player whilst deactivated.
* Changed Statue with blade damage, from 20 to 200.
* Enhanced Rolling Spindle detection to avoid them going down through pits.
* Enhanced Sentry Guns, with a new ItemFlags[3], to contain the ID of the inventory item that deactivates the sentry guns ( by default PUZZLE_ITEM5 )
* Enhanced Dart Emitter, with a new ItemFlags[0], to contain the number of frames between shots ( by default 32 in dart emitter, and 24 in homing dar emitter ).
* Enhanced raptor behaviour and handling.
- OCB 0: Classic behaviour
- OCB 1: Can jump up/down up to 4 steps and jump across gaps up to 2 blocks wide.
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Raptor.wad2
* Added TR3 seal mutant.
- OCB 0: Normal enemy behaviour. (TR3 RX-Tech mines level)
- OCB 1: Trap like behaviour. (TR3 Antarctica level)
* Added variable framerate , that allows the engine to run at an unlocked framerate for a much smoother experience. Setting can be toggled on or off in the graphical settings menu.
* Added a customisable global lensflare effect. https://tombengine.com/docs/level-settings/#lensflare
* Added a customisable starry sky and meteor effect (based on TR5). https://tombengine.com/docs/level-settings/#stars
* Added the ability to display "Lara's Home" entry in the main menu.
* Added F12 as alternative to PrtSc for screenshots.
* Added option to enable or disable menu option looping.
- Menu scrolling using held inputs will stop at the last option until a new input is made.
* Added TR3 seal mutant. https://tombengine.com/docs/ocb-and-setup-instructions/#sealmutant
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Seal_Mutant.wad2
* Add new sound conditions: Quicksand and Underwater.
- Quicksand - sound effect plays when a moveable is in quicksand.
- Underwater - sound plays when the camera is submerged.
* Added TR4 Enemy_Jeep https://tombengine.com/docs/ocb-and-setup-instructions/#enemy_jeep
* Changed Rome Hammer to not hurt player whilst deactivated.
* Changed Statue with blade damage, from 20 to 200.
* Changed sound effect that is triggered when using the `level.rumble` feature in a level. Sound effect now part of the default soundmap (ID 359) and additional hardcoded pitch shift has been removed.
* Changed hardcoded sound for RAISING_BLOCKS back to the soundID used in TRLE (ID 149)
* Changed Water sound condition to ShallowWater.
* Added option to enable or disable menu option looping.
* Menu scrolling using held inputs will stop at the last option until a new input is made.
* Added the ability to display "Lara's Home" entry in the main menu.
* Enhanced Rolling Spindle detection to avoid them going down through pits.
* Enhanced Sentry Guns, with a new ItemFlags[3], to contain the ID of the inventory item that deactivates the sentry guns ( by default PUZZLE_ITEM5 )
* Enhanced Dart Emitter, with a new ItemFlags[0], to contain the number of frames between shots ( by default 32 in dart emitter, and 24 in homing-dart emitter ).
* Enhanced raptor behaviour and handling. https://tombengine.com/docs/ocb-and-setup-instructions/#raptor
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Raptor.wad2
* The limit of 32 active Flame Emitters has been removed.
### Lua API changes
* Added Flow.EnableHomeLevel() function.
* Added Flow.IsStringPresent() function.
* Added Flow.LensFlare() and Flow.Starfield() classes.
* Added Inventory.GetUsedItem(), Inventory.SetUsedItem() and Inventory.ClearUsedItem() functions.
* Added Input.KeyClearAll()
* Added Flow.EnableHomeLevel()
* Added Input.KeyClearAll() function.
* Added Moveable.GetJointRotation() and optional 'offset' parameter for Moveable.GetJointPosition().
* Added Room:GetRoomNumber() function.
* Removed anims.monkeyAutoJump. It is now a player menu configuration.
* Fixed Volume:GetActive() method
* Fixed Volume:GetActive() method.
## [Version 1.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.1) - 2024-04-21

View file

@ -234,6 +234,10 @@ scripts too.</p>
<td class="summary">Get translated string.</td>
</tr>
<tr>
<td class="name" ><a href="#IsStringPresent">IsStringPresent(string)</a></td>
<td class="summary">Check if translated string is present.</td>
</tr>
<tr>
<td class="name" ><a href="#SetLanguageNames">SetLanguageNames(table)</a></td>
<td class="summary">Set language names for translations.</td>
</tr>
@ -927,6 +931,28 @@ You will not need to call them manually.
</dd>
<dt>
<a name = "IsStringPresent"></a>
<strong>IsStringPresent(string)</strong>
</dt>
<dd>
Check if translated string is present.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">string</span>
<span class="types"><span class="type">key</span></span>
key for translated string
</li>
</ul>
</dd>
<dt>
<a name = "SetLanguageNames"></a>

View file

@ -108,7 +108,7 @@ pickups, and Lara herself (see also <a href="../2 classes/Objects.LaraObject.htm
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#Moveable">Moveable(object, name, position, rotation, roomID, animNumber, frameNumber, hp, OCB, AIBits)</a></td>
<td class="name" ><a href="#Moveable">Moveable(object, name, position, rotation, roomNumber, animNumber, frameNumber, hp, OCB, AIBits)</a></td>
<td class="summary">Used to generate a new moveable dynamically at runtime.</td>
</tr>
<tr>
@ -390,7 +390,7 @@ pickups, and Lara herself (see also <a href="../2 classes/Objects.LaraObject.htm
<dl class="function">
<dt>
<a name = "Moveable"></a>
<strong>Moveable(object, name, position, rotation, roomID, animNumber, frameNumber, hp, OCB, AIBits)</strong>
<strong>Moveable(object, name, position, rotation, roomNumber, animNumber, frameNumber, hp, OCB, AIBits)</strong>
</dt>
<dd>
Used to generate a new moveable dynamically at runtime. <br/>
@ -418,9 +418,9 @@ most can just be ignored (see usage).
<span class="types"><a class="type" href="../3 primitive classes/Rotation.html#">Rotation</a></span>
rotation rotation about x, y, and z axes (default Rotation(0, 0, 0))
</li>
<li><span class="parameter">roomID</span>
<li><span class="parameter">roomNumber</span>
<span class="types"><span class="type">int</span></span>
room ID item is in (default: calculated automatically)
the room number the moveable is in (default: calculated automatically).
</li>
<li><span class="parameter">animNumber</span>
<span class="types"><span class="type">int</span></span>

View file

@ -100,7 +100,7 @@
<div id="content">
<h1>Class <code>Objects.Room</code></h1>
<p>Rooms</p>
<p>Room object.</p>
<p>
</p>
@ -109,8 +109,12 @@
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" ><a href="#Room:GetActive">Room:GetActive()</a></td>
<td class="summary">Determine whether the room is active or not</td>
<td class="name" ><a href="#Room:GetRoomNumber">Room:GetRoomNumber()</a></td>
<td class="summary">Get the room's number.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:GetName">Room:GetName()</a></td>
<td class="summary">Get the room's unique string identifier.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:GetColor">Room:GetColor()</a></td>
@ -121,28 +125,28 @@
<td class="summary">Get the room's reverb type.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:SetReverbType">Room:SetReverbType(new)</a></td>
<td class="name" ><a href="#Room:SetName">Room:SetName(name)</a></td>
<td class="summary">Set the room's unique string identifier.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:SetReverbType">Room:SetReverbType(Reverb)</a></td>
<td class="summary">Set the room's reverb type.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:GetName">Room:GetName()</a></td>
<td class="summary">Get the room's unique string identifier.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:SetName">Room:SetName(name)</a></td>
<td class="summary">Set the room's name (its unique string identifier).</td>
<td class="name" ><a href="#Room:SetFlag">Room:SetFlag(flagID, Boolean)</a></td>
<td class="summary">Set the room's specified flag.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:GetFlag">Room:GetFlag(flagID)</a></td>
<td class="summary">Get the room's specified flag value (true or false).</td>
</tr>
<tr>
<td class="name" ><a href="#Room:SetFlag">Room:SetFlag(flagID, the)</a></td>
<td class="summary">Set the room's specified flag value.</td>
<td class="name" ><a href="#Room:IsTagPresent">Room:IsTagPresent(tag)</a></td>
<td class="summary">Check if the specified tag is set for the room.</td>
</tr>
<tr>
<td class="name" ><a href="#Room:IsTagPresent">Room:IsTagPresent(tag)</a></td>
<td class="summary">Checks if specified tag is set for this room.</td>
<td class="name" ><a href="#Room:GetActive">Room:GetActive()</a></td>
<td class="summary">Check if the room is active.</td>
</tr>
</table>
@ -154,11 +158,11 @@
<dl class="function">
<dt>
<a name = "Room:GetActive"></a>
<strong>Room:GetActive()</strong>
<a name = "Room:GetRoomNumber"></a>
<strong>Room:GetRoomNumber()</strong>
</dt>
<dd>
Determine whether the room is active or not
Get the room's number. ()
@ -166,8 +170,29 @@
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
true if the room is active
<span class="types"><span class="type">int</span></span>
Room number.
</ol>
</dd>
<dt>
<a name = "Room:GetName"></a>
<strong>Room:GetName()</strong>
</dt>
<dd>
Get the room's unique string identifier. ()
<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>
Room name.
</ol>
@ -179,7 +204,7 @@
<strong>Room:GetColor()</strong>
</dt>
<dd>
Get the room's ambient light color.
Get the room's ambient light color. ()
@ -188,7 +213,7 @@
<ol>
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
ambient light color of the room
Ambient light color.
</ol>
@ -200,7 +225,7 @@
<strong>Room:GetReverbType()</strong>
</dt>
<dd>
Get the room's reverb type.
Get the room's reverb type. ()
@ -209,50 +234,7 @@
<ol>
<span class="types"><a class="type" href="../4 enums/Objects.RoomReverb.html#">RoomReverb</a></span>
room's reverb type
</ol>
</dd>
<dt>
<a name = "Room:SetReverbType"></a>
<strong>Room:SetReverbType(new)</strong>
</dt>
<dd>
Set the room's reverb type.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">new</span>
<span class="types"><a class="type" href="../4 enums/Objects.RoomReverb.html#">RoomReverb</a></span>
reverb type of the room
</li>
</ul>
</dd>
<dt>
<a name = "Room:GetName"></a>
<strong>Room:GetName()</strong>
</dt>
<dd>
Get the room's unique string identifier.
<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>
the room's name
Reverb type.
</ol>
@ -264,7 +246,7 @@
<strong>Room:SetName(name)</strong>
</dt>
<dd>
Set the room's name (its unique string identifier).
Set the room's unique string identifier. ()
@ -272,7 +254,55 @@
<ul>
<li><span class="parameter">name</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
The room's new name
New name.
</li>
</ul>
</dd>
<dt>
<a name = "Room:SetReverbType"></a>
<strong>Room:SetReverbType(Reverb)</strong>
</dt>
<dd>
Set the room's reverb type. ()
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">Reverb</span>
<span class="types"><a class="type" href="../4 enums/Objects.RoomReverb.html#">RoomReverb</a></span>
type.
</li>
</ul>
</dd>
<dt>
<a name = "Room:SetFlag"></a>
<strong>Room:SetFlag(flagID, Boolean)</strong>
</dt>
<dd>
Set the room's specified flag. ()
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">flagID</span>
<span class="types"><a class="type" href="../4 enums/Objects.RoomFlagID.html#">RoomFlagID</a></span>
Room flag ID.
</li>
<li><span class="parameter">Boolean</span>
<span class="types"><span class="type">bool</span></span>
to set the flag to.
</li>
</ul>
@ -286,7 +316,7 @@
<strong>Room:GetFlag(flagID)</strong>
</dt>
<dd>
Get the room's specified flag value (true or false).
Get the room's specified flag value (true or false). ()
@ -294,39 +324,7 @@
<ul>
<li><span class="parameter">flagID</span>
<span class="types"><a class="type" href="../4 enums/Objects.RoomFlagID.html#">RoomFlagID</a></span>
The room's flag ID
</li>
</ul>
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
the room's specified flag value
</ol>
</dd>
<dt>
<a name = "Room:SetFlag"></a>
<strong>Room:SetFlag(flagID, the)</strong>
</dt>
<dd>
Set the room's specified flag value.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">flagID</span>
<span class="types"><a class="type" href="../4 enums/Objects.RoomFlagID.html#">RoomFlagID</a></span>
The room's flag ID
</li>
<li><span class="parameter">the</span>
<span class="types"><span class="type">bool</span></span>
room's new flag value
Room flag ID.
</li>
</ul>
@ -340,7 +338,7 @@
<strong>Room:IsTagPresent(tag)</strong>
</dt>
<dd>
Checks if specified tag is set for this room.
Check if the specified tag is set for the room. ()
@ -348,7 +346,7 @@
<ul>
<li><span class="parameter">tag</span>
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
A text tag to check (case sensitive)
Text tag to check (case sensitive).
</li>
</ul>
@ -356,7 +354,28 @@
<ol>
<span class="types"><span class="type">bool</span></span>
true if tag is present, false if not
Boolean of the tag's presence.
</ol>
</dd>
<dt>
<a name = "Room:GetActive"></a>
<strong>Room:GetActive()</strong>
</dt>
<dd>
Check if the room is active. ()
<h3>Returns:</h3>
<ol>
<span class="types"><span class="type">bool</span></span>
Boolean of the room's active status.
</ol>

View file

@ -171,8 +171,8 @@ LARA_PETROL_MESH
LARA_DIRT_MESH
LARA_CROWBAR_ANIM
LARA_TORCH_ANIM
SINGLE_BRAID_HAIR
DUAL_PIGTAIL_HAIR
HAIR_PRIMARY
HAIR_SECONDARY
SNOWMOBILE_LARA_ANIMS
SNOWMOBILE
QUAD_LARA_ANIMS

View file

@ -217,7 +217,7 @@ local door = GetMoveableByName("door_type4_14")
</tr>
<tr>
<td class="name" ><a href="2 classes/Objects.Room.html">Objects.Room</a></td>
<td class="summary">Rooms</td>
<td class="summary">Room object.</td>
</tr>
<tr>
<td class="name" ><a href="2 classes/Objects.Sink.html">Objects.Sink</a></td>

View file

@ -71,7 +71,7 @@ namespace TEN::Entities::Player
PlayerBehaviorStateRoutines[LS_PUSHABLE_PUSH] = std::pair(lara_as_pushable_push, lara_default_col);
PlayerBehaviorStateRoutines[LS_PUSHABLE_PULL] = std::pair(lara_as_pushable_pull, lara_default_col);
PlayerBehaviorStateRoutines[LS_PUSHABLE_GRAB] = std::pair(lara_as_pushable_grab, lara_default_col);
PlayerBehaviorStateRoutines[LS_PICKUP] = std::pair(lara_as_pickup, lara_default_col);
PlayerBehaviorStateRoutines[LS_PICKUP] = std::pair(lara_as_pickup, lara_col_pickup);
PlayerBehaviorStateRoutines[LS_SWITCH_DOWN] = std::pair(lara_as_switch_on, lara_default_col);
PlayerBehaviorStateRoutines[LS_SWITCH_UP] = std::pair(lara_as_switch_off, lara_default_col);
PlayerBehaviorStateRoutines[LS_INSERT_KEY] = std::pair(lara_as_use_key, lara_default_col);
@ -99,7 +99,7 @@ namespace TEN::Entities::Player
PlayerBehaviorStateRoutines[LS_TEST_3] = std::pair(lara_void_func, lara_void_func);
PlayerBehaviorStateRoutines[LS_WADE_FORWARD] = std::pair(lara_as_wade_forward, lara_col_wade_forward);
PlayerBehaviorStateRoutines[LS_UNDERWATER_ROLL] = std::pair(lara_as_underwater_roll_180, lara_col_underwater_roll_180);
PlayerBehaviorStateRoutines[LS_PICKUP_FLARE] = std::pair(lara_as_pickup_flare, lara_default_col);
PlayerBehaviorStateRoutines[LS_PICKUP_FLARE] = std::pair(lara_as_pickup_flare, lara_col_pickup);
PlayerBehaviorStateRoutines[LS_JUMP_ROLL_180] = std::pair(lara_void_func, lara_void_func);
PlayerBehaviorStateRoutines[LS_KICK] = std::pair(lara_void_func, lara_void_func);
PlayerBehaviorStateRoutines[LS_ZIP_LINE] = std::pair(lara_as_zip_line, lara_void_func);

View file

@ -362,8 +362,6 @@ void LaraAboveWater(ItemInfo* item, CollisionInfo* coll)
coll->Setup.PrevFrameNumber = item->Animation.FrameNumber;
coll->Setup.PrevState = item->Animation.ActiveState;
UpdateLaraRoom(item, -LARA_HEIGHT / 2);
// Handle look-around.
if (((IsHeld(In::Look) && CanPlayerLookAround(*item)) ||
(player.Control.Look.IsUsingBinoculars || player.Control.Look.IsUsingLasersight)) &&
@ -378,6 +376,8 @@ void LaraAboveWater(ItemInfo* item, CollisionInfo* coll)
}
player.Control.Look.Mode = LookMode::None;
UpdateLaraRoom(item, -LARA_HEIGHT / 2);
// Process vehicles.
if (HandleLaraVehicle(item, coll))
return;

View file

@ -39,17 +39,7 @@ void lara_void_func(ItemInfo* item, CollisionInfo* coll)
void lara_default_col(ItemInfo* item, CollisionInfo* coll)
{
auto& player = GetLaraInfo(*item);
player.Control.MoveAngle = item->Pose.Orientation.y;
coll->Setup.LowerFloorBound = STEPUP_HEIGHT;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;
coll->Setup.LowerCeilingBound = 0;
coll->Setup.BlockFloorSlopeDown = true;
coll->Setup.BlockFloorSlopeUp = true;
coll->Setup.ForwardAngle = player.Control.MoveAngle;
GetCollisionInfo(coll, item);
LaraResetGravityStatus(item, coll);
LaraDefaultCollision(item, coll);
}
// Boulder death.

View file

@ -507,6 +507,22 @@ void LaraSurfaceCollision(ItemInfo* item, CollisionInfo* coll)
}
}
void LaraDefaultCollision(ItemInfo* item, CollisionInfo* coll)
{
auto& player = GetLaraInfo(*item);
player.Control.MoveAngle = item->Pose.Orientation.y;
coll->Setup.LowerFloorBound = STEPUP_HEIGHT;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;
coll->Setup.LowerCeilingBound = 0;
coll->Setup.BlockFloorSlopeDown = true;
coll->Setup.BlockFloorSlopeUp = true;
coll->Setup.ForwardAngle = player.Control.MoveAngle;
GetCollisionInfo(coll, item);
LaraResetGravityStatus(item, coll);
}
void LaraSwimCollision(ItemInfo* item, CollisionInfo* coll)
{
auto* lara = GetLaraInfo(item);

View file

@ -25,6 +25,7 @@ void GetLaraDeadlyBounds();
void LaraJumpCollision(ItemInfo* item, CollisionInfo* coll, short moveAngle);
void LaraSurfaceCollision(ItemInfo* item, CollisionInfo* coll);
void LaraSwimCollision(ItemInfo* item, CollisionInfo* coll);
void LaraDefaultCollision(ItemInfo* item, CollisionInfo* coll);
void LaraWaterCurrent(ItemInfo* item, CollisionInfo* coll);

View file

@ -13,8 +13,12 @@
#include "Game/Lara/PlayerStateMachine.h"
#include "Game/Setup.h"
#include "Objects/TR2/Vehicles/skidoo.h"
#include "Objects/TR2/Vehicles/speedboat.h"
#include "Objects/TR3/Vehicles/kayak.h"
#include "Objects/TR3/Vehicles/minecart.h"
#include "Objects/TR3/Vehicles/quad_bike.h"
#include "Objects/TR3/Vehicles/rubber_boat.h"
#include "Objects/TR3/Vehicles/upv.h"
#include "Objects/TR4/Vehicles/jeep.h"
#include "Objects/TR4/Vehicles/motorbike.h"
#include "Specific/level.h"
@ -122,6 +126,9 @@ void InitializeLaraAnims(ItemInfo* item)
player.LeftArm.Locked = false;
player.RightArm.Locked = false;
if (PlayerVehicleObjectID != GAME_OBJECT_ID::ID_NO_OBJECT)
return;
if (TestEnvironment(ENV_FLAG_WATER, item))
{
SetAnimation(item, LA_UNDERWATER_IDLE);
@ -175,7 +182,7 @@ void InitializeLaraStartPosition(ItemInfo& playerItem)
playerItem.Location.Height = playerItem.Pose.Position.y;
}
static void InitializePlayerVehicle(ItemInfo& playerItem)
void InitializePlayerVehicle(ItemInfo& playerItem)
{
if (PlayerVehicleObjectID == GAME_OBJECT_ID::ID_NO_OBJECT)
return;
@ -187,7 +194,6 @@ static void InitializePlayerVehicle(ItemInfo& playerItem)
// Restore vehicle.
TENLog("Transferring vehicle " + GetObjectName(PlayerVehicleObjectID) + " from the previous level.");
vehicle->Pose = playerItem.Pose;
ItemNewRoom(vehicle->Index, playerItem.RoomNumber);
SetLaraVehicle(&playerItem, vehicle);
playerItem.Animation = PlayerAnim;
@ -197,6 +203,7 @@ static void InitializePlayerVehicle(ItemInfo& playerItem)
{
case GAME_OBJECT_ID::ID_KAYAK:
InitializeKayak(vehicle->Index);
KayakPaddleTake(GetKayakInfo(&g_Level.Items[vehicle->Index]), &playerItem);
break;
case GAME_OBJECT_ID::ID_MOTORBIKE:
@ -215,9 +222,38 @@ static void InitializePlayerVehicle(ItemInfo& playerItem)
InitializeSkidoo(vehicle->Index);
break;
case GAME_OBJECT_ID::ID_MINECART:
MinecartWrenchTake(GetMinecartInfo(&g_Level.Items[vehicle->Index]), &playerItem);
break;
case GAME_OBJECT_ID::ID_SPEEDBOAT:
InitializeSpeedboat(vehicle->Index);
DoSpeedboatMount(&g_Level.Items[vehicle->Index], &playerItem, VehicleMountType::LevelStart);
break;
case GAME_OBJECT_ID::ID_RUBBER_BOAT:
InitializeRubberBoat(vehicle->Index);
DoRubberBoatMount(&g_Level.Items[vehicle->Index], &playerItem, VehicleMountType::LevelStart);
break;
case GAME_OBJECT_ID::ID_UPV:
DoUPVMount(&g_Level.Items[vehicle->Index], &playerItem, VehicleMountType::LevelStart);
GetUPVInfo(&g_Level.Items[vehicle->Index])->Flags = UPVFlags::UPV_FLAG_CONTROL;
break;
default:
break;
}
// HACK: Reset activity status because boats need to be on active item linked list.
if (vehicle->ObjectNumber == GAME_OBJECT_ID::ID_RUBBER_BOAT ||
vehicle->ObjectNumber == GAME_OBJECT_ID::ID_SPEEDBOAT)
{
RemoveActiveItem(vehicle->Index, false);
AddActiveItem(vehicle->Index);
g_Level.Items[vehicle->Index].Status = ITEM_ACTIVE;
}
}
void InitializeLaraLevelJump(ItemInfo* item, LaraInfo* playerBackup)
@ -260,9 +296,6 @@ void InitializeLaraLevelJump(ItemInfo* item, LaraInfo* playerBackup)
// Restore hit points.
item->HitPoints = PlayerHitPoints;
// Restore vehicle.
InitializePlayerVehicle(*item);
}
void InitializeLaraDefaultInventory(ItemInfo& item)

View file

@ -9,3 +9,4 @@ void InitializeLaraAnims(ItemInfo* item);
void InitializeLaraStartPosition(ItemInfo& playerItem);
void InitializeLaraLevelJump(ItemInfo* item, LaraInfo* playerBackup);
void InitializeLaraDefaultInventory(ItemInfo& item);
void InitializePlayerVehicle(ItemInfo& playerItem);

View file

@ -8,6 +8,7 @@
#include "Game/items.h"
#include "Game/Lara/PlayerContext.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_collide.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_tests.h"
#include "Objects/Generic/Object/rope.h"
@ -45,6 +46,12 @@ void lara_as_pickup(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = GetNextAnimState(item);
}
void lara_col_pickup(ItemInfo* item, CollisionInfo* coll)
{
LaraDefaultCollision(item, coll);
ShiftItem(item, coll);
}
// State: LS_PICKUP_FLARE (67)
// Collision: lara_default_col()
void lara_as_pickup_flare(ItemInfo* item, CollisionInfo* coll)

View file

@ -14,6 +14,7 @@ struct CollisionInfo;
void lara_as_pickup(ItemInfo* item, CollisionInfo* coll);
void lara_as_pickup_flare(ItemInfo* item, CollisionInfo* coll);
void lara_col_pickup(ItemInfo* item, CollisionInfo* coll);
// ------
// SWITCH

View file

@ -935,11 +935,11 @@ bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll)
// Extra bridge check.
if (coll->Front.Bridge != NO_VALUE)
{
int bridgeBorder = GetBridgeBorder(g_Level.Items[coll->Front.Bridge], false) - item->Pose.Position.y;
frontFloor = GetBridgeBorder(g_Level.Items[coll->Front.Bridge], false) - item->Pose.Position.y;
frontFloor = bridgeBorder - CLICK(0.5f);
if (frontFloor <= -CLICK(2) ||
frontFloor > CLICK(1.25f) - 4)
int bridgeBorder = frontFloor - CLICK(0.5f);
if (bridgeBorder <= -CLICK(2) ||
bridgeBorder > CLICK(1.25f) - 4)
{
return false;
}

View file

@ -952,9 +952,19 @@ void BinocularCamera(ItemInfo* item)
player.Inventory.IsBusy = false;
Camera.type = BinocularOldCamera;
Camera.target = LastTarget;
AlterFOV(LastFOV);
return;
}
if (IsHeld(In::Action))
{
ClearAction(In::Action);
auto origin = Camera.pos.ToVector3i();
auto target = Camera.target.ToVector3i();
LaraTorch(&origin, &target, player.ExtraHeadRot.y, 192);
}
}
AlterFOV(7 * (ANGLE(11.5f) - player.Control.Look.OpticRange), false);
@ -1022,13 +1032,6 @@ void BinocularCamera(ItemInfo* item)
Camera.oldType = Camera.type;
GetTargetOnLOS(&Camera.pos, &Camera.target, false, false);
if (IsHeld(In::Action))
{
auto origin = Camera.pos.ToVector3i();
auto target = Camera.target.ToVector3i();
LaraTorch(&origin, &target, player.ExtraHeadRot.y, 192);
}
}
void ConfirmCameraTargetPos()
@ -1518,6 +1521,21 @@ void ItemsCollideCamera()
staticList.clear();
}
void UpdateCamera()
{
if (UseSpotCam)
{
// Draw flyby cameras.
CalculateSpotCameras();
}
else
{
// Do the standard camera.
TrackCameraInit = false;
CalculateCamera(LaraCollision);
}
}
void UpdateMikePos(const ItemInfo& item)
{
if (Camera.mikeAtLara)
@ -1545,7 +1563,8 @@ void UpdateMikePos(const ItemInfo& item)
void RumbleScreen()
{
if (!(GlobalCounter & 0x1FF))
SoundEffect(SFX_TR5_KLAXON, nullptr, SoundEnvironment::Land, 0.25f);
// SFX Enum Changed from TR5 and pitch shift removed. User can set this in their sound XML. Stranger1992 31st August 2024
SoundEffect(SFX_TR4_ENVIORONMENT_RUMBLE, nullptr, SoundEnvironment::Land);
if (RumbleTimer >= 0)
RumbleTimer++;

View file

@ -103,16 +103,18 @@ void RumbleScreen();
bool TestBoundsCollideCamera(const GameBoundingBox& bounds, const Pose& pose, short radius);
void ItemPushCamera(GameBoundingBox* bounds, Pose* pos, short radius);
void ItemsCollideCamera();
void RefreshFixedCamera(short camNumber);
void ObjCamera(ItemInfo* camSlotId, int camMeshID, ItemInfo* targetItem, int targetMeshID, bool cond);
void MoveObjCamera(GameVector* ideal, ItemInfo* camSlotId, int camMeshID, ItemInfo* targetItem, int targetMeshID);
void RefreshFixedCamera(short camNumber);
void ClearObjCamera();
void SetScreenFadeOut(float speed, bool force = false);
void SetScreenFadeIn(float speed, bool force = false);
void SetCinematicBars(float height, float speed);
void ClearCinematicBars();
void UpdateCamera();
void UpdateFadeScreenAndCinematicBars();
void UpdateMikePos(const ItemInfo& item);
void ClearObjCamera();
float GetParticleDistanceFade(const Vector3i& pos);

View file

@ -566,6 +566,9 @@ bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
const auto& bounds = GetBestFrame(*item).BoundingBox;
const auto& playerBounds = GetBestFrame(*laraItem).BoundingBox;
if (bounds.GetExtents() == Vector3::Zero || playerBounds.GetExtents() == Vector3::Zero)
return false;
if ((item->Pose.Position.y + bounds.Y2) <= (laraItem->Pose.Position.y + playerBounds.Y1))
return false;

View file

@ -182,6 +182,10 @@ int FloorInfo::GetSurfaceHeight(int x, int z, bool isFloor) const
auto normal = tri.Plane.Normal();
float relPlaneHeight = -((normal.x * sectorPoint.x) + (normal.z * sectorPoint.y)) / normal.y;
// Due to precision loss, we can't recover NO_HEIGHT constant from the plane, and must return original integer constant.
if (tri.Plane.D() == (float)NO_HEIGHT)
return NO_HEIGHT;
// Return sector floor or ceiling height. NOTE: Bridges ignored.
return (tri.Plane.D() + relPlaneHeight);
}

View file

@ -27,13 +27,11 @@ using namespace TEN::Effects::Smoke;
constexpr auto ESCAPE_DIST = BLOCK(5);
constexpr auto STALK_DIST = BLOCK(3);
constexpr auto REACHED_GOAL_RADIUS = 640;
constexpr auto REACHED_GOAL_RADIUS = BLOCK(0.625);
constexpr auto ATTACK_RANGE = SQUARE(BLOCK(3));
constexpr auto ESCAPE_CHANCE = 0x800;
constexpr auto RECOVER_CHANCE = 0x100;
constexpr auto BIFF_AVOID_TURN = ANGLE(11.25f);
constexpr auto FEELER_DISTANCE = CLICK(2);
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);
@ -681,14 +679,13 @@ void CreatureJoint(ItemInfo* item, short joint, short required, short maxAngle)
if (!item->IsCreature())
return;
constexpr auto MAX_CHANGE = ANGLE(5.0f);
auto* creature = GetCreatureInfo(item);
short change = required - creature->JointRotation[joint];
if (change > MAX_CHANGE)
change = MAX_CHANGE;
else if (change < -MAX_CHANGE)
change = -MAX_CHANGE;
if (change > ANGLE(3.0f))
change = ANGLE(3.0f);
else if (change < ANGLE(-3.0f))
change = ANGLE(-3.0f);
creature->JointRotation[joint] += change;
if (creature->JointRotation[joint] > maxAngle)
@ -896,7 +893,7 @@ int CreatureCreature(short itemNumber)
{
auto* linked = &g_Level.Items[link];
if (link != itemNumber && linked != LaraItem && linked->Status == ITEM_ACTIVE && linked->HitPoints > 0) // TODO: deal with LaraItem global.
if (link != itemNumber && linked != LaraItem && linked->IsCreature() && linked->Status == ITEM_ACTIVE && linked->HitPoints > 0) // TODO: deal with LaraItem global.
{
int xDistance = abs(linked->Pose.Position.x - x);
int zDistance = abs(linked->Pose.Position.z - z);
@ -1044,7 +1041,7 @@ bool SearchLOT(LOTInfo* LOT, int depth)
if ((node->searchNumber & SEARCH_NUMBER) < (expand->searchNumber & SEARCH_NUMBER))
continue;
if (node->searchNumber & BLOCKED_SEARCH)
if (node->searchNumber & SEARCH_BLOCKED)
{
if ((node->searchNumber & SEARCH_NUMBER) == (expand->searchNumber & SEARCH_NUMBER))
continue;
@ -1053,12 +1050,12 @@ bool SearchLOT(LOTInfo* LOT, int depth)
}
else
{
if ((node->searchNumber & SEARCH_NUMBER) == (expand->searchNumber & SEARCH_NUMBER) && !(expand->searchNumber & BLOCKED_SEARCH))
if ((node->searchNumber & SEARCH_NUMBER) == (expand->searchNumber & SEARCH_NUMBER) && !(expand->searchNumber & SEARCH_BLOCKED))
continue;
if (g_Level.PathfindingBoxes[boxNumber].flags & LOT->BlockMask)
{
expand->searchNumber = node->searchNumber | BLOCKED_SEARCH;
expand->searchNumber = node->searchNumber | SEARCH_BLOCKED;
}
else
{
@ -1191,8 +1188,7 @@ bool IsCreatureVaultAvailable(ItemInfo* item, int stepCount)
item->ObjectNumber != ID_LIZARD &&
item->ObjectNumber != ID_APE &&
item->ObjectNumber != ID_SMALL_SPIDER &&
item->ObjectNumber != ID_SOPHIA_LEIGH_BOSS &&
item->ObjectNumber != ID_VON_CROY);
item->ObjectNumber != ID_SOPHIA_LEIGH_BOSS);
case -2:
return (item->ObjectNumber != ID_BADDY1 &&
@ -1203,8 +1199,7 @@ bool IsCreatureVaultAvailable(ItemInfo* item, int stepCount)
item->ObjectNumber != ID_LIZARD &&
item->ObjectNumber != ID_APE &&
item->ObjectNumber != ID_SMALL_SPIDER &&
item->ObjectNumber != ID_SOPHIA_LEIGH_BOSS &&
item->ObjectNumber != ID_VON_CROY);
item->ObjectNumber != ID_SOPHIA_LEIGH_BOSS);
}
return true;
@ -1440,94 +1435,50 @@ void FindAITarget(CreatureInfo* creature, short objectNumber)
void FindAITargetObject(CreatureInfo* creature, int objectNumber)
{
const auto& item = g_Level.Items[creature->ItemNumber];
AITargetFlags data = {};
data.checkDistance = false;
data.checkOcb = item.ItemFlags[3] != 0;
data.objectNumber = objectNumber;
data.ocb = item.ItemFlags[3];
data.checkSameZone = true;
if (FindAITargetObject(creature, &data))
{
*creature->AITarget = data.foundItem;
creature->Enemy = creature->AITarget;
}
FindAITargetObject(creature, objectNumber, item.ItemFlags[3], true);
}
void FindAITargetObject(CreatureInfo* creature, int objectNumber, int ocb, bool checkSameZone)
{
AITargetFlags data = {};
data.checkDistance = false;
data.checkOcb = ocb != NO_VALUE;
data.objectNumber = objectNumber;
data.ocb = ocb;
data.checkSameZone = checkSameZone;
if (FindAITargetObject(creature, &data))
{
*creature->AITarget = data.foundItem;
creature->Enemy = creature->AITarget;
}
}
bool FindAITargetObject(CreatureInfo* creature, AITargetFlags* data)
{
if (g_Level.AIObjects.empty())
return false;
auto& item = g_Level.Items[creature->ItemNumber];
if (g_Level.AIObjects.empty())
return;
AI_OBJECT* foundObject = nullptr;
for (auto& aiObject : g_Level.AIObjects)
{
// Check if the objectNumber match.
if (aiObject.objectNumber != data->objectNumber)
continue;
// Check if the room is valid.
if (aiObject.roomNumber == NO_VALUE)
continue;
// Check if distance is valid.
if (data->checkDistance)
{
if (Vector3i::Distance(item.Pose.Position, aiObject.pos.Position) > data->maxDistance)
continue;
}
// Check if the ocb is the same, useful for paths.
if (data->checkOcb)
{
if (aiObject.triggerFlags != data->ocb)
continue;
}
// Check if the zone number is the same.
if (data->checkSameZone)
if (aiObject.objectNumber == objectNumber &&
aiObject.triggerFlags == ocb &&
aiObject.roomNumber != NO_VALUE)
{
int* zone = g_Level.Zones[(int)creature->LOT.Zone][(int)FlipStatus].data();
auto* room = &g_Level.Rooms[item.RoomNumber];
// NOTE: Avoid changing the boxNumber of the item/ai_item, so a local variable is required !
// Where just searching for AIobject near him.
int boxNum = GetSector(room, item.Pose.Position.x - room->Position.x, item.Pose.Position.z - room->Position.z)->PathfindingBoxID;
item.BoxNumber = GetSector(room, item.Pose.Position.x - room->Position.x, item.Pose.Position.z - room->Position.z)->PathfindingBoxID;
room = &g_Level.Rooms[aiObject.roomNumber];
int aiBoxNum = GetSector(room, aiObject.pos.Position.x - room->Position.x, aiObject.pos.Position.z - room->Position.z)->PathfindingBoxID;
aiObject.boxNumber = GetSector(room, aiObject.pos.Position.x - room->Position.x, aiObject.pos.Position.z - room->Position.z)->PathfindingBoxID;
// If box is invalid or zone is not the same, go next.
if (boxNum == NO_VALUE || aiBoxNum == NO_VALUE)
continue;
// If the zone is invalid, go next.
if (zone[boxNum] != zone[aiBoxNum])
continue;
if (item.BoxNumber == NO_VALUE || aiObject.boxNumber == NO_VALUE)
return;
if (checkSameZone && (zone[item.BoxNumber] != zone[aiObject.boxNumber]))
return;
// Don't check for same zone. Needed for Sophia Leigh.
foundObject = &aiObject;
}
// Don't check for same zone.
// Needed for Sophia Leigh.
foundObject = &aiObject;
}
if (foundObject == nullptr)
return false;
ItemInfo aiItem = {};
if (foundObject == nullptr)
return;
auto& aiItem = *creature->AITarget;
creature->Enemy = &aiItem;
aiItem.ObjectNumber = foundObject->objectNumber;
aiItem.RoomNumber = foundObject->roomNumber;
aiItem.Pose.Position = foundObject->pos.Position;
@ -1536,14 +1487,14 @@ bool FindAITargetObject(CreatureInfo* creature, AITargetFlags* data)
aiItem.TriggerFlags = foundObject->triggerFlags;
aiItem.BoxNumber = foundObject->boxNumber;
if (!(aiItem.Flags & IFLAG_TRIGGERED))
if (!(creature->AITarget->Flags & ItemFlags::IFLAG_TRIGGERED))
{
aiItem.Pose.Position.x += CLICK(1) * phd_sin(aiItem.Pose.Orientation.y);
aiItem.Pose.Position.z += CLICK(1) * phd_cos(aiItem.Pose.Orientation.y);
}
float sinY = phd_sin(creature->AITarget->Pose.Orientation.y);
float cosY = phd_cos(creature->AITarget->Pose.Orientation.y);
data->foundItem = aiItem;
return true;
creature->AITarget->Pose.Position.x += CLICK(1) * sinY;
creature->AITarget->Pose.Position.z += CLICK(1) * cosY;
}
}
int TargetReachable(ItemInfo* item, ItemInfo* enemy)
@ -1604,7 +1555,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
AI->enemyZone |= BLOCKED;
}
else if (item->BoxNumber != NO_VALUE &&
creature->LOT.Node[item->BoxNumber].searchNumber == (creature->LOT.SearchNumber | BLOCKED_SEARCH))
creature->LOT.Node[item->BoxNumber].searchNumber == (creature->LOT.SearchNumber | SEARCH_BLOCKED))
{
AI->enemyZone |= BLOCKED;
}
@ -1833,7 +1784,7 @@ void GetCreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent)
auto* enemy = creature->Enemy;
auto* LOT = &creature->LOT;
if (item->BoxNumber == NO_VALUE || creature->LOT.Node[item->BoxNumber].searchNumber == (creature->LOT.SearchNumber | BLOCKED_SEARCH))
if (item->BoxNumber == NO_VALUE || creature->LOT.Node[item->BoxNumber].searchNumber == (creature->LOT.SearchNumber | SEARCH_BLOCKED))
creature->LOT.RequiredBox = NO_VALUE;
if (creature->Mood != MoodType::Attack && creature->LOT.RequiredBox != NO_VALUE && !ValidBox(item, AI->zoneNumber, creature->LOT.TargetBox))
@ -1956,7 +1907,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
int right = boxRight;
int top = boxTop;
int bottom = boxBottom;
int direction = ALL_CLIP;
int direction = CLIP_ALL;
do
{
@ -1996,7 +1947,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
if (target->z < (boxLeft + CLICK(2)))
target->z = boxLeft + CLICK(2);
if (direction & SECONDARY_CLIP)
if (direction & CLIP_SECONDARY)
return TARGET_TYPE::SECONDARY_TARGET;
if (boxTop > top)
@ -2011,10 +1962,10 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
{
target->z = (right - CLICK(2));
if (direction != ALL_CLIP)
if (direction != CLIP_ALL)
return TARGET_TYPE::SECONDARY_TARGET;
direction |= (ALL_CLIP | SECONDARY_CLIP);
direction |= (CLIP_ALL | CLIP_SECONDARY);
}
}
else if (item->Pose.Position.z > boxRight && direction != CLIP_LEFT)
@ -2026,7 +1977,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
if (target->z > boxRight - CLICK(2))
target->z = boxRight - CLICK(2);
if (direction & SECONDARY_CLIP)
if (direction & CLIP_SECONDARY)
return TARGET_TYPE::SECONDARY_TARGET;
if (boxTop > top)
@ -2041,10 +1992,10 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
{
target->z = left + CLICK(2);
if (direction != ALL_CLIP)
if (direction != CLIP_ALL)
return TARGET_TYPE::SECONDARY_TARGET;
direction |= (ALL_CLIP | SECONDARY_CLIP);
direction |= (CLIP_ALL | CLIP_SECONDARY);
}
}
@ -2057,7 +2008,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
if (target->x < boxTop + CLICK(2))
target->x = boxTop + CLICK(2);
if (direction & SECONDARY_CLIP)
if (direction & CLIP_SECONDARY)
return TARGET_TYPE::SECONDARY_TARGET;
if (boxLeft > left)
@ -2072,10 +2023,10 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
{
target->x = bottom - CLICK(2);
if (direction != ALL_CLIP)
if (direction != CLIP_ALL)
return TARGET_TYPE::SECONDARY_TARGET;
direction |= (ALL_CLIP | SECONDARY_CLIP);
direction |= (CLIP_ALL | CLIP_SECONDARY);
}
}
else if (item->Pose.Position.x > boxBottom && direction != CLIP_TOP)
@ -2087,7 +2038,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
if (target->x > (boxBottom - CLICK(2)))
target->x = (boxBottom - CLICK(2));
if (direction & SECONDARY_CLIP)
if (direction & CLIP_SECONDARY)
return TARGET_TYPE::SECONDARY_TARGET;
if (boxLeft > left)
@ -2102,10 +2053,10 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
{
target->x = top + CLICK(2);
if (direction != ALL_CLIP)
if (direction != CLIP_ALL)
return TARGET_TYPE::SECONDARY_TARGET;
direction |= (ALL_CLIP | SECONDARY_CLIP);
direction |= (CLIP_ALL | CLIP_SECONDARY);
}
}
}
@ -2116,7 +2067,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
{
target->z = LOT->Target.z;
}
else if (!(direction & SECONDARY_CLIP))
else if (!(direction & CLIP_SECONDARY))
{
if (target->z < (boxLeft + CLICK(2)))
target->z = boxLeft + CLICK(2);
@ -2128,7 +2079,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
{
target->x = LOT->Target.x;
}
else if (!(direction & SECONDARY_CLIP))
else if (!(direction & CLIP_SECONDARY))
{
if (target->x < (boxTop + CLICK(2)))
target->x = boxTop + CLICK(2);
@ -2145,7 +2096,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
break;
} while (boxNumber != NO_VALUE);
if (!(direction & SECONDARY_CLIP))
if (!(direction & CLIP_SECONDARY))
{
if (target->z < (boxLeft + CLICK(2)))
target->z = boxLeft + CLICK(2);
@ -2153,7 +2104,7 @@ TARGET_TYPE CalculateTarget(Vector3i* target, ItemInfo* item, LOTInfo* LOT)
target->z = boxRight - CLICK(2);
}
if (!(direction & SECONDARY_CLIP))
if (!(direction & CLIP_SECONDARY))
{
if (target->x < (boxTop + CLICK(2)))
target->x = boxTop + CLICK(2);
@ -2224,26 +2175,29 @@ void InitializeItemBoxData()
}
}
bool CanCreatureJump(ItemInfo& item, float stepDist, JumpDistance jumpDistType)
bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType)
{
const auto& creature = *GetCreatureInfo(&item);
if (creature.Enemy == nullptr)
return false;
float stepDist = BLOCK(0.92f);
int vPos = item.Pose.Position.y;
auto height1 = GetPointCollision(item, item.Pose.Orientation.y, stepDist ).GetFloorHeight();
auto height2 = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 2).GetFloorHeight();
auto height3 = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 3).GetFloorHeight();
auto height4 = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 4).GetFloorHeight();
auto pointCollA = GetPointCollision(item, item.Pose.Orientation.y, stepDist);
auto pointCollB = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 2);
auto pointCollC = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 3);
switch (jumpDistType)
{
default:
case JumpDistance::Block1:
if (item.BoxNumber == creature.Enemy->BoxNumber ||
vPos >= (height1 - STEPUP_HEIGHT) ||
vPos >= (height2 + CLICK(1)) ||
vPos <= (height2 - CLICK(1)))
vPos >= (pointCollA.GetFloorHeight() - STEPUP_HEIGHT) ||
vPos >= (pointCollB.GetFloorHeight() + CLICK(1)) ||
vPos <= (pointCollB.GetFloorHeight() - CLICK(1)) ||
pointCollA.GetSector().PathfindingBoxID == NO_VALUE ||
pointCollB.GetSector().PathfindingBoxID == NO_VALUE)
{
return false;
}
@ -2252,27 +2206,18 @@ bool CanCreatureJump(ItemInfo& item, float stepDist, JumpDistance jumpDistType)
case JumpDistance::Block2:
if (item.BoxNumber == creature.Enemy->BoxNumber ||
vPos >= (height1 - STEPUP_HEIGHT) ||
vPos >= (height2 - STEPUP_HEIGHT) ||
vPos >= (height3 + CLICK(1)) ||
vPos <= (height3 - CLICK(1)))
vPos >= (pointCollA.GetFloorHeight() - STEPUP_HEIGHT) ||
vPos >= (pointCollB.GetFloorHeight() - STEPUP_HEIGHT) ||
vPos >= (pointCollC.GetFloorHeight() + CLICK(1)) ||
vPos <= (pointCollC.GetFloorHeight() - CLICK(1)) ||
pointCollA.GetSector().PathfindingBoxID == NO_VALUE ||
pointCollB.GetSector().PathfindingBoxID == NO_VALUE ||
pointCollC.GetSector().PathfindingBoxID == NO_VALUE)
{
return false;
}
break;
case JumpDistance::Block3:
if (item.BoxNumber == creature.Enemy->BoxNumber ||
vPos >= height1 - STEPUP_HEIGHT ||
vPos >= height2 - STEPUP_HEIGHT ||
vPos >= height3 - STEPUP_HEIGHT ||
vPos >= height4 + CLICK(1) ||
vPos <= height4 - CLICK(1))
{
return false;
}
break;
}
return true;

View file

@ -56,29 +56,33 @@ struct OVERLAP
#define CreatureEffectFunction short(int x, int y, int z, short speed, short yRot, short roomNumber)
constexpr auto BOX_BLOCKED = (1 << 14); // unpassable for other enemies, always set for movable blocks & closed doors
constexpr auto BOX_LAST = (1 << 15); // unpassable by large enemies (T-Rex, Centaur, etc), always set behind doors
// TODO: Following constants can be moved to new flag enums for improved clarity.
constexpr auto REVERSE = 0x4000;
constexpr auto BLOCKABLE = 0x8000;
constexpr auto BLOCKED = 0x4000;
constexpr auto SEARCH_NUMBER = 0x7FFF;
constexpr auto BLOCKED_SEARCH = 0x8000;
constexpr auto BOX_JUMP = 0x800;
constexpr auto BOX_MONKEY = 0x2000;
constexpr auto BLOCKED = 0x4000;
constexpr auto SEARCH_NUMBER = INT_MAX;
constexpr auto SEARCH_BLOCKED = (1 << 31);
constexpr auto BOX_JUMP = 0x800;
constexpr auto BOX_MONKEY = 0x2000;
constexpr auto BOX_END_BIT = 0x8000;
constexpr auto EXPAND_LEFT = 0x1;
constexpr auto EXPAND_RIGHT = 0x2;
constexpr auto EXPAND_TOP = 0x4;
constexpr auto EXPAND_LEFT = 0x1;
constexpr auto EXPAND_RIGHT = 0x2;
constexpr auto EXPAND_TOP = 0x4;
constexpr auto EXPAND_BOTTOM = 0x8;
constexpr auto NO_FLYING = 0;
constexpr auto FLY_ZONE = 0x2000;
constexpr auto CLIP_LEFT = 0x1;
constexpr auto CLIP_RIGHT = 0x2;
constexpr auto CLIP_TOP = 0x4;
constexpr auto FLY_ZONE = 0x2000;
constexpr auto CLIP_LEFT = 0x1;
constexpr auto CLIP_RIGHT = 0x2;
constexpr auto CLIP_TOP = 0x4;
constexpr auto CLIP_BOTTOM = 0x8;
constexpr auto SECONDARY_CLIP = 0x10;
constexpr auto ALL_CLIP = (CLIP_LEFT | CLIP_RIGHT | CLIP_TOP | CLIP_BOTTOM);
constexpr auto CLIP_ALL = (CLIP_LEFT | CLIP_RIGHT | CLIP_TOP | CLIP_BOTTOM);
constexpr auto CLIP_SECONDARY = 0x10;
struct AITargetFlags
{

View file

@ -59,6 +59,7 @@
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/winmain.h"
#include "Game/Lara/lara_initialise.h"
using namespace std::chrono;
using namespace TEN::Effects;
@ -89,7 +90,6 @@ using namespace TEN::Renderer;
int GameTimer = 0;
int GlobalCounter = 0;
int Wibble = 0;
bool InitializeGame;
bool DoTheGame;
@ -185,27 +185,13 @@ GameStatus ControlPhase(int numFrames)
ApplyActionQueue();
ClearActionQueue();
UpdateCamera();
UpdateAllItems();
UpdateAllEffects();
UpdateLara(LaraItem, isTitle);
g_GameScriptEntities->TestCollidingObjects();
if (UseSpotCam)
{
// Draw flyby cameras.
CalculateSpotCameras();
}
else
{
// Do the standard camera.
TrackCameraInit = false;
CalculateCamera(LaraCollision);
}
// Update oscillator seed.
Wibble = (Wibble + WIBBLE_SPEED) & WIBBLE_MAX;
// Smash shatters and clear stopper flags under them.
UpdateShatters();
@ -213,6 +199,7 @@ GameStatus ControlPhase(int numFrames)
Weather.Update();
// Update effects.
UpdateWibble();
StreamerEffect.Update();
UpdateSparks();
UpdateFireSparks();
@ -545,6 +532,10 @@ void InitializeOrLoadGame(bool loadGame)
{
SaveGame::LoadHub(CurrentLevel);
TENLog("Starting new level.", LogLevel::Info);
// Restore vehicle.
auto* item = FindItem(ID_LARA);
InitializePlayerVehicle(*item);
}
g_GameScript->OnStart();

View file

@ -50,15 +50,11 @@ enum FadeStatus
constexpr int MAX_ROOMS = 1024;
constexpr int WIBBLE_SPEED = 4;
constexpr int WIBBLE_MAX = UCHAR_MAX - WIBBLE_SPEED + 1;
constexpr int LOOP_FRAME_COUNT = 2;
extern int GameTimer;
extern int RumbleTimer;
extern int GlobalCounter;
extern int Wibble;
extern bool InitializeGame;
extern bool DoTheGame;

View file

@ -312,7 +312,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo
ShatterImpactData.impactDirection = dir;
ShatterImpactData.impactLocation = ShatterItem.sphere.Center;
ShatterObject(&ShatterItem, 0, 128, target2.RoomNumber, 0);
TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0);
TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0);
}
else
{
@ -512,91 +512,21 @@ static bool DoRayBox(const GameVector& origin, const GameVector& target, const G
// If mesh is visible.
if (item->MeshBits & (1 << i))
{
float distance;
const auto& sphere = spheres[i];
// NOTE: Not worth doing what's commented below. *Rewrite completely.*
// TODO: this approach is the correct one but, again, Core's math is a mystery and this test was meant
// to fail deliberately in some way. I've so added again Core's legacy test for allowing the current game logic
// but after more testing we should trash it in the future and restore the new way.
#if 0
// Create the bounding sphere and test it against the ray
BoundingSphere sph = BoundingSphere(Vector3(sphere->x, sphere->y, sphere->z), sphere->r);
float newDist;
if (sph.Intersects(rayStart, rayDirNormalized, newDist))
if (sphere.Intersects(rayOrigin, rayDir, distance))
{
// HACK: Core seems to take in account for distance not the real hit point but the centre of the sphere.
// This can work well for example for GUARDIAN because the head sphere is so big that would always be hit
// and eyes would not be destroyed.
newDist = sqrt(SQUARE(sphere->x - start->x) + SQUARE(sphere->y - start->y) + SQUARE(sphere->z - start->z));
// Test for minimum distance.
// Test for min distance
if (newDist < minDistance)
if (distance < minDist)
{
minDistance = newDist;
meshPtr = &g_Level.Meshes[obj->meshIndex + i];
minDist = distance;
meshIndex = object->meshIndex + i;
bit = 1 << i;
sp = i;
}
}
#endif
Vector3i p[4];
p[1].x = origin.x;
p[1].y = origin.y;
p[1].z = origin.z;
p[2].x = target.x;
p[2].y = target.y;
p[2].z = target.z;
p[3].x = sphere.Center.x;
p[3].y = sphere.Center.y;
p[3].z = sphere.Center.z;
int r0 = (p[3].x - p[1].x) * (p[2].x - p[1].x) +
(p[3].y - p[1].y) * (p[2].y - p[1].y) +
(p[3].z - p[1].z) * (p[2].z - p[1].z);
int r1 = SQUARE(p[2].x - p[1].x) +
SQUARE(p[2].y - p[1].y) +
SQUARE(p[2].z - p[1].z);
if (((r0 < 0 && r1 < 0) ||
(r1 > 0 && r0 > 0)) &&
(abs(r0) <= abs(r1)))
{
r1 >>= 16;
if (r1)
r0 /= r1;
else
r0 = 0;
p[0].x = p[1].x + ((r0 * (p[2].x - p[1].x)) >> 16);
p[0].y = p[1].y + ((r0 * (p[2].y - p[1].y)) >> 16);
p[0].z = p[1].z + ((r0 * (p[2].z - p[1].z)) >> 16);
int dx = SQUARE(p[0].x - p[3].x);
int dy = SQUARE(p[0].y - p[3].y);
int dz = SQUARE(p[0].z - p[3].z);
int distance = dx + dy + dz;
if (distance < SQUARE(sphere.Radius))
{
dx = SQUARE(sphere.Center.x - origin.x);
dy = SQUARE(sphere.Center.y - origin.y);
dz = SQUARE(sphere.Center.z - origin.z);
distance = dx + dy + dz;
if (distance < minDist)
{
minDist = distance;
meshIndex = object->meshIndex + i;
bit = 1 << i;
sp = i;
}
}
}
}
}

View file

@ -9,6 +9,7 @@
#include "Game/Lara/lara.h"
#include "Game/Setup.h"
#include "Specific/level.h"
#include "Specific/trutils.h"
using namespace TEN::Collision::Room;
@ -205,6 +206,41 @@ void InitializeSlot(short itemNumber, bool makeTarget)
SlotsUsed++;
}
void TargetNearestEntity(ItemInfo& item, const std::vector<GAME_OBJECT_ID>& keyObjectIds, bool ignoreKeyObjectIds)
{
auto& creature = *GetCreatureInfo(&item);
float closestDistSqr = INFINITY;
for (auto& target : ActiveCreatures)
{
auto& targetItem = g_Level.Items[target->ItemNumber];
if (targetItem.Index == item.Index)
continue;
// Ignore or specifically target key object IDs.
if (!keyObjectIds.empty() && (ignoreKeyObjectIds ? Contains(keyObjectIds, targetItem.ObjectNumber) : !Contains(keyObjectIds, targetItem.ObjectNumber)))
continue;
if (&targetItem != &item && targetItem.HitPoints > 0 && targetItem.Status != ITEM_INVISIBLE)
{
float distSqr = Vector3i::DistanceSquared(item.Pose.Position, targetItem.Pose.Position);
if (distSqr < closestDistSqr)
{
creature.Enemy = &targetItem;
closestDistSqr = distSqr;
}
}
}
// Handle player as special case.
if (!keyObjectIds.empty() && (ignoreKeyObjectIds ? Contains(keyObjectIds, ID_LARA) : !Contains(keyObjectIds, ID_LARA)))
return;
float distToPlayerSqr = Vector3i::DistanceSquared(item.Pose.Position, LaraItem->Pose.Position);
if (distToPlayerSqr < closestDistSqr)
creature.Enemy = LaraItem;
}
void SetEntityTarget(short itemNum, short target)
{
auto* item = &g_Level.Items[itemNum];

View file

@ -7,6 +7,7 @@ void InitializeLOTarray(int allocMem);
bool EnableEntityAI(short itemNum, bool always, bool makeTarget = true);
void InitializeSlot(short itemNum, bool makeTarget);
void SetEntityTarget(short itemNum, short target);
void TargetNearestEntity(ItemInfo& item, const std::vector<GAME_OBJECT_ID>& keyObjectIds = {}, bool ignoreKeyObjectIds = true);
void DisableEntityAI(short itemNumber);
void ClearLOT(LOTInfo* LOT);
void CreateZone(ItemInfo* item);

View file

@ -339,17 +339,23 @@ void Antitrigger(short const value, short const flags)
item->ItemFlags[1] = 100;
}
item->Flags &= ~(CODE_BITS | REVERSE);
item->Flags &= ~(CODE_BITS | IFLAG_REVERSE);
if (flags & ONESHOT)
item->Flags |= ATONESHOT;
if (item->Active && Objects[item->ObjectNumber].intelligent)
if (item->Active)
{
DisableEntityAI(value);
RemoveActiveItem(value, false);
item->Active = false;
item->Status = ITEM_INVISIBLE;
item->Status = ITEM_DEACTIVATED;
if (Objects[item->ObjectNumber].intelligent)
{
DisableEntityAI(value);
RemoveActiveItem(value, false);
item->Status = ITEM_INVISIBLE;
}
}
}

View file

@ -40,6 +40,9 @@ using namespace TEN::Math::Random;
using TEN::Renderer::g_Renderer;
constexpr int WIBBLE_SPEED = 4;
constexpr int WIBBLE_MAX = UCHAR_MAX - WIBBLE_SPEED + 1;
// New particle class
Particle Particles[MAX_PARTICLES];
ParticleDynamic ParticleDynamics[MAX_PARTICLE_DYNAMICS];
@ -49,7 +52,9 @@ FX_INFO EffectList[NUM_EFFECTS];
GameBoundingBox DeadlyBounds;
SPLASH_SETUP SplashSetup;
SPLASH_STRUCT Splashes[MAX_SPLASHES];
int SplashCount = 0;
int Wibble = 0;
Vector3i NodeVectors[ParticleNodeOffsetIDs::NodeMax];
NODEOFFSET_INFO NodeOffsets[ParticleNodeOffsetIDs::NodeMax] =
@ -180,6 +185,13 @@ void SetSpriteSequence(Particle& particle, GAME_OBJECT_ID objectID)
particle.spriteIndex = Objects[objectID].meshIndex + (int)round(Lerp(0.0f, numSprites, normalizedAge));
}
void UpdateWibble()
{
// Update oscillator seed.
Wibble = (Wibble + WIBBLE_SPEED) & WIBBLE_MAX;
}
void UpdateSparks()
{
auto bounds = GameBoundingBox(LaraItem);

View file

@ -18,6 +18,8 @@ constexpr auto NUM_EFFECTS = 256;
constexpr auto MAX_PARTICLES = 1024;
constexpr auto MAX_PARTICLE_DYNAMICS = 8;
extern int Wibble;
enum SpriteEnumFlag
{
SP_NONE = 0,
@ -279,5 +281,6 @@ void TriggerRocketFire(int x, int y, int z);
void TriggerExplosionBubbles(int x, int y, int z, short roomNumber);
void Ricochet(Pose& pos);
void ProcessEffects(ItemInfo* item);
void UpdateWibble();
void TriggerDynamicLight(const Vector3& pos, const Color& color, float falloff);

View file

@ -321,7 +321,7 @@ namespace TEN::Effects::Hair
{
auto& unit = Units[i];
auto objectID = (i == 0) ? ID_SINGLE_BRAID_HAIR : ID_DUAL_PIGTAIL_HAIR;
auto objectID = (i == 0) ? ID_HAIR_PRIMARY : ID_HAIR_SECONDARY;
const auto& object = Objects[objectID];
unit.IsEnabled = (object.loaded && (i == 0 || (i == 1 && isYoung)));

View file

@ -53,7 +53,7 @@ SMOKE_SPARKS SmokeSparks[MAX_SPARKS_SMOKE];
GUNSHELL_STRUCT Gunshells[MAX_GUNSHELL];
BLOOD_STRUCT Blood[MAX_SPARKS_BLOOD];
SHOCKWAVE_STRUCT ShockWaves[MAX_SHOCKWAVE];
FIRE_LIST Fires[MAX_FIRE_LIST];
std::vector<FIRE_LIST> Fires;
int GetFreeFireSpark()
{
@ -371,31 +371,21 @@ void UpdateFireProgress()
void AddFire(int x, int y, int z, short roomNum, float size, short fade)
{
FIRE_LIST* fptr = &Fires[0];
int i = 0;
while (fptr->on)
{
fptr++;
if (++i >= MAX_FIRE_LIST)
return;
}
FIRE_LIST newFire;
if (fade)
fptr->on = fade;
else
fptr->on = 1;
newFire.fade = (fade == 0 ? 1 : (unsigned char)fade);
newFire.x = x;
newFire.y = y;
newFire.z = z;
newFire.roomNumber = roomNum;
newFire.size = size;
fptr->x = x;
fptr->y = y;
fptr->z = z;
fptr->roomNumber = roomNum;
fptr->size = size;
Fires.push_back(newFire);
}
void ClearFires()
{
for (int i = 0; i < MAX_FIRE_LIST; i++)
Fires[i].on = false;
Fires.clear();
}
void UpdateFireSparks()

View file

@ -119,7 +119,7 @@ struct FIRE_LIST
int x;
int y;
int z;
byte on;
unsigned char fade;
float size;
short roomNumber;
};
@ -209,7 +209,6 @@ extern int NextSpider;
extern int NextGunShell;
constexpr auto MAX_SPARKS_FIRE = 20;
constexpr auto MAX_FIRE_LIST = 32;
constexpr auto MAX_SPARKS_SMOKE = 32;
constexpr auto MAX_SPARKS_BLOOD = 32;
constexpr auto MAX_GUNFLASH = 4;
@ -221,7 +220,7 @@ extern SMOKE_SPARKS SmokeSparks[MAX_SPARKS_SMOKE];
extern GUNSHELL_STRUCT Gunshells[MAX_GUNSHELL];
extern BLOOD_STRUCT Blood[MAX_SPARKS_BLOOD];
extern SHOCKWAVE_STRUCT ShockWaves[MAX_SHOCKWAVE];
extern FIRE_LIST Fires[MAX_FIRE_LIST];
extern std::vector<FIRE_LIST> Fires;
void TriggerBlood(int x, int y, int z, int unk, int num);
void TriggerExplosionBubble(int x, int y, int z, short roomNumber);
@ -236,7 +235,7 @@ void ThrowPoison(const ItemInfo& item, int boneID, const Vector3& offset, const
void ThrowPoison(const ItemInfo& item, const CreatureBiteInfo& bite, const Vector3& vel, const Color& colorStart, const Color& colorEnd, int spriteID = 0);
void UpdateFireProgress();
void ClearFires();
void AddFire(int x, int y, int z, short roomNum, float size, short fade);
void AddFire(int x, int y, int z, short roomNum, float size, short fade = 1);
void UpdateFireSparks();
int GetFreeSmokeSpark();
void UpdateSmoke();

View file

@ -8,7 +8,6 @@
#include "Game/items.h"
#include "Game/Setup.h"
#include "Specific/level.h"
#include "Specific/trutils.h"
using namespace TEN::Collision::Point;
using namespace TEN::Utils;
@ -18,31 +17,6 @@ CreatureInfo* GetCreatureInfo(ItemInfo* item)
return (CreatureInfo*)item->Data;
}
void TargetNearestEntity(ItemInfo* item, CreatureInfo* creature, const std::vector<GAME_OBJECT_ID>& keyObjectIds, bool ignoreKeyObjectIds)
{
float closestDistSqr = INFINITY;
for (int itemNumber = 0; itemNumber < g_Level.NumItems; itemNumber++)
{
auto* targetItem = &g_Level.Items[itemNumber];
if (targetItem == nullptr || targetItem->Index == item->Index)
continue;
// Ignore or specifically target key object IDs.
if (ignoreKeyObjectIds ? Contains(keyObjectIds, targetItem->ObjectNumber) : !Contains(keyObjectIds, targetItem->ObjectNumber))
continue;
if (targetItem != item && targetItem->HitPoints > 0 && targetItem->Status != ITEM_INVISIBLE)
{
float distSqr = Vector3i::DistanceSquared(item->Pose.Position, targetItem->Pose.Position);
if (distSqr < closestDistSqr)
{
creature->Enemy = targetItem;
closestDistSqr = distSqr;
}
}
}
}
bool IsNextSectorValid(const ItemInfo& item, const Vector3& dir, float dist, bool canFloat)
{
auto projectedPos = Geometry::TranslatePoint(item.Pose.Position, dir, dist);

View file

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

View file

@ -2299,10 +2299,22 @@ static void ParseLevel(const Save::SaveGame* s, bool hubMode)
// Don't load player data in hub mode.
if (item->ObjectNumber == ID_LARA && hubMode)
{
//item->Pose = ToPose(*savedItem->pose());
item->RoomNumber = savedItem->room_number();
item->Floor = savedItem->floor();
item->BoxNumber = savedItem->box_number();
continue;
}
if (item->Index == Lara.Context.Vehicle && hubMode)
{
//item->Pose = ToPose(*savedItem->pose());
item->RoomNumber = savedItem->room_number();
item->Floor = savedItem->floor();
item->BoxNumber = savedItem->box_number();
continue;
}
// Position
item->Pose = ToPose(*savedItem->pose());

View file

@ -182,7 +182,7 @@ namespace TEN::Entities::Effects
else
{
// Normal flames.
AddFire(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber, 2.0f, 0);
AddFire(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber, 2.0f);
TriggerDynamicLight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z,
16 - (GetRandomControl() & 1),

View file

@ -411,7 +411,7 @@ namespace TEN::Entities::Doors
void ShutThatDoor(DOORPOS_DATA* doorPos, DOOR_DATA* dd)
{
static const auto WALL_PLANE = Plane(-Vector3::UnitY, -CLICK(127));
static const auto WALL_PLANE = Plane(-Vector3::UnitY, (float)NO_HEIGHT);
FloorInfo* floor = doorPos->floor;

View file

@ -124,7 +124,7 @@ namespace TEN::Entities::Creatures::TR3
else
{
// NOTE: Ignores other small dinosaurs.
TargetNearestEntity(&item, &creature, RaptorIgnoredObjectIds);
TargetNearestEntity(item, RaptorIgnoredObjectIds);
AI_INFO ai;
if (item.AIBits)

View file

@ -3,6 +3,7 @@
#include "Game/animation.h"
#include "Game/control/box.h"
#include "Game/control/lot.h"
#include "Game/effects/effects.h"
#include "Game/effects/tomb4fx.h"
#include "Game/Lara/lara.h"
@ -205,7 +206,7 @@ namespace TEN::Entities::Creatures::TR3
}
else
{
TargetNearestEntity(&item, &creature, SealMutantAttackTargetObjectIds, false);
TargetNearestEntity(item, SealMutantAttackTargetObjectIds, false);
}
AI_INFO ai;

View file

@ -95,7 +95,7 @@ namespace TEN::Entities::Creatures::TR3
}
else
{
TargetNearestEntity(item, creature, FlamethrowerTargetIds, false);
TargetNearestEntity(*item, FlamethrowerTargetIds, false);
}
AI_INFO AI;

View file

@ -33,9 +33,9 @@ namespace TEN::Entities::Vehicles
constexpr int KAYAK_VELOCITY_LR_ACCEL = 16 * VEHICLE_VELOCITY_SCALE;
constexpr int KAYAK_VELOCITY_HOLD_TURN_DECEL = 0.5f * VEHICLE_VELOCITY_SCALE;
constexpr int KAYAK_VELOCITY_FRICTION_DECEL = 0.5f * VEHICLE_VELOCITY_SCALE;
constexpr int KAYAK_VELOCITY_MAX = 56 * VEHICLE_VELOCITY_SCALE;
constexpr auto KAYAK_FLAG_PADDLE_MESH = 0x80;
constexpr auto KAYAK_WAKE_OFFSET = Vector3(BLOCK(0.1f), 0.0f, BLOCK(0.25f));
// TODO: Very confusing.
@ -221,6 +221,20 @@ namespace TEN::Entities::Vehicles
// SetupRipple(x, kayakItem->Pose.Position.y, z, -2 - (GetRandomControl() & 1), 0, Objects[ID_KAYAK_PADDLE_TRAIL_SPRITE].meshIndex,TO_RAD(kayakItem->Pose.Orientation.y));
}
void KayakPaddleTake(KayakInfo* kayak, ItemInfo* laraItem)
{
kayak->Flags |= KAYAK_FLAG_PADDLE_MESH;
laraItem->Model.MeshIndex[LM_RHAND] = Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND;
laraItem->MeshBits.Clear(KayakLaraLegJoints);
}
void KayakPaddlePut(KayakInfo* kayak, ItemInfo* laraItem)
{
kayak->Flags &= ~KAYAK_FLAG_PADDLE_MESH;
laraItem->Model.MeshIndex[LM_RHAND] = laraItem->Model.BaseMesh + LM_RHAND;
laraItem->MeshBits.Set(KayakLaraLegJoints);
}
int KayakGetCollisionAnim(ItemInfo* kayakItem, int xDiff, int zDiff)
{
xDiff = kayakItem->Pose.Position.x - xDiff;
@ -897,28 +911,14 @@ namespace TEN::Entities::Vehicles
break;
case KAYAK_STATE_MOUNT_LEFT:
if (TestAnimNumber(*laraItem, KAYAK_ANIM_GET_PADDLE) &&
frame == 24 &&
!(kayak->Flags & 0x80))
{
kayak->Flags |= 0x80;
laraItem->Model.MeshIndex[LM_RHAND] = Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND;
laraItem->MeshBits.Clear(KayakLaraLegJoints);
}
if (TestAnimNumber(*laraItem, KAYAK_ANIM_GET_PADDLE) && frame == 24 && !(kayak->Flags & KAYAK_FLAG_PADDLE_MESH))
KayakPaddleTake(kayak, laraItem);
break;
case KAYAK_STATE_DISMOUNT:
if (TestAnimNumber(*laraItem, KAYAK_ANIM_DISMOUNT_START) &&
frame == 27 &&
kayak->Flags & 0x80)
{
kayak->Flags &= ~0x80;
laraItem->Model.MeshIndex[LM_RHAND] = laraItem->Model.BaseMesh + LM_RHAND;
laraItem->MeshBits.Set(KayakLaraLegJoints);
}
laraItem->Animation.TargetState = laraItem->Animation.RequiredState;
if (TestAnimNumber(*laraItem, KAYAK_ANIM_DISMOUNT_START) && frame == 27 && kayak->Flags & KAYAK_FLAG_PADDLE_MESH)
KayakPaddlePut(kayak, laraItem);
break;
case KAYAK_STATE_DISMOUNT_LEFT:

View file

@ -6,24 +6,27 @@ struct ItemInfo;
namespace TEN::Entities::Vehicles
{
KayakInfo* GetKayakInfo(ItemInfo* kayakItem);
void InitializeKayak(short itemNumber);
void KayakPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void DoKayakMount(ItemInfo* kayakItem, ItemInfo* laraItem, VehicleMountType mountType);
void KayakPaddleTake(KayakInfo* kayak, ItemInfo* laraItem);
void KayakPaddlePut(KayakInfo* kayak, ItemInfo* laraItem);
void KayakDraw(ItemInfo* kayakItem);
void KayakDoRipple(ItemInfo* kayakItem, int xOffset, int zOffset);
int KayakGetCollisionAnim(ItemInfo* kayakItem, int xDiff, int zDiff);
int KayakDoDynamics(int height, int verticalVelocity, int* y);
int KayakGetCollisionAnim(ItemInfo* kayakItem, int xDiff, int zDiff);
int KayakDoDynamics(int height, int verticalVelocity, int* y);
void KayakDoCurrent(ItemInfo* kayakItem, ItemInfo* laraItem);
bool KayakCanGetOut(ItemInfo* kayakItem, int dir);
int KayakDoShift(ItemInfo* kayakItem, Vector3i* pos, Vector3i* old);
int KayakDoShift(ItemInfo* kayakItem, Vector3i* pos, Vector3i* old);
void KayakToBackground(ItemInfo* kayakItem, ItemInfo* laraItem);
void KayakUserInput(ItemInfo* kayakItem, ItemInfo* laraItem);
void KayakToItemCollision(ItemInfo* kayakItem, ItemInfo* laraItem);
void KayakLaraRapidsDrown(ItemInfo* laraItem);
void PreDrawWakeFx(ItemInfo* kayakItem);
bool KayakControl(ItemInfo* laraItem);
}

View file

@ -241,6 +241,18 @@ namespace TEN::Entities::Vehicles
}
}
void MinecartWrenchTake(MinecartInfo* minecart, ItemInfo* laraItem)
{
laraItem->Model.MeshIndex[LM_RHAND] = Objects[ID_MINECART_LARA_ANIMS].meshIndex + LM_RHAND;
minecart->Flags |= MINECART_FLAG_WRENCH_MESH;
}
void MinecartWrenchPut(MinecartInfo* minecart, ItemInfo* laraItem)
{
laraItem->Model.MeshIndex[LM_RHAND] = laraItem->Model.BaseMesh + LM_RHAND;
minecart->Flags &= ~MINECART_FLAG_WRENCH_MESH;
}
static int GetMinecartCollision(ItemInfo* minecartItem, short angle, int distance)
{
auto probe = GetPointCollision(*minecartItem, angle, distance, -LARA_HEIGHT);
@ -755,8 +767,7 @@ namespace TEN::Entities::Vehicles
if (laraItem->Animation.FrameNumber == GetFrameIndex(minecartItem, MINECART_WRENCH_MESH_TOGGLE_FRAME) &&
minecart->Flags & MINECART_FLAG_WRENCH_MESH)
{
laraItem->Model.MeshIndex[LM_RHAND] = laraItem->Model.BaseMesh + LM_RHAND;
minecart->Flags &= ~MINECART_FLAG_WRENCH_MESH;
MinecartWrenchPut(minecart, laraItem);
}
if (minecart->Flags & MINECART_FLAG_DISMOUNT_RIGHT)
@ -804,8 +815,7 @@ namespace TEN::Entities::Vehicles
if (!(minecart->Flags & MINECART_FLAG_WRENCH_MESH) &&
laraItem->Animation.FrameNumber == GetFrameIndex(minecartItem, MINECART_WRENCH_MESH_TOGGLE_FRAME))
{
laraItem->Model.MeshIndex[LM_RHAND] = Objects[ID_MINECART_LARA_ANIMS].meshIndex + LM_RHAND;
minecart->Flags |= MINECART_FLAG_WRENCH_MESH;
MinecartWrenchTake(minecart, laraItem);
}
}

View file

@ -7,9 +7,13 @@ struct ItemInfo;
namespace TEN::Entities::Vehicles
{
void InitializeMinecart(short itemNumber);
MinecartInfo* GetMinecartInfo(ItemInfo* minecartItem);
void MinecartPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void DoMinecartMount(ItemInfo* minecartItem, ItemInfo* laraItem, VehicleMountType mountType);
bool MinecartControl(ItemInfo* laraItem);
void MinecartWrenchTake(MinecartInfo* minecart, ItemInfo* laraItem);
void MinecartWrenchPut(MinecartInfo* minecart, ItemInfo* laraItem);
}

View file

@ -151,21 +151,13 @@ namespace TEN::Entities::Vehicles
UPV_BITE_RIGHT_RUDDER_RIGHT = 4,
UPV_BITE_RIGHT_RUDDER_LEFT = 5 // Unused.
};
enum UPVFlags
{
UPV_FLAG_CONTROL = (1 << 0),
UPV_FLAG_SURFACE = (1 << 1),
UPV_FLAG_DIVE = (1 << 2),
UPV_FLAG_DEAD = (1 << 3)
};
UPVInfo* GetUPVInfo(ItemInfo* UPVItem)
{
return (UPVInfo*)UPVItem->Data;
}
void UPVInitialize(short itemNumber)
void InitializeUPV(short itemNumber)
{
auto* UPVItem = &g_Level.Items[itemNumber];
UPVItem->Data = UPVInfo();

View file

@ -6,7 +6,16 @@ struct ItemInfo;
namespace TEN::Entities::Vehicles
{
void UPVInitialize(short itemNumber);
enum UPVFlags
{
UPV_FLAG_CONTROL = (1 << 0),
UPV_FLAG_SURFACE = (1 << 1),
UPV_FLAG_DIVE = (1 << 2),
UPV_FLAG_DEAD = (1 << 3)
};
void InitializeUPV(short itemNumber);
UPVInfo* GetUPVInfo(ItemInfo* UPVItem);
void UPVPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void DoUPVMount(ItemInfo* UPVItem, ItemInfo* laraItem, VehicleMountType mountType);

View file

@ -583,7 +583,7 @@ static void StartVehicles(ObjectInfo* obj)
obj = &Objects[ID_UPV];
if (obj->loaded)
{
obj->Initialize = UPVInitialize;
obj->Initialize = InitializeUPV;
obj->control = UPVEffects;
obj->collision = UPVPlayerCollision;
obj->shadowType = ShadowMode::Lara;

View file

@ -3,6 +3,7 @@
#include "Game/collision/collide_room.h"
#include "Game/control/flipeffect.h"
#include "Game/effects/effects.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Setup.h"

View file

@ -3,6 +3,7 @@
#include "Game/animation.h"
#include "Game/control/control.h"
#include "Game/control/lot.h"
#include "Game/effects/effects.h"
#include "Game/itemdata/creature_info.h"
#include "Game/items.h"
@ -286,7 +287,7 @@ namespace TEN::Entities::TR4
}
else
{
TargetNearestEntity(item, creature);
TargetNearestEntity(*item);
}
AI_INFO ai;

View file

@ -73,6 +73,6 @@ void SarcophagusCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* c
}
else
{
CollectMultiplePickups(sarcItem->Index);
CollectCarriedItems(sarcItem);
}
}

View file

@ -591,9 +591,12 @@ namespace TEN::Entities::Creatures::TR5
void DoGuardianDeath(int itemNumber, ItemInfo& item)
{
const auto& guardian = GetGuardianInfo(item);
ExplodeItemNode(&g_Level.Items[guardian.BaseItem], 0, 0, 128);
KillItem(guardian.BaseItem);
if (g_Level.Items[guardian.BaseItem].ObjectNumber == ID_LASERHEAD_BASE)
{
ExplodeItemNode(&g_Level.Items[guardian.BaseItem], 0, 0, 128);
KillItem(guardian.BaseItem);
}
ExplodeItemNode(&item, 0, 0, 128);
@ -605,7 +608,6 @@ namespace TEN::Entities::Creatures::TR5
TriggerShockwave(&item.Pose, 32, 160, 64, 0, 128, 64, 36, EulerAngles(0x3000, 0.0f, 0.0f), 0, true, false, false, (int)ShockwaveStyle::Normal);
TriggerShockwave(&item.Pose, 32, 160, 64, 0, 128, 64, 36, EulerAngles(0x6000, 0.0f, 0.0f), 0, true, false, false, (int)ShockwaveStyle::Normal);
g_Level.Items[guardian.PuzzleItem].Pose.Position.y = item.Pose.Position.y;
TestTriggers(&item, true);
SoundEffect(SFX_TR5_GOD_HEAD_BLAST, &item.Pose, SoundEnvironment::Land, 0.5f);

View file

@ -21,6 +21,5 @@ namespace TEN::Entities::Creatures::TR5
short yRot;
int BaseItem;
std::array<int, GUARDIAN_TENTACLE_COUNT> Tentacles = {};
int PuzzleItem;
};
}

View file

@ -93,7 +93,7 @@ namespace TEN::Entities::Generic
void ShakeRaisingBlock(ItemInfo* item)
{
SoundEffect(SFX_TR4_RAISING_BLOCK, &item->Pose);
SoundEffect(SFX_TR4_RAISING_BLOCK_2, &item->Pose);
if (item->TriggerFlags == 0)
return;

View file

@ -128,7 +128,7 @@ namespace TEN::Entities::Generic
}
else
{
SoundEffect(SFX_TR4_RUMBLE_NEXTDOOR, &item->Pose);
SoundEffect(SFX_TR4_RAISING_BLOCK_2, &item->Pose);
item->Pose.Position.y -= 4;
}
}
@ -140,7 +140,7 @@ namespace TEN::Entities::Generic
}
else
{
SoundEffect(SFX_TR4_RUMBLE_NEXTDOOR, &item->Pose);
SoundEffect(SFX_TR4_RAISING_BLOCK_2, &item->Pose);
item->Pose.Position.y += 4;
}
}

View file

@ -38,7 +38,7 @@ void SmashObject(short itemNumber)
auto* box = &g_Level.PathfindingBoxes[room->Sectors[sector].PathfindingBoxID];
if (box->flags & 0x8000)
box->flags &= ~BOX_BLOCKED;
box->flags &= ~BLOCKED;
SoundEffect(SFX_TR5_SMASH_GLASS, &item->Pose);

View file

@ -40,8 +40,8 @@ enum GAME_OBJECT_ID : short
ID_LARA_DIRT_MESH,
ID_LARA_CROWBAR_ANIM,
ID_LARA_TORCH_ANIM,
ID_SINGLE_BRAID_HAIR,
ID_DUAL_PIGTAIL_HAIR,
ID_HAIR_PRIMARY,
ID_HAIR_SECONDARY,
ID_SNOWMOBILE_LARA_ANIMS = 50,
ID_SNOWMOBILE,

View file

@ -516,7 +516,7 @@ namespace TEN::Renderer
&moveable,
&g_Level.Meshes[obj->meshIndex + j],
j, MoveablesIds[i] == ID_LARA_SKIN_JOINTS,
MoveablesIds[i] == ID_SINGLE_BRAID_HAIR || MoveablesIds[i] == ID_DUAL_PIGTAIL_HAIR, &lastVertex, &lastIndex);
MoveablesIds[i] == ID_HAIR_PRIMARY || MoveablesIds[i] == ID_HAIR_SECONDARY, &lastVertex, &lastIndex);
moveable.ObjectMeshes.push_back(mesh);
_meshes.push_back(mesh);
@ -687,7 +687,7 @@ namespace TEN::Renderer
}
}
}
else if (MoveablesIds[i] == ID_SINGLE_BRAID_HAIR && isSkinPresent)
else if (MoveablesIds[i] == ID_HAIR_PRIMARY && isSkinPresent)
{
for (int j = 0; j < obj->nmeshes; j++)
{
@ -776,7 +776,7 @@ namespace TEN::Renderer
}
}
}
else if (MoveablesIds[i] == ID_DUAL_PIGTAIL_HAIR && isSkinPresent)
else if (MoveablesIds[i] == ID_HAIR_SECONDARY && isSkinPresent)
{
for (int j = 0; j < obj->nmeshes; j++)
{

View file

@ -50,7 +50,6 @@ extern BLOOD_STRUCT Blood[MAX_SPARKS_BLOOD];
extern FIRE_SPARKS FireSparks[MAX_SPARKS_FIRE];
extern SMOKE_SPARKS SmokeSparks[MAX_SPARKS_SMOKE];
extern SHOCKWAVE_STRUCT ShockWaves[MAX_SHOCKWAVE];
extern FIRE_LIST Fires[MAX_FIRE_LIST];
extern Particle Particles[MAX_PARTICLES];
extern SPLASH_STRUCT Splashes[MAX_SPLASHES];
extern std::array<DebrisFragment, MAX_DEBRIS> DebrisFragments;
@ -293,26 +292,22 @@ namespace TEN::Renderer
void Renderer::PrepareFires(RenderView& view)
{
for (int k = 0; k < MAX_FIRE_LIST; k++)
for (const auto& fire : Fires)
{
auto* fire = &Fires[k];
if (fire->on)
{
auto fade = fire->on == 1 ? 1.0f : (float)(255 - fire->on) / 255.0f;
auto fade = (fire.fade == 1 ? 1.0f : (float)(255 - fire.fade) / 255.0f);
for (int i = 0; i < MAX_SPARKS_FIRE; i++)
for (int i = 0; i < MAX_SPARKS_FIRE; i++)
{
auto* spark = &FireSparks[i];
if (spark->on)
{
auto* spark = &FireSparks[i];
if (spark->on)
{
AddSpriteBillboard(
&_sprites[spark->def],
Vector3(fire->x + spark->x * fire->size / 2, fire->y + spark->y * fire->size / 2, fire->z + spark->z * fire->size / 2),
Vector4(spark->r / 255.0f * fade, spark->g / 255.0f * fade, spark->b / 255.0f * fade, 1.0f),
TO_RAD(spark->rotAng << 4),
spark->scalar,
Vector2(spark->size * fire->size, spark->size * fire->size), BlendMode::Additive, true, view);
}
AddSpriteBillboard(
&_sprites[spark->def],
Vector3(fire.x + spark->x * fire.size / 2, fire.y + spark->y * fire.size / 2, fire.z + spark->z * fire.size / 2),
Vector4(spark->r / 255.0f * fade, spark->g / 255.0f * fade, spark->b / 255.0f * fade, 1.0f),
TO_RAD(spark->rotAng << 4),
spark->scalar,
Vector2(spark->size * fire.size, spark->size * fire.size), BlendMode::Additive, true, view);
}
}
}

View file

@ -1260,8 +1260,8 @@ namespace TEN::Renderer
case RendererDebugPage::PlayerStats:
PrintDebugMessage("PLAYER STATS");
PrintDebugMessage("AnimObjectID: %d", LaraItem->Animation.AnimObjectID);
PrintDebugMessage("AnimNumber: %d", LaraItem->Animation.AnimNumber);
PrintDebugMessage("FrameNumber: %d", LaraItem->Animation.FrameNumber);
PrintDebugMessage("AnimNumber: %d", LaraItem->Animation.AnimNumber - Objects[LaraItem->Animation.AnimObjectID].animIndex);
PrintDebugMessage("FrameNumber: %d", LaraItem->Animation.FrameNumber - GetAnimData(LaraItem).frameBase);
PrintDebugMessage("ActiveState: %d", LaraItem->Animation.ActiveState);
PrintDebugMessage("TargetState: %d", LaraItem->Animation.TargetState);
PrintDebugMessage("Velocity: %.3f, %.3f, %.3f", LaraItem->Animation.Velocity.z, LaraItem->Animation.Velocity.y, LaraItem->Animation.Velocity.x);

View file

@ -85,6 +85,9 @@ void Renderer::UpdateLaraAnimations(bool force)
if (!force && rItem.DoneAnimations)
return;
if (_moveableObjects.empty())
return;
auto& playerObject = *_moveableObjects[ID_LARA];
// Clear extra rotations.

View file

@ -22,10 +22,8 @@ static constexpr char ScriptReserved_Sink[] = "Sink";
static constexpr char ScriptReserved_SoundSource[] = "SoundSource";
static constexpr char ScriptReserved_AIObject[] = "AIObject";
static constexpr char ScriptReserved_Volume[] = "Volume";
static constexpr char ScriptReserved_Room[] = "Room";
static constexpr char ScriptReserved_Color[] = "Color";
static constexpr char ScriptReserved_DisplayString[] = "DisplayString";
static constexpr char ScriptReserved_Vec2[] = "Vec2";
static constexpr char ScriptReserved_Rotation[] = "Rotation";
static constexpr char ScriptReserved_LevelFunc[] = "LevelFunc";
@ -122,6 +120,7 @@ static constexpr char ScriptReserved_SetFlags[] = "SetFlags";
static constexpr char ScriptReserved_SetTranslated[] = "SetTranslated";
static constexpr char ScriptReserved_GetPosition[] = "GetPosition";
static constexpr char ScriptReserved_GetJointPosition[] = "GetJointPosition";
static constexpr char ScriptReserved_GetJointRotation[] = "GetJointRotation";
static constexpr char ScriptReserved_SetPosition[] = "SetPosition";
static constexpr char ScriptReserved_GetRotation[] = "GetRotation";
static constexpr char ScriptReserved_SetRotation[] = "SetRotation";
@ -198,7 +197,6 @@ static constexpr char ScriptReserved_IsMoveableInside[] = "IsMoveableInside";
static constexpr char ScriptReserved_GetFlag[] = "GetFlag";
static constexpr char ScriptReserved_SetFlag[] = "SetFlag";
static constexpr char ScriptReserved_IsTagPresent[] = "IsTagPresent";
static constexpr char ScriptReserved_SetReverbType[] = "SetReverbType";
// Flow Functions
static constexpr char ScriptReserved_AddLevel[] = "AddLevel";
@ -230,6 +228,7 @@ static constexpr char ScriptReserved_EnablePointFilter[] = "EnablePointFilter";
// Flow Functions
static constexpr char ScriptReserved_SetStrings[] = "SetStrings";
static constexpr char ScriptReserved_GetString[] = "GetString";
static constexpr char ScriptReserved_IsStringPresent[] = "IsStringPresent";
static constexpr char ScriptReserved_SetLanguageNames[] = "SetLanguageNames";
// Flow Tables
@ -372,22 +371,39 @@ static constexpr char ScriptReserved_LogLevelError[] = "ERROR";
// Internal
static constexpr char ScriptReserved_LaraObject[] = "LaraObject";
// Room
constexpr char ScriptReserved_Room[] = "Room";
constexpr char ScriptReserved_RoomGetActive[] = "GetActive";
constexpr char ScriptReserved_RoomGetColor[] = "GetColor";
constexpr char ScriptReserved_RoomGetFlag[] = "GetFlag";
constexpr char ScriptReserved_RoomGetName[] = "GetName";
constexpr char ScriptReserved_RoomGetReverbType[] = "GetReverbType";
constexpr char ScriptReserved_RoomGetRoomNumber[] = "GetRoomNumber";
constexpr char ScriptReserved_RoomIsTagPresent[] = "IsTagPresent";
constexpr char ScriptReserved_RoomSetFlag[] = "SetFlag";
constexpr char ScriptReserved_RoomSetName[] = "SetName";
constexpr char ScriptReserved_RoomSetReverbType[] = "SetReverbType";
// Vec2
constexpr char ScriptReserved_Vec2[] = "Vec2";
constexpr char ScriptReserved_Vec2Cross[] = "Cross";
constexpr char ScriptReserved_Vec2Distance[] = "Distance";
constexpr char ScriptReserved_Vec2Dot[] = "Dot";
constexpr char ScriptReserved_Vec2Length[] = "Length";
constexpr char ScriptReserved_Vec2Lerp[] = "Lerp";
constexpr char ScriptReserved_Vec2SetLength[] = "ToLength";
constexpr char ScriptReserved_Vec2Normalize[] = "Normalize";
constexpr char ScriptReserved_Vec2Rotate[] = "Rotate";
constexpr char ScriptReserved_Vec2Lerp[] = "Lerp";
constexpr char ScriptReserved_Vec2Cross[] = "Cross";
constexpr char ScriptReserved_Vec2Dot[] = "Dot";
constexpr char ScriptReserved_Vec2Distance[] = "Distance";
constexpr char ScriptReserved_Vec2Length[] = "Length";
// Vec3
constexpr char ScriptReserved_Vec3[] = "Vec3";
constexpr char ScriptReserved_Vec3Cross[] = "Cross";
constexpr char ScriptReserved_Vec3Distance[] = "Distance";
constexpr char ScriptReserved_Vec3Dot[] = "Dot";
constexpr char ScriptReserved_Vec3Length[] = "Length";
constexpr char ScriptReserved_Vec3Lerp[] = "Lerp";
constexpr char ScriptReserved_Vec3Normalize[] = "Normalize";
constexpr char ScriptReserved_Vec3Rotate[] = "Rotate";
constexpr char ScriptReserved_Vec3Lerp[] = "Lerp";
constexpr char ScriptReserved_Vec3Cross[] = "Cross";
constexpr char ScriptReserved_Vec3Dot[] = "Dot";
constexpr char ScriptReserved_Vec3Distance[] = "Distance";
constexpr char ScriptReserved_Vec3Length[] = "Length";

View file

@ -284,7 +284,7 @@ namespace TEN::Scripting::Effects
*/
static void EmitFire(Vec3 pos, TypeOrNil<float> size)
{
AddFire(pos.x, pos.y, pos.z, FindRoomNumber(Vector3i(pos.x, pos.y, pos.z)), USE_IF_HAVE(float, size, 1), 0);
AddFire(pos.x, pos.y, pos.z, FindRoomNumber(Vector3i(pos.x, pos.y, pos.z)), USE_IF_HAVE(float, size, 1));
}
/***Make an explosion. Does not hurt Lara

View file

@ -248,6 +248,12 @@ You will not need to call them manually.
*/
tableFlow.set_function(ScriptReserved_GetString, &FlowHandler::GetString, this);
/*** Check if translated string is present.
@function IsStringPresent
@tparam key string key for translated string
*/
tableFlow.set_function(ScriptReserved_IsStringPresent, &FlowHandler::IsStringPresent, this);
/*** Set language names for translations.
Specify which translations in the strings table correspond to which languages.
@function SetLanguageNames
@ -371,6 +377,11 @@ char const * FlowHandler::GetString(const char* id) const
}
}
bool FlowHandler::IsStringPresent(const char* id) const
{
return _translationMap.find(id) != _translationMap.end();
}
Settings* FlowHandler::GetSettings()
{
return &_settings;

View file

@ -51,6 +51,7 @@ public:
void AddLevel(Level const& level);
void LoadFlowScript();
char const* GetString(const char* id) const;
bool IsStringPresent(const char* id) const;
void SetStrings(sol::nested<std::unordered_map<std::string, std::vector<std::string>>>&& src);
void SetLanguageNames(sol::as_table_t<std::vector<std::string>>&& src);
void SetAnimations(const Animations& src);

View file

@ -79,7 +79,7 @@ most can just be ignored (see usage).
@tparam string name Lua name of the item
@tparam Vec3 position position in level
@tparam Rotation rotation rotation rotation about x, y, and z axes (default Rotation(0, 0, 0))
@tparam int roomID room ID item is in (default: calculated automatically)
@tparam int roomNumber the room number the moveable is in (default: calculated automatically).
@tparam int animNumber animation number
@tparam int frameNumber frame number
@tparam int hp HP of item
@ -179,14 +179,14 @@ void Moveable::Register(sol::state& state, sol::table& parent)
/// Set effect to moveable
// @function Moveable:SetEffect
// @tparam Effects.EffectID effect Type of effect to assign.
// @tparam float timeout time (in seconds) after which effect turns off (optional).
// @tparam[opt] float timeout time (in seconds) after which effect turns off.
ScriptReserved_SetEffect, &Moveable::SetEffect,
/// Set custom colored burn effect to moveable
// @function Moveable:SetCustomEffect
// @tparam Color Color1 color the primary color of the effect (also used for lighting).
// @tparam Color Color2 color the secondary color of the effect.
// @tparam float timeout time (in seconds) after which effect turns off (optional).
// @tparam[opt] float timeout time (in seconds) after which effect turns off.
ScriptReserved_SetCustomEffect, &Moveable::SetCustomEffect,
/// Get current moveable effect
@ -365,9 +365,16 @@ ScriptReserved_GetSlotHP, & Moveable::GetSlotHP,
/// Get the object's joint position
// @function Moveable:GetJointPosition
// @tparam int index of a joint to get position
// @treturn Vec3 a copy of the moveable's position
// @tparam[opt] Vec3 offset a pre-rotation offset to the joint
// @treturn Vec3 a copy of the moveable's joint position
ScriptReserved_GetJointPosition, & Moveable::GetJointPos,
/// Get the object's joint rotation
// @function Moveable:GetJointRotation
// @tparam int index of a joint to get rotation
// @treturn Rotation a calculated copy of the moveable's joint rotation
ScriptReserved_GetJointRotation, & Moveable::GetJointRot,
ScriptReserved_SetPosition, & Moveable::SetPos,
/// Get the moveable's rotation
@ -428,6 +435,9 @@ ScriptReserved_GetSlotHP, & Moveable::GetSlotHP,
// @function Moveable:SetOnCollidedWithObject
// @tparam function func callback function to be called (must be in LevelFuncs hierarchy). This function can take two arguments; these will store the two @{Moveable}s taking part in the collision.
// @usage
// -- obj1 is the collision moveable
// -- obj2 is the collider moveable
//
// LevelFuncs.objCollided = function(obj1, obj2)
// print(obj1:GetName() .. " collided with " .. obj2:GetName())
// end
@ -603,12 +613,18 @@ void Moveable::SetPos(const Vec3& pos, sol::optional<bool> updateRoom)
UpdateBridgeItem(*m_item);
}
Vec3 Moveable::GetJointPos(int jointIndex) const
Vec3 Moveable::GetJointPos(int jointIndex, sol::optional<Vec3> offset) const
{
auto result = GetJointPosition(m_item, jointIndex);
Vector3i vec = offset.has_value() ? offset->ToVector3i() : Vector3i(0, 0, 0);
auto result = GetJointPosition(m_item, jointIndex, vec);
return Vec3(result.x, result.y, result.z);
}
Rotation Moveable::GetJointRot(int jointIndex) const
{
return GetBoneOrientation(*m_item, jointIndex);
}
// This does not guarantee that the returned value will be identical
// to a value written in via SetRot - only that the angle measures
// will be mathematically equal

View file

@ -1,4 +1,5 @@
#pragma once
#include "Scripting/Internal/ScriptUtil.h"
#include "Scripting/Internal/TEN/Objects/NamedBase.h"
#include "Scripting/Internal/TEN/Objects/Room/RoomObject.h"
@ -47,9 +48,10 @@ public:
void Destroy();
[[nodiscard]] Vec3 GetPos() const;
[[nodiscard]] Vec3 GetJointPos(int index) const;
[[nodiscard]] Vec3 GetJointPos(int index, sol::optional<Vec3> offset) const;
void SetPos(const Vec3& pos, sol::optional<bool> updateRoom);
[[nodiscard]] Rotation GetJointRot(int index) const;
[[nodiscard]] Rotation GetRot() const;
void SetRot(const Rotation& rot);

View file

@ -53,8 +53,8 @@ The following constants are inside ObjID.
LARA_DIRT_MESH
LARA_CROWBAR_ANIM
LARA_TORCH_ANIM
SINGLE_BRAID_HAIR
DUAL_PIGTAIL_HAIR
HAIR_PRIMARY
HAIR_SECONDARY
SNOWMOBILE_LARA_ANIMS
SNOWMOBILE
QUAD_LARA_ANIMS
@ -1231,8 +1231,8 @@ static const std::unordered_map<std::string, GAME_OBJECT_ID> kObjIDs {
{ "LARA_DIRT_MESH", ID_LARA_DIRT_MESH },
{ "LARA_CROWBAR_ANIM", ID_LARA_CROWBAR_ANIM },
{ "LARA_TORCH_ANIM", ID_LARA_TORCH_ANIM },
{ "SINGLE_BRAID_HAIR", ID_SINGLE_BRAID_HAIR },
{ "DUAL_PIGTAIL_HAIR", ID_DUAL_PIGTAIL_HAIR },
{ "HAIR_PRIMARY", ID_HAIR_PRIMARY },
{ "HAIR_SECONDARY", ID_HAIR_SECONDARY },
{ "SNOWMOBILE_LARA_ANIMS", ID_SNOWMOBILE_LARA_ANIMS },
{ "SNOWMOBILE", ID_SNOWMOBILE },
{ "QUAD_LARA_ANIMS", ID_QUAD_LARA_ANIMS },

View file

@ -13,141 +13,148 @@
#include "Specific/level.h"
#include "Specific/trutils.h"
/***
Rooms
/// Room object.
// @tenclass Objects.Room
// @pragma nostrip
@tenclass Objects.Room
@pragma nostrip
*/
//namespace TEN::Scripting
//{
static auto IndexError = index_error_maker(Room, ScriptReserved_Volume);
static auto NewIndexError = newindex_error_maker(Room, ScriptReserved_Volume);
static auto IndexError = index_error_maker(Room, ScriptReserved_Volume);
static auto NewIndexError = newindex_error_maker(Room, ScriptReserved_Volume);
Room::Room(ROOM_INFO& room) : m_room{ room }
{};
void Room::Register(sol::table& parent)
{
parent.new_usertype<Room>(ScriptReserved_Room,
sol::no_constructor,
sol::meta_function::index, IndexError,
sol::meta_function::new_index, NewIndexError,
/// Determine whether the room is active or not
// @function Room:GetActive
// @treturn bool true if the room is active
ScriptReserved_GetActive, &Room::GetActive,
/// Get the room's ambient light color.
// @function Room:GetColor
// @treturn Color ambient light color of the room
ScriptReserved_GetColor, & Room::GetColor,
/// Get the room's reverb type.
// @function Room:GetReverbType
// @treturn Objects.RoomReverb room's reverb type
ScriptReserved_GetPosition, &Room::GetReverbType,
/// Set the room's reverb type.
// @function Room:SetReverbType
// @tparam Objects.RoomReverb new reverb type of the room
ScriptReserved_SetReverbType, &Room::SetReverbType,
/// Get the room's unique string identifier.
// @function Room:GetName
// @treturn string the room's name
ScriptReserved_GetName, &Room::GetName,
/// Set the room's name (its unique string identifier).
// @function Room:SetName
// @tparam string name The room's new name
ScriptReserved_SetName, &Room::SetName,
/// Get the room's specified flag value (true or false).
// @function Room:GetFlag
// @tparam Objects.RoomFlagID flagID The room's flag ID
// @treturn bool the room's specified flag value
ScriptReserved_GetFlag, &Room::GetFlag,
/// Set the room's specified flag value.
// @function Room:SetFlag
// @tparam Objects.RoomFlagID flagID The room's flag ID
// @tparam bool the room's new flag value
ScriptReserved_SetFlag, &Room::SetFlag,
/// Checks if specified tag is set for this room.
// @function Room:IsTagPresent
// @tparam string tag A text tag to check (case sensitive)
// @treturn bool true if tag is present, false if not
ScriptReserved_IsTagPresent, &Room::IsTagPresent);
}
bool Room::GetActive() const
{
return m_room.Active();
}
ScriptColor Room::GetColor() const
{
return ScriptColor{ m_room.ambient };
}
ReverbType Room::GetReverbType() const
{
return m_room.reverbType;
}
void Room::SetReverbType(ReverbType reverb)
{
m_room.reverbType = reverb;
}
std::string Room::GetName() const
{
return m_room.Name;
}
void Room::SetName(const std::string& name)
{
if (!ScriptAssert(!name.empty(), "Unable to set name. Name cannot be blank."))
return;
// Remove old name if it already exists.
if (s_callbackSetName(name, m_room))
Room::Room(ROOM_INFO& room) :
_room(room)
{
s_callbackRemoveName(m_room.Name);
m_room.Name = name;
}
else
};
void Room::Register(sol::table& parent)
{
ScriptAssertF(false, "Could not add name {} - does an object with this name already exist?", name);
TENLog("Name will not be set", LogLevel::Warning, LogConfig::All);
// Register type.
parent.new_usertype<Room>(
ScriptReserved_Room,
sol::no_constructor,
sol::meta_function::index, IndexError,
sol::meta_function::new_index, NewIndexError,
ScriptReserved_RoomGetRoomNumber, &Room::GetRoomNumber,
ScriptReserved_RoomGetName, &Room::GetName,
ScriptReserved_RoomGetColor, &Room::GetColor,
ScriptReserved_RoomGetReverbType, &Room::GetReverbType,
ScriptReserved_RoomSetName, &Room::SetName,
ScriptReserved_RoomSetReverbType, &Room::SetReverbType,
ScriptReserved_RoomSetFlag, &Room::SetFlag,
ScriptReserved_RoomIsTagPresent, &Room::IsTagPresent,
ScriptReserved_RoomGetActive, &Room::GetActive,
ScriptReserved_RoomGetFlag, &Room::GetFlag);
}
}
bool Room::GetFlag(RoomEnvFlags flag) const
{
return ((m_room.flags & flag) == flag);
}
void Room::SetFlag(RoomEnvFlags flag, bool value)
{
if (value)
/// Get the room's number.
// @function Room:GetRoomNumber()
// @treturn int Room number.
int Room::GetRoomNumber() const
{
m_room.flags |= flag;
return _room.RoomNumber;
}
else
/// Get the room's unique string identifier.
// @function Room:GetName()
// @treturn string Room name.
std::string Room::GetName() const
{
m_room.flags &= ~flag;
return _room.Name;
}
}
bool Room::IsTagPresent(const std::string& tag) const
{
if (m_room.Tags.empty())
return false;
/// Get the room's ambient light color.
// @function Room:GetColor()
// @treturn Color Ambient light color.
ScriptColor Room::GetColor() const
{
return ScriptColor(_room.ambient);
}
return std::any_of(
m_room.Tags.begin(), m_room.Tags.end(),
[&tag](const std::string& value) { return (value == tag); });
}
/// Get the room's reverb type.
// @function Room:GetReverbType()
// @treturn Objects.RoomReverb Reverb type.
ReverbType Room::GetReverbType() const
{
return _room.reverbType;
}
/// Set the room's unique string identifier.
// @function Room:SetName()
// @tparam string name New name.
void Room::SetName(const std::string& name)
{
if (!ScriptAssert(!name.empty(), "Unable to set name. Name cannot be blank."))
return;
// Remove previous name if it already exists.
if (s_callbackSetName(name, _room))
{
s_callbackRemoveName(_room.Name);
_room.Name = name;
}
else
{
ScriptAssertF(false, "Could not add name {} - does an object with this name already exist?", name);
TENLog("Name will not be set", LogLevel::Warning, LogConfig::All);
}
}
/// Set the room's reverb type.
// @function Room:SetReverbType()
// @tparam Objects.RoomReverb Reverb type.
void Room::SetReverbType(ReverbType reverb)
{
_room.reverbType = reverb;
}
/// Set the room's specified flag.
// @function Room:SetFlag()
// @tparam Objects.RoomFlagID flagID Room flag ID.
// @tparam bool Boolean to set the flag to.
void Room::SetFlag(RoomEnvFlags flag, bool value)
{
if (value)
{
_room.flags |= flag;
}
else
{
_room.flags &= ~flag;
}
}
/// Get the room's specified flag value (true or false).
// @function Room:GetFlag()
// @tparam Objects.RoomFlagID flagID Room flag ID.
bool Room::IsTagPresent(const std::string& tag) const
{
if (_room.Tags.empty())
return false;
return std::any_of(
_room.Tags.begin(), _room.Tags.end(),
[&tag](const std::string& value)
{
return (value == tag);
});
}
/// Check if the specified tag is set for the room.
// @function Room:IsTagPresent()
// @tparam string tag Text tag to check (case sensitive).
// @treturn bool Boolean of the tag's presence.
bool Room::GetActive() const
{
return _room.Active();
}
/// Check if the room is active.
// @function Room:GetActive()
// @treturn bool Boolean of the room's active status.
bool Room::GetFlag(RoomEnvFlags flag) const
{
return ((_room.flags & flag) == flag);
}
//}

View file

@ -1,4 +1,5 @@
#pragma once
#include "Game/room.h"
#include "Scripting/Internal/TEN/Objects/NamedBase.h"
@ -7,32 +8,50 @@ enum class ReverbType;
class ScriptColor;
class Vec3;
class Room : public NamedBase<Room, ROOM_INFO&>
{
public:
using IdentifierType = std::reference_wrapper<ROOM_INFO>;
Room(ROOM_INFO& room);
~Room() = default;
//namespace TEN::Scripting
//{
class Room : public NamedBase<Room, ROOM_INFO&>
{
private:
// Members
Room& operator =(const Room& other) = delete;
Room(const Room& other) = delete;
ROOM_INFO& _room;
static void Register(sol::table& parent);
public:
using IdentifierType = std::reference_wrapper<ROOM_INFO>;
[[nodiscard]] bool GetActive() const;
[[nodiscard]] ScriptColor GetColor() const;
static void Register(sol::table& parent);
[[nodiscard]] std::string GetName() const;
void SetName(const std::string& name);
// Constructors
[[nodiscard]] bool GetFlag(RoomEnvFlags flag) const;
void SetFlag(RoomEnvFlags flag, bool value);
Room(ROOM_INFO& room);
Room(const Room& room) = delete;
[[nodiscard]] ReverbType GetReverbType() const;
void SetReverbType(ReverbType reverbType);
// Destructors
[[nodiscard]] bool IsTagPresent(const std::string& tag) const;
~Room() = default;
private:
ROOM_INFO& m_room;
};
// Getters
int GetRoomNumber() const;
std::string GetName() const;
ScriptColor GetColor() const;
ReverbType GetReverbType() const;
// Setters
void SetName(const std::string& name);
void SetReverbType(ReverbType reverbType);
void SetFlag(RoomEnvFlags flag, bool value);
// Inquirers
bool IsTagPresent(const std::string& tag) const;
bool GetActive() const; // TODO: Rename to IsActive().
bool GetFlag(RoomEnvFlags flag) const; // TODO: Rename to HasFlag().
// Operators
Room& operator =(const Room& room) = delete;
};
//}

View file

@ -9,10 +9,12 @@ public:
static void Register(sol::table& parent);
// Members
float x = 0;
float y = 0;
// Constructors
Vec2(float x, float y);
Vec2(float value);
Vec2(const Vector2& vector);
@ -28,6 +30,7 @@ public:
float Length() const;
// Meta functions
std::string ToString() const;
static Vec2 Add(const Vec2& vector0, const Vec2& vector1);
static Vec2 Subtract(const Vec2& vector0, const Vec2& vector1);
@ -38,9 +41,11 @@ public:
static bool IsEqualTo(const Vec2& vector0, const Vec2& vector1);
// Converters
Vector2 ToVector2() const;
//Vector2i ToVector2i() const;
// Operators
operator Vector2() const;
};

View file

@ -1,10 +1,10 @@
#pragma once
namespace sol { class state; }
class GameVector;
class Pose;
class Rotation;
class Vector3i;
namespace sol { class state; }
class Vec3
{
@ -12,11 +12,13 @@ public:
static void Register(sol::table& parent);
// Members
float x = 0;
float y = 0;
float z = 0;
// Constructors
Vec3() {};
Vec3(float x, float y, float z);
Vec3(float value);
@ -24,6 +26,7 @@ public:
Vec3(const Vector3i& vector);
// Utilities
Vec3 Normalize() const;
Vec3 Rotate(const Rotation& rot) const;
Vec3 Lerp(const Vec3& vector, float alpha) const;
@ -33,6 +36,7 @@ public:
float Length() const;
// Meta functions
std::string ToString() const;
static Vec3 Add(const Vec3& vector0, const Vec3& vector1);
static Vec3 Subtract(const Vec3& vector0, const Vec3& vector1);
@ -43,10 +47,12 @@ public:
static bool IsEqualTo(const Vec3& vector0, const Vec3& vector1);
// Converters
Vector3 ToVector3() const;
Vector3i ToVector3i() const;
GameVector ToGameVector() const;
// Operators
operator Vector3() const;
};

View file

@ -151,7 +151,7 @@ enum SOUND_EFFECTS
SFX_TR4_LARA_DEATH3 = 146,
SFX_TR4_ROLLING_BALL = 147,
SFX_TR4_RAISING_BLOCK = 148,
SFX_TR4_RUMBLE_NEXTDOOR = 149,
SFX_TR4_RAISING_BLOCK_2 = 149,
SFX_TR4_LOOP_FOR_SMALL_FIRES = 150,
SFX_TR4_CHAINS_LIBRARY = 151,
SFX_TR4_VEHICLE_JEEP_START = 152,
@ -361,7 +361,7 @@ enum SOUND_EFFECTS
SFX_TR4_LIGHT_BEAM_LOOP = 356,
SFX_TR4_GUIDE_FIRE_LIGHT = 357,
SFX_TR4_AUTOGUNS = 358,
SFX_TR4_EMPTY8 = 359,
SFX_TR4_ENVIORONMENT_RUMBLE = 359,
SFX_TR4_STEAM = 360,
SFX_TR4_GARAGE_DOOR = 361,
SFX_TR4_WIND = 362,

View file

@ -632,9 +632,9 @@ namespace TEN::Input
{
// Save screenshot.
static bool dbScreenshot = true;
if (KeyMap[KC_SYSRQ] && dbScreenshot)
if ((KeyMap[KC_SYSRQ] || KeyMap[KC_F12]) && dbScreenshot)
g_Renderer.SaveScreenshot();
dbScreenshot = !KeyMap[KC_SYSRQ];
dbScreenshot = !(KeyMap[KC_SYSRQ] || KeyMap[KC_F12]);
// Toggle fullscreen.
static bool dbFullscreen = true;

View file

@ -3,43 +3,42 @@
#include "Specific/clock.h"
namespace TEN::Input
{
InputAction::InputAction(ActionID actionID)
{
ID = actionID;
_id = actionID;
}
ActionID InputAction::GetID() const
{
return ID;
return _id;
}
float InputAction::GetValue() const
{
return Value;
return _value;
}
float InputAction::GetTimeActive() const
{
return TimeActive;
return _timeActive;
}
float InputAction::GetTimeInactive() const
{
return TimeInactive;
return _timeInactive;
}
bool InputAction::IsClicked() const
{
return ((Value != 0.0f) && (PrevValue == 0.0f));
return ((_value != 0.0f) && (_prevValue == 0.0f));
}
bool InputAction::IsHeld(float delayInSec) const
{
float delayInFrameTime = (delayInSec == 0.0f) ? 0.0f : round(delayInSec / DELTA_TIME);
return ((Value != 0.0f) && (TimeActive >= delayInFrameTime));
return ((_value != 0.0f) && (_timeActive >= delayInFrameTime));
}
// NOTE: To avoid stutter on second pulse, ensure initialDelayInSec is multiple of delayInSec.
@ -48,12 +47,12 @@ namespace TEN::Input
if (IsClicked())
return true;
if (!IsHeld() || PrevTimeActive == 0.0f || TimeActive == PrevTimeActive)
if (!IsHeld() || _prevTimeActive == 0.0f || _timeActive == _prevTimeActive)
return false;
float activeDelayInFrameTime = (TimeActive > round(initialDelayInSec / DELTA_TIME)) ? round(delayInSec / DELTA_TIME) : round(initialDelayInSec / DELTA_TIME);
float delayInFrameTime = std::floor(TimeActive / activeDelayInFrameTime) * activeDelayInFrameTime;
if (delayInFrameTime > (std::floor(PrevTimeActive / activeDelayInFrameTime) * activeDelayInFrameTime))
float activeDelayInFrameTime = (_timeActive > round(initialDelayInSec / DELTA_TIME)) ? round(delayInSec / DELTA_TIME) : round(initialDelayInSec / DELTA_TIME);
float delayInFrameTime = std::floor(_timeActive / activeDelayInFrameTime) * activeDelayInFrameTime;
if (delayInFrameTime > (std::floor(_prevTimeActive / activeDelayInFrameTime) * activeDelayInFrameTime))
return true;
// Keeping version counting real time for future reference. -- Sezz 2022.10.01
@ -70,7 +69,7 @@ namespace TEN::Input
bool InputAction::IsReleased(float maxDelayInSec) const
{
float maxDelayInFrameTime = (maxDelayInSec == INFINITY) ? INFINITY : round(maxDelayInSec / DELTA_TIME);
return ((Value == 0.0f) && (PrevValue != 0.0f) && (TimeActive <= maxDelayInFrameTime));
return ((_value == 0.0f) && (_prevValue != 0.0f) && (_timeActive <= maxDelayInFrameTime));
}
void InputAction::Update(bool value)
@ -89,57 +88,57 @@ namespace TEN::Input
if (IsClicked())
{
PrevTimeActive = 0.0f;
TimeActive = 0.0f;
TimeInactive += FRAME_TIME;// DELTA_TIME;
_prevTimeActive = 0.0f;
_timeActive = 0.0f;
_timeInactive += FRAME_TIME;// DELTA_TIME;
}
else if (IsReleased())
{
PrevTimeActive = TimeActive;
TimeActive += FRAME_TIME;// DELTA_TIME;
TimeInactive = 0.0f;
_prevTimeActive = _timeActive;
_timeActive += FRAME_TIME;// DELTA_TIME;
_timeInactive = 0.0f;
}
else if (IsHeld())
{
PrevTimeActive = TimeActive;
TimeActive += FRAME_TIME;// DELTA_TIME;
TimeInactive = 0.0f;
_prevTimeActive = _timeActive;
_timeActive += FRAME_TIME;// DELTA_TIME;
_timeInactive = 0.0f;
}
else
{
PrevTimeActive = 0.0f;
TimeActive = 0.0f;
TimeInactive += FRAME_TIME;// DELTA_TIME;
_prevTimeActive = 0.0f;
_timeActive = 0.0f;
_timeInactive += FRAME_TIME;// DELTA_TIME;
}
}
void InputAction::Clear()
{
Value = 0.0f;
PrevValue = 0.0f;
TimeActive = 0.0f;
PrevTimeActive = 0.0f;
TimeInactive = 0.0f;
_value = 0.0f;
_prevValue = 0.0f;
_timeActive = 0.0f;
_prevTimeActive = 0.0f;
_timeInactive = 0.0f;
}
void InputAction::DrawDebug() const
{
PrintDebugMessage("ID: %d", (int)ID);
PrintDebugMessage("ID: %d", (int)_id);
PrintDebugMessage("IsClicked: %d", IsClicked());
PrintDebugMessage("IsHeld: %d", IsHeld());
PrintDebugMessage("IsPulsed (.2s, .6s): %d", IsPulsed(0.2f, 0.6f));
PrintDebugMessage("IsReleased: %d", IsReleased());
PrintDebugMessage("");
PrintDebugMessage("Value: %.3f", Value);
PrintDebugMessage("PrevValue: %.3f", PrevValue);
PrintDebugMessage("TimeActive: %.3f", TimeActive);
PrintDebugMessage("PrevTimeActive: %.3f", PrevTimeActive);
PrintDebugMessage("TimeInactive: %.3f", TimeInactive);
PrintDebugMessage("Value: %.3f", _value);
PrintDebugMessage("PrevValue: %.3f", _prevValue);
PrintDebugMessage("TimeActive: %.3f", _timeActive);
PrintDebugMessage("PrevTimeActive: %.3f", _prevTimeActive);
PrintDebugMessage("TimeInactive: %.3f", _timeInactive);
}
void InputAction::UpdateValue(float value)
{
PrevValue = Value;
Value = value;
_prevValue = _value;
_value = value;
}
}

View file

@ -65,12 +65,12 @@ namespace TEN::Input
private:
// Members
ActionID ID = In::Forward;
float Value = 0.0f;
float PrevValue = 0.0f;
float TimeActive = 0.0f;
float PrevTimeActive = 0.0f;
float TimeInactive = 0.0f;
ActionID _id = In::Forward;
float _value = 0.0f;
float _prevValue = 0.0f;
float _timeActive = 0.0f;
float _prevTimeActive = 0.0f;
float _timeInactive = 0.0f;
public:
// Constructors

View file

@ -1471,7 +1471,8 @@ void GetCarriedItems()
const auto& object = Objects[item.ObjectNumber];
if (object.intelligent ||
(item.ObjectNumber >= ID_SEARCH_OBJECT1 && item.ObjectNumber <= ID_SEARCH_OBJECT3))
(item.ObjectNumber >= ID_SEARCH_OBJECT1 && item.ObjectNumber <= ID_SEARCH_OBJECT3) ||
(item.ObjectNumber == ID_SARCOPHAGUS))
{
for (short linkNumber = g_Level.Rooms[item.RoomNumber].itemNumber; linkNumber != NO_VALUE; linkNumber = g_Level.Items[linkNumber].NextItem)
{