mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-05-02 09:47:58 +03:00
1953 lines
54 KiB
C++
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;
|
|
}
|