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

894 lines
24 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-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxFlameLick(DBloodActor* actor, sectortype*) // 0
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-28 11:39:52 +00:00
double top, bottom;
2021-12-29 19:03:42 +00:00
GetActorExtents(actor, &top, &bottom);
auto tex = TexMan.GetGameTexture(actor->spr.spritetexture());
double nDist = (actor->spr.scale.X * tex->GetDisplayWidth()) * (1. / 4);
2021-12-29 19:03:42 +00:00
for (int i = 0; i < 3; i++)
{
2022-09-28 11:39:52 +00:00
DAngle nAngle = RandomAngle();
DVector2 dv = nAngle.ToVector() * nDist;
DVector2 pos = actor->spr.pos.XY() + dv;
double z = bottom - RandomD(bottom - top, 8);
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_32, actor->sector(), DVector3(pos, z));
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-28 11:39:52 +00:00
pFX->vel.X = actor->vel.X + Random2F(-int(16 * dv.X));
pFX->vel.Y = actor->vel.Y + Random2F(-int(16 * dv.Y));
2022-09-16 16:49:21 +00:00
pFX->vel.Z = actor->vel.Z - Random2F(0x1aaaa);
2021-12-29 19:03:42 +00:00
}
}
if (actor->xspr.burnTime > 0)
evPostActor(actor, 5, kCallbackFXFlameLick);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void Remove(DBloodActor* actor, sectortype*) // 1
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
evKillActor(actor, kCallbackFXFlareSpark);
if (actor->hasX())
seqKill(actor);
sfxKill3DSound(actor, 0, -1);
DeleteSprite(actor);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void FlareBurst(DBloodActor* actor, sectortype*) // 2
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-18 11:53:03 +00:00
auto nAngVec = actor->vel.XY().Angle().ToVector();
double nRadius = FixedToFloat(0x55555);
2021-12-29 19:03:42 +00:00
for (int i = 0; i < 8; i++)
{
auto spawnedactor = actSpawnSprite(actor, kStatProjectile);
spawnedactor->spr.setspritetexture(aTexIds[kTexFLAREBURST]);
2021-12-29 19:03:42 +00:00
spawnedactor->spr.shade = -128;
spawnedactor->spr.scale = DVector2(0.5, 0.5);
2023-09-30 08:01:27 +00:00
spawnedactor->ChangeType(kMissileFlareAlt);
spawnedactor->clipdist = 0.5;
2021-12-29 19:03:42 +00:00
spawnedactor->SetOwner(actor);
2022-09-18 11:53:03 +00:00
auto spAngVec = DAngle::fromBam(i << 29).ToVector().Rotated90CW() * nRadius;
if (i & 1) spAngVec *= 0.5;
spawnedactor->vel += DVector3(DVector2(0, spAngVec.X).Rotated(nAngVec.X, nAngVec.Y), spAngVec.Y);
2021-12-29 19:03:42 +00:00
evPostActor(spawnedactor, 960, kCallbackRemove);
}
evPostActor(actor, 0, kCallbackRemove);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxFlareSpark(DBloodActor* actor, sectortype*) // 3
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_28, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:09:28 +00:00
pFX->vel.X = actor->vel.X + Random2F(0x1aaaa);
pFX->vel.Y = actor->vel.Y + Random2F(0x1aaaa);
2022-09-16 16:49:21 +00:00
pFX->vel.Z = actor->vel.Z - Random2F(0x1aaaa);
2021-12-29 19:03:42 +00:00
}
evPostActor(actor, 4, kCallbackFXFlareSpark);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxFlareSparkLite(DBloodActor* actor, sectortype*) // 4
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_28, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:09:28 +00:00
pFX->vel.X = actor->vel.X + Random2F(0x1aaaa);
pFX->vel.Y = actor->vel.Y + Random2F(0x1aaaa);
2022-09-16 16:49:21 +00:00
pFX->vel.Z = actor->vel.Z - Random2F(0x1aaaa);
2021-12-29 19:03:42 +00:00
}
evPostActor(actor, 12, kCallbackFXFlareSparkLite);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxZombieBloodSpurt(DBloodActor* actor, sectortype*) // 5
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
assert(actor->hasX());
double top, bottom;
2021-12-29 19:03:42 +00:00
GetActorExtents(actor, &top, &bottom);
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_27, actor->sector(), DVector3(actor->spr.pos.XY(), top));
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:09:28 +00:00
pFX->vel.X = actor->vel.X + Random2F(0x11111);
pFX->vel.Y = actor->vel.Y + Random2F(0x11111);
pFX->vel.Z = actor->vel.Z - 6.66666;
2021-12-29 19:03:42 +00:00
}
if (actor->xspr.data1 > 0)
{
evPostActor(actor, 4, kCallbackFXZombieSpurt);
actor->xspr.data1 -= 4;
}
else if (actor->xspr.data2 > 0)
{
evPostActor(actor, 60, kCallbackFXZombieSpurt);
actor->xspr.data1 = 40;
actor->xspr.data2--;
}
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxBloodSpurt(DBloodActor* actor, sectortype*) // 6
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_27, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
if (pFX)
{
pFX->spr.Angles.Yaw = nullAngle;
2022-09-03 08:12:09 +00:00
pFX->vel = actor->vel * (1./256);
2021-12-29 19:03:42 +00:00
}
evPostActor(actor, 6, kCallbackFXBloodSpurt);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxArcSpark(DBloodActor* actor, sectortype*) // 7
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_15, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:09:28 +00:00
pFX->vel.X = actor->vel.X + Random2F(0x10000);
pFX->vel.Y = actor->vel.Y + Random2F(0x10000);
2022-09-16 16:49:21 +00:00
pFX->vel.Z = actor->vel.Z - Random2F(0x1aaaa);
2021-12-29 19:03:42 +00:00
}
evPostActor(actor, 3, kCallbackFXArcSpark);
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxDynPuff(DBloodActor* actor, sectortype*) // 8
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-28 11:45:46 +00:00
if (actor->vel.Z)
2021-12-29 19:03:42 +00:00
{
auto tex = TexMan.GetGameTexture(actor->spr.spritetexture());
double nDist = (actor->spr.scale.X * tex->GetDisplayWidth()) * (1. / 2);
DVector3 pos = actor->spr.pos + (actor->spr.Angles.Yaw - DAngle90).ToVector() * nDist;
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_7, actor->sector(), pos);
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:11:05 +00:00
pFX->vel = actor->vel;
2021-12-29 19:03:42 +00:00
}
}
evPostActor(actor, 12, kCallbackFXDynPuff);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void Respawn(DBloodActor* actor, sectortype*) // 9
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
assert(actor->hasX());
if (actor->spr.statnum != kStatRespawn && actor->spr.statnum != kStatThing) {
viewSetSystemMessage("Sprite #%d is not on Respawn or Thing list\n", actor->GetIndex());
return;
}
else if (!(actor->spr.flags & kHitagRespawn)) {
viewSetSystemMessage("Sprite #%d does not have the respawn attribute\n", actor->GetIndex());
return;
}
switch (actor->xspr.respawnPending) {
case 1: {
int nTime = MulScale(actGetRespawnTime(actor), 0x4000, 16);
actor->xspr.respawnPending = 2;
evPostActor(actor, nTime, kCallbackRespawn);
break;
}
case 2: {
int nTime = MulScale(actGetRespawnTime(actor), 0x2000, 16);
actor->xspr.respawnPending = 3;
evPostActor(actor, nTime, kCallbackRespawn);
break;
}
case 3: {
assert(actor->spr.intowner != kStatRespawn);
assert(actor->spr.intowner >= 0 && actor->spr.intowner < kMaxStatus);
ChangeActorStat(actor, actor->spr.intowner);
2023-09-30 08:01:27 +00:00
actor->ChangeType(actor->spr.inittype);
2021-12-29 19:03:42 +00:00
actor->SetOwner(nullptr);
actor->spr.flags &= ~kHitagRespawn;
2022-11-20 10:27:51 +00:00
actor->vel.Zero();
2021-12-29 19:03:42 +00:00
actor->xspr.respawnPending = 0;
actor->xspr.burnTime = 0;
actor->xspr.isTriggered = 0;
if (actor->IsDudeActor())
{
int nType = actor->GetType() - kDudeBase;
2022-08-10 22:13:36 +00:00
actor->spr.pos = actor->basePoint;
2021-12-29 19:03:42 +00:00
actor->spr.cstat |= CSTAT_SPRITE_BLOOD_BIT1 | CSTAT_SPRITE_BLOCK_ALL;
#ifdef NOONE_EXTENSIONS
if (!gModernMap || actor->xspr.sysData2 <= 0) actor->xspr.health = dudeInfo[actor->GetType() - kDudeBase].startHealth << 4;
2021-12-29 19:03:42 +00:00
else actor->xspr.health = ClipRange(actor->xspr.sysData2 << 4, 1, 65535);
switch (actor->GetType()) {
2021-12-29 19:03:42 +00:00
default:
actor->clipdist = getDudeInfo(nType + kDudeBase)->fClipdist();
2021-12-29 19:03:42 +00:00
if (getSequence(getDudeInfo(nType + kDudeBase)->seqStartID))
seqSpawn(getDudeInfo(nType + kDudeBase)->seqStartID, actor, -1);
break;
case kDudeModernCustom:
seqSpawn(genDudeSeqStartId(actor), actor, -1);
break;
}
// return dude to the patrol state
if (gModernMap && actor->xspr.dudeFlag4) {
actor->xspr.data3 = 0;
actor->SetTarget(nullptr);
}
#else
actor->clipdist = getDudeInfo(nType + kDudeBase)->fClipdist();
2021-12-29 19:03:42 +00:00
actor->xspr.health = getDudeInfo(nType + kDudeBase)->startHealth << 4;
if (getSequence(getDudeInfo(nType + kDudeBase)->seqStartID))
seqSpawn(getDudeInfo(nType + kDudeBase)->seqStartID, actor, -1);
#endif
aiInitSprite(actor);
actor->xspr.key = 0;
}
else if (actor->GetType() == kThingTNTBarrel) {
2021-12-29 19:03:42 +00:00
actor->spr.cstat |= CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN;
actor->spr.cstat &= ~CSTAT_SPRITE_INVISIBLE;
}
2022-09-29 13:13:19 +00:00
gFX.fxSpawnActor(FX_29, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
sfxPlay3DSound(actor, 350, -1, 0);
break;
}
}
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void PlayerBubble(DBloodActor* actor, sectortype*) // 10
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
if (actor->IsPlayerActor())
{
auto pPlayer = getPlayer(actor);
2021-12-29 19:03:42 +00:00
if (!pPlayer->bubbleTime)
return;
2022-09-28 11:48:37 +00:00
double top, bottom;
2021-12-29 19:03:42 +00:00
GetActorExtents(actor, &top, &bottom);
auto tex = TexMan.GetGameTexture(actor->spr.spritetexture());
double nDist = (actor->spr.scale.X * tex->GetDisplayWidth()) * (1. / 2);
2021-12-29 19:03:42 +00:00
for (int i = 0; i < (pPlayer->bubbleTime >> 6); i++)
{
DVector2 pos = actor->spr.pos.XY() + actor->spr.Angles.Yaw.ToVector() * nDist;
2022-09-28 11:48:37 +00:00
double z = bottom - RandomD(bottom - top, 8);
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor((FX_ID)(FX_23 + Random(3)), actor->sector(), DVector3(pos, z));
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:09:28 +00:00
pFX->vel.X = actor->vel.X + Random2F(0x1aaaa);
pFX->vel.Y = actor->vel.Y + Random2F(0x1aaaa);
pFX->vel.Z = actor->vel.Z + Random2F(0x1aaaa);
2021-12-29 19:03:42 +00:00
}
}
evPostActor(actor, 4, kCallbackPlayerBubble);
}
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void EnemyBubble(DBloodActor* actor, sectortype*) // 11
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-28 11:50:32 +00:00
double top, bottom;
2021-12-29 19:03:42 +00:00
GetActorExtents(actor, &top, &bottom);
auto tex = TexMan.GetGameTexture(actor->spr.spritetexture());
double nDist = (actor->spr.scale.X * tex->GetDisplayWidth()) * (1. / 2);
for (int i = 0; i < int(abs(actor->vel.Z) * 0.25); i++)
2021-12-29 19:03:42 +00:00
{
2022-09-28 11:50:32 +00:00
auto nAngle = RandomAngle();
DVector2 pos = actor->spr.pos.XY() + nAngle.ToVector() * nDist;
double z = bottom - RandomD(bottom - top, 8);
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor((FX_ID)(FX_23 + Random(3)), actor->sector(), DVector3(pos, z));
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:09:28 +00:00
pFX->vel.X = actor->vel.X + Random2F(0x1aaaa);
pFX->vel.Y = actor->vel.Y + Random2F(0x1aaaa);
pFX->vel.Z = actor->vel.Z + Random2F(0x1aaaa);
2021-12-29 19:03:42 +00:00
}
}
evPostActor(actor, 4, kCallbackEnemeyBubble);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void CounterCheck(DBloodActor*, sectortype* pSector) // 12
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!pSector || pSector->type != kSectorCounter) return;
if (!pSector->hasX()) return;
XSECTOR* pXSector = &pSector->xs();
int nReq = pXSector->waitTimeA;
int nType = pXSector->data;
int nCount = 0;
if (!nType || !nReq) return;
BloodSectIterator it(pSector);
while (auto actor = it.Next())
{
if (actor->GetType() == nType) nCount++;
2021-12-29 19:03:42 +00:00
}
if (nCount < nReq) {
evPostSector(pSector, 5, kCallbackCounterCheck);
return;
}
else {
//pXSector->waitTimeA = 0; //do not reset necessary objects counter to zero
trTriggerSector(pSector, kCmdOn);
pXSector->locked = 1; //lock sector, so it can be opened again later
}
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void FinishHim(DBloodActor* actor, sectortype*) // 13
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
if (actor->IsPlayerActor() && playerSeqPlaying(getPlayer(actor), 16) && actor == getPlayer(myconnectindex)->GetActor())
2021-12-29 19:03:42 +00:00
sndStartSample(3313, -1, 1, 0);
}
2021-11-24 00:05:17 +00:00
void fxBloodBits(DBloodActor* actor, sectortype*) // 14
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-26 15:59:18 +00:00
double ceilZ, floorZ;
2021-12-29 19:03:42 +00:00
Collision floorColl, ceilColl;
GetZRange(actor, &ceilZ, &ceilColl, &floorZ, &floorColl, actor->clipdist * 0.25, CLIPMASK0);
2022-09-26 15:59:18 +00:00
double top, bottom;
2021-12-29 19:03:42 +00:00
GetActorExtents(actor, &top, &bottom);
2022-09-26 15:59:18 +00:00
actor->spr.pos.Z += floorZ - bottom;
DAngle nAngle = RandomAngle();
2022-09-26 15:59:18 +00:00
int nDist = Random(16);
auto pos = actor->spr.pos + nAngle.ToVector() * nDist * 4;
2022-09-29 13:13:19 +00:00
gFX.fxSpawnActor(FX_48, actor->sector(), DVector3(pos, actor->spr.pos.Z));
if (actor->spr.Angles.Yaw == DAngle180)
2021-12-29 19:03:42 +00:00
{
int nChannel = 28 + (actor->GetIndex() & 2); // this is a little stupid...
sfxPlay3DSound(actor, 385, nChannel, 1);
}
if (Chance(0x5000))
{
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_36, actor->sector(), DVector3(pos, floorZ - 0.25));
2021-12-29 19:03:42 +00:00
if (pFX)
pFX->spr.Angles.Yaw = nAngle;
2021-12-29 19:03:42 +00:00
}
gFX.remove(actor);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxTeslaAlt(DBloodActor* actor, sectortype*) // 15
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
2022-09-29 13:13:19 +00:00
auto pFX = gFX.fxSpawnActor(FX_49, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
if (pFX)
{
2022-09-03 08:09:28 +00:00
pFX->vel.X = actor->vel.X + Random2F(0x1aaaa);
pFX->vel.Y = actor->vel.Y + Random2F(0x1aaaa);
2022-09-16 16:49:21 +00:00
pFX->vel.Z = actor->vel.Z - Random2F(0x1aaaa);
2021-12-29 19:03:42 +00:00
}
evPostActor(actor, 3, kCallbackFXTeslaAlt);
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static const int tommySleeveSnd[] = { 608, 609, 611 }; // unused?
static const int sawedOffSleeveSnd[] = { 610, 612 };
2021-11-24 00:05:17 +00:00
void fxBouncingSleeve(DBloodActor* actor, sectortype*) // 16
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
double ceilZ, floorZ;
2021-12-29 19:03:42 +00:00
Collision floorColl, ceilColl;
GetZRange(actor, &ceilZ, &ceilColl, &floorZ, &floorColl, actor->clipdist * 0.25, CLIPMASK0);
double top, bottom;
GetActorExtents(actor, &top, &bottom);
actor->spr.pos.Z += floorZ - bottom;
2021-12-29 19:03:42 +00:00
double veldiff = actor->vel.Z - actor->sector()->velFloor;
2021-12-29 19:03:42 +00:00
2022-09-03 08:12:09 +00:00
if (actor->vel.Z == 0) sleeveStopBouncing(actor);
else if (veldiff > 0)
{
auto vec4 = actFloorBounceVector(actor, veldiff, actor->sector(), FixedToFloat(0x9000));
actor->vel = vec4.XYZ();
if (actor->sector()->velFloor == 0 && abs(actor->vel.Z) < 2)
{
2021-12-29 19:03:42 +00:00
sleeveStopBouncing(actor);
return;
}
int nChannel = 28 + (actor->GetIndex() & 2);
// tommy sleeve
if (actor->GetType() >= fxType(FX_37) && actor->GetType() <= fxType(FX_39))
{
2021-12-29 19:03:42 +00:00
Random(3);
sfxPlay3DSound(actor, 608 + Random(2), nChannel, 1);
// sawed-off sleeve
}
else
{
2021-12-29 19:03:42 +00:00
sfxPlay3DSound(actor, sawedOffSleeveSnd[Random(2)], nChannel, 1);
}
}
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-12-29 19:03:42 +00:00
void sleeveStopBouncing(DBloodActor* actor)
2021-08-27 15:04:34 +00:00
{
2022-11-20 10:27:51 +00:00
actor->vel.Zero();
2021-12-29 19:03:42 +00:00
if (actor->hasX()) seqKill(actor);
sfxKill3DSound(actor, -1, -1);
switch (actor->GetType()) {
case fxType(FX_37):
case fxType(FX_38):
case fxType(FX_39):
actor->spr.setspritetexture(aTexIds[kTexBULLETCASE]);
2021-12-29 19:03:42 +00:00
break;
case fxType(FX_40):
case fxType(FX_41):
case fxType(FX_42):
actor->spr.setspritetexture(aTexIds[kTexSHELLCASE]);
2021-12-29 19:03:42 +00:00
break;
}
2023-09-30 11:26:00 +00:00
actor->ChangeType(fxType(FX_51));
actor->spr.scale = DVector2(0.15625, 0.15625);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void returnFlagToBase(DBloodActor* actor, sectortype*) // 17
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
auto aOwner = actor->GetOwner();
if (aOwner)
{
switch (actor->GetType())
2021-12-29 19:03:42 +00:00
{
case kItemFlagA:
trTriggerSprite(aOwner, kCmdOn, aOwner);
2021-12-29 19:03:42 +00:00
sndStartSample(8003, 255, 2, 0);
gBlueFlagDropped = false;
viewSetMessage("Blue Flag returned to base.");
break;
case kItemFlagB:
trTriggerSprite(aOwner, kCmdOn, aOwner);
2021-12-29 19:03:42 +00:00
sndStartSample(8002, 255, 2, 0);
gRedFlagDropped = false;
viewSetMessage("Red Flag returned to base.");
break;
}
}
evPostActor(actor, 0, kCallbackRemove);
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxPodBloodSpray(DBloodActor* actor, sectortype*) // 18
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
DBloodActor* pFX;
if (actor->GetType() == fxType(FX_53))
2022-09-29 13:13:19 +00:00
pFX = gFX.fxSpawnActor(FX_53, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
else
2022-09-29 13:13:19 +00:00
pFX = gFX.fxSpawnActor(FX_54, actor->sector(), actor->spr.pos);
2021-12-29 19:03:42 +00:00
if (pFX)
{
pFX->spr.Angles.Yaw = nullAngle;
2022-09-03 08:12:09 +00:00
pFX->vel = actor->vel * (1./256);
2021-12-29 19:03:42 +00:00
}
evPostActor(actor, 6, kCallbackFXPodBloodSpray);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void fxPodBloodSplat(DBloodActor* actor, sectortype*) // 19
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
double ceilZ, floorZ;
2021-12-29 19:03:42 +00:00
Collision floorColl, ceilColl;
GetZRange(actor, &ceilZ, &ceilColl, &floorZ, &floorColl, actor->clipdist * 0.25, CLIPMASK0);
double top, bottom;
2021-12-29 19:03:42 +00:00
GetActorExtents(actor, &top, &bottom);
actor->spr.pos.Z += floorZ - bottom;
DAngle nAngle = RandomAngle();
int nDist = Random(16);
auto pos = actor->spr.pos.XY() + nAngle.ToVector() * nDist * 4;
if (actor->spr.Angles.Yaw == DAngle180 && actor->GetType() == fxType(FX_53))
2021-12-29 19:03:42 +00:00
{
int nChannel = 28 + (actor->GetIndex() & 2);
assert(nChannel < 32);
sfxPlay3DSound(actor, 385, nChannel, 1);
}
DBloodActor* pFX = NULL;
if (actor->GetType() == fxType(FX_53) || actor->GetType() == kThingPodGreenBall)
2021-12-29 19:03:42 +00:00
{
if (Chance(0x500) || actor->GetType() == kThingPodGreenBall)
2022-09-29 13:13:19 +00:00
pFX = gFX.fxSpawnActor(FX_55, actor->sector(), DVector3(pos, floorZ - 0.25));
2021-12-29 19:03:42 +00:00
if (pFX)
pFX->spr.Angles.Yaw = nAngle;
2021-12-29 19:03:42 +00:00
}
else
{
2022-09-29 13:13:19 +00:00
pFX = gFX.fxSpawnActor(FX_32, actor->sector(), DVector3(pos, floorZ - 0.25));
2021-12-29 19:03:42 +00:00
if (pFX)
pFX->spr.Angles.Yaw = nAngle;
2021-12-29 19:03:42 +00:00
}
gFX.remove(actor);
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void LeechStateTimer(DBloodActor* actor, sectortype*) // 20
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
if (actor->spr.statnum == kStatThing && !(actor->spr.flags & 32)) {
switch (actor->GetType()) {
2021-12-29 19:03:42 +00:00
case kThingDroppedLifeLeech:
#ifdef NOONE_EXTENSIONS
case kModernThingEnemyLifeLeech:
#endif
actor->xspr.stateTimer = 0;
break;
}
}
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void sub_76A08(DBloodActor* actor, DBloodActor* actor2, BloodPlayer* pPlayer) // ???
2019-09-19 22:42:45 +00:00
{
2022-08-23 20:52:22 +00:00
double top, bottom;
2021-12-29 19:03:42 +00:00
GetActorExtents(actor, &top, &bottom);
2022-08-23 20:52:22 +00:00
actor->spr.pos = actor2->spr.pos.plusZ(-(bottom - actor->spr.pos.Z));
actor->spr.Angles.Yaw = actor2->spr.Angles.Yaw;
ChangeActorSect(actor, actor2->sector());
2021-12-29 19:03:42 +00:00
sfxPlay3DSound(actor2, 201, -1, 0);
2022-11-20 10:27:51 +00:00
actor->vel.Zero();
2021-12-29 19:03:42 +00:00
viewBackupSpriteLoc(actor);
if (pPlayer)
{
playerResetInertia(pPlayer);
pPlayer->zViewVel = pPlayer->zWeaponVel = 0;
}
2019-09-19 22:42:45 +00:00
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void DropVoodooCb(DBloodActor* actor, sectortype*) // unused
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
if (!actor) return;
auto Owner = actor->GetOwner();
if (Owner == nullptr)
{
evPostActor(actor, 0, kCallbackRemove);
return;
}
BloodPlayer* pPlayer;
2021-12-29 19:03:42 +00:00
if (Owner->IsPlayerActor())
pPlayer = getPlayer(Owner);
2021-12-29 19:03:42 +00:00
else
pPlayer = nullptr;
if (!pPlayer)
{
evPostActor(actor, 0, kCallbackRemove);
return;
}
actor->spr.Angles.Yaw = (Owner->spr.pos - actor->spr.pos).Angle();
2021-12-29 19:03:42 +00:00
if (actor->hasX())
{
if (actor->xspr.data1 == 0)
{
evPostActor(actor, 0, kCallbackRemove);
return;
}
BloodStatIterator it(kStatDude);
while (auto actor2 = it.Next())
{
auto nextactor = it.Peek();
if (Owner == actor2)
continue;
if (actor2->hasX())
{
BloodPlayer* pPlayer2;
2021-12-29 19:03:42 +00:00
if (actor2->IsPlayerActor())
pPlayer2 = getPlayer(actor2);
2021-12-29 19:03:42 +00:00
else
pPlayer2 = nullptr;
if (actor2->xspr.health > 0 && (pPlayer2 || actor2->xspr.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) || nextactor == nullptr)
{
int nDmg = actDamageSprite(actor, actor2, kDamageSpirit, actor->xspr.data1 << 4);
actor->xspr.data1 = ClipLow(actor->xspr.data1 - nDmg, 0);
sub_76A08(actor2, actor, pPlayer2);
evPostActor(actor, 0, kCallbackRemove);
return;
}
}
else
{
int vd = 0x2666;
switch (actor2->GetType())
2021-12-29 19:03:42 +00:00
{
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) || nextactor == nullptr))
{
sub_76A08(actor2, actor, NULL);
evPostActor(actor, 0, kCallbackRemove);
return;
}
}
}
}
}
actor->xspr.data1 = ClipLow(actor->xspr.data1 - 1, 0);
evPostActor(actor, 0, kCallbackRemove);
}
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void callbackCondition(DBloodActor* actor, sectortype*)
2021-09-01 19:54:23 +00:00
{
2021-12-29 19:03:42 +00:00
if (actor->xspr.isTriggered) return;
TRCONDITION const* pCond = &gConditions[actor->xspr.sysData1];
for (auto& obj : pCond->objects)
{
2021-12-29 19:03:42 +00:00
EVENT evn;
evn.target = obj.obj;
evn.cmd = obj.cmd;
2021-12-29 19:03:42 +00:00
evn.funcID = kCallbackCondition;
useCondition(actor, evn);
}
evPostActor(actor, actor->xspr.busyTime, kCallbackCondition);
return;
}
2021-12-29 19:03:42 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-11-24 00:05:17 +00:00
void(*gCallback[kCallbackMax])(DBloodActor*, sectortype*) =
2019-09-19 22:42:45 +00:00
{
2021-12-29 19:03:42 +00:00
fxFlameLick,
Remove,
FlareBurst,
fxFlareSpark,
fxFlareSparkLite,
fxZombieBloodSpurt,
fxBloodSpurt,
fxArcSpark,
fxDynPuff,
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