mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-16 09:21:12 +00:00
519 lines
14 KiB
C++
519 lines
14 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 "compat.h"
|
|
#include "build.h"
|
|
|
|
#include "blood.h"
|
|
|
|
BEGIN_BLD_NS
|
|
|
|
struct GIBFX
|
|
{
|
|
FX_ID fxId;
|
|
int at1;
|
|
int chance;
|
|
int at9;
|
|
int atd;
|
|
int at11;
|
|
};
|
|
|
|
|
|
struct GIBTHING
|
|
{
|
|
int type;
|
|
int Kills;
|
|
int chance;
|
|
int atc;
|
|
int at10;
|
|
};
|
|
|
|
struct GIBLIST
|
|
{
|
|
GIBFX *gibFX;
|
|
int Kills;
|
|
GIBTHING *at8;
|
|
int atc;
|
|
int at10;
|
|
};
|
|
|
|
GIBFX gibFxGlassT[] = {
|
|
{ FX_18, 0, 65536, 3, 200, 400 },
|
|
{ FX_31, 0, 32768, 5, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxGlassS[] = {
|
|
{ FX_18, 0, 65536, 8, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxBurnShard[] = {
|
|
{ FX_16, 0, 65536, 12, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxWoodShard[] = {
|
|
{ FX_17, 0, 65536, 12, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxMetalShard[] = {
|
|
{ FX_30, 0, 65536, 12, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxFireSpark[] = {
|
|
{ FX_14, 0, 65536, 8, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxShockSpark[] = {
|
|
{ FX_15, 0, 65536, 8, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxBloodChunks[] = {
|
|
{ FX_13, 0, 65536, 8, 90, 600 }
|
|
};
|
|
|
|
GIBFX gibFxBubblesS[] = {
|
|
{ FX_25, 0, 65536, 8, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxBubblesM[] = {
|
|
{ FX_24, 0, 65536, 8, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxBubblesL[] = {
|
|
{ FX_23, 0, 65536, 8, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxIcicles[] = {
|
|
{ FX_31, 0, 65536, 15, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxGlassCombo1[] = {
|
|
{ FX_18, 0, 65536, 15, 200, 400 },
|
|
{ FX_31, 0, 65536, 10, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxGlassCombo2[] = {
|
|
{ FX_18, 0, 65536, 5, 200, 400 },
|
|
{ FX_20, 0, 53248, 5, 200, 400 },
|
|
{ FX_21, 0, 53248, 5, 200, 400 },
|
|
{ FX_19, 0, 53248, 5, 200, 400 },
|
|
{ FX_22, 0, 53248, 5, 200, 400 }
|
|
};
|
|
|
|
GIBFX gibFxWoodCombo[] = {
|
|
{ FX_16, 0, 65536, 8, 500, 1000 },
|
|
{ FX_17, 0, 65536, 8, 500, 1000 },
|
|
{ FX_14, 0, 65536, 8, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxMedicCombo[] = {
|
|
{ FX_18, 0, 32768, 7, 200, 400 },
|
|
{ FX_30, 0, 65536, 7, 500, 1000 },
|
|
{ FX_13, 0, 65536, 10, 90, 600 },
|
|
{ FX_14, 0, 32768, 7, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxFlareSpark[] = {
|
|
{ FX_28, 0, 32768, 15, 128, -128 }
|
|
};
|
|
|
|
GIBFX gibFxBloodBits[] = {
|
|
{ FX_13, 0, 45056, 8, 90, 600 }
|
|
};
|
|
|
|
GIBFX gibFxRockShards[] = {
|
|
{ FX_46, 0, 65536, 10, 300, 800 },
|
|
{ FX_31, 0, 32768, 10, 200, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxPaperCombo1[] = {
|
|
{ FX_47, 0, 65536, 12, 300, 600 },
|
|
{ FX_14, 0, 65536, 8, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFxPlantCombo1[] = {
|
|
{ FX_44, 0, 45056, 8, 400, 800 },
|
|
{ FX_45, 0, 45056, 8, 300, 800 },
|
|
{ FX_14, 0, 45056, 6, 500, 1000 }
|
|
};
|
|
|
|
GIBFX gibFx13BBA8[] = {
|
|
{ FX_49, 0, 65536, 4, 80, 300 }
|
|
};
|
|
|
|
GIBFX gibFx13BBC0[] = {
|
|
{ FX_50, 0, 65536, 4, 80, 0 }
|
|
};
|
|
|
|
GIBFX gibFx13BBD8[] = {
|
|
{ FX_50, 0, 65536, 20, 800, -40 },
|
|
{ FX_15, 0, 65536, 15, 400, 10 }
|
|
};
|
|
|
|
GIBFX gibFx13BC04[] = {
|
|
{ FX_32, 0, 65536, 8, 100, 0 }
|
|
};
|
|
|
|
GIBFX gibFx13BC1C[] = {
|
|
{ FX_56, 0, 65536, 8, 100, 0 }
|
|
};
|
|
|
|
GIBTHING gibHuman[] = {
|
|
{ 425, 1454, 917504, 300, 900 },
|
|
{ 425, 1454, 917504, 300, 900 },
|
|
{ 425, 1267, 917504, 300, 900 },
|
|
{ 425, 1267, 917504, 300, 900 },
|
|
{ 425, 1268, 917504, 300, 900 },
|
|
{ 425, 1269, 917504, 300, 900 },
|
|
{ 425, 1456, 917504, 300, 900 }
|
|
};
|
|
|
|
GIBTHING gibMime[] = {
|
|
{ 425, 2405, 917504, 300, 900 },
|
|
{ 425, 2405, 917504, 300, 900 },
|
|
{ 425, 2404, 917504, 300, 900 },
|
|
{ 425, 1268, 32768, 300, 900 },
|
|
{ 425, 1269, 32768, 300, 900 },
|
|
{ 425, 1456, 32768, 300, 900 },
|
|
};
|
|
|
|
GIBTHING gibHound[] = {
|
|
{ 425, 1326, 917504, 300, 900 },
|
|
{ 425, 1268, 32768, 300, 900 },
|
|
{ 425, 1269, 32768, 300, 900 },
|
|
{ 425, 1456, 32768, 300, 900 }
|
|
};
|
|
|
|
GIBTHING gibFleshGargoyle[] = {
|
|
{ 425, 1369, 917504, 300, 900 },
|
|
{ 425, 1361, 917504, 300, 900 },
|
|
{ 425, 1268, 32768, 300, 900 },
|
|
{ 425, 1269, 32768, 300, 900 },
|
|
{ 425, 1456, 32768, 300, 900 }
|
|
};
|
|
|
|
GIBTHING gibAxeZombieHead[] = {
|
|
{ 427, 3405, 917504, 0, 0 }
|
|
};
|
|
|
|
GIBLIST gibList[] = {
|
|
{ gibFxGlassT, 2, NULL, 0, 300 },
|
|
{ gibFxGlassS, 1, NULL, 0, 300 },
|
|
{ gibFxBurnShard, 1, NULL, 0, 0 },
|
|
{ gibFxWoodShard, 1, NULL, 0, 0 },
|
|
{ gibFxMetalShard, 1, NULL, 0, 0 },
|
|
{ gibFxFireSpark, 1, NULL, 0, 0 },
|
|
{ gibFxShockSpark, 1, NULL, 0, 0 },
|
|
{ gibFxBloodChunks, 1, NULL, 0, 0 },
|
|
{ gibFxBubblesS, 1, NULL, 0, 0 },
|
|
{ gibFxBubblesM, 1, NULL, 0, 0 },
|
|
{ gibFxBubblesL, 1, NULL, 0, 0 },
|
|
{ gibFxIcicles, 1, NULL, 0, 0 },
|
|
{ gibFxGlassCombo1, 2, NULL, 0, 300 },
|
|
{ gibFxGlassCombo2, 5, NULL, 0, 300 },
|
|
{ gibFxWoodCombo, 3, NULL, 0, 0 },
|
|
{ NULL, 0, gibHuman, 7, 0 },
|
|
{ gibFxMedicCombo, 4, NULL, 0, 0 },
|
|
{ gibFxFlareSpark, 1, NULL, 0, 0 },
|
|
{ gibFxBloodBits, 1, NULL, 0, 0 },
|
|
{ gibFxRockShards, 2, NULL, 0, 0 },
|
|
{ gibFxPaperCombo1, 2, NULL, 0, 0 },
|
|
{ gibFxPlantCombo1, 3, NULL, 0, 0 },
|
|
{ gibFx13BBA8, 1, NULL, 0, 0 },
|
|
{ gibFx13BBC0, 1, NULL, 0, 0 },
|
|
{ gibFx13BBD8, 2, NULL, 0, 0 },
|
|
{ gibFx13BC04, 1, NULL, 0, 0 },
|
|
{ gibFx13BC1C, 1, NULL, 0, 0 },
|
|
{ NULL, 0, gibAxeZombieHead, 1, 0 },
|
|
{ NULL, 0, gibMime, 6, 0 },
|
|
{ NULL, 0, gibHound, 4, 0 },
|
|
{ NULL, 0, gibFleshGargoyle, 5, 0 },
|
|
};
|
|
|
|
void gibCalcWallArea(int a1, int &a2, int &a3, int &a4, int &a5, int &a6, int &a7, int &a8)
|
|
{
|
|
walltype *pWall = &wall[a1];
|
|
a2 = (pWall->x+wall[pWall->point2].x)>>1;
|
|
a3 = (pWall->y+wall[pWall->point2].y)>>1;
|
|
int nSector = sectorofwall(a1);
|
|
int32_t ceilZ, floorZ;
|
|
getzsofslope(nSector, a2, a3, &ceilZ, &floorZ);
|
|
int32_t ceilZ2, floorZ2;
|
|
getzsofslope(pWall->nextsector, a2, a3, &ceilZ2, &floorZ2);
|
|
ceilZ = ClipLow(ceilZ, ceilZ2);
|
|
floorZ = ClipHigh(floorZ, floorZ2);
|
|
a7 = floorZ-ceilZ;
|
|
a5 = wall[pWall->point2].x-pWall->x;
|
|
a6 = wall[pWall->point2].y-pWall->y;
|
|
a8 = (a7>>8)*approxDist(a5>>4, a6>>4);
|
|
a4 = (ceilZ+floorZ)>>1;
|
|
}
|
|
|
|
int ChanceToCount(int a1, int a2)
|
|
{
|
|
int vb = a2;
|
|
if (a1 < 0x10000)
|
|
{
|
|
for (int i = 0; i < a2; i++)
|
|
if (!Chance(a1))
|
|
vb--;
|
|
}
|
|
return vb;
|
|
}
|
|
|
|
void GibFX(spritetype *pSprite, GIBFX *pGFX, CGibPosition *pPos, CGibVelocity *pVel)
|
|
{
|
|
int nSector = pSprite->sectnum;
|
|
if (adult_lockout && gGameOptions.nGameType == 0 && pGFX->fxId == FX_13)
|
|
return;
|
|
CGibPosition gPos(pSprite->x, pSprite->y, pSprite->z);
|
|
if (pPos)
|
|
gPos = *pPos;
|
|
int32_t ceilZ, floorZ;
|
|
getzsofslope(nSector, gPos.x, gPos.y, &ceilZ, &floorZ);
|
|
int nCount = ChanceToCount(pGFX->chance, pGFX->at9);
|
|
int dz1 = floorZ-gPos.z;
|
|
int dz2 = gPos.z-ceilZ;
|
|
int top, bottom;
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
for (int i = 0; i < nCount; i++)
|
|
{
|
|
if (!pPos && (pSprite->cstat&48) == 0)
|
|
{
|
|
int nAngle = Random(2048);
|
|
gPos.x = pSprite->x+MulScale(pSprite->clipdist<<2, Cos(nAngle), 30);
|
|
gPos.y = pSprite->y+MulScale(pSprite->clipdist<<2, Sin(nAngle), 30);
|
|
gPos.z = bottom-Random(bottom-top);
|
|
}
|
|
spritetype *pFX = gFX.fxSpawn(pGFX->fxId, nSector, gPos.x, gPos.y, gPos.z, 0);
|
|
if (pFX)
|
|
{
|
|
if (pGFX->at1 < 0)
|
|
pFX->pal = pSprite->pal;
|
|
if (pVel)
|
|
{
|
|
xvel[pFX->index] = pVel->vx+Random2(pGFX->atd);
|
|
yvel[pFX->index] = pVel->vy+Random2(pGFX->atd);
|
|
zvel[pFX->index] = pVel->vz-Random(pGFX->at11);
|
|
}
|
|
else
|
|
{
|
|
xvel[pFX->index] = Random2((pGFX->atd<<18)/120);
|
|
yvel[pFX->index] = Random2((pGFX->atd<<18)/120);
|
|
switch(pSprite->cstat&48)
|
|
{
|
|
case 16:
|
|
zvel[pFX->index] = Random2((pGFX->at11<<18)/120);
|
|
break;
|
|
default:
|
|
if (dz2 < dz1 && dz2 < 0x4000)
|
|
{
|
|
zvel[pFX->index] = 0;
|
|
}
|
|
else if (dz2 > dz1 && dz1 < 0x4000)
|
|
{
|
|
zvel[pFX->index] = -(int)Random((abs(pGFX->at11)<<18)/120);
|
|
}
|
|
else
|
|
{
|
|
if ((pGFX->at11<<18)/120 < 0)
|
|
zvel[pFX->index] = -(int)Random((abs(pGFX->at11)<<18)/120);
|
|
else
|
|
zvel[pFX->index] = Random2((pGFX->at11<<18)/120);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GibThing(spritetype *pSprite, GIBTHING *pGThing, CGibPosition *pPos, CGibVelocity *pVel)
|
|
{
|
|
if (adult_lockout && gGameOptions.nGameType <= 0)
|
|
switch (pGThing->type) {
|
|
case kThingBloodBits:
|
|
case kThingZombieHead:
|
|
return;
|
|
}
|
|
|
|
if (pGThing->chance == 65536 || Chance(pGThing->chance))
|
|
{
|
|
int nSector = pSprite->sectnum;
|
|
int top, bottom;
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
int x, y, z;
|
|
if (!pPos)
|
|
{
|
|
int nAngle = Random(2048);
|
|
x = pSprite->x+MulScale(pSprite->clipdist<<2, Cos(nAngle), 30);
|
|
y = pSprite->y+MulScale(pSprite->clipdist<<2, Sin(nAngle), 30);
|
|
z = bottom-Random(bottom-top);
|
|
}
|
|
else
|
|
{
|
|
x = pPos->x;
|
|
y = pPos->y;
|
|
z = pPos->z;
|
|
}
|
|
int32_t ceilZ, floorZ;
|
|
getzsofslope(nSector, x, y, &ceilZ, &floorZ);
|
|
int dz1 = floorZ-z;
|
|
int dz2 = z-ceilZ;
|
|
spritetype *pGib = actSpawnThing(nSector, x, y, z, pGThing->type);
|
|
assert(pGib != NULL);
|
|
if (pGThing->Kills > -1)
|
|
pGib->picnum = pGThing->Kills;
|
|
if (pVel)
|
|
{
|
|
xvel[pGib->index] = pVel->vx+Random2(pGThing->atc);
|
|
yvel[pGib->index] = pVel->vy+Random2(pGThing->atc);
|
|
zvel[pGib->index] = pVel->vz-Random(pGThing->at10);
|
|
}
|
|
else
|
|
{
|
|
xvel[pGib->index] = Random2((pGThing->atc<<18)/120);
|
|
yvel[pGib->index] = Random2((pGThing->atc<<18)/120);
|
|
switch (pSprite->cstat&48)
|
|
{
|
|
case 16:
|
|
zvel[pGib->index] = Random2((pGThing->at10<<18)/120);
|
|
break;
|
|
default:
|
|
if (dz2 < dz1 && dz2 < 0x4000)
|
|
{
|
|
zvel[pGib->index] = 0;
|
|
}
|
|
else if (dz2 > dz1 && dz1 < 0x4000)
|
|
{
|
|
zvel[pGib->index] = -(int)Random((pGThing->at10<<18)/120);
|
|
}
|
|
else
|
|
{
|
|
zvel[pGib->index] = Random2((pGThing->at10<<18)/120);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GibSprite(spritetype *pSprite, GIBTYPE nGibType, CGibPosition *pPos, CGibVelocity *pVel)
|
|
{
|
|
assert(pSprite != NULL);
|
|
assert(nGibType >= 0 && nGibType < kGibMax);
|
|
if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors)
|
|
return;
|
|
GIBLIST *pGib = &gibList[nGibType];
|
|
for (int i = 0; i < pGib->Kills; i++)
|
|
{
|
|
GIBFX *pGibFX = &pGib->gibFX[i];
|
|
assert(pGibFX->chance > 0);
|
|
GibFX(pSprite, pGibFX, pPos, pVel);
|
|
}
|
|
for (int i = 0; i < pGib->atc; i++)
|
|
{
|
|
GIBTHING *pGibThing = &pGib->at8[i];
|
|
assert(pGibThing->chance > 0);
|
|
GibThing(pSprite, pGibThing, pPos, pVel);
|
|
}
|
|
}
|
|
|
|
void GibFX(int nWall, GIBFX * pGFX, int a3, int a4, int a5, int a6, CGibVelocity * pVel)
|
|
{
|
|
assert(nWall >= 0 && nWall < numwalls);
|
|
walltype *pWall = &wall[nWall];
|
|
int nCount = ChanceToCount(pGFX->chance, pGFX->at9);
|
|
int nSector = sectorofwall(nWall);
|
|
for (int i = 0; i < nCount; i++)
|
|
{
|
|
int r1 = Random(a6);
|
|
int r2 = Random(a5);
|
|
int r3 = Random(a4);
|
|
spritetype *pGib = gFX.fxSpawn(pGFX->fxId, nSector, pWall->x+r3, pWall->y+r2, a3+r1, 0);
|
|
if (pGib)
|
|
{
|
|
if (pGFX->at1 < 0)
|
|
pGib->pal = pWall->pal;
|
|
if (!pVel)
|
|
{
|
|
xvel[pGib->index] = Random2((pGFX->atd<<18)/120);
|
|
yvel[pGib->index] = Random2((pGFX->atd<<18)/120);
|
|
zvel[pGib->index] = -(int)Random((pGFX->at11<<18)/120);
|
|
}
|
|
else
|
|
{
|
|
xvel[pGib->index] = Random2((pVel->vx<<18)/120);
|
|
yvel[pGib->index] = Random2((pVel->vy<<18)/120);
|
|
zvel[pGib->index] = -(int)Random((pVel->vz<<18)/120);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GibWall(int nWall, GIBTYPE nGibType, CGibVelocity *pVel)
|
|
{
|
|
assert(nWall >= 0 && nWall < numwalls);
|
|
assert(nGibType >= 0 && nGibType < kGibMax);
|
|
int cx, cy, cz, wx, wy, wz;
|
|
walltype *pWall = &wall[nWall];
|
|
cx = (pWall->x+wall[pWall->point2].x)>>1;
|
|
cy = (pWall->y+wall[pWall->point2].y)>>1;
|
|
int nSector = sectorofwall(nWall);
|
|
int32_t ceilZ, floorZ;
|
|
getzsofslope(nSector, cx, cy, &ceilZ, &floorZ);
|
|
int32_t ceilZ2, floorZ2;
|
|
getzsofslope(pWall->nextsector, cx, cy, &ceilZ2, &floorZ2);
|
|
ceilZ = ClipLow(ceilZ, ceilZ2);
|
|
floorZ = ClipHigh(floorZ, floorZ2);
|
|
wz = floorZ-ceilZ;
|
|
wx = wall[pWall->point2].x-pWall->x;
|
|
wy = wall[pWall->point2].y-pWall->y;
|
|
cz = (ceilZ+floorZ)>>1;
|
|
GIBLIST *pGib = &gibList[nGibType];
|
|
sfxPlay3DSound(cx, cy, cz, pGib->at10, nSector);
|
|
for (int i = 0; i < pGib->Kills; i++)
|
|
{
|
|
GIBFX *pGibFX = &pGib->gibFX[i];
|
|
assert(pGibFX->chance > 0);
|
|
GibFX(nWall, pGibFX, ceilZ, wx, wy, wz, pVel);
|
|
}
|
|
}
|
|
|
|
void gibPrecache()
|
|
{
|
|
for (int i = 0; i < kGibMax; i++)
|
|
{
|
|
auto const pThing = gibList[i].at8;
|
|
if (pThing)
|
|
{
|
|
for (int j = 0; j < gibList[i].atc; j++)
|
|
{
|
|
if (pThing[j].Kills >= 0)
|
|
tilePrecacheTile(pThing[j].Kills, -1, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
END_BLD_NS
|