raze/source/games/blood/src/callback.cpp

786 lines
26 KiB
C++
Raw Normal View History

2019-09-19 22:42:45 +00:00
//-------------------------------------------------------------------------
/*
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!
2019-09-19 22:42:45 +00:00
#include "build.h"
#include "blood.h"
2020-12-05 17:32:49 +00:00
#include "bloodactor.h"
2019-09-19 22:42:45 +00:00
BEGIN_BLD_NS
2021-09-01 19:54:23 +00:00
void fxFlameLick(DBloodActor* actor, int) // 0
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
XSPRITE *pXSprite = &actor->x();
2019-09-19 22:42:45 +00:00
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
for (int i = 0; i < 3; i++)
{
int nDist = (pSprite->xrepeat*(tileWidth(pSprite->picnum)/2))>>3;
2019-09-19 22:42:45 +00:00
int nAngle = Random(2048);
int dx = MulScale(nDist, Cos(nAngle), 30);
int dy = MulScale(nDist, Sin(nAngle), 30);
2019-09-19 22:42:45 +00:00
int x = pSprite->x + dx;
int y = pSprite->y + dy;
int z = bottom-Random(bottom-top);
auto pFX = gFX.fxSpawnActor(FX_32, pSprite->sectnum, x, y, z, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(-dx);
pFX->yvel() = actor->yvel() + Random2(-dy);
pFX->zvel() = actor->zvel() - Random(0x1aaaa);
2019-09-19 22:42:45 +00:00
}
}
if (pXSprite->burnTime > 0)
2021-09-01 19:54:23 +00:00
evPost(actor, 5, kCallbackFXFlameLick);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void Remove(DBloodActor* actor, int) // 1
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
evKill(actor, kCallbackFXFlareSpark);
2019-09-19 22:42:45 +00:00
if (pSprite->extra > 0)
seqKill(3, pSprite->extra);
sfxKill3DSound(pSprite, 0, -1);
2021-09-01 19:54:23 +00:00
DeleteSprite(actor);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void FlareBurst(DBloodActor* actor, int) // 2
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
int nAngle = getangle(actor->xvel(), actor->yvel());
2019-09-19 22:42:45 +00:00
int nRadius = 0x55555;
for (int i = 0; i < 8; i++)
{
auto spawnedactor = actSpawnSprite(actor, 5);
spritetype *pSpawn = &spawnedactor->s();
2019-09-19 22:42:45 +00:00
pSpawn->picnum = 2424;
pSpawn->shade = -128;
pSpawn->xrepeat = pSpawn->yrepeat = 32;
pSpawn->type = kMissileFlareAlt;
2019-09-19 22:42:45 +00:00
pSpawn->clipdist = 2;
pSpawn->owner = pSprite->owner;
int nAngle2 = (i<<11)/8;
int dx = 0;
int dy = mulscale30r(nRadius, Sin(nAngle2));
int dz = mulscale30r(nRadius, -Cos(nAngle2));
if (i&1)
{
dy >>= 1;
dz >>= 1;
}
RotateVector(&dx, &dy, nAngle);
spawnedactor->xvel() += dx;
spawnedactor->yvel() += dy;
spawnedactor->zvel() += dz;
evPost(spawnedactor, 960, kCallbackRemove);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
evPost(actor, 0, kCallbackRemove);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void fxFlareSpark(DBloodActor* actor, int) // 3
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
auto pFX = gFX.fxSpawnActor(FX_28, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(0x1aaaa);
pFX->yvel() = actor->yvel() + Random2(0x1aaaa);
pFX->zvel() = actor->zvel() - Random(0x1aaaa);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
evPost(actor, 4, kCallbackFXFlareSpark);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void fxFlareSparkLite(DBloodActor* actor, int) // 4
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
auto pFX = gFX.fxSpawnActor(FX_28, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(0x1aaaa);
pFX->yvel() = actor->yvel() + Random2(0x1aaaa);
pFX->zvel() = actor->zvel() - Random(0x1aaaa);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
evPost(actor, 12, kCallbackFXFlareSparkLite);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void fxZombieBloodSpurt(DBloodActor* actor, int) // 5
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
int nXSprite = pSprite->extra;
assert(nXSprite > 0 && nXSprite < kMaxXSprites);
2021-09-01 19:54:23 +00:00
XSPRITE *pXSprite = &actor->x();
2019-09-19 22:42:45 +00:00
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
auto pFX = gFX.fxSpawnActor(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, top, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(0x11111);
pFX->yvel() = actor->yvel() + Random2(0x11111);
pFX->zvel() = actor->zvel() - 0x6aaaa;
2019-09-19 22:42:45 +00:00
}
if (pXSprite->data1 > 0)
{
2021-09-01 19:54:23 +00:00
evPost(actor, 4, kCallbackFXZombieSpurt);
2019-09-19 22:42:45 +00:00
pXSprite->data1 -= 4;
}
else if (pXSprite->data2 > 0)
{
2021-09-01 19:54:23 +00:00
evPost(actor, 60, kCallbackFXZombieSpurt);
2019-09-19 22:42:45 +00:00
pXSprite->data1 = 40;
pXSprite->data2--;
}
}
2021-09-01 19:54:23 +00:00
void fxBloodSpurt(DBloodActor* actor, int) // 6
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
auto pFX = gFX.fxSpawnActor(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->s().ang = 0;
pFX->xvel() = actor->xvel()>>8;
pFX->yvel() = actor->yvel()>>8;
pFX->zvel() = actor->zvel()>>8;
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
evPost(actor, 6, kCallbackFXBloodSpurt);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void fxArcSpark(DBloodActor* actor, int) // 7
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype* pSprite = &actor->s();
auto pFX = gFX.fxSpawnActor(FX_15, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(0x10000);
pFX->yvel() = actor->yvel() + Random2(0x10000);
pFX->zvel() = actor->zvel() - Random(0x1aaaa);
}
2021-09-01 19:54:23 +00:00
evPost(actor, 3, kCallbackFXArcSpark);
}
2021-09-01 19:54:23 +00:00
void fxDynPuff(DBloodActor* actor, int) // 8
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
if (actor->zvel())
2019-09-19 22:42:45 +00:00
{
int nDist = (pSprite->xrepeat*(tileWidth(pSprite->picnum)/2))>>2;
int x = pSprite->x + MulScale(nDist, Cos(pSprite->ang-512), 30);
int y = pSprite->y + MulScale(nDist, Sin(pSprite->ang-512), 30);
2019-09-19 22:42:45 +00:00
int z = pSprite->z;
auto pFX = gFX.fxSpawnActor(FX_7, pSprite->sectnum, x, y, z, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->xvel() = actor->xvel();
pFX->yvel() = actor->yvel();
pFX->zvel() = actor->zvel();
2019-09-19 22:42:45 +00:00
}
}
2021-09-01 19:54:23 +00:00
evPost(actor, 12, kCallbackFXDynPuff);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void Respawn(DBloodActor* actor, int) // 9
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
assert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites);
XSPRITE *pXSprite = &xsprite[pSprite->extra];
if (pSprite->statnum != kStatRespawn && pSprite->statnum != kStatThing) {
2021-09-01 19:54:23 +00:00
viewSetSystemMessage("Sprite #%d is not on Respawn or Thing list\n", actor->GetIndex());
return;
} else if (!(pSprite->flags & kHitagRespawn)) {
2021-09-01 19:54:23 +00:00
viewSetSystemMessage("Sprite #%d does not have the respawn attribute\n", actor->GetIndex());
return;
2019-09-19 22:42:45 +00:00
}
switch (pXSprite->respawnPending) {
case 1: {
2021-08-30 18:21:49 +00:00
int nTime = MulScale(actGetRespawnTime(actor), 0x4000, 16);
pXSprite->respawnPending = 2;
2021-09-01 19:54:23 +00:00
evPost(actor, nTime, kCallbackRespawn);
break;
2019-09-19 22:42:45 +00:00
}
case 2: {
2021-08-30 18:21:49 +00:00
int nTime = MulScale(actGetRespawnTime(actor), 0x2000, 16);
pXSprite->respawnPending = 3;
2021-09-01 19:54:23 +00:00
evPost(actor, nTime, kCallbackRespawn);
break;
}
case 3: {
assert(pSprite->owner != kStatRespawn);
assert(pSprite->owner >= 0 && pSprite->owner < kMaxStatus);
2021-09-01 19:54:23 +00:00
ChangeSpriteStat(actor->s().index, pSprite->owner);
pSprite->type = pSprite->inittype;
pSprite->owner = -1;
pSprite->flags &= ~kHitagRespawn;
2021-09-01 19:54:23 +00:00
actor->xvel() = actor->yvel() = actor->zvel() = 0;
pXSprite->respawnPending = 0;
pXSprite->burnTime = 0;
pXSprite->isTriggered = 0;
if (IsDudeSprite(pSprite)) {
int nType = pSprite->type-kDudeBase;
2021-09-01 19:54:23 +00:00
pSprite->x = actor->basePoint().x;
pSprite->y = actor->basePoint().y;
pSprite->z = actor->basePoint().z;
pSprite->cstat |= 0x1101;
#ifdef NOONE_EXTENSIONS
if (!gModernMap || pXSprite->sysData2 <= 0) pXSprite->health = dudeInfo[pSprite->type - kDudeBase].startHealth << 4;
else pXSprite->health = ClipRange(pXSprite->sysData2 << 4, 1, 65535);
2020-12-06 20:56:09 +00:00
switch (pSprite->type) {
default:
pSprite->clipdist = getDudeInfo(nType + kDudeBase)->clipdist;
if (getSequence(getDudeInfo(nType + kDudeBase)->seqStartID))
seqSpawn(getDudeInfo(nType + kDudeBase)->seqStartID, 3, pSprite->extra, -1);
break;
case kDudeModernCustom:
2021-05-06 08:51:39 +00:00
seqSpawn(genDudeSeqStartId(actor), 3, pSprite->extra, -1);
break;
}
2020-12-06 20:56:09 +00:00
// return dude to the patrol state
if (gModernMap && pXSprite->dudeFlag4) {
pXSprite->data3 = 0;
actor->SetTarget(nullptr);
2020-12-06 20:56:09 +00:00
}
#else
pSprite->clipdist = getDudeInfo(nType + kDudeBase)->clipdist;
pXSprite->health = getDudeInfo(nType + kDudeBase)->startHealth << 4;
if (getSequence(getDudeInfo(nType + kDudeBase)->seqStartID))
seqSpawn(getDudeInfo(nType + kDudeBase)->seqStartID, 3, pSprite->extra, -1);
#endif
aiInitSprite(actor);
pXSprite->key = 0;
} else if (pSprite->type == kThingTNTBarrel) {
pSprite->cstat |= CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN;
pSprite->cstat &= (unsigned short)~CSTAT_SPRITE_INVISIBLE;
}
gFX.fxSpawnActor(FX_29, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
sfxPlay3DSound(pSprite, 350, -1, 0);
break;
2019-09-19 22:42:45 +00:00
}
}
}
2021-09-01 19:54:23 +00:00
void PlayerBubble(DBloodActor* actor, int) // 10
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
if (IsPlayerSprite(pSprite))
{
PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
assert(pPlayer != NULL);
if (!pPlayer->bubbleTime)
2019-09-19 22:42:45 +00:00
return;
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
for (int i = 0; i < (pPlayer->bubbleTime>>6); i++)
2019-09-19 22:42:45 +00:00
{
int nDist = (pSprite->xrepeat*(tileWidth(pSprite->picnum)/2))>>2;
2019-09-19 22:42:45 +00:00
int nAngle = Random(2048);
int x = pSprite->x + MulScale(nDist, Cos(nAngle), 30);
int y = pSprite->y + MulScale(nDist, Sin(nAngle), 30);
2019-09-19 22:42:45 +00:00
int z = bottom-Random(bottom-top);
auto pFX = gFX.fxSpawnActor((FX_ID)(FX_23+Random(3)), pSprite->sectnum, x, y, z, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(0x1aaaa);
pFX->yvel() = actor->yvel() + Random2(0x1aaaa);
pFX->zvel() = actor->zvel() + Random2(0x1aaaa);
2019-09-19 22:42:45 +00:00
}
}
2021-09-01 19:54:23 +00:00
evPost(actor, 4, kCallbackPlayerBubble);
2019-09-19 22:42:45 +00:00
}
}
2021-09-01 19:54:23 +00:00
void EnemyBubble(DBloodActor* actor, int) // 11
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
2021-09-01 19:54:23 +00:00
for (int i = 0; i < (abs(actor->zvel())>>18); i++)
2019-09-19 22:42:45 +00:00
{
int nDist = (pSprite->xrepeat*(tileWidth(pSprite->picnum)/2))>>2;
2019-09-19 22:42:45 +00:00
int nAngle = Random(2048);
int x = pSprite->x + MulScale(nDist, Cos(nAngle), 30);
int y = pSprite->y + MulScale(nDist, Sin(nAngle), 30);
2019-09-19 22:42:45 +00:00
int z = bottom-Random(bottom-top);
auto pFX = gFX.fxSpawnActor((FX_ID)(FX_23+Random(3)), pSprite->sectnum, x, y, z, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(0x1aaaa);
pFX->yvel() = actor->yvel() + Random2(0x1aaaa);
pFX->zvel() = actor->zvel() + Random2(0x1aaaa);
2019-09-19 22:42:45 +00:00
}
}
2021-09-01 19:54:23 +00:00
evPost(actor, 4, kCallbackEnemeyBubble);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void CounterCheck(DBloodActor*, int nSector) // 12
2019-09-19 22:42:45 +00:00
{
if (nSector < 0 || nSector >= kMaxSectors) return;
if (sector[nSector].type != kSectorCounter) return;
if (sector[nSector].extra <= 0) return;
XSECTOR *pXSector = &xsector[sector[nSector].extra];
int nReq = pXSector->waitTimeA; int nType = pXSector->data; int nCount = 0;
if (!nType || !nReq) return;
int nSprite;
SectIterator it(nSector);
while ((nSprite = it.NextIndex()) >= 0)
{
if (sprite[nSprite].type == nType) nCount++;
2019-10-20 15:09:01 +00:00
}
if (nCount < nReq) {
evPostSector(nSector, 5, kCallbackCounterCheck);
return;
} else {
2019-10-20 15:09:01 +00:00
//pXSector->waitTimeA = 0; //do not reset necessary objects counter to zero
trTriggerSector(nSector, pXSector, kCmdOn);
2019-10-20 15:09:01 +00:00
pXSector->locked = 1; //lock sector, so it can be opened again later
}
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void FinishHim(DBloodActor* actor, int) // 13
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype* pSprite = &actor->s();
int nXSprite = pSprite->extra;
2021-09-01 19:54:23 +00:00
XSPRITE* pXSprite = &actor->x();
if (IsPlayerSprite(pSprite) && playerSeqPlaying(&gPlayer[pSprite->type - kDudePlayer1], 16) && pXSprite->target_i == gMe->nSprite)
sndStartSample(3313, -1, 1, 0);
}
2021-09-01 19:54:23 +00:00
void fxBloodBits(DBloodActor* actor, int) // 14
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
int ceilZ, ceilHit, floorZ, floorHit;
GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist, CLIPMASK0);
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
pSprite->z += floorZ-bottom;
int nAngle = Random(2048);
int nDist = Random(16)<<4;
int x = pSprite->x+MulScale(nDist, Cos(nAngle), 28);
int y = pSprite->y+MulScale(nDist, Sin(nAngle), 28);
gFX.fxSpawnActor(FX_48, pSprite->sectnum, x, y, pSprite->z, 0);
2019-09-19 22:42:45 +00:00
if (pSprite->ang == 1024)
{
int nChannel = 28+(pSprite->index&2);
assert(nChannel < 32);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pSprite, 385, nChannel, 1);
}
if (Chance(0x5000))
{
auto pFX = gFX.fxSpawnActor(FX_36, pSprite->sectnum, x, y, floorZ-64, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
pFX->s().ang = nAngle;
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
gFX.remove(actor->s().index);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void fxTeslaAlt(DBloodActor* actor, int) // 15
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype* pSprite = &actor->s();
auto pFX = gFX.fxSpawnActor(FX_49, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
pFX->xvel() = actor->xvel() + Random2(0x1aaaa);
pFX->yvel() = actor->yvel() + Random2(0x1aaaa);
pFX->zvel() = actor->zvel() - Random(0x1aaaa);
}
2021-09-01 19:54:23 +00:00
evPost(actor, 3, kCallbackFXTeslaAlt);
}
int tommySleeveSnd[] = { 608, 609, 611 }; // unused?
int sawedOffSleeveSnd[] = { 610, 612 };
2021-09-01 19:54:23 +00:00
void fxBouncingSleeve(DBloodActor* actor, int) // 16
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype* pSprite = &actor->s(); int ceilZ, ceilHit, floorZ, floorHit;
GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist, CLIPMASK0);
int top, bottom; GetSpriteExtents(pSprite, &top, &bottom);
pSprite->z += floorZ - bottom;
2021-09-01 19:54:23 +00:00
int zv = actor->zvel() - velFloor[pSprite->sectnum];
2021-09-01 19:54:23 +00:00
if (actor->zvel() == 0) sleeveStopBouncing(pSprite);
else if (zv > 0) {
2021-09-01 19:54:23 +00:00
actFloorBounceVector((int*)& actor->xvel(), (int*)& actor->yvel(), &zv, pSprite->sectnum, 0x9000);
actor->zvel() = zv;
if (velFloor[pSprite->sectnum] == 0 && abs(actor->zvel()) < 0x20000) {
sleeveStopBouncing(pSprite);
return;
}
int nChannel = 28 + (pSprite->index & 2);
assert(nChannel < 32);
// tommy sleeve
if (pSprite->type >= 37 && pSprite->type <= 39) {
Random(3);
sfxPlay3DSound(pSprite, 608 + Random(2), nChannel, 1);
// sawed-off sleeve
} else {
sfxPlay3DSound(pSprite, sawedOffSleeveSnd[Random(2)], nChannel, 1);
}
}
}
void sleeveStopBouncing(spritetype* pSprite) {
2019-09-19 22:42:45 +00:00
xvel[pSprite->index] = yvel[pSprite->index] = zvel[pSprite->index] = 0;
if (pSprite->extra > 0) seqKill(3, pSprite->extra);
2019-09-19 22:42:45 +00:00
sfxKill3DSound(pSprite, -1, -1);
switch (pSprite->type) {
2019-09-19 22:42:45 +00:00
case 37:
case 38:
case 39:
pSprite->picnum = 2465;
break;
case 40:
case 41:
case 42:
pSprite->picnum = 2464;
break;
}
2019-09-19 22:42:45 +00:00
pSprite->type = 51;
pSprite->xrepeat = pSprite->yrepeat = 10;
}
2021-09-01 19:54:23 +00:00
void returnFlagToBase(DBloodActor* actor, int) // 17
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype* pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
if (pSprite->owner >= 0 && pSprite->owner < kMaxSprites)
{
spritetype* pOwner = &sprite[pSprite->owner];
XSPRITE* pXOwner = &xsprite[pOwner->extra];
switch (pSprite->type) {
case kItemFlagA:
trTriggerSprite(pOwner->index, pXOwner, kCmdOn);
2019-10-20 15:09:01 +00:00
sndStartSample(8003, 255, 2, 0);
gBlueFlagDropped = false;
viewSetMessage("Blue Flag returned to base.");
break;
case kItemFlagB:
trTriggerSprite(pOwner->index, pXOwner, kCmdOn);
2019-10-20 15:09:01 +00:00
sndStartSample(8002, 255, 2, 0);
gRedFlagDropped = false;
viewSetMessage("Red Flag returned to base.");
break;
2019-09-19 22:42:45 +00:00
}
}
evPost(actor, 0, kCallbackRemove);
}
2021-09-01 19:54:23 +00:00
void fxPodBloodSpray(DBloodActor* actor, int) // 18
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype* pSprite = &actor->s();
DBloodActor* pFX;
if (pSprite->type == 53)
pFX = gFX.fxSpawnActor(FX_53, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
else
pFX = gFX.fxSpawnActor(FX_54, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
pFX->s().ang = 0;
pFX->xvel() = actor->xvel() >> 8;
pFX->yvel() = actor->yvel() >> 8;
pFX->zvel() = actor->zvel() >> 8;
}
2021-09-01 19:54:23 +00:00
evPost(actor, 6, kCallbackFXPodBloodSpray);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void fxPodBloodSplat(DBloodActor* actor, int) // 19
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
int ceilZ, ceilHit, floorZ, floorHit;
GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist, CLIPMASK0);
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
pSprite->z += floorZ-bottom;
int nAngle = Random(2048);
int nDist = Random(16)<<4;
int x = pSprite->x+MulScale(nDist, Cos(nAngle), 28);
int y = pSprite->y+MulScale(nDist, Sin(nAngle), 28);
2019-09-19 22:42:45 +00:00
if (pSprite->ang == 1024)
{
int nChannel = 28+(pSprite->index&2);
assert(nChannel < 32);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pSprite, 385, nChannel, 1);
}
DBloodActor *pFX = NULL;
if (pSprite->type == 53 || pSprite->type == kThingPodGreenBall)
2019-09-19 22:42:45 +00:00
{
if (Chance(0x500) || pSprite->type == kThingPodGreenBall)
pFX = gFX.fxSpawnActor(FX_55, pSprite->sectnum, x, y, floorZ-64, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
pFX->s().ang = nAngle;
2019-09-19 22:42:45 +00:00
}
else
{
pFX = gFX.fxSpawnActor(FX_32, pSprite->sectnum, x, y, floorZ-64, 0);
2019-09-19 22:42:45 +00:00
if (pFX)
pFX->s().ang = nAngle;
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
gFX.remove(actor->s().index);
2019-09-19 22:42:45 +00:00
}
2021-09-01 19:54:23 +00:00
void LeechStateTimer(DBloodActor* actor, int) // 20
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
if (pSprite->statnum == kStatThing && !(pSprite->flags & 32)) {
switch (pSprite->type) {
case kThingDroppedLifeLeech:
#ifdef NOONE_EXTENSIONS
case kModernThingEnemyLifeLeech:
#endif
2019-09-19 22:42:45 +00:00
xsprite[pSprite->extra].stateTimer = 0;
break;
}
}
}
2021-09-01 19:54:23 +00:00
void sub_76A08(DBloodActor *actor, spritetype *pSprite2, PLAYER *pPlayer) // ???
2019-09-19 22:42:45 +00:00
{
int top, bottom;
2021-09-01 19:54:23 +00:00
auto pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
GetSpriteExtents(pSprite, &top, &bottom);
pSprite->x = pSprite2->x;
pSprite->y = pSprite2->y;
pSprite->z = sector[pSprite2->sectnum].floorz-(bottom-pSprite->z);
pSprite->ang = pSprite2->ang;
2021-09-01 19:54:23 +00:00
ChangeSpriteSect(pSprite->index, pSprite2->sectnum);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pSprite2, 201, -1, 0);
2021-09-01 19:54:23 +00:00
actor->xvel() = actor->yvel() = actor->zvel() = 0;
viewBackupSpriteLoc(pSprite->index, pSprite);
2019-09-19 22:42:45 +00:00
if (pPlayer)
{
playerResetInertia(pPlayer);
pPlayer->zViewVel = pPlayer->zWeaponVel = 0;
2019-09-19 22:42:45 +00:00
}
}
2021-09-01 19:54:23 +00:00
void DropVoodooCb(DBloodActor* actor, int) // unused
2019-09-19 22:42:45 +00:00
{
if (!actor) return;
2021-09-01 19:54:23 +00:00
spritetype *pSprite = &actor->s();
int nOwner = pSprite->owner;
2019-09-19 22:42:45 +00:00
if (nOwner < 0 || nOwner >= kMaxSprites)
{
2021-09-01 19:54:23 +00:00
evPost(actor, 0, kCallbackRemove);
2019-09-19 22:42:45 +00:00
return;
}
spritetype *pOwner = &sprite[nOwner];
PLAYER *pPlayer;
if (IsPlayerSprite(pOwner))
pPlayer = &gPlayer[pOwner->type-kDudePlayer1];
else
pPlayer = NULL;
if (!pPlayer)
{
2021-09-01 19:54:23 +00:00
evPost(actor, 0, kCallbackRemove);
2019-09-19 22:42:45 +00:00
return;
}
pSprite->ang = getangle(pOwner->x-pSprite->x, pOwner->y-pSprite->y);
int nXSprite = pSprite->extra;
if (nXSprite > 0)
{
2021-09-01 19:54:23 +00:00
XSPRITE *pXSprite = &actor->x();
2019-09-19 22:42:45 +00:00
if (pXSprite->data1 == 0)
{
2021-09-01 19:54:23 +00:00
evPost(actor, 0, kCallbackRemove);
2019-09-19 22:42:45 +00:00
return;
}
2020-10-15 15:15:45 +00:00
int nSprite2;
StatIterator it(kStatDude);
while ((nSprite2 = it.NextIndex()) >= 0)
2019-09-19 22:42:45 +00:00
{
2020-10-15 15:15:45 +00:00
int nNextSprite = it.PeekIndex();
2019-09-19 22:42:45 +00:00
if (nOwner == nSprite2)
continue;
auto actor2 = &bloodActors[nSprite2];
2019-09-19 22:42:45 +00:00
spritetype *pSprite2 = &sprite[nSprite2];
int nXSprite2 = pSprite2->extra;
if (nXSprite2 > 0 && nXSprite2 < kMaxXSprites)
{
XSPRITE *pXSprite2 = &xsprite[nXSprite2];
PLAYER *pPlayer2;
if (IsPlayerSprite(pSprite2))
pPlayer2 = &gPlayer[pSprite2->type-kDudePlayer1];
else
pPlayer2 = NULL;
if (pXSprite2->health > 0 && (pPlayer2 || pXSprite2->key == 0))
{
if (pPlayer2)
{
if (gGameOptions.nGameType == 1)
continue;
if (gGameOptions.nGameType == 3 && pPlayer->teamId == pPlayer2->teamId)
2019-09-19 22:42:45 +00:00
continue;
int t = 0x8000/ClipLow(gNetPlayers-1, 1);
if (!powerupCheck(pPlayer2, kPwUpDeathMask))
t += ((3200-pPlayer2->armor[2])<<15)/3200;
2019-09-19 22:42:45 +00:00
if (Chance(t) || nNextSprite < 0)
{
int nDmg = actDamageSprite(actor, actor2, kDamageSpirit, pXSprite->data1<<4);
2019-09-19 22:42:45 +00:00
pXSprite->data1 = ClipLow(pXSprite->data1-nDmg, 0);
2021-09-01 19:54:23 +00:00
sub_76A08(actor2, pSprite, pPlayer2);
evPost(actor, 0, kCallbackRemove);
2019-09-19 22:42:45 +00:00
return;
}
}
else
{
int vd = 0x2666;
switch (pSprite2->type)
{
case kDudeBoneEel:
case kDudeBat:
case kDudeRat:
case kDudeTinyCaleb:
case kDudeBeast:
2019-09-19 22:42:45 +00:00
vd = 0x147;
break;
case kDudeZombieAxeBuried:
case kDudePodGreen:
case kDudeTentacleGreen:
case kDudePodFire:
case kDudeTentacleFire:
case kDudePodMother:
case kDudeTentacleMother:
case kDudeCerberusTwoHead:
case kDudeCerberusOneHead:
case kDudeTchernobog:
case kDudeBurningInnocent:
case kDudeBurningCultist:
case kDudeBurningZombieAxe:
case kDudeBurningZombieButcher:
case kDudeCultistReserved:
case kDudeZombieAxeLaying:
case kDudeInnocent:
case kDudeBurningTinyCaleb:
case kDudeBurningBeast:
2019-09-19 22:42:45 +00:00
vd = 0;
break;
}
if (vd && (Chance(vd) || nNextSprite < 0))
{
2021-09-01 19:54:23 +00:00
sub_76A08(actor2, pSprite, NULL);
evPost(actor, 0, kCallbackRemove);
2019-09-19 22:42:45 +00:00
return;
}
}
}
}
}
pXSprite->data1 = ClipLow(pXSprite->data1-1, 0);
2021-09-01 19:54:23 +00:00
evPost(actor, 0, kCallbackRemove);
}
}
2021-09-01 19:54:23 +00:00
void callbackCondition(DBloodActor* actor, int)
{
XSPRITE* pXSprite = &actor->x();
- Don't change statnum of thing converted to debris back to thing on impact trigger when firing with vector weapon. - Change damage scale and min firedist for Custom Dude. - Remove unnecessary checks in callback of tracking condition. - Fix picWidth() function. - Better initialization of modern stuff. - kModernSeqSpawner: disable all other spawners with same TX ID when enabling current. - Fix: sceneQav was not playing if resurrected with COUSTEAU cheat. - kModernPictureChanger: remove kModernFlag01 feature (deprecated). - kModernSectorFXChanger: add flags to control where exactly light effect should appear. - kModernCondition: - add delay before sending command if condition is true. - take in account state, so kCmdState and kCmdNotState is useful. - fix wrong comparison result in some conditions. - add new various conditions. - kModernPlayerControl: - fix start / stop playing qav scene when triggered with event command converted to sprite command. - add a way to resurrect / heal player. - add event commands to toggle inventory item status via trigger. - fix that Remote and Proximity detonators cannot be given. - add clear all screen effects option. - proper percents for changing movement / jumping. - kModernRandomTX, kModernSequentialTX: change event redirection mode activation from kModernTypeFlag02 to kCmdLink. - kModernSpriteDamager: treat damage value as percents by default, take in account god mode. - kModernEffectGen: fix wrong cstat for effects. - kModernPropertiesChanger: proper underwater status for sectors and players or enemies in it. - Players: assign or update current player's sprite index for all conditions. # Conflicts: # source/blood/src/nnexts.cpp
2020-05-05 18:50:14 +00:00
if (pXSprite->isTriggered) return;
TRCONDITION* pCond = &gCondition[pXSprite->sysData1];
2021-05-12 00:00:06 +00:00
for (unsigned i = 0; i < pCond->length; i++) {
EVENT evn; evn.index = pCond->obj[i].index; evn.type = pCond->obj[i].type;
evn.cmd = pCond->obj[i].cmd; evn.funcID = kCallbackCondition;
- Don't change statnum of thing converted to debris back to thing on impact trigger when firing with vector weapon. - Change damage scale and min firedist for Custom Dude. - Remove unnecessary checks in callback of tracking condition. - Fix picWidth() function. - Better initialization of modern stuff. - kModernSeqSpawner: disable all other spawners with same TX ID when enabling current. - Fix: sceneQav was not playing if resurrected with COUSTEAU cheat. - kModernPictureChanger: remove kModernFlag01 feature (deprecated). - kModernSectorFXChanger: add flags to control where exactly light effect should appear. - kModernCondition: - add delay before sending command if condition is true. - take in account state, so kCmdState and kCmdNotState is useful. - fix wrong comparison result in some conditions. - add new various conditions. - kModernPlayerControl: - fix start / stop playing qav scene when triggered with event command converted to sprite command. - add a way to resurrect / heal player. - add event commands to toggle inventory item status via trigger. - fix that Remote and Proximity detonators cannot be given. - add clear all screen effects option. - proper percents for changing movement / jumping. - kModernRandomTX, kModernSequentialTX: change event redirection mode activation from kModernTypeFlag02 to kCmdLink. - kModernSpriteDamager: treat damage value as percents by default, take in account god mode. - kModernEffectGen: fix wrong cstat for effects. - kModernPropertiesChanger: proper underwater status for sectors and players or enemies in it. - Players: assign or update current player's sprite index for all conditions. # Conflicts: # source/blood/src/nnexts.cpp
2020-05-05 18:50:14 +00:00
useCondition(&sprite[pXSprite->reference], pXSprite, evn);
}
2021-09-01 19:54:23 +00:00
evPost(actor, pXSprite->busyTime, kCallbackCondition);
return;
}
2021-09-01 19:54:23 +00:00
void(*gCallback[kCallbackMax])(DBloodActor*, int) =
2019-09-19 22:42:45 +00:00
{
fxFlameLick,
2019-09-19 22:42:45 +00:00
Remove,
FlareBurst,
fxFlareSpark,
fxFlareSparkLite,
fxZombieBloodSpurt,
fxBloodSpurt,
fxArcSpark,
fxDynPuff,
2019-09-19 22:42:45 +00:00
Respawn,
PlayerBubble,
EnemyBubble,
CounterCheck,
FinishHim,
fxBloodBits,
fxTeslaAlt,
fxBouncingSleeve,
returnFlagToBase,
fxPodBloodSpray,
fxPodBloodSplat,
LeechStateTimer,
DropVoodooCb, // unused
#ifdef NOONE_EXTENSIONS
callbackUniMissileBurst, // the code is in nnexts.cpp
callbackMakeMissileBlocking, // the code is in nnexts.cpp
callbackGenDudeUpdate, // the code is in nnexts.cpp
callbackCondition,
#endif
2019-09-19 22:42:45 +00:00
};
END_BLD_NS