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