mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Use real bounding box tests for GetCollidedObjects (#1343)
* Initial commit * Use DX bboxes for item collision tests * Update collide_item.cpp * Update collide_item.cpp * Update collide_item.cpp * Code cleanup * More code cleanup and optimizations * Remove obsolete radius argument for GetCollidedObjects, mode code cleanups * Fix ITEM_INVISIBLE typos * Restore custom radius where needed * Restore custom radius for polerope * Function cleanup * Simplify function * Remove CollidedItems and CollidedMeshes globals * Use custom radius for pickups and explosion * Add changelog, shuffle function order * Simplify * fix syntax error * Rename local variable * Update collide_item.cpp --------- Co-authored-by: Sezz <sezzary@outlook.com> Co-authored-by: Jakub <kubabilinski03@gmail.com>
This commit is contained in:
parent
35bf1470c9
commit
266f1b5b6a
26 changed files with 482 additions and 511 deletions
|
@ -28,6 +28,7 @@ Lua API changes:
|
|||
* Added Flow.GetFlipMapStatus() function to get current flipmap status.
|
||||
* Added Moveable:GetMeshCount() function to get number of moveable meshes.
|
||||
* Added Static:GetHP() and Static:SetHP() functions to change shatterable static mesh hit points.
|
||||
* Fixed Moveable:SetOnCollidedWithObject() callback.
|
||||
|
||||
Version 1.3
|
||||
===========
|
||||
|
|
|
@ -145,6 +145,14 @@ associated getters and setters.</td>
|
|||
<td class="summary">Set the name of the function to be called when the moveable is shot by Lara.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Moveable:SetOnCollidedWithObject">Moveable:SetOnCollidedWithObject(func)</a></td>
|
||||
<td class="summary">Set the function to be called when this moveable collides with another moveable</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Moveable:SetOnCollidedWithRoom">Moveable:SetOnCollidedWithRoom(func)</a></td>
|
||||
<td class="summary">Set the function called when this moveable collides with room geometry (e.g.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Moveable:SetOnKilled">Moveable:SetOnKilled(callback)</a></td>
|
||||
<td class="summary">Set the name of the function to be called when the moveable is destroyed/killed
|
||||
Note that enemy death often occurs at the end of an animation, and not at the exact moment
|
||||
|
@ -274,14 +282,6 @@ associated getters and setters.</td>
|
|||
<td class="summary">Borrow animation from an object</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Moveable:SetOnCollidedWithObject">Moveable:SetOnCollidedWithObject(func)</a></td>
|
||||
<td class="summary">Set the function to be called when this moveable collides with another moveable</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Moveable:SetOnCollidedWithRoom">Moveable:SetOnCollidedWithRoom(func)</a></td>
|
||||
<td class="summary">Set the function called when this moveable collides with room geometry (e.g.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Moveable:GetPosition">Moveable:GetPosition()</a></td>
|
||||
<td class="summary">Get the object's position</td>
|
||||
</tr>
|
||||
|
@ -641,6 +641,64 @@ most can just be ignored (see usage).
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Moveable:SetOnCollidedWithObject"></a>
|
||||
<strong>Moveable:SetOnCollidedWithObject(func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the function to be called when this moveable collides with another moveable
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
callback function to be called (must be in LevelFuncs hierarchy). This function can take two arguments; these will store the two <a href="../2 classes/Objects.Moveable.html#Moveable">Moveable</a>s taking part in the collision.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.objCollided = <span class="keyword">function</span>(obj1, obj2)
|
||||
<span class="global">print</span>(obj1:GetName() .. <span class="string">" collided with "</span> .. obj2:GetName())
|
||||
<span class="keyword">end</span>
|
||||
baddy:SetOnCollidedWithObject(LevelFuncs.objCollided)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Moveable:SetOnCollidedWithRoom"></a>
|
||||
<strong>Moveable:SetOnCollidedWithRoom(func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the function called when this moveable collides with room geometry (e.g. a wall or floor). This function can take an argument that holds the <a href="../2 classes/Objects.Moveable.html#Moveable">Moveable</a> that collided with geometry.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
callback function to be called (must be in LevelFuncs hierarchy)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.roomCollided = <span class="keyword">function</span>(obj)
|
||||
<span class="global">print</span>(obj:GetName() .. <span class="string">" collided with room geometry"</span>)
|
||||
<span class="keyword">end</span>
|
||||
baddy:SetOnCollidedWithRoom(LevelFuncs.roomCollided)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Moveable:SetOnKilled"></a>
|
||||
|
@ -1372,64 +1430,6 @@ shiva:SetObjectID(TEN.Objects.ObjID.BIGMEDI_ITEM)</pre>
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Moveable:SetOnCollidedWithObject"></a>
|
||||
<strong>Moveable:SetOnCollidedWithObject(func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the function to be called when this moveable collides with another moveable
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
callback function to be called (must be in LevelFuncs hierarchy). This function can take two arguments; these will store the two <a href="../2 classes/Objects.Moveable.html#Moveable">Moveable</a>s taking part in the collision.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.objCollided = <span class="keyword">function</span>(obj1, obj2)
|
||||
<span class="global">print</span>(obj1:GetName() .. <span class="string">" collided with "</span> .. obj2:GetName())
|
||||
<span class="keyword">end</span>
|
||||
baddy:SetOnCollidedWithObject(LevelFuncs.objCollided)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Moveable:SetOnCollidedWithRoom"></a>
|
||||
<strong>Moveable:SetOnCollidedWithRoom(func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the function called when this moveable collides with room geometry (e.g. a wall or floor). This function can take an argument that holds the <a href="../2 classes/Objects.Moveable.html#Moveable">Moveable</a> that collided with geometry.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
callback function to be called (must be in LevelFuncs hierarchy)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.roomCollided = <span class="keyword">function</span>(obj)
|
||||
<span class="global">print</span>(obj:GetName() .. <span class="string">" collided with room geometry"</span>)
|
||||
<span class="keyword">end</span>
|
||||
baddy:SetOnCollidedWithRoom(LevelFuncs.roomCollided)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Moveable:GetPosition"></a>
|
||||
|
|
|
@ -2111,6 +2111,35 @@ most can just be ignored (see usage).</description>
|
|||
</parameters>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<module>Objects.Moveable</module>
|
||||
<caller>Moveable</caller>
|
||||
<name>SetOnCollidedWithObject</name>
|
||||
<summary>Set the function to be called when this moveable collides with another moveable</summary>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>func</name>
|
||||
<type>function</type>
|
||||
<description>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.</description>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<module>Objects.Moveable</module>
|
||||
<caller>Moveable</caller>
|
||||
<name>SetOnCollidedWithRoom</name>
|
||||
<summary>Set the function called when this moveable collides with room geometry (e.g.</summary>
|
||||
<description>a wall or floor). This function can take an argument that holds the @{Moveable} that collided with geometry.</description>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>func</name>
|
||||
<type>function</type>
|
||||
<description>callback function to be called (must be in LevelFuncs hierarchy)</description>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<module>Objects.Moveable</module>
|
||||
<caller>Moveable</caller>
|
||||
|
@ -2595,35 +2624,6 @@ most can just be ignored (see usage).</description>
|
|||
</parameters>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<module>Objects.Moveable</module>
|
||||
<caller>Moveable</caller>
|
||||
<name>SetOnCollidedWithObject</name>
|
||||
<summary>Set the function to be called when this moveable collides with another moveable</summary>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>func</name>
|
||||
<type>function</type>
|
||||
<description>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.</description>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<module>Objects.Moveable</module>
|
||||
<caller>Moveable</caller>
|
||||
<name>SetOnCollidedWithRoom</name>
|
||||
<summary>Set the function called when this moveable collides with room geometry (e.g.</summary>
|
||||
<description>a wall or floor). This function can take an argument that holds the @{Moveable} that collided with geometry.</description>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>func</name>
|
||||
<type>function</type>
|
||||
<description>callback function to be called (must be in LevelFuncs hierarchy)</description>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</function>
|
||||
|
||||
<function>
|
||||
<module>Objects.Moveable</module>
|
||||
<caller>Moveable</caller>
|
||||
|
|
|
@ -753,8 +753,8 @@ bool TestLaraObjectCollision(ItemInfo* item, short headingAngle, int forward, in
|
|||
item->Pose.Position.y += down;
|
||||
item->Pose.Position.z += phd_cos(item->Pose.Orientation.y + headingAngle) * forward + phd_sin(headingAngle + ANGLE(90.0f) * sideSign) * abs(right);
|
||||
|
||||
bool result = GetCollidedObjects(item, LARA_RADIUS, true, CollidedItems, CollidedMeshes, 0);
|
||||
bool isCollided = !GetCollidedObjects(*item, true, false).IsEmpty();
|
||||
|
||||
item->Pose = prevPose;
|
||||
return result;
|
||||
return isCollided;
|
||||
}
|
||||
|
|
|
@ -324,10 +324,10 @@ void CreateFlare(ItemInfo& laraItem, GAME_OBJECT_ID objectID, bool isThrown)
|
|||
flareItem.Pose.Position = pos;
|
||||
|
||||
int floorHeight = GetCollision(pos.x, pos.y, pos.z, laraItem.RoomNumber).Position.Floor;
|
||||
auto hasCollided = GetCollidedObjects(&flareItem, 0, true, CollidedItems, CollidedMeshes, true);
|
||||
auto isCollided = !GetCollidedObjects(flareItem, true, true).IsEmpty();
|
||||
bool hasLanded = false;
|
||||
|
||||
if (floorHeight < pos.y || hasCollided)
|
||||
if (floorHeight < pos.y || isCollided)
|
||||
{
|
||||
hasLanded = true;
|
||||
flareItem.Pose.Position.x = laraItem.Pose.Position.x + 320 * phd_sin(flareItem.Pose.Orientation.y);
|
||||
|
|
|
@ -54,9 +54,9 @@ void InitializeLara(bool restore)
|
|||
ZeroMemory(&Lara, sizeof(LaraInfo));
|
||||
|
||||
LaraItem->Data = &Lara;
|
||||
Lara.Context = PlayerContext(*LaraItem, LaraCollision);
|
||||
|
||||
LaraItem->Collidable = false;
|
||||
|
||||
Lara.Context = PlayerContext(*LaraItem, LaraCollision);
|
||||
|
||||
Lara.Status.Air = LARA_AIR_MAX;
|
||||
Lara.Status.Exposure = LARA_EXPOSURE_MAX;
|
||||
|
|
|
@ -1554,42 +1554,34 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
|
|||
}
|
||||
|
||||
// Found possible collided items and statics.
|
||||
GetCollidedObjects(&projectile, radius, true, &CollidedItems[0], &CollidedMeshes[0], false);
|
||||
|
||||
// If no collided items and meshes are found, exit the loop.
|
||||
if (!CollidedItems[0] && !CollidedMeshes[0])
|
||||
auto collObjects = GetCollidedObjects(projectile, true, false, radius);
|
||||
if (collObjects.IsEmpty())
|
||||
break;
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
// Run through statics.
|
||||
for (auto* staticPtr : collObjects.StaticPtrs)
|
||||
{
|
||||
auto* meshPtr = CollidedMeshes[i];
|
||||
if (!meshPtr)
|
||||
break;
|
||||
|
||||
hasHit = hasHitNotByEmitter = doShatter = true;
|
||||
doExplosion = isExplosive;
|
||||
|
||||
if (StaticObjects[meshPtr->staticNumber].shatterType == ShatterType::None)
|
||||
if (StaticObjects[staticPtr->staticNumber].shatterType == ShatterType::None)
|
||||
continue;
|
||||
|
||||
meshPtr->HitPoints -= damage;
|
||||
if (meshPtr->HitPoints <= 0)
|
||||
ShatterObject(nullptr, meshPtr, -128, projectile.RoomNumber, 0);
|
||||
staticPtr->HitPoints -= damage;
|
||||
if (staticPtr->HitPoints <= 0)
|
||||
ShatterObject(nullptr, staticPtr, -128, projectile.RoomNumber, 0);
|
||||
|
||||
if (!isExplosive)
|
||||
continue;
|
||||
|
||||
TriggerExplosionSparks(meshPtr->pos.Position.x, meshPtr->pos.Position.y, meshPtr->pos.Position.z, 3, -2, 0, projectile.RoomNumber);
|
||||
auto pose = Pose(meshPtr->pos.Position.x, meshPtr->pos.Position.y - 128, meshPtr->pos.Position.z, 0, meshPtr->pos.Orientation.y, 0);
|
||||
TriggerExplosionSparks(staticPtr->pos.Position.x, staticPtr->pos.Position.y, staticPtr->pos.Position.z, 3, -2, 0, projectile.RoomNumber);
|
||||
auto pose = Pose(staticPtr->pos.Position.x, staticPtr->pos.Position.y - 128, staticPtr->pos.Position.z, 0, staticPtr->pos.Orientation.y, 0);
|
||||
TriggerShockwave(&pose, 40, 176, 64, 0, 96, 128, 16, EulerAngles::Identity, 0, true, false, false, (int)ShockwaveStyle::Normal);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
// Run through items.
|
||||
for (auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
auto* itemPtr = CollidedItems[i];
|
||||
if (itemPtr == nullptr)
|
||||
break;
|
||||
#
|
||||
// Object was already affected by collision, skip it.
|
||||
if (std::find(affectedObjects.begin(), affectedObjects.end(), itemPtr->Index) != affectedObjects.end())
|
||||
continue;
|
||||
|
@ -1652,7 +1644,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
|
|||
SmashObject(itemPtr->Index);
|
||||
KillItem(itemPtr->Index);
|
||||
}
|
||||
else if (currentObject.collision && !(itemPtr->Status & ITEM_INVISIBLE))
|
||||
else if (currentObject.collision && itemPtr->Status != ITEM_INVISIBLE)
|
||||
{
|
||||
doShatter = hasHit = true;
|
||||
doExplosion = isExplosive;
|
||||
|
|
|
@ -1784,8 +1784,8 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, fl
|
|||
|
||||
bool atLeastOnePoleCollided = false;
|
||||
|
||||
if (GetCollidedObjects(item, BLOCK(1), true, CollidedItems, nullptr, false) &&
|
||||
CollidedItems[0] != nullptr)
|
||||
auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(1), ObjectCollectionMode::Items);
|
||||
if (!collObjects.IsEmpty())
|
||||
{
|
||||
auto laraBox = GameBoundingBox(item).ToBoundingOrientedBox(item->Pose);
|
||||
|
||||
|
@ -1803,16 +1803,12 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, fl
|
|||
|
||||
//g_Renderer.AddDebugSphere(sphere.Center, 16.0f, Vector4(1, 0, 0, 1), RendererDebugPage::CollisionStats);
|
||||
|
||||
int i = 0;
|
||||
while (CollidedItems[i] != nullptr)
|
||||
for (const auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
auto*& object = CollidedItems[i];
|
||||
i++;
|
||||
|
||||
if (object->ObjectNumber != ID_POLEROPE)
|
||||
if (itemPtr->ObjectNumber != ID_POLEROPE)
|
||||
continue;
|
||||
|
||||
auto poleBox = GameBoundingBox(object).ToBoundingOrientedBox(object->Pose);
|
||||
auto poleBox = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose);
|
||||
poleBox.Extents = poleBox.Extents + Vector3(coll->Setup.Radius, 0.0f, coll->Setup.Radius);
|
||||
|
||||
//g_Renderer.AddDebugBox(poleBox, Vector4(0, 0, 1, 1), RendererDebugPage::CollisionStats);
|
||||
|
|
|
@ -23,12 +23,11 @@
|
|||
using namespace TEN::Math;
|
||||
using namespace TEN::Renderer;
|
||||
|
||||
GameBoundingBox GlobalCollisionBounds;
|
||||
ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS];
|
||||
MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS];
|
||||
|
||||
constexpr auto ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD = 6;
|
||||
|
||||
// Globals
|
||||
GameBoundingBox GlobalCollisionBounds;
|
||||
|
||||
void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
@ -100,174 +99,162 @@ void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionIn
|
|||
}
|
||||
}
|
||||
|
||||
bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, ItemInfo** collidedItems, MESH_INFO** collidedMeshes, bool ignoreLara)
|
||||
CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, bool ignorePlayer, float customRadius, ObjectCollectionMode mode)
|
||||
{
|
||||
short numItems = 0;
|
||||
short numMeshes = 0;
|
||||
constexpr auto EXTENTS_LENGTH_MIN = 2.0f;
|
||||
constexpr auto ROUGH_BOX_HEIGHT_MIN = BLOCK(1 / 8.0f);
|
||||
|
||||
// Collect all the rooms where to check
|
||||
for (auto i : g_Level.Rooms[collidingItem->RoomNumber].neighbors)
|
||||
auto collObjects = CollidedObjectData{};
|
||||
|
||||
int itemCount = 0;
|
||||
int staticCount = 0;
|
||||
|
||||
// Establish parameters of colliding item.
|
||||
const auto& collidingBounds = GetBestFrame(collidingItem).BoundingBox;
|
||||
auto collidingExtents = collidingBounds.GetExtents();
|
||||
auto collidingSphere = BoundingSphere(collidingBounds.GetCenter() + collidingItem.Pose.Position.ToVector3(), collidingExtents.Length());
|
||||
auto collidingCircle = Vector3(collidingSphere.Center.x, collidingSphere.Center.z, (customRadius > 0.0f) ? customRadius : std::hypot(collidingExtents.x, collidingExtents.z));
|
||||
|
||||
// Quickly discard collision if colliding item bounds are below tolerance threshold.
|
||||
if (collidingSphere.Radius <= EXTENTS_LENGTH_MIN)
|
||||
return collObjects;
|
||||
|
||||
// Run through neighboring rooms.
|
||||
const auto& room = g_Level.Rooms[collidingItem.RoomNumber];
|
||||
for (int roomNumber : room.neighbors)
|
||||
{
|
||||
if (!g_Level.Rooms[i].Active())
|
||||
auto& neighborRoom = g_Level.Rooms[roomNumber];
|
||||
if (!neighborRoom.Active())
|
||||
continue;
|
||||
|
||||
auto* room = &g_Level.Rooms[i];
|
||||
|
||||
if (collidedMeshes)
|
||||
// Collect items.
|
||||
if (mode == ObjectCollectionMode::All ||
|
||||
mode == ObjectCollectionMode::Items)
|
||||
{
|
||||
for (int j = 0; j < room->mesh.size(); j++)
|
||||
{
|
||||
auto* mesh = &room->mesh[j];
|
||||
const auto& bBox = GetBoundsAccurate(*mesh, false);
|
||||
|
||||
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
|
||||
continue;
|
||||
|
||||
if ((collidingItem->Pose.Position.y + radius + CLICK(0.5f)) < (mesh->pos.Position.y + bBox.Y1))
|
||||
continue;
|
||||
|
||||
if (collidingItem->Pose.Position.y > (mesh->pos.Position.y + bBox.Y2))
|
||||
continue;
|
||||
|
||||
float sinY = phd_sin(mesh->pos.Orientation.y);
|
||||
float cosY = phd_cos(mesh->pos.Orientation.y);
|
||||
|
||||
float rx = ((collidingItem->Pose.Position.x - mesh->pos.Position.x) * cosY) - ((collidingItem->Pose.Position.z - mesh->pos.Position.z) * sinY);
|
||||
float rz = ((collidingItem->Pose.Position.z - mesh->pos.Position.z) * cosY) + ((collidingItem->Pose.Position.x - mesh->pos.Position.x) * sinY);
|
||||
|
||||
if ((radius + rx + CLICK(0.5f) < bBox.X1) || (rx - radius - CLICK(0.5f) > bBox.X2))
|
||||
continue;
|
||||
|
||||
if ((radius + rz + CLICK(0.5f) < bBox.Z1) || (rz - radius - CLICK(0.5f) > bBox.Z2))
|
||||
continue;
|
||||
|
||||
collidedMeshes[numMeshes++] = mesh;
|
||||
|
||||
if (!radius)
|
||||
{
|
||||
collidedItems[0] = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
collidedMeshes[numMeshes] = nullptr;
|
||||
}
|
||||
|
||||
if (collidedItems)
|
||||
{
|
||||
int itemNumber = room->itemNumber;
|
||||
int itemNumber = neighborRoom.itemNumber;
|
||||
if (itemNumber != NO_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
const auto& object = Objects[item.ObjectNumber];
|
||||
|
||||
if (item == collidingItem ||
|
||||
(ignoreLara && item->ObjectNumber == ID_LARA) ||
|
||||
(onlyVisible && item->Status == ITEM_INVISIBLE) ||
|
||||
item->Flags & IFLAG_KILLED ||
|
||||
item->MeshBits == NO_JOINT_BITS ||
|
||||
(Objects[item->ObjectNumber].drawRoutine == nullptr && item->ObjectNumber != ID_LARA) ||
|
||||
(Objects[item->ObjectNumber].collision == nullptr && item->ObjectNumber != ID_LARA))
|
||||
itemNumber = item.NextItem;
|
||||
|
||||
// Ignore player (if applicable).
|
||||
if (ignorePlayer && item.IsLara())
|
||||
continue;
|
||||
|
||||
// Ignore invisible item (if applicable).
|
||||
if (onlyVisible && item.Status == ITEM_INVISIBLE)
|
||||
continue;
|
||||
|
||||
// Ignore items not feasible for collision.
|
||||
if (item.Index == collidingItem.Index ||
|
||||
item.Flags & IFLAG_KILLED || item.MeshBits == NO_JOINT_BITS ||
|
||||
(object.drawRoutine == nullptr && !item.IsLara()) ||
|
||||
(object.collision == nullptr && !item.IsLara()))
|
||||
{
|
||||
itemNumber = item->NextItem;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: This is awful and we need a better system.
|
||||
if (item->ObjectNumber == ID_UPV && item->HitPoints == 1)
|
||||
// HACK: Ignore UPV and big gun.
|
||||
if ((item.ObjectNumber == ID_UPV || item.ObjectNumber == ID_BIGGUN) && item.HitPoints == 1)
|
||||
continue;
|
||||
|
||||
// Test rough distance to discard objects more than 6 blocks away.
|
||||
float dist = Vector3i::Distance(item.Pose.Position, collidingItem.Pose.Position);
|
||||
if (dist > COLLISION_CHECK_DISTANCE)
|
||||
continue;
|
||||
|
||||
const auto& bounds = GetBestFrame(item).BoundingBox;
|
||||
auto extents = bounds.GetExtents();
|
||||
|
||||
// If item bounding box extents is below tolerance threshold, discard object.
|
||||
if (extents.Length() <= EXTENTS_LENGTH_MIN)
|
||||
continue;
|
||||
|
||||
// Test rough vertical distance to discard objects not intersecting vertically.
|
||||
if (((collidingItem.Pose.Position.y + collidingBounds.Y1) - ROUGH_BOX_HEIGHT_MIN) >
|
||||
((item.Pose.Position.y + bounds.Y2) + ROUGH_BOX_HEIGHT_MIN))
|
||||
{
|
||||
itemNumber = item->NextItem;
|
||||
continue;
|
||||
}
|
||||
if (item->ObjectNumber == ID_BIGGUN && item->HitPoints == 1)
|
||||
if (((collidingItem.Pose.Position.y + collidingBounds.Y2) + ROUGH_BOX_HEIGHT_MIN) <
|
||||
((item.Pose.Position.y + bounds.Y1) - ROUGH_BOX_HEIGHT_MIN))
|
||||
{
|
||||
itemNumber = item->NextItem;
|
||||
continue;
|
||||
}
|
||||
|
||||
int dx = collidingItem->Pose.Position.x - item->Pose.Position.x;
|
||||
int dy = collidingItem->Pose.Position.y - item->Pose.Position.y;
|
||||
int dz = collidingItem->Pose.Position.z - item->Pose.Position.z;
|
||||
// Test rough circle intersection to discard objects not intersecting horizontally.
|
||||
auto circle = Vector3(item.Pose.Position.x, item.Pose.Position.z, std::hypot(extents.x, extents.z));
|
||||
if (!Geometry::CircleIntersects(circle, collidingCircle))
|
||||
continue;
|
||||
|
||||
auto bounds = GetBestFrame(*item).BoundingBox;
|
||||
auto box0 = bounds.ToBoundingOrientedBox(item.Pose);
|
||||
auto box1 = collidingBounds.ToBoundingOrientedBox(collidingItem.Pose);
|
||||
|
||||
if (dx >= -BLOCK(2) && dx <= BLOCK(2) &&
|
||||
dy >= -BLOCK(2) && dy <= BLOCK(2) &&
|
||||
dz >= -BLOCK(2) && dz <= BLOCK(2) &&
|
||||
(collidingItem->Pose.Position.y + radius + CLICK(0.5f)) >= (item->Pose.Position.y + bounds.Y1) &&
|
||||
(collidingItem->Pose.Position.y - radius - CLICK(0.5f)) <= (item->Pose.Position.y + bounds.Y2))
|
||||
{
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
float cosY = phd_cos(item->Pose.Orientation.y);
|
||||
// Override extents if specified.
|
||||
if (customRadius > 0.0f)
|
||||
box1.Extents = Vector3(customRadius);
|
||||
|
||||
int rx = (dx * cosY) - (dz * sinY);
|
||||
int rz = (dz * cosY) + (dx * sinY);
|
||||
|
||||
// TODO: Modify asset to avoid hardcoded bounds change. -- Sezz 2023.04.30
|
||||
if (item->ObjectNumber == ID_TURN_SWITCH)
|
||||
{
|
||||
bounds.X1 = -CLICK(1);
|
||||
bounds.X2 = CLICK(1);
|
||||
bounds.Z1 = -CLICK(1);
|
||||
bounds.Z1 = CLICK(1);
|
||||
}
|
||||
|
||||
if ((radius + rx + CLICK(0.5f)) >= bounds.X1 &&
|
||||
(rx - radius - CLICK(0.5f)) <= bounds.X2)
|
||||
{
|
||||
if ((radius + rz + CLICK(0.5f)) >= bounds.Z1 &&
|
||||
(rz - radius - CLICK(0.5f)) <= bounds.Z2)
|
||||
{
|
||||
collidedItems[numItems++] = item;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((collidingItem->Pose.Position.y + radius + CLICK(0.5f)) >= (item->Pose.Position.y + bounds.Y1) &&
|
||||
(collidingItem->Pose.Position.y - radius - CLICK(0.5f)) <= (item->Pose.Position.y + bounds.Y2))
|
||||
{
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
float cosY = phd_cos(item->Pose.Orientation.y);
|
||||
|
||||
int rx = (dx * cosY) - (dz * sinY);
|
||||
int rz = (dz * cosY) + (dx * sinY);
|
||||
|
||||
// TODO: Modify asset to avoid hardcoded bounds change. -- Sezz 2023.04.30
|
||||
if (item->ObjectNumber == ID_TURN_SWITCH)
|
||||
{
|
||||
bounds.X1 = -CLICK(1);
|
||||
bounds.X2 = CLICK(1);
|
||||
bounds.Z1 = -CLICK(1);
|
||||
bounds.Z1 = CLICK(1);
|
||||
}
|
||||
|
||||
if ((radius + rx + CLICK(0.5f)) >= bounds.X1 &&
|
||||
(rx - radius - CLICK(0.5f)) <= bounds.X2)
|
||||
{
|
||||
if ((radius + rz + CLICK(0.5f)) >= bounds.Z1 &&
|
||||
(rz - radius - CLICK(0.5f)) <= bounds.Z2)
|
||||
{
|
||||
collidedItems[numItems++] = item;
|
||||
|
||||
if (!radius)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itemNumber = item->NextItem;
|
||||
// Test accurate box intersection.
|
||||
if (box0.Intersects(box1))
|
||||
collObjects.ItemPtrs.push_back(&item);
|
||||
}
|
||||
while (itemNumber != NO_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
collidedItems[numItems] = nullptr;
|
||||
// Collect statics.
|
||||
if (mode == ObjectCollectionMode::All ||
|
||||
mode == ObjectCollectionMode::Statics)
|
||||
{
|
||||
for (auto& staticObj : neighborRoom.mesh)
|
||||
{
|
||||
// Discard invisible statics.
|
||||
if (!(staticObj.flags & StaticMeshFlags::SM_VISIBLE))
|
||||
continue;
|
||||
|
||||
// Test rough distance to discard statics beyond collision check threshold.
|
||||
float dist = Vector3i::Distance(staticObj.pos.Position, collidingItem.Pose.Position);
|
||||
if (dist > COLLISION_CHECK_DISTANCE)
|
||||
continue;
|
||||
|
||||
const auto& bounds = GetBoundsAccurate(staticObj, false);
|
||||
|
||||
// Test rough vertical distance to discard statics not intersecting vertically.
|
||||
if (((collidingItem.Pose.Position.y + collidingBounds.Y1) - ROUGH_BOX_HEIGHT_MIN) >
|
||||
((staticObj.pos.Position.y + bounds.Y2) + ROUGH_BOX_HEIGHT_MIN))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (((collidingItem.Pose.Position.y + collidingBounds.Y2) + ROUGH_BOX_HEIGHT_MIN) <
|
||||
((staticObj.pos.Position.y + bounds.Y1) - ROUGH_BOX_HEIGHT_MIN))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test rough circle intersection to discard statics not intersecting horizontally.
|
||||
auto circle = Vector3(staticObj.pos.Position.x, staticObj.pos.Position.z, (bounds.GetExtents() * Vector3(1.0f, 0.0f, 1.0f)).Length());
|
||||
if (!Geometry::CircleIntersects(circle, collidingCircle))
|
||||
continue;
|
||||
|
||||
auto box0 = bounds.ToBoundingOrientedBox(staticObj.pos.Position);
|
||||
auto box1 = collidingBounds.ToBoundingOrientedBox(collidingItem.Pose);
|
||||
|
||||
// Override extents if specified.
|
||||
if (customRadius > 0.0f)
|
||||
box1.Extents = Vector3(customRadius);
|
||||
|
||||
// Test accurate box intersection.
|
||||
if (box0.Intersects(box1))
|
||||
collObjects.StaticPtrs.push_back(&staticObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (numItems || numMeshes);
|
||||
return collObjects;
|
||||
}
|
||||
|
||||
bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll)
|
||||
|
|
|
@ -7,14 +7,17 @@ struct CollisionResult;
|
|||
struct ItemInfo;
|
||||
struct MESH_INFO;
|
||||
|
||||
constexpr auto MAX_COLLIDED_OBJECTS = 1024;
|
||||
constexpr auto ITEM_RADIUS_YMAX = BLOCK(3);
|
||||
|
||||
constexpr auto ITEM_RADIUS_YMAX = BLOCK(3);
|
||||
constexpr auto VEHICLE_COLLISION_TERMINAL_VELOCITY = 30.0f;
|
||||
|
||||
extern GameBoundingBox GlobalCollisionBounds;
|
||||
extern ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS];
|
||||
extern MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS];
|
||||
|
||||
enum class ObjectCollectionMode
|
||||
{
|
||||
All,
|
||||
Items,
|
||||
Statics
|
||||
};
|
||||
|
||||
struct ObjectCollisionBounds
|
||||
{
|
||||
|
@ -22,8 +25,16 @@ struct ObjectCollisionBounds
|
|||
std::pair<EulerAngles, EulerAngles> OrientConstraint = {};
|
||||
};
|
||||
|
||||
struct CollidedObjectData
|
||||
{
|
||||
std::vector<ItemInfo*> ItemPtrs = {};
|
||||
std::vector<MESH_INFO*> StaticPtrs = {};
|
||||
|
||||
bool IsEmpty() const { return (ItemPtrs.empty() && StaticPtrs.empty()); };
|
||||
};
|
||||
|
||||
void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, ItemInfo** collidedItems, MESH_INFO** collidedMeshes, bool ignoreLara);
|
||||
CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible, bool ignorePlayer, float customRadius = 0.0f, ObjectCollectionMode mode = ObjectCollectionMode::All);
|
||||
bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll);
|
||||
|
||||
|
|
|
@ -229,42 +229,35 @@ static void HideOrDisablePickup(ItemInfo& pickupItem)
|
|||
|
||||
void CollectMultiplePickups(int itemNumber)
|
||||
{
|
||||
auto* firstItem = &g_Level.Items[itemNumber];
|
||||
GetCollidedObjects(firstItem, LARA_RADIUS, true, CollidedItems, CollidedMeshes, true);
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
auto& firstItem = g_Level.Items[itemNumber];
|
||||
|
||||
auto collObjects = GetCollidedObjects(firstItem, true, true, LARA_RADIUS, ObjectCollectionMode::Items);
|
||||
collObjects.ItemPtrs.push_back(&firstItem);
|
||||
for (auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
auto* currentItem = CollidedItems[i];
|
||||
|
||||
if (currentItem == nullptr)
|
||||
currentItem = firstItem;
|
||||
|
||||
if (!Objects[currentItem->ObjectNumber].isPickup)
|
||||
if (!Objects[itemPtr->ObjectNumber].isPickup)
|
||||
continue;
|
||||
|
||||
// HACK: Exclude flares and torches from pickup batches.
|
||||
if ((currentItem->ObjectNumber == ID_FLARE_ITEM && currentItem->Active) ||
|
||||
currentItem->ObjectNumber == ID_BURNING_TORCH_ITEM)
|
||||
if ((itemPtr->ObjectNumber == ID_FLARE_ITEM && itemPtr->Active) ||
|
||||
itemPtr->ObjectNumber == ID_BURNING_TORCH_ITEM)
|
||||
{
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
PickedUpObject(currentItem->ObjectNumber);
|
||||
g_Hud.PickupSummary.AddDisplayPickup(currentItem->ObjectNumber, currentItem->Pose.Position.ToVector3());
|
||||
PickedUpObject(itemPtr->ObjectNumber);
|
||||
g_Hud.PickupSummary.AddDisplayPickup(itemPtr->ObjectNumber, itemPtr->Pose.Position.ToVector3());
|
||||
|
||||
if (currentItem->TriggerFlags & (1 << 8))
|
||||
if (itemPtr->TriggerFlags & (1 << 8))
|
||||
{
|
||||
for (int i = 0; i < g_Level.NumItems; i++)
|
||||
{
|
||||
if (g_Level.Items[i].ObjectNumber == currentItem->ObjectNumber)
|
||||
if (g_Level.Items[i].ObjectNumber == itemPtr->ObjectNumber)
|
||||
KillItem(i);
|
||||
}
|
||||
}
|
||||
|
||||
HideOrDisablePickup(*currentItem);
|
||||
|
||||
if (currentItem == firstItem)
|
||||
break;
|
||||
HideOrDisablePickup(*itemPtr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -846,8 +839,7 @@ void DropPickups(ItemInfo* item)
|
|||
|
||||
origin.y = yPos; // Initialize drop origin Y point as floor height at centerpoint, in case all corner tests fail.
|
||||
|
||||
// Also collect objects which are around.
|
||||
bool collidedWithObjects = GetCollidedObjects(item, extents.Length(), true, CollidedItems, CollidedMeshes, true);
|
||||
auto collObjects = GetCollidedObjects(*item, true, true);
|
||||
|
||||
short startAngle = ANGLE(Random::GenerateInt(0, 3) * 90); // Randomize start corner.
|
||||
|
||||
|
@ -892,26 +884,22 @@ void DropPickups(ItemInfo* item)
|
|||
// Iterate through all found items and statics around, and determine if dummy sphere
|
||||
// intersects any of those. If so, try other corner.
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
for (const auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
auto* currentItem = CollidedItems[i];
|
||||
if (!currentItem)
|
||||
break;
|
||||
|
||||
if (GameBoundingBox(currentItem).ToBoundingOrientedBox(currentItem->Pose).Intersects(sphere))
|
||||
auto box = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose);
|
||||
if (box.Intersects(sphere))
|
||||
{
|
||||
collidedWithObject = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
for (auto* staticPtr : collObjects.StaticPtrs)
|
||||
{
|
||||
auto* currentMesh = CollidedMeshes[i];
|
||||
if (!currentMesh)
|
||||
break;
|
||||
auto& object = StaticObjects[staticPtr->staticNumber];
|
||||
|
||||
if (StaticObjects[currentMesh->staticNumber].collisionBox.ToBoundingOrientedBox(currentMesh->pos).Intersects(sphere))
|
||||
auto box = object.collisionBox.ToBoundingOrientedBox(staticPtr->pos);
|
||||
if (box.Intersects(sphere))
|
||||
{
|
||||
collidedWithObject = true;
|
||||
break;
|
||||
|
|
|
@ -384,4 +384,9 @@ namespace TEN::Math::Geometry
|
|||
|
||||
return (distSqr <= radiusSqr);
|
||||
}
|
||||
|
||||
bool CircleIntersects(const Vector3& circle0, const Vector3& circle1)
|
||||
{
|
||||
return (sqrt(SQUARE(circle1.x - circle0.x) + SQUARE(circle1.y - circle0.y)) <= (circle0.z + circle1.z));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,4 +54,7 @@ namespace TEN::Math::Geometry
|
|||
bool IsPointInBox(const Vector3& point, const BoundingBox& box);
|
||||
bool IsPointInBox(const Vector3& point, const BoundingOrientedBox& box);
|
||||
bool IsPointInSphere(const Vector3& point, const BoundingSphere& sphere);
|
||||
|
||||
// Intersection inquirers
|
||||
bool CircleIntersects(const Vector3& circle0, const Vector3& circle1);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include "framework.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "Math/Math.h"
|
||||
|
||||
namespace TEN::Math
|
||||
|
|
|
@ -62,24 +62,19 @@ namespace TEN::Entities::Effects
|
|||
|
||||
void BurnNearbyItems(ItemInfo* item, int radius)
|
||||
{
|
||||
GetCollidedObjects(item, radius, true, &CollidedItems[0], &CollidedMeshes[0], false);
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
auto collObjects = GetCollidedObjects(*item, true, false, radius, ObjectCollectionMode::Items);
|
||||
for (auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
auto* currentItem = CollidedItems[i];
|
||||
if (!currentItem)
|
||||
break;
|
||||
|
||||
if (TestEnvironment(ENV_FLAG_WATER, currentItem->RoomNumber))
|
||||
if (TestEnvironment(ENV_FLAG_WATER, itemPtr->RoomNumber))
|
||||
continue;
|
||||
|
||||
if ((!currentItem->IsCreature() && !currentItem->IsLara()) || currentItem->HitPoints <= 0)
|
||||
if ((!itemPtr->IsCreature() && !itemPtr->IsLara()) || itemPtr->HitPoints <= 0)
|
||||
continue;
|
||||
|
||||
if (currentItem->IsLara() && GetLaraInfo(item)->Control.WaterStatus == WaterStatus::FlyCheat)
|
||||
if (itemPtr->IsLara() && GetLaraInfo(item)->Control.WaterStatus == WaterStatus::FlyCheat)
|
||||
continue;
|
||||
|
||||
ItemBurn(currentItem, currentItem->IsLara() ? -1 : FLAME_ITEM_BURN_TIMEOUT);
|
||||
ItemBurn(itemPtr, itemPtr->IsLara() ? -1 : FLAME_ITEM_BURN_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,8 +142,6 @@ void ElectricityWiresControl(short itemNumber)
|
|||
|
||||
SoundEffect(SFX_TR5_ELECTRIC_WIRES, &item->Pose);
|
||||
|
||||
GetCollidedObjects(item, BLOCK(4), true, CollidedItems, nullptr, 0) && CollidedItems[0];
|
||||
|
||||
auto* object = &Objects[item->ObjectNumber];
|
||||
|
||||
auto cableBox = GameBoundingBox(item).ToBoundingOrientedBox(item->Pose);
|
||||
|
@ -179,21 +177,18 @@ void ElectricityWiresControl(short itemNumber)
|
|||
if (GetRandomControl() & 1)
|
||||
return;
|
||||
|
||||
int k = 0;
|
||||
while (CollidedItems[k] != nullptr)
|
||||
auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(2), ObjectCollectionMode::Items);
|
||||
for (auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
auto* collItem = CollidedItems[k];
|
||||
auto* collObj = &Objects[collItem->ObjectNumber];
|
||||
const auto& object = Objects[itemPtr->ObjectNumber];
|
||||
|
||||
k++;
|
||||
|
||||
if (collItem->ObjectNumber != ID_LARA && !collObj->intelligent)
|
||||
if (itemPtr->ObjectNumber != ID_LARA && !object.intelligent)
|
||||
continue;
|
||||
|
||||
bool isWaterNearby = false;
|
||||
auto npcBox = GameBoundingBox(collItem).ToBoundingOrientedBox(collItem->Pose);
|
||||
auto npcBox = GameBoundingBox(itemPtr).ToBoundingOrientedBox(itemPtr->Pose);
|
||||
|
||||
for (int i = 0; i < object->nmeshes; i++)
|
||||
for (int i = 0; i < object.nmeshes; i++)
|
||||
{
|
||||
auto pos = GetJointPosition(item, i, Vector3i(0, 0, CLICK(1)));
|
||||
short roomNumber = item->RoomNumber;
|
||||
|
@ -217,11 +212,11 @@ void ElectricityWiresControl(short itemNumber)
|
|||
if (pos.y < cableBottomPlane)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < collObj->nmeshes; j++)
|
||||
for (int j = 0; j < object.nmeshes; j++)
|
||||
{
|
||||
auto collPos = GetJointPosition(collItem, j);
|
||||
auto collPos = GetJointPosition(itemPtr, j);
|
||||
|
||||
auto collJointRoom = GetCollision(collPos.x, collPos.y, collPos.z, collItem->RoomNumber).RoomNumber;
|
||||
auto collJointRoom = GetCollision(collPos.x, collPos.y, collPos.z, itemPtr->RoomNumber).RoomNumber;
|
||||
|
||||
if (!isWaterNearby && isTouchingWater && roomNumber == collJointRoom)
|
||||
isWaterNearby = true;
|
||||
|
@ -233,29 +228,35 @@ void ElectricityWiresControl(short itemNumber)
|
|||
{
|
||||
if (!isWaterNearby)
|
||||
{
|
||||
if (collItem->Effect.Type != EffectType::Smoke)
|
||||
if (itemPtr->Effect.Type != EffectType::Smoke)
|
||||
{
|
||||
ItemBlueElectricBurn(collItem, 2 *FPS);
|
||||
ItemBlueElectricBurn(itemPtr, 2 *FPS);
|
||||
}
|
||||
else
|
||||
ItemSmoke(collItem, -1);
|
||||
{
|
||||
ItemSmoke(itemPtr, -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (instantKill)
|
||||
DoDamage(collItem, INT_MAX);
|
||||
{
|
||||
DoDamage(itemPtr, INT_MAX);
|
||||
}
|
||||
else
|
||||
DoDamage(collItem, 8);
|
||||
{
|
||||
DoDamage(itemPtr, 8);
|
||||
}
|
||||
|
||||
for (int j = 0; j < collObj->nmeshes; j++)
|
||||
for (int j = 0; j < object.nmeshes; j++)
|
||||
{
|
||||
if ((GetRandomControl() & 127) < 16)
|
||||
TriggerElectricitySparks(collItem, j, false);
|
||||
TriggerElectricitySparks(itemPtr, j, false);
|
||||
}
|
||||
|
||||
TriggerDynamicLight(
|
||||
collItem->Pose.Position.x,
|
||||
collItem->Pose.Position.y,
|
||||
collItem->Pose.Position.z,
|
||||
itemPtr->Pose.Position.x,
|
||||
itemPtr->Pose.Position.y,
|
||||
itemPtr->Pose.Position.z,
|
||||
5,
|
||||
0,
|
||||
(GetRandomControl() & 0x3F) + 0x2F,
|
||||
|
|
|
@ -116,28 +116,24 @@ namespace TEN::Entities::Generic
|
|||
// Test object collision.
|
||||
auto prevPos = pushableItem.Pose.Position;
|
||||
pushableItem.Pose.Position = targetPos;
|
||||
GetCollidedObjects(&pushableItem, BLOCK(0.25f), true, &CollidedItems[0], &CollidedMeshes[0], true);
|
||||
auto collObjects = GetCollidedObjects(pushableItem, true, true);
|
||||
pushableItem.Pose.Position = prevPos;
|
||||
|
||||
if (CollidedMeshes[0])
|
||||
if (!collObjects.StaticPtrs.empty())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
for (const auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
if (CollidedItems[i] == nullptr)
|
||||
break;
|
||||
|
||||
const auto& item = *CollidedItems[i];
|
||||
const auto& object = Objects[item.ObjectNumber];
|
||||
const auto& object = Objects[itemPtr->ObjectNumber];
|
||||
|
||||
if (object.isPickup)
|
||||
continue;
|
||||
|
||||
if (!item.IsBridge())
|
||||
if (!itemPtr->IsBridge())
|
||||
return false;
|
||||
|
||||
const auto& bridge = GetBridgeObject(item);
|
||||
if (!bridge.GetFloorHeight(item, item.Pose.Position).has_value())
|
||||
const auto& bridge = GetBridgeObject(*itemPtr);
|
||||
if (!bridge.GetFloorHeight(*itemPtr, itemPtr->Pose.Position).has_value())
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -191,34 +187,30 @@ namespace TEN::Entities::Generic
|
|||
// Collide with objects.
|
||||
auto prevPos = LaraItem->Pose.Position;
|
||||
LaraItem->Pose.Position = pointColl.Coordinates;
|
||||
GetCollidedObjects(LaraItem, LARA_RADIUS, true, &CollidedItems[0], &CollidedMeshes[0], true);
|
||||
auto collObjects = GetCollidedObjects(*LaraItem, true, true);
|
||||
LaraItem->Pose.Position = prevPos;
|
||||
|
||||
if (CollidedMeshes[0])
|
||||
if (!collObjects.StaticPtrs.empty())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||
for (const auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
if (CollidedItems[i] == nullptr)
|
||||
break;
|
||||
const auto& object = Objects[itemPtr->ObjectNumber];
|
||||
|
||||
const auto& item = *CollidedItems[i];
|
||||
const auto& object = Objects[item.ObjectNumber];
|
||||
|
||||
if (&item == &pushableItem)
|
||||
if (itemPtr->Index == pushableItem.Index)
|
||||
continue;
|
||||
|
||||
if (object.isPickup)
|
||||
continue;
|
||||
|
||||
if (!item.IsBridge())
|
||||
if (!itemPtr->IsBridge())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& bridge = GetBridgeObject(item);
|
||||
if (!bridge.GetFloorHeight(item, item.Pose.Position).has_value())
|
||||
const auto& bridge = GetBridgeObject(*itemPtr);
|
||||
if (!bridge.GetFloorHeight(*itemPtr, itemPtr->Pose.Position).has_value())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,14 +231,13 @@ namespace TEN::Entities::Generic
|
|||
item->Pose.Orientation.z = 0;
|
||||
}
|
||||
|
||||
auto velocity = Vector3i(
|
||||
auto vel = Vector3i(
|
||||
item->Animation.Velocity.z * phd_sin(item->Pose.Orientation.y),
|
||||
item->Animation.Velocity.y,
|
||||
item->Animation.Velocity.z * phd_cos(item->Pose.Orientation.y)
|
||||
);
|
||||
item->Animation.Velocity.z * phd_cos(item->Pose.Orientation.y));
|
||||
|
||||
auto prevPos = item->Pose.Position;
|
||||
item->Pose.Position += Vector3i(velocity.x, 0, velocity.z);
|
||||
item->Pose.Position += Vector3i(vel.x, 0, vel.z);
|
||||
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item) ||
|
||||
TestEnvironment(ENV_FLAG_SWAMP, item))
|
||||
|
@ -250,26 +249,31 @@ namespace TEN::Entities::Generic
|
|||
item->ItemFlags[3] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Animation.Velocity.y += 6;
|
||||
}
|
||||
|
||||
item->Pose.Position.y += item->Animation.Velocity.y;
|
||||
DoProjectileDynamics(itemNumber, prevPos.x, prevPos.y, prevPos.z, velocity.x, velocity.y, velocity.z);
|
||||
DoProjectileDynamics(itemNumber, prevPos.x, prevPos.y, prevPos.z, vel.x, vel.y, vel.z);
|
||||
|
||||
// Collide with entities.
|
||||
if (GetCollidedObjects(item, 0, true, CollidedItems, CollidedMeshes, true))
|
||||
auto collObjects = GetCollidedObjects(*item, true, true);
|
||||
if (!collObjects.IsEmpty())
|
||||
{
|
||||
LaraCollision.Setup.EnableObjectPush = true;
|
||||
if (CollidedItems[0])
|
||||
if (!collObjects.ItemPtrs.empty())
|
||||
{
|
||||
if (!Objects[CollidedItems[0]->ObjectNumber].intelligent &&
|
||||
CollidedItems[0]->ObjectNumber != ID_LARA)
|
||||
const auto& object = Objects[collObjects.ItemPtrs.front()->ObjectNumber];
|
||||
|
||||
if (!object.intelligent &&
|
||||
!collObjects.ItemPtrs.front()->IsLara())
|
||||
{
|
||||
ObjectCollision(CollidedItems[0]->Index, item, &LaraCollision);
|
||||
ObjectCollision(collObjects.ItemPtrs.front()->Index, item, &LaraCollision);
|
||||
}
|
||||
}
|
||||
else if (CollidedMeshes[0])
|
||||
else if (!collObjects.StaticPtrs.empty())
|
||||
{
|
||||
ItemPushStatic(item, *CollidedMeshes[0], &LaraCollision);
|
||||
ItemPushStatic(item, *collObjects.StaticPtrs.front(), &LaraCollision);
|
||||
}
|
||||
|
||||
item->Animation.Velocity.z = -int(item->Animation.Velocity.z / 1.5f);
|
||||
|
|
|
@ -112,22 +112,25 @@ namespace TEN::Entities::Creatures::TR1
|
|||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
|
||||
if (item.ItemFlags[0] == NO_VALUE)
|
||||
{
|
||||
TENLog("Failed to do the skateboard kid control (itemNumber: " + std::to_string(itemNumber) + "), the skateboard itemNumber is missing, probably failed to be created !");
|
||||
return;
|
||||
}
|
||||
|
||||
auto& creature = *GetCreatureInfo(&item);
|
||||
auto& skateItem = g_Level.Items[item.ItemFlags[0]];
|
||||
short headingAngle = 0;
|
||||
auto extraHeadRot = EulerAngles::Identity;
|
||||
auto extraTorsoRot = EulerAngles::Identity;
|
||||
|
||||
if (skateItem.Status & ITEM_INVISIBLE)
|
||||
if (skateItem.Status == ITEM_INVISIBLE)
|
||||
{
|
||||
skateItem.Active = true;
|
||||
skateItem.Status &= ~(ITEM_INVISIBLE);
|
||||
skateItem.Status = ITEM_ACTIVE;
|
||||
}
|
||||
|
||||
for (auto& flash : creature.MuzzleFlash)
|
||||
|
|
|
@ -107,18 +107,18 @@ namespace TEN::Entities::Traps
|
|||
break;
|
||||
}
|
||||
|
||||
if (GetCollidedObjects(&item, CLICK(1), true, CollidedItems, CollidedMeshes, true))
|
||||
auto collObjects = GetCollidedObjects(item, true, true);
|
||||
if (!collObjects.IsEmpty())
|
||||
{
|
||||
int lp = 0;
|
||||
while (CollidedItems[lp] != nullptr)
|
||||
for (auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
if (Objects[CollidedItems[lp]->ObjectNumber].intelligent)
|
||||
{
|
||||
CollidedItems[lp]->HitPoints = 0;
|
||||
ItemElectricBurn(CollidedItems[lp], 120);
|
||||
}
|
||||
const auto& object = Objects[itemPtr->ObjectNumber];
|
||||
|
||||
lp++;
|
||||
if (object.intelligent)
|
||||
{
|
||||
itemPtr->HitPoints = 0;
|
||||
ItemElectricBurn(itemPtr, 120);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1008,7 +1008,9 @@ namespace TEN::Entities::Vehicles
|
|||
auto* item = &g_Level.Items[itemNum];
|
||||
short nextItem = item->NextItem;
|
||||
|
||||
if (item->Collidable && item->Status != ITEM_INVISIBLE)
|
||||
if (item->Collidable &&
|
||||
item->Status != ITEM_INVISIBLE &&
|
||||
item != laraItem && item != kayakItem)
|
||||
{
|
||||
auto* object = &Objects[item->ObjectNumber];
|
||||
|
||||
|
|
|
@ -50,18 +50,20 @@ namespace TEN::Entities::Traps
|
|||
auto pointColl0 = GetCollision(&item, item.Pose.Orientation.y, (forwardVel >= 0) ? bounds.Z2 : bounds.Z1, bounds.Y2);
|
||||
auto pointColl1 = GetCollision(&item, item.Pose.Orientation.y, (forwardVel >= 0) ? bounds.Z2 : bounds.Z1, bounds.Y2, (bounds.X2 - bounds.X1) / 2);
|
||||
|
||||
if (GetCollidedObjects(&item, CLICK(1), true, CollidedItems, CollidedMeshes, true))
|
||||
auto collObjects = GetCollidedObjects(item, true, true);
|
||||
if (!collObjects.IsEmpty())
|
||||
{
|
||||
int collidedItemNumber = 0;
|
||||
while (CollidedItems[collidedItemNumber] != nullptr)
|
||||
for (auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
if (Objects[CollidedItems[collidedItemNumber]->ObjectNumber].intelligent)
|
||||
const auto& object = Objects[itemPtr->ObjectNumber];
|
||||
|
||||
if (object.intelligent)
|
||||
{
|
||||
CollidedItems[collidedItemNumber]->HitPoints = 0;
|
||||
itemPtr->HitPoints = 0;
|
||||
}
|
||||
else if (CollidedItems[collidedItemNumber]->ObjectNumber == ID_SPIKY_WALL && !item.ItemFlags[1])
|
||||
else if (itemPtr->ObjectNumber == ID_SPIKY_WALL && !item.ItemFlags[1])
|
||||
{
|
||||
CollidedItems[collidedItemNumber]->TriggerFlags = 0;
|
||||
itemPtr->TriggerFlags = 0;
|
||||
|
||||
item.TriggerFlags = 0;
|
||||
item.Status = ITEM_DEACTIVATED;
|
||||
|
@ -69,8 +71,6 @@ namespace TEN::Entities::Traps
|
|||
|
||||
StopSoundEffect(SFX_TR4_ROLLING_BALL);
|
||||
}
|
||||
|
||||
collidedItemNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ void SmashObject(short itemNumber)
|
|||
|
||||
SoundEffect(SFX_TR5_SMASH_GLASS, &item->Pose);
|
||||
|
||||
item->Collidable = 0;
|
||||
item->Collidable = false;
|
||||
item->MeshBits = 0xFFFE;
|
||||
|
||||
ExplodingDeath(itemNumber, BODY_DO_EXPLOSION | BODY_NO_BOUNCE);
|
||||
|
|
|
@ -129,49 +129,43 @@ void ExplosionControl(short itemNumber)
|
|||
}
|
||||
}
|
||||
|
||||
GetCollidedObjects(item, 2048, true, CollidedItems, CollidedMeshes, 1);
|
||||
if (CollidedItems[0] || CollidedMeshes[0])
|
||||
auto collObjects = GetCollidedObjects(*item, true, true, BLOCK(2), ObjectCollectionMode::All);
|
||||
if (!collObjects.IsEmpty())
|
||||
{
|
||||
int i = 0;
|
||||
while (CollidedItems[i])
|
||||
for (auto* itemPtr : collObjects.ItemPtrs)
|
||||
{
|
||||
if (CollidedItems[i]->ObjectNumber >= ID_SMASH_OBJECT1 && CollidedItems[i]->ObjectNumber <= ID_SMASH_OBJECT16)
|
||||
if (itemPtr->ObjectNumber >= ID_SMASH_OBJECT1 && itemPtr->ObjectNumber <= ID_SMASH_OBJECT16)
|
||||
{
|
||||
TriggerExplosionSparks(CollidedItems[i]->Pose.Position.x, CollidedItems[i]->Pose.Position.y, CollidedItems[i]->Pose.Position.z, 3, -2, 0, CollidedItems[i]->RoomNumber);
|
||||
CollidedItems[i]->Pose.Position.y -= 128;
|
||||
TriggerShockwave(&CollidedItems[i]->Pose, 48, 304, 96, 128, 96, 0, 24, EulerAngles::Identity, 0, true, false, false, (int)ShockwaveStyle::Normal);
|
||||
CollidedItems[i]->Pose.Position.y += 128;
|
||||
ExplodeItemNode(CollidedItems[i], 0, 0, 80);
|
||||
SmashObject(CollidedItems[i]->Index);
|
||||
KillItem(CollidedItems[i]->Index);
|
||||
TriggerExplosionSparks(itemPtr->Pose.Position.x, itemPtr->Pose.Position.y, itemPtr->Pose.Position.z, 3, -2, 0, itemPtr->RoomNumber);
|
||||
itemPtr->Pose.Position.y -= 128;
|
||||
TriggerShockwave(&itemPtr->Pose, 48, 304, 96, 128, 96, 0, 24, EulerAngles::Identity, 0, true, false, false, (int)ShockwaveStyle::Normal);
|
||||
itemPtr->Pose.Position.y += 128;
|
||||
ExplodeItemNode(itemPtr, 0, 0, 80);
|
||||
SmashObject(itemPtr->Index);
|
||||
KillItem(itemPtr->Index);
|
||||
}
|
||||
else if (CollidedItems[i]->ObjectNumber != ID_SWITCH_TYPE7 && CollidedItems[i]->ObjectNumber != ID_SWITCH_TYPE8)
|
||||
else if (itemPtr->ObjectNumber != ID_SWITCH_TYPE7 && itemPtr->ObjectNumber != ID_SWITCH_TYPE8)
|
||||
{
|
||||
if (Objects[CollidedItems[i]->ObjectNumber].intelligent)
|
||||
DoExplosiveDamage(*LaraItem, *CollidedItems[i], *item, Weapons[(int)LaraWeaponType::GrenadeLauncher].ExplosiveDamage);
|
||||
if (Objects[itemPtr->ObjectNumber].intelligent)
|
||||
DoExplosiveDamage(*LaraItem, *itemPtr, *item, Weapons[(int)LaraWeaponType::GrenadeLauncher].ExplosiveDamage);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* @FIXME This calls CrossbowHitSwitchType78() */
|
||||
// @FIXME: This calls CrossbowHitSwitchType78()
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (CollidedMeshes[i])
|
||||
for (auto* staticPtr : collObjects.StaticPtrs)
|
||||
{
|
||||
if (StaticObjects[CollidedMeshes[i]->staticNumber].shatterType != ShatterType::None)
|
||||
if (StaticObjects[staticPtr->staticNumber].shatterType != ShatterType::None)
|
||||
{
|
||||
TriggerExplosionSparks(CollidedMeshes[i]->pos.Position.x, CollidedMeshes[i]->pos.Position.y, CollidedMeshes[i]->pos.Position.z, 3, -2, 0, item->RoomNumber);
|
||||
CollidedMeshes[i]->pos.Position.y -= 128;
|
||||
TriggerShockwave(&CollidedMeshes[i]->pos, 40, 176, 64, 128, 96, 0, 16, EulerAngles::Identity, 0, true, false, false, (int)ShockwaveStyle::Normal);
|
||||
CollidedMeshes[i]->pos.Position.y += 128;
|
||||
SoundEffect(GetShatterSound(CollidedMeshes[i]->staticNumber), &CollidedMeshes[i]->pos);
|
||||
ShatterObject(NULL, CollidedMeshes[i], -128, item->RoomNumber, 0);
|
||||
TriggerExplosionSparks(staticPtr->pos.Position.x, staticPtr->pos.Position.y, staticPtr->pos.Position.z, 3, -2, 0, item->RoomNumber);
|
||||
staticPtr->pos.Position.y -= 128;
|
||||
TriggerShockwave(&staticPtr->pos, 40, 176, 64, 128, 96, 0, 16, EulerAngles::Identity, 0, true, false, false, (int)ShockwaveStyle::Normal);
|
||||
staticPtr->pos.Position.y += 128;
|
||||
SoundEffect(GetShatterSound(staticPtr->staticNumber), &staticPtr->pos);
|
||||
ShatterObject(nullptr, staticPtr, -128, item->RoomNumber, 0);
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
AlertNearbyGuards(item);
|
||||
|
|
|
@ -165,62 +165,42 @@ void Moveable::Register(sol::state& state, sol::table& parent)
|
|||
|
||||
ScriptReserved_SetVisible, &Moveable::SetVisible,
|
||||
|
||||
/// Explode item. This also kills and disables item.
|
||||
// @function Moveable:Explode
|
||||
/// Explode item. This also kills and disables item.
|
||||
// @function Moveable:Explode
|
||||
ScriptReserved_Explode, &Moveable::Explode,
|
||||
|
||||
/// Shatter item. This also kills and disables item.
|
||||
// @function Moveable:Shatter
|
||||
/// Shatter item. This also kills and disables item.
|
||||
// @function Moveable:Shatter
|
||||
ScriptReserved_Shatter, &Moveable::Shatter,
|
||||
|
||||
/// 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).
|
||||
/// 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).
|
||||
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).
|
||||
/// 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).
|
||||
ScriptReserved_SetCustomEffect, &Moveable::SetCustomEffect,
|
||||
|
||||
/// Get current moveable effect
|
||||
// @function Moveable:GetEffect
|
||||
// @treturn Effects.EffectID effect type currently assigned to moveable.
|
||||
/// Get current moveable effect
|
||||
// @function Moveable:GetEffect
|
||||
// @treturn Effects.EffectID effect type currently assigned to moveable.
|
||||
ScriptReserved_GetEffect, &Moveable::GetEffect,
|
||||
|
||||
/// Get the moveable's status.
|
||||
// @function Moveable:GetStatus()
|
||||
// @treturn Objects.MoveableStatus The moveable's status.
|
||||
/// Get the moveable's status.
|
||||
// @function Moveable:GetStatus()
|
||||
// @treturn Objects.MoveableStatus The moveable's status.
|
||||
ScriptReserved_GetStatus, &Moveable::GetStatus,
|
||||
|
||||
/// Set the moveable's status.
|
||||
// @function Moveable:SetStatus()
|
||||
// @tparam Objects.MoveableStatus status The new status of the moveable.
|
||||
/// Set the moveable's status.
|
||||
// @function Moveable:SetStatus()
|
||||
// @tparam Objects.MoveableStatus status The new status of the moveable.
|
||||
ScriptReserved_SetStatus, &Moveable::SetStatus,
|
||||
|
||||
/// Set the name of the function to be called when the moveable is shot by Lara.
|
||||
// Note that this will be triggered twice when shot with both pistols at once.
|
||||
// @function Moveable:SetOnHit
|
||||
// @tparam function callback function in LevelFuncs hierarchy to call when moveable is shot
|
||||
ScriptReserved_SetOnHit, &Moveable::SetOnHit,
|
||||
|
||||
ScriptReserved_SetOnCollidedWithObject, &Moveable::SetOnCollidedWithObject,
|
||||
|
||||
ScriptReserved_SetOnCollidedWithRoom, &Moveable::SetOnCollidedWithRoom,
|
||||
|
||||
/// Set the name of the function to be called when the moveable is destroyed/killed
|
||||
// Note that enemy death often occurs at the end of an animation, and not at the exact moment
|
||||
// the enemy's HP becomes zero.
|
||||
// @function Moveable:SetOnKilled
|
||||
// @tparam function callback function in LevelFuncs hierarchy to call when enemy is killed
|
||||
// @usage
|
||||
// LevelFuncs.baddyKilled = function(theBaddy) print("You killed a baddy!") end
|
||||
// baddy:SetOnKilled(LevelFuncs.baddyKilled)
|
||||
ScriptReserved_SetOnKilled, &Moveable::SetOnKilled,
|
||||
|
||||
/// Retrieve the object ID
|
||||
// @function Moveable:GetObjectID
|
||||
// @treturn int a number representing the ID of the object
|
||||
|
@ -433,7 +413,43 @@ ScriptReserved_GetSlotHP, & Moveable::GetSlotHP,
|
|||
// @tparam Objects.ObjID ObjectID to take animation and stateID from,
|
||||
// @tparam int animNumber animation from object
|
||||
// @tparam int stateID state from object
|
||||
ScriptReserved_AnimFromObject, &Moveable::AnimFromObject);
|
||||
ScriptReserved_AnimFromObject, &Moveable::AnimFromObject,
|
||||
|
||||
/// Set the name of the function to be called when the moveable is shot by Lara.
|
||||
// Note that this will be triggered twice when shot with both pistols at once.
|
||||
// @function Moveable:SetOnHit
|
||||
// @tparam function callback function in LevelFuncs hierarchy to call when moveable is shot
|
||||
ScriptReserved_SetOnHit, &Moveable::SetOnHit,
|
||||
|
||||
/// Set the function to be called when this moveable collides with another moveable
|
||||
// @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
|
||||
// LevelFuncs.objCollided = function(obj1, obj2)
|
||||
// print(obj1:GetName() .. " collided with " .. obj2:GetName())
|
||||
// end
|
||||
// baddy:SetOnCollidedWithObject(LevelFuncs.objCollided)
|
||||
ScriptReserved_SetOnCollidedWithObject, &Moveable::SetOnCollidedWithObject,
|
||||
|
||||
/// Set the function called when this moveable collides with room geometry (e.g. a wall or floor). This function can take an argument that holds the @{Moveable} that collided with geometry.
|
||||
// @function Moveable:SetOnCollidedWithRoom
|
||||
// @tparam function func callback function to be called (must be in LevelFuncs hierarchy)
|
||||
// @usage
|
||||
// LevelFuncs.roomCollided = function(obj)
|
||||
// print(obj:GetName() .. " collided with room geometry")
|
||||
// end
|
||||
// baddy:SetOnCollidedWithRoom(LevelFuncs.roomCollided)
|
||||
ScriptReserved_SetOnCollidedWithRoom, &Moveable::SetOnCollidedWithRoom,
|
||||
|
||||
/// Set the name of the function to be called when the moveable is destroyed/killed
|
||||
// Note that enemy death often occurs at the end of an animation, and not at the exact moment
|
||||
// the enemy's HP becomes zero.
|
||||
// @function Moveable:SetOnKilled
|
||||
// @tparam function callback function in LevelFuncs hierarchy to call when enemy is killed
|
||||
// @usage
|
||||
// LevelFuncs.baddyKilled = function(theBaddy) print("You killed a baddy!") end
|
||||
// baddy:SetOnKilled(LevelFuncs.baddyKilled)
|
||||
ScriptReserved_SetOnKilled, &Moveable::SetOnKilled);
|
||||
}
|
||||
|
||||
void Moveable::Init()
|
||||
|
@ -502,27 +518,11 @@ void Moveable::SetOnKilled(const TypeOrNil<LevelFunc>& cb)
|
|||
SetLevelFuncCallback(cb, ScriptReserved_SetOnKilled, *this, m_item->Callbacks.OnKilled);
|
||||
}
|
||||
|
||||
/// Set the function to be called when this moveable collides with another moveable
|
||||
// @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
|
||||
// LevelFuncs.objCollided = function(obj1, obj2)
|
||||
// print(obj1:GetName() .. " collided with " .. obj2:GetName())
|
||||
// end
|
||||
// baddy:SetOnCollidedWithObject(LevelFuncs.objCollided)
|
||||
void Moveable::SetOnCollidedWithObject(const TypeOrNil<LevelFunc>& cb)
|
||||
{
|
||||
SetLevelFuncCallback(cb, ScriptReserved_SetOnCollidedWithObject, *this, m_item->Callbacks.OnObjectCollided);
|
||||
}
|
||||
|
||||
/// Set the function called when this moveable collides with room geometry (e.g. a wall or floor). This function can take an argument that holds the @{Moveable} that collided with geometry.
|
||||
// @function Moveable:SetOnCollidedWithRoom
|
||||
// @tparam function func callback function to be called (must be in LevelFuncs hierarchy)
|
||||
// @usage
|
||||
// LevelFuncs.roomCollided = function(obj)
|
||||
// print(obj:GetName() .. " collided with room geometry")
|
||||
// end
|
||||
// baddy:SetOnCollidedWithRoom(LevelFuncs.roomCollided)
|
||||
void Moveable::SetOnCollidedWithRoom(const TypeOrNil<LevelFunc>& cb)
|
||||
{
|
||||
SetLevelFuncCallback(cb, ScriptReserved_SetOnCollidedWithRoom, *this, m_item->Callbacks.OnRoomCollided);
|
||||
|
|
|
@ -172,32 +172,27 @@ ObjectsHandler::ObjectsHandler(sol::state* lua, sol::table& parent) :
|
|||
|
||||
void ObjectsHandler::TestCollidingObjects()
|
||||
{
|
||||
// Remove any items which can't collide.
|
||||
for (const auto id : m_collidingItemsToRemove)
|
||||
m_collidingItems.erase(id);
|
||||
// Remove items which can't collide.
|
||||
for (int itemNumber : m_collidingItemsToRemove)
|
||||
m_collidingItems.erase(itemNumber);
|
||||
m_collidingItemsToRemove.clear();
|
||||
|
||||
for (const auto idOne : m_collidingItems)
|
||||
for (int itemNumber0 : m_collidingItems)
|
||||
{
|
||||
auto item = &g_Level.Items[idOne];
|
||||
if (!item->Callbacks.OnObjectCollided.empty())
|
||||
auto& item = g_Level.Items[itemNumber0];
|
||||
if (!item.Callbacks.OnObjectCollided.empty())
|
||||
{
|
||||
// Test against other moveables.
|
||||
GetCollidedObjects(item, 0, true, CollidedItems, nullptr, false);
|
||||
size_t i = 0;
|
||||
while (CollidedItems[i])
|
||||
{
|
||||
short idTwo = CollidedItems[i] - &g_Level.Items[0];
|
||||
g_GameScript->ExecuteFunction(item->Callbacks.OnObjectCollided, idOne, idTwo);
|
||||
++i;
|
||||
}
|
||||
auto collObjects = GetCollidedObjects(item, true, false);
|
||||
for (const auto& collidedItemPtr : collObjects.ItemPtrs)
|
||||
g_GameScript->ExecuteFunction(item.Callbacks.OnObjectCollided, itemNumber0, collidedItemPtr->Index);
|
||||
}
|
||||
|
||||
if (!item->Callbacks.OnRoomCollided.empty())
|
||||
if (!item.Callbacks.OnRoomCollided.empty())
|
||||
{
|
||||
// Test against room geometry.
|
||||
if (TestItemRoomCollisionAABB(item))
|
||||
g_GameScript->ExecuteFunction(item->Callbacks.OnRoomCollided, idOne);
|
||||
if (TestItemRoomCollisionAABB(&item))
|
||||
g_GameScript->ExecuteFunction(item.Callbacks.OnRoomCollided, itemNumber0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,10 +205,10 @@ void ObjectsHandler::AssignLara()
|
|||
bool ObjectsHandler::NotifyKilled(ItemInfo* key)
|
||||
{
|
||||
auto it = moveables.find(key);
|
||||
if (std::end(moveables) != it)
|
||||
if (it != std::end(moveables))
|
||||
{
|
||||
for (auto& m : moveables[key])
|
||||
m->Invalidate();
|
||||
for (auto* movPtr : moveables[key])
|
||||
movPtr->Invalidate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue