TombEngine/TR5Main/Objects/TR5/tr5_particle_enemies.cpp
2020-04-08 18:34:18 +02:00

773 lines
No EOL
16 KiB
C++

#include "../oldobjects.h"
#include "../../Game/sphere.h"
#include "../../Game/items.h"
#include "../../Game/tomb4fx.h"
#include "../../Game/effect2.h"
#include "../../Game/Box.h"
#include "../../Game/people.h"
#include "../../Game/debris.h"
#include "../../Game/draw.h"
#include "../../Game/control.h"
#include "../../Game/effects.h"
#include "../../Specific/setup.h"
int NextBat;
BAT_STRUCT* Bats;
int NextSpider;
SPIDER_STRUCT* Spiders;
int NextRat;
RAT_STRUCT* Rats;
void InitialiseLittleBats(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
if (item->pos.yRot == 0)
{
item->pos.zPos += 512;
}
else if (item->pos.yRot == -ANGLE(180))
{
item->pos.zPos -= 512;
}
else if (item->pos.yRot == -ANGLE(90))
{
item->pos.xPos -= 512;
}
else if (item->pos.yRot == ANGLE(90))
{
item->pos.xPos += 512;
}
if (Objects[ID_BATS_EMITTER].loaded)
ZeroMemory(Bats, NUM_BATS * sizeof(BAT_STRUCT));
//LOWORD(item) = sub_402F27(ebx0, Bats, 0, 1920);
}
void ControlLittleBats(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
if (TriggerActive(item))
{
if (item->triggerFlags)
{
TriggerLittleBat(item);
item->triggerFlags--;
}
else
{
KillItem(itemNumber);
}
}
}
short GetNextBat()
{
short batNumber = NextBat;
int index = 0;
BAT_STRUCT* bat = &Bats[NextBat];
while (bat->on)
{
if (batNumber == NUM_BATS - 1)
{
bat = (BAT_STRUCT*)Bats;
batNumber = 0;
}
else
{
batNumber++;
bat++;
}
index++;
if (index >= NUM_BATS)
return NO_ITEM;
}
NextBat = (batNumber + 1) & (NUM_BATS - 1);
return batNumber;
}
void TriggerLittleBat(ITEM_INFO* item)
{
short batNumber = GetNextBat();
if (batNumber != NO_ITEM)
{
BAT_STRUCT* bat = &Bats[batNumber];
bat->roomNumber = item->roomNumber;
bat->pos.xPos = item->pos.xPos;
bat->pos.yPos = item->pos.yPos;
bat->pos.zPos = item->pos.zPos;
bat->pos.yRot = (GetRandomControl() & 0x7FF) + item->pos.yRot + -ANGLE(180) - 1024;
bat->on = 1;
bat->flags = 0;
bat->pos.xRot = (GetRandomControl() & 0x3FF) - 512;
bat->speed = (GetRandomControl() & 0x1F) + 16;
bat->laraTarget = GetRandomControl() & 0x1FF;
bat->counter = 20 * ((GetRandomControl() & 7) + 15);
}
}
short GetNextSpider()
{
short spiderNum = NextSpider;
int i = 0;
SPIDER_STRUCT* spider = &Spiders[NextSpider];
while (spider->on)
{
if (spiderNum == NUM_SPIDERS - 1)
{
spider = &Spiders[0];
spiderNum = 0;
}
else
{
++spiderNum;
++spider;
}
if (++i >= NUM_SPIDERS)
return -1;
}
NextSpider = (spiderNum + 1) & 0x3F;
return spiderNum;
}
void ClearSpiders()
{
if (Objects[ID_SPIDERS_EMITTER].loaded)
{
ZeroMemory(Spiders, NUM_SPIDERS * sizeof(SPIDER_STRUCT));
NextSpider = 0;
FlipEffect = -1;
}
}
void ClearSpidersPatch(ITEM_INFO* item)
{
ClearSpiders();
}
void InitialiseSpiders(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
short flags = item->triggerFlags / -24;
item->pos.xRot = ANGLE(45);
item->itemFlags[1] = flags & 2;
item->itemFlags[2] = flags & 4;
item->itemFlags[0] = flags & 1;
item->triggerFlags = flags % 1000;
if (flags & 1)
{
ClearSpiders();
return;
}
if (item->pos.yRot > -28672 && item->pos.yRot < -4096)
{
item->pos.xPos += 512;
}
else if(item->pos.yRot > 4096 && item->pos.yRot < 28672)
{
item->pos.xPos -= 512;
}
if (item->pos.yRot > -8192 && item->pos.yRot < 8192)
{
item->pos.zPos -= 512;
}
else if (item->pos.yRot < -20480 || item->pos.yRot > 20480)
{
item->pos.zPos += 512;
}
ClearSpiders();
}
void ControlSpiders(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
if (item->triggerFlags)
{
if (!item->itemFlags[2] || !(GetRandomControl() & 0xF))
{
item->triggerFlags--;
if (item->itemFlags[2] && GetRandomControl() & 1)
item->itemFlags[2]--;
short spiderNum = GetNextSpider();
if (spiderNum != -1)
{
SPIDER_STRUCT* spider = &Spiders[spiderNum];
spider->pos.xPos = item->pos.xPos;
spider->pos.yPos = item->pos.yPos;
spider->pos.zPos = item->pos.zPos;
spider->roomNumber = item->roomNumber;
if (item->itemFlags[0])
{
spider->pos.yRot = 2 * GetRandomControl();
spider->fallspeed = -16 - (GetRandomControl() & 0x1F);
}
else
{
spider->fallspeed = 0;
spider->pos.yRot = item->pos.yRot + (GetRandomControl() & 0x3FFF) - ANGLE(45);
}
spider->pos.xRot = 0;
spider->pos.zRot = 0;
spider->on = true;
spider->flags = 0;
spider->speed = (GetRandomControl() & 0x1F) + 1;
}
}
}
}
short GetNextRat()
{
short ratNum = NextRat;
int i = 0;
RAT_STRUCT* rat = &Rats[NextRat];
while (rat->on)
{
if (ratNum == NUM_RATS - 1)
{
rat = &Rats[0];
ratNum = 0;
}
else
{
ratNum++;
rat++;
}
i++;
if (i >= NUM_RATS)
return NO_ITEM;
}
NextRat = (ratNum + 1) & 0x1F;
return ratNum;
}
void ControlLittleRats(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
if (item->triggerFlags)
{
if (!item->itemFlags[2] || !(GetRandomControl() & 0xF))
{
item->triggerFlags--;
if (item->itemFlags[2] && GetRandomControl() & 1)
item->itemFlags[2]--;
short ratNum = GetNextRat();
if (ratNum != -1)
{
RAT_STRUCT* rat = &Rats[ratNum];
rat->pos.xPos = item->pos.xPos;
rat->pos.yPos = item->pos.yPos;
rat->pos.zPos = item->pos.zPos;
rat->roomNumber = item->roomNumber;
if (item->itemFlags[0])
{
rat->pos.yRot = 2 * GetRandomControl();
rat->fallspeed = -16 - (GetRandomControl() & 31);
}
else
{
rat->fallspeed = 0;
rat->pos.yRot = item->pos.yRot + (GetRandomControl() & 0x3FFF) - ANGLE(45);
}
rat->pos.xRot = 0;
rat->pos.zRot = 0;
rat->on = 1;
rat->flags = GetRandomControl() & 30;
rat->speed = (GetRandomControl() & 31) + 1;
}
}
}
}
void ClearRats()
{
if (Objects[ID_RATS_EMITTER].loaded)
{
ZeroMemory(Rats, NUM_RATS * sizeof(RAT_STRUCT));
NextRat = 0;
FlipEffect = -1;
}
}
void InitialiseLittleRats(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
char flags = item->triggerFlags / 1000;
item->pos.xRot = ANGLE(45);
item->itemFlags[1] = flags & 2;
item->itemFlags[2] = flags & 4;
item->itemFlags[0] = flags & 1;
item->triggerFlags = item->triggerFlags % 1000;
if (flags & 1)
{
ClearRats();
return;
}
if (item->pos.yRot > -28672 && item->pos.yRot < -4096)
{
item->pos.xPos += 512;
}
else if (item->pos.yRot > 4096 && item->pos.yRot < 28672)
{
item->pos.xPos -= 512;
}
if (item->pos.yRot > -8192 && item->pos.yRot < 8192)
{
item->pos.zPos -= 512;
}
else if (item->pos.yRot < -20480 || item->pos.yRot > 20480)
{
item->pos.zPos += 512;
}
ClearRats();
}
void UpdateBats()
{
if (!Objects[ID_BATS_EMITTER].loaded)
return;
short* bounds = GetBoundsAccurate(LaraItem);
int x1 = LaraItem->pos.xPos + bounds[0] - (bounds[0] >> 2);
int x2 = LaraItem->pos.xPos + bounds[1] - (bounds[1] >> 2);
int y1 = LaraItem->pos.yPos + bounds[2] - (bounds[2] >> 2);
int y2 = LaraItem->pos.yPos + bounds[3] - (bounds[3] >> 2);
int z1 = LaraItem->pos.zPos + bounds[4] - (bounds[4] >> 2);
int z2 = LaraItem->pos.zPos + bounds[5] - (bounds[5] >> 2);
int minDistance = 0xFFFFFFF;
int minIndex = -1;
for (int i = 0; i < NUM_BATS; i++)
{
BAT_STRUCT* bat = &Bats[i];
if (!bat->on)
continue;
if ((Lara.burn || LaraItem->hitPoints <= 0)
&& bat->counter > 90
&& !(GetRandomControl() & 7))
bat->counter = 90;
if (!(--bat->counter))
{
bat->on = 0;
continue;
}
if (!(GetRandomControl() & 7))
{
bat->laraTarget = GetRandomControl() % 640 + 128;
bat->xTarget = (GetRandomControl() & 0x7F) - 64;
bat->zTarget = (GetRandomControl() & 0x7F) - 64;
}
short angles[2];
phd_GetVectorAngles(
LaraItem->pos.xPos + 8 * bat->xTarget - bat->pos.xPos,
LaraItem->pos.yPos - bat->laraTarget - bat->pos.yPos,
LaraItem->pos.zPos + 8 * bat->zTarget - bat->pos.zPos,
angles);
int distance = SQUARE(LaraItem->pos.zPos - bat->pos.zPos) +
SQUARE(LaraItem->pos.xPos - bat->pos.xPos);
if (distance < minDistance)
{
minDistance = distance;
minIndex = i;
}
distance = SQRT_ASM(distance) >> 3;
if (distance < 48)
distance = 48;
else if (distance > 128)
distance = 128;
if (bat->speed < distance)
bat->speed++;
else if (bat->speed > distance)
bat->speed--;
if (bat->counter > 90)
{
short speed = bat->speed << 7;
short xAngle = abs(angles[1] - bat->pos.xRot) >> 3;
short yAngle = abs(angles[0] - bat->pos.yRot) >> 3;
if (xAngle < -speed)
xAngle = -speed;
else if (xAngle > speed)
xAngle = speed;
if (yAngle < -speed)
yAngle = -speed;
else if (yAngle > speed)
yAngle = speed;
bat->pos.yRot += yAngle;
bat->pos.xRot += xAngle;
}
int sp = bat->speed * COS(bat->pos.xRot) >> W2V_SHIFT;
bat->pos.xPos += sp * SIN(bat->pos.yRot) >> W2V_SHIFT;
bat->pos.yPos += bat->speed * SIN(-bat->pos.xRot) >> W2V_SHIFT;
bat->pos.zPos += sp * COS(bat->pos.yRot) >> W2V_SHIFT;
if ((i % 2 == 0)
&& bat->pos.xPos > x1
&& bat->pos.xPos < x2
&& bat->pos.yPos > y1
&& bat->pos.yPos < y2
&& bat->pos.zPos > z1
&& bat->pos.zPos < z2)
{
TriggerBlood(bat->pos.xPos, bat->pos.yPos, bat->pos.zPos, 2 * GetRandomControl(), 2);
if (LaraItem->hitPoints > 0)
LaraItem->hitPoints -= 2;
}
}
if (minIndex != -1)
{
BAT_STRUCT* bat = &Bats[minIndex];
if (!(GetRandomControl() & 4))
SoundEffect(SFX_BATS_1, &bat->pos, 0);
}
}
void UpdateRats()
{
if (Objects[ID_RATS_EMITTER].loaded)
{
for (int i = 0; i < NUM_RATS; i++)
{
RAT_STRUCT* rat = &Rats[i];
if (rat->on)
{
int oldX = rat->pos.xPos;
int oldY = rat->pos.yPos;
int oldZ = rat->pos.zPos;
rat->pos.xPos += rat->speed * SIN(rat->pos.yRot) >> W2V_SHIFT;
rat->pos.yPos += rat->fallspeed;
rat->pos.zPos += rat->speed * COS(rat->pos.yRot) >> W2V_SHIFT;
rat->fallspeed += GRAVITY;
int dx = LaraItem->pos.xPos - rat->pos.xPos;
int dy = LaraItem->pos.yPos - rat->pos.yPos;
int dz = LaraItem->pos.zPos - rat->pos.zPos;
short angle;
if (rat->flags >= 170)
angle = rat->pos.yRot - (short)ATAN(dz, dx);
else
angle = (short)ATAN(dz, dx) - rat->pos.yRot;
if (abs(dx) < 85 && abs(dy) < 85 && abs(dz) < 85)
{
LaraItem->hitPoints--;
LaraItem->hitStatus = true;
}
// if life is even
if (rat->flags & 1)
{
// if rat is very near
if (abs(dz) + abs(dx) <= 1024)
{
if (rat->speed & 1)
rat->pos.yRot += 512;
else
rat->pos.yRot -= 512;
rat->speed = 48 - (abs(angle) >> 10);
}
else
{
if (rat->speed < (i & 31) + 24)
rat->speed++;
if (abs(angle) >= 2048)
{
if (angle >= 0)
rat->pos.yRot += 1024;
else
rat->pos.yRot -= 1024;
}
else
{
rat->pos.yRot += 8 * (Wibble - i);
}
}
}
__int16 oldRoomNumber = rat->roomNumber;
FLOOR_INFO* floor = GetFloor(rat->pos.xPos, rat->pos.yPos, rat->pos.zPos, &rat->roomNumber);
int height = GetFloorHeight(floor, rat->pos.xPos, rat->pos.yPos, rat->pos.zPos);
// if height is higher than 5 clicks
if (height < rat->pos.yPos - 1280 ||
height == NO_HEIGHT)
{
// if timer is higher than 170 time to disappear
if (rat->flags > 170)
{
rat->on = 0;
NextRat = 0;
}
if (angle <= 0)
rat->pos.yRot -= ANGLE(90);
else
rat->pos.yRot += ANGLE(90);
// reset rat to old position and disable fall
rat->pos.xPos = oldX;
rat->pos.yPos = oldY;
rat->pos.zPos = oldZ;
rat->fallspeed = 0;
}
else
{
// if height is lower than Y + 64
if (height >= rat->pos.yPos - 64)
{
// if rat is higher than floor
if (height >= rat->pos.yPos)
{
// if fallspeed is too much or life is ended then kill rat
if (rat->fallspeed >= 500 ||
rat->flags >= 200)
{
rat->on = 0;
NextRat = 0;
}
else
{
rat->pos.xRot = -128 * rat->fallspeed;
}
}
else
{
rat->pos.yPos = height;
rat->fallspeed = 0;
rat->flags |= 1;
}
}
else
{
// if block is higher than rat position then run vertically
rat->pos.xRot = 14336;
rat->pos.xPos = oldX;
rat->pos.yPos = oldY - 24;
rat->pos.zPos = oldZ;
rat->fallspeed = 0;
}
}
if (!(Wibble & 60))
rat->flags += 2;
ROOM_INFO* r = &Rooms[rat->roomNumber];
if (r->flags & ENV_FLAG_WATER)
{
rat->fallspeed = 0;
rat->speed = 16;
rat->pos.yPos = r->maxceiling + 50;
if (Rooms[oldRoomNumber].flags & ENV_FLAG_WATER)
{
if (!(GetRandomControl() & 0xF))
{
SetupRipple(rat->pos.xPos, r->maxceiling, rat->pos.zPos, (GetRandomControl() & 3) + 48, 2);
}
}
else
{
AddWaterSparks(rat->pos.xPos, r->maxceiling, rat->pos.zPos, 16);
SetupRipple(rat->pos.xPos, r->maxceiling, rat->pos.zPos, (GetRandomControl() & 3) + 48, 2);
SoundEffect(SFX_RATSPLASH, &rat->pos, 0);
}
}
if (!i && !(GetRandomControl() & 4))
SoundEffect(SFX_RATS_1, &rat->pos, 0);
}
}
}
}
void UpdateSpiders()
{
if (Objects[ID_SPIDERS_EMITTER].loaded)
{
for (int i = 0; i < NUM_SPIDERS; i++)
{
SPIDER_STRUCT* spider = &Spiders[i];
if (spider->on)
{
int x = spider->pos.xPos;
int y = spider->pos.yPos;
int z = spider->pos.zPos;
spider->pos.xPos += spider->speed * SIN(spider->pos.yRot) >> W2V_SHIFT;
spider->pos.yPos += spider->fallspeed;
spider->pos.zPos += spider->speed * COS(spider->pos.yRot) >> W2V_SHIFT;
spider->fallspeed += GRAVITY;
int dx = LaraItem->pos.xPos - spider->pos.xPos;
int dy = LaraItem->pos.yPos - spider->pos.yPos;
int dz = LaraItem->pos.zPos - spider->pos.zPos;
short angle = ATAN(dz, dx) - spider->pos.yRot;
if (abs(dx) < 85 && abs(dy) < 85 && abs(dz) < 85)
{
LaraItem->hitPoints -= 3;
LaraItem->hitStatus = true;
TriggerBlood(spider->pos.xPos, spider->pos.yPos, spider->pos.zPos, spider->pos.yRot, 1);
}
if (spider->flags)
{
if (abs(dx) + abs(dz) <= 768)
{
if (spider->speed & 1)
spider->pos.yRot += 512;
else
spider->pos.yRot -= 512;
spider->speed = 48 - (abs(angle) >> 10);
}
else
{
if (spider->speed < (i & 0x1F) + 24)
spider->speed++;
if (abs(angle) >= 2048)
{
if (angle >= 0)
spider->pos.yRot += 1024;
else
spider->pos.yRot -= 1024;
}
else
{
spider->pos.yRot += 8 * (Wibble - i);
}
}
}
FLOOR_INFO* floor = GetFloor(spider->pos.xPos, spider->pos.yPos, spider->pos.zPos, &spider->roomNumber);
int height = GetFloorHeight(floor, spider->pos.xPos, spider->pos.yPos, spider->pos.zPos);
if (height >= spider->pos.yPos - 1280 || height == -32512)
{
if (height >= spider->pos.yPos - 64)
{
if (spider->pos.yPos <= height)
{
if (spider->fallspeed >= 500)
{
spider->on = false;
NextSpider = 0;
}
else
{
spider->pos.xRot = -128 * spider->fallspeed;
}
}
else
{
spider->pos.yPos = height;
spider->fallspeed = 0;
spider->flags = 1;
}
}
else
{
spider->pos.xRot = 14336;
spider->pos.xPos = x;
spider->pos.yPos = y - 8;
spider->pos.zPos = z;
spider->fallspeed = 0;
if (!(GetRandomControl() & 0x1F))
spider->pos.yRot += -ANGLE(180);
}
}
else
{
if (angle <= 0)
spider->pos.yRot -= ANGLE(90);
else
spider->pos.yRot += ANGLE(90);
spider->pos.xPos = x;
spider->pos.yPos = y;
spider->pos.zPos = z;
spider->fallspeed = 0;
}
if (spider->pos.yPos < Rooms[spider->roomNumber].maxceiling + 50)
{
spider->fallspeed = 1;
spider->pos.yRot += -32768;
spider->pos.yPos = Rooms[spider->roomNumber].maxceiling + 50;
}
if (!i && !(GetRandomControl() & 4))
SoundEffect(SFX_BEETLES, &spider->pos, 0);
}
}
}
}