raze/source/blood/src/callback.cpp
Grind Core 0d82280b87 - Added event initiator for sectors and walls
- Added dynamic dispersion for custom dude
- Added dynamic attack distance for custom dude
- Added dynamic damage resistance scale for custom dude
- There was some refactor actions performed
- Added player RX channels for triggering the players
- Updates for modern types
- There was added player control modern type which can do a lot things. It's
still WIP
- Other updates and fixes mainly related to gModernMap

Note that your save games will be no longer compatible

# Conflicts:
#	source/blood/src/actor.h
#	source/blood/src/aiunicult.h
#	source/blood/src/db.h
#	source/blood/src/player.h
#	source/blood/src/triggers.h
#	source/blood/src/weapon.cpp
2019-11-11 22:59:05 +01:00

794 lines
26 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 "ai.h"
#include "blood.h"
#include "callback.h"
#include "config.h"
#include "db.h"
#include "dude.h"
#include "eventq.h"
#include "fx.h"
#include "gameutil.h"
#include "globals.h"
#include "levels.h"
#include "player.h"
#include "seq.h"
#include "sfx.h"
#include "sound.h"
#include "trig.h"
#include "triggers.h"
#include "view.h"
BEGIN_BLD_NS
void fxFlameLick(int nSprite) // 0
{
spritetype *pSprite = &sprite[nSprite];
int nXSprite = pSprite->extra;
XSPRITE *pXSprite = &xsprite[nXSprite];
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
for (int i = 0; i < 3; i++)
{
int nDist = (pSprite->xrepeat*(tilesiz[pSprite->picnum].x/2))>>3;
int nAngle = Random(2048);
int dx = mulscale30(nDist, Cos(nAngle));
int dy = mulscale30(nDist, Sin(nAngle));
int x = pSprite->x + dx;
int y = pSprite->y + dy;
int z = bottom-Random(bottom-top);
spritetype *pFX = gFX.fxSpawn(FX_32, pSprite->sectnum, x, y, z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(-dx);
yvel[pFX->index] = yvel[nSprite] + Random2(-dy);
zvel[pFX->index] = zvel[nSprite] - Random(0x1aaaa);
}
}
if (pXSprite->burnTime > 0)
evPost(nSprite, 3, 5, kCallbackFXFlameLick);
}
void Remove(int nSprite) // 1
{
spritetype *pSprite = &sprite[nSprite];
evKill(nSprite, 3);
if (pSprite->extra > 0)
seqKill(3, pSprite->extra);
sfxKill3DSound(pSprite, 0, -1);
DeleteSprite(nSprite);
}
void FlareBurst(int nSprite) // 2
{
dassert(nSprite >= 0 && nSprite < kMaxSprites);
spritetype *pSprite = &sprite[nSprite];
int nAngle = getangle(xvel[nSprite], yvel[nSprite]);
int nRadius = 0x55555;
for (int i = 0; i < 8; i++)
{
spritetype *pSpawn = actSpawnSprite(pSprite, 5);
pSpawn->picnum = 2424;
pSpawn->shade = -128;
pSpawn->xrepeat = pSpawn->yrepeat = 32;
pSpawn->type = kMissileFlareAlt;
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);
xvel[pSpawn->index] += dx;
yvel[pSpawn->index] += dy;
zvel[pSpawn->index] += dz;
evPost(pSpawn->index, 3, 960, kCallbackRemove);
}
evPost(nSprite, 3, 0, kCallbackRemove);
}
void fxFlareSpark(int nSprite) // 3
{
spritetype *pSprite = &sprite[nSprite];
spritetype *pFX = gFX.fxSpawn(FX_28, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(0x1aaaa);
yvel[pFX->index] = yvel[nSprite] + Random2(0x1aaaa);
zvel[pFX->index] = zvel[nSprite] - Random(0x1aaaa);
}
evPost(nSprite, 3, 4, kCallbackFXFlareSpark);
}
void fxFlareSparkLite(int nSprite) // 4
{
spritetype *pSprite = &sprite[nSprite];
spritetype *pFX = gFX.fxSpawn(FX_28, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(0x1aaaa);
yvel[pFX->index] = yvel[nSprite] + Random2(0x1aaaa);
zvel[pFX->index] = zvel[nSprite] - Random(0x1aaaa);
}
evPost(nSprite, 3, 12, kCallbackFXFlareSparkLite);
}
void fxZombieBloodSpurt(int nSprite) // 5
{
dassert(nSprite >= 0 && nSprite < kMaxSprites);
spritetype *pSprite = &sprite[nSprite];
int nXSprite = pSprite->extra;
dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
XSPRITE *pXSprite = &xsprite[nXSprite];
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
spritetype *pFX = gFX.fxSpawn(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, top, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(0x11111);
yvel[pFX->index] = yvel[nSprite] + Random2(0x11111);
zvel[pFX->index] = zvel[nSprite] - 0x6aaaa;
}
if (pXSprite->data1 > 0)
{
evPost(nSprite, 3, 4, kCallbackFXZombieSpurt);
pXSprite->data1 -= 4;
}
else if (pXSprite->data2 > 0)
{
evPost(nSprite, 3, 60, kCallbackFXZombieSpurt);
pXSprite->data1 = 40;
pXSprite->data2--;
}
}
void fxBloodSpurt(int nSprite) // 6
{
spritetype *pSprite = &sprite[nSprite];
spritetype *pFX = gFX.fxSpawn(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
pFX->ang = 0;
xvel[pFX->index] = xvel[nSprite]>>8;
yvel[pFX->index] = yvel[nSprite]>>8;
zvel[pFX->index] = zvel[nSprite]>>8;
}
evPost(nSprite, 3, 6, kCallbackFXBloodSpurt);
}
void fxArcSpark(int nSprite) // 7
{
spritetype* pSprite = &sprite[nSprite];
spritetype* pFX = gFX.fxSpawn(FX_15, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(0x10000);
yvel[pFX->index] = yvel[nSprite] + Random2(0x10000);
zvel[pFX->index] = zvel[nSprite] - Random(0x1aaaa);
}
evPost(nSprite, 3, 3, kCallbackFXArcSpark);
}
void fxDynPuff(int nSprite) // 8
{
spritetype *pSprite = &sprite[nSprite];
if (zvel[nSprite])
{
int nDist = (pSprite->xrepeat*(tilesiz[pSprite->picnum].x/2))>>2;
int x = pSprite->x + mulscale30(nDist, Cos(pSprite->ang-512));
int y = pSprite->y + mulscale30(nDist, Sin(pSprite->ang-512));
int z = pSprite->z;
spritetype *pFX = gFX.fxSpawn(FX_7, pSprite->sectnum, x, y, z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite];
yvel[pFX->index] = yvel[nSprite];
zvel[pFX->index] = zvel[nSprite];
}
}
evPost(nSprite, 3, 12, kCallbackFXDynPuff);
}
void Respawn(int nSprite) // 9
{
spritetype *pSprite = &sprite[nSprite];
dassert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites);
XSPRITE *pXSprite = &xsprite[pSprite->extra];
if (pSprite->statnum != kStatRespawn && pSprite->statnum != kStatThing) {
viewSetSystemMessage("Sprite #%d is not on Respawn or Thing list\n", nSprite);
return;
} else if (!(pSprite->flags & kHitagRespawn)) {
viewSetSystemMessage("Sprite #%d does not have the respawn attribute\n", nSprite);
return;
}
switch (pXSprite->respawnPending) {
case 1: {
int nTime = mulscale16(actGetRespawnTime(pSprite), 0x4000);
pXSprite->respawnPending = 2;
evPost(nSprite, 3, nTime, kCallbackRespawn);
break;
}
case 2: {
int nTime = mulscale16(actGetRespawnTime(pSprite), 0x2000);
pXSprite->respawnPending = 3;
evPost(nSprite, 3, nTime, kCallbackRespawn);
break;
}
case 3: {
dassert(pSprite->owner != kStatRespawn);
dassert(pSprite->owner >= 0 && pSprite->owner < kMaxStatus);
ChangeSpriteStat(nSprite, pSprite->owner);
pSprite->type = pSprite->inittype;
pSprite->owner = -1;
pSprite->flags &= ~kHitagRespawn;
xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0;
pXSprite->respawnPending = 0;
pXSprite->burnTime = 0;
pXSprite->isTriggered = 0;
if (IsDudeSprite(pSprite)) {
int nType = pSprite->type-kDudeBase;
pSprite->x = baseSprite[nSprite].x;
pSprite->y = baseSprite[nSprite].y;
pSprite->z = baseSprite[nSprite].z;
pSprite->cstat |= 0x1101;
pSprite->clipdist = dudeInfo[nType].clipdist;
pXSprite->health = dudeInfo[nType].startHealth<<4;
if (gSysRes.Lookup(dudeInfo[nType].seqStartID, "SEQ"))
seqSpawn(dudeInfo[nType].seqStartID, 3, pSprite->extra, -1);
aiInitSprite(pSprite);
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.fxSpawn(FX_29, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
sfxPlay3DSound(pSprite, 350, -1, 0);
break;
}
}
}
void PlayerBubble(int nSprite) // 10
{
spritetype *pSprite = &sprite[nSprite];
if (IsPlayerSprite(pSprite))
{
PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
dassert(pPlayer != NULL);
if (!pPlayer->bubbleTime)
return;
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
for (int i = 0; i < (pPlayer->bubbleTime>>6); i++)
{
int nDist = (pSprite->xrepeat*(tilesiz[pSprite->picnum].x/2))>>2;
int nAngle = Random(2048);
int x = pSprite->x + mulscale30(nDist, Cos(nAngle));
int y = pSprite->y + mulscale30(nDist, Sin(nAngle));
int z = bottom-Random(bottom-top);
spritetype *pFX = gFX.fxSpawn((FX_ID)(FX_23+Random(3)), pSprite->sectnum, x, y, z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(0x1aaaa);
yvel[pFX->index] = yvel[nSprite] + Random2(0x1aaaa);
zvel[pFX->index] = zvel[nSprite] + Random2(0x1aaaa);
}
}
evPost(nSprite, 3, 4, kCallbackPlayerBubble);
}
}
void EnemyBubble(int nSprite) // 11
{
spritetype *pSprite = &sprite[nSprite];
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
for (int i = 0; i < (klabs(zvel[nSprite])>>18); i++)
{
int nDist = (pSprite->xrepeat*(tilesiz[pSprite->picnum].x/2))>>2;
int nAngle = Random(2048);
int x = pSprite->x + mulscale30(nDist, Cos(nAngle));
int y = pSprite->y + mulscale30(nDist, Sin(nAngle));
int z = bottom-Random(bottom-top);
spritetype *pFX = gFX.fxSpawn((FX_ID)(FX_23+Random(3)), pSprite->sectnum, x, y, z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(0x1aaaa);
yvel[pFX->index] = yvel[nSprite] + Random2(0x1aaaa);
zvel[pFX->index] = zvel[nSprite] + Random2(0x1aaaa);
}
}
evPost(nSprite, 3, 4, kCallbackEnemeyBubble);
}
void CounterCheck(int nSector) // 12
{
dassert(nSector >= 0 && nSector < kMaxSectors);
// By NoOne: remove check below, so every sector can be counter if command 12 (this callback) received.
//if (pSector->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;
for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite]) {
if (sprite[nSprite].type == nType) nCount++;
}
if (nCount < nReq) {
evPost(nSector, 6, 5, kCallbackCounterCheck);
return;
} else {
//pXSector->waitTimeA = 0; //do not reset necessary objects counter to zero
trTriggerSector(nSector, pXSector, kCmdOn, -1);
pXSector->locked = 1; //lock sector, so it can be opened again later
}
}
void FinishHim(int nSprite) // 13
{
spritetype* pSprite = &sprite[nSprite];
int nXSprite = pSprite->extra;
XSPRITE* pXSprite = &xsprite[nXSprite];
if (playerSeqPlaying(&gPlayer[pSprite->type - kDudePlayer1], 16) && pXSprite->target == gMe->nSprite)
sndStartSample(3313, -1, 1, 0);
}
void fxBloodBits(int nSprite) // 14
{
spritetype *pSprite = &sprite[nSprite];
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+mulscale28(nDist, Cos(nAngle));
int y = pSprite->y+mulscale28(nDist, Sin(nAngle));
gFX.fxSpawn(FX_48, pSprite->sectnum, x, y, pSprite->z, 0);
if (pSprite->ang == 1024)
{
int nChannel = 28+(pSprite->index&2);
dassert(nChannel < 32);
sfxPlay3DSound(pSprite, 385, nChannel, 1);
}
if (Chance(0x5000))
{
spritetype *pFX = gFX.fxSpawn(FX_36, pSprite->sectnum, x, y, floorZ-64, 0);
if (pFX)
pFX->ang = nAngle;
}
gFX.sub_73FFC(nSprite);
}
void fxTeslaAlt(int nSprite) // 15
{
spritetype* pSprite = &sprite[nSprite];
spritetype* pFX = gFX.fxSpawn(FX_49, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
xvel[pFX->index] = xvel[nSprite] + Random2(0x1aaaa);
yvel[pFX->index] = yvel[nSprite] + Random2(0x1aaaa);
zvel[pFX->index] = zvel[nSprite] - Random(0x1aaaa);
}
evPost(nSprite, 3, 3, kCallbackFXTeslaAlt);
}
int tommySleeveSnd[] = { 608, 609, 611 }; // unused?
int sawedOffSleeveSnd[] = { 610, 612 };
void fxBouncingSleeve(int nSprite) // 16
{
spritetype* pSprite = &sprite[nSprite]; 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 zv = zvel[nSprite] - velFloor[pSprite->sectnum];
if (zvel[nSprite] == 0) sleeveStopBouncing(pSprite);
else if (zv > 0) {
actFloorBounceVector((int*)& xvel[nSprite], (int*)& yvel[nSprite], &zv, pSprite->sectnum, 0x9000);
zvel[nSprite] = zv;
if (velFloor[pSprite->sectnum] == 0 && klabs(zvel[nSprite]) < 0x20000) {
sleeveStopBouncing(pSprite);
return;
}
int nChannel = 28 + (pSprite->index & 2);
dassert(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) {
xvel[pSprite->index] = yvel[pSprite->index] = zvel[pSprite->index] = 0;
if (pSprite->extra > 0) seqKill(3, pSprite->extra);
sfxKill3DSound(pSprite, -1, -1);
switch (pSprite->type) {
case 37:
case 38:
case 39:
pSprite->picnum = 2465;
break;
case 40:
case 41:
case 42:
pSprite->picnum = 2464;
break;
}
pSprite->type = 51;
pSprite->xrepeat = pSprite->yrepeat = 10;
}
void returnFlagToBase(int nSprite) // 17
{
spritetype* pSprite = &sprite[nSprite];
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, pOwner->index);
sndStartSample(8003, 255, 2, 0);
gBlueFlagDropped = false;
viewSetMessage("Blue Flag returned to base.");
break;
case kItemFlagB:
trTriggerSprite(pOwner->index, pXOwner, kCmdOn, pOwner->index);
sndStartSample(8002, 255, 2, 0);
gRedFlagDropped = false;
viewSetMessage("Red Flag returned to base.");
break;
}
}
evPost(pSprite->index, 3, 0, kCallbackRemove);
}
void fxPodBloodSpray(int nSprite) // 18
{
spritetype* pSprite = &sprite[nSprite];
spritetype* pFX;
if (pSprite->type == 53)
pFX = gFX.fxSpawn(FX_53, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
else
pFX = gFX.fxSpawn(FX_54, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
if (pFX)
{
pFX->ang = 0;
xvel[pFX->index] = xvel[nSprite] >> 8;
yvel[pFX->index] = yvel[nSprite] >> 8;
zvel[pFX->index] = zvel[nSprite] >> 8;
}
evPost(nSprite, 3, 6, kCallbackFXPodBloodSpray);
}
void fxPodBloodSplat(int nSprite) // 19
{
spritetype *pSprite = &sprite[nSprite];
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+mulscale28(nDist, Cos(nAngle));
int y = pSprite->y+mulscale28(nDist, Sin(nAngle));
if (pSprite->ang == 1024)
{
int nChannel = 28+(pSprite->index&2);
dassert(nChannel < 32);
sfxPlay3DSound(pSprite, 385, nChannel, 1);
}
spritetype *pFX = NULL;
if (pSprite->type == 53 || pSprite->type == kThingPodGreenBall)
{
if (Chance(0x500) || pSprite->type == kThingPodGreenBall)
pFX = gFX.fxSpawn(FX_55, pSprite->sectnum, x, y, floorZ-64, 0);
if (pFX)
pFX->ang = nAngle;
}
else
{
pFX = gFX.fxSpawn(FX_32, pSprite->sectnum, x, y, floorZ-64, 0);
if (pFX)
pFX->ang = nAngle;
}
gFX.sub_73FFC(nSprite);
}
void LeechStateTimer(int nSprite) // 20
{
spritetype *pSprite = &sprite[nSprite];
if (pSprite->statnum == kStatThing && !(pSprite->flags & 32)) {
switch (pSprite->type) {
case kThingDroppedLifeLeech:
case kModernThingEnemyLifeLeech:
xsprite[pSprite->extra].stateTimer = 0;
break;
}
}
}
void sub_76A08(spritetype *pSprite, spritetype *pSprite2, PLAYER *pPlayer) // ???
{
int top, bottom;
int nSprite = pSprite->index;
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;
ChangeSpriteSect(nSprite, pSprite2->sectnum);
sfxPlay3DSound(pSprite2, 201, -1, 0);
xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0;
viewBackupSpriteLoc(nSprite, pSprite);
if (pPlayer)
{
playerResetInertia(pPlayer);
pPlayer->zViewVel = pPlayer->zWeaponVel = 0;
}
}
void DropVoodoo(int nSprite) // unused
{
spritetype *pSprite = &sprite[nSprite];
int nOwner = actSpriteOwnerToSpriteId(pSprite);
if (nOwner < 0 || nOwner >= kMaxSprites)
{
evPost(nSprite, 3, 0, kCallbackRemove);
return;
}
spritetype *pOwner = &sprite[nOwner];
PLAYER *pPlayer;
if (IsPlayerSprite(pOwner))
pPlayer = &gPlayer[pOwner->type-kDudePlayer1];
else
pPlayer = NULL;
if (!pPlayer)
{
evPost(nSprite, 3, 0, kCallbackRemove);
return;
}
pSprite->ang = getangle(pOwner->x-pSprite->x, pOwner->y-pSprite->y);
int nXSprite = pSprite->extra;
if (nXSprite > 0)
{
XSPRITE *pXSprite = &xsprite[nXSprite];
if (pXSprite->data1 == 0)
{
evPost(nSprite, 3, 0, kCallbackRemove);
return;
}
int nSprite2, nNextSprite;
for (nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nNextSprite)
{
nNextSprite = nextspritestat[nSprite2];
if (nOwner == nSprite2)
continue;
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)
continue;
int t = 0x8000/ClipLow(gNetPlayers-1, 1);
if (!powerupCheck(pPlayer2, kPwUpDeathMask))
t += ((3200-pPlayer2->armor[2])<<15)/3200;
if (Chance(t) || nNextSprite < 0)
{
int nDmg = actDamageSprite(nOwner, pSprite2, DAMAGE_TYPE_5, pXSprite->data1<<4);
pXSprite->data1 = ClipLow(pXSprite->data1-nDmg, 0);
sub_76A08(pSprite2, pSprite, pPlayer2);
evPost(nSprite, 3, 0, kCallbackRemove);
return;
}
}
else
{
int vd = 0x2666;
switch (pSprite2->type)
{
case kDudeBoneEel:
case kDudeBat:
case kDudeRat:
case kDudeTinyCaleb:
case kDudeBeast:
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:
vd = 0;
break;
}
if (vd && (Chance(vd) || nNextSprite < 0))
{
sub_76A08(pSprite2, pSprite, NULL);
evPost(nSprite, 3, 0, kCallbackRemove);
return;
}
}
}
}
}
pXSprite->data1 = ClipLow(pXSprite->data1-1, 0);
evPost(nSprite, 3, 0, kCallbackRemove);
}
}
void UniMissileBurst(int nSprite) // 22
{
dassert(nSprite >= 0 && nSprite < kMaxSprites);
if (sprite[nSprite].statnum != kStatProjectile) return;
spritetype * pSprite = &sprite[nSprite];
int nAngle = getangle(xvel[nSprite], yvel[nSprite]);
int nRadius = 0x55555;
for (int i = 0; i < 8; i++)
{
spritetype* pBurst = actSpawnSprite(pSprite, 5);
pBurst->type = pSprite->type;
pBurst->shade = pSprite->shade;
pBurst->picnum = pSprite->picnum;
pBurst->cstat = pSprite->cstat;
if ((pBurst->cstat & CSTAT_SPRITE_BLOCK)) {
pBurst->cstat &= ~CSTAT_SPRITE_BLOCK; // we don't want missiles impact each other
evPost(pBurst->xvel, 3, 100, kCallbackMissileSpriteBlock); // so set blocking flag a bit later
}
pBurst->pal = pSprite->pal;
pBurst->clipdist = pSprite->clipdist / 4;
pBurst->flags = pSprite->flags;
pBurst->xrepeat = pSprite->xrepeat / 2;
pBurst->yrepeat = pSprite->yrepeat / 2;
pBurst->ang = ((pSprite->ang + missileInfo[pSprite->type - kMissileBase].angleOfs) & 2047);
pBurst->owner = pSprite->owner;
actBuildMissile(pBurst, pBurst->extra, pSprite->xvel);
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);
xvel[pBurst->index] += dx;
yvel[pBurst->index] += dy;
zvel[pBurst->index] += dz;
evPost(pBurst->index, 3, 960, kCallbackRemove);
}
evPost(nSprite, 3, 0, kCallbackRemove);
}
void makeMissileBlocking(int nSprite) // 23
{
dassert(nSprite >= 0 && nSprite < kMaxSprites);
if (sprite[nSprite].statnum != kStatProjectile) return;
sprite[nSprite].cstat |= CSTAT_SPRITE_BLOCK;
}
void(*gCallback[kCallbackMax])(int) =
{
fxFlameLick,
Remove,
FlareBurst,
fxFlareSpark,
fxFlareSparkLite,
fxZombieBloodSpurt,
fxBloodSpurt,
fxArcSpark,
fxDynPuff,
Respawn,
PlayerBubble,
EnemyBubble,
CounterCheck,
FinishHim,
fxBloodBits,
fxTeslaAlt,
fxBouncingSleeve,
returnFlagToBase,
fxPodBloodSpray,
fxPodBloodSplat,
LeechStateTimer,
DropVoodoo, // unused
UniMissileBurst,
makeMissileBlocking,
};
END_BLD_NS