TombEngine/TR5Main/Game/camera.cpp

1974 lines
50 KiB
C++
Raw Normal View History

2020-12-21 13:16:29 -03:00
#include "framework.h"
2021-12-22 16:23:57 +03:00
#include "Game/camera.h"
2021-12-24 03:32:19 +03:00
2021-12-22 16:23:57 +03:00
#include "Game/animation.h"
2021-12-24 03:32:19 +03:00
#include "Game/collision/collide_room.h"
#include "Game/control/los.h"
2021-12-22 16:23:57 +03:00
#include "Game/effects/effects.h"
#include "Game/effects/debris.h"
#include "Game/effects/weather.h"
2021-12-24 03:32:19 +03:00
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_fire.h"
2021-12-22 16:23:57 +03:00
#include "Game/room.h"
#include "Game/savegame.h"
#include "Objects/Generic/Object/burning_torch.h"
2021-12-24 03:32:19 +03:00
#include "Sound/sound.h"
#include "Specific/input.h"
#include "Specific/level.h"
#include "Specific/setup.h"
2021-09-25 11:27:47 +02:00
2021-08-30 18:03:21 +03:00
using TEN::Renderer::g_Renderer;
2021-11-08 19:18:33 +03:00
using namespace TEN::Entities::Generic;
using namespace TEN::Effects::Environment;
constexpr auto COLL_CHECK_THRESHOLD = SECTOR(4);
constexpr auto COLL_CANCEL_THRESHOLD = SECTOR(2);
2021-12-14 16:20:27 +03:00
constexpr auto COLL_DISCARD_THRESHOLD = CLICK(0.5f);
2021-12-14 16:53:43 +03:00
constexpr auto CAMERA_RADIUS = CLICK(1);
2021-12-14 15:37:05 +03:00
2020-12-21 13:16:29 -03:00
struct OLD_CAMERA
{
short currentAnimState;
short goalAnimState;
int targetDistance;
short actualElevation;
short targetElevation;
short actualAngle;
PHD_3DPOS pos;
PHD_3DPOS pos2;
PHD_VECTOR target;
};
float LfAspectCorrection;
GAME_VECTOR LastTarget;
byte SniperCamActive;
extern int KeyTriggerActive;
PHD_VECTOR CurrentCameraPosition;
SVECTOR CurrentCameraRotation;
GAME_VECTOR LastIdeal;
GAME_VECTOR Ideals[5];
OLD_CAMERA OldCam;
int CameraSnaps = 0;
int TargetSnaps = 0;
GAME_VECTOR LookCamPosition;
GAME_VECTOR LookCamTarget;
int LSHKTimer = 0;
int LSHKShotsFired = 0;
PHD_VECTOR CamOldPos;
CAMERA_INFO Camera;
GAME_VECTOR ForcedFixedCamera;
int UseForcedFixedCamera;
int NumberCameras;
int BinocularRange;
int BinocularOn;
CAMERA_TYPE BinocularOldCamera;
2021-11-08 19:35:17 +03:00
bool LaserSight;
2020-12-21 13:16:29 -03:00
int PhdPerspective;
short CurrentFOV;
2021-10-22 16:33:15 +03:00
int RumbleTimer = 0;
int RumbleCounter = 0;
2020-12-21 13:16:29 -03:00
void LookAt(CAMERA_INFO* cam, short roll)
{
Vector3 position = Vector3(cam->pos.x, cam->pos.y, cam->pos.z);
Vector3 target = Vector3(cam->target.x, cam->target.y, cam->target.z);
Vector3 up = Vector3(0.0f, -1.0f, 0.0f);
float fov = TO_RAD(CurrentFOV / 1.333333f);
float r = 0; TO_RAD(roll);
g_Renderer.UpdateCameraMatrices(cam, r, fov);
}
void AlterFOV(int value)
{
CurrentFOV = value;
PhdPerspective = g_Renderer.ScreenWidth / 2 * phd_cos(CurrentFOV / 2) / phd_sin(CurrentFOV / 2);
}
void InitialiseCamera()
{
2021-11-07 14:58:02 +03:00
Camera.shift = LaraItem->pos.yPos - WALL_SIZE;
2020-12-21 13:16:29 -03:00
LastTarget.x = LaraItem->pos.xPos;
LastTarget.y = Camera.shift;
LastTarget.z = LaraItem->pos.zPos;
LastTarget.roomNumber = LaraItem->roomNumber;
Camera.target.x = LastTarget.x;
Camera.target.y = Camera.shift;
Camera.target.z = LastTarget.z;
Camera.target.roomNumber = LaraItem->roomNumber;
Camera.pos.x = LastTarget.x;
Camera.pos.y = Camera.shift;
Camera.pos.z = LastTarget.z - 100;
Camera.pos.roomNumber = LaraItem->roomNumber;
2021-11-10 22:48:00 +11:00
Camera.targetDistance = WALL_SIZE + STEP_SIZE * 2;
2020-12-21 13:16:29 -03:00
Camera.item = NULL;
Camera.numberFrames = 1;
2021-09-25 16:03:28 -05:00
Camera.type = CAMERA_TYPE::CHASE_CAMERA;
2020-12-21 13:16:29 -03:00
Camera.speed = 1;
Camera.flags = CF_FOLLOW_CENTER;
Camera.bounce = 0;
Camera.number = -1;
2021-11-08 19:28:14 +03:00
Camera.fixedCamera = false;
2020-12-21 13:16:29 -03:00
AlterFOV(14560);
UseForcedFixedCamera = 0;
CalculateCamera();
}
void MoveCamera(GAME_VECTOR* ideal, int speed)
{
GAME_VECTOR from, to;
if (BinocularOn < 0)
{
speed = 1;
BinocularOn++;
}
2021-11-10 11:26:58 +11:00
if (OldCam.pos.xRot != LaraItem->pos.xRot ||
OldCam.pos.yRot != LaraItem->pos.yRot ||
OldCam.pos.zRot != LaraItem->pos.zRot ||
OldCam.pos2.xRot != Lara.headXrot ||
OldCam.pos2.yRot != Lara.headYrot ||
OldCam.pos2.xPos != Lara.torsoXrot ||
OldCam.pos2.yPos != Lara.torsoYrot ||
OldCam.pos.xPos != LaraItem->pos.xPos ||
OldCam.pos.yPos != LaraItem->pos.yPos ||
OldCam.pos.zPos != LaraItem->pos.zPos ||
OldCam.currentAnimState != LaraItem->currentAnimState ||
OldCam.goalAnimState != LaraItem->goalAnimState ||
OldCam.targetDistance != Camera.targetDistance ||
OldCam.targetElevation != Camera.targetElevation ||
OldCam.actualElevation != Camera.actualElevation ||
OldCam.actualAngle != Camera.actualAngle ||
OldCam.target.x != Camera.target.x ||
OldCam.target.y != Camera.target.y ||
OldCam.target.z != Camera.target.z ||
Camera.oldType != Camera.type ||
BinocularOn < 0)
2020-12-21 13:16:29 -03:00
{
OldCam.pos.xRot = LaraItem->pos.xRot;
OldCam.pos.yRot = LaraItem->pos.yRot;
OldCam.pos.zRot = LaraItem->pos.zRot;
OldCam.pos2.xRot = Lara.headXrot;
OldCam.pos2.yRot = Lara.headYrot;
OldCam.pos2.xPos = Lara.torsoXrot;
OldCam.pos2.yPos = Lara.torsoYrot;
OldCam.pos.xPos = LaraItem->pos.xPos;
OldCam.pos.yPos = LaraItem->pos.yPos;
OldCam.pos.zPos = LaraItem->pos.zPos;
OldCam.currentAnimState = LaraItem->currentAnimState;
OldCam.goalAnimState = LaraItem->goalAnimState;
OldCam.targetDistance = Camera.targetDistance;
OldCam.targetElevation = Camera.targetElevation;
OldCam.actualElevation = Camera.actualElevation;
OldCam.actualAngle = Camera.actualAngle;
OldCam.target.x = Camera.target.x;
OldCam.target.y = Camera.target.y;
OldCam.target.z = Camera.target.z;
LastIdeal.x = ideal->x;
LastIdeal.y = ideal->y;
LastIdeal.z = ideal->z;
LastIdeal.roomNumber = ideal->roomNumber;
}
else
{
ideal->x = LastIdeal.x;
ideal->y = LastIdeal.y;
ideal->z = LastIdeal.z;
ideal->roomNumber = LastIdeal.roomNumber;
}
Camera.pos.x += (ideal->x - Camera.pos.x) / speed;
Camera.pos.y += (ideal->y - Camera.pos.y) / speed;
Camera.pos.z += (ideal->z - Camera.pos.z) / speed;
Camera.pos.roomNumber = ideal->roomNumber;
if (Camera.bounce)
{
if (Camera.bounce <= 0)
{
int bounce = -Camera.bounce;
int bounce2 = -Camera.bounce >> 2;
2020-12-21 13:16:29 -03:00
Camera.target.x += GetRandomControl() % bounce - bounce2;
Camera.target.y += GetRandomControl() % bounce - bounce2;
Camera.target.z += GetRandomControl() % bounce - bounce2;
Camera.bounce += 5;
}
else
{
Camera.pos.y += Camera.bounce;
Camera.target.y += Camera.bounce;
Camera.bounce = 0;
}
}
2021-12-30 23:50:52 +11:00
int y = Camera.pos.y;
if (g_Level.Rooms[Camera.pos.roomNumber].flags & ENV_FLAG_SWAMP)
y = g_Level.Rooms[Camera.pos.roomNumber].y - STEP_SIZE;
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
auto probe = GetCollisionResult(Camera.pos.x, y, Camera.pos.z, Camera.pos.roomNumber);
if (y < probe.Position.Ceiling ||
y > probe.Position.Floor)
2020-12-21 13:16:29 -03:00
{
2021-12-01 18:12:04 +03:00
LOSAndReturnTarget(&Camera.target, &Camera.pos, 0);
2020-12-21 13:16:29 -03:00
2021-11-10 22:48:00 +11:00
if (abs(Camera.pos.x - ideal->x) < (WALL_SIZE - STEP_SIZE) &&
abs(Camera.pos.y - ideal->y) < (WALL_SIZE - STEP_SIZE) &&
abs(Camera.pos.z - ideal->z) < (WALL_SIZE - STEP_SIZE))
2020-12-21 13:16:29 -03:00
{
to.x = Camera.pos.x;
to.y = Camera.pos.y;
to.z = Camera.pos.z;
to.roomNumber = Camera.pos.roomNumber;
from.x = ideal->x;
from.y = ideal->y;
from.z = ideal->z;
from.roomNumber = ideal->roomNumber;
2021-12-01 18:12:04 +03:00
if (!LOSAndReturnTarget(&from, &to, 0) &&
2021-11-10 11:26:58 +11:00
++CameraSnaps >= 8)
2020-12-21 13:16:29 -03:00
{
Camera.pos.x = ideal->x;
Camera.pos.y = ideal->y;
Camera.pos.z = ideal->z;
Camera.pos.roomNumber = ideal->roomNumber;
CameraSnaps = 0;
}
}
}
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.roomNumber);
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
int buffer = CLICK(1) - 1;
if ((Camera.pos.y - buffer) < probe.Position.Ceiling &&
(Camera.pos.y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
Camera.pos.y = (probe.Position.Floor + probe.Position.Ceiling) / 2;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((Camera.pos.y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
Camera.pos.y = probe.Position.Floor - buffer;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((Camera.pos.y - buffer) < probe.Position.Ceiling &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
Camera.pos.y = probe.Position.Ceiling + buffer;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if (probe.Position.Ceiling >= probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT)
2020-12-21 13:16:29 -03:00
{
Camera.pos.x = ideal->x;
Camera.pos.y = ideal->y;
Camera.pos.z = ideal->z;
Camera.pos.roomNumber = ideal->roomNumber;
}
2021-09-08 16:11:19 -05:00
ItemsCollideCamera();
2021-12-30 23:50:52 +11:00
Camera.pos.roomNumber = GetCollisionResult(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.roomNumber).RoomNumber;
2020-12-21 13:16:29 -03:00
LookAt(&Camera, 0);
if (Camera.mikeAtLara)
{
Camera.mikePos.x = LaraItem->pos.xPos;
Camera.mikePos.y = LaraItem->pos.yPos;
Camera.mikePos.z = LaraItem->pos.zPos;
Camera.oldType = Camera.type;
}
else
{
short angle = phd_atan(Camera.target.z - Camera.pos.z, Camera.target.x - Camera.pos.x);
Camera.mikePos.x = Camera.pos.x + PhdPerspective * phd_sin(angle);
Camera.mikePos.y = Camera.pos.y;
Camera.mikePos.z = Camera.pos.z + PhdPerspective * phd_cos(angle);
Camera.oldType = Camera.type;
}
}
void ChaseCamera(ITEM_INFO* item)
{
if (!Camera.targetElevation)
2021-11-10 11:26:58 +11:00
Camera.targetElevation = -ANGLE(10.0f);
2020-12-21 13:16:29 -03:00
Camera.targetElevation += item->pos.xRot;
UpdateCameraElevation();
2021-12-30 23:50:52 +11:00
// Clamp x rotation.
2021-11-10 11:26:58 +11:00
if (Camera.actualElevation > ANGLE(85.0f))
Camera.actualElevation = ANGLE(85.0f);
else if (Camera.actualElevation < -ANGLE(85.0f))
Camera.actualElevation = -ANGLE(85.0f);
2020-12-21 13:16:29 -03:00
int distance = Camera.targetDistance * phd_cos(Camera.actualElevation);
2021-12-30 23:50:52 +11:00
auto probe = GetCollisionResult(Camera.target.x, Camera.target.y + STEP_SIZE, Camera.target.z, Camera.target.roomNumber);
2021-11-07 04:54:48 +03:00
2021-12-30 23:50:52 +11:00
if (g_Level.Rooms[probe.RoomNumber].flags & ENV_FLAG_SWAMP)
Camera.target.y = g_Level.Rooms[probe.RoomNumber].y - STEP_SIZE;
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
int y = Camera.target.y;
probe = GetCollisionResult(Camera.target.x, y, Camera.target.z, Camera.target.roomNumber);
if (((y < probe.Position.Ceiling || probe.Position.Floor < y) || probe.Position.Floor <= probe.Position.Ceiling) ||
(probe.Position.Floor == NO_HEIGHT || probe.Position.Ceiling == NO_HEIGHT))
2020-12-21 13:16:29 -03:00
{
TargetSnaps++;
Camera.target.x = LastTarget.x;
Camera.target.y = LastTarget.y;
Camera.target.z = LastTarget.z;
Camera.target.roomNumber = LastTarget.roomNumber;
}
else
TargetSnaps = 0;
for (int i = 0; i < 5; i++)
Ideals[i].y = Camera.target.y + Camera.targetDistance * phd_sin(Camera.actualElevation);
2021-12-30 23:50:52 +11:00
int farthest = INT_MAX;
2020-12-21 13:16:29 -03:00
int farthestnum = 0;
GAME_VECTOR temp[2];
for (int i = 0; i < 5; i++)
{
short angle;
if (i == 0)
angle = Camera.actualAngle;
else
2021-11-10 11:26:58 +11:00
angle = (i - 1) * ANGLE(90.0f);
2020-12-21 13:16:29 -03:00
Ideals[i].x = Camera.target.x - distance * phd_sin(angle);
Ideals[i].z = Camera.target.z - distance * phd_cos(angle);
Ideals[i].roomNumber = Camera.target.roomNumber;
2021-12-01 18:12:04 +03:00
if (LOSAndReturnTarget(&Camera.target, &Ideals[i], 200))
2020-12-21 13:16:29 -03:00
{
temp[0].x = Ideals[i].x;
temp[0].y = Ideals[i].y;
temp[0].z = Ideals[i].z;
temp[0].roomNumber = Ideals[i].roomNumber;
temp[1].x = Camera.pos.x;
temp[1].y = Camera.pos.y;
temp[1].z = Camera.pos.z;
temp[1].roomNumber = Camera.pos.roomNumber;
2021-12-01 18:12:04 +03:00
if (i == 0 || LOSAndReturnTarget(&temp[0], &temp[1], 0))
2020-12-21 13:16:29 -03:00
{
if (i == 0)
{
farthestnum = 0;
break;
}
int dx = (Camera.pos.x - Ideals[i].x) * (Camera.pos.x - Ideals[i].x);
dx += (Camera.pos.z - Ideals[i].z) * (Camera.pos.z - Ideals[i].z);
if (dx < farthest)
{
farthest = dx;
farthestnum = i;
}
}
}
else if (i == 0)
{
temp[0].x = Ideals[i].x;
temp[0].y = Ideals[i].y;
temp[0].z = Ideals[i].z;
temp[0].roomNumber = Ideals[i].roomNumber;
temp[1].x = Camera.pos.x;
temp[1].y = Camera.pos.y;
temp[1].z = Camera.pos.z;
temp[1].roomNumber = Camera.pos.roomNumber;
2021-12-01 18:12:04 +03:00
if (i == 0 || LOSAndReturnTarget(&temp[0], &temp[1], 0))
2020-12-21 13:16:29 -03:00
{
int dx = (Camera.target.x - Ideals[i].x) * (Camera.target.x - Ideals[i].x);
int dz = (Camera.target.z - Ideals[i].z) * (Camera.target.z - Ideals[i].z);
if ((dx + dz) > 0x90000)
{
farthestnum = 0;
break;
}
}
}
}
2021-11-14 20:10:15 +11:00
GAME_VECTOR ideal = { Ideals[farthestnum].x , Ideals[farthestnum].y, Ideals[farthestnum].z };
2020-12-21 13:16:29 -03:00
ideal.roomNumber = Ideals[farthestnum].roomNumber;
2021-11-10 11:26:58 +11:00
CameraCollisionBounds(&ideal, (STEP_SIZE + STEP_SIZE / 2), 1);
2020-12-21 13:16:29 -03:00
MoveCamera(&ideal, Camera.speed);
}
void UpdateCameraElevation()
{
if (Camera.laraNode != -1)
{
2021-11-14 20:10:15 +11:00
PHD_VECTOR pos = { 0, 0, 0 };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos, Camera.laraNode);
2021-11-14 20:10:15 +11:00
PHD_VECTOR pos1 = { 0, -STEP_SIZE, WALL_SIZE * 2 };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos1, Camera.laraNode);
pos.z = pos1.z - pos.z;
pos.x = pos1.x - pos.x;
Camera.actualAngle = Camera.targetAngle + phd_atan(pos.z, pos.x);
}
else
Camera.actualAngle = LaraItem->pos.yRot + Camera.targetAngle;
2021-12-30 23:50:52 +11:00
Camera.actualElevation += (Camera.targetElevation - Camera.actualElevation) / 8;
2020-12-21 13:16:29 -03:00
}
void CombatCamera(ITEM_INFO* item)
{
Camera.target.x = item->pos.xPos;
Camera.target.z = item->pos.zPos;
if (Lara.target)
{
Camera.targetAngle = Lara.targetAngles[0];
Camera.targetElevation = Lara.targetAngles[1] + item->pos.xRot;
}
else
{
Camera.targetAngle = Lara.headYrot + Lara.torsoYrot;
2021-11-10 11:26:58 +11:00
Camera.targetElevation = Lara.headXrot + Lara.torsoXrot + item->pos.xRot - ANGLE(15.0f);
2020-12-21 13:16:29 -03:00
}
2021-12-30 23:50:52 +11:00
auto probe = GetCollisionResult(Camera.target.x, Camera.target.y + STEP_SIZE, Camera.target.z, Camera.target.roomNumber);
if (g_Level.Rooms[probe.RoomNumber].flags & ENV_FLAG_SWAMP)
Camera.target.y = g_Level.Rooms[probe.RoomNumber].y - STEP_SIZE;
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(Camera.target.x, Camera.target.y, Camera.target.z, Camera.target.roomNumber);
Camera.target.roomNumber = probe.RoomNumber;
2021-12-30 23:50:52 +11:00
int buffer = CLICK(0.25f);
if ((probe.Position.Ceiling + buffer) > (probe.Position.Floor - buffer) &&
probe.Position.Floor != NO_HEIGHT &&
probe.Position.Ceiling != NO_HEIGHT)
2020-12-21 13:16:29 -03:00
{
2021-12-30 23:50:52 +11:00
Camera.target.y = (probe.Position.Ceiling + probe.Position.Floor) >> 1;
2020-12-21 13:16:29 -03:00
Camera.targetElevation = 0;
}
2021-12-30 23:50:52 +11:00
else if (Camera.target.y > (probe.Position.Floor - buffer) &&
probe.Position.Floor != NO_HEIGHT)
2020-12-21 13:16:29 -03:00
{
2021-12-30 23:50:52 +11:00
Camera.target.y = probe.Position.Floor - buffer;
2020-12-21 13:16:29 -03:00
Camera.targetElevation = 0;
}
2021-12-30 23:50:52 +11:00
else if (Camera.target.y < (probe.Position.Ceiling + buffer) &&
probe.Position.Ceiling != NO_HEIGHT)
2020-12-21 13:16:29 -03:00
{
2021-12-30 23:50:52 +11:00
Camera.target.y = probe.Position.Ceiling + buffer;
2020-12-21 13:16:29 -03:00
Camera.targetElevation = 0;
}
int y = Camera.target.y;
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(Camera.target.x, y, Camera.target.z, Camera.target.roomNumber);
Camera.target.roomNumber = probe.RoomNumber;
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
if (y < probe.Position.Ceiling ||
y > probe.Position.Floor ||
probe.Position.Ceiling >= probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT)
2020-12-21 13:16:29 -03:00
{
TargetSnaps++;
Camera.target.x = LastTarget.x;
Camera.target.y = LastTarget.y;
Camera.target.z = LastTarget.z;
Camera.target.roomNumber = LastTarget.roomNumber;
}
else
TargetSnaps = 0;
UpdateCameraElevation();
2021-11-10 22:48:00 +11:00
Camera.targetDistance = WALL_SIZE + STEP_SIZE * 2;
2020-12-21 13:16:29 -03:00
int distance = Camera.targetDistance * phd_cos(Camera.actualElevation);
for (int i = 0; i < 5; i++)
Ideals[i].y = Camera.target.y + Camera.targetDistance * phd_sin(Camera.actualElevation);
2021-11-10 11:26:58 +11:00
int farthest = INT_MAX;
2020-12-21 13:16:29 -03:00
int farthestnum = 0;
GAME_VECTOR temp[2];
for (int i = 0; i < 5; i++)
{
short angle;
if (i == 0)
angle = Camera.actualAngle;
else
2021-11-10 11:26:58 +11:00
angle = (i - 1) * ANGLE(90.0f);
2020-12-21 13:16:29 -03:00
Ideals[i].x = Camera.target.x - distance * phd_sin(angle);
Ideals[i].z = Camera.target.z - distance * phd_cos(angle);
Ideals[i].roomNumber = Camera.target.roomNumber;
2021-12-01 18:12:04 +03:00
if (LOSAndReturnTarget(&Camera.target, &Ideals[i], 200))
2020-12-21 13:16:29 -03:00
{
temp[0].x = Ideals[i].x;
temp[0].y = Ideals[i].y;
temp[0].z = Ideals[i].z;
temp[0].roomNumber = Ideals[i].roomNumber;
temp[1].x = Camera.pos.x;
temp[1].y = Camera.pos.y;
temp[1].z = Camera.pos.z;
temp[1].roomNumber = Camera.pos.roomNumber;
2021-11-10 11:26:58 +11:00
if (i == 0 ||
2021-12-01 18:12:04 +03:00
LOSAndReturnTarget(&temp[0], &temp[1], 0))
2020-12-21 13:16:29 -03:00
{
if (i == 0)
{
farthestnum = 0;
break;
}
int dx = (Camera.pos.x - Ideals[i].x) * (Camera.pos.x - Ideals[i].x);
dx += (Camera.pos.z - Ideals[i].z) * (Camera.pos.z - Ideals[i].z);
if (dx < farthest)
{
farthest = dx;
farthestnum = i;
}
}
}
else if (i == 0)
{
temp[0].x = Ideals[i].x;
temp[0].y = Ideals[i].y;
temp[0].z = Ideals[i].z;
temp[0].roomNumber = Ideals[i].roomNumber;
temp[1].x = Camera.pos.x;
temp[1].y = Camera.pos.y;
temp[1].z = Camera.pos.z;
temp[1].roomNumber = Camera.pos.roomNumber;
2021-11-10 11:26:58 +11:00
if (i == 0 ||
2021-12-01 18:12:04 +03:00
LOSAndReturnTarget(&temp[0], &temp[1], 0))
2020-12-21 13:16:29 -03:00
{
int dx = (Camera.target.x - Ideals[i].x) * (Camera.target.x - Ideals[i].x);
int dz = (Camera.target.z - Ideals[i].z) * (Camera.target.z - Ideals[i].z);
if ((dx + dz) > 0x90000)
{
farthestnum = 0;
break;
}
}
}
}
2021-11-14 20:10:15 +11:00
GAME_VECTOR ideal = { Ideals[farthestnum].x, Ideals[farthestnum].y, Ideals[farthestnum].z };
2020-12-21 13:16:29 -03:00
ideal.roomNumber = Ideals[farthestnum].roomNumber;
2021-11-10 11:26:58 +11:00
CameraCollisionBounds(&ideal, (STEP_SIZE + STEP_SIZE / 2), 1);
2020-12-21 13:16:29 -03:00
2021-09-25 16:03:28 -05:00
if (Camera.oldType == CAMERA_TYPE::FIXED_CAMERA)
2020-12-21 13:16:29 -03:00
Camera.speed = 1;
MoveCamera(&ideal, Camera.speed);
}
2021-11-10 11:26:58 +11:00
bool CameraCollisionBounds(GAME_VECTOR* ideal, int push, int yFirst)
2020-12-21 13:16:29 -03:00
{
2021-12-30 23:50:52 +11:00
int x = ideal->x;
int y = ideal->y;
int z = ideal->z;
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
COLL_RESULT probe;
2020-12-21 13:16:29 -03:00
if (yFirst)
{
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x, y, z, ideal->roomNumber);
int buffer = CLICK(1) - 1;
if ((y - buffer) < probe.Position.Ceiling &&
(y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
y = (probe.Position.Floor + probe.Position.Ceiling) / 2;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
y = probe.Position.Floor - buffer;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((y - buffer) < probe.Position.Ceiling &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
y = probe.Position.Ceiling + buffer;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
}
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x - push, y, z, ideal->roomNumber);
if (y > probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor ||
y < probe.Position.Ceiling)
2021-11-10 11:26:58 +11:00
{
2020-12-21 13:16:29 -03:00
x = (x & (~1023)) + push;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x, y, z - push, ideal->roomNumber);
if (y > probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor ||
y < probe.Position.Ceiling)
2021-11-10 11:26:58 +11:00
{
2020-12-21 13:16:29 -03:00
z = (z & (~1023)) + push;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x + push, y, z, ideal->roomNumber);
if (y > probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor ||
y < probe.Position.Ceiling)
2021-11-10 11:26:58 +11:00
{
2020-12-21 13:16:29 -03:00
x = (x | 1023) - push;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x, y, z + push, ideal->roomNumber);
if (y > probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor ||
y < probe.Position.Ceiling)
2021-11-10 11:26:58 +11:00
{
2020-12-21 13:16:29 -03:00
z = (z | 1023) - push;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
if (!yFirst)
{
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x, y, z, ideal->roomNumber);
int buffer = CLICK(1) - 1;
if ((y - buffer) < probe.Position.Ceiling &&
(y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
y = (probe.Position.Floor + probe.Position.Ceiling) / 2;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
y = probe.Position.Floor - buffer;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((y - buffer) < probe.Position.Ceiling &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
y = probe.Position.Ceiling + buffer;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
}
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x, y, z, ideal->roomNumber);
if (y > probe.Position.Floor ||
y < probe.Position.Ceiling ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor)
2021-11-10 11:26:58 +11:00
{
return true;
}
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
ideal->roomNumber = probe.RoomNumber;
2020-12-21 13:16:29 -03:00
ideal->x = x;
ideal->y = y;
ideal->z = z;
2021-11-10 11:26:58 +11:00
return false;
2020-12-21 13:16:29 -03:00
}
void FixedCamera(ITEM_INFO* item)
{
GAME_VECTOR from, to;
2021-09-11 18:59:14 +03:00
// Fixed cameras before TR3 had optional "movement" effect.
// Later for some reason it was forced to always be 1, and actual speed value
// from camera trigger was ignored. In TEN, we move speed value out of legacy
// floordata trigger to camera itself and make use of it again. Still, by default,
// value is 1 for UseForcedFixedCamera hack.
int moveSpeed = 1;
2020-12-21 13:16:29 -03:00
if (UseForcedFixedCamera)
{
from.x = ForcedFixedCamera.x;
from.y = ForcedFixedCamera.y;
from.z = ForcedFixedCamera.z;
from.roomNumber = ForcedFixedCamera.roomNumber;
}
else
{
LEVEL_CAMERA_INFO* camera = &g_Level.Cameras[Camera.number];
2020-12-21 13:16:29 -03:00
from.x = camera->x;
from.y = camera->y;
from.z = camera->z;
from.roomNumber = camera->roomNumber;
2020-12-21 13:16:29 -03:00
2021-09-11 18:59:14 +03:00
// Multiply original speed by 8 to comply with original bitshifted speed from TR1-2
2021-09-14 17:11:21 +03:00
moveSpeed = camera->speed * 8 + 1;
2020-12-21 13:16:29 -03:00
}
2021-11-08 19:28:14 +03:00
Camera.fixedCamera = true;
2020-12-21 13:16:29 -03:00
2021-09-11 18:59:14 +03:00
MoveCamera(&from, moveSpeed);
2020-12-21 13:16:29 -03:00
if (Camera.timer)
{
if (!--Camera.timer)
Camera.timer = -1;
}
}
void LookCamera(ITEM_INFO* item)
{
2021-12-30 23:50:52 +11:00
LaraInfo*& info = item->data;
short headXrot = info->headXrot;
short headYrot = info->headYrot;
short torsoXrot = info->torsoXrot;
short torsoYrot = info->torsoYrot;
info->torsoXrot = 0;
info->torsoYrot = 0;
info->headXrot *= 2;
info->headYrot *= 2;
// Clamp head rotation.
if (info->headXrot > ANGLE(55.0f))
info->headXrot = ANGLE(55.0f);
else if (info->headXrot < -ANGLE(75.0f))
info->headXrot = -ANGLE(75.0f);
if (info->headYrot < -ANGLE(80.0f))
info->headYrot = -ANGLE(80.0f);
else if (info->headYrot > ANGLE(80.0f))
info->headYrot = ANGLE(80.0f);
if (abs(info->headXrot - OldCam.pos.xRot) >= 16)
OldCam.pos.xRot = (info->headXrot + OldCam.pos.xRot) / 2;
2020-12-21 13:16:29 -03:00
else
2021-12-30 23:50:52 +11:00
OldCam.pos.xRot = info->headXrot;
if (abs(info->headYrot - OldCam.pos.yRot) >= 16)
OldCam.pos.yRot = (info->headYrot + OldCam.pos.yRot) / 2;
2020-12-21 13:16:29 -03:00
else
2021-12-30 23:50:52 +11:00
OldCam.pos.yRot = info->headYrot;
2020-12-21 13:16:29 -03:00
2021-11-14 20:10:15 +11:00
PHD_VECTOR pos = { 0, STEP_SIZE / 16, STEP_SIZE / 4 };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos, LM_HEAD);
2021-12-30 23:50:52 +11:00
auto probe = GetCollisionResult(pos.x, pos.y, pos.z, item->roomNumber);
if (probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor ||
pos.y > probe.Position.Floor ||
pos.y < probe.Position.Ceiling)
2020-12-21 13:16:29 -03:00
{
2021-11-14 20:10:15 +11:00
pos = { 0, STEP_SIZE / 16 , 0 };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos, LM_HEAD);
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(pos.x, pos.y + STEP_SIZE, pos.z, item->roomNumber);
if (g_Level.Rooms[probe.RoomNumber].flags & ENV_FLAG_SWAMP)
2020-12-21 13:16:29 -03:00
{
2021-12-30 23:50:52 +11:00
pos.y = g_Level.Rooms[probe.RoomNumber].y - STEP_SIZE;
probe = GetCollisionResult(pos.x, pos.y, pos.z, probe.RoomNumber);
2020-12-21 13:16:29 -03:00
}
else
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(pos.x, pos.y, pos.z, probe.RoomNumber);
if (probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor ||
pos.y > probe.Position.Floor ||
pos.y < probe.Position.Ceiling)
2020-12-21 13:16:29 -03:00
{
pos.x = 0;
2021-11-10 11:26:58 +11:00
pos.y = STEP_SIZE / 16;
2021-12-30 23:50:52 +11:00
pos.z = -CLICK(0.25f);
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos, LM_HEAD);
}
}
2021-11-14 20:10:15 +11:00
PHD_VECTOR pos2 = { 0, 0, -WALL_SIZE };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos2, LM_HEAD);
2021-12-30 23:50:52 +11:00
PHD_VECTOR pos3 = { 0, 0, CLICK(8) };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos3, LM_HEAD);
int dx = (pos2.x - pos.x) >> 3;
int dy = (pos2.y - pos.y) >> 3;
int dz = (pos2.z - pos.z) >> 3;
2020-12-21 13:16:29 -03:00
int x = pos.x;
int y = pos.y;
int z = pos.z;
2021-12-30 23:50:52 +11:00
int roomNum;
probe.RoomNumber = item->roomNumber;
2020-12-21 13:16:29 -03:00
int i = 0;
for (i = 0; i < 8; i++)
{
2021-12-30 23:50:52 +11:00
roomNum = probe.RoomNumber;
probe = GetCollisionResult(x, y + STEP_SIZE, z, probe.RoomNumber);
if (g_Level.Rooms[probe.RoomNumber].flags & ENV_FLAG_SWAMP)
2020-12-21 13:16:29 -03:00
{
2021-12-30 23:50:52 +11:00
y = g_Level.Rooms[probe.RoomNumber].y - STEP_SIZE;
2020-12-21 13:16:29 -03:00
break;
}
else
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(x, y, z, probe.RoomNumber);
if (probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
probe.Position.Ceiling >= probe.Position.Floor ||
y > probe.Position.Floor ||
y < probe.Position.Ceiling)
2021-11-10 11:26:58 +11:00
{
2020-12-21 13:16:29 -03:00
break;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
x += dx;
y += dy;
z += dz;
}
if (i)
{
x -= dx;
y -= dy;
z -= dz;
}
2021-11-14 20:10:15 +11:00
GAME_VECTOR ideal = { x, y, z };
2021-11-10 11:26:58 +11:00
ideal.roomNumber = roomNum;
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
if (OldCam.pos.xRot == info->headXrot &&
OldCam.pos.yRot == info->headYrot &&
OldCam.pos.xPos == item->pos.xPos &&
OldCam.pos.yPos == item->pos.yPos &&
OldCam.pos.zPos == item->pos.zPos &&
OldCam.currentAnimState == item->currentAnimState &&
OldCam.goalAnimState == item->goalAnimState &&
2021-09-25 16:03:28 -05:00
Camera.oldType == CAMERA_TYPE::LOOK_CAMERA)
2020-12-21 13:16:29 -03:00
{
ideal.x = LookCamPosition.x;
ideal.y = LookCamPosition.y;
ideal.z = LookCamPosition.z;
ideal.roomNumber = LookCamPosition.roomNumber;
pos3.x = LookCamTarget.x;
pos3.y = LookCamTarget.y;
pos3.z = LookCamTarget.z;
}
else
{
2021-12-30 23:50:52 +11:00
OldCam.pos.xRot = info->headXrot;
OldCam.pos.yRot = info->headYrot;
OldCam.pos.xPos = item->pos.xPos;
OldCam.pos.yPos = item->pos.yPos;
OldCam.pos.zPos = item->pos.zPos;
OldCam.currentAnimState = item->currentAnimState;
OldCam.goalAnimState = item->goalAnimState;
2020-12-21 13:16:29 -03:00
LookCamPosition.x = ideal.x;
LookCamPosition.y = ideal.y;
LookCamPosition.z = ideal.z;
LookCamPosition.roomNumber = ideal.roomNumber;
LookCamTarget.x = pos3.x;
LookCamTarget.y = pos3.y;
LookCamTarget.z = pos3.z;
}
2021-12-30 23:50:52 +11:00
CameraCollisionBounds(&ideal, (CLICK(1) - CLICK(0.25f) / 2), 1);
2020-12-21 13:16:29 -03:00
2021-09-25 16:03:28 -05:00
if (Camera.oldType == CAMERA_TYPE::FIXED_CAMERA)
2020-12-21 13:16:29 -03:00
{
Camera.pos.x = ideal.x;
Camera.pos.y = ideal.y;
Camera.pos.z = ideal.z;
Camera.target.x = pos3.x;
Camera.target.y = pos3.y;
Camera.target.z = pos3.z;
2021-12-30 23:50:52 +11:00
Camera.target.roomNumber = item->roomNumber;
2020-12-21 13:16:29 -03:00
}
else
{
Camera.pos.x += (ideal.x - Camera.pos.x) >> 2;
Camera.pos.y += (ideal.y - Camera.pos.y) >> 2;
Camera.pos.z += (ideal.z - Camera.pos.z) >> 2;
Camera.target.x += (pos3.x - Camera.target.x) >> 2;
Camera.target.y += (pos3.y - Camera.target.y) >> 2;
Camera.target.z += (pos3.z - Camera.target.z) >> 2;
2021-12-30 23:50:52 +11:00
Camera.target.roomNumber = item->roomNumber;
2020-12-21 13:16:29 -03:00
}
if (Camera.bounce && Camera.type == Camera.oldType)
{
if (Camera.bounce <= 0)
{
Camera.target.x += GetRandomControl() % (-Camera.bounce) - (-Camera.bounce >> 1);
Camera.target.y += GetRandomControl() % (-Camera.bounce) - (-Camera.bounce >> 1);
Camera.target.z += GetRandomControl() % (-Camera.bounce) - (-Camera.bounce >> 1);
2020-12-21 13:16:29 -03:00
Camera.bounce += 5;
}
else
{
Camera.pos.y += Camera.bounce;
Camera.target.y += Camera.bounce;
Camera.bounce = 0;
}
}
y = Camera.pos.y;
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(Camera.pos.x, y, Camera.pos.z, Camera.pos.roomNumber);
2021-11-10 11:26:58 +11:00
2021-12-30 23:50:52 +11:00
int buffer = CLICK(1) - 1;
if ((y - buffer) < probe.Position.Ceiling &&
(y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT && probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
Camera.pos.y = (probe.Position.Floor + probe.Position.Ceiling) / 2;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((y + buffer) > probe.Position.Floor &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
Camera.pos.y = probe.Position.Floor - buffer;
2021-11-10 11:26:58 +11:00
}
2021-12-30 23:50:52 +11:00
else if ((y - buffer) < probe.Position.Ceiling &&
probe.Position.Ceiling < probe.Position.Floor &&
probe.Position.Ceiling != NO_HEIGHT &&
probe.Position.Floor != NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-30 23:50:52 +11:00
Camera.pos.y = probe.Position.Ceiling + buffer;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
y = Camera.pos.y;
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(Camera.pos.x, y, Camera.pos.z, Camera.pos.roomNumber);
if ((g_Level.Rooms[probe.RoomNumber].flags & ENV_FLAG_SWAMP))
Camera.pos.y = g_Level.Rooms[probe.RoomNumber].y - STEP_SIZE;
else if (y < probe.Position.Ceiling ||
y > probe.Position.Floor ||
probe.Position.Ceiling >= probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT)
2021-11-10 11:26:58 +11:00
{
2021-12-01 18:12:04 +03:00
LOSAndReturnTarget(&Camera.target, &Camera.pos, 0);
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
y = Camera.pos.y;
2021-12-30 23:50:52 +11:00
probe = GetCollisionResult(Camera.pos.x, y, Camera.pos.z, Camera.pos.roomNumber);
if (y < probe.Position.Ceiling ||
y > probe.Position.Floor ||
probe.Position.Ceiling >= probe.Position.Floor ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT ||
g_Level.Rooms[probe.RoomNumber].flags & ENV_FLAG_SWAMP)
2020-12-21 13:16:29 -03:00
{
Camera.pos.x = pos.x;
Camera.pos.y = pos.y;
Camera.pos.z = pos.z;
2021-12-30 23:50:52 +11:00
Camera.pos.roomNumber = item->roomNumber;
2020-12-21 13:16:29 -03:00
}
2021-09-08 16:11:19 -05:00
ItemsCollideCamera();
2020-12-21 13:16:29 -03:00
GetFloor(Camera.pos.x, Camera.pos.y, Camera.pos.z, &Camera.pos.roomNumber);
LookAt(&Camera, 0);
if (Camera.mikeAtLara)
{
2021-12-30 23:50:52 +11:00
Camera.actualAngle = item->pos.yRot + info->headYrot + info->torsoYrot;
Camera.mikePos.x = item->pos.xPos;
Camera.mikePos.y = item->pos.yPos;
Camera.mikePos.z = item->pos.zPos;
2020-12-21 13:16:29 -03:00
}
else
{
Camera.actualAngle = phd_atan(Camera.target.z - Camera.pos.z, Camera.target.x - Camera.pos.x);
Camera.mikePos.x = Camera.pos.x + PhdPerspective * phd_sin(Camera.actualAngle);
Camera.mikePos.z = Camera.pos.z + PhdPerspective * phd_cos(Camera.actualAngle);
Camera.mikePos.y = Camera.pos.y;
}
Camera.oldType = Camera.type;
2021-12-30 23:50:52 +11:00
info->headXrot = headXrot;
info->headYrot = headYrot;
info->torsoXrot = torsoXrot;
info->torsoYrot = torsoYrot;
2020-12-21 13:16:29 -03:00
}
void BounceCamera(ITEM_INFO* item, short bounce, short maxDistance)
{
2021-11-10 11:26:58 +11:00
int distance = sqrt(
SQUARE(item->pos.xPos - Camera.pos.x) +
SQUARE(item->pos.yPos - Camera.pos.y) +
SQUARE(item->pos.zPos - Camera.pos.z));
2021-12-30 23:50:52 +11:00
2020-12-21 13:16:29 -03:00
if (distance < maxDistance)
{
if (maxDistance == -1)
Camera.bounce = bounce;
else
Camera.bounce = -(bounce * (maxDistance - distance) / maxDistance);
}
else if (maxDistance == -1)
Camera.bounce = bounce;
}
void BinocularCamera(ITEM_INFO* item)
{
2021-12-30 23:50:52 +11:00
LaraInfo*& info = item->data;
static bool exitingBinoculars = false;
2020-12-21 13:16:29 -03:00
if (LSHKTimer)
--LSHKTimer;
if (!LaserSight)
{
if (InputBusy & IN_DRAW)
2021-12-30 23:50:52 +11:00
exitingBinoculars = true;
else if (exitingBinoculars)
2020-12-21 13:16:29 -03:00
{
2021-12-30 23:50:52 +11:00
exitingBinoculars = false;
2020-12-21 13:16:29 -03:00
BinocularRange = 0;
AlterFOV(14560);
2021-12-30 23:50:52 +11:00
item->meshBits = -1;
info->busy = false;
info->headYrot = 0;
info->headXrot = 0;
info->torsoYrot = 0;
info->torsoXrot = 0;
2020-12-21 13:16:29 -03:00
Camera.type = BinocularOldCamera;
2021-11-10 11:26:58 +11:00
2020-12-21 13:16:29 -03:00
return;
}
}
2021-12-30 23:50:52 +11:00
item->meshBits = 0;
2020-12-21 13:16:29 -03:00
AlterFOV(7 * (2080 - BinocularRange));
2021-12-30 23:50:52 +11:00
short headXrot = info->headXrot * 2;
short headYrot = info->headYrot;
2020-12-21 13:16:29 -03:00
2021-11-10 11:26:58 +11:00
if (headXrot > ANGLE(75.0f))
headXrot = ANGLE(75.0f);
else if (headXrot < -ANGLE(75.0f))
headXrot = -ANGLE(75.0f);
2020-12-21 13:16:29 -03:00
2021-11-10 11:26:58 +11:00
if (headYrot > ANGLE(80.0f))
headYrot = ANGLE(80.0f);
else if (headYrot < -ANGLE(80.0f))
headYrot = -ANGLE(80.0f);
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
int x = item->pos.xPos;
int y = item->pos.yPos - CLICK(2);
int z = item->pos.zPos;
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
auto probe = GetCollisionResult(x, y, z, item->roomNumber);
if (probe.Position.Ceiling <= (y - CLICK(1)))
y -= CLICK(1);
2020-12-21 13:16:29 -03:00
else
2021-12-30 23:50:52 +11:00
y = probe.Position.Ceiling + CLICK(0.25f);
2020-12-21 13:16:29 -03:00
Camera.pos.x = x;
Camera.pos.y = y;
Camera.pos.z = z;
2021-12-30 23:50:52 +11:00
Camera.pos.roomNumber = probe.RoomNumber;
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
int l = (WALL_SIZE * 20 + CLICK(1)) * phd_cos(headXrot);
2020-12-21 13:16:29 -03:00
2021-12-30 23:50:52 +11:00
int tx = x + l * phd_sin(item->pos.yRot + headYrot);
int ty = y - (WALL_SIZE * 20 + CLICK(1)) * phd_sin(headXrot);
int tz = z + l * phd_cos(item->pos.yRot + headYrot);
2020-12-21 13:16:29 -03:00
2021-09-25 16:03:28 -05:00
if (Camera.oldType == CAMERA_TYPE::FIXED_CAMERA)
2020-12-21 13:16:29 -03:00
{
Camera.target.x = tx;
Camera.target.y = ty;
Camera.target.z = tz;
2021-12-30 23:50:52 +11:00
Camera.target.roomNumber = item->roomNumber;
2020-12-21 13:16:29 -03:00
}
else
{
Camera.target.x += (tx - Camera.target.x) >> 2;
Camera.target.y += (ty - Camera.target.y) >> 2;
Camera.target.z += (tz - Camera.target.z) >> 2;
2021-12-30 23:50:52 +11:00
Camera.target.roomNumber = item->roomNumber;
2020-12-21 13:16:29 -03:00
}
2021-12-30 23:50:52 +11:00
if (Camera.bounce &&
Camera.type == Camera.oldType)
2020-12-21 13:16:29 -03:00
{
if (Camera.bounce <= 0)
{
2021-11-10 11:26:58 +11:00
Camera.target.x += (STEP_SIZE / 16) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce >> 1));
Camera.target.y += (STEP_SIZE / 16) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce >> 1));
Camera.target.z += (STEP_SIZE / 16) * (GetRandomControl() % (-Camera.bounce) - (-Camera.bounce >> 1));
2020-12-21 13:16:29 -03:00
Camera.bounce += 5;
}
else
{
Camera.bounce = 0;
Camera.target.y += Camera.bounce;
}
}
2021-12-30 23:50:52 +11:00
Camera.target.roomNumber = GetCollisionResult(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.target.roomNumber).RoomNumber;
2020-12-21 13:16:29 -03:00
LookAt(&Camera, 0);
if (Camera.mikeAtLara)
{
2021-12-30 23:50:52 +11:00
Camera.actualAngle = item->pos.yRot + info->headYrot + info->torsoYrot;
Camera.mikePos.x = item->pos.xPos;
Camera.mikePos.y = item->pos.yPos;
Camera.mikePos.z = item->pos.zPos;
2020-12-21 13:16:29 -03:00
}
else
{
Camera.actualAngle = phd_atan(Camera.target.z - Camera.pos.z, Camera.target.x - Camera.pos.x);
Camera.mikePos.x = Camera.pos.x + PhdPerspective * phd_sin(Camera.actualAngle);
Camera.mikePos.z = Camera.pos.z + PhdPerspective * phd_cos(Camera.actualAngle);
Camera.mikePos.y = Camera.pos.y;
}
Camera.oldType = Camera.type;
int range = 0;
int flags = 0;
if (!(InputBusy & IN_WALK))
{
range = 64;
flags = 0x10000;
}
else
{
range = 32;
flags = 0x8000;
}
if (InputBusy & IN_SPRINT)
{
BinocularRange -= range;
if (BinocularRange < 128)
BinocularRange = 128;
else
SoundEffect(SFX_TR5_ZOOM_VIEW_WHIRR, 0, (flags << 8) | 6);
2020-12-21 13:16:29 -03:00
}
else if (InputBusy & IN_DUCK)
{
BinocularRange += range;
if (BinocularRange > 1536)
BinocularRange = 1536;
else
SoundEffect(SFX_TR5_ZOOM_VIEW_WHIRR, 0, (flags << 8) | 6);
2020-12-21 13:16:29 -03:00
}
2021-11-14 20:10:15 +11:00
PHD_VECTOR src = { Camera.pos.x, Camera.pos.y, Camera.pos.z };
PHD_VECTOR target = { Camera.target.x, Camera.target.y, Camera.target.z };
2020-12-21 13:16:29 -03:00
if (LaserSight)
{
int firing = 0;
2021-12-30 23:50:52 +11:00
Ammo& ammo = GetAmmo(item, info->gunType);
2020-12-21 13:16:29 -03:00
2021-11-10 11:26:58 +11:00
if (!(InputBusy & IN_ACTION) ||
WeaponDelay ||
!ammo)
2020-12-21 13:16:29 -03:00
{
if (!(InputBusy & IN_ACTION))
{
2021-12-30 23:50:52 +11:00
if (info->gunType != WEAPON_CROSSBOW)
2020-12-21 13:16:29 -03:00
WeaponDelay = 0;
2021-11-10 11:26:58 +11:00
2020-12-21 13:16:29 -03:00
LSHKShotsFired = 0;
Camera.bounce = 0;
}
}
else
{
2021-12-30 23:50:52 +11:00
if (info->gunType == WEAPON_REVOLVER)
2020-12-21 13:16:29 -03:00
{
firing = 1;
WeaponDelay = 16;
Statistics.Game.AmmoUsed++;
2021-11-10 11:26:58 +11:00
2021-01-26 10:03:59 +01:00
if (!ammo.hasInfinite())
2021-01-25 13:58:23 +01:00
(ammo)--;
2021-08-11 15:18:22 +03:00
Camera.bounce = -16 - (GetRandomControl() & 0x1F);
2020-12-21 13:16:29 -03:00
}
2021-12-30 23:50:52 +11:00
else if (info->gunType == WEAPON_CROSSBOW)
2020-12-21 13:16:29 -03:00
{
firing = 1;
WeaponDelay = 32;
}
else
{
2021-12-30 23:50:52 +11:00
if (info->Weapons[WEAPON_HK].SelectedAmmo == WEAPON_AMMO1)
2020-12-21 13:16:29 -03:00
{
WeaponDelay = 12;
firing = 1;
2021-11-10 11:26:58 +11:00
2021-12-30 23:50:52 +11:00
if (info->Weapons[WEAPON_HK].HasSilencer)
SoundEffect(SFX_TR5_HK_SILENCED, 0, 0);
2020-12-21 13:16:29 -03:00
else
{
SoundEffect(SFX_TR4_EXPLOSION1, 0, 83888140);
SoundEffect(SFX_TR5_HK_FIRE, 0, 0);
2020-12-21 13:16:29 -03:00
}
}
2021-12-30 23:50:52 +11:00
else if (info->Weapons[WEAPON_HK].SelectedAmmo == WEAPON_AMMO2)
2020-12-21 13:16:29 -03:00
{
if (!LSHKTimer)
{
if (++LSHKShotsFired == 5)
{
LSHKShotsFired = 0;
WeaponDelay = 12;
}
2021-11-10 11:26:58 +11:00
2020-12-21 13:16:29 -03:00
LSHKTimer = 4;
firing = 1;
2021-11-10 11:26:58 +11:00
2021-12-30 23:50:52 +11:00
if (info->Weapons[WEAPON_HK].HasSilencer)
SoundEffect(SFX_TR5_HK_SILENCED, 0, 0);
2020-12-21 13:16:29 -03:00
else
{
SoundEffect(SFX_TR4_EXPLOSION1, 0, 83888140);
SoundEffect(SFX_TR5_HK_FIRE, 0, 0);
2020-12-21 13:16:29 -03:00
}
}
else
{
Camera.bounce = -16 - (GetRandomControl() & 0x1F);
2021-12-30 23:50:52 +11:00
if (info->Weapons[WEAPON_HK].HasSilencer)
SoundEffect(SFX_TR5_HK_SILENCED, 0, 0);
2020-12-21 13:16:29 -03:00
else
{
SoundEffect(SFX_TR4_EXPLOSION1, 0, 83888140);
SoundEffect(SFX_TR5_HK_FIRE, 0, 0);
2020-12-21 13:16:29 -03:00
}
}
}
else
{
if (LSHKTimer)
{
2021-12-30 23:50:52 +11:00
if (info->Weapons[WEAPON_HK].HasSilencer)
SoundEffect(SFX_TR5_HK_SILENCED, 0, 0);
2020-12-21 13:16:29 -03:00
else
{
SoundEffect(SFX_TR4_EXPLOSION1, 0, 83888140);
SoundEffect(SFX_TR5_HK_FIRE, 0, 0);
2020-12-21 13:16:29 -03:00
}
}
else
{
LSHKTimer = 4;
firing = 1;
2021-11-10 11:26:58 +11:00
2021-12-30 23:50:52 +11:00
if (info->Weapons[WEAPON_HK].HasSilencer)
SoundEffect(SFX_TR5_HK_SILENCED, 0, 0);
2020-12-21 13:16:29 -03:00
else
{
SoundEffect(SFX_TR4_EXPLOSION1, 0, 83888140);
SoundEffect(SFX_TR5_HK_FIRE, 0, 0);
2020-12-21 13:16:29 -03:00
}
}
Camera.bounce = -16 - (GetRandomControl() & 0x1F);
}
2021-01-26 10:03:59 +01:00
if (!ammo.hasInfinite())
2021-01-25 13:58:23 +01:00
(ammo)--;
2020-12-21 13:16:29 -03:00
}
}
GetTargetOnLOS(&Camera.pos, &Camera.target, 1, firing);
}
else
{
GetTargetOnLOS(&Camera.pos, &Camera.target, 0, 0);
if (!(InputBusy & IN_ACTION))
2020-12-21 13:16:29 -03:00
{
// Reimplement this mode?
2020-12-21 13:16:29 -03:00
}
else
2021-12-30 23:50:52 +11:00
LaraTorch(&src, &target, info->headYrot, 192);
2020-12-21 13:16:29 -03:00
}
}
void ConfirmCameraTargetPos()
{
2021-11-14 20:10:15 +11:00
PHD_VECTOR pos = { 0, 0, 0 };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos, LM_TORSO);
if (Camera.laraNode != -1)
{
Camera.target.x = pos.x;
Camera.target.y = pos.y;
Camera.target.z = pos.z;
}
else
{
Camera.target.x = LaraItem->pos.xPos;
Camera.target.y = (Camera.target.y + pos.y) >> 1;
2020-12-21 13:16:29 -03:00
Camera.target.z = LaraItem->pos.zPos;
}
2021-12-30 23:50:52 +11:00
int y = Camera.target.y;
auto probe = GetCollisionResult(Camera.target.x, y, Camera.target.z, Camera.target.roomNumber);
if (y < probe.Position.Ceiling ||
probe.Position.Floor < y ||
probe.Position.Floor <= probe.Position.Ceiling ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT)
2020-12-21 13:16:29 -03:00
{
Camera.target.x = pos.x;
Camera.target.y = pos.y;
Camera.target.z = pos.z;
}
}
void CalculateCamera()
{
CamOldPos.x = Camera.pos.x;
CamOldPos.y = Camera.pos.y;
CamOldPos.z = Camera.pos.z;
if (BinocularRange != 0)
{
BinocularOn = 1;
BinocularCamera(LaraItem);
if (BinocularRange != 0)
return;
}
if (BinocularOn == 1)
BinocularOn = -8;
if (UseForcedFixedCamera != 0)
{
2021-09-25 16:03:28 -05:00
Camera.type = CAMERA_TYPE::FIXED_CAMERA;
if (Camera.oldType != CAMERA_TYPE::FIXED_CAMERA)
2020-12-21 13:16:29 -03:00
Camera.speed = 1;
}
// Camera is in a water room, play water sound effect.
2021-11-10 11:26:58 +11:00
if (g_Level.Rooms[Camera.pos.roomNumber].flags & ENV_FLAG_WATER)
2020-12-21 13:16:29 -03:00
{
SoundEffect(SFX_TR4_UNDERWATER, NULL, SFX_ALWAYS);
2021-11-08 19:28:14 +03:00
if (Camera.underwater == false)
Camera.underwater = true;
}
else
{
2021-11-08 19:28:14 +03:00
if (Camera.underwater == true)
Camera.underwater = false;
2020-12-21 13:16:29 -03:00
}
ITEM_INFO* item;
2021-11-08 19:28:14 +03:00
bool fixedCamera = false;
2021-11-10 11:26:58 +11:00
if (Camera.item != NULL &&
(Camera.type == CAMERA_TYPE::FIXED_CAMERA || Camera.type == CAMERA_TYPE::HEAVY_CAMERA))
2020-12-21 13:16:29 -03:00
{
item = Camera.item;
2021-11-08 19:28:14 +03:00
fixedCamera = true;
2020-12-21 13:16:29 -03:00
}
else
{
item = LaraItem;
2021-11-08 19:28:14 +03:00
fixedCamera = false;
2020-12-21 13:16:29 -03:00
}
BOUNDING_BOX* bounds = GetBoundsAccurate(item);
int x;
2021-11-07 14:58:02 +03:00
int y = ((bounds->Y1 + bounds->Y2) / 2) + item->pos.yPos - STEP_SIZE;
2020-12-21 13:16:29 -03:00
int z;
if (Camera.item)
{
if (!fixedCamera)
{
2021-11-10 11:26:58 +11:00
auto dx = Camera.item->pos.xPos - item->pos.xPos;
auto dz = Camera.item->pos.zPos - item->pos.zPos;
2020-12-21 13:16:29 -03:00
int shift = sqrt(SQUARE(dx) + SQUARE(dz));
short angle = phd_atan(dz, dx) - item->pos.yRot;
short tilt = phd_atan(shift, y - (bounds->Y1 + bounds->Y2) / 2 - Camera.item->pos.yPos);
bounds = GetBoundsAccurate(Camera.item);
2021-12-30 23:50:52 +11:00
angle /= 2;
tilt /= 2;
2020-12-21 13:16:29 -03:00
2021-11-10 11:26:58 +11:00
if (angle > -ANGLE(50.0f) && angle < ANGLE(50.0f) && tilt > -ANGLE(85.0f) && tilt < ANGLE(85.0f))
2020-12-21 13:16:29 -03:00
{
short change = angle - Lara.headYrot;
2021-11-10 11:26:58 +11:00
if (change > ANGLE(4.0f))
Lara.headYrot += ANGLE(4.0f);
else if (change < -ANGLE(4.0f))
Lara.headYrot -= ANGLE(4.0f);
2020-12-21 13:16:29 -03:00
else
Lara.headYrot += change;
Lara.torsoYrot = Lara.headYrot;
change = tilt - Lara.headXrot;
2021-11-10 11:26:58 +11:00
if (change > ANGLE(4.0f))
Lara.headXrot += ANGLE(4.0f);
else if (change < -ANGLE(4.0f))
Lara.headXrot -= ANGLE(4.0f);
2020-12-21 13:16:29 -03:00
else
Lara.headXrot += change;
Lara.torsoXrot = Lara.headXrot;
2021-09-25 16:03:28 -05:00
Camera.type = CAMERA_TYPE::LOOK_CAMERA;
2020-12-21 13:16:29 -03:00
Camera.item->lookedAt = 1;
}
}
}
2021-11-10 11:26:58 +11:00
if (Camera.type == CAMERA_TYPE::LOOK_CAMERA ||
Camera.type == CAMERA_TYPE::COMBAT_CAMERA)
2020-12-21 13:16:29 -03:00
{
2021-09-25 16:03:28 -05:00
if (Camera.type == CAMERA_TYPE::COMBAT_CAMERA)
2020-12-21 13:16:29 -03:00
{
LastTarget.x = Camera.target.x;
LastTarget.y = Camera.target.y;
LastTarget.z = Camera.target.z;
LastTarget.roomNumber = Camera.target.roomNumber;
}
Camera.target.roomNumber = item->roomNumber;
if (Camera.fixedCamera || BinocularOn < 0)
{
Camera.target.y = y;
Camera.speed = 1;
}
else
{
Camera.target.y += (y - Camera.target.y) >> 2;
2021-09-25 16:03:28 -05:00
Camera.speed = Camera.type != CAMERA_TYPE::LOOK_CAMERA ? 8 : 4;
2020-12-21 13:16:29 -03:00
}
2021-11-08 19:28:14 +03:00
Camera.fixedCamera = false;
2021-09-25 16:03:28 -05:00
if (Camera.type == CAMERA_TYPE::LOOK_CAMERA)
2020-12-21 13:16:29 -03:00
LookCamera(item);
else
CombatCamera(item);
}
else
{
LastTarget.x = Camera.target.x;
LastTarget.y = Camera.target.y;
LastTarget.z = Camera.target.z;
LastTarget.roomNumber = Camera.target.roomNumber;
Camera.target.roomNumber = item->roomNumber;
Camera.target.y = y;
2021-11-10 11:26:58 +11:00
if (Camera.type != CAMERA_TYPE::CHASE_CAMERA &&
Camera.flags != CF_CHASE_OBJECT &&
(Camera.number != -1 &&(SniperCamActive = g_Level.Cameras[Camera.number].flags & 3, g_Level.Cameras[Camera.number].flags & 2)))
2020-12-21 13:16:29 -03:00
{
2021-11-14 20:10:15 +11:00
PHD_VECTOR pos = { 0, 0, 0 };
2020-12-21 13:16:29 -03:00
GetLaraJointPosition(&pos, LM_TORSO);
x = pos.x;
y = pos.y;
z = pos.z;
Camera.target.x = pos.x;
Camera.target.y = pos.y;
Camera.target.z = pos.z;
}
else
{
2021-11-10 11:26:58 +11:00
auto shift = (bounds->X1 + bounds->X2 + bounds->Z1 + bounds->Z2) / 4;
2020-12-21 13:16:29 -03:00
x = item->pos.xPos + shift * phd_sin(item->pos.yRot);
z = item->pos.zPos + shift * phd_cos(item->pos.yRot);
Camera.target.x = x;
Camera.target.z = z;
if (item->objectNumber == ID_LARA)
{
ConfirmCameraTargetPos();
x = Camera.target.x;
y = Camera.target.y;
z = Camera.target.z;
}
}
if (fixedCamera == Camera.fixedCamera)
{
2021-11-08 19:28:14 +03:00
Camera.fixedCamera = false;
2021-11-10 11:26:58 +11:00
if (Camera.speed != 1 &&
Camera.oldType != CAMERA_TYPE::LOOK_CAMERA &&
BinocularOn >= 0)
2020-12-21 13:16:29 -03:00
{
if (TargetSnaps <= 8)
{
x = LastTarget.x + ((x - LastTarget.x) >> 2);
2020-12-21 13:16:29 -03:00
Camera.target.x = x;
y = LastTarget.y + ((y - LastTarget.y) >> 2);
2020-12-21 13:16:29 -03:00
Camera.target.y = y;
z = LastTarget.z + ((z - LastTarget.z) >> 2);
2020-12-21 13:16:29 -03:00
Camera.target.z = z;
}
else
TargetSnaps = 0;
}
}
else
{
2021-11-08 19:28:14 +03:00
Camera.fixedCamera = true;
2020-12-21 13:16:29 -03:00
Camera.speed = 1;
}
2021-12-30 23:50:52 +11:00
Camera.target.roomNumber = GetCollisionResult(x, y, z, Camera.target.roomNumber).RoomNumber;
2020-12-21 13:16:29 -03:00
2021-11-10 11:26:58 +11:00
if (abs(LastTarget.x - Camera.target.x) < 4 &&
abs(LastTarget.y - Camera.target.y) < 4 &&
abs(LastTarget.z - Camera.target.z) < 4)
2020-12-21 13:16:29 -03:00
{
Camera.target.x = LastTarget.x;
Camera.target.y = LastTarget.y;
Camera.target.z = LastTarget.z;
}
2021-09-25 16:03:28 -05:00
if (Camera.type != CAMERA_TYPE::CHASE_CAMERA && Camera.flags != CF_CHASE_OBJECT)
2020-12-21 13:16:29 -03:00
FixedCamera(item);
else
ChaseCamera(item);
}
Camera.fixedCamera = fixedCamera;
Camera.last = Camera.number;
2021-11-10 11:26:58 +11:00
if (Camera.type != CAMERA_TYPE::HEAVY_CAMERA ||
Camera.timer == -1)
2020-12-21 13:16:29 -03:00
{
2021-09-25 16:03:28 -05:00
Camera.type = CAMERA_TYPE::CHASE_CAMERA;
2020-12-21 13:16:29 -03:00
Camera.speed = 10;
Camera.number = -1;
Camera.lastItem = Camera.item;
Camera.item = NULL;
Camera.targetElevation = 0;
Camera.targetAngle = 0;
Camera.targetDistance = 1536;
Camera.flags = 0;
Camera.laraNode = -1;
}
}
void LookLeftRight()
{
2021-09-25 16:03:28 -05:00
Camera.type = CAMERA_TYPE::LOOK_CAMERA;
2020-12-21 13:16:29 -03:00
if (TrInput & IN_LEFT)
{
TrInput &= ~IN_LEFT;
if (Lara.headYrot > -ANGLE(44.0f))
{
if (BinocularRange)
Lara.headYrot += ANGLE(2.0f) * (BinocularRange - 1792) / 1536;
else
Lara.headYrot -= ANGLE(2.0f);
}
}
else if (TrInput & IN_RIGHT)
{
TrInput &= ~IN_RIGHT;
if (Lara.headYrot < ANGLE(44.0f))
{
if (BinocularRange)
Lara.headYrot += ANGLE(2.0f) * (1792 - BinocularRange) / 1536;
else
Lara.headYrot += ANGLE(2.0f);
}
}
2021-11-10 11:26:58 +11:00
if (Lara.gunStatus != LG_HANDS_BUSY &&
Lara.Vehicle == NO_ITEM &&
!Lara.leftArm.lock &&
!Lara.rightArm.lock)
{
2020-12-21 13:16:29 -03:00
Lara.torsoYrot = Lara.headYrot;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
}
void LookUpDown()
{
2021-09-25 16:03:28 -05:00
Camera.type = CAMERA_TYPE::LOOK_CAMERA;
2020-12-21 13:16:29 -03:00
if (TrInput & IN_FORWARD)
{
TrInput &= ~IN_FORWARD;
if (Lara.headXrot > -ANGLE(35.0f))
{
if (BinocularRange)
Lara.headXrot += ANGLE(2.0f) * (BinocularRange - 1792) / 3072;
else
Lara.headXrot -= ANGLE(2.0f);
}
}
else if (TrInput & IN_BACK)
{
TrInput &= ~IN_BACK;
if (Lara.headXrot < ANGLE(30.0f))
{
if (BinocularRange)
Lara.headXrot += ANGLE(2.0f) * (1792 - BinocularRange) / 3072;
else
Lara.headXrot += ANGLE(2.0f);
}
}
2021-11-10 11:26:58 +11:00
if (Lara.gunStatus != LG_HANDS_BUSY &&
Lara.Vehicle == NO_ITEM &&
!Lara.leftArm.lock &&
!Lara.rightArm.lock)
{
2020-12-21 13:16:29 -03:00
Lara.torsoXrot = Lara.headXrot;
2021-11-10 11:26:58 +11:00
}
2020-12-21 13:16:29 -03:00
}
void ResetLook(ITEM_INFO* item)
2020-12-21 13:16:29 -03:00
{
LaraInfo*& info = item->data;
2021-09-25 16:03:28 -05:00
if (Camera.type != CAMERA_TYPE::LOOK_CAMERA)
2020-12-21 13:16:29 -03:00
{
if (abs(info->headXrot) > ANGLE(0.1f))
info->headXrot += info->headXrot / -8;
else
info->headXrot = 0;
2020-12-21 13:16:29 -03:00
if (abs(info->headYrot) > ANGLE(0.1f))
info->headYrot += info->headYrot / -8;
else
info->headYrot = 0;
2021-09-28 22:05:47 +10:00
if (abs(info->headZrot) > ANGLE(0.1f))
info->headZrot += info->headZrot / -8;
else
info->headZrot = 0;
2020-12-21 13:16:29 -03:00
if (info->gunStatus != LG_HANDS_BUSY &&
!info->leftArm.lock &&
!info->rightArm.lock &&
info->Vehicle == NO_ITEM)
2020-12-21 13:16:29 -03:00
{
info->torsoXrot = info->headXrot;
info->torsoYrot = info->headYrot;
info->torsoZrot = info->headZrot;
2020-12-21 13:16:29 -03:00
}
else
{
if (!info->headXrot)
info->torsoXrot = 0;
if (!info->headYrot)
info->torsoYrot = 0;
if (!info->headZrot)
info->torsoZrot = 0;
2020-12-21 13:16:29 -03:00
}
}
}
2021-10-04 03:14:05 +03:00
2021-10-10 03:51:26 +03:00
bool TestBoundsCollideCamera(BOUNDING_BOX* bounds, PHD_3DPOS* pos, short radius)
2021-09-08 16:11:19 -05:00
{
auto dxBox = TO_DX_BBOX(*pos, bounds);
auto sphere = BoundingSphere(Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z), radius);
return sphere.Intersects(dxBox);
2021-09-08 16:11:19 -05:00
}
2021-10-10 03:51:26 +03:00
void ItemPushCamera(BOUNDING_BOX* bounds, PHD_3DPOS* pos, short radius)
2021-09-08 16:11:19 -05:00
{
2021-12-30 23:50:52 +11:00
int dx = Camera.pos.x - pos->xPos;
int dz = Camera.pos.z - pos->zPos;
2021-10-10 03:51:26 +03:00
auto sin = phd_sin(pos->yRot);
auto cos = phd_cos(pos->yRot);
auto x = dx * cos - dz * sin;
auto z = dx * sin + dz * cos;
2021-12-30 23:50:52 +11:00
int xmin = bounds->X1 - radius;
int xmax = bounds->X2 + radius;
int zmin = bounds->Z1 - radius;
int zmax = bounds->Z2 + radius;
2021-09-08 16:11:19 -05:00
2021-09-16 18:51:21 -05:00
if (x <= xmin || x >= xmax || z <= zmin || z >= zmax)
2021-09-08 16:11:19 -05:00
return;
2021-10-10 03:51:26 +03:00
auto left = x - xmin;
auto right = xmax - x;
auto top = zmax - z;
auto bottom = z - zmin;
2021-09-08 16:11:19 -05:00
2021-10-10 03:51:26 +03:00
if (left <= right && left <= top && left <= bottom) // Left is closest
2021-09-08 16:11:19 -05:00
x -= left;
2021-10-10 03:51:26 +03:00
else if (right <= left && right <= top && right <= bottom) // Right is closest
2021-09-08 16:11:19 -05:00
x += right;
2021-10-10 03:51:26 +03:00
else if (top <= left && top <= right && top <= bottom) // Top is closest
2021-09-08 16:11:19 -05:00
z += top;
else
2021-10-10 03:51:26 +03:00
z -= bottom; // Bottom
2021-09-08 16:11:19 -05:00
Camera.pos.x = pos->xPos + (cos * x + sin * z);
Camera.pos.z = pos->zPos + (cos * z - sin * x);
2021-10-10 03:51:26 +03:00
2021-12-14 16:20:27 +03:00
auto coll = GetCollisionResult(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.roomNumber);
if (coll.Position.Floor == NO_HEIGHT || Camera.pos.y > coll.Position.Floor || Camera.pos.y < coll.Position.Ceiling)
2021-09-08 16:11:19 -05:00
{
Camera.pos.x = CamOldPos.x;
Camera.pos.y = CamOldPos.y;
Camera.pos.z = CamOldPos.z;
2021-12-14 16:20:27 +03:00
Camera.pos.roomNumber = coll.RoomNumber;
2021-09-08 16:11:19 -05:00
}
}
static bool CheckItemCollideCamera(ITEM_INFO* item)
2021-09-08 16:11:19 -05:00
{
2021-10-10 03:51:26 +03:00
auto dx = Camera.pos.x - item->pos.xPos;
auto dy = Camera.pos.y - item->pos.yPos;
auto dz = Camera.pos.z - item->pos.zPos;
2021-09-16 18:51:21 -05:00
2021-12-30 23:50:52 +11:00
bool closeEnough = dx > -COLL_CHECK_THRESHOLD && dx < COLL_CHECK_THRESHOLD &&
2021-12-14 15:37:05 +03:00
dz > -COLL_CHECK_THRESHOLD && dz < COLL_CHECK_THRESHOLD &&
dy > -COLL_CHECK_THRESHOLD && dy < COLL_CHECK_THRESHOLD;
2021-09-16 18:51:21 -05:00
2021-12-30 23:50:52 +11:00
if (!closeEnough || !item->collidable || !Objects[item->objectNumber].usingDrawAnimatingItem)
2021-12-14 16:51:15 +03:00
return false;
2021-12-14 15:37:05 +03:00
// TODO: Find a better way to define objects which are collidable with camera.
2021-12-15 03:33:16 +03:00
auto obj = &Objects[item->objectNumber];
if (obj->intelligent || obj->isPickup || obj->isPuzzleHole || obj->collision == nullptr)
2021-12-14 16:51:15 +03:00
return false;
2021-09-08 16:11:19 -05:00
2021-12-14 16:51:15 +03:00
// Check extents, if any 2 bounds are smaller than threshold, discard.
Vector3 extents = TO_DX_BBOX(item->pos, GetBoundsAccurate(item)).Extents;
if ((abs(extents.x) < COLL_DISCARD_THRESHOLD && abs(extents.y) < COLL_DISCARD_THRESHOLD) ||
(abs(extents.x) < COLL_DISCARD_THRESHOLD && abs(extents.z) < COLL_DISCARD_THRESHOLD) ||
(abs(extents.y) < COLL_DISCARD_THRESHOLD && abs(extents.z) < COLL_DISCARD_THRESHOLD))
return false;
2021-12-14 16:51:15 +03:00
return true;
2021-09-16 18:51:21 -05:00
}
2021-09-08 16:11:19 -05:00
2021-09-16 18:51:21 -05:00
std::vector<short> FillCollideableItemList()
{
std::vector<short> itemList;
auto roomList = GetRoomList(Camera.pos.roomNumber);
2021-09-08 16:11:19 -05:00
2021-09-16 18:51:21 -05:00
for (short i = 0; i < g_Level.NumItems; i++)
2021-09-08 16:11:19 -05:00
{
auto item = &g_Level.Items[i];
if (!roomList.count(item->roomNumber))
continue;
if (!CheckItemCollideCamera(&g_Level.Items[i]))
continue;
itemList.push_back(i);
2021-09-16 18:51:21 -05:00
}
2021-09-08 16:11:19 -05:00
2021-09-16 18:51:21 -05:00
return itemList;
}
2021-09-08 16:11:19 -05:00
2021-09-16 18:51:21 -05:00
static bool CheckStaticCollideCamera(MESH_INFO* mesh)
{
auto dx = Camera.pos.x - mesh->pos.xPos;
auto dy = Camera.pos.y - mesh->pos.yPos;
auto dz = Camera.pos.z - mesh->pos.zPos;
2021-12-14 15:37:05 +03:00
2021-12-30 23:50:52 +11:00
bool closeEnough = dx > -COLL_CHECK_THRESHOLD && dx < COLL_CHECK_THRESHOLD &&
2021-12-14 16:51:15 +03:00
dz > -COLL_CHECK_THRESHOLD && dz < COLL_CHECK_THRESHOLD &&
2021-12-14 15:37:05 +03:00
dy > -COLL_CHECK_THRESHOLD && dy < COLL_CHECK_THRESHOLD;
2021-09-16 18:51:21 -05:00
2021-12-30 23:50:52 +11:00
if (!closeEnough)
2021-12-14 16:51:15 +03:00
return false;
2021-09-16 18:51:21 -05:00
2021-12-14 16:51:15 +03:00
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
return false;
auto stat = &StaticObjects[mesh->staticNumber];
auto extents = Vector3(abs(stat->collisionBox.X1 - stat->collisionBox.X2),
abs(stat->collisionBox.Y1 - stat->collisionBox.Y2),
abs(stat->collisionBox.Z1 - stat->collisionBox.Z2));
// Check extents, if any 2 bounds are smaller than threshold, discard.
if ((abs(extents.x) < COLL_DISCARD_THRESHOLD && abs(extents.y) < COLL_DISCARD_THRESHOLD) ||
(abs(extents.x) < COLL_DISCARD_THRESHOLD && abs(extents.z) < COLL_DISCARD_THRESHOLD) ||
(abs(extents.y) < COLL_DISCARD_THRESHOLD && abs(extents.z) < COLL_DISCARD_THRESHOLD))
return false;
return true;
2021-09-16 18:51:21 -05:00
}
std::vector<MESH_INFO*> FillCollideableStaticsList()
{
std::vector<MESH_INFO*> staticList;
auto roomList = GetRoomList(Camera.pos.roomNumber);
2021-09-16 18:51:21 -05:00
for (auto i : roomList)
2021-09-16 18:51:21 -05:00
{
auto room = &g_Level.Rooms[i];
2021-09-16 18:51:21 -05:00
for (short j = 0; j < room->mesh.size(); j++)
{
2021-12-14 16:51:15 +03:00
if (!CheckStaticCollideCamera(&room->mesh[j]))
continue;
2021-09-16 18:51:21 -05:00
staticList.push_back(&room->mesh[j]);
}
2021-09-08 16:11:19 -05:00
}
2021-09-16 18:51:21 -05:00
return staticList;
}
void ItemsCollideCamera()
{
2021-10-10 03:51:26 +03:00
auto rad = 128;
auto itemList = FillCollideableItemList();
2021-09-16 18:51:21 -05:00
2021-10-10 03:51:26 +03:00
// Collide with the items list
2021-12-14 15:37:05 +03:00
2021-10-10 03:51:26 +03:00
for (int i = 0; i < itemList.size(); i++)
2021-09-08 16:11:19 -05:00
{
2021-10-10 03:51:26 +03:00
auto item = &g_Level.Items[itemList[i]];
2021-09-08 16:11:19 -05:00
2021-09-16 18:51:21 -05:00
if (!item)
2021-09-08 16:11:19 -05:00
return;
2021-10-10 03:51:26 +03:00
auto dx = abs(LaraItem->pos.xPos - item->pos.xPos);
auto dy = abs(LaraItem->pos.yPos - item->pos.yPos);
auto dz = abs(LaraItem->pos.zPos - item->pos.zPos);
2021-09-16 18:51:21 -05:00
2021-10-10 03:51:26 +03:00
// If camera is stuck behind some item, and Lara runs off somewhere
2021-12-14 15:37:05 +03:00
if (dx > COLL_CANCEL_THRESHOLD || dz > COLL_CANCEL_THRESHOLD || dy > COLL_CANCEL_THRESHOLD)
2021-09-16 18:51:21 -05:00
continue;
2021-10-10 03:51:26 +03:00
auto bounds = GetBoundsAccurate(item);
2021-12-14 16:20:27 +03:00
if (TestBoundsCollideCamera(bounds, &item->pos, CAMERA_RADIUS))
ItemPushCamera(bounds, &item->pos, rad);
2021-09-16 18:51:21 -05:00
#ifdef _DEBUG
TEN::Renderer::g_Renderer.addDebugBox(TO_DX_BBOX(item->pos, bounds),
2021-12-14 15:37:05 +03:00
Vector4(1.0f, 0.0f, 0.0f, 1.0f), RENDERER_DEBUG_PAGE::DIMENSION_STATS);
2021-09-16 18:51:21 -05:00
#endif
2021-09-08 16:11:19 -05:00
}
2021-09-16 18:51:21 -05:00
2021-10-10 03:51:26 +03:00
itemList.clear(); // Done
2021-09-16 18:51:21 -05:00
2021-12-14 15:37:05 +03:00
// Collide with static meshes
2021-09-16 18:51:21 -05:00
2021-10-10 03:51:26 +03:00
auto staticList = FillCollideableStaticsList();
2021-09-16 18:51:21 -05:00
2021-10-10 03:51:26 +03:00
for (int i = 0; i < staticList.size(); i++)
2021-09-16 18:51:21 -05:00
{
2021-10-10 03:51:26 +03:00
auto mesh = staticList[i];
auto stat = &StaticObjects[mesh->staticNumber];
2021-09-16 18:51:21 -05:00
if (!mesh || !stat)
return;
auto dx = abs(LaraItem->pos.xPos - mesh->pos.xPos);
auto dy = abs(LaraItem->pos.yPos - mesh->pos.yPos);
auto dz = abs(LaraItem->pos.zPos - mesh->pos.zPos);
2021-09-16 18:51:21 -05:00
2021-12-14 15:37:05 +03:00
if (dx > COLL_CANCEL_THRESHOLD || dz > COLL_CANCEL_THRESHOLD || dy > COLL_CANCEL_THRESHOLD)
2021-09-16 18:51:21 -05:00
continue;
2021-11-20 15:23:07 +03:00
auto bounds = &stat->collisionBox;
2021-12-14 16:20:27 +03:00
if (TestBoundsCollideCamera(bounds, &mesh->pos, CAMERA_RADIUS))
ItemPushCamera(bounds, &mesh->pos, rad);
2021-09-16 18:51:21 -05:00
#ifdef _DEBUG
TEN::Renderer::g_Renderer.addDebugBox(TO_DX_BBOX(mesh->pos, bounds),
2021-12-14 15:37:05 +03:00
Vector4(1.0f, 0.0f, 0.0f, 1.0f), RENDERER_DEBUG_PAGE::DIMENSION_STATS);
2021-09-16 18:51:21 -05:00
#endif
}
2021-10-10 03:51:26 +03:00
staticList.clear(); // Done
2021-09-08 16:11:19 -05:00
}
2021-10-04 03:14:05 +03:00
void RumbleScreen()
{
if (!(GlobalCounter & 0x1FF))
SoundEffect(SFX_TR5_KLAXON, 0, 4104);
if (RumbleTimer >= 0)
RumbleTimer++;
if (RumbleTimer > 450)
{
if (!(GetRandomControl() & 0x1FF))
{
2021-10-22 16:33:15 +03:00
RumbleCounter = 0;
2021-10-04 03:14:05 +03:00
RumbleTimer = -32 - (GetRandomControl() & 0x1F);
return;
}
}
if (RumbleTimer < 0)
{
2021-10-22 16:33:15 +03:00
if (RumbleCounter >= abs(RumbleTimer))
2021-10-04 03:14:05 +03:00
{
Camera.bounce = -(GetRandomControl() % abs(RumbleTimer));
RumbleTimer++;
}
else
{
2021-10-22 16:33:15 +03:00
RumbleCounter++;
Camera.bounce = -(GetRandomControl() % RumbleCounter);
2021-10-04 03:14:05 +03:00
}
}
2021-11-10 11:26:58 +11:00
}