2018-11-01 09:10:32 +01:00
|
|
|
#include "lara1gun.h"
|
|
|
|
#include "..\Global\global.h"
|
|
|
|
#include "items.h"
|
|
|
|
#include "lara.h"
|
|
|
|
#include "larafire.h"
|
|
|
|
#include "draw.h"
|
|
|
|
#include "Box.h"
|
|
|
|
#include "control.h"
|
|
|
|
#include "effects.h"
|
2018-11-01 22:45:59 +01:00
|
|
|
#include "effect2.h"
|
|
|
|
#include "lot.h"
|
2018-11-14 20:54:18 +01:00
|
|
|
#include "collide.h"
|
|
|
|
#include "debris.h"
|
2018-11-15 18:39:13 +01:00
|
|
|
#include "lara2gun.h"
|
2018-11-01 09:10:32 +01:00
|
|
|
|
|
|
|
extern LaraExtraInfo g_LaraExtra;
|
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
void __cdecl FireHarpoon()
|
|
|
|
{
|
|
|
|
// If no ammo then exit
|
|
|
|
if (g_LaraExtra.numHarpoonAmmos <= 0)
|
|
|
|
return;
|
|
|
|
|
2018-11-14 20:54:18 +01:00
|
|
|
// Create a new item for harpoon
|
2018-11-01 22:45:59 +01:00
|
|
|
__int16 itemNumber = CreateItem();
|
|
|
|
if (itemNumber != NO_ITEM)
|
|
|
|
{
|
|
|
|
GAME_VECTOR pos;
|
2018-11-02 13:02:10 +01:00
|
|
|
D3DXVECTOR3 dxPos;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
ITEM_INFO* item = &Items[itemNumber];
|
|
|
|
|
|
|
|
item->shade = 0x4210 | 0x8000;
|
2018-11-02 13:02:10 +01:00
|
|
|
item->objectNumber = ID_HARPOON;
|
2018-11-01 22:45:59 +01:00
|
|
|
item->roomNumber = LaraItem->roomNumber;
|
2018-11-02 13:02:10 +01:00
|
|
|
pos.x = dxPos.x = -2;
|
2018-11-14 20:54:18 +01:00
|
|
|
pos.y = dxPos.y = 0; // -273 - 100;
|
2018-11-02 13:02:10 +01:00
|
|
|
pos.z = dxPos.z = 77;
|
|
|
|
|
|
|
|
g_Renderer->GetLaraBonePosition(&dxPos, HAND_R);
|
2018-11-14 20:54:18 +01:00
|
|
|
GetLaraJointPosition((PHD_VECTOR*)&pos, HAND_R);
|
2018-11-02 13:02:10 +01:00
|
|
|
|
2018-11-14 20:54:18 +01:00
|
|
|
/*item->pos.xPos = pos.x = dxPos.x;
|
2018-11-02 13:02:10 +01:00
|
|
|
item->pos.yPos = pos.y = dxPos.y;
|
2018-11-14 20:54:18 +01:00
|
|
|
item->pos.zPos = pos.z = dxPos.z;*/
|
|
|
|
|
|
|
|
item->pos.xPos = pos.x;
|
|
|
|
item->pos.yPos = pos.y;
|
|
|
|
item->pos.zPos = pos.z;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
InitialiseItem(itemNumber);
|
|
|
|
|
|
|
|
if (Lara.target)
|
|
|
|
{
|
|
|
|
FindTargetPoint(Lara.target, &pos);
|
|
|
|
|
|
|
|
item->pos.yRot = ATAN(pos.z - item->pos.zPos, pos.x - item->pos.xPos);
|
|
|
|
__int32 distance = SQRT_ASM(SQUARE(pos.z - item->pos.zPos) + SQUARE(pos.x - item->pos.xPos));
|
|
|
|
item->pos.xRot = -ATAN(distance, pos.y - item->pos.yPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->pos.xRot = LaraItem->pos.xRot + Lara.torsoXrot;
|
|
|
|
item->pos.yRot = LaraItem->pos.yRot + Lara.torsoYrot;
|
|
|
|
}
|
2018-11-14 20:54:18 +01:00
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
item->pos.zRot = 0;
|
|
|
|
|
|
|
|
item->fallspeed = (__int16)(-HARPOON_SPEED * SIN(item->pos.xRot) >> W2V_SHIFT);
|
|
|
|
item->speed = (__int16)(HARPOON_SPEED * COS(item->pos.xRot) >> W2V_SHIFT);
|
|
|
|
item->hitPoints = HARPOON_TIME;
|
|
|
|
|
|
|
|
AddActiveItem(itemNumber);
|
|
|
|
|
|
|
|
Savegame.Level.AmmoUsed++;
|
|
|
|
Savegame.Game.AmmoUsed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void __cdecl ControlHarpoonBolt(__int16 itemNumber)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &Items[itemNumber];
|
|
|
|
|
|
|
|
__int32 oldX = item->pos.xPos;
|
|
|
|
__int32 oldY = item->pos.yPos;
|
|
|
|
__int32 oldZ = item->pos.zPos;
|
|
|
|
__int16 oldRoom = item->roomNumber;
|
|
|
|
|
|
|
|
item->pos.xPos += item->speed * SIN(item->pos.yRot) >> W2V_SHIFT;
|
|
|
|
item->pos.yPos += item->fallspeed;
|
2018-11-14 20:54:18 +01:00
|
|
|
item->pos.zPos += item->speed * COS(item->pos.yRot) >> W2V_SHIFT;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
__int16 roomNumber = item->roomNumber;
|
|
|
|
FLOOR_INFO* floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
|
|
|
|
item->floor = GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
|
|
|
|
|
|
|
|
if (item->roomNumber != roomNumber)
|
|
|
|
ItemNewRoom(itemNumber, roomNumber);
|
|
|
|
|
2018-11-14 20:54:18 +01:00
|
|
|
// First check if the harpoon has it an item
|
2018-11-01 22:45:59 +01:00
|
|
|
__int16 targetItemNumber = 0;
|
|
|
|
ITEM_INFO* target;
|
|
|
|
for (targetItemNumber = Rooms[item->roomNumber].itemNumber; targetItemNumber != NO_ITEM; targetItemNumber = target->nextItem)
|
|
|
|
{
|
|
|
|
target = &Items[targetItemNumber];
|
|
|
|
if (target == LaraItem || !target->collidable)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (/*target->objectNumber == SMASH_WINDOW ||
|
|
|
|
target->objectNumber == SMASH_OBJECT1 ||
|
|
|
|
target->objectNumber == SMASH_OBJECT2 ||
|
|
|
|
target->objectNumber == SMASH_OBJECT3 ||
|
|
|
|
target->objectNumber == CARCASS ||
|
|
|
|
target->objectNumber == EXTRAFX6 ||*/
|
|
|
|
(target->status != ITEM_INVISIBLE && Objects[target->objectNumber].collision))
|
|
|
|
{
|
|
|
|
// check against bounds of target for collision
|
|
|
|
__int16* bounds = GetBestFrame(target);
|
|
|
|
if (item->pos.yPos < target->pos.yPos + bounds[2] || item->pos.yPos > target->pos.yPos + bounds[3])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// get vector from target to bolt and check against x,z bounds
|
|
|
|
__int16 c = COS(target->pos.yRot);
|
|
|
|
__int16 s = SIN(target->pos.yRot);
|
|
|
|
|
|
|
|
__int32 x = item->pos.xPos - target->pos.xPos;
|
|
|
|
__int32 z = item->pos.zPos - target->pos.zPos;
|
|
|
|
__int32 rx = (c * x - s * z) >> W2V_SHIFT;
|
|
|
|
|
|
|
|
__int32 ox = oldX - target->pos.xPos;
|
|
|
|
__int32 oz = oldZ - target->pos.zPos;
|
|
|
|
__int32 sx = (c * ox - s * oz) >> W2V_SHIFT;
|
|
|
|
|
|
|
|
if ((rx < bounds[0] && sx < bounds[0]) || (rx > bounds[1] && sx > bounds[1]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
__int32 rz = (c * z + s * x) >> W2V_SHIFT;
|
|
|
|
__int32 sz = (c * oz + s * ox) >> W2V_SHIFT;
|
|
|
|
|
|
|
|
if ((rz < bounds[4] && sz < bounds[4]) || (rz > bounds[5] && sz > bounds[5]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
/*if (target->objectNumber == SMASH_OBJECT1 && CurrentLevel != LV_CRASH)
|
|
|
|
{
|
|
|
|
SmashWindow(targetItemNumber);
|
|
|
|
}
|
|
|
|
else if (target->objectNumber == SMASH_WINDOW ||
|
|
|
|
target->objectNumber == SMASH_OBJECT2 ||
|
|
|
|
target->objectNumber == SMASH_OBJECT3)
|
|
|
|
{
|
|
|
|
SmashWindow(targetItemNumber);
|
|
|
|
}
|
|
|
|
else if (target->objectNumber == CARCASS || target->objectNumber == EXTRAFX6)
|
|
|
|
{
|
|
|
|
if (item->status != ACTIVE)
|
|
|
|
{
|
|
|
|
item->status = ACTIVE;
|
|
|
|
AddActiveItem(targetItemNumber);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (target->objectNumber != SMASH_OBJECT1)
|
|
|
|
{*/
|
|
|
|
if (Objects[target->objectNumber].intelligent)
|
|
|
|
{
|
|
|
|
DoLotsOfBlood(item->pos.xPos, item->pos.yPos, item->pos.zPos, 0, 0, item->roomNumber, 3);
|
|
|
|
HitTarget(target, NULL, Weapons[WEAPON_HARPOON].damage << item->itemFlags[0], 0);
|
|
|
|
Savegame.Level.AmmoHits++;
|
|
|
|
Savegame.Game.AmmoHits++;
|
|
|
|
}
|
2018-11-14 20:54:18 +01:00
|
|
|
|
|
|
|
KillItem(itemNumber);
|
|
|
|
item->afterDeath = 0;
|
|
|
|
return;
|
|
|
|
}
|
2018-11-01 22:45:59 +01:00
|
|
|
}
|
|
|
|
|
2018-11-14 20:54:18 +01:00
|
|
|
// Has harpoon hit a wall?
|
2018-11-01 22:45:59 +01:00
|
|
|
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 + ((((rcossin_tbl[((item->hitPoints - 192) << 9) & 4095] >> 1) - 1024)*(item->hitPoints - 192)) >> 6);
|
|
|
|
item->hitPoints--;
|
|
|
|
}
|
|
|
|
|
|
|
|
item->hitPoints--;
|
2018-11-14 20:54:18 +01:00
|
|
|
if (item->hitPoints <= 0)
|
2018-11-01 22:45:59 +01:00
|
|
|
{
|
|
|
|
KillItem(itemNumber);
|
2018-11-14 20:54:18 +01:00
|
|
|
item->afterDeath = 0;
|
2018-11-01 22:45:59 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
item->speed = item->fallspeed = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->pos.zRot += ANGLE(35);
|
|
|
|
if (!(Rooms[item->roomNumber].flags & 1))
|
|
|
|
{
|
|
|
|
item->pos.xRot -= ANGLE(1);
|
|
|
|
if (item->pos.xRot < -16384)
|
|
|
|
item->pos.xRot = -16384;
|
|
|
|
item->fallspeed = (__int16)(-HARPOON_SPEED * SIN(item->pos.xRot) >> W2V_SHIFT);
|
|
|
|
item->speed = (__int16)(HARPOON_SPEED * COS(item->pos.xRot) >> W2V_SHIFT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-11-14 20:54:18 +01:00
|
|
|
// Create bubbles
|
2018-11-01 22:45:59 +01:00
|
|
|
if ((Wibble & 15) == 0)
|
2018-11-14 20:54:18 +01:00
|
|
|
CreateBubble(&item->pos, item->roomNumber, 0);
|
2018-11-01 22:45:59 +01:00
|
|
|
//TriggerRocketSmoke(item->pos.xPos, item->pos.yPos, item->pos.zPos, 64);
|
|
|
|
item->fallspeed = (__int16)(-(HARPOON_SPEED >> 1) * SIN(item->pos.xRot) >> W2V_SHIFT);
|
|
|
|
item->speed = (__int16)((HARPOON_SPEED >> 1) * COS(item->pos.xRot) >> W2V_SHIFT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
roomNumber = item->roomNumber;
|
|
|
|
floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
|
|
|
|
GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
|
|
|
|
if (item->roomNumber != roomNumber)
|
|
|
|
ItemNewRoom(itemNumber, roomNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
long tbx, tby, tbz;
|
|
|
|
|
|
|
|
void __cdecl FireGrenade()
|
|
|
|
{
|
|
|
|
__int32 x, y, z;
|
|
|
|
|
|
|
|
if (g_LaraExtra.numGrenadeAmmos <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Lara.hasFired = true;
|
|
|
|
|
|
|
|
/* Create a grenade object and launch it on its way */
|
|
|
|
__int16 itemNumber = CreateItem();
|
|
|
|
if (itemNumber != NO_ITEM)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &Items[itemNumber];
|
|
|
|
|
|
|
|
item->shade = 0x4210 | 0x8000;
|
|
|
|
|
|
|
|
item->objectNumber = ID_GRENADE;
|
|
|
|
item->roomNumber = LaraItem->roomNumber;
|
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
D3DXVECTOR3 pos;
|
2018-11-01 22:45:59 +01:00
|
|
|
pos.x = 0;
|
2018-11-02 13:02:10 +01:00
|
|
|
pos.y = -(GRENADE_YOFF + 96);
|
2018-11-01 22:45:59 +01:00
|
|
|
pos.z = GRENADE_ZOFF;
|
2018-11-02 13:02:10 +01:00
|
|
|
g_Renderer->GetLaraBonePosition(&pos, HAND_R);
|
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
item->pos.xPos = x = pos.x;
|
|
|
|
item->pos.yPos = y = pos.y;
|
|
|
|
item->pos.zPos = z = pos.z;
|
|
|
|
FLOOR_INFO* floor = GetFloor(pos.x, pos.y, pos.z, &item->roomNumber);
|
|
|
|
if (GetFloorHeight(floor, pos.x, pos.y, pos.z) < pos.y)
|
|
|
|
{
|
|
|
|
item->pos.xPos = LaraItem->pos.xPos;
|
|
|
|
item->pos.yPos = pos.y;
|
|
|
|
item->pos.zPos = LaraItem->pos.zPos;
|
|
|
|
item->roomNumber = LaraItem->roomNumber;
|
2018-11-02 07:35:19 +01:00
|
|
|
}
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
pos.x = 0;
|
2018-11-02 13:02:10 +01:00
|
|
|
pos.y = -(GRENADE_YOFF + 1024);
|
2018-11-01 22:45:59 +01:00
|
|
|
pos.z = GRENADE_ZOFF;
|
2018-11-02 13:02:10 +01:00
|
|
|
|
|
|
|
g_Renderer->GetLaraBonePosition(&pos, HAND_R);
|
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
SmokeCountL = 32;
|
|
|
|
SmokeWeapon = WEAPON_GRENADE;
|
|
|
|
|
|
|
|
for (__int32 i = 0; i < 5; i++)
|
|
|
|
TriggerGunSmoke(x, y, z, pos.x - x, pos.y - y, pos.z - z, 1, WEAPON_GRENADE, 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 = (-item->speed * SIN(item->pos.xRot)) >> W2V_SHIFT;
|
2018-11-02 13:02:10 +01:00
|
|
|
item->currentAnimState = item->pos.xRot;
|
2018-11-01 22:45:59 +01:00
|
|
|
item->goalAnimState = item->pos.yRot; // Goal anim state is Y rotation so the object can rotate in Y axis blah blah
|
|
|
|
item->requiredAnimState = 0; // Flag to say if rolling on floor.
|
|
|
|
item->hitPoints = 4 * 30; // Explode after 3 seconds.
|
|
|
|
|
|
|
|
AddActiveItem(itemNumber);
|
|
|
|
|
|
|
|
Savegame.Level.AmmoUsed++;
|
|
|
|
Savegame.Game.AmmoUsed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define GRENADE_BLAST_RADIUS (WALL_SIZE)
|
|
|
|
|
|
|
|
void __cdecl ControlGrenade(__int16 itemNumber)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &Items[itemNumber];
|
|
|
|
|
|
|
|
__int32 oldX = item->pos.xPos;
|
|
|
|
__int32 oldY = item->pos.yPos;
|
|
|
|
__int32 oldZ = item->pos.zPos;
|
|
|
|
|
|
|
|
__int32 xv;
|
|
|
|
__int32 yv;
|
|
|
|
__int32 zv;
|
|
|
|
|
|
|
|
item->shade = 0x4210 | 0x8000;
|
|
|
|
// Fall more slowly in water.
|
|
|
|
|
|
|
|
bool aboveWater = false;
|
|
|
|
if (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)*ONE_DEGREE);
|
|
|
|
if (item->requiredAnimState)
|
|
|
|
item->pos.yRot += (((item->speed >> 2) + 3)*ONE_DEGREE);
|
|
|
|
else
|
|
|
|
item->pos.xRot += (((item->speed >> 2) + 3)*ONE_DEGREE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aboveWater = true;
|
|
|
|
item->fallspeed += GRAVITY >> 1;
|
|
|
|
if (item->speed)
|
|
|
|
{
|
|
|
|
item->pos.zRot += (((item->speed >> 2) + 7)*ONE_DEGREE);
|
|
|
|
if (item->requiredAnimState)
|
|
|
|
item->pos.yRot += (((item->speed >> 1) + 7)*ONE_DEGREE);
|
|
|
|
else
|
|
|
|
item->pos.xRot += (((item->speed >> 1) + 7)*ONE_DEGREE);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*phd_PushUnitMatrix();
|
|
|
|
|
|
|
|
*(phd_mxptr + M03) = 0;
|
|
|
|
*(phd_mxptr + M13) = 0;
|
|
|
|
*(phd_mxptr + M23) = 0;
|
|
|
|
|
|
|
|
phd_RotYXZ(item->pos.yRot, item->pos.xRot, item->pos.zRot);
|
|
|
|
phd_TranslateRel(0, 0, -64);*/
|
2018-11-02 13:02:10 +01:00
|
|
|
/*#
|
|
|
|
wx = (*(phd_mxptr + M03) >> W2V_SHIFT);
|
|
|
|
wy = (*(phd_mxptr + M13) >> W2V_SHIFT);
|
|
|
|
wz = (*(phd_mxptr + M23) >> W2V_SHIFT);
|
|
|
|
endif
|
|
|
|
phd_PopMatrix();*/
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
D3DXMATRIX transform;
|
|
|
|
D3DXMATRIX translation;
|
|
|
|
D3DXMATRIX rotation;
|
|
|
|
|
|
|
|
D3DXMatrixRotationYawPitchRoll(&rotation, TR_ANGLE_TO_RAD(item->pos.yRot), TR_ANGLE_TO_RAD(item->pos.xRot),
|
|
|
|
TR_ANGLE_TO_RAD(item->pos.zRot));
|
|
|
|
D3DXMatrixTranslation(&translation, 0, 0, -64);
|
|
|
|
D3DXMatrixMultiply(&transform, &rotation, &translation);
|
2018-11-02 13:02:10 +01:00
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
__int32 wx = transform._14;
|
|
|
|
__int32 wy = transform._24;
|
|
|
|
__int32 wz = transform._34;
|
|
|
|
|
|
|
|
if (item->speed && aboveWater)
|
|
|
|
TriggerRocketSmoke(wx + item->pos.xPos, wy + item->pos.yPos, wz + item->pos.zPos, -1);
|
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
xv = ((item->speed * SIN(item->goalAnimState)) >> W2V_SHIFT);
|
|
|
|
yv = item->fallspeed;
|
|
|
|
zv = ((item->speed * COS(item->goalAnimState)) >> W2V_SHIFT);
|
|
|
|
|
|
|
|
item->pos.xPos += xv;
|
|
|
|
item->pos.yPos += yv;
|
|
|
|
item->pos.zPos += zv;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
__int16 sYrot = item->pos.yRot;
|
|
|
|
item->pos.yRot = item->goalAnimState;
|
2018-11-02 13:02:10 +01:00
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
DoProperDetection(itemNumber, oldX, oldY, oldZ, xv, yv, zv);
|
2018-11-02 13:02:10 +01:00
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
item->goalAnimState = item->pos.yRot;
|
|
|
|
item->pos.yRot = sYrot;
|
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
/*if (Rooms[item->roomNumber].flags & 1 && abovewater)
|
2018-11-01 22:45:59 +01:00
|
|
|
{
|
|
|
|
splash_setup.x = item->pos.xPos;
|
|
|
|
splash_setup.y = room[item->roomNumber].maxceiling;
|
|
|
|
splash_setup.z = item->pos.zPos;
|
|
|
|
splash_setup.InnerXZoff = 16;
|
|
|
|
splash_setup.InnerXZsize = 12;
|
|
|
|
splash_setup.InnerYsize = -96;
|
|
|
|
splash_setup.InnerXZvel = 0xa0;
|
|
|
|
splash_setup.InnerYvel = -(item->fallspeed << 5) - (64 << 5);
|
|
|
|
splash_setup.InnerGravity = 0x80;
|
|
|
|
splash_setup.InnerFriction = 7;
|
|
|
|
splash_setup.MiddleXZoff = 24;
|
|
|
|
splash_setup.MiddleXZsize = 24;
|
|
|
|
splash_setup.MiddleYsize = -64;
|
|
|
|
splash_setup.MiddleXZvel = 0xe0;
|
|
|
|
splash_setup.MiddleYvel = -(item->fallspeed << 4) - (64 << 4);
|
|
|
|
splash_setup.MiddleGravity = 0x48;
|
|
|
|
splash_setup.MiddleFriction = 8;
|
|
|
|
splash_setup.OuterXZoff = 32;
|
|
|
|
splash_setup.OuterXZsize = 32;
|
|
|
|
splash_setup.OuterXZvel = 0x110;
|
|
|
|
splash_setup.OuterFriction = 9;
|
|
|
|
SetupSplash(&splash_setup);
|
|
|
|
}*/
|
|
|
|
|
|
|
|
// Has the timer ran out ? If so, explodes with blast radius.
|
|
|
|
|
|
|
|
__int32 radius = 0;
|
2018-11-02 13:02:10 +01:00
|
|
|
bool explode = false;
|
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
if (item->hitPoints)
|
|
|
|
{
|
|
|
|
item->hitPoints--;
|
|
|
|
if (!item->hitPoints)
|
|
|
|
{
|
|
|
|
radius = GRENADE_BLAST_RADIUS;
|
|
|
|
explode = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
// Get all surrounding rooms inside the radius
|
|
|
|
ROOM_INFO* room = &Rooms[item->roomNumber];
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
vector<__int32> items;
|
|
|
|
items.push_back(Lara.itemNumber);
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
if (room->door)
|
2018-11-01 22:45:59 +01:00
|
|
|
{
|
2018-11-02 13:02:10 +01:00
|
|
|
__int32 numPortals = *(room->door);
|
|
|
|
__int16* portals = room->door;
|
|
|
|
portals++;
|
|
|
|
|
|
|
|
for (__int32 i = 0; i < numPortals; i++)
|
2018-11-01 22:45:59 +01:00
|
|
|
{
|
2018-11-02 13:02:10 +01:00
|
|
|
__int16 roomNumber = *portals;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
room = &Rooms[roomNumber];
|
|
|
|
ITEM_INFO* target = NULL;
|
|
|
|
|
|
|
|
for (__int16 targetItemNumber = room->itemNumber; targetItemNumber != NO_ITEM; targetItemNumber = target->nextItem)
|
2018-11-01 22:45:59 +01:00
|
|
|
{
|
2018-11-02 13:02:10 +01:00
|
|
|
target = &Items[targetItemNumber];
|
|
|
|
|
|
|
|
// Ignore not collidable items
|
|
|
|
if (target != LaraItem && !target->collidable)
|
2018-11-01 22:45:59 +01:00
|
|
|
continue;
|
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
items.push_back(targetItemNumber);
|
|
|
|
}
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
portals += 16;
|
|
|
|
}
|
|
|
|
}
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
for (__int32 i = 0; i < items.size(); i++)
|
|
|
|
{
|
|
|
|
ITEM_INFO* target = &Items[items[i]];
|
|
|
|
|
|
|
|
// Destroy only smashable objects, shatters (check later), Lara and intelligent entities
|
|
|
|
if (target->objectNumber == ID_SMASH_OBJECT1 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT2 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT3 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT4 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT5 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT6 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT7 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT8 ||
|
|
|
|
target->objectNumber == ID_LARA ||
|
|
|
|
(Objects[target->objectNumber].intelligent &&
|
|
|
|
target->status != ITEM_INVISIBLE &&
|
|
|
|
Objects[target->objectNumber].collision)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
__int16* bounds = GetBestFrame(target);
|
|
|
|
if (item->pos.yPos + radius < target->pos.yPos + bounds[2] || item->pos.yPos - radius > target->pos.yPos + bounds[3])
|
|
|
|
continue;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
// get vector from target to bolt and check against x,z bounds
|
|
|
|
__int32 c = COS(target->pos.yRot);
|
|
|
|
__int32 s = SIN(target->pos.yRot);
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
__int32 x = item->pos.xPos - target->pos.xPos;
|
|
|
|
__int32 z = item->pos.zPos - target->pos.zPos;
|
|
|
|
__int32 rx = (c*x - s * z) >> W2V_SHIFT;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
__int32 ox = oldX - target->pos.xPos;
|
|
|
|
__int32 oz = oldZ - target->pos.zPos;
|
|
|
|
__int32 sx = (c*ox - s * oz) >> W2V_SHIFT;
|
2018-11-01 22:45:59 +01:00
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
if ((rx + radius < bounds[0] && sx + radius < bounds[0]) ||
|
|
|
|
(rx - radius > bounds[1] && sx - radius > bounds[1]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
__int32 rz = (c*z + s * x) >> W2V_SHIFT;
|
|
|
|
__int32 sz = (c*oz + s * ox) >> W2V_SHIFT;
|
|
|
|
|
|
|
|
if ((rz + radius < bounds[4] && sz + radius < bounds[4]) ||
|
|
|
|
(rz - radius > bounds[5] && sz - radius > bounds[5]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (target->objectNumber == ID_SMASH_OBJECT1 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT2 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT3 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT4 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT5 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT6 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT7 ||
|
|
|
|
target->objectNumber == ID_SMASH_OBJECT8)
|
|
|
|
{
|
|
|
|
SmashItem(items[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Hit the target
|
|
|
|
HitTarget(target, NULL, 500, 0);
|
|
|
|
|
|
|
|
// Update level statistics
|
|
|
|
Savegame.Level.AmmoHits++;
|
|
|
|
Savegame.Game.AmmoHits++;
|
|
|
|
|
|
|
|
// Check if entity is dead
|
|
|
|
if (target->hitPoints <= 0)
|
2018-11-01 22:45:59 +01:00
|
|
|
{
|
2018-11-02 13:02:10 +01:00
|
|
|
Savegame.Level.Kills++;
|
|
|
|
Savegame.Game.Kills++;
|
|
|
|
|
|
|
|
if (target->objectNumber != ID_LARA)
|
|
|
|
CreatureDie(items[i], 1);
|
|
|
|
else
|
2018-11-01 22:45:59 +01:00
|
|
|
{
|
2018-11-02 13:02:10 +01:00
|
|
|
LaraItem->hitPoints = -16384;
|
|
|
|
LaraItem->hitStatus = true;
|
2018-11-01 22:45:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-02 13:02:10 +01:00
|
|
|
if (!explode)
|
|
|
|
{
|
|
|
|
explode = true;
|
|
|
|
radius = GRENADE_BLAST_RADIUS;
|
|
|
|
break;
|
2018-11-01 22:45:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-02 13:02:10 +01:00
|
|
|
|
|
|
|
}
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
if (explode)
|
|
|
|
{
|
|
|
|
if (Rooms[item->roomNumber].flags & ENV_FLAG_WATER) // Just broken water surface ?
|
|
|
|
TriggerUnderwaterExplosion(item);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, item->roomNumber); // -2 = Set off a dynamic light controller.
|
|
|
|
for (__int32 x = 0; x < 2; x++)
|
|
|
|
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -1, 0, item->roomNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
AlertNearbyGuards(item);
|
|
|
|
SoundEffect(105, &item->pos, ENV_FLAG_PITCH_SHIFT | 0x1800000);
|
|
|
|
SoundEffect(106, &item->pos, 0); // Explosion ?
|
2018-11-02 13:02:10 +01:00
|
|
|
if (itemNumber != Lara.itemNumber)
|
|
|
|
KillItem(itemNumber);
|
2018-11-01 22:45:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void __cdecl DrawShotgun(__int32 weaponType)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item;
|
|
|
|
|
|
|
|
if (Lara.weaponItem == NO_ITEM)
|
|
|
|
{
|
|
|
|
Lara.weaponItem = CreateItem();
|
|
|
|
|
|
|
|
item = &Items[Lara.weaponItem];
|
|
|
|
|
|
|
|
item->objectNumber = WeaponObject(weaponType);
|
|
|
|
|
|
|
|
if (weaponType == WEAPON_ROCKET)
|
2018-11-02 07:35:19 +01:00
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex + 1;
|
2018-11-01 22:45:59 +01:00
|
|
|
else if (weaponType == WEAPON_GRENADE)
|
2018-11-02 07:35:19 +01:00
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex + ROCKET_DRAW_ANIM;
|
2018-11-01 22:45:59 +01:00
|
|
|
else
|
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex + HARPOON_DRAW_ANIM; // M16 too
|
|
|
|
|
|
|
|
item->frameNumber = Anims[item->animNumber].frameBase;
|
|
|
|
item->goalAnimState = 1;
|
|
|
|
item->currentAnimState = 1;
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
item->roomNumber = 255;
|
|
|
|
|
|
|
|
Lara.rightArm.frameBase = Objects[item->objectNumber].frameBase;
|
|
|
|
Lara.leftArm.frameBase = Lara.rightArm.frameBase;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item = &Items[Lara.weaponItem];
|
|
|
|
}
|
|
|
|
|
|
|
|
AnimateItem(item);
|
|
|
|
|
|
|
|
if (item->currentAnimState != 0 && item->currentAnimState != 6)
|
|
|
|
{
|
|
|
|
if (item->frameNumber - Anims[item->animNumber].frameBase == Weapons[weaponType].drawFrame)
|
|
|
|
{
|
|
|
|
DrawShotgunMeshes(weaponType);
|
|
|
|
}
|
|
|
|
else if (Lara.waterStatus == 1)
|
|
|
|
{
|
|
|
|
item->goalAnimState = 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ReadyShotgun(weaponType);
|
|
|
|
}
|
|
|
|
|
|
|
|
Lara.leftArm.frameBase = Lara.rightArm.frameBase = Anims[item->animNumber].framePtr;
|
|
|
|
Lara.leftArm.frameNumber = Lara.rightArm.frameNumber = item->frameNumber - Anims[item->animNumber].frameBase;
|
|
|
|
Lara.leftArm.animNumber = Lara.rightArm.animNumber = item->animNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __cdecl AnimateShotgun(__int32 weaponType)
|
|
|
|
{
|
|
|
|
bool harpoonFired = false;
|
|
|
|
|
|
|
|
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 = -16;
|
|
|
|
pos.y = 228;
|
|
|
|
pos.z = 32;
|
|
|
|
}
|
|
|
|
else if (SmokeWeapon == WEAPON_GRENADE)
|
|
|
|
{
|
|
|
|
pos.x = 0;
|
|
|
|
pos.y = 180;
|
|
|
|
pos.z = 80;
|
|
|
|
}
|
|
|
|
else if (SmokeWeapon == WEAPON_ROCKET)
|
|
|
|
{
|
|
|
|
pos.x = 0;
|
|
|
|
pos.y = 84;
|
|
|
|
pos.z = 72;
|
|
|
|
}
|
|
|
|
|
2018-11-02 07:35:19 +01:00
|
|
|
GetLaraJointPosition(&pos, UARM_L);
|
2018-11-01 22:45:59 +01:00
|
|
|
|
|
|
|
if (LaraItem->meshBits)
|
|
|
|
TriggerGunSmoke(pos.x, pos.y, pos.z, 0, 0, 0, 0, SmokeWeapon, SmokeCountL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ITEM_INFO* item = &Items[Lara.weaponItem];
|
|
|
|
bool running = (weaponType == WEAPON_HK && LaraItem->speed != 0);
|
|
|
|
|
|
|
|
switch (item->currentAnimState)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
HKFlag = 0;
|
|
|
|
HKTimer = 0;
|
|
|
|
HKFlag2 = 0;
|
|
|
|
|
|
|
|
if (Lara.waterStatus == 1 || running)
|
|
|
|
item->goalAnimState = 6;
|
|
|
|
else if ((!(TrInput & IN_ACTION) || Lara.target) && Lara.leftArm.lock == 0)
|
|
|
|
item->goalAnimState = 4;
|
|
|
|
else
|
|
|
|
item->goalAnimState = 2;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
HKFlag = 0;
|
|
|
|
HKTimer = 0;
|
|
|
|
HKFlag2 = 0;
|
|
|
|
|
|
|
|
if (Lara.waterStatus == 1 || running)
|
|
|
|
{
|
|
|
|
if ((!(TrInput & IN_ACTION) || Lara.target) && Lara.leftArm.lock == 0)
|
|
|
|
item->goalAnimState = 7;
|
|
|
|
else
|
|
|
|
item->goalAnimState = 8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
item->goalAnimState = 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
if (item->frameNumber == Anims[item->animNumber].frameBase)
|
|
|
|
{
|
|
|
|
item->goalAnimState = 4;
|
|
|
|
|
|
|
|
if (Lara.waterStatus != 1 && !running && !harpoonFired)
|
|
|
|
{
|
|
|
|
if ((TrInput & IN_ACTION) && (!Lara.target || Lara.leftArm.lock))
|
|
|
|
{
|
|
|
|
if (weaponType == WEAPON_HARPOON)
|
|
|
|
{
|
|
|
|
FireHarpoon();
|
|
|
|
if (!(g_LaraExtra.harpoonAmmo & 3))
|
|
|
|
harpoonFired = true;
|
|
|
|
}
|
|
|
|
else if (weaponType == WEAPON_ROCKET)
|
|
|
|
{
|
|
|
|
//FireRocket();
|
|
|
|
}
|
|
|
|
else if (weaponType == WEAPON_GRENADE)
|
|
|
|
{
|
|
|
|
FireGrenade();
|
|
|
|
}
|
|
|
|
else if (weaponType == WEAPON_CROSSBOW)
|
|
|
|
{
|
|
|
|
FireCrossbow(NULL);
|
|
|
|
}
|
|
|
|
else if (weaponType == WEAPON_HK)
|
|
|
|
{
|
|
|
|
FireHK(0);
|
|
|
|
HKFlag = 1;
|
|
|
|
|
|
|
|
if (Lara.HKtypeCarried & 2)
|
|
|
|
{
|
|
|
|
SoundEffect(14, 0, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SoundEffect(105, &LaraItem->pos, 83888140);
|
|
|
|
SoundEffect(68, &LaraItem->pos, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
FireShotgun();
|
|
|
|
|
|
|
|
item->goalAnimState = 2;
|
|
|
|
}
|
|
|
|
else if (Lara.leftArm.lock)
|
|
|
|
item->goalAnimState = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->goalAnimState != 2 && HKFlag && !(Lara.HKtypeCarried & 2))
|
|
|
|
{
|
|
|
|
StopSoundEffect(68);
|
|
|
|
SoundEffect(69, &LaraItem->pos, 0);
|
|
|
|
HKFlag = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (HKFlag)
|
|
|
|
{
|
|
|
|
if (Lara.HKtypeCarried & 2)
|
|
|
|
{
|
|
|
|
SoundEffect(14, 0, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SoundEffect(105, &LaraItem->pos, 83888140);
|
|
|
|
SoundEffect(68, &LaraItem->pos, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (weaponType == WEAPON_SHOTGUN && !(TrInput & IN_ACTION) && !Lara.leftArm.lock)
|
|
|
|
{
|
|
|
|
item->goalAnimState = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->frameNumber - Anims[item->animNumber].frameBase == 12 && weaponType == WEAPON_SHOTGUN)
|
|
|
|
TriggerGunShell(1, 370, 4);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
if (item->frameNumber - Anims[item->animNumber].frameBase == 0)
|
|
|
|
{
|
|
|
|
item->goalAnimState = 7;
|
|
|
|
|
|
|
|
if ((Lara.waterStatus == 1 || running) && !harpoonFired)
|
|
|
|
{
|
|
|
|
if ((TrInput & IN_ACTION) && (!Lara.target || Lara.leftArm.lock))
|
|
|
|
{
|
|
|
|
if (weaponType == WEAPON_HARPOON)
|
|
|
|
{
|
|
|
|
FireHarpoon();
|
|
|
|
if (!(g_LaraExtra.harpoonAmmo & 3))
|
|
|
|
harpoonFired = true;
|
|
|
|
}
|
|
|
|
else if (weaponType == WEAPON_HK && (!(Lara.HKtypeCarried & 0x18) || !HKTimer))
|
|
|
|
{
|
|
|
|
FireHK(1);
|
|
|
|
HKFlag = 1;
|
|
|
|
item->goalAnimState = 8;
|
|
|
|
if (Lara.HKtypeCarried & 2)
|
|
|
|
{
|
|
|
|
SoundEffect(14, 0, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SoundEffect(105, &LaraItem->pos, 83888140);
|
|
|
|
SoundEffect(68, &LaraItem->pos, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->goalAnimState = 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
item->goalAnimState = 8;
|
|
|
|
}
|
|
|
|
else if (Lara.leftArm.lock)
|
|
|
|
item->goalAnimState = 6;
|
|
|
|
}
|
|
|
|
else if (item->goalAnimState != 8 && HKFlag && !(Lara.HKtypeCarried & 2))
|
|
|
|
{
|
|
|
|
StopSoundEffect(68);
|
|
|
|
SoundEffect(69, &LaraItem->pos, 0);
|
|
|
|
HKFlag = 0;
|
|
|
|
}
|
|
|
|
else if (HKFlag)
|
|
|
|
{
|
|
|
|
if (Lara.HKtypeCarried & 2)
|
|
|
|
{
|
|
|
|
SoundEffect(14, 0, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SoundEffect(105, &LaraItem->pos, 83888140);
|
|
|
|
SoundEffect(68, &LaraItem->pos, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
AnimateItem(item);
|
|
|
|
|
|
|
|
Lara.leftArm.frameBase = Lara.rightArm.frameBase = Anims[item->animNumber].framePtr;
|
|
|
|
Lara.leftArm.frameNumber = Lara.rightArm.frameNumber = item->frameNumber - Anims[item->animNumber].frameBase;
|
|
|
|
Lara.leftArm.animNumber = Lara.rightArm.animNumber = item->animNumber;
|
|
|
|
}
|
|
|
|
|
2018-11-14 20:54:18 +01:00
|
|
|
void __cdecl ControlCrossbowBolt(__int16 itemNumber)
|
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
|
2018-11-14 20:54:18 +01:00
|
|
|
ITEM_INFO* item = &Items[itemNumber];
|
|
|
|
|
|
|
|
__int32 oldX = item->pos.xPos;
|
|
|
|
__int32 oldY = item->pos.yPos;
|
|
|
|
__int32 oldZ = item->pos.zPos;
|
|
|
|
__int16 roomNumber = item->roomNumber;
|
|
|
|
|
2018-11-18 22:53:38 +01:00
|
|
|
bool land = false;
|
|
|
|
bool explode = false;
|
2018-11-14 20:54:18 +01:00
|
|
|
|
|
|
|
if (Rooms[roomNumber].flags & ENV_FLAG_WATER)
|
|
|
|
{
|
|
|
|
if (item->speed > 64)
|
|
|
|
item->speed -= (item->speed >> 4);
|
|
|
|
if (GlobalCounter & 1)
|
|
|
|
CreateBubble(&item->pos, roomNumber, 4, 7);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
land = true;
|
2018-11-14 20:54:18 +01:00
|
|
|
}
|
|
|
|
|
2018-11-18 22:53:38 +01:00
|
|
|
//printf("COS XROT: %d\n", COS(item->pos.xRot) >> W2V_SHIFT);
|
|
|
|
//printf("SIN YROT: %d\n", SIN(item->pos.yRot) >> W2V_SHIFT);
|
|
|
|
|
|
|
|
item->pos.xPos += ((item->speed * COS(item->pos.xRot) >> W2V_SHIFT) * SIN(item->pos.yRot)) >> W2V_SHIFT;
|
2018-11-14 20:54:18 +01:00
|
|
|
item->pos.yPos += item->speed * SIN(-item->pos.xRot) >> W2V_SHIFT;
|
2018-11-18 22:53:38 +01:00
|
|
|
item->pos.zPos += ((item->speed * COS(item->pos.xRot) >> W2V_SHIFT) * COS(item->pos.yRot)) >> W2V_SHIFT;
|
2018-11-14 20:54:18 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (item->itemFlags[0] != 3)
|
|
|
|
{
|
|
|
|
ExplodeItemNode(item, 0, 0, 256);
|
|
|
|
KillItem(itemNumber);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-18 22:53:38 +01:00
|
|
|
explode = true;
|
2018-11-14 20:54:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (item->roomNumber != roomNumber)
|
|
|
|
ItemNewRoom(itemNumber, roomNumber);
|
|
|
|
|
2018-11-18 22:53:38 +01:00
|
|
|
if ((Rooms[item->roomNumber].flags & ENV_FLAG_WATER) && land)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
|
|
|
SetupRipple(item->pos.xPos, Rooms[item->roomNumber].minfloor, item->pos.zPos, (GetRandomControl() & 7) + 8, 0);
|
|
|
|
}
|
|
|
|
|
2018-11-18 22:53:38 +01:00
|
|
|
__int32 y = (explode ? 2048 : 128);
|
2018-11-14 20:54:18 +01:00
|
|
|
|
|
|
|
__int32 someIndex = 0;
|
|
|
|
bool foundCollidedObjects = false;
|
|
|
|
|
2018-11-18 22:53:38 +01:00
|
|
|
do
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
GetCollidedObjects(item, y, 1, &CollidedItems[0], &CollidedMeshes[0], 1);
|
|
|
|
|
|
|
|
if (!CollidedItems[0] && !CollidedMeshes[0])
|
2018-11-14 20:54:18 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
foundCollidedObjects = true;
|
|
|
|
|
2018-11-18 22:53:38 +01:00
|
|
|
if (item->itemFlags[0] != 3 || explode)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
if (CollidedItems)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
ITEM_INFO* currentItem = CollidedItems[0];
|
2018-11-14 20:54:18 +01:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
if (explode)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
if (item->objectNumber < ID_SMASH_OBJECT1 || item->objectNumber > ID_SMASH_OBJECT8)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
|
|
|
if (currentItem->objectNumber == ID_SWITCH_TYPE7 || currentItem->objectNumber == ID_SWITCH_TYPE8)
|
|
|
|
CrossbowHitSwitchType78(item, currentItem, 0);
|
|
|
|
else if (Objects[item->objectNumber].hitEffect)
|
|
|
|
DoGrenadeDamageOnBaddie(currentItem, item);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TriggerExplosionSparks(currentItem->pos.xPos, currentItem->pos.yPos, currentItem->pos.zPos, 3, -2, 0, currentItem->roomNumber);
|
|
|
|
currentItem->pos.yPos -= 128;
|
|
|
|
TriggerShockwave(¤tItem->pos, 19922992, 96, 411066368, 0, 0);
|
|
|
|
currentItem->pos.yPos += 128;
|
|
|
|
ExplodeItemNode(currentItem, 0, 0, 128);
|
2018-11-18 22:53:38 +01:00
|
|
|
__int16 currentItemNumber = (currentItem - CollidedItems[0]) / sizeof(ITEM_INFO);
|
2018-11-14 20:54:18 +01:00
|
|
|
SmashObject(currentItemNumber);
|
|
|
|
KillItem(currentItemNumber);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (currentItem->objectNumber == ID_SWITCH_TYPE7 || currentItem->objectNumber == ID_SWITCH_TYPE8)
|
|
|
|
{
|
|
|
|
CrossbowHitSwitchType78(item, currentItem, 1);
|
|
|
|
}
|
|
|
|
else if (Objects[currentItem->objectNumber].hitEffect)
|
|
|
|
{
|
|
|
|
HitTarget(currentItem, (GAME_VECTOR*)&item->pos, 5, 0);
|
|
|
|
if (item->itemFlags[0] == 2 && !Objects[currentItem->objectNumber].explodableMeshbits) // CHECK THIS
|
|
|
|
item->active = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentItem++;
|
|
|
|
} while (currentItem);
|
|
|
|
}
|
2018-11-18 22:53:38 +01:00
|
|
|
if (CollidedMeshes)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
MESH_INFO* currentMesh = CollidedMeshes[0];
|
2018-11-14 20:54:18 +01:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (currentMesh->staticNumber >= 50 && currentMesh->staticNumber < 58)
|
|
|
|
{
|
2018-11-18 22:53:38 +01:00
|
|
|
if (explode)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
|
|
|
TriggerExplosionSparks(currentMesh->x, currentMesh->y, currentMesh->z, 3, -2, 0, item->roomNumber);
|
|
|
|
currentMesh->y -= 128;
|
|
|
|
TriggerShockwave((PHD_3DPOS*)¤tMesh, 0xB00028, 64, 0x10806000, 0, 0);
|
|
|
|
currentMesh->y += 128;
|
|
|
|
}
|
|
|
|
ShatterObject((SHATTER_ITEM*)item, NULL, -128, item->roomNumber, 0);
|
|
|
|
SmashedMeshRoom[SmashedMeshCount] = item->roomNumber;
|
|
|
|
SmashedMesh[SmashedMeshCount] = currentMesh;
|
|
|
|
SmashedMeshCount++;
|
|
|
|
currentMesh->Flags &= ~1;
|
|
|
|
}
|
|
|
|
currentMesh++;
|
|
|
|
} while (currentMesh);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
someIndex++;
|
2018-11-18 22:53:38 +01:00
|
|
|
explode = true;
|
2018-11-14 20:54:18 +01:00
|
|
|
y = 2048;
|
2018-11-18 22:53:38 +01:00
|
|
|
} while (someIndex < 2);
|
|
|
|
|
|
|
|
if (!explode)
|
2018-11-14 20:54:18 +01:00
|
|
|
{
|
|
|
|
if (foundCollidedObjects)
|
|
|
|
{
|
|
|
|
ExplodeItemNode(item, 0, 0, 256);
|
|
|
|
KillItem(itemNumber);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
|
|
|
|
TriggerUnderwaterExplosion(item);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TriggerShockwave(&item->pos, 19922992, 96, 411066368, 0, 0);
|
|
|
|
item->pos.yPos += 128;
|
|
|
|
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, item->roomNumber);
|
|
|
|
for (__int32 j = 0; j < 2; j++)
|
|
|
|
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -1, 0, item->roomNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
AlertNearbyGuards(item);
|
|
|
|
SoundEffect(105, &item->pos, 0x1800004);
|
|
|
|
SoundEffect(106, &item->pos, 0);
|
|
|
|
|
|
|
|
if (foundCollidedObjects)
|
|
|
|
{
|
|
|
|
ExplodeItemNode(item, 0, 0, 256);
|
|
|
|
KillItem(itemNumber);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-15 18:39:13 +01:00
|
|
|
void __cdecl RifleHandler(__int32 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 != CAMERA_TYPE::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)
|
|
|
|
{
|
|
|
|
TriggerDynamics(
|
|
|
|
LaraItem->pos.xPos + (SIN(LaraItem->pos.yRot) >> 4) + GetRandomControl() - 128,
|
|
|
|
LaraItem->pos.yPos + (GetRandomControl() & 0x7F) - 575,
|
|
|
|
LaraItem->pos.zPos + (COS(LaraItem->pos.yRot) >> 4) + GetRandomControl() - 128,
|
|
|
|
12,
|
|
|
|
(GetRandomControl() & 0x3F) + 192,
|
|
|
|
(GetRandomControl() & 0x1F) + 128,
|
|
|
|
GetRandomControl() & 0x3F
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else if (weaponType == WEAPON_REVOLVER)
|
|
|
|
{
|
|
|
|
PHD_VECTOR pos;
|
|
|
|
|
|
|
|
pos.x = GetRandomControl() - 128;
|
|
|
|
pos.y = (GetRandomControl() & 0x7F) - 63;
|
|
|
|
pos.z = GetRandomControl() - 128;
|
|
|
|
|
|
|
|
GetLaraJointPosition(&pos, 11);
|
|
|
|
|
|
|
|
TriggerDynamics(pos.x, pos.y, pos.z, 12,
|
|
|
|
(GetRandomControl() & 0x3F) + 192,
|
|
|
|
(GetRandomControl() & 0x1F) + 128,
|
|
|
|
(GetRandomControl() & 0x3F));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void __cdecl FireCrossbow(PHD_3DPOS* pos)
|
|
|
|
{
|
|
|
|
__int16* ammos = GetAmmo(WEAPON_CROSSBOW);
|
|
|
|
if (*ammos <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Lara.hasFired = true;
|
|
|
|
|
|
|
|
__int16 itemNumber = CreateItem();
|
|
|
|
if (itemNumber != NO_ITEM)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &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)
|
2018-11-18 22:53:38 +01:00
|
|
|
(*ammos)--;
|
2018-11-15 18:39:13 +01:00
|
|
|
|
|
|
|
PHD_VECTOR jointPos;
|
|
|
|
jointPos.x = 0;
|
|
|
|
jointPos.y = 228;
|
|
|
|
jointPos.z = 32;
|
|
|
|
|
|
|
|
GetLaraJointPosition(&jointPos, 11);
|
|
|
|
|
|
|
|
item->roomNumber = LaraItem->roomNumber;
|
|
|
|
|
|
|
|
FLOOR_INFO* floor = GetFloor(jointPos.x, jointPos.y, jointPos.z, &item->roomNumber);
|
|
|
|
__int32 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);
|
|
|
|
|
|
|
|
if (Lara.crossbowTypeCarried & 0x08)
|
|
|
|
{
|
|
|
|
item->itemFlags[0] = 1;
|
|
|
|
}
|
|
|
|
else if (Lara.crossbowTypeCarried & 0x10)
|
|
|
|
{
|
|
|
|
item->itemFlags[0] = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->itemFlags[0] = 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
SoundEffect(235, 0, 0);
|
|
|
|
|
|
|
|
Savegame.Level.AmmoUsed++;
|
|
|
|
Savegame.Game.AmmoUsed++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-01 22:45:59 +01:00
|
|
|
void __cdecl Inject_Lara1Gun()
|
|
|
|
{
|
2018-11-12 20:15:55 +01:00
|
|
|
INJECT(0x0044EAC0, DrawShotgun);
|
|
|
|
INJECT(0x0044EE00, AnimateShotgun);
|
2018-11-15 18:39:13 +01:00
|
|
|
INJECT(0x0044DCC0, RifleHandler);
|
|
|
|
INJECT(0x00429ED0, FireCrossbow)
|
2018-11-01 22:45:59 +01:00
|
|
|
}
|