TombEngine/TR5Main/Objects/TR3/Vehicles/quad.cpp

1454 lines
39 KiB
C++

#include "framework.h"
#include "Objects/TR3/Vehicles/quad.h"
#include "Game/animation.h"
#include "Game/camera.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/collide_item.h"
#include "Game/effects/effects.h"
#include "Game/effects/tomb4fx.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_flare.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_one_gun.h"
#include "Game/misc.h"
#include "Objects/TR3/Vehicles/quad_info.h"
#include "Sound/sound.h"
#include "Specific/level.h"
#include "Specific/input.h"
#include "Specific/setup.h"
#include "Specific/prng.h"
using std::vector;
using namespace TEN::Math::Random;
#define MAX_VELOCITY 0xA000
#define MIN_DRIFT_VELOCITY 0x3000
#define BRAKE 0x0280
#define REVERSE_ACCELERATION -0x0300
#define MAX_BACK -0x3000
#define MAX_REVS 0xa000
#define TERMINAL_VERTICAL_VELOCITY 240
#define QUAD_SLIP 100
#define QUAD_SLIP_SIDE 50
#define QUAD_FRONT 550
#define QUAD_BACK -550
#define QUAD_SIDE 260
#define QUAD_RADIUS 500
#define QUAD_HEIGHT 512
// TODO
#define QUAD_HIT_LEFT 11
#define QUAD_HIT_RIGHT 12
#define QUAD_HIT_FRONT 13
#define QUAD_HIT_BACK 14
#define DAMAGE_START 140
#define DAMAGE_LENGTH 14
#define DISMOUNT_DISTANCE 385 // Precise root bone offset derived from final frame of animation.
#define QUAD_UNDO_TURN ANGLE(2.0f)
#define QUAD_TURN_RATE (ANGLE(0.5f) + QUAD_UNDO_TURN)
#define QUAD_TURN_MAX ANGLE(5.0f)
#define QUAD_DRIFT_TURN_RATE (ANGLE(0.75f) + QUAD_UNDO_TURN)
#define QUAD_DRIFT_TURN_MAX ANGLE(8.0f)
#define MIN_MOMENTUM_TURN ANGLE(3.0f)
#define MAX_MOMENTUM_TURN ANGLE(1.5f)
#define QUAD_MAX_MOM_TURN ANGLE(150.0f)
#define QUAD_MAX_HEIGHT CLICK(1)
#define QUAD_MIN_BOUNCE ((MAX_VELOCITY / 2) / CLICK(1))
// TODO: Common controls for all vehicles + unique settings page to set them. @Sezz 2021.11.14
#define QUAD_IN_ACCELERATE IN_ACTION
#define QUAD_IN_BRAKE IN_JUMP
#define QUAD_IN_DRIFT (IN_CROUCH | IN_SPRINT)
#define QUAD_IN_DISMOUNT IN_ROLL
#define QUAD_IN_LEFT IN_LEFT
#define QUAD_IN_RIGHT IN_RIGHT
enum QuadState
{
QUAD_STATE_DRIVE = 1,
QUAD_STATE_TURN_LEFT = 2,
QUAD_STATE_SLOW = 5,
QUAD_STATE_BRAKE = 6,
QUAD_STATE_BIKE_DEATH = 7,
QUAD_STATE_FALL = 8,
QUAD_STATE_MOUNT_RIGHT = 9,
QUAD_STATE_DISMOUNT_RIGHT = 10,
QUAD_STATE_HIT_BACK = 11,
QUAD_STATE_HIT_FRONT = 12,
QUAD_STATE_HIT_LEFT = 13,
QUAD_STATE_HIT_RIGHT = 14,
QUAD_STATE_IDLE = 15,
QUAD_STATE_LAND = 17,
QUAD_STATE_STOP_SLOWLY = 18,
QUAD_STATE_FALL_DEATH = 19,
QUAD_STATE_FALL_OFF = 20,
QUAD_STATE_WHEELIE = 21, // Unused.
QUAD_STATE_TURN_RIGHT = 22,
QUAD_STATE_MOUNT_LEFT = 23,
QUAD_STATE_DISMOUNT_LEFT = 24,
};
enum QuadAnim
{
QUAD_ANIM_IDLE_DEATH = 0,
QUAD_ANIM_UNK_1 = 1,
QUAD_ANIM_DRIVE_BACK = 2,
QUAD_ANIM_TURN_LEFT_START = 3,
QUAD_ANIM_TURN_LEFT_CONTINUE = 4,
QUAD_ANIM_TURN_LEFT_END = 5,
QUAD_ANIM_LEAP_START = 6,
QUAD_ANIM_LEAP_CONTINUE = 7,
QUAD_ANIM_LEAP_END = 8,
QUAD_ANIM_MOUNT_RIGHT = 9,
QUAD_ANIM_DISMOUNT_RIGHT = 10,
QUAD_ANIM_HIT_FRONT = 11,
QUAD_ANIM_HIT_BACK = 12,
QUAD_ANIM_HIT_RIGHT = 13,
QUAD_ANIM_HIT_LEFT = 14,
QUAD_ANIM_UNK_2 = 15,
QUAD_ANIM_UNK_3 = 16,
QUAD_ANIM_UNK_4 = 17,
QUAD_ANIM_IDLE = 18,
QUAD_ANIM_FALL_OFF_DEATH = 19,
QUAD_ANIM_TURN_RIGHT_START = 20,
QUAD_ANIM_TURN_RIGHT_CONTINUE = 21,
QUAD_ANIM_TURN_RIGHT_END = 22,
QUAD_ANIM_MOUNT_LEFT = 23,
QUAD_ANIM_DISMOUNT_LEFT = 24,
QUAD_ANIM_LEAP_START2 = 25,
QUAD_ANIM_LEAP_CONTINUE2 = 26,
QUAD_ANIM_LEAP_END2 = 27,
QUAD_ANIM_LEAP_TO_FREEFALL = 28
};
enum QuadEffectPosition
{
EXHAUST_LEFT = 0,
EXHAUST_RIGHT = 1,
FRONT_LEFT_TYRE = 2,
FRONT_RIGHT_TYRE = 3,
BACK_LEFT_TYRE = 4,
BACK_RIGHT_TYRE = 5
};
enum QuadFlags
{
QUAD_FLAG_DEAD = 0x80,
QUAD_FLAG_IS_FALLING = 0x40
};
BITE_INFO quadEffectsPositions[6] =
{
{ -56, -32, -380, 0 },
{ 56, -32, -380, 0 },
{ -8, 180, -48, 3 },
{ 8, 180, -48, 4 },
{ 90, 180, -32, 6 },
{ -90, 180, -32, 7 }
};
void InitialiseQuadBike(short itemNumber)
{
auto* quadItem = &g_Level.Items[itemNumber];
quadItem->Data = QuadInfo();
auto* quad = (QuadInfo*)quadItem->Data;
quad->TurnRate = 0;
quad->MomentumAngle = quadItem->Position.yRot;
quad->ExtraRotation = 0;
quad->Velocity = 0;
quad->LeftVerticalVelocity = 0;
quad->RightVerticalVelocity = 0;
quad->Pitch = 0;
quad->Flags = 0;
}
static void QuadbikeExplode(ITEM_INFO* laraItem, ITEM_INFO* quadItem)
{
auto* lara = GetLaraInfo(laraItem);
if (TestEnvironment(ENV_FLAG_WATER, quadItem))
TriggerUnderwaterExplosion(quadItem, 1);
else
{
TriggerExplosionSparks(quadItem->Position.xPos, quadItem->Position.yPos, quadItem->Position.zPos, 3, -2, 0, quadItem->RoomNumber);
for (int i = 0; i < 3; i++)
TriggerExplosionSparks(quadItem->Position.xPos, quadItem->Position.yPos, quadItem->Position.zPos, 3, -1, 0, quadItem->RoomNumber);
}
auto pos = PHD_3DPOS(quadItem->Position.xPos, quadItem->Position.yPos - CLICK(0.5f), quadItem->Position.zPos, 0, quadItem->Position.yRot, 0);
TriggerShockwave(&pos, 50, 180, 40, GenerateFloat(160, 200), 60, 60, 64, GenerateFloat(0, 359), 0);
SoundEffect(SFX_TR4_EXPLOSION1, NULL, 0);
SoundEffect(SFX_TR4_EXPLOSION2, NULL, 0);
quadItem->Status = ITEM_DEACTIVATED;
lara->Vehicle = NO_ITEM;
}
static int CanQuadbikeGetOff(int direction)
{
auto* item = &g_Level.Items[Lara.Vehicle];
short angle;
if (direction < 0)
angle = item->Position.yRot - ANGLE(90.0f);
else
angle = item->Position.yRot + ANGLE(90.0f);
int x = item->Position.xPos + CLICK(2) * phd_sin(angle);
int y = item->Position.yPos;
int z = item->Position.zPos + CLICK(2) * phd_cos(angle);
auto collResult = GetCollisionResult(x, y, z, item->RoomNumber);
if (collResult.Position.FloorSlope ||
collResult.Position.Floor == NO_HEIGHT)
{
return false;
}
if (abs(collResult.Position.Floor - item->Position.yPos) > CLICK(2))
return false;
if ((collResult.Position.Ceiling - item->Position.yPos) > -LARA_HEIGHT ||
(collResult.Position.Floor - collResult.Position.Ceiling) < LARA_HEIGHT)
{
return false;
}
return true;
}
static bool QuadCheckGetOff(ITEM_INFO* laraItem, ITEM_INFO* quadItem)
{
auto* lara = GetLaraInfo(laraItem);
auto* quad = (QuadInfo*)quadItem->Data;
if (lara->Vehicle == NO_ITEM)
return true;
if ((laraItem->ActiveState == QUAD_STATE_DISMOUNT_RIGHT || laraItem->ActiveState == QUAD_STATE_DISMOUNT_LEFT) &&
TestLastFrame(laraItem))
{
if (laraItem->ActiveState == QUAD_STATE_DISMOUNT_LEFT)
laraItem->Position.yRot += ANGLE(90.0f);
else
laraItem->Position.yRot -= ANGLE(90.0f);
SetAnimation(laraItem, LA_STAND_IDLE);
laraItem->Position.xPos -= DISMOUNT_DISTANCE * phd_sin(laraItem->Position.yRot);
laraItem->Position.zPos -= DISMOUNT_DISTANCE * phd_cos(laraItem->Position.yRot);
laraItem->Position.xRot = 0;
laraItem->Position.zRot = 0;
lara->Vehicle = NO_ITEM;
lara->Control.HandStatus = HandStatus::Free;
if (laraItem->ActiveState == QUAD_STATE_FALL_OFF)
{
PHD_VECTOR pos = { 0, 0, 0 };
SetAnimation(laraItem, LA_FREEFALL);
GetJointAbsPosition(laraItem, &pos, LM_HIPS);
laraItem->Position.xPos = pos.x;
laraItem->Position.yPos = pos.y;
laraItem->Position.zPos = pos.z;
laraItem->VerticalVelocity = quadItem->VerticalVelocity;
laraItem->Airborne = true;
laraItem->Position.xRot = 0;
laraItem->Position.zRot = 0;
laraItem->HitPoints = 0;
lara->Control.HandStatus = HandStatus::Free;
quadItem->Flags |= ONESHOT;
return false;
}
else if (laraItem->ActiveState == QUAD_STATE_FALL_DEATH)
{
laraItem->TargetState = LS_DEATH;
laraItem->VerticalVelocity = DAMAGE_START + DAMAGE_LENGTH;
laraItem->VerticalVelocity = 0;
quad->Flags |= QUAD_FLAG_DEAD;
return false;
}
return true;
}
else
return true;
}
static int GetOnQuadBike(ITEM_INFO* laraItem, ITEM_INFO* quadItem, COLL_INFO* coll)
{
auto* lara = GetLaraInfo(laraItem);
if (!(TrInput & IN_ACTION) ||
laraItem->Airborne ||
lara->Control.HandStatus != HandStatus::Free ||
quadItem->Flags & ONESHOT ||
abs(quadItem->Position.yPos - laraItem->Position.yPos) > CLICK(1))
{
return false;
}
auto dist = pow(laraItem->Position.xPos - quadItem->Position.xPos, 2) + pow(laraItem->Position.zPos - quadItem->Position.zPos, 2);
if (dist > 170000)
return false;
auto probe = GetCollisionResult(quadItem);
if (probe.Position.Floor < -32000)
return false;
else
{
short angle = phd_atan(quadItem->Position.zPos - laraItem->Position.zPos, quadItem->Position.xPos - laraItem->Position.xPos);
angle -= quadItem->Position.yRot;
if ((angle > -ANGLE(45.0f)) && (angle < ANGLE(135.0f)))
{
short tempAngle = laraItem->Position.yRot - quadItem->Position.yRot;
if (tempAngle > ANGLE(45.0f) && tempAngle < ANGLE(135.0f))
return true;
else
return false;
}
else
{
short tempAngle = laraItem->Position.yRot - quadItem->Position.yRot;
if (tempAngle > ANGLE(225.0f) && tempAngle < ANGLE(315.0f))
return true;
else
return false;
}
}
return true;
}
static void QuadBaddieCollision(ITEM_INFO* laraItem, ITEM_INFO* quadItem)
{
vector<short> roomsList;
roomsList.push_back(quadItem->RoomNumber);
auto* roomInfo = &g_Level.Rooms[quadItem->RoomNumber];
for (int i = 0; i < roomInfo->doors.size(); i++)
roomsList.push_back(roomInfo->doors[i].room);
for (int i = 0; i < roomsList.size(); i++)
{
auto itemNum = g_Level.Rooms[roomsList[i]].itemNumber;
while (itemNum != NO_ITEM)
{
auto* item = &g_Level.Items[itemNum];
if (item->Collidable &&
item->Status != ITEM_INVISIBLE &&
item != laraItem && item != quadItem)
{
auto* object = &Objects[item->ObjectNumber];
if (object->collision && object->intelligent)
{
int x = quadItem->Position.xPos - item->Position.xPos;
int y = quadItem->Position.yPos - item->Position.yPos;
int z = quadItem->Position.zPos - item->Position.zPos;
if (x > -SECTOR(2) && x < SECTOR(2) &&
z > -SECTOR(2) && z < SECTOR(2) &&
y > -SECTOR(2) && y < SECTOR(2))
{
if (TestBoundsCollide(item, quadItem, QUAD_RADIUS))
{
DoLotsOfBlood(item->Position.xPos, quadItem->Position.yPos - CLICK(1), item->Position.zPos, quadItem->VerticalVelocity, quadItem->Position.yRot, item->RoomNumber, 3);
item->HitPoints = 0;
}
}
}
}
itemNum = item->NextItem;
}
}
}
static int GetQuadCollisionAnim(ITEM_INFO* quadItem, PHD_VECTOR* p)
{
p->x = quadItem->Position.xPos - p->x;
p->z = quadItem->Position.zPos - p->z;
if (p->x || p->z)
{
float c = phd_cos(quadItem->Position.yRot);
float s = phd_sin(quadItem->Position.yRot);
int front = p->z * c + p->x * s;
int side = -p->z * s + p->x * c;
if (abs(front) > abs(side))
{
if (front > 0)
return QUAD_HIT_BACK;
else
return QUAD_HIT_FRONT;
}
else
{
if (side > 0)
return QUAD_HIT_LEFT;
else
return QUAD_HIT_RIGHT;
}
}
return 0;
}
static int TestQuadHeight(ITEM_INFO* quadItem, int dz, int dx, PHD_VECTOR* pos)
{
pos->y = quadItem->Position.yPos - dz * phd_sin(quadItem->Position.xRot) + dx * phd_sin(quadItem->Position.zRot);
float c = phd_cos(quadItem->Position.yRot);
float s = phd_sin(quadItem->Position.yRot);
pos->z = quadItem->Position.zPos + dz * c - dx * s;
pos->x = quadItem->Position.xPos + dz * s + dx * c;
auto probe = GetCollisionResult(pos->x, pos->y, pos->z, quadItem->RoomNumber);
if (probe.Position.Ceiling > pos->y ||
probe.Position.Ceiling == NO_HEIGHT)
{
return NO_HEIGHT;
}
return probe.Position.Floor;
}
static int DoQuadShift(ITEM_INFO* quadItem, PHD_VECTOR* pos, PHD_VECTOR* old)
{
COLL_RESULT probe;
int x = pos->x / SECTOR(1);
int z = pos->z / SECTOR(1);
int oldX = old->x / SECTOR(1);
int oldZ = old->z / SECTOR(1);
int shiftX = pos->x & (WALL_SIZE - 1);
int shiftZ = pos->z & (WALL_SIZE - 1);
if (x == oldX)
{
if (z == oldZ)
{
quadItem->Position.zPos += (old->z - pos->z);
quadItem->Position.xPos += (old->x - pos->x);
}
else if (z > oldZ)
{
quadItem->Position.zPos -= shiftZ + 1;
return (pos->x - quadItem->Position.xPos);
}
else
{
quadItem->Position.zPos += WALL_SIZE - shiftZ;
return (quadItem->Position.xPos - pos->x);
}
}
else if (z == oldZ)
{
if (x > oldX)
{
quadItem->Position.xPos -= shiftX + 1;
return (quadItem->Position.zPos - pos->z);
}
else
{
quadItem->Position.xPos += WALL_SIZE - shiftX;
return (pos->z - quadItem->Position.zPos);
}
}
else
{
x = 0;
z = 0;
probe = GetCollisionResult(old->x, pos->y, pos->z, quadItem->RoomNumber);
if (probe.Position.Floor < (old->y - CLICK(1)))
{
if (pos->z > old->z)
z = -shiftZ - 1;
else
z = WALL_SIZE - shiftZ;
}
probe = GetCollisionResult(pos->x, pos->y, old->z, quadItem->RoomNumber);
if (probe.Position.Floor < (old->y - CLICK(1)))
{
if (pos->x > old->x)
x = -shiftX - 1;
else
x = WALL_SIZE - shiftX;
}
if (x && z)
{
quadItem->Position.zPos += z;
quadItem->Position.xPos += x;
}
else if (z)
{
quadItem->Position.zPos += z;
if (z > 0)
return (quadItem->Position.xPos - pos->x);
else
return (pos->x - quadItem->Position.xPos);
}
else if (x)
{
quadItem->Position.xPos += x;
if (x > 0)
return (pos->z - quadItem->Position.zPos);
else
return (quadItem->Position.zPos - pos->z);
}
else
{
quadItem->Position.zPos += (old->z - pos->z);
quadItem->Position.xPos += (old->x - pos->x);
}
}
return 0;
}
static int DoQuadDynamics(int height, int fallspeed, int* y)
{
if (height > *y)
{
*y += fallspeed;
if (*y > height - QUAD_MIN_BOUNCE)
{
*y = height;
fallspeed = 0;
}
else
fallspeed += 6;
}
else
{
int kick = (height - *y) * 4;
if (kick < -80)
kick = -80;
fallspeed += ((kick - fallspeed) / 8);
if (*y > height)
*y = height;
}
return fallspeed;
}
static int QuadDynamics(ITEM_INFO* laraItem, ITEM_INFO* quadItem)
{
auto* lara = GetLaraInfo(laraItem);
auto* quad = (QuadInfo*)quadItem->Data;
quad->NoDismount = false;
PHD_VECTOR oldFrontLeft, oldFrontRight, oldBottomLeft, oldBottomRight;
int holdFrontLeft = TestQuadHeight(quadItem, QUAD_FRONT, -QUAD_SIDE, &oldFrontLeft);
int holdFrontRight = TestQuadHeight(quadItem, QUAD_FRONT, QUAD_SIDE, &oldFrontRight);
int holdBottomLeft = TestQuadHeight(quadItem, -QUAD_FRONT, -QUAD_SIDE, &oldBottomLeft);
int holdBottomRight = TestQuadHeight(quadItem, -QUAD_FRONT, QUAD_SIDE, &oldBottomRight);
PHD_VECTOR mtlOld, mtrOld, mmlOld, mmrOld;
int hmml_old = TestQuadHeight(quadItem, 0, -QUAD_SIDE, &mmlOld);
int hmmr_old = TestQuadHeight(quadItem, 0, QUAD_SIDE, &mmrOld);
int hmtl_old = TestQuadHeight(quadItem, QUAD_FRONT / 2, -QUAD_SIDE, &mtlOld);
int hmtr_old = TestQuadHeight(quadItem, QUAD_FRONT / 2, QUAD_SIDE, &mtrOld);
PHD_VECTOR moldBottomLeft, moldBottomRight;
int hmoldBottomLeft = TestQuadHeight(quadItem, -QUAD_FRONT / 2, -QUAD_SIDE, &moldBottomLeft);
int hmoldBottomRight = TestQuadHeight(quadItem, -QUAD_FRONT / 2, QUAD_SIDE, &moldBottomRight);
PHD_VECTOR old;
old.x = quadItem->Position.xPos;
old.y = quadItem->Position.yPos;
old.z = quadItem->Position.zPos;
if (oldBottomLeft.y > holdBottomLeft)
oldBottomLeft.y = holdBottomLeft;
if (oldBottomRight.y > holdBottomRight)
oldBottomRight.y = holdBottomRight;
if (oldFrontLeft.y > holdFrontLeft)
oldFrontLeft.y = holdFrontLeft;
if (oldFrontRight.y > holdFrontRight)
oldFrontRight.y = holdFrontRight;
if (moldBottomLeft.y > hmoldBottomLeft)
moldBottomLeft.y = hmoldBottomLeft;
if (moldBottomRight.y > hmoldBottomRight)
moldBottomRight.y = hmoldBottomRight;
if (mtlOld.y > hmtl_old)
mtlOld.y = hmtl_old;
if (mtrOld.y > hmtr_old)
mtrOld.y = hmtr_old;
if (mmlOld.y > hmml_old)
mmlOld.y = hmml_old;
if (mmrOld.y > hmmr_old)
mmrOld.y = hmmr_old;
if (quadItem->Position.yPos > (quadItem->Floor - CLICK(1)))
{
if (quad->TurnRate < -QUAD_UNDO_TURN)
quad->TurnRate += QUAD_UNDO_TURN;
else if (quad->TurnRate > QUAD_UNDO_TURN)
quad->TurnRate -= QUAD_UNDO_TURN;
else
quad->TurnRate = 0;
quadItem->Position.yRot += quad->TurnRate + quad->ExtraRotation;
short momentum;
momentum = MIN_MOMENTUM_TURN - (((((MIN_MOMENTUM_TURN - MAX_MOMENTUM_TURN) * 256) / MAX_VELOCITY) * quad->Velocity) / 256);
if (!(TrInput & QUAD_IN_ACCELERATE) && quad->Velocity > 0)
momentum += (momentum / 4);
short rot = quadItem->Position.yRot - quad->MomentumAngle;
if (rot < -MAX_MOMENTUM_TURN)
{
if (rot < -QUAD_MAX_MOM_TURN)
{
rot = -QUAD_MAX_MOM_TURN;
quad->MomentumAngle = quadItem->Position.yRot - rot;
}
else
quad->MomentumAngle -= momentum;
}
else if (rot > MAX_MOMENTUM_TURN)
{
if (rot > QUAD_MAX_MOM_TURN)
{
rot = QUAD_MAX_MOM_TURN;
quad->MomentumAngle = quadItem->Position.yRot - rot;
}
else
quad->MomentumAngle += momentum;
}
else
quad->MomentumAngle = quadItem->Position.yRot;
}
else
quadItem->Position.yRot += quad->TurnRate + quad->ExtraRotation;
auto probe = GetCollisionResult(quadItem);
int speed = 0;
if (quadItem->Position.yPos >= probe.Position.Floor)
speed = quadItem->VerticalVelocity * phd_cos(quadItem->Position.xRot);
else
speed = quadItem->VerticalVelocity;
quadItem->Position.zPos += speed * phd_cos(quad->MomentumAngle);
quadItem->Position.xPos += speed * phd_sin(quad->MomentumAngle);
int slip = QUAD_SLIP * phd_sin(quadItem->Position.xRot);
if (abs(slip) > QUAD_SLIP / 2)
{
if (slip > 0)
slip -= 10;
else
slip += 10;
quadItem->Position.zPos -= slip * phd_cos(quadItem->Position.yRot);
quadItem->Position.xPos -= slip * phd_sin(quadItem->Position.yRot);
}
slip = QUAD_SLIP_SIDE * phd_sin(quadItem->Position.zRot);
if (abs(slip) > QUAD_SLIP_SIDE / 2)
{
quadItem->Position.zPos -= slip * phd_sin(quadItem->Position.yRot);
quadItem->Position.xPos += slip * phd_cos(quadItem->Position.yRot);
}
PHD_VECTOR moved;
moved.x = quadItem->Position.xPos;
moved.z = quadItem->Position.zPos;
if (!(quadItem->Flags & ONESHOT))
QuadBaddieCollision(laraItem, quadItem);
short rot = 0;
short rotAdd = 0;
PHD_VECTOR fl;
int heightFrontLeft = TestQuadHeight(quadItem, QUAD_FRONT, -QUAD_SIDE, &fl);
if (heightFrontLeft < (oldFrontLeft.y - CLICK(1)))
rot = DoQuadShift(quadItem, &fl, &oldFrontLeft);
PHD_VECTOR mtl;
int hmtl = TestQuadHeight(quadItem, QUAD_FRONT / 2, -QUAD_SIDE, &mtl);
if (hmtl < (mtlOld.y - CLICK(1)))
DoQuadShift(quadItem, &mtl, &mtlOld);
PHD_VECTOR mml;
int hmml = TestQuadHeight(quadItem, 0, -QUAD_SIDE, &mml);
if (hmml < (mmlOld.y - CLICK(1)))
DoQuadShift(quadItem, &mml, &mmlOld);
PHD_VECTOR mbl;
int hmbl = TestQuadHeight(quadItem, -QUAD_FRONT / 2, -QUAD_SIDE, &mbl);
if (hmbl < (moldBottomLeft.y - CLICK(1)))
DoQuadShift(quadItem, &mbl, &moldBottomLeft);
PHD_VECTOR bl;
int heightBackLeft = TestQuadHeight(quadItem, -QUAD_FRONT, -QUAD_SIDE, &bl);
if (heightBackLeft < (oldBottomLeft.y - CLICK(1)))
{
rotAdd = DoQuadShift(quadItem, &bl, &oldBottomLeft);
if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0))
rot += rotAdd;
}
PHD_VECTOR fr;
int heightFrontRight = TestQuadHeight(quadItem, QUAD_FRONT, QUAD_SIDE, &fr);
if (heightFrontRight < (oldFrontRight.y - CLICK(1)))
{
rotAdd = DoQuadShift(quadItem, &fr, &oldFrontRight);
if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0))
rot += rotAdd;
}
PHD_VECTOR mtr;
int hmtr = TestQuadHeight(quadItem, QUAD_FRONT / 2, QUAD_SIDE, &mtr);
if (hmtr < (mtrOld.y - CLICK(1)))
DoQuadShift(quadItem, &mtr, &mtrOld);
PHD_VECTOR mmr;
int hmmr = TestQuadHeight(quadItem, 0, QUAD_SIDE, &mmr);
if (hmmr < (mmrOld.y - CLICK(1)))
DoQuadShift(quadItem, &mmr, &mmrOld);
PHD_VECTOR mbr;
int hmbr = TestQuadHeight(quadItem, -QUAD_FRONT / 2, QUAD_SIDE, &mbr);
if (hmbr < (moldBottomRight.y - CLICK(1)))
DoQuadShift(quadItem, &mbr, &moldBottomRight);
PHD_VECTOR br;
int heightBackRight = TestQuadHeight(quadItem, -QUAD_FRONT, QUAD_SIDE, &br);
if (heightBackRight < (oldBottomRight.y - CLICK(1)))
{
rotAdd = DoQuadShift(quadItem, &br, &oldBottomRight);
if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0))
rot += rotAdd;
}
probe = GetCollisionResult(quadItem);
if (probe.Position.Floor < quadItem->Position.yPos - CLICK(1))
DoQuadShift(quadItem, (PHD_VECTOR*)&quadItem->Position, &old);
quad->ExtraRotation = rot;
int collide = GetQuadCollisionAnim(quadItem, &moved);
int newVelocity = 0;
if (collide)
{
newVelocity = (quadItem->Position.zPos - old.z) * phd_cos(quad->MomentumAngle) + (quadItem->Position.xPos - old.x) * phd_sin(quad->MomentumAngle);
newVelocity *= 256;
if (&g_Level.Items[lara->Vehicle] == quadItem &&
quad->Velocity == MAX_VELOCITY &&
newVelocity < (quad->Velocity - 10))
{
laraItem->HitPoints -= (quad->Velocity - newVelocity) / 128;
laraItem->HitStatus = 1;
}
if (quad->Velocity > 0 && newVelocity < quad->Velocity)
quad->Velocity = (newVelocity < 0) ? 0 : newVelocity;
else if (quad->Velocity < 0 && newVelocity > quad->Velocity)
quad->Velocity = (newVelocity > 0) ? 0 : newVelocity;
if (quad->Velocity < MAX_BACK)
quad->Velocity = MAX_BACK;
}
return collide;
}
static void AnimateQuadBike(ITEM_INFO* laraItem, ITEM_INFO* quadItem, int collide, bool dead)
{
auto* quad = (QuadInfo*)quadItem->Data;
if (quadItem->Position.yPos != quadItem->Floor &&
laraItem->ActiveState != QUAD_STATE_FALL &&
laraItem->ActiveState != QUAD_STATE_LAND &&
laraItem->ActiveState != QUAD_STATE_FALL_OFF &&
!dead)
{
if (quad->Velocity < 0)
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_LEAP_START;
else
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_LEAP_START2;
laraItem->FrameNumber = GetFrameNumber(laraItem, laraItem->AnimNumber);
laraItem->ActiveState = QUAD_STATE_FALL;
laraItem->TargetState = QUAD_STATE_FALL;
}
else if (collide &&
laraItem->ActiveState != QUAD_STATE_HIT_FRONT &&
laraItem->ActiveState != QUAD_STATE_HIT_BACK &&
laraItem->ActiveState != QUAD_STATE_HIT_LEFT &&
laraItem->ActiveState != QUAD_STATE_HIT_RIGHT &&
laraItem->ActiveState != QUAD_STATE_FALL_OFF &&
quad->Velocity > (MAX_VELOCITY / 3) &&
!dead)
{
if (collide == QUAD_HIT_FRONT)
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_BACK;
laraItem->ActiveState = QUAD_STATE_HIT_FRONT;
laraItem->TargetState = QUAD_STATE_HIT_FRONT;
}
else if (collide == QUAD_HIT_BACK)
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_FRONT;
laraItem->ActiveState = QUAD_STATE_HIT_BACK;
laraItem->TargetState = QUAD_STATE_HIT_BACK;
}
else if (collide == QUAD_HIT_LEFT)
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_RIGHT;
laraItem->ActiveState = QUAD_STATE_HIT_LEFT;
laraItem->TargetState = QUAD_STATE_HIT_LEFT;
}
else
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_LEFT;
laraItem->ActiveState = QUAD_STATE_HIT_RIGHT;
laraItem->TargetState = QUAD_STATE_HIT_RIGHT;
}
laraItem->FrameNumber = GetFrameNumber(laraItem, laraItem->AnimNumber);
SoundEffect(SFX_TR3_QUAD_FRONT_IMPACT, &quadItem->Position, 0);
}
else
{
switch (laraItem->ActiveState)
{
case QUAD_STATE_IDLE:
if (dead)
laraItem->TargetState = QUAD_STATE_BIKE_DEATH;
else if (TrInput & QUAD_IN_DISMOUNT &&
quad->Velocity == 0 &&
!quad->NoDismount)
{
if (TrInput & QUAD_IN_LEFT && CanQuadbikeGetOff(-1))
laraItem->TargetState = QUAD_STATE_DISMOUNT_LEFT;
else if (TrInput & QUAD_IN_RIGHT && CanQuadbikeGetOff(1))
laraItem->TargetState = QUAD_STATE_DISMOUNT_RIGHT;
}
else if (TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE))
laraItem->TargetState = QUAD_STATE_DRIVE;
break;
case QUAD_STATE_DRIVE:
if (dead)
{
if (quad->Velocity > (MAX_VELOCITY / 2))
laraItem->TargetState = QUAD_STATE_FALL_DEATH;
else
laraItem->TargetState = QUAD_STATE_BIKE_DEATH;
}
else if (!(TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) &&
(quad->Velocity / 256) == 0)
{
laraItem->TargetState = QUAD_STATE_IDLE;
}
else if (TrInput & QUAD_IN_LEFT &&
!quad->DriftStarting)
{
laraItem->TargetState = QUAD_STATE_TURN_LEFT;
}
else if (TrInput & QUAD_IN_RIGHT &&
!quad->DriftStarting)
{
laraItem->TargetState = QUAD_STATE_TURN_RIGHT;
}
else if (TrInput & QUAD_IN_BRAKE)
{
if (quad->Velocity > (MAX_VELOCITY / 3 * 2))
laraItem->TargetState = QUAD_STATE_BRAKE;
else
laraItem->TargetState = QUAD_STATE_SLOW;
}
break;
case QUAD_STATE_BRAKE:
case QUAD_STATE_SLOW:
case QUAD_STATE_STOP_SLOWLY:
if ((quad->Velocity / 256) == 0)
laraItem->TargetState = QUAD_STATE_IDLE;
else if (TrInput & QUAD_IN_LEFT)
laraItem->TargetState = QUAD_STATE_TURN_LEFT;
else if (TrInput & QUAD_IN_RIGHT)
laraItem->TargetState = QUAD_STATE_TURN_RIGHT;
break;
case QUAD_STATE_TURN_LEFT:
if ((quad->Velocity / 256) == 0)
laraItem->TargetState = QUAD_STATE_IDLE;
else if (TrInput & QUAD_IN_RIGHT)
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_TURN_RIGHT_START;
laraItem->FrameNumber = GetFrameNumber(laraItem, laraItem->AnimNumber);
laraItem->ActiveState = QUAD_STATE_TURN_RIGHT;
laraItem->TargetState = QUAD_STATE_TURN_RIGHT;
}
else if (!(TrInput & QUAD_IN_LEFT))
laraItem->TargetState = QUAD_STATE_DRIVE;
break;
case QUAD_STATE_TURN_RIGHT:
if ((quad->Velocity / 256) == 0)
laraItem->TargetState = QUAD_STATE_IDLE;
else if (TrInput & QUAD_IN_LEFT)
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_TURN_LEFT_START;
laraItem->FrameNumber = GetFrameNumber(laraItem, laraItem->AnimNumber);
laraItem->ActiveState = QUAD_STATE_TURN_LEFT;
laraItem->TargetState = QUAD_STATE_TURN_LEFT;
}
else if (!(TrInput & QUAD_IN_RIGHT))
laraItem->TargetState = QUAD_STATE_DRIVE;
break;
case QUAD_STATE_FALL:
if (quadItem->Position.yPos == quadItem->Floor)
laraItem->TargetState = QUAD_STATE_LAND;
else if (quadItem->VerticalVelocity > TERMINAL_VERTICAL_VELOCITY)
quad->Flags |= QUAD_FLAG_IS_FALLING;
break;
case QUAD_STATE_FALL_OFF:
break;
case QUAD_STATE_HIT_FRONT:
case QUAD_STATE_HIT_BACK:
case QUAD_STATE_HIT_LEFT:
case QUAD_STATE_HIT_RIGHT:
if (TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE))
laraItem->TargetState = QUAD_STATE_DRIVE;
break;
}
if (TestEnvironment(ENV_FLAG_WATER, quadItem) ||
TestEnvironment(ENV_FLAG_SWAMP, quadItem))
{
laraItem->TargetState = QUAD_STATE_FALL_OFF;
laraItem->Position.yPos = quadItem->Position.yPos + 700;
laraItem->RoomNumber = quadItem->RoomNumber;
laraItem->HitPoints = 0;
QuadbikeExplode(laraItem, quadItem);
}
}
}
static int QuadUserControl(ITEM_INFO* quadItem, int height, int* pitch)
{
auto* quad = (QuadInfo*)quadItem->Data;
bool drive = false; // Never changes?
if (!(TrInput & QUAD_IN_DRIFT) &&
!quad->Velocity && !quad->CanStartDrift)
{
quad->CanStartDrift = true;
}
else if (quad->Velocity)
quad->CanStartDrift = false;
if (!(TrInput & QUAD_IN_DRIFT))
quad->DriftStarting = false;
if (!quad->DriftStarting)
{
if (quad->Revs > 0x10)
{
quad->Velocity += (quad->Revs / 16);
quad->Revs -= (quad->Revs / 8);
}
else
quad->Revs = 0;
}
if (quadItem->Position.yPos >= (height - CLICK(1)))
{
if (TrInput & IN_LOOK && !quad->Velocity)
LookUpDown(LaraItem);
// Driving forward.
if (quad->Velocity > 0)
{
if (TrInput & QUAD_IN_DRIFT &&
!quad->DriftStarting &&
quad->Velocity > MIN_DRIFT_VELOCITY)
{
if (TrInput & QUAD_IN_LEFT)
{
quad->TurnRate -= QUAD_DRIFT_TURN_RATE;
if (quad->TurnRate < -QUAD_DRIFT_TURN_MAX)
quad->TurnRate = -QUAD_DRIFT_TURN_MAX;
}
else if (TrInput & QUAD_IN_RIGHT)
{
quad->TurnRate += QUAD_DRIFT_TURN_RATE;
if (quad->TurnRate > QUAD_DRIFT_TURN_MAX)
quad->TurnRate = QUAD_DRIFT_TURN_MAX;
}
}
else
{
if (TrInput & QUAD_IN_LEFT)
{
quad->TurnRate -= QUAD_TURN_RATE;
if (quad->TurnRate < -QUAD_TURN_MAX)
quad->TurnRate = -QUAD_TURN_MAX;
}
else if (TrInput & QUAD_IN_RIGHT)
{
quad->TurnRate += QUAD_TURN_RATE;
if (quad->TurnRate > QUAD_TURN_MAX)
quad->TurnRate = QUAD_TURN_MAX;
}
}
}
// Driving back.
else if (quad->Velocity < 0)
{
if (TrInput & QUAD_IN_DRIFT &&
!quad->DriftStarting &&
quad->Velocity < (-MIN_DRIFT_VELOCITY + 0x800))
{
if (TrInput & QUAD_IN_LEFT)
{
quad->TurnRate -= QUAD_DRIFT_TURN_RATE;
if (quad->TurnRate < -QUAD_DRIFT_TURN_MAX)
quad->TurnRate = -QUAD_DRIFT_TURN_MAX;
}
else if (TrInput & QUAD_IN_RIGHT)
{
quad->TurnRate += QUAD_DRIFT_TURN_RATE;
if (quad->TurnRate > QUAD_DRIFT_TURN_MAX)
quad->TurnRate = QUAD_DRIFT_TURN_MAX;
}
}
else
{
if (TrInput & QUAD_IN_RIGHT)
{
quad->TurnRate -= QUAD_TURN_RATE;
if (quad->TurnRate < -QUAD_TURN_MAX)
quad->TurnRate = -QUAD_TURN_MAX;
}
else if (TrInput & QUAD_IN_LEFT)
{
quad->TurnRate += QUAD_TURN_RATE;
if (quad->TurnRate > QUAD_TURN_MAX)
quad->TurnRate = QUAD_TURN_MAX;
}
}
}
// Driving back / braking.
if (TrInput & QUAD_IN_BRAKE)
{
if (TrInput & QUAD_IN_DRIFT &&
(quad->CanStartDrift || quad->DriftStarting))
{
quad->DriftStarting = true;
quad->Revs -= 0x200;
if (quad->Revs < MAX_BACK)
quad->Revs = MAX_BACK;
}
else if (quad->Velocity > 0)
quad->Velocity -= BRAKE;
else
{
if (quad->Velocity > MAX_BACK)
quad->Velocity += REVERSE_ACCELERATION;
}
}
else if (TrInput & QUAD_IN_ACCELERATE)
{
if (TrInput & QUAD_IN_DRIFT &&
(quad->CanStartDrift || quad->DriftStarting))
{
quad->DriftStarting = true;
quad->Revs += 0x200;
if (quad->Revs >= MAX_VELOCITY)
quad->Revs = MAX_VELOCITY;
}
else if (quad->Velocity < MAX_VELOCITY)
{
if (quad->Velocity < 0x4000)
quad->Velocity += (8 + (0x4000 + 0x800 - quad->Velocity) / 8);
else if (quad->Velocity < 0x7000)
quad->Velocity += (4 + (0x7000 + 0x800 - quad->Velocity) / 16);
else if (quad->Velocity < MAX_VELOCITY)
quad->Velocity += (2 + (MAX_VELOCITY - quad->Velocity) / 8);
}
else
quad->Velocity = MAX_VELOCITY;
quad->Velocity -= abs(quadItem->Position.yRot - quad->MomentumAngle) / 64;
}
else if (quad->Velocity > 0x0100)
quad->Velocity -= 0x0100;
else if (quad->Velocity < -0x0100)
quad->Velocity += 0x0100;
else
quad->Velocity = 0;
if (!(TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) &&
quad->DriftStarting &&
quad->Revs)
{
if (quad->Revs > 0x8)
quad->Revs -= quad->Revs / 8;
else
quad->Revs = 0;
}
quadItem->VerticalVelocity = quad->Velocity / 256;
if (quad->EngineRevs > 0x7000)
quad->EngineRevs = -0x2000;
int revs = 0;
if (quad->Velocity < 0)
revs = abs(quad->Velocity / 2);
else if (quad->Velocity < 0x7000)
revs = -0x2000 + (quad->Velocity * (0x6800 - -0x2000)) / 0x7000;
else if (quad->Velocity <= MAX_VELOCITY)
revs = -0x2800 + ((quad->Velocity - 0x7000) * (0x7000 - -0x2800)) / (MAX_VELOCITY - 0x7000);
revs += abs(quad->Revs);
quad->EngineRevs += (revs - quad->EngineRevs) / 8;
}
else
{
if (quad->EngineRevs < 0xA000)
quad->EngineRevs += (0xA000 - quad->EngineRevs) / 8;
}
*pitch = quad->EngineRevs;
return drive;
}
void QuadBikeCollision(short itemNumber, ITEM_INFO* laraItem, COLL_INFO* coll)
{
auto* lara = GetLaraInfo(laraItem);
auto* quadItem = &g_Level.Items[itemNumber];
auto* quad = (QuadInfo*)quadItem->Data;
if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM)
return;
if (GetOnQuadBike(laraItem, &g_Level.Items[itemNumber], coll))
{
lara->Vehicle = itemNumber;
if (lara->Control.Weapon.GunType == WEAPON_FLARE)
{
CreateFlare(laraItem, ID_FLARE_ITEM, 0);
UndrawFlareMeshes(laraItem);
lara->Flare.ControlLeft = false;
lara->Control.Weapon.RequestGunType = lara->Control.Weapon.GunType = WEAPON_NONE;
}
lara->Control.HandStatus = HandStatus::Busy;
short angle = phd_atan(quadItem->Position.zPos - laraItem->Position.zPos, quadItem->Position.xPos - laraItem->Position.xPos);
angle -= quadItem->Position.yRot;
if (angle > -ANGLE(45.0f) && angle < ANGLE(135.0f))
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_MOUNT_LEFT;
laraItem->ActiveState = laraItem->TargetState = QUAD_STATE_MOUNT_LEFT;
}
else
{
laraItem->AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_MOUNT_RIGHT;
laraItem->ActiveState = laraItem->TargetState = QUAD_STATE_MOUNT_RIGHT;
}
laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase;
laraItem->Position.xPos = quadItem->Position.xPos;
laraItem->Position.yPos = quadItem->Position.yPos;
laraItem->Position.zPos = quadItem->Position.zPos;
laraItem->Position.yRot = quadItem->Position.yRot;
ResetLaraFlex(laraItem);
lara->HitDirection = -1;
quadItem->HitPoints = 1;
AnimateItem(laraItem);
quad->Revs = 0;
}
else
ObjectCollision(itemNumber, laraItem, coll);
}
static void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int speed, int moving)
{
auto* spark = &Sparks[GetFreeSpark()];
spark->on = true;
spark->sR = 0;
spark->sG = 0;
spark->sB = 0;
spark->dR = 96;
spark->dG = 96;
spark->dB = 128;
if (moving)
{
spark->dR = (spark->dR * speed) / 32;
spark->dG = (spark->dG * speed) / 32;
spark->dB = (spark->dB * speed) / 32;
}
spark->sLife = spark->life = (GetRandomControl() & 3) + 20 - (speed / 4096);
if (spark->sLife < 9)
spark->sLife = spark->life = 9;
spark->colFadeSpeed = 4;
spark->fadeToBlack = 4;
spark->extras = 0;
spark->dynamic = -1;
spark->x = x + ((GetRandomControl() & 15) - 8);
spark->y = y + ((GetRandomControl() & 15) - 8);
spark->z = z + ((GetRandomControl() & 15) - 8);
int zv = speed * phd_cos(angle) / 4;
int xv = speed * phd_sin(angle) / 4;
spark->xVel = xv + ((GetRandomControl() & 255) - 128);
spark->yVel = -(GetRandomControl() & 7) - 8;
spark->zVel = zv + ((GetRandomControl() & 255) - 128);
spark->friction = 4;
if (GetRandomControl() & 1)
{
spark->flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
spark->rotAng = GetRandomControl() & 4095;
if (GetRandomControl() & 1)
spark->rotAdd = -(GetRandomControl() & 7) - 24;
else
spark->rotAdd = (GetRandomControl() & 7) + 24;
}
else
spark->flags = SP_SCALE | SP_DEF | SP_EXPDEF;
spark->def = Objects[ID_DEFAULT_SPRITES].meshIndex;
spark->scalar = 2;
spark->gravity = -(GetRandomControl() & 3) - 4;
spark->maxYvel = -(GetRandomControl() & 7) - 8;
int size = (GetRandomControl() & 7) + 64 + (speed / 128);
spark->dSize = size;
spark->size = spark->sSize = size / 2;
}
bool QuadBikeControl(ITEM_INFO* laraItem, COLL_INFO* coll)
{
auto* lara = GetLaraInfo(laraItem);
auto* quadItem = &g_Level.Items[lara->Vehicle];
auto* quad = (QuadInfo*)quadItem->Data;
GAME_VECTOR oldPos;
oldPos.x = quadItem->Position.xPos;
oldPos.y = quadItem->Position.yPos;
oldPos.z = quadItem->Position.zPos;
oldPos.roomNumber = quadItem->RoomNumber;
bool collide = QuadDynamics(laraItem, quadItem);
auto probe = GetCollisionResult(quadItem);
PHD_VECTOR frontLeft, frontRight;
auto floorHeightLeft = TestQuadHeight(quadItem, QUAD_FRONT, -QUAD_SIDE, &frontLeft);
auto floorHeightRight = TestQuadHeight(quadItem, QUAD_FRONT, QUAD_SIDE, &frontRight);
TestTriggers(quadItem, false);
bool dead = false;
if (laraItem->HitPoints <= 0)
{
TrInput &= ~(IN_LEFT | IN_RIGHT | IN_BACK | IN_FORWARD);
dead = true;
}
int drive = -1;
int pitch = 0;
if (quad->Flags)
collide = false;
else
{
switch (laraItem->ActiveState)
{
case QUAD_STATE_MOUNT_LEFT:
case QUAD_STATE_MOUNT_RIGHT:
case QUAD_STATE_DISMOUNT_LEFT:
case QUAD_STATE_DISMOUNT_RIGHT:
drive = -1;
collide = false;
break;
default:
drive = QuadUserControl(quadItem, probe.Position.Floor, &pitch);
break;
}
}
if (quad->Velocity || quad->Revs)
{
quad->Pitch = pitch;
if (quad->Pitch < -0x8000)
quad->Pitch = -0x8000;
else if (quad->Pitch > 0xA000)
quad->Pitch = 0xA000;
SoundEffect(SFX_TR3_QUAD_MOVE, &quadItem->Position, 0, 0.5f + (float)abs(quad->Pitch) / (float)MAX_VELOCITY);
}
else
{
if (drive != -1)
SoundEffect(SFX_TR3_QUAD_IDLE, &quadItem->Position, 0);
quad->Pitch = 0;
}
quadItem->Floor = probe.Position.Floor;
short rotAdd = quad->Velocity / 4;
quad->RearRot -= rotAdd;
quad->RearRot -= (quad->Revs / 8);
quad->FrontRot -= rotAdd;
quad->LeftVerticalVelocity = DoQuadDynamics(floorHeightLeft, quad->LeftVerticalVelocity, (int*)&frontLeft.y);
quad->RightVerticalVelocity = DoQuadDynamics(floorHeightRight, quad->RightVerticalVelocity, (int*)&frontRight.y);
quadItem->VerticalVelocity = DoQuadDynamics(probe.Position.Floor, quadItem->VerticalVelocity, (int*)&quadItem->Position.yPos);
probe.Position.Floor = (frontLeft.y + frontRight.y) / 2;
short xRot = phd_atan(QUAD_FRONT, quadItem->Position.yPos - probe.Position.Floor);
short zRot = phd_atan(QUAD_SIDE, probe.Position.Floor - frontLeft.y);
quadItem->Position.xRot += ((xRot - quadItem->Position.xRot) / 2);
quadItem->Position.zRot += ((zRot - quadItem->Position.zRot) / 2);
if (!(quad->Flags & QUAD_FLAG_DEAD))
{
if (probe.RoomNumber != quadItem->RoomNumber)
{
ItemNewRoom(lara->Vehicle, probe.RoomNumber);
ItemNewRoom(lara->ItemNumber, probe.RoomNumber);
}
laraItem->Position = quadItem->Position;
AnimateQuadBike(laraItem, quadItem, collide, dead);
AnimateItem(laraItem);
quadItem->AnimNumber = Objects[ID_QUAD].animIndex + (laraItem->AnimNumber - Objects[ID_QUAD_LARA_ANIMS].animIndex);
quadItem->FrameNumber = g_Level.Anims[quadItem->AnimNumber].frameBase + (laraItem->FrameNumber - g_Level.Anims[laraItem->AnimNumber].frameBase);
Camera.targetElevation = -ANGLE(30.0f);
if (quad->Flags & QUAD_FLAG_IS_FALLING)
{
if (quadItem->Position.yPos == quadItem->Floor)
{
ExplodingDeath(lara->ItemNumber, 0xffffffff, 1);
laraItem->HitPoints = 0;
laraItem->Flags |= ONESHOT;
QuadbikeExplode(laraItem, quadItem);
return false;
}
}
}
if (laraItem->ActiveState != QUAD_STATE_MOUNT_RIGHT &&
laraItem->ActiveState != QUAD_STATE_MOUNT_LEFT &&
laraItem->ActiveState != QUAD_STATE_DISMOUNT_RIGHT &&
laraItem->ActiveState != QUAD_STATE_DISMOUNT_LEFT)
{
PHD_VECTOR pos;
int speed = 0;
short angle = 0;
for (int i = 0; i < 2; i++)
{
pos.x = quadEffectsPositions[i].x;
pos.y = quadEffectsPositions[i].y;
pos.z = quadEffectsPositions[i].z;
GetJointAbsPosition(quadItem, &pos, quadEffectsPositions[i].meshNum);
angle = quadItem->Position.yRot + ((i == 0) ? 0x9000 : 0x7000);
if (quadItem->VerticalVelocity > 32)
{
if (quadItem->VerticalVelocity < 64)
{
speed = 64 - quadItem->VerticalVelocity;
TriggerQuadExhaustSmoke(pos.x, pos.y, pos.z, angle, speed, 1);
}
}
else
{
if (quad->SmokeStart < 16)
{
speed = ((quad->SmokeStart * 2) + (GetRandomControl() & 7) + (GetRandomControl() & 16)) * 128;
quad->SmokeStart++;
}
else if (quad->DriftStarting)
speed = (abs(quad->Revs) * 2) + ((GetRandomControl() & 7) * 128);
else if ((GetRandomControl() & 3) == 0)
speed = ((GetRandomControl() & 15) + (GetRandomControl() & 16)) * 128;
else
speed = 0;
TriggerQuadExhaustSmoke(pos.x, pos.y, pos.z, angle, speed, 0);
}
}
}
else
quad->SmokeStart = 0;
return QuadCheckGetOff(laraItem, quadItem);
}