Move globals into KayakInfo; cleanup

This commit is contained in:
Sezz 2022-03-03 21:31:32 +11:00
parent d494a115b0
commit 6603670a93
11 changed files with 404 additions and 406 deletions

View file

@ -585,7 +585,7 @@ bool HandleLaraVehicle(ITEM_INFO* item, COLL_INFO* coll)
break;
case ID_UPV:
SubControl(item, coll);
UPVControl(item, coll);
break;
case ID_MINECART:

View file

@ -474,8 +474,8 @@ void FireHarpoon(ITEM_INFO* laraItem)
}
item->Position.zRot = 0;
item->Velocity = HARPOON_SPEED * phd_cos(item->Position.xRot);
item->VerticalVelocity = -HARPOON_SPEED * phd_sin(item->Position.xRot);
item->Velocity = HARPOON_VELOCITY * phd_cos(item->Position.xRot);
item->VerticalVelocity = -HARPOON_VELOCITY * phd_sin(item->Position.xRot);
item->HitPoints = HARPOON_TIME;
AddActiveItem(itemNumber);
@ -507,8 +507,8 @@ void HarpoonBoltControl(short itemNumber)
if (item->Position.xRot < -ANGLE(90.0f))
item->Position.xRot = -ANGLE(90.0f);
item->VerticalVelocity = -HARPOON_SPEED * phd_sin(item->Position.xRot);
item->Velocity = HARPOON_SPEED * phd_cos(item->Position.xRot);
item->VerticalVelocity = -HARPOON_VELOCITY * phd_sin(item->Position.xRot);
item->Velocity = HARPOON_VELOCITY * phd_cos(item->Position.xRot);
aboveWater = true;
}
else
@ -518,8 +518,8 @@ void HarpoonBoltControl(short itemNumber)
CreateBubble((PHD_VECTOR*)&item->Position, item->RoomNumber, 0, 0, BUBBLE_FLAG_CLUMP | BUBBLE_FLAG_HIGH_AMPLITUDE, 0, 0, 0); // CHECK
TriggerRocketSmoke(item->Position.xPos, item->Position.yPos, item->Position.zPos, 64);
item->VerticalVelocity = -HARPOON_SPEED * phd_sin(item->Position.xRot) / 2;
item->Velocity = HARPOON_SPEED * phd_cos(item->Position.xRot) / 2;
item->VerticalVelocity = -HARPOON_VELOCITY * phd_sin(item->Position.xRot) / 2;
item->Velocity = HARPOON_VELOCITY * phd_cos(item->Position.xRot) / 2;
aboveWater = false;
}
@ -697,7 +697,7 @@ void FireGrenade(ITEM_INFO* laraItem)
item->Position.yRot += lara->ExtraTorsoRot.yRot;
}
item->Velocity = GRENADE_SPEED;
item->Velocity = GRENADE_VELOCITY;
item->VerticalVelocity = -512 * phd_sin(item->Position.xRot);
item->ActiveState = item->Position.xRot;
item->TargetState = item->Position.yRot;
@ -1180,14 +1180,14 @@ void RocketControl(short itemNumber)
bool abovewater = false;
if (TestEnvironment(ENV_FLAG_WATER, item->RoomNumber))
{
if (item->Velocity > (ROCKET_SPEED / 4))
if (item->Velocity > (ROCKET_VELOCITY / 4))
item->Velocity -= item->Velocity / 4;
else
{
item->Velocity += (item->Velocity / 4) + 4;
if (item->Velocity > (ROCKET_SPEED / 4))
item->Velocity = ROCKET_SPEED / 4;
if (item->Velocity > (ROCKET_VELOCITY / 4))
item->Velocity = ROCKET_VELOCITY / 4;
}
item->Position.zRot += (((item->Velocity / 8) + 3) * ANGLE(1.0f));
@ -1195,7 +1195,7 @@ void RocketControl(short itemNumber)
}
else
{
if (item->Velocity < ROCKET_SPEED)
if (item->Velocity < ROCKET_VELOCITY)
item->Velocity += (item->Velocity / 4) + 4;
item->Position.zRot += (((item->Velocity / 4) + 7) * ANGLE(1.0f));

View file

@ -4,6 +4,29 @@
struct ITEM_INFO;
enum LaraWeaponType;
#define PELLET_SCATTER ANGLE(20.0f)
constexpr auto HARPOON_DRAW_ANIM = 1;
constexpr auto ROCKET_DRAW_ANIM = 0;
constexpr auto HARPOON_VELOCITY = CLICK(1);
constexpr auto HARPOON_TIME = 300;
constexpr auto ROCKET_VELOCITY = CLICK(2);
constexpr auto GRENADE_VELOCITY = CLICK(0.5f);
constexpr auto MAX_GRENADE_VERTICAL_VELOCITY = CLICK(0.5f);
constexpr auto GRENADE_Y_OFFSET = 180;
constexpr auto GRENADE_Z_OFFSET = 80;
constexpr auto CROSSBOW_DAMAGE = 5;
constexpr auto CROSSBOW_AMMO1 = 1;
constexpr auto CROSSBOW_AMMO2 = 2;
constexpr auto CROSSBOW_AMMO3 = 2;
constexpr auto CROSSBOW_HIT_RADIUS = CLICK(0.5f);
constexpr auto CROSSBOW_EXPLODE_RADIUS = SECTOR(2);
constexpr auto GRENADE_HIT_RADIUS = CLICK(0.5f);
constexpr auto GRENADE_EXPLODE_RADIUS = SECTOR(2);
constexpr auto ROCKET_HIT_RADIUS = CLICK(0.5f);
constexpr auto ROCKET_EXPLODE_RADIUS = SECTOR(2);
constexpr auto HARPOON_HIT_RADIUS = CLICK(0.5f);
enum class GrenadeType
{
Normal,
@ -13,29 +36,6 @@ enum class GrenadeType
Flags
};
#define PELLET_SCATTER ANGLE(20.0f)
constexpr auto HARPOON_DRAW_ANIM = 1;
constexpr auto ROCKET_DRAW_ANIM = 0;
constexpr auto HARPOON_SPEED = 256;
constexpr auto HARPOON_TIME = 300;
constexpr auto ROCKET_SPEED = 512;
constexpr auto GRENADE_SPEED = 128;
constexpr auto MAX_GRENADE_FALLSPEED = 128;
constexpr auto GRENADE_YOFF = 180;
constexpr auto GRENADE_ZOFF = 80;
constexpr auto CROSSBOW_DAMAGE = 5;
constexpr auto CROSSBOW_AMMO1 = 1;
constexpr auto CROSSBOW_AMMO2 = 2;
constexpr auto CROSSBOW_AMMO3 = 2;
constexpr auto CROSSBOW_HIT_RADIUS = 128;
constexpr auto CROSSBOW_EXPLODE_RADIUS = SECTOR(2);
constexpr auto GRENADE_HIT_RADIUS = 128;
constexpr auto GRENADE_EXPLODE_RADIUS = SECTOR(2);
constexpr auto ROCKET_HIT_RADIUS = 128;
constexpr auto ROCKET_EXPLODE_RADIUS = SECTOR(2);
constexpr auto HARPOON_HIT_RADIUS = 128;
void AnimateShotgun(ITEM_INFO* laraItem, LaraWeaponType weaponType);
void ReadyShotgun(ITEM_INFO* laraItem, LaraWeaponType weaponType);
void FireShotgun(ITEM_INFO* laraItem);

View file

@ -3,6 +3,7 @@
#include "Game/animation.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h"
#include "Game/effects/effects.h"
#include "Game/Lara/lara.h"
#include "Game/items.h"
@ -14,58 +15,50 @@
#define ROCKET_DAMAGE 100
#define DIVER_HARPOON_DAMAGE 50
#define SHARD_SPEED 250
#define ROCKET_SPEED 220
#define NATLAGUN_SPEED 400
#define SHARD_VELOCITY 250
#define ROCKET_VELOCITY 220
#define NATLAGUN_VELOCITY 400
void ShootAtLara(FX_INFO *fx)
{
int x, y, z, distance;
BOUNDING_BOX* bounds;
int x = LaraItem->Position.xPos - fx->pos.xPos;
int y = LaraItem->Position.yPos - fx->pos.yPos;
int z = LaraItem->Position.zPos - fx->pos.zPos;
x = LaraItem->Position.xPos - fx->pos.xPos;
y = LaraItem->Position.yPos - fx->pos.yPos;
z = LaraItem->Position.zPos - fx->pos.zPos;
bounds = GetBoundsAccurate(LaraItem);
auto* bounds = GetBoundsAccurate(LaraItem);
y += bounds->Y2 + (bounds->Y1 - bounds->Y2) * 3 / 4;
distance = sqrt(SQUARE(x) + SQUARE(z));
int distance = sqrt(pow(x, 2) + pow(z, 2));
fx->pos.xRot = -phd_atan(distance, y);
fx->pos.yRot = phd_atan(z, x);
/* Random scatter (only a little bit else it's too hard to avoid) */
// Random scatter (only a little bit else it's too hard to avoid).
fx->pos.xRot += (GetRandomControl() - 0x4000) / 0x40;
fx->pos.yRot += (GetRandomControl() - 0x4000) / 0x40;
}
void ControlMissile(short fxNumber)
{
FX_INFO *fx;
FLOOR_INFO *floor;
short roomNumber;
int speed;
fx = &EffectList[fxNumber];
auto* fx = &EffectList[fxNumber];
printf("ControlMissile\n");
if (fx->objectNumber == ID_SCUBA_HARPOON && !(g_Level.Rooms[fx->roomNumber].flags & 1) && fx->pos.xRot > -0x3000)
fx->pos.xRot -= ONE_DEGREE;
fx->pos.xRot -= ANGLE(1.0f);
fx->pos.yPos += fx->speed * phd_sin(-fx->pos.xRot);
speed = fx->speed * phd_cos(fx->pos.xRot);
int speed = fx->speed * phd_cos(fx->pos.xRot);
fx->pos.zPos += speed * phd_cos(fx->pos.yRot);
fx->pos.xPos += speed * phd_sin(fx->pos.yRot);
roomNumber = fx->roomNumber;
floor = GetFloor(fx->pos.xPos, fx->pos.yPos, fx->pos.zPos, &roomNumber);
/* Check for hitting something */
if (fx->pos.yPos >= GetFloorHeight(floor, fx->pos.xPos, fx->pos.yPos, fx->pos.zPos) ||
fx->pos.yPos <= GetCeiling(floor, fx->pos.xPos, fx->pos.yPos, fx->pos.zPos))
auto probe = GetCollisionResult(fx->pos.xPos, fx->pos.yPos, fx->pos.zPos, fx->roomNumber);
// Check for hitting something.
if (fx->pos.yPos >= probe.Position.Floor ||
fx->pos.yPos <= probe.Position.Ceiling)
{
if (/*fx->objectNumber == KNIFE ||*/ fx->objectNumber == ID_SCUBA_HARPOON)
{
/* Change shard into ricochet */
// Change shard into ricochet.
// fx->speed = 0;
// fx->frameNumber = -GetRandomControl()/11000;
// fx->counter = 6;
@ -80,10 +73,10 @@ void ControlMissile(short fxNumber)
return;
}
if (roomNumber != fx->roomNumber)
EffectNewRoom(fxNumber, roomNumber);
if (probe.RoomNumber != fx->roomNumber)
EffectNewRoom(fxNumber, probe.RoomNumber);
/* Check for hitting Lara */
// Check for hitting Lara.
/*if (fx->objectNumber == DRAGON_FIRE)
{
if (ItemNearLara(&fx->pos, 350))
@ -108,14 +101,15 @@ void ControlMissile(short fxNumber)
SoundEffect(317, &fx->pos, 0);
KillEffect(fxNumber);
}
LaraItem->HitStatus = 1;
LaraItem->HitStatus = 1;
fx->pos.yRot = LaraItem->Position.yRot;
fx->speed = LaraItem->Velocity;
fx->frameNumber = fx->counter = 0;
}
/* Create bubbles in wake of harpoon bolt */
// Create bubbles in wake of harpoon bolt.
//if (fx->objectNumber == ID_SCUBA_HARPOON && g_Level.Rooms[fx->roomNumber].flags & 1)
// CreateBubble(&fx->pos, fx->roomNumber, 1, 0);
/*else if (fx->objectNumber == DRAGON_FIRE && !fx->counter--)
@ -128,42 +122,41 @@ void ControlMissile(short fxNumber)
fx->pos.zRot += 30 * ONE_DEGREE;*/
}
void ControlNatlaGun(short fx_number)
void ControlNatlaGun(short fxNumber)
{
FX_INFO* fx, *newfx;
OBJECT_INFO* object;
FLOOR_INFO* floor;
short roomNumber;
int x, y, z;
auto* fx = &EffectList[fxNumber];
auto* object = &Objects[fx->objectNumber];
fx = &EffectList[fx_number];
object = &Objects[fx->objectNumber];
fx->frameNumber--;
if (fx->frameNumber <= Objects[fx->objectNumber].nmeshes)
KillEffect(fx_number);
KillEffect(fxNumber);
/* If first frame, then start another explosion at next position */
if (fx->frameNumber == -1)
{
z = fx->pos.zPos + fx->speed * phd_cos(fx->pos.yRot);
x = fx->pos.xPos + fx->speed * phd_sin(fx->pos.yRot);
y = fx->pos.yPos;
roomNumber = fx->roomNumber;
floor = GetFloor(x, y, z, &roomNumber);
int z = fx->pos.zPos + fx->speed * phd_cos(fx->pos.yRot);
int x = fx->pos.xPos + fx->speed * phd_sin(fx->pos.yRot);
int y = fx->pos.yPos;
/* Don't create one if hit a wall */
if (y >= GetFloorHeight(floor, x, y, z) || y <= GetCeiling(floor, x, y, z))
return;
auto probe = GetCollisionResult(x, y, z, fx->roomNumber);
fx_number = CreateNewEffect(roomNumber);
if (fx_number != NO_ITEM)
// Don't create one if hit a wall.
if (y >= probe.Position.Floor ||
y <= probe.Position.Ceiling)
{
newfx = &EffectList[fx_number];
return;
}
fxNumber = CreateNewEffect(probe.RoomNumber);
if (fxNumber != NO_ITEM)
{
auto* newfx = &EffectList[fxNumber];
newfx->pos.xPos = x;
newfx->pos.yPos = y;
newfx->pos.zPos = z;
newfx->pos.yRot = fx->pos.yRot;
newfx->roomNumber = roomNumber;
newfx->roomNumber = probe.RoomNumber;
newfx->speed = fx->speed;
newfx->frameNumber = 0;
newfx->objectNumber = ID_PROJ_NATLA;
@ -171,77 +164,71 @@ void ControlNatlaGun(short fx_number)
}
}
short ShardGun(int x, int y, int z, short speed, short yrot, short roomNumber)
short ShardGun(int x, int y, int z, short velocity, short yRot, short roomNumber)
{
short fx_number;
FX_INFO* fx;
fx_number = CreateNewEffect(roomNumber);
if (fx_number != NO_ITEM)
short fxNumber = CreateNewEffect(roomNumber);
if (fxNumber != NO_ITEM)
{
fx = &EffectList[fx_number];
auto* fx = &EffectList[fxNumber];
fx->pos.xPos = x;
fx->pos.yPos = y;
fx->pos.zPos = z;
fx->roomNumber = roomNumber;
fx->pos.xRot = fx->pos.zRot = 0;
fx->pos.yRot = yrot;
fx->speed = SHARD_SPEED;
fx->pos.yRot = yRot;
fx->speed = SHARD_VELOCITY;
fx->frameNumber = 0;
fx->objectNumber = ID_PROJ_SHARD;
fx->shade = 14 * 256;
ShootAtLara(fx);
}
return (fx_number);
return fxNumber;
}
short BombGun(int x, int y, int z, short speed, short yrot, short roomNumber)
short BombGun(int x, int y, int z, short velocity, short yRot, short roomNumber)
{
short fx_number;
FX_INFO* fx;
fx_number = CreateNewEffect(roomNumber);
if (fx_number != NO_ITEM)
short fxNumber = CreateNewEffect(roomNumber);
if (fxNumber != NO_ITEM)
{
fx = &EffectList[fx_number];
auto* fx = &EffectList[fxNumber];
fx->pos.xPos = x;
fx->pos.yPos = y;
fx->pos.zPos = z;
fx->roomNumber = roomNumber;
fx->pos.xRot = fx->pos.zRot = 0;
fx->pos.yRot = yrot;
fx->speed = ROCKET_SPEED;
fx->pos.yRot = yRot;
fx->speed = ROCKET_VELOCITY;
fx->frameNumber = 0;
fx->objectNumber = ID_PROJ_BOMB;
fx->shade = 16 * 256;
ShootAtLara(fx);
}
return (fx_number);
return fxNumber;
}
short NatlaGun(int x, int y, int z, short speed, short yrot, short roomNumber)
short NatlaGun(int x, int y, int z, short velocity, short yRot, short roomNumber)
{
short fx_number;
FX_INFO* fx;
fx_number = CreateNewEffect(roomNumber);
if (fx_number != NO_ITEM)
short fxNumber = CreateNewEffect(roomNumber);
if (fxNumber != NO_ITEM)
{
fx = &EffectList[fx_number];
auto* fx = &EffectList[fxNumber];
fx->pos.xPos = x;
fx->pos.yPos = y;
fx->pos.zPos = z;
fx->roomNumber = roomNumber;
fx->pos.xRot = fx->pos.zRot = 0;
fx->pos.yRot = yrot;
fx->speed = NATLAGUN_SPEED;
fx->pos.yRot = yRot;
fx->speed = NATLAGUN_VELOCITY;
fx->frameNumber = 0;
fx->objectNumber = ID_PROJ_NATLA;
fx->shade = 16 * 256;
ShootAtLara(fx);
}
return (fx_number);
}
return fxNumber;
}

View file

@ -3,8 +3,8 @@
void ShootAtLara(FX_INFO* fx);
void ControlMissile(short fxNumber);
void ControlNatlaGun(short fx_number);
void ControlNatlaGun(short fxNumber);
short ShardGun(int x, int y, int z, short speed, short yrot, short roomNumber);
short BombGun(int x, int y, int z, short speed, short yrot, short roomNumber); // RocketGun = BombGun
short NatlaGun(int x, int y, int z, short speed, short yrot, short roomNumber);
short ShardGun(int x, int y, int z, short velocity, short yRot, short roomNumber);
short BombGun(int x, int y, int z, short velocity, short yRot, short roomNumber); // RocketGun = BombGun
short NatlaGun(int x, int y, int z, short velocity, short yRot, short roomNumber);

View file

@ -139,8 +139,6 @@ struct WAKE_PTS
};
WAKE_PTS WakePts[NUM_WAKE_SPRITES][2];
byte CurrentStartWake = 0;
byte WakeShade = 0;
void InitialiseKayak(short itemNumber)
{
@ -155,9 +153,10 @@ void InitialiseKayak(short itemNumber)
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;
@ -172,7 +171,9 @@ void KayakDraw(ITEM_INFO* kayakItem)
void KayakDoWake(ITEM_INFO* kayakItem, int xOffset, int zOffset, short rotate)
{
if (WakePts[CurrentStartWake][rotate].life)
auto* kayak = (KayakInfo*)kayakItem->Data;
if (WakePts[kayak->CurrentStartWake][rotate].life)
return;
float s = phd_sin(kayakItem->Position.yRot);
@ -220,21 +221,21 @@ void KayakDoWake(ITEM_INFO* kayakItem, int xOffset, int zOffset, short rotate)
xv[1] = (WAKE_VELOCITY + 2) * phd_sin(angle2);
zv[1] = (WAKE_VELOCITY + 2) * phd_cos(angle2);
WakePts[CurrentStartWake][rotate].y = kayakItem->Position.yPos + KAYAK_DRAW_SHIFT;
WakePts[CurrentStartWake][rotate].life = 0x40;
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[CurrentStartWake][rotate].x[i] = x;
WakePts[CurrentStartWake][rotate].z[i] = z;
WakePts[CurrentStartWake][rotate].xvel[i] = xv[i];
WakePts[CurrentStartWake][rotate].zvel[i] = zv[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)
{
CurrentStartWake++;
CurrentStartWake &= (NUM_WAKE_SPRITES - 1);
kayak->CurrentStartWake++;
kayak->CurrentStartWake &= (NUM_WAKE_SPRITES - 1);
}
}
}
@ -425,9 +426,9 @@ void KayakDoCurrent(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
int dx = target.x - laraItem->Position.xPos;
int dz = target.z - laraItem->Position.zPos;
int speed = g_Level.Sinks[sinkval].strength;
dx = phd_sin(angle * 16) * speed * 1024;
dz = phd_cos(angle * 16) * speed * 1024;
int velocity = g_Level.Sinks[sinkval].strength;
dx = phd_sin(angle * 16) * velocity * 1024;
dz = phd_cos(angle * 16) * velocity * 1024;
lara->ExtraVelocity.x += (dx - lara->ExtraVelocity.x) / 16;
lara->ExtraVelocity.z += (dz - lara->ExtraVelocity.z) / 16;
@ -813,6 +814,7 @@ void KayakUserInput(ITEM_INFO* laraItem, ITEM_INFO* kayakItem)
if (!frame)
kayak->LeftRightCount = 0;
// TODO: Sort out the bitwise operations.
if (frame == 2 && !(kayak->LeftRightCount & 0x80))
kayak->LeftRightCount++;
@ -1354,13 +1356,13 @@ bool KayakControl(ITEM_INFO* laraItem)
!lara->ExtraVelocity.x &&
!lara->ExtraVelocity.z)
{
if (WakeShade)
WakeShade--;
if (kayak->WakeShade)
kayak->WakeShade--;
}
else
{
if (WakeShade < 16)
WakeShade++;
if (kayak->WakeShade < 16)
kayak->WakeShade++;
}
KayakUpdateWakeFX();

View file

@ -17,5 +17,8 @@ struct KayakInfo
bool Forward;
bool TrueWater;
int CurrentStartWake;
int WakeShade;
char Flags;
};

