TombEngine/TR5Main/Game/effect.cpp

401 lines
8.6 KiB
C++
Raw Normal View History

#include "framework.h"
#include "effect.h"
#include "effect2.h"
2019-12-01 08:13:19 +01:00
#include "Lara.h"
#include "lot.h"
#include "tomb4fx.h"
#include "hair.h"
2019-12-01 08:13:19 +01:00
#include "draw.h"
#include "sphere.h"
#include "footprint.h"
#include "level.h"
#include "debris.h"
#include "setup.h"
#include "camera.h"
2020-04-24 19:15:05 +02:00
#include "savegame.h"
#include "sound.h"
#include "tr5_rats_emitter.h"
#include "tr5_bats_emitter.h"
#include "tr5_spider_emitter.h"
2020-06-15 00:32:58 -05:00
#include "pickup.h"
using std::function;
constexpr auto ITEM_RADIUS_YMAX = SECTOR(3);
int wf = 256;
2020-05-28 22:17:55 +02:00
using namespace T5M::Effects::Footprints;
2020-01-08 13:25:42 +01:00
short FXType;
FX_INFO* EffectList;
2020-01-08 13:25:42 +01:00
function<EffectFunction> effect_routines[59] =
{
turn180_effect,
floor_shake_effect,
PoseidonSFX,
LaraBubbles,
finish_level_effect,
ActivateCamera,
ActivateKey,
RubbleFX,
SwapCrowbar,
2020-06-15 00:32:58 -05:00
pickup,
SoundFlipEffect,
ExplosionFX,
lara_hands_free,
2020-07-23 15:29:02 -05:00
puzzle,
void_effect,
void_effect,
shoot_right_gun,
shoot_left_gun,
void_effect,
void_effect,
void_effect,
invisibility_on,
invisibility_off,
void_effect,
void_effect,
void_effect,
reset_hair,
void_effect,
SetFog,
void_effect,
LaraLocation,
ClearSpidersPatch,
AddFootprint,
void_effect, // resettest
void_effect,
void_effect,
void_effect,
void_effect,
void_effect,
void_effect,
void_effect,
void_effect,
void_effect,
void_effect,
void_effect,
LaraLocationPad,
2020-07-25 18:02:35 +02:00
KillActiveBaddies
};
2020-06-15 00:32:58 -05:00
void pickup(ITEM_INFO* item)
{
do_pickup();
}
2020-07-23 15:29:02 -05:00
void puzzle(ITEM_INFO* item)
{
do_puzzle();
}
// TODO: here are sound for lara footstep too !
void AddFootprint(ITEM_INFO* item)
{
if (item != LaraItem)
return;
FOOTPRINT_STRUCT footprint;
PHD_3DPOS footprintPosition;
if (CheckFootOnFloor(*item, LM_LFOOT, footprintPosition))
{
if (footprints.size() >= MAX_FOOTPRINTS)
footprints.pop_back();
memset(&footprint, 0, sizeof(FOOTPRINT_STRUCT));
footprint.pos = footprintPosition;
2020-01-07 16:19:54 +01:00
footprint.lifeStartFading = 30 * 10;
2020-01-07 17:07:45 +01:00
footprint.startOpacity = 64;
footprint.life = 30 * 20;
footprint.active = true;
footprints.push_front(footprint);
}
if (CheckFootOnFloor(*item, LM_RFOOT, footprintPosition))
{
if (footprints.size() >= MAX_FOOTPRINTS)
footprints.pop_back();
memset(&footprint, 0, sizeof(FOOTPRINT_STRUCT));
footprint.pos = footprintPosition;
2020-01-07 16:19:54 +01:00
footprint.lifeStartFading = 30*10;
2020-01-07 17:07:45 +01:00
footprint.startOpacity = 64;
footprint.life = 30 * 20;
footprint.active = true;
footprints.push_front(footprint);
}
}
void reset_hair(ITEM_INFO* item)
{
InitialiseHair();
}
void invisibility_off(ITEM_INFO* item)
{
item->status = ITEM_ACTIVE;
}
void invisibility_on(ITEM_INFO* item)
{
item->status = ITEM_INVISIBLE;
}
void SetFog(ITEM_INFO* item)//39A44(<), 39F44(<) (F)
{
FlipEffect = -1;
}
void shoot_left_gun(ITEM_INFO* item)//39A34(<), 39F34(<) (F)
{
Lara.leftArm.flash_gun = 3;
}
void shoot_right_gun(ITEM_INFO* item)//39A24(<), 39F24(<) (F)
{
Lara.rightArm.flash_gun = 3;
}
void lara_hands_free(ITEM_INFO* item)//39A18(<), 39F18(<) (F)
{
Lara.gunStatus = LG_NO_ARMS;
}
void KillActiveBaddies(ITEM_INFO* item)//39938(<), 39E38(<) (F)
{
if (NextItemActive != NO_ITEM)
{
short itemNum = NextItemActive;
ITEM_INFO* targetItem;
do
2019-12-01 08:13:19 +01:00
{
targetItem = &g_Level.Items[itemNum];
if (Objects[targetItem->objectNumber].intelligent)
2019-12-01 08:13:19 +01:00
{
targetItem->status = ITEM_INVISIBLE;
if (*(int*)&item != 0xABCDEF)
2019-12-01 08:13:19 +01:00
{
RemoveActiveItem(itemNum);
DisableBaddieAI(itemNum);
targetItem->flags |= IFLAG_INVISIBLE;
2019-12-01 08:13:19 +01:00
}
}
itemNum = targetItem->nextActive;
} while (itemNum != NO_ITEM);
2019-12-01 08:13:19 +01:00
}
FlipEffect = -1;
2019-12-01 08:13:19 +01:00
}
void LaraLocationPad(ITEM_INFO* item)//39710(<), 39C10(<) (F)
2019-12-01 08:13:19 +01:00
{
FlipEffect = -1;
Lara.location = TriggerTimer;
Lara.locationPad = TriggerTimer;
}
void LaraLocation(ITEM_INFO* item)//396D0(<), 39BD0(<) (F)
{
FlipEffect = -1;
Lara.location = TriggerTimer;
if (Lara.highestLocation < TriggerTimer)
Lara.highestLocation = TriggerTimer;
}
void ExplosionFX(ITEM_INFO* item)//39694(<), 39B94(<) (F)
{
SoundEffect(SFX_EXPLOSION1, NULL, 0);
Camera.bounce = -75;
FlipEffect = -1;
}
void SwapCrowbar(ITEM_INFO* item)//39638(<), 39B38(<) (F)
{
2020-07-03 07:05:33 +02:00
if (Lara.meshPtrs[LM_RHAND] == Objects[ID_LARA].meshIndex + LM_RHAND)
Lara.meshPtrs[LM_RHAND] = Objects[ID_LARA_CROWBAR_ANIM].meshIndex + LM_RHAND;
else
2020-07-03 07:05:33 +02:00
Lara.meshPtrs[LM_RHAND] = Objects[ID_LARA].meshIndex + LM_RHAND;
}
void ActivateKey(ITEM_INFO* item)//39624(<), 39B24(<) (F)
{
KeyTriggerActive = 1;
}
void ActivateCamera(ITEM_INFO* item)//39610(<), 39B10(<) (F)
{
KeyTriggerActive = 2;
}
void PoseidonSFX(ITEM_INFO* item)//395E0(<), 39AE0(<) (F)
{
SoundEffect(SFX_GRAB_OPEN, NULL, 0);
FlipEffect = -1;
}
void RubbleFX(ITEM_INFO* item)//39534(<), 39A34(<) (F)
{
int itemNumber = FindItemNumber(ID_EARTHQUAKE);
if (itemNumber != NO_ITEM)
{
ITEM_INFO* eq = &g_Level.Items[itemNumber];
AddActiveItem(itemNumber);
eq->status = ITEM_ACTIVE;
eq->flags |= IFLAG_ACTIVATION_MASK;
}
else
{
Camera.bounce = -150;
}
FlipEffect = -1;
}
void SoundFlipEffect(ITEM_INFO* item)//39500(<), 39A00(<) (F)
{
SoundEffect(TriggerTimer, NULL, 0);
FlipEffect = -1;
}
void floor_shake_effect(ITEM_INFO* item)//39410, 39910 (F)
{
int x = abs(item->pos.xPos - Camera.pos.x);
int y = abs(item->pos.yPos - Camera.pos.y);
int z = abs(item->pos.zPos - Camera.pos.z);
if (x < SECTOR(16) && y < SECTOR(16) && z < SECTOR(16))
{
Camera.bounce = 66 * ((x * x + y * y + z * z) / 256 - 0x100000) / 0x100000;
}
}
void turn180_effect(ITEM_INFO* item)//393F4(<), 398F4(<) (F)
{
item->pos.yRot -= ANGLE(180);
item->pos.xRot = -item->pos.xRot;
}
void finish_level_effect(ITEM_INFO* item)//393D4(<), 398D4(<) (F)
{
LevelComplete = CurrentLevel + 1;
}
void void_effect(ITEM_INFO* item)//393CC(<), 398CC(<) (F)
{
}
2019-12-22 19:01:36 +01:00
void ControlWaterfallMist(short itemNumber) // ControlWaterfallMist
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
int x, z;
if (item->pos.yRot == -ANGLE(180))
{
2020-04-25 16:23:53 +02:00
x = item->pos.xPos - (phd_sin(item->pos.yRot + ANGLE(180)) >> 3) + ((rcossin_tbl[2048] * wf) >> W2V_SHIFT);
z = item->pos.zPos - (phd_cos(item->pos.yRot + ANGLE(180)) >> 3) + ((rcossin_tbl[2049] * wf) >> W2V_SHIFT);
}
else
{
//3934C
2020-04-25 16:23:53 +02:00
x = item->pos.xPos - (phd_sin(item->pos.yRot + ANGLE(180)) >> 3) + ((phd_sin(item->pos.yRot + ANGLE(90)) * wf) >> W2V_SHIFT);
z = item->pos.zPos - (phd_cos(item->pos.yRot + ANGLE(180)) >> 3) + ((phd_cos(item->pos.yRot + ANGLE(90)) * wf) >> W2V_SHIFT);
}
//393A0
TriggerWaterfallMist(x, item->pos.yPos, z, item->pos.yRot + ANGLE(180));
SoundEffect(SFX_WATERFALL_LOOP, &item->pos, 0);
}
short DoBloodSplat(int x, int y, int z, short a4, short a5, short roomNumber)
{
short roomNum = roomNumber;
GetFloor(x, y, z, &roomNum);
if (g_Level.Rooms[roomNum].flags & ENV_FLAG_WATER)
TriggerUnderwaterBlood(x, y, z, a4);
else
TriggerBlood(x, y, z, a5 >> 4, a4);
return 0;
}
static bool ItemCollide(int value, int radius)
{
return value >= -radius && value <= radius;
}
static bool ItemInRange(int x, int z, int radius)
{
return (SQUARE(x) + SQUARE(z)) <= SQUARE(radius);
}
bool ItemNearLara(PHD_3DPOS* pos, int radius)
{
2020-07-25 18:02:35 +02:00
BOUNDING_BOX* bounds;
GAME_VECTOR target;
target.x = pos->xPos - LaraItem->pos.xPos;
target.y = pos->yPos - LaraItem->pos.yPos;
target.z = pos->zPos - LaraItem->pos.zPos;
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
return false;
if (!ItemCollide(target.x, radius) || !ItemCollide(target.z, radius))
return false;
if (!ItemInRange(target.x, target.z, radius))
return false;
2020-07-25 18:02:35 +02:00
bounds = GetBoundsAccurate(LaraItem);
if (target.y >= bounds->Y1 && target.y <= (bounds->Y2 + LARA_RAD))
return true;
return false;
}
bool ItemNearTarget(PHD_3DPOS* src, ITEM_INFO* target, int radius)
{
2020-07-25 18:02:35 +02:00
BOUNDING_BOX* bounds;
PHD_VECTOR pos;
pos.x = src->xPos - target->pos.xPos;
pos.y = src->yPos - target->pos.yPos;
pos.z = src->zPos - target->pos.zPos;
if (!ItemCollide(pos.y, ITEM_RADIUS_YMAX))
return false;
if (!ItemCollide(pos.x, radius) || !ItemCollide(pos.z, radius))
return false;
if (!ItemInRange(pos.x, pos.z, radius))
return false;
2020-07-25 18:02:35 +02:00
bounds = GetBoundsAccurate(target);
if (pos.y >= bounds->Y1 && pos.y <= bounds->Y2)
return true;
return false;
2019-12-01 08:13:19 +01:00
}
void Richochet(PHD_3DPOS* pos)
{
short angle = mGetAngle(pos->zPos, pos->xPos, LaraItem->pos.zPos, LaraItem->pos.xPos);
GAME_VECTOR target;
target.x = pos->xPos;
target.y = pos->yPos;
target.z = pos->zPos;
TriggerRicochetSpark(&target, angle / 16, 3, 0);
SoundEffect(SFX_LARA_RICOCHET, pos, 0);
}
void DoLotsOfBlood(int x, int y, int z, int speed, short direction, short roomNumber, int count)
{
for (int i = 0; i < count; i++)
{
DoBloodSplat(x + 256 - (GetRandomControl() * 512 / 0x8000),
y + 256 - (GetRandomControl() * 512 / 0x8000),
z + 256 - (GetRandomControl() * 512 / 0x8000),
speed, direction, roomNumber);
}
}