TombEngine/TR5Main/Game/lara1gun.cpp

1953 lines
54 KiB
C++

#include "framework.h"
#include "lara1gun.h"
#include "items.h"
#include "Lara.h"
#include "larafire.h"
#include "draw.h"
#include "box.h"
#include "control.h"
#include "effect.h"
#include "effect2.h"
#include "tomb4fx.h"
#include "lot.h"
#include "collide.h"
#include "debris.h"
#include "lara2gun.h"
#include "switch.h"
#include "objects.h"
#include "sphere.h"
#include "traps.h"
#include "camera.h"
#include "GameFlowScript.h"
#include "level.h"
#include "setup.h"
#include "input.h"
#include "savegame.h"
#include "sound.h"
#include "bubble.h"
extern GameFlow* g_GameFlow;
int HKCounter = 0;
int HKTimer = 0;
int HKFlag = 0;
byte HKFlag2 = 0;
void FireHarpoon()
{
short* ammos = GetAmmo(WEAPON_CROSSBOW);
if (*ammos != 0)
{
Lara.hasFired = true;
// Create a new item for harpoon
short itemNumber = CreateItem();
if (itemNumber != NO_ITEM)
{
if (*ammos != -1)
(*ammos)--;
GAME_VECTOR pos;
ITEM_INFO* item = &g_Level.Items[itemNumber];
item->shade = 0x4210 | 0x8000;
item->objectNumber = ID_HARPOON;
item->roomNumber = LaraItem->roomNumber;
PHD_VECTOR jointPos;
jointPos.x = -2;
jointPos.y = 273 + 100;
jointPos.z = 77;
GetLaraJointPosition(&jointPos, LM_RHAND);
FLOOR_INFO* floor = GetFloor(jointPos.x, jointPos.y, jointPos.z, &item->roomNumber);
int height = GetFloorHeight(floor, jointPos.x, jointPos.y, jointPos.z);
if (height >= jointPos.y)
{
item->pos.xPos = jointPos.x;
item->pos.yPos = jointPos.y;
item->pos.zPos = jointPos.z;
}
else
{
item->pos.xPos = LaraItem->pos.xPos;
item->pos.yPos = jointPos.y;
item->pos.zPos = LaraItem->pos.zPos;
item->roomNumber = LaraItem->roomNumber;
}
InitialiseItem(itemNumber);
item->pos.xRot = Lara.leftArm.xRot + LaraItem->pos.xRot;
item->pos.zRot = 0;
item->pos.yRot = Lara.leftArm.yRot + LaraItem->pos.yRot;
if (!Lara.leftArm.lock)
{
item->pos.xRot += Lara.torsoXrot;
item->pos.yRot += Lara.torsoYrot;
}
item->pos.zRot = 0;
item->fallspeed = (short)(-HARPOON_SPEED * phd_sin(item->pos.xRot) >> W2V_SHIFT);
item->speed = (short)(HARPOON_SPEED * phd_cos(item->pos.xRot) >> W2V_SHIFT);
item->hitPoints = HARPOON_TIME;
AddActiveItem(itemNumber);
Savegame.Level.AmmoUsed++;
Savegame.Game.AmmoUsed++;
}
}
}
void ControlHarpoonBolt(short itemNumber)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
// Store old position for later
int oldX = item->pos.xPos;
int oldY = item->pos.yPos;
int oldZ = item->pos.zPos;
short roomNumber = item->roomNumber;
/*if (item->pos.yPos >= item->floor || item->pos.yPos <= GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos))
{
if (item->hitPoints == HARPOON_TIME)
{
item->currentAnimState = item->pos.xRot;
}
if (item->hitPoints >= 192)
{
item->pos.xRot = item->currentAnimState + ((((phd_sin(item->hitPoints * 2048) / 8) - 1024) * (item->hitPoints - 192)) / 64);
item->hitPoints--;
}
item->hitPoints--;
if (!item->hitPoints)
{
KillItem(itemNumber);
return;
}
item->speed = item->fallspeed = 0;
}
else
{
item->pos.zRot += ANGLE(35);
if (!(g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER))
{
item->pos.xRot -= (ANGLE(1));
if (item->pos.xRot < -ANGLE(90))
item->pos.xRot = -ANGLE(90);
item->fallspeed = (short)(-HARPOON_SPEED * phd_sin(item->pos.xRot) >> W2V_SHIFT);
item->speed = (short)(HARPOON_SPEED * phd_cos(item->pos.xRot) >> W2V_SHIFT);
}
else
{
if ((Wibble & 15) == 0)
CreateBubble((PHD_VECTOR*)&item->pos, item->roomNumber, 2, 8);
TriggerRocketSmoke(item->pos.xPos, item->pos.yPos, item->pos.zPos, 64);
item->fallspeed = (short)(-(HARPOON_SPEED >> 1) * phd_sin(item->pos.xRot) >> W2V_SHIFT);
item->speed = (short)((HARPOON_SPEED >> 1) * phd_cos(item->pos.xRot) >> W2V_SHIFT);
}
}*/
bool aboveWater = false;
// Update speed and check if above water
item->pos.zRot += ANGLE(35);
if (!(g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER))
{
item->pos.xRot -= ANGLE(1);
if (item->pos.xRot < -16384)
item->pos.xRot = -16384;
item->fallspeed = (short)(-HARPOON_SPEED * phd_sin(item->pos.xRot) >> W2V_SHIFT);
item->speed = (short)(HARPOON_SPEED * phd_cos(item->pos.xRot) >> W2V_SHIFT);
aboveWater = true;
}
else
{
// Create bubbles
if ((Wibble & 15) == 0)
CreateBubble((PHD_VECTOR*)& item->pos, item->roomNumber, 0, 0, BUBBLE_FLAG_CLUMP | BUBBLE_FLAG_HIGH_AMPLITUDE, 0, 0, 0); // CHECK
TriggerRocketSmoke(item->pos.xPos, item->pos.yPos, item->pos.zPos, 64);
item->fallspeed = (short)(-(HARPOON_SPEED >> 1) * phd_sin(item->pos.xRot) >> W2V_SHIFT);
item->speed = (short)((HARPOON_SPEED >> 1) * phd_cos(item->pos.xRot) >> W2V_SHIFT);
aboveWater = false;
}
// Update bolt's position
item->pos.xPos += ((item->speed * phd_cos(item->pos.xRot) >> W2V_SHIFT) * phd_sin(item->pos.yRot)) >> W2V_SHIFT;
item->pos.yPos += item->speed * phd_sin(-item->pos.xRot) >> W2V_SHIFT;
item->pos.zPos += ((item->speed * phd_cos(item->pos.xRot) >> W2V_SHIFT) * phd_cos(item->pos.yRot)) >> W2V_SHIFT;
roomNumber = item->roomNumber;
FLOOR_INFO * floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
// Check if bolt has hit a solid wall
if (GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) < item->pos.yPos ||
GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) > item->pos.yPos)
{
// I have hit a solid wall, this is the end for the bolt
item->pos.xPos = oldX;
item->pos.yPos = oldY;
item->pos.zPos = oldZ;
ExplodeItemNode(item, 0, 0, EXPLODE_NORMAL);
KillItem(itemNumber);
return;
}
// Has harpoon changed room?
if (item->roomNumber != roomNumber)
ItemNewRoom(itemNumber, roomNumber);
// If now in water and before in land, add a ripple
if ((g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER) && aboveWater)
{
SetupRipple(item->pos.xPos, g_Level.Rooms[item->roomNumber].minfloor, item->pos.zPos, (GetRandomControl() & 7) + 8, 0);
}
int n = 0;
bool foundCollidedObjects = false;
// Found possible collided items and statics
GetCollidedObjects(item, HARPOON_HIT_RADIUS, 1, &CollidedItems[0], &CollidedMeshes[0], 1);
// If no collided items and meshes are found, then exit the loop
if (!CollidedItems[0] && !CollidedMeshes[0])
return;
foundCollidedObjects = true;
if (CollidedItems[0])
{
ITEM_INFO* currentItem = CollidedItems[0];
OBJECT_INFO* currentObj = &Objects[currentItem->objectNumber];
int k = 0;
do
{
if ((currentObj->intelligent && currentObj->collision && currentItem->status == ITEM_ACTIVE)
|| currentItem->objectNumber == ID_LARA
|| (currentItem->flags & 0x40 &&
(Objects[currentItem->objectNumber].explodableMeshbits || currentItem == LaraItem)))
{
// All active intelligent creatures explode, if their HP is <= 0
// Explosion is handled by CreatureDie()
// Also Lara can be damaged
// HitTarget() is called inside this
DoExplosiveDamageOnBaddie(currentItem, item, WEAPON_CROSSBOW);
}
else if (currentItem->objectNumber >= ID_SMASH_OBJECT1 && currentItem->objectNumber <= ID_SMASH_OBJECT8)
{
// Smash objects are legacy objects from TRC, let's make them explode in the legacy way
TriggerExplosionSparks(currentItem->pos.xPos, currentItem->pos.yPos, currentItem->pos.zPos, 3, -2, 0, currentItem->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentItem->pos.xPos, currentItem->pos.yPos - 128, currentItem->pos.zPos), 48, 304, 96, 0, 96, 128, 24, 0, 0);
ExplodeItemNode(currentItem, 0, 0, 128);
short currentItemNumber = (currentItem - CollidedItems[0]);
SmashObject(currentItemNumber);
KillItem(currentItemNumber);
}
// TODO_LUA: we need to handle it with an event like OnDestroy
/*else if (currentObj->hitEffect == HIT_SPECIAL)
{
// Some objects need a custom behaviour
//HitSpecial(item, currentItem, 1);
}*/
// All other items (like puzzles) don't explode
k++;
currentItem = CollidedItems[k];
} while (currentItem);
}
if (CollidedMeshes[0])
{
MESH_INFO* currentMesh = CollidedMeshes[0];
int k = 0;
do
{
STATIC_INFO* s = &StaticObjects[currentMesh->staticNumber];
if (s->shatterType != SHT_NONE)
{
currentMesh->hitPoints -= Weapons[WEAPON_CROSSBOW].damage;
if (currentMesh->hitPoints <= 0)
{
TriggerExplosionSparks(currentMesh->x, currentMesh->y, currentMesh->z, 3, -2, 0, item->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentMesh->x, currentMesh->y - 128, currentMesh->z, 0, currentMesh->yRot, 0), 40, 176, 64, 0, 96, 128, 16, 0, 0);
ShatterObject((SHATTER_ITEM*)item, NULL, -128, item->roomNumber, 0); // TODO: this wont work !!
SmashedMeshRoom[SmashedMeshCount] = item->roomNumber;
SmashedMesh[SmashedMeshCount] = currentMesh;
SmashedMeshCount++;
currentMesh->flags &= ~1;
}
}
k++;
currentMesh = CollidedMeshes[k];
} while (currentMesh);
}
// If harpoon has hit some objects then shatter itself
if (foundCollidedObjects)
{
ExplodeItemNode(item, 0, 0, EXPLODE_NORMAL);
KillItem(itemNumber);
}
}
long tbx, tby, tbz;
void FireGrenade()
{
int x = 0;
int y = 0;
int z = 0;
short* ammo = GetAmmo(WEAPON_GRENADE_LAUNCHER);
if (*ammo != 0)
{
Lara.hasFired = true;
short itemNumber = CreateItem();
if (itemNumber != NO_ITEM)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
item->shade = 0xC210;
item->objectNumber = ID_GRENADE;
item->roomNumber = LaraItem->roomNumber;
PHD_VECTOR jointPos;
jointPos.x = 0;
jointPos.y = 276;
jointPos.z = 80;
GetLaraJointPosition(&jointPos, LM_RHAND);
item->pos.xPos = x = jointPos.x;
item->pos.yPos = y = jointPos.y;
item->pos.zPos = z = jointPos.z;
FLOOR_INFO* floor = GetFloor(jointPos.x, jointPos.y, jointPos.z, &item->roomNumber);
int height = GetFloorHeight(floor, jointPos.x, jointPos.y, jointPos.z);
if (height < jointPos.y)
{
item->pos.xPos = LaraItem->pos.xPos;
item->pos.yPos = jointPos.y;
item->pos.zPos = LaraItem->pos.zPos;
item->roomNumber = LaraItem->roomNumber;
}
jointPos.x = 0;
jointPos.y = 1204;
jointPos.z = 5;
GetLaraJointPosition(&jointPos, LM_RHAND);
SmokeCountL = 32;
SmokeWeapon = WEAPON_GRENADE_LAUNCHER;
if (LaraItem->meshBits)
{
for (int i = 0; i < 5; i++)
TriggerGunSmoke(x, y, z, jointPos.x - x, jointPos.y - y, jointPos.z - z, 1, WEAPON_GRENADE_LAUNCHER, 32);
}
InitialiseItem(itemNumber);
item->pos.xRot = LaraItem->pos.xRot + Lara.leftArm.xRot;
item->pos.yRot = LaraItem->pos.yRot + Lara.leftArm.yRot;
item->pos.zRot = 0;
if (!Lara.leftArm.lock)
{
item->pos.xRot += Lara.torsoXrot;
item->pos.yRot += Lara.torsoYrot;
}
item->speed = GRENADE_SPEED;
item->fallspeed = (-512 * phd_sin(item->pos.xRot)) >> W2V_SHIFT;
item->currentAnimState = item->pos.xRot;
item->goalAnimState = item->pos.yRot;
item->requiredAnimState = 0;
item->hitPoints = 120;
item->itemFlags[0] = WEAPON_AMMO2;
AddActiveItem(itemNumber);
if (*ammo != -1)
(*ammo)--;
item->itemFlags[0] = Lara.Weapons[WEAPON_GRENADE_LAUNCHER].SelectedAmmo;
Savegame.Level.AmmoUsed++;
Savegame.Game.AmmoUsed++;
}
}
}
enum GRENADE_TYPE
{
GRENADE_NORMAL,
GRENADE_SUPER,
GRENADE_FLASH,
GRENADE_ULTRA,
GRENADE_FLAGS
};
void ControlGrenade(short itemNumber)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
if (item->itemFlags[1])
{
item->itemFlags[1]--;
if (item->itemFlags[1])
{
if (item->itemFlags[0] == GRENADE_FLASH)
{
// Flash grenades
if (item->itemFlags[1] == 1)
{
WeaponEnemyTimer = 120;
FlashFadeR = 255;
FlashFadeG = 255;
FlashFadeB = 255;
}
else
{
FlashFadeR = (GetRandomControl() & 0x1F) + 224;
FlashFadeG = FlashFadeB = FlashFadeR - GetRandomControl() & 0x1F;
}
FlashFader = 32;
GrenadeExplosionEffects(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
GrenadeExplosionEffects(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
}
else
{
// Trigger a new grenade in the case of GRENADE_SUPER until itemFlags[1] is > 0
short newGrenadeItemNumber = CreateItem();
if (newGrenadeItemNumber != NO_ITEM)
{
ITEM_INFO* newGrenade = &g_Level.Items[newGrenadeItemNumber];
newGrenade->shade = 0xC210;
newGrenade->objectNumber = ID_GRENADE;
newGrenade->roomNumber = item->roomNumber;
newGrenade->pos.xPos = (GetRandomControl() & 0x1FF) + item->pos.xPos - 256;
newGrenade->pos.yPos = item->pos.yPos - 256;
newGrenade->pos.zPos = (GetRandomControl() & 0x1FF) + item->pos.zPos - 256;
InitialiseItem(newGrenadeItemNumber);
newGrenade->pos.xRot = (GetRandomControl() & 0x3FFF) + ANGLE(45);
newGrenade->pos.yRot = GetRandomControl() * 2;
newGrenade->pos.zRot = 0;
newGrenade->speed = 64;
newGrenade->fallspeed = -64 * phd_sin(newGrenade->pos.xRot) >> W2V_SHIFT;
newGrenade->currentAnimState = newGrenade->pos.xRot;
newGrenade->goalAnimState = newGrenade->pos.yRot;
newGrenade->requiredAnimState = 0;
AddActiveItem(newGrenadeItemNumber);
newGrenade->status = ITEM_INVISIBLE;
newGrenade->itemFlags[2] = item->itemFlags[2];
newGrenade->hitPoints = 60; // 3000;
newGrenade->itemFlags[0] = GRENADE_NORMAL;
if (g_Level.Rooms[newGrenade->roomNumber].flags & ENV_FLAG_WATER)
newGrenade->hitPoints = 1;
}
}
return;
}
KillItem(itemNumber);
return;
}
// Store old position for later
int oldX = item->pos.xPos;
int oldY = item->pos.yPos;
int oldZ = item->pos.zPos;
int xv;
int yv;
int zv;
item->shade = 0xC210;
// Check if above water and update speed and fallspeed
bool aboveWater = false;
if (g_Level.Rooms[item->roomNumber].flags & 1)
{
aboveWater = false;
item->fallspeed += (5 - item->fallspeed) >> 1;
item->speed -= item->speed >> 2;
if (item->speed)
{
item->pos.zRot += (((item->speed >> 4) + 3) * ANGLE(1));
if (item->requiredAnimState)
item->pos.yRot += (((item->speed >> 2) + 3) * ANGLE(1));
else
item->pos.xRot += (((item->speed >> 2) + 3) * ANGLE(1));
}
}
else
{
aboveWater = true;
item->fallspeed += 3;
if (item->speed)
{
item->pos.zRot += (((item->speed >> 2) + 7) * ANGLE(1));
if (item->requiredAnimState)
item->pos.yRot += (((item->speed >> 1) + 7) * ANGLE(1));
else
item->pos.xRot += (((item->speed >> 1) + 7) * ANGLE(1));
}
}
// Trigger fire and smoke sparks in the direction of motion
if (item->speed && aboveWater)
{
Matrix world = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->pos.yRot - ANGLE(180)),
TO_RAD(item->pos.xRot),
TO_RAD(item->pos.zRot)
) * Matrix::CreateTranslation(0, 0, -64);
int wx = world.Translation().x;
int wy = world.Translation().y;
int wz = world.Translation().z;
TriggerRocketSmoke(wx + item->pos.xPos, wy + item->pos.yPos, wz + item->pos.zPos, -1);
TriggerRocketFire(wx + item->pos.xPos, wy + item->pos.yPos, wz + item->pos.zPos);
}
// Update grenade position
xv = ((item->speed * phd_sin(item->goalAnimState)) >> W2V_SHIFT);
yv = item->fallspeed;
zv = ((item->speed * phd_cos(item->goalAnimState)) >> W2V_SHIFT);
item->pos.xPos += xv;
item->pos.yPos += yv;
item->pos.zPos += zv;
FLOOR_INFO* floor;
int height;
int ceiling;
short roomNumber;
// Never implemented in original game
if (item->itemFlags[0] == GRENADE_ULTRA)
{
roomNumber = item->roomNumber;
floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
height = GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
ceiling = GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (height < item->pos.yPos || ceiling > item->pos.yPos)
item->hitPoints = 1;
}
else
{
// Do grenade's physics
short sYrot = item->pos.yRot;
item->pos.yRot = item->goalAnimState;
DoProperDetection(itemNumber, oldX, oldY, oldZ, xv, yv, zv);
item->goalAnimState = item->pos.yRot;
item->pos.yRot = sYrot;
}
roomNumber = item->roomNumber;
floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
if (item->itemFlags[0] == GRENADE_ULTRA)
GrenadeLauncherSpecialEffect1(item->pos.xPos, item->pos.yPos, item->pos.zPos, -1, 1);
// Check if it's time to explode
int radius = 0;
bool explode = false;
if (item->hitPoints)
{
item->hitPoints--;
if (item->hitPoints)
{
if (item->hitPoints > 118)
{
return;
}
}
else
{
radius = 2048;
explode = 1;
}
}
// If is not a flash grenade then try to destroy surrounding objects
if (!(item->itemFlags[0] == GRENADE_FLASH && explode))
{
int radius = (explode ? GRENADE_EXPLODE_RADIUS : GRENADE_HIT_RADIUS);
bool foundCollidedObjects = false;
for (int n = 0; n < 2; n++)
{
// Step 0: check for specific collision in a small radius
// Step 1: done only if explosion, try to smash all objects in the blast radius
// Found possible collided items and statics
GetCollidedObjects(item, radius, 1, &CollidedItems[0], &CollidedMeshes[0], false);
// If no collided items and meshes are found, then exit the loop
if (!CollidedItems[0] && !CollidedMeshes[0])
break;
foundCollidedObjects = true;
if (item->itemFlags[0] != GRENADE_FLASH || explode)
{
if (CollidedItems[0])
{
if (explode)
{
ITEM_INFO* currentItem = CollidedItems[0];
OBJECT_INFO* currentObj = &Objects[currentItem->objectNumber];
int k = 0;
do
{
if ((currentObj->intelligent && currentObj->collision && currentItem->status == ITEM_ACTIVE)
|| currentItem->objectNumber == ID_LARA
|| (currentItem->flags & 0x40 &&
(Objects[currentItem->objectNumber].explodableMeshbits || currentItem == LaraItem)))
{
// All active intelligent creatures explode, if their HP is <= 0
// Explosion is handled by CreatureDie()
// Also Lara can be damaged
// HitTarget() is called inside this
DoExplosiveDamageOnBaddie(currentItem, item, WEAPON_GRENADE_LAUNCHER);
}
else if (currentItem->objectNumber >= ID_SMASH_OBJECT1 && currentItem->objectNumber <= ID_SMASH_OBJECT8)
{
// Smash objects are legacy objects from TRC, let's make them explode in the legacy way
TriggerExplosionSparks(currentItem->pos.xPos, currentItem->pos.yPos, currentItem->pos.zPos, 3, -2, 0, currentItem->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentItem->pos.xPos, currentItem->pos.yPos - 128, currentItem->pos.zPos), 48, 304, 96, 0, 96, 128, 24, 0, 0);
ExplodeItemNode(currentItem, 0, 0, 128);
short currentItemNumber = (currentItem - CollidedItems[0]);
SmashObject(currentItemNumber);
KillItem(currentItemNumber);
}
// TODO_LUA: we need to handle it with an event like OnDestroy
/*else if (currentObj->hitEffect == HIT_SPECIAL)
{
// Some objects need a custom behaviour
//HitSpecial(item, currentItem, 1);
}*/
// All other items (like puzzles) don't explode
k++;
currentItem = CollidedItems[k];
} while (currentItem);
}
if (CollidedMeshes[0])
{
MESH_INFO* currentMesh = CollidedMeshes[0];
int k = 0;
do
{
STATIC_INFO* s = &StaticObjects[currentMesh->staticNumber];
if (s->shatterType != SHT_NONE)
{
currentMesh->hitPoints -= Weapons[WEAPON_GRENADE_LAUNCHER].damage;
if (currentMesh->hitPoints <= 0)
{
TriggerExplosionSparks(currentMesh->x, currentMesh->y, currentMesh->z, 3, -2, 0, item->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentMesh->x, currentMesh->y - 128, currentMesh->z, 0, currentMesh->yRot, 0), 40, 176, 64, 0, 96, 128, 16, 0, 0);
ShatterObject((SHATTER_ITEM*)item, NULL, -128, item->roomNumber, 0); // TODO: this wont work !!
SmashedMeshRoom[SmashedMeshCount] = item->roomNumber;
SmashedMesh[SmashedMeshCount] = currentMesh;
SmashedMeshCount++;
currentMesh->flags &= ~1;
}
}
k++;
currentMesh = CollidedMeshes[k];
} while (currentMesh);
}
}
}
explode = true;
radius = GRENADE_EXPLODE_RADIUS;
}
}
// Handle explosion effects
if (explode || (item->itemFlags[0] == GRENADE_FLASH && explode))
{
if (item->itemFlags[0] == GRENADE_FLASH)
{
FlashFader = 32;
FlashFadeR = 255;
FlashFadeG = 255;
FlashFadeB = 255;
GrenadeExplosionEffects(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
GrenadeExplosionEffects(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
}
else if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
{
TriggerUnderwaterExplosion(item, 0);
}
else
{
item->pos.yPos -= 128;
TriggerShockwave(&item->pos, 48, 304, 96, 0, 96, 128, 24, 0, 0);
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, item->roomNumber);
for (int x = 0; x < 2; x++)
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -1, 0, item->roomNumber);
}
AlertNearbyGuards(item);
SoundEffect(SFX_EXPLOSION1, &item->pos, PITCH_SHIFT | 0x1800000);
SoundEffect(SFX_EXPLOSION2, &item->pos, 0);
// Setup the counter for spawned grenades in the case of flash and super grenades ammos
if (item->itemFlags[0] != GRENADE_NORMAL && item->itemFlags[0] != GRENADE_ULTRA)
{
item->meshBits = 0;
item->itemFlags[1] = (item->itemFlags[0] != GRENADE_SUPER ? 16 : 4);
return;
}
KillItem(itemNumber);
return;
}
}
void ControlRocket(short itemNumber)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
// Save old position for later
short oldroom = item->roomNumber;
int oldx = item->pos.xPos;
int oldy = item->pos.yPos;
int oldz = item->pos.zPos;
// Update speed and rotation and check if above water or underwater
bool abovewater = false;
if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
{
if (item->speed > ROCKET_SPEED / 4)
item->speed -= (item->speed / 4);
else
{
item->speed += (item->speed / 4) + 4;
if (item->speed > ROCKET_SPEED / 4)
item->speed = ROCKET_SPEED / 4;
}
item->pos.zRot += (((item->speed / 8) + 3) * ANGLE(1));
abovewater = false;
}
else
{
if (item->speed < ROCKET_SPEED)
item->speed += (item->speed / 4) + 4;
item->pos.zRot += (((item->speed / 4) + 7) * ANGLE(1));
abovewater = true;
}
item->shade = 0x4210 | 0x8000;
// Calculate offset in rocket direction for fire and smoke sparks
Matrix world = Matrix::CreateFromYawPitchRoll(
TO_RAD(item->pos.yRot - ANGLE(180)),
TO_RAD(item->pos.xRot),
TO_RAD(item->pos.zRot)
) * Matrix::CreateTranslation(0, 0, -64);
int wx = world.Translation().x;
int wy = world.Translation().y;
int wz = world.Translation().z;
// Trigger fire, smoke and lighting
TriggerRocketSmoke(wx + item->pos.xPos, wy + item->pos.yPos, wz + item->pos.zPos, -1);
TriggerRocketFire(wx + item->pos.xPos, wy + item->pos.yPos, wz + item->pos.zPos);
TriggerDynamicLight(wx + item->pos.xPos + (GetRandomControl() & 15) - 8, wy + item->pos.yPos + (GetRandomControl() & 15) - 8, wz + item->pos.zPos + (GetRandomControl() & 15) - 8, 14, 28 + (GetRandomControl() & 3), 16 + (GetRandomControl() & 7), (GetRandomControl() & 7));
// If underwater generate bubbles
if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
{
PHD_VECTOR pos;
pos.x = wx + item->pos.xPos;
pos.y = wy + item->pos.yPos;
pos.z = wz + item->pos.zPos;
CreateBubble(&pos, item->roomNumber, 4, 8, 0, 0, 0, 0);
}
// Update rocket's position
short speed = (item->speed * phd_cos(item->pos.xRot)) >> W2V_SHIFT;
item->pos.xPos += (speed * phd_sin(item->pos.yRot)) >> W2V_SHIFT;
item->pos.yPos += -((item->speed * phd_sin(item->pos.xRot)) >> W2V_SHIFT);
item->pos.zPos += (speed * phd_cos(item->pos.yRot)) >> W2V_SHIFT;
bool explode = false;
// Check if solid wall and then decide if explode or not
short roomNumber = item->roomNumber;
FLOOR_INFO * floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
if (GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) < item->pos.yPos ||
GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) > item->pos.yPos)
{
item->pos.xPos = oldx;
item->pos.yPos = oldy;
item->pos.zPos = oldz;
explode = true;
}
// Has bolt changed room?
if (item->roomNumber != roomNumber)
ItemNewRoom(itemNumber, roomNumber);
// If now in water and before in land, add a ripple
if ((g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER) && abovewater)
SetupRipple(item->pos.xPos, g_Level.Rooms[item->roomNumber].minfloor, item->pos.zPos, (GetRandomControl() & 7) + 8, 0);
int radius = (explode ? ROCKET_EXPLODE_RADIUS : ROCKET_HIT_RADIUS);
bool foundCollidedObjects = false;
for (int n = 0; n < 2; n++)
{
// Step 0: check for specific collision in a small radius
// Step 1: done only if explosion, try to smash all objects in the blast radius
// Found possible collided items and statics
GetCollidedObjects(item, radius, 1, &CollidedItems[0], &CollidedMeshes[0], true);
// If no collided items and meshes are found, then exit the loop
if (!CollidedItems[0] && !CollidedMeshes[0])
break;
if (CollidedItems[0])
{
ITEM_INFO* currentItem = CollidedItems[0];
OBJECT_INFO* currentObj = &Objects[currentItem->objectNumber];
int k = 0;
do
{
if ((currentObj->intelligent && currentObj->collision && currentItem->status == ITEM_ACTIVE)
|| currentItem->objectNumber == ID_LARA
|| (currentItem->flags & 0x40 &&
(Objects[currentItem->objectNumber].explodableMeshbits || currentItem == LaraItem)))
{
// All active intelligent creatures explode, if their HP is <= 0
// Explosion is handled by CreatureDie()
// Also Lara can be damaged
// HitTarget() is called inside this
DoExplosiveDamageOnBaddie(currentItem, item, WEAPON_ROCKET_LAUNCHER);
}
else if (currentItem->objectNumber >= ID_SMASH_OBJECT1 && currentItem->objectNumber <= ID_SMASH_OBJECT8)
{
// Smash objects are legacy objects from TRC, let's make them explode in the legacy way
TriggerExplosionSparks(currentItem->pos.xPos, currentItem->pos.yPos, currentItem->pos.zPos, 3, -2, 0, currentItem->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentItem->pos.xPos, currentItem->pos.yPos - 128, currentItem->pos.zPos), 48, 304, 96, 0, 96, 128, 24, 0, 0);
ExplodeItemNode(currentItem, 0, 0, 128);
short currentItemNumber = (currentItem - CollidedItems[0]);
SmashObject(currentItemNumber);
KillItem(currentItemNumber);
}
// TODO_LUA: we need to handle it with an event like OnDestroy
/*else if (currentObj->hitEffect == HIT_SPECIAL)
{
// Some objects need a custom behaviour
//HitSpecial(item, currentItem, 1);
}*/
// All other items (like puzzles) don't explode
k++;
currentItem = CollidedItems[k];
} while (currentItem);
}
if (CollidedMeshes[0])
{
MESH_INFO* currentMesh = CollidedMeshes[0];
int k = 0;
do
{
STATIC_INFO* s = &StaticObjects[currentMesh->staticNumber];
if (s->shatterType != SHT_NONE)
{
currentMesh->hitPoints -= Weapons[WEAPON_ROCKET_LAUNCHER].damage;
if (currentMesh->hitPoints <= 0)
{
TriggerExplosionSparks(currentMesh->x, currentMesh->y, currentMesh->z, 3, -2, 0, item->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentMesh->x, currentMesh->y - 128, currentMesh->z, 0, currentMesh->yRot, 0), 40, 176, 64, 0, 96, 128, 16, 0, 0);
ShatterObject((SHATTER_ITEM*)item, NULL, -128, item->roomNumber, 0); // TODO: this wont work !!
SmashedMeshRoom[SmashedMeshCount] = item->roomNumber;
SmashedMesh[SmashedMeshCount] = currentMesh;
SmashedMeshCount++;
currentMesh->flags &= ~1;
}
}
k++;
currentMesh = CollidedMeshes[k];
} while (currentMesh);
}
explode = true;
radius = ROCKET_EXPLODE_RADIUS;
}
// Do explosion if needed
if (explode)
{
if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
TriggerUnderwaterExplosion(item, 0);
else
{
TriggerShockwave(&item->pos, 48, 304, 96, 0, 96, 128, 24, 0, 0);
item->pos.yPos += 128;
TriggerExplosionSparks(oldx, oldy, oldz, 3, -2, 0, item->roomNumber);
for (int j = 0; j < 2; j++)
TriggerExplosionSparks(oldx, oldy, oldz, 3, -1, 0, item->roomNumber);
}
AlertNearbyGuards(item);
SoundEffect(SFX_EXPLOSION1, &item->pos, PITCH_SHIFT | 0x1800000);
SoundEffect(SFX_EXPLOSION2, &item->pos, 0);
ExplodeItemNode(item, 0, 0, EXPLODE_NORMAL);
KillItem(itemNumber);
}
}
void draw_shotgun(int weaponType)
{
ITEM_INFO* item;
if (Lara.weaponItem == NO_ITEM)
{
Lara.weaponItem = CreateItem();
item = &g_Level.Items[Lara.weaponItem];
item->objectNumber = WeaponObject(weaponType);
if (weaponType == WEAPON_ROCKET_LAUNCHER)
item->animNumber = Objects[item->objectNumber].animIndex + 1;
else if (weaponType == WEAPON_GRENADE_LAUNCHER)
item->animNumber = Objects[item->objectNumber].animIndex + 0;
else
item->animNumber = Objects[item->objectNumber].animIndex + 1;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = WSTATE_DRAW;
item->currentAnimState = WSTATE_DRAW;
item->status = ITEM_ACTIVE;
item->roomNumber = NO_ROOM;
Lara.rightArm.frameBase = Objects[item->objectNumber].frameBase;
Lara.leftArm.frameBase = Lara.rightArm.frameBase;
}
else
{
item = &g_Level.Items[Lara.weaponItem];
}
AnimateItem(item);
if (item->currentAnimState != 0 && item->currentAnimState != 6)
{
if (item->frameNumber - g_Level.Anims[item->animNumber].frameBase == Weapons[weaponType].drawFrame)
draw_shotgun_meshes(weaponType);
else if (Lara.waterStatus == 1)
item->goalAnimState = 6;
}
else
{
ready_shotgun(weaponType);
}
Lara.leftArm.frameBase = Lara.rightArm.frameBase = g_Level.Anims[item->animNumber].framePtr;
Lara.leftArm.frameNumber = Lara.rightArm.frameNumber = item->frameNumber - g_Level.Anims[item->animNumber].frameBase;
Lara.leftArm.animNumber = Lara.rightArm.animNumber = item->animNumber;
}
void AnimateShotgun(int weaponType)
{
if (HKTimer)
{
HKFlag = 0;
HKTimer--;
}
if (SmokeCountL)
{
PHD_VECTOR pos;
if (SmokeWeapon == WEAPON_HK)
{
pos.x = 0;
pos.y = 228;
pos.z = 96;
}
else if (SmokeWeapon == WEAPON_SHOTGUN)
{
pos.x = 0;
pos.y = 228;
pos.z = 0;
}
else if (SmokeWeapon == WEAPON_GRENADE_LAUNCHER)
{
pos.x = 0;
pos.y = 180;
pos.z = 80;
}
else if (SmokeWeapon == WEAPON_ROCKET_LAUNCHER)
{
pos.x = 0;
pos.y = 84;
pos.z = 72;
}
GetLaraJointPosition(&pos, LM_RHAND);
if (LaraItem->meshBits)
TriggerGunSmoke(pos.x, pos.y, pos.z, 0, 0, 0, 0, SmokeWeapon, SmokeCountL);
}
ITEM_INFO* item = &g_Level.Items[Lara.weaponItem];
bool running = (weaponType == WEAPON_HK && LaraItem->speed != 0);
bool harpoonFired = false;
switch (item->currentAnimState)
{
case WSTATE_AIM:
HKFlag = 0;
HKTimer = 0;
HKFlag2 = 0;
if (Lara.waterStatus == LW_UNDERWATER || running)
item->goalAnimState = WSTATE_UW_AIM;
else if ((!(TrInput & IN_ACTION) || Lara.target) && Lara.leftArm.lock == 0)
item->goalAnimState = WSTATE_UNAIM;
else
item->goalAnimState = WSTATE_RECOIL;
break;
case WSTATE_UW_AIM:
HKFlag = 0;
HKTimer = 0;
HKFlag2 = 0;
if (Lara.waterStatus == LW_UNDERWATER || running)
{
if ((!(TrInput & IN_ACTION) || Lara.target) && Lara.leftArm.lock == 0)
item->goalAnimState = WSTATE_UW_UNAIM;
else
item->goalAnimState = WSTATE_UW_RECOIL;
}
else
item->goalAnimState = WSTATE_AIM;
break;
case WSTATE_RECOIL:
if (item->frameNumber == g_Level.Anims[item->animNumber].frameBase)
{
item->goalAnimState = WSTATE_UNAIM;
if (Lara.waterStatus != LW_UNDERWATER && !running && !harpoonFired)
{
if ((TrInput & IN_ACTION) && (!Lara.target || Lara.leftArm.lock))
{
if (weaponType == WEAPON_HARPOON_GUN)
{
FireHarpoon();
if (!(Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[0] & 3))
harpoonFired = true;
}
else if (weaponType == WEAPON_ROCKET_LAUNCHER)
{
FireRocket();
}
else if (weaponType == WEAPON_GRENADE_LAUNCHER)
{
FireGrenade();
}
else if (weaponType == WEAPON_CROSSBOW)
{
FireCrossbow(NULL);
}
else if (weaponType == WEAPON_HK)
{
FireHK(0);
HKFlag = 1;
if (Lara.Weapons[WEAPON_HK].HasSilencer)
{
SoundEffect(SFX_HK_SILENCED, 0, 0);
}
else
{
SoundEffect(SFX_EXPLOSION1, &LaraItem->pos, 83888140);
SoundEffect(SFX_HK_FIRE, &LaraItem->pos, 0);
}
}
else
FireShotgun();
item->goalAnimState = WSTATE_RECOIL;
}
else if (Lara.leftArm.lock)
item->goalAnimState = 0;
}
if (item->goalAnimState != WSTATE_RECOIL
&& HKFlag
&& !(Lara.Weapons[WEAPON_HK].HasSilencer))
{
StopSoundEffect(SFX_HK_FIRE);
SoundEffect(SFX_HK_STOP, &LaraItem->pos, 0);
HKFlag = 0;
}
}
else if (HKFlag)
{
if (Lara.Weapons[WEAPON_HK].HasSilencer)
{
SoundEffect(SFX_HK_SILENCED, 0, 0);
}
else
{
SoundEffect(SFX_EXPLOSION1, &LaraItem->pos, 83888140);
SoundEffect(SFX_HK_FIRE, &LaraItem->pos, 0);
}
}
else if (weaponType == WEAPON_SHOTGUN && !(TrInput & IN_ACTION) && !Lara.leftArm.lock)
{
item->goalAnimState = WSTATE_UNAIM;
}
if (item->frameNumber - g_Level.Anims[item->animNumber].frameBase == 12
&& weaponType == WEAPON_SHOTGUN)
TriggerGunShell(1, ID_SHOTGUNSHELL, WEAPON_SHOTGUN);
break;
case WSTATE_UW_RECOIL:
if (item->frameNumber - g_Level.Anims[item->animNumber].frameBase == 0)
{
item->goalAnimState = WSTATE_UW_UNAIM;
if ((Lara.waterStatus == LW_UNDERWATER || running)
&& !harpoonFired)
{
if ((TrInput & IN_ACTION)
&& (!Lara.target
|| Lara.leftArm.lock))
{
if (weaponType == WEAPON_HARPOON_GUN)
{
FireHarpoon();
if (!(Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[0] & 3))
harpoonFired = true;
}
else if (weaponType == WEAPON_HK && (/*!(Lara.HKtypeCarried & 0x18) || */!HKTimer))
{
FireHK(1);
HKFlag = 1;
item->goalAnimState = 8;
if (Lara.Weapons[WEAPON_HK].HasSilencer)
{
SoundEffect(14, 0, 0);
}
else
{
SoundEffect(SFX_EXPLOSION1, &LaraItem->pos, 83888140);
SoundEffect(SFX_HK_FIRE, &LaraItem->pos, 0);
}
}
else
{
item->goalAnimState = WSTATE_UW_AIM;
}
item->goalAnimState = WSTATE_UW_RECOIL;
}
else if (Lara.leftArm.lock)
item->goalAnimState = WSTATE_UW_AIM;
}
else if (item->goalAnimState != WSTATE_UW_RECOIL
&& HKFlag
&& !(Lara.Weapons[WEAPON_HK].HasSilencer))
{
StopSoundEffect(SFX_HK_FIRE);
SoundEffect(SFX_HK_STOP, &LaraItem->pos, 0);
HKFlag = 0;
}
else if (HKFlag)
{
if (Lara.Weapons[WEAPON_HK].HasSilencer)
{
SoundEffect(SFX_HK_SILENCED, 0, 0);
}
else
{
SoundEffect(SFX_EXPLOSION1, &LaraItem->pos, 83888140);
SoundEffect(SFX_HK_FIRE, &LaraItem->pos, 0);
}
}
}
break;
default:
break;
}
AnimateItem(item);
Lara.leftArm.frameBase = Lara.rightArm.frameBase = g_Level.Anims[item->animNumber].framePtr;
Lara.leftArm.frameNumber = Lara.rightArm.frameNumber = item->frameNumber - g_Level.Anims[item->animNumber].frameBase;
Lara.leftArm.animNumber = Lara.rightArm.animNumber = item->animNumber;
}
enum CROSSBOW_TYPE
{
CROSSBOW_NORMAL,
CROSSBOW_POISON,
CROSSBOW_EXPLODE
};
void ControlCrossbowBolt(short itemNumber)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
// Store old position for later
int oldX = item->pos.xPos;
int oldY = item->pos.yPos;
int oldZ = item->pos.zPos;
short roomNumber = item->roomNumber;
bool aboveWater = false;
bool explode = false;
// Update speed and check if above water
if (g_Level.Rooms[roomNumber].flags & ENV_FLAG_WATER)
{
PHD_VECTOR bubblePos(item->pos.xPos, item->pos.yPos, item->pos.zPos);
if (item->speed > 64)
item->speed -= (item->speed >> 4);
if (GlobalCounter & 1)
CreateBubble(&bubblePos, roomNumber, 4, 7, 0, 0, 0, 0);
aboveWater = false;
}
else
{
aboveWater = true;
}
// Update bolt's position
item->pos.xPos += ((item->speed * phd_cos(item->pos.xRot) >> W2V_SHIFT) * phd_sin(item->pos.yRot)) >> W2V_SHIFT;
item->pos.yPos += item->speed * phd_sin(-item->pos.xRot) >> W2V_SHIFT;
item->pos.zPos += ((item->speed * phd_cos(item->pos.xRot) >> W2V_SHIFT) * phd_cos(item->pos.yRot)) >> W2V_SHIFT;
roomNumber = item->roomNumber;
FLOOR_INFO* floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
// Check if bolt has hit a solid wall
if (GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) < item->pos.yPos ||
GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) > item->pos.yPos)
{
// I have hit a solid wall, this is the end for the bolt
item->pos.xPos = oldX;
item->pos.yPos = oldY;
item->pos.zPos = oldZ;
// If ammos are normal, then just shatter the bolt and quit
if (item->itemFlags[0] != CROSSBOW_EXPLODE)
{
ExplodeItemNode(item, 0, 0, EXPLODE_NORMAL);
KillItem(itemNumber);
return;
}
else
{
// Otherwise, bolt must explode
explode = true;
}
}
// Has bolt changed room?
if (item->roomNumber != roomNumber)
ItemNewRoom(itemNumber, roomNumber);
// If now in water and before in land, add a ripple
if ((g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER) && aboveWater)
{
SetupRipple(item->pos.xPos, g_Level.Rooms[item->roomNumber].minfloor, item->pos.zPos, (GetRandomControl() & 7) + 8, 0);
}
int radius = (explode ? CROSSBOW_EXPLODE_RADIUS : CROSSBOW_HIT_RADIUS);
bool foundCollidedObjects = false;
for (int n = 0; n < 2; n++)
{
// Step 0: check for specific collision in a small radius
// Step 1: done only if explosion, try to smash all objects in the blast radius
// Found possible collided items and statics
GetCollidedObjects(item, radius, 1, &CollidedItems[0], &CollidedMeshes[0], true);
// If no collided items and meshes are found, then exit the loop
if (!CollidedItems[0] && !CollidedMeshes[0])
break;
foundCollidedObjects = true;
if (item->itemFlags[0] != CROSSBOW_EXPLODE || explode)
{
if (CollidedItems[0])
{
ITEM_INFO* currentItem = CollidedItems[0];
OBJECT_INFO* currentObj = &Objects[currentItem->objectNumber];
int k = 0;
do
{
if ((currentObj->intelligent && currentObj->collision && currentItem->status == ITEM_ACTIVE)
|| currentItem->objectNumber == ID_LARA
|| (currentItem->flags & 0x40 &&
(Objects[currentItem->objectNumber].explodableMeshbits || currentItem == LaraItem)))
{
// All active intelligent creatures explode, if their HP is <= 0
// Explosion is handled by CreatureDie()
// Also Lara can be damaged
// HitTarget() is called inside this
DoExplosiveDamageOnBaddie(currentItem, item, WEAPON_CROSSBOW);
}
else if (currentItem->objectNumber >= ID_SMASH_OBJECT1 && currentItem->objectNumber <= ID_SMASH_OBJECT8)
{
// Smash objects are legacy objects from TRC, let's make them explode in the legacy way
TriggerExplosionSparks(currentItem->pos.xPos, currentItem->pos.yPos, currentItem->pos.zPos, 3, -2, 0, currentItem->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentItem->pos.xPos, currentItem->pos.yPos - 128, currentItem->pos.zPos), 48, 304, 96, 0, 96, 128, 24, 0, 0);
ExplodeItemNode(currentItem, 0, 0, 128);
short currentItemNumber = (currentItem - CollidedItems[0]);
SmashObject(currentItemNumber);
KillItem(currentItemNumber);
}
// TODO_LUA: we need to handle it with an event like OnDestroy
/*else if (currentObj->hitEffect == HIT_SPECIAL)
{
// Some objects need a custom behaviour
//HitSpecial(item, currentItem, 1);
}*/
// All other items (like puzzles) don't explode
k++;
currentItem = CollidedItems[k];
} while (currentItem);
}
if (CollidedMeshes[0])
{
MESH_INFO* currentMesh = CollidedMeshes[0];
int k = 0;
do
{
STATIC_INFO* s = &StaticObjects[currentMesh->staticNumber];
if (s->shatterType != SHT_NONE)
{
currentMesh->hitPoints -= Weapons[WEAPON_CROSSBOW].damage;
if (currentMesh->hitPoints <= 0)
{
TriggerExplosionSparks(currentMesh->x, currentMesh->y, currentMesh->z, 3, -2, 0, item->roomNumber);
TriggerShockwave(&PHD_3DPOS(currentMesh->x, currentMesh->y - 128, currentMesh->z, 0, currentMesh->yRot, 0), 40, 176, 64, 0, 96, 128, 16, 0, 0);
ShatterObject((SHATTER_ITEM*)item, NULL, -128, item->roomNumber, 0); // TODO: this wont work !!
SmashedMeshRoom[SmashedMeshCount] = item->roomNumber;
SmashedMesh[SmashedMeshCount] = currentMesh;
SmashedMeshCount++;
currentMesh->flags &= ~1;
}
}
k++;
currentMesh = CollidedMeshes[k];
} while (currentMesh);
}
break;
}
explode = true;
radius = CROSSBOW_EXPLODE_RADIUS;
};
if (!explode)
{
// If bolt has hit some objects then shatter itself
if (foundCollidedObjects)
{
ExplodeItemNode(item, 0, 0, EXPLODE_NORMAL);
KillItem(itemNumber);
}
}
else
{
// Explode
if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
{
TriggerUnderwaterExplosion(item, 0);
}
else
{
TriggerShockwave(&item->pos, 48, 304, 96, 0, 96, 128, 24, 0, 0);
item->pos.yPos += 128;
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, item->roomNumber);
for (int j = 0; j < 2; j++)
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -1, 0, item->roomNumber);
}
AlertNearbyGuards(item);
SoundEffect(SFX_EXPLOSION1, &item->pos, PITCH_SHIFT | 0x1800000);
SoundEffect(SFX_EXPLOSION2, &item->pos, 0);
ExplodeItemNode(item, 0, 0, EXPLODE_NORMAL);
KillItem(itemNumber);
}
}
void RifleHandler(int weaponType)
{
WEAPON_INFO* weapon = &Weapons[weaponType];
LaraGetNewTarget(weapon);
if (TrInput & IN_ACTION)
LaraTargetInfo(weapon);
AimWeapon(weapon, &Lara.leftArm);
if (Lara.leftArm.lock)
{
Lara.torsoXrot = Lara.leftArm.xRot;
Lara.torsoYrot = Lara.leftArm.yRot;
if (Camera.oldType != LOOK_CAMERA && !BinocularRange)
{
Lara.headYrot = 0;
Lara.headXrot = 0;
}
}
if (weaponType == WEAPON_REVOLVER)
AnimatePistols(WEAPON_REVOLVER);
else
AnimateShotgun(weaponType);
if (Lara.rightArm.flash_gun)
{
if (weaponType == WEAPON_SHOTGUN || weaponType == WEAPON_HK)
{
PHD_VECTOR pos = {};
pos.y = -64;
GetLaraJointPosition(&pos, LM_RHAND);
TriggerDynamicLight(
pos.x,
pos.y,
pos.z,
12,
(GetRandomControl() & 0x3F) + 192,
(GetRandomControl() & 0x1F) + 128,
GetRandomControl() & 0x3F
);
}
else if (weaponType == WEAPON_REVOLVER)
{
PHD_VECTOR pos = {};
pos.y = -32;
GetLaraJointPosition(&pos, LM_RHAND);
TriggerDynamicLight(pos.x, pos.y, pos.z, 12, (GetRandomControl() & 0x3F) + 192, (GetRandomControl() & 0x1F) + 128, (GetRandomControl() & 0x3F));
}
}
}
void FireCrossbow(PHD_3DPOS* pos)
{
short* ammos = GetAmmo(WEAPON_CROSSBOW);
if (*ammos != 0)
{
Lara.hasFired = true;
short itemNumber = CreateItem();
if (itemNumber != NO_ITEM)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
item->objectNumber = ID_CROSSBOW_BOLT;
item->shade = 0xC210;
if (pos)
{
item->roomNumber = LaraItem->roomNumber;
item->pos.xPos = pos->xPos;
item->pos.yPos = pos->yPos;
item->pos.zPos = pos->zPos;
InitialiseItem(itemNumber);
item->pos.xRot = pos->xRot;
item->pos.yRot = pos->yRot;
item->pos.zRot = pos->zRot;
}
else
{
if (*ammos != -1)
(*ammos)--;
PHD_VECTOR jointPos;
jointPos.x = 0;
jointPos.y = 228;
jointPos.z = 32;
GetLaraJointPosition(&jointPos, LM_RHAND);
item->roomNumber = LaraItem->roomNumber;
FLOOR_INFO * floor = GetFloor(jointPos.x, jointPos.y, jointPos.z, &item->roomNumber);
int height = GetFloorHeight(floor, jointPos.x, jointPos.y, jointPos.z);
if (height >= jointPos.y)
{
item->pos.xPos = jointPos.x;
item->pos.yPos = jointPos.y;
item->pos.zPos = jointPos.z;
}
else
{
item->pos.xPos = LaraItem->pos.xPos;
item->pos.yPos = jointPos.y;
item->pos.zPos = LaraItem->pos.zPos;
item->roomNumber = LaraItem->roomNumber;
}
InitialiseItem(itemNumber);
item->pos.xRot = Lara.leftArm.xRot + LaraItem->pos.xRot;
item->pos.zRot = 0;
item->pos.yRot = Lara.leftArm.yRot + LaraItem->pos.yRot;
if (!Lara.leftArm.lock)
{
item->pos.xRot += Lara.torsoXrot;
item->pos.yRot += Lara.torsoYrot;
}
}
item->speed = 512;
AddActiveItem(itemNumber);
item->itemFlags[0] = Lara.Weapons[WEAPON_CROSSBOW].SelectedAmmo;
SoundEffect(SFX_TR4_LARA_CROSSBOW, 0, 0);
Savegame.Level.AmmoUsed++;
Savegame.Game.AmmoUsed++;
}
}
}
void FireRocket()
{
short* ammos = GetAmmo(WEAPON_ROCKET_LAUNCHER);
if (*ammos != 0)
{
Lara.hasFired = true;
short itemNumber = CreateItem();
if (itemNumber != NO_ITEM)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
item->objectNumber = ID_ROCKET;
item->roomNumber = LaraItem->roomNumber;
if (*ammos != -1)
(*ammos)--;
PHD_VECTOR jointPos;
jointPos.x = 0;
jointPos.y = 180;
jointPos.z = 72;
GetLaraJointPosition(&jointPos, LM_RHAND);
int x, y, z;
item->pos.xPos = x = jointPos.x;
item->pos.yPos = y = jointPos.y;
item->pos.zPos = z = jointPos.z;
jointPos.x = 0;
jointPos.y = 180 + 1024;
jointPos.z = 72;
SmokeCountL = 32;
SmokeWeapon = WEAPON_ROCKET_LAUNCHER;
for (int i = 0; i < 5; i++)
TriggerGunSmoke(x, y, z, jointPos.x - x, jointPos.y - y, jointPos.z - z, 1, WEAPON_ROCKET_LAUNCHER, 32);
InitialiseItem(itemNumber);
item->pos.xRot = LaraItem->pos.xRot + Lara.leftArm.xRot;
item->pos.yRot = LaraItem->pos.yRot + Lara.leftArm.yRot;
item->pos.zRot = 0;
if (!Lara.leftArm.lock)
{
item->pos.xRot += Lara.torsoXrot;
item->pos.yRot += Lara.torsoYrot;
}
item->speed = 512 >> 5;
item->itemFlags[0] = 0;
AddActiveItem(itemNumber);
SoundEffect(SFX_EXPLOSION1, 0, 0);
Savegame.Level.AmmoUsed++;
Savegame.Game.AmmoUsed++;
}
}
}
void DoExplosiveDamageOnBaddie(ITEM_INFO* dest, ITEM_INFO* src, int weapon)
{
if (!(dest->flags & 0x8000))
{
if (dest != LaraItem || LaraItem->hitPoints <= 0)
{
if (!src->itemFlags[2])
{
dest->hitStatus = true;
OBJECT_INFO* obj = &Objects[dest->objectNumber];
if (!obj->undead)
{
HitTarget(dest, 0, Weapons[weapon].damage, 1);
if (dest != LaraItem)
{
Savegame.Game.AmmoHits++;
if (dest->hitPoints <= 0)
{
Savegame.Level.Kills++;
CreatureDie((dest - g_Level.Items.data()), 1);
}
}
}
}
}
else
{
LaraItem->hitPoints -= Weapons[weapon].damage;
if (!(g_Level.Rooms[dest->roomNumber].flags & ENV_FLAG_WATER) && LaraItem->hitPoints <= Weapons[weapon].damage)
LaraBurn();
}
}
}
void SomeSparkEffect(int x, int y, int z, int count)
{
for (int i = 0; i < count; i++)
{
SPARKS* spark = &Sparks[GetFreeSpark()];
spark->on = 1;
spark->sR = 112;
spark->sG = (GetRandomControl() & 0x1F) + -128;
spark->sB = (GetRandomControl() & 0x1F) + -128;
spark->colFadeSpeed = 4;
spark->fadeToBlack = 8;
spark->life = 24;
spark->dR = spark->sR >> 1;
spark->dG = spark->sG >> 1;
spark->dB = spark->sB >> 1;
spark->sLife = 24;
spark->transType = COLADD;
spark->friction = 5;
spark->xVel = -128 * phd_sin(GetRandomControl());
spark->yVel = -640 - (byte)GetRandomControl();
spark->zVel = -128 * phd_cos(GetRandomControl());
spark->flags = 0;
spark->x = x + (spark->xVel >> 3);
spark->y = y - (spark->yVel >> 5);
spark->z = z + (spark->zVel >> 3);
spark->maxYvel = 0;
spark->gravity = (GetRandomControl() & 0xF) + 64;
}
}
void TriggerUnderwaterExplosion(ITEM_INFO* item, int flag)
{
if (flag)
{
int x = (GetRandomControl() & 0x1FF) + item->pos.xPos - 256;
int y = item->pos.yPos;
int z = (GetRandomControl() & 0x1FF) + item->pos.zPos - 256;
TriggerExplosionBubbles(x, y, z, item->roomNumber);
TriggerExplosionSparks(x, y, z, 2, -1, 1, item->roomNumber);
int wh = GetWaterHeight(x, y, z, item->roomNumber);
if (wh != NO_HEIGHT)
SomeSparkEffect(x, wh, z, 8);
}
else
{
TriggerExplosionBubble(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
TriggerExplosionSparks(item->pos.xPos, item->pos.yPos, item->pos.zPos, 2, -2, 1, item->roomNumber);
for (int i = 0; i < 3; i++)
{
TriggerExplosionSparks(item->pos.xPos, item->pos.yPos, item->pos.zPos, 2, -1, 1, item->roomNumber);
}
int wh = GetWaterHeight(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
if (wh != NO_HEIGHT)
{
int dy = item->pos.yPos - wh;
if (dy < 2048)
{
SplashSetup.y = wh;
SplashSetup.x = item->pos.xPos;
SplashSetup.z = item->pos.zPos;
SplashSetup.innerRadius = 160;
SplashSetup.splashPower = 2048 - dy;
SetupSplash(&SplashSetup, item->roomNumber);
}
}
}
}
void undraw_shotgun(int weapon)
{
ITEM_INFO* item = &g_Level.Items[Lara.weaponItem];
item->goalAnimState = 3;
AnimateItem(item);
if (item->status == ITEM_DEACTIVATED)
{
Lara.gunStatus = LG_NO_ARMS;
Lara.target = nullptr;
Lara.rightArm.lock = false;
Lara.leftArm.lock = false;
KillItem(Lara.weaponItem);
Lara.weaponItem = NO_ITEM;
Lara.rightArm.frameNumber = 0;
Lara.leftArm.frameNumber = 0;
}
else if (item->currentAnimState == 3 && item->frameNumber - g_Level.Anims[item->animNumber].frameBase == 21)
{
undraw_shotgun_meshes(weapon);
}
Lara.rightArm.frameBase = g_Level.Anims[item->animNumber].framePtr;
Lara.leftArm.frameBase = g_Level.Anims[item->animNumber].framePtr;
Lara.rightArm.frameNumber = item->frameNumber - g_Level.Anims[item->animNumber].frameBase;
Lara.leftArm.frameNumber = item->frameNumber - g_Level.Anims[item->animNumber].frameBase;
Lara.rightArm.animNumber = item->animNumber;
Lara.leftArm.animNumber = Lara.rightArm.animNumber;
}
void undraw_shotgun_meshes(int weapon)
{
Lara.holsterInfo.backHolster = HolsterSlotForWeapon(static_cast<LARA_WEAPON_TYPE>(weapon));
Lara.meshPtrs[LM_RHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_RHAND;
}
void draw_shotgun_meshes(int weaponType)
{
Lara.holsterInfo.backHolster = HOLSTER_SLOT::Empty;
Lara.meshPtrs[LM_RHAND] = Objects[WeaponObjectMesh(weaponType)].meshIndex + LM_RHAND;
}
void HitSpecial(ITEM_INFO* projectile, ITEM_INFO* target, int flags)
{
}
void FireHK(int mode)
{
if (Lara.Weapons[WEAPON_HK].SelectedAmmo == WEAPON_AMMO1)
{
HKTimer = 12;
}
else if (Lara.Weapons[WEAPON_HK].SelectedAmmo == WEAPON_AMMO2)
{
HKCounter++;
if (HKCounter == 5)
{
HKCounter = 0;
HKTimer = 12;
}
}
short angles[2];
angles[1] = Lara.leftArm.xRot;
angles[0] = Lara.leftArm.yRot + LaraItem->pos.yRot;
if (!Lara.leftArm.lock)
{
angles[0] = Lara.torsoYrot + Lara.leftArm.yRot + LaraItem->pos.yRot;
angles[1] = Lara.torsoXrot + Lara.leftArm.xRot;
}
if (mode)
{
Weapons[WEAPON_HK].shotAccuracy = 2184;
Weapons[WEAPON_HK].damage = 1;
}
else
{
Weapons[WEAPON_HK].shotAccuracy = 728;
Weapons[WEAPON_HK].damage = 3;
}
if (FireWeapon(WEAPON_HK, Lara.target, LaraItem, angles) != FW_NOAMMO)
{
SmokeCountL = 12;
SmokeWeapon = WEAPON_HK;
TriggerGunShell(1, ID_GUNSHELL, WEAPON_HK);
Lara.rightArm.flash_gun = Weapons[WEAPON_HK].flashTime;
}
}
void FireShotgun()
{
short angles[2];
angles[1] = Lara.leftArm.xRot;
angles[0] = Lara.leftArm.yRot + LaraItem->pos.yRot;
if (!Lara.leftArm.lock)
{
angles[0] = Lara.torsoYrot + Lara.leftArm.yRot + LaraItem->pos.yRot;
angles[1] = Lara.torsoXrot + Lara.leftArm.xRot;
}
short loopAngles[2];
bool fired = false;
int value = (Lara.Weapons[WEAPON_SHOTGUN].SelectedAmmo == WEAPON_AMMO1 ? 1820 : 5460);
for (int i = 0; i < 6; i++)
{
loopAngles[0] = angles[0] + value * (GetRandomControl() - 0x4000) / 0x10000;
loopAngles[1] = angles[1] + value * (GetRandomControl() - 0x4000) / 0x10000;
if (FireWeapon(WEAPON_SHOTGUN, Lara.target, LaraItem, loopAngles) != FW_NOAMMO)
fired = true;
}
if (fired)
{
PHD_VECTOR pos;
pos.x = 0;
pos.y = 228;
pos.z = 32;
GetLaraJointPosition(&pos, LM_RHAND);
PHD_VECTOR pos2;
pos2.x = pos.x;
pos2.y = pos.y;
pos2.z = pos.z;
pos.x = 0;
pos.y = 1508;
pos.z = 32;
GetLaraJointPosition(&pos, LM_RHAND);
SmokeCountL = 32;
SmokeWeapon = WEAPON_SHOTGUN;
if (LaraItem->meshBits != 0)
{
for (int i = 0; i < 7; i++)
TriggerGunSmoke(pos2.x, pos2.y, pos2.z, pos.x - pos2.x, pos.y - pos2.y, pos.z - pos2.z, 1, SmokeWeapon, SmokeCountL);
}
Lara.rightArm.flash_gun = Weapons[WEAPON_SHOTGUN].flashTime;
SoundEffect(SFX_EXPLOSION1, &LaraItem->pos, 20971524);
SoundEffect(Weapons[WEAPON_SHOTGUN].sampleNum, &LaraItem->pos, 0);
Savegame.Game.AmmoUsed++;
}
}
void ready_shotgun(int weaponType)
{
Lara.gunStatus = LG_READY;
Lara.leftArm.zRot = 0;
Lara.leftArm.yRot = 0;
Lara.leftArm.xRot = 0;
Lara.rightArm.zRot = 0;
Lara.rightArm.yRot = 0;
Lara.rightArm.xRot = 0;
Lara.rightArm.frameNumber = 0;
Lara.leftArm.frameNumber = 0;
Lara.rightArm.lock = false;
Lara.leftArm.lock = false;
Lara.target = nullptr;
Lara.rightArm.frameBase = Objects[WeaponObject(weaponType)].frameBase;
Lara.leftArm.frameBase = Objects[WeaponObject(weaponType)].frameBase;
}