TombEngine/TR5Main/Objects/Vehicles/kayak.cpp
2019-12-15 16:19:01 +01:00

1368 lines
No EOL
32 KiB
C++

#include "../newobjects.h"
#include "../../Game/effects.h"
#include "../../Game/effect2.h"
#include "../../Game/draw.h"
#include "../../Game/camera.h"
#include "../../Game/lara.h"
#include "../../Game/collide.h"
#include "../../Game/laraflar.h"
#include "../../Game/items.h"
extern LaraExtraInfo g_LaraExtra;
#define MAX_SPEED 0x380000
#define KAYAK_COLLIDE 64
#define GETOFF_DIST CLICK(3) // minimum collision distance
#define KAYAK_TO_BADDIE_RADIUS CLICK(1)
#define KAYAK_FRICTION 0x8000
#define KAYAK_ROT_FRIC 0x50000
#define KAYAK_DFLECT_ROT 0x80000
#define KAYAK_FWD_VEL 0x180000
#define KAYAK_FWD_ROT 0x800000
#define KAYAK_LR_VEL 0x100000
#define KAYAK_LR_ROT 0xc00000
#define KAYAK_MAX_LR 0xc00000
#define KAYAK_TURN_ROT 0x200000
#define KAYAK_MAX_TURN 0x1000000
#define KAYAK_TURN_BRAKE 0x8000
#define KAYAK_HARD_ROT 0x1000000
#define KAYAK_MAX_STAT 0x1000000
#define BOAT_SLIP 50
#define BOAT_SIDE_SLIP 50
#define HIT_BACK 1
#define HIT_FRONT 2
#define HIT_LEFT 3
#define HIT_RIGHT 4
#define KAYAK_DRAW_SHIFT 32
#define LARA_LEG_BITS (MESH_BITS(LM_HIPS) | MESH_BITS(LM_LTHIGH) | MESH_BITS(LM_LSHIN) | MESH_BITS(LM_LFOOT) | MESH_BITS(LM_RTHIGH) | MESH_BITS(LM_RSHIN) | MESH_BITS(LM_RFOOT))
#define KAYAK_X STEP_SIZE/2
#define KAYAK_Z STEP_SIZE/2
#define SKIDOO_MAX_KICK -80
#define SKIDOO_MIN_BOUNCE ((MAX_SPEED/2)>>8)
enum KAYAK_STATE {
KS_BACK,
KS_POSE,
KS_LEFT,
KS_RIGHT,
KS_CLIMBIN,
KS_DEATHIN,
KS_FORWARD,
KS_ROLL,
KS_DROWNIN,
KS_JUMPOUT,
KS_TURNL,
KS_TURNR,
KS_CLIMBINR,
KS_CLIMBOUTL,
KS_CLIMBOUTR,
};
void DoKayakRipple(ITEM_INFO* v, short xoff, short zoff)
{
RIPPLE_STRUCT* r;
int s, c, x, z;
FLOOR_INFO* floor;
short room_number;
c = COS(v->pos.yRot);
s = SIN(v->pos.yRot);
x = v->pos.xPos + (((zoff * s) + (xoff * c)) >> W2V_SHIFT);
z = v->pos.zPos + (((zoff * c) - (xoff * s)) >> W2V_SHIFT);
room_number = v->roomNumber;
floor = GetFloor(x, v->pos.yPos, z, &room_number);
if (GetWaterHeight(x, v->pos.yPos, z, room_number) != NO_HEIGHT)
{
SetupRipple(x, v->pos.yPos, z, -2 - (GetRandomControl() & 1), 0);
}
}
void KayakSplash(ITEM_INFO* item, long fallspeed, long water)
{
/*
SplashSetup.x = item->pos.xPos;
SplashSetup.y = water;
SplashSetup.z = item->pos.zPos;
SplashSetup.InnerXZoff = 16 << 3;
SplashSetup.InnerXZsize = 12 << 2;
SplashSetup.InnerYsize = -96 << 2;
SplashSetup.InnerXZvel = 0xa0;
SplashSetup.InnerYvel = -fallspeed << 5;
SplashSetup.InnerGravity = 0x80;
SplashSetup.InnerFriction = 7;
SplashSetup.MiddleXZoff = 24 << 3;
SplashSetup.MiddleXZsize = 24 << 2;
SplashSetup.MiddleYsize = -64 << 2;
SplashSetup.MiddleXZvel = 0xe0;
SplashSetup.MiddleYvel = -fallspeed << 4;
SplashSetup.MiddleGravity = 0x48;
SplashSetup.MiddleFriction = 8;
SplashSetup.OuterXZoff = 32 << 3;
SplashSetup.OuterXZsize = 32 << 2;
SplashSetup.OuterXZvel = 0x110;
SplashSetup.OuterFriction = -9;
SetupSplash(&SplashSetup);
SplashCount = 16;
*/
}
void TriggerRapidsMist(long x, long y, long z)
{
SPARKS* sptr;
long xsize;
sptr = &Sparks[GetFreeSpark()];
sptr->on = 1;
sptr->sR = 128;
sptr->sG = 128;
sptr->sB = 128;
sptr->dR = 192;
sptr->dG = 192;
sptr->dB = 192;
sptr->colFadeSpeed = 2;
sptr->fadeToBlack = 4; // 8
sptr->sLife = sptr->life = 6 + (GetRandomControl() & 3);
sptr->transType = 1;
sptr->extras = 0;
sptr->dynamic = -1;
sptr->x = x + ((GetRandomControl() & 15) - 8);
sptr->y = y + ((GetRandomControl() & 15) - 8);
sptr->z = z + ((GetRandomControl() & 15) - 8);
sptr->xVel = ((GetRandomControl() & 255) - 128);
sptr->zVel = ((GetRandomControl() & 255) - 128);
sptr->yVel = ((GetRandomControl() & 255) - 128);
sptr->friction = 3;
if (GetRandomControl() & 1)
{
sptr->flags = SP_SCALE|SP_ROTATE|SP_EXPDEF;
sptr->rotAng = GetRandomControl() & 4095;
if (GetRandomControl() & 1)
sptr->rotAdd = -(GetRandomControl() & 15) - 16;
else
sptr->rotAdd = (GetRandomControl() & 15) + 16;
}
else
{
sptr->flags = SP_SCALE;
}
sptr->scalar = 4;
sptr->gravity = 0;
sptr->maxYvel = 0;
xsize = (GetRandomControl() & 7) + 16;
sptr->size = sptr->sSize = xsize >> 1;
sptr->dSize = xsize;
}
int GetInKayak(short item_number, COLL_INFO* coll)
{
int dist;
int x, z;
ITEM_INFO* kayak;
FLOOR_INFO* floor;
short room_number;
if (!(TrInput & IN_ACTION) || Lara.gunStatus != LG_NO_ARMS || LaraItem->gravityStatus)
return 0;
kayak = &Items[item_number];
/* -------- is Lara close enough to use the vehicle */
x = LaraItem->pos.xPos - kayak->pos.xPos;
z = LaraItem->pos.zPos - kayak->pos.zPos;
dist = (x * x) + (z * z);
if (dist > (130000))
return 0;
/*
* SONY BUG FIX 10000001: If player stops in exactly right place and buries
* skidoo in avalanche, then they can get on it and crash the game
*/
room_number = kayak->roomNumber;
floor = GetFloor(kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos, &room_number);
if (GetFloorHeight(floor, kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos) > -32000)
{
short ang;
unsigned short tempang;
ang = ATAN(kayak->pos.zPos - LaraItem->pos.zPos, kayak->pos.xPos - LaraItem->pos.xPos);
ang -= kayak->pos.yRot;
tempang = LaraItem->pos.yRot - kayak->pos.yRot;
if (ang > -ANGLE(45) && ang < ANGLE(135))
{
if (tempang > ANGLE(45) && tempang < ANGLE(135))
return -1;
}
else
{
if (tempang > ANGLE(225) && tempang < ANGLE(315))
return 1;
}
}
return 0;
}
int GetKayakCollisionAnim(ITEM_INFO* v, int xdiff, int zdiff)
{
xdiff = v->pos.xPos - xdiff;
zdiff = v->pos.zPos - zdiff;
/* -------- determine direction of travel relative to rotation */
if ((xdiff) || (zdiff))
{
int c, s, front, side;
c = COS(v->pos.yRot);
s = SIN(v->pos.yRot);
front = ((zdiff * c) + (xdiff * s)) >> W2V_SHIFT;
side = ((-zdiff * s) + (xdiff * c)) >> W2V_SHIFT;
if (abs(front) > abs(side))
{
if (front > 0)
return HIT_BACK;
else
return HIT_FRONT;
}
else
{
if (side > 0)
return HIT_LEFT;
else
return HIT_RIGHT;
}
}
return 0;
}
int DoKayakDynamics(int height, int fallspeed, int* y)
{
int kick;
if (height > * y)
{
/* In air */
*y += fallspeed;
if (*y > height)
// if (*y > height - SKIDOO_MIN_BOUNCE)
{
*y = height;
fallspeed = 0;
}
else
fallspeed += GRAVITY;
}
else
{
/* On ground: get up push from height change (if not a closed door and so NO_HEIGHT) */
kick = (height - *y) << 2;
if (kick < SKIDOO_MAX_KICK)
kick = SKIDOO_MAX_KICK;
fallspeed += ((kick - fallspeed) >> 3);
if (*y > height)
*y = height;
}
return fallspeed;
}
void DoKayakCurrent(ITEM_INFO* item)
{
ROOM_INFO* r;
PHD_VECTOR target;
long angle, dx, dz, speed, sinkval;
r = &Rooms[item->roomNumber];
if (!Lara.currentActive)
{
long shifter, absvel;
absvel = abs(Lara.currentActive);
if (absvel > 16)
shifter = 4;
else if (absvel > 8)
shifter = 3;
else
shifter = 2;
Lara.currentXvel -= Lara.currentXvel >> shifter;
if (abs(Lara.currentXvel) < 4)
Lara.currentXvel = 0;
absvel = abs(Lara.currentZvel);
if (absvel > 16)
shifter = 4;
else if (absvel > 8)
shifter = 3;
else
shifter = 2;
Lara.currentZvel -= Lara.currentZvel >> shifter;
if (abs(Lara.currentZvel) < 4)
Lara.currentZvel = 0;
if (Lara.currentXvel == 0 && Lara.currentZvel == 0)
return;
}
else
{
sinkval = Lara.currentActive - 1;
target.x = Camera.fixed[sinkval].x;
target.y = Camera.fixed[sinkval].y;
target.z = Camera.fixed[sinkval].z;
angle = ((mGetAngle(target.x, target.z, LaraItem->pos.xPos, LaraItem->pos.zPos) - 0x4000) >> 4) & 4095;
dx = target.x - LaraItem->pos.xPos;
dz = target.z - LaraItem->pos.zPos;
speed = Camera.fixed[sinkval].data;
dx = (((rcossin_tbl[(angle << 1)] * speed))) >> 2;
dz = (((rcossin_tbl[(angle << 1) + 1] * speed))) >> 2;
Lara.currentXvel += (dx - Lara.currentXvel) >> 4;
Lara.currentZvel += (dz - Lara.currentZvel) >> 4;
}
/* Move Lara in direction of sink. */
item->pos.xPos += Lara.currentXvel >> 8;
item->pos.zPos += Lara.currentZvel >> 8;
/* Reset current (will get set again so long as Lara is over triggers) */
Lara.currentActive = 0;
}
int TestKayakHeight(ITEM_INFO* item, int x, int z, PHD_VECTOR* pos)
{
int h;
FLOOR_INFO* floor;
short room_number;
phd_PushUnitMatrix();
MatrixPtr[M03] = item->pos.xPos;
MatrixPtr[M13] = item->pos.yPos;
MatrixPtr[M23] = item->pos.zPos;
phd_RotYXZ(item->pos.yRot, item->pos.xRot, item->pos.zRot);
phd_TranslateRel(x, 0, z);
pos->x = (MatrixPtr[M03] >> W2V_SHIFT);
pos->y = (MatrixPtr[M13] >> W2V_SHIFT);
pos->z = (MatrixPtr[M23] >> W2V_SHIFT);
room_number = item->roomNumber;
GetFloor(pos->x, pos->y, pos->z, &room_number);
if ((h = GetWaterHeight(pos->x, pos->y, pos->z, room_number)) == NO_HEIGHT)
{
room_number = item->roomNumber;
floor = GetFloor(pos->x, pos->y, pos->z, &room_number);
if ((h = GetFloorHeight(floor, pos->x, pos->y, pos->z)) == NO_HEIGHT)
return h;
}
return h - 5;
}
int CanKayakGetOut(ITEM_INFO* kayak, int direction)
{
int height;
PHD_VECTOR pos;
height = TestKayakHeight(kayak, (direction < 0) ? -GETOFF_DIST : GETOFF_DIST, 0, &pos);
if ((kayak->pos.yPos - height) > 0)
return 0;
return 1;
}
int DoKayakShift(ITEM_INFO* v, PHD_VECTOR* pos, PHD_VECTOR* old)
{
int x, z;
int x_old, z_old;
int shift_x, shift_z;
int height;
FLOOR_INFO* floor;
short room_number;
x = pos->x >> WALL_SHIFT;
z = pos->z >> WALL_SHIFT;
x_old = old->x >> WALL_SHIFT;
z_old = old->z >> WALL_SHIFT;
shift_x = pos->x & (WALL_SIZE - 1);
shift_z = pos->z & (WALL_SIZE - 1);
if (x == x_old)
{
old->x = 0;
if (z == z_old)
{
v->pos.zPos += (old->z - pos->z);
v->pos.xPos += (old->x - pos->x);
}
else if (z > z_old)
{
v->pos.zPos -= shift_z + 1;
return (pos->x - v->pos.xPos);
}
else
{
v->pos.zPos += WALL_SIZE - shift_z;
return (v->pos.xPos - pos->x);
}
}
else if (z == z_old)
{
old->z = 0;
if (x > x_old)
{
v->pos.xPos -= shift_x + 1;
return (v->pos.zPos - pos->z);
}
else
{
v->pos.xPos += WALL_SIZE - shift_x;
return (pos->z - v->pos.zPos);
}
}
else
{
/* A diagonal hit; means a barrage of tests needed to determine best shift */
x = 0;
z = 0;
room_number = v->roomNumber;
floor = GetFloor(old->x, pos->y, pos->z, &room_number);
height = GetFloorHeight(floor, old->x, pos->y, pos->z);
if (height < old->y - STEP_SIZE)
{
if (pos->z > old->z)
z = -shift_z - 1;
else
z = WALL_SIZE - shift_z;
}
room_number = v->roomNumber;
floor = GetFloor(pos->x, pos->y, old->z, &room_number);
height = GetFloorHeight(floor, pos->x, pos->y, old->z);
if (height < old->y - STEP_SIZE)
{
if (pos->x > old->x)
x = -shift_x - 1;
else
x = WALL_SIZE - shift_x;
}
/* -------- corner collision */
if (x && z)
{
v->pos.zPos += z;
v->pos.xPos += x;
}
/* -------- edge collisions */
else if (z)
{
v->pos.zPos += z;
if (z > 0)
return (v->pos.xPos - pos->x);
else
return (pos->x - v->pos.xPos);
}
else if (x)
{
v->pos.xPos += x;
if (x > 0)
return (pos->z - v->pos.zPos);
else
return (v->pos.zPos - pos->z);
}
/* -------- diagnal collision */
else
{
v->pos.zPos += (old->z - pos->z);
v->pos.xPos += (old->x - pos->x);
}
}
return 0;
}
void KayakToBackground(ITEM_INFO* kayak, KAYAK_INFO* kinfo)
{
int h, slip = 0, rot = 0;
PHD_VECTOR pos;
int collide, oldx, oldz;
FLOOR_INFO* floor;
short room_number;
int height[8];
PHD_VECTOR oldpos[9];
int x, z;
int fh, lh, rh;
PHD_VECTOR fpos, lpos, rpos;
kinfo->OldPos = kayak->pos;
/* -------- determine valid Kayak positions */
height[0] = TestKayakHeight(kayak, 0, 1024, &oldpos[0]);
height[1] = TestKayakHeight(kayak, -96, 512, &oldpos[1]);
height[2] = TestKayakHeight(kayak, 96, 512, &oldpos[2]);
height[3] = TestKayakHeight(kayak, -KAYAK_X, KAYAK_Z, &oldpos[3]);
height[4] = TestKayakHeight(kayak, KAYAK_X, KAYAK_Z, &oldpos[4]);
height[5] = TestKayakHeight(kayak, -KAYAK_X, -320, &oldpos[5]);
height[6] = TestKayakHeight(kayak, KAYAK_X, -320, &oldpos[6]);
height[7] = TestKayakHeight(kayak, 0, -640, &oldpos[7]);
for (collide = 0; collide < 8; collide++)
{
if (oldpos[collide].y > height[collide])
oldpos[collide].y = height[collide];
}
oldpos[8].x = kayak->pos.xPos;
oldpos[8].y = kayak->pos.yPos;
oldpos[8].z = kayak->pos.zPos;
/* -------- move Kayak according to velocities & current */
fh = TestKayakHeight(kayak, 0, 1024, &fpos);
lh = TestKayakHeight(kayak, -KAYAK_X, KAYAK_Z, &lpos);
rh = TestKayakHeight(kayak, KAYAK_X, KAYAK_Z, &rpos);
kayak->pos.yRot += (kinfo->Rot >> 16);
kayak->pos.xPos += (kayak->speed * SIN(kayak->pos.yRot)) >> W2V_SHIFT;
kayak->pos.zPos += (kayak->speed * COS(kayak->pos.yRot)) >> W2V_SHIFT;
DoKayakCurrent(kayak);
/* -------- move Kayak vertically */
kinfo->FallSpeedL = DoKayakDynamics(lh, kinfo->FallSpeedL, &lpos.y);
kinfo->FallSpeedR = DoKayakDynamics(rh, kinfo->FallSpeedR, &rpos.y);
kinfo->FallSpeedF = DoKayakDynamics(fh, kinfo->FallSpeedF, &fpos.y);
kayak->fallspeed = DoKayakDynamics(kinfo->Water, kayak->fallspeed, &kayak->pos.yPos);
h = (lpos.y + rpos.y) >> 1;
x = ATAN(1024, kayak->pos.yPos - fpos.y);
z = ATAN(KAYAK_X, h - lpos.y);
kayak->pos.xRot = x;
kayak->pos.zRot = z;
/* -------- slide Kayak back down slopes */
oldx = kayak->pos.xPos;
oldz = kayak->pos.zPos;
/* -------- collide against background */
if ((h = TestKayakHeight(kayak, 0, -640, &pos)) < (oldpos[7].y - KAYAK_COLLIDE))
rot = DoKayakShift(kayak, &pos, &oldpos[7]);
if ((h = TestKayakHeight(kayak, KAYAK_X, -320, &pos)) < (oldpos[6].y - KAYAK_COLLIDE))
rot += DoKayakShift(kayak, &pos, &oldpos[6]);
if ((h = TestKayakHeight(kayak, -KAYAK_X, -320, &pos)) < (oldpos[5].y - KAYAK_COLLIDE))
rot += DoKayakShift(kayak, &pos, &oldpos[5]);
if ((h = TestKayakHeight(kayak, KAYAK_X, KAYAK_Z, &pos)) < (oldpos[4].y - KAYAK_COLLIDE))
rot += DoKayakShift(kayak, &pos, &oldpos[4]);
if ((h = TestKayakHeight(kayak, -KAYAK_X, KAYAK_Z, &pos)) < (oldpos[3].y - KAYAK_COLLIDE))
rot += DoKayakShift(kayak, &pos, &oldpos[3]);
if ((h = TestKayakHeight(kayak, 96, 512, &pos)) < (oldpos[2].y - KAYAK_COLLIDE))
rot += DoKayakShift(kayak, &pos, &oldpos[2]);
if ((h = TestKayakHeight(kayak, -96, 512, &pos)) < (oldpos[1].y - KAYAK_COLLIDE))
rot += DoKayakShift(kayak, &pos, &oldpos[1]);
if ((h = TestKayakHeight(kayak, 0, 1024, &pos)) < (oldpos[0].y - KAYAK_COLLIDE))
rot += DoKayakShift(kayak, &pos, &oldpos[0]);
kayak->pos.yRot += rot;
/* -------- update the vehicle's actual position */
room_number = kayak->roomNumber;
floor = GetFloor(kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos, &room_number);
h = GetWaterHeight(kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos, room_number);
if (h == NO_HEIGHT)
h = GetFloorHeight(floor, kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos);
if (h < (kayak->pos.yPos - KAYAK_COLLIDE))
DoKayakShift(kayak, (PHD_VECTOR*)&kayak->pos, &oldpos[8]);
/* -------- if position is still invalid - find somewhere decent */
room_number = kayak->roomNumber;
floor = GetFloor(kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos, &room_number);
h = GetWaterHeight(kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos, room_number);
if (h == NO_HEIGHT)
h = GetFloorHeight(floor, kayak->pos.xPos, kayak->pos.yPos, kayak->pos.zPos);
if (h == NO_HEIGHT)
{
GAME_VECTOR kpos;
kpos.x = kinfo->OldPos.xPos;
kpos.y = kinfo->OldPos.yPos;
kpos.z = kinfo->OldPos.zPos;
kpos.roomNumber = kayak->roomNumber;
CameraCollisionBounds(&kpos, 256, 0);
{
kayak->pos.xPos = kpos.x;
kayak->pos.yPos = kpos.y;
kayak->pos.zPos = kpos.z;
kayak->roomNumber = kpos.roomNumber;
}
}
/* -------- adjust speed upon collision */
collide = GetKayakCollisionAnim(kayak, oldx, oldz);
if (slip || collide)
{
int newspeed;
newspeed = ((kayak->pos.zPos - oldpos[8].z) * COS(kayak->pos.yRot) + (kayak->pos.xPos - oldpos[8].x) * SIN(kayak->pos.yRot)) >> W2V_SHIFT;
newspeed <<= 8;
if (slip)
{
/* Only if slip is above certain amount, and boat is not in FAST speed range */
if (kinfo->Vel <= MAX_SPEED)
kinfo->Vel = newspeed;
}
else
{
if (kinfo->Vel > 0 && newspeed < kinfo->Vel)
kinfo->Vel = newspeed;
else if (kinfo->Vel < 0 && newspeed > kinfo->Vel)
kinfo->Vel = newspeed;
}
if (kinfo->Vel < -MAX_SPEED)
kinfo->Vel = -MAX_SPEED;
}
}
void KayakUserInput(ITEM_INFO* kayak, ITEM_INFO* lara, KAYAK_INFO* kinfo)
{
short frame;
char lr;
/* -------- kill Lara in boat */
if ((lara->hitPoints <= 0) && (lara->currentAnimState != KS_DEATHIN))
{
lara->animNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + 5;
lara->frameNumber = Anims[lara->animNumber].frameBase;
lara->currentAnimState = lara->goalAnimState = KS_DEATHIN;
}
/* -------- control Kayak */
frame = lara->frameNumber - Anims[lara->animNumber].frameBase;
switch (lara->currentAnimState)
{
case KS_POSE:
if ((TrInput & IN_ROLL) && (!Lara.currentActive) && (!Lara.currentXvel) && (!Lara.currentZvel))
{
if ((TrInput & IN_LEFT) && (CanKayakGetOut(kayak, -1)))
{
lara->goalAnimState = KS_JUMPOUT;
lara->requiredAnimState = KS_CLIMBOUTL;
}
else if ((TrInput & IN_RIGHT) && (CanKayakGetOut(kayak, 1)))
{
lara->goalAnimState = KS_JUMPOUT;
lara->requiredAnimState = KS_CLIMBOUTR;
}
}
else if (TrInput & IN_FORWARD)
{
lara->goalAnimState = KS_RIGHT;
kinfo->Turn = 0;
kinfo->Forward = 1;
}
else if (TrInput & IN_BACK)
{
lara->goalAnimState = KS_BACK;
}
else if (TrInput & IN_LEFT)
{
lara->goalAnimState = KS_LEFT;
if (kinfo->Vel)
kinfo->Turn = 0;
else
kinfo->Turn = 1; // harder turn
kinfo->Forward = 0;
}
else if (TrInput & IN_RIGHT)
{
lara->goalAnimState = KS_RIGHT;
if (kinfo->Vel)
kinfo->Turn = 0;
else
kinfo->Turn = 1; // harder turn
kinfo->Forward = 0;
}
else if ((TrInput & IN_LSTEP) && ((kinfo->Vel) || (Lara.currentXvel) || (Lara.currentZvel)))
{
lara->goalAnimState = KS_TURNL;
}
else if ((TrInput & IN_RSTEP) && ((kinfo->Vel) || (Lara.currentXvel) || (Lara.currentZvel)))
{
lara->goalAnimState = KS_TURNR;
}
break;
case KS_LEFT:
if (kinfo->Forward)
{
if (!frame)
lr = 0;
if ((frame == 2) && (!(lr & 0x80)))
lr++;
else if (frame > 2)
lr &= ~0x80;
if (TrInput & IN_FORWARD)
{
if (TrInput & IN_LEFT)
{
if ((lr & ~0x80) >= 2)
lara->goalAnimState = KS_RIGHT;
}
else
lara->goalAnimState = KS_RIGHT;
}
else
lara->goalAnimState = KS_POSE;
}
else if (!(TrInput & IN_LEFT))
lara->goalAnimState = KS_POSE;
/* -------- apply velocities */
if (frame == 7)
{
if (kinfo->Forward)
{
if ((kinfo->Rot -= KAYAK_FWD_ROT) < -KAYAK_MAX_TURN)
kinfo->Rot = -KAYAK_MAX_TURN;
kinfo->Vel += KAYAK_FWD_VEL;
}
else if (kinfo->Turn)
{
if ((kinfo->Rot -= KAYAK_HARD_ROT) < -KAYAK_MAX_STAT)
kinfo->Rot = -KAYAK_MAX_STAT;
}
else
{
if ((kinfo->Rot -= KAYAK_LR_ROT) < -KAYAK_MAX_LR)
kinfo->Rot = -KAYAK_MAX_LR;
kinfo->Vel += KAYAK_LR_VEL;
}
}
/* -------- initialise ripple effects */
if ((frame > 6) && (frame < 24) && (frame & 1))
DoKayakRipple(kayak, -384, -64);
break;
/* --------------------- */
case KS_RIGHT:
/* --------------------- */
if (kinfo->Forward)
{
if (!frame)
lr = 0;
if ((frame == 2) && (!(lr & 0x80)))
lr++;
else if (frame > 2)
lr &= ~0x80;
if (TrInput & IN_FORWARD)
{
if (TrInput & IN_RIGHT)
{
if ((lr & ~0x80) >= 2)
lara->goalAnimState = KS_LEFT;
}
else
lara->goalAnimState = KS_LEFT;
}
else
lara->goalAnimState = KS_POSE;
}
else if (!(TrInput & IN_RIGHT))
lara->goalAnimState = KS_POSE;
/* -------- apply velocities */
if (frame == 7)
{
if (kinfo->Forward)
{
if ((kinfo->Rot += KAYAK_FWD_ROT) > KAYAK_MAX_TURN)
kinfo->Rot = KAYAK_MAX_TURN;
kinfo->Vel += KAYAK_FWD_VEL;
}
else if (kinfo->Turn)
{
if ((kinfo->Rot += KAYAK_HARD_ROT) > KAYAK_MAX_STAT)
kinfo->Rot = KAYAK_MAX_STAT;
}
else
{
if ((kinfo->Rot += KAYAK_LR_ROT) > KAYAK_MAX_LR)
kinfo->Rot = KAYAK_MAX_LR;
kinfo->Vel += KAYAK_LR_VEL;
}
}
/* -------- initialise ripple effects */
if ((frame > 6) && (frame < 24) && (frame & 1))
DoKayakRipple(kayak, 384, -64);
break;
/* --------------------- */
case KS_BACK:
/* --------------------- */
if (!(TrInput & IN_BACK))
lara->goalAnimState = KS_POSE;
if ((lara->animNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex) == 2)
{
if (frame == 8)
{
kinfo->Rot += KAYAK_FWD_ROT;
kinfo->Vel -= KAYAK_FWD_VEL;
}
if (frame == 31)
{
kinfo->Rot -= KAYAK_FWD_ROT;
kinfo->Vel -= KAYAK_FWD_VEL;
}
if ((frame < 15) && (frame & 1))
DoKayakRipple(kayak, 384, -128);
else if ((frame >= 20) && (frame <= 34) && (frame & 1))
DoKayakRipple(kayak, -384, -128);
}
break;
case KS_TURNL:
if ((!(TrInput & IN_LSTEP)) || ((!kinfo->Vel) && (!Lara.currentXvel) && (!Lara.currentZvel)))
{
lara->goalAnimState = KS_POSE;
}
else if ((lara->animNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex) == 26)
{
if (kinfo->Vel >= 0)
{
if ((kinfo->Rot -= KAYAK_TURN_ROT) < -KAYAK_MAX_TURN)
kinfo->Rot = -KAYAK_MAX_TURN;
if ((kinfo->Vel += -KAYAK_TURN_BRAKE) < 0)
kinfo->Vel = 0;
}
if (kinfo->Vel < 0)
{
kinfo->Rot += KAYAK_TURN_ROT;
if ((kinfo->Vel += KAYAK_TURN_BRAKE) > 0)
kinfo->Vel = 0;
}
if (!(Wibble & 3))
DoKayakRipple(kayak, -256, -256);
}
break;
case KS_TURNR:
if (!(TrInput & IN_RSTEP) || ((!kinfo->Vel) && (!Lara.currentXvel) && (!Lara.currentZvel)))
{
lara->goalAnimState = KS_POSE;
}
else if ((lara->animNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex) == 27)
{
if (kinfo->Vel >= 0)
{
if ((kinfo->Rot += KAYAK_TURN_ROT) > KAYAK_MAX_TURN)
kinfo->Rot = KAYAK_MAX_TURN;
if ((kinfo->Vel += -KAYAK_TURN_BRAKE) < 0)
kinfo->Vel = 0;
}
if (kinfo->Vel < 0)
{
kinfo->Rot -= KAYAK_TURN_ROT;
if ((kinfo->Vel += KAYAK_TURN_BRAKE) > 0)
kinfo->Vel = 0;
}
if (!(Wibble & 3))
DoKayakRipple(kayak, 256, -256);
}
break;
/* --------------------- */
case KS_CLIMBIN:
/* --------------------- */
if ((lara->animNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + 4) && (frame == 24) && (!(kinfo->Flags & 0x80)))
{
short* tmp;
tmp = Lara.meshPtrs[LM_RHAND];
LARA_MESHES(ID_KAYAK_LARA_ANIMS, LM_RHAND);
Meshes[Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND] = tmp;
lara->meshBits &= ~LARA_LEG_BITS;
kinfo->Flags |= 0x80;
}
break;
case KS_JUMPOUT:
if ((lara->animNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + 14) && (frame == 27) && (kinfo->Flags & 0x80))
{
short* tmp;
tmp = Lara.meshPtrs[LM_RHAND];
Lara.meshPtrs[LM_RHAND] = Meshes[Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND];
Meshes[Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND] = tmp;
lara->meshBits |= LARA_LEG_BITS;
kinfo->Flags &= ~0x80;
}
lara->goalAnimState = lara->requiredAnimState;
break;
case KS_CLIMBOUTL:
if ((lara->animNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + 24) && (frame == 83))
{
PHD_VECTOR vec = { 0, 350, 500 };
GetLaraJointPosition(&vec, LM_HIPS);
lara->pos.xPos = vec.x;
lara->pos.yPos = vec.y;
lara->pos.zPos = vec.z;
lara->pos.xRot = 0;
lara->pos.yRot = kayak->pos.yRot - 0x4000;
lara->pos.zRot = 0;
lara->animNumber = 23;
lara->frameNumber = Anims[lara->animNumber].frameBase;
lara->currentAnimState = lara->goalAnimState = 9;
lara->fallspeed = 0;
lara->gravityStatus = true;
Lara.gunStatus = LG_NO_ARMS;
g_LaraExtra.Vehicle = NO_ITEM;
}
break;
case KS_CLIMBOUTR:
if ((lara->animNumber ==Objects[ID_KAYAK_LARA_ANIMS].animIndex + 32) && (frame == 83))
{
PHD_VECTOR vec = { 0, 350, 500 };
GetLaraJointPosition(&vec, LM_HIPS);
lara->pos.xPos = vec.x;
lara->pos.yPos = vec.y;
lara->pos.zPos = vec.z;
lara->pos.xRot = 0;
lara->pos.yRot = kayak->pos.yRot + 0x4000;
lara->pos.zRot = 0;
lara->animNumber = 23;
lara->frameNumber = Anims[lara->animNumber].frameBase;
lara->currentAnimState = lara->goalAnimState = 9;
lara->fallspeed = 0;
lara->gravityStatus = true;
Lara.gunStatus = LG_NO_ARMS;
g_LaraExtra.Vehicle = NO_ITEM;
}
}
/* -------- slow Kayak with friction */
if (kinfo->Vel > 0)
{
if ((kinfo->Vel -= KAYAK_FRICTION) < 0)
kinfo->Vel = 0;
}
else if (kinfo->Vel < 0)
{
if ((kinfo->Vel += KAYAK_FRICTION) > 0)
kinfo->Vel = 0;
}
if (kinfo->Vel > MAX_SPEED)
kinfo->Vel = MAX_SPEED;
else if (kinfo->Vel < -MAX_SPEED)
kinfo->Vel = -MAX_SPEED;
kayak->speed = (kinfo->Vel >> 16);
/* -------- unwind rotation */
if (kinfo->Rot >= 0)
{
if ((kinfo->Rot -= KAYAK_ROT_FRIC) < 0)
kinfo->Rot = 0;
}
else if (kinfo->Rot < 0)
{
if ((kinfo->Rot += KAYAK_ROT_FRIC) > 0)
kinfo->Rot = 0;
}
}
void KayakToBaddieCollision(ITEM_INFO* kayak)
{
#define TARGET_DIST (WALL_SIZE*2) // Up to this Distance more Complicated checks are made
vector<short> roomsList;
short numDoors, *door;
roomsList.push_back(kayak->roomNumber);
/* -------- get nearby rooms */
door = Rooms[kayak->roomNumber].door;
if (door)
{
numDoors = *door;
door++;
for (int i = 0; i < numDoors; i++)
{
roomsList.push_back(*door);
door += 16;
}
}
/* -------- collide with all baddies in these rooms */
for (int i = 0; i < roomsList.size(); i++)
{
short item_num;
item_num = Rooms[roomsList[i]].itemNumber; // Only do collision with Items on Draw list
while (item_num != NO_ITEM)
{
short nex;
ITEM_INFO* item;
item = &Items[item_num];
nex = item->nextItem; // Store next Item in list as Current may be deleted!!!
if (item->collidable && item->status != ITEM_INVISIBLE)
{
OBJECT_INFO* object;
object = &Objects[item->objectNumber];
if ((object->collision)
&& ((item->objectNumber == ID_TEETH_SPIKES)
|| (item->objectNumber == ID_DARTS)
//|| (item->objectNumber == ID_TEETH_TRAP)
//|| ((item->objectNumber == BLADE)
&& (item->currentAnimState != 1)) // STOP
//|| ((item->objectNumber == ICICLES)
&& (item->currentAnimState != 3)) // LAND
{
int x, y, z;
x = kayak->pos.xPos - item->pos.xPos;
y = kayak->pos.yPos - item->pos.yPos;
z = kayak->pos.zPos - item->pos.zPos;
if ((x > -TARGET_DIST)
&& (x < TARGET_DIST)
&& (z > -TARGET_DIST)
&& (z < TARGET_DIST)
&& (y > -TARGET_DIST)
&& (y < TARGET_DIST))
{
if (TestBoundsCollide(item, kayak, KAYAK_TO_BADDIE_RADIUS))
{
DoLotsOfBlood(LaraItem->pos.xPos, LaraItem->pos.yPos - STEP_SIZE, LaraItem->pos.zPos, kayak->speed, kayak->pos.yRot, LaraItem->roomNumber, 3);
LaraItem->hitPoints -= 5;
}
}
}
}
item_num = nex;
}
}
#undef TARGET_DIST
}
void LaraRapidsDrown()
{
ITEM_INFO* l = LaraItem;
l->animNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + 25;
l->frameNumber = Anims[l->animNumber].frameBase;
l->currentAnimState = 12;
l->goalAnimState = 12;
l->hitPoints = 0;
l->fallspeed = 0;
l->gravityStatus = false;
l->speed = 0;
AnimateItem(l);
g_LaraExtra.ExtraAnim = 1;
Lara.gunStatus = LG_NO_ARMS;
Lara.gunType = WEAPON_NONE;
Lara.hitDirection = -1;
}
void InitialiseKayak(short item_number)
{
int i;
ITEM_INFO* v;
KAYAK_INFO* Kayak;
v = &Items[item_number];
Kayak = (KAYAK_INFO*)GameMalloc(sizeof(KAYAK_INFO));
v->data = (void*)Kayak;
Kayak->Vel = 0;
Kayak->Rot = 0;
Kayak->Flags = 0;
Kayak->FallSpeedF = 0;
Kayak->FallSpeedL = 0;
Kayak->FallSpeedR = 0;
Kayak->OldPos = v->pos;
}
void DrawKayak(ITEM_INFO* kayak)
{
kayak->pos.yPos += KAYAK_DRAW_SHIFT;
DrawAnimatingItem(kayak);
kayak->pos.yPos -= KAYAK_DRAW_SHIFT;
}
void KayakCollision(short item_number, ITEM_INFO* l, COLL_INFO* coll)
{
int geton;
if ((l->hitPoints < 0) || (g_LaraExtra.Vehicle != NO_ITEM))
return;
if ((geton = GetInKayak(item_number, coll)))
{
KAYAK_INFO* Kayak;
ITEM_INFO* v = &Items[item_number];
g_LaraExtra.Vehicle = item_number;
/* -------- throw flare away if using */
if (Lara.gunType == WEAPON_FLARE)
{
CreateFlare(ID_FLARE_ITEM, FALSE);
undraw_flare_meshes();
Lara.flareControlLeft = 0;
Lara.requestGunType = Lara.gunType = LG_NO_ARMS;
}
/* -------- initiate animation */
if (geton > 0)
l->animNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + 3;
else
l->animNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + 28;
l->frameNumber = Anims[l->animNumber].frameBase;
l->currentAnimState = l->goalAnimState = KS_CLIMBIN;
Lara.waterStatus = LW_UNDERWATER;
l->pos.xPos = v->pos.xPos;
l->pos.yPos = v->pos.yPos;
l->pos.zPos = v->pos.zPos;
l->pos.yRot = v->pos.yRot;
l->pos.xRot = l->pos.zRot = 0;
l->gravityStatus = true;
l->speed = 0;
l->fallspeed = 0;
if (l->roomNumber != v->roomNumber)
ItemNewRoom(Lara.itemNumber, v->roomNumber);
AnimateItem(l);
Kayak = (KAYAK_INFO*)v->data;
Kayak->Water = v->pos.yPos;
Kayak->Flags = 0;
}
else
{
coll->enableBaddiePush = true;
ObjectCollision(item_number, l, coll);
}
}
int KayakControl()
{
int h;
KAYAK_INFO* Kayak;
ITEM_INFO* v, * l;
FLOOR_INFO* floor;
int ofs, water, lp;
short room_number;
l = LaraItem;
v = &Items[g_LaraExtra.Vehicle];
Kayak = (KAYAK_INFO*)v->data;
if (TrInput & IN_LOOK)
LookUpDown();
/* -------- update dynamics */
ofs = v->fallspeed;
KayakUserInput(v, l, Kayak);
KayakToBackground(v, Kayak);
/* -------- determine water level */
room_number = v->roomNumber;
floor = GetFloor(v->pos.xPos, v->pos.yPos, v->pos.zPos, &room_number);
h = GetFloorHeight(floor, v->pos.xPos, v->pos.yPos, v->pos.zPos);
TestTriggers(TriggerIndex, 0, 0);
Kayak->Water = water = GetWaterHeight(v->pos.xPos, v->pos.yPos, v->pos.zPos, room_number);
if (water == NO_HEIGHT)
{
Kayak->Water = water = h;
Kayak->TrueWater = 0;
}
else
{
Kayak->Water -= 5;
Kayak->TrueWater = 1;
}
if (((ofs - v->fallspeed) > 128) && (v->fallspeed == 0) && (water != NO_HEIGHT))
{
long damage;
if ((damage = (ofs - v->fallspeed)) > 160)
l->hitPoints -= (damage - 160) << 3;
KayakSplash(v, ofs - v->fallspeed, water);
}
/* -------- move Lara to Kayak pos */
if (g_LaraExtra.Vehicle != NO_ITEM)
{
if (v->roomNumber != room_number)
{
ItemNewRoom(g_LaraExtra.Vehicle, room_number);
ItemNewRoom(Lara.itemNumber, room_number);
}
l->pos.xPos = v->pos.xPos;
l->pos.yPos = v->pos.yPos + KAYAK_DRAW_SHIFT;
l->pos.zPos = v->pos.zPos;
l->pos.xRot = v->pos.xRot;
l->pos.yRot = v->pos.yRot;
l->pos.zRot = v->pos.zRot >> 1;
/* -------- animate Lara then Kayak */
AnimateItem(l);
v->animNumber = Objects[ID_KAYAK].animIndex + (l->animNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex);
v->frameNumber = Anims[v->animNumber].frameBase + (l->frameNumber - Anims[l->animNumber].frameBase);
Camera.targetElevation = -ANGLE(30);
Camera.targetDistance = WALL_SIZE * 2;
}
/* -------- process effects */
/*
if ((!(Wibble & 15)) && (Kayak->TrueWater))
{
DoWake(v, -128, 0, 0);
DoWake(v, 128, 0, 1);
}
if ((wibble & 7))
{
if ((!Kayak->TrueWater) && (v->fallspeed < 20))
{
PHD_VECTOR dest;
static char cnt = 0;
static short MistZPos[10] = { 900, 750, 600, 450, 300, 150, 0, -150, -300, -450 };
// static sint16 MistXPos[10] = { 32, 64, 128, 192, 256, 480, 480, 350, 256, 64};
static short MistXPos[10] = { 32, 96, 170, 220, 300, 400, 400, 300, 200, 64 };
cnt ^= 1;
for (lp = cnt; lp < 10; lp += 2)
{
//dest.x = (GetRandomControl()%MistXPos[lp]) - (MistXPos[lp]>>1);
if (GetRandomControl() & 1)
dest.x = (MistXPos[lp] >> 1);
else
dest.x = -(MistXPos[lp] >> 1);
dest.y = 50;
dest.z = MistZPos[lp];
phd_PushUnitMatrix();
phd_RotYXZ(v->pos.y_rot, v->pos.x_rot, v->pos.z_rot);
dest.x = v->pos.x_pos + ((*(mptr + M00) * dest.x + *(mptr + M01) * dest.y + *(mptr + M02) * dest.z) >> W2V_SHIFT);
dest.y = v->pos.y_pos + ((*(mptr + M10) * dest.x + *(mptr + M11) * dest.y + *(mptr + M12) * dest.z) >> W2V_SHIFT);
dest.z = v->pos.z_pos + ((*(mptr + M20) * dest.x + *(mptr + M21) * dest.y + *(mptr + M22) * dest.z) >> W2V_SHIFT);
TriggerRapidsMist(dest.x, dest.y, dest.z);
phd_PopMatrix();
}
}
}
*/
KayakToBaddieCollision(v);
return (g_LaraExtra.Vehicle != NO_ITEM) ? 1 : 0;
}