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

1372 lines
36 KiB
C++

#include "framework.h"
#include "Objects/TR3/Vehicles/kayak.h"
#include "Game/animation.h"
#include "Game/camera.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h"
#include "Game/control/control.h"
#include "Game/effects/effects.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_flare.h"
#include "Game/Lara/lara_helpers.h"
#include "Objects/TR3/Vehicles/kayak_info.h"
#include "Specific/level.h"
#include "Specific/input.h"
#include "Specific/setup.h"
using std::vector;
#define KAYAK_COLLIDE CLICK(0.25f)
#define DISMOUNT_DISTANCE CLICK(3) // TODO: Find accurate distance.
#define KAYAK_TO_ENTITY_RADIUS CLICK(1)
#define MAX_VELOCITY 0x380000
#define KAYAK_FRICTION 0x8000
#define KAYAK_ROTATE_FRICTION 0x50000
#define KAYAK_DEFLECT_ROTATION 0x80000
#define KAYAK_FORWARD_VELOCITY 0x180000
#define KAYAK_FORWARD_ROTATION 0x800000
#define KAYAK_LEFT_RIGHT_VELOCITY 0x100000
#define KAYAK_LEFT_RIGHT_ROTATION 0xc00000
#define KAYAK_MAX_LEFT_RIGHT 0xc00000
#define KAYAK_TURN_ROTATION 0x200000
#define KAYAK_MAX_TURN 0x1000000
#define KAYAK_TURN_BRAKE 0x8000
#define KAYAK_HARD_ROTATION 0x1000000
#define KAYAK_MAX_STAT 0x1000000
#define HIT_BACK 1
#define HIT_FRONT 2
#define HIT_LEFT 3
#define HIT_RIGHT 4
#define KAYAK_MOUNT_LEFT_FRAME GetFrameNumber(KAYAK_ANIM_MOUNT_RIGHT, 0)
#define KAYAK_IDLE_FRAME GetFrameNumber(KAYAK_ANIM_IDLE, 0)
#define KAYAK_MOUNT_RIGHT_FRAME GetFrameNumber(KAYAK_ANIM_MOUNT_LEFT, 0)
#define KAYAK_DRAW_SHIFT 32
#define LARA_LEG_BITS ((1 << LM_HIPS) | (1 << LM_LTHIGH) | (1 << LM_LSHIN) | (1 << LM_LFOOT) | (1 << LM_RTHIGH) | (1 << LM_RSHIN) | (1 << LM_RFOOT))
#define NUM_WAKE_SPRITES 32
#define WAKE_SIZE 32
#define WAKE_VELOCITY 4
#define KAYAK_X 128
#define KAYAK_Z 128
#define KAYAK_MAX_KICK -80
#define KAYAK_MIN_BOUNCE ((MAX_VELOCITY / 2) / 256)
#define KAYAK_IN_FORWARD IN_FORWARD
#define KAYAK_IN_BACK IN_BACK
#define KAYAK_IN_LEFT IN_LEFT
#define KAYAK_IN_RIGHT IN_RIGHT
#define KAYAK_IN_HOLD_LEFT IN_LSTEP
#define KAYAK_IN_HOLD_RIGHT IN_RSTEP
#define KAYAK_IN_DISMOUNT (IN_JUMP | IN_ROLL)
enum KayakState
{
KAYAK_STATE_BACK = 0,
KAYAK_STATE_IDLE = 1,
KAYAK_STATE_TURN_LEFT = 2,
KAYAK_STATE_TURN_RIGHT = 3,
KAYAK_STATE_MOUNT_LEFT = 4,
KAYAK_STATE_IDLE_DEATH = 5,
KAYAK_STATE_FORWARD = 6,
KAYAK_STATE_CAPSIZE_RECOVER = 7, // Unused.
KAYAK_STATE_CAPSIZE_DEATH = 8, // Unused.
KAYAK_STATE_DISMOUNT = 9,
KAYAK_STATE_HOLD_LEFT = 10,
KAYAK_STATE_HOLD_RIGHT = 11,
KAYAK_STATE_MOUNT_RIGHT = 12,
KAYAK_STATE_DISMOUNT_LEFT = 13,
KAYAK_STATE_DISMOUNT_RIGHT = 14,
};
enum KayakAnim
{
KAYAK_ANIM_PADDLE_BACK_END = 0,
KAYAK_ANIM_PADDLE_BACK_START = 1,
KAYAK_ANIM_PADDLE_BACK = 2,
KAYAK_ANIM_MOUNT_RIGHT = 3,
KAYAK_ANIM_GET_PADDLE = 4,
KAYAK_ANIM_IDLE_DEATH = 5,
KAYAK_ANIM_CAPSIZE_DEATH = 6, // Unused.
KAYAK_ANIM_PADDLE_FORWARD_END = 7, // Unused.
KAYAK_ANIM_PADDLE_FORWARD = 8, // Unused.
KAYAK_ANIM_PADDLE_FORWARD_START = 9, // Unused.
KAYAK_ANIM_HIT_BACK = 10,
KAYAK_ANIM_HIT_FRONT = 11,
KAYAK_ANIM_HIT_RIGHT = 12,
KAYAK_ANIM_CAPSIZE_LEFT = 13, // Unused.
KAYAK_ANIM_DISMOUNT_START = 14,
KAYAK_ANIM_PADDLE_LEFT = 15,
KAYAK_ANIM_IDLE = 16,
KAYAK_ANIM_PADDLE_RIGHT = 17,
KAYAK_ANIM_CAPSIZE_STRUGGLE = 18, // Unused.
KAYAK_ANIM_CAPSIZE_RECOVER_LEFT = 19, // Unused.
KAYAK_ANIM_HOLD_PADDLE_LEFT_START = 20,
KAYAK_ANIM_HOLD_PADDLE_LEFT_END = 21,
KAYAK_ANIM_HOLD_PADDLE_RIGHT_START = 22,
KAYAK_ANIM_HOLD_PADDLE_RIGHT_END = 23,
KAYAK_ANIM_DISMOUNT_LEFT = 24,
KAYAK_ANIM_OVERBOARD_DEATH = 25,
KAYAK_ANIM_HOLD_PADDLE_LEFT = 26,
KAYAK_ANIM_HOLD_PADDLE_RIGHT = 27,
KAYAK_ANIM_MOUNT_LEFT = 28,
KAYAK_ANIM_HIT_LEFT = 29,
KAYAK_ANIM_CAPSIZE_RIGHT = 30, // Unused.
KAYAK_ANIM_CAPSIZE_RECOVER_RIGHT = 31, // Unused.
KAYAK_ANIM_DISMOUNT_RIGHT = 32
};
enum class KayakMountType
{
None,
Left,
Right
};
struct WAKE_PTS
{
int x[2];
int y;
int z[2];
short xvel[2];
short zvel[2];
byte life;
byte pad[3];
};
WAKE_PTS WakePts[NUM_WAKE_SPRITES][2];
void InitialiseKayak(short itemNumber)
{
auto* kayakItem = &g_Level.Items[itemNumber];
kayakItem->Data = KayakInfo();
auto* kayak = (KayakInfo*)kayakItem->Data;
kayak->TurnRate = 0;
kayak->Velocity = 0;
kayak->FrontVerticalVelocity = 0;
kayak->LeftVerticalVelocity = 0;
kayak->RightVerticalVelocity = 0;
kayak->LeftRightCount = 0;
kayak->OldPos = kayakItem->Position;
kayak->CurrentStartWake = 0;
kayak->WakeShade = 0;
kayak->Flags = 0;
for (int i = 0; i < NUM_WAKE_SPRITES; i++)
{
WakePts[i][0].life = 0;
WakePts[i][1].life = 0;
}
}
void KayakDraw(ITEM_INFO* kayakItem)
{
DrawAnimatingItem(kayakItem);
}
void KayakDoWake(ITEM_INFO* kayakItem, int xOffset, int zOffset, short rotate)
{
auto* kayak = (KayakInfo*)kayakItem->Data;
if (WakePts[kayak->CurrentStartWake][rotate].life)
return;
float s = phd_sin(kayakItem->Position.yRot);
float c = phd_cos(kayakItem->Position.yRot);
int x = kayakItem->Position.xPos + zOffset * s + xOffset * c;
int z = kayakItem->Position.zPos + zOffset * c - xOffset * s;
int probedRoomNum = GetCollisionResult(x, kayakItem->Position.yPos, z, kayakItem->RoomNumber).RoomNumber;
int waterHeight = GetWaterHeight(x, kayakItem->Position.yPos, z, probedRoomNum);
if (waterHeight != NO_HEIGHT)
{
short angle1, angle2;
if (kayakItem->Velocity < 0)
{
if (!rotate)
{
angle1 = kayakItem->Position.yRot - ANGLE(10.0f);
angle2 = kayakItem->Position.yRot - ANGLE(30.0f);
}
else
{
angle1 = kayakItem->Position.yRot + ANGLE(10.0f);
angle2 = kayakItem->Position.yRot + ANGLE(30.0f);
}
}
else
{
if (!rotate)
{
angle1 = kayakItem->Position.yRot - ANGLE(170.0f);
angle2 = kayakItem->Position.yRot - ANGLE(150.0f);
}
else
{
angle1 = kayakItem->Position.yRot + ANGLE(170.0f);
angle2 = kayakItem->Position.yRot + ANGLE(150.0f);
}
}
int xv[2], zv[2];
xv[0] = WAKE_VELOCITY * phd_sin(angle1);
zv[0] = WAKE_VELOCITY * phd_cos(angle1);
xv[1] = (WAKE_VELOCITY + 2) * phd_sin(angle2);
zv[1] = (WAKE_VELOCITY + 2) * phd_cos(angle2);
WakePts[kayak->CurrentStartWake][rotate].y = kayakItem->Position.yPos + KAYAK_DRAW_SHIFT;
WakePts[kayak->CurrentStartWake][rotate].life = 0x40;
for (int i = 0; i < 2; i++)
{
WakePts[kayak->CurrentStartWake][rotate].x[i] = x;
WakePts[kayak->CurrentStartWake][rotate].z[i] = z;
WakePts[kayak->CurrentStartWake][rotate].xvel[i] = xv[i];
WakePts[kayak->CurrentStartWake][rotate].zvel[i] = zv[i];
}
if (rotate == 1)
{
kayak->CurrentStartWake++;
kayak->CurrentStartWake &= (NUM_WAKE_SPRITES - 1);
}
}
}
void KayakDoRipple(ITEM_INFO* kayakItem, int xOffset, int zOffset)
{
float s = phd_sin(kayakItem->Position.yRot);
float c = phd_cos(kayakItem->Position.yRot);
int x = kayakItem->Position.xPos + zOffset * s + xOffset * c;
int z = kayakItem->Position.zPos + zOffset * c - xOffset * s;
int probedRoomNum = GetCollisionResult(x, kayakItem->Position.yPos, z, kayakItem->RoomNumber).RoomNumber;
int waterHeight = GetWaterHeight(x, kayakItem->Position.yPos, z, probedRoomNum);
if (waterHeight != NO_HEIGHT)
SetupRipple(x, kayakItem->Position.yPos, z, -2 - (GetRandomControl() & 1), 0, Objects[ID_KAYAK_PADDLE_TRAIL_SPRITE].meshIndex,TO_RAD(kayakItem->Position.yRot));
}
void KayakUpdateWakeFX()
{
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < NUM_WAKE_SPRITES; j++)
{
if (WakePts[j][i].life)
{
WakePts[j][i].life--;
WakePts[j][i].x[0] += WakePts[j][i].xvel[0];
WakePts[j][i].z[0] += WakePts[j][i].zvel[0];
WakePts[j][i].x[1] += WakePts[j][i].xvel[1];
WakePts[j][i].z[1] += WakePts[j][i].zvel[1];
}
}
}
}
KayakMountType KayakGetMountType(ITEM_INFO* laraItem, short itemNumber)
{
auto* lara = GetLaraInfo(laraItem);
auto* kayakItem = &g_Level.Items[itemNumber];
if (!(TrInput & IN_ACTION) ||
lara->Control.HandStatus != HandStatus::Free ||
laraItem->Airborne)
{
return KayakMountType::None;
}
int distance = pow(laraItem->Position.xPos - kayakItem->Position.xPos, 2) + pow(laraItem->Position.zPos - kayakItem->Position.zPos, 2);
if (distance > pow(360, 2))
return KayakMountType::None;
auto probe = GetCollisionResult(kayakItem);
if (probe.Position.Floor > -32000)
{
short angle = phd_atan(kayakItem->Position.zPos - laraItem->Position.zPos, kayakItem->Position.xPos - laraItem->Position.xPos);
angle -= kayakItem->Position.yRot;
int deltaAngle = laraItem->Position.yRot - kayakItem->Position.yRot;
if (angle > -ANGLE(45.0f) && angle < ANGLE(135.0f))
{
deltaAngle = laraItem->Position.yRot - kayakItem->Position.yRot;
if (deltaAngle > ANGLE(45.0f) && deltaAngle < ANGLE(135.0f))
return KayakMountType::Left;
}
else
{
deltaAngle = laraItem->Position.yRot - kayakItem->Position.yRot;
if (deltaAngle > ANGLE(225.0f) && deltaAngle < ANGLE(315.0f))
return KayakMountType::Right;
}
}
return KayakMountType::None;
}
int KayakGetCollisionAnim(ITEM_INFO* kayakItem, int xDiff, int zDiff)
{
xDiff = kayakItem->Position.xPos - xDiff;
zDiff = kayakItem->Position.zPos - zDiff;
if ((xDiff) || (zDiff))
{
float s = phd_sin(kayakItem->Position.yRot);
float c = phd_cos(kayakItem->Position.yRot);
int front = zDiff * c + xDiff * s;
int side = -zDiff * s + xDiff * c;
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 KayakDoDynamics(int height, int verticalVelocity, int* y)
{
if (height > * y)
{
*y += verticalVelocity;
if (*y > height)
{
*y = height;
verticalVelocity = 0;
}
else
verticalVelocity += GRAVITY;
}
else
{
int kick = (height - *y) * 4;
if (kick < KAYAK_MAX_KICK)
kick = KAYAK_MAX_KICK;
verticalVelocity += (kick - verticalVelocity) / 8;
if (*y > height)
*y = height;
}
return verticalVelocity;
}
void KayakDoCurrent(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
auto* lara = GetLaraInfo(laraItem);
auto* room = &g_Level.Rooms[kayakItem->RoomNumber];
if (!lara->WaterCurrentActive)
{
int absVelocity = abs(lara->WaterCurrentPull.x);
int shift;
if (absVelocity > 16)
shift = 4;
else if (absVelocity > 8)
shift = 3;
else
shift = 2;
lara->WaterCurrentPull.x -= lara->WaterCurrentPull.x >> shift;
if (abs(lara->WaterCurrentPull.x) < 4)
lara->WaterCurrentPull.x = 0;
absVelocity = abs(lara->WaterCurrentPull.z);
if (absVelocity > 16)
shift = 4;
else if (absVelocity > 8)
shift = 3;
else
shift = 2;
lara->WaterCurrentPull.z -= lara->WaterCurrentPull.z >> shift;
if (abs(lara->WaterCurrentPull.z) < 4)
lara->WaterCurrentPull.z = 0;
if (lara->WaterCurrentPull.x == 0 && lara->WaterCurrentPull.z == 0)
return;
}
else
{
int sinkval = lara->WaterCurrentActive - 1;
PHD_VECTOR target;
target.x = g_Level.Sinks[sinkval].x;
target.y = g_Level.Sinks[sinkval].y;
target.z = g_Level.Sinks[sinkval].z;
int angle = (((mGetAngle(target.x, target.z, laraItem->Position.xPos, laraItem->Position.zPos) - ANGLE(90))) / 16) & 4095;
int dx = target.x - laraItem->Position.xPos;
int dz = target.z - laraItem->Position.zPos;
int velocity = g_Level.Sinks[sinkval].strength;
dx = phd_sin(angle * 16) * velocity * 1024;
dz = phd_cos(angle * 16) * velocity * 1024;
lara->WaterCurrentPull.x += (dx - lara->WaterCurrentPull.x) / 16;
lara->WaterCurrentPull.z += (dz - lara->WaterCurrentPull.z) / 16;
}
kayakItem->Position.xPos += lara->WaterCurrentPull.x / 256;
kayakItem->Position.zPos += lara->WaterCurrentPull.z / 256;
lara->WaterCurrentActive = 0;
}
int KayakTestHeight(ITEM_INFO* kayakItem, int x, int z, PHD_VECTOR* pos)
{
Matrix world =
Matrix::CreateFromYawPitchRoll(TO_RAD(kayakItem->Position.yRot), TO_RAD(kayakItem->Position.xRot), TO_RAD(kayakItem->Position.zRot)) *
Matrix::CreateTranslation(kayakItem->Position.xPos, kayakItem->Position.yPos, kayakItem->Position.zPos);
Vector3 vec = Vector3(x, 0, z);
vec = Vector3::Transform(vec, world);
pos->x = vec.x;
pos->y = vec.y;
pos->z = vec.z;
auto probe = GetCollisionResult(pos->x, pos->y, pos->z, kayakItem->RoomNumber);
int probedRoomNum = probe.RoomNumber;
int height = GetWaterHeight(pos->x, pos->y, pos->z, probedRoomNum);
if (height == NO_HEIGHT)
{
height = probe.Position.Floor;
if (height == NO_HEIGHT)
return height;
}
return (height - 5);
}
bool KayakCanGetOut(ITEM_INFO* kayakItem, int dir)
{
PHD_VECTOR pos;
int height = KayakTestHeight(kayakItem, (dir < 0) ? -DISMOUNT_DISTANCE : DISMOUNT_DISTANCE, 0, &pos);
if ((kayakItem->Position.yPos - height) > 0)
return false;
return true;
}
int KayakDoShift(ITEM_INFO* kayakItem, PHD_VECTOR* pos, PHD_VECTOR* old)
{
int x = pos->x / SECTOR(1);
int z = pos->z / SECTOR(1);
int xOld = old->x / SECTOR(1);
int zOld = old->z / SECTOR(1);
int xShift = pos->x & (SECTOR(1) - 1);
int zShift = pos->z & (SECTOR(1) - 1);
if (x == xOld)
{
old->x = 0;
if (z == zOld)
{
kayakItem->Position.zPos += old->z - pos->z;
kayakItem->Position.xPos += old->x - pos->x;
}
else if (z > zOld)
{
kayakItem->Position.zPos -= zShift + 1;
return (pos->x - kayakItem->Position.xPos);
}
else
{
kayakItem->Position.zPos += SECTOR(1) - zShift;
return (kayakItem->Position.xPos - pos->x);
}
}
else if (z == zOld)
{
old->z = 0;
if (x > xOld)
{
kayakItem->Position.xPos -= xShift + 1;
return (kayakItem->Position.zPos - pos->z);
}
else
{
kayakItem->Position.xPos += SECTOR(1) - xShift;
return (pos->z - kayakItem->Position.zPos);
}
}
else
{
x = 0;
z = 0;
auto probe = GetCollisionResult(old->x, pos->y, pos->z, kayakItem->RoomNumber);
if (probe.Position.Floor < (old->y - CLICK(1)))
{
if (pos->z > old->z)
z = -zShift - 1;
else
z = SECTOR(1) - zShift;
}
probe = GetCollisionResult(pos->x, pos->y, old->z, kayakItem->RoomNumber);
if (probe.Position.Floor < (old->y - CLICK(1)))
{
if (pos->x > old->x)
x = -xShift - 1;
else
x = SECTOR(1) - xShift;
}
if (x && z)
{
kayakItem->Position.xPos += x;
kayakItem->Position.zPos += z;
}
else if (x)
{
kayakItem->Position.xPos += x;
if (x > 0)
return (pos->z - kayakItem->Position.zPos);
else
return (kayakItem->Position.zPos - pos->z);
}
else if (z)
{
kayakItem->Position.zPos += z;
if (z > 0)
return (kayakItem->Position.xPos - pos->x);
else
return (pos->x - kayakItem->Position.xPos);
}
else
{
kayakItem->Position.xPos += (old->x - pos->x);
kayakItem->Position.zPos += (old->z - pos->z);
}
}
return 0;
}
void KayakToBackground(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
auto* kayak = (KayakInfo*)kayakItem->Data;
kayak->OldPos = kayakItem->Position;
PHD_VECTOR oldPos[9];
int height[8];
height[0] = KayakTestHeight(kayakItem, 0, 1024, &oldPos[0]);
height[1] = KayakTestHeight(kayakItem, -96, 512, &oldPos[1]);
height[2] = KayakTestHeight(kayakItem, 96, 512, &oldPos[2]);
height[3] = KayakTestHeight(kayakItem, -128, 128, &oldPos[3]);
height[4] = KayakTestHeight(kayakItem, 128, 128, &oldPos[4]);
height[5] = KayakTestHeight(kayakItem, -128, -320, &oldPos[5]);
height[6] = KayakTestHeight(kayakItem, 128, -320, &oldPos[6]);
height[7] = KayakTestHeight(kayakItem, 0, -640, &oldPos[7]);
for (int i = 0; i < 8; i++)
{
if (oldPos[i].y > height[i])
oldPos[i].y = height[i];
}
oldPos[8].x = kayakItem->Position.xPos;
oldPos[8].y = kayakItem->Position.yPos;
oldPos[8].z = kayakItem->Position.zPos;
PHD_VECTOR frontPos, leftPos, rightPos;
int frontHeight = KayakTestHeight(kayakItem, 0, 1024, &frontPos);
int leftHeight = KayakTestHeight(kayakItem, -KAYAK_X, KAYAK_Z, &leftPos);
int rightHeight = KayakTestHeight(kayakItem, KAYAK_X, KAYAK_Z, &rightPos);
kayakItem->Position.yRot += kayak->TurnRate / (USHRT_MAX + 1);
kayakItem->Position.xPos += kayakItem->Velocity * phd_sin(kayakItem->Position.yRot);
kayakItem->Position.zPos += kayakItem->Velocity * phd_cos(kayakItem->Position.yRot);
KayakDoCurrent(laraItem,kayakItem);
kayak->LeftVerticalVelocity = KayakDoDynamics(leftHeight, kayak->LeftVerticalVelocity, &leftPos.y);
kayak->RightVerticalVelocity = KayakDoDynamics(rightHeight, kayak->RightVerticalVelocity, &rightPos.y);
kayak->FrontVerticalVelocity = KayakDoDynamics(frontHeight, kayak->FrontVerticalVelocity, &frontPos.y);
kayakItem->VerticalVelocity = KayakDoDynamics(kayak->WaterHeight, kayakItem->VerticalVelocity, &kayakItem->Position.yPos);
int height2 = (leftPos.y + rightPos.y) / 2;
int x = phd_atan(1024, kayakItem->Position.yPos - frontPos.y);
int z = phd_atan(KAYAK_X, height2 - leftPos.y);
kayakItem->Position.xRot = x;
kayakItem->Position.zRot = z;
int xOld = kayakItem->Position.xPos;
int zOld = kayakItem->Position.zPos;
int rot = 0;
PHD_VECTOR pos;
if ((height2 = KayakTestHeight(kayakItem, 0, -CLICK(2.5f), &pos)) < (oldPos[7].y - KAYAK_COLLIDE))
rot = KayakDoShift(kayakItem, &pos, &oldPos[7]);
if ((height2 = KayakTestHeight(kayakItem, CLICK(0.5f), -CLICK(1.25f), &pos)) < (oldPos[6].y - KAYAK_COLLIDE))
rot += KayakDoShift(kayakItem, &pos, &oldPos[6]);
if ((height2 = KayakTestHeight(kayakItem, -CLICK(0.5f), -CLICK(1.25f), &pos)) < (oldPos[5].y - KAYAK_COLLIDE))
rot += KayakDoShift(kayakItem, &pos, &oldPos[5]);
if ((height2 = KayakTestHeight(kayakItem, CLICK(0.5f), CLICK(0.5f), &pos)) < (oldPos[4].y - KAYAK_COLLIDE))
rot += KayakDoShift(kayakItem, &pos, &oldPos[4]);
if ((height2 = KayakTestHeight(kayakItem, -CLICK(0.5f), CLICK(0.5f), &pos)) < (oldPos[3].y - KAYAK_COLLIDE))
rot += KayakDoShift(kayakItem, &pos, &oldPos[3]);
if ((height2 = KayakTestHeight(kayakItem, 96, CLICK(2), &pos)) < (oldPos[2].y - KAYAK_COLLIDE))
rot += KayakDoShift(kayakItem, &pos, &oldPos[2]);
if ((height2 = KayakTestHeight(kayakItem, -96, CLICK(2), &pos)) < (oldPos[1].y - KAYAK_COLLIDE))
rot += KayakDoShift(kayakItem, &pos, &oldPos[1]);
if ((height2 = KayakTestHeight(kayakItem, 0, CLICK(4), &pos)) < (oldPos[0].y - KAYAK_COLLIDE))
rot += KayakDoShift(kayakItem, &pos, &oldPos[0]);
kayakItem->Position.yRot += rot;
auto probe = GetCollisionResult(kayakItem);
int probedRoomNum = probe.RoomNumber;
height2 = GetWaterHeight(kayakItem->Position.xPos, kayakItem->Position.yPos, kayakItem->Position.zPos, probedRoomNum);
if (height2 == NO_HEIGHT)
height2 = probe.Position.Floor;
if (height2 < (kayakItem->Position.yPos - KAYAK_COLLIDE))
KayakDoShift(kayakItem, (PHD_VECTOR*)&kayakItem->Position, &oldPos[8]);
probe = GetCollisionResult(kayakItem);
probedRoomNum = probe.RoomNumber;
height2 = GetWaterHeight(kayakItem->Position.xPos, kayakItem->Position.yPos, kayakItem->Position.zPos, probedRoomNum);
if (height2 == NO_HEIGHT)
height2 = probe.Position.Floor;
if (height2 == NO_HEIGHT)
{
GAME_VECTOR kayakPos;
kayakPos.x = kayak->OldPos.xPos;
kayakPos.y = kayak->OldPos.yPos;
kayakPos.z = kayak->OldPos.zPos;
kayakPos.roomNumber = kayakItem->RoomNumber;
CameraCollisionBounds(&kayakPos, 256, 0);
{
kayakItem->Position.xPos = kayakPos.x;
kayakItem->Position.yPos = kayakPos.y;
kayakItem->Position.zPos = kayakPos.z;
kayakItem->RoomNumber = kayakPos.roomNumber;
}
}
int collide = KayakGetCollisionAnim(kayakItem, xOld, zOld);
int slip = 0; // Remnant?
if (slip || collide)
{
int newVelocity;
newVelocity = (kayakItem->Position.zPos - oldPos[8].z) * phd_cos(kayakItem->Position.yRot) + (kayakItem->Position.xPos - oldPos[8].x) * phd_sin(kayakItem->Position.yRot);
newVelocity *= 256;
if (slip)
{
if (kayak->Velocity <= MAX_VELOCITY)
kayak->Velocity = newVelocity;
}
else
{
if (kayak->Velocity > 0 && newVelocity < kayak->Velocity)
kayak->Velocity = newVelocity;
else if (kayak->Velocity < 0 && newVelocity > kayak->Velocity)
kayak->Velocity = newVelocity;
}
if (kayak->Velocity < -MAX_VELOCITY)
kayak->Velocity = -MAX_VELOCITY;
}
}
void KayakUserInput(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
auto* lara = GetLaraInfo(laraItem);
auto* kayak = (KayakInfo*)kayakItem->Data;
if (laraItem->HitPoints <= 0 &&
laraItem->ActiveState != KAYAK_STATE_IDLE_DEATH)
{
laraItem->AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_IDLE_DEATH;
laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase;
laraItem->ActiveState = laraItem->TargetState = KAYAK_STATE_IDLE_DEATH;
}
int frame = laraItem->FrameNumber - g_Level.Anims[laraItem->AnimNumber].frameBase;
switch (laraItem->ActiveState)
{
case KAYAK_STATE_IDLE:
if (TrInput & KAYAK_IN_DISMOUNT &&
!lara->WaterCurrentActive &&
!lara->WaterCurrentPull.x && !lara->WaterCurrentPull.z)
{
if (TrInput & KAYAK_IN_LEFT && KayakCanGetOut(kayakItem, -1))
{
laraItem->TargetState = KAYAK_STATE_DISMOUNT;
laraItem->RequiredState = KAYAK_STATE_DISMOUNT_LEFT;
}
else if (TrInput & KAYAK_IN_RIGHT && KayakCanGetOut(kayakItem, 1))
{
laraItem->TargetState = KAYAK_STATE_DISMOUNT;
laraItem->RequiredState = KAYAK_STATE_DISMOUNT_RIGHT;
}
}
else if (TrInput & KAYAK_IN_FORWARD)
{
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
kayak->Turn = false;
kayak->Forward = true;
}
else if (TrInput & KAYAK_IN_BACK)
laraItem->TargetState = KAYAK_STATE_BACK;
else if (TrInput & KAYAK_IN_LEFT)
{
laraItem->TargetState = KAYAK_STATE_TURN_LEFT;
if (kayak->Velocity)
kayak->Turn = false;
else
kayak->Turn = true;
kayak->Forward = false;
}
else if (TrInput & KAYAK_IN_RIGHT)
{
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
if (kayak->Velocity)
kayak->Turn = false;
else
kayak->Turn = true;
kayak->Forward = false;
}
else if (TrInput & KAYAK_IN_HOLD_LEFT &&
(kayak->Velocity ||
lara->WaterCurrentPull.x ||
lara->WaterCurrentPull.z))
{
laraItem->TargetState = KAYAK_STATE_HOLD_LEFT;
}
else if (TrInput & KAYAK_IN_HOLD_RIGHT &&
(kayak->Velocity ||
lara->WaterCurrentPull.x ||
lara->WaterCurrentPull.z))
{
laraItem->TargetState = KAYAK_STATE_HOLD_RIGHT;
}
break;
case KAYAK_STATE_TURN_LEFT:
if (kayak->Forward)
{
if (!frame)
kayak->LeftRightCount = 0;
// TODO: Sort out the bitwise operations.
if (frame == 2 && !(kayak->LeftRightCount & 0x80))
kayak->LeftRightCount++;
else if (frame > 2)
kayak->LeftRightCount &= ~0x80;
if (TrInput & KAYAK_IN_FORWARD)
{
if (TrInput & KAYAK_IN_LEFT)
{
if ((kayak->LeftRightCount & ~0x80) >= 2)
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
}
else
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
}
else
laraItem->TargetState = KAYAK_STATE_IDLE;
}
else if (!(TrInput & KAYAK_IN_LEFT))
laraItem->TargetState = KAYAK_STATE_IDLE;
if (frame == 7)
{
if (kayak->Forward)
{
kayak->TurnRate -= KAYAK_FORWARD_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_TURN)
kayak->TurnRate = -KAYAK_MAX_TURN;
kayak->Velocity += KAYAK_FORWARD_VELOCITY;
}
else if (kayak->Turn)
{
kayak->TurnRate -= KAYAK_HARD_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_STAT)
kayak->TurnRate = -KAYAK_MAX_STAT;
}
else
{
kayak->TurnRate -= KAYAK_LEFT_RIGHT_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_LEFT_RIGHT)
kayak->TurnRate = -KAYAK_MAX_LEFT_RIGHT;
kayak->Velocity += KAYAK_LEFT_RIGHT_VELOCITY;
}
}
if (frame > 6 && frame < 24 && frame & 1)
KayakDoRipple(kayakItem, -CLICK(1.5f), -CLICK(0.25f));
break;
case KAYAK_STATE_TURN_RIGHT:
if (kayak->Forward)
{
if (!frame)
kayak->LeftRightCount = 0;
if (frame == 2 && !(kayak->LeftRightCount & 0x80))
kayak->LeftRightCount++;
else if (frame > 2)
kayak->LeftRightCount &= ~0x80;
if (TrInput & KAYAK_IN_FORWARD)
{
if (TrInput & KAYAK_IN_RIGHT)
{
if ((kayak->LeftRightCount & ~0x80) >= 2)
laraItem->TargetState = KAYAK_STATE_TURN_LEFT;
}
else
laraItem->TargetState = KAYAK_STATE_TURN_LEFT;
}
else
laraItem->TargetState = KAYAK_STATE_IDLE;
}
else if (!(TrInput & KAYAK_IN_RIGHT))
laraItem->TargetState = KAYAK_STATE_IDLE;
if (frame == 7)
{
if (kayak->Forward)
{
kayak->TurnRate += KAYAK_FORWARD_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_TURN)
kayak->TurnRate = KAYAK_MAX_TURN;
kayak->Velocity += KAYAK_FORWARD_VELOCITY;
}
else if (kayak->Turn)
{
kayak->TurnRate += KAYAK_HARD_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_STAT)
kayak->TurnRate = KAYAK_MAX_STAT;
}
else
{
kayak->TurnRate += KAYAK_LEFT_RIGHT_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_LEFT_RIGHT)
kayak->TurnRate = KAYAK_MAX_LEFT_RIGHT;
kayak->Velocity += KAYAK_LEFT_RIGHT_VELOCITY;
}
}
if (frame > 6 && frame < 24 && frame & 1)
KayakDoRipple(kayakItem, CLICK(1.5f), -CLICK(0.25f));
break;
case KAYAK_STATE_BACK:
if (!(TrInput & KAYAK_IN_BACK))
laraItem->TargetState = KAYAK_STATE_IDLE;
if ((laraItem->AnimNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex) == KAYAK_ANIM_PADDLE_BACK)
{
if (frame == 8)
{
kayak->TurnRate += KAYAK_FORWARD_ROTATION;
kayak->Velocity -= KAYAK_FORWARD_VELOCITY;
}
if (frame == 31)
{
kayak->TurnRate -= KAYAK_FORWARD_ROTATION;
kayak->Velocity -= KAYAK_FORWARD_VELOCITY;
}
if (frame < 15 && frame & 1)
KayakDoRipple(kayakItem, CLICK(1.5f), -CLICK(0.5f));
else if (frame >= 20 && frame <= 34 && frame & 1)
KayakDoRipple(kayakItem, -CLICK(1.5f), -CLICK(0.5f));
}
break;
case KAYAK_STATE_HOLD_LEFT:
if (!(TrInput & KAYAK_IN_HOLD_LEFT) ||
(!kayak->Velocity &&
!lara->WaterCurrentPull.x &&
!lara->WaterCurrentPull.z))
{
laraItem->TargetState = KAYAK_STATE_IDLE;
}
else if ((laraItem->AnimNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex) == KAYAK_ANIM_HOLD_PADDLE_LEFT)
{
if (kayak->Velocity >= 0)
{
kayak->TurnRate -= KAYAK_TURN_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_TURN)
kayak->TurnRate = -KAYAK_MAX_TURN;
kayak->Velocity += -KAYAK_TURN_BRAKE;
if (kayak->Velocity < 0)
kayak->Velocity = 0;
}
if (kayak->Velocity < 0)
{
kayak->TurnRate += KAYAK_TURN_ROTATION;
kayak->Velocity += KAYAK_TURN_BRAKE;
if (kayak->Velocity > 0)
kayak->Velocity = 0;
}
if (!(Wibble & 3))
KayakDoRipple(kayakItem, -CLICK(1), -CLICK(1));
}
break;
case KAYAK_STATE_HOLD_RIGHT:
if (!(TrInput & KAYAK_IN_HOLD_RIGHT) ||
(!kayak->Velocity &&
!lara->WaterCurrentPull.x &&
!lara->WaterCurrentPull.z))
{
laraItem->TargetState = KAYAK_STATE_IDLE;
}
else if ((laraItem->AnimNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex) == KAYAK_ANIM_HOLD_PADDLE_RIGHT)
{
if (kayak->Velocity >= 0)
{
kayak->TurnRate += KAYAK_TURN_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_TURN)
kayak->TurnRate = KAYAK_MAX_TURN;
kayak->Velocity += -KAYAK_TURN_BRAKE;
if (kayak->Velocity < 0)
kayak->Velocity = 0;
}
if (kayak->Velocity < 0)
{
kayak->TurnRate -= KAYAK_TURN_ROTATION;
kayak->Velocity += KAYAK_TURN_BRAKE;
if (kayak->Velocity > 0)
kayak->Velocity = 0;
}
if (!(Wibble & 3))
KayakDoRipple(kayakItem, CLICK(1), -CLICK(1));
}
break;
case KAYAK_STATE_MOUNT_LEFT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_GET_PADDLE &&
frame == 24 &&
!(kayak->Flags & 0x80))
{
kayak->Flags |= 0x80;
lara->MeshPtrs[LM_RHAND] = Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND;
laraItem->MeshBits &= ~LARA_LEG_BITS;
}
break;
case KAYAK_STATE_DISMOUNT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_DISMOUNT_START &&
frame == 27 &&
kayak->Flags & 0x80)
{
kayak->Flags &= ~0x80;
lara->MeshPtrs[LM_RHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_RHAND;
laraItem->MeshBits |= LARA_LEG_BITS;
}
laraItem->TargetState = laraItem->RequiredState;
break;
case KAYAK_STATE_DISMOUNT_LEFT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_DISMOUNT_LEFT &&
frame == 83)
{
PHD_VECTOR vec = { 0, 350, 500 };
GetLaraJointPosition(&vec, LM_HIPS);
SetAnimation(laraItem, LA_JUMP_FORWARD);
laraItem->Position.xPos = vec.x;
laraItem->Position.yPos = vec.y;
laraItem->Position.zPos = vec.z;
laraItem->Position.xRot = 0;
laraItem->Position.yRot = kayakItem->Position.yRot - ANGLE(90.0f);
laraItem->Position.zRot = 0;
laraItem->Velocity = 40;
laraItem->VerticalVelocity = -50;
laraItem->Airborne = true;
lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM;
kayak->LeftRightCount = 0;
}
break;
case KAYAK_STATE_DISMOUNT_RIGHT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_DISMOUNT_RIGHT &&
frame == 83)
{
PHD_VECTOR vec = { 0, 350, 500 };
GetLaraJointPosition(&vec, LM_HIPS);
SetAnimation(laraItem, LA_JUMP_FORWARD);
laraItem->Position.xPos = vec.x;
laraItem->Position.yPos = vec.y;
laraItem->Position.zPos = vec.z;
laraItem->Position.xRot = 0;
laraItem->Position.yRot = kayakItem->Position.yRot + ANGLE(90.0f);
laraItem->Position.zRot = 0;
laraItem->Velocity = 40;
laraItem->VerticalVelocity = -50;
laraItem->Airborne = true;
lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM;
kayak->LeftRightCount = 0;
}
}
if (kayak->Velocity > 0)
{
kayak->Velocity -= KAYAK_FRICTION;
if (kayak->Velocity < 0)
kayak->Velocity = 0;
}
else if (kayak->Velocity < 0)
{
kayak->Velocity += KAYAK_FRICTION;
if (kayak->Velocity > 0)
kayak->Velocity = 0;
}
if (kayak->Velocity > MAX_VELOCITY)
kayak->Velocity = MAX_VELOCITY;
else if (kayak->Velocity < -MAX_VELOCITY)
kayak->Velocity = -MAX_VELOCITY;
kayakItem->Velocity = (kayak->Velocity / (USHRT_MAX + 1));
;
if (kayak->TurnRate >= 0)
{
kayak->TurnRate -= KAYAK_ROTATE_FRICTION;
if (kayak->TurnRate < 0)
kayak->TurnRate = 0;
}
else if (kayak->TurnRate < 0)
{
kayak->TurnRate += KAYAK_ROTATE_FRICTION;
if (kayak->TurnRate > 0)
kayak->TurnRate = 0;
}
}
void KayakToItemCollision(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
short roomsToCheck[128];
short numRoomsToCheck = 0;
roomsToCheck[numRoomsToCheck++] = kayakItem->RoomNumber;
auto* room = &g_Level.Rooms[kayakItem->RoomNumber];
for (int i = 0; i < room->doors.size(); i++)
roomsToCheck[numRoomsToCheck++] = room->doors[i].room;
for (int i = 0; i < numRoomsToCheck; i++)
{
short itemNum = g_Level.Rooms[roomsToCheck[i]].itemNumber;
while (itemNum != NO_ITEM)
{
auto* item = &g_Level.Items[itemNum];
short nextItem = item->NextItem;
if (item->Collidable && item->Status != ITEM_INVISIBLE)
{
auto* object = &Objects[item->ObjectNumber];
if (object->collision &&
(item->ObjectNumber == ID_TEETH_SPIKES ||
item->ObjectNumber == ID_DARTS &&
item->ActiveState != 1))
{
int x = kayakItem->Position.xPos - item->Position.xPos;
int y = kayakItem->Position.yPos - item->Position.yPos;
int z = kayakItem->Position.zPos - item->Position.zPos;
if (x > -2048 && x < 2048 &&
y > -2048 && y < 2048 &&
z > -2048 && z < 2048)
{
if (TestBoundsCollide(item, kayakItem, KAYAK_TO_ENTITY_RADIUS))
{
DoLotsOfBlood(laraItem->Position.xPos, laraItem->Position.yPos - STEP_SIZE, laraItem->Position.zPos, kayakItem->Velocity, kayakItem->Position.yRot, laraItem->RoomNumber, 3);
laraItem->HitPoints -= 5;
}
}
}
}
itemNum = nextItem;
}
}
}
void KayakLaraRapidsDrown(ITEM_INFO* laraItem)
{
auto* lara = GetLaraInfo(laraItem);
laraItem->AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_OVERBOARD_DEATH;
laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase;
laraItem->ActiveState = 12; // TODO
laraItem->TargetState = 12;
laraItem->HitPoints = 0;
laraItem->Velocity = 0;
laraItem->VerticalVelocity = 0;
laraItem->Airborne = false;
AnimateItem(laraItem);
lara->ExtraAnim = 1;
lara->Control.HandStatus = HandStatus::Busy;
lara->Control.Weapon.GunType = WEAPON_NONE;
lara->HitDirection = -1;
}
void KayakCollision(short itemNumber, ITEM_INFO* laraItem, COLL_INFO* coll)
{
auto* lara = GetLaraInfo(laraItem);
auto* kayakItem = &g_Level.Items[itemNumber];
auto* kayak = (KayakInfo*)kayakItem->Data;
if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM)
return;
KayakMountType mountType = KayakGetMountType(laraItem, itemNumber);
if (mountType != KayakMountType::None)
{
lara->Vehicle = itemNumber;
if (lara->Control.Weapon.GunType == WEAPON_FLARE)
{
CreateFlare(laraItem, ID_FLARE_ITEM, 0);
UndrawFlareMeshes(laraItem);
lara->Flare.ControlLeft = 0;
lara->Control.Weapon.RequestGunType = lara->Control.Weapon.GunType = WEAPON_NONE;
}
if (mountType == KayakMountType::Right)
laraItem->AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_MOUNT_RIGHT;
else if (mountType == KayakMountType::Left)
laraItem->AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_MOUNT_LEFT;
laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase;
laraItem->ActiveState = laraItem->TargetState = KAYAK_STATE_MOUNT_LEFT;
laraItem->Position.xPos = kayakItem->Position.xPos;
laraItem->Position.yPos = kayakItem->Position.yPos;
laraItem->Position.zPos = kayakItem->Position.zPos;
laraItem->Position.xRot = 0;
laraItem->Position.yRot = kayakItem->Position.yRot;
laraItem->Position.zRot = 0;
laraItem->Velocity = 0;
laraItem->VerticalVelocity = 0;
laraItem->Airborne = false;
lara->Control.WaterStatus = WaterStatus::Dry;
if (laraItem->RoomNumber != kayakItem->RoomNumber)
ItemNewRoom(lara->ItemNumber, kayakItem->RoomNumber);
AnimateItem(laraItem);
kayak->WaterHeight = kayakItem->Position.yPos;
kayak->Flags = 0;
}
else
{
coll->Setup.EnableObjectPush = true;
ObjectCollision(itemNumber, laraItem, coll);
}
}
bool KayakControl(ITEM_INFO* laraItem)
{
auto* lara = GetLaraInfo(laraItem);
auto* kayakItem = &g_Level.Items[lara->Vehicle];
auto* kayak = (KayakInfo*)kayakItem->Data;
if (TrInput & IN_LOOK)
LookUpDown(laraItem);
int ofs = kayakItem->VerticalVelocity;
KayakUserInput(laraItem, kayakItem);
KayakToBackground(laraItem, kayakItem);
TestTriggers(kayakItem, false);
auto probe = GetCollisionResult(kayakItem);
int water = GetWaterHeight(kayakItem->Position.xPos, kayakItem->Position.yPos, kayakItem->Position.zPos, probe.RoomNumber);
kayak->WaterHeight = water;
if (kayak->WaterHeight == NO_HEIGHT)
{
water = probe.Position.Floor;
kayak->WaterHeight = water;
kayak->TrueWater = false;
}
else
{
kayak->WaterHeight -= 5;
kayak->TrueWater = true;
}
if ((ofs - kayakItem->VerticalVelocity) > 128 &&
kayakItem->VerticalVelocity == 0 &&
water != NO_HEIGHT)
{
int damage = ofs - kayakItem->VerticalVelocity;
if (damage > 160)
laraItem->HitPoints -= (damage - 160) * 8;
}
if (lara->Vehicle != NO_ITEM)
{
if (kayakItem->RoomNumber != probe.RoomNumber)
{
ItemNewRoom(lara->Vehicle, probe.RoomNumber);
ItemNewRoom(lara->ItemNumber, probe.RoomNumber);
}
laraItem->Position.xPos = kayakItem->Position.xPos;
laraItem->Position.yPos = kayakItem->Position.yPos;
laraItem->Position.zPos = kayakItem->Position.zPos;
laraItem->Position.xRot = kayakItem->Position.xRot;
laraItem->Position.yRot = kayakItem->Position.yRot;
laraItem->Position.zRot = kayakItem->Position.zRot / 2;
AnimateItem(laraItem);
kayakItem->AnimNumber = Objects[ID_KAYAK].animIndex + (laraItem->AnimNumber - Objects[ID_KAYAK_LARA_ANIMS].animIndex);
kayakItem->FrameNumber = g_Level.Anims[kayakItem->AnimNumber].frameBase + (laraItem->FrameNumber - g_Level.Anims[laraItem->AnimNumber].frameBase);
Camera.targetElevation = -ANGLE(30.0f);
Camera.targetDistance = CLICK(8);
}
if (!(Wibble & 15) && kayak->TrueWater)
{
KayakDoWake(kayakItem, -CLICK(0.5f), 0, 0);
KayakDoWake(kayakItem, CLICK(0.5f), 0, 1);
}
if (Wibble & 7)
{
if (!kayak->TrueWater && kayakItem->VerticalVelocity < 20)
{
PHD_VECTOR dest;
char cnt = 0;
short MistZPos[10] = { 900, 750, 600, 450, 300, 150, 0, -150, -300, -450 };
short MistXPos[10] = { 32, 96, 170, 220, 300, 400, 400, 300, 200, 64 };
cnt ^= 1;
for (int i = cnt; i < 10; i += 2)
{
if (GetRandomControl() & 1)
dest.x = (MistXPos[i] / 2);
else
dest.x = -(MistXPos[i] / 2);
dest.y = 50;
dest.z = MistZPos[i];
}
}
}
if (!kayakItem->Velocity &&
!lara->WaterCurrentPull.x &&
!lara->WaterCurrentPull.z)
{
if (kayak->WakeShade)
kayak->WakeShade--;
}
else
{
if (kayak->WakeShade < 16)
kayak->WakeShade++;
}
KayakUpdateWakeFX();
KayakToItemCollision(laraItem, kayakItem);
return (lara->Vehicle != NO_ITEM) ? true : false;
}