raze-gles/source/blood/src/fx.cpp

358 lines
13 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h" // Must come before everything else!
#include "build.h"
#include "common_game.h"
#include "actor.h"
#include "blood.h"
#include "callback.h"
#include "config.h"
#include "db.h"
#include "eventq.h"
#include "fx.h"
#include "gameutil.h"
#include "levels.h"
#include "seq.h"
#include "trig.h"
#include "view.h"
BEGIN_BLD_NS
CFX gFX;
struct FXDATA {
CALLBACK_ID funcID; // callback
char at1; // detail
short at2; // seq
short at4; // flags
int at6; // gravity
int ata; // air drag
int ate;
short at12; // picnum
unsigned char at14; // xrepeat
unsigned char at15; // yrepeat
short at16; // cstat
signed char at18; // shade
char at19; // pal
};
FXDATA gFXData[] = {
{ kCallbackNone, 0, 49, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 0, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 0, 51, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 0, 52, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 0, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 0, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 0, 45, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 0, 46, 1, -128, 8192, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 2, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 2, 42, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 2, 43, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 48, 3, -256, 8192, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 60, 3, -256, 8192, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackFXBloodBits, 2, 0, 1, 46603, 2048, 480, 2154, 40, 40, 0, -12, 0 },
{ kCallbackNone, 2, 0, 3, 46603, 5120, 480, 2269, 24, 24, 0, -128, 0 },
{ kCallbackNone, 2, 0, 3, 46603, 5120, 480, 1720, 24, 24, 0, -128, 0 },
{ kCallbackNone, 1, 0, 1, 58254, 3072, 480, 2280, 48, 48, 0, -128, 0 },
{ kCallbackNone, 1, 0, 1, 58254, 3072, 480, 3135, 48, 48, 0, -128, 0 },
{ kCallbackNone, 0, 0, 3, 58254, 1024, 480, 3261, 32, 32, 0, 0, 0 },
{ kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3265, 32, 32, 0, 0, 0 },
{ kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3269, 32, 32, 0, 0, 0 },
{ kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3273, 32, 32, 0, 0, 0 },
{ kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3277, 32, 32, 0, 0, 0 },
{ kCallbackNone, 2, 0, 1, -27962, 8192, 600, 1128, 16, 16, 514, -16, 0 }, // bubble 1
{ kCallbackNone, 2, 0, 1, -18641, 8192, 600, 1128, 12, 12, 514, -16, 0 }, // bubble 2
{ kCallbackNone, 2, 0, 1, -9320, 8192, 600, 1128, 8, 8, 514, -16, 0 }, // bubble 3
{ kCallbackNone, 2, 0, 1, -18641, 8192, 600, 1131, 32, 32, 514, -16, 0 },
{ kCallbackFXBloodBits, 2, 0, 3, 27962, 4096, 480, 733, 32, 32, 0, -16, 0 },
{ kCallbackNone, 1, 0, 3, 18641, 4096, 120, 2261, 12, 12, 0, -128, 0 },
{ kCallbackNone, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 0, 3, 58254, 3328, 480, 2185, 48, 48, 0, 0, 0 },
{ kCallbackNone, 0, 0, 3, 58254, 1024, 480, 2620, 48, 48, 0, 0, 0 },
{ kCallbackNone, 1, 55, 1, -13981, 5120, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 56, 1, -13981, 5120, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 57, 1, 0, 2048, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 58, 1, 0, 2048, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 2, 0, 0, 0, 0, 960, 956, 32, 32, 610, 0, 0 },
{ kCallbackFXBouncingSleeve, 2, 62, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackFXBouncingSleeve, 2, 63, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackFXBouncingSleeve, 2, 64, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackFXBouncingSleeve, 2, 65, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackFXBouncingSleeve, 2, 66, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackFXBouncingSleeve, 2, 67, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 0, 3, 0, 0, 0, 838, 16, 16, 80, -8, 0 },
{ kCallbackNone, 0, 0, 3, 34952, 8192, 0, 2078, 64, 64, 0, -8, 0 },
{ kCallbackNone, 0, 0, 3, 34952, 8192, 0, 1106, 64, 64, 0, -8, 0 },
{ kCallbackNone, 0, 0, 3, 58254, 3328, 480, 2406, 48, 48, 0, 0, 0 },
{ kCallbackNone, 1, 0, 3, 46603, 4096, 480, 3511, 64, 64, 0, -128, 0 },
{ kCallbackNone, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 2, 11, 3, -256, 8192, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 2, 11, 3, 0, 8192, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ kCallbackNone, 1, 30, 3, 0, 0, 0, 0, 40, 40, 80, -8, 0 },
{ kCallbackFXPodBloodSplat, 2, 0, 3, 27962, 4096, 480, 4023, 32, 32, 0, -16, 0 },
{ kCallbackFXPodBloodSplat, 2, 0, 3, 27962, 4096, 480, 4028, 32, 32, 0, -16, 0 },
{ kCallbackNone, 2, 0, 0, 0, 0, 480, 926, 32, 32, 610, -12, 0 },
{ kCallbackNone, 1, 70, 1, -13981, 5120, 0, 0, 0, 0, 0, 0, 0 }
};
void CFX::sub_73FB0(int nSprite)
{
if (nSprite < 0 || nSprite >= kMaxSprites)
return;
evKill(nSprite, 3);
if (sprite[nSprite].extra > 0)
seqKill(3, sprite[nSprite].extra);
DeleteSprite(nSprite);
}
void CFX::sub_73FFC(int nSprite)
{
if (nSprite < 0 || nSprite >= kMaxSprites)
return;
spritetype *pSprite = &sprite[nSprite];
if (pSprite->extra > 0)
seqKill(3, pSprite->extra);
if (pSprite->statnum != kStatFree)
actPostSprite(nSprite, kStatFree);
}
spritetype * CFX::fxSpawn(FX_ID nFx, int nSector, int x, int y, int z, unsigned int a6)
{
if (nSector < 0 || nSector >= numsectors)
return NULL;
int nSector2 = nSector;
if (!FindSector(x, y, z, &nSector2))
return NULL;
if (gbAdultContent && gGameOptions.nGameType <= 0)
{
switch (nFx)
{
case FX_0:
case FX_1:
case FX_2:
case FX_3:
case FX_13:
case FX_34:
case FX_35:
case FX_36:
return NULL;
default:
break;
}
}
if (nFx < 0 || nFx >= kFXMax)
return NULL;
FXDATA *pFX = &gFXData[nFx];
if (gStatCount[1] == 512)
{
int nSprite = headspritestat[kStatFX];;
while ((sprite[nSprite].flags & 32) && nSprite != -1)
nSprite = nextspritestat[nSprite];
if (nSprite == -1)
return NULL;
sub_73FB0(nSprite);
}
spritetype *pSprite = actSpawnSprite(nSector, x, y, z, 1, 0);
pSprite->type = nFx;
pSprite->picnum = pFX->at12;
pSprite->cstat |= pFX->at16;
pSprite->shade = pFX->at18;
pSprite->pal = pFX->at19;
qsprite_filler[pSprite->index] = pFX->at1;
if (pFX->at14 > 0)
pSprite->xrepeat = pFX->at14;
if (pFX->at15 > 0)
pSprite->yrepeat = pFX->at15;
if ((pFX->at4 & 1) && Chance(0x8000))
pSprite->cstat |= 4;
if ((pFX->at4 & 2) && Chance(0x8000))
pSprite->cstat |= 8;
if (pFX->at2)
{
int nXSprite = dbInsertXSprite(pSprite->index);
seqSpawn(pFX->at2, 3, nXSprite, -1);
}
if (a6 == 0)
a6 = pFX->ate;
if (a6)
evPost((int)pSprite->index, 3, a6+Random2(a6>>1), kCallbackRemove);
return pSprite;
}
void CFX::fxProcess(void)
{
for (int nSprite = headspritestat[kStatFX]; nSprite >= 0; nSprite = nextspritestat[nSprite])
{
spritetype *pSprite = &sprite[nSprite];
viewBackupSpriteLoc(nSprite, pSprite);
short nSector = pSprite->sectnum;
dassert(nSector >= 0 && nSector < kMaxSectors);
dassert(pSprite->type < kFXMax);
FXDATA *pFXData = &gFXData[pSprite->type];
actAirDrag(pSprite, pFXData->ata);
if (xvel[nSprite])
pSprite->x += xvel[nSprite]>>12;
if (yvel[nSprite])
pSprite->y += yvel[nSprite]>>12;
if (zvel[nSprite])
pSprite->z += zvel[nSprite]>>8;
// Weird...
if (xvel[nSprite] || (yvel[nSprite] && pSprite->z >= sector[pSprite->sectnum].floorz))
{
updatesector(pSprite->x, pSprite->y, &nSector);
if (nSector == -1)
{
sub_73FFC(nSprite);
continue;
}
if (getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y) <= pSprite->z)
{
if (pFXData->funcID < 0 || pFXData->funcID >= kCallbackMax)
{
sub_73FFC(nSprite);
continue;
}
dassert(gCallback[pFXData->funcID] != NULL);
gCallback[pFXData->funcID](nSprite);
continue;
}
if (nSector != pSprite->sectnum)
{
dassert(nSector >= 0 && nSector < kMaxSectors);
ChangeSpriteSect(nSprite, nSector);
}
}
if (xvel[nSprite] || yvel[nSprite] || zvel[nSprite])
{
int32_t floorZ, ceilZ;
getzsofslope(nSector, pSprite->x, pSprite->y, &ceilZ, &floorZ);
if (ceilZ > pSprite->z && !(sector[nSector].ceilingstat&1))
{
sub_73FFC(nSprite);
continue;
}
if (floorZ < pSprite->z)
{
if (pFXData->funcID < 0 || pFXData->funcID >= kCallbackMax)
{
sub_73FFC(nSprite);
continue;
}
dassert(gCallback[pFXData->funcID] != NULL);
gCallback[pFXData->funcID](nSprite);
continue;
}
}
zvel[nSprite] += pFXData->at6;
}
}
void fxSpawnBlood(spritetype *pSprite, int a2)
{
UNREFERENCED_PARAMETER(a2);
if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors)
return;
int nSector = pSprite->sectnum;
if (!FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector))
return;
if (gbAdultContent && gGameOptions.nGameType <= 0)
return;
spritetype *pBlood = gFX.fxSpawn(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pBlood)
{
pBlood->ang = 1024;
xvel[pBlood->index] = Random2(0x6aaaa);
yvel[pBlood->index] = Random2(0x6aaaa);
zvel[pBlood->index] = -Random(0x10aaaa)-100;
evPost(pBlood->index, 3, 8, kCallbackFXBloodSpurt);
}
}
void sub_746D4(spritetype *pSprite, int a2)
{
UNREFERENCED_PARAMETER(a2);
if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors)
return;
int nSector = pSprite->sectnum;
if (!FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector))
return;
if (gbAdultContent && gGameOptions.nGameType <= 0)
return;
spritetype *pSpawn;
if (pSprite->type == kDudePodGreen)
pSpawn = gFX.fxSpawn(FX_53, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
else
pSpawn = gFX.fxSpawn(FX_54, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pSpawn)
{
pSpawn->ang = 1024;
xvel[pSpawn->index] = Random2(0x6aaaa);
yvel[pSpawn->index] = Random2(0x6aaaa);
zvel[pSpawn->index] = -Random(0x10aaaa)-100;
evPost(pSpawn->index, 3, 8, kCallbackFXPodBloodSpray);
}
}
void fxSpawnEjectingBrass(spritetype *pSprite, int z, int a3, int a4)
{
int x = pSprite->x+mulscale28(pSprite->clipdist-4, Cos(pSprite->ang));
int y = pSprite->y+mulscale28(pSprite->clipdist-4, Sin(pSprite->ang));
x += mulscale30(a3, Cos(pSprite->ang+512));
y += mulscale30(a3, Sin(pSprite->ang+512));
spritetype *pBrass = gFX.fxSpawn((FX_ID)(FX_37+Random(3)), pSprite->sectnum, x, y, z, 0);
if (pBrass)
{
if (!VanillaMode())
pBrass->ang = Random(2047);
int nDist = (a4<<18)/120+Random2(((a4/4)<<18)/120);
int nAngle = pSprite->ang+Random2(56)+512;
xvel[pBrass->index] = mulscale30(nDist, Cos(nAngle));
yvel[pBrass->index] = mulscale30(nDist, Sin(nAngle));
zvel[pBrass->index] = zvel[pSprite->index]-(0x20000+(Random2(40)<<18)/120);
}
}
void fxSpawnEjectingShell(spritetype *pSprite, int z, int a3, int a4)
{
int x = pSprite->x+mulscale28(pSprite->clipdist-4, Cos(pSprite->ang));
int y = pSprite->y+mulscale28(pSprite->clipdist-4, Sin(pSprite->ang));
x += mulscale30(a3, Cos(pSprite->ang+512));
y += mulscale30(a3, Sin(pSprite->ang+512));
spritetype *pShell = gFX.fxSpawn((FX_ID)(FX_40+Random(3)), pSprite->sectnum, x, y, z, 0);
if (pShell)
{
if (!VanillaMode())
pShell->ang = Random(2047);
int nDist = (a4<<18)/120+Random2(((a4/4)<<18)/120);
int nAngle = pSprite->ang+Random2(56)+512;
xvel[pShell->index] = mulscale30(nDist, Cos(nAngle));
yvel[pShell->index] = mulscale30(nDist, Sin(nAngle));
zvel[pShell->index] = zvel[pSprite->index]-(0x20000+(Random2(20)<<18)/120);
}
}
END_BLD_NS