mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-05-01 17:28:00 +03:00
1463 lines
38 KiB
C++
1463 lines
38 KiB
C++
#include "framework.h"
|
|
#include "quad.h"
|
|
#include "quad_info.h"
|
|
#include "lara.h"
|
|
#include "effects/effects.h"
|
|
#include "items.h"
|
|
#include "collide.h"
|
|
#include "camera.h"
|
|
#include "effects/tomb4fx.h"
|
|
#include "lara_flare.h"
|
|
#include "lara_one_gun.h"
|
|
#include "misc.h"
|
|
#include "setup.h"
|
|
#include "level.h"
|
|
#include "input.h"
|
|
#include "animation.h"
|
|
#include "Sound/sound.h"
|
|
#include "Specific/prng.h"
|
|
|
|
using std::vector;
|
|
using namespace TEN::Math::Random;
|
|
|
|
#define MAX_VELOCITY 0xA000
|
|
#define MIN_DRIFT_SPEED 0x3000
|
|
#define BRAKE 0x0280
|
|
#define REVERSE_ACC -0x0300
|
|
#define MAX_BACK -0x3000
|
|
#define MAX_REVS 0xa000
|
|
#define TERMINAL_FALLSPEED 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
|
|
#define QUAD_SNOW 500 // Unused.
|
|
|
|
#define QUAD_HIT_LEFT 11
|
|
#define QUAD_HIT_RIGHT 12
|
|
#define QUAD_HIT_FRONT 13
|
|
#define QUAD_HIT_BACK 14
|
|
|
|
#define SMAN_SHOT_DAMAGE 10
|
|
#define SMAN_LARA_DAMAGE 50
|
|
|
|
#define DAMAGE_START 140
|
|
#define DAMAGE_LENGTH 14
|
|
|
|
#define DISMOUNT_DISTANCE 385 // Root bone offset 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 STEP_SIZE
|
|
#define QUAD_MIN_BOUNCE (MAX_VELOCITY / 2) / 256
|
|
|
|
// 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_DUCK | 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 QuadFlags {
|
|
QUAD_FLAG_DEAD = 0x80,
|
|
QUAD_FLAG_IS_FALLING = 0x40
|
|
};
|
|
|
|
enum QuadEffectPosition {
|
|
EXHAUST_LEFT = 0,
|
|
EXHAUST_RIGHT,
|
|
FRONT_LEFT_TYRE,
|
|
FRONT_RIGHT_TYRE,
|
|
BACK_LEFT_TYRE,
|
|
BACK_RIGHT_TYRE
|
|
};
|
|
|
|
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 }
|
|
};
|
|
|
|
bool QuadDriftStarting;
|
|
bool QuadCanDriftStart;
|
|
int QuadSmokeStart;
|
|
bool QuadNoGetOff;
|
|
|
|
void InitialiseQuadBike(short itemNumber)
|
|
{
|
|
ITEM_INFO* quad = &g_Level.Items[itemNumber];
|
|
quad->data = QUAD_INFO();
|
|
QUAD_INFO* quadInfo = quad->data;
|
|
|
|
quadInfo->velocity = 0;
|
|
quadInfo->turnRate = 0;
|
|
quadInfo->leftFallspeed = 0;
|
|
quadInfo->rightFallspeed = 0;
|
|
quadInfo->momentumAngle = quad->pos.yRot;
|
|
quadInfo->extraRotation = 0;
|
|
quadInfo->trackMesh = 0;
|
|
quadInfo->pitch = 0;
|
|
quadInfo->flags = 0;
|
|
}
|
|
|
|
static void QuadbikeExplode(ITEM_INFO* lara, ITEM_INFO* quad)
|
|
{
|
|
LaraInfo*& laraInfo = LaraItem->data;
|
|
|
|
if (g_Level.Rooms[quad->roomNumber].flags & ENV_FLAG_WATER)
|
|
TriggerUnderwaterExplosion(quad, 1);
|
|
else
|
|
{
|
|
TriggerExplosionSparks(quad->pos.xPos, quad->pos.yPos, quad->pos.zPos, 3, -2, 0, quad->roomNumber);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
TriggerExplosionSparks(quad->pos.xPos, quad->pos.yPos, quad->pos.zPos, 3, -1, 0, quad->roomNumber);
|
|
}
|
|
|
|
auto pos = PHD_3DPOS(quad->pos.xPos, quad->pos.yPos - (STEP_SIZE / 2), quad->pos.zPos, 0, quad->pos.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);
|
|
|
|
quad->status = ITEM_DEACTIVATED;
|
|
laraInfo->Vehicle = NO_ITEM;
|
|
}
|
|
|
|
static int CanQuadbikeGetOff(int direction)
|
|
{
|
|
short angle;
|
|
auto item = &g_Level.Items[Lara.Vehicle];
|
|
|
|
if (direction < 0)
|
|
angle = item->pos.yRot - ANGLE(90.0f);
|
|
else
|
|
angle = item->pos.yRot + ANGLE(90.0f);
|
|
|
|
int x = item->pos.xPos + (STEP_SIZE * 2) * phd_sin(angle);
|
|
int y = item->pos.yPos;
|
|
int z = item->pos.zPos + (STEP_SIZE * 2) * phd_cos(angle);
|
|
|
|
auto collResult = GetCollisionResult(x, y, z, item->roomNumber);
|
|
|
|
if (collResult.Position.Slope ||
|
|
collResult.Position.Floor == NO_HEIGHT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (abs(collResult.Position.Floor - item->pos.yPos) > (STEP_SIZE * 2))
|
|
return false;
|
|
|
|
if ((collResult.Position.Ceiling - item->pos.yPos) > -LARA_HEIGHT ||
|
|
(collResult.Position.Floor - collResult.Position.Ceiling) < LARA_HEIGHT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool QuadCheckGetOff(ITEM_INFO* lara, ITEM_INFO* quad)
|
|
{
|
|
LaraInfo*& laraInfo = lara->data;
|
|
auto quadInfo = (QUAD_INFO*)quad->data;
|
|
|
|
if (laraInfo->Vehicle == NO_ITEM)
|
|
return true;
|
|
|
|
if ((lara->currentAnimState == QUAD_STATE_DISMOUNT_RIGHT || lara->currentAnimState == QUAD_STATE_DISMOUNT_LEFT) &&
|
|
TestLastFrame(lara, lara->animNumber))
|
|
{
|
|
if (lara->currentAnimState == QUAD_STATE_DISMOUNT_LEFT)
|
|
lara->pos.yRot += ANGLE(90.0f);
|
|
else
|
|
lara->pos.yRot -= ANGLE(90.0f);
|
|
|
|
SetAnimation(lara, LA_STAND_IDLE);
|
|
lara->pos.xPos -= DISMOUNT_DISTANCE * phd_sin(lara->pos.yRot);
|
|
lara->pos.zPos -= DISMOUNT_DISTANCE * phd_cos(lara->pos.yRot);
|
|
lara->pos.xRot = 0;
|
|
lara->pos.zRot = 0;
|
|
laraInfo->Vehicle = NO_ITEM;
|
|
laraInfo->gunStatus = LG_NO_ARMS;
|
|
|
|
if (lara->currentAnimState == QUAD_STATE_FALL_OFF)
|
|
{
|
|
PHD_VECTOR pos = { 0, 0, 0 };
|
|
|
|
SetAnimation(lara, LA_FREEFALL);
|
|
GetJointAbsPosition(lara, &pos, LM_HIPS);
|
|
|
|
lara->pos.xPos = pos.x;
|
|
lara->pos.yPos = pos.y;
|
|
lara->pos.zPos = pos.z;
|
|
lara->fallspeed = quad->fallspeed;
|
|
lara->gravityStatus = true;
|
|
lara->pos.xRot = 0;
|
|
lara->pos.zRot = 0;
|
|
lara->hitPoints = 0;
|
|
laraInfo->gunStatus = LG_NO_ARMS;
|
|
quad->flags |= ONESHOT;
|
|
|
|
return false;
|
|
}
|
|
else if (lara->currentAnimState == QUAD_STATE_FALL_DEATH)
|
|
{
|
|
quadInfo->flags |= QUAD_FLAG_DEAD;
|
|
lara->goalAnimState = LS_DEATH;
|
|
lara->fallspeed = DAMAGE_START + DAMAGE_LENGTH;
|
|
lara->speed = 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
static int GetOnQuadBike(ITEM_INFO* lara, ITEM_INFO* quad, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& laraInfo = lara->data;
|
|
|
|
if (!(TrInput & IN_ACTION) ||
|
|
lara->gravityStatus ||
|
|
laraInfo->gunStatus != LG_NO_ARMS ||
|
|
quad->flags & ONESHOT ||
|
|
abs(quad->pos.yPos - lara->pos.yPos) > STEP_SIZE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto dist = pow(lara->pos.xPos - quad->pos.xPos, 2) + pow(lara->pos.zPos - quad->pos.zPos, 2);
|
|
if (dist > 170000)
|
|
return false;
|
|
|
|
auto probe = GetCollisionResult(quad);
|
|
if (probe.Position.Floor < -32000)
|
|
return false;
|
|
else
|
|
{
|
|
short angle = phd_atan(quad->pos.zPos - lara->pos.zPos, quad->pos.xPos - lara->pos.xPos);
|
|
angle -= quad->pos.yRot;
|
|
|
|
if ((angle > -ANGLE(45.0f)) && (angle < ANGLE(135.0f)))
|
|
{
|
|
short tempAngle = lara->pos.yRot - quad->pos.yRot;
|
|
if (tempAngle > ANGLE(45.0f) && tempAngle < ANGLE(135.0f))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
short tempAngle = lara->pos.yRot - quad->pos.yRot;
|
|
if (tempAngle > ANGLE(225.0f) && tempAngle < ANGLE(315.0f))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void QuadBaddieCollision(ITEM_INFO* lara, ITEM_INFO* quad)
|
|
{
|
|
vector<short> roomsList;
|
|
roomsList.push_back(quad->roomNumber);
|
|
|
|
ROOM_INFO* room = &g_Level.Rooms[quad->roomNumber];
|
|
for (int i = 0; i < room->doors.size(); i++)
|
|
roomsList.push_back(room->doors[i].room);
|
|
|
|
for (int i = 0; i < roomsList.size(); i++)
|
|
{
|
|
auto itemNum = g_Level.Rooms[roomsList[i]].itemNumber;
|
|
|
|
while (itemNum != NO_ITEM)
|
|
{
|
|
ITEM_INFO* item = &g_Level.Items[itemNum];
|
|
if (item->collidable &&
|
|
item->status != ITEM_INVISIBLE &&
|
|
item != lara &&
|
|
item != quad)
|
|
{
|
|
OBJECT_INFO* object = &Objects[item->objectNumber];
|
|
if (object->collision && object->intelligent)
|
|
{
|
|
auto x = quad->pos.xPos - item->pos.xPos;
|
|
auto y = quad->pos.yPos - item->pos.yPos;
|
|
auto z = quad->pos.zPos - item->pos.zPos;
|
|
|
|
if (x > -4096 && x < 4096 && z > -4096 && z < 4096 && y > -4096 && y < 4096)
|
|
{
|
|
if (TestBoundsCollide(item, quad, QUAD_RADIUS))
|
|
{
|
|
DoLotsOfBlood(item->pos.xPos, quad->pos.yPos - STEP_SIZE, item->pos.zPos, quad->speed, quad->pos.yRot, item->roomNumber, 3);
|
|
item->hitPoints = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
itemNum = item->nextItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int GetQuadCollisionAnim(ITEM_INFO* quad, PHD_VECTOR* p)
|
|
{
|
|
p->x = quad->pos.xPos - p->x;
|
|
p->z = quad->pos.zPos - p->z;
|
|
|
|
if (p->x || p->z)
|
|
{
|
|
float c = phd_cos(quad->pos.yRot);
|
|
float s = phd_sin(quad->pos.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* quad, int dz, int dx, PHD_VECTOR* pos)
|
|
{
|
|
pos->y = quad->pos.yPos - dz * phd_sin(quad->pos.xRot) + dx * phd_sin(quad->pos.zRot);
|
|
|
|
float c = phd_cos(quad->pos.yRot);
|
|
float s = phd_sin(quad->pos.yRot);
|
|
|
|
pos->z = quad->pos.zPos + dz * c - dx * s;
|
|
pos->x = quad->pos.xPos + dz * s + dx * c;
|
|
|
|
auto probe = GetCollisionResult(pos->x, pos->y, pos->z, quad->roomNumber);
|
|
if (probe.Position.Ceiling > pos->y ||
|
|
probe.Position.Ceiling == NO_HEIGHT)
|
|
{
|
|
return NO_HEIGHT;
|
|
}
|
|
|
|
return probe.Position.Floor;
|
|
}
|
|
|
|
static int DoQuadShift(ITEM_INFO* quad, PHD_VECTOR* pos, PHD_VECTOR* old)
|
|
{
|
|
COLL_RESULT probe;
|
|
auto x = pos->x / SECTOR(1);
|
|
auto z = pos->z / SECTOR(1);
|
|
auto oldX = old->x / SECTOR(1);
|
|
auto oldZ = old->z / SECTOR(1);
|
|
auto shiftX = pos->x & (WALL_SIZE - 1);
|
|
auto shiftZ = pos->z & (WALL_SIZE - 1);
|
|
|
|
if (x == oldX)
|
|
{
|
|
if (z == oldZ)
|
|
{
|
|
quad->pos.zPos += (old->z - pos->z);
|
|
quad->pos.xPos += (old->x - pos->x);
|
|
}
|
|
else if (z > oldZ)
|
|
{
|
|
quad->pos.zPos -= shiftZ + 1;
|
|
|
|
return (pos->x - quad->pos.xPos);
|
|
}
|
|
else
|
|
{
|
|
quad->pos.zPos += WALL_SIZE - shiftZ;
|
|
|
|
return (quad->pos.xPos - pos->x);
|
|
}
|
|
}
|
|
else if (z == oldZ)
|
|
{
|
|
if (x > oldX)
|
|
{
|
|
quad->pos.xPos -= shiftX + 1;
|
|
|
|
return (quad->pos.zPos - pos->z);
|
|
}
|
|
else
|
|
{
|
|
quad->pos.xPos += WALL_SIZE - shiftX;
|
|
|
|
return (pos->z - quad->pos.zPos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
x = 0;
|
|
z = 0;
|
|
|
|
probe = GetCollisionResult(old->x, pos->y, pos->z, quad->roomNumber);
|
|
if (probe.Position.Floor < (old->y - STEP_SIZE))
|
|
{
|
|
if (pos->z > old->z)
|
|
z = -shiftZ - 1;
|
|
else
|
|
z = WALL_SIZE - shiftZ;
|
|
}
|
|
|
|
probe = GetCollisionResult(pos->x, pos->y, old->z, quad->roomNumber);
|
|
if (probe.Position.Floor < (old->y - STEP_SIZE))
|
|
{
|
|
if (pos->x > old->x)
|
|
x = -shiftX - 1;
|
|
else
|
|
x = WALL_SIZE - shiftX;
|
|
}
|
|
|
|
if (x && z)
|
|
{
|
|
quad->pos.zPos += z;
|
|
quad->pos.xPos += x;
|
|
}
|
|
else if (z)
|
|
{
|
|
quad->pos.zPos += z;
|
|
|
|
if (z > 0)
|
|
return (quad->pos.xPos - pos->x);
|
|
else
|
|
return (pos->x - quad->pos.xPos);
|
|
}
|
|
else if (x)
|
|
{
|
|
quad->pos.xPos += x;
|
|
|
|
if (x > 0)
|
|
return (pos->z - quad->pos.zPos);
|
|
else
|
|
return (quad->pos.zPos - pos->z);
|
|
}
|
|
else
|
|
{
|
|
quad->pos.zPos += (old->z - pos->z);
|
|
quad->pos.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* lara, ITEM_INFO* quad)
|
|
{
|
|
LaraInfo*& laraInfo = lara->data;
|
|
auto quadInfo = (QUAD_INFO*)quad->data;
|
|
|
|
COLL_RESULT probe;
|
|
PHD_VECTOR moved, fl, fr, br, bl, mtl, mbl, mtr, mbr, mml, mmr;
|
|
PHD_VECTOR old, oldFrontLeft, oldFrontRight, oldBottomLeft, oldBottomRight, mtl_old, moldBottomLeft, mtr_old, moldBottomRight, mml_old, mmr_old;
|
|
int heightFrontLeft, heightFrontRight, heightBackRight, heightBackLeft, hmtl, hmbl, hmtr, hmbr, hmml, hmmr;
|
|
int holdFrontRight, holdFrontLeft, holdBottomRight, holdBottomLeft, hmtl_old, hmoldBottomLeft, hmtr_old, hmoldBottomRight, hmml_old, hmmr_old;
|
|
int slip, collide;
|
|
short rot, rotadd;
|
|
int newspeed;
|
|
|
|
QuadNoGetOff = false;
|
|
|
|
holdFrontLeft = TestQuadHeight(quad, QUAD_FRONT, -QUAD_SIDE, &oldFrontLeft);
|
|
holdFrontRight = TestQuadHeight(quad, QUAD_FRONT, QUAD_SIDE, &oldFrontRight);
|
|
holdBottomLeft = TestQuadHeight(quad, -QUAD_FRONT, -QUAD_SIDE, &oldBottomLeft);
|
|
holdBottomRight = TestQuadHeight(quad, -QUAD_FRONT, QUAD_SIDE, &oldBottomRight);
|
|
hmml_old = TestQuadHeight(quad, 0, -QUAD_SIDE, &mml_old);
|
|
hmmr_old = TestQuadHeight(quad, 0, QUAD_SIDE, &mmr_old);
|
|
hmtl_old = TestQuadHeight(quad, QUAD_FRONT / 2, -QUAD_SIDE, &mtl_old);
|
|
hmtr_old = TestQuadHeight(quad, QUAD_FRONT / 2, QUAD_SIDE, &mtr_old);
|
|
hmoldBottomLeft = TestQuadHeight(quad, -QUAD_FRONT / 2, -QUAD_SIDE, &moldBottomLeft);
|
|
hmoldBottomRight = TestQuadHeight(quad, -QUAD_FRONT / 2, QUAD_SIDE, &moldBottomRight);
|
|
|
|
old.x = quad->pos.xPos;
|
|
old.y = quad->pos.yPos;
|
|
old.z = quad->pos.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 (mtl_old.y > hmtl_old)
|
|
mtl_old.y = hmtl_old;
|
|
if (mtr_old.y > hmtr_old)
|
|
mtr_old.y = hmtr_old;
|
|
if (mml_old.y > hmml_old)
|
|
mml_old.y = hmml_old;
|
|
if (mmr_old.y > hmmr_old)
|
|
mmr_old.y = hmmr_old;
|
|
|
|
if (quad->pos.yPos > (quad->floor - STEP_SIZE))
|
|
{
|
|
short momentum;
|
|
|
|
if (quadInfo->turnRate < -QUAD_UNDO_TURN)
|
|
quadInfo->turnRate += QUAD_UNDO_TURN;
|
|
|
|
else if (quadInfo->turnRate > QUAD_UNDO_TURN)
|
|
quadInfo->turnRate -= QUAD_UNDO_TURN;
|
|
|
|
else
|
|
quadInfo->turnRate = 0;
|
|
|
|
quad->pos.yRot += quadInfo->turnRate + quadInfo->extraRotation;
|
|
|
|
rot = quad->pos.yRot - quadInfo->momentumAngle;
|
|
|
|
momentum = MIN_MOMENTUM_TURN - (((((MIN_MOMENTUM_TURN - MAX_MOMENTUM_TURN) * 256) / MAX_VELOCITY) * quadInfo->velocity) / 256);
|
|
if (!(TrInput & QUAD_IN_ACCELERATE) && quadInfo->velocity > 0)
|
|
momentum += (momentum / 4);
|
|
|
|
if (rot < -MAX_MOMENTUM_TURN)
|
|
{
|
|
if (rot < -QUAD_MAX_MOM_TURN)
|
|
{
|
|
rot = -QUAD_MAX_MOM_TURN;
|
|
quadInfo->momentumAngle = quad->pos.yRot - rot;
|
|
}
|
|
else
|
|
quadInfo->momentumAngle -= momentum;
|
|
}
|
|
else if (rot > MAX_MOMENTUM_TURN)
|
|
{
|
|
if (rot > QUAD_MAX_MOM_TURN)
|
|
{
|
|
rot = QUAD_MAX_MOM_TURN;
|
|
quadInfo->momentumAngle = quad->pos.yRot - rot;
|
|
}
|
|
else
|
|
quadInfo->momentumAngle += momentum;
|
|
}
|
|
else
|
|
quadInfo->momentumAngle = quad->pos.yRot;
|
|
}
|
|
|
|
else
|
|
quad->pos.yRot += quadInfo->turnRate + quadInfo->extraRotation;
|
|
|
|
probe = GetCollisionResult(quad);
|
|
int speed = 0;
|
|
if (quad->pos.yPos >= probe.Position.Floor)
|
|
speed = quad->speed * phd_cos(quad->pos.xRot);
|
|
else
|
|
speed = quad->speed;
|
|
|
|
quad->pos.zPos += speed * phd_cos(quadInfo->momentumAngle);
|
|
quad->pos.xPos += speed * phd_sin(quadInfo->momentumAngle);
|
|
|
|
slip = QUAD_SLIP * phd_sin(quad->pos.xRot);
|
|
if (abs(slip) > QUAD_SLIP / 2)
|
|
{
|
|
if (slip > 0)
|
|
slip -= 10;
|
|
else
|
|
slip += 10;
|
|
quad->pos.zPos -= slip * phd_cos(quad->pos.yRot);
|
|
quad->pos.xPos -= slip * phd_sin(quad->pos.yRot);
|
|
}
|
|
|
|
slip = QUAD_SLIP_SIDE * phd_sin(quad->pos.zRot);
|
|
if (abs(slip) > QUAD_SLIP_SIDE / 2)
|
|
{
|
|
quad->pos.zPos -= slip * phd_sin(quad->pos.yRot);
|
|
quad->pos.xPos += slip * phd_cos(quad->pos.yRot);
|
|
}
|
|
|
|
moved.x = quad->pos.xPos;
|
|
moved.z = quad->pos.zPos;
|
|
|
|
if (!(quad->flags & ONESHOT))
|
|
QuadBaddieCollision(lara, quad);
|
|
|
|
rot = 0;
|
|
|
|
heightFrontLeft = TestQuadHeight(quad, QUAD_FRONT, -QUAD_SIDE, &fl);
|
|
if (heightFrontLeft < oldFrontLeft.y - STEP_SIZE)
|
|
rot = DoQuadShift(quad, &fl, &oldFrontLeft);
|
|
|
|
hmtl = TestQuadHeight(quad, QUAD_FRONT / 2, -QUAD_SIDE, &mtl);
|
|
if (hmtl < mtl_old.y - STEP_SIZE)
|
|
DoQuadShift(quad, &mtl, &mtl_old);
|
|
|
|
hmml = TestQuadHeight(quad, 0, -QUAD_SIDE, &mml);
|
|
if (hmml < mml_old.y - STEP_SIZE)
|
|
DoQuadShift(quad, &mml, &mml_old);
|
|
|
|
hmbl = TestQuadHeight(quad, -QUAD_FRONT / 2, -QUAD_SIDE, &mbl);
|
|
if (hmbl < moldBottomLeft.y - STEP_SIZE)
|
|
DoQuadShift(quad, &mbl, &moldBottomLeft);
|
|
|
|
heightBackLeft = TestQuadHeight(quad, -QUAD_FRONT, -QUAD_SIDE, &bl);
|
|
if (heightBackLeft < oldBottomLeft.y - STEP_SIZE)
|
|
{
|
|
rotadd = DoQuadShift(quad, &bl, &oldBottomLeft);
|
|
if ((rotadd > 0 && rot >= 0) || (rotadd < 0 && rot <= 0))
|
|
rot += rotadd;
|
|
}
|
|
|
|
heightFrontRight = TestQuadHeight(quad, QUAD_FRONT, QUAD_SIDE, &fr);
|
|
if (heightFrontRight < oldFrontRight.y - STEP_SIZE)
|
|
{
|
|
rotadd = DoQuadShift(quad, &fr, &oldFrontRight);
|
|
if ((rotadd > 0 && rot >= 0) || (rotadd < 0 && rot <= 0))
|
|
rot += rotadd;
|
|
}
|
|
|
|
hmtr = TestQuadHeight(quad, QUAD_FRONT / 2, QUAD_SIDE, &mtr);
|
|
if (hmtr < mtr_old.y - STEP_SIZE)
|
|
DoQuadShift(quad, &mtr, &mtr_old);
|
|
|
|
hmmr = TestQuadHeight(quad, 0, QUAD_SIDE, &mmr);
|
|
if (hmmr < mmr_old.y - STEP_SIZE)
|
|
DoQuadShift(quad, &mmr, &mmr_old);
|
|
|
|
hmbr = TestQuadHeight(quad, -QUAD_FRONT / 2, QUAD_SIDE, &mbr);
|
|
if (hmbr < moldBottomRight.y - STEP_SIZE)
|
|
DoQuadShift(quad, &mbr, &moldBottomRight);
|
|
|
|
heightBackRight = TestQuadHeight(quad, -QUAD_FRONT, QUAD_SIDE, &br);
|
|
if (heightBackRight < oldBottomRight.y - STEP_SIZE)
|
|
{
|
|
rotadd = DoQuadShift(quad, &br, &oldBottomRight);
|
|
if ((rotadd > 0 && rot >= 0) || (rotadd < 0 && rot <= 0))
|
|
rot += rotadd;
|
|
}
|
|
|
|
probe = GetCollisionResult(quad);
|
|
if (probe.Position.Floor < quad->pos.yPos - STEP_SIZE)
|
|
DoQuadShift(quad, (PHD_VECTOR*)&quad->pos, &old);
|
|
|
|
quadInfo->extraRotation = rot;
|
|
|
|
collide = GetQuadCollisionAnim(quad, &moved);
|
|
|
|
if (collide)
|
|
{
|
|
newspeed = (quad->pos.zPos - old.z) * phd_cos(quadInfo->momentumAngle) + (quad->pos.xPos - old.x) * phd_sin(quadInfo->momentumAngle);
|
|
|
|
newspeed *= 256;
|
|
|
|
if (&g_Level.Items[laraInfo->Vehicle] == quad &&
|
|
quadInfo->velocity == MAX_VELOCITY &&
|
|
newspeed < (quadInfo->velocity - 10))
|
|
{
|
|
lara->hitPoints -= (quadInfo->velocity - newspeed) / 128;
|
|
lara->hitStatus = 1;
|
|
}
|
|
|
|
if (quadInfo->velocity > 0 && newspeed < quadInfo->velocity)
|
|
quadInfo->velocity = (newspeed < 0) ? 0 : newspeed;
|
|
|
|
else if (quadInfo->velocity < 0 && newspeed > quadInfo->velocity)
|
|
quadInfo->velocity = (newspeed > 0) ? 0 : newspeed;
|
|
|
|
if (quadInfo->velocity < MAX_BACK)
|
|
quadInfo->velocity = MAX_BACK;
|
|
}
|
|
|
|
return collide;
|
|
}
|
|
|
|
static void AnimateQuadBike(ITEM_INFO* lara, ITEM_INFO* quad, int collide, int dead)
|
|
{
|
|
auto quadInfo = (QUAD_INFO*)quad->data;
|
|
|
|
if (quad->pos.yPos != quad->floor &&
|
|
lara->currentAnimState != QUAD_STATE_FALL &&
|
|
lara->currentAnimState != QUAD_STATE_LAND &&
|
|
lara->currentAnimState != QUAD_STATE_FALL_OFF &&
|
|
!dead)
|
|
{
|
|
if (quadInfo->velocity < 0)
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_LEAP_START;
|
|
else
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_LEAP_START2;
|
|
|
|
lara->frameNumber = GetFrameNumber(lara, lara->animNumber);
|
|
lara->currentAnimState = QUAD_STATE_FALL;
|
|
lara->goalAnimState = QUAD_STATE_FALL;
|
|
}
|
|
else if (collide &&
|
|
lara->currentAnimState != QUAD_STATE_HIT_FRONT &&
|
|
lara->currentAnimState != QUAD_STATE_HIT_BACK &&
|
|
lara->currentAnimState != QUAD_STATE_HIT_LEFT &&
|
|
lara->currentAnimState != QUAD_STATE_HIT_RIGHT &&
|
|
lara->currentAnimState != QUAD_STATE_FALL_OFF &&
|
|
quadInfo->velocity > (MAX_VELOCITY / 3) &&
|
|
!dead)
|
|
{
|
|
if (collide == QUAD_HIT_FRONT)
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_BACK;
|
|
lara->currentAnimState = QUAD_STATE_HIT_FRONT;
|
|
lara->goalAnimState = QUAD_STATE_HIT_FRONT;
|
|
}
|
|
else if (collide == QUAD_HIT_BACK)
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_FRONT;
|
|
lara->currentAnimState = QUAD_STATE_HIT_BACK;
|
|
lara->goalAnimState = QUAD_STATE_HIT_BACK;
|
|
}
|
|
else if (collide == QUAD_HIT_LEFT)
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_RIGHT;
|
|
lara->currentAnimState = QUAD_STATE_HIT_LEFT;
|
|
lara->goalAnimState = QUAD_STATE_HIT_LEFT;
|
|
}
|
|
else
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_LEFT;
|
|
lara->currentAnimState = QUAD_STATE_HIT_RIGHT;
|
|
lara->goalAnimState = QUAD_STATE_HIT_RIGHT;
|
|
}
|
|
|
|
lara->frameNumber = GetFrameNumber(lara, lara->animNumber);
|
|
SoundEffect(SFX_TR3_QUAD_FRONT_IMPACT, &quad->pos, 0);
|
|
}
|
|
else
|
|
{
|
|
switch (lara->currentAnimState)
|
|
{
|
|
case QUAD_STATE_IDLE:
|
|
if (dead)
|
|
lara->goalAnimState = QUAD_STATE_BIKE_DEATH;
|
|
else if (TrInput & QUAD_IN_DISMOUNT &&
|
|
quadInfo->velocity == 0 &&
|
|
!QuadNoGetOff)
|
|
{
|
|
if (TrInput & QUAD_IN_LEFT && CanQuadbikeGetOff(-1))
|
|
lara->goalAnimState = QUAD_STATE_DISMOUNT_LEFT;
|
|
else if (TrInput & QUAD_IN_RIGHT && CanQuadbikeGetOff(1))
|
|
lara->goalAnimState = QUAD_STATE_DISMOUNT_RIGHT;
|
|
}
|
|
else if (TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE))
|
|
lara->goalAnimState = QUAD_STATE_DRIVE;
|
|
|
|
break;
|
|
|
|
case QUAD_STATE_DRIVE:
|
|
if (dead)
|
|
{
|
|
if (quadInfo->velocity > (MAX_VELOCITY / 2))
|
|
lara->goalAnimState = QUAD_STATE_FALL_DEATH;
|
|
else
|
|
lara->goalAnimState = QUAD_STATE_BIKE_DEATH;
|
|
}
|
|
else if (!(TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) &&
|
|
(quadInfo->velocity / 256) == 0)
|
|
{
|
|
lara->goalAnimState = QUAD_STATE_IDLE;
|
|
}
|
|
else if (TrInput & QUAD_IN_LEFT &&
|
|
!QuadDriftStarting)
|
|
{
|
|
lara->goalAnimState = QUAD_STATE_TURN_LEFT;
|
|
}
|
|
else if (TrInput & QUAD_IN_RIGHT &&
|
|
!QuadDriftStarting)
|
|
{
|
|
lara->goalAnimState = QUAD_STATE_TURN_RIGHT;
|
|
}
|
|
else if (TrInput & QUAD_IN_BRAKE)
|
|
{
|
|
if (quadInfo->velocity > (MAX_VELOCITY / 3 * 2))
|
|
lara->goalAnimState = QUAD_STATE_BRAKE;
|
|
else
|
|
lara->goalAnimState = QUAD_STATE_SLOW;
|
|
}
|
|
|
|
break;
|
|
|
|
case QUAD_STATE_BRAKE:
|
|
case QUAD_STATE_SLOW:
|
|
case QUAD_STATE_STOP_SLOWLY:
|
|
if ((quadInfo->velocity / 256) == 0)
|
|
lara->goalAnimState = QUAD_STATE_IDLE;
|
|
else if (TrInput & QUAD_IN_LEFT)
|
|
lara->goalAnimState = QUAD_STATE_TURN_LEFT;
|
|
else if (TrInput & QUAD_IN_RIGHT)
|
|
lara->goalAnimState = QUAD_STATE_TURN_RIGHT;
|
|
|
|
break;
|
|
|
|
case QUAD_STATE_TURN_LEFT:
|
|
if ((quadInfo->velocity / 256) == 0)
|
|
lara->goalAnimState = QUAD_STATE_IDLE;
|
|
else if (TrInput & QUAD_IN_RIGHT)
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_TURN_RIGHT_START;
|
|
lara->frameNumber = GetFrameNumber(lara, lara->animNumber);
|
|
lara->currentAnimState = QUAD_STATE_TURN_RIGHT;
|
|
lara->goalAnimState = QUAD_STATE_TURN_RIGHT;
|
|
}
|
|
else if (!(TrInput & QUAD_IN_LEFT))
|
|
lara->goalAnimState = QUAD_STATE_DRIVE;
|
|
|
|
break;
|
|
|
|
case QUAD_STATE_TURN_RIGHT:
|
|
if ((quadInfo->velocity / 256) == 0)
|
|
lara->goalAnimState = QUAD_STATE_IDLE;
|
|
else if (TrInput & QUAD_IN_LEFT)
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_TURN_LEFT_START;
|
|
lara->frameNumber = GetFrameNumber(lara, lara->animNumber);
|
|
lara->currentAnimState = QUAD_STATE_TURN_LEFT;
|
|
lara->goalAnimState = QUAD_STATE_TURN_LEFT;
|
|
}
|
|
else if (!(TrInput & QUAD_IN_RIGHT))
|
|
lara->goalAnimState = QUAD_STATE_DRIVE;
|
|
|
|
break;
|
|
|
|
case QUAD_STATE_FALL:
|
|
if (quad->pos.yPos == quad->floor)
|
|
lara->goalAnimState = QUAD_STATE_LAND;
|
|
else if (quad->fallspeed > TERMINAL_FALLSPEED)
|
|
quadInfo->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))
|
|
lara->goalAnimState = QUAD_STATE_DRIVE;
|
|
|
|
break;
|
|
}
|
|
|
|
if (g_Level.Rooms[quad->roomNumber].flags & (ENV_FLAG_WATER | ENV_FLAG_SWAMP))
|
|
{
|
|
lara->goalAnimState = QUAD_STATE_FALL_OFF;
|
|
lara->pos.yPos = quad->pos.yPos + 700;
|
|
lara->roomNumber = quad->roomNumber;
|
|
lara->hitPoints = 0;
|
|
QuadbikeExplode(lara, quad);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int QuadUserControl(ITEM_INFO* quad, int height, int* pitch)
|
|
{
|
|
auto quadInfo = (QUAD_INFO*)quad->data;
|
|
bool drive = false; // Never changes?
|
|
|
|
if (!(TrInput & QUAD_IN_DRIFT) &&
|
|
!quadInfo->velocity &&
|
|
!QuadCanDriftStart)
|
|
{
|
|
QuadCanDriftStart = true;
|
|
}
|
|
else if (quadInfo->velocity)
|
|
QuadCanDriftStart = false;
|
|
|
|
if (!(TrInput & QUAD_IN_DRIFT))
|
|
QuadDriftStarting = false;
|
|
|
|
if (!QuadDriftStarting)
|
|
{
|
|
if (quadInfo->revs > 0x10)
|
|
{
|
|
quadInfo->velocity += (quadInfo->revs / 16);
|
|
quadInfo->revs -= (quadInfo->revs / 8);
|
|
}
|
|
else
|
|
quadInfo->revs = 0;
|
|
}
|
|
|
|
if (quad->pos.yPos >= (height - STEP_SIZE))
|
|
{
|
|
if (TrInput & IN_LOOK &&
|
|
!quadInfo->velocity)
|
|
{
|
|
LookUpDown();
|
|
}
|
|
|
|
// Driving forward.
|
|
if (quadInfo->velocity > 0)
|
|
{
|
|
if (TrInput & QUAD_IN_DRIFT &&
|
|
!QuadDriftStarting &&
|
|
quadInfo->velocity > MIN_DRIFT_SPEED)
|
|
{
|
|
if (TrInput & QUAD_IN_LEFT)
|
|
{
|
|
quadInfo->turnRate -= QUAD_DRIFT_TURN_RATE;
|
|
if (quadInfo->turnRate < -QUAD_DRIFT_TURN_MAX)
|
|
quadInfo->turnRate = -QUAD_DRIFT_TURN_MAX;
|
|
}
|
|
else if (TrInput & QUAD_IN_RIGHT)
|
|
{
|
|
quadInfo->turnRate += QUAD_DRIFT_TURN_RATE;
|
|
if (quadInfo->turnRate > QUAD_DRIFT_TURN_MAX)
|
|
quadInfo->turnRate = QUAD_DRIFT_TURN_MAX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TrInput & QUAD_IN_LEFT)
|
|
{
|
|
quadInfo->turnRate -= QUAD_TURN_RATE;
|
|
if (quadInfo->turnRate < -QUAD_TURN_MAX)
|
|
quadInfo->turnRate = -QUAD_TURN_MAX;
|
|
}
|
|
else if (TrInput & QUAD_IN_RIGHT)
|
|
{
|
|
quadInfo->turnRate += QUAD_TURN_RATE;
|
|
if (quadInfo->turnRate > QUAD_TURN_MAX)
|
|
quadInfo->turnRate = QUAD_TURN_MAX;
|
|
}
|
|
}
|
|
}
|
|
// Driving back.
|
|
else if (quadInfo->velocity < 0)
|
|
{
|
|
if (TrInput & QUAD_IN_DRIFT &&
|
|
!QuadDriftStarting &&
|
|
quadInfo->velocity < (-MIN_DRIFT_SPEED + 0x800))
|
|
{
|
|
if (TrInput & QUAD_IN_LEFT)
|
|
{
|
|
quadInfo->turnRate -= QUAD_DRIFT_TURN_RATE;
|
|
if (quadInfo->turnRate < -QUAD_DRIFT_TURN_MAX)
|
|
quadInfo->turnRate = -QUAD_DRIFT_TURN_MAX;
|
|
}
|
|
else if (TrInput & QUAD_IN_RIGHT)
|
|
{
|
|
quadInfo->turnRate += QUAD_DRIFT_TURN_RATE;
|
|
if (quadInfo->turnRate > QUAD_DRIFT_TURN_MAX)
|
|
quadInfo->turnRate = QUAD_DRIFT_TURN_MAX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TrInput & QUAD_IN_RIGHT)
|
|
{
|
|
quadInfo->turnRate -= QUAD_TURN_RATE;
|
|
if (quadInfo->turnRate < -QUAD_TURN_MAX)
|
|
quadInfo->turnRate = -QUAD_TURN_MAX;
|
|
}
|
|
else if (TrInput & QUAD_IN_LEFT)
|
|
{
|
|
quadInfo->turnRate += QUAD_TURN_RATE;
|
|
if (quadInfo->turnRate > QUAD_TURN_MAX)
|
|
quadInfo->turnRate = QUAD_TURN_MAX;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Driving back / braking.
|
|
if (TrInput & QUAD_IN_BRAKE)
|
|
{
|
|
if (TrInput & QUAD_IN_DRIFT &&
|
|
(QuadCanDriftStart || QuadDriftStarting))
|
|
{
|
|
QuadDriftStarting = true;
|
|
quadInfo->revs -= 0x200;
|
|
if (quadInfo->revs < MAX_BACK)
|
|
quadInfo->revs = MAX_BACK;
|
|
}
|
|
else if (quadInfo->velocity > 0)
|
|
quadInfo->velocity -= BRAKE;
|
|
else
|
|
{
|
|
if (quadInfo->velocity > MAX_BACK)
|
|
quadInfo->velocity += REVERSE_ACC;
|
|
}
|
|
}
|
|
else if (TrInput & QUAD_IN_ACCELERATE)
|
|
{
|
|
if (TrInput & QUAD_IN_DRIFT &&
|
|
(QuadCanDriftStart || QuadDriftStarting))
|
|
{
|
|
QuadDriftStarting = true;
|
|
quadInfo->revs += 0x200;
|
|
if (quadInfo->revs >= MAX_VELOCITY)
|
|
quadInfo->revs = MAX_VELOCITY;
|
|
}
|
|
else if (quadInfo->velocity < MAX_VELOCITY)
|
|
{
|
|
if (quadInfo->velocity < 0x4000)
|
|
quadInfo->velocity += (8 + (0x4000 + 0x800 - quadInfo->velocity) / 8);
|
|
else if (quadInfo->velocity < 0x7000)
|
|
quadInfo->velocity += (4 + (0x7000 + 0x800 - quadInfo->velocity) / 16);
|
|
else if (quadInfo->velocity < MAX_VELOCITY)
|
|
quadInfo->velocity += (2 + (MAX_VELOCITY - quadInfo->velocity) / 8);
|
|
}
|
|
else
|
|
quadInfo->velocity = MAX_VELOCITY;
|
|
|
|
quadInfo->velocity -= abs(quad->pos.yRot - quadInfo->momentumAngle) / 64;
|
|
}
|
|
|
|
else if (quadInfo->velocity > 0x0100)
|
|
quadInfo->velocity -= 0x0100;
|
|
else if (quadInfo->velocity < -0x0100)
|
|
quadInfo->velocity += 0x0100;
|
|
else
|
|
quadInfo->velocity = 0;
|
|
|
|
if (!(TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) &&
|
|
QuadDriftStarting &&
|
|
quadInfo->revs)
|
|
{
|
|
if (quadInfo->revs > 0x8)
|
|
quadInfo->revs -= quadInfo->revs / 8;
|
|
else
|
|
quadInfo->revs = 0;
|
|
}
|
|
|
|
quad->speed = quadInfo->velocity / 256;
|
|
|
|
if (quadInfo->engineRevs > 0x7000)
|
|
quadInfo->engineRevs = -0x2000;
|
|
|
|
int revs = 0;
|
|
if (quadInfo->velocity < 0)
|
|
revs = abs(quadInfo->velocity / 2);
|
|
else if (quadInfo->velocity < 0x7000)
|
|
revs = -0x2000 + (quadInfo->velocity * (0x6800 - -0x2000)) / 0x7000;
|
|
else if (quadInfo->velocity <= MAX_VELOCITY)
|
|
revs = -0x2800 + ((quadInfo->velocity - 0x7000) * (0x7000 - -0x2800)) / (MAX_VELOCITY - 0x7000);
|
|
|
|
revs += abs(quadInfo->revs);
|
|
quadInfo->engineRevs += (revs - quadInfo->engineRevs) / 8;
|
|
}
|
|
else
|
|
{
|
|
if (quadInfo->engineRevs < 0xA000)
|
|
quadInfo->engineRevs += (0xA000 - quadInfo->engineRevs) / 8;
|
|
}
|
|
|
|
*pitch = quadInfo->engineRevs;
|
|
|
|
return drive;
|
|
}
|
|
|
|
void QuadBikeCollision(short itemNumber, ITEM_INFO* lara, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& laraInfo = lara->data;
|
|
ITEM_INFO* quad = &g_Level.Items[itemNumber];
|
|
auto quadInfo = (QUAD_INFO*)quad->data;
|
|
|
|
if (lara->hitPoints < 0 || laraInfo->Vehicle != NO_ITEM)
|
|
return;
|
|
|
|
if (GetOnQuadBike(lara, &g_Level.Items[itemNumber], coll))
|
|
{
|
|
short ang;
|
|
|
|
laraInfo->Vehicle = itemNumber;
|
|
|
|
if (laraInfo->gunType == WEAPON_FLARE)
|
|
{
|
|
CreateFlare(lara, ID_FLARE_ITEM, 0);
|
|
undraw_flare_meshes(lara);
|
|
laraInfo->flareControlLeft = 0;
|
|
laraInfo->requestGunType = laraInfo->gunType = WEAPON_NONE;
|
|
}
|
|
|
|
laraInfo->gunStatus = LG_HANDS_BUSY;
|
|
|
|
ang = phd_atan(quad->pos.zPos - lara->pos.zPos, quad->pos.xPos - lara->pos.xPos);
|
|
ang -= quad->pos.yRot;
|
|
|
|
if (ang > -ANGLE(45.0f) && ang < ANGLE(135.0f))
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_MOUNT_LEFT;
|
|
lara->currentAnimState = lara->goalAnimState = QUAD_STATE_MOUNT_LEFT;
|
|
}
|
|
else
|
|
{
|
|
lara->animNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_MOUNT_RIGHT;
|
|
lara->currentAnimState = lara->goalAnimState = QUAD_STATE_MOUNT_RIGHT;
|
|
}
|
|
|
|
lara->frameNumber = g_Level.Anims[lara->animNumber].frameBase;
|
|
|
|
quad->hitPoints = 1;
|
|
lara->pos.xPos = quad->pos.xPos;
|
|
lara->pos.yPos = quad->pos.yPos;
|
|
lara->pos.zPos = quad->pos.zPos;
|
|
lara->pos.yRot = quad->pos.yRot;
|
|
laraInfo->headXrot = laraInfo->headYrot = 0;
|
|
laraInfo->torsoXrot = laraInfo->torsoYrot = 0;
|
|
laraInfo->hitDirection = -1;
|
|
|
|
AnimateItem(lara);
|
|
|
|
quadInfo->revs = 0;
|
|
}
|
|
else
|
|
ObjectCollision(itemNumber, lara, coll);
|
|
}
|
|
|
|
static void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int speed, int moving)
|
|
{
|
|
SPARKS* 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;
|
|
}
|
|
|
|
int QuadBikeControl(ITEM_INFO* lara, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& laraInfo = lara->data;
|
|
ITEM_INFO* quad = &g_Level.Items[laraInfo->Vehicle];
|
|
auto quadInfo = (QUAD_INFO*)quad->data;
|
|
|
|
short xRot, zRot, rotadd;
|
|
int pitch, dead = 0;
|
|
|
|
GAME_VECTOR oldpos;
|
|
oldpos.x = quad->pos.xPos;
|
|
oldpos.y = quad->pos.yPos;
|
|
oldpos.z = quad->pos.zPos;
|
|
oldpos.roomNumber = quad->roomNumber;
|
|
|
|
bool collide = QuadDynamics(lara, quad);
|
|
|
|
short roomNumber = quad->roomNumber;
|
|
FLOOR_INFO* floor = GetFloor(quad->pos.xPos, quad->pos.yPos, quad->pos.zPos, &roomNumber);
|
|
auto height = GetFloorHeight(floor, quad->pos.xPos, quad->pos.yPos, quad->pos.zPos);
|
|
auto ceiling = GetCeiling(floor, quad->pos.xPos, quad->pos.yPos, quad->pos.zPos);
|
|
|
|
PHD_VECTOR frontLeft;
|
|
PHD_VECTOR frontRight;
|
|
auto floorHeightLeft = TestQuadHeight(quad, QUAD_FRONT, -QUAD_SIDE, &frontLeft);
|
|
auto floorHeightRight = TestQuadHeight(quad, QUAD_FRONT, QUAD_SIDE, &frontRight);
|
|
|
|
TestTriggers(quad, false);
|
|
|
|
if (lara->hitPoints <= 0)
|
|
{
|
|
TrInput &= ~(IN_LEFT | IN_RIGHT | IN_BACK | IN_FORWARD);
|
|
dead = 1;
|
|
}
|
|
|
|
int drive = -1;
|
|
|
|
if (quadInfo->flags)
|
|
collide = false;
|
|
else
|
|
{
|
|
switch (lara->currentAnimState)
|
|
{
|
|
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(quad, height, &pitch);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (quadInfo->velocity || quadInfo->revs)
|
|
{
|
|
int absvel = abs(quadInfo->velocity) + 1; // unused?
|
|
quadInfo->pitch = pitch;
|
|
if (quadInfo->pitch < -0x8000)
|
|
quadInfo->pitch = -0x8000;
|
|
else if (quadInfo->pitch > 0xA000)
|
|
quadInfo->pitch = 0xA000;
|
|
|
|
SoundEffect(SFX_TR3_QUAD_MOVE, &quad->pos, 0, 0.5f + (float)abs(quadInfo->pitch) / (float)MAX_VELOCITY);
|
|
}
|
|
else
|
|
{
|
|
if (drive != -1)
|
|
SoundEffect(SFX_TR3_QUAD_IDLE, &quad->pos, 0);
|
|
|
|
quadInfo->pitch = 0;
|
|
}
|
|
|
|
quad->floor = height;
|
|
|
|
rotadd = quadInfo->velocity / 4;
|
|
quadInfo->rearRot -= rotadd;
|
|
quadInfo->rearRot -= (quadInfo->revs / 8);
|
|
quadInfo->frontRot -= rotadd;
|
|
|
|
quadInfo->leftFallspeed = DoQuadDynamics(floorHeightLeft, quadInfo->leftFallspeed, (int*)&frontLeft.y);
|
|
quadInfo->rightFallspeed = DoQuadDynamics(floorHeightRight, quadInfo->rightFallspeed, (int*)&frontRight.y);
|
|
quad->fallspeed = DoQuadDynamics(height, quad->fallspeed, (int*)&quad->pos.yPos);
|
|
|
|
height = (frontLeft.y + frontRight.y) / 2;
|
|
xRot = phd_atan(QUAD_FRONT, quad->pos.yPos - height);
|
|
zRot = phd_atan(QUAD_SIDE, height - frontLeft.y);
|
|
|
|
quad->pos.xRot += ((xRot - quad->pos.xRot) / 2);
|
|
quad->pos.zRot += ((zRot - quad->pos.zRot) / 2);
|
|
|
|
if (!(quadInfo->flags & QUAD_FLAG_DEAD))
|
|
{
|
|
if (roomNumber != quad->roomNumber)
|
|
{
|
|
ItemNewRoom(laraInfo->Vehicle, roomNumber);
|
|
ItemNewRoom(laraInfo->itemNumber, roomNumber);
|
|
}
|
|
|
|
lara->pos.xPos = quad->pos.xPos;
|
|
lara->pos.yPos = quad->pos.yPos;
|
|
lara->pos.zPos = quad->pos.zPos;
|
|
lara->pos.xRot = quad->pos.xRot;
|
|
lara->pos.yRot = quad->pos.yRot;
|
|
lara->pos.zRot = quad->pos.zRot;
|
|
|
|
AnimateQuadBike(lara, quad, collide, dead);
|
|
AnimateItem(lara);
|
|
|
|
quad->animNumber = Objects[ID_QUAD].animIndex + (lara->animNumber - Objects[ID_QUAD_LARA_ANIMS].animIndex);
|
|
quad->frameNumber = g_Level.Anims[quad->animNumber].frameBase + (lara->frameNumber - g_Level.Anims[lara->animNumber].frameBase);
|
|
|
|
Camera.targetElevation = -ANGLE(30.0f);
|
|
|
|
if (quadInfo->flags & QUAD_FLAG_IS_FALLING)
|
|
{
|
|
if (quad->pos.yPos == quad->floor)
|
|
{
|
|
ExplodingDeath(laraInfo->itemNumber, 0xffffffff, 1);
|
|
lara->hitPoints = 0;
|
|
lara->flags |= ONESHOT;
|
|
QuadbikeExplode(lara, quad);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lara->currentAnimState != QUAD_STATE_MOUNT_RIGHT &&
|
|
lara->currentAnimState != QUAD_STATE_MOUNT_LEFT &&
|
|
lara->currentAnimState != QUAD_STATE_DISMOUNT_RIGHT &&
|
|
lara->currentAnimState != 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(quad, &pos, quadEffectsPositions[i].meshNum);
|
|
angle = quad->pos.yRot + ((i == 0) ? 0x9000 : 0x7000);
|
|
if (quad->speed > 32)
|
|
{
|
|
if (quad->speed < 64)
|
|
{
|
|
speed = 64 - quad->speed;
|
|
TriggerQuadExhaustSmoke(pos.x, pos.y, pos.z, angle, speed, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (QuadSmokeStart < 16)
|
|
{
|
|
speed = ((QuadSmokeStart * 2) + (GetRandomControl() & 7) + (GetRandomControl() & 16)) * 128;
|
|
QuadSmokeStart++;
|
|
}
|
|
else if (QuadDriftStarting)
|
|
speed = (abs(quadInfo->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
|
|
QuadSmokeStart = 0;
|
|
|
|
return QuadCheckGetOff(lara, quad);
|
|
}
|