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

2702 lines
84 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"
#include "build.h"
2019-09-19 22:42:45 +00:00
#include "blood.h"
2020-12-03 17:00:07 +00:00
#include "bloodactor.h"
2019-09-19 22:42:45 +00:00
BEGIN_BLD_NS
2019-09-19 22:42:45 +00:00
void FirePitchfork(int, PLAYER *pPlayer);
void FireSpray(int, PLAYER *pPlayer);
void ThrowCan(int, PLAYER *pPlayer);
void DropCan(int, PLAYER *pPlayer);
void ExplodeCan(int, PLAYER *pPlayer);
void ThrowBundle(int, PLAYER *pPlayer);
void DropBundle(int, PLAYER *pPlayer);
void ExplodeBundle(int, PLAYER *pPlayer);
void ThrowProx(int, PLAYER *pPlayer);
void DropProx(int, PLAYER *pPlayer);
void ThrowRemote(int, PLAYER *pPlayer);
void DropRemote(int, PLAYER *pPlayer);
void FireRemote(int, PLAYER *pPlayer);
void FireShotgun(int nTrigger, PLAYER *pPlayer);
void EjectShell(int, PLAYER *pPlayer);
void FireTommy(int nTrigger, PLAYER *pPlayer);
void FireSpread(int nTrigger, PLAYER *pPlayer);
void AltFireSpread(int nTrigger, PLAYER *pPlayer);
void AltFireSpread2(int nTrigger, PLAYER *pPlayer);
void FireFlare(int nTrigger, PLAYER *pPlayer);
void AltFireFlare(int nTrigger, PLAYER *pPlayer);
void FireVoodoo(int nTrigger, PLAYER *pPlayer);
void AltFireVoodoo(int nTrigger, PLAYER *pPlayer);
void DropVoodoo(int nTrigger, PLAYER *pPlayer);
void FireTesla(int nTrigger, PLAYER *pPlayer);
void AltFireTesla(int nTrigger, PLAYER *pPlayer);
void FireNapalm(int nTrigger, PLAYER *pPlayer);
void FireNapalm2(int nTrigger, PLAYER *pPlayer);
void AltFireNapalm(int nTrigger, PLAYER *pPlayer);
void FireLifeLeech(int nTrigger, PLAYER *pPlayer);
void AltFireLifeLeech(int nTrigger, PLAYER *pPlayer);
void FireBeast(int nTrigger, PLAYER * pPlayer);
typedef void(*QAVTypeCast)(int, void *);
void (*qavClientCallback[])(int, void*) =
2019-09-19 22:42:45 +00:00
{
(QAVTypeCast)FirePitchfork,
(QAVTypeCast)FireSpray,
(QAVTypeCast)ThrowCan,
(QAVTypeCast)DropCan,
(QAVTypeCast)ExplodeCan,
(QAVTypeCast)ThrowBundle,
(QAVTypeCast)DropBundle,
(QAVTypeCast)ExplodeBundle,
(QAVTypeCast)ThrowProx,
(QAVTypeCast)DropProx,
(QAVTypeCast)ThrowRemote,
(QAVTypeCast)DropRemote,
(QAVTypeCast)FireRemote,
(QAVTypeCast)FireShotgun,
(QAVTypeCast)EjectShell,
(QAVTypeCast)FireTommy,
(QAVTypeCast)AltFireSpread2,
(QAVTypeCast)FireSpread,
(QAVTypeCast)AltFireSpread,
(QAVTypeCast)FireFlare,
(QAVTypeCast)AltFireFlare,
(QAVTypeCast)FireVoodoo,
(QAVTypeCast)AltFireVoodoo,
(QAVTypeCast)FireTesla,
(QAVTypeCast)AltFireTesla,
(QAVTypeCast)FireNapalm,
(QAVTypeCast)FireNapalm2,
(QAVTypeCast)FireLifeLeech,
(QAVTypeCast)FireBeast,
(QAVTypeCast)AltFireLifeLeech,
(QAVTypeCast)DropVoodoo,
(QAVTypeCast)AltFireNapalm,
};
enum
2019-09-19 22:42:45 +00:00
{
nClientFirePitchfork,
nClientFireSpray,
nClientThrowCan,
nClientDropCan,
nClientExplodeCan,
nClientThrowBundle,
nClientDropBundle,
nClientExplodeBundle,
nClientThrowProx,
nClientDropProx,
nClientThrowRemote,
nClientDropRemote,
nClientFireRemote,
nClientFireShotgun,
nClientEjectShell,
nClientFireTommy,
nClientAltFireSpread2,
nClientFireSpread,
nClientAltFireSpread,
nClientFireFlare,
nClientAltFireFlare,
nClientFireVoodoo,
nClientAltFireVoodoo,
nClientFireTesla,
nClientAltFireTesla,
nClientFireNapalm,
nClientFireNapalm2,
nClientFireLifeLeech,
nClientFireBeast,
nClientAltFireLifeLeech,
nClientDropVoodoo,
nClientAltFireNapalm,
};
bool checkFired6or7(PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
switch (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
case kWeapSpraycan:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 5:
case 6:
return 1;
case 7:
if (VanillaMode())
return 0;
return 1;
2019-09-19 22:42:45 +00:00
}
break;
case kWeapDynamite:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 4:
case 5:
case 6:
return 1;
}
break;
}
return 0;
}
static bool BannedUnderwater(int nWeapon)
2019-09-19 22:42:45 +00:00
{
return nWeapon == kWeapSpraycan || nWeapon == kWeapDynamite;
2019-09-19 22:42:45 +00:00
}
static bool CheckWeaponAmmo(PLAYER *pPlayer, int weapon, int ammotype, int count)
2019-09-19 22:42:45 +00:00
{
if (gInfiniteAmmo)
return 1;
2020-11-21 18:30:00 +00:00
if (ammotype == -1)
2019-09-19 22:42:45 +00:00
return 1;
if (weapon == kWeapRemote && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11)
2019-09-19 22:42:45 +00:00
return 1;
if (weapon == kWeapLifeLeech && pPlayer->pXSprite->health > 0)
2019-09-19 22:42:45 +00:00
return 1;
2020-11-21 18:30:00 +00:00
return pPlayer->ammoCount[ammotype] >= count;
2019-09-19 22:42:45 +00:00
}
static bool CheckAmmo(PLAYER *pPlayer, int ammotype, int count)
2019-09-19 22:42:45 +00:00
{
if (gInfiniteAmmo)
return 1;
2020-11-21 18:30:00 +00:00
if (ammotype == -1)
2019-09-19 22:42:45 +00:00
return 1;
if (pPlayer->curWeapon == kWeapRemote && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11)
2019-09-19 22:42:45 +00:00
return 1;
if (pPlayer->curWeapon == kWeapLifeLeech && pPlayer->pXSprite->health >= unsigned(count<<4))
2019-09-19 22:42:45 +00:00
return 1;
2020-11-21 18:30:00 +00:00
return pPlayer->ammoCount[ammotype] >= count;
2019-09-19 22:42:45 +00:00
}
static bool checkAmmo2(PLAYER *pPlayer, int ammotype, int amount)
2019-09-19 22:42:45 +00:00
{
if (gInfiniteAmmo)
return 1;
2020-11-21 18:30:00 +00:00
if (ammotype == -1)
2019-09-19 22:42:45 +00:00
return 1;
2020-11-21 18:30:00 +00:00
return pPlayer->ammoCount[ammotype] >= amount;
2019-09-19 22:42:45 +00:00
}
void SpawnBulletEject(PLAYER *pPlayer, int a2, int a3)
{
POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
pPlayer->zView = pPlayer->pSprite->z-pPosture->eyeAboveZ;
int dz = pPlayer->zWeapon-(pPlayer->zWeapon-pPlayer->zView)/2;
fxSpawnEjectingBrass(pPlayer->pSprite, dz, a2, a3);
2019-09-19 22:42:45 +00:00
}
void SpawnShellEject(PLAYER *pPlayer, int a2, int a3)
{
POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
pPlayer->zView = pPlayer->pSprite->z-pPosture->eyeAboveZ;
int t = pPlayer->zWeapon - pPlayer->zView;
int dz = pPlayer->zWeapon-t+(t>>2);
fxSpawnEjectingShell(pPlayer->pSprite, dz, a2, a3);
2019-09-19 22:42:45 +00:00
}
void WeaponInit(void)
{
auto doInit = [](const int base)
2019-09-19 22:42:45 +00:00
{
for (int i = base; i < (kQAVEnd + base); i++)
{
auto pQAV = getQAV(i);
if (!pQAV)
I_Error("Could not load QAV %d\n", i);
pQAV->nSprite = -1;
}
};
doInit(0);
doInit(10000);
2019-09-19 22:42:45 +00:00
}
void WeaponPrecache()
{
auto doPrecache = [](const int base)
{
for (int i = base; i < (kQAVEnd + base); i++)
{
auto pQAV = getQAV(i);
if (pQAV)
pQAV->Precache();
}
};
doPrecache(0);
doPrecache(10000);
}
void WeaponDraw(PLAYER *pPlayer, int shade, double xpos, double ypos, int palnum)
2019-09-19 22:42:45 +00:00
{
assert(pPlayer != NULL);
if (pPlayer->weaponQav == kQAVNone)
2019-09-19 22:42:45 +00:00
return;
auto pQAV = getQAV(pPlayer->weaponQav);
2020-11-21 18:30:00 +00:00
int duration;
double smoothratio;
qavProcessTimer(pPlayer, pQAV, &duration, &smoothratio, pPlayer->weaponState == -1, pPlayer->curWeapon == kWeapShotgun && pPlayer->weaponState == 7);
2020-11-21 18:30:00 +00:00
pQAV->x = int(xpos);
pQAV->y = int(ypos);
2019-09-19 22:42:45 +00:00
int flags = 2;
int nInv = powerupCheck(pPlayer, kPwUpShadowCloak);
if (nInv >= 120 * 8 || (nInv != 0 && (PlayClock & 32)))
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
shade = -128;
2019-09-19 22:42:45 +00:00
flags |= 1;
}
pQAV->Draw(xpos, ypos, duration, flags, shade, palnum, true, smoothratio);
2019-09-19 22:42:45 +00:00
}
void WeaponPlay(PLAYER *pPlayer)
{
assert(pPlayer != NULL);
if (pPlayer->weaponQav == kQAVNone)
2019-09-19 22:42:45 +00:00
return;
auto pQAV = getQAV(pPlayer->weaponQav);
2019-09-19 22:42:45 +00:00
pQAV->nSprite = pPlayer->pSprite->index;
int nTicks = pQAV->duration - pPlayer->weaponTimer;
pQAV->Play(nTicks-4, nTicks, pPlayer->qavCallback, pPlayer);
2019-09-19 22:42:45 +00:00
}
static void StartQAV(PLAYER *pPlayer, int nWeaponQAV, int callback = -1, bool looped = false)
2019-09-19 22:42:45 +00:00
{
assert(nWeaponQAV < kQAVEnd);
auto res_id = qavGetCorrectID(nWeaponQAV);
auto pQAV = getQAV(res_id);
pPlayer->weaponQav = res_id;
pPlayer->weaponTimer = pQAV->duration;
2020-11-21 18:30:00 +00:00
pPlayer->qavCallback = callback;
pPlayer->qavLoop = looped;
pPlayer->qavLastTick = I_GetTime(pQAV->ticrate);
pPlayer->qavTimer = pQAV->duration;
//pQAV->Preload();
2019-09-19 22:42:45 +00:00
WeaponPlay(pPlayer);
pPlayer->weaponTimer -= 4;
2019-09-19 22:42:45 +00:00
}
static void SetQAV(PLAYER *pPlayer, int nWeaponQAV)
{
assert(nWeaponQAV < kQAVEnd);
pPlayer->weaponQav = qavGetCorrectID(nWeaponQAV);
pPlayer->qavTimer = 0;
pPlayer->qavLastTick = 0;
}
2019-09-19 22:42:45 +00:00
struct WEAPONTRACK
{
int aimSpeedHorz;
int aimSpeedVert;
int angleRange;
int thingAngle;
int seeker;
2019-09-19 22:42:45 +00:00
bool bIsProjectile;
};
WEAPONTRACK gWeaponTrack[] = {
{ 0, 0, 0, 0, 0, false },
{ 0x6000, 0x6000, 0x71, 0x55, 0x111111, false },
{ 0x8000, 0x8000, 0x71, 0x55, 0x2aaaaa, true },
{ 0x10000, 0x10000, 0x38, 0x1c, 0, false },
{ 0x6000, 0x8000, 0x38, 0x1c, 0, false },
{ 0x6000, 0x6000, 0x38, 0x1c, 0x2aaaaa, true },
{ 0x6000, 0x6000, 0x71, 0x55, 0, true },
{ 0x6000, 0x6000, 0x71, 0x38, 0, true },
{ 0x8000, 0x10000, 0x71, 0x55, 0x255555, true },
{ 0x10000, 0x10000, 0x71, 0, 0, true },
{ 0x10000, 0x10000, 0xaa, 0, 0, false },
{ 0x6000, 0x6000, 0x71, 0x55, 0, true },
{ 0x6000, 0x6000, 0x71, 0x55, 0, true },
{ 0x6000, 0x6000, 0x71, 0x55, 0, false },
};
void UpdateAimVector(PLAYER * pPlayer)
{
spritetype *pSprite;
assert(pPlayer != NULL);
2019-09-19 22:42:45 +00:00
spritetype *pPSprite = pPlayer->pSprite;
int x = pPSprite->x;
int y = pPSprite->y;
int z = pPlayer->zWeapon;
2019-09-19 22:42:45 +00:00
Aim aim;
aim.dx = CosScale16(pPSprite->ang);
aim.dy = SinScale16(pPSprite->ang);
aim.dz = pPlayer->slope;
WEAPONTRACK *pWeaponTrack = &gWeaponTrack[pPlayer->curWeapon];
2019-09-19 22:42:45 +00:00
int nTarget = -1;
pPlayer->aimTargetsCount = 0;
2020-11-29 08:00:00 +00:00
int autoaim = Autoaim(pPlayer->nPlayer);
if (autoaim == 1 || (autoaim == 2 && !pWeaponTrack->bIsProjectile) || pPlayer->curWeapon == kWeapVoodooDoll || pPlayer->curWeapon == kWeapLifeLeech)
2019-09-19 22:42:45 +00:00
{
int nClosest = 0x7fffffff;
2020-10-15 15:15:45 +00:00
int nSprite;
StatIterator it(kStatDude);
while ((nSprite = it.NextIndex()) >= 0)
2019-09-19 22:42:45 +00:00
{
pSprite = &sprite[nSprite];
if (pSprite == pPSprite)
continue;
2019-09-22 16:17:06 +00:00
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pSprite))
continue;
if (pSprite->flags&32)
2019-09-19 22:42:45 +00:00
continue;
if (!(pSprite->flags&8))
2019-09-19 22:42:45 +00:00
continue;
int x2 = pSprite->x;
int y2 = pSprite->y;
int z2 = pSprite->z;
int nDist = approxDist(x2-x, y2-y);
if (nDist == 0 || nDist > 51200)
continue;
if (pWeaponTrack->seeker)
2019-09-19 22:42:45 +00:00
{
int t = DivScale(nDist,pWeaponTrack->seeker, 12);
2019-09-19 22:42:45 +00:00
x2 += (xvel[nSprite]*t)>>12;
y2 += (yvel[nSprite]*t)>>12;
z2 += (zvel[nSprite]*t)>>8;
}
int lx = x + MulScale(Cos(pPSprite->ang), nDist, 30);
int ly = y + MulScale(Sin(pPSprite->ang), nDist, 30);
int lz = z + MulScale(pPlayer->slope, nDist, 10);
int zRange = MulScale(9460, nDist, 10);
2019-09-19 22:42:45 +00:00
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
if (lz-zRange>bottom || lz+zRange<top)
continue;
int angle = getangle(x2-x,y2-y);
if (abs(((angle-pPSprite->ang+1024)&2047)-1024) > pWeaponTrack->angleRange)
2019-09-19 22:42:45 +00:00
continue;
if (pPlayer->aimTargetsCount < 16 && cansee(x,y,z,pPSprite->sectnum,x2,y2,z2,pSprite->sectnum))
pPlayer->aimTargets[pPlayer->aimTargetsCount++] = nSprite;
2019-09-19 22:42:45 +00:00
// Inlined?
int dz = (lz-z2)>>8;
int dy = (ly-y2)>>4;
int dx = (lx-x2)>>4;
int nDist2 = ksqrt(dx*dx+dy*dy+dz*dz);
if (nDist2 >= nClosest)
continue;
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
2019-09-19 22:42:45 +00:00
int center = (pSprite->yrepeat*pDudeInfo->aimHeight)<<2;
int dzCenter = (z2-center)-z;
if (cansee(x, y, z, pPSprite->sectnum, x2, y2, z2, pSprite->sectnum))
{
nClosest = nDist2;
aim.dx = CosScale16(angle);
aim.dy = SinScale16(angle);
aim.dz = DivScale(dzCenter, nDist, 10);
2019-09-19 22:42:45 +00:00
nTarget = nSprite;
}
}
if (pWeaponTrack->thingAngle > 0)
2019-09-19 22:42:45 +00:00
{
2020-10-15 15:15:45 +00:00
int nSprite;
StatIterator it(kStatThing);
while ((nSprite = it.NextIndex()) >= 0)
2019-09-19 22:42:45 +00:00
{
pSprite = &sprite[nSprite];
2019-09-22 16:17:06 +00:00
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pSprite))
continue;
if (!(pSprite->flags&8))
2019-09-19 22:42:45 +00:00
continue;
int x2 = pSprite->x;
int y2 = pSprite->y;
int z2 = pSprite->z;
int dx = x2-x;
int dy = y2-y;
int dz = z2-z;
int nDist = approxDist(dx, dy);
if (nDist == 0 || nDist > 51200)
continue;
int lx = x + MulScale(Cos(pPSprite->ang), nDist, 30);
int ly = y + MulScale(Sin(pPSprite->ang), nDist, 30);
int lz = z + MulScale(pPlayer->slope, nDist, 10);
int zRange = MulScale(9460, nDist, 10);
2019-09-19 22:42:45 +00:00
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
if (lz-zRange>bottom || lz+zRange<top)
continue;
int angle = getangle(dx,dy);
if (abs(((angle-pPSprite->ang+1024)&2047)-1024) > pWeaponTrack->thingAngle)
2019-09-19 22:42:45 +00:00
continue;
if (pPlayer->aimTargetsCount < 16 && cansee(x,y,z,pPSprite->sectnum,pSprite->x,pSprite->y,pSprite->z,pSprite->sectnum))
pPlayer->aimTargets[pPlayer->aimTargetsCount++] = nSprite;
2019-09-19 22:42:45 +00:00
// Inlined?
int dz2 = (lz-z2)>>8;
int dy2 = (ly-y2)>>4;
int dx2 = (lx-x2)>>4;
int nDist2 = ksqrt(dx2*dx2+dy2*dy2+dz2*dz2);
if (nDist2 >= nClosest)
continue;
if (cansee(x, y, z, pPSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum))
{
nClosest = nDist2;
aim.dx = CosScale16(angle);
aim.dy = SinScale16(angle);
aim.dz = DivScale(dz, nDist, 10);
2019-09-19 22:42:45 +00:00
nTarget = nSprite;
}
}
}
}
Aim aim2;
aim2 = aim;
RotateVector((int*)&aim2.dx, (int*)&aim2.dy, -pPSprite->ang);
aim2.dz -= pPlayer->slope;
pPlayer->relAim.dx = interpolatedvalue(pPlayer->relAim.dx, aim2.dx, pWeaponTrack->aimSpeedHorz);
pPlayer->relAim.dy = interpolatedvalue(pPlayer->relAim.dy, aim2.dy, pWeaponTrack->aimSpeedHorz);
pPlayer->relAim.dz = interpolatedvalue(pPlayer->relAim.dz, aim2.dz, pWeaponTrack->aimSpeedVert);
pPlayer->aim = pPlayer->relAim;
RotateVector((int*)&pPlayer->aim.dx, (int*)&pPlayer->aim.dy, pPSprite->ang);
pPlayer->aim.dz += pPlayer->slope;
pPlayer->aimTarget = nTarget;
2019-09-19 22:42:45 +00:00
}
struct t_WeaponModes
{
2020-11-21 18:30:00 +00:00
int update;
int ammoType;
2019-09-19 22:42:45 +00:00
};
t_WeaponModes weaponModes[] = {
{ 0, -1 },
{ 1, -1 },
{ 1, 1 },
{ 1, 2 },
{ 1, 3 },
{ 1, 4 },
{ 1, 5 },
{ 1, 6 },
{ 1, 7 },
{ 1, 8 },
{ 1, 9 },
{ 1, 10 },
{ 1, 11 },
{ 0, -1 },
};
void WeaponRaise(PLAYER *pPlayer)
{
assert(pPlayer != NULL);
int prevWeapon = pPlayer->curWeapon;
pPlayer->curWeapon = pPlayer->newWeapon;
pPlayer->newWeapon = kWeapNone;
2020-11-21 18:30:00 +00:00
pPlayer->weaponAmmo = weaponModes[pPlayer->curWeapon].ammoType;
switch (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
case kWeapPitchFork:
pPlayer->weaponState = 0;
StartQAV(pPlayer, kQAVFORKUP);
2019-09-19 22:42:45 +00:00
break;
case kWeapSpraycan:
if (pPlayer->weaponState == 2)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 3;
StartQAV(pPlayer, kQAVCANPREF);
2019-09-19 22:42:45 +00:00
}
else
{
pPlayer->weaponState = 0;
StartQAV(pPlayer, kQAVLITEOPEN);
2019-09-19 22:42:45 +00:00
}
break;
case kWeapDynamite:
if (gInfiniteAmmo || checkAmmo2(pPlayer, 5, 1))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 3;
if (prevWeapon == kWeapSpraycan)
StartQAV(pPlayer, kQAVBUNUP);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVBUNUP2);
2019-09-19 22:42:45 +00:00
}
break;
case kWeapProximity:
if (gInfiniteAmmo || checkAmmo2(pPlayer, 10, 1))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 7;
StartQAV(pPlayer, kQAVPROXUP);
2019-09-19 22:42:45 +00:00
}
break;
case kWeapRemote:
if (gInfiniteAmmo || checkAmmo2(pPlayer, 11, 1))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 10;
StartQAV(pPlayer, kQAVREMUP2);
2019-09-19 22:42:45 +00:00
}
else
{
StartQAV(pPlayer, kQAVREMUP3);
pPlayer->weaponState = 11;
2019-09-19 22:42:45 +00:00
}
break;
case kWeapShotgun:
if (powerupCheck(pPlayer, kPwUpTwoGuns))
2019-09-19 22:42:45 +00:00
{
if (gInfiniteAmmo || pPlayer->ammoCount[2] >= 4)
StartQAV(pPlayer, kQAV2SHOTUP);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSHOTUP);
if (gInfiniteAmmo || pPlayer->ammoCount[2] >= 4)
pPlayer->weaponState = 7;
else if (pPlayer->ammoCount[2] > 1)
pPlayer->weaponState = 3;
else if (pPlayer->ammoCount[2] > 0)
pPlayer->weaponState = 2;
2019-09-19 22:42:45 +00:00
else
pPlayer->weaponState = 1;
2019-09-19 22:42:45 +00:00
}
else
{
if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1)
pPlayer->weaponState = 3;
else if (pPlayer->ammoCount[2] > 0)
pPlayer->weaponState = 2;
2019-09-19 22:42:45 +00:00
else
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVSHOTUP);
2019-09-19 22:42:45 +00:00
}
break;
case kWeapTommyGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAV2TOMUP);
2019-09-19 22:42:45 +00:00
}
else
{
pPlayer->weaponState = 0;
StartQAV(pPlayer, kQAVTOMUP);
2019-09-19 22:42:45 +00:00
}
break;
case kWeapVoodooDoll:
if (gInfiniteAmmo || checkAmmo2(pPlayer, 9, 1))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVVDUP);
2019-09-19 22:42:45 +00:00
}
break;
case kWeapFlareGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
2019-09-19 22:42:45 +00:00
{
StartQAV(pPlayer, kQAVFLAR2UP);
pPlayer->weaponState = 3;
2019-09-19 22:42:45 +00:00
}
else
{
StartQAV(pPlayer, kQAVFLARUP);
pPlayer->weaponState = 2;
2019-09-19 22:42:45 +00:00
}
break;
case kWeapTeslaCannon:
if (checkAmmo2(pPlayer, 7, 1))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 2;
if (powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNUP);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNUP);
2019-09-19 22:42:45 +00:00
}
else
{
pPlayer->weaponState = 3;
StartQAV(pPlayer, kQAVSGUNUP);
2019-09-19 22:42:45 +00:00
}
break;
case kWeapNapalm:
if (powerupCheck(pPlayer, kPwUpTwoGuns))
2019-09-19 22:42:45 +00:00
{
StartQAV(pPlayer, kQAV2NAPUP);
pPlayer->weaponState = 3;
2019-09-19 22:42:45 +00:00
}
else
{
StartQAV(pPlayer, kQAVNAPUP);
pPlayer->weaponState = 2;
2019-09-19 22:42:45 +00:00
}
break;
case kWeapLifeLeech:
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVSTAFUP);
2019-09-19 22:42:45 +00:00
break;
case kWeapBeast:
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVBSTUP);
2019-09-19 22:42:45 +00:00
break;
}
}
void WeaponLower(PLAYER *pPlayer)
{
assert(pPlayer != NULL);
if (checkFired6or7(pPlayer))
2019-09-19 22:42:45 +00:00
return;
pPlayer->throwPower = 0;
int prevState = pPlayer->weaponState;
switch (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
case kWeapPitchFork:
StartQAV(pPlayer, kQAVFORKDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapSpraycan:
2019-09-19 22:42:45 +00:00
sfxKill3DSound(pPlayer->pSprite, -1, 441);
switch (prevState)
{
case 1:
if (VanillaMode())
{
StartQAV(pPlayer, kQAVLITECLO2);
}
else
{
if (pPlayer->newWeapon == kWeapDynamite) // do not put away lighter if TNT was selected while throwing a spray can
{
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVCANDOWN);
WeaponRaise(pPlayer);
return;
}
}
2019-09-19 22:42:45 +00:00
break;
case 2:
pPlayer->weaponState = 1;
2019-09-19 22:42:45 +00:00
WeaponRaise(pPlayer);
return;
case 4:
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVCANDOWN);
if (VanillaMode())
{
pPlayer->newWeapon = kWeapNone;
WeaponLower(pPlayer);
}
else
{
if (pPlayer->newWeapon == kWeapDynamite)
{
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVCANDOWN);
return;
}
else
{
WeaponLower(pPlayer);
}
}
2019-09-19 22:42:45 +00:00
break;
case 3:
if (pPlayer->newWeapon == kWeapDynamite)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVCANDOWN);
2019-09-19 22:42:45 +00:00
return;
}
else if (pPlayer->newWeapon == kWeapSpraycan)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVCANDOWN);
pPlayer->newWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
WeaponLower(pPlayer);
}
else
{
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVCANDOWN);
2019-09-19 22:42:45 +00:00
}
break;
case 7: // throwing ignited alt fire spray
if (VanillaMode() || (pPlayer->newWeapon != 0))
break;
pPlayer->weaponState = 1;
StartQAV(pPlayer, 11, -1, 0);
break;
2019-09-19 22:42:45 +00:00
}
break;
case kWeapDynamite:
2019-09-19 22:42:45 +00:00
switch (prevState)
{
case 1:
if (!VanillaMode() && (pPlayer->newWeapon == kWeapSpraycan)) // do not put away lighter after TNT is thrown if while throwing the weapon was switched already to spray
{
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVBUNDOWN);
WeaponRaise(pPlayer);
return;
}
StartQAV(pPlayer, kQAVLITECLO2);
2019-09-19 22:42:45 +00:00
break;
case 2:
WeaponRaise(pPlayer);
break;
case 3:
if (pPlayer->newWeapon == kWeapSpraycan)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVBUNDOWN);
2019-09-19 22:42:45 +00:00
}
else
{
StartQAV(pPlayer, kQAVBUNDOWN2);
2019-09-19 22:42:45 +00:00
}
break;
default:
break;
}
break;
case kWeapProximity:
2019-09-19 22:42:45 +00:00
switch (prevState)
{
case 7:
StartQAV(pPlayer, kQAVPROXDOWN);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapRemote:
2019-09-19 22:42:45 +00:00
switch (prevState)
{
case 10:
StartQAV(pPlayer, kQAVREMDOWN2);
2019-09-19 22:42:45 +00:00
break;
case 11:
StartQAV(pPlayer, kQAVREMDOWN3);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapShotgun:
if (powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SHOTDWN);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSHOTDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapTommyGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && pPlayer->weaponState == 1)
StartQAV(pPlayer, kQAV2TOMDOWN);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVTOMDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapFlareGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && pPlayer->weaponState == 3)
StartQAV(pPlayer, kQAVFLAR2DWN);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVFLARDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapVoodooDoll:
StartQAV(pPlayer, kQAVVDDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapTeslaCannon:
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNDWN);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapNapalm:
if (powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2NAPDOWN);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVNAPDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapLifeLeech:
StartQAV(pPlayer, kQAVSTAFDOWN);
2019-09-19 22:42:45 +00:00
break;
case kWeapBeast:
StartQAV(pPlayer, kQAVBSTDOWN);
2019-09-19 22:42:45 +00:00
break;
}
pPlayer->curWeapon = kWeapNone;
pPlayer->qavLoop = 0;
2019-09-19 22:42:45 +00:00
}
void WeaponUpdateState(PLAYER *pPlayer)
{
static int lastWeapon = 0;
static int lastState = 0;
XSPRITE *pXSprite = pPlayer->pXSprite;
int va = pPlayer->curWeapon;
int vb = pPlayer->weaponState;
2019-09-19 22:42:45 +00:00
if (va != lastWeapon || vb != lastState)
{
lastWeapon = va;
lastState = vb;
}
switch (lastWeapon)
{
case kWeapPitchFork:
SetQAV(pPlayer, kQAVFORKIDLE);
2019-09-19 22:42:45 +00:00
break;
case kWeapSpraycan:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 0:
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVLITEFLAM);
2019-09-19 22:42:45 +00:00
break;
case 1:
if (CheckAmmo(pPlayer, 6, 1))
{
pPlayer->weaponState = 3;
StartQAV(pPlayer, kQAVCANPREF);
2019-09-19 22:42:45 +00:00
}
else
SetQAV(pPlayer, kQAVLITEIDLE);
2019-09-19 22:42:45 +00:00
break;
case 3:
SetQAV(pPlayer, kQAVCANIDLE);
2019-09-19 22:42:45 +00:00
break;
case 4:
if (CheckAmmo(pPlayer, 6, 1))
{
SetQAV(pPlayer, kQAVCANIDLE);
pPlayer->weaponState = 3;
2019-09-19 22:42:45 +00:00
}
else
{
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVCANDOWN);
2019-09-19 22:42:45 +00:00
}
sfxKill3DSound(pPlayer->pSprite, -1, 441);
break;
}
break;
case kWeapDynamite:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 1:
if (pPlayer->weaponAmmo == 5 && CheckAmmo(pPlayer, 5, 1))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 3;
StartQAV(pPlayer, kQAVBUNUP);
2019-09-19 22:42:45 +00:00
}
break;
case 0:
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVLITEFLAM);
2019-09-19 22:42:45 +00:00
break;
case 2:
if (pPlayer->ammoCount[5] > 0)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 3;
StartQAV(pPlayer, kQAVBUNUP);
2019-09-19 22:42:45 +00:00
}
else
SetQAV(pPlayer, kQAVLITEIDLE);
2019-09-19 22:42:45 +00:00
break;
case 3:
SetQAV(pPlayer, kQAVBUNIDLE);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapProximity:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 7:
SetQAV(pPlayer, kQAVPROXIDLE);
2019-09-19 22:42:45 +00:00
break;
case 8:
pPlayer->weaponState = 7;
StartQAV(pPlayer, kQAVPROXUP);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapRemote:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 10:
SetQAV(pPlayer, kQAVREMIDLE1);
2019-09-19 22:42:45 +00:00
break;
case 11:
SetQAV(pPlayer, kQAVREMIDLE2);
2019-09-19 22:42:45 +00:00
break;
case 12:
if (pPlayer->ammoCount[11] > 0)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 10;
StartQAV(pPlayer, kQAVREMUP2);
2019-09-19 22:42:45 +00:00
}
else
pPlayer->weaponState = -1;
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapShotgun:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 6:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer, 2, 4)))
pPlayer->weaponState = 7;
2019-09-19 22:42:45 +00:00
else
pPlayer->weaponState = 1;
2019-09-19 22:42:45 +00:00
break;
case 7:
SetQAV(pPlayer, kQAV2SHOTI);
2019-09-19 22:42:45 +00:00
break;
case 1:
if (CheckAmmo(pPlayer, 2, 1))
{
sfxPlay3DSound(pPlayer->pSprite, 410, 3, 2);
StartQAV(pPlayer, kQAVSHOTL1, nClientEjectShell);
if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1)
pPlayer->weaponState = 3;
2019-09-19 22:42:45 +00:00
else
pPlayer->weaponState = 2;
2019-09-19 22:42:45 +00:00
}
else
SetQAV(pPlayer, kQAVSHOTI3);
2019-09-19 22:42:45 +00:00
break;
case 2:
SetQAV(pPlayer, kQAVSHOTI2);
2019-09-19 22:42:45 +00:00
break;
case 3:
SetQAV(pPlayer, kQAVSHOTI1);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapTommyGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
2019-09-19 22:42:45 +00:00
{
SetQAV(pPlayer, kQAV2TOMIDLE);
pPlayer->weaponState = 1;
2019-09-19 22:42:45 +00:00
}
else
{
SetQAV(pPlayer, kQAVTOMIDLE);
pPlayer->weaponState = 0;
2019-09-19 22:42:45 +00:00
}
break;
case kWeapFlareGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns))
2019-09-19 22:42:45 +00:00
{
if (vb == 3 && checkAmmo2(pPlayer, 1, 2))
SetQAV(pPlayer, kQAVFLAR2I);
2019-09-19 22:42:45 +00:00
else
{
SetQAV(pPlayer, kQAVFLARIDLE);
pPlayer->weaponState = 2;
2019-09-19 22:42:45 +00:00
}
}
else
SetQAV(pPlayer, kQAVFLARIDLE);
2019-09-19 22:42:45 +00:00
break;
case kWeapVoodooDoll:
if (pXSprite->height < 256 && pPlayer->swayHeight != 0)
StartQAV(pPlayer, kQAVVDIDLE2);
2019-09-19 22:42:45 +00:00
else
SetQAV(pPlayer, kQAVVDIDLE1);
2019-09-19 22:42:45 +00:00
break;
case kWeapTeslaCannon:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 2:
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
SetQAV(pPlayer, kQAV2SGUNIDL);
2019-09-19 22:42:45 +00:00
else
SetQAV(pPlayer, kQAVSGUNIDL1);
2019-09-19 22:42:45 +00:00
break;
case 3:
SetQAV(pPlayer, kQAVSGUNIDL2);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapNapalm:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 3:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer,4, 4)))
SetQAV(pPlayer, kQAV2NAPIDLE);
2019-09-19 22:42:45 +00:00
else
SetQAV(pPlayer, kQAVNAPIDLE);
2019-09-19 22:42:45 +00:00
break;
case 2:
SetQAV(pPlayer, kQAVNAPIDLE);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapLifeLeech:
2019-09-19 22:42:45 +00:00
switch (vb)
{
case 2:
SetQAV(pPlayer, kQAVSTAFIDL1);
2019-09-19 22:42:45 +00:00
break;
}
break;
case kWeapBeast:
SetQAV(pPlayer, kQAVBSTIDLE);
2019-09-19 22:42:45 +00:00
break;
}
}
void FirePitchfork(int, PLAYER *pPlayer)
{
2021-08-30 18:28:24 +00:00
auto actor = &bloodActors[pPlayer->pSprite->index];
Aim *aim = &pPlayer->aim;
2019-09-19 22:42:45 +00:00
int r1 = Random2(2000);
int r2 = Random2(2000);
int r3 = Random2(2000);
for (int i = 0; i < 4; i++)
2021-08-30 18:28:24 +00:00
actFireVector(actor, (2*i-3)*40, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r1, aim->dy+r2, aim->dz+r3, kVectorTine);
2019-09-19 22:42:45 +00:00
}
void FireSpray(int, PLAYER *pPlayer)
{
playerFireMissile(pPlayer, 0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFlameSpray);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 6, 4);
if (CheckAmmo(pPlayer, 6, 1))
sfxPlay3DSound(pPlayer->pSprite, 441, 1, 2);
else
sfxKill3DSound(pPlayer->pSprite, -1, 441);
}
void ThrowCan(int, PLAYER *pPlayer)
{
sfxKill3DSound(pPlayer->pSprite, -1, 441);
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedSpray, nSpeed);
2019-09-19 22:42:45 +00:00
if (pSprite)
{
sfxPlay3DSound(pSprite, 441, 0, 0);
evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn);
2019-09-19 22:42:45 +00:00
int nXSprite = pSprite->extra;
XSPRITE *pXSprite = &xsprite[nXSprite];
pXSprite->Impact = 1;
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
pPlayer->throwPower = 0;
2019-09-19 22:42:45 +00:00
}
}
void DropCan(int, PLAYER *pPlayer)
{
sfxKill3DSound(pPlayer->pSprite, -1, 441);
spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
2019-09-19 22:42:45 +00:00
if (pSprite)
{
evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn);
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
2019-09-19 22:42:45 +00:00
}
}
void ExplodeCan(int, PLAYER *pPlayer)
{
sfxKill3DSound(pPlayer->pSprite, -1, 441);
spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
evPost(pSprite->index, 3, 0, kCmdOn);
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
StartQAV(pPlayer, kQAVCANBOOM);
pPlayer->curWeapon = kWeapNone;
pPlayer->throwPower = 0;
2019-09-19 22:42:45 +00:00
}
void ThrowBundle(int, PLAYER *pPlayer)
{
sfxKill3DSound(pPlayer->pSprite, 16, -1);
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedTNTBundle, nSpeed);
2019-09-19 22:42:45 +00:00
int nXSprite = pSprite->extra;
XSPRITE *pXSprite = &xsprite[nXSprite];
if (pPlayer->fuseTime < 0)
2019-09-19 22:42:45 +00:00
pXSprite->Impact = 1;
else
evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 5, 1);
pPlayer->throwPower = 0;
2019-09-19 22:42:45 +00:00
}
void DropBundle(int, PLAYER *pPlayer)
{
sfxKill3DSound(pPlayer->pSprite, 16, -1);
spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 5, 1);
}
void ExplodeBundle(int, PLAYER *pPlayer)
{
sfxKill3DSound(pPlayer->pSprite, 16, -1);
spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
evPost(pSprite->index, 3, 0, kCmdOn);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 5, 1);
StartQAV(pPlayer, kQAVDYNEXPLO);
pPlayer->curWeapon = kWeapNone;
pPlayer->throwPower = 0;
2019-09-19 22:42:45 +00:00
}
void ThrowProx(int, PLAYER *pPlayer)
{
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedProxBomb, nSpeed);
evPost(pSprite->index, 3, 240, kCmdOn);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 10, 1);
pPlayer->throwPower = 0;
2019-09-19 22:42:45 +00:00
}
void DropProx(int, PLAYER *pPlayer)
{
spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedProxBomb, 0);
evPost(pSprite->index, 3, 240, kCmdOn);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 10, 1);
}
void ThrowRemote(int, PLAYER *pPlayer)
{
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedRemoteBomb, nSpeed);
2019-09-19 22:42:45 +00:00
int nXSprite = pSprite->extra;
XSPRITE *pXSprite = &xsprite[nXSprite];
pXSprite->rxID = 90+(pPlayer->pSprite->type-kDudePlayer1);
UseAmmo(pPlayer, 11, 1);
pPlayer->throwPower = 0;
2019-09-19 22:42:45 +00:00
}
void DropRemote(int, PLAYER *pPlayer)
{
spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedRemoteBomb, 0);
2019-09-19 22:42:45 +00:00
int nXSprite = pSprite->extra;
XSPRITE *pXSprite = &xsprite[nXSprite];
pXSprite->rxID = 90+(pPlayer->pSprite->type-kDudePlayer1);
UseAmmo(pPlayer, 11, 1);
}
void FireRemote(int, PLAYER *pPlayer)
{
evSend(0, 0, 90+(pPlayer->pSprite->type-kDudePlayer1), kCmdOn);
2019-09-19 22:42:45 +00:00
}
enum { kMaxShotgunBarrels = 4 };
2019-09-19 22:42:45 +00:00
void FireShotgun(int nTrigger, PLAYER *pPlayer)
{
2021-08-30 18:28:24 +00:00
auto actor = &bloodActors[pPlayer->pSprite->index];
assert(nTrigger > 0 && nTrigger <= kMaxShotgunBarrels);
2019-09-19 22:42:45 +00:00
if (nTrigger == 1)
{
sfxPlay3DSound(pPlayer->pSprite, 411, 2, 0);
pPlayer->tiltEffect = 30;
pPlayer->visibility = 20;
2019-09-19 22:42:45 +00:00
}
else
{
sfxPlay3DSound(pPlayer->pSprite, 412, 2, 0);
pPlayer->tiltEffect = 50;
pPlayer->visibility = 40;
2019-09-19 22:42:45 +00:00
}
int n = nTrigger<<4;
for (int i = 0; i < n; i++)
{
int r1, r2, r3;
VECTOR_TYPE nType;
if (nTrigger == 1)
{
r1 = Random3(1500);
r2 = Random3(1500);
r3 = Random3(500);
nType = kVectorShell;
2019-09-19 22:42:45 +00:00
}
else
{
r1 = Random3(2500);
r2 = Random3(2500);
r3 = Random3(1500);
nType = kVectorShellAP;
2019-09-19 22:42:45 +00:00
}
2021-08-30 18:28:24 +00:00
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, nType);
2019-09-19 22:42:45 +00:00
}
UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger);
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void EjectShell(int, PLAYER *pPlayer)
{
SpawnShellEject(pPlayer, 25, 35);
SpawnShellEject(pPlayer, 48, 35);
}
void FireTommy(int nTrigger, PLAYER *pPlayer)
{
2021-08-30 18:28:24 +00:00
auto actor = &bloodActors[pPlayer->pSprite->index];
Aim *aim = &pPlayer->aim;
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
switch (nTrigger)
{
case 1:
{
int r1 = Random3(400);
int r2 = Random3(1200);
int r3 = Random3(1200);
2021-08-30 18:28:24 +00:00
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyRegular);
2019-09-19 22:42:45 +00:00
SpawnBulletEject(pPlayer, -15, -45);
pPlayer->visibility = 20;
2019-09-19 22:42:45 +00:00
break;
}
case 2:
{
int r1 = Random3(400);
int r2 = Random3(1200);
int r3 = Random3(1200);
2021-08-30 18:28:24 +00:00
actFireVector(actor, -120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyRegular);
2019-09-19 22:42:45 +00:00
SpawnBulletEject(pPlayer, -140, -45);
r1 = Random3(400);
r2 = Random3(1200);
r3 = Random3(1200);
2021-08-30 18:28:24 +00:00
actFireVector(actor, 120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyRegular);
2019-09-19 22:42:45 +00:00
SpawnBulletEject(pPlayer, 140, 45);
pPlayer->visibility = 30;
2019-09-19 22:42:45 +00:00
break;
}
}
UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger);
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
enum { kMaxSpread = 14 };
2019-09-19 22:42:45 +00:00
void FireSpread(int nTrigger, PLAYER *pPlayer)
{
2021-08-30 18:28:24 +00:00
auto actor = &bloodActors[pPlayer->pSprite->index];
assert(nTrigger > 0 && nTrigger <= kMaxSpread);
Aim *aim = &pPlayer->aim;
2019-09-19 22:42:45 +00:00
int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
int dx = CosScale16(angle);
int dy = SinScale16(angle);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
int r1, r2, r3;
r1 = Random3(300);
r2 = Random3(600);
r3 = Random3(600);
2021-08-30 18:28:24 +00:00
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
2019-09-19 22:42:45 +00:00
r1 = Random2(90);
r2 = Random2(30);
SpawnBulletEject(pPlayer, r2, r1);
pPlayer->visibility = 20;
UseAmmo(pPlayer, pPlayer->weaponAmmo, 1);
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void AltFireSpread(int nTrigger, PLAYER *pPlayer)
{
2021-08-30 18:28:24 +00:00
auto actor = &bloodActors[pPlayer->pSprite->index];
assert(nTrigger > 0 && nTrigger <= kMaxSpread);
Aim *aim = &pPlayer->aim;
2019-09-19 22:42:45 +00:00
int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
int dx = CosScale16(angle);
int dy = SinScale16(angle);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
int r1, r2, r3;
r1 = Random3(300);
r2 = Random3(600);
r3 = Random3(600);
2021-08-30 18:28:24 +00:00
actFireVector(actor, -120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
2019-09-19 22:42:45 +00:00
r1 = Random2(45);
r2 = Random2(120);
SpawnBulletEject(pPlayer, r2, r1);
r1 = Random3(300);
r2 = Random3(600);
r3 = Random3(600);
2021-08-30 18:28:24 +00:00
actFireVector(actor, 120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
2019-09-19 22:42:45 +00:00
r1 = Random2(-45);
r2 = Random2(-120);
SpawnBulletEject(pPlayer, r2, r1);
pPlayer->tiltEffect = 20;
pPlayer->visibility = 30;
UseAmmo(pPlayer, pPlayer->weaponAmmo, 2);
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void AltFireSpread2(int nTrigger, PLAYER *pPlayer)
{
2021-08-30 18:28:24 +00:00
auto actor = &bloodActors[pPlayer->pSprite->index];
assert(nTrigger > 0 && nTrigger <= kMaxSpread);
Aim *aim = &pPlayer->aim;
2019-09-19 22:42:45 +00:00
int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
int dx = CosScale16(angle);
int dy = SinScale16(angle);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
2019-09-19 22:42:45 +00:00
{
int r1, r2, r3;
r1 = Random3(300);
r2 = Random3(600);
r3 = Random3(600);
2021-08-30 18:28:24 +00:00
actFireVector(actor, -120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
2019-09-19 22:42:45 +00:00
r1 = Random2(45);
r2 = Random2(120);
SpawnBulletEject(pPlayer, r2, r1);
r1 = Random3(300);
r2 = Random3(600);
r3 = Random3(600);
2021-08-30 18:28:24 +00:00
actFireVector(actor, 120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
2019-09-19 22:42:45 +00:00
r1 = Random2(-45);
r2 = Random2(-120);
SpawnBulletEject(pPlayer, r2, r1);
pPlayer->tiltEffect = 30;
pPlayer->visibility = 45;
UseAmmo(pPlayer, pPlayer->weaponAmmo, 2);
2019-09-19 22:42:45 +00:00
}
else
{
int r1, r2, r3;
r1 = Random3(300);
r2 = Random3(600);
r3 = Random3(600);
2021-08-30 18:28:24 +00:00
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
2019-09-19 22:42:45 +00:00
r1 = Random2(90);
r2 = Random2(30);
SpawnBulletEject(pPlayer, r2, r1);
pPlayer->tiltEffect = 20;
pPlayer->visibility = 30;
UseAmmo(pPlayer, pPlayer->weaponAmmo, 1);
2019-09-19 22:42:45 +00:00
}
pPlayer->flashEffect = 1;
if (!checkAmmo2(pPlayer, 3, 1))
2019-09-19 22:42:45 +00:00
{
WeaponLower(pPlayer);
pPlayer->weaponState = -1;
2019-09-19 22:42:45 +00:00
}
}
void FireFlare(int nTrigger, PLAYER *pPlayer)
{
spritetype *pSprite = pPlayer->pSprite;
int offset = 0;
switch (nTrigger)
{
case 2:
offset = -120;
break;
case 3:
offset = 120;
break;
}
playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFlareRegular);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 1, 1);
sfxPlay3DSound(pSprite, 420, 2, 0);
pPlayer->visibility = 30;
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void AltFireFlare(int nTrigger, PLAYER *pPlayer)
{
spritetype *pSprite = pPlayer->pSprite;
int offset = 0;
switch (nTrigger)
{
case 2:
offset = -120;
break;
case 3:
offset = 120;
break;
}
playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFlareAlt);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 1, 8);
sfxPlay3DSound(pSprite, 420, 2, 0);
pPlayer->visibility = 45;
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void FireVoodoo(int nTrigger, PLAYER *pPlayer)
{
nTrigger--;
auto actor = pPlayer->actor();
2019-09-19 22:42:45 +00:00
spritetype *pSprite = pPlayer->pSprite;
if (nTrigger == 4)
{
actDamageSprite(actor, actor, kDamageBullet, 1<<4);
2019-09-19 22:42:45 +00:00
return;
}
assert(pPlayer->voodooTarget >= 0);
auto targetactor = &bloodActors[pPlayer->voodooTarget];
spritetype *pTarget = &targetactor->s();
2019-09-22 07:53:42 +00:00
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
return;
2019-09-19 22:42:45 +00:00
switch (nTrigger)
{
case 0:
{
sfxPlay3DSound(pSprite, 460, 2, 0);
fxSpawnBlood(pTarget, 17<<4);
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 17<<4);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 9, nDamage/4);
break;
}
case 1:
{
sfxPlay3DSound(pSprite, 460, 2, 0);
fxSpawnBlood(pTarget, 17<<4);
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 9<<4);
if (IsPlayerSprite(pTarget))
2019-09-19 22:42:45 +00:00
WeaponLower(&gPlayer[pTarget->type-kDudePlayer1]);
UseAmmo(pPlayer, 9, nDamage/4);
break;
}
case 3:
{
sfxPlay3DSound(pSprite, 463, 2, 0);
fxSpawnBlood(pTarget, 17<<4);
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 49<<4);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 9, nDamage/4);
break;
}
case 2:
{
sfxPlay3DSound(pSprite, 460, 2, 0);
fxSpawnBlood(pTarget, 17<<4);
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 11<<4);
if (IsPlayerSprite(pTarget))
2019-09-19 22:42:45 +00:00
{
PLAYER *pOtherPlayer = &gPlayer[pTarget->type - kDudePlayer1];
pOtherPlayer->blindEffect = 128;
2019-09-19 22:42:45 +00:00
}
UseAmmo(pPlayer, 9, nDamage/4);
break;
}
}
}
void AltFireVoodoo(int nTrigger, PLAYER *pPlayer)
{
auto actor = pPlayer->actor();
if (nTrigger == 2) {
// by NoOne: trying to simulate v1.0x voodoo here.
// dunno how exactly it works, but at least it not spend all the ammo on alt fire
if (gGameOptions.weaponsV10x && !VanillaMode()) {
int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount);
if (nCount > 0)
{
for (int i = 0; i < pPlayer->aimTargetsCount; i++)
{
int nTarget = pPlayer->aimTargets[i];
auto targetactor = &bloodActors[nTarget];
spritetype* pTarget = &targetactor->s();
2019-09-22 07:53:42 +00:00
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
continue;
int nDist = approxDist(pTarget->x - pPlayer->pSprite->x, pTarget->y - pPlayer->pSprite->y);
if (nDist > 0 && nDist < 51200)
{
int vc = pPlayer->ammoCount[9] >> 3;
int v8 = pPlayer->ammoCount[9] << 1;
int nDamage = (v8 + Random(vc)) << 4;
nDamage = (nDamage * ((51200 - nDist) + 1)) / 51200;
nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, nDamage);
if (IsPlayerSprite(pTarget))
{
PLAYER* pOtherPlayer = &gPlayer[pTarget->type - kDudePlayer1];
if (!pOtherPlayer->godMode || !powerupCheck(pOtherPlayer, kPwUpDeathMask))
powerupActivate(pOtherPlayer, kPwUpDeliriumShroom);
}
fxSpawnBlood(pTarget, 0);
}
}
}
UseAmmo(pPlayer, 9, 20);
pPlayer->weaponState = 0;
return;
}
//int nAmmo = pPlayer->ammCount[9];
int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount);
if (nCount > 0)
{
int v4 = pPlayer->ammoCount[9] - (pPlayer->ammoCount[9] / nCount) * nCount;
for (int i = 0; i < pPlayer->aimTargetsCount; i++)
2019-09-19 22:42:45 +00:00
{
int nTarget = pPlayer->aimTargets[i];
auto targetactor = &bloodActors[nTarget];
spritetype* pTarget = &targetactor->s();
2019-09-22 07:53:42 +00:00
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
continue;
if (v4 > 0)
v4--;
int nDist = approxDist(pTarget->x - pPlayer->pSprite->x, pTarget->y - pPlayer->pSprite->y);
if (nDist > 0 && nDist < 51200)
2019-09-19 22:42:45 +00:00
{
int vc = pPlayer->ammoCount[9] >> 3;
int v8 = pPlayer->ammoCount[9] << 1;
int nDamage = (v8 + Random2(vc)) << 4;
nDamage = (nDamage * ((51200 - nDist) + 1)) / 51200;
nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, nDamage);
UseAmmo(pPlayer, 9, nDamage);
if (IsPlayerSprite(pTarget))
{
PLAYER* pOtherPlayer = &gPlayer[pTarget->type - kDudePlayer1];
if (!pOtherPlayer->godMode || !powerupCheck(pOtherPlayer, kPwUpDeathMask))
powerupActivate(pOtherPlayer, kPwUpDeliriumShroom);
}
fxSpawnBlood(pTarget, 0);
2019-09-19 22:42:45 +00:00
}
}
}
UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]);
pPlayer->hasWeapon[10] = 0;
pPlayer->weaponState = -1;
2019-09-19 22:42:45 +00:00
}
}
void DropVoodoo(int , PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
sfxPlay3DSound(pPlayer->pSprite, 455, 2, 0);
spritetype *pSprite = playerFireThing(pPlayer, 0, -4730, kThingVoodooHead, 0xccccc);
2019-09-19 22:42:45 +00:00
if (pSprite)
{
int nXSprite = pSprite->extra;
XSPRITE *pXSprite = &xsprite[nXSprite];
pXSprite->data1 = pPlayer->ammoCount[9];
evPost(pSprite->index, 3, 90, kCallbackDropVoodoo);
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]);
pPlayer->hasWeapon[10] = 0;
2019-09-19 22:42:45 +00:00
}
}
struct TeslaMissile
{
2020-11-21 21:25:26 +00:00
int offset; // offset
int id; // id
int ammouse; // ammo use
int sound; // sound
int light; // light
int flash; // weapon flash
2019-09-19 22:42:45 +00:00
};
void FireTesla(int nTrigger, PLAYER *pPlayer)
{
TeslaMissile teslaMissile[6] =
{
{ 0, 306, 1, 470, 20, 1 },
{ -140, 306, 1, 470, 30, 1 },
{ 140, 306, 1, 470, 30, 1 },
{ 0, 302, 35, 471, 40, 1 },
{ -140, 302, 35, 471, 50, 1 },
{ 140, 302, 35, 471, 50, 1 },
};
if (nTrigger > 0 && nTrigger <= 6)
{
nTrigger--;
spritetype *pSprite = pPlayer->pSprite;
TeslaMissile *pMissile = &teslaMissile[nTrigger];
2020-11-21 21:25:26 +00:00
if (!checkAmmo2(pPlayer, 7, pMissile->ammouse))
2019-09-19 22:42:45 +00:00
{
pMissile = &teslaMissile[0];
2020-11-21 21:25:26 +00:00
if (!checkAmmo2(pPlayer, 7, pMissile->ammouse))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = -1;
SetQAV(pPlayer, kQAVSGUNIDL2);
pPlayer->flashEffect = 0;
2019-09-19 22:42:45 +00:00
return;
}
}
2020-11-21 21:25:26 +00:00
playerFireMissile(pPlayer, pMissile->offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, pMissile->id);
UseAmmo(pPlayer, 7, pMissile->ammouse);
sfxPlay3DSound(pSprite, pMissile->sound, 1, 0);
pPlayer->visibility = pMissile->light;
pPlayer->flashEffect = pMissile->flash;
2019-09-19 22:42:45 +00:00
}
}
void AltFireTesla(int , PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
spritetype *pSprite = pPlayer->pSprite;
playerFireMissile(pPlayer, 0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileTeslaAlt);
UseAmmo(pPlayer, pPlayer->weaponAmmo, 35);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pSprite, 471, 2, 0);
pPlayer->visibility = 40;
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void FireNapalm(int nTrigger, PLAYER *pPlayer)
{
spritetype *pSprite = pPlayer->pSprite;
int offset = 0;
switch (nTrigger)
{
case 2:
offset = -50;
break;
case 3:
offset = 50;
break;
}
playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pSprite, 480, 2, 0);
UseAmmo(pPlayer, 4, 1);
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void FireNapalm2(int , PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
spritetype *pSprite = pPlayer->pSprite;
playerFireMissile(pPlayer, -120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
playerFireMissile(pPlayer, 120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pSprite, 480, 2, 0);
UseAmmo(pPlayer, 4, 2);
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
void AltFireNapalm(int , PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
int nSpeed = MulScale(0x8000, 0x177777, 16)+0x66666;
spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingNapalmBall, nSpeed);
2019-09-19 22:42:45 +00:00
if (pMissile)
{
XSPRITE *pXSprite = &xsprite[pMissile->extra];
pXSprite->data4 = ClipHigh(pPlayer->ammoCount[4], 12);
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 4, pXSprite->data4);
seqSpawn(22, 3, pMissile->extra, -1);
actBurnSprite(pPlayer->pSprite->index, pXSprite, 600);
evPost(pMissile->index, 3, 0, kCallbackFXFlameLick);
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pMissile, 480, 2, 0);
pPlayer->visibility = 30;
pPlayer->flashEffect = 1;
2019-09-19 22:42:45 +00:00
}
}
void FireLifeLeech(int nTrigger, PLAYER *pPlayer)
{
if (!CheckAmmo(pPlayer, 8, 1))
return;
int r1 = Random2(2000);
int r2 = Random2(2000);
int r3 = Random2(1000);
auto actor = pPlayer->actor();
spritetype *pMissile = playerFireMissile(pPlayer, 0, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, 315);
2019-09-19 22:42:45 +00:00
if (pMissile)
{
XSPRITE *pXSprite = &xsprite[pMissile->extra];
pXSprite->target_i = pPlayer->aimTarget;
2019-09-19 22:42:45 +00:00
pMissile->ang = (nTrigger==2) ? 1024 : 0;
}
if (checkAmmo2(pPlayer, 8, 1))
2019-09-19 22:42:45 +00:00
UseAmmo(pPlayer, 8, 1);
else
actDamageSprite(actor, actor, kDamageSpirit, 16);
pPlayer->visibility = ClipHigh(pPlayer->visibility+5, 50);
2019-09-19 22:42:45 +00:00
}
void AltFireLifeLeech(int , PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
auto actor = pPlayer->actor();
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 455, 2, 0);
spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingDroppedLifeLeech, 0x19999);
2019-09-19 22:42:45 +00:00
if (pMissile)
{
pMissile->cstat |= 4096;
XSPRITE *pXSprite = &xsprite[pMissile->extra];
pXSprite->Push = 1;
pXSprite->Proximity = 1;
pXSprite->DudeLockout = 1;
pXSprite->data4 = ClipHigh(pPlayer->ammoCount[4], 12);
2019-09-19 22:42:45 +00:00
pXSprite->stateTimer = 1;
evPost(pMissile->index, 3, 120, kCallbackLeechStateTimer);
2019-09-19 22:42:45 +00:00
if (gGameOptions.nGameType <= 1)
{
int nAmmo = pPlayer->ammoCount[8];
2021-05-12 00:00:06 +00:00
if (nAmmo < 25 && pPlayer->pXSprite->health > unsigned((25-nAmmo)<<4))
2019-09-19 22:42:45 +00:00
{
actDamageSprite(actor, actor, kDamageSpirit, ((25-nAmmo)<<4));
2019-09-19 22:42:45 +00:00
nAmmo = 25;
}
pXSprite->data3 = nAmmo;
UseAmmo(pPlayer, 8, nAmmo);
}
else
{
pXSprite->data3 = pPlayer->ammoCount[8];
pPlayer->ammoCount[8] = 0;
2019-09-19 22:42:45 +00:00
}
pPlayer->hasWeapon[9] = 0;
2019-09-19 22:42:45 +00:00
}
}
void FireBeast(int , PLAYER * pPlayer)
2019-09-19 22:42:45 +00:00
{
2021-08-30 18:28:24 +00:00
auto actor = &bloodActors[pPlayer->pSprite->index];
2019-09-19 22:42:45 +00:00
int r1 = Random2(2000);
int r2 = Random2(2000);
int r3 = Random2(2000);
2021-08-30 18:28:24 +00:00
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, kVectorBeastSlash);
2019-09-19 22:42:45 +00:00
}
uint8_t gWeaponUpgrade[][13] = {
2019-09-19 22:42:45 +00:00
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
};
int WeaponUpgrade(PLAYER *pPlayer, int newWeapon)
2019-09-19 22:42:45 +00:00
{
int weapon = pPlayer->curWeapon;
if (!checkFired6or7(pPlayer) && (cl_weaponswitch&1) && (gWeaponUpgrade[pPlayer->curWeapon][newWeapon] || (cl_weaponswitch&2)))
2019-09-19 22:42:45 +00:00
weapon = newWeapon;
return weapon;
}
int OrderNext[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 1 };
int OrderPrev[] = { 12, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1 };
static int WeaponFindNext(PLAYER *pPlayer, int *a2, int bDir)
2019-09-19 22:42:45 +00:00
{
int weapon = pPlayer->curWeapon;
2019-09-19 22:42:45 +00:00
do
{
if (bDir)
weapon = OrderNext[weapon];
else
weapon = OrderPrev[weapon];
2020-11-21 18:30:00 +00:00
if (weaponModes[weapon].update && pPlayer->hasWeapon[weapon])
2019-09-19 22:42:45 +00:00
{
if (weapon == kWeapLifeLeech)
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
if (CheckAmmo(pPlayer, weaponModes[weapon].ammoType, 1))
2019-09-19 22:42:45 +00:00
break;
}
else
{
2020-11-21 18:30:00 +00:00
if (checkAmmo2(pPlayer, weaponModes[weapon].ammoType, 1))
2019-09-19 22:42:45 +00:00
break;
}
}
} while (weapon != pPlayer->curWeapon);
if (weapon == pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
if (!weaponModes[weapon].update || !CheckAmmo(pPlayer, weaponModes[weapon].ammoType, 1))
weapon = kWeapPitchFork;
2019-09-19 22:42:45 +00:00
}
if (a2)
*a2 = 0;
return weapon;
}
static int WeaponFindLoaded(PLAYER *pPlayer, int *a2)
2019-09-19 22:42:45 +00:00
{
int v4 = 1;
2019-09-19 22:42:45 +00:00
int v14 = 0;
2020-11-21 18:30:00 +00:00
if (weaponModes[pPlayer->curWeapon].update > 1)
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
for (int i = 0; i < weaponModes[pPlayer->curWeapon].update; i++)
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
if (CheckAmmo(pPlayer, weaponModes[pPlayer->curWeapon].ammoType, 1))
2019-09-19 22:42:45 +00:00
{
v14 = i;
v4 = pPlayer->curWeapon;
2019-09-19 22:42:45 +00:00
break;
}
}
}
if (v4 == kWeapPitchFork)
2019-09-19 22:42:45 +00:00
{
int vc = 0;
for (int i = 0; i < 14; i++)
{
int weapon = pPlayer->weaponOrder[vc][i];
if (pPlayer->hasWeapon[weapon])
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
for (int j = 0; j < weaponModes[weapon].update; j++)
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
if (CheckWeaponAmmo(pPlayer, weapon, weaponModes[weapon].ammoType, 1))
2019-09-19 22:42:45 +00:00
{
if (a2)
*a2 = j;
return weapon;
}
}
}
}
}
else if (a2)
*a2 = v14;
return v4;
}
int processSprayCan(PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 5:
if (!(pPlayer->input.actions & SB_ALTFIRE))
pPlayer->weaponState = 6;
2019-09-19 22:42:45 +00:00
return 1;
case 6:
if (pPlayer->input.actions & SB_ALTFIRE)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 3;
pPlayer->fuseTime = pPlayer->weaponTimer;
StartQAV(pPlayer, kQAVCANDROP, nClientDropCan);
2019-09-19 22:42:45 +00:00
}
else if (pPlayer->input.actions & SB_FIRE)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 7;
pPlayer->fuseTime = 0;
pPlayer->throwTime = PlayClock;
2019-09-19 22:42:45 +00:00
}
return 1;
case 7:
{
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
if (!(pPlayer->input.actions & SB_FIRE))
2019-09-19 22:42:45 +00:00
{
if (!pPlayer->fuseTime)
pPlayer->fuseTime = pPlayer->weaponTimer;
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVCANTHRO, nClientThrowCan);
2019-09-19 22:42:45 +00:00
}
return 1;
}
}
return 0;
}
static bool processTNT(PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 4:
if (!(pPlayer->input.actions & SB_ALTFIRE))
pPlayer->weaponState = 5;
2019-09-19 22:42:45 +00:00
return 1;
case 5:
if (pPlayer->input.actions & SB_ALTFIRE)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 1;
pPlayer->fuseTime = pPlayer->weaponTimer;
StartQAV(pPlayer, kQAVBUNDROP, nClientDropBundle);
2019-09-19 22:42:45 +00:00
}
else if (pPlayer->input.actions & SB_FIRE)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 6;
pPlayer->fuseTime = 0;
pPlayer->throwTime = PlayClock;
2019-09-19 22:42:45 +00:00
}
return 1;
case 6:
{
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
if (!(pPlayer->input.actions & SB_FIRE))
2019-09-19 22:42:45 +00:00
{
if (!pPlayer->fuseTime)
pPlayer->fuseTime = pPlayer->weaponTimer;
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVBUNTHRO, nClientThrowBundle);
2019-09-19 22:42:45 +00:00
}
return 1;
}
}
return 0;
}
static bool processProxy(PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 9:
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
pPlayer->weaponTimer = 0;
pPlayer->qavTimer = 0;
if (!(pPlayer->input.actions & SB_FIRE))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 8;
StartQAV(pPlayer, kQAVPROXTHRO, nClientThrowProx);
2019-09-19 22:42:45 +00:00
}
break;
}
return 0;
}
static bool processRemote(PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 13:
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
if (!(pPlayer->input.actions & SB_FIRE))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 11;
StartQAV(pPlayer, kQAVREMTHRO, nClientThrowRemote);
2019-09-19 22:42:45 +00:00
}
break;
}
return 0;
}
static bool processLeech(PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 4:
pPlayer->weaponState = 6;
StartQAV(pPlayer, kQAVSTAFIRE1, nClientFireLifeLeech, 1);
2019-09-19 22:42:45 +00:00
return 1;
case 6:
if (!(pPlayer->input.actions & SB_ALTFIRE))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVSTAFPOST);
2019-09-19 22:42:45 +00:00
return 1;
}
break;
case 8:
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVSTAFPOST);
2019-09-19 22:42:45 +00:00
return 1;
}
return 0;
}
static bool processTesla(PLAYER *pPlayer)
2019-09-19 22:42:45 +00:00
{
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 4:
pPlayer->weaponState = 5;
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla, 1);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla, 1);
2019-09-19 22:42:45 +00:00
return 1;
case 5:
if (!(pPlayer->input.actions & SB_FIRE))
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 2;
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNPST);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNPOST);
2019-09-19 22:42:45 +00:00
return 1;
}
break;
case 7:
pPlayer->weaponState = 2;
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNPST);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNPOST);
2019-09-19 22:42:45 +00:00
return 1;
}
return 0;
}
void WeaponProcess(PLAYER *pPlayer) {
pPlayer->flashEffect = ClipLow(pPlayer->flashEffect - 1, 0);
#ifdef NOONE_EXTENSIONS
if (gPlayerCtrl[pPlayer->nPlayer].qavScene.index >= 0 && pPlayer->pXSprite->health > 0) {
playerQavSceneProcess(pPlayer, &gPlayerCtrl[pPlayer->nPlayer].qavScene);
UpdateAimVector(pPlayer);
return;
}
#endif
2019-09-19 22:42:45 +00:00
if (pPlayer->pXSprite->health == 0)
{
pPlayer->qavLoop = 0;
2019-09-19 22:42:45 +00:00
sfxKill3DSound(pPlayer->pSprite, 1, -1);
}
if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->curWeapon))
2019-09-19 22:42:45 +00:00
{
if (checkFired6or7(pPlayer))
2019-09-19 22:42:45 +00:00
{
if (pPlayer->curWeapon == kWeapSpraycan)
2019-09-19 22:42:45 +00:00
{
pPlayer->fuseTime = pPlayer->weaponTimer;
2019-09-19 22:42:45 +00:00
DropCan(1, pPlayer);
pPlayer->weaponState = 3;
2019-09-19 22:42:45 +00:00
}
else if (pPlayer->curWeapon == kWeapDynamite)
2019-09-19 22:42:45 +00:00
{
pPlayer->fuseTime = pPlayer->weaponTimer;
2019-09-19 22:42:45 +00:00
DropBundle(1, pPlayer);
pPlayer->weaponState = 1;
2019-09-19 22:42:45 +00:00
}
}
WeaponLower(pPlayer);
pPlayer->throwPower = 0;
2019-09-19 22:42:45 +00:00
}
WeaponPlay(pPlayer);
UpdateAimVector(pPlayer);
pPlayer->weaponTimer -= 4;
bool bShoot = pPlayer->input.actions & SB_FIRE;
bool bShoot2 = pPlayer->input.actions & SB_ALTFIRE;
const int prevNewWeaponVal = pPlayer->input.getNewWeapon(); // used to fix scroll issue for banned weapons
if ((bShoot || bShoot2 || prevNewWeaponVal) && pPlayer->weaponQav == qavGetCorrectID(kQAVVDIDLE2)) pPlayer->weaponTimer = 0;
if (pPlayer->qavLoop && pPlayer->pXSprite->health > 0)
2019-09-19 22:42:45 +00:00
{
if (bShoot && CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1))
2019-09-19 22:42:45 +00:00
{
auto pQAV = getQAV(pPlayer->weaponQav);
while (pPlayer->weaponTimer <= 0)
{
pPlayer->weaponTimer += pQAV->duration;
pPlayer->qavTimer += pQAV->duration;
}
2019-09-19 22:42:45 +00:00
}
else
{
pPlayer->weaponTimer = 0;
pPlayer->qavTimer = 0;
pPlayer->qavLoop = 0;
2019-09-19 22:42:45 +00:00
}
return;
}
pPlayer->weaponTimer = ClipLow(pPlayer->weaponTimer, 0);
switch (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
case kWeapSpraycan:
if (processSprayCan(pPlayer))
2019-09-19 22:42:45 +00:00
return;
break;
case kWeapDynamite:
if (processTNT(pPlayer))
2019-09-19 22:42:45 +00:00
return;
break;
case kWeapProximity:
if (processProxy(pPlayer))
2019-09-19 22:42:45 +00:00
return;
break;
case kWeapRemote:
if (processRemote(pPlayer))
2019-09-19 22:42:45 +00:00
return;
break;
}
if (pPlayer->weaponTimer > 0)
2019-09-19 22:42:45 +00:00
return;
if (pPlayer->pXSprite->health == 0 || pPlayer->curWeapon == kWeapNone)
pPlayer->weaponQav = kQAVNone;
switch (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
case kWeapLifeLeech:
if (processLeech(pPlayer))
2019-09-19 22:42:45 +00:00
return;
break;
case kWeapTeslaCannon:
if (processTesla(pPlayer))
2019-09-19 22:42:45 +00:00
return;
break;
}
if (VanillaMode())
2019-09-19 22:42:45 +00:00
{
if (pPlayer->nextWeapon)
{
sfxKill3DSound(pPlayer->pSprite, -1, 441);
pPlayer->weaponState = 0;
pPlayer->newWeapon = pPlayer->nextWeapon;
pPlayer->nextWeapon = kWeapNone;
}
2019-09-19 22:42:45 +00:00
}
if (pPlayer->input.getNewWeapon() == WeaponSel_Next)
2019-09-19 22:42:45 +00:00
{
pPlayer->input.setNewWeapon(kWeapNone);
if (VanillaMode())
{
pPlayer->weaponState = 0;
}
pPlayer->nextWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
int t;
int weapon = WeaponFindNext(pPlayer, &t, 1);
pPlayer->weaponMode[weapon] = t;
if (VanillaMode())
2019-09-19 22:42:45 +00:00
{
if (pPlayer->curWeapon)
{
WeaponLower(pPlayer);
pPlayer->nextWeapon = weapon;
return;
}
2019-09-19 22:42:45 +00:00
}
pPlayer->newWeapon = weapon;
2019-09-19 22:42:45 +00:00
}
else if (pPlayer->input.getNewWeapon() == WeaponSel_Prev)
2019-09-19 22:42:45 +00:00
{
pPlayer->input.setNewWeapon(kWeapNone);
if (VanillaMode())
{
pPlayer->weaponState = 0;
}
pPlayer->nextWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
int t;
int weapon = WeaponFindNext(pPlayer, &t, 0);
pPlayer->weaponMode[weapon] = t;
if (VanillaMode())
2019-09-19 22:42:45 +00:00
{
if (pPlayer->curWeapon)
{
WeaponLower(pPlayer);
pPlayer->nextWeapon = weapon;
return;
}
2019-09-19 22:42:45 +00:00
}
pPlayer->newWeapon = weapon;
2019-09-19 22:42:45 +00:00
}
else if (pPlayer->input.getNewWeapon() == WeaponSel_Alt)
{
int weapon;
switch (pPlayer->curWeapon)
{
case kWeapDynamite:
weapon = kWeapProximity;
break;
case kWeapProximity:
weapon = kWeapRemote;
break;
case kWeapRemote:
weapon = kWeapDynamite;
break;
default:
return;
}
pPlayer->input.setNewWeapon(kWeapNone);
pPlayer->weaponState = 0;
pPlayer->nextWeapon = kWeapNone;
int t = 0;
pPlayer->weaponMode[weapon] = t;
if (pPlayer->curWeapon)
{
WeaponLower(pPlayer);
pPlayer->nextWeapon = weapon;
return;
}
pPlayer->newWeapon = weapon;
}
if (!VanillaMode())
{
if (pPlayer->nextWeapon)
{
sfxKill3DSound(pPlayer->pSprite, -1, 441);
pPlayer->newWeapon = pPlayer->nextWeapon;
pPlayer->nextWeapon = kWeapNone;
}
}
if (pPlayer->weaponState == -1)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 0;
2019-09-19 22:42:45 +00:00
int t;
int weapon = WeaponFindLoaded(pPlayer, &t);
pPlayer->weaponMode[weapon] = t;
if (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
WeaponLower(pPlayer);
pPlayer->nextWeapon = weapon;
2019-09-19 22:42:45 +00:00
return;
}
pPlayer->newWeapon = weapon;
2019-09-19 22:42:45 +00:00
}
if (pPlayer->newWeapon)
2019-09-19 22:42:45 +00:00
{
if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->newWeapon) && !checkFired6or7(pPlayer) && !VanillaMode()) // skip banned weapons when underwater and using next/prev weapon key inputs
{
if (prevNewWeaponVal == WeaponSel_Next || prevNewWeaponVal == WeaponSel_Prev) // if player switched weapons
{
PLAYER tmpPlayer = *pPlayer;
tmpPlayer.curWeapon = pPlayer->newWeapon; // set current banned weapon to curweapon so WeaponFindNext() can find the next weapon
for (int i = 0; i < 3; i++) // attempt twice to find a new weapon
{
tmpPlayer.curWeapon = WeaponFindNext(&tmpPlayer, NULL, (char)(prevNewWeaponVal == WeaponSel_Next));
if (!BannedUnderwater(tmpPlayer.curWeapon)) // if new weapon is not a banned weapon, set to new current weapon
{
pPlayer->newWeapon = tmpPlayer.curWeapon;
pPlayer->weaponMode[pPlayer->newWeapon] = 0;
break;
}
}
}
}
if (pPlayer->newWeapon == kWeapDynamite)
2019-09-19 22:42:45 +00:00
{
if (pPlayer->curWeapon == kWeapDynamite)
2019-09-19 22:42:45 +00:00
{
if (checkAmmo2(pPlayer, 10, 1))
pPlayer->newWeapon = kWeapProximity;
else if (checkAmmo2(pPlayer, 11, 1))
pPlayer->newWeapon = kWeapRemote;
2019-09-19 22:42:45 +00:00
}
else if (pPlayer->curWeapon == kWeapProximity)
2019-09-19 22:42:45 +00:00
{
if (checkAmmo2(pPlayer, 11, 1))
pPlayer->newWeapon = kWeapRemote;
else if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
pPlayer->newWeapon = kWeapDynamite;
2019-09-19 22:42:45 +00:00
}
else if (pPlayer->curWeapon == kWeapRemote)
2019-09-19 22:42:45 +00:00
{
if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
pPlayer->newWeapon = kWeapDynamite;
else if (checkAmmo2(pPlayer, 10, 1))
pPlayer->newWeapon = kWeapProximity;
2019-09-19 22:42:45 +00:00
}
else
{
if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
pPlayer->newWeapon = kWeapDynamite;
else if (checkAmmo2(pPlayer, 10, 1))
pPlayer->newWeapon = kWeapVoodooDoll;
else if (checkAmmo2(pPlayer, 11, 1))
pPlayer->newWeapon = kWeapRemote;
2019-09-19 22:42:45 +00:00
}
}
2021-08-23 01:40:05 +00:00
else if ((pPlayer->newWeapon == kWeapSpraycan) && !VanillaMode())
{
if ((pPlayer->curWeapon == kWeapSpraycan) && (pPlayer->weaponState == 2)) // fix spray can state glitch when switching from spray to tnt and back quickly
{
pPlayer->weaponState = 1;
pPlayer->newWeapon = kWeapNone;
return;
}
}
if (pPlayer->pXSprite->health == 0 || pPlayer->hasWeapon[pPlayer->newWeapon] == 0)
2019-09-19 22:42:45 +00:00
{
pPlayer->newWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
return;
}
if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->newWeapon) && !checkFired6or7(pPlayer))
2019-09-19 22:42:45 +00:00
{
pPlayer->newWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
return;
}
int nWeapon = pPlayer->newWeapon;
2020-11-21 18:30:00 +00:00
int v4c = weaponModes[nWeapon].update;
if (!pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
2020-11-21 18:30:00 +00:00
int nAmmoType = weaponModes[nWeapon].ammoType;
2019-09-19 22:42:45 +00:00
if (v4c > 1)
{
if (CheckAmmo(pPlayer, nAmmoType, 1) || nAmmoType == 11)
WeaponRaise(pPlayer);
pPlayer->newWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
}
else
{
2020-11-21 18:30:00 +00:00
if (CheckWeaponAmmo(pPlayer, nWeapon, nAmmoType, 1))
2019-09-19 22:42:45 +00:00
WeaponRaise(pPlayer);
else
{
pPlayer->weaponState = 0;
2019-09-19 22:42:45 +00:00
int t;
int weapon = WeaponFindLoaded(pPlayer, &t);
pPlayer->weaponMode[weapon] = t;
if (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
WeaponLower(pPlayer);
pPlayer->nextWeapon = weapon;
2019-09-19 22:42:45 +00:00
return;
}
pPlayer->newWeapon = weapon;
2019-09-19 22:42:45 +00:00
}
}
return;
}
if (nWeapon == pPlayer->curWeapon && v4c <= 1)
2019-09-19 22:42:45 +00:00
{
pPlayer->newWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
return;
}
int i = 0;
if (nWeapon == pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
i = 1;
for (; i <= v4c; i++)
{
int v6c = (pPlayer->weaponMode[nWeapon]+i)%v4c;
2020-11-21 18:30:00 +00:00
if (CheckWeaponAmmo(pPlayer, nWeapon, weaponModes[nWeapon].ammoType, 1))
2019-09-19 22:42:45 +00:00
{
WeaponLower(pPlayer);
pPlayer->weaponMode[nWeapon] = v6c;
2019-09-19 22:42:45 +00:00
return;
}
}
pPlayer->newWeapon = kWeapNone;
2019-09-19 22:42:45 +00:00
return;
}
if (pPlayer->curWeapon && !CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1) && pPlayer->weaponAmmo != 11)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = -1;
2019-09-19 22:42:45 +00:00
return;
}
if (bShoot)
{
switch (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
case kWeapPitchFork:
StartQAV(pPlayer, kQAVPFORK, nClientFirePitchfork);
2019-09-19 22:42:45 +00:00
return;
case kWeapSpraycan:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 3:
pPlayer->weaponState = 4;
StartQAV(pPlayer, kQAVCANFIRE, nClientFireSpray, 1);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapDynamite:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 3:
pPlayer->weaponState = 6;
pPlayer->fuseTime = -1;
pPlayer->throwTime = PlayClock;
StartQAV(pPlayer, kQAVBUNFUSE, nClientExplodeBundle);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapProximity:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 7:
SetQAV(pPlayer, kQAVPROXIDLE);
pPlayer->weaponState = 9;
pPlayer->throwTime = PlayClock;
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapRemote:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 10:
SetQAV(pPlayer, kQAVREMIDLE1);
pPlayer->weaponState = 13;
pPlayer->throwTime = PlayClock;
2019-09-19 22:42:45 +00:00
return;
case 11:
pPlayer->weaponState = 12;
StartQAV(pPlayer, kQAVREMFIRE, nClientFireRemote);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapShotgun:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 7:
pPlayer->weaponState = 6;
StartQAV(pPlayer, kQAV2SHOTF2, nClientFireShotgun);
2019-09-19 22:42:45 +00:00
return;
case 3:
pPlayer->weaponState = 2;
StartQAV(pPlayer, kQAVSHOTF1, nClientFireShotgun);
2019-09-19 22:42:45 +00:00
return;
case 2:
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVSHOTF2, nClientFireShotgun);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapTommyGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
StartQAV(pPlayer, kQAV2TOMFIRE, nClientFireTommy, 1);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVTOMFIRE, nClientFireTommy, 1);
2019-09-19 22:42:45 +00:00
return;
case kWeapFlareGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare);
2019-09-19 22:42:45 +00:00
return;
case kWeapVoodooDoll:
2019-09-19 22:42:45 +00:00
{
static int nChance[] = { 0xa000, 0xc000, 0xe000, 0x10000 };
int nRand = wrand()*2;
int i;
for (i = 0; nChance[i] < nRand; i++)
{
}
pPlayer->voodooTarget = pPlayer->aimTarget;
if (pPlayer->voodooTarget == -1 || sprite[pPlayer->voodooTarget].statnum != kStatDude)
2019-09-19 22:42:45 +00:00
i = 4;
StartQAV(pPlayer,kQAVVDFIRE1 + i, nClientFireVoodoo);
2019-09-19 22:42:45 +00:00
return;
}
case kWeapTeslaCannon:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 2:
pPlayer->weaponState = 4;
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla);
2019-09-19 22:42:45 +00:00
return;
case 5:
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapNapalm:
if (powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2NAPFIRE, nClientFireNapalm);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVNAPFIRE, nClientFireNapalm);
2019-09-19 22:42:45 +00:00
return;
case kWeapLifeLeech:
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 494, 2, 0);
StartQAV(pPlayer, kQAVSTAFIRE4, nClientFireLifeLeech);
2019-09-19 22:42:45 +00:00
return;
case kWeapBeast:
StartQAV(pPlayer, kQAVBSTATAK1 + Random(4), nClientFireBeast);
2019-09-19 22:42:45 +00:00
return;
}
}
if (bShoot2)
{
switch (pPlayer->curWeapon)
2019-09-19 22:42:45 +00:00
{
case kWeapPitchFork:
StartQAV(pPlayer, kQAVPFORK, nClientFirePitchfork);
2019-09-19 22:42:45 +00:00
return;
case kWeapSpraycan:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 3:
pPlayer->weaponState = 5;
StartQAV(pPlayer, kQAVCANFIRE2, nClientExplodeCan);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapDynamite:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 3:
pPlayer->weaponState = 4;
StartQAV(pPlayer, kQAVBUNFUSE, nClientExplodeBundle);
2019-09-19 22:42:45 +00:00
return;
case 7:
pPlayer->weaponState = 8;
StartQAV(pPlayer, kQAVPROXDROP, nClientDropProx);
2019-09-19 22:42:45 +00:00
return;
case 10:
pPlayer->weaponState = 11;
StartQAV(pPlayer, kQAVREMDROP, nClientDropRemote);
2019-09-19 22:42:45 +00:00
return;
case 11:
if (pPlayer->ammoCount[11] > 0)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 10;
StartQAV(pPlayer, kQAVREMUP1);
2019-09-19 22:42:45 +00:00
}
return;
}
break;
case kWeapProximity:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 7:
pPlayer->weaponState = 8;
StartQAV(pPlayer, kQAVPROXDROP, nClientDropProx);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapRemote:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 10:
pPlayer->weaponState = 11;
StartQAV(pPlayer, kQAVREMDROP, nClientDropRemote);
2019-09-19 22:42:45 +00:00
return;
case 11:
if (pPlayer->ammoCount[11] > 0)
2019-09-19 22:42:45 +00:00
{
pPlayer->weaponState = 10;
StartQAV(pPlayer, kQAVREMUP1);
2019-09-19 22:42:45 +00:00
}
return;
}
break;
case kWeapShotgun:
switch (pPlayer->weaponState)
2019-09-19 22:42:45 +00:00
{
case 7:
pPlayer->weaponState = 6;
StartQAV(pPlayer, kQAV2SHOTFIR, nClientFireShotgun);
2019-09-19 22:42:45 +00:00
return;
case 3:
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVSHOTF3, nClientFireShotgun);
2019-09-19 22:42:45 +00:00
return;
case 2:
pPlayer->weaponState = 1;
StartQAV(pPlayer, kQAVSHOTF2, nClientFireShotgun);
2019-09-19 22:42:45 +00:00
return;
}
break;
case kWeapTommyGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
StartQAV(pPlayer, kQAV2TOMALT, nClientAltFireSpread2);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVTOMSPRED, nClientAltFireSpread2);
2019-09-19 22:42:45 +00:00
return;
case kWeapVoodooDoll:
2019-09-19 22:42:45 +00:00
sfxPlay3DSound(pPlayer->pSprite, 461, 2, 0);
StartQAV(pPlayer, kQAVVDSPEL1, nClientAltFireVoodoo);
2019-09-19 22:42:45 +00:00
return;
#if 0
case kWeapFlareGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare, 0);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare, 0);
2019-09-19 22:42:45 +00:00
return;
#endif
case kWeapTeslaCannon:
if (checkAmmo2(pPlayer, 7, 35))
2019-09-19 22:42:45 +00:00
{
if (checkAmmo2(pPlayer, 7, 70) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNALT, nClientFireTesla);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNFIR4, nClientFireTesla);
2019-09-19 22:42:45 +00:00
}
else
{
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla);
2019-09-19 22:42:45 +00:00
}
return;
case kWeapNapalm:
if (powerupCheck(pPlayer, kPwUpTwoGuns))
// by NoOne: allow napalm launcher alt fire act like in v1.0x versions
2021-08-23 01:40:05 +00:00
if (gGameOptions.weaponsV10x && !VanillaMode()) StartQAV(pPlayer, kQAV2NAPFIR2, nClientFireNapalm2);
else StartQAV(pPlayer, kQAV2NAPFIRE, nClientAltFireNapalm);
2019-09-19 22:42:45 +00:00
else
2021-08-23 01:40:05 +00:00
StartQAV(pPlayer, kQAVNAPFIRE, (gGameOptions.weaponsV10x && !VanillaMode()) ? nClientFireNapalm : nClientAltFireNapalm);
2019-09-19 22:42:45 +00:00
return;
case kWeapFlareGun:
2019-09-19 22:42:45 +00:00
if (CheckAmmo(pPlayer, 1, 8))
{
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 16))
StartQAV(pPlayer, kQAVFLAR2FIR, nClientAltFireFlare);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVFLARFIR2, nClientAltFireFlare);
2019-09-19 22:42:45 +00:00
}
else
{
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare);
2019-09-19 22:42:45 +00:00
else
StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare);
2019-09-19 22:42:45 +00:00
}
return;
case kWeapLifeLeech:
if (gGameOptions.nGameType <= 1 && !checkAmmo2(pPlayer, 8, 1) && pPlayer->pXSprite->health < (25 << 4))
2019-09-19 22:42:45 +00:00
{
sfxPlay3DSound(pPlayer->pSprite, 494, 2, 0);
StartQAV(pPlayer, kQAVSTAFIRE4, nClientFireLifeLeech);
2019-09-19 22:42:45 +00:00
}
else
{
StartQAV(pPlayer, kQAVSTAFDOWN);
2019-09-19 22:42:45 +00:00
AltFireLifeLeech(1, pPlayer);
pPlayer->weaponState = -1;
2019-09-19 22:42:45 +00:00
}
return;
}
}
WeaponUpdateState(pPlayer);
}
2020-11-21 18:30:00 +00:00
void teslaHit(spritetype *pMissile, int a2)
2019-09-19 22:42:45 +00:00
{
2021-09-16 19:18:26 +00:00
auto missileactor = &bloodActors[pMissile->index];
uint8_t sectmap[(kMaxSectors+7)>>3];
2019-09-19 22:42:45 +00:00
int x = pMissile->x;
int y = pMissile->y;
int z = pMissile->z;
int nDist = 300;
int nSector = pMissile->sectnum;
2021-09-16 19:18:26 +00:00
auto owner = missileactor->GetOwner();
const bool newSectCheckMethod = !cl_bloodvanillaexplosions && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, nDist, sectmap, nullptr, newSectCheckMethod);
bool v4 = true;
2020-12-03 17:00:07 +00:00
DBloodActor* actor = nullptr;
actHitcodeToData(a2, &gHitInfo, &actor);
if (a2 == 3 && actor && actor->s().statnum == kStatDude)
v4 = false;
2021-09-16 19:18:26 +00:00
BloodStatIterator it(kStatDude);
while (auto hitactor = it.Next())
2019-09-19 22:42:45 +00:00
{
2021-09-16 19:18:26 +00:00
if (hitactor != owner || v4)
2019-09-19 22:42:45 +00:00
{
2021-09-16 19:18:26 +00:00
spritetype *pHitSprite = &hitactor->s();
if (pHitSprite->flags&32)
2019-09-19 22:42:45 +00:00
continue;
2021-09-16 19:18:26 +00:00
if (TestBitString(sectmap, pHitSprite->sectnum) && CheckProximity(pHitSprite, x, y, z, nSector, nDist))
2019-09-19 22:42:45 +00:00
{
2021-09-16 19:18:26 +00:00
int dx = pMissile->x-pHitSprite->x;
int dy = pMissile->y-pHitSprite->y;
2019-09-19 22:42:45 +00:00
int nDamage = ClipLow((nDist-(ksqrt(dx*dx+dy*dy)>>4)+20)>>1, 10);
2021-09-16 19:18:26 +00:00
if (hitactor == owner)
2019-09-19 22:42:45 +00:00
nDamage /= 2;
2021-09-16 19:18:26 +00:00
actDamageSprite(owner, hitactor, kDamageTesla, nDamage<<4);
2019-09-19 22:42:45 +00:00
}
}
}
2020-10-15 15:15:45 +00:00
it.Reset(kStatThing);
2021-09-16 19:18:26 +00:00
while (auto hitactor = it.Next())
2019-09-19 22:42:45 +00:00
{
2021-09-16 19:18:26 +00:00
spritetype *pHitSprite = &hitactor->s();
if (pHitSprite->flags&32)
2019-09-19 22:42:45 +00:00
continue;
2021-09-16 19:18:26 +00:00
if (TestBitString(sectmap, pHitSprite->sectnum) && CheckProximity(pHitSprite, x, y, z, nSector, nDist))
2019-09-19 22:42:45 +00:00
{
2021-09-16 19:18:26 +00:00
XSPRITE *pXSprite = &hitactor->x();
2019-09-19 22:42:45 +00:00
if (!pXSprite->locked)
{
2021-09-16 19:18:26 +00:00
int dx = pMissile->x-pHitSprite->x;
int dy = pMissile->y-pHitSprite->y;
2019-09-19 22:42:45 +00:00
int nDamage = ClipLow(nDist-(ksqrt(dx*dx+dy*dy)>>4)+20, 20);
2021-09-16 19:18:26 +00:00
actDamageSprite(owner, hitactor, kDamageTesla, nDamage << 4);
2019-09-19 22:42:45 +00:00
}
}
}
}
END_BLD_NS