Merge branch 'MontyTRC89:develop' into develop

This commit is contained in:
davidmarr 2024-10-25 14:44:24 +02:00 committed by GitHub
commit c7c3451c1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 108 additions and 83 deletions

View file

@ -55,6 +55,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* 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.
* 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 )

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);
@ -895,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);
@ -1043,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;
@ -1052,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
{
@ -1557,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;
}
@ -1786,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))
@ -1909,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
{
@ -1949,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)
@ -1964,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)
@ -1979,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)
@ -1994,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);
}
}
@ -2010,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)
@ -2025,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)
@ -2040,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)
@ -2055,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);
}
}
}
@ -2069,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);
@ -2081,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);
@ -2098,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);
@ -2106,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);
@ -2184,6 +2182,7 @@ bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType)
return false;
float stepDist = BLOCK(0.92f);
int vPos = item.Pose.Position.y;
auto pointCollA = GetPointCollision(item, item.Pose.Orientation.y, stepDist);
auto pointCollB = GetPointCollision(item, item.Pose.Orientation.y, stepDist * 2);
@ -2196,7 +2195,9 @@ bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType)
if (item.BoxNumber == creature.Enemy->BoxNumber ||
vPos >= (pointCollA.GetFloorHeight() - STEPUP_HEIGHT) ||
vPos >= (pointCollB.GetFloorHeight() + CLICK(1)) ||
vPos <= (pointCollB.GetFloorHeight() - CLICK(1)))
vPos <= (pointCollB.GetFloorHeight() - CLICK(1)) ||
pointCollA.GetSector().PathfindingBoxID == NO_VALUE ||
pointCollB.GetSector().PathfindingBoxID == NO_VALUE)
{
return false;
}
@ -2208,7 +2209,10 @@ bool CanCreatureJump(ItemInfo& item, JumpDistance jumpDistType)
vPos >= (pointCollA.GetFloorHeight() - STEPUP_HEIGHT) ||
vPos >= (pointCollB.GetFloorHeight() - STEPUP_HEIGHT) ||
vPos >= (pointCollC.GetFloorHeight() + CLICK(1)) ||
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;
}

View file

@ -55,29 +55,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 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_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 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;
void GetCreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent);
void CreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent);

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,7 +339,7 @@ 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;

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

@ -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

@ -123,7 +123,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

@ -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

@ -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

@ -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,