mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-15 15:11:41 +00:00
138690d34e
This will just copy the object without performing the read barrier on the source data which is not what we want here. For these assignments the type must be explicit.
2716 lines
84 KiB
C++
2716 lines
84 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
|
Copyright (C) 2019 Nuke.YKT
|
|
|
|
This file is part of NBlood.
|
|
|
|
NBlood is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License version 2
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
#include "ns.h" // Must come before everything else!
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "build.h"
|
|
|
|
#include "blood.h"
|
|
#include "bloodactor.h"
|
|
|
|
BEGIN_BLD_NS
|
|
|
|
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*) =
|
|
{
|
|
(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
|
|
{
|
|
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)
|
|
{
|
|
switch (pPlayer->curWeapon)
|
|
{
|
|
case kWeapSpraycan:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 5:
|
|
case 6:
|
|
return 1;
|
|
case 7:
|
|
if (VanillaMode())
|
|
return 0;
|
|
return 1;
|
|
}
|
|
break;
|
|
case kWeapDynamite:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool BannedUnderwater(int nWeapon)
|
|
{
|
|
return nWeapon == kWeapSpraycan || nWeapon == kWeapDynamite;
|
|
}
|
|
|
|
static bool CheckWeaponAmmo(const PLAYER *pPlayer, int weapon, int ammotype, int count)
|
|
{
|
|
if (gInfiniteAmmo)
|
|
return 1;
|
|
if (ammotype == -1)
|
|
return 1;
|
|
if (weapon == kWeapRemote && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11)
|
|
return 1;
|
|
if (weapon == kWeapLifeLeech && pPlayer->pXSprite->health > 0)
|
|
return 1;
|
|
return pPlayer->ammoCount[ammotype] >= count;
|
|
}
|
|
|
|
static bool CheckAmmo(const PLAYER *pPlayer, int ammotype, int count)
|
|
{
|
|
if (gInfiniteAmmo)
|
|
return 1;
|
|
if (ammotype == -1)
|
|
return 1;
|
|
if (pPlayer->curWeapon == kWeapRemote && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11)
|
|
return 1;
|
|
if (pPlayer->curWeapon == kWeapLifeLeech && pPlayer->pXSprite->health >= unsigned(count<<4))
|
|
return 1;
|
|
return pPlayer->ammoCount[ammotype] >= count;
|
|
}
|
|
|
|
static bool checkAmmo2(const PLAYER *pPlayer, int ammotype, int amount)
|
|
{
|
|
if (gInfiniteAmmo)
|
|
return 1;
|
|
if (ammotype == -1)
|
|
return 1;
|
|
return pPlayer->ammoCount[ammotype] >= amount;
|
|
}
|
|
|
|
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->actor, dz, a2, a3);
|
|
}
|
|
|
|
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->actor, dz, a2, a3);
|
|
}
|
|
|
|
void WeaponInit(void)
|
|
{
|
|
auto doInit = [](const int base)
|
|
{
|
|
for (int i = base; i < (kQAVEnd + base); i++)
|
|
{
|
|
auto pQAV = getQAV(i);
|
|
if (!pQAV)
|
|
I_Error("Could not load QAV %d\n", i);
|
|
}
|
|
};
|
|
|
|
doInit(0);
|
|
doInit(10000);
|
|
}
|
|
|
|
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)
|
|
{
|
|
assert(pPlayer != NULL);
|
|
if (pPlayer->weaponQav == kQAVNone)
|
|
return;
|
|
auto pQAV = getQAV(pPlayer->weaponQav);
|
|
int duration;
|
|
double smoothratio;
|
|
|
|
qavProcessTimer(pPlayer, pQAV, &duration, &smoothratio, pPlayer->weaponState == -1, pPlayer->curWeapon == kWeapShotgun && pPlayer->weaponState == 7);
|
|
|
|
pQAV->x = int(xpos);
|
|
pQAV->y = int(ypos);
|
|
int flags = 2;
|
|
int nInv = powerupCheck(pPlayer, kPwUpShadowCloak);
|
|
if (nInv >= 120 * 8 || (nInv != 0 && (PlayClock & 32)))
|
|
{
|
|
shade = -128;
|
|
flags |= 1;
|
|
}
|
|
pQAV->Draw(xpos, ypos, duration, flags, shade, palnum, true, smoothratio);
|
|
}
|
|
|
|
void WeaponPlay(PLAYER *pPlayer)
|
|
{
|
|
assert(pPlayer != NULL);
|
|
if (pPlayer->weaponQav == kQAVNone)
|
|
return;
|
|
auto pQAV = getQAV(pPlayer->weaponQav);
|
|
int nTicks = pQAV->duration - pPlayer->weaponTimer;
|
|
pQAV->Play(nTicks-4, nTicks, pPlayer->qavCallback, pPlayer);
|
|
}
|
|
|
|
static void StartQAV(PLAYER *pPlayer, int nWeaponQAV, int callback = -1, bool looped = false)
|
|
{
|
|
assert(nWeaponQAV < kQAVEnd);
|
|
auto res_id = qavGetCorrectID(nWeaponQAV);
|
|
auto pQAV = getQAV(res_id);
|
|
pPlayer->weaponQav = res_id;
|
|
pPlayer->weaponTimer = pQAV->duration;
|
|
pPlayer->qavCallback = callback;
|
|
pPlayer->qavLoop = looped;
|
|
pPlayer->qavLastTick = I_GetTime(pQAV->ticrate);
|
|
pPlayer->qavTimer = pQAV->duration;
|
|
//pQAV->Preload();
|
|
WeaponPlay(pPlayer);
|
|
pPlayer->weaponTimer -= 4;
|
|
}
|
|
|
|
static void SetQAV(PLAYER *pPlayer, int nWeaponQAV)
|
|
{
|
|
assert(nWeaponQAV < kQAVEnd);
|
|
pPlayer->weaponQav = qavGetCorrectID(nWeaponQAV);
|
|
pPlayer->qavTimer = 0;
|
|
pPlayer->qavLastTick = 0;
|
|
}
|
|
|
|
struct WEAPONTRACK
|
|
{
|
|
int aimSpeedHorz;
|
|
int aimSpeedVert;
|
|
int angleRange;
|
|
int thingAngle;
|
|
int seeker;
|
|
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);
|
|
spritetype *pPSprite = pPlayer->pSprite;
|
|
int x = pPSprite->x;
|
|
int y = pPSprite->y;
|
|
int z = pPlayer->zWeapon;
|
|
Aim aim;
|
|
aim.dx = bcos(pPSprite->ang);
|
|
aim.dy = bsin(pPSprite->ang);
|
|
aim.dz = pPlayer->slope;
|
|
WEAPONTRACK *pWeaponTrack = &gWeaponTrack[pPlayer->curWeapon];
|
|
DBloodActor* targetactor = nullptr;
|
|
pPlayer->aimTargetsCount = 0;
|
|
int autoaim = Autoaim(pPlayer->nPlayer);
|
|
if (autoaim == 1 || (autoaim == 2 && !pWeaponTrack->bIsProjectile) || pPlayer->curWeapon == kWeapVoodooDoll || pPlayer->curWeapon == kWeapLifeLeech)
|
|
{
|
|
int nClosest = 0x7fffffff;
|
|
BloodStatIterator it(kStatDude);
|
|
while (auto actor = it.Next())
|
|
{
|
|
pSprite = &actor->s();
|
|
if (pSprite == pPSprite)
|
|
continue;
|
|
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pSprite))
|
|
continue;
|
|
if (pSprite->flags&32)
|
|
continue;
|
|
if (!(pSprite->flags&8))
|
|
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)
|
|
{
|
|
int t = DivScale(nDist, pWeaponTrack->seeker, 12);
|
|
x2 += (actor->xvel * t) >> 12;
|
|
y2 += (actor->yvel * t) >> 12;
|
|
z2 += (actor->zvel * 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);
|
|
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)
|
|
continue;
|
|
if (pPlayer->aimTargetsCount < 16 && cansee(x,y,z,pPSprite->sector(),x2,y2,z2,pSprite->sector()))
|
|
pPlayer->aimTargets[pPlayer->aimTargetsCount++] = actor;
|
|
// 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);
|
|
int center = (pSprite->yrepeat*pDudeInfo->aimHeight)<<2;
|
|
int dzCenter = (z2-center)-z;
|
|
if (cansee(x, y, z, pPSprite->sector(), x2, y2, z2, pSprite->sector()))
|
|
{
|
|
nClosest = nDist2;
|
|
aim.dx = bcos(angle);
|
|
aim.dy = bsin(angle);
|
|
aim.dz = DivScale(dzCenter, nDist, 10);
|
|
targetactor = actor;
|
|
}
|
|
}
|
|
if (pWeaponTrack->thingAngle > 0)
|
|
{
|
|
BloodStatIterator it(kStatThing);
|
|
while (auto actor = it.Next())
|
|
{
|
|
pSprite = &actor->s();
|
|
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pSprite))
|
|
continue;
|
|
if (!(pSprite->flags&8))
|
|
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);
|
|
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)
|
|
continue;
|
|
if (pPlayer->aimTargetsCount < 16 && cansee(x,y,z,pPSprite->sector(),pSprite->x,pSprite->y,pSprite->z,pSprite->sector()))
|
|
pPlayer->aimTargets[pPlayer->aimTargetsCount++] = actor;
|
|
// 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->sector(), pSprite->x, pSprite->y, pSprite->z, pSprite->sector()))
|
|
{
|
|
nClosest = nDist2;
|
|
aim.dx = bcos(angle);
|
|
aim.dy = bsin(angle);
|
|
aim.dz = DivScale(dz, nDist, 10);
|
|
targetactor = actor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 = targetactor;
|
|
}
|
|
|
|
struct t_WeaponModes
|
|
{
|
|
int update;
|
|
int ammoType;
|
|
};
|
|
|
|
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;
|
|
pPlayer->weaponAmmo = weaponModes[pPlayer->curWeapon].ammoType;
|
|
switch (pPlayer->curWeapon)
|
|
{
|
|
case kWeapPitchFork:
|
|
pPlayer->weaponState = 0;
|
|
StartQAV(pPlayer, kQAVFORKUP);
|
|
break;
|
|
case kWeapSpraycan:
|
|
if (pPlayer->weaponState == 2)
|
|
{
|
|
pPlayer->weaponState = 3;
|
|
StartQAV(pPlayer, kQAVCANPREF);
|
|
}
|
|
else
|
|
{
|
|
pPlayer->weaponState = 0;
|
|
StartQAV(pPlayer, kQAVLITEOPEN);
|
|
}
|
|
break;
|
|
case kWeapDynamite:
|
|
if (gInfiniteAmmo || checkAmmo2(pPlayer, 5, 1))
|
|
{
|
|
pPlayer->weaponState = 3;
|
|
if (prevWeapon == kWeapSpraycan)
|
|
StartQAV(pPlayer, kQAVBUNUP);
|
|
else
|
|
StartQAV(pPlayer, kQAVBUNUP2);
|
|
}
|
|
break;
|
|
case kWeapProximity:
|
|
if (gInfiniteAmmo || checkAmmo2(pPlayer, 10, 1))
|
|
{
|
|
pPlayer->weaponState = 7;
|
|
StartQAV(pPlayer, kQAVPROXUP);
|
|
}
|
|
break;
|
|
case kWeapRemote:
|
|
if (gInfiniteAmmo || checkAmmo2(pPlayer, 11, 1))
|
|
{
|
|
pPlayer->weaponState = 10;
|
|
StartQAV(pPlayer, kQAVREMUP2);
|
|
}
|
|
else
|
|
{
|
|
StartQAV(pPlayer, kQAVREMUP3);
|
|
pPlayer->weaponState = 11;
|
|
}
|
|
break;
|
|
case kWeapShotgun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
{
|
|
if (gInfiniteAmmo || pPlayer->ammoCount[2] >= 4)
|
|
StartQAV(pPlayer, kQAV2SHOTUP);
|
|
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;
|
|
else
|
|
pPlayer->weaponState = 1;
|
|
}
|
|
else
|
|
{
|
|
if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1)
|
|
pPlayer->weaponState = 3;
|
|
else if (pPlayer->ammoCount[2] > 0)
|
|
pPlayer->weaponState = 2;
|
|
else
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVSHOTUP);
|
|
}
|
|
break;
|
|
case kWeapTommyGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
|
|
{
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAV2TOMUP);
|
|
}
|
|
else
|
|
{
|
|
pPlayer->weaponState = 0;
|
|
StartQAV(pPlayer, kQAVTOMUP);
|
|
}
|
|
break;
|
|
case kWeapVoodooDoll:
|
|
if (gInfiniteAmmo || checkAmmo2(pPlayer, 9, 1))
|
|
{
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVVDUP);
|
|
}
|
|
break;
|
|
case kWeapFlareGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
|
|
{
|
|
StartQAV(pPlayer, kQAVFLAR2UP);
|
|
pPlayer->weaponState = 3;
|
|
}
|
|
else
|
|
{
|
|
StartQAV(pPlayer, kQAVFLARUP);
|
|
pPlayer->weaponState = 2;
|
|
}
|
|
break;
|
|
case kWeapTeslaCannon:
|
|
if (checkAmmo2(pPlayer, 7, 1))
|
|
{
|
|
pPlayer->weaponState = 2;
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNUP);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNUP);
|
|
}
|
|
else
|
|
{
|
|
pPlayer->weaponState = 3;
|
|
StartQAV(pPlayer, kQAVSGUNUP);
|
|
}
|
|
break;
|
|
case kWeapNapalm:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
{
|
|
StartQAV(pPlayer, kQAV2NAPUP);
|
|
pPlayer->weaponState = 3;
|
|
}
|
|
else
|
|
{
|
|
StartQAV(pPlayer, kQAVNAPUP);
|
|
pPlayer->weaponState = 2;
|
|
}
|
|
break;
|
|
case kWeapLifeLeech:
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVSTAFUP);
|
|
break;
|
|
case kWeapBeast:
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVBSTUP);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WeaponLower(PLAYER *pPlayer)
|
|
{
|
|
assert(pPlayer != NULL);
|
|
if (checkFired6or7(pPlayer))
|
|
return;
|
|
pPlayer->throwPower = 0;
|
|
int prevState = pPlayer->weaponState;
|
|
switch (pPlayer->curWeapon)
|
|
{
|
|
case kWeapPitchFork:
|
|
StartQAV(pPlayer, kQAVFORKDOWN);
|
|
break;
|
|
case kWeapSpraycan:
|
|
sfxKill3DSound(pPlayer->actor, -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;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
pPlayer->weaponState = 1;
|
|
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);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
if (pPlayer->newWeapon == kWeapDynamite)
|
|
{
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVCANDOWN);
|
|
return;
|
|
}
|
|
else if (pPlayer->newWeapon == kWeapSpraycan)
|
|
{
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVCANDOWN);
|
|
pPlayer->newWeapon = kWeapNone;
|
|
WeaponLower(pPlayer);
|
|
}
|
|
else
|
|
{
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVCANDOWN);
|
|
}
|
|
break;
|
|
case 7: // throwing ignited alt fire spray
|
|
if (VanillaMode() || (pPlayer->newWeapon != 0))
|
|
break;
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVCANDOWN);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapDynamite:
|
|
switch (prevState)
|
|
{
|
|
case 1:
|
|
if (VanillaMode())
|
|
{
|
|
StartQAV(pPlayer, kQAVLITECLO2);
|
|
}
|
|
else
|
|
{
|
|
if (pPlayer->newWeapon == kWeapSpraycan) // do not put away lighter if TNT was selected while throwing a spray can
|
|
{
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVCANDOWN);
|
|
WeaponRaise(pPlayer);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
WeaponRaise(pPlayer);
|
|
break;
|
|
case 3:
|
|
if (pPlayer->newWeapon == kWeapSpraycan)
|
|
{
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVBUNDOWN);
|
|
}
|
|
else
|
|
{
|
|
StartQAV(pPlayer, kQAVBUNDOWN2);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapProximity:
|
|
switch (prevState)
|
|
{
|
|
case 7:
|
|
StartQAV(pPlayer, kQAVPROXDOWN);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapRemote:
|
|
switch (prevState)
|
|
{
|
|
case 10:
|
|
StartQAV(pPlayer, kQAVREMDOWN2);
|
|
break;
|
|
case 11:
|
|
StartQAV(pPlayer, kQAVREMDOWN3);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapShotgun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SHOTDWN);
|
|
else
|
|
StartQAV(pPlayer, kQAVSHOTDOWN);
|
|
break;
|
|
case kWeapTommyGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && pPlayer->weaponState == 1)
|
|
StartQAV(pPlayer, kQAV2TOMDOWN);
|
|
else
|
|
StartQAV(pPlayer, kQAVTOMDOWN);
|
|
break;
|
|
case kWeapFlareGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && pPlayer->weaponState == 3)
|
|
StartQAV(pPlayer, kQAVFLAR2DWN);
|
|
else
|
|
StartQAV(pPlayer, kQAVFLARDOWN);
|
|
break;
|
|
case kWeapVoodooDoll:
|
|
StartQAV(pPlayer, kQAVVDDOWN);
|
|
break;
|
|
case kWeapTeslaCannon:
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNDWN);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNDOWN);
|
|
break;
|
|
case kWeapNapalm:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2NAPDOWN);
|
|
else
|
|
StartQAV(pPlayer, kQAVNAPDOWN);
|
|
break;
|
|
case kWeapLifeLeech:
|
|
StartQAV(pPlayer, kQAVSTAFDOWN);
|
|
break;
|
|
case kWeapBeast:
|
|
StartQAV(pPlayer, kQAVBSTDOWN);
|
|
break;
|
|
}
|
|
pPlayer->curWeapon = kWeapNone;
|
|
pPlayer->qavLoop = 0;
|
|
}
|
|
|
|
void WeaponUpdateState(PLAYER *pPlayer)
|
|
{
|
|
static int lastWeapon = 0;
|
|
static int lastState = 0;
|
|
XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
int va = pPlayer->curWeapon;
|
|
int vb = pPlayer->weaponState;
|
|
if (va != lastWeapon || vb != lastState)
|
|
{
|
|
lastWeapon = va;
|
|
lastState = vb;
|
|
}
|
|
switch (lastWeapon)
|
|
{
|
|
case kWeapPitchFork:
|
|
SetQAV(pPlayer, kQAVFORKIDLE);
|
|
break;
|
|
case kWeapSpraycan:
|
|
switch (vb)
|
|
{
|
|
case 0:
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVLITEFLAM);
|
|
break;
|
|
case 1:
|
|
if (CheckAmmo(pPlayer, 6, 1))
|
|
{
|
|
pPlayer->weaponState = 3;
|
|
StartQAV(pPlayer, kQAVCANPREF);
|
|
}
|
|
else
|
|
SetQAV(pPlayer, kQAVLITEIDLE);
|
|
break;
|
|
case 3:
|
|
SetQAV(pPlayer, kQAVCANIDLE);
|
|
break;
|
|
case 4:
|
|
if (CheckAmmo(pPlayer, 6, 1))
|
|
{
|
|
SetQAV(pPlayer, kQAVCANIDLE);
|
|
pPlayer->weaponState = 3;
|
|
}
|
|
else
|
|
{
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVCANDOWN);
|
|
}
|
|
sfxKill3DSound(pPlayer->actor, -1, 441);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapDynamite:
|
|
switch (vb)
|
|
{
|
|
case 1:
|
|
if (pPlayer->weaponAmmo == 5 && CheckAmmo(pPlayer, 5, 1))
|
|
{
|
|
pPlayer->weaponState = 3;
|
|
StartQAV(pPlayer, kQAVBUNUP);
|
|
}
|
|
break;
|
|
case 0:
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVLITEFLAM);
|
|
break;
|
|
case 2:
|
|
if (pPlayer->ammoCount[5] > 0)
|
|
{
|
|
pPlayer->weaponState = 3;
|
|
StartQAV(pPlayer, kQAVBUNUP);
|
|
}
|
|
else
|
|
SetQAV(pPlayer, kQAVLITEIDLE);
|
|
break;
|
|
case 3:
|
|
SetQAV(pPlayer, kQAVBUNIDLE);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapProximity:
|
|
switch (vb)
|
|
{
|
|
case 7:
|
|
SetQAV(pPlayer, kQAVPROXIDLE);
|
|
break;
|
|
case 8:
|
|
pPlayer->weaponState = 7;
|
|
StartQAV(pPlayer, kQAVPROXUP);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapRemote:
|
|
switch (vb)
|
|
{
|
|
case 10:
|
|
SetQAV(pPlayer, kQAVREMIDLE1);
|
|
break;
|
|
case 11:
|
|
SetQAV(pPlayer, kQAVREMIDLE2);
|
|
break;
|
|
case 12:
|
|
if (pPlayer->ammoCount[11] > 0)
|
|
{
|
|
pPlayer->weaponState = 10;
|
|
StartQAV(pPlayer, kQAVREMUP2);
|
|
}
|
|
else
|
|
pPlayer->weaponState = -1;
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapShotgun:
|
|
switch (vb)
|
|
{
|
|
case 6:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer, 2, 4)))
|
|
pPlayer->weaponState = 7;
|
|
else
|
|
pPlayer->weaponState = 1;
|
|
break;
|
|
case 7:
|
|
SetQAV(pPlayer, kQAV2SHOTI);
|
|
break;
|
|
case 1:
|
|
if (CheckAmmo(pPlayer, 2, 1))
|
|
{
|
|
sfxPlay3DSound(pPlayer->actor, 410, 3, 2);
|
|
StartQAV(pPlayer, kQAVSHOTL1, nClientEjectShell);
|
|
if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1)
|
|
pPlayer->weaponState = 3;
|
|
else
|
|
pPlayer->weaponState = 2;
|
|
}
|
|
else
|
|
SetQAV(pPlayer, kQAVSHOTI3);
|
|
break;
|
|
case 2:
|
|
SetQAV(pPlayer, kQAVSHOTI2);
|
|
break;
|
|
case 3:
|
|
SetQAV(pPlayer, kQAVSHOTI1);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapTommyGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
|
|
{
|
|
SetQAV(pPlayer, kQAV2TOMIDLE);
|
|
pPlayer->weaponState = 1;
|
|
}
|
|
else
|
|
{
|
|
SetQAV(pPlayer, kQAVTOMIDLE);
|
|
pPlayer->weaponState = 0;
|
|
}
|
|
break;
|
|
case kWeapFlareGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
{
|
|
if (vb == 3 && checkAmmo2(pPlayer, 1, 2))
|
|
SetQAV(pPlayer, kQAVFLAR2I);
|
|
else
|
|
{
|
|
SetQAV(pPlayer, kQAVFLARIDLE);
|
|
pPlayer->weaponState = 2;
|
|
}
|
|
}
|
|
else
|
|
SetQAV(pPlayer, kQAVFLARIDLE);
|
|
break;
|
|
case kWeapVoodooDoll:
|
|
if (pXSprite->height < 256 && pPlayer->swayHeight != 0)
|
|
StartQAV(pPlayer, kQAVVDIDLE2);
|
|
else
|
|
SetQAV(pPlayer, kQAVVDIDLE1);
|
|
break;
|
|
case kWeapTeslaCannon:
|
|
switch (vb)
|
|
{
|
|
case 2:
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
SetQAV(pPlayer, kQAV2SGUNIDL);
|
|
else
|
|
SetQAV(pPlayer, kQAVSGUNIDL1);
|
|
break;
|
|
case 3:
|
|
SetQAV(pPlayer, kQAVSGUNIDL2);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapNapalm:
|
|
switch (vb)
|
|
{
|
|
case 3:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer,4, 4)))
|
|
SetQAV(pPlayer, kQAV2NAPIDLE);
|
|
else
|
|
SetQAV(pPlayer, kQAVNAPIDLE);
|
|
break;
|
|
case 2:
|
|
SetQAV(pPlayer, kQAVNAPIDLE);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapLifeLeech:
|
|
switch (vb)
|
|
{
|
|
case 2:
|
|
SetQAV(pPlayer, kQAVSTAFIDL1);
|
|
break;
|
|
}
|
|
break;
|
|
case kWeapBeast:
|
|
SetQAV(pPlayer, kQAVBSTIDLE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FirePitchfork(int, PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
Aim *aim = &pPlayer->aim;
|
|
int r1 = Random2(2000);
|
|
int r2 = Random2(2000);
|
|
int r3 = Random2(2000);
|
|
for (int i = 0; i < 4; i++)
|
|
actFireVector(actor, (2*i-3)*40, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r1, aim->dy+r2, aim->dz+r3, kVectorTine);
|
|
}
|
|
|
|
void FireSpray(int, PLAYER *pPlayer)
|
|
{
|
|
playerFireMissile(pPlayer, 0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFlameSpray);
|
|
UseAmmo(pPlayer, 6, 4);
|
|
if (CheckAmmo(pPlayer, 6, 1))
|
|
sfxPlay3DSound(pPlayer->actor, 441, 1, 2);
|
|
else
|
|
sfxKill3DSound(pPlayer->actor, -1, 441);
|
|
}
|
|
|
|
void ThrowCan(int, PLAYER *pPlayer)
|
|
{
|
|
sfxKill3DSound(pPlayer->actor, -1, 441);
|
|
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
|
|
sfxPlay3DSound(pPlayer->actor, 455, 1, 0);
|
|
auto spawned = playerFireThing(pPlayer, 0, -9460, kThingArmedSpray, nSpeed);
|
|
if (spawned)
|
|
{
|
|
sfxPlay3DSound(spawned, 441, 0, 0);
|
|
evPostActor(spawned, pPlayer->fuseTime, kCmdOn);
|
|
spawned->x().Impact = 1;
|
|
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
|
|
pPlayer->throwPower = 0;
|
|
}
|
|
}
|
|
|
|
void DropCan(int, PLAYER *pPlayer)
|
|
{
|
|
sfxKill3DSound(pPlayer->actor, -1, 441);
|
|
auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
|
|
if (spawned)
|
|
{
|
|
evPostActor(spawned, pPlayer->fuseTime, kCmdOn);
|
|
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
|
|
}
|
|
}
|
|
|
|
void ExplodeCan(int, PLAYER *pPlayer)
|
|
{
|
|
sfxKill3DSound(pPlayer->actor, -1, 441);
|
|
auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
|
|
if (spawned)
|
|
{
|
|
evPostActor(spawned, 0, kCmdOn);
|
|
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
|
|
StartQAV(pPlayer, kQAVCANBOOM);
|
|
pPlayer->curWeapon = kWeapNone;
|
|
pPlayer->throwPower = 0;
|
|
}
|
|
}
|
|
|
|
void ThrowBundle(int, PLAYER *pPlayer)
|
|
{
|
|
sfxKill3DSound(pPlayer->actor, 16, -1);
|
|
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
|
|
sfxPlay3DSound(pPlayer->actor, 455, 1, 0);
|
|
auto spawned = playerFireThing(pPlayer, 0, -9460, kThingArmedTNTBundle, nSpeed);
|
|
if (spawned)
|
|
{
|
|
if (pPlayer->fuseTime < 0)
|
|
spawned->x().Impact = 1;
|
|
else
|
|
evPostActor(spawned, pPlayer->fuseTime, kCmdOn);
|
|
UseAmmo(pPlayer, 5, 1);
|
|
pPlayer->throwPower = 0;
|
|
}
|
|
}
|
|
|
|
void DropBundle(int, PLAYER *pPlayer)
|
|
{
|
|
sfxKill3DSound(pPlayer->actor, 16, -1);
|
|
auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
|
|
if (spawned)
|
|
{
|
|
evPostActor(spawned, pPlayer->fuseTime, kCmdOn);
|
|
UseAmmo(pPlayer, 5, 1);
|
|
}
|
|
}
|
|
|
|
void ExplodeBundle(int, PLAYER *pPlayer)
|
|
{
|
|
sfxKill3DSound(pPlayer->actor, 16, -1);
|
|
auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
|
|
if (spawned)
|
|
{
|
|
evPostActor(spawned, 0, kCmdOn);
|
|
UseAmmo(pPlayer, 5, 1);
|
|
StartQAV(pPlayer, kQAVDYNEXPLO);
|
|
pPlayer->curWeapon = kWeapNone;
|
|
pPlayer->throwPower = 0;
|
|
}
|
|
}
|
|
|
|
void ThrowProx(int, PLAYER *pPlayer)
|
|
{
|
|
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
|
|
sfxPlay3DSound(pPlayer->actor, 455, 1, 0);
|
|
auto spawned = playerFireThing(pPlayer, 0, -9460, kThingArmedProxBomb, nSpeed);
|
|
if (spawned)
|
|
{
|
|
evPostActor(spawned, 240, kCmdOn);
|
|
UseAmmo(pPlayer, 10, 1);
|
|
pPlayer->throwPower = 0;
|
|
}
|
|
}
|
|
|
|
void DropProx(int, PLAYER *pPlayer)
|
|
{
|
|
auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedProxBomb, 0);
|
|
if (spawned)
|
|
{
|
|
evPostActor(spawned, 240, kCmdOn);
|
|
UseAmmo(pPlayer, 10, 1);
|
|
}
|
|
}
|
|
|
|
void ThrowRemote(int, PLAYER *pPlayer)
|
|
{
|
|
int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
|
|
sfxPlay3DSound(pPlayer->actor, 455, 1, 0);
|
|
auto spawned = playerFireThing(pPlayer, 0, -9460, kThingArmedRemoteBomb, nSpeed);
|
|
if (spawned)
|
|
{
|
|
spawned->x().rxID = 90 + (pPlayer->pSprite->type - kDudePlayer1);
|
|
UseAmmo(pPlayer, 11, 1);
|
|
pPlayer->throwPower = 0;
|
|
}
|
|
}
|
|
|
|
void DropRemote(int, PLAYER *pPlayer)
|
|
{
|
|
auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedRemoteBomb, 0);
|
|
if (spawned)
|
|
{
|
|
spawned->x().rxID = 90 + (pPlayer->pSprite->type - kDudePlayer1);
|
|
UseAmmo(pPlayer, 11, 1);
|
|
}
|
|
}
|
|
|
|
void FireRemote(int, PLAYER *pPlayer)
|
|
{
|
|
evSendGame(90+(pPlayer->pSprite->type-kDudePlayer1), kCmdOn);
|
|
}
|
|
|
|
enum { kMaxShotgunBarrels = 4 };
|
|
|
|
void FireShotgun(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
assert(nTrigger > 0 && nTrigger <= kMaxShotgunBarrels);
|
|
if (nTrigger == 1)
|
|
{
|
|
sfxPlay3DSound(pPlayer->actor, 411, 2, 0);
|
|
pPlayer->tiltEffect = 30;
|
|
pPlayer->visibility = 20;
|
|
}
|
|
else
|
|
{
|
|
sfxPlay3DSound(pPlayer->actor, 412, 2, 0);
|
|
pPlayer->tiltEffect = 50;
|
|
pPlayer->visibility = 40;
|
|
}
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
r1 = Random3(2500);
|
|
r2 = Random3(2500);
|
|
r3 = Random3(1500);
|
|
nType = kVectorShellAP;
|
|
}
|
|
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, nType);
|
|
}
|
|
UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger);
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
void EjectShell(int, PLAYER *pPlayer)
|
|
{
|
|
SpawnShellEject(pPlayer, 25, 35);
|
|
SpawnShellEject(pPlayer, 48, 35);
|
|
}
|
|
|
|
void FireTommy(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
Aim *aim = &pPlayer->aim;
|
|
sfxPlay3DSound(pPlayer->actor, 431, -1, 0);
|
|
switch (nTrigger)
|
|
{
|
|
case 1:
|
|
{
|
|
int r1 = Random3(400);
|
|
int r2 = Random3(1200);
|
|
int r3 = Random3(1200);
|
|
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyRegular);
|
|
SpawnBulletEject(pPlayer, -15, -45);
|
|
pPlayer->visibility = 20;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
int r1 = Random3(400);
|
|
int r2 = Random3(1200);
|
|
int r3 = Random3(1200);
|
|
actFireVector(actor, -120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyRegular);
|
|
SpawnBulletEject(pPlayer, -140, -45);
|
|
r1 = Random3(400);
|
|
r2 = Random3(1200);
|
|
r3 = Random3(1200);
|
|
actFireVector(actor, 120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyRegular);
|
|
SpawnBulletEject(pPlayer, 140, 45);
|
|
pPlayer->visibility = 30;
|
|
break;
|
|
}
|
|
}
|
|
UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger);
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
enum { kMaxSpread = 14 };
|
|
|
|
void FireSpread(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
assert(nTrigger > 0 && nTrigger <= kMaxSpread);
|
|
Aim *aim = &pPlayer->aim;
|
|
int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
|
|
int dx = bcos(angle);
|
|
int dy = bsin(angle);
|
|
sfxPlay3DSound(pPlayer->actor, 431, -1, 0);
|
|
int r1, r2, r3;
|
|
r1 = Random3(300);
|
|
r2 = Random3(600);
|
|
r3 = Random3(600);
|
|
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
|
|
r1 = Random2(90);
|
|
r2 = Random2(30);
|
|
SpawnBulletEject(pPlayer, r2, r1);
|
|
pPlayer->visibility = 20;
|
|
UseAmmo(pPlayer, pPlayer->weaponAmmo, 1);
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
void AltFireSpread(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
assert(nTrigger > 0 && nTrigger <= kMaxSpread);
|
|
Aim *aim = &pPlayer->aim;
|
|
int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
|
|
int dx = bcos(angle);
|
|
int dy = bsin(angle);
|
|
sfxPlay3DSound(pPlayer->actor, 431, -1, 0);
|
|
int r1, r2, r3;
|
|
r1 = Random3(300);
|
|
r2 = Random3(600);
|
|
r3 = Random3(600);
|
|
actFireVector(actor, -120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
|
|
r1 = Random2(45);
|
|
r2 = Random2(120);
|
|
SpawnBulletEject(pPlayer, r2, r1);
|
|
r1 = Random3(300);
|
|
r2 = Random3(600);
|
|
r3 = Random3(600);
|
|
actFireVector(actor, 120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
|
|
r1 = Random2(-45);
|
|
r2 = Random2(-120);
|
|
SpawnBulletEject(pPlayer, r2, r1);
|
|
pPlayer->tiltEffect = 20;
|
|
pPlayer->visibility = 30;
|
|
UseAmmo(pPlayer, pPlayer->weaponAmmo, 2);
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
void AltFireSpread2(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
assert(nTrigger > 0 && nTrigger <= kMaxSpread);
|
|
Aim *aim = &pPlayer->aim;
|
|
int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
|
|
int dx = bcos(angle);
|
|
int dy = bsin(angle);
|
|
sfxPlay3DSound(pPlayer->actor, 431, -1, 0);
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
|
|
{
|
|
int r1, r2, r3;
|
|
r1 = Random3(300);
|
|
r2 = Random3(600);
|
|
r3 = Random3(600);
|
|
actFireVector(actor, -120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
|
|
r1 = Random2(45);
|
|
r2 = Random2(120);
|
|
SpawnBulletEject(pPlayer, r2, r1);
|
|
r1 = Random3(300);
|
|
r2 = Random3(600);
|
|
r3 = Random3(600);
|
|
actFireVector(actor, 120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
|
|
r1 = Random2(-45);
|
|
r2 = Random2(-120);
|
|
SpawnBulletEject(pPlayer, r2, r1);
|
|
pPlayer->tiltEffect = 30;
|
|
pPlayer->visibility = 45;
|
|
UseAmmo(pPlayer, pPlayer->weaponAmmo, 2);
|
|
}
|
|
else
|
|
{
|
|
int r1, r2, r3;
|
|
r1 = Random3(300);
|
|
r2 = Random3(600);
|
|
r3 = Random3(600);
|
|
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
|
|
r1 = Random2(90);
|
|
r2 = Random2(30);
|
|
SpawnBulletEject(pPlayer, r2, r1);
|
|
pPlayer->tiltEffect = 20;
|
|
pPlayer->visibility = 30;
|
|
UseAmmo(pPlayer, pPlayer->weaponAmmo, 1);
|
|
}
|
|
pPlayer->flashEffect = 1;
|
|
if (!checkAmmo2(pPlayer, 3, 1))
|
|
{
|
|
WeaponLower(pPlayer);
|
|
pPlayer->weaponState = -1;
|
|
}
|
|
}
|
|
|
|
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);
|
|
UseAmmo(pPlayer, 1, 1);
|
|
sfxPlay3DSound(pPlayer->actor, 420, 2, 0);
|
|
pPlayer->visibility = 30;
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
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);
|
|
UseAmmo(pPlayer, 1, 8);
|
|
sfxPlay3DSound(pPlayer->actor, 420, 2, 0);
|
|
pPlayer->visibility = 45;
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
void FireVoodoo(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
nTrigger--;
|
|
DBloodActor* actor = pPlayer->actor;
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
if (nTrigger == 4)
|
|
{
|
|
actDamageSprite(actor, actor, kDamageBullet, 1<<4);
|
|
return;
|
|
}
|
|
DBloodActor* targetactor = pPlayer->voodooTarget;
|
|
if (!targetactor) return;
|
|
spritetype *pTarget = &targetactor->s();
|
|
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
|
|
return;
|
|
switch (nTrigger)
|
|
{
|
|
case 0:
|
|
{
|
|
sfxPlay3DSound(actor, 460, 2, 0);
|
|
fxSpawnBlood(targetactor, 17<<4);
|
|
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 17<<4);
|
|
UseAmmo(pPlayer, 9, nDamage/4);
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
sfxPlay3DSound(actor, 460, 2, 0);
|
|
fxSpawnBlood(targetactor, 17<<4);
|
|
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 9<<4);
|
|
if (IsPlayerSprite(pTarget))
|
|
WeaponLower(&gPlayer[pTarget->type-kDudePlayer1]);
|
|
UseAmmo(pPlayer, 9, nDamage/4);
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
sfxPlay3DSound(actor, 463, 2, 0);
|
|
fxSpawnBlood(targetactor, 17<<4);
|
|
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 49<<4);
|
|
UseAmmo(pPlayer, 9, nDamage/4);
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
sfxPlay3DSound(actor, 460, 2, 0);
|
|
fxSpawnBlood(targetactor, 17<<4);
|
|
int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 11<<4);
|
|
if (IsPlayerSprite(pTarget))
|
|
{
|
|
PLAYER *pOtherPlayer = &gPlayer[pTarget->type - kDudePlayer1];
|
|
pOtherPlayer->blindEffect = 128;
|
|
}
|
|
UseAmmo(pPlayer, 9, nDamage/4);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AltFireVoodoo(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* 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++)
|
|
{
|
|
DBloodActor* targetactor = pPlayer->aimTargets[i];
|
|
if (!targetactor) continue;
|
|
spritetype* pTarget = &targetactor->s();
|
|
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(targetactor, 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++)
|
|
{
|
|
DBloodActor* targetactor = pPlayer->aimTargets[i];
|
|
if (!targetactor) continue;
|
|
spritetype* pTarget = &targetactor->s();
|
|
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)
|
|
{
|
|
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(targetactor, 0);
|
|
}
|
|
}
|
|
}
|
|
UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]);
|
|
pPlayer->hasWeapon[10] = 0;
|
|
pPlayer->weaponState = -1;
|
|
}
|
|
}
|
|
|
|
void DropVoodoo(int , PLAYER *pPlayer)
|
|
{
|
|
sfxPlay3DSound(pPlayer->actor, 455, 2, 0);
|
|
auto spawned = playerFireThing(pPlayer, 0, -4730, kThingVoodooHead, 0xccccc);
|
|
if (spawned)
|
|
{
|
|
spawned->x().data1 = pPlayer->ammoCount[9];
|
|
evPostActor(spawned, 90, kCallbackDropVoodoo);
|
|
UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
|
|
UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]);
|
|
pPlayer->hasWeapon[10] = 0;
|
|
}
|
|
}
|
|
|
|
struct TeslaMissile
|
|
{
|
|
int offset; // offset
|
|
int id; // id
|
|
int ammouse; // ammo use
|
|
int sound; // sound
|
|
int light; // light
|
|
int flash; // weapon flash
|
|
};
|
|
|
|
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];
|
|
if (!checkAmmo2(pPlayer, 7, pMissile->ammouse))
|
|
{
|
|
pMissile = &teslaMissile[0];
|
|
if (!checkAmmo2(pPlayer, 7, pMissile->ammouse))
|
|
{
|
|
pPlayer->weaponState = -1;
|
|
SetQAV(pPlayer, kQAVSGUNIDL2);
|
|
pPlayer->flashEffect = 0;
|
|
return;
|
|
}
|
|
}
|
|
playerFireMissile(pPlayer, pMissile->offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, pMissile->id);
|
|
UseAmmo(pPlayer, 7, pMissile->ammouse);
|
|
sfxPlay3DSound(pPlayer->actor, pMissile->sound, 1, 0);
|
|
pPlayer->visibility = pMissile->light;
|
|
pPlayer->flashEffect = pMissile->flash;
|
|
}
|
|
}
|
|
|
|
void AltFireTesla(int , PLAYER *pPlayer)
|
|
{
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
playerFireMissile(pPlayer, 0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileTeslaAlt);
|
|
UseAmmo(pPlayer, pPlayer->weaponAmmo, 35);
|
|
sfxPlay3DSound(pPlayer->actor, 471, 2, 0);
|
|
pPlayer->visibility = 40;
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
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);
|
|
sfxPlay3DSound(pPlayer->actor, 480, 2, 0);
|
|
UseAmmo(pPlayer, 4, 1);
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
void FireNapalm2(int , PLAYER *pPlayer)
|
|
{
|
|
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);
|
|
sfxPlay3DSound(pPlayer->actor, 480, 2, 0);
|
|
UseAmmo(pPlayer, 4, 2);
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
|
|
void AltFireNapalm(int , PLAYER *pPlayer)
|
|
{
|
|
int nSpeed = MulScale(0x8000, 0x177777, 16)+0x66666;
|
|
auto missile = playerFireThing(pPlayer, 0, -4730, kThingNapalmBall, nSpeed);
|
|
if (missile)
|
|
{
|
|
XSPRITE *pXSprite = &missile->x();
|
|
pXSprite->data4 = ClipHigh(pPlayer->ammoCount[4], 12);
|
|
UseAmmo(pPlayer, 4, pXSprite->data4);
|
|
seqSpawn(22, missile, -1);
|
|
actBurnSprite(pPlayer->actor, missile, 600);
|
|
evPostActor(missile, 0, kCallbackFXFlameLick);
|
|
sfxPlay3DSound(missile, 480, 2, 0);
|
|
pPlayer->visibility = 30;
|
|
pPlayer->flashEffect = 1;
|
|
}
|
|
}
|
|
|
|
void FireLifeLeech(int nTrigger, PLAYER *pPlayer)
|
|
{
|
|
if (!CheckAmmo(pPlayer, 8, 1))
|
|
return;
|
|
int r1 = Random2(2000);
|
|
int r2 = Random2(2000);
|
|
int r3 = Random2(1000);
|
|
DBloodActor* actor = pPlayer->actor;
|
|
auto missileActor = playerFireMissile(pPlayer, 0, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, 315);
|
|
if (missileActor)
|
|
{
|
|
missileActor->SetTarget(pPlayer->aimTarget);
|
|
missileActor->s().ang = (nTrigger==2) ? 1024 : 0;
|
|
}
|
|
if (checkAmmo2(pPlayer, 8, 1))
|
|
UseAmmo(pPlayer, 8, 1);
|
|
else
|
|
actDamageSprite(actor, actor, kDamageSpirit, 16);
|
|
pPlayer->visibility = ClipHigh(pPlayer->visibility+5, 50);
|
|
}
|
|
|
|
void AltFireLifeLeech(int , PLAYER *pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
sfxPlay3DSound(pPlayer->actor, 455, 2, 0);
|
|
auto missile = playerFireThing(pPlayer, 0, -4730, kThingDroppedLifeLeech, 0x19999);
|
|
if (missile)
|
|
{
|
|
auto pMissile = &missile->s();
|
|
pMissile->cstat |= 4096;
|
|
XSPRITE *pXSprite = &missile->x();
|
|
pXSprite->Push = 1;
|
|
pXSprite->Proximity = 1;
|
|
pXSprite->DudeLockout = 1;
|
|
pXSprite->data4 = ClipHigh(pPlayer->ammoCount[4], 12);
|
|
pXSprite->stateTimer = 1;
|
|
evPostActor(missile, 120, kCallbackLeechStateTimer);
|
|
if (gGameOptions.nGameType <= 1)
|
|
{
|
|
int nAmmo = pPlayer->ammoCount[8];
|
|
if (nAmmo < 25 && pPlayer->pXSprite->health > unsigned((25-nAmmo)<<4))
|
|
{
|
|
actDamageSprite(actor, actor, kDamageSpirit, ((25-nAmmo)<<4));
|
|
nAmmo = 25;
|
|
}
|
|
pXSprite->data3 = nAmmo;
|
|
UseAmmo(pPlayer, 8, nAmmo);
|
|
}
|
|
else
|
|
{
|
|
pXSprite->data3 = pPlayer->ammoCount[8];
|
|
pPlayer->ammoCount[8] = 0;
|
|
}
|
|
pPlayer->hasWeapon[9] = 0;
|
|
}
|
|
}
|
|
|
|
void FireBeast(int , PLAYER * pPlayer)
|
|
{
|
|
DBloodActor* actor = pPlayer->actor;
|
|
int r1 = Random2(2000);
|
|
int r2 = Random2(2000);
|
|
int r3 = Random2(2000);
|
|
actFireVector(actor, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, kVectorBeastSlash);
|
|
}
|
|
|
|
uint8_t gWeaponUpgrade[][13] = {
|
|
{ 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)
|
|
{
|
|
int weapon = pPlayer->curWeapon;
|
|
if (!checkFired6or7(pPlayer) && (cl_weaponswitch&1) && (gWeaponUpgrade[pPlayer->curWeapon][newWeapon] || (cl_weaponswitch&2)))
|
|
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(const PLAYER *pPlayer, int *a2, int bDir)
|
|
{
|
|
int weapon = pPlayer->curWeapon;
|
|
do
|
|
{
|
|
if (bDir)
|
|
weapon = OrderNext[weapon];
|
|
else
|
|
weapon = OrderPrev[weapon];
|
|
if (weaponModes[weapon].update && pPlayer->hasWeapon[weapon])
|
|
{
|
|
if (weapon == kWeapLifeLeech)
|
|
{
|
|
if (CheckAmmo(pPlayer, weaponModes[weapon].ammoType, 1))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (checkAmmo2(pPlayer, weaponModes[weapon].ammoType, 1))
|
|
break;
|
|
}
|
|
}
|
|
} while (weapon != pPlayer->curWeapon);
|
|
if (weapon == pPlayer->curWeapon)
|
|
{
|
|
if (!weaponModes[weapon].update || !CheckAmmo(pPlayer, weaponModes[weapon].ammoType, 1))
|
|
weapon = kWeapPitchFork;
|
|
}
|
|
if (a2)
|
|
*a2 = 0;
|
|
return weapon;
|
|
}
|
|
|
|
static int WeaponFindLoaded(const PLAYER *pPlayer, int *a2)
|
|
{
|
|
int v4 = 1;
|
|
int v14 = 0;
|
|
if (weaponModes[pPlayer->curWeapon].update > 1)
|
|
{
|
|
for (int i = 0; i < weaponModes[pPlayer->curWeapon].update; i++)
|
|
{
|
|
if (CheckAmmo(pPlayer, weaponModes[pPlayer->curWeapon].ammoType, 1))
|
|
{
|
|
v14 = i;
|
|
v4 = pPlayer->curWeapon;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (v4 == kWeapPitchFork)
|
|
{
|
|
int vc = 0;
|
|
for (int i = 0; i < 14; i++)
|
|
{
|
|
int weapon = pPlayer->weaponOrder[vc][i];
|
|
if (pPlayer->hasWeapon[weapon])
|
|
{
|
|
for (int j = 0; j < weaponModes[weapon].update; j++)
|
|
{
|
|
if (CheckWeaponAmmo(pPlayer, weapon, weaponModes[weapon].ammoType, 1))
|
|
{
|
|
if (a2)
|
|
*a2 = j;
|
|
return weapon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (a2)
|
|
*a2 = v14;
|
|
return v4;
|
|
}
|
|
|
|
int processSprayCan(PLAYER *pPlayer)
|
|
{
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 5:
|
|
if (!(pPlayer->input.actions & SB_ALTFIRE))
|
|
pPlayer->weaponState = 6;
|
|
return 1;
|
|
case 6:
|
|
if (pPlayer->input.actions & SB_ALTFIRE)
|
|
{
|
|
pPlayer->weaponState = 3;
|
|
pPlayer->fuseTime = pPlayer->weaponTimer;
|
|
StartQAV(pPlayer, kQAVCANDROP, nClientDropCan);
|
|
}
|
|
else if (pPlayer->input.actions & SB_FIRE)
|
|
{
|
|
pPlayer->weaponState = 7;
|
|
pPlayer->fuseTime = 0;
|
|
pPlayer->throwTime = PlayClock;
|
|
}
|
|
return 1;
|
|
case 7:
|
|
{
|
|
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
|
|
if (!(pPlayer->input.actions & SB_FIRE))
|
|
{
|
|
if (!pPlayer->fuseTime)
|
|
pPlayer->fuseTime = pPlayer->weaponTimer;
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVCANTHRO, nClientThrowCan);
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool processTNT(PLAYER *pPlayer)
|
|
{
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 4:
|
|
if (!(pPlayer->input.actions & SB_ALTFIRE))
|
|
pPlayer->weaponState = 5;
|
|
return 1;
|
|
case 5:
|
|
if (pPlayer->input.actions & SB_ALTFIRE)
|
|
{
|
|
pPlayer->weaponState = 1;
|
|
pPlayer->fuseTime = pPlayer->weaponTimer;
|
|
StartQAV(pPlayer, kQAVBUNDROP, nClientDropBundle);
|
|
}
|
|
else if (pPlayer->input.actions & SB_FIRE)
|
|
{
|
|
pPlayer->weaponState = 6;
|
|
pPlayer->fuseTime = 0;
|
|
pPlayer->throwTime = PlayClock;
|
|
}
|
|
return 1;
|
|
case 6:
|
|
{
|
|
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
|
|
if (!(pPlayer->input.actions & SB_FIRE))
|
|
{
|
|
if (!pPlayer->fuseTime)
|
|
pPlayer->fuseTime = pPlayer->weaponTimer;
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVBUNTHRO, nClientThrowBundle);
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool processProxy(PLAYER *pPlayer)
|
|
{
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 9:
|
|
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
|
|
pPlayer->weaponTimer = 0;
|
|
pPlayer->qavTimer = 0;
|
|
if (!(pPlayer->input.actions & SB_FIRE))
|
|
{
|
|
pPlayer->weaponState = 8;
|
|
StartQAV(pPlayer, kQAVPROXTHRO, nClientThrowProx);
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool processRemote(PLAYER *pPlayer)
|
|
{
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 13:
|
|
pPlayer->throwPower = ClipHigh(DivScale(PlayClock-pPlayer->throwTime,240, 16), 65536);
|
|
if (!(pPlayer->input.actions & SB_FIRE))
|
|
{
|
|
pPlayer->weaponState = 11;
|
|
StartQAV(pPlayer, kQAVREMTHRO, nClientThrowRemote);
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool processLeech(PLAYER *pPlayer)
|
|
{
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 4:
|
|
pPlayer->weaponState = 6;
|
|
StartQAV(pPlayer, kQAVSTAFIRE1, nClientFireLifeLeech, 1);
|
|
return 1;
|
|
case 6:
|
|
if (!(pPlayer->input.actions & SB_ALTFIRE))
|
|
{
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVSTAFPOST);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 8:
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVSTAFPOST);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool processTesla(PLAYER *pPlayer)
|
|
{
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 4:
|
|
pPlayer->weaponState = 5;
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla, 1);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla, 1);
|
|
return 1;
|
|
case 5:
|
|
if (!(pPlayer->input.actions & SB_FIRE))
|
|
{
|
|
pPlayer->weaponState = 2;
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNPST);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNPOST);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 7:
|
|
pPlayer->weaponState = 2;
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNPST);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNPOST);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void WeaponProcess(PLAYER *pPlayer) {
|
|
|
|
pPlayer->flashEffect = ClipLow(pPlayer->flashEffect - 1, 0);
|
|
|
|
#ifdef NOONE_EXTENSIONS
|
|
if (gPlayerCtrl[pPlayer->nPlayer].qavScene.initiator != nullptr && pPlayer->pXSprite->health > 0) {
|
|
playerQavSceneProcess(pPlayer, &gPlayerCtrl[pPlayer->nPlayer].qavScene);
|
|
UpdateAimVector(pPlayer);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (pPlayer->pXSprite->health == 0)
|
|
{
|
|
pPlayer->qavLoop = 0;
|
|
sfxKill3DSound(pPlayer->actor, 1, -1);
|
|
}
|
|
if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->curWeapon))
|
|
{
|
|
if (checkFired6or7(pPlayer))
|
|
{
|
|
if (pPlayer->curWeapon == kWeapSpraycan)
|
|
{
|
|
pPlayer->fuseTime = pPlayer->weaponTimer;
|
|
DropCan(1, pPlayer);
|
|
pPlayer->weaponState = 3;
|
|
}
|
|
else if (pPlayer->curWeapon == kWeapDynamite)
|
|
{
|
|
pPlayer->fuseTime = pPlayer->weaponTimer;
|
|
DropBundle(1, pPlayer);
|
|
pPlayer->weaponState = 1;
|
|
}
|
|
}
|
|
WeaponLower(pPlayer);
|
|
pPlayer->throwPower = 0;
|
|
}
|
|
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)
|
|
{
|
|
if (bShoot && CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1))
|
|
{
|
|
auto pQAV = getQAV(pPlayer->weaponQav);
|
|
while (pPlayer->weaponTimer <= 0)
|
|
{
|
|
pPlayer->weaponTimer += pQAV->duration;
|
|
pPlayer->qavTimer += pQAV->duration;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayer->weaponTimer = 0;
|
|
pPlayer->qavTimer = 0;
|
|
pPlayer->qavLoop = 0;
|
|
}
|
|
return;
|
|
}
|
|
pPlayer->weaponTimer = ClipLow(pPlayer->weaponTimer, 0);
|
|
switch (pPlayer->curWeapon)
|
|
{
|
|
case kWeapSpraycan:
|
|
if (processSprayCan(pPlayer))
|
|
return;
|
|
break;
|
|
case kWeapDynamite:
|
|
if (processTNT(pPlayer))
|
|
return;
|
|
break;
|
|
case kWeapProximity:
|
|
if (processProxy(pPlayer))
|
|
return;
|
|
break;
|
|
case kWeapRemote:
|
|
if (processRemote(pPlayer))
|
|
return;
|
|
break;
|
|
}
|
|
if (pPlayer->weaponTimer > 0)
|
|
return;
|
|
if (pPlayer->pXSprite->health == 0 || pPlayer->curWeapon == kWeapNone)
|
|
pPlayer->weaponQav = kQAVNone;
|
|
switch (pPlayer->curWeapon)
|
|
{
|
|
case kWeapLifeLeech:
|
|
if (processLeech(pPlayer))
|
|
return;
|
|
break;
|
|
case kWeapTeslaCannon:
|
|
if (processTesla(pPlayer))
|
|
return;
|
|
break;
|
|
}
|
|
if (VanillaMode())
|
|
{
|
|
if (pPlayer->nextWeapon)
|
|
{
|
|
sfxKill3DSound(pPlayer->actor, -1, 441);
|
|
pPlayer->weaponState = 0;
|
|
pPlayer->newWeapon = pPlayer->nextWeapon;
|
|
pPlayer->nextWeapon = kWeapNone;
|
|
}
|
|
}
|
|
if (pPlayer->input.getNewWeapon() == WeaponSel_Next)
|
|
{
|
|
pPlayer->input.setNewWeapon(kWeapNone);
|
|
if (VanillaMode())
|
|
{
|
|
pPlayer->weaponState = 0;
|
|
}
|
|
pPlayer->nextWeapon = kWeapNone;
|
|
int t;
|
|
int weapon = WeaponFindNext(pPlayer, &t, 1);
|
|
pPlayer->weaponMode[weapon] = t;
|
|
if (VanillaMode())
|
|
{
|
|
if (pPlayer->curWeapon)
|
|
{
|
|
WeaponLower(pPlayer);
|
|
pPlayer->nextWeapon = weapon;
|
|
return;
|
|
}
|
|
}
|
|
pPlayer->newWeapon = weapon;
|
|
}
|
|
else if (pPlayer->input.getNewWeapon() == WeaponSel_Prev)
|
|
{
|
|
pPlayer->input.setNewWeapon(kWeapNone);
|
|
if (VanillaMode())
|
|
{
|
|
pPlayer->weaponState = 0;
|
|
}
|
|
pPlayer->nextWeapon = kWeapNone;
|
|
int t;
|
|
int weapon = WeaponFindNext(pPlayer, &t, 0);
|
|
pPlayer->weaponMode[weapon] = t;
|
|
if (VanillaMode())
|
|
{
|
|
if (pPlayer->curWeapon)
|
|
{
|
|
WeaponLower(pPlayer);
|
|
pPlayer->nextWeapon = weapon;
|
|
return;
|
|
}
|
|
}
|
|
pPlayer->newWeapon = weapon;
|
|
}
|
|
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->actor, -1, 441);
|
|
pPlayer->newWeapon = pPlayer->nextWeapon;
|
|
pPlayer->nextWeapon = kWeapNone;
|
|
}
|
|
}
|
|
if (pPlayer->weaponState == -1)
|
|
{
|
|
pPlayer->weaponState = 0;
|
|
int t;
|
|
int weapon = WeaponFindLoaded(pPlayer, &t);
|
|
pPlayer->weaponMode[weapon] = t;
|
|
if (pPlayer->curWeapon)
|
|
{
|
|
WeaponLower(pPlayer);
|
|
pPlayer->nextWeapon = weapon;
|
|
return;
|
|
}
|
|
pPlayer->newWeapon = weapon;
|
|
}
|
|
if (pPlayer->newWeapon)
|
|
{
|
|
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
|
|
{
|
|
int saveweapon = pPlayer->curWeapon;
|
|
pPlayer->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
|
|
{
|
|
pPlayer->curWeapon = WeaponFindNext(pPlayer, NULL, (char)(prevNewWeaponVal == WeaponSel_Next));
|
|
if (!BannedUnderwater(pPlayer->curWeapon)) // if new weapon is not a banned weapon, set to new current weapon
|
|
{
|
|
pPlayer->newWeapon = pPlayer->curWeapon;
|
|
pPlayer->weaponMode[pPlayer->newWeapon] = 0;
|
|
break;
|
|
}
|
|
}
|
|
pPlayer->curWeapon = saveweapon;
|
|
}
|
|
}
|
|
if (pPlayer->newWeapon == kWeapDynamite)
|
|
{
|
|
if (pPlayer->curWeapon == kWeapDynamite)
|
|
{
|
|
if (checkAmmo2(pPlayer, 10, 1))
|
|
pPlayer->newWeapon = kWeapProximity;
|
|
else if (checkAmmo2(pPlayer, 11, 1))
|
|
pPlayer->newWeapon = kWeapRemote;
|
|
}
|
|
else if (pPlayer->curWeapon == kWeapProximity)
|
|
{
|
|
if (checkAmmo2(pPlayer, 11, 1))
|
|
pPlayer->newWeapon = kWeapRemote;
|
|
else if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
|
|
pPlayer->newWeapon = kWeapDynamite;
|
|
}
|
|
else if (pPlayer->curWeapon == kWeapRemote)
|
|
{
|
|
if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
|
|
pPlayer->newWeapon = kWeapDynamite;
|
|
else if (checkAmmo2(pPlayer, 10, 1))
|
|
pPlayer->newWeapon = kWeapProximity;
|
|
}
|
|
else
|
|
{
|
|
if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
|
|
pPlayer->newWeapon = kWeapDynamite;
|
|
else if (checkAmmo2(pPlayer, 10, 1))
|
|
pPlayer->newWeapon = kWeapProximity;
|
|
else if (checkAmmo2(pPlayer, 11, 1))
|
|
pPlayer->newWeapon = kWeapRemote;
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
pPlayer->newWeapon = kWeapNone;
|
|
return;
|
|
}
|
|
if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->newWeapon) && !checkFired6or7(pPlayer))
|
|
{
|
|
pPlayer->newWeapon = kWeapNone;
|
|
return;
|
|
}
|
|
int nWeapon = pPlayer->newWeapon;
|
|
int v4c = weaponModes[nWeapon].update;
|
|
if (!pPlayer->curWeapon)
|
|
{
|
|
int nAmmoType = weaponModes[nWeapon].ammoType;
|
|
if (v4c > 1)
|
|
{
|
|
if (CheckAmmo(pPlayer, nAmmoType, 1) || nAmmoType == 11)
|
|
WeaponRaise(pPlayer);
|
|
pPlayer->newWeapon = kWeapNone;
|
|
}
|
|
else
|
|
{
|
|
if (CheckWeaponAmmo(pPlayer, nWeapon, nAmmoType, 1))
|
|
WeaponRaise(pPlayer);
|
|
else
|
|
{
|
|
pPlayer->weaponState = 0;
|
|
int t;
|
|
int weapon = WeaponFindLoaded(pPlayer, &t);
|
|
pPlayer->weaponMode[weapon] = t;
|
|
if (pPlayer->curWeapon)
|
|
{
|
|
WeaponLower(pPlayer);
|
|
pPlayer->nextWeapon = weapon;
|
|
return;
|
|
}
|
|
pPlayer->newWeapon = weapon;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (nWeapon == pPlayer->curWeapon && v4c <= 1)
|
|
{
|
|
pPlayer->newWeapon = kWeapNone;
|
|
return;
|
|
}
|
|
int i = 0;
|
|
if (nWeapon == pPlayer->curWeapon)
|
|
i = 1;
|
|
for (; i <= v4c; i++)
|
|
{
|
|
int v6c = (pPlayer->weaponMode[nWeapon]+i)%v4c;
|
|
if (CheckWeaponAmmo(pPlayer, nWeapon, weaponModes[nWeapon].ammoType, 1))
|
|
{
|
|
WeaponLower(pPlayer);
|
|
pPlayer->weaponMode[nWeapon] = v6c;
|
|
return;
|
|
}
|
|
}
|
|
pPlayer->newWeapon = kWeapNone;
|
|
return;
|
|
}
|
|
if (pPlayer->curWeapon && !CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1) && pPlayer->weaponAmmo != 11)
|
|
{
|
|
pPlayer->weaponState = -1;
|
|
return;
|
|
}
|
|
if (bShoot)
|
|
{
|
|
switch (pPlayer->curWeapon)
|
|
{
|
|
case kWeapPitchFork:
|
|
StartQAV(pPlayer, kQAVPFORK, nClientFirePitchfork);
|
|
return;
|
|
case kWeapSpraycan:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 3:
|
|
pPlayer->weaponState = 4;
|
|
StartQAV(pPlayer, kQAVCANFIRE, nClientFireSpray, 1);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapDynamite:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 3:
|
|
pPlayer->weaponState = 6;
|
|
pPlayer->fuseTime = -1;
|
|
pPlayer->throwTime = PlayClock;
|
|
StartQAV(pPlayer, kQAVBUNFUSE, nClientExplodeBundle);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapProximity:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 7:
|
|
SetQAV(pPlayer, kQAVPROXIDLE);
|
|
pPlayer->weaponState = 9;
|
|
pPlayer->throwTime = PlayClock;
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapRemote:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 10:
|
|
SetQAV(pPlayer, kQAVREMIDLE1);
|
|
pPlayer->weaponState = 13;
|
|
pPlayer->throwTime = PlayClock;
|
|
return;
|
|
case 11:
|
|
pPlayer->weaponState = 12;
|
|
StartQAV(pPlayer, kQAVREMFIRE, nClientFireRemote);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapShotgun:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 7:
|
|
pPlayer->weaponState = 6;
|
|
StartQAV(pPlayer, kQAV2SHOTF2, nClientFireShotgun);
|
|
return;
|
|
case 3:
|
|
pPlayer->weaponState = 2;
|
|
StartQAV(pPlayer, kQAVSHOTF1, nClientFireShotgun);
|
|
return;
|
|
case 2:
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVSHOTF2, nClientFireShotgun);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapTommyGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
|
|
StartQAV(pPlayer, kQAV2TOMFIRE, nClientFireTommy, 1);
|
|
else
|
|
StartQAV(pPlayer, kQAVTOMFIRE, nClientFireTommy, 1);
|
|
return;
|
|
case kWeapFlareGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
|
|
StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare);
|
|
else
|
|
StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare);
|
|
return;
|
|
case kWeapVoodooDoll:
|
|
{
|
|
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 == nullptr || pPlayer->voodooTarget->s().statnum != kStatDude)
|
|
i = 4;
|
|
StartQAV(pPlayer,kQAVVDFIRE1 + i, nClientFireVoodoo);
|
|
return;
|
|
}
|
|
case kWeapTeslaCannon:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 2:
|
|
pPlayer->weaponState = 4;
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla);
|
|
return;
|
|
case 5:
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapNapalm:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2NAPFIRE, nClientFireNapalm);
|
|
else
|
|
StartQAV(pPlayer, kQAVNAPFIRE, nClientFireNapalm);
|
|
return;
|
|
case kWeapLifeLeech:
|
|
sfxPlay3DSound(pPlayer->actor, 494, 2, 0);
|
|
StartQAV(pPlayer, kQAVSTAFIRE4, nClientFireLifeLeech);
|
|
return;
|
|
case kWeapBeast:
|
|
StartQAV(pPlayer, kQAVBSTATAK1 + Random(4), nClientFireBeast);
|
|
return;
|
|
}
|
|
}
|
|
if (bShoot2)
|
|
{
|
|
switch (pPlayer->curWeapon)
|
|
{
|
|
case kWeapPitchFork:
|
|
StartQAV(pPlayer, kQAVPFORK, nClientFirePitchfork);
|
|
return;
|
|
case kWeapSpraycan:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 3:
|
|
pPlayer->weaponState = 5;
|
|
StartQAV(pPlayer, kQAVCANFIRE2, nClientExplodeCan);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapDynamite:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 3:
|
|
pPlayer->weaponState = 4;
|
|
StartQAV(pPlayer, kQAVBUNFUSE, nClientExplodeBundle);
|
|
return;
|
|
case 7:
|
|
pPlayer->weaponState = 8;
|
|
StartQAV(pPlayer, kQAVPROXDROP, nClientDropProx);
|
|
return;
|
|
case 10:
|
|
pPlayer->weaponState = 11;
|
|
StartQAV(pPlayer, kQAVREMDROP, nClientDropRemote);
|
|
return;
|
|
case 11:
|
|
if (pPlayer->ammoCount[11] > 0)
|
|
{
|
|
pPlayer->weaponState = 10;
|
|
StartQAV(pPlayer, kQAVREMUP1);
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapProximity:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 7:
|
|
pPlayer->weaponState = 8;
|
|
StartQAV(pPlayer, kQAVPROXDROP, nClientDropProx);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapRemote:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 10:
|
|
pPlayer->weaponState = 11;
|
|
StartQAV(pPlayer, kQAVREMDROP, nClientDropRemote);
|
|
return;
|
|
case 11:
|
|
if (pPlayer->ammoCount[11] > 0)
|
|
{
|
|
pPlayer->weaponState = 10;
|
|
StartQAV(pPlayer, kQAVREMUP1);
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapShotgun:
|
|
switch (pPlayer->weaponState)
|
|
{
|
|
case 7:
|
|
pPlayer->weaponState = 6;
|
|
StartQAV(pPlayer, kQAV2SHOTFIR, nClientFireShotgun);
|
|
return;
|
|
case 3:
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVSHOTF3, nClientFireShotgun);
|
|
return;
|
|
case 2:
|
|
pPlayer->weaponState = 1;
|
|
StartQAV(pPlayer, kQAVSHOTF2, nClientFireShotgun);
|
|
return;
|
|
}
|
|
break;
|
|
case kWeapTommyGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
|
|
StartQAV(pPlayer, kQAV2TOMALT, nClientAltFireSpread2);
|
|
else
|
|
StartQAV(pPlayer, kQAVTOMSPRED, nClientAltFireSpread2);
|
|
return;
|
|
case kWeapVoodooDoll:
|
|
sfxPlay3DSound(pPlayer->actor, 461, 2, 0);
|
|
StartQAV(pPlayer, kQAVVDSPEL1, nClientAltFireVoodoo);
|
|
return;
|
|
#if 0
|
|
case kWeapFlareGun:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
|
|
StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare, 0);
|
|
else
|
|
StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare, 0);
|
|
return;
|
|
#endif
|
|
case kWeapTeslaCannon:
|
|
if (checkAmmo2(pPlayer, 7, 35))
|
|
{
|
|
if (checkAmmo2(pPlayer, 7, 70) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNALT, nClientFireTesla);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNFIR4, nClientFireTesla);
|
|
}
|
|
else
|
|
{
|
|
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla);
|
|
else
|
|
StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla);
|
|
}
|
|
return;
|
|
case kWeapNapalm:
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns))
|
|
// by NoOne: allow napalm launcher alt fire act like in v1.0x versions
|
|
if (gGameOptions.weaponsV10x && !VanillaMode()) StartQAV(pPlayer, kQAV2NAPFIR2, nClientFireNapalm2);
|
|
else StartQAV(pPlayer, kQAV2NAPFIRE, nClientAltFireNapalm);
|
|
else
|
|
StartQAV(pPlayer, kQAVNAPFIRE, (gGameOptions.weaponsV10x && !VanillaMode()) ? nClientFireNapalm : nClientAltFireNapalm);
|
|
return;
|
|
case kWeapFlareGun:
|
|
if (CheckAmmo(pPlayer, 1, 8))
|
|
{
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 16))
|
|
StartQAV(pPlayer, kQAVFLAR2FIR, nClientAltFireFlare);
|
|
else
|
|
StartQAV(pPlayer, kQAVFLARFIR2, nClientAltFireFlare);
|
|
}
|
|
else
|
|
{
|
|
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2))
|
|
StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare);
|
|
else
|
|
StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare);
|
|
}
|
|
return;
|
|
case kWeapLifeLeech:
|
|
if (gGameOptions.nGameType <= 1 && !checkAmmo2(pPlayer, 8, 1) && pPlayer->pXSprite->health < (25 << 4))
|
|
{
|
|
sfxPlay3DSound(pPlayer->actor, 494, 2, 0);
|
|
StartQAV(pPlayer, kQAVSTAFIRE4, nClientFireLifeLeech);
|
|
}
|
|
else
|
|
{
|
|
StartQAV(pPlayer, kQAVSTAFDOWN);
|
|
AltFireLifeLeech(1, pPlayer);
|
|
pPlayer->weaponState = -1;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
WeaponUpdateState(pPlayer);
|
|
}
|
|
|
|
void teslaHit(DBloodActor *missileactor, int a2)
|
|
{
|
|
auto pMissile = &missileactor->s();
|
|
int x = pMissile->x;
|
|
int y = pMissile->y;
|
|
int z = pMissile->z;
|
|
int nDist = 300;
|
|
auto pSector = pMissile->sector();
|
|
auto owneractor = missileactor->GetOwner();
|
|
const bool newSectCheckMethod = !cl_bloodvanillaexplosions && !VanillaMode(); // use new sector checking logic
|
|
auto sectorMap = GetClosestSpriteSectors(pSector, x, y, nDist, nullptr, newSectCheckMethod);
|
|
bool v4 = true;
|
|
DBloodActor* actor = nullptr;
|
|
actHitcodeToData(a2, &gHitInfo, &actor);
|
|
if (a2 == 3 && actor && actor->s().statnum == kStatDude)
|
|
v4 = false;
|
|
BloodStatIterator it(kStatDude);
|
|
while (auto hitactor = it.Next())
|
|
{
|
|
if (hitactor != owneractor || v4)
|
|
{
|
|
spritetype *pHitSprite = &hitactor->s();
|
|
if (pHitSprite->flags&32)
|
|
continue;
|
|
if (CheckSector(sectorMap, pHitSprite) && CheckProximity(hitactor, x, y, z, pSector, nDist))
|
|
{
|
|
int dx = pMissile->x-pHitSprite->x;
|
|
int dy = pMissile->y-pHitSprite->y;
|
|
int nDamage = ClipLow((nDist-(ksqrt(dx*dx+dy*dy)>>4)+20)>>1, 10);
|
|
if (hitactor == owneractor)
|
|
nDamage /= 2;
|
|
actDamageSprite(owneractor, hitactor, kDamageTesla, nDamage<<4);
|
|
}
|
|
}
|
|
}
|
|
it.Reset(kStatThing);
|
|
while (auto hitactor = it.Next())
|
|
{
|
|
spritetype *pHitSprite = &hitactor->s();
|
|
if (pHitSprite->flags&32)
|
|
continue;
|
|
if (CheckSector(sectorMap, pHitSprite) && CheckProximity(hitactor, x, y, z, pSector, nDist))
|
|
{
|
|
XSPRITE *pXSprite = &hitactor->x();
|
|
if (!pXSprite->locked)
|
|
{
|
|
int dx = pMissile->x-pHitSprite->x;
|
|
int dy = pMissile->y-pHitSprite->y;
|
|
int nDamage = ClipLow(nDist-(ksqrt(dx*dx+dy*dy)>>4)+20, 20);
|
|
actDamageSprite(owneractor, hitactor, kDamageTesla, nDamage << 4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
END_BLD_NS
|