raze/source/games/blood/src/animatesprite.cpp
2021-12-25 21:28:45 +01:00

1003 lines
35 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 <stdlib.h>
#include <string.h>
#include "build.h"
#include "v_font.h"
#include "blood.h"
#include "zstring.h"
#include "razemenu.h"
#include "gstrings.h"
#include "v_2ddrawer.h"
#include "v_video.h"
#include "v_font.h"
#include "hw_voxels.h"
#include "gamefuncs.h"
#include "glbackend/glbackend.h"
BEGIN_BLD_NS
static fixed_t gCameraAng;
int dword_172CE0[16][3];
static void RotateYZ(int *, int *pY, int *pZ, int ang)
{
int oY, oZ, angSin, angCos;
oY = *pY;
oZ = *pZ;
angSin = Sin(ang);
angCos = Cos(ang);
*pY = dmulscale30r(oY,angCos,oZ,-angSin);
*pZ = dmulscale30r(oY,angSin,oZ,angCos);
}
static void RotateXZ(int *pX, int *, int *pZ, int ang)
{
int oX, oZ, angSin, angCos;
oX = *pX;
oZ = *pZ;
angSin = Sin(ang);
angCos = Cos(ang);
*pX = dmulscale30r(oX,angCos,oZ,-angSin);
*pZ = dmulscale30r(oX,angSin,oZ,angCos);
}
template<typename T> tspritetype* viewInsertTSprite(spritetype* tsprite, int& spritesortcnt, int nSector, int nStatnum, T const * const parentTSprite)
{
if (spritesortcnt >= MAXSPRITESONSCREEN)
return nullptr;
int nTSprite = spritesortcnt;
tspritetype *pTSprite = &tsprite[nTSprite];
memset(pTSprite, 0, sizeof(tspritetype));
pTSprite->cstat = 128;
pTSprite->xrepeat = 64;
pTSprite->yrepeat = 64;
pTSprite->owner = -1;
pTSprite->type = -spritesortcnt;
pTSprite->statnum = nStatnum;
pTSprite->sectnum = nSector;
spritesortcnt++;
if (parentTSprite)
{
pTSprite->x = parentTSprite->x;
pTSprite->y = parentTSprite->y;
pTSprite->z = parentTSprite->z;
pTSprite->owner = parentTSprite->owner;
pTSprite->ang = parentTSprite->ang;
}
pTSprite->x += Cos(gCameraAng)>>25;
pTSprite->y += Sin(gCameraAng)>>25;
return pTSprite;
}
static const int effectDetail[kViewEffectMax] = {
4, 4, 4, 4, 0, 0, 0, 0, 0, 1, 4, 4, 0, 0, 0, 1, 0, 0, 0
};
struct WEAPONICON {
int16_t nTile;
uint8_t zOffset;
};
static const WEAPONICON gWeaponIcon[] = {
{ -1, 0 },
{ -1, 0 }, // 1: pitchfork
{ 524, 6 }, // 2: flare gun
{ 559, 6 }, // 3: shotgun
{ 558, 8 }, // 4: tommy gun
{ 526, 6 }, // 5: napalm launcher
{ 589, 11 }, // 6: dynamite
{ 618, 11 }, // 7: spray can
{ 539, 6 }, // 8: tesla gun
{ 800, 0 }, // 9: life leech
{ 525, 11 }, // 10: voodoo doll
{ 811, 11 }, // 11: proxy bomb
{ 810, 11 }, // 12: remote bomb
{ -1, 0 },
};
static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int nTSprite, VIEW_EFFECT nViewEffect)
{
assert(nViewEffect >= 0 && nViewEffect < kViewEffectMax);
auto pTSprite = &tsprite[nTSprite];
auto owneractor = &bloodActors[pTSprite->owner];
if (gDetail < effectDetail[nViewEffect] || nTSprite >= MAXSPRITESONSCREEN) return NULL;
switch (nViewEffect)
{
case kViewEffectSpotProgress: {
XSPRITE* pXSprite = &owneractor->x();
int perc = (100 * pXSprite->data3) / kMaxPatrolSpotValue;
int width = (94 * pXSprite->data3) / kMaxPatrolSpotValue;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
auto pNSprite2 = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite2)
break;
pNSprite2->picnum = 2203;
pNSprite2->xrepeat = width;
pNSprite2->yrepeat = 20;
pNSprite2->pal = 10;
if (perc >= 75) pNSprite2->pal = 0;
else if (perc >= 50) pNSprite2->pal = 6;
pNSprite2->z = top - 2048;
pNSprite2->shade = -128;
break;
}
case kViewEffectAtom:
for (int i = 0; i < 16; i++)
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int ang = (PlayClock*2048)/120;
int nRand1 = dword_172CE0[i][0];
int nRand2 = dword_172CE0[i][1];
int nRand3 = dword_172CE0[i][2];
ang += nRand3;
int x = MulScale(512, Cos(ang), 30);
int y = MulScale(512, Sin(ang), 30);
int z = 0;
RotateYZ(&x, &y, &z, nRand1);
RotateXZ(&x, &y, &z, nRand2);
pNSprite->x = pTSprite->x + x;
pNSprite->y = pTSprite->y + y;
pNSprite->z = pTSprite->z + (z<<4);
pNSprite->picnum = 1720;
pNSprite->shade = -128;
}
break;
case kViewEffectFlag:
case kViewEffectBigFlag:
{
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->pal = 0;
pNSprite->z = top;
if (nViewEffect == kViewEffectFlag)
pNSprite->xrepeat = pNSprite->yrepeat = 24;
else
pNSprite->xrepeat = pNSprite->yrepeat = 64;
pNSprite->picnum = 3558;
return pNSprite;
}
case kViewEffectTesla:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->z = pTSprite->z;
pNSprite->cstat |= 2;
pNSprite->shade = -128;
pNSprite->xrepeat = pTSprite->xrepeat;
pNSprite->yrepeat = pTSprite->yrepeat;
pNSprite->picnum = 2135;
break;
}
case kViewEffectShoot:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->pal = 0;
pNSprite->xrepeat = pNSprite->yrepeat = 64;
pNSprite->picnum = 2605;
return pNSprite;
}
case kViewEffectReflectiveBall:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = 26;
pNSprite->pal = 0;
pNSprite->cstat |= 2;
pNSprite->xrepeat = pNSprite->yrepeat = 64;
pNSprite->picnum = 2089;
break;
}
case kViewEffectPhase:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->shade = 26;
pNSprite->pal = 0;
pNSprite->cstat |= 2;
pNSprite->xrepeat = pNSprite->yrepeat = 24;
pNSprite->picnum = 626;
pNSprite->z = top;
break;
}
case kViewEffectTrail:
{
int nAng = pTSprite->ang;
if (pTSprite->cstat & 16)
{
nAng = (nAng+512)&2047;
}
else
{
nAng = (nAng+1024)&2047;
}
for (int i = 0; i < 5 && spritesortcnt < MAXSPRITESONSCREEN; i++)
{
int nSector = pTSprite->sectnum;
auto pNSprite = viewInsertTSprite<tspritetype>(tsprite, spritesortcnt, nSector, 32767, NULL);
if (!pNSprite)
break;
int nLen = 128+(i<<7);
int x = MulScale(nLen, Cos(nAng), 30);
pNSprite->x = pTSprite->x + x;
int y = MulScale(nLen, Sin(nAng), 30);
pNSprite->y = pTSprite->y + y;
pNSprite->z = pTSprite->z;
assert(validSectorIndex(nSector));
FindSector(pNSprite->x, pNSprite->y, pNSprite->z, &nSector);
pNSprite->sectnum = nSector;
pNSprite->owner = pTSprite->owner;
pNSprite->picnum = pTSprite->picnum;
pNSprite->cstat |= 2;
if (i < 2)
pNSprite->cstat |= 514;
pNSprite->shade = ClipLow(pTSprite->shade-16, -128);
pNSprite->xrepeat = pTSprite->xrepeat;
pNSprite->yrepeat = pTSprite->yrepeat;
pNSprite->picnum = pTSprite->picnum;
}
break;
}
case kViewEffectFlame:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->z = pTSprite->z;
pNSprite->picnum = 908;
pNSprite->statnum = kStatDecoration;
pNSprite->xrepeat = pNSprite->yrepeat = (tileWidth(pTSprite->picnum)*pTSprite->xrepeat)/64;
break;
}
case kViewEffectSmokeHigh:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = top;
if (IsDudeSprite(pTSprite))
pNSprite->picnum = 672;
else
pNSprite->picnum = 754;
pNSprite->cstat |= 2;
pNSprite->shade = 8;
pNSprite->xrepeat = pTSprite->xrepeat;
pNSprite->yrepeat = pTSprite->yrepeat;
break;
}
case kViewEffectSmokeLow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = bottom;
if (pTSprite->type >= kDudeBase && pTSprite->type < kDudeMax)
pNSprite->picnum = 672;
else
pNSprite->picnum = 754;
pNSprite->cstat |= 2;
pNSprite->shade = 8;
pNSprite->xrepeat = pTSprite->xrepeat;
pNSprite->yrepeat = pTSprite->yrepeat;
break;
}
case kViewEffectTorchHigh:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = top;
pNSprite->picnum = 2101;
pNSprite->shade = -128;
pNSprite->xrepeat = pNSprite->yrepeat = (tileWidth(pTSprite->picnum)*pTSprite->xrepeat)/32;
break;
}
case kViewEffectTorchLow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = bottom;
pNSprite->picnum = 2101;
pNSprite->shade = -128;
pNSprite->xrepeat = pNSprite->yrepeat = (tileWidth(pTSprite->picnum)*pTSprite->xrepeat)/32;
break;
}
case kViewEffectShadow:
{
if (r_shadows)
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->z = getflorzofslope(pTSprite->sectnum, pNSprite->x, pNSprite->y);
pNSprite->shade = 127;
pNSprite->cstat |= 2;
pNSprite->xrepeat = pTSprite->xrepeat;
pNSprite->yrepeat = pTSprite->yrepeat >> 2;
pNSprite->picnum = pTSprite->picnum;
pNSprite->pal = 5;
int height = tileHeight(pNSprite->picnum);
int center = height / 2 + tileTopOffset(pNSprite->picnum);
pNSprite->z -= (pNSprite->yrepeat << 2) * (height - center);
}
break;
}
case kViewEffectFlareHalo:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->pal = 2;
pNSprite->cstat |= 2;
pNSprite->z = pTSprite->z;
pNSprite->xrepeat = pTSprite->xrepeat;
pNSprite->yrepeat = pTSprite->yrepeat;
pNSprite->picnum = 2427;
break;
}
case kViewEffectCeilGlow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
sectortype *pSector = &sector[pTSprite->sectnum];
pNSprite->x = pTSprite->x;
pNSprite->y = pTSprite->y;
pNSprite->z = pSector->ceilingz;
pNSprite->picnum = 624;
pNSprite->shade = ((pTSprite->z-pSector->ceilingz)>>8)-64;
pNSprite->pal = 2;
pNSprite->xrepeat = pNSprite->yrepeat = 64;
pNSprite->cstat |= 106;
pNSprite->ang = pTSprite->ang;
pNSprite->owner = pTSprite->owner;
break;
}
case kViewEffectFloorGlow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
sectortype *pSector = &sector[pTSprite->sectnum];
pNSprite->x = pTSprite->x;
pNSprite->y = pTSprite->y;
pNSprite->z = pSector->floorz;
pNSprite->picnum = 624;
uint8_t nShade = (pSector->floorz-pTSprite->z)>>8;
pNSprite->shade = nShade-32;
pNSprite->pal = 2;
pNSprite->xrepeat = pNSprite->yrepeat = nShade;
pNSprite->cstat |= 98;
pNSprite->ang = pTSprite->ang;
pNSprite->owner = pTSprite->owner;
break;
}
case kViewEffectSpear:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->z = pTSprite->z;
if (gDetail > 1)
pNSprite->cstat |= 514;
pNSprite->shade = ClipLow(pTSprite->shade-32, -128);
pNSprite->xrepeat = pTSprite->xrepeat;
pNSprite->yrepeat = 64;
pNSprite->picnum = 775;
break;
}
case kViewEffectShowWeapon:
{
assert(pTSprite->type >= kDudePlayer1 && pTSprite->type <= kDudePlayer8);
PLAYER *pPlayer = &gPlayer[pTSprite->type-kDudePlayer1];
WEAPONICON weaponIcon = gWeaponIcon[pPlayer->curWeapon];
auto& nTile = weaponIcon.nTile;
if (nTile < 0) break;
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->x = pTSprite->x;
pNSprite->y = pTSprite->y;
pNSprite->z = pTSprite->z-(32<<8);
pNSprite->z -= weaponIcon.zOffset<<8; // offset up
pNSprite->picnum = nTile;
pNSprite->shade = pTSprite->shade;
pNSprite->xrepeat = 32;
pNSprite->yrepeat = 32;
auto& nVoxel = voxelIndex[nTile];
if (cl_showweapon == 2 && r_voxels && nVoxel != -1)
{
pNSprite->ang = (gView->pSprite->ang + 512) & 2047; // always face viewer
pNSprite->cstat |= 48;
pNSprite->cstat &= ~8;
pNSprite->picnum = nVoxel;
if (pPlayer->curWeapon == kWeapLifeLeech) // position lifeleech behind player
{
pNSprite->x += MulScale(128, Cos(gView->pSprite->ang), 30);
pNSprite->y += MulScale(128, Sin(gView->pSprite->ang), 30);
}
if ((pPlayer->curWeapon == kWeapLifeLeech) || (pPlayer->curWeapon == kWeapVoodooDoll)) // make lifeleech/voodoo doll always face viewer like sprite
pNSprite->ang = (pNSprite->ang + 512) & 2047; // offset angle 90 degrees
}
break;
}
}
return NULL;
}
static void viewApplyDefaultPal(tspritetype *pTSprite, sectortype const *pSector)
{
XSECTOR const *pXSector = pSector->hasX()? &pSector->xs() : nullptr;
if (pXSector && pXSector->color && (VanillaMode() || pSector->floorpal != 0))
{
copyfloorpal(pTSprite, pSector);
}
}
void viewProcessSprites(spritetype* tsprite, int& spritesortcnt, int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t smoothratio)
{
// shift before interpolating to increase precision.
int myclock = (PlayClock<<3) + MulScale(4<<3, smoothratio, 16);
assert(spritesortcnt <= MAXSPRITESONSCREEN);
gCameraAng = cA;
int nViewSprites = spritesortcnt;
for (int nTSprite = spritesortcnt-1; nTSprite >= 0; nTSprite--)
{
tspritetype *pTSprite = &tsprite[nTSprite];
auto owneractor = &bloodActors[pTSprite->owner];
XSPRITE *pTXSprite = NULL;
if (sprite[pTSprite->owner].detail > gDetail)
{
pTSprite->xrepeat = 0;
continue;
}
if (owneractor->hasX())
{
pTXSprite = &owneractor->x();
}
int nTile = pTSprite->picnum;
if (nTile < 0 || nTile >= kMaxTiles)
{
pTSprite->xrepeat = 0;
continue;
}
// skip picnum 0 on face sprites. picnum 0 is a simple wall texture in Blood,
// but there are maps that use 0 on some operator sprites that may show up in potals as a result.
// Since the wall texture is perfectly fine for wall and floor sprites, these will be allowed to pass.
if (nTile == 0 && (pTSprite->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) == CSTAT_SPRITE_ALIGNMENT_FACING)
{
pTSprite->xrepeat = 0;
continue;
}
int nSprite = pTSprite->owner;
if (cl_interpolate && owneractor->interpolated && !(pTSprite->flags&512))
{
pTSprite->pos = pTSprite->interpolatedvec3(gInterpolate);
pTSprite->ang = pTSprite->interpolatedang(gInterpolate);
}
int nAnim = 0;
switch (picanm[nTile].extra & 7) {
case 0:
//assert(nXSprite > 0 && nXSprite < kMaxXSprites);
if (pTXSprite == nullptr) break;
switch (pTSprite->type) {
case kSwitchToggle:
case kSwitchOneWay:
if (pTXSprite->state) nAnim = 1;
break;
case kSwitchCombo:
nAnim = pTXSprite->data1;
break;
}
break;
case 1:
{
if (tilehasmodelorvoxel(pTSprite->picnum, pTSprite->pal) && !(spriteext[nSprite].flags&SPREXT_NOTMD))
{
pTSprite->cstat &= ~4;
break;
}
int dX = cX - pTSprite->x;
int dY = cY - pTSprite->y;
RotateVector(&dX, &dY, 128-pTSprite->ang);
nAnim = GetOctant(dX, dY);
if (nAnim <= 4)
{
pTSprite->cstat &= ~4;
}
else
{
nAnim = 8 - nAnim;
pTSprite->cstat |= 4;
}
break;
}
case 2:
{
if (tilehasmodelorvoxel(pTSprite->picnum, pTSprite->pal) && !(spriteext[nSprite].flags&SPREXT_NOTMD))
{
pTSprite->cstat &= ~4;
break;
}
int dX = cX - pTSprite->x;
int dY = cY - pTSprite->y;
RotateVector(&dX, &dY, 128-pTSprite->ang);
nAnim = GetOctant(dX, dY);
break;
}
case 3:
{
if (pTXSprite)
{
if (owneractor->hit.florhit.type == kHitNone)
nAnim = 1;
}
else
{
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
if (getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) > bottom)
nAnim = 1;
}
break;
}
case 6:
case 7:
{
if (hw_models && md_tilehasmodel(pTSprite->picnum, pTSprite->pal) >= 0 && !(spriteext[nSprite].flags&SPREXT_NOTMD))
break;
// Can be overridden by def script
if (r_voxels && tiletovox[pTSprite->picnum] == -1 && voxelIndex[pTSprite->picnum] != -1 && !(spriteext[nSprite].flags&SPREXT_NOTMD))
{
if ((pTSprite->flags&kHitagRespawn) == 0)
{
pTSprite->cstat |= 48;
pTSprite->cstat &= ~(4|8);
pTSprite->yoffset += tileTopOffset(pTSprite->picnum);
pTSprite->picnum = voxelIndex[pTSprite->picnum];
if ((picanm[nTile].extra&7) == 7)
{
pTSprite->ang = myclock & 2047;
}
}
}
break;
}
}
while (nAnim > 0)
{
pTSprite->picnum += picanm[pTSprite->picnum].num+1;
nAnim--;
}
if ((pTSprite->cstat&48) != 48 && r_voxels && !(spriteext[nSprite].flags&SPREXT_NOTMD))
{
int const nRootTile = pTSprite->picnum;
int nAnimTile = pTSprite->picnum + animateoffs_replace(pTSprite->picnum, 32768+pTSprite->owner);
#if 0
if (tiletovox[nAnimTile] != -1)
{
pTSprite->yoffset += tileTopOffset(nAnimTile);
pTSprite->xoffset += tileLeftOffset(nAnimTile);
}
#endif
int const nVoxel = tiletovox[pTSprite->picnum];
if (nVoxel != -1 && (picanm[nRootTile].extra & 7) == 7)
pTSprite->cstat2 |= CSTAT2_SPRITE_MDLROTATE; // per-sprite rotation setting.
}
if ((pTSprite->cstat&48) != 48 && hw_models && !(spriteext[nSprite].flags&SPREXT_NOTMD))
{
int const nRootTile = pTSprite->picnum;
int nAnimTile = pTSprite->picnum + animateoffs_replace(pTSprite->picnum, 32768+pTSprite->owner);
if (tile2model[Ptile2tile(nAnimTile, pTSprite->pal)].modelid >= 0 &&
tile2model[Ptile2tile(nAnimTile, pTSprite->pal)].framenum >= 0)
{
pTSprite->yoffset += tileTopOffset(nAnimTile);
pTSprite->xoffset += tileLeftOffset(nAnimTile);
if ((picanm[nRootTile].extra&7) == 7)
pTSprite->cstat2 |= CSTAT2_SPRITE_MDLROTATE; // per-sprite rotation setting.
}
}
sectortype *pSector = &sector[pTSprite->sectnum];
XSECTOR const* pXSector = pSector->hasX() ? &pSector->xs() : nullptr;
int nShade = pTSprite->shade;
if ((pSector->ceilingstat&1) && (pSector->floorstat&32768) == 0)
{
nShade += tileShade[pSector->ceilingpicnum]+pSector->ceilingshade;
}
else
{
nShade += tileShade[pSector->floorpicnum]+pSector->floorshade;
}
nShade += tileShade[pTSprite->picnum];
pTSprite->shade = ClipRange(nShade, -128, 127);
if ((pTSprite->flags&kHitagRespawn) && sprite[pTSprite->owner].owner == 3)
{
assert(pTXSprite != NULL);
pTSprite->xrepeat = 48;
pTSprite->yrepeat = 48;
pTSprite->shade = -128;
pTSprite->picnum = 2272 + 2*pTXSprite->respawnPending;
pTSprite->cstat &= ~514;
if (((IsItemSprite(pTSprite) || IsAmmoSprite(pTSprite)) && gGameOptions.nItemSettings == 2)
|| (IsWeaponSprite(pTSprite) && gGameOptions.nWeaponSettings == 3))
{
pTSprite->xrepeat = pTSprite->yrepeat = 48;
}
else
{
pTSprite->xrepeat = pTSprite->yrepeat = 0;
}
}
if (spritesortcnt >= MAXSPRITESONSCREEN) continue;
if (pTXSprite && pTXSprite->burnTime > 0)
{
pTSprite->shade = ClipRange(pTSprite->shade-16-QRandom(8), -128, 127);
}
if (pTSprite->flags&256)
{
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectSmokeHigh);
}
if (pTSprite->flags&1024)
{
pTSprite->cstat |= 4;
}
if (pTSprite->flags&2048)
{
pTSprite->cstat |= 8;
}
switch (pTSprite->statnum) {
case kStatDecoration: {
switch (pTSprite->type) {
case kDecorationCandle:
if (!pTXSprite || pTXSprite->state == 1) {
pTSprite->shade = -128;
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectPhase);
} else {
pTSprite->shade = -8;
}
break;
case kDecorationTorch:
if (!pTXSprite || pTXSprite->state == 1) {
pTSprite->picnum++;
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectTorchHigh);
} else {
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectSmokeHigh);
}
break;
default:
viewApplyDefaultPal(pTSprite, pSector);
break;
}
}
break;
case kStatItem: {
switch (pTSprite->type) {
case kItemFlagABase:
if (pTXSprite && pTXSprite->state > 0 && gGameOptions.nGameType == 3) {
auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectBigFlag);
if (pNTSprite) pNTSprite->pal = 10;
}
break;
case kItemFlagBBase:
if (pTXSprite && pTXSprite->state > 0 && gGameOptions.nGameType == 3) {
auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectBigFlag);
if (pNTSprite) pNTSprite->pal = 7;
}
break;
case kItemFlagA:
pTSprite->pal = 10;
pTSprite->cstat |= 1024;
break;
case kItemFlagB:
pTSprite->pal = 7;
pTSprite->cstat |= 1024;
break;
default:
if (pTSprite->type >= kItemKeySkull && pTSprite->type < kItemKeyMax)
pTSprite->shade = -128;
viewApplyDefaultPal(pTSprite, pSector);
break;
}
}
break;
case kStatProjectile: {
switch (pTSprite->type) {
case kMissileTeslaAlt:
pTSprite->yrepeat = 128;
pTSprite->cstat |= 32;
break;
case kMissileTeslaRegular:
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectTesla);
break;
case kMissileButcherKnife:
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectTrail);
break;
case kMissileFlareRegular:
case kMissileFlareAlt:
if (pTSprite->statnum == kStatFlare) {
if (owneractor->GetTarget() == gView->actor)
{
pTSprite->xrepeat = 0;
break;
}
}
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectFlareHalo);
if (pTSprite->type != kMissileFlareRegular) break;
sectortype *pSector = &sector[pTSprite->sectnum];
int zDiff = (pTSprite->z - pSector->ceilingz) >> 8;
if ((pSector->ceilingstat&1) == 0 && zDiff < 64) {
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectCeilGlow);
}
zDiff = (pSector->floorz - pTSprite->z) >> 8;
if ((pSector->floorstat&1) == 0 && zDiff < 64) {
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectFloorGlow);
}
break;
}
break;
}
case kStatDude:
{
if (pTSprite->type == kDudeHand && pTXSprite->aiState == &hand13A3B4)
{
auto target = owneractor->GetTarget();
if (target && target->IsPlayerActor())
{
pTSprite->xrepeat = 0;
break;
}
}
if (pXSector && pXSector->color) copyfloorpal(pTSprite, pSector);
if (powerupCheck(gView, kPwUpBeastVision) > 0) pTSprite->shade = -128;
if (IsPlayerSprite(pTSprite)) {
PLAYER *pPlayer = &gPlayer[pTSprite->type-kDudePlayer1];
if (powerupCheck(pPlayer, kPwUpShadowCloak) && !powerupCheck(gView, kPwUpBeastVision)) {
pTSprite->cstat |= 2;
pTSprite->pal = 5;
} else if (powerupCheck(pPlayer, kPwUpDeathMask)) {
pTSprite->shade = -128;
pTSprite->pal = 5;
} else if (powerupCheck(pPlayer, kPwUpDoppleganger)) {
pTSprite->pal = 11+(gView->teamId&3);
}
if (powerupCheck(pPlayer, kPwUpReflectShots)) {
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectReflectiveBall);
}
if (cl_showweapon && gGameOptions.nGameType > 0 && gView) {
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectShowWeapon);
}
if (pPlayer->flashEffect && (gView != pPlayer || gViewPos != VIEWPOS_0)) {
auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectShoot);
if (pNTSprite) {
POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
pNTSprite->x += MulScale(pPosture->zOffset, Cos(pTSprite->ang), 28);
pNTSprite->y += MulScale(pPosture->zOffset, Sin(pTSprite->ang), 28);
pNTSprite->z = pPlayer->pSprite->z-pPosture->xOffset;
}
}
if (pPlayer->hasFlag > 0 && gGameOptions.nGameType == 3) {
if (pPlayer->hasFlag&1) {
auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectFlag);
if (pNTSprite)
{
pNTSprite->pal = 10;
pNTSprite->cstat |= 4;
}
}
if (pPlayer->hasFlag&2) {
auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectFlag);
if (pNTSprite)
{
pNTSprite->pal = 7;
pNTSprite->cstat |= 4;
}
}
}
}
if (pTSprite->owner != gView->actor->GetSpriteIndex() || gViewPos != VIEWPOS_0) {
if (getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) >= cZ)
{
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectShadow);
}
}
if (gModernMap) { // add target spot indicator for patrol dudes
if (pTXSprite->dudeFlag4 && aiInPatrolState(pTXSprite->aiState) && pTXSprite->data3 > 0 && pTXSprite->data3 <= kMaxPatrolSpotValue)
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectSpotProgress);
}
break;
}
case kStatTraps: {
if (pTSprite->type == kTrapSawCircular) {
if (pTXSprite->state) {
if (pTXSprite->data1) {
pTSprite->picnum = 772;
if (pTXSprite->data2)
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectSpear);
}
}
else if (pTXSprite->data1) pTSprite->picnum = 773;
else pTSprite->picnum = 656;
}
break;
}
case kStatThing: {
viewApplyDefaultPal(pTSprite, pSector);
if (pTSprite->type < kThingBase || pTSprite->type >= kThingMax || owneractor->hit.florhit.type == kHitNone)
{
if ((pTSprite->flags & kPhysMove) && getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) >= cZ)
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectShadow);
}
}
break;
}
}
for (int nTSprite = spritesortcnt-1; nTSprite >= nViewSprites; nTSprite--)
{
tspritetype *pTSprite = &tsprite[nTSprite];
int nAnim = 0;
switch (picanm[pTSprite->picnum].extra&7)
{
case 1:
{
int dX = cX - pTSprite->x;
int dY = cY - pTSprite->y;
RotateVector(&dX, &dY, 128-pTSprite->ang);
nAnim = GetOctant(dX, dY);
if (nAnim <= 4)
{
pTSprite->cstat &= ~4;
}
else
{
nAnim = 8 - nAnim;
pTSprite->cstat |= 4;
}
break;
}
case 2:
{
int dX = cX - pTSprite->x;
int dY = cY - pTSprite->y;
RotateVector(&dX, &dY, 128-pTSprite->ang);
nAnim = GetOctant(dX, dY);
break;
}
}
while (nAnim > 0)
{
pTSprite->picnum += picanm[pTSprite->picnum].num+1;
nAnim--;
}
}
}
void GameInterface::processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio)
{
viewProcessSprites(tsprite, spritesortcnt, viewx, viewy, viewz, viewang.asbuild(), int(smoothRatio));
}
int display_mirror;
void GameInterface::EnterPortal(spritetype* viewer, int type)
{
if (type == PORTAL_WALL_MIRROR)
{
display_mirror++;
if (viewer) viewer->cstat &= ~CSTAT_SPRITE_INVISIBLE;
}
}
void GameInterface::LeavePortal(spritetype* viewer, int type)
{
if (type == PORTAL_WALL_MIRROR)
{
display_mirror--;
if (viewer && display_mirror == 0 && !(viewer->cstat & CSTAT_SPRITE_TRANSLUCENT)) viewer->cstat |= CSTAT_SPRITE_INVISIBLE;
}
}
END_BLD_NS