/ source / blood / src / weapon.cpp
weapon.cpp
   1  //-------------------------------------------------------------------------
   2  /*
   3  Copyright (C) 2010-2019 EDuke32 developers and contributors
   4  Copyright (C) 2019 Nuke.YKT
   5  
   6  This file is part of NBlood.
   7  
   8  NBlood is free software; you can redistribute it and/or
   9  modify it under the terms of the GNU General Public License version 2
  10  as published by the Free Software Foundation.
  11  
  12  This program is distributed in the hope that it will be useful,
  13  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15  
  16  See the GNU General Public License for more details.
  17  
  18  You should have received a copy of the GNU General Public License
  19  along with this program; if not, write to the Free Software
  20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  21  */
  22  //-------------------------------------------------------------------------
  23  #include <stdio.h>
  24  #include <stdlib.h>
  25  #include <string.h>
  26  #include "crc32.h"
  27  #include "compat.h"
  28  #include "build.h"
  29  #include "mmulti.h"
  30  #include "common_game.h"
  31  #include "actor.h"
  32  #include "blood.h"
  33  #include "db.h"
  34  #include "demo.h"
  35  #include "callback.h"
  36  #include "config.h"
  37  #include "eventq.h"
  38  #include "fx.h"
  39  #include "gameutil.h"
  40  #include "globals.h"
  41  #include "levels.h"
  42  #include "loadsave.h"
  43  #include "player.h"
  44  #include "qav.h"
  45  #include "resource.h"
  46  #include "seq.h"
  47  #include "sfx.h"
  48  #include "sound.h"
  49  #include "trig.h"
  50  #include "warp.h"
  51  #ifdef NOONE_EXTENSIONS
  52  #include "nnexts.h"
  53  #endif
  54  #include "view.h"
  55  
  56  #define kQAVEndVanilla  125
  57  #define kQAVCanDown2Fix 125 // custom qav for spray can unequip animation fix
  58  #define kQAVEnd         126
  59  
  60  #define kQAVCanDown2CRC 1233299925
  61  
  62  void FirePitchfork(int, PLAYER *pPlayer);
  63  void FireSpray(int, PLAYER *pPlayer);
  64  void ThrowCan(int, PLAYER *pPlayer);
  65  void DropCan(int, PLAYER *pPlayer);
  66  void ExplodeCan(int, PLAYER *pPlayer);
  67  void ThrowBundle(int, PLAYER *pPlayer);
  68  void DropBundle(int, PLAYER *pPlayer);
  69  void ExplodeBundle(int, PLAYER *pPlayer);
  70  void ThrowProx(int, PLAYER *pPlayer);
  71  void DropProx(int, PLAYER *pPlayer);
  72  void ThrowRemote(int, PLAYER *pPlayer);
  73  void DropRemote(int, PLAYER *pPlayer);
  74  void FireRemote(int, PLAYER *pPlayer);
  75  void FireShotgun(int nTrigger, PLAYER *pPlayer);
  76  void EjectShell(int, PLAYER *pPlayer);
  77  void FireTommy(int nTrigger, PLAYER *pPlayer);
  78  void FireSpread(int nTrigger, PLAYER *pPlayer);
  79  void AltFireSpread(int nTrigger, PLAYER *pPlayer);
  80  void AltFireSpread2(int nTrigger, PLAYER *pPlayer);
  81  void FireFlare(int nTrigger, PLAYER *pPlayer);
  82  void AltFireFlare(int nTrigger, PLAYER *pPlayer);
  83  void FireVoodoo(int nTrigger, PLAYER *pPlayer);
  84  void AltFireVoodoo(int nTrigger, PLAYER *pPlayer);
  85  void DropVoodoo(int nTrigger, PLAYER *pPlayer);
  86  void FireTesla(int nTrigger, PLAYER *pPlayer);
  87  void AltFireTesla(int nTrigger, PLAYER *pPlayer);
  88  void FireNapalm(int nTrigger, PLAYER *pPlayer);
  89  void FireNapalm2(int nTrigger, PLAYER *pPlayer);
  90  void AltFireNapalm(int nTrigger, PLAYER *pPlayer);
  91  void FireLifeLeech(int nTrigger, PLAYER *pPlayer);
  92  void AltFireLifeLeech(int nTrigger, PLAYER *pPlayer);
  93  void FireBeast(int nTrigger, PLAYER * pPlayer);
  94  
  95  typedef void(*QAVTypeCast)(int, void *);
  96  
  97  int nClientFirePitchfork = qavRegisterClient((QAVTypeCast)FirePitchfork);
  98  int nClientFireSpray = qavRegisterClient((QAVTypeCast)FireSpray);
  99  int nClientThrowCan = qavRegisterClient((QAVTypeCast)ThrowCan);
 100  int nClientDropCan = qavRegisterClient((QAVTypeCast)DropCan);
 101  int nClientExplodeCan = qavRegisterClient((QAVTypeCast)ExplodeCan);
 102  int nClientThrowBundle = qavRegisterClient((QAVTypeCast)ThrowBundle);
 103  int nClientDropBundle = qavRegisterClient((QAVTypeCast)DropBundle);
 104  int nClientExplodeBundle = qavRegisterClient((QAVTypeCast)ExplodeBundle);
 105  int nClientThrowProx = qavRegisterClient((QAVTypeCast)ThrowProx);
 106  int nClientDropProx = qavRegisterClient((QAVTypeCast)DropProx);
 107  int nClientThrowRemote = qavRegisterClient((QAVTypeCast)ThrowRemote);
 108  int nClientDropRemote = qavRegisterClient((QAVTypeCast)DropRemote);
 109  int nClientFireRemote = qavRegisterClient((QAVTypeCast)FireRemote);
 110  int nClientFireShotgun = qavRegisterClient((QAVTypeCast)FireShotgun);
 111  int nClientEjectShell = qavRegisterClient((QAVTypeCast)EjectShell);
 112  int nClientFireTommy = qavRegisterClient((QAVTypeCast)FireTommy);
 113  int nClientAltFireSpread2 = qavRegisterClient((QAVTypeCast)AltFireSpread2);
 114  int nClientFireSpread = qavRegisterClient((QAVTypeCast)FireSpread);
 115  int nClientAltFireSpread = qavRegisterClient((QAVTypeCast)AltFireSpread);
 116  int nClientFireFlare = qavRegisterClient((QAVTypeCast)FireFlare);
 117  int nClientAltFireFlare = qavRegisterClient((QAVTypeCast)AltFireFlare);
 118  int nClientFireVoodoo = qavRegisterClient((QAVTypeCast)FireVoodoo);
 119  int nClientAltFireVoodoo = qavRegisterClient((QAVTypeCast)AltFireVoodoo);
 120  int nClientFireTesla = qavRegisterClient((QAVTypeCast)FireTesla);
 121  int nClientAltFireTesla = qavRegisterClient((QAVTypeCast)AltFireTesla);
 122  int nClientFireNapalm = qavRegisterClient((QAVTypeCast)FireNapalm);
 123  int nClientFireNapalm2 = qavRegisterClient((QAVTypeCast)FireNapalm2);
 124  int nClientFireLifeLeech = qavRegisterClient((QAVTypeCast)FireLifeLeech);
 125  int nClientFireBeast = qavRegisterClient((QAVTypeCast)FireBeast);
 126  int nClientAltFireLifeLeech = qavRegisterClient((QAVTypeCast)AltFireLifeLeech);
 127  int nClientDropVoodoo = qavRegisterClient((QAVTypeCast)DropVoodoo);
 128  int nClientAltFireNapalm = qavRegisterClient((QAVTypeCast)AltFireNapalm);
 129  
 130  QAV *weaponQAV[kQAVEnd];
 131  
 132  void QAV::PlaySound(int nSound)
 133  {
 134      sndStartSample(nSound, -1, -1, 0);
 135  }
 136  
 137  void QAV::PlaySound3D(spritetype *pSprite, int nSound, int a3, int a4)
 138  {
 139      sfxPlay3DSound(pSprite, nSound, a3, a4);
 140  }
 141  
 142  char checkLitSprayOrTNT(PLAYER *pPlayer)
 143  {
 144      switch (pPlayer->curWeapon)
 145      {
 146      case kWeaponSprayCan:
 147          switch (pPlayer->weaponState)
 148          {
 149          case 5:
 150          case 6:
 151              return 1;
 152          case 7:
 153              if (VanillaMode())
 154                  return 0;
 155              return 1;
 156          }
 157          break;
 158      case kWeaponTNT:
 159          switch (pPlayer->weaponState)
 160          {
 161          case 4:
 162          case 5:
 163          case 6:
 164              return 1;
 165          }
 166          break;
 167      }
 168      return 0;
 169  }
 170  
 171  char BannedUnderwater(int nWeapon)
 172  {
 173      return nWeapon == kWeaponSprayCan || nWeapon == kWeaponTNT;
 174  }
 175  
 176  char CheckWeaponAmmo(PLAYER *pPlayer, int nWeapon, int nAmmo, int minAmmo)
 177  {
 178      if (gInfiniteAmmo)
 179          return 1;
 180      if (nAmmo == -1)
 181          return 1;
 182      if (nWeapon == kWeaponRemoteTNT && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11)
 183          return 1;
 184      if (nWeapon == kWeaponLifeLeech && pPlayer->pXSprite->health > 0)
 185          return 1;
 186      return pPlayer->ammoCount[nAmmo] >= minAmmo;
 187  }
 188  
 189  char CheckAmmo(PLAYER *pPlayer, int nAmmo, int minAmmo)
 190  {
 191      if (gInfiniteAmmo)
 192          return 1;
 193      if (nAmmo == -1)
 194          return 1;
 195      if (pPlayer->curWeapon == kWeaponRemoteTNT && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11)
 196          return 1;
 197      if (pPlayer->curWeapon == kWeaponLifeLeech && pPlayer->pXSprite->health >= (minAmmo<<4))
 198          return 1;
 199      return pPlayer->ammoCount[nAmmo] >= minAmmo;
 200  }
 201  
 202  char checkAmmo2(PLAYER *pPlayer, int ammotype, int amount)
 203  {
 204      if (gInfiniteAmmo)
 205          return 1;
 206      if (ammotype == -1)
 207          return 1;
 208      return pPlayer->ammoCount[ammotype] >= amount;
 209  }
 210  
 211  void SpawnBulletEject(PLAYER *pPlayer, int a2, int a3)
 212  {
 213      if ((MIRRORMODE & 1) && !VanillaMode(true)) // mirror mode enabled, invert position for bullet ejection
 214          a2 = -a2, a3 = -a3;
 215      POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
 216      pPlayer->zView = pPlayer->pSprite->z-pPosture->eyeAboveZ;
 217      int dz = pPlayer->zWeapon-(pPlayer->zWeapon-pPlayer->zView)/2;
 218      if ((pPlayer->q16look < 0) && WeaponsNotBlood() && !VanillaMode()) // adjust z height offset for pitch angle
 219      {
 220          CONSTEXPR int upAngle = 289;
 221          CONSTEXPR int downAngle = -347;
 222          dz -= fix16_clamp(pPlayer->q16look, F16(downAngle), F16(upAngle))>>13;
 223      }
 224      fxSpawnEjectingBrass(pPlayer->pSprite, dz, a2, a3);
 225  }
 226  
 227  void SpawnShellEject(PLAYER *pPlayer, int a2, int a3)
 228  {
 229      if ((MIRRORMODE & 1) && !VanillaMode(true)) // mirror mode enabled, invert position for shell ejection
 230          a2 = -a2, a3 = -a3;
 231      POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
 232      pPlayer->zView = pPlayer->pSprite->z-pPosture->eyeAboveZ;
 233      int t = pPlayer->zWeapon - pPlayer->zView;
 234      int dz = pPlayer->zWeapon-t+(t>>2);
 235      if ((pPlayer->q16look < 0) && WeaponsNotBlood() && !VanillaMode()) // adjust z height offset for pitch angle
 236      {
 237          CONSTEXPR int upAngle = 289;
 238          CONSTEXPR int downAngle = -347;
 239          dz -= fix16_clamp(pPlayer->q16look, F16(downAngle), F16(upAngle))>>13;
 240      }
 241      fxSpawnEjectingShell(pPlayer->pSprite, dz, a2, a3);
 242  }
 243  
 244  void WeaponInit(void)
 245  {
 246      DICTNODE *hRes;
 247      char bFixCanDown2 = 0;
 248      for (int i = 0; i < kQAVEndVanilla; i++)
 249      {
 250          hRes = gSysRes.Lookup(i, "QAV");
 251          if (!hRes)
 252              ThrowError("Could not load QAV %d\n", i);
 253          weaponQAV[i] = (QAV*)gSysRes.Lock(hRes);
 254          weaponQAV[i]->nSprite = -1;
 255          if (i == 11)
 256              bFixCanDown2 = Bcrc32((void *)weaponQAV[i], hRes->size, 0) == kQAVCanDown2CRC;
 257      }
 258      hRes = gSysRes.Lookup("NEWCANDOWN2", "QAV");
 259      if (hRes && bFixCanDown2)
 260      {
 261          weaponQAV[kQAVCanDown2Fix] = (QAV*)gSysRes.Lock(hRes);
 262          weaponQAV[kQAVCanDown2Fix]->nSprite = -1;
 263      }
 264      else
 265          weaponQAV[kQAVCanDown2Fix] = NULL;
 266  }
 267  
 268  void WeaponPrecache(void)
 269  {
 270      for (int i = 0; i < kQAVEnd; i++)
 271      {
 272          if (weaponQAV[i])
 273              weaponQAV[i]->Precache();
 274      }
 275  }
 276  
 277  void WeaponDraw(PLAYER *pPlayer, int a2, int x, int y, int a5)
 278  {
 279      dassert(pPlayer != NULL);
 280      if (pPlayer->weaponQav == -1)
 281          return;
 282      QAV * pQAV = weaponQAV[pPlayer->weaponQav];
 283      int v4;
 284      if (pPlayer->weaponTimer == 0)
 285      {
 286          if (((pPlayer->weaponState == -1) || (pPlayer->curWeapon == kWeaponShotgun && pPlayer->weaponState == 7)) && !VanillaMode()) // if shotgun with guns akimbo, or ran out of ammo, set to last seq frame
 287              v4 = pQAV->at10-1;
 288          else
 289              v4 = (int)totalclock % pQAV->at10;
 290      }
 291      else
 292          v4 = pQAV->at10 - pPlayer->weaponTimer;
 293      pQAV->x = x;
 294      pQAV->y = y;
 295      int flags = 2 | kQavOrientationQ16 | RS_LERP;
 296      if ((gWeaponInterpolate <= 1) || (pPlayer->curWeapon == kWeaponSprayCan && pPlayer->weaponState == 4)) // if interpolate weapon animation is off or problematic lerp state, remove interpolation flag
 297      {
 298          flags &= ~RS_LERP;
 299      }
 300  
 301      int nInv = powerupCheck(pPlayer, kPwUpShadowCloak);
 302      if (nInv >= 120 * 8 || (nInv != 0 && ((int)totalclock & 32)))
 303      {
 304          a2 = -128;
 305          flags |= 1;
 306      }
 307      if (powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup && !VanillaMode()) // if quad damage is active, tint weapon red
 308      {
 309          const int nWarningTime = 5;
 310          int nRemainingSeconds = pPlayer->pwUpTime[kPwUpTwoGuns] / 100;
 311          if ((nRemainingSeconds > nWarningTime) || (((int)totalclock & 32) && (nRemainingSeconds > 2)) || (((int)totalclock & 16) && (nRemainingSeconds <= 2))) // flash weapon when nearing end
 312          {
 313              a5 = kMediumGoo;
 314          }
 315      }
 316      pQAV->Draw(v4, flags, a2, a5, W_WEAPON_START);
 317  }
 318  
 319  void WeaponPlay(PLAYER *pPlayer)
 320  {
 321      dassert(pPlayer != NULL);
 322      if (pPlayer->weaponQav == -1)
 323          return;
 324      QAV *pQAV = weaponQAV[pPlayer->weaponQav];
 325      pQAV->nSprite = pPlayer->pSprite->index;
 326      int nTicks = pQAV->at10 - pPlayer->weaponTimer;
 327      pQAV->Play(nTicks-kTicsPerFrame, nTicks, pPlayer->qavCallback, pPlayer);
 328  }
 329  
 330  void StartQAV(PLAYER *pPlayer, int nWeaponQAV, int a3 = -1, char a4 = 0)
 331  {
 332      dassert(nWeaponQAV < kQAVEnd);
 333      pPlayer->weaponQav = nWeaponQAV;
 334      pPlayer->weaponTimer = weaponQAV[nWeaponQAV]->at10;
 335      pPlayer->qavCallback = a3;
 336      pPlayer->qavLoop = a4;
 337      weaponQAV[nWeaponQAV]->Preload();
 338      WeaponPlay(pPlayer);
 339      pPlayer->weaponTimer -= kTicsPerFrame;
 340  }
 341  
 342  struct WEAPONTRACK
 343  {
 344      int at0; // x aim speed
 345      int at4; // y aim speed
 346      int at8; // angle range
 347      int atc;
 348      int at10; // predict
 349      bool bIsProjectile;
 350  };
 351  
 352  WEAPONTRACK gWeaponTrack[] = {
 353      { 0, 0, 0, 0, 0, false },
 354      { 0x6000, 0x6000, 0x71, 0x55, 0x111111, false },
 355      { 0x8000, 0x8000, 0x71, 0x55, 0x2aaaaa, true },
 356      { 0x10000, 0x10000, 0x38, 0x1c, 0, false },
 357      { 0x6000, 0x8000, 0x38, 0x1c, 0, false },
 358      { 0x6000, 0x6000, 0x38, 0x1c, 0x2aaaaa, true },
 359      { 0x6000, 0x6000, 0x71, 0x55, 0, true },
 360      { 0x6000, 0x6000, 0x71, 0x38, 0, true },
 361      { 0x8000, 0x10000, 0x71, 0x55, 0x255555, true },
 362      { 0x10000, 0x10000, 0x71, 0, 0, true },
 363      { 0x10000, 0x10000, 0xaa, 0, 0, false },
 364      { 0x6000, 0x6000, 0x71, 0x55, 0, true },
 365      { 0x6000, 0x6000, 0x71, 0x55, 0, true },
 366      { 0x6000, 0x6000, 0x71, 0x55, 0, false },
 367  };
 368  
 369  void UpdateAimVector(PLAYER * pPlayer)
 370  {
 371      short nSprite;
 372      spritetype *pSprite;
 373      dassert(pPlayer != NULL);
 374      spritetype *pPSprite = pPlayer->pSprite;
 375      int x = pPSprite->x;
 376      int y = pPSprite->y;
 377      int z = pPlayer->zWeapon;
 378      int nSector = pPSprite->sectnum;
 379      Aim aim;
 380      aim.dx = Cos(pPSprite->ang)>>16;
 381      aim.dy = Sin(pPSprite->ang)>>16;
 382      aim.dz = pPlayer->slope;
 383      WEAPONTRACK *pWeaponTrack = &gWeaponTrack[pPlayer->curWeapon];
 384      int nTarget = -1;
 385      pPlayer->aimTargetsCount = 0;
 386      const char bPitchforkFireBallReady = (pPlayer->curWeapon == kWeaponPitchfork) && (pPlayer->weaponState == 4) && (pPlayer->throwPower == 65536) && gGameOptions.bQuadDamagePowerup && WeaponsNotBlood() && !VanillaMode(); // never autoaim when ready to shoot fireball
 387      const char bAutoAimWeapon = pPlayer->curWeapon == kWeaponVoodoo || pPlayer->curWeapon == kWeaponLifeLeech || (gProfile[pPlayer->nPlayer].nAutoAim == 4 && pPlayer->curWeapon == kWeaponPitchfork && !bPitchforkFireBallReady); // always autoaim for these weapons
 388      char bAutoAim = (gProfile[pPlayer->nPlayer].nAutoAim == 1) || (gProfile[pPlayer->nPlayer].nAutoAim == 2 && !pWeaponTrack->bIsProjectile && !bPitchforkFireBallReady);
 389      char bOnlyTargetRatsEels = (gProfile[pPlayer->nPlayer].nAutoAim == 3) && !pWeaponTrack->bIsProjectile && (pPlayer->curWeapon != kWeaponVoodoo) && (pPlayer->curWeapon != kWeaponLifeLeech);
 390      if (!bAutoAim && WeaponsNotBlood() && !VanillaMode()) // use autoaim for tommygun alt fire
 391      {
 392          bAutoAim = (pPlayer->curWeapon == kWeaponTommy) && (pPlayer->weaponQav == 73 || pPlayer->weaponQav == 67);
 393          if (bAutoAim)
 394              bOnlyTargetRatsEels = 0; // overrides rats/eels only targeting mode
 395      }
 396      if (bAutoAim || bAutoAimWeapon || bOnlyTargetRatsEels)
 397      {
 398          if (!VanillaMode()) // check for ror so autoaim can work peering above water
 399              CheckLink(&x, &y, &z, &nSector);
 400          int nClosest = 0x7fffffff;
 401          int nWeapAng = pWeaponTrack->at8;
 402          if ((pPlayer->curWeapon == kWeaponLifeLeech) && (gGameOptions.nGameType == kGameTypeSinglePlayer) && WeaponsNotBlood() && !VanillaMode()) // increase effectiveness of life leech for single player
 403              nWeapAng += kAng30;
 404          if (nWeapAng && !gDemo.bPlaying && !gDemo.bRecording && (gGameOptions.nGameType == kGameTypeSinglePlayer) && (numplayers == 1))
 405          {
 406              int nAutoAimModifier = divscale16(fix16_from_int(gAutoAimRange+1), fix16_from_int(5));
 407              nAutoAimModifier = mulscale16(fix16_from_int(nWeapAng), nAutoAimModifier);
 408              nWeapAng = fix16_to_int(nAutoAimModifier);
 409              nWeapAng = ClipHigh(nWeapAng, kAng180-1);
 410          }
 411          for (nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
 412          {
 413              pSprite = &sprite[nSprite];
 414              if (pSprite == pPSprite)
 415                  continue;
 416              if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pSprite))
 417                  continue;
 418              if (pSprite->flags&32)
 419                  continue;
 420              if (!(pSprite->flags&8))
 421                  continue;
 422              if (bOnlyTargetRatsEels && (pSprite->type != kDudeRat) && (pSprite->type != kDudeBoneEel))
 423                  continue;
 424              int x2 = pSprite->x;
 425              int y2 = pSprite->y;
 426              int z2 = pSprite->z;
 427              int nDist = approxDist(x2-x, y2-y);
 428              if (nDist == 0 || nDist > 51200)
 429                  continue;
 430              if (pWeaponTrack->at10)
 431              {
 432                  int t = divscale12(nDist,pWeaponTrack->at10);
 433                  x2 += (xvel[nSprite]*t)>>12;
 434                  y2 += (yvel[nSprite]*t)>>12;
 435                  z2 += (zvel[nSprite]*t)>>8;
 436              }
 437              int lx = x + mulscale30(Cos(pPSprite->ang), nDist);
 438              int ly = y + mulscale30(Sin(pPSprite->ang), nDist);
 439              int lz = z + mulscale10(pPlayer->slope, nDist);
 440              int zRange = mulscale10(9460, nDist);
 441              int top, bottom;
 442              GetSpriteExtents(pSprite, &top, &bottom);
 443              if (lz-zRange>bottom || lz+zRange<top)
 444                  continue;
 445              int angle = getangle(x2-x,y2-y);
 446              if (klabs(((angle-pPSprite->ang+1024)&2047)-1024) > nWeapAng)
 447                  continue;
 448              if (pPlayer->aimTargetsCount < 16 && cansee(x,y,z,nSector,x2,y2,z2,pSprite->sectnum))
 449                  pPlayer->aimTargets[pPlayer->aimTargetsCount++] = nSprite;
 450              // Inlined?
 451              int dz = (lz-z2)>>8;
 452              int dy = (ly-y2)>>4;
 453              int dx = (lx-x2)>>4;
 454              int nDist2 = ksqrt(dx*dx+dy*dy+dz*dz);
 455              if (nDist2 >= nClosest)
 456                  continue;
 457              DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
 458              int center = (pSprite->yrepeat*pDudeInfo->aimHeight)<<2;
 459              int dzCenter = (z2-center)-z;
 460              if (cansee(x, y, z, nSector, x2, y2, z2, pSprite->sectnum))
 461              {
 462                  nClosest = nDist2;
 463                  aim.dx = Cos(angle)>>16;
 464                  aim.dy = Sin(angle)>>16;
 465                  aim.dz = divscale10(dzCenter, nDist);
 466                  nTarget = nSprite;
 467              }
 468          }
 469          if (pWeaponTrack->atc > 0)
 470          {
 471              for (nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
 472              {
 473                  pSprite = &sprite[nSprite];
 474                  if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pSprite))
 475                      continue;
 476                  if (!(pSprite->flags&8))
 477                      continue;
 478                  if (bOnlyTargetRatsEels && (pSprite->type != kDudeRat) && (pSprite->type != kDudeBoneEel))
 479                      continue;
 480                  int x2 = pSprite->x;
 481                  int y2 = pSprite->y;
 482                  int z2 = pSprite->z;
 483                  int dx = x2-x;
 484                  int dy = y2-y;
 485                  int dz = z2-z;
 486                  int nDist = approxDist(dx, dy);
 487                  if (nDist == 0 || nDist > 51200)
 488                      continue;
 489                  int lx = x + mulscale30(Cos(pPSprite->ang), nDist);
 490                  int ly = y + mulscale30(Sin(pPSprite->ang), nDist);
 491                  int lz = z + mulscale10(pPlayer->slope, nDist);
 492                  int zRange = mulscale10(9460, nDist);
 493                  int top, bottom;
 494                  GetSpriteExtents(pSprite, &top, &bottom);
 495                  if (lz-zRange>bottom || lz+zRange<top)
 496                      continue;
 497                  int angle = getangle(dx,dy);
 498                  if (klabs(((angle-pPSprite->ang+1024)&2047)-1024) > pWeaponTrack->atc)
 499                      continue;
 500                  if (pPlayer->aimTargetsCount < 16 && cansee(x,y,z,nSector,pSprite->x,pSprite->y,pSprite->z,pSprite->sectnum))
 501                      pPlayer->aimTargets[pPlayer->aimTargetsCount++] = nSprite;
 502                  // Inlined?
 503                  int dz2 = (lz-z2)>>8;
 504                  int dy2 = (ly-y2)>>4;
 505                  int dx2 = (lx-x2)>>4;
 506                  int nDist2 = ksqrt(dx2*dx2+dy2*dy2+dz2*dz2);
 507                  if (nDist2 >= nClosest)
 508                      continue;
 509                  if (cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum))
 510                  {
 511                      nClosest = nDist2;
 512                      aim.dx = Cos(angle)>>16;
 513                      aim.dy = Sin(angle)>>16;
 514                      aim.dz = divscale10(dz, nDist);
 515                      nTarget = nSprite;
 516                  }
 517              }
 518          }
 519      }
 520      Aim aim2;
 521      aim2 = aim;
 522      RotateVector((int*)&aim2.dx, (int*)&aim2.dy, -pPSprite->ang);
 523      aim2.dz -= pPlayer->slope;
 524      pPlayer->relAim.dx = interpolate(pPlayer->relAim.dx, aim2.dx, pWeaponTrack->at0, 1);
 525      pPlayer->relAim.dy = interpolate(pPlayer->relAim.dy, aim2.dy, pWeaponTrack->at0, 1);
 526      pPlayer->relAim.dz = interpolate(pPlayer->relAim.dz, aim2.dz, pWeaponTrack->at4, 1);
 527      pPlayer->aim = pPlayer->relAim;
 528      RotateVector((int*)&pPlayer->aim.dx, (int*)&pPlayer->aim.dy, pPSprite->ang);
 529      pPlayer->aim.dz += pPlayer->slope;
 530      pPlayer->aimTarget = nTarget;
 531  }
 532  
 533  struct t_WeaponModes
 534  {
 535      int at0;
 536      int at4;
 537  };
 538  
 539  t_WeaponModes weaponModes[] = {
 540      { 0, -1 },
 541      { 1, -1 },
 542      { 1, 1 },
 543      { 1, 2 },
 544      { 1, 3 },
 545      { 1, 4 },
 546      { 1, 5 },
 547      { 1, 6 },
 548      { 1, 7 },
 549      { 1, 8 },
 550      { 1, 9 },
 551      { 1, 10 },
 552      { 1, 11 },
 553      { 0, -1 },
 554  };
 555  
 556  void WeaponRaise(PLAYER *pPlayer)
 557  {
 558      dassert(pPlayer != NULL);
 559      int prevWeapon = pPlayer->curWeapon;
 560      pPlayer->curWeapon = pPlayer->input.newWeapon;
 561      pPlayer->input.newWeapon = kWeaponNone;
 562      pPlayer->weaponAmmo = weaponModes[pPlayer->curWeapon].at4;
 563      switch (pPlayer->curWeapon)
 564      {
 565      case kWeaponPitchfork:
 566          pPlayer->weaponState = 0;
 567          StartQAV(pPlayer, 0, -1, 0);
 568          break;
 569      case kWeaponSprayCan:
 570          if (pPlayer->weaponState == 2)
 571          {
 572              pPlayer->weaponState = 3;
 573              StartQAV(pPlayer, 8, -1, 0);
 574          }
 575          else
 576          {
 577              pPlayer->weaponState = 0;
 578              StartQAV(pPlayer, 4, -1, 0);
 579          }
 580          break;
 581      case kWeaponTNT:
 582          if (gInfiniteAmmo || checkAmmo2(pPlayer, 5, 1))
 583          {
 584              if ((pPlayer->weaponState == 2) && (prevWeapon == kWeaponNone) && !VanillaMode()) // if quickly switching from tnt to spray can and back, don't put away lighter
 585                  prevWeapon = kWeaponSprayCan;
 586              pPlayer->weaponState = 3;
 587              if (prevWeapon == kWeaponSprayCan)
 588                  StartQAV(pPlayer, 16, -1, 0);
 589              else
 590                  StartQAV(pPlayer, 18, -1, 0);
 591          }
 592          break;
 593      case kWeaponProxyTNT:
 594          if (gInfiniteAmmo || checkAmmo2(pPlayer, 10, 1))
 595          {
 596              pPlayer->weaponState = 7;
 597              StartQAV(pPlayer, 25, -1, 0);
 598          }
 599          break;
 600      case kWeaponRemoteTNT:
 601          if (gInfiniteAmmo || checkAmmo2(pPlayer, 11, 1))
 602          {
 603              pPlayer->weaponState = 10;
 604              StartQAV(pPlayer, 31, -1, 0);
 605          }
 606          else
 607          {
 608              StartQAV(pPlayer, 32, -1, 0);
 609              pPlayer->weaponState = 11;
 610          }
 611          break;
 612      case kWeaponShotgun:
 613          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
 614          {
 615              if (gInfiniteAmmo || pPlayer->ammoCount[2] >= 4)
 616                  StartQAV(pPlayer, 59, -1, 0);
 617              else
 618                  StartQAV(pPlayer, 50, -1, 0);
 619              if (gInfiniteAmmo || pPlayer->ammoCount[2] >= 4)
 620                  pPlayer->weaponState = 7;
 621              else if (pPlayer->ammoCount[2] > 1)
 622                  pPlayer->weaponState = 3;
 623              else if (pPlayer->ammoCount[2] > 0)
 624                  pPlayer->weaponState = 2;
 625              else
 626                  pPlayer->weaponState = 1;
 627          }
 628          else
 629          {
 630              if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1)
 631                  pPlayer->weaponState = 3;
 632              else if (pPlayer->ammoCount[2] > 0)
 633                  pPlayer->weaponState = 2;
 634              else
 635                  pPlayer->weaponState = 1;
 636              StartQAV(pPlayer, 50, -1, 0);
 637          }
 638          break;
 639      case kWeaponTommy:
 640          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 3, 2))
 641          {
 642              pPlayer->weaponState = 1;
 643              StartQAV(pPlayer, 69, -1, 0);
 644          }
 645          else
 646          {
 647              pPlayer->weaponState = 0;
 648              StartQAV(pPlayer, 64, -1, 0);
 649          }
 650          break;
 651      case kWeaponVoodoo:
 652          if (gInfiniteAmmo || checkAmmo2(pPlayer, 9, 1))
 653          {
 654              pPlayer->weaponState = 2;
 655              StartQAV(pPlayer, 100, -1, 0);
 656          }
 657          break;
 658      case kWeaponFlare:
 659          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 1, 2))
 660          {
 661              StartQAV(pPlayer, 45, -1, 0);
 662              pPlayer->weaponState = 3;
 663          }
 664          else
 665          {
 666              StartQAV(pPlayer, 41, -1, 0);
 667              pPlayer->weaponState = 2;
 668          }
 669          break;
 670      case kWeaponTesla:
 671          if (checkAmmo2(pPlayer, 7, 1))
 672          {
 673              pPlayer->weaponState = 2;
 674              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
 675                  StartQAV(pPlayer, 82, -1, 0);
 676              else
 677                  StartQAV(pPlayer, 74, -1, 0);
 678          }
 679          else
 680          {
 681              pPlayer->weaponState = 3;
 682              StartQAV(pPlayer, 74, -1, 0);
 683          }
 684          break;
 685      case kWeaponNapalm:
 686          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
 687          {
 688              StartQAV(pPlayer, 120, -1, 0);
 689              pPlayer->weaponState = 3;
 690          }
 691          else
 692          {
 693              StartQAV(pPlayer, 89, -1, 0);
 694              pPlayer->weaponState = 2;
 695          }
 696          break;
 697      case kWeaponLifeLeech:
 698          pPlayer->weaponState = 2;
 699          StartQAV(pPlayer, 111, -1, 0);
 700          break;
 701      case kWeaponBeast:
 702          pPlayer->weaponState = 2;
 703          StartQAV(pPlayer, 93, -1, 0);
 704          break;
 705      }
 706  }
 707  
 708  void WeaponLower(PLAYER *pPlayer)
 709  {
 710      dassert(pPlayer != NULL);
 711      if (checkLitSprayOrTNT(pPlayer))
 712          return;
 713      pPlayer->throwPower = 0;
 714      const int prevState = pPlayer->weaponState;
 715      const int prevWeapon = pPlayer->curWeapon;
 716      switch (pPlayer->curWeapon)
 717      {
 718      case kWeaponPitchfork:
 719          StartQAV(pPlayer, 3, -1, 0);
 720          break;
 721      case kWeaponSprayCan:
 722          sfxKill3DSound(pPlayer->pSprite, -1, 441);
 723          switch (prevState)
 724          {
 725          case 1:
 726              if (VanillaMode())
 727              {
 728                  StartQAV(pPlayer, 7, -1, 0);
 729              }
 730              else if (pPlayer->input.newWeapon == kWeaponTNT) // do not put away lighter if TNT was selected while throwing a spray can
 731              {
 732                  pPlayer->weaponState = 2;
 733                  StartQAV(pPlayer, 11, -1, 0);
 734                  WeaponRaise(pPlayer);
 735                  return;
 736              }
 737              break;
 738          case 2:
 739              pPlayer->weaponState = 1;
 740              WeaponRaise(pPlayer);
 741              return;
 742          case 4:
 743              if (pPlayer->input.newWeapon == kWeaponTNT && !VanillaMode())
 744              {
 745                  pPlayer->weaponState = 2;
 746                  StartQAV(pPlayer, 11, -1, 0);
 747                  return;
 748              }
 749              pPlayer->weaponState = 1;
 750              StartQAV(pPlayer, 11, -1, 0);
 751              if (VanillaMode())
 752                  pPlayer->input.newWeapon = kWeaponNone;
 753              WeaponLower(pPlayer);
 754              break;
 755          case 0:
 756              if ((pPlayer->input.newWeapon == kWeaponTNT) && !VanillaMode()) // if switched to tnt before lighter is ignited, don't execute spray can equip qav
 757              {
 758                  pPlayer->weaponState = 3;
 759                  StartQAV(pPlayer, 16, -1, 0);
 760                  WeaponRaise(pPlayer);
 761                  return;
 762              }
 763              break;
 764          case 3:
 765              if (pPlayer->input.newWeapon == kWeaponTNT)
 766              {
 767                  pPlayer->weaponState = 2;
 768                  StartQAV(pPlayer, 11, -1, 0);
 769                  return;
 770              }
 771              pPlayer->weaponState = 1;
 772              if (pPlayer->input.newWeapon == kWeaponSprayCan)
 773              {
 774                  StartQAV(pPlayer, 11, -1, 0);
 775                  pPlayer->input.newWeapon = kWeaponNone;
 776                  WeaponLower(pPlayer);
 777              }
 778              else // use fixed qav animation for lowering spray can
 779                  StartQAV(pPlayer, !VanillaMode() && weaponQAV[kQAVCanDown2Fix] ? kQAVCanDown2Fix : 11, -1, 0);
 780              break;
 781          case 7: // throwing ignited alt fire spray (this happens when submerging underwater while holding down throw spray can)
 782              if (VanillaMode() || (pPlayer->input.newWeapon != kWeaponNone))
 783                  break;
 784              pPlayer->weaponState = 1;
 785              StartQAV(pPlayer, 11, -1, 0);
 786              break;
 787          }
 788          break;
 789      case kWeaponTNT:
 790          switch (prevState)
 791          {
 792          case 1:
 793              if (!VanillaMode() && (pPlayer->input.newWeapon == kWeaponSprayCan)) // do not put away lighter if switched to spray can
 794              {
 795                  pPlayer->weaponState = 2;
 796                  StartQAV(pPlayer, 11, -1, 0);
 797                  WeaponRaise(pPlayer);
 798                  return;
 799              }
 800              StartQAV(pPlayer, 7, -1, 0);
 801              break;
 802          case 2:
 803              WeaponRaise(pPlayer);
 804              break;
 805          case 3:
 806              if (pPlayer->input.newWeapon == kWeaponSprayCan)
 807              {
 808                  pPlayer->weaponState = 2;
 809                  StartQAV(pPlayer, 17, -1, 0);
 810              }
 811              else
 812              {
 813                  StartQAV(pPlayer, 19, -1, 0);
 814              }
 815              break;
 816          default:
 817              break;
 818          }
 819          break;
 820      case kWeaponProxyTNT:
 821          switch (prevState)
 822          {
 823          case 7:
 824              StartQAV(pPlayer, 26, -1, 0);
 825              break;
 826          }
 827          break;
 828      case kWeaponRemoteTNT:
 829          switch (prevState)
 830          {
 831          case 10:
 832              StartQAV(pPlayer, 34, -1, 0);
 833              break;
 834          case 11:
 835              StartQAV(pPlayer, 35, -1, 0);
 836              break;
 837          }
 838          break;
 839      case kWeaponShotgun:
 840          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && (VanillaMode() || (gInfiniteAmmo || CheckAmmo(pPlayer, 2, 4))))
 841              StartQAV(pPlayer, 63, -1, 0);
 842          else
 843              StartQAV(pPlayer, 58, -1, 0);
 844          break;
 845      case kWeaponTommy:
 846          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && pPlayer->weaponState == 1)
 847              StartQAV(pPlayer, 72, -1, 0);
 848          else
 849              StartQAV(pPlayer, 68, -1, 0);
 850          break;
 851      case kWeaponFlare:
 852          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && pPlayer->weaponState == 3)
 853              StartQAV(pPlayer, 49, -1, 0);
 854          else
 855              StartQAV(pPlayer, 44, -1, 0);
 856          break;
 857      case kWeaponVoodoo:
 858          StartQAV(pPlayer, 109, -1, 0);
 859          break;
 860      case kWeaponTesla:
 861          if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
 862              StartQAV(pPlayer, 88, -1, 0);
 863          else
 864              StartQAV(pPlayer, 81, -1, 0);
 865          break;
 866      case kWeaponNapalm:
 867          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && (VanillaMode() || (gInfiniteAmmo || CheckAmmo(pPlayer, 4, 2))))
 868              StartQAV(pPlayer, 124, -1, 0);
 869          else
 870              StartQAV(pPlayer, 92, -1, 0);
 871          break;
 872      case kWeaponLifeLeech:
 873          StartQAV(pPlayer, 119, -1, 0);
 874          break;
 875      case kWeaponBeast:
 876          StartQAV(pPlayer, 99, -1, 0);
 877          break;
 878      }
 879      pPlayer->curWeapon = kWeaponNone;
 880      pPlayer->qavLoop = 0;
 881  
 882      if ((prevWeapon != kWeaponTNT && prevWeapon != kWeaponSprayCan) && !VanillaMode()) // reset weapon state after switching weapon (except when switching from tnt/spray)
 883          pPlayer->weaponState = 0;
 884  }
 885  
 886  void WeaponUpdateState(PLAYER *pPlayer)
 887  {
 888      static int lastWeapon = kWeaponNone;
 889      static int lastState = 0;
 890      XSPRITE *pXSprite = pPlayer->pXSprite;
 891      int va = pPlayer->curWeapon;
 892      int vb = pPlayer->weaponState;
 893      if (va != lastWeapon || vb != lastState)
 894      {
 895          lastWeapon = va;
 896          lastState = vb;
 897      }
 898      switch (lastWeapon)
 899      {
 900      case kWeaponPitchfork:
 901          pPlayer->weaponQav = 1;
 902          break;
 903      case kWeaponSprayCan:
 904          switch (vb)
 905          {
 906          case 0:
 907              pPlayer->weaponState = 1;
 908              StartQAV(pPlayer, 5, -1, 0);
 909              break;
 910          case 1:
 911              if (CheckAmmo(pPlayer, 6, 1))
 912              {
 913                  pPlayer->weaponState = 3;
 914                  StartQAV(pPlayer, 8, -1, 0);
 915              }
 916              else
 917                  pPlayer->weaponQav = 6;
 918              break;
 919          case 3:
 920              pPlayer->weaponQav = 9;
 921              break;
 922          case 4:
 923              if (CheckAmmo(pPlayer, 6, 1))
 924              {
 925                  pPlayer->weaponQav = 9;
 926                  pPlayer->weaponState = 3;
 927              }
 928              else
 929              {
 930                  pPlayer->weaponState = 1;
 931                  StartQAV(pPlayer, 11, -1, 0);
 932              }
 933              sfxKill3DSound(pPlayer->pSprite, -1, 441);
 934              break;
 935          }
 936          break;
 937      case kWeaponTNT:
 938          switch (vb)
 939          {
 940          case 1:
 941              if (pPlayer->weaponAmmo == 5 && CheckAmmo(pPlayer, 5, 1))
 942              {
 943                  pPlayer->weaponState = 3;
 944                  StartQAV(pPlayer, 16, -1, 0);
 945              }
 946              break;
 947          case 0:
 948              pPlayer->weaponState = 1;
 949              StartQAV(pPlayer, 5, -1, 0);
 950              break;
 951          case 2:
 952              if (pPlayer->ammoCount[5] > 0)
 953              {
 954                  pPlayer->weaponState = 3;
 955                  StartQAV(pPlayer, 16, -1, 0);
 956              }
 957              else
 958                  pPlayer->weaponQav = 6;
 959              break;
 960          case 3:
 961              pPlayer->weaponQav = 20;
 962              break;
 963          }
 964          break;
 965      case kWeaponProxyTNT:
 966          switch (vb)
 967          {
 968          case 7:
 969              pPlayer->weaponQav = 27;
 970              break;
 971          case 8:
 972              pPlayer->weaponState = 7;
 973              StartQAV(pPlayer, 25, -1, 0);
 974              break;
 975          }
 976          break;
 977      case kWeaponRemoteTNT:
 978          switch (vb)
 979          {
 980          case 10:
 981              pPlayer->weaponQav = 36;
 982              break;
 983          case 11:
 984              pPlayer->weaponQav = 37;
 985              break;
 986          case 12:
 987              if (pPlayer->ammoCount[11] > 0)
 988              {
 989                  pPlayer->weaponState = 10;
 990                  StartQAV(pPlayer, 31, -1, 0);
 991              }
 992              else
 993                  pPlayer->weaponState = -1;
 994              break;
 995          }
 996          break;
 997      case kWeaponShotgun:
 998          switch (vb)
 999          {
1000          case 6:
1001              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && (gInfiniteAmmo || CheckAmmo(pPlayer, 2, 4)))
1002                  pPlayer->weaponState = 7;
1003              else
1004                  pPlayer->weaponState = 1;
1005              break;
1006          case 7:
1007              pPlayer->weaponQav = 60;
1008              break;
1009          case 1:
1010              if (CheckAmmo(pPlayer, 2, 1))
1011              {
1012                  sfxPlay3DSound(pPlayer->pSprite, 410, 3, 2);
1013                  StartQAV(pPlayer, 57, nClientEjectShell, 0);
1014                  if (powerupCheck(pPlayer, kPwUpTwoGuns) && !gGameOptions.bQuadDamagePowerup && (gInfiniteAmmo || CheckAmmo(pPlayer, 2, 4)) && !VanillaMode()) // if we now have enough ammo to carry two shotguns, update the gun state and give back our second shotgun
1015                      pPlayer->weaponState = 7;
1016                  else if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1)
1017                      pPlayer->weaponState = 3;
1018                  else
1019                      pPlayer->weaponState = 2;
1020              }
1021              else
1022                  pPlayer->weaponQav = 51;
1023              break;
1024          case 2:
1025              pPlayer->weaponQav = 52;
1026              break;
1027          case 3:
1028              pPlayer->weaponQav = 53;
1029              break;
1030          }
1031          break;
1032      case kWeaponTommy:
1033          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 3, 2))
1034          {
1035              pPlayer->weaponQav = 70;
1036              pPlayer->weaponState = 1;
1037          }
1038          else
1039          {
1040              pPlayer->weaponQav = 65;
1041              pPlayer->weaponState = 0;
1042          }
1043          break;
1044      case kWeaponFlare:
1045          if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
1046          {
1047              if (vb == 3 && checkAmmo2(pPlayer, 1, 2))
1048                  pPlayer->weaponQav = 46;
1049              else
1050              {
1051                  pPlayer->weaponQav = 42;
1052                  pPlayer->weaponState = 2;
1053              }
1054          }
1055          else
1056              pPlayer->weaponQav = 42;
1057          break;
1058      case kWeaponVoodoo:
1059          if (pXSprite->height < 256 && klabs(pPlayer->swayHeight) > 768)
1060              pPlayer->weaponQav = 102;
1061          else
1062              pPlayer->weaponQav = 101;
1063          break;
1064      case kWeaponTesla:
1065          switch (vb)
1066          {
1067          case 2:
1068              if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
1069                  pPlayer->weaponQav = 83;
1070              else
1071                  pPlayer->weaponQav = 75;
1072              break;
1073          case 3:
1074              pPlayer->weaponQav = 76;
1075              break;
1076          }
1077          break;
1078      case kWeaponNapalm:
1079          switch (vb)
1080          {
1081          case 3:
1082              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && (gInfiniteAmmo || CheckAmmo(pPlayer, 4, VanillaMode() ? 4 : 2)))
1083                  pPlayer->weaponQav = 121;
1084              else
1085                  pPlayer->weaponQav = 90;
1086              break;
1087          case 2:
1088              pPlayer->weaponQav = 90;
1089              break;
1090          }
1091          break;
1092      case kWeaponLifeLeech:
1093          switch (vb)
1094          {
1095          case 2:
1096              pPlayer->weaponQav = 112;
1097              break;
1098          }
1099          break;
1100      case kWeaponBeast:
1101          pPlayer->weaponQav = 94;
1102          break;
1103      }
1104  }
1105  
1106  void FirePitchfork(int nTrigger, PLAYER *pPlayer)
1107  {
1108      Aim *aim = &pPlayer->aim;
1109  #ifdef NOONE_EXTENSIONS
1110      if (nTrigger == 1024)
1111      {
1112          // for WLB
1113          for (int i = 0; i < 4; i++)
1114              actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon - pPlayer->pSprite->z, aim->dx, aim->dy, aim->dz, kVectorTine);
1115          return;
1116      }
1117  #endif
1118      int r1 = Random2(2000);
1119      int r2 = Random2(2000);
1120      int r3 = Random2(2000);
1121      int n = 1;
1122      if (!VanillaMode())
1123      {
1124          if (WeaponsNotBlood()) // add charge up attack
1125          {
1126              r1 = r2 = 0; // remove random x/y rotation offset
1127              if (pPlayer->weaponState >= 3) // if held and let go alt fire, use player throwpower as damage multiplier
1128              {
1129                  int divPower = (pPlayer->throwPower >> 8) >> 6; // divide power into 4
1130                  n += divPower;
1131                  if (divPower > 1) // if above half way point, play sfx
1132                      sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
1133              }
1134          }
1135          if (gGameOptions.bQuadDamagePowerup) // if quad damage is active
1136          {
1137              if (WeaponsNotBlood() && (pPlayer->weaponState == 4) && (pPlayer->throwPower == 65536)) // if missile attack ready (maxed out power)
1138              {
1139                  playerFireMissile(pPlayer, -50, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireball);
1140                  sfxPlay3DSound(pPlayer->pSprite, 480, 2, 0);
1141                  pPlayer->flashEffect = 1;
1142                  return;
1143              }
1144              if (powerupCheck(pPlayer, kPwUpTwoGuns))
1145              {
1146                  n *= 4;
1147              }
1148          }
1149      }
1150      for (int j = 0; j < n; j++)
1151      {
1152          for (int i = 0; i < 4; i++)
1153              actFireVector(pPlayer->pSprite, (2*i-3)*40, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r1, aim->dy+r2, aim->dz+r3, kVectorTine);
1154      }
1155  }
1156  
1157  void FireSpray(int, PLAYER *pPlayer)
1158  {
1159      int n = 1;
1160      if (powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup && !VanillaMode()) // if quad damage is active
1161          n *= 4;
1162      for (int i = 0; i < n; i++)
1163          playerFireMissile(pPlayer, 0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFlameSpray);
1164      UseAmmo(pPlayer, 6, 4);
1165      if (CheckAmmo(pPlayer, 6, 1))
1166          sfxPlay3DSound(pPlayer->pSprite, 441, 1, 2);
1167      else
1168          sfxKill3DSound(pPlayer->pSprite, -1, 441);
1169  }
1170  
1171  void ThrowCan(int, PLAYER *pPlayer)
1172  {
1173      sfxKill3DSound(pPlayer->pSprite, -1, 441);
1174      int nSpeed = mulscale16(pPlayer->throwPower, 0x177777)+0x66666;
1175      sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
1176      spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedSpray, nSpeed);
1177      if (pSprite)
1178      {
1179          sfxPlay3DSound(pSprite, 441, 0, 0);
1180          pSprite->shade = -128;
1181          evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite);
1182          int nXSprite = pSprite->extra;
1183          XSPRITE *pXSprite = &xsprite[nXSprite];
1184          pXSprite->Impact = 1;
1185          UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
1186          pPlayer->throwPower = 0;
1187      }
1188  }
1189  
1190  void DropCan(int, PLAYER *pPlayer)
1191  {
1192      sfxKill3DSound(pPlayer->pSprite, -1, 441);
1193      spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
1194      if (pSprite)
1195      {
1196          evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite);
1197          UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
1198      }
1199  }
1200  
1201  void ExplodeCan(int, PLAYER *pPlayer)
1202  {
1203      sfxKill3DSound(pPlayer->pSprite, -1, 441);
1204      spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0);
1205      evPost(pSprite->index, 3, 0, kCmdOn, pPlayer->nSprite);
1206      UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
1207      StartQAV(pPlayer, 15, -1);
1208      pPlayer->curWeapon = kWeaponNone;
1209      pPlayer->throwPower = 0;
1210  }
1211  
1212  void ThrowBundle(int, PLAYER *pPlayer)
1213  {
1214      sfxKill3DSound(pPlayer->pSprite, 16, -1);
1215      int nSpeed = mulscale16(pPlayer->throwPower, 0x177777)+0x66666;
1216      sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
1217      spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedTNTBundle, nSpeed);
1218      int nXSprite = pSprite->extra;
1219      XSPRITE *pXSprite = &xsprite[nXSprite];
1220      if (pPlayer->fuseTime < 0)
1221          pXSprite->Impact = 1;
1222      else
1223          evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite);
1224      UseAmmo(pPlayer, 5, 1);
1225      pPlayer->throwPower = 0;
1226  }
1227  
1228  void DropBundle(int, PLAYER *pPlayer)
1229  {
1230      sfxKill3DSound(pPlayer->pSprite, 16, -1);
1231      spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
1232      evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite);
1233      UseAmmo(pPlayer, 5, 1);
1234  }
1235  
1236  void ExplodeBundle(int, PLAYER *pPlayer)
1237  {
1238      sfxKill3DSound(pPlayer->pSprite, 16, -1);
1239      spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0);
1240      evPost(pSprite->index, 3, 0, kCmdOn, pPlayer->nSprite);
1241      UseAmmo(pPlayer, 5, 1);
1242      StartQAV(pPlayer, 24, -1, 0);
1243      pPlayer->curWeapon = kWeaponNone;
1244      pPlayer->throwPower = 0;
1245  }
1246  
1247  void ThrowProx(int, PLAYER *pPlayer)
1248  {
1249      int nSpeed = mulscale16(pPlayer->throwPower, 0x177777)+0x66666;
1250      sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
1251      spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedProxBomb, nSpeed);
1252      evPost(pSprite->index, 3, 240, kCmdOn, pPlayer->nSprite);
1253      UseAmmo(pPlayer, 10, 1);
1254      pPlayer->throwPower = 0;
1255  }
1256  
1257  void DropProx(int, PLAYER *pPlayer)
1258  {
1259      spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedProxBomb, 0);
1260      evPost(pSprite->index, 3, 240, kCmdOn, pPlayer->nSprite);
1261      UseAmmo(pPlayer, 10, 1);
1262  }
1263  
1264  void ThrowRemote(int, PLAYER *pPlayer)
1265  {
1266      int nSpeed = mulscale16(pPlayer->throwPower, 0x177777)+0x66666;
1267      sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0);
1268      spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedRemoteBomb, nSpeed);
1269      int nXSprite = pSprite->extra;
1270      XSPRITE *pXSprite = &xsprite[nXSprite];
1271      pXSprite->rxID = 90+(pPlayer->pSprite->type-kDudePlayer1);
1272      UseAmmo(pPlayer, 11, 1);
1273      pPlayer->throwPower = 0;
1274  }
1275  
1276  void DropRemote(int, PLAYER *pPlayer)
1277  {
1278      spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedRemoteBomb, 0);
1279      int nXSprite = pSprite->extra;
1280      XSPRITE *pXSprite = &xsprite[nXSprite];
1281      pXSprite->rxID = 90+(pPlayer->pSprite->type-kDudePlayer1);
1282      UseAmmo(pPlayer, 11, 1);
1283  }
1284  
1285  void FireRemote(int, PLAYER *pPlayer)
1286  {
1287      evSend(0, 0, 90+(pPlayer->pSprite->type-kDudePlayer1), kCmdOn, pPlayer->nSprite);
1288  }
1289  
1290  #define kMaxShotgunBarrels 4
1291  
1292  void FireShotgun(int nTrigger, PLAYER *pPlayer)
1293  {
1294      dassert(nTrigger > 0 && nTrigger <= kMaxShotgunBarrels);
1295      if (nTrigger == 1)
1296      {
1297          sfxPlay3DSound(pPlayer->pSprite, 411, 2, 0);
1298          pPlayer->tiltEffect = 30;
1299          pPlayer->visibility = 20;
1300      }
1301      else
1302      {
1303          sfxPlay3DSound(pPlayer->pSprite, 412, 2, 0);
1304          pPlayer->tiltEffect = 50;
1305          pPlayer->visibility = 40;
1306      }
1307      int n = nTrigger<<4;
1308      if (powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup && !VanillaMode()) // if quad damage is active
1309      {
1310          n <<= 2;
1311          pPlayer->tiltEffect = nTrigger == 1 ? 40 : 75;
1312      }
1313      for (int i = 0; i < n; i++)
1314      {
1315          int r1, r2, r3;
1316          VECTOR_TYPE nType;
1317          if (nTrigger == 1)
1318          {
1319              r1 = Random3(1500);
1320              r2 = Random3(1500);
1321              r3 = Random3(500);
1322              nType = kVectorShell;
1323          }
1324          else
1325          {
1326              r1 = Random3(2500);
1327              r2 = Random3(2500);
1328              r3 = Random3(1500);
1329              nType = kVectorShellAP;
1330          }
1331          actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, nType);
1332      }
1333      UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger);
1334      pPlayer->flashEffect = 1;
1335      if (pPlayer == gMe && powerupCheck(pPlayer, kPwUpTwoGuns))
1336          ctrlJoystickRumble(n>>2);
1337  }
1338  
1339  void EjectShell(int, PLAYER *pPlayer)
1340  {
1341      SpawnShellEject(pPlayer, 25, 35);
1342      SpawnShellEject(pPlayer, 48, 35);
1343  }
1344  
1345  void FireTommy(int nTrigger, PLAYER *pPlayer)
1346  {
1347      Aim *aim = &pPlayer->aim;
1348      sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
1349      if (powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup && !VanillaMode()) // if quad damage is active
1350      {
1351          for (int i = 0; i < 5; i++)
1352          {
1353              switch (nTrigger)
1354              {
1355              case 1:
1356              {
1357                  int r1 = Random3(400);
1358                  int r2 = Random3(1200);
1359                  int r3 = Random3(1200);
1360                  actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyregular);
1361                  if (i == 0)
1362                  {
1363                      SpawnBulletEject(pPlayer, -15, -45);
1364                      pPlayer->tiltEffect = 30;
1365                      pPlayer->visibility = 20;
1366                  }
1367                  break;
1368              }
1369              case 2:
1370              {
1371                  int r1 = Random3(400);
1372                  int r2 = Random3(1200);
1373                  int r3 = Random3(1200);
1374                  actFireVector(pPlayer->pSprite, -120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyregular);
1375                  if (i == 0)
1376                      SpawnBulletEject(pPlayer, -140, -45);
1377                  r1 = Random3(400);
1378                  r2 = Random3(1200);
1379                  r3 = Random3(1200);
1380                  actFireVector(pPlayer->pSprite, 120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyregular);
1381                  if (i == 0)
1382                  {
1383                      SpawnBulletEject(pPlayer, 140, 45);
1384                      pPlayer->tiltEffect = 50;
1385                      pPlayer->visibility = 30;
1386                  }
1387                  break;
1388              }
1389              }
1390          }
1391      }
1392      else switch (nTrigger)
1393      {
1394      case 1:
1395      {
1396          int r1 = Random3(400);
1397          int r2 = Random3(1200);
1398          int r3 = Random3(1200);
1399          actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyregular);
1400          SpawnBulletEject(pPlayer, -15, -45);
1401          pPlayer->visibility = 20;
1402          break;
1403      }
1404      case 2:
1405      {
1406          int r1 = Random3(400);
1407          int r2 = Random3(1200);
1408          int r3 = Random3(1200);
1409          actFireVector(pPlayer->pSprite, -120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyregular);
1410          SpawnBulletEject(pPlayer, -140, -45);
1411          r1 = Random3(400);
1412          r2 = Random3(1200);
1413          r3 = Random3(1200);
1414          actFireVector(pPlayer->pSprite, 120, pPlayer->zWeapon-pPlayer->pSprite->z, aim->dx+r3, aim->dy+r2, aim->dz+r1, kVectorTommyregular);
1415          SpawnBulletEject(pPlayer, 140, 45);
1416          pPlayer->visibility = 30;
1417          break;
1418      }
1419      }
1420      UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger);
1421      pPlayer->flashEffect = 1;
1422      if (pPlayer == gMe && powerupCheck(pPlayer, kPwUpTwoGuns))
1423          ctrlJoystickRumble(nTrigger<<(gGameOptions.bQuadDamagePowerup && !VanillaMode() ? 5 : 3));
1424  }
1425  
1426  #define kMaxSpread 14
1427  
1428  void FireSpread(int nTrigger, PLAYER *pPlayer)
1429  {
1430      dassert(nTrigger > 0 && nTrigger <= kMaxSpread);
1431      Aim *aim = &pPlayer->aim;
1432      int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
1433      int dx = Cos(angle)>>16;
1434      int dy = Sin(angle)>>16;
1435      sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
1436      int r1, r2, r3;
1437      r1 = Random3(300);
1438      r2 = Random3(600);
1439      r3 = Random3(600);
1440      actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
1441      r1 = Random2(90);
1442      r2 = Random2(30);
1443      SpawnBulletEject(pPlayer, r2, r1);
1444      pPlayer->visibility = 20;
1445      UseAmmo(pPlayer, pPlayer->weaponAmmo, 1);
1446      pPlayer->flashEffect = 1;
1447  }
1448  
1449  void AltFireSpread(int nTrigger, PLAYER *pPlayer)
1450  {
1451      dassert(nTrigger > 0 && nTrigger <= kMaxSpread);
1452      Aim *aim = &pPlayer->aim;
1453      int angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
1454      int dx = Cos(angle)>>16;
1455      int dy = Sin(angle)>>16;
1456      sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
1457      int r1, r2, r3;
1458      r1 = Random3(300);
1459      r2 = Random3(600);
1460      r3 = Random3(600);
1461      actFireVector(pPlayer->pSprite, -120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
1462      r1 = Random2(45);
1463      r2 = Random2(120);
1464      SpawnBulletEject(pPlayer, r2, r1);
1465      r1 = Random3(300);
1466      r2 = Random3(600);
1467      r3 = Random3(600);
1468      actFireVector(pPlayer->pSprite, 120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
1469      r1 = Random2(-45);
1470      r2 = Random2(-120);
1471      SpawnBulletEject(pPlayer, r2, r1);
1472      pPlayer->tiltEffect = 20;
1473      pPlayer->visibility = 30;
1474      UseAmmo(pPlayer, pPlayer->weaponAmmo, 2);
1475      pPlayer->flashEffect = 1;
1476  }
1477  
1478  void AltFireSpread2(int nTrigger, PLAYER *pPlayer)
1479  {
1480      dassert(nTrigger > 0 && nTrigger <= kMaxSpread);
1481      Aim *aim = &pPlayer->aim;
1482      int angle;
1483      if ((MIRRORMODE & 1) && !VanillaMode(true)) // mirror mode enabled, invert tommy gun spread (only for single-player)
1484          angle = (getangle(aim->dx, aim->dy)-((112*(nTrigger-1))/14-56))&2047;
1485      else
1486          angle = (getangle(aim->dx, aim->dy)+((112*(nTrigger-1))/14-56))&2047;
1487      int dx = Cos(angle)>>16;
1488      int dy = Sin(angle)>>16;
1489      sfxPlay3DSound(pPlayer->pSprite, 431, -1, 0);
1490      if (powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup && !VanillaMode()) // if quad damage is active
1491      {
1492          for(int i = 0; i < 5; i++)
1493          {
1494              int r1, r2, r3;
1495              r1 = Random3(300);
1496              r2 = Random3(600);
1497              r3 = Random3(600);
1498              actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
1499              r1 = Random2(90);
1500              r2 = Random2(30);
1501              if (i == 0)
1502                  SpawnBulletEject(pPlayer, r2, r1);
1503          }
1504          UseAmmo(pPlayer, pPlayer->weaponAmmo, 1);
1505          pPlayer->tiltEffect = 45;
1506          pPlayer->visibility = 45;
1507      }
1508      else if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 3, 2))
1509      {
1510          int r1, r2, r3;
1511          r1 = Random3(300);
1512          r2 = Random3(600);
1513          r3 = Random3(600);
1514          actFireVector(pPlayer->pSprite, -120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
1515          r1 = Random2(45);
1516          r2 = Random2(120);
1517          SpawnBulletEject(pPlayer, r2, r1);
1518          r1 = Random3(300);
1519          r2 = Random3(600);
1520          r3 = Random3(600);
1521          actFireVector(pPlayer->pSprite, 120, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
1522          r1 = Random2(-45);
1523          r2 = Random2(-120);
1524          SpawnBulletEject(pPlayer, r2, r1);
1525          pPlayer->tiltEffect = 30;
1526          pPlayer->visibility = 45;
1527          UseAmmo(pPlayer, pPlayer->weaponAmmo, 2);
1528      }
1529      else
1530      {
1531          int r1, r2, r3;
1532          r1 = Random3(300);
1533          r2 = Random3(600);
1534          r3 = Random3(600);
1535          actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, dx+r3, dy+r2, aim->dz+r1, kVectorTommyAP);
1536          r1 = Random2(90);
1537          r2 = Random2(30);
1538          SpawnBulletEject(pPlayer, r2, r1);
1539          pPlayer->tiltEffect = 20;
1540          pPlayer->visibility = 30;
1541          UseAmmo(pPlayer, pPlayer->weaponAmmo, 1);
1542      }
1543      pPlayer->flashEffect = 1;
1544      if (!checkAmmo2(pPlayer, 3, 1))
1545      {
1546          WeaponLower(pPlayer);
1547          pPlayer->weaponState = -1;
1548      }
1549      if (pPlayer == gMe && powerupCheck(pPlayer, kPwUpTwoGuns))
1550          ctrlJoystickRumble(nTrigger<<3);
1551  }
1552  
1553  void FireFlare(int nTrigger, PLAYER *pPlayer)
1554  {
1555      spritetype *pSprite = pPlayer->pSprite;
1556      int offset = 0;
1557      switch (nTrigger)
1558      {
1559      case 2:
1560          offset = -120;
1561          break;
1562      case 3:
1563          offset = 120;
1564          break;
1565      }
1566      playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFlareRegular);
1567      UseAmmo(pPlayer, 1, 1);
1568      sfxPlay3DSound(pSprite, 420, 2, 0);
1569      pPlayer->visibility = 30;
1570      pPlayer->flashEffect = 1;
1571  }
1572  
1573  void AltFireFlare(int nTrigger, PLAYER *pPlayer)
1574  {
1575      spritetype *pSprite = pPlayer->pSprite;
1576      int offset = 0;
1577      switch (nTrigger)
1578      {
1579      case 2:
1580          offset = -120;
1581          break;
1582      case 3:
1583          offset = 120;
1584          break;
1585      }
1586      playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFlareAlt);
1587      UseAmmo(pPlayer, 1, 8);
1588      sfxPlay3DSound(pSprite, 420, 2, 0);
1589      pPlayer->visibility = 45;
1590      pPlayer->flashEffect = 1;
1591  }
1592  
1593  void FireVoodoo(int nTrigger, PLAYER *pPlayer)
1594  {
1595      nTrigger--;
1596      int nSprite = pPlayer->nSprite;
1597      spritetype *pSprite = pPlayer->pSprite;
1598      if (nTrigger == 4)
1599      {
1600          actDamageSprite(nSprite, pSprite, kDamageBullet, 1<<4);
1601          return;
1602      }
1603      dassert(pPlayer->voodooTarget >= 0);
1604      spritetype *pTarget = &sprite[pPlayer->voodooTarget];
1605      if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
1606          return;
1607      switch (nTrigger)
1608      {
1609      case 0:
1610      {
1611          sfxPlay3DSound(pSprite, 460, 2, 0);
1612          fxSpawnBlood(pTarget, 17<<4);
1613          int nDamage = actDamageSprite(nSprite, pTarget, kDamageSpirit, 17<<4);
1614          UseAmmo(pPlayer, 9, nDamage/4);
1615          break;
1616      }
1617      case 1:
1618      {
1619          sfxPlay3DSound(pSprite, 460, 2, 0);
1620          fxSpawnBlood(pTarget, 17<<4);
1621          int nDamage = actDamageSprite(nSprite, pTarget, kDamageSpirit, 9<<4);
1622          if (IsPlayerSprite(pTarget))
1623              WeaponLower(&gPlayer[pTarget->type-kDudePlayer1]);
1624          UseAmmo(pPlayer, 9, nDamage/4);
1625          break;
1626      }
1627      case 3:
1628      {
1629          sfxPlay3DSound(pSprite, 463, 2, 0);
1630          fxSpawnBlood(pTarget, 17<<4);
1631          int nDamage = actDamageSprite(nSprite, pTarget, kDamageSpirit, 49<<4);
1632          UseAmmo(pPlayer, 9, nDamage/4);
1633          break;
1634      }
1635      case 2:
1636      {
1637          sfxPlay3DSound(pSprite, 460, 2, 0);
1638          fxSpawnBlood(pTarget, 17<<4);
1639          int nDamage = actDamageSprite(nSprite, pTarget, kDamageSpirit, 11<<4);
1640          if (IsPlayerSprite(pTarget))
1641          {
1642              PLAYER *pOtherPlayer = &gPlayer[pTarget->type - kDudePlayer1];
1643              pOtherPlayer->blindEffect = 128;
1644          }
1645          UseAmmo(pPlayer, 9, nDamage/4);
1646          break;
1647      }
1648      }
1649  }
1650  
1651  void AltFireVoodoo(int nTrigger, PLAYER *pPlayer)
1652  {
1653      vec3_t pos = pPlayer->pSprite->xyz;
1654      if (!VanillaMode()) // check for ror so voodoo doll attack can work peering above water
1655      {
1656          pos.z = pPlayer->zWeapon; // offset view height to weapon level
1657          int nSector = pPlayer->pSprite->sectnum;
1658          CheckLink(&pos.x, &pos.y, &pos.z, &nSector);
1659      }
1660      if (nTrigger == 2) {
1661  
1662          // by NoOne: trying to simulate v1.0x voodoo here.
1663          // dunno how exactly it works, but at least it not spend all the ammo on alt fire
1664          if ((WeaponsV10x() || (gGameOptions.nGameType == kGameTypeSinglePlayer && WeaponsNotBlood())) && !VanillaMode()) {
1665              int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount);
1666              if (nCount > 0)
1667              {
1668                  for (int i = 0; i < pPlayer->aimTargetsCount; i++)
1669                  {
1670                      int nTarget = pPlayer->aimTargets[i];
1671                      spritetype* pTarget = &sprite[nTarget];
1672                      if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
1673                          continue;
1674                      int nDist = approxDist(pTarget->x - pos.x, pTarget->y - pos.y);
1675                      if (nDist > 0 && nDist < 51200)
1676                      {
1677                          int vc = pPlayer->ammoCount[9] >> 3;
1678                          int v8 = pPlayer->ammoCount[9] << 1;
1679                          int nDamage = (v8 + Random(vc)) << 4;
1680                          nDamage = (nDamage * ((51200 - nDist) + 1)) / 51200;
1681                          nDamage = actDamageSprite(pPlayer->nSprite, pTarget, kDamageSpirit, nDamage);
1682  
1683                          if (IsPlayerSprite(pTarget))
1684                          {
1685                              PLAYER* pOtherPlayer = &gPlayer[pTarget->type - kDudePlayer1];
1686                              if (!pOtherPlayer->godMode || !powerupCheck(pOtherPlayer, kPwUpDeathMask))
1687                                  powerupActivate(pOtherPlayer, kPwUpDeliriumShroom);
1688                          }
1689                          fxSpawnBlood(pTarget, 0);
1690                      }
1691                  }
1692              }
1693  
1694              if (WeaponsNotBlood() && !gInfiniteAmmo) // use all ammo on alt fire
1695              {
1696                  UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]);
1697                  pPlayer->hasWeapon[kWeaponVoodoo] = 0;
1698                  pPlayer->weaponState = -1;
1699                  return;
1700              }
1701              UseAmmo(pPlayer, 9, 20);
1702              pPlayer->weaponState = 0;
1703              return;
1704          }
1705  
1706          //int nAmmo = pPlayer->ammCount[9];
1707          int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount);
1708          if (nCount > 0)
1709          {
1710              int v4 = pPlayer->ammoCount[9] - (pPlayer->ammoCount[9] / nCount) * nCount;
1711              for (int i = 0; i < pPlayer->aimTargetsCount; i++)
1712              {
1713                  int nTarget = pPlayer->aimTargets[i];
1714                  spritetype* pTarget = &sprite[nTarget];
1715                  if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, pTarget))
1716                      continue;
1717                  if (v4 > 0)
1718                      v4--;
1719                  int nDist = approxDist(pTarget->x - pos.x, pTarget->y - pos.y);
1720                  if (nDist > 0 && nDist < 51200)
1721                  {
1722                      int vc = pPlayer->ammoCount[9] >> 3;
1723                      int v8 = pPlayer->ammoCount[9] << 1;
1724                      int nDamage = (v8 + Random2(vc)) << 4;
1725                      nDamage = (nDamage * ((51200 - nDist) + 1)) / 51200;
1726                      nDamage = actDamageSprite(pPlayer->nSprite, pTarget, kDamageSpirit, nDamage);
1727                      UseAmmo(pPlayer, 9, nDamage);
1728                      if (IsPlayerSprite(pTarget))
1729                      {
1730                          PLAYER* pOtherPlayer = &gPlayer[pTarget->type - kDudePlayer1];
1731                          if (!pOtherPlayer->godMode || !powerupCheck(pOtherPlayer, kPwUpDeathMask))
1732                              powerupActivate(pOtherPlayer, kPwUpDeliriumShroom);
1733                      }
1734                      fxSpawnBlood(pTarget, 0);
1735                  }
1736              }
1737          }
1738          UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]);
1739          if (gInfiniteAmmo && !VanillaMode())
1740          {
1741              pPlayer->weaponState = 0;
1742              return;
1743          }
1744          pPlayer->hasWeapon[kWeaponVoodoo] = 0;
1745          pPlayer->weaponState = -1;
1746      }
1747  }
1748  
1749  void DropVoodoo(int nTrigger, PLAYER *pPlayer)
1750  {
1751      UNREFERENCED_PARAMETER(nTrigger);
1752      sfxPlay3DSound(pPlayer->pSprite, 455, 2, 0);
1753      spritetype *pSprite = playerFireThing(pPlayer, 0, -4730, kThingVoodooHead, 0xccccc);
1754      if (pSprite)
1755      {
1756          int nXSprite = pSprite->extra;
1757          XSPRITE *pXSprite = &xsprite[nXSprite];
1758          pXSprite->data1 = pPlayer->ammoCount[9];
1759          evPost(pSprite->index, 3, 90, kCallbackDropVoodoo);
1760          UseAmmo(pPlayer, 6, gAmmoItemData[0].count);
1761          UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]);
1762          pPlayer->hasWeapon[kWeaponVoodoo] = 0;
1763      }
1764  }
1765  
1766  struct TeslaMissile
1767  {
1768      int at0; // offset
1769      int at4; // id
1770      int at8; // ammo use
1771      int atc; // sound
1772      int at10; // light
1773      int at14; // weapon flash
1774  };
1775  
1776  void FireTesla(int nTrigger, PLAYER *pPlayer)
1777  {
1778      TeslaMissile teslaMissile[6] = 
1779      {
1780          { 0, 306, 1, 470, 20, 1 },
1781          { -140, 306, 1, 470, 30, 1 },
1782          { 140, 306, 1, 470, 30, 1 },
1783          { 0, 302, 35, 471, 40, 1 },
1784          { -140, 302, 35, 471, 50, 1 },
1785          { 140, 302, 35, 471, 50, 1 },
1786      };
1787      if (nTrigger > 0 && nTrigger <= 6)
1788      {
1789          nTrigger--;
1790          spritetype *pSprite = pPlayer->pSprite;
1791          TeslaMissile *pMissile = &teslaMissile[nTrigger];
1792          if (!checkAmmo2(pPlayer, 7, pMissile->at8))
1793          {
1794              pMissile = &teslaMissile[0];
1795              if (!checkAmmo2(pPlayer, 7, pMissile->at8))
1796              {
1797                  pPlayer->weaponState = -1;
1798                  pPlayer->weaponQav = 76;
1799                  pPlayer->flashEffect = 0;
1800                  return;
1801              }
1802          }
1803          playerFireMissile(pPlayer, pMissile->at0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, pMissile->at4);
1804          UseAmmo(pPlayer, pPlayer->weaponAmmo, pMissile->at8);
1805          sfxPlay3DSound(pSprite, pMissile->atc, 1, 0);
1806          pPlayer->visibility = pMissile->at10;
1807          pPlayer->flashEffect = pMissile->at14;
1808      }
1809  }
1810  
1811  void AltFireTesla(int nTrigger, PLAYER *pPlayer)
1812  {
1813      UNREFERENCED_PARAMETER(nTrigger);
1814      spritetype *pSprite = pPlayer->pSprite;
1815      playerFireMissile(pPlayer, 0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileTeslaAlt);
1816      UseAmmo(pPlayer, pPlayer->weaponAmmo, 35);
1817      sfxPlay3DSound(pSprite, 471, 2, 0);
1818      pPlayer->visibility = 40;
1819      pPlayer->flashEffect = 1;
1820  }
1821  
1822  void FireNapalm(int nTrigger, PLAYER *pPlayer)
1823  {
1824      spritetype *pSprite = pPlayer->pSprite;
1825      int offset = 0;
1826      switch (nTrigger)
1827      {
1828      case 2:
1829          offset = -50;
1830          break;
1831      case 3:
1832          offset = 50;
1833          break;
1834      }
1835      spritetype *pMissile = playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
1836      sfxPlay3DSound(pSprite, 480, 2, 0);
1837      UseAmmo(pPlayer, 4, 1);
1838      pPlayer->flashEffect = 1;
1839      if (pMissile && gGameOptions.bNapalmFalloff && !VanillaMode()) // adjust projectile speed and pitch
1840      {
1841          const int nSprite = pMissile->index;
1842          xvel[nSprite] += (xvel[nSprite]>>4) + (xvel[nSprite]>>5);
1843          yvel[nSprite] += (yvel[nSprite]>>4) + (yvel[nSprite]>>5);
1844          zvel[nSprite] -= 58254<<3;
1845      }
1846  }
1847  
1848  void FireNapalm2(int nTrigger, PLAYER *pPlayer)
1849  {
1850      UNREFERENCED_PARAMETER(nTrigger);
1851      spritetype *pSprite = pPlayer->pSprite;
1852      spritetype *pMissile1 = playerFireMissile(pPlayer, -120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
1853      spritetype *pMissile2 = playerFireMissile(pPlayer, 120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
1854      sfxPlay3DSound(pSprite, 480, 2, 0);
1855      UseAmmo(pPlayer, 4, 2);
1856      pPlayer->flashEffect = 1;
1857      if (pMissile1 && pMissile2 && gGameOptions.bNapalmFalloff && !VanillaMode()) // adjust projectile speed and pitch
1858      {
1859          int nSprite = pMissile1->index;
1860          xvel[nSprite] += (xvel[nSprite]>>4) + (xvel[nSprite]>>5);
1861          yvel[nSprite] += (yvel[nSprite]>>4) + (yvel[nSprite]>>5);
1862          zvel[nSprite] -= 58254<<3;
1863          nSprite = pMissile2->index;
1864          xvel[nSprite] += (xvel[nSprite]>>4) + (xvel[nSprite]>>5);
1865          yvel[nSprite] += (yvel[nSprite]>>4) + (yvel[nSprite]>>5);
1866          zvel[nSprite] -= 58254<<3;
1867      }
1868  }
1869  
1870  void AltFireNapalm(int nTrigger, PLAYER *pPlayer)
1871  {
1872      UNREFERENCED_PARAMETER(nTrigger);
1873      char UNUSED(bAkimbo) = powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode());
1874      int nSpeed = mulscale16(0x8000, 0x177777)+0x66666;
1875      spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingNapalmBall, nSpeed);
1876      if (pMissile)
1877      {
1878          XSPRITE *pXSprite = &xsprite[pMissile->extra];
1879          pXSprite->data4 = ClipHigh(pPlayer->ammoCount[4], 12);
1880          UseAmmo(pPlayer, 4, pXSprite->data4);
1881          seqSpawn(22, 3, pMissile->extra, -1);
1882          actBurnSprite(actSpriteIdToOwnerId(pPlayer->pSprite->index), pXSprite, 600);
1883          evPost(pMissile->index, 3, 0, kCallbackFXFlameLick);
1884          sfxPlay3DSound(pMissile, 480, 2, 0);
1885          pPlayer->visibility = 30;
1886          pPlayer->flashEffect = 1;
1887      }
1888  }
1889  
1890  void FireLifeLeech(int nTrigger, PLAYER *pPlayer)
1891  {
1892      if (!CheckAmmo(pPlayer, 8, 1))
1893          return;
1894      int r1 = Random2(2000);
1895      int r2 = Random2(2000);
1896      int r3 = Random2(1000);
1897      int nMissileType = kMissileLifeLeechRegular;
1898      if (gLifeleechRnd && !VanillaMode()) // if random projectiles for lifeleech flag is on
1899          nMissileType = kMissileBase + Random(kMissileLifeLeechAltSmall-kMissileBase);
1900      spritetype *pMissile = playerFireMissile(pPlayer, 0, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, nMissileType);
1901      if (pMissile)
1902      {
1903          XSPRITE *pXSprite = &xsprite[pMissile->extra];
1904          pXSprite->target = pPlayer->aimTarget;
1905          pMissile->ang = (nTrigger==2) ? 1024 : 0;
1906      }
1907      if (checkAmmo2(pPlayer, 8, 1))
1908          UseAmmo(pPlayer, 8, 1);
1909      else
1910          actDamageSprite(pPlayer->nSprite, pPlayer->pSprite, kDamageSpirit, 16);
1911      pPlayer->visibility = ClipHigh(pPlayer->visibility+5, 50);
1912  }
1913  
1914  void AltFireLifeLeech(int nTrigger, PLAYER *pPlayer)
1915  {
1916      UNREFERENCED_PARAMETER(nTrigger);
1917      sfxPlay3DSound(pPlayer->pSprite, 455, 2, 0);
1918      int nSpeed = 0x19999;
1919      if (WeaponsNotBlood() && !VanillaMode()) // use player throwpower as speed
1920          nSpeed = mulscale16(pPlayer->throwPower, 0x177777)+0x66666;
1921      spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingDroppedLifeLeech, nSpeed);
1922      if (pMissile)
1923      {
1924          pMissile->cstat |= 4096;
1925          XSPRITE *pXSprite = &xsprite[pMissile->extra];
1926          if (WeaponsNotBlood() && !VanillaMode()) // lower overall lifeleech health from 150: 75 + (player's hp / 2)
1927              pXSprite->health = ClipHigh((75 + (pPlayer->pXSprite->health >> 5)) << 4, pXSprite->health); // don't go above original health value (possible with life seed)
1928          pXSprite->Push = 1;
1929          pXSprite->Proximity = 1;
1930          pXSprite->DudeLockout = 1;
1931          pXSprite->stateTimer = 1;
1932          evPost(pMissile->index, 3, 120, kCallbackLeechStateTimer);
1933          if (gGameOptions.nGameType <= kGameTypeCoop)
1934          {
1935              int nAmmo = pPlayer->ammoCount[8];
1936              if (nAmmo < 25 && pPlayer->pXSprite->health > ((25-nAmmo)<<4))
1937              {
1938                  actDamageSprite(pPlayer->nSprite, pPlayer->pSprite, kDamageSpirit, ((25-nAmmo)<<4));
1939                  nAmmo = 25;
1940              }
1941              pXSprite->data3 = nAmmo;
1942              UseAmmo(pPlayer, 8, nAmmo);
1943          }
1944          else
1945          {
1946              pXSprite->data3 = pPlayer->ammoCount[8];
1947              pPlayer->ammoCount[8] = 0;
1948          }
1949          pPlayer->hasWeapon[kWeaponLifeLeech] = 0;
1950      }
1951  }
1952  
1953  void FireBeast(int nTrigger, PLAYER * pPlayer)
1954  {
1955      UNREFERENCED_PARAMETER(nTrigger);
1956      int r1 = Random2(2000);
1957      int r2 = Random2(2000);
1958      int r3 = Random2(2000);
1959      actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, kVectorBeastSlash);
1960  }
1961  
1962  char WeaponIsEquipable(PLAYER *pPlayer, int nWeapon, char checkUnderwater = true)
1963  {
1964      if (!(nWeapon >= kWeaponPitchfork && nWeapon <= kWeaponRemoteTNT)) // invalid weapon
1965          return 0;
1966      if (checkUnderwater && pPlayer->isUnderwater && BannedUnderwater(nWeapon))
1967          return 0;
1968      if (pPlayer->hasWeapon[nWeapon])
1969      {
1970          for (int j = 0; j < weaponModes[nWeapon].at0; j++)
1971          {
1972              if (CheckWeaponAmmo(pPlayer, nWeapon, weaponModes[nWeapon].at4, 1))
1973                  return 1;
1974          }
1975      }
1976      return 0;
1977  }
1978  
1979  char gWeaponUpgrade[][13] = {
1980      { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
1981      { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
1982      { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
1983      { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
1984      { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
1985      { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
1986      { 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0 },
1987      { 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 },
1988      { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
1989      { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
1990      { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
1991      { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
1992      { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
1993  };
1994  
1995  char WeaponUpgrade(PLAYER *pPlayer, char newWeapon)
1996  {
1997      char weapon = pPlayer->curWeapon;
1998      const char nWeaponSwitch = gProfile[pPlayer->nPlayer].nWeaponSwitch;
1999      if (nWeaponSwitch && !checkLitSprayOrTNT(pPlayer))
2000      {
2001          char bNewWeapon = 0;
2002          switch (nWeaponSwitch)
2003          {
2004          case 1: // by rating
2005              bNewWeapon = gWeaponUpgrade[weapon][newWeapon];
2006              break;
2007          case 2: // if new
2008              bNewWeapon = 1;
2009              break;
2010          case 3: // only switch when holding pitchfork
2011              bNewWeapon = (weapon == kWeaponPitchfork);
2012              break;
2013          default:
2014              ThrowError("nWeaponSwitch out of range\n");
2015              break;
2016          }
2017          if (bNewWeapon)
2018              weapon = newWeapon;
2019      }
2020      return weapon;
2021  }
2022  
2023  int OrderNext[] = { kWeaponPitchfork, kWeaponFlare, kWeaponShotgun, kWeaponTommy, kWeaponNapalm, kWeaponTNT, kWeaponSprayCan, kWeaponTesla, kWeaponLifeLeech, kWeaponVoodoo, kWeaponProxyTNT, kWeaponRemoteTNT, kWeaponPitchfork, kWeaponPitchfork };
2024  int OrderPrev[] = { kWeaponRemoteTNT, kWeaponRemoteTNT, kWeaponPitchfork, kWeaponFlare, kWeaponShotgun, kWeaponTommy, kWeaponNapalm, kWeaponTNT, kWeaponSprayCan, kWeaponTesla, kWeaponLifeLeech, kWeaponVoodoo, kWeaponProxyTNT, kWeaponPitchfork };
2025  
2026  char WeaponFindNext(PLAYER *pPlayer, int *a2, char bDir)
2027  {
2028      int weapon = pPlayer->curWeapon;
2029      if (!weapon && gProfile[pPlayer->nPlayer].bWeaponFastSwitch && !VanillaMode()) // if fast weapon select is on, and player is switching weapon, use new weapon as the current weapon
2030          weapon = pPlayer->input.newWeapon;
2031      do
2032      {
2033          if (bDir)
2034              weapon = OrderNext[weapon];
2035          else
2036              weapon = OrderPrev[weapon];
2037          if (weaponModes[weapon].at0 && pPlayer->hasWeapon[weapon])
2038          {
2039              if (weapon == kWeaponLifeLeech)
2040              {
2041                  if (CheckAmmo(pPlayer, weaponModes[weapon].at4, 1))
2042                      break;
2043              }
2044              else
2045              {
2046                  if (checkAmmo2(pPlayer, weaponModes[weapon].at4, 1))
2047                      break;
2048              }
2049          }
2050      } while (weapon != pPlayer->curWeapon);
2051      if (weapon == pPlayer->curWeapon)
2052      {
2053          if (!weaponModes[weapon].at0 || !CheckAmmo(pPlayer, weaponModes[weapon].at4, 1))
2054              weapon = kWeaponPitchfork;
2055      }
2056      if (a2)
2057          *a2 = 0;
2058      return weapon;
2059  }
2060  
2061  char WeaponFindLoaded(PLAYER *pPlayer, int *a2)
2062  {
2063      char weapon = kWeaponPitchfork;
2064      int v14 = 0;
2065      if (weaponModes[pPlayer->curWeapon].at0 > 1)
2066      {
2067          for (int i = 0; i < weaponModes[pPlayer->curWeapon].at0; i++)
2068          {
2069              if (CheckAmmo(pPlayer, weaponModes[pPlayer->curWeapon].at4, 1))
2070              {
2071                  v14 = i;
2072                  weapon = pPlayer->curWeapon;
2073                  break;
2074              }
2075          }
2076      }
2077      if (weapon == kWeaponPitchfork)
2078      {
2079          int vc = 0;
2080          for (int i = 0; i < kWeaponMax; i++)
2081          {
2082              int weapon = pPlayer->weaponOrder[vc][i];
2083              if (pPlayer->hasWeapon[weapon])
2084              {
2085                  for (int j = 0; j < weaponModes[weapon].at0; j++)
2086                  {
2087                      if (CheckWeaponAmmo(pPlayer, weapon, weaponModes[weapon].at4, 1))
2088                      {
2089                          if (a2)
2090                              *a2 = j;
2091                          return weapon;
2092                      }
2093                  }
2094              }
2095          }
2096      }
2097      if (a2)
2098          *a2 = v14;
2099      return weapon;
2100  }
2101  
2102  char processSprayCan(PLAYER *pPlayer)
2103  {
2104      const char bUseShootAsThrow = !VanillaMode() && pPlayer->input.buttonFlags.shoot;
2105      switch (pPlayer->weaponState)
2106      {
2107      case 5:
2108          if (!pPlayer->input.buttonFlags.shoot2 || bUseShootAsThrow)
2109              pPlayer->weaponState = 6;
2110          return 1;
2111      case 6:
2112          if (pPlayer->input.buttonFlags.shoot2 && !bUseShootAsThrow)
2113          {
2114              pPlayer->weaponState = 3;
2115              pPlayer->fuseTime = pPlayer->weaponTimer;
2116              StartQAV(pPlayer, 13, nClientDropCan, 0);
2117          }
2118          else if (pPlayer->input.buttonFlags.shoot)
2119          {
2120              pPlayer->weaponState = 7;
2121              pPlayer->fuseTime = 0;
2122              pPlayer->throwTime = (int)gFrameClock;
2123          }
2124          return 1;
2125      case 7:
2126      {
2127          pPlayer->throwPower = ClipHigh(divscale16((int)gFrameClock-pPlayer->throwTime,240), 65536);
2128          if (!pPlayer->input.buttonFlags.shoot)
2129          {
2130              if (!pPlayer->fuseTime)
2131                  pPlayer->fuseTime = pPlayer->weaponTimer;
2132              pPlayer->weaponState = 1;
2133              StartQAV(pPlayer, 14, nClientThrowCan, 0);
2134          }
2135          return 1;
2136      }
2137      }
2138      return 0;
2139  }
2140  
2141  char processTNT(PLAYER *pPlayer)
2142  {
2143      const char bUseShootAsThrow = !VanillaMode() && pPlayer->input.buttonFlags.shoot;
2144      switch (pPlayer->weaponState)
2145      {
2146      case 4:
2147          if (!pPlayer->input.buttonFlags.shoot2 || bUseShootAsThrow)
2148              pPlayer->weaponState = 5;
2149          return 1;
2150      case 5:
2151          if (pPlayer->input.buttonFlags.shoot2 && !bUseShootAsThrow)
2152          {
2153              pPlayer->weaponState = 1;
2154              pPlayer->fuseTime = pPlayer->weaponTimer;
2155              StartQAV(pPlayer, 22, nClientDropBundle, 0);
2156          }
2157          else if (pPlayer->input.buttonFlags.shoot)
2158          {
2159              pPlayer->weaponState = 6;
2160              pPlayer->fuseTime = 0;
2161              pPlayer->throwTime = (int)gFrameClock;
2162          }
2163          return 1;
2164      case 6:
2165      {
2166          pPlayer->throwPower = ClipHigh(divscale16((int)gFrameClock-pPlayer->throwTime,240), 65536);
2167          if (!pPlayer->input.buttonFlags.shoot)
2168          {
2169              if (!pPlayer->fuseTime)
2170                  pPlayer->fuseTime = pPlayer->weaponTimer;
2171              pPlayer->weaponState = 1;
2172              StartQAV(pPlayer, 23, nClientThrowBundle, 0);
2173          }
2174          return 1;
2175      }
2176      }
2177      return 0;
2178  }
2179  
2180  char processProxy(PLAYER *pPlayer)
2181  {
2182      switch (pPlayer->weaponState)
2183      {
2184      case 9:
2185          pPlayer->throwPower = ClipHigh(divscale16((int)gFrameClock-pPlayer->throwTime,240), 65536);
2186          pPlayer->weaponTimer = 0;
2187          if (!pPlayer->input.buttonFlags.shoot)
2188          {
2189              pPlayer->weaponState = 8;
2190              StartQAV(pPlayer, 29, nClientThrowProx, 0);
2191          }
2192          return 1;
2193      }
2194      return 0;
2195  }
2196  
2197  char processRemote(PLAYER *pPlayer)
2198  {
2199      switch (pPlayer->weaponState)
2200      {
2201      case 13:
2202          pPlayer->throwPower = ClipHigh(divscale16((int)gFrameClock-pPlayer->throwTime,240), 65536);
2203          if (!pPlayer->input.buttonFlags.shoot)
2204          {
2205              pPlayer->weaponState = 11;
2206              StartQAV(pPlayer, 39, nClientThrowRemote, 0);
2207          }
2208          return 1;
2209      }
2210      return 0;
2211  }
2212  
2213  void processPitchfork(PLAYER *pPlayer)
2214  {
2215      if (pPlayer->weaponState < 3)
2216          return;
2217      const int chargeSpeed = powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup ? 240 : 210; // charge slower when quad damage is active (fireball alt fire attack)
2218      pPlayer->throwPower = ClipHigh(divscale16((int)gFrameClock-pPlayer->throwTime, chargeSpeed), 65536);
2219      if ((pPlayer->weaponState == 3) && (pPlayer->throwPower == 65536) && powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup) // if maxed throwing power and quad damage is active
2220      {
2221          sfxPlay3DSound(pPlayer->pSprite, 361, 1, 0); // flame sfx
2222          sfxPlay3DSound(pPlayer->pSprite, 2200, 2, 0); // pod hit sfx
2223          pPlayer->weaponState = 4; // ready up missile attack (allow missile state to hold even after quad damage runs out)
2224      }
2225      if (!pPlayer->input.buttonFlags.shoot2)
2226      {
2227          StartQAV(pPlayer, 2, -1, 0);
2228          FirePitchfork(1, pPlayer);
2229          pPlayer->weaponState = 0;
2230          pPlayer->throwPower = 0;
2231      }
2232  }
2233  
2234  void processLifeLeech(PLAYER *pPlayer)
2235  {
2236      if (pPlayer->weaponState != 3)
2237          return;
2238      if ((gGameOptions.nGameType <= kGameTypeCoop) && !checkAmmo2(pPlayer, 8, 1) && pPlayer->pXSprite->health < (25 << 4))
2239      {
2240          sfxPlay3DSound(pPlayer->pSprite, 494, 2, 0);
2241          StartQAV(pPlayer, 116, nClientFireLifeLeech, 0);
2242          pPlayer->weaponState = 2;
2243          pPlayer->throwPower = 0;
2244          return;
2245      }
2246      pPlayer->throwPower = ClipHigh(divscale16((int)gFrameClock-pPlayer->throwTime,240), 65536);
2247      if (!pPlayer->input.buttonFlags.shoot2)
2248      {
2249          StartQAV(pPlayer, 119, -1, 0);
2250          AltFireLifeLeech(1, pPlayer);
2251          pPlayer->weaponState = -1;
2252          pPlayer->throwPower = 0;
2253          if (gInfiniteAmmo && !VanillaMode()) // keep lifeleech after dropping
2254          {
2255              pPlayer->hasWeapon[kWeaponLifeLeech] = 1;
2256              pPlayer->weaponState = 2;
2257          }
2258      }
2259  }
2260  
2261  char processLeech(PLAYER *pPlayer)
2262  {
2263      switch (pPlayer->weaponState)
2264      {
2265      case 4:
2266          pPlayer->weaponState = 6;
2267          StartQAV(pPlayer, 114, nClientFireLifeLeech, 1);
2268          return 1;
2269      case 6:
2270          if (!pPlayer->input.buttonFlags.shoot2)
2271          {
2272              pPlayer->weaponState = 2;
2273              StartQAV(pPlayer, 118, -1, 0);
2274              return 1;
2275          }
2276          break;
2277      case 8:
2278          pPlayer->weaponState = 2;
2279          StartQAV(pPlayer, 118, -1, 0);
2280          return 1;
2281      }
2282      return 0;
2283  }
2284  
2285  char processTesla(PLAYER *pPlayer)
2286  {
2287      switch (pPlayer->weaponState)
2288      {
2289      case 4:
2290          pPlayer->weaponState = 5;
2291          if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2292              StartQAV(pPlayer, 84, nClientFireTesla, 1);
2293          else
2294              StartQAV(pPlayer, 77, nClientFireTesla, 1);
2295          return 1;
2296      case 5:
2297          if (!pPlayer->input.buttonFlags.shoot)
2298          {
2299              pPlayer->weaponState = 2;
2300              if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2301                  StartQAV(pPlayer, 87, -1, 0);
2302              else
2303                  StartQAV(pPlayer, 80, -1, 0);
2304              return 1;
2305          }
2306          break;
2307      case 7:
2308          pPlayer->weaponState = 2;
2309          if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2310              StartQAV(pPlayer, 87, -1, 0);
2311          else
2312              StartQAV(pPlayer, 80, -1, 0);
2313          return 1;
2314      }
2315      return 0;
2316  }
2317  
2318  void WeaponProcess(PLAYER *pPlayer) {
2319  
2320      pPlayer->flashEffect = ClipLow(pPlayer->flashEffect - 1, 0);
2321      
2322      #ifdef NOONE_EXTENSIONS
2323      if (gPlayerCtrl[pPlayer->nPlayer].qavScene.index >= 0 && pPlayer->pXSprite->health > 0) {
2324          playerQavSceneProcess(pPlayer, &gPlayerCtrl[pPlayer->nPlayer].qavScene);
2325          UpdateAimVector(pPlayer);
2326          return;
2327      }
2328      #endif
2329  
2330      char bAlreadySetLastWeapon = 0;
2331      char bTNTRemoteProxyCycling = 1;
2332      if (pPlayer->input.newWeapon > kWeaponMax && !VanillaMode(true)) // by default, the TNT weapon will cycle between remote/proxy variants - if the user selected the weapon with the radial menu, disable this behavior
2333      {
2334          pPlayer->input.newWeapon -= kWeaponMax;
2335          bTNTRemoteProxyCycling = 0;
2336      }
2337      if (pPlayer->pXSprite->health == 0)
2338      {
2339          pPlayer->qavLoop = 0;
2340          sfxKill3DSound(pPlayer->pSprite, 1, -1);
2341      }
2342      if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->curWeapon))
2343      {
2344          const int prevWeapon = pPlayer->curWeapon;
2345          if (checkLitSprayOrTNT(pPlayer))
2346          {
2347              if (pPlayer->curWeapon == kWeaponSprayCan)
2348              {
2349                  pPlayer->fuseTime = pPlayer->weaponTimer;
2350                  DropCan(1, pPlayer);
2351                  pPlayer->weaponState = 3;
2352              }
2353              else if (pPlayer->curWeapon == kWeaponTNT)
2354              {
2355                  pPlayer->fuseTime = pPlayer->weaponTimer;
2356                  DropBundle(1, pPlayer);
2357                  pPlayer->weaponState = 1;
2358              }
2359          }
2360          WeaponLower(pPlayer);
2361          pPlayer->throwPower = 0;
2362          if (!VanillaMode()) // if not in vanilla mode, find next weapon to switch to
2363          {
2364              if (WeaponIsEquipable(pPlayer, pPlayer->lastWeapon)) // switch to last weapon if available
2365              {
2366                  pPlayer->nextWeapon = pPlayer->lastWeapon;
2367              }
2368              else // find a suitable next weapon
2369              {
2370                  int t;
2371                  const char weapon = WeaponFindLoaded(pPlayer, &t);
2372                  if (WeaponIsEquipable(pPlayer, weapon))
2373                  {
2374                      pPlayer->weaponMode[weapon] = t;
2375                      pPlayer->nextWeapon = weapon;
2376                  }
2377                  else // couldn't find anything, use pitchfork
2378                  {
2379                      pPlayer->nextWeapon = kWeaponPitchfork;
2380                  }
2381              }
2382              pPlayer->lastWeapon = prevWeapon;
2383              bAlreadySetLastWeapon = 1;
2384              bTNTRemoteProxyCycling = 0;
2385          }
2386      }
2387      WeaponPlay(pPlayer);
2388      UpdateAimVector(pPlayer);
2389      pPlayer->weaponTimer -= kTicsPerFrame;
2390      char bShoot = pPlayer->input.buttonFlags.shoot;
2391      char bShoot2 = pPlayer->input.buttonFlags.shoot2;
2392      if (pPlayer->qavLoop && pPlayer->pXSprite->health > 0)
2393      {
2394          if (bShoot && CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1))
2395          {
2396              while (pPlayer->weaponTimer <= 0)
2397                  pPlayer->weaponTimer += weaponQAV[pPlayer->weaponQav]->at10;
2398          }
2399          else
2400          {
2401              pPlayer->weaponTimer = 0;
2402              pPlayer->qavLoop = 0;
2403          }
2404          return;
2405      }
2406      pPlayer->weaponTimer = ClipLow(pPlayer->weaponTimer, 0);
2407      switch (pPlayer->curWeapon)
2408      {
2409      case kWeaponSprayCan:
2410          if (processSprayCan(pPlayer))
2411              return;
2412          break;
2413      case kWeaponTNT:
2414          if (processTNT(pPlayer))
2415              return;
2416          break;
2417      case kWeaponProxyTNT:
2418          if (processProxy(pPlayer))
2419              return;
2420          break;
2421      case kWeaponRemoteTNT:
2422          if (processRemote(pPlayer))
2423              return;
2424          break;
2425      case kWeaponLifeLeech:
2426          if (WeaponsNotBlood() && !VanillaMode()) // allow player to charge up throw like tnt
2427              processLifeLeech(pPlayer);
2428          break;
2429      case kWeaponPitchfork:
2430          if (WeaponsNotBlood() && !VanillaMode()) // allow player to charge up pitchfork attack
2431              processPitchfork(pPlayer);
2432          break;
2433      }
2434      if (pPlayer->weaponTimer > 0)
2435          return;
2436      if (pPlayer->pXSprite->health == 0 || pPlayer->curWeapon == kWeaponNone)
2437          pPlayer->weaponQav = -1;
2438      switch (pPlayer->curWeapon)
2439      {
2440      case kWeaponLifeLeech:
2441          if (processLeech(pPlayer))
2442              return;
2443          break;
2444      case kWeaponTesla:
2445          if (processTesla(pPlayer))
2446              return;
2447          break;
2448      }
2449      if (pPlayer->nextWeapon && VanillaMode())
2450      {
2451          sfxKill3DSound(pPlayer->pSprite, -1, 441);
2452          pPlayer->weaponState = 0;
2453          pPlayer->input.newWeapon = pPlayer->nextWeapon;
2454          pPlayer->nextWeapon = kWeaponNone;
2455      }
2456      if (!gProfile[pPlayer->nPlayer].bWeaponFastSwitch && (pPlayer->curWeapon == kWeaponNone) && (pPlayer->input.newWeapon != kWeaponNone) && !VanillaMode()) // if fast weapon select is off, and player is switching weapon (and not holstered), clear next/prev/last keyflags
2457      {
2458          pPlayer->input.keyFlags.nextWeapon = 0;
2459          pPlayer->input.keyFlags.prevWeapon = 0;
2460          pPlayer->input.keyFlags.lastWeapon = 0;
2461      }
2462      const KEYFLAGS oldKeyFlags = pPlayer->input.keyFlags; // used to fix next/prev weapon issue for banned weapons
2463      if (pPlayer->input.keyFlags.lastWeapon)
2464      {
2465          pPlayer->input.keyFlags.lastWeapon = 0;
2466          if (!VanillaMode())
2467          {
2468              int weapon = pPlayer->curWeapon;
2469              if (!weapon && gProfile[pPlayer->nPlayer].bWeaponFastSwitch) // if fast weapon select is on, and player is switching weapon, use new weapon as the current weapon
2470                  weapon = pPlayer->input.newWeapon;
2471              if (weapon && (weapon != pPlayer->lastWeapon)) // if current weapon is different to last weapon
2472              {
2473                  if (WeaponIsEquipable(pPlayer, pPlayer->lastWeapon)) // if last weapon can be switched to
2474                  {
2475                      pPlayer->input.keyFlags.nextWeapon = 0;
2476                      pPlayer->input.keyFlags.prevWeapon = 0;
2477                      pPlayer->nextWeapon = kWeaponNone;
2478                      pPlayer->weaponMode[pPlayer->lastWeapon] = 0;
2479                      pPlayer->input.newWeapon = pPlayer->lastWeapon;
2480                      pPlayer->lastWeapon = weapon;
2481                      bAlreadySetLastWeapon = 1;
2482                      bTNTRemoteProxyCycling = 0;
2483                  }
2484              }
2485          }
2486          else
2487          {
2488              viewSetMessage("Last weapon button disabled for vanilla mode...", 0, MESSAGE_PRIORITY_PICKUP);
2489          }
2490      }
2491      if (pPlayer->input.keyFlags.nextWeapon)
2492      {
2493          pPlayer->input.keyFlags.nextWeapon = 0;
2494          if (VanillaMode())
2495          {
2496              pPlayer->weaponState = 0;
2497          }
2498          pPlayer->nextWeapon = kWeaponNone;
2499          int t;
2500          char weapon = WeaponFindNext(pPlayer, &t, 1);
2501          pPlayer->weaponMode[weapon] = t;
2502          if (VanillaMode())
2503          {
2504              if (pPlayer->curWeapon)
2505              {
2506                  WeaponLower(pPlayer);
2507                  pPlayer->nextWeapon = weapon;
2508                  return;
2509              }
2510          }
2511          else
2512          {
2513              bTNTRemoteProxyCycling = 0; // next weapon should bypass tnt cycling logic
2514          }
2515          pPlayer->input.newWeapon = weapon;
2516      }
2517      if (pPlayer->input.keyFlags.prevWeapon)
2518      {
2519          pPlayer->input.keyFlags.prevWeapon = 0;
2520          if (VanillaMode())
2521          {
2522              pPlayer->weaponState = 0;
2523          }
2524          pPlayer->nextWeapon = kWeaponNone;
2525          int t;
2526          char weapon = WeaponFindNext(pPlayer, &t, 0);
2527          pPlayer->weaponMode[weapon] = t;
2528          if (VanillaMode())
2529          {
2530              if (pPlayer->curWeapon)
2531              {
2532                  WeaponLower(pPlayer);
2533                  pPlayer->nextWeapon = weapon;
2534                  return;
2535              }
2536          }
2537          else
2538          {
2539              bTNTRemoteProxyCycling = 0; // prev weapon should bypass tnt cycling logic
2540          }
2541          pPlayer->input.newWeapon = weapon;
2542      }
2543      if (pPlayer->nextWeapon && !VanillaMode())
2544      {
2545          sfxKill3DSound(pPlayer->pSprite, -1, 441);
2546          pPlayer->input.newWeapon = pPlayer->nextWeapon;
2547          pPlayer->nextWeapon = kWeaponNone;
2548      }
2549      if (pPlayer->weaponState == -1)
2550      {
2551          pPlayer->weaponState = 0;
2552          int t;
2553          char weapon = WeaponFindLoaded(pPlayer, &t);
2554          pPlayer->weaponMode[weapon] = t;
2555          if (pPlayer->curWeapon)
2556          {
2557              pPlayer->lastWeapon = pPlayer->curWeapon;
2558              WeaponLower(pPlayer);
2559              pPlayer->nextWeapon = weapon;
2560              return;
2561          }
2562          pPlayer->input.newWeapon = weapon;
2563      }
2564      if (pPlayer->input.newWeapon)
2565      {
2566          if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->input.newWeapon) && !checkLitSprayOrTNT(pPlayer) && !VanillaMode()) // skip banned weapons when underwater and using next/prev weapon key inputs
2567          {
2568              if (oldKeyFlags.nextWeapon || oldKeyFlags.prevWeapon) // if player switched weapons
2569              {
2570                  const char oldWeapon = pPlayer->curWeapon;
2571                  pPlayer->curWeapon = pPlayer->input.newWeapon; // set current banned weapon to curweapon so WeaponFindNext() can find the next weapon
2572                  for (int i = 0; i < 3; i++) // attempt to find a non-banned weapon
2573                  {
2574                      pPlayer->curWeapon = WeaponFindNext(pPlayer, NULL, (char)(oldKeyFlags.nextWeapon == 1));
2575                      if (!BannedUnderwater(pPlayer->curWeapon)) // if new weapon is not a banned weapon, set to new current weapon
2576                      {
2577                          pPlayer->input.newWeapon = pPlayer->curWeapon;
2578                          pPlayer->weaponMode[pPlayer->input.newWeapon] = 0;
2579                          break;
2580                      }
2581                  }
2582                  pPlayer->curWeapon = oldWeapon;
2583              }
2584          }
2585          if (bTNTRemoteProxyCycling && (pPlayer->input.newWeapon == kWeaponTNT))
2586          {
2587              if (pPlayer->curWeapon == kWeaponTNT)
2588              {
2589                  if (checkAmmo2(pPlayer, 10, 1))
2590                      pPlayer->input.newWeapon = kWeaponProxyTNT;
2591                  else if (checkAmmo2(pPlayer, 11, 1))
2592                      pPlayer->input.newWeapon = kWeaponRemoteTNT;
2593              }
2594              else if (pPlayer->curWeapon == kWeaponProxyTNT)
2595              {
2596                  if (checkAmmo2(pPlayer, 11, 1))
2597                      pPlayer->input.newWeapon = kWeaponRemoteTNT;
2598                  else if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
2599                      pPlayer->input.newWeapon = kWeaponTNT;
2600              }
2601              else if (pPlayer->curWeapon == kWeaponRemoteTNT)
2602              {
2603                  if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
2604                      pPlayer->input.newWeapon = kWeaponTNT;
2605                  else if (checkAmmo2(pPlayer, 10, 1))
2606                      pPlayer->input.newWeapon = kWeaponProxyTNT;
2607              }
2608              else
2609              {
2610                  if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0)
2611                      pPlayer->input.newWeapon = kWeaponTNT;
2612                  else if (checkAmmo2(pPlayer, 10, 1))
2613                      pPlayer->input.newWeapon = kWeaponProxyTNT;
2614                  else if (checkAmmo2(pPlayer, 11, 1))
2615                      pPlayer->input.newWeapon = kWeaponRemoteTNT;
2616              }
2617          }
2618          else if ((pPlayer->input.newWeapon == kWeaponSprayCan) && !VanillaMode())
2619          {
2620              if ((pPlayer->curWeapon == kWeaponSprayCan) && (pPlayer->weaponState == 2)) // fix spray can state glitch when switching from spray to tnt and back quickly
2621              {
2622                  pPlayer->weaponState = 1;
2623                  pPlayer->input.newWeapon = kWeaponNone;
2624                  return;
2625              }
2626          }
2627          if ((pPlayer->pXSprite->health > 0) && !pPlayer->hasWeapon[pPlayer->input.newWeapon] && !pPlayer->curWeapon && !VanillaMode()) // if trying to switch to missing/out of ammo weapon, switch to loaded weapon instead of holstering
2628          {
2629              int t;
2630              char weapon = WeaponFindLoaded(pPlayer, &t);
2631              pPlayer->weaponMode[weapon] = t;
2632              pPlayer->input.newWeapon = weapon;
2633              return;
2634          }
2635          if (pPlayer->pXSprite->health == 0 || !pPlayer->hasWeapon[pPlayer->input.newWeapon])
2636          {
2637              pPlayer->input.newWeapon = kWeaponNone;
2638              return;
2639          }
2640          if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->input.newWeapon) && !checkLitSprayOrTNT(pPlayer))
2641          {
2642              pPlayer->input.newWeapon = kWeaponNone;
2643              return;
2644          }
2645          int nWeapon = pPlayer->input.newWeapon;
2646          int v4c = weaponModes[nWeapon].at0;
2647          if (!pPlayer->curWeapon)
2648          {
2649              int nAmmoType = weaponModes[nWeapon].at4;
2650              if (v4c > 1)
2651              {
2652                  if (CheckAmmo(pPlayer, nAmmoType, 1) || nAmmoType == 11)
2653                      WeaponRaise(pPlayer);
2654                  pPlayer->input.newWeapon = kWeaponNone;
2655              }
2656              else
2657              {
2658                  if (CheckWeaponAmmo(pPlayer, nWeapon, nAmmoType, 1))
2659                      WeaponRaise(pPlayer);
2660                  else
2661                  {
2662                      pPlayer->weaponState = 0;
2663                      int t;
2664                      char weapon = WeaponFindLoaded(pPlayer, &t);
2665                      pPlayer->weaponMode[weapon] = t;
2666                      if (pPlayer->curWeapon)
2667                      {
2668                          WeaponLower(pPlayer);
2669                          pPlayer->nextWeapon = weapon;
2670                          return;
2671                      }
2672                      pPlayer->input.newWeapon = weapon;
2673                  }
2674              }
2675              return;
2676          }
2677          if (nWeapon == pPlayer->curWeapon && v4c <= 1)
2678          {
2679              pPlayer->input.newWeapon = kWeaponNone;
2680              return;
2681          }
2682          int i = 0;
2683          if (nWeapon == pPlayer->curWeapon)
2684              i = 1;
2685          for (; i <= v4c; i++)
2686          {
2687              int v6c = (pPlayer->weaponMode[nWeapon]+i)%v4c;
2688              if (CheckWeaponAmmo(pPlayer, nWeapon, weaponModes[nWeapon].at4, 1))
2689              {
2690                  if (!bAlreadySetLastWeapon) // set new weapon to last weapon slot
2691                      pPlayer->lastWeapon = pPlayer->curWeapon;
2692                  WeaponLower(pPlayer);
2693                  pPlayer->weaponMode[nWeapon] = v6c;
2694                  return;
2695              }
2696          }
2697          pPlayer->input.newWeapon = kWeaponNone;
2698          return;
2699      }
2700      if (pPlayer->curWeapon && !CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1) && pPlayer->weaponAmmo != 11)
2701      {
2702          pPlayer->weaponState = -1;
2703          return;
2704      }
2705      if (bShoot)
2706      {
2707          switch (pPlayer->curWeapon)
2708          {
2709          case kWeaponPitchfork:
2710              if ((pPlayer->weaponState >= 3) && WeaponsNotBlood() && !VanillaMode()) // do not allow player to use pitchfork while charging up attack
2711                  break;
2712              StartQAV(pPlayer, 2, nClientFirePitchfork, 0);
2713              return;
2714          case kWeaponSprayCan:
2715              switch (pPlayer->weaponState)
2716              {
2717              case 3:
2718                  pPlayer->weaponState = 4;
2719                  StartQAV(pPlayer, 10, nClientFireSpray, 1);
2720                  return;
2721              }
2722              break;
2723          case kWeaponTNT:
2724              switch (pPlayer->weaponState)
2725              {
2726              case 3:
2727                  pPlayer->weaponState = 6;
2728                  pPlayer->fuseTime = -1;
2729                  pPlayer->throwTime = (int)gFrameClock;
2730                  StartQAV(pPlayer, 21, nClientExplodeBundle, 0);
2731                  return;
2732              }
2733              break;
2734          case kWeaponProxyTNT:
2735              switch (pPlayer->weaponState)
2736              {
2737              case 7:
2738                  pPlayer->weaponQav = 27;
2739                  pPlayer->weaponState = 9;
2740                  pPlayer->throwTime = (int)gFrameClock;
2741                  return;
2742              }
2743              break;
2744          case kWeaponRemoteTNT:
2745              switch (pPlayer->weaponState)
2746              {
2747              case 10:
2748                  pPlayer->weaponQav = 36;
2749                  pPlayer->weaponState = 13;
2750                  pPlayer->throwTime = (int)gFrameClock;
2751                  return;
2752              case 11:
2753                  pPlayer->weaponState = 12;
2754                  StartQAV(pPlayer, 40, nClientFireRemote, 0);
2755                  return;
2756              }
2757              break;
2758          case kWeaponShotgun:
2759              if (powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup && WeaponsNotBlood() && !VanillaMode()) // if quad damage is active, do not reload shotgun
2760              {
2761                  pPlayer->weaponState = 2;
2762                  StartQAV(pPlayer, 55, nClientFireShotgun, 0);
2763                  return;
2764              }
2765              switch (pPlayer->weaponState)
2766              {
2767              case 7:
2768                  pPlayer->weaponState = 6;
2769                  StartQAV(pPlayer, 61, nClientFireShotgun, 0);
2770                  return;
2771              case 3:
2772                  pPlayer->weaponState = 2;
2773                  StartQAV(pPlayer, 54, nClientFireShotgun, 0);
2774                  return;
2775              case 2:
2776                  pPlayer->weaponState = 1;
2777                  StartQAV(pPlayer, 55, nClientFireShotgun, 0);
2778                  return;
2779              }
2780              break;
2781          case kWeaponTommy:
2782              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 3, 2))
2783                  StartQAV(pPlayer, 71, nClientFireTommy, 1);
2784              else
2785                  StartQAV(pPlayer, 66, nClientFireTommy, 1);
2786              return;
2787          case kWeaponFlare:
2788              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 1, 2))
2789                  StartQAV(pPlayer, 48, nClientFireFlare, 0);
2790              else
2791                  StartQAV(pPlayer, 43, nClientFireFlare, 0);
2792              return;
2793          case kWeaponVoodoo:
2794          {
2795              static int nChance[] = { 0xa000, 0xc000, 0xe000, 0x10000 };
2796              int nRand = wrand()*2;
2797              int i;
2798              for (i = 0; nChance[i] < nRand; i++)
2799              {
2800              }
2801              pPlayer->voodooTarget = pPlayer->aimTarget;
2802              if (pPlayer->voodooTarget == -1 || sprite[pPlayer->voodooTarget].statnum != kStatDude)
2803                  i = 4;
2804              StartQAV(pPlayer,103+i, nClientFireVoodoo, 0);
2805              return;
2806          }
2807          case kWeaponTesla:
2808              switch (pPlayer->weaponState)
2809              {
2810              case 2:
2811                  pPlayer->weaponState = 4;
2812                  if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2813                      StartQAV(pPlayer, 84, nClientFireTesla, 0);
2814                  else
2815                      StartQAV(pPlayer, 77, nClientFireTesla, 0);
2816                  return;
2817              case 5:
2818                  if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2819                      StartQAV(pPlayer, 84, nClientFireTesla, 0);
2820                  else
2821                      StartQAV(pPlayer, 77, nClientFireTesla, 0);
2822                  return;
2823              }
2824              break;
2825          case kWeaponNapalm:
2826              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && (VanillaMode() || (gInfiniteAmmo || CheckAmmo(pPlayer, 4, 2))))
2827                  StartQAV(pPlayer, 122, nClientFireNapalm, 0);
2828              else
2829                  StartQAV(pPlayer, 91, nClientFireNapalm, 0);
2830              return;
2831          case kWeaponLifeLeech:
2832              if (WeaponsNotBlood() && !VanillaMode() && (pPlayer->weaponState == 3)) // if currently throwing lifeleech, don't allow lifeleech to be fired
2833                  break;
2834              sfxPlay3DSound(pPlayer->pSprite, 494, 2, 0);
2835              StartQAV(pPlayer, 116, nClientFireLifeLeech, 0);
2836              return;
2837          case kWeaponBeast:
2838              StartQAV(pPlayer, 95+Random(4), nClientFireBeast, 0);
2839              return;
2840          }
2841      }
2842      if (bShoot2)
2843      {
2844          switch (pPlayer->curWeapon)
2845          {
2846          case kWeaponPitchfork:
2847              if (!VanillaMode())
2848              {
2849                  if (gAlphaPitchfork) // if alpha pitchfork cheat is active
2850                  {
2851                      StartQAV(pPlayer, 2, nClientFirePitchfork, 0); // default pitchfork attack
2852                      playerFireMissile(pPlayer, -50, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireball);
2853                      pPlayer->flashEffect = 1;
2854                      return;
2855                  }
2856                  else if (WeaponsNotBlood()) // allow player to charge up pitchfork attack
2857                  {
2858                      pPlayer->weaponTimer = 1;
2859                      pPlayer->qavLoop = 0;
2860                      if (pPlayer->weaponState >= 3)
2861                          return;
2862                      pPlayer->weaponState = 3;
2863                      pPlayer->throwTime = (int)gFrameClock;
2864                      pPlayer->throwPower = 0;
2865                      return;
2866                  }
2867              }
2868              StartQAV(pPlayer, 2, nClientFirePitchfork, 0); // default pitchfork attack
2869              return;
2870          case kWeaponSprayCan:
2871              switch (pPlayer->weaponState)
2872              {
2873              case 3:
2874                  pPlayer->weaponState = 5;
2875                  StartQAV(pPlayer, 12, nClientExplodeCan, 0);
2876                  return;
2877              }
2878              break;
2879          case kWeaponTNT:
2880              switch (pPlayer->weaponState)
2881              {
2882              case 3:
2883                  pPlayer->weaponState = 4;
2884                  StartQAV(pPlayer, 21, nClientExplodeBundle, 0);
2885                  return;
2886              case 7:
2887                  pPlayer->weaponState = 8;
2888                  StartQAV(pPlayer, 28, nClientDropProx, 0);
2889                  return;
2890              case 10:
2891                  pPlayer->weaponState = 11;
2892                  StartQAV(pPlayer, 38, nClientDropRemote, 0);
2893                  return;
2894              case 11:
2895                  if (pPlayer->ammoCount[11] > 0)
2896                  {
2897                      pPlayer->weaponState = 10;
2898                      StartQAV(pPlayer, 30, -1, 0);
2899                  }
2900                  return;
2901              }
2902              break;
2903          case kWeaponProxyTNT:
2904              switch (pPlayer->weaponState)
2905              {
2906              case 7:
2907                  pPlayer->weaponState = 8;
2908                  StartQAV(pPlayer, 28, nClientDropProx, 0);
2909                  return;
2910              }
2911              break;
2912          case kWeaponRemoteTNT:
2913              switch (pPlayer->weaponState)
2914              {
2915              case 10:
2916                  pPlayer->weaponState = 11;
2917                  StartQAV(pPlayer, 38, nClientDropRemote, 0);
2918                  return;
2919              case 11:
2920                  if (pPlayer->ammoCount[11] > 0)
2921                  {
2922                      pPlayer->weaponState = 10;
2923                      StartQAV(pPlayer, 30, -1, 0);
2924                  }
2925                  return;
2926              }
2927              break;
2928          case kWeaponShotgun:
2929              if (powerupCheck(pPlayer, kPwUpTwoGuns) && gGameOptions.bQuadDamagePowerup && WeaponsNotBlood() && !VanillaMode()) // if quad damage is active, do not reload shotgun
2930              {
2931                  pPlayer->weaponState = 2;
2932                  StartQAV(pPlayer, 56, nClientFireShotgun, 0);
2933                  return;
2934              }
2935              if (!powerupCheck(pPlayer, kPwUpTwoGuns) && gShotgunAltFireReload && (numplayers == 1) && !VanillaMode()) // alt fire always reloads if only one shell is loaded
2936              {
2937                  if ((pPlayer->weaponState == 2) && CheckAmmo(pPlayer, 2, 1) && (pPlayer->ammoCount[2] || gInfiniteAmmo))
2938                  {
2939                      sfxPlay3DSound(pPlayer->pSprite, 410, 3, 2);
2940                      StartQAV(pPlayer, 57, nClientEjectShell, 0);
2941                      pPlayer->weaponState = 3;
2942                      return;
2943                  }
2944              }
2945              switch (pPlayer->weaponState)
2946              {
2947              case 7:
2948                  pPlayer->weaponState = 6;
2949                  StartQAV(pPlayer, 62, nClientFireShotgun, 0);
2950                  return;
2951              case 3:
2952                  pPlayer->weaponState = 1;
2953                  StartQAV(pPlayer, 56, nClientFireShotgun, 0);
2954                  return;
2955              case 2:
2956                  pPlayer->weaponState = 1;
2957                  StartQAV(pPlayer, 55, nClientFireShotgun, 0);
2958                  return;
2959              }
2960              break;
2961          case kWeaponTommy:
2962              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 3, 2))
2963                  StartQAV(pPlayer, 73, nClientAltFireSpread2, 0);
2964              else
2965                  StartQAV(pPlayer, 67, nClientAltFireSpread2, 0);
2966              return;
2967          case kWeaponVoodoo:
2968              sfxPlay3DSound(pPlayer->pSprite, 461, 2, 0);
2969              StartQAV(pPlayer, 110, nClientAltFireVoodoo, 0);
2970              return;
2971  #if 0
2972          case kWeaponFlare:
2973              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 1, 2))
2974                  StartQAV(pPlayer, 48, nClientFireFlare, 0);
2975              else
2976                  StartQAV(pPlayer, 43, nClientFireFlare, 0);
2977              return;
2978  #endif
2979          case kWeaponTesla:
2980              if (checkAmmo2(pPlayer, 7, 35))
2981              {
2982                  if (checkAmmo2(pPlayer, 7, 70) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2983                      StartQAV(pPlayer, 85, nClientFireTesla, 0);
2984                  else
2985                      StartQAV(pPlayer, 78, nClientFireTesla, 0);
2986              }
2987              else
2988              {
2989                  if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2990                      StartQAV(pPlayer, 84, nClientFireTesla, 0);
2991                  else
2992                      StartQAV(pPlayer, 77, nClientFireTesla, 0);
2993              }
2994              return;
2995          case kWeaponNapalm:
2996              if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()))
2997              {
2998                  if (WeaponsV10x() && !VanillaMode()) // by NoOne: allow napalm launcher alt fire act like in v1.0x versions
2999                      StartQAV(pPlayer, 123, nClientFireNapalm2, 0);
3000                  else
3001                      StartQAV(pPlayer, 122, nClientAltFireNapalm, 0);
3002              }
3003              else
3004                  StartQAV(pPlayer, 91, (WeaponsV10x() && !VanillaMode()) ? nClientFireNapalm : nClientAltFireNapalm, 0);
3005              return;
3006          case kWeaponFlare:
3007              if (CheckAmmo(pPlayer, 1, 8))
3008              {
3009                  if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 1, 16))
3010                      StartQAV(pPlayer, 48, nClientAltFireFlare, 0);
3011                  else
3012                      StartQAV(pPlayer, 43, nClientAltFireFlare, 0);
3013              }
3014              else
3015              {
3016                  if (powerupCheck(pPlayer, kPwUpTwoGuns) && (!gGameOptions.bQuadDamagePowerup || VanillaMode()) && checkAmmo2(pPlayer, 1, 2))
3017                      StartQAV(pPlayer, 48, nClientFireFlare, 0);
3018                  else
3019                      StartQAV(pPlayer, 43, nClientFireFlare, 0);
3020              }
3021              return;
3022          case kWeaponLifeLeech:
3023              if (WeaponsNotBlood() && !VanillaMode()) // allow player to throw lifeleech like tnt
3024              {
3025                  pPlayer->weaponTimer = 1;
3026                  pPlayer->qavLoop = 0;
3027                  if (pPlayer->weaponState == 3)
3028                      return;
3029                  pPlayer->weaponState = 3;
3030                  pPlayer->throwTime = (int)gFrameClock;
3031                  pPlayer->throwPower = 0;
3032              }
3033              else if (gGameOptions.nGameType <= kGameTypeCoop && !checkAmmo2(pPlayer, 8, 1) && pPlayer->pXSprite->health < (25 << 4))
3034              {
3035                  sfxPlay3DSound(pPlayer->pSprite, 494, 2, 0);
3036                  StartQAV(pPlayer, 116, nClientFireLifeLeech, 0);
3037              }
3038              else
3039              {
3040                  StartQAV(pPlayer, 119, -1, 0);
3041                  AltFireLifeLeech(1, pPlayer);
3042                  pPlayer->weaponState = -1;
3043                  if (gInfiniteAmmo && !VanillaMode()) // keep lifeleech after dropping
3044                  {
3045                      pPlayer->hasWeapon[kWeaponLifeLeech] = 1;
3046                      pPlayer->weaponState = 2;
3047                  }
3048              }
3049              return;
3050          }
3051      }
3052      WeaponUpdateState(pPlayer);
3053  }
3054  
3055  void teslaHit(spritetype *pMissile, int a2)
3056  {
3057      char sectmap[bitmap_size(kMaxSectors)];
3058      int x = pMissile->x;
3059      int y = pMissile->y;
3060      int z = pMissile->z;
3061      int nDist = 300;
3062      int nSector = pMissile->sectnum;
3063      int nOwner = actSpriteOwnerToSpriteId(pMissile);
3064      gAffectedSectors[0] = -1;
3065      gAffectedXWalls[0] = -1;
3066      const bool bAccurateCheck = (nOwner >= 0) && !VanillaMode() && IsDudeSprite(&sprite[nOwner]); // use new sector checking logic
3067      GetClosestSpriteSectors(nSector, x, y, nDist, gAffectedSectors, sectmap, gAffectedXWalls, bAccurateCheck);
3068      char v4 = 1;
3069      int v24 = -1;
3070      actHitcodeToData(a2, &gHitInfo, &v24, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
3071      if (a2 == 3 && v24 >= 0 && sprite[v24].statnum == kStatDude)
3072          v4 = 0;
3073      for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
3074      {
3075          if (nSprite != nOwner || v4)
3076          {
3077              spritetype *pSprite = &sprite[nSprite];
3078              if (pSprite->flags&32)
3079                  continue;
3080              if (TestBitString(sectmap, pSprite->sectnum) && CheckProximity(pSprite, x, y, z, nSector, nDist))
3081              {
3082                  int dx = pMissile->x-pSprite->x;
3083                  int dy = pMissile->y-pSprite->y;
3084                  int nDamage = (nDist-(ksqrt(dx*dx+dy*dy)>>4)+20)>>1;
3085                  if (nDamage < 0)
3086                      nDamage = 10;
3087                  if (nSprite == nOwner)
3088                      nDamage /= 2;
3089                  actDamageSprite(nOwner, pSprite, kDamageTesla, nDamage<<4);
3090              }
3091          }
3092      }
3093      for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
3094      {
3095          spritetype *pSprite = &sprite[nSprite];
3096          if (pSprite->flags&32)
3097              continue;
3098          if (TestBitString(sectmap, pSprite->sectnum) && CheckProximity(pSprite, x, y, z, nSector, nDist))
3099          {
3100              XSPRITE *pXSprite = &xsprite[pSprite->extra];
3101              if (!pXSprite->locked)
3102              {
3103                  int dx = pMissile->x-pSprite->x;
3104                  int dy = pMissile->y-pSprite->y;
3105                  int nDamage = nDist-(ksqrt(dx*dx+dy*dy)>>4)+20;
3106                  if (nDamage < 0)
3107                      nDamage = 20;
3108                  actDamageSprite(nOwner, pSprite, kDamageTesla, nDamage<<4);
3109              }
3110          }
3111      }
3112  }
3113  
3114  class WeaponLoadSave : public LoadSave
3115  {
3116  public:
3117      virtual void Load();
3118      virtual void Save();
3119  };
3120  
3121  void WeaponLoadSave::Load()
3122  {
3123  }
3124  
3125  void WeaponLoadSave::Save()
3126  {
3127  }
3128  
3129  static WeaponLoadSave *myLoadSave;
3130  
3131  void WeaponLoadSaveConstruct(void)
3132  {
3133      myLoadSave = new WeaponLoadSave();
3134  }
3135