//-------------------------------------------------------------------------
/*
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 "compat.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(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(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(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);
            pQAV->nSprite = -1;
        }
    };

    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);
    pQAV->nSprite = pPlayer->pSprite->index;
    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];
    int nTarget = -1;
    pPlayer->aimTargetsCount = 0;
    int autoaim = Autoaim(pPlayer->nPlayer);
    if (autoaim == 1 || (autoaim == 2 && !pWeaponTrack->bIsProjectile) || pPlayer->curWeapon == kWeapVoodooDoll || pPlayer->curWeapon == kWeapLifeLeech)
    {
        int nClosest = 0x7fffffff;
        int nSprite;
        StatIterator it(kStatDude);
        while ((nSprite = it.NextIndex()) >= 0)
        {
            pSprite = &sprite[nSprite];
            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 += (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);
            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->sectnum,x2,y2,z2,pSprite->sectnum))
                pPlayer->aimTargets[pPlayer->aimTargetsCount++] = nSprite;
            // 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->sectnum, x2, y2, z2, pSprite->sectnum))
            {
                nClosest = nDist2;
                aim.dx = bcos(angle);
                aim.dy = bsin(angle);
                aim.dz = DivScale(dzCenter, nDist, 10);
                nTarget = nSprite;
            }
        }
        if (pWeaponTrack->thingAngle > 0)
        {
            int nSprite;
            StatIterator it(kStatThing);
            while ((nSprite = it.NextIndex()) >= 0)
            {
                pSprite = &sprite[nSprite];
                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->sectnum,pSprite->x,pSprite->y,pSprite->z,pSprite->sectnum))
                    pPlayer->aimTargets[pPlayer->aimTargetsCount++] = nSprite;
                // 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 = bcos(angle);
                    aim.dy = bsin(angle);
                    aim.dz = DivScale(dz, nDist, 10);
                    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;
}

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->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;
                }
            }
            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->pSprite, -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->pSprite, 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)
{
    auto actor = &bloodActors[pPlayer->pSprite->index];
    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->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;
    sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
    spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedSpray, nSpeed);
    if (pSprite)
    {
        sfxPlay3DSound(pSprite, 441, 0, 0);
        evPostActor(&bloodActors[pSprite->index], pPlayer->fuseTime, kCmdOn);
        int nXSprite = pSprite->extra;
        XSPRITE *pXSprite = &xsprite[nXSprite];
        pXSprite->Impact = 1;
        UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
        pPlayer->throwPower = 0;
    }
}

void DropCan(int, PLAYER *pPlayer)
{
    sfxKill3DSound(pPlayer->pSprite, -1, 441);
    spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
    if (pSprite)
    {
        evPostActor(&bloodActors[pSprite->index], pPlayer->fuseTime, kCmdOn);
        UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
    }
}

void ExplodeCan(int, PLAYER *pPlayer)
{
    sfxKill3DSound(pPlayer->pSprite, -1, 441);
    spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
    evPostActor(&bloodActors[pSprite->index], 0, kCmdOn);
    UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
    StartQAV(pPlayer, kQAVCANBOOM);
    pPlayer->curWeapon = kWeapNone;
    pPlayer->throwPower = 0;
}

void ThrowBundle(int, PLAYER *pPlayer)
{
    sfxKill3DSound(pPlayer->pSprite, 16, -1);
    int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
    sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
    spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedTNTBundle, nSpeed);
    int nXSprite = pSprite->extra;
    XSPRITE *pXSprite = &xsprite[nXSprite];
    if (pPlayer->fuseTime < 0)
        pXSprite->Impact = 1;
    else
        evPostActor(&bloodActors[pSprite->index], pPlayer->fuseTime, kCmdOn);
    UseAmmo(pPlayer, 5, 1);
    pPlayer->throwPower = 0;
}

void DropBundle(int, PLAYER *pPlayer)
{
    sfxKill3DSound(pPlayer->pSprite, 16, -1);
    spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
    evPostActor(&bloodActors[pSprite->index], pPlayer->fuseTime, kCmdOn);
    UseAmmo(pPlayer, 5, 1);
}

void ExplodeBundle(int, PLAYER *pPlayer)
{
    sfxKill3DSound(pPlayer->pSprite, 16, -1);
    spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
    evPostActor(&bloodActors[pSprite->index], 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->pSprite, 455, 1, 0);
    spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedProxBomb, nSpeed);
    evPostActor(&bloodActors[pSprite->index], 240, kCmdOn);
    UseAmmo(pPlayer, 10, 1);
    pPlayer->throwPower = 0;
}

void DropProx(int, PLAYER *pPlayer)
{
    spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedProxBomb, 0);
    evPostActor(&bloodActors[pSprite->index], 240, kCmdOn);
    UseAmmo(pPlayer, 10, 1);
}

void ThrowRemote(int, PLAYER *pPlayer)
{
    int nSpeed = MulScale(pPlayer->throwPower, 0x177777, 16)+0x66666;
    sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
    spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedRemoteBomb, nSpeed);
    int nXSprite = pSprite->extra;
    XSPRITE *pXSprite = &xsprite[nXSprite];
    pXSprite->rxID = 90+(pPlayer->pSprite->type-kDudePlayer1);
    UseAmmo(pPlayer, 11, 1);
    pPlayer->throwPower = 0;
}

void DropRemote(int, PLAYER *pPlayer)
{
    spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedRemoteBomb, 0);
    int nXSprite = pSprite->extra;
    XSPRITE *pXSprite = &xsprite[nXSprite];
    pXSprite->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)
{
    auto actor = &bloodActors[pPlayer->pSprite->index];
    assert(nTrigger > 0 && nTrigger <= kMaxShotgunBarrels);
    if (nTrigger == 1)
    {
        sfxPlay3DSound(pPlayer->pSprite, 411, 2, 0);
        pPlayer->tiltEffect = 30;
        pPlayer->visibility = 20;
    }
    else
    {
        sfxPlay3DSound(pPlayer->pSprite, 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)
{
    auto actor = &bloodActors[pPlayer->pSprite->index];
    Aim *aim = &pPlayer->aim;
    sfxPlay3DSound(pPlayer->pSprite, 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)
{
    auto actor = &bloodActors[pPlayer->pSprite->index];
    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->pSprite, 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)
{
    auto actor = &bloodActors[pPlayer->pSprite->index];
    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->pSprite, 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)
{
    auto actor = &bloodActors[pPlayer->pSprite->index];
    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->pSprite, 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(pSprite, 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(pSprite, 420, 2, 0);
    pPlayer->visibility = 45;
    pPlayer->flashEffect = 1;
}

void FireVoodoo(int nTrigger, PLAYER *pPlayer)
{
    nTrigger--;
    auto actor = pPlayer->actor();
    spritetype *pSprite = pPlayer->pSprite;
    if (nTrigger == 4)
    {
        actDamageSprite(actor, actor, kDamageBullet, 1<<4);
        return;
    }
    assert(pPlayer->voodooTarget >= 0);
    auto targetactor = &bloodActors[pPlayer->voodooTarget];
    spritetype *pTarget = &targetactor->s();
    if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
        return;
    switch (nTrigger)
    {
    case 0:
    {
        sfxPlay3DSound(pSprite, 460, 2, 0);
        fxSpawnBlood(targetactor, 17<<4);
        int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 17<<4);
        UseAmmo(pPlayer, 9, nDamage/4);
        break;
    }
    case 1:
    {
        sfxPlay3DSound(pSprite, 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(pSprite, 463, 2, 0);
        fxSpawnBlood(targetactor, 17<<4);
        int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 49<<4);
        UseAmmo(pPlayer, 9, nDamage/4);
        break;
    }
    case 2:
    {
        sfxPlay3DSound(pSprite, 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)
{
    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();
                    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++)
            {
                int nTarget = pPlayer->aimTargets[i];
                auto targetactor = &bloodActors[nTarget];
                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->pSprite, 455, 2, 0);
    spritetype *pSprite = playerFireThing(pPlayer, 0, -4730, kThingVoodooHead, 0xccccc);
    if (pSprite)
    {
        int nXSprite = pSprite->extra;
        XSPRITE *pXSprite = &xsprite[nXSprite];
        pXSprite->data1 = pPlayer->ammoCount[9];
        evPostActor(&bloodActors[pSprite->index], 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(pSprite, 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(pSprite, 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(pSprite, 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(pSprite, 480, 2, 0);
    UseAmmo(pPlayer, 4, 2);
    pPlayer->flashEffect = 1;
}

void AltFireNapalm(int , PLAYER *pPlayer)
{
    int nSpeed = MulScale(0x8000, 0x177777, 16)+0x66666;
    spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingNapalmBall, nSpeed);
    if (pMissile)
    {
        XSPRITE *pXSprite = &xsprite[pMissile->extra];
        pXSprite->data4 = ClipHigh(pPlayer->ammoCount[4], 12);
        UseAmmo(pPlayer, 4, pXSprite->data4);
        seqSpawn(22, 3, pMissile->extra, -1);
        actBurnSprite(pPlayer->pSprite->index, pXSprite, 600);
        evPostActor(&bloodActors[pMissile->index], 0, kCallbackFXFlameLick);
        sfxPlay3DSound(pMissile, 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);
    auto actor = pPlayer->actor();
    spritetype *pMissile = playerFireMissile(pPlayer, 0, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, 315);
    if (pMissile)
    {
        XSPRITE *pXSprite = &xsprite[pMissile->extra];
        pXSprite->target_i = pPlayer->aimTarget;
        pMissile->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)
{
    auto actor = pPlayer->actor();
    sfxPlay3DSound(pPlayer->pSprite, 455, 2, 0);
    spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingDroppedLifeLeech, 0x19999);
    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);
        pXSprite->stateTimer = 1;
        evPostActor(&bloodActors[pMissile->index], 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)
{
    auto actor = &bloodActors[pPlayer->pSprite->index];
    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(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(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.index >= 0 && pPlayer->pXSprite->health > 0) {
        playerQavSceneProcess(pPlayer, &gPlayerCtrl[pPlayer->nPlayer].qavScene);
        UpdateAimVector(pPlayer);
        return;
    }
    #endif

    if (pPlayer->pXSprite->health == 0)
    {
        pPlayer->qavLoop = 0;
        sfxKill3DSound(pPlayer->pSprite, 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->pSprite, -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->pSprite, -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
            {
                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)
        {
            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 == -1 || sprite[pPlayer->voodooTarget].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->pSprite, 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->pSprite, 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->pSprite, 494, 2, 0);
                StartQAV(pPlayer, kQAVSTAFIRE4, nClientFireLifeLeech);
            }
            else
            {
                StartQAV(pPlayer, kQAVSTAFDOWN);
                AltFireLifeLeech(1, pPlayer);
                pPlayer->weaponState = -1;
            }
            return;
        }
    }
    WeaponUpdateState(pPlayer);
}

void teslaHit(spritetype *pMissile, int a2)
{
    auto missileactor = &bloodActors[pMissile->index];
    uint8_t sectmap[(kMaxSectors+7)>>3];
    int x = pMissile->x;
    int y = pMissile->y;
    int z = pMissile->z;
    int nDist = 300;
    int nSector = pMissile->sectnum;
    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;
    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 != owner || v4)
        {
            spritetype *pHitSprite = &hitactor->s();
            if (pHitSprite->flags&32)
                continue;
            if (TestBitString(sectmap, pHitSprite->sectnum) && CheckProximity(pHitSprite, x, y, z, nSector, 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 == owner)
                    nDamage /= 2;
                actDamageSprite(owner, hitactor, kDamageTesla, nDamage<<4);
            }
        }
    }
    it.Reset(kStatThing);
    while (auto hitactor = it.Next())
    {
        spritetype *pHitSprite = &hitactor->s();
        if (pHitSprite->flags&32)
            continue;
        if (TestBitString(sectmap, pHitSprite->sectnum) && CheckProximity(pHitSprite, x, y, z, nSector, 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(owner, hitactor, kDamageTesla, nDamage << 4);
            }
        }
    }
}

END_BLD_NS