View file

@ -5,6 +5,7 @@
#include "Game/camera.h"
#include "Game/collision/sphere.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h"
#include "Game/control/box.h"
#include "Game/control/los.h"
#include "Game/effects/bubble.h"
@ -43,24 +44,24 @@ BITE_INFO UPVBites[6] =
#define ROT_SLOWACCEL 0x200000
#define ROT_FRICTION 0x100000
#define MAX_ROTATION 0x1c00000
#define UPDOWN_ACCEL (ANGLE(2.0f) * 65536)
#define UPDOWN_SLOWACCEL (ANGLE(1.0f) * 65536)
#define UPDOWN_FRICTION (ANGLE(1.0f) * 65536)
#define MAX_UPDOWN (ANGLE(2.0f) * 65536)
#define UPDOWN_ACCEL (ANGLE(2.0f) * (USHRT_MAX + 1))
#define UPDOWN_SLOWACCEL (ANGLE(1.0f) * (USHRT_MAX + 1))
#define UPDOWN_FRICTION (ANGLE(1.0f) * (USHRT_MAX + 1))
#define MAX_UPDOWN (ANGLE(2.0f) * (USHRT_MAX + 1))
#define UPDOWN_LIMIT ANGLE(80.0f)
#define UPDOWN_SPEED 10
#define SURFACE_DIST 210
#define SURFACE_ANGLE ANGLE(30.0f)
#define DIVE_ANGLE ANGLE(15.0f)
#define DIVE_SPEED ANGLE(5.0f)
#define SUB_DRAW_SHIFT 128
#define SUB_RADIUS 300
#define SUB_HEIGHT 400
#define SUB_LENGTH SECTOR(1)
#define FRONT_TOLERANCE (ANGLE(45.0f) * 65536)
#define TOP_TOLERANCE (ANGLE(45.0f) * 65536)
#define WALLDEFLECT (ANGLE(2.0f) * 65536)
#define GETOFF_DIST SECTOR(1)
#define UPV_DRAW_SHIFT 128
#define UPV_RADIUS 300
#define UPV_HEIGHT 400
#define UPV_LENGTH SECTOR(1)
#define FRONT_TOLERANCE (ANGLE(45.0f) * (USHRT_MAX + 1))
#define TOP_TOLERANCE (ANGLE(45.0f) * (USHRT_MAX + 1))
#define WALL_DEFLECT (ANGLE(2.0f) * (USHRT_MAX + 1))
#define DISMOUNT_DISTANCE SECTOR(1)
#define HARPOON_VELOCITY CLICK(1)
#define HARPOON_RELOAD 15
@ -75,26 +76,26 @@ BITE_INFO UPVBites[6] =
#define MOUNT_UNDERWATER_SOUND_FRAME 30
#define MOUNT_UNDERWATER_CONTROL_FRAME 42
#define UPV_IN_PROPEL IN_JUMP
#define UPV_IN_UP IN_FORWARD
#define UPV_IN_DOWN IN_BACK
#define UPV_IN_LEFT IN_LEFT
#define UPV_IN_RIGHT IN_RIGHT
#define UPV_IN_FIRE IN_ACTION
#define UPV_IN_DISMOUNT IN_ROLL
#define UPV_IN_PROPEL IN_JUMP
#define UPV_IN_UP IN_FORWARD
#define UPV_IN_DOWN IN_BACK
#define UPV_IN_LEFT IN_LEFT
#define UPV_IN_RIGHT IN_RIGHT
#define UPV_IN_FIRE IN_ACTION
#define UPV_IN_DISMOUNT IN_ROLL
enum UPVState
{
UPV_STATE_DEATH,
UPV_STATE_HIT,
UPV_STATE_DISMOUNT_SURFACE,
UPV_STATE_UNK1,
UPV_STATE_MOVE,
UPV_STATE_IDLE,
UPV_STATE_UNK2,
UPV_STATE_UNK3,
UPV_STATE_MOUNT,
UPV_STATE_DISMOUNT_UNDERWATER
UPV_STATE_DEATH = 0,
UPV_STATE_HIT = 1,
UPV_STATE_DISMOUNT_WATER_SURFACE = 2,
UPV_STATE_UNK1 = 3,
UPV_STATE_MOVE = 4,
UPV_STATE_IDLE = 5,
UPV_STATE_UNK2 = 6, // TODO
UPV_STATE_UNK3 = 7, // TODO
UPV_STATE_MOUNT = 8,
UPV_STATE_DISMOUNT_UNDERWATER = 9
};
// TODO
@ -111,75 +112,76 @@ enum UPVAnim
UPV_ANIM_MOUNT_UNDERWATER = 13,
};
enum SUB_BITE_FLAG
enum UPVBiteFlags
{
SUB_FAN = 0,
SUB_FRONT_LIGHT,
SUB_LEFT_FIN_LEFT,
SUB_LEFT_FIN_RIGHT,
SUB_RIGHT_FIN_RIGHT,
SUB_RIGHT_FIN_LEFT
UPV_FAN = 0,
UPV_FRONT_LIGHT = 1,
UPV_LEFT_FIN_LEFT = 2,
UPV_LEFT_FIN_RIGHT = 3,
UPV_RIGHT_FIN_RIGHT = 4,
UPV_RIGHT_FIN_LEFT = 5
};
void UPVInitialise(short itemNumber)
{
ITEM_INFO* UPVItem = &g_Level.Items[itemNumber];
auto* UPVItem = &g_Level.Items[itemNumber];
UPVItem->Data = UPVInfo();
UPVInfo* UPV = UPVItem->Data;
auto* UPV = (UPVInfo*)UPVItem->Data;
UPV->Vel = UPV->Rot = 0;
UPV->Velocity = 0;
UPV->Rot = 0;
UPV->Flags = UPV_SURFACE;
UPV->WeaponTimer = 0;
UPV->HarpoonTimer = 0;
UPV->HarpoonLeft = false;
}
static void FireSubHarpoon(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
static void FireUPVHarpoon(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
{
short itemNum = CreateItem();
auto* lara = GetLaraInfo(laraItem);
auto UPV = (UPVInfo*)UPVItem->Data;
if (itemNum != NO_ITEM)
short itemNumber = CreateItem();
if (itemNumber != NO_ITEM)
{
static char lr = 0;
PHD_VECTOR pos { (lr ? 22 : -22), 24, 230 };
ITEM_INFO* harpoonItem = &g_Level.Items[itemNum];
auto* harpoonItem = &g_Level.Items[itemNumber];
harpoonItem->ObjectNumber = ID_HARPOON;
harpoonItem->Shade = 0xC210;
harpoonItem->RoomNumber = UPVItem->RoomNumber;
PHD_VECTOR pos{ (UPV->HarpoonLeft ? 22 : -22), 24, 230 };
GetJointAbsPosition(UPVItem, &pos, UPV_TURBINE_BONE);
harpoonItem->Position.xPos = pos.x;
harpoonItem->Position.yPos = pos.y;
harpoonItem->Position.zPos = pos.z;
InitialiseItem(itemNum);
InitialiseItem(itemNumber);
harpoonItem->Position.xRot = UPVItem->Position.xRot;
harpoonItem->Position.yRot = UPVItem->Position.yRot;
harpoonItem->Position.zRot = 0;
// TODO: Huh?
harpoonItem->VerticalVelocity = -HARPOON_VELOCITY * phd_sin(harpoonItem->Position.xRot);
harpoonItem->VerticalVelocity = HARPOON_VELOCITY * phd_cos(harpoonItem->Position.xRot);
harpoonItem->HitPoints = HARPOON_TIME;
harpoonItem->ItemFlags[0] = 1;
AddActiveItem(itemNum);
AddActiveItem(itemNumber);
SoundEffect(SFX_TR3_LARA_HARPOON_FIRE_WATER, &LaraItem->Position, 2);
SoundEffect(SFX_TR3_LARA_HARPOON_FIRE_WATER, &laraItem->Position, 2);
if (Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1])
Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1]--;
if (lara->Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1])
lara->Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1]--;
Statistics.Game.AmmoUsed++;
lr ^= 1;
UPV->HarpoonLeft = !UPV->HarpoonLeft;
}
}
static void TriggerSubMist(long x, long y, long z, long speed, short angle)
static void TriggerUPVMist(long x, long y, long z, long velocity, short angle)
{
long size, xv, zv;
SPARKS* sptr;
sptr = &Sparks[GetFreeSpark()];
auto* sptr = &Sparks[GetFreeSpark()];
sptr->on = 1;
sptr->sR = 0;
@ -200,8 +202,8 @@ static void TriggerSubMist(long x, long y, long z, long speed, short angle)
sptr->x = x + ((GetRandomControl() & 15) - 8);
sptr->y = y + ((GetRandomControl() & 15) - 8);
sptr->z = z + ((GetRandomControl() & 15) - 8);
zv = speed * phd_cos(angle) / 4;
xv = speed * phd_sin(angle) / 4;
long zv = velocity * phd_cos(angle) / 4;
long xv = velocity * phd_sin(angle) / 4;
sptr->xVel = xv + ((GetRandomControl() & 127) - 64);
sptr->yVel = 0;
sptr->zVel = zv + ((GetRandomControl() & 127) - 64);
@ -222,61 +224,57 @@ static void TriggerSubMist(long x, long y, long z, long speed, short angle)
sptr->scalar = 3;
sptr->gravity = sptr->maxYvel = 0;
size = (GetRandomControl() & 7) + (speed / 2) + 16;
long size = (GetRandomControl() & 7) + (velocity / 2) + 16;
sptr->size = sptr->sSize = size / 4;
sptr->dSize = size;
}
void SubEffects(short itemNum)
void UPVEffects(short itemNumber)
{
if (itemNum == NO_ITEM)
if (itemNumber == NO_ITEM)
return;
ITEM_INFO* laraItem = LaraItem;
auto* laraItem = LaraItem;
auto* lara = GetLaraInfo(laraItem);
ITEM_INFO* UPVItem = &g_Level.Items[itemNum];
UPVInfo* UPV = UPVItem->Data;
auto* UPVItem = &g_Level.Items[itemNumber];
auto* UPV = (UPVInfo*)UPVItem->Data;
PHD_VECTOR pos;
long lp;
if (lara->Vehicle == itemNum)
if (lara->Vehicle == itemNumber)
{
if (!UPV->Vel)
if (!UPV->Velocity)
UPV->FanRot += ANGLE(2.0f);
else
UPV->FanRot += (UPV->Vel / 4069);
UPV->FanRot += UPV->Velocity / 4069;
if (UPV->Vel)
if (UPV->Velocity)
{
pos = { UPVBites[SUB_FAN].x, UPVBites[SUB_FAN].y, UPVBites[SUB_FAN].z };
GetJointAbsPosition(UPVItem, &pos, UPVBites[SUB_FAN].meshNum);
TriggerSubMist(pos.x, pos.y + SUB_DRAW_SHIFT, pos.z, abs(UPV->Vel) / 65536, UPVItem->Position.yRot + ANGLE(180.0f));
pos = { UPVBites[UPV_FAN].x, UPVBites[UPV_FAN].y, UPVBites[UPV_FAN].z };
GetJointAbsPosition(UPVItem, &pos, UPVBites[UPV_FAN].meshNum);
TriggerUPVMist(pos.x, pos.y + UPV_DRAW_SHIFT, pos.z, abs(UPV->Velocity) / (USHRT_MAX + 1), UPVItem->Position.yRot + ANGLE(180.0f));
if ((GetRandomControl() & 1) == 0)
{
PHD_3DPOS pos3d;
short roomNum;
pos3d.xPos = pos.x + (GetRandomControl() & 63) - 32;
pos3d.yPos = pos.y + SUB_DRAW_SHIFT;
pos3d.zPos = pos.z + (GetRandomControl() & 63) - 32;
roomNum = UPVItem->RoomNumber;
GetFloor(pos3d.xPos, pos3d.yPos, pos3d.zPos, &roomNum);
CreateBubble((PHD_VECTOR*)&pos3d, roomNum, 4, 8, BUBBLE_FLAG_CLUMP, 0, 0, 0);
PHD_3DPOS pos2;
pos2.xPos = pos.x + (GetRandomControl() & 63) - 32;
pos2.yPos = pos.y + UPV_DRAW_SHIFT;
pos2.zPos = pos.z + (GetRandomControl() & 63) - 32;
short probedRoomNumber = GetCollisionResult(pos2.xPos, pos2.yPos, pos2.zPos, UPVItem->RoomNumber).RoomNumber;
CreateBubble((PHD_VECTOR*)&pos2, probedRoomNumber, 4, 8, BUBBLE_FLAG_CLUMP, 0, 0, 0);
}
}
}
for (lp = 0; lp < 2; lp++)
for (int lp = 0; lp < 2; lp++)
{
int random = 31 - (GetRandomControl() & 3);
pos = { UPVBites[UPV_FRONT_LIGHT].x, UPVBites[UPV_FRONT_LIGHT].y, UPVBites[UPV_FRONT_LIGHT].z << (lp * 6) };
GetJointAbsPosition(UPVItem, &pos, UPVBites[UPV_FRONT_LIGHT].meshNum);
GAME_VECTOR source, target;
long r;
r = 31 - (GetRandomControl() & 3);
pos = { UPVBites[SUB_FRONT_LIGHT].x, UPVBites[SUB_FRONT_LIGHT].y, UPVBites[SUB_FRONT_LIGHT].z << (lp * 6) };
GetJointAbsPosition(UPVItem, &pos, UPVBites[SUB_FRONT_LIGHT].meshNum);
if (lp == 1)
{
target.x = pos.x;
@ -294,11 +292,11 @@ void SubEffects(short itemNum)
source.roomNumber = UPVItem->RoomNumber;
}
TriggerDynamicLight(pos.x, pos.y, pos.z, 16 + (lp << 3), r, r, r);
TriggerDynamicLight(pos.x, pos.y, pos.z, 16 + (lp << 3), random, random, random);
}
if (UPV->WeaponTimer)
UPV->WeaponTimer--;
if (UPV->HarpoonTimer)
UPV->HarpoonTimer--;
}
static bool TestUPVDismount(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
@ -309,14 +307,14 @@ static bool TestUPVDismount(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
return false;
short moveAngle = UPVItem->Position.yRot + ANGLE(180.0f);
int speed = GETOFF_DIST * phd_cos(UPVItem->Position.xRot);
int x = UPVItem->Position.xPos + speed * phd_sin(moveAngle);
int z = UPVItem->Position.zPos + speed * phd_cos(moveAngle);
int y = UPVItem->Position.yPos - GETOFF_DIST * phd_sin(-UPVItem->Position.xRot);
auto probe = GetCollisionResult(x, y, z, UPVItem->RoomNumber);
int velocity = DISMOUNT_DISTANCE * phd_cos(UPVItem->Position.xRot);
int x = UPVItem->Position.xPos + velocity * phd_sin(moveAngle);
int z = UPVItem->Position.zPos + velocity * phd_cos(moveAngle);
int y = UPVItem->Position.yPos - DISMOUNT_DISTANCE * phd_sin(-UPVItem->Position.xRot);
if (probe.Position.Floor < y ||
(probe.Position.Floor - probe.Position.Ceiling) < STEP_SIZE ||
auto probe = GetCollisionResult(x, y, z, UPVItem->RoomNumber);
if ((probe.Position.Floor - probe.Position.Ceiling) < CLICK(1) ||
probe.Position.Floor < y ||
probe.Position.Ceiling > y ||
probe.Position.Floor == NO_HEIGHT ||
probe.Position.Ceiling == NO_HEIGHT)
@ -338,97 +336,99 @@ static bool TestUPVMount(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
return false;
}
int y = abs(laraItem->Position.yPos - (UPVItem->Position.yPos - STEP_SIZE / 2));
int dist = pow(laraItem->Position.xPos - UPVItem->Position.xPos, 2) + pow(laraItem->Position.zPos - UPVItem->Position.zPos, 2);
short rotDelta = abs(laraItem->Position.yRot - UPVItem->Position.yRot);
if (y > STEP_SIZE ||
dist > pow(STEP_SIZE * 2, 2) ||
rotDelta > ANGLE(35.0f) || rotDelta < -ANGLE(35.0f) ||
GetCollisionResult(UPVItem).Position.Floor < -32000)
{
int y = abs(laraItem->Position.yPos - (UPVItem->Position.yPos - CLICK(0.5f)));
if (y > CLICK(1))
return false;
int distance = pow(laraItem->Position.xPos - UPVItem->Position.xPos, 2) + pow(laraItem->Position.zPos - UPVItem->Position.zPos, 2);
if (distance > pow(CLICK(2), 2))
return false;
short deltaAngle = abs(laraItem->Position.yRot - UPVItem->Position.yRot);
if (deltaAngle > ANGLE(35.0f) || deltaAngle < -ANGLE(35.0f))
return false;
if (GetCollisionResult(UPVItem).Position.Floor < -32000)
return false;
}
return true;
}
static void DoCurrent(ITEM_INFO* item)
static void DoCurrent(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
{
auto* lara = GetLaraInfo(laraItem);
PHD_VECTOR target;
if (!Lara.Control.WaterCurrentActive)
if (!lara->Control.WaterCurrentActive)
{
long shifter, absvel;
absvel = abs(Lara.ExtraVelocity.x);
if (absvel > 16)
shifter = 4;
else if (absvel > 8)
shifter = 3;
int absVel = abs(lara->ExtraVelocity.x);
int shift;
if (absVel > 16)
shift = 4;
else if (absVel > 8)
shift = 3;
else
shifter = 2;
shift = 2;
Lara.ExtraVelocity.x -= Lara.ExtraVelocity.x >> shifter;
lara->ExtraVelocity.x -= lara->ExtraVelocity.x >> shift;
if (abs(Lara.ExtraVelocity.x) < 4)
Lara.ExtraVelocity.x = 0;
if (abs(lara->ExtraVelocity.x) < 4)
lara->ExtraVelocity.x = 0;
absvel = abs(Lara.ExtraVelocity.z);
if (absvel > 16)
shifter = 4;
else if (absvel > 8)
shifter = 3;
absVel = abs(lara->ExtraVelocity.z);
if (absVel > 16)
shift = 4;
else if (absVel > 8)
shift = 3;
else
shifter = 2;
shift = 2;
Lara.ExtraVelocity.z -= Lara.ExtraVelocity.z >> shifter;
if (abs(Lara.ExtraVelocity.z) < 4)
Lara.ExtraVelocity.z = 0;
lara->ExtraVelocity.z -= lara->ExtraVelocity.z >> shift;
if (abs(lara->ExtraVelocity.z) < 4)
lara->ExtraVelocity.z = 0;
if (Lara.ExtraVelocity.x == 0 && Lara.ExtraVelocity.z == 0)
if (lara->ExtraVelocity.x == 0 && lara->ExtraVelocity.z == 0)
return;
}
else
{
long angle, dx, dz, speed, sinkval;
int sinkVal = lara->Control.WaterCurrentActive - 1;
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.0f)) / 16) & 4095;
sinkval = Lara.Control.WaterCurrentActive - 1;
target.x = g_Level.Sinks[sinkval].x;
target.y = g_Level.Sinks[sinkval].y;
target.z = g_Level.Sinks[sinkval].z;
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;
dx = target.x - LaraItem->Position.xPos;
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;
speed = g_Level.Sinks[sinkval].strength;
dx = phd_sin(angle * 16) * speed * 1024;
dz = phd_cos(angle * 16) * speed * 1024;
Lara.ExtraVelocity.x += ((dx - Lara.ExtraVelocity.x) / 16);
Lara.ExtraVelocity.z += ((dz - Lara.ExtraVelocity.z) / 16);
lara->ExtraVelocity.x += ((dx - lara->ExtraVelocity.x) / 16);
lara->ExtraVelocity.z += ((dz - lara->ExtraVelocity.z) / 16);
}
item->Position.xPos += (Lara.ExtraVelocity.x / 256);
item->Position.zPos += (Lara.ExtraVelocity.z / 256);
Lara.Control.WaterCurrentActive = 0;
lara->Control.WaterCurrentActive = 0;
UPVItem->Position.xPos += lara->ExtraVelocity.x / CLICK(1);
UPVItem->Position.zPos += lara->ExtraVelocity.z / CLICK(1);
}
static void BackgroundCollision(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
{
auto* lara = GetLaraInfo(laraItem);
UPVInfo* UPV = UPVItem->Data;
auto* UPV = (UPVInfo*)UPVItem->Data;
COLL_INFO cinfo, * coll = &cinfo; // ??
coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
coll->Setup.UpperFloorBound = -SUB_HEIGHT;
coll->Setup.LowerCeilingBound = SUB_HEIGHT;
coll->Setup.UpperFloorBound = -UPV_HEIGHT;
coll->Setup.LowerCeilingBound = UPV_HEIGHT;
coll->Setup.OldPosition.x = UPVItem->Position.xPos;
coll->Setup.OldPosition.y = UPVItem->Position.yPos;
coll->Setup.OldPosition.z = UPVItem->Position.zPos;
coll->Setup.Radius = SUB_RADIUS;
coll->Setup.Radius = UPV_RADIUS;
coll->Setup.FloorSlopeIsWall = false;
coll->Setup.FloorSlopeIsPit = false;
coll->Setup.DeathFlagIsPit = false;
@ -436,12 +436,18 @@ static void BackgroundCollision(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
coll->Setup.EnableObjectPush = true;
coll->Setup.Mode = CollProbeMode::Quadrants;
if ((UPVItem->Position.xRot >= -16384) && (UPVItem->Position.xRot <= 16384))
coll->Setup.ForwardAngle = lara->Control.MoveAngle = UPVItem->Position.yRot;
if ((UPVItem->Position.xRot >= -(SHRT_MAX / 2 + 1)) && (UPVItem->Position.xRot <= (SHRT_MAX / 2 + 1)))
{
lara->Control.MoveAngle = UPVItem->Position.yRot;
coll->Setup.ForwardAngle = lara->Control.MoveAngle;
}
else
coll->Setup.ForwardAngle = lara->Control.MoveAngle = UPVItem->Position.yRot - ANGLE(180.0f);
{
lara->Control.MoveAngle = UPVItem->Position.yRot - ANGLE(180.0f);
coll->Setup.ForwardAngle = lara->Control.MoveAngle;
}
int height = phd_sin(UPVItem->Position.xRot) * SUB_LENGTH;
int height = phd_sin(UPVItem->Position.xRot) * UPV_LENGTH;
if (height < 0)
height = -height;
if (height < 200)
@ -455,28 +461,28 @@ static void BackgroundCollision(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
if (coll->CollisionType == CT_FRONT)
{
if (UPV->RotX > FRONT_TOLERANCE)
UPV->RotX += WALLDEFLECT;
else if (UPV->RotX < -FRONT_TOLERANCE)
UPV->RotX -= WALLDEFLECT;
if (UPV->XRot > FRONT_TOLERANCE)
UPV->XRot += WALL_DEFLECT;
else if (UPV->XRot < -FRONT_TOLERANCE)
UPV->XRot -= WALL_DEFLECT;
else
{
if (abs(UPV->Vel) >= MAX_VELOCITY)
if (abs(UPV->Velocity) >= MAX_VELOCITY)
{
laraItem->TargetState = UPV_STATE_HIT;
UPV->Vel = -UPV->Vel / 2;
UPV->Velocity = -UPV->Velocity / 2;
}
else
UPV->Vel = 0;
UPV->Velocity = 0;
}
}
else if (coll->CollisionType == CT_TOP)
{
if (UPV->RotX >= -TOP_TOLERANCE)
UPV->RotX -= WALLDEFLECT;
if (UPV->XRot >= -TOP_TOLERANCE)
UPV->XRot -= WALL_DEFLECT;
}
else if (coll->CollisionType == CT_TOP_FRONT)
UPV->Vel = 0;
UPV->Velocity = 0;
else if (coll->CollisionType == CT_LEFT)
UPVItem->Position.yRot += ANGLE(5.0f);
else if (coll->CollisionType == CT_RIGHT)
@ -486,21 +492,21 @@ static void BackgroundCollision(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
UPVItem->Position.xPos = coll->Setup.OldPosition.x;
UPVItem->Position.yPos = coll->Setup.OldPosition.y;
UPVItem->Position.zPos = coll->Setup.OldPosition.z;
UPV->Vel = 0;
UPV->Velocity = 0;
return;
}
if (coll->Middle.Floor < 0)
{
UPVItem->Position.yPos += coll->Middle.Floor;
UPV->RotX += WALLDEFLECT;
UPV->XRot += WALL_DEFLECT;
}
}
static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
static void UPVControl(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
{
auto* lara = GetLaraInfo(laraItem);
UPVInfo* UPV = UPVItem->Data;
auto* UPV = (UPVInfo*)UPVItem->Data;
TestUPVDismount(laraItem, UPVItem);
@ -547,9 +553,9 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
else
{
if (TrInput & UPV_IN_UP)
UPV->RotX -= UPDOWN_ACCEL;
UPV->XRot -= UPDOWN_ACCEL;
else if (TrInput & UPV_IN_DOWN)
UPV->RotX += UPDOWN_ACCEL;
UPV->XRot += UPDOWN_ACCEL;
}
if (TrInput & UPV_IN_PROPEL)
@ -561,7 +567,7 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
UPV->Flags |= UPV_DIVE;
}
UPV->Vel += ACCELERATION;
UPV->Velocity += ACCELERATION;
}
else
@ -605,19 +611,19 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
else
{
if (TrInput & UPV_IN_UP)
UPV->RotX -= UPDOWN_ACCEL;
UPV->XRot -= UPDOWN_ACCEL;
else if (TrInput & UPV_IN_DOWN)
UPV->RotX += UPDOWN_ACCEL;
UPV->XRot += UPDOWN_ACCEL;
}
if (TrInput & UPV_IN_DISMOUNT && TestUPVDismount(laraItem, UPVItem))
{
if (UPV->Vel > 0)
UPV->Vel -= ACCELERATION;
if (UPV->Velocity > 0)
UPV->Velocity -= ACCELERATION;
else
{
if (UPV->Flags & UPV_SURFACE)
laraItem->TargetState = UPV_STATE_DISMOUNT_SURFACE;
laraItem->TargetState = UPV_STATE_DISMOUNT_WATER_SURFACE;
else
laraItem->TargetState = UPV_STATE_DISMOUNT_UNDERWATER;
@ -631,8 +637,8 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
else if (TrInput & UPV_IN_PROPEL)
{
if (TrInput & UPV_IN_UP &&
UPV->Flags & UPV_SURFACE &&
UPVItem->Position.xRot > -DIVE_ANGLE)
UPVItem->Position.xRot > -DIVE_ANGLE &&
UPV->Flags & UPV_SURFACE)
{
UPV->Flags |= UPV_DIVE;
}
@ -670,15 +676,17 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
if (anim == UPV_ANIM_DISMOUNT_UNDERWATER && frame == DISMOUNT_UNDERWATER_FRAME)
{
UPV->Flags &= ~UPV_CONTROL;
PHD_VECTOR vec = { 0, 0, 0 };
GAME_VECTOR VPos, LPos;
PHD_VECTOR vec = { 0, 0, 0 };
GetLaraJointPosition(&vec, LM_HIPS);
GAME_VECTOR LPos;
LPos.x = vec.x;
LPos.y = vec.y;
LPos.z = vec.z;
LPos.roomNumber = UPVItem->RoomNumber;
GAME_VECTOR VPos;
VPos.x = UPVItem->Position.xPos;
VPos.y = UPVItem->Position.yPos;
VPos.z = UPVItem->Position.zPos;
@ -705,12 +713,11 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
break;
case UPV_STATE_DISMOUNT_SURFACE:
case UPV_STATE_DISMOUNT_WATER_SURFACE:
if (anim == UPV_ANIM_DISMOUNT_SURFACE && frame == DISMOUNT_SURFACE_FRAME)
{
UPV->Flags &= ~UPV_CONTROL;
int waterDepth, waterHeight, heightFromWater;
PHD_VECTOR vec = { 0, 0, 0 };
waterDepth = GetWaterSurface(laraItem);
waterHeight = GetWaterHeight(laraItem);
@ -720,6 +727,7 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
else
heightFromWater = NO_HEIGHT;
PHD_VECTOR vec = { 0, 0, 0 };
GetLaraJointPosition(&vec, LM_HIPS);
laraItem->Position.xPos = vec.x;
@ -730,14 +738,15 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
SetAnimation(laraItem, LA_ONWATER_IDLE);
laraItem->VerticalVelocity = 0;
laraItem->Airborne = false;
laraItem->Position.xRot = laraItem->Position.zRot = 0;
laraItem->Position.xRot = 0;
laraItem->Position.zRot = 0;
UpdateItemRoom(laraItem, -LARA_HEIGHT / 2);
ResetLaraFlex(laraItem);
lara->Control.WaterStatus = WaterStatus::TreadWater;
lara->WaterSurfaceDist = -heightFromWater;
lara->Control.Count.Dive = 11;
ResetLaraFlex(laraItem);
lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM;
@ -745,7 +754,7 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
}
else
{
UPV->RotX -= UPDOWN_ACCEL;
UPV->XRot -= UPDOWN_ACCEL;
if (UPVItem->Position.xRot < 0)
UPVItem->Position.xRot = 0;
}
@ -756,7 +765,6 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
if (anim == UPV_ANIM_DEATH && (frame == DEATH_FRAME_1 || frame == DEATH_FRAME_2))
{
PHD_VECTOR vec = { 0, 0, 0 };
GetLaraJointPosition(&vec, LM_HIPS);
laraItem->Position.xPos = vec.x;
@ -767,7 +775,7 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
SetAnimation(UPVItem, LA_UNDERWATER_DEATH, 17);
laraItem->VerticalVelocity = 0;
laraItem->Airborne = 0;
laraItem->Airborne = false;
UPV->Flags |= UPV_DEAD;
}
@ -784,23 +792,23 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
UPV->Flags &= ~UPV_DIVE;
}
if (UPV->Vel > 0)
if (UPV->Velocity > 0)
{
UPV->Vel -= FRICTION;
if (UPV->Vel < 0)
UPV->Vel = 0;
UPV->Velocity -= FRICTION;
if (UPV->Velocity < 0)
UPV->Velocity = 0;
}
else if (UPV->Vel < 0)
else if (UPV->Velocity < 0)
{
UPV->Vel += FRICTION;
if (UPV->Vel > 0)
UPV->Vel = 0;
UPV->Velocity += FRICTION;
if (UPV->Velocity > 0)
UPV->Velocity = 0;
}
if (UPV->Vel > MAX_VELOCITY)
UPV->Vel = MAX_VELOCITY;
else if (UPV->Vel < -MAX_VELOCITY)
UPV->Vel = -MAX_VELOCITY;
if (UPV->Velocity > MAX_VELOCITY)
UPV->Velocity = MAX_VELOCITY;
else if (UPV->Velocity < -MAX_VELOCITY)
UPV->Velocity = -MAX_VELOCITY;
if (UPV->Rot > 0)
{
@ -815,17 +823,17 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
UPV->Rot = 0;
}
if (UPV->RotX > 0)
if (UPV->XRot > 0)
{
UPV->RotX -= UPDOWN_FRICTION;
if (UPV->RotX < 0)
UPV->RotX = 0;
UPV->XRot -= UPDOWN_FRICTION;
if (UPV->XRot < 0)
UPV->XRot = 0;
}
else if (UPV->RotX < 0)
else if (UPV->XRot < 0)
{
UPV->RotX += UPDOWN_FRICTION;
if (UPV->RotX > 0)
UPV->RotX = 0;
UPV->XRot += UPDOWN_FRICTION;
if (UPV->XRot > 0)
UPV->XRot = 0;
}
if (UPV->Rot > MAX_ROTATION)
@ -833,54 +841,53 @@ static void UserInput(ITEM_INFO* laraItem, ITEM_INFO* UPVItem)
else if (UPV->Rot < -MAX_ROTATION)
UPV->Rot = -MAX_ROTATION;
if (UPV->RotX > MAX_UPDOWN)
UPV->RotX = MAX_UPDOWN;
else if (UPV->RotX < -MAX_UPDOWN)
UPV->RotX = -MAX_UPDOWN;
if (UPV->XRot > MAX_UPDOWN)
UPV->XRot = MAX_UPDOWN;
else if (UPV->XRot < -MAX_UPDOWN)
UPV->XRot = -MAX_UPDOWN;
}
void NoGetOnCollision(short itemNum, ITEM_INFO* laraitem, COLL_INFO* coll)
void NoGetOnCollision(short itemNumber, ITEM_INFO* laraItem, COLL_INFO* coll)
{
ITEM_INFO* item = &g_Level.Items[itemNum];
auto* item = &g_Level.Items[itemNumber];
if (!TestBoundsCollide(item, laraitem, coll->Setup.Radius))
if (!TestBoundsCollide(item, laraItem, coll->Setup.Radius))
return;
if (!TestCollision(item, laraitem))
if (!TestCollision(item, laraItem))
return;
ItemPushItem(item, laraitem, coll, 0, 0);
ItemPushItem(item, laraItem, coll, 0, 0);
}
void SubCollision(short itemNum, ITEM_INFO* laraItem, COLL_INFO* coll)
void UPVCollision(short itemNumber, ITEM_INFO* laraItem, COLL_INFO* coll)
{
auto* lara = GetLaraInfo(laraItem);
auto* UPVItem = &g_Level.Items[itemNumber];
if (laraItem->HitPoints <= 0 || lara->Vehicle != NO_ITEM)
return;
ITEM_INFO* UPVItem = &g_Level.Items[itemNum];
if (TestUPVMount(laraItem, UPVItem))
{
lara->Vehicle = itemNum;
lara->Vehicle = itemNumber;
lara->Control.WaterStatus = WaterStatus::Dry;
if (lara->Control.WeaponControl.GunType == WEAPON_FLARE)
{
CreateFlare(LaraItem, ID_FLARE_ITEM, 0);
CreateFlare(laraItem, ID_FLARE_ITEM, 0);
UndrawFlareMeshes(laraItem);
lara->Flare.ControlLeft = false;
lara->Control.WeaponControl.RequestGunType = lara->Control.WeaponControl.GunType = WEAPON_NONE;
}
lara->Control.HandStatus = HandStatus::Busy;
laraItem->Position.xPos = UPVItem->Position.xPos;
laraItem->Position.yPos = UPVItem->Position.yPos;
laraItem->Position.zPos = UPVItem->Position.zPos;
laraItem->Position.xRot = UPVItem->Position.xRot;
laraItem->Position.yRot = UPVItem->Position.yRot;
laraItem->Position.zRot = UPVItem->Position.zRot;
lara->Control.HandStatus = HandStatus::Busy;
UPVItem->HitPoints = 1;
if (laraItem->ActiveState == LS_ONWATER_STOP || laraItem->ActiveState == LS_ONWATER_FORWARD)
@ -893,36 +900,36 @@ void SubCollision(short itemNum, ITEM_INFO* laraItem, COLL_INFO* coll)
laraItem->AnimNumber = Objects[ID_UPV_LARA_ANIMS].animIndex + UPV_ANIM_MOUNT_UNDERWATER;
laraItem->ActiveState = laraItem->TargetState = UPV_STATE_MOUNT;
}
laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase;
laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase;
AnimateItem(laraItem);
}
else
{
UPVItem->Position.yPos += SUB_DRAW_SHIFT;
NoGetOnCollision(itemNum, laraItem, coll);
UPVItem->Position.yPos -= SUB_DRAW_SHIFT;
UPVItem->Position.yPos += UPV_DRAW_SHIFT;
NoGetOnCollision(itemNumber, laraItem, coll);
UPVItem->Position.yPos -= UPV_DRAW_SHIFT;
}
}
bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
bool UPVControl(ITEM_INFO* laraItem, COLL_INFO* coll)
{
auto* lara = GetLaraInfo(laraItem);
ITEM_INFO* UPVItem = &g_Level.Items[lara->Vehicle];
UPVInfo* UPV = UPVItem->Data;
auto* UPVItem = &g_Level.Items[lara->Vehicle];
auto* UPV = (UPVInfo*)UPVItem->Data;
auto oldPos = UPVItem->Position;
auto probe = GetCollisionResult(UPVItem);
if (!(UPV->Flags & UPV_DEAD))
{
UserInput(laraItem, UPVItem);
UPVControl(laraItem, UPVItem);
UPVItem->VerticalVelocity = UPV->Vel / 65536;
UPVItem->VerticalVelocity = UPV->Velocity / (USHRT_MAX + 1);
UPVItem->Position.xRot += UPV->RotX / 65536;
UPVItem->Position.yRot += UPV->Rot / 65536;
UPVItem->Position.zRot = UPV->Rot / 65536;
UPVItem->Position.xRot += UPV->XRot / (USHRT_MAX + 1);
UPVItem->Position.yRot += UPV->Rot / (USHRT_MAX + 1);
UPVItem->Position.zRot = UPV->Rot / (USHRT_MAX + 1);
if (UPVItem->Position.xRot > UPDOWN_LIMIT)
UPVItem->Position.xRot = UPDOWN_LIMIT;
@ -937,7 +944,7 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
int newHeight = GetCollisionResult(UPVItem).Position.Floor;
int waterHeight = GetWaterHeight(UPVItem);
if ((newHeight - waterHeight) < SUB_HEIGHT || (newHeight < UPVItem->Position.yPos - SUB_HEIGHT / 2))
if ((newHeight - waterHeight) < UPV_HEIGHT || (newHeight < UPVItem->Position.yPos - UPV_HEIGHT / 2))
{
UPVItem->Position.xPos = oldPos.xPos;
UPVItem->Position.yPos = oldPos.yPos;
@ -949,7 +956,7 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
if (UPV->Flags & UPV_CONTROL && !(UPV->Flags & UPV_DEAD))
{
if (!(g_Level.Rooms[UPVItem->RoomNumber].flags & ENV_FLAG_WATER) &&
if (!TestEnvironment(ENV_FLAG_WATER, UPVItem->RoomNumber) &&
waterHeight != NO_HEIGHT)
{
if ((waterHeight - UPVItem->Position.yPos) >= -SURFACE_DIST)
@ -957,7 +964,7 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
if (!(UPV->Flags & UPV_SURFACE))
{
SoundEffect(SFX_TR4_LARA_BREATH, &LaraItem->Position, 2);
SoundEffect(SFX_TR4_LARA_BREATH, &laraItem->Position, 2);
UPV->Flags &= ~UPV_DIVE;
}
@ -970,7 +977,7 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
if (!(UPV->Flags & UPV_SURFACE))
{
SoundEffect(SFX_TR4_LARA_BREATH, &LaraItem->Position, 2);
SoundEffect(SFX_TR4_LARA_BREATH, &laraItem->Position, 2);
UPV->Flags &= ~UPV_DIVE;
}
@ -985,7 +992,6 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
if (laraItem->HitPoints > 0)
{
lara->Air--;
if (lara->Air < 0)
{
lara->Air = -1;
@ -998,31 +1004,30 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
if (laraItem->HitPoints >= 0)
{
lara->Air += 10;
if (lara->Air > 1800)
lara->Air = 1800;
if (lara->Air > LARA_AIR_MAX)
lara->Air = LARA_AIR_MAX;
}
}
}
TestTriggers(UPVItem, false);
SubEffects(lara->Vehicle);
UPVEffects(lara->Vehicle);
if (!(UPV->Flags & UPV_DEAD) &&
lara->Vehicle != NO_ITEM)
{
DoCurrent(UPVItem);
DoCurrent(laraItem, UPVItem);
if (TrInput & UPV_IN_FIRE &&
UPV->Flags & UPV_CONTROL &&
!UPV->WeaponTimer)
!UPV->HarpoonTimer)
{
if (laraItem->ActiveState != UPV_STATE_DISMOUNT_UNDERWATER &&
laraItem->ActiveState != UPV_STATE_DISMOUNT_SURFACE &&
laraItem->ActiveState != UPV_STATE_DISMOUNT_WATER_SURFACE &&
laraItem->ActiveState != UPV_STATE_MOUNT)
{
FireSubHarpoon(laraItem, UPVItem);
UPV->WeaponTimer = HARPOON_RELOAD;
FireUPVHarpoon(laraItem, UPVItem);
UPV->HarpoonTimer = HARPOON_RELOAD;
}
}
@ -1043,7 +1048,7 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
BackgroundCollision(laraItem, UPVItem);
if (UPV->Flags & UPV_CONTROL)
SoundEffect(SFX_TR3_LITTLE_SUB_LOOP, (PHD_3DPOS*)&UPVItem->Position.xPos, 2 | 4 | 0x1000000 | (UPVItem->VerticalVelocity * 65536));
SoundEffect(SFX_TR3_LITTLE_SUB_LOOP, (PHD_3DPOS*)&UPVItem->Position.xPos, 2 | 4 | 0x1000000 | (UPVItem->VerticalVelocity * (USHRT_MAX + 1)));
UPVItem->AnimNumber = Objects[ID_UPV].animIndex + (laraItem->AnimNumber - Objects[ID_UPV_LARA_ANIMS].animIndex);
UPVItem->FrameNumber = g_Level.Anims[UPVItem->AnimNumber].frameBase + (laraItem->FrameNumber - g_Level.Anims[laraItem->AnimNumber].frameBase);
@ -1064,11 +1069,10 @@ bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll)
BackgroundCollision(laraItem, UPVItem);
UPV->RotX = 0;
UPV->XRot = 0;
SetAnimation(UPVItem, UPV_ANIM_IDLE);
UPVItem->VerticalVelocity = 0;
UPVItem->VerticalVelocity = 0;
UPVItem->Airborne = true;
AnimateItem(UPVItem);

View file

@ -2,7 +2,7 @@
#include "Game/items.h"
#include "Game/collision/collide_room.h"
void UPVInitialise(short itemNum);
void SubCollision(short itemNum, ITEM_INFO* laraItem, COLL_INFO* coll);
void SubEffects(short itemNum);
bool SubControl(ITEM_INFO* laraItem, COLL_INFO* coll);
void UPVInitialise(short itemNumber);
void UPVCollision(short itemNumber, ITEM_INFO* laraItem, COLL_INFO* coll);
void UPVEffects(short itemNumber);
bool UPVControl(ITEM_INFO* laraItem, COLL_INFO* coll);

View file

@ -2,10 +2,12 @@
struct UPVInfo
{
int Vel;
int Velocity;
int Rot;
int RotX;
int XRot;
short FanRot;
unsigned int HarpoonTimer;
bool HarpoonLeft;
char Flags;
char WeaponTimer;
};

View file

@ -445,8 +445,8 @@ static void StartVehicles(OBJECT_INFO* obj)
if (obj->loaded)
{
obj->initialise = UPVInitialise;
obj->control = SubEffects;
obj->collision = SubCollision;
obj->control = UPVEffects;
obj->collision = UPVCollision;
// obj->drawRoutine = SubDraw;
obj->hitEffect = HIT_RICOCHET;
obj->saveAnim = true;