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

1373 lines
36 KiB
C++
Raw Normal View History

#include "framework.h"
2021-12-24 03:32:19 +03:00
#include "Objects/TR3/Vehicles/kayak.h"
2021-12-24 11:08:16 +03:00
2021-12-22 16:23:57 +03:00
#include "Game/animation.h"
#include "Game/camera.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h"
2021-12-24 11:08:16 +03:00
#include "Game/control/control.h"
#include "Game/effects/effects.h"
2021-12-22 16:23:57 +03:00
#include "Game/items.h"
2021-12-24 11:08:16 +03:00
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_flare.h"
#include "Game/Lara/lara_helpers.h"
2021-12-24 11:08:16 +03:00
#include "Objects/TR3/Vehicles/kayak_info.h"
2021-12-22 16:23:57 +03:00
#include "Specific/level.h"
#include "Specific/input.h"
2021-12-24 11:08:16 +03:00
#include "Specific/setup.h"
2021-09-25 11:27:47 +02:00
using std::vector;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
#define KAYAK_COLLIDE CLICK(0.25f)
#define DISMOUNT_DISTANCE CLICK(3) // TODO: Find accurate distance.
2022-02-22 23:22:43 +11:00
#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
2020-09-16 06:49:58 +02:00
#define HIT_BACK 1
#define HIT_FRONT 2
#define HIT_LEFT 3
#define HIT_RIGHT 4
2022-01-07 16:27:50 +11:00
#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)
2020-09-16 06:49:58 +02:00
#define KAYAK_DRAW_SHIFT 32
2022-01-07 16:27:50 +11:00
#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))
2020-09-16 06:49:58 +02:00
#define NUM_WAKE_SPRITES 32
#define WAKE_SIZE 32
2022-02-22 23:22:43 +11:00
#define WAKE_VELOCITY 4
2020-09-16 06:49:58 +02:00
#define KAYAK_X 128
#define KAYAK_Z 128
2022-01-07 16:27:50 +11:00
#define KAYAK_MAX_KICK -80
2022-02-22 23:22:43 +11:00
#define KAYAK_MIN_BOUNCE ((MAX_VELOCITY / 2) / 256)
2022-01-07 16:27:50 +11:00
#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
{
2022-02-22 23:22:43 +11:00
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,
2022-01-07 16:27:50 +11:00
};
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
};
2020-09-16 06:49:58 +02:00
2022-02-20 17:12:42 +11:00
enum class KayakMountType
{
None,
Left,
Right
};
2020-09-16 06:49:58 +02:00
struct WAKE_PTS
{
2020-09-16 06:49:58 +02:00
int x[2];
int y;
int z[2];
short xvel[2];
short zvel[2];
byte life;
byte pad[3];
};
2021-03-24 12:49:36 -05:00
WAKE_PTS WakePts[NUM_WAKE_SPRITES][2];
2022-02-20 17:12:42 +11:00
void InitialiseKayak(short itemNumber)
2022-01-07 16:27:50 +11:00
{
2022-02-20 17:12:42 +11:00
auto* kayakItem = &g_Level.Items[itemNumber];
kayakItem->Data = KayakInfo();
2022-02-28 21:02:19 +11:00
auto* kayak = (KayakInfo*)kayakItem->Data;
2022-02-20 17:12:42 +11:00
2022-02-28 21:02:19 +11:00
kayak->TurnRate = 0;
kayak->Velocity = 0;
kayak->FrontVerticalVelocity = 0;
kayak->LeftVerticalVelocity = 0;
kayak->RightVerticalVelocity = 0;
kayak->LeftRightCount = 0;
kayak->OldPos = kayakItem->Position;
2022-03-03 21:31:32 +11:00
kayak->CurrentStartWake = 0;
kayak->WakeShade = 0;
2022-02-28 21:02:19 +11:00
kayak->Flags = 0;
2022-02-20 17:12:42 +11:00
2022-01-07 16:27:50 +11:00
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)
2020-09-16 06:49:58 +02:00
{
2022-03-03 21:31:32 +11:00
auto* kayak = (KayakInfo*)kayakItem->Data;
if (WakePts[kayak->CurrentStartWake][rotate].life)
2020-09-16 06:49:58 +02:00
return;
float s = phd_sin(kayakItem->Position.yRot);
float c = phd_cos(kayakItem->Position.yRot);
2020-09-16 06:49:58 +02:00
int x = kayakItem->Position.xPos + zOffset * s + xOffset * c;
int z = kayakItem->Position.zPos + zOffset * c - xOffset * s;
2020-09-16 06:49:58 +02:00
int probedRoomNum = GetCollisionResult(x, kayakItem->Position.yPos, z, kayakItem->RoomNumber).RoomNumber;
int waterHeight = GetWaterHeight(x, kayakItem->Position.yPos, z, probedRoomNum);
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
if (waterHeight != NO_HEIGHT)
2020-09-16 06:49:58 +02:00
{
2022-01-07 16:27:50 +11:00
short angle1, angle2;
if (kayakItem->Velocity < 0)
2020-09-16 06:49:58 +02:00
{
if (!rotate)
{
angle1 = kayakItem->Position.yRot - ANGLE(10.0f);
angle2 = kayakItem->Position.yRot - ANGLE(30.0f);
2020-09-16 06:49:58 +02:00
}
else
{
angle1 = kayakItem->Position.yRot + ANGLE(10.0f);
angle2 = kayakItem->Position.yRot + ANGLE(30.0f);
2020-09-16 06:49:58 +02:00
}
}
else
{
if (!rotate)
{
angle1 = kayakItem->Position.yRot - ANGLE(170.0f);
angle2 = kayakItem->Position.yRot - ANGLE(150.0f);
2020-09-16 06:49:58 +02:00
}
else
{
angle1 = kayakItem->Position.yRot + ANGLE(170.0f);
angle2 = kayakItem->Position.yRot + ANGLE(150.0f);
2020-09-16 06:49:58 +02:00
}
}
2022-01-07 16:27:50 +11:00
int xv[2], zv[2];
2022-02-22 23:22:43 +11:00
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);
2020-09-16 06:49:58 +02:00
2022-03-03 21:31:32 +11:00
WakePts[kayak->CurrentStartWake][rotate].y = kayakItem->Position.yPos + KAYAK_DRAW_SHIFT;
WakePts[kayak->CurrentStartWake][rotate].life = 0x40;
2022-01-07 16:27:50 +11:00
2020-09-16 06:49:58 +02:00
for (int i = 0; i < 2; i++)
{
2022-03-03 21:31:32 +11:00
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];
2020-09-16 06:49:58 +02:00
}
if (rotate == 1)
{
2022-03-03 21:31:32 +11:00
kayak->CurrentStartWake++;
kayak->CurrentStartWake &= (NUM_WAKE_SPRITES - 1);
2020-09-16 06:49:58 +02:00
}
}
}
2022-01-07 16:27:50 +11:00
void KayakDoRipple(ITEM_INFO* kayakItem, int xOffset, int zOffset)
2020-09-16 06:49:58 +02:00
{
float s = phd_sin(kayakItem->Position.yRot);
float c = phd_cos(kayakItem->Position.yRot);
2020-09-16 06:49:58 +02:00
int x = kayakItem->Position.xPos + zOffset * s + xOffset * c;
int z = kayakItem->Position.zPos + zOffset * c - xOffset * s;
2020-09-16 06:49:58 +02:00
int probedRoomNum = GetCollisionResult(x, kayakItem->Position.yPos, z, kayakItem->RoomNumber).RoomNumber;
int waterHeight = GetWaterHeight(x, kayakItem->Position.yPos, z, probedRoomNum);
2022-01-07 16:27:50 +11:00
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));
}
2020-09-16 06:49:58 +02:00
void KayakUpdateWakeFX()
{
2020-09-16 06:49:58 +02:00
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];
}
}
}
}
2022-02-20 17:12:42 +11:00
KayakMountType KayakGetMountType(ITEM_INFO* laraItem, short itemNumber)
{
2022-02-28 21:02:19 +11:00
auto* lara = GetLaraInfo(laraItem);
2022-02-20 17:12:42 +11:00
auto* kayakItem = &g_Level.Items[itemNumber];
2022-01-07 16:27:50 +11:00
if (!(TrInput & IN_ACTION) ||
2022-02-28 21:02:19 +11:00
lara->Control.HandStatus != HandStatus::Free ||
laraItem->Airborne)
2022-01-07 16:27:50 +11:00
{
return KayakMountType::None;
}
2022-02-20 17:12:42 +11:00
int distance = pow(laraItem->Position.xPos - kayakItem->Position.xPos, 2) + pow(laraItem->Position.zPos - kayakItem->Position.zPos, 2);
if (distance > pow(360, 2))
2022-01-07 16:27:50 +11:00
return KayakMountType::None;
2022-01-07 16:27:50 +11:00
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;
2022-01-07 16:27:50 +11:00
if (angle > -ANGLE(45.0f) && angle < ANGLE(135.0f))
{
deltaAngle = laraItem->Position.yRot - kayakItem->Position.yRot;
2022-01-07 16:27:50 +11:00
if (deltaAngle > ANGLE(45.0f) && deltaAngle < ANGLE(135.0f))
return KayakMountType::Left;
}
else
{
deltaAngle = laraItem->Position.yRot - kayakItem->Position.yRot;
2022-01-07 16:27:50 +11:00
if (deltaAngle > ANGLE(225.0f) && deltaAngle < ANGLE(315.0f))
return KayakMountType::Right;
}
}
2022-01-07 16:27:50 +11:00
return KayakMountType::None;
}
2022-01-07 16:27:50 +11:00
int KayakGetCollisionAnim(ITEM_INFO* kayakItem, int xDiff, int zDiff)
{
xDiff = kayakItem->Position.xPos - xDiff;
zDiff = kayakItem->Position.zPos - zDiff;
2022-01-07 16:27:50 +11:00
if ((xDiff) || (zDiff))
{
float s = phd_sin(kayakItem->Position.yRot);
float c = phd_cos(kayakItem->Position.yRot);
2022-01-07 16:27:50 +11:00
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;
}
2022-02-20 17:12:42 +11:00
int KayakDoDynamics(int height, int verticalVelocity, int* y)
{
if (height > * y)
{
2022-02-20 17:12:42 +11:00
*y += verticalVelocity;
if (*y > height)
{
*y = height;
2022-02-20 17:12:42 +11:00
verticalVelocity = 0;
}
else
2022-02-20 17:12:42 +11:00
verticalVelocity += GRAVITY;
}
else
{
2020-10-06 10:36:30 -05:00
int kick = (height - *y) * 4;
2022-01-07 16:27:50 +11:00
if (kick < KAYAK_MAX_KICK)
kick = KAYAK_MAX_KICK;
2022-02-20 17:12:42 +11:00
verticalVelocity += (kick - verticalVelocity) / 8;
2020-09-16 06:49:58 +02:00
if (*y > height)
*y = height;
}
2022-02-20 17:12:42 +11:00
return verticalVelocity;
}
2022-02-20 17:12:42 +11:00
void KayakDoCurrent(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
2022-02-28 21:02:19 +11:00
auto* lara = GetLaraInfo(laraItem);
2022-02-20 17:12:42 +11:00
auto* room = &g_Level.Rooms[kayakItem->RoomNumber];
if (!lara->WaterCurrentActive)
{
int absVelocity = abs(lara->WaterCurrentPull.x);
2022-01-07 16:27:50 +11:00
int shift;
2022-02-20 17:12:42 +11:00
if (absVelocity > 16)
2022-01-07 16:27:50 +11:00
shift = 4;
2022-02-20 17:12:42 +11:00
else if (absVelocity > 8)
2022-01-07 16:27:50 +11:00
shift = 3;
else
2022-01-07 16:27:50 +11:00
shift = 2;
lara->WaterCurrentPull.x -= lara->WaterCurrentPull.x >> shift;
if (abs(lara->WaterCurrentPull.x) < 4)
lara->WaterCurrentPull.x = 0;
absVelocity = abs(lara->WaterCurrentPull.z);
2022-02-20 17:12:42 +11:00
if (absVelocity > 16)
2022-01-07 16:27:50 +11:00
shift = 4;
2022-02-20 17:12:42 +11:00
else if (absVelocity > 8)
2022-01-07 16:27:50 +11:00
shift = 3;
else
2022-01-07 16:27:50 +11:00
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;
2020-09-16 06:49:58 +02:00
PHD_VECTOR target;
target.x = g_Level.Sinks[sinkval].x;
target.y = g_Level.Sinks[sinkval].y;
target.z = g_Level.Sinks[sinkval].z;
2020-09-16 06:49:58 +02:00
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;
2022-03-03 21:31:32 +11:00
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;
}
2022-01-07 16:27:50 +11:00
int KayakTestHeight(ITEM_INFO* kayakItem, int x, int z, PHD_VECTOR* pos)
{
2020-09-16 06:49:58 +02:00
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);
2020-09-16 06:49:58 +02:00
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);
2022-01-07 16:27:50 +11:00
int probedRoomNum = probe.RoomNumber;
2022-01-07 16:27:50 +11:00
int height = GetWaterHeight(pos->x, pos->y, pos->z, probedRoomNum);
if (height == NO_HEIGHT)
{
2022-01-07 16:27:50 +11:00
height = probe.Position.Floor;
if (height == NO_HEIGHT)
return height;
}
2022-01-07 16:27:50 +11:00
return (height - 5);
}
2022-01-07 16:27:50 +11:00
bool KayakCanGetOut(ITEM_INFO* kayakItem, int dir)
{
PHD_VECTOR pos;
2022-01-07 16:27:50 +11:00
int height = KayakTestHeight(kayakItem, (dir < 0) ? -DISMOUNT_DISTANCE : DISMOUNT_DISTANCE, 0, &pos);
if ((kayakItem->Position.yPos - height) > 0)
2020-09-16 06:49:58 +02:00
return false;
2020-09-16 06:49:58 +02:00
return true;
}
2022-01-07 16:27:50 +11:00
int KayakDoShift(ITEM_INFO* kayakItem, PHD_VECTOR* pos, PHD_VECTOR* old)
{
2022-01-07 16:27:50 +11:00
int x = pos->x / SECTOR(1);
int z = pos->z / SECTOR(1);
2022-01-07 16:27:50 +11:00
int xOld = old->x / SECTOR(1);
int zOld = old->z / SECTOR(1);
2022-02-22 23:22:43 +11:00
int xShift = pos->x & (SECTOR(1) - 1);
int zShift = pos->z & (SECTOR(1) - 1);
2022-01-07 16:27:50 +11:00
if (x == xOld)
{
old->x = 0;
2022-01-07 16:27:50 +11:00
if (z == zOld)
{
kayakItem->Position.zPos += old->z - pos->z;
kayakItem->Position.xPos += old->x - pos->x;
}
2022-01-07 16:27:50 +11:00
else if (z > zOld)
{
kayakItem->Position.zPos -= zShift + 1;
return (pos->x - kayakItem->Position.xPos);
}
else
{
2022-02-22 23:22:43 +11:00
kayakItem->Position.zPos += SECTOR(1) - zShift;
return (kayakItem->Position.xPos - pos->x);
}
}
2022-01-07 16:27:50 +11:00
else if (z == zOld)
{
old->z = 0;
2022-01-07 16:27:50 +11:00
if (x > xOld)
{
kayakItem->Position.xPos -= xShift + 1;
return (kayakItem->Position.zPos - pos->z);
}
else
{
2022-02-22 23:22:43 +11:00
kayakItem->Position.xPos += SECTOR(1) - xShift;
return (pos->z - kayakItem->Position.zPos);
}
}
else
{
2022-01-07 16:27:50 +11:00
x = 0;
z = 0;
auto probe = GetCollisionResult(old->x, pos->y, pos->z, kayakItem->RoomNumber);
2022-02-22 23:22:43 +11:00
if (probe.Position.Floor < (old->y - CLICK(1)))
{
if (pos->z > old->z)
2022-01-07 16:27:50 +11:00
z = -zShift - 1;
else
2022-02-22 23:22:43 +11:00
z = SECTOR(1) - zShift;
}
probe = GetCollisionResult(pos->x, pos->y, old->z, kayakItem->RoomNumber);
2022-02-22 23:22:43 +11:00
if (probe.Position.Floor < (old->y - CLICK(1)))
{
if (pos->x > old->x)
2022-01-07 16:27:50 +11:00
x = -xShift - 1;
else
2022-02-22 23:22:43 +11:00
x = SECTOR(1) - xShift;
}
2022-01-07 16:27:50 +11:00
if (x && z)
{
kayakItem->Position.xPos += x;
kayakItem->Position.zPos += z;
}
2022-01-07 16:27:50 +11:00
else if (x)
{
kayakItem->Position.xPos += x;
2022-01-07 16:27:50 +11:00
if (x > 0)
return (pos->z - kayakItem->Position.zPos);
else
return (kayakItem->Position.zPos - pos->z);
}
2022-01-07 16:27:50 +11:00
else if (z)
{
kayakItem->Position.zPos += z;
2022-01-07 16:27:50 +11:00
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;
}
2022-02-20 17:12:42 +11:00
void KayakToBackground(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
2022-02-28 21:02:19 +11:00
auto* kayak = (KayakInfo*)kayakItem->Data;
2022-02-28 21:02:19 +11:00
kayak->OldPos = kayakItem->Position;
2022-01-07 16:27:50 +11:00
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]);
2020-09-16 06:49:58 +02:00
for (int i = 0; i < 8; i++)
{
2022-01-07 16:27:50 +11:00
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;
2021-03-24 12:49:36 -05:00
2022-01-07 16:27:50 +11:00
PHD_VECTOR frontPos, leftPos, rightPos;
2022-02-20 17:12:42 +11:00
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);
2022-02-28 21:02:19 +11:00
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);
2022-02-20 17:12:42 +11:00
KayakDoCurrent(laraItem,kayakItem);
2022-02-28 21:02:19 +11:00
kayak->LeftVerticalVelocity = KayakDoDynamics(leftHeight, kayak->LeftVerticalVelocity, &leftPos.y);
kayak->RightVerticalVelocity = KayakDoDynamics(rightHeight, kayak->RightVerticalVelocity, &rightPos.y);
kayak->FrontVerticalVelocity = KayakDoDynamics(frontHeight, kayak->FrontVerticalVelocity, &frontPos.y);
2022-02-28 21:02:19 +11:00
kayakItem->VerticalVelocity = KayakDoDynamics(kayak->WaterHeight, kayakItem->VerticalVelocity, &kayakItem->Position.yPos);
2022-02-20 17:12:42 +11:00
int height2 = (leftPos.y + rightPos.y) / 2;
int x = phd_atan(1024, kayakItem->Position.yPos - frontPos.y);
2022-02-20 17:12:42 +11:00
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;
2022-01-07 16:27:50 +11:00
int rot = 0;
PHD_VECTOR pos;
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, 0, -CLICK(2.5f), &pos)) < (oldPos[7].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot = KayakDoShift(kayakItem, &pos, &oldPos[7]);
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, CLICK(0.5f), -CLICK(1.25f), &pos)) < (oldPos[6].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot += KayakDoShift(kayakItem, &pos, &oldPos[6]);
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, -CLICK(0.5f), -CLICK(1.25f), &pos)) < (oldPos[5].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot += KayakDoShift(kayakItem, &pos, &oldPos[5]);
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, CLICK(0.5f), CLICK(0.5f), &pos)) < (oldPos[4].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot += KayakDoShift(kayakItem, &pos, &oldPos[4]);
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, -CLICK(0.5f), CLICK(0.5f), &pos)) < (oldPos[3].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot += KayakDoShift(kayakItem, &pos, &oldPos[3]);
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, 96, CLICK(2), &pos)) < (oldPos[2].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot += KayakDoShift(kayakItem, &pos, &oldPos[2]);
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, -96, CLICK(2), &pos)) < (oldPos[1].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot += KayakDoShift(kayakItem, &pos, &oldPos[1]);
2022-02-20 17:12:42 +11:00
if ((height2 = KayakTestHeight(kayakItem, 0, CLICK(4), &pos)) < (oldPos[0].y - KAYAK_COLLIDE))
2022-01-07 16:27:50 +11:00
rot += KayakDoShift(kayakItem, &pos, &oldPos[0]);
kayakItem->Position.yRot += rot;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
auto probe = GetCollisionResult(kayakItem);
int probedRoomNum = probe.RoomNumber;
2022-02-20 17:12:42 +11:00
height2 = GetWaterHeight(kayakItem->Position.xPos, kayakItem->Position.yPos, kayakItem->Position.zPos, probedRoomNum);
if (height2 == NO_HEIGHT)
height2 = probe.Position.Floor;
2022-02-20 17:12:42 +11:00
if (height2 < (kayakItem->Position.yPos - KAYAK_COLLIDE))
KayakDoShift(kayakItem, (PHD_VECTOR*)&kayakItem->Position, &oldPos[8]);
2022-01-07 16:27:50 +11:00
probe = GetCollisionResult(kayakItem);
probedRoomNum = probe.RoomNumber;
2022-02-20 17:12:42 +11:00
height2 = GetWaterHeight(kayakItem->Position.xPos, kayakItem->Position.yPos, kayakItem->Position.zPos, probedRoomNum);
if (height2 == NO_HEIGHT)
height2 = probe.Position.Floor;
2022-02-20 17:12:42 +11:00
if (height2 == NO_HEIGHT)
{
2022-02-20 17:12:42 +11:00
GAME_VECTOR kayakPos;
2022-02-28 21:02:19 +11:00
kayakPos.x = kayak->OldPos.xPos;
kayakPos.y = kayak->OldPos.yPos;
kayakPos.z = kayak->OldPos.zPos;
2022-02-20 17:12:42 +11:00
kayakPos.roomNumber = kayakItem->RoomNumber;
2022-02-20 17:12:42 +11:00
CameraCollisionBounds(&kayakPos, 256, 0);
{
2022-02-20 17:12:42 +11:00
kayakItem->Position.xPos = kayakPos.x;
kayakItem->Position.yPos = kayakPos.y;
kayakItem->Position.zPos = kayakPos.z;
kayakItem->RoomNumber = kayakPos.roomNumber;
}
}
2022-01-07 16:27:50 +11:00
int collide = KayakGetCollisionAnim(kayakItem, xOld, zOld);
2020-09-16 06:49:58 +02:00
2022-02-20 17:12:42 +11:00
int slip = 0; // Remnant?
if (slip || collide)
{
2022-02-20 17:12:42 +11:00
int newVelocity;
2020-09-16 06:49:58 +02:00
2022-02-20 17:12:42 +11:00
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)
{
2022-02-28 21:02:19 +11:00
if (kayak->Velocity <= MAX_VELOCITY)
kayak->Velocity = newVelocity;
}
else
{
2022-02-28 21:02:19 +11:00
if (kayak->Velocity > 0 && newVelocity < kayak->Velocity)
kayak->Velocity = newVelocity;
2022-02-28 21:02:19 +11:00
else if (kayak->Velocity < 0 && newVelocity > kayak->Velocity)
kayak->Velocity = newVelocity;
}
2022-02-28 21:02:19 +11:00
if (kayak->Velocity < -MAX_VELOCITY)
kayak->Velocity = -MAX_VELOCITY;
}
}
2022-02-20 17:12:42 +11:00
void KayakUserInput(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
2022-02-28 21:02:19 +11:00
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;
2020-09-16 06:49:58 +02:00
switch (laraItem->ActiveState)
{
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_IDLE:
if (TrInput & KAYAK_IN_DISMOUNT &&
!lara->WaterCurrentActive &&
!lara->WaterCurrentPull.x && !lara->WaterCurrentPull.z)
{
2022-01-07 16:27:50 +11:00
if (TrInput & KAYAK_IN_LEFT && KayakCanGetOut(kayakItem, -1))
{
laraItem->TargetState = KAYAK_STATE_DISMOUNT;
laraItem->RequiredState = KAYAK_STATE_DISMOUNT_LEFT;
}
2022-01-07 16:27:50 +11:00
else if (TrInput & KAYAK_IN_RIGHT && KayakCanGetOut(kayakItem, 1))
{
laraItem->TargetState = KAYAK_STATE_DISMOUNT;
laraItem->RequiredState = KAYAK_STATE_DISMOUNT_RIGHT;
}
}
2022-01-07 16:27:50 +11:00
else if (TrInput & KAYAK_IN_FORWARD)
{
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
2022-02-28 21:02:19 +11:00
kayak->Turn = false;
kayak->Forward = true;
}
2022-01-07 16:27:50 +11:00
else if (TrInput & KAYAK_IN_BACK)
laraItem->TargetState = KAYAK_STATE_BACK;
2022-01-07 16:27:50 +11:00
else if (TrInput & KAYAK_IN_LEFT)
{
laraItem->TargetState = KAYAK_STATE_TURN_LEFT;
2022-02-28 21:02:19 +11:00
if (kayak->Velocity)
kayak->Turn = false;
else
2022-02-28 21:02:19 +11:00
kayak->Turn = true;
2022-02-28 21:02:19 +11:00
kayak->Forward = false;
}
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
else if (TrInput & KAYAK_IN_RIGHT)
{
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
2022-02-28 21:02:19 +11:00
if (kayak->Velocity)
kayak->Turn = false;
else
2022-02-28 21:02:19 +11:00
kayak->Turn = true;
2022-02-28 21:02:19 +11:00
kayak->Forward = false;
}
2022-01-07 16:27:50 +11:00
else if (TrInput & KAYAK_IN_HOLD_LEFT &&
2022-02-28 21:02:19 +11:00
(kayak->Velocity ||
lara->WaterCurrentPull.x ||
lara->WaterCurrentPull.z))
{
laraItem->TargetState = KAYAK_STATE_HOLD_LEFT;
}
2022-01-07 16:27:50 +11:00
else if (TrInput & KAYAK_IN_HOLD_RIGHT &&
2022-02-28 21:02:19 +11:00
(kayak->Velocity ||
lara->WaterCurrentPull.x ||
lara->WaterCurrentPull.z))
{
laraItem->TargetState = KAYAK_STATE_HOLD_RIGHT;
}
2020-09-16 06:49:58 +02:00
break;
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_TURN_LEFT:
2022-02-28 21:02:19 +11:00
if (kayak->Forward)
{
if (!frame)
2022-02-28 21:02:19 +11:00
kayak->LeftRightCount = 0;
2020-09-16 06:49:58 +02:00
2022-03-03 21:31:32 +11:00
// TODO: Sort out the bitwise operations.
2022-02-28 21:02:19 +11:00
if (frame == 2 && !(kayak->LeftRightCount & 0x80))
kayak->LeftRightCount++;
2020-09-16 06:49:58 +02:00
else if (frame > 2)
2022-02-28 21:02:19 +11:00
kayak->LeftRightCount &= ~0x80;
2022-01-07 16:27:50 +11:00
if (TrInput & KAYAK_IN_FORWARD)
{
2022-01-07 16:27:50 +11:00
if (TrInput & KAYAK_IN_LEFT)
{
2022-02-28 21:02:19 +11:00
if ((kayak->LeftRightCount & ~0x80) >= 2)
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
}
else
laraItem->TargetState = KAYAK_STATE_TURN_RIGHT;
}
else
laraItem->TargetState = KAYAK_STATE_IDLE;
}
2022-01-07 16:27:50 +11:00
else if (!(TrInput & KAYAK_IN_LEFT))
laraItem->TargetState = KAYAK_STATE_IDLE;
if (frame == 7)
{
2022-02-28 21:02:19 +11:00
if (kayak->Forward)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate -= KAYAK_FORWARD_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_TURN)
kayak->TurnRate = -KAYAK_MAX_TURN;
2022-02-28 21:02:19 +11:00
kayak->Velocity += KAYAK_FORWARD_VELOCITY;
}
2022-02-28 21:02:19 +11:00
else if (kayak->Turn)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate -= KAYAK_HARD_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_STAT)
kayak->TurnRate = -KAYAK_MAX_STAT;
}
else
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate -= KAYAK_LEFT_RIGHT_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_LEFT_RIGHT)
kayak->TurnRate = -KAYAK_MAX_LEFT_RIGHT;
2022-02-28 21:02:19 +11:00
kayak->Velocity += KAYAK_LEFT_RIGHT_VELOCITY;
}
}
2022-01-07 16:27:50 +11:00
if (frame > 6 && frame < 24 && frame & 1)
KayakDoRipple(kayakItem, -CLICK(1.5f), -CLICK(0.25f));
2020-09-16 06:49:58 +02:00
break;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_TURN_RIGHT:
2022-02-28 21:02:19 +11:00
if (kayak->Forward)
{
if (!frame)
2022-02-28 21:02:19 +11:00
kayak->LeftRightCount = 0;
2022-02-28 21:02:19 +11:00
if (frame == 2 && !(kayak->LeftRightCount & 0x80))
kayak->LeftRightCount++;
else if (frame > 2)
2022-02-28 21:02:19 +11:00
kayak->LeftRightCount &= ~0x80;
2022-01-07 16:27:50 +11:00
if (TrInput & KAYAK_IN_FORWARD)
{
2022-01-07 16:27:50 +11:00
if (TrInput & KAYAK_IN_RIGHT)
{
2022-02-28 21:02:19 +11:00
if ((kayak->LeftRightCount & ~0x80) >= 2)
laraItem->TargetState = KAYAK_STATE_TURN_LEFT;
}
else
laraItem->TargetState = KAYAK_STATE_TURN_LEFT;
}
else
laraItem->TargetState = KAYAK_STATE_IDLE;
}
2022-01-07 16:27:50 +11:00
else if (!(TrInput & KAYAK_IN_RIGHT))
laraItem->TargetState = KAYAK_STATE_IDLE;
if (frame == 7)
{
2022-02-28 21:02:19 +11:00
if (kayak->Forward)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate += KAYAK_FORWARD_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_TURN)
kayak->TurnRate = KAYAK_MAX_TURN;
2022-02-28 21:02:19 +11:00
kayak->Velocity += KAYAK_FORWARD_VELOCITY;
}
2022-02-28 21:02:19 +11:00
else if (kayak->Turn)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate += KAYAK_HARD_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_STAT)
kayak->TurnRate = KAYAK_MAX_STAT;
}
else
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate += KAYAK_LEFT_RIGHT_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_LEFT_RIGHT)
kayak->TurnRate = KAYAK_MAX_LEFT_RIGHT;
2022-02-28 21:02:19 +11:00
kayak->Velocity += KAYAK_LEFT_RIGHT_VELOCITY;
}
}
2022-01-07 16:27:50 +11:00
if (frame > 6 && frame < 24 && frame & 1)
KayakDoRipple(kayakItem, CLICK(1.5f), -CLICK(0.25f));
2020-09-16 06:49:58 +02:00
break;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
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)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate += KAYAK_FORWARD_ROTATION;
kayak->Velocity -= KAYAK_FORWARD_VELOCITY;
}
if (frame == 31)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate -= KAYAK_FORWARD_ROTATION;
kayak->Velocity -= KAYAK_FORWARD_VELOCITY;
}
2022-01-07 16:27:50 +11:00
if (frame < 15 && frame & 1)
KayakDoRipple(kayakItem, CLICK(1.5f), -CLICK(0.5f));
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
else if (frame >= 20 && frame <= 34 && frame & 1)
KayakDoRipple(kayakItem, -CLICK(1.5f), -CLICK(0.5f));
}
break;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_HOLD_LEFT:
if (!(TrInput & KAYAK_IN_HOLD_LEFT) ||
2022-02-28 21:02:19 +11:00
(!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)
{
2022-02-28 21:02:19 +11:00
if (kayak->Velocity >= 0)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate -= KAYAK_TURN_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_TURN)
kayak->TurnRate = -KAYAK_MAX_TURN;
2022-02-28 21:02:19 +11:00
kayak->Velocity += -KAYAK_TURN_BRAKE;
if (kayak->Velocity < 0)
kayak->Velocity = 0;
}
2022-01-07 16:27:50 +11:00
2022-02-28 21:02:19 +11:00
if (kayak->Velocity < 0)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate += KAYAK_TURN_ROTATION;
2022-02-28 21:02:19 +11:00
kayak->Velocity += KAYAK_TURN_BRAKE;
if (kayak->Velocity > 0)
kayak->Velocity = 0;
}
if (!(Wibble & 3))
2022-01-07 16:27:50 +11:00
KayakDoRipple(kayakItem, -CLICK(1), -CLICK(1));
}
2020-09-16 06:49:58 +02:00
break;
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_HOLD_RIGHT:
if (!(TrInput & KAYAK_IN_HOLD_RIGHT) ||
2022-02-28 21:02:19 +11:00
(!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)
{
2022-02-28 21:02:19 +11:00
if (kayak->Velocity >= 0)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate += KAYAK_TURN_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_TURN)
kayak->TurnRate = KAYAK_MAX_TURN;
2022-02-28 21:02:19 +11:00
kayak->Velocity += -KAYAK_TURN_BRAKE;
if (kayak->Velocity < 0)
kayak->Velocity = 0;
}
2022-01-07 16:27:50 +11:00
2022-02-28 21:02:19 +11:00
if (kayak->Velocity < 0)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate -= KAYAK_TURN_ROTATION;
2022-02-28 21:02:19 +11:00
kayak->Velocity += KAYAK_TURN_BRAKE;
if (kayak->Velocity > 0)
kayak->Velocity = 0;
}
if (!(Wibble & 3))
2022-01-07 16:27:50 +11:00
KayakDoRipple(kayakItem, CLICK(1), -CLICK(1));
}
break;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_MOUNT_LEFT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_GET_PADDLE &&
2022-01-07 16:27:50 +11:00
frame == 24 &&
2022-02-28 21:02:19 +11:00
!(kayak->Flags & 0x80))
{
2022-02-28 21:02:19 +11:00
kayak->Flags |= 0x80;
lara->MeshPtrs[LM_RHAND] = Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND;
laraItem->MeshBits &= ~LARA_LEG_BITS;
}
2022-01-07 16:27:50 +11:00
break;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_DISMOUNT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_DISMOUNT_START &&
2022-01-07 16:27:50 +11:00
frame == 27 &&
2022-02-28 21:02:19 +11:00
kayak->Flags & 0x80)
{
2022-02-28 21:02:19 +11:00
kayak->Flags &= ~0x80;
lara->MeshPtrs[LM_RHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_RHAND;
laraItem->MeshBits |= LARA_LEG_BITS;
}
laraItem->TargetState = laraItem->RequiredState;
2020-09-16 06:49:58 +02:00
break;
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_DISMOUNT_LEFT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_DISMOUNT_LEFT &&
2022-01-07 16:27:50 +11:00
frame == 83)
{
PHD_VECTOR vec = { 0, 350, 500 };
2019-12-15 16:19:01 +01:00
GetLaraJointPosition(&vec, LM_HIPS);
2022-01-07 16:27:50 +11:00
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;
2022-02-20 17:12:42 +11:00
laraItem->VerticalVelocity = -50;
laraItem->Airborne = true;
2022-02-28 21:02:19 +11:00
lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM;
kayak->LeftRightCount = 0;
}
2022-01-07 16:27:50 +11:00
break;
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
case KAYAK_STATE_DISMOUNT_RIGHT:
if (laraItem->AnimNumber == Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_DISMOUNT_RIGHT &&
2022-01-07 16:27:50 +11:00
frame == 83)
{
PHD_VECTOR vec = { 0, 350, 500 };
2019-12-15 16:19:01 +01:00
GetLaraJointPosition(&vec, LM_HIPS);
2022-01-07 16:27:50 +11:00
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;
2022-02-20 17:12:42 +11:00
laraItem->VerticalVelocity = -50;
laraItem->Airborne = true;
2022-02-28 21:02:19 +11:00
lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM;
kayak->LeftRightCount = 0;
}
}
2022-02-28 21:02:19 +11:00
if (kayak->Velocity > 0)
{
2022-02-28 21:02:19 +11:00
kayak->Velocity -= KAYAK_FRICTION;
if (kayak->Velocity < 0)
kayak->Velocity = 0;
}
2022-02-28 21:02:19 +11:00
else if (kayak->Velocity < 0)
{
2022-02-28 21:02:19 +11:00
kayak->Velocity += KAYAK_FRICTION;
if (kayak->Velocity > 0)
kayak->Velocity = 0;
}
2022-02-28 21:02:19 +11:00
if (kayak->Velocity > MAX_VELOCITY)
kayak->Velocity = MAX_VELOCITY;
else if (kayak->Velocity < -MAX_VELOCITY)
kayak->Velocity = -MAX_VELOCITY;
2022-02-28 21:02:19 +11:00
kayakItem->Velocity = (kayak->Velocity / (USHRT_MAX + 1));
2022-02-20 17:12:42 +11:00
;
2022-02-28 21:02:19 +11:00
if (kayak->TurnRate >= 0)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate -= KAYAK_ROTATE_FRICTION;
if (kayak->TurnRate < 0)
kayak->TurnRate = 0;
}
2022-02-28 21:02:19 +11:00
else if (kayak->TurnRate < 0)
{
2022-02-28 21:02:19 +11:00
kayak->TurnRate += KAYAK_ROTATE_FRICTION;
if (kayak->TurnRate > 0)
kayak->TurnRate = 0;
}
}
2022-02-20 17:12:42 +11:00
void KayakToItemCollision(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
{
2020-09-16 06:49:58 +02:00
short roomsToCheck[128];
short numRoomsToCheck = 0;
roomsToCheck[numRoomsToCheck++] = kayakItem->RoomNumber;
2022-02-20 17:12:42 +11:00
auto* room = &g_Level.Rooms[kayakItem->RoomNumber];
for (int i = 0; i < room->doors.size(); i++)
2020-09-16 06:49:58 +02:00
roomsToCheck[numRoomsToCheck++] = room->doors[i].room;
2020-09-16 06:49:58 +02:00
for (int i = 0; i < numRoomsToCheck; i++)
{
2022-01-07 16:27:50 +11:00
short itemNum = g_Level.Rooms[roomsToCheck[i]].itemNumber;
2020-09-16 06:49:58 +02:00
while (itemNum != NO_ITEM)
{
2022-02-20 17:12:42 +11:00
auto* item = &g_Level.Items[itemNum];
short nextItem = item->NextItem;
if (item->Collidable && item->Status != ITEM_INVISIBLE)
{
2022-02-20 17:12:42 +11:00
auto* object = &Objects[item->ObjectNumber];
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
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;
2022-01-07 16:27:50 +11:00
if (x > -2048 && x < 2048 &&
y > -2048 && y < 2048 &&
z > -2048 && z < 2048)
{
2022-02-22 23:22:43 +11:00
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;
}
}
}
}
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
itemNum = nextItem;
}
2020-09-16 06:49:58 +02:00
}
}
2022-01-07 16:27:50 +11:00
void KayakLaraRapidsDrown(ITEM_INFO* laraItem)
{
2022-02-28 21:02:19 +11:00
auto* lara = GetLaraInfo(laraItem);
2022-01-07 16:27:50 +11:00
laraItem->AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_OVERBOARD_DEATH;
laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase;
2022-02-20 17:12:42 +11:00
laraItem->ActiveState = 12; // TODO
laraItem->TargetState = 12;
laraItem->HitPoints = 0;
laraItem->Velocity = 0;
2022-02-20 17:12:42 +11:00
laraItem->VerticalVelocity = 0;
laraItem->Airborne = false;
2022-01-07 16:27:50 +11:00
AnimateItem(laraItem);
2022-02-28 21:02:19 +11:00
lara->ExtraAnim = 1;
lara->Control.HandStatus = HandStatus::Busy;
lara->Control.Weapon.GunType = WEAPON_NONE;
lara->HitDirection = -1;
}
2022-01-07 16:27:50 +11:00
void KayakCollision(short itemNumber, ITEM_INFO* laraItem, COLL_INFO* coll)
{
2022-02-28 21:02:19 +11:00
auto* lara = GetLaraInfo(laraItem);
2022-02-20 17:12:42 +11:00
auto* kayakItem = &g_Level.Items[itemNumber];
2022-02-28 21:02:19 +11:00
auto* kayak = (KayakInfo*)kayakItem->Data;
2022-02-28 21:02:19 +11:00
if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM)
return;
2022-02-20 17:12:42 +11:00
KayakMountType mountType = KayakGetMountType(laraItem, itemNumber);
2022-01-07 16:27:50 +11:00
if (mountType != KayakMountType::None)
{
2022-02-28 21:02:19 +11:00
lara->Vehicle = itemNumber;
if (lara->Control.Weapon.GunType == WEAPON_FLARE)
{
2022-01-07 16:27:50 +11:00
CreateFlare(laraItem, ID_FLARE_ITEM, 0);
UndrawFlareMeshes(laraItem);
2022-02-28 21:02:19 +11:00
lara->Flare.ControlLeft = 0;
lara->Control.Weapon.RequestGunType = lara->Control.Weapon.GunType = WEAPON_NONE;
}
2022-01-07 16:27:50 +11:00
if (mountType == KayakMountType::Right)
laraItem->AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_MOUNT_RIGHT;
2022-01-07 16:27:50 +11:00
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;
2022-02-20 17:12:42 +11:00
laraItem->Position.xRot = 0;
laraItem->Position.yRot = kayakItem->Position.yRot;
2022-02-20 17:12:42 +11:00
laraItem->Position.zRot = 0;
laraItem->Velocity = 0;
laraItem->VerticalVelocity = 0;
2022-02-20 17:12:42 +11:00
laraItem->Airborne = false;
2022-02-28 21:02:19 +11:00
lara->Control.WaterStatus = WaterStatus::Dry;
if (laraItem->RoomNumber != kayakItem->RoomNumber)
2022-02-28 21:02:19 +11:00
ItemNewRoom(lara->ItemNumber, kayakItem->RoomNumber);
2022-01-07 16:27:50 +11:00
AnimateItem(laraItem);
2022-02-28 21:02:19 +11:00
kayak->WaterHeight = kayakItem->Position.yPos;
kayak->Flags = 0;
}
else
{
2021-09-10 00:20:59 +03:00
coll->Setup.EnableObjectPush = true;
2022-01-07 16:27:50 +11:00
ObjectCollision(itemNumber, laraItem, coll);
}
}
2022-02-20 17:12:42 +11:00
bool KayakControl(ITEM_INFO* laraItem)
{
2022-02-28 21:02:19 +11:00
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;
2020-09-16 06:49:58 +02:00
2022-02-20 17:12:42 +11:00
KayakUserInput(laraItem, kayakItem);
KayakToBackground(laraItem, kayakItem);
2022-01-07 16:27:50 +11:00
TestTriggers(kayakItem, false);
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
auto probe = GetCollisionResult(kayakItem);
int water = GetWaterHeight(kayakItem->Position.xPos, kayakItem->Position.yPos, kayakItem->Position.zPos, probe.RoomNumber);
2022-02-28 21:02:19 +11:00
kayak->WaterHeight = water;
2022-02-28 21:02:19 +11:00
if (kayak->WaterHeight == NO_HEIGHT)
{
2022-01-07 16:27:50 +11:00
water = probe.Position.Floor;
2022-02-28 21:02:19 +11:00
kayak->WaterHeight = water;
kayak->TrueWater = false;
}
else
{
2022-02-28 21:02:19 +11:00
kayak->WaterHeight -= 5;
kayak->TrueWater = true;
}
if ((ofs - kayakItem->VerticalVelocity) > 128 &&
kayakItem->VerticalVelocity == 0 &&
2022-01-07 16:27:50 +11:00
water != NO_HEIGHT)
{
int damage = ofs - kayakItem->VerticalVelocity;
2022-01-07 16:27:50 +11:00
if (damage > 160)
laraItem->HitPoints -= (damage - 160) * 8;
}
2022-02-28 21:02:19 +11:00
if (lara->Vehicle != NO_ITEM)
{
if (kayakItem->RoomNumber != probe.RoomNumber)
{
2022-02-28 21:02:19 +11:00
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;
2022-01-07 16:27:50 +11:00
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);
2020-09-16 06:49:58 +02:00
2022-01-07 16:27:50 +11:00
Camera.targetElevation = -ANGLE(30.0f);
Camera.targetDistance = CLICK(8);
}
2022-02-28 21:02:19 +11:00
if (!(Wibble & 15) && kayak->TrueWater)
{
2022-01-07 16:27:50 +11:00
KayakDoWake(kayakItem, -CLICK(0.5f), 0, 0);
KayakDoWake(kayakItem, CLICK(0.5f), 0, 1);
}
2022-01-07 16:27:50 +11:00
if (Wibble & 7)
{
2022-02-28 21:02:19 +11:00
if (!kayak->TrueWater && kayakItem->VerticalVelocity < 20)
{
PHD_VECTOR dest;
2020-09-16 06:49:58 +02:00
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;
2020-09-16 06:49:58 +02:00
for (int i = cnt; i < 10; i += 2)
{
if (GetRandomControl() & 1)
2020-10-06 10:36:30 -05:00
dest.x = (MistXPos[i] / 2);
else
2020-10-06 10:36:30 -05:00
dest.x = -(MistXPos[i] / 2);
dest.y = 50;
2020-09-16 06:49:58 +02:00
dest.z = MistZPos[i];
}
}
}
2022-01-07 16:27:50 +11:00
if (!kayakItem->Velocity &&
!lara->WaterCurrentPull.x &&
!lara->WaterCurrentPull.z)
2020-09-16 06:49:58 +02:00
{
2022-03-03 21:31:32 +11:00
if (kayak->WakeShade)
kayak->WakeShade--;
2020-09-16 06:49:58 +02:00
}
else
{
2022-03-03 21:31:32 +11:00
if (kayak->WakeShade < 16)
kayak->WakeShade++;
2020-09-16 06:49:58 +02:00
}
KayakUpdateWakeFX();
2022-02-20 17:12:42 +11:00
KayakToItemCollision(laraItem, kayakItem);
2022-02-28 21:02:19 +11:00
return (lara->Vehicle != NO_ITEM) ? true : false;
2020-09-16 06:49:58 +02:00
}