2019-12-05 17:35:57 +01:00
|
|
|
#include "../newobjects.h"
|
|
|
|
#include "../../Game/lara.h"
|
|
|
|
#include "../../Game/items.h"
|
|
|
|
#include "../../Game/collide.h"
|
2019-12-08 07:38:22 +01:00
|
|
|
#include "../../Game/sphere.h"
|
2019-11-20 11:24:36 +01:00
|
|
|
|
|
|
|
extern LaraExtraInfo g_LaraExtra;
|
|
|
|
|
2019-12-05 17:35:57 +01:00
|
|
|
enum BOAT_STATE {
|
2019-11-20 11:24:36 +01:00
|
|
|
BOAT_GETON,
|
|
|
|
BOAT_STILL,
|
|
|
|
BOAT_MOVING,
|
|
|
|
BOAT_JUMPR,
|
|
|
|
BOAT_JUMPL,
|
|
|
|
BOAT_HIT,
|
|
|
|
BOAT_FALL,
|
|
|
|
BOAT_TURNR,
|
|
|
|
BOAT_DEATH,
|
|
|
|
BOAT_TURNL
|
|
|
|
};
|
|
|
|
|
2019-11-20 14:12:21 +01:00
|
|
|
#define IN_ACCELERATE IN_ACTION
|
|
|
|
#define IN_REVERSE IN_JUMP
|
|
|
|
#define IN_DISMOUNT IN_ROLL
|
|
|
|
#define IN_TURBO (IN_SPRINT|IN_DUCK)
|
|
|
|
#define IN_TURNL (IN_LEFT|IN_LSTEP)
|
|
|
|
#define IN_TURNR (IN_RIGHT|IN_RSTEP)
|
2019-11-20 11:24:36 +01:00
|
|
|
#define BOAT_GETONLW_ANIM 0
|
|
|
|
#define BOAT_GETONRW_ANIM 8
|
|
|
|
#define BOAT_GETONJ_ANIM 6
|
|
|
|
#define BOAT_GETON_START 1
|
|
|
|
#define BOAT_FALL_ANIM 15
|
|
|
|
#define BOAT_DEATH_ANIM 18
|
2019-11-20 14:12:21 +01:00
|
|
|
#define BOAT_UNDO_TURN (ONE_DEGREE/4)
|
|
|
|
#define BOAT_TURN (ONE_DEGREE/8)
|
2019-11-20 11:24:36 +01:00
|
|
|
#define BOAT_MAX_TURN ANGLE(4)
|
|
|
|
#define BOAT_MAX_SPEED 110
|
|
|
|
#define BOAT_SLOW_SPEED (BOAT_MAX_SPEED/3)
|
|
|
|
#define BOAT_FAST_SPEED (BOAT_MAX_SPEED+75)
|
|
|
|
#define BOAT_MIN_SPEED 20
|
|
|
|
#define BOAT_ACCELERATION 5
|
|
|
|
#define BOAT_BRAKE 5
|
|
|
|
#define BOAT_SLOWDOWN 1
|
|
|
|
#define BOAT_REVERSE -2 // -5
|
|
|
|
#define BOAT_MAX_BACK -20
|
|
|
|
#define BOAT_MAX_KICK -80
|
|
|
|
#define BOAT_SLIP 10
|
|
|
|
#define BOAT_SIDE_SLIP 30
|
|
|
|
#define BOAT_FRONT 750
|
|
|
|
#define BOAT_SIDE 300
|
|
|
|
#define BOAT_RADIUS 500
|
|
|
|
#define BOAT_SNOW 500
|
2019-11-20 14:12:21 +01:00
|
|
|
#define BOAT_MAX_HEIGHT (STEP_SIZE)
|
|
|
|
#define GETOFF_DIST (1024)
|
2019-11-20 11:24:36 +01:00
|
|
|
#define BOAT_WAKE 700
|
|
|
|
#define BOAT_SOUND_CEILING (WALL_SIZE*5)
|
|
|
|
#define BOAT_TIP (BOAT_FRONT+250)
|
|
|
|
#define SKIDOO_HIT_LEFT 11
|
|
|
|
#define SKIDOO_HIT_RIGHT 12
|
|
|
|
#define SKIDOO_HIT_FRONT 13
|
|
|
|
#define SKIDOO_HIT_BACK 14
|
|
|
|
|
2019-11-20 14:12:21 +01:00
|
|
|
// TODO: (boat) render problem, water height problem, enter problem.
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void GetBoatGetOff(ITEM_INFO* boat)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
/* Wait for last frame of getoff anims before returning to normal Lara control */
|
|
|
|
if ((LaraItem->currentAnimState == BOAT_JUMPR || LaraItem->currentAnimState == BOAT_JUMPL) && LaraItem->frameNumber == Anims[LaraItem->animNumber].frameEnd)
|
|
|
|
{
|
2019-11-27 15:12:35 +01:00
|
|
|
short room_number;
|
2019-11-20 14:12:21 +01:00
|
|
|
int x, y, z;
|
|
|
|
FLOOR_INFO* floor;
|
|
|
|
|
|
|
|
if (LaraItem->currentAnimState == BOAT_JUMPL)
|
|
|
|
LaraItem->pos.yRot -= 0x4000;
|
|
|
|
else
|
|
|
|
LaraItem->pos.yRot += 0x4000;
|
|
|
|
|
|
|
|
LaraItem->animNumber = 77;
|
|
|
|
LaraItem->frameNumber = Anims[LaraItem->animNumber].frameBase;
|
|
|
|
LaraItem->currentAnimState = LaraItem->goalAnimState = 3;
|
|
|
|
LaraItem->gravityStatus = true;
|
|
|
|
LaraItem->fallspeed = -40;
|
|
|
|
LaraItem->speed = 20;
|
|
|
|
LaraItem->pos.xRot = LaraItem->pos.zRot = 0;
|
|
|
|
g_LaraExtra.Vehicle = NO_ITEM;
|
|
|
|
|
|
|
|
room_number = LaraItem->roomNumber;
|
|
|
|
x = LaraItem->pos.xPos + (360 * SIN(LaraItem->pos.yRot) >> W2V_SHIFT);
|
|
|
|
y = LaraItem->pos.yPos - 90;
|
|
|
|
z = LaraItem->pos.zPos + (360 * COS(LaraItem->pos.yRot) >> W2V_SHIFT);
|
|
|
|
floor = GetFloor(x, y, z, &room_number);
|
|
|
|
if (GetFloorHeight(floor, x, y, z) >= y - STEP_SIZE)
|
|
|
|
{
|
|
|
|
LaraItem->pos.xPos = x;
|
|
|
|
LaraItem->pos.zPos = z;
|
|
|
|
if (room_number != LaraItem->roomNumber)
|
|
|
|
ItemNewRoom(Lara.itemNumber, room_number);
|
|
|
|
}
|
|
|
|
LaraItem->pos.yPos = y;
|
|
|
|
|
|
|
|
/* Set boat to still anim */
|
2020-03-21 07:29:31 +01:00
|
|
|
boat->animNumber = Objects[ID_SPEEDBOAT].animIndex;
|
2019-11-20 14:12:21 +01:00
|
|
|
boat->frameNumber = Anims[boat->animNumber].frameBase;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int CanGetOff(int direction)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
ITEM_INFO* v;
|
|
|
|
FLOOR_INFO* floor;
|
2019-11-27 15:12:35 +01:00
|
|
|
short room_number, angle;
|
2019-11-20 14:12:21 +01:00
|
|
|
int x, y, z, height, ceiling;
|
|
|
|
|
|
|
|
v = &Items[g_LaraExtra.Vehicle];
|
|
|
|
|
|
|
|
if (direction < 0)
|
|
|
|
angle = v->pos.yRot - 0x4000;
|
|
|
|
else
|
|
|
|
angle = v->pos.yRot + 0x4000;
|
|
|
|
|
|
|
|
x = v->pos.xPos + (GETOFF_DIST * SIN(angle) >> W2V_SHIFT);
|
|
|
|
y = v->pos.yPos;
|
|
|
|
z = v->pos.zPos + (GETOFF_DIST * COS(angle) >> W2V_SHIFT);
|
|
|
|
|
|
|
|
room_number = v->roomNumber;
|
|
|
|
floor = GetFloor(x, y, z, &room_number);
|
|
|
|
height = GetFloorHeight(floor, x, y, z);
|
|
|
|
|
|
|
|
if ((height - v->pos.yPos) < -(WALL_SIZE / 2))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((HeightType == BIG_SLOPE) || (HeightType == DIAGONAL))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ceiling = GetCeiling(floor, x, y, z);
|
|
|
|
if ((ceiling - v->pos.yPos > -LARA_HITE) || (height - ceiling < LARA_HITE))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int BoatCheckGetOn(short itemNum, COLL_INFO* coll)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
/* Returns 0 if no get on, 1 if right get on and 2 if left get on and 3 if jump geton */
|
|
|
|
int geton = 0, dist;
|
|
|
|
short rot;
|
|
|
|
ITEM_INFO* boat;
|
|
|
|
|
|
|
|
if (Lara.gunStatus != LG_NO_ARMS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
boat = &Items[itemNum];
|
|
|
|
|
|
|
|
dist = ((LaraItem->pos.zPos - boat->pos.zPos) * COS(-boat->pos.yRot) - (LaraItem->pos.xPos - boat->pos.xPos) * SIN(-boat->pos.yRot)) >> W2V_SHIFT;
|
|
|
|
if (dist > 200)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Check if Lara is close enough and in right position to get onto boat */
|
|
|
|
rot = boat->pos.yRot - LaraItem->pos.yRot;
|
|
|
|
if (Lara.waterStatus == LW_SURFACE || Lara.waterStatus == LW_WADE)
|
|
|
|
{
|
|
|
|
if (!(TrInput & IN_ACTION) || LaraItem->gravityStatus || boat->speed)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (rot > 0x2000 && rot < 0x6000)
|
|
|
|
geton = 1; //right
|
|
|
|
else if (rot > -0x6000 && rot < -0x2000)
|
|
|
|
geton = 2; //left
|
|
|
|
}
|
|
|
|
else if (Lara.waterStatus == LW_ABOVE_WATER)
|
|
|
|
{
|
|
|
|
if (LaraItem->fallspeed > 0)
|
|
|
|
{
|
|
|
|
if (rot > -0x6000 && rot < 0x6000 && (LaraItem->pos.yPos + 512) > boat->pos.yPos)
|
|
|
|
geton = 3; //jump
|
|
|
|
}
|
|
|
|
else if (LaraItem->fallspeed == 0)
|
|
|
|
{
|
|
|
|
if (rot > -0x6000 && rot < 0x6000)
|
|
|
|
{
|
|
|
|
if (LaraItem->pos.xPos == boat->pos.xPos && LaraItem->pos.yPos == boat->pos.yPos && LaraItem->pos.zPos == boat->pos.zPos)
|
|
|
|
geton = 4; // must have started on same spot as boat
|
|
|
|
else
|
|
|
|
geton = 3; //jump
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!geton)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Is Lara actually close enough to get on the thing? */
|
|
|
|
if (!TestBoundsCollide(boat, LaraItem, coll->radius))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!TestCollision(boat, LaraItem))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return geton;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int TestWaterHeight(ITEM_INFO* item, int z_off, int x_off, PHD_VECTOR* pos)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
/* Get water height at a position offset from the origin.
|
|
|
|
Moves the vector in 'pos' to the required test position too */
|
|
|
|
FLOOR_INFO* floor;
|
|
|
|
int s, c, height;
|
|
|
|
short room_number;
|
|
|
|
|
|
|
|
/* Get y pos correctly, but don't bother changing z_off and x_off using x_rot and z_rot */
|
|
|
|
pos->y = item->pos.yPos - (z_off * SIN(item->pos.xRot) >> W2V_SHIFT) +
|
|
|
|
(x_off * SIN(item->pos.zRot) >> W2V_SHIFT);
|
|
|
|
|
|
|
|
c = COS(item->pos.yRot);
|
|
|
|
s = SIN(item->pos.yRot);
|
|
|
|
|
|
|
|
pos->z = item->pos.zPos + ((z_off * c - x_off * s) >> W2V_SHIFT);
|
|
|
|
pos->x = item->pos.xPos + ((z_off * s + x_off * c) >> W2V_SHIFT);
|
|
|
|
|
|
|
|
/* Try to get water height; if none get ground height instead */
|
|
|
|
room_number = item->roomNumber;
|
|
|
|
GetFloor(pos->x, pos->y, pos->z, &room_number); // get correct room (as GetWaterHeight doesn't)
|
|
|
|
height = GetWaterHeight(pos->x, pos->y, pos->z, room_number);
|
|
|
|
if (height == NO_HEIGHT)
|
|
|
|
{
|
|
|
|
floor = GetFloor(pos->x, pos->y, pos->z, &room_number);
|
|
|
|
height = GetFloorHeight(floor, pos->x, pos->y, pos->z);
|
|
|
|
if (height == NO_HEIGHT)
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
return height - 5; // make sure boat is above water line else all sorts of weirdness results
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void DoBoatCollision(int itemNum)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
ITEM_INFO* item, * boat;
|
|
|
|
int item_number, distance, x, z, radius;
|
|
|
|
|
|
|
|
boat = &Items[itemNum];
|
|
|
|
|
|
|
|
/* Check if hit something in the water */
|
|
|
|
item_number = Rooms[boat->roomNumber].itemNumber;
|
|
|
|
while (item_number != NO_ITEM)
|
|
|
|
{
|
|
|
|
item = &Items[item_number];
|
|
|
|
|
2020-03-21 07:29:31 +01:00
|
|
|
if (item->objectNumber == ID_SPEEDBOAT && item_number != itemNum && g_LaraExtra.Vehicle != item_number)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
// other boat
|
|
|
|
x = item->pos.xPos - boat->pos.xPos;
|
|
|
|
z = item->pos.zPos - boat->pos.zPos;
|
|
|
|
radius = SQUARE(BOAT_RADIUS * 2);
|
|
|
|
distance = SQUARE(x) + SQUARE(z);
|
|
|
|
if (distance < radius)
|
|
|
|
{
|
|
|
|
boat->pos.xPos = item->pos.xPos - x * radius / distance;
|
|
|
|
boat->pos.zPos = item->pos.zPos - z * radius / distance;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: adding mine and gondola ! (boat)
|
|
|
|
|
|
|
|
item_number = item->nextItem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
short DoBoatShift(ITEM_INFO* skidoo, PHD_VECTOR* pos, PHD_VECTOR* old)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
int x, z;
|
|
|
|
int x_old, z_old;
|
|
|
|
int shift_x, shift_z;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (z == z_old)
|
|
|
|
{
|
|
|
|
/* Neither shift; may have hit a very steep slope, so need to push back to old position */
|
|
|
|
skidoo->pos.zPos += (old->z - pos->z);
|
|
|
|
skidoo->pos.xPos += (old->x - pos->x);
|
|
|
|
}
|
|
|
|
else if (z > z_old)
|
|
|
|
{
|
|
|
|
/* Z shift left */
|
|
|
|
skidoo->pos.zPos -= shift_z + 1;
|
|
|
|
return (pos->x - skidoo->pos.xPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Z shift right */
|
|
|
|
skidoo->pos.zPos += WALL_SIZE - shift_z;
|
|
|
|
return (skidoo->pos.xPos - pos->x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (z == z_old)
|
|
|
|
{
|
|
|
|
if (x > x_old)
|
|
|
|
{
|
|
|
|
/* X shift up */
|
|
|
|
skidoo->pos.xPos -= shift_x + 1;
|
|
|
|
return (skidoo->pos.zPos - pos->z);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* X shift down */
|
|
|
|
skidoo->pos.xPos += WALL_SIZE - shift_x;
|
|
|
|
return (pos->z - skidoo->pos.zPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* A diagonal hit; means a barrage of tests needed to determine best shift */
|
2019-11-27 15:12:35 +01:00
|
|
|
short room_number;
|
2019-11-20 14:12:21 +01:00
|
|
|
FLOOR_INFO* floor;
|
|
|
|
int height;
|
|
|
|
|
|
|
|
x = z = 0;
|
|
|
|
|
|
|
|
room_number = skidoo->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 = skidoo->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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x && z)
|
|
|
|
{
|
|
|
|
/* Corner or side collision */
|
|
|
|
skidoo->pos.zPos += z;
|
|
|
|
skidoo->pos.xPos += x;
|
|
|
|
}
|
|
|
|
else if (z)
|
|
|
|
{
|
|
|
|
skidoo->pos.zPos += z;
|
|
|
|
if (z > 0)
|
|
|
|
return (skidoo->pos.xPos - pos->x);
|
|
|
|
else
|
|
|
|
return (pos->x - skidoo->pos.xPos);
|
|
|
|
}
|
|
|
|
else if (x)
|
|
|
|
{
|
|
|
|
skidoo->pos.xPos += x;
|
|
|
|
if (x > 0)
|
|
|
|
return (pos->z - skidoo->pos.zPos);
|
|
|
|
else
|
|
|
|
return (skidoo->pos.zPos - pos->z);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Pure diagonal collision */
|
|
|
|
skidoo->pos.zPos += (old->z - pos->z);
|
|
|
|
skidoo->pos.xPos += (old->x - pos->x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int GetBoatCollisionAnim(ITEM_INFO* skidoo, PHD_VECTOR* moved)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
int c, s, front, side;
|
|
|
|
|
|
|
|
moved->x = skidoo->pos.xPos - moved->x;
|
|
|
|
moved->z = skidoo->pos.zPos - moved->z;
|
|
|
|
|
|
|
|
if (moved->x || moved->z)
|
|
|
|
{
|
|
|
|
/* Get direction of movement relative to facing */
|
|
|
|
c = COS(skidoo->pos.yRot);
|
|
|
|
s = SIN(skidoo->pos.yRot);
|
|
|
|
front = (moved->z * c + moved->x * s) >> W2V_SHIFT;
|
|
|
|
side = (-moved->z * s + moved->x * c) >> W2V_SHIFT;
|
|
|
|
if (abs(front) > abs(side))
|
|
|
|
{
|
|
|
|
if (front > 0)
|
|
|
|
return SKIDOO_HIT_BACK;
|
|
|
|
else
|
|
|
|
return SKIDOO_HIT_FRONT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (side > 0)
|
|
|
|
return SKIDOO_HIT_LEFT;
|
|
|
|
else
|
|
|
|
return SKIDOO_HIT_RIGHT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int DoBoatDynamics(int height, int fallspeed, int* y)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
if (height > * y)
|
|
|
|
{
|
|
|
|
/* In air */
|
|
|
|
*y += fallspeed;
|
|
|
|
if (*y > height)
|
|
|
|
{
|
|
|
|
*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) */
|
|
|
|
fallspeed += ((height - *y - fallspeed) >> 3);
|
|
|
|
if (fallspeed < BOAT_MAX_BACK)
|
|
|
|
fallspeed = BOAT_MAX_BACK;
|
|
|
|
|
|
|
|
if (*y > height)
|
|
|
|
*y = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fallspeed;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int BoatDynamics(short itemNum)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
ITEM_INFO* boat;
|
|
|
|
BOAT_INFO* binfo;
|
|
|
|
PHD_VECTOR moved, fl, fr, br, bl, f;
|
|
|
|
PHD_VECTOR old, fl_old, fr_old, bl_old, br_old, f_old;
|
|
|
|
int hfl, hfr, hbr, hbl, hf;
|
|
|
|
int hfr_old, hfl_old, hbr_old, hbl_old, hf_old;
|
|
|
|
FLOOR_INFO* floor;
|
|
|
|
int height, slip, collide;
|
2019-11-27 15:12:35 +01:00
|
|
|
short room_number, rot;
|
2019-11-20 14:12:21 +01:00
|
|
|
int newspeed;
|
|
|
|
|
|
|
|
boat = &Items[itemNum];
|
|
|
|
binfo = (BOAT_INFO*)boat->data;
|
|
|
|
|
|
|
|
/* Remove tilt angle (and add again at the end) as effects control */
|
|
|
|
boat->pos.zRot -= binfo->tilt_angle;
|
|
|
|
|
|
|
|
/* First get positions and heights of boat's corners + centre */
|
|
|
|
hfl_old = TestWaterHeight(boat, BOAT_FRONT, -BOAT_SIDE, &fl_old);
|
|
|
|
hfr_old = TestWaterHeight(boat, BOAT_FRONT, BOAT_SIDE, &fr_old);
|
|
|
|
hbl_old = TestWaterHeight(boat, -BOAT_FRONT, -BOAT_SIDE, &bl_old);
|
|
|
|
hbr_old = TestWaterHeight(boat, -BOAT_FRONT, BOAT_SIDE, &br_old);
|
|
|
|
hf_old = TestWaterHeight(boat, BOAT_TIP, 0, &f_old);
|
|
|
|
old.x = boat->pos.xPos;
|
|
|
|
old.y = boat->pos.yPos;
|
|
|
|
old.z = boat->pos.zPos;
|
|
|
|
|
|
|
|
/* Back left/right may be slightly below ground, so correct for this */
|
|
|
|
if (bl_old.y > hbl_old)
|
|
|
|
bl_old.y = hbl_old;
|
|
|
|
if (br_old.y > hbr_old)
|
|
|
|
br_old.y = hbr_old;
|
|
|
|
if (fl_old.y > hfl_old)
|
|
|
|
fl_old.y = hfl_old;
|
|
|
|
if (fr_old.y > hfr_old)
|
|
|
|
fr_old.y = hfr_old;
|
|
|
|
if (f_old.y > hf_old)
|
|
|
|
f_old.y = hf_old;
|
|
|
|
|
|
|
|
boat->pos.yRot += binfo->boat_turn + binfo->extra_rotation;
|
|
|
|
binfo->tilt_angle = binfo->boat_turn * 6;
|
|
|
|
|
|
|
|
/* Move boat according to speed */
|
|
|
|
boat->pos.zPos += boat->speed * COS(boat->pos.yRot) >> W2V_SHIFT;
|
|
|
|
boat->pos.xPos += boat->speed * SIN(boat->pos.yRot) >> W2V_SHIFT;
|
|
|
|
if (boat->speed >= 0)
|
|
|
|
binfo->prop_rot += (boat->speed * ANGLE(3));
|
|
|
|
else
|
|
|
|
binfo->prop_rot += ANGLE(33);
|
|
|
|
|
|
|
|
/* Slide boat according to tilts (to avoid getting stuck on slopes) */
|
|
|
|
slip = BOAT_SIDE_SLIP * SIN(boat->pos.zRot) >> W2V_SHIFT;
|
|
|
|
if (!slip && boat->pos.zRot)
|
|
|
|
slip = (boat->pos.zRot > 0) ? 1 : -1;
|
|
|
|
boat->pos.zPos -= slip * SIN(boat->pos.yRot) >> W2V_SHIFT;
|
|
|
|
boat->pos.xPos += slip * COS(boat->pos.yRot) >> W2V_SHIFT;
|
|
|
|
|
|
|
|
slip = BOAT_SLIP * SIN(boat->pos.xRot) >> W2V_SHIFT;
|
|
|
|
if (!slip && boat->pos.xRot)
|
|
|
|
slip = (boat->pos.xRot > 0) ? 1 : -1;
|
|
|
|
boat->pos.zPos -= slip * COS(boat->pos.yRot) >> W2V_SHIFT;
|
|
|
|
boat->pos.xPos -= slip * SIN(boat->pos.yRot) >> W2V_SHIFT;
|
|
|
|
|
|
|
|
/* Remember desired position in case of collisions moving us about */
|
|
|
|
moved.x = boat->pos.xPos;
|
|
|
|
moved.z = boat->pos.zPos;
|
|
|
|
|
|
|
|
/* Collision with other boat? */
|
|
|
|
DoBoatCollision(itemNum);
|
|
|
|
|
|
|
|
/* Test new positions of points (one at a time) and shift boat accordingly */
|
|
|
|
rot = 0;
|
|
|
|
hbl = TestWaterHeight(boat, -BOAT_FRONT, -BOAT_SIDE, &bl);
|
|
|
|
if (hbl < bl_old.y - STEP_SIZE / 2)
|
|
|
|
rot = DoBoatShift(boat, &bl, &bl_old);
|
|
|
|
|
|
|
|
hbr = TestWaterHeight(boat, -BOAT_FRONT, BOAT_SIDE, &br);
|
|
|
|
if (hbr < br_old.y - STEP_SIZE / 2)
|
|
|
|
rot += DoBoatShift(boat, &br, &br_old);
|
|
|
|
|
|
|
|
hfl = TestWaterHeight(boat, BOAT_FRONT, -BOAT_SIDE, &fl);
|
|
|
|
if (hfl < fl_old.y - STEP_SIZE / 2)
|
|
|
|
rot += DoBoatShift(boat, &fl, &fl_old);
|
|
|
|
|
|
|
|
hfr = TestWaterHeight(boat, BOAT_FRONT, BOAT_SIDE, &fr);
|
|
|
|
if (hfr < fr_old.y - STEP_SIZE / 2)
|
|
|
|
rot += DoBoatShift(boat, &fr, &fr_old);
|
|
|
|
|
|
|
|
if (!slip)
|
|
|
|
{
|
|
|
|
hf = TestWaterHeight(boat, BOAT_TIP, 0, &f);
|
|
|
|
if (hf < f_old.y - STEP_SIZE / 2)
|
|
|
|
DoBoatShift(boat, &f, &f_old);
|
|
|
|
}
|
|
|
|
|
|
|
|
room_number = boat->roomNumber;
|
|
|
|
floor = GetFloor(boat->pos.xPos, boat->pos.yPos, boat->pos.zPos, &room_number);
|
|
|
|
height = GetWaterHeight(boat->pos.xPos, boat->pos.yPos - 5, boat->pos.zPos, room_number);
|
|
|
|
|
|
|
|
if (height == NO_HEIGHT)
|
|
|
|
height = GetFloorHeight(floor, boat->pos.xPos, boat->pos.yPos - 5, boat->pos.zPos);
|
|
|
|
|
|
|
|
if (height < boat->pos.yPos - (STEP_SIZE / 2))
|
|
|
|
DoBoatShift(boat, (PHD_VECTOR*)&boat->pos, &old);
|
|
|
|
|
|
|
|
binfo->extra_rotation = rot;
|
|
|
|
|
|
|
|
/* Get collision anim if boat has been moved from desired position by collisions */
|
|
|
|
collide = GetBoatCollisionAnim(boat, &moved);
|
|
|
|
|
|
|
|
/* Check final movement if slipped or collided and adjust speed */
|
|
|
|
if (slip || collide)
|
|
|
|
{
|
|
|
|
newspeed = ((boat->pos.zPos - old.z) * COS(boat->pos.yRot) + (boat->pos.xPos - old.x) * SIN(boat->pos.yRot)) >> W2V_SHIFT;
|
|
|
|
|
|
|
|
if (boat->speed > BOAT_MAX_SPEED + BOAT_ACCELERATION && newspeed < boat->speed - 10)
|
|
|
|
{
|
|
|
|
LaraItem->hitPoints -= boat->speed;
|
|
|
|
LaraItem->hitStatus = 1;
|
|
|
|
SoundEffect(31, &LaraItem->pos, 0);
|
|
|
|
newspeed >>= 1;
|
|
|
|
boat->speed >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Adjust speed if serious change */
|
|
|
|
if (slip)
|
|
|
|
{
|
|
|
|
/* Only if slip is above certain amount, and boat is not in FAST speed range */
|
|
|
|
if (boat->speed <= BOAT_MAX_SPEED + 10)
|
|
|
|
boat->speed = newspeed;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (boat->speed > 0 && newspeed < boat->speed)
|
|
|
|
boat->speed = newspeed;
|
|
|
|
else if (boat->speed < 0 && newspeed > boat->speed)
|
|
|
|
boat->speed = newspeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (boat->speed < BOAT_MAX_BACK)
|
|
|
|
boat->speed = BOAT_MAX_BACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return collide;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int BoatUserControl(ITEM_INFO* boat)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
/* Return whether to straighten up or not */
|
|
|
|
int no_turn = 1, max_speed;
|
|
|
|
BOAT_INFO* binfo;
|
|
|
|
|
|
|
|
binfo = (BOAT_INFO*)boat->data;
|
|
|
|
|
|
|
|
if (boat->pos.yPos >= binfo->water - STEP_SIZE / 2 && binfo->water != NO_HEIGHT)
|
|
|
|
{
|
|
|
|
/* If on the water surface, user has control; allow for reversing! */
|
|
|
|
if ((!(TrInput & IN_DISMOUNT) && !(TrInput & IN_LOOK)) || boat->speed)
|
|
|
|
{
|
|
|
|
if (((TrInput & IN_TURNL) && !(TrInput & IN_REVERSE)) || ((TrInput & IN_TURNR) && (TrInput & IN_REVERSE)))
|
|
|
|
{
|
|
|
|
if (binfo->boat_turn > 0)
|
|
|
|
binfo->boat_turn -= BOAT_UNDO_TURN;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
binfo->boat_turn -= BOAT_TURN;
|
|
|
|
if (binfo->boat_turn < -BOAT_MAX_TURN)
|
|
|
|
binfo->boat_turn = -BOAT_MAX_TURN;
|
|
|
|
}
|
|
|
|
no_turn = 0;
|
|
|
|
}
|
|
|
|
else if (((TrInput & IN_TURNR) && !(TrInput & IN_REVERSE)) || ((TrInput & IN_TURNL) && (TrInput & IN_REVERSE)))
|
|
|
|
{
|
|
|
|
if (binfo->boat_turn < 0)
|
|
|
|
binfo->boat_turn += BOAT_UNDO_TURN;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
binfo->boat_turn += BOAT_TURN;
|
|
|
|
if (binfo->boat_turn > BOAT_MAX_TURN)
|
|
|
|
binfo->boat_turn = BOAT_MAX_TURN;
|
|
|
|
}
|
|
|
|
no_turn = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TrInput & IN_REVERSE)
|
|
|
|
{
|
|
|
|
if (boat->speed > 0)
|
|
|
|
boat->speed -= BOAT_BRAKE;
|
|
|
|
else if (boat->speed > BOAT_MAX_BACK)
|
|
|
|
boat->speed += BOAT_REVERSE;
|
|
|
|
}
|
|
|
|
else if (TrInput & IN_ACCELERATE)
|
|
|
|
{
|
|
|
|
if (TrInput & IN_TURBO)
|
|
|
|
max_speed = BOAT_FAST_SPEED;
|
|
|
|
else
|
|
|
|
max_speed = (TrInput & IN_STEPSHIFT) ? BOAT_SLOW_SPEED : BOAT_MAX_SPEED;
|
|
|
|
|
|
|
|
if (boat->speed < max_speed)
|
|
|
|
boat->speed += BOAT_ACCELERATION / 2 + BOAT_ACCELERATION * boat->speed / (2 * max_speed);
|
|
|
|
else if (boat->speed > max_speed + BOAT_SLOWDOWN)
|
|
|
|
boat->speed -= BOAT_SLOWDOWN;
|
|
|
|
}
|
|
|
|
else if (boat->speed >= 0 && boat->speed < BOAT_MIN_SPEED && (TrInput & (IN_TURNL | IN_TURNR)))
|
|
|
|
{
|
|
|
|
if (boat->speed == 0 && !(TrInput & IN_DISMOUNT))
|
|
|
|
boat->speed = BOAT_MIN_SPEED; // If user wants to turn, boat will move forward
|
|
|
|
}
|
|
|
|
else if (boat->speed > BOAT_SLOWDOWN)
|
|
|
|
boat->speed -= BOAT_SLOWDOWN;
|
|
|
|
else
|
|
|
|
boat->speed = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (boat->speed >= 0 && boat->speed < BOAT_MIN_SPEED && (TrInput & (IN_TURNL | IN_TURNR)))
|
|
|
|
{
|
|
|
|
if (boat->speed == 0 && !(TrInput & IN_DISMOUNT))
|
|
|
|
boat->speed = BOAT_MIN_SPEED; // If user wants to turn, boat will move forward
|
|
|
|
}
|
|
|
|
else if (boat->speed > BOAT_SLOWDOWN)
|
|
|
|
boat->speed -= BOAT_SLOWDOWN;
|
|
|
|
else
|
|
|
|
boat->speed = 0;
|
|
|
|
|
|
|
|
if ((TrInput & IN_LOOK) && boat->speed == 0)
|
|
|
|
LookUpDown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return no_turn;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void BoatAnimation(ITEM_INFO* boat, int collide)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
BOAT_INFO* binfo;
|
|
|
|
|
|
|
|
binfo = (BOAT_INFO*)boat->data;
|
|
|
|
|
|
|
|
/* Do animation stuff */
|
|
|
|
if (LaraItem->hitPoints <= 0)
|
|
|
|
{
|
|
|
|
if (LaraItem->currentAnimState != BOAT_DEATH)
|
|
|
|
{
|
2020-03-21 07:29:31 +01:00
|
|
|
LaraItem->animNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + BOAT_DEATH_ANIM;
|
2019-11-20 14:12:21 +01:00
|
|
|
LaraItem->frameNumber = Anims[LaraItem->animNumber].frameBase;
|
|
|
|
LaraItem->currentAnimState = LaraItem->goalAnimState = BOAT_DEATH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (boat->pos.yPos < binfo->water - (STEP_SIZE / 2) && boat->fallspeed > 0)
|
|
|
|
{
|
|
|
|
if (LaraItem->currentAnimState != BOAT_FALL)
|
|
|
|
{
|
2020-03-21 07:29:31 +01:00
|
|
|
LaraItem->animNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + BOAT_FALL_ANIM;
|
2019-11-20 14:12:21 +01:00
|
|
|
LaraItem->frameNumber = Anims[LaraItem->animNumber].frameBase;
|
|
|
|
LaraItem->currentAnimState = LaraItem->goalAnimState = BOAT_FALL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (collide)
|
|
|
|
{
|
|
|
|
if (LaraItem->currentAnimState != BOAT_HIT)
|
|
|
|
{
|
2020-03-21 07:29:31 +01:00
|
|
|
LaraItem->animNumber = (short)(Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + collide);
|
2019-11-20 14:12:21 +01:00
|
|
|
LaraItem->frameNumber = Anims[LaraItem->animNumber].frameBase;
|
|
|
|
LaraItem->currentAnimState = LaraItem->goalAnimState = BOAT_HIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (LaraItem->currentAnimState)
|
|
|
|
{
|
|
|
|
case BOAT_STILL:
|
|
|
|
if (TrInput & IN_DISMOUNT)
|
|
|
|
{
|
|
|
|
if (boat->speed == 0)
|
|
|
|
{
|
|
|
|
if ((TrInput & IN_TURNR) && CanGetOff(boat->pos.yRot + 0x4000))
|
|
|
|
LaraItem->goalAnimState = BOAT_JUMPR;
|
|
|
|
else if (TrInput & IN_TURNL && CanGetOff(boat->pos.yRot - 0x4000))
|
|
|
|
LaraItem->goalAnimState = BOAT_JUMPL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (boat->speed > 0)
|
|
|
|
LaraItem->goalAnimState = BOAT_MOVING;
|
|
|
|
break;
|
|
|
|
case BOAT_MOVING:
|
|
|
|
if (boat->speed <= 0)
|
|
|
|
LaraItem->goalAnimState = BOAT_STILL;
|
|
|
|
else if (TrInput & IN_TURNR)
|
|
|
|
LaraItem->goalAnimState = BOAT_TURNR;
|
|
|
|
else if (TrInput & IN_TURNL)
|
|
|
|
LaraItem->goalAnimState = BOAT_TURNL;
|
|
|
|
break;
|
|
|
|
case BOAT_FALL:
|
|
|
|
LaraItem->goalAnimState = BOAT_MOVING;
|
|
|
|
break;
|
|
|
|
case BOAT_TURNR:
|
|
|
|
if (boat->speed <= 0)
|
|
|
|
LaraItem->goalAnimState = BOAT_STILL;
|
|
|
|
else if (!(TrInput & IN_TURNR))
|
|
|
|
LaraItem->goalAnimState = BOAT_MOVING;
|
|
|
|
break;
|
|
|
|
case BOAT_TURNL:
|
|
|
|
if (boat->speed <= 0)
|
|
|
|
LaraItem->goalAnimState = BOAT_STILL;
|
|
|
|
else if (!(TrInput & IN_TURNL))
|
|
|
|
LaraItem->goalAnimState = BOAT_MOVING;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void BoatSplash(ITEM_INFO* item, long fallspeed, long water)
|
2019-11-20 14:12:21 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
splash_setup.x = item->pos.x_pos;
|
|
|
|
splash_setup.y = water;
|
|
|
|
splash_setup.z = item->pos.z_pos;
|
|
|
|
splash_setup.InnerXZoff = 16 << 2;
|
|
|
|
splash_setup.InnerXZsize = 12 << 2;
|
|
|
|
splash_setup.InnerYsize = -96 << 2;
|
|
|
|
splash_setup.InnerXZvel = 0xa0;
|
|
|
|
splash_setup.InnerYvel = -fallspeed << 7;
|
|
|
|
splash_setup.InnerGravity = 0x80;
|
|
|
|
splash_setup.InnerFriction = 7;
|
|
|
|
splash_setup.MiddleXZoff = 24 << 2;
|
|
|
|
splash_setup.MiddleXZsize = 24 << 2;
|
|
|
|
splash_setup.MiddleYsize = -64 << 2;
|
|
|
|
splash_setup.MiddleXZvel = 0xe0;
|
|
|
|
splash_setup.MiddleYvel = -fallspeed << 6;
|
|
|
|
splash_setup.MiddleGravity = 0x48;
|
|
|
|
splash_setup.MiddleFriction = 8;
|
|
|
|
splash_setup.OuterXZoff = 32 << 2;
|
|
|
|
splash_setup.OuterXZsize = 32 << 2;
|
|
|
|
splash_setup.OuterXZvel = 0x110;
|
|
|
|
splash_setup.OuterFriction = 9;
|
|
|
|
SetupSplash(&splash_setup);
|
|
|
|
SplashCount = 16;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void InitialiseBoat(short itemNum)
|
2019-11-20 11:24:36 +01:00
|
|
|
{
|
2019-11-20 14:12:21 +01:00
|
|
|
ITEM_INFO* boat;
|
|
|
|
BOAT_INFO* binfo;
|
2019-11-20 11:24:36 +01:00
|
|
|
|
2019-11-20 14:12:21 +01:00
|
|
|
boat = &Items[itemNum];
|
|
|
|
binfo = (BOAT_INFO*)GameMalloc(sizeof(BOAT_INFO));
|
|
|
|
boat->data = (void*)binfo;
|
|
|
|
binfo->boat_turn = 0;
|
|
|
|
binfo->left_fallspeed = 0;
|
|
|
|
binfo->right_fallspeed = 0;
|
|
|
|
binfo->tilt_angle = 0;
|
|
|
|
binfo->extra_rotation = 0;
|
|
|
|
binfo->water = 0;
|
|
|
|
binfo->pitch = 0;
|
2019-11-20 11:24:36 +01:00
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void BoatCollision(short itemNum, ITEM_INFO* litem, COLL_INFO* coll)
|
2019-11-20 11:24:36 +01:00
|
|
|
{
|
2019-11-20 14:12:21 +01:00
|
|
|
/* This routine is only for when Lara is not on the boat and she would like to be */
|
|
|
|
int geton;
|
|
|
|
ITEM_INFO* boat;
|
|
|
|
|
|
|
|
/* If Lara dead or already on the boat, then no collision */
|
|
|
|
if (litem->hitPoints < 0 || g_LaraExtra.Vehicle != NO_ITEM)
|
|
|
|
return;
|
|
|
|
|
|
|
|
boat = &Items[itemNum];
|
|
|
|
|
|
|
|
/* If player isn't pressing control or Lara is busy, then do normal object collision */
|
|
|
|
geton = BoatCheckGetOn(itemNum, coll);
|
|
|
|
if (!geton)
|
|
|
|
{
|
|
|
|
coll->enableBaddiePush = true;
|
|
|
|
ObjectCollision(itemNum, litem, coll);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (geton == 2)
|
2020-03-21 07:29:31 +01:00
|
|
|
litem->animNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + BOAT_GETONLW_ANIM;
|
2019-11-20 14:12:21 +01:00
|
|
|
else if (geton == 1)
|
2020-03-21 07:29:31 +01:00
|
|
|
litem->animNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + BOAT_GETONRW_ANIM;
|
2019-11-20 14:12:21 +01:00
|
|
|
else if (geton == 3)
|
2020-03-21 07:29:31 +01:00
|
|
|
litem->animNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + BOAT_GETONJ_ANIM;
|
2019-11-20 14:12:21 +01:00
|
|
|
else
|
2020-03-21 07:29:31 +01:00
|
|
|
litem->animNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + BOAT_GETON_START;
|
2019-11-20 14:12:21 +01:00
|
|
|
|
|
|
|
Lara.waterStatus = LW_ABOVE_WATER;
|
|
|
|
litem->pos.xPos = boat->pos.xPos;
|
|
|
|
litem->pos.yPos = boat->pos.yPos - 5;
|
|
|
|
litem->pos.zPos = boat->pos.zPos;
|
|
|
|
litem->pos.yRot = boat->pos.yRot;
|
|
|
|
litem->pos.xRot = litem->pos.zRot = 0;
|
|
|
|
litem->gravityStatus = false;
|
|
|
|
litem->speed = 0;
|
|
|
|
litem->fallspeed = 0;
|
|
|
|
litem->frameNumber = Anims[litem->animNumber].frameBase;
|
|
|
|
litem->currentAnimState = litem->goalAnimState = BOAT_GETON;
|
2019-11-20 11:24:36 +01:00
|
|
|
|
2019-11-20 14:12:21 +01:00
|
|
|
if (litem->roomNumber != boat->roomNumber)
|
|
|
|
ItemNewRoom(Lara.itemNumber, boat->roomNumber);
|
|
|
|
|
|
|
|
AnimateItem(litem);
|
|
|
|
|
|
|
|
/* Add to active item list from this point onward */
|
|
|
|
if (Items[itemNum].status != ITEM_ACTIVE)
|
|
|
|
{
|
|
|
|
AddActiveItem(itemNum);
|
|
|
|
Items[itemNum].status = ITEM_ACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: play a cd when starting ! (boat)
|
|
|
|
//S_CDPlay(12, 0);
|
|
|
|
|
|
|
|
/* Yeeha! Get in that boat girly */
|
|
|
|
g_LaraExtra.Vehicle = itemNum;
|
2019-11-20 11:24:36 +01:00
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void BoatControl(short itemNumber)
|
2019-11-20 11:24:36 +01:00
|
|
|
{
|
2019-11-20 14:12:21 +01:00
|
|
|
ITEM_INFO* boat;
|
|
|
|
BOAT_INFO* binfo;
|
|
|
|
PHD_VECTOR fl, fr, prop;
|
|
|
|
int hfl, hfr, no_turn = 1, drive = 0;
|
|
|
|
FLOOR_INFO* floor;
|
|
|
|
int height, collide, water, ceiling, pitch, h, ofs, nowake;
|
2019-11-27 15:12:35 +01:00
|
|
|
short roomNumber, x_rot, z_rot;
|
2019-11-20 14:12:21 +01:00
|
|
|
|
|
|
|
boat = &Items[itemNumber];
|
|
|
|
binfo = (BOAT_INFO*)boat->data;
|
|
|
|
collide = BoatDynamics(itemNumber);
|
|
|
|
|
|
|
|
/* Now got final position, so get heights under middle and corners (will only have changed
|
|
|
|
from above if collision occurred, but recalc anyway as hardly big maths) */
|
|
|
|
hfl = TestWaterHeight(boat, BOAT_FRONT, -BOAT_SIDE, &fl);
|
|
|
|
hfr = TestWaterHeight(boat, BOAT_FRONT, BOAT_SIDE, &fr);
|
|
|
|
|
|
|
|
roomNumber = boat->roomNumber;
|
|
|
|
floor = GetFloor(boat->pos.xPos, boat->pos.yPos, boat->pos.zPos, &roomNumber);
|
|
|
|
height = GetFloorHeight(floor, boat->pos.xPos, boat->pos.yPos, boat->pos.zPos);
|
|
|
|
ceiling = GetCeiling(floor, boat->pos.xPos, boat->pos.yPos, boat->pos.zPos);
|
|
|
|
|
|
|
|
TestTriggers(TriggerIndex, 0, 0);
|
|
|
|
TestTriggers(TriggerIndex, 1, 0); // HEAVY too
|
|
|
|
|
|
|
|
binfo->water = water = GetWaterHeight(boat->pos.xPos, boat->pos.yPos, boat->pos.zPos, roomNumber);
|
|
|
|
|
|
|
|
/* Deal with user input; will effect next frame, but relies on info calced this frame */
|
|
|
|
if (g_LaraExtra.Vehicle == itemNumber && LaraItem->hitPoints > 0)
|
|
|
|
{
|
|
|
|
switch (LaraItem->currentAnimState)
|
|
|
|
{
|
|
|
|
case BOAT_GETON:
|
|
|
|
case BOAT_JUMPR:
|
|
|
|
case BOAT_JUMPL:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
drive = 1;
|
|
|
|
no_turn = BoatUserControl(boat);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Boat has no driver */
|
|
|
|
if (boat->speed > BOAT_SLOWDOWN)
|
|
|
|
boat->speed -= BOAT_SLOWDOWN;
|
|
|
|
else
|
|
|
|
boat->speed = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (no_turn)
|
|
|
|
{
|
|
|
|
/* Straighten up if not under control */
|
|
|
|
if (binfo->boat_turn < -BOAT_UNDO_TURN)
|
|
|
|
binfo->boat_turn += BOAT_UNDO_TURN;
|
|
|
|
else if (binfo->boat_turn > BOAT_UNDO_TURN)
|
|
|
|
binfo->boat_turn -= BOAT_UNDO_TURN;
|
|
|
|
else
|
|
|
|
binfo->boat_turn = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get floor height (keep slightly above water surface) */
|
|
|
|
boat->floor = height - 5;
|
|
|
|
if (binfo->water == NO_HEIGHT)
|
|
|
|
binfo->water = height;
|
|
|
|
else
|
|
|
|
binfo->water -= 5;
|
|
|
|
|
|
|
|
/* Do fallspeed effects on boat */
|
|
|
|
binfo->left_fallspeed = DoBoatDynamics(hfl, binfo->left_fallspeed, (int*)&fl.y);
|
|
|
|
binfo->right_fallspeed = DoBoatDynamics(hfr, binfo->right_fallspeed, (int*)&fr.y);
|
|
|
|
ofs = boat->fallspeed;
|
|
|
|
boat->fallspeed = DoBoatDynamics(binfo->water, boat->fallspeed, (int*)&boat->pos.yPos);
|
|
|
|
if (ofs - boat->fallspeed > 32 && boat->fallspeed == 0 && water != NO_HEIGHT)
|
|
|
|
BoatSplash(boat, ofs - boat->fallspeed, water);
|
|
|
|
|
|
|
|
/* Rotate boat to match these heights */
|
|
|
|
height = (fl.y + fr.y);
|
|
|
|
if (height < 0)
|
|
|
|
height = -(abs(height) >> 1);
|
|
|
|
else
|
|
|
|
height = height >> 1;
|
|
|
|
|
|
|
|
x_rot = ATAN(BOAT_FRONT, boat->pos.yPos - height);
|
|
|
|
z_rot = ATAN(BOAT_SIDE, height - fl.y);
|
|
|
|
|
|
|
|
boat->pos.xRot += (x_rot - boat->pos.xRot) >> 1;
|
|
|
|
boat->pos.zRot += (z_rot - boat->pos.zRot) >> 1;
|
|
|
|
|
|
|
|
/* Auto level the boat on flat water (to stop evil shifts) */
|
|
|
|
if (!x_rot && abs(boat->pos.xRot) < 4)
|
|
|
|
boat->pos.xRot = 0;
|
|
|
|
if (!z_rot && abs(boat->pos.zRot) < 4)
|
|
|
|
boat->pos.zRot = 0;
|
|
|
|
|
|
|
|
/* If Lara is on the boat, do all her animation and move her to same position as boat, etc */
|
|
|
|
if (g_LaraExtra.Vehicle == itemNumber)
|
|
|
|
{
|
|
|
|
BoatAnimation(boat, collide);
|
|
|
|
|
|
|
|
if (roomNumber != boat->roomNumber)
|
|
|
|
{
|
|
|
|
ItemNewRoom(g_LaraExtra.Vehicle, roomNumber);
|
|
|
|
ItemNewRoom(Lara.itemNumber, roomNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move Lara to the boat position */
|
|
|
|
boat->pos.zRot += binfo->tilt_angle;
|
|
|
|
LaraItem->pos.xPos = boat->pos.xPos;
|
|
|
|
LaraItem->pos.yPos = boat->pos.yPos;
|
|
|
|
LaraItem->pos.zPos = boat->pos.zPos;
|
|
|
|
LaraItem->pos.xRot = boat->pos.xRot;
|
|
|
|
LaraItem->pos.yRot = boat->pos.yRot;
|
|
|
|
LaraItem->pos.zRot = boat->pos.zRot;
|
|
|
|
|
|
|
|
AnimateItem(LaraItem);
|
|
|
|
|
|
|
|
/* Set boat on the exact same anim frame */
|
|
|
|
if (LaraItem->hitPoints > 0)
|
|
|
|
{
|
2020-03-21 07:29:31 +01:00
|
|
|
boat->animNumber = Objects[ID_SPEEDBOAT].animIndex + (LaraItem->animNumber - Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex);
|
2019-11-20 14:12:21 +01:00
|
|
|
boat->frameNumber = Anims[boat->animNumber].frameBase + (LaraItem->frameNumber - Anims[LaraItem->animNumber].frameBase);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set camera */
|
|
|
|
Camera.targetElevation = -ANGLE(20);
|
|
|
|
Camera.targetDistance = WALL_SIZE * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If Lara isn't in the boat, then just move the boat */
|
|
|
|
if (roomNumber != boat->roomNumber)
|
|
|
|
ItemNewRoom(itemNumber, roomNumber);
|
|
|
|
boat->pos.zRot += binfo->tilt_angle;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do sound effect */
|
|
|
|
pitch = boat->speed;
|
|
|
|
binfo->pitch += (pitch - binfo->pitch) >> 2;
|
|
|
|
|
|
|
|
if (boat->speed > 8)
|
|
|
|
SoundEffect(197, &boat->pos, 4 + ((0x10000 - (BOAT_MAX_SPEED - binfo->pitch) * 100) << 8));
|
|
|
|
else if (drive)
|
|
|
|
SoundEffect(195, &boat->pos, 4 + ((0x10000 - (BOAT_MAX_SPEED - binfo->pitch) * 100) << 8));
|
|
|
|
|
|
|
|
// /* If boat is moving, then do wake */
|
|
|
|
// if (boat->speed && water-5 == boat->pos.y_pos)
|
|
|
|
// DoWakeEffect(boat);
|
|
|
|
|
|
|
|
if (g_LaraExtra.Vehicle != itemNumber)
|
|
|
|
return;
|
2019-11-20 11:24:36 +01:00
|
|
|
|
2019-11-20 14:12:21 +01:00
|
|
|
GetBoatGetOff(boat);
|
2019-11-20 11:24:36 +01:00
|
|
|
}
|