//------------------------------------------------------------------------- /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 Nuke.YKT This file is part of NBlood. NBlood is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include "ns.h" // Must come before everything else! #include #include #include #include "build.h" #include "blood.h" #include "bloodactor.h" BEGIN_BLD_NS void FirePitchfork(int, PLAYER* pPlayer); void FireSpray(int, PLAYER* pPlayer); void ThrowCan(int, PLAYER* pPlayer); void DropCan(int, PLAYER* pPlayer); void ExplodeCan(int, PLAYER* pPlayer); void ThrowBundle(int, PLAYER* pPlayer); void DropBundle(int, PLAYER* pPlayer); void ExplodeBundle(int, PLAYER* pPlayer); void ThrowProx(int, PLAYER* pPlayer); void DropProx(int, PLAYER* pPlayer); void ThrowRemote(int, PLAYER* pPlayer); void DropRemote(int, PLAYER* pPlayer); void FireRemote(int, PLAYER* pPlayer); void FireShotgun(int nTrigger, PLAYER* pPlayer); void EjectShell(int, PLAYER* pPlayer); void FireTommy(int nTrigger, PLAYER* pPlayer); void FireSpread(int nTrigger, PLAYER* pPlayer); void AltFireSpread(int nTrigger, PLAYER* pPlayer); void AltFireSpread2(int nTrigger, PLAYER* pPlayer); void FireFlare(int nTrigger, PLAYER* pPlayer); void AltFireFlare(int nTrigger, PLAYER* pPlayer); void FireVoodoo(int nTrigger, PLAYER* pPlayer); void AltFireVoodoo(int nTrigger, PLAYER* pPlayer); void DropVoodoo(int nTrigger, PLAYER* pPlayer); void FireTesla(int nTrigger, PLAYER* pPlayer); void AltFireTesla(int nTrigger, PLAYER* pPlayer); void FireNapalm(int nTrigger, PLAYER* pPlayer); void FireNapalm2(int nTrigger, PLAYER* pPlayer); void AltFireNapalm(int nTrigger, PLAYER* pPlayer); void FireLifeLeech(int nTrigger, PLAYER* pPlayer); void AltFireLifeLeech(int nTrigger, PLAYER* pPlayer); void FireBeast(int nTrigger, PLAYER* pPlayer); typedef void(*QAVTypeCast)(int, void*); void (*qavClientCallback[])(int, void*) = { (QAVTypeCast)FirePitchfork, (QAVTypeCast)FireSpray, (QAVTypeCast)ThrowCan, (QAVTypeCast)DropCan, (QAVTypeCast)ExplodeCan, (QAVTypeCast)ThrowBundle, (QAVTypeCast)DropBundle, (QAVTypeCast)ExplodeBundle, (QAVTypeCast)ThrowProx, (QAVTypeCast)DropProx, (QAVTypeCast)ThrowRemote, (QAVTypeCast)DropRemote, (QAVTypeCast)FireRemote, (QAVTypeCast)FireShotgun, (QAVTypeCast)EjectShell, (QAVTypeCast)FireTommy, (QAVTypeCast)AltFireSpread2, (QAVTypeCast)FireSpread, (QAVTypeCast)AltFireSpread, (QAVTypeCast)FireFlare, (QAVTypeCast)AltFireFlare, (QAVTypeCast)FireVoodoo, (QAVTypeCast)AltFireVoodoo, (QAVTypeCast)FireTesla, (QAVTypeCast)AltFireTesla, (QAVTypeCast)FireNapalm, (QAVTypeCast)FireNapalm2, (QAVTypeCast)FireLifeLeech, (QAVTypeCast)FireBeast, (QAVTypeCast)AltFireLifeLeech, (QAVTypeCast)DropVoodoo, (QAVTypeCast)AltFireNapalm, }; enum { nClientFirePitchfork, nClientFireSpray, nClientThrowCan, nClientDropCan, nClientExplodeCan, nClientThrowBundle, nClientDropBundle, nClientExplodeBundle, nClientThrowProx, nClientDropProx, nClientThrowRemote, nClientDropRemote, nClientFireRemote, nClientFireShotgun, nClientEjectShell, nClientFireTommy, nClientAltFireSpread2, nClientFireSpread, nClientAltFireSpread, nClientFireFlare, nClientAltFireFlare, nClientFireVoodoo, nClientAltFireVoodoo, nClientFireTesla, nClientAltFireTesla, nClientFireNapalm, nClientFireNapalm2, nClientFireLifeLeech, nClientFireBeast, nClientAltFireLifeLeech, nClientDropVoodoo, nClientAltFireNapalm, }; //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- double getThrowPower(PLAYER* pPlayer) { return pPlayer->throwPower * 23.46666 + 6.4; } void setThrowPower(PLAYER* pPlayer) { pPlayer->throwPower = min((PlayClock - pPlayer->throwTime) / 240., 1.); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool checkLitSprayOrTNT(PLAYER* pPlayer) { switch (pPlayer->curWeapon) { case kWeapSpraycan: switch (pPlayer->weaponState) { case 5: case 6: return 1; case 7: if (VanillaMode()) return 0; return 1; } break; case kWeapDynamite: switch (pPlayer->weaponState) { case 4: case 5: case 6: return 1; } break; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool BannedUnderwater(int nWeapon) { return nWeapon == kWeapSpraycan || nWeapon == kWeapDynamite; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool CheckWeaponAmmo(PLAYER* pPlayer, int weapon, int ammotype, int count) { if (gInfiniteAmmo) return 1; if (ammotype == -1) return 1; if (weapon == kWeapRemote && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11) return 1; if (weapon == kWeapLifeLeech && pPlayer->actor->xspr.health > 0) return 1; return pPlayer->ammoCount[ammotype] >= count; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool CheckAmmo(PLAYER* pPlayer, int ammotype, int count) { if (gInfiniteAmmo) return 1; if (ammotype == -1) return 1; if (pPlayer->curWeapon == kWeapRemote && pPlayer->weaponAmmo == 11 && pPlayer->weaponState == 11) return 1; if (pPlayer->curWeapon == kWeapLifeLeech && pPlayer->actor->xspr.health >= unsigned(count << 4)) return 1; return pPlayer->ammoCount[ammotype] >= count; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool checkAmmo2(const PLAYER* pPlayer, int ammotype, int amount) { if (gInfiniteAmmo) return 1; if (ammotype == -1) return 1; return pPlayer->ammoCount[ammotype] >= amount; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpawnBulletEject(PLAYER* pPlayer, double dist, int rdist) { POSTURE* pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture]; pPlayer->zView = pPlayer->actor->spr.pos.Z - pPosture->eyeAboveZ; double dz = pPlayer->zWeapon - (pPlayer->zWeapon - pPlayer->zView) * 0.5; fxSpawnEjectingBrass(pPlayer->actor, dz, dist, rdist); } void SpawnShellEject(PLAYER* pPlayer, double dist, int rdist) { POSTURE* pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture]; pPlayer->zView = pPlayer->actor->spr.pos.Z - pPosture->eyeAboveZ; double t = pPlayer->zWeapon - pPlayer->zView; double dz = pPlayer->zWeapon - t + (t * 0.25); fxSpawnEjectingShell(pPlayer->actor, dz, dist, rdist); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponInit(void) { auto doInit = [](const int base) { for (int i = base; i < (kQAVEnd + base); i++) { auto pQAV = getQAV(i); if (!pQAV) I_Error("Could not load QAV %d\n", i); } }; doInit(0); doInit(10000); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponPrecache() { auto doPrecache = [](const int base) { for (int i = base; i < (kQAVEnd + base); i++) { auto pQAV = getQAV(i); if (pQAV) pQAV->Precache(); } }; doPrecache(0); doPrecache(10000); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponDraw(PLAYER* pPlayer, int shade, double xpos, double ypos, int palnum, DAngle angle) { assert(pPlayer != NULL); if (pPlayer->weaponQav == kQAVNone) return; auto pQAV = getQAV(pPlayer->weaponQav); int duration; double interpfrac; qavProcessTimer(pPlayer, pQAV, &duration, &interpfrac, pPlayer->weaponState == -1, pPlayer->curWeapon == kWeapShotgun && pPlayer->weaponState == 7); pQAV->x = xpos; pQAV->y = ypos; int flags = 2; int nInv = powerupCheck(pPlayer, kPwUpShadowCloak); if (nInv >= 120 * 8 || (nInv != 0 && (PlayClock & 32))) { shade = -128; flags |= 1; } pQAV->Draw(duration, flags, shade, palnum, true, interpfrac, angle); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponPlay(PLAYER* pPlayer) { assert(pPlayer != NULL); if (pPlayer->weaponQav == kQAVNone) return; auto pQAV = getQAV(pPlayer->weaponQav); int nTicks = pQAV->duration - pPlayer->weaponTimer; pQAV->Play(nTicks - 4, nTicks, pPlayer->qavCallback, pPlayer); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static void StartQAV(PLAYER* pPlayer, int nWeaponQAV, int callback = -1, bool looped = false) { assert(nWeaponQAV < kQAVEnd); auto res_id = qavGetCorrectID(nWeaponQAV); auto pQAV = getQAV(res_id); pPlayer->weaponQav = res_id; pPlayer->weaponTimer = pQAV->duration; pPlayer->qavCallback = callback; pPlayer->qavLoop = looped; pPlayer->qavLastTick = I_GetTime(pQAV->ticrate); pPlayer->qavTimer = pQAV->duration; //pQAV->Preload(); WeaponPlay(pPlayer); pPlayer->weaponTimer -= 4; } static void SetQAV(PLAYER* pPlayer, int nWeaponQAV) { assert(nWeaponQAV < kQAVEnd); pPlayer->weaponQav = qavGetCorrectID(nWeaponQAV); pPlayer->qavTimer = 0; pPlayer->qavLastTick = 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- struct WEAPONTRACK { int aimSpeedHorz; int aimSpeedVert; int angleRange; int thingAngle; int seeker; bool bIsProjectile; }; WEAPONTRACK gWeaponTrack[] = { { 0, 0, 0, 0, 0, false }, { 0x6000, 0x6000, 0x71, 0x55, 0x111111, false }, { 0x8000, 0x8000, 0x71, 0x55, 0x2aaaaa, true }, { 0x10000, 0x10000, 0x38, 0x1c, 0, false }, { 0x6000, 0x8000, 0x38, 0x1c, 0, false }, { 0x6000, 0x6000, 0x38, 0x1c, 0x2aaaaa, true }, { 0x6000, 0x6000, 0x71, 0x55, 0, true }, { 0x6000, 0x6000, 0x71, 0x38, 0, true }, { 0x8000, 0x10000, 0x71, 0x55, 0x255555, true }, { 0x10000, 0x10000, 0x71, 0, 0, true }, { 0x10000, 0x10000, 0xaa, 0, 0, false }, { 0x6000, 0x6000, 0x71, 0x55, 0, true }, { 0x6000, 0x6000, 0x71, 0x55, 0, true }, { 0x6000, 0x6000, 0x71, 0x55, 0, false }, }; //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void UpdateAimVector(PLAYER* pPlayer) { assert(pPlayer != NULL); auto plActor = pPlayer->actor; DVector3 pos(plActor->spr.pos.XY(), pPlayer->zWeapon); DVector3 Aim(plActor->spr.Angles.Yaw.ToVector(), pPlayer->slope); WEAPONTRACK* pWeaponTrack = &gWeaponTrack[pPlayer->curWeapon]; DBloodActor* targetactor = nullptr; pPlayer->aimTargetsCount = 0; int autoaim = Autoaim(pPlayer->nPlayer); if (autoaim == 1 || (autoaim == 2 && !pWeaponTrack->bIsProjectile) || pPlayer->curWeapon == kWeapVoodooDoll || pPlayer->curWeapon == kWeapLifeLeech) { double nClosest = 0x7fffffff; BloodStatIterator it(kStatDude); while (auto actor = it.Next()) { if (plActor == actor) continue; if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, actor)) continue; if (actor->spr.flags & 32) continue; if (!(actor->spr.flags & 8)) continue; auto pos2 = actor->spr.pos; double nDist = (pos2.XY() - pos.XY()).Length(); if (nDist == 0 || nDist > 3200) continue; if (pWeaponTrack->seeker) { double t = nDist * 4096 / pWeaponTrack->seeker; pos2 += actor->vel * t; } DVector3 lpos = pos + DVector3(plActor->spr.Angles.Yaw.ToVector(), pPlayer->slope) * nDist; double zRange = nDist * (9460 / 16384.); double top, bottom; GetActorExtents(actor, &top, &bottom); if (lpos.Z - zRange > bottom || lpos.Z + zRange < top) continue; DAngle angle = (pos2 - pos).Angle(); DAngle deltaangle = absangle(angle, plActor->spr.Angles.Yaw); if (deltaangle > DAngle::fromBuild(pWeaponTrack->angleRange)) continue; if (pPlayer->aimTargetsCount < 16 && cansee(pos, plActor->sector(), pos2, actor->sector())) pPlayer->aimTargets[pPlayer->aimTargetsCount++] = actor; double nDist2 = (lpos - pos2).Length(); if (nDist2 >= nClosest) continue; DUDEINFO* pDudeInfo = getDudeInfo(actor); if (cansee(pos, plActor->sector(), pos2, actor->sector())) { double center = (actor->spr.scale.Y * pDudeInfo->aimHeight); double dzCenter = (pos2.Z - center) - pos.Z; nClosest = nDist2; Aim.XY() = angle.ToVector(); Aim.Z = dzCenter / nDist; targetactor = actor; } } if (pWeaponTrack->thingAngle > 0) { BloodStatIterator itr(kStatThing); while (auto actor = itr.Next()) { if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, actor)) continue; if (!(actor->spr.flags & 8)) continue; auto pos2 = actor->spr.pos; auto dv = pos2 - pos; double nDist = dv.Length(); if (nDist == 0 || nDist > 3200) continue; DVector3 lpos = pos + DVector3(plActor->spr.Angles.Yaw.ToVector(), pPlayer->slope) * nDist; double zRange = nDist * (9460 / 16384.); double top, bottom; GetActorExtents(actor, &top, &bottom); if (lpos.Z - zRange > bottom || lpos.Z + zRange < top) continue; DAngle angle = dv.Angle(); DAngle deltaangle = absangle(angle, plActor->spr.Angles.Yaw); if (deltaangle > DAngle::fromBuild(pWeaponTrack->thingAngle)) continue; if (pPlayer->aimTargetsCount < 16 && cansee(pos, plActor->sector(), pos2, actor->sector())) pPlayer->aimTargets[pPlayer->aimTargetsCount++] = actor; double nDist2 = (lpos - pos2).Length(); if (nDist2 >= nClosest) continue; if (cansee(pos, plActor->sector(), pos2, actor->sector())) { nClosest = nDist2; Aim.XY() = angle.ToVector(); Aim.Z = dv.Z / nDist; targetactor = actor; } } } } DVector3 Aim2(Aim); Aim2.XY() = Aim2.XY().Rotated(-plActor->spr.Angles.Yaw); Aim2.Z -= pPlayer->slope; pPlayer->relAim.X = interpolatedvalue(pPlayer->relAim.X, Aim2.X, FixedToFloat(pWeaponTrack->aimSpeedHorz)); pPlayer->relAim.Y = interpolatedvalue(pPlayer->relAim.Y, Aim2.Y, FixedToFloat(pWeaponTrack->aimSpeedHorz)); pPlayer->relAim.Z = interpolatedvalue(pPlayer->relAim.Z, Aim2.Z, FixedToFloat(pWeaponTrack->aimSpeedVert)); pPlayer->aim = pPlayer->relAim; pPlayer->aim.XY() = pPlayer->aim.XY().Rotated(plActor->spr.Angles.Yaw); pPlayer->aim.Z += pPlayer->slope; pPlayer->aimTarget = targetactor; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- struct t_WeaponModes { int update; int ammoType; }; t_WeaponModes weaponModes[] = { { 0, -1 }, { 1, -1 }, { 1, 1 }, { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 1, 6 }, { 1, 7 }, { 1, 8 }, { 1, 9 }, { 1, 10 }, { 1, 11 }, { 0, -1 }, }; void WeaponRaise(PLAYER* pPlayer) { assert(pPlayer != NULL); int prevWeapon = pPlayer->curWeapon; pPlayer->curWeapon = pPlayer->newWeapon; pPlayer->newWeapon = kWeapNone; pPlayer->weaponAmmo = weaponModes[pPlayer->curWeapon].ammoType; switch (pPlayer->curWeapon) { case kWeapPitchFork: pPlayer->weaponState = 0; StartQAV(pPlayer, kQAVFORKUP); break; case kWeapSpraycan: if (pPlayer->weaponState == 2) { pPlayer->weaponState = 3; StartQAV(pPlayer, kQAVCANPREF); } else { pPlayer->weaponState = 0; StartQAV(pPlayer, kQAVLITEOPEN); } break; case kWeapDynamite: if (gInfiniteAmmo || checkAmmo2(pPlayer, 5, 1)) { pPlayer->weaponState = 3; if (prevWeapon == kWeapSpraycan) StartQAV(pPlayer, kQAVBUNUP); else StartQAV(pPlayer, kQAVBUNUP2); } break; case kWeapProximity: if (gInfiniteAmmo || checkAmmo2(pPlayer, 10, 1)) { pPlayer->weaponState = 7; StartQAV(pPlayer, kQAVPROXUP); } break; case kWeapRemote: if (gInfiniteAmmo || checkAmmo2(pPlayer, 11, 1)) { pPlayer->weaponState = 10; StartQAV(pPlayer, kQAVREMUP2); } else { StartQAV(pPlayer, kQAVREMUP3); pPlayer->weaponState = 11; } break; case kWeapShotgun: if (powerupCheck(pPlayer, kPwUpTwoGuns)) { if (gInfiniteAmmo || pPlayer->ammoCount[2] >= 4) StartQAV(pPlayer, kQAV2SHOTUP); else StartQAV(pPlayer, kQAVSHOTUP); if (gInfiniteAmmo || pPlayer->ammoCount[2] >= 4) pPlayer->weaponState = 7; else if (pPlayer->ammoCount[2] > 1) pPlayer->weaponState = 3; else if (pPlayer->ammoCount[2] > 0) pPlayer->weaponState = 2; else pPlayer->weaponState = 1; } else { if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1) pPlayer->weaponState = 3; else if (pPlayer->ammoCount[2] > 0) pPlayer->weaponState = 2; else pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVSHOTUP); } break; case kWeapTommyGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2)) { pPlayer->weaponState = 1; StartQAV(pPlayer, kQAV2TOMUP); } else { pPlayer->weaponState = 0; StartQAV(pPlayer, kQAVTOMUP); } break; case kWeapVoodooDoll: if (gInfiniteAmmo || checkAmmo2(pPlayer, 9, 1)) { pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVVDUP); } break; case kWeapFlareGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2)) { StartQAV(pPlayer, kQAVFLAR2UP); pPlayer->weaponState = 3; } else { StartQAV(pPlayer, kQAVFLARUP); pPlayer->weaponState = 2; } break; case kWeapTeslaCannon: if (checkAmmo2(pPlayer, 7, 1)) { pPlayer->weaponState = 2; if (powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNUP); else StartQAV(pPlayer, kQAVSGUNUP); } else { pPlayer->weaponState = 3; StartQAV(pPlayer, kQAVSGUNUP); } break; case kWeapNapalm: if (powerupCheck(pPlayer, kPwUpTwoGuns)) { StartQAV(pPlayer, kQAV2NAPUP); pPlayer->weaponState = 3; } else { StartQAV(pPlayer, kQAVNAPUP); pPlayer->weaponState = 2; } break; case kWeapLifeLeech: pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVSTAFUP); break; case kWeapBeast: pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVBSTUP); break; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponLower(PLAYER* pPlayer) { assert(pPlayer != NULL); if (checkLitSprayOrTNT(pPlayer)) return; pPlayer->throwPower = 0; int prevState = pPlayer->weaponState; switch (pPlayer->curWeapon) { case kWeapPitchFork: StartQAV(pPlayer, kQAVFORKDOWN); break; case kWeapSpraycan: sfxKill3DSound(pPlayer->actor, -1, 441); switch (prevState) { case 1: if (VanillaMode()) { StartQAV(pPlayer, kQAVLITECLO2); } else { if (pPlayer->newWeapon == kWeapDynamite) // do not put away lighter if TNT was selected while throwing a spray can { pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVCANDOWN); WeaponRaise(pPlayer); return; } } break; case 2: pPlayer->weaponState = 1; WeaponRaise(pPlayer); return; case 4: pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVCANDOWN); if (VanillaMode()) { pPlayer->newWeapon = kWeapNone; WeaponLower(pPlayer); } else { if (pPlayer->newWeapon == kWeapDynamite) { pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVCANDOWN); return; } else { WeaponLower(pPlayer); } } break; case 3: if (pPlayer->newWeapon == kWeapDynamite) { pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVCANDOWN); return; } else if (pPlayer->newWeapon == kWeapSpraycan) { pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVCANDOWN); pPlayer->newWeapon = kWeapNone; WeaponLower(pPlayer); } else { pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVCANDOWN); } break; case 7: // throwing ignited alt fire spray if (VanillaMode() || (pPlayer->newWeapon != 0)) break; pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVCANDOWN); break; } break; case kWeapDynamite: switch (prevState) { case 1: if (VanillaMode()) { StartQAV(pPlayer, kQAVLITECLO2); } else { if (pPlayer->newWeapon == kWeapSpraycan) // do not put away lighter if TNT was selected while throwing a spray can { pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVCANDOWN); WeaponRaise(pPlayer); return; } } break; case 2: WeaponRaise(pPlayer); break; case 3: if (pPlayer->newWeapon == kWeapSpraycan) { pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVBUNDOWN); } else { pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVBUNDOWN2); } break; default: break; } break; case kWeapProximity: switch (prevState) { case 7: StartQAV(pPlayer, kQAVPROXDOWN); break; } break; case kWeapRemote: switch (prevState) { case 10: StartQAV(pPlayer, kQAVREMDOWN2); break; case 11: StartQAV(pPlayer, kQAVREMDOWN3); break; } break; case kWeapShotgun: if (powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SHOTDWN); else StartQAV(pPlayer, kQAVSHOTDOWN); break; case kWeapTommyGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && pPlayer->weaponState == 1) StartQAV(pPlayer, kQAV2TOMDOWN); else StartQAV(pPlayer, kQAVTOMDOWN); break; case kWeapFlareGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && pPlayer->weaponState == 3) StartQAV(pPlayer, kQAVFLAR2DWN); else StartQAV(pPlayer, kQAVFLARDOWN); break; case kWeapVoodooDoll: StartQAV(pPlayer, kQAVVDDOWN); break; case kWeapTeslaCannon: if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNDWN); else StartQAV(pPlayer, kQAVSGUNDOWN); break; case kWeapNapalm: if (powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2NAPDOWN); else StartQAV(pPlayer, kQAVNAPDOWN); break; case kWeapLifeLeech: StartQAV(pPlayer, kQAVSTAFDOWN); break; case kWeapBeast: StartQAV(pPlayer, kQAVBSTDOWN); break; } pPlayer->curWeapon = kWeapNone; pPlayer->qavLoop = 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponUpdateState(PLAYER* pPlayer) { static int lastWeapon = 0; static int lastState = 0; int va = pPlayer->curWeapon; int vb = pPlayer->weaponState; if (va != lastWeapon || vb != lastState) { lastWeapon = va; lastState = vb; } switch (lastWeapon) { case kWeapPitchFork: SetQAV(pPlayer, kQAVFORKIDLE); break; case kWeapSpraycan: switch (vb) { case 0: pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVLITEFLAM); break; case 1: if (CheckAmmo(pPlayer, 6, 1)) { pPlayer->weaponState = 3; StartQAV(pPlayer, kQAVCANPREF); } else SetQAV(pPlayer, kQAVLITEIDLE); break; case 3: SetQAV(pPlayer, kQAVCANIDLE); break; case 4: if (CheckAmmo(pPlayer, 6, 1)) { SetQAV(pPlayer, kQAVCANIDLE); pPlayer->weaponState = 3; } else { pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVCANDOWN); } sfxKill3DSound(pPlayer->actor, -1, 441); break; } break; case kWeapDynamite: switch (vb) { case 1: if (pPlayer->weaponAmmo == 5 && CheckAmmo(pPlayer, 5, 1)) { pPlayer->weaponState = 3; StartQAV(pPlayer, kQAVBUNUP); } break; case 0: pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVLITEFLAM); break; case 2: if (pPlayer->ammoCount[5] > 0) { pPlayer->weaponState = 3; StartQAV(pPlayer, kQAVBUNUP); } else SetQAV(pPlayer, kQAVLITEIDLE); break; case 3: SetQAV(pPlayer, kQAVBUNIDLE); break; } break; case kWeapProximity: switch (vb) { case 7: SetQAV(pPlayer, kQAVPROXIDLE); break; case 8: pPlayer->weaponState = 7; StartQAV(pPlayer, kQAVPROXUP); break; } break; case kWeapRemote: switch (vb) { case 10: SetQAV(pPlayer, kQAVREMIDLE1); break; case 11: SetQAV(pPlayer, kQAVREMIDLE2); break; case 12: if (pPlayer->ammoCount[11] > 0) { pPlayer->weaponState = 10; StartQAV(pPlayer, kQAVREMUP2); } else pPlayer->weaponState = -1; break; } break; case kWeapShotgun: switch (vb) { case 6: if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer, 2, 4))) pPlayer->weaponState = 7; else pPlayer->weaponState = 1; break; case 7: SetQAV(pPlayer, kQAV2SHOTI); break; case 1: if (CheckAmmo(pPlayer, 2, 1)) { sfxPlay3DSound(pPlayer->actor, 410, 3, 2); StartQAV(pPlayer, kQAVSHOTL1, nClientEjectShell); if (gInfiniteAmmo || pPlayer->ammoCount[2] > 1) pPlayer->weaponState = 3; else pPlayer->weaponState = 2; } else SetQAV(pPlayer, kQAVSHOTI3); break; case 2: SetQAV(pPlayer, kQAVSHOTI2); break; case 3: SetQAV(pPlayer, kQAVSHOTI1); break; } break; case kWeapTommyGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2)) { SetQAV(pPlayer, kQAV2TOMIDLE); pPlayer->weaponState = 1; } else { SetQAV(pPlayer, kQAVTOMIDLE); pPlayer->weaponState = 0; } break; case kWeapFlareGun: if (powerupCheck(pPlayer, kPwUpTwoGuns)) { if (vb == 3 && checkAmmo2(pPlayer, 1, 2)) SetQAV(pPlayer, kQAVFLAR2I); else { SetQAV(pPlayer, kQAVFLARIDLE); pPlayer->weaponState = 2; } } else SetQAV(pPlayer, kQAVFLARIDLE); break; case kWeapVoodooDoll: if (pPlayer->actor->xspr.height < 256 && pPlayer->swayHeight != 0) StartQAV(pPlayer, kQAVVDIDLE2); else SetQAV(pPlayer, kQAVVDIDLE1); break; case kWeapTeslaCannon: switch (vb) { case 2: if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) SetQAV(pPlayer, kQAV2SGUNIDL); else SetQAV(pPlayer, kQAVSGUNIDL1); break; case 3: SetQAV(pPlayer, kQAVSGUNIDL2); break; } break; case kWeapNapalm: switch (vb) { case 3: if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer, 4, 4))) SetQAV(pPlayer, kQAV2NAPIDLE); else SetQAV(pPlayer, kQAVNAPIDLE); break; case 2: SetQAV(pPlayer, kQAVNAPIDLE); break; } break; case kWeapLifeLeech: switch (vb) { case 2: SetQAV(pPlayer, kQAVSTAFIDL1); break; } break; case kWeapBeast: SetQAV(pPlayer, kQAVBSTIDLE); break; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FirePitchfork(int, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; double r1 = Random2F(2000, 14); double r2 = Random2F(2000, 14); double r3 = Random2F(2000, 14); for (int i = 0; i < 4; i++) actFireVector(actor, (2 * i - 3) * 2.5, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, pPlayer->aim + DVector3(r1, r2, r3), kVectorTine); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FireSpray(int, PLAYER* pPlayer) { playerFireMissile(pPlayer, 0, pPlayer->aim, kMissileFlameSpray); UseAmmo(pPlayer, 6, 4); if (CheckAmmo(pPlayer, 6, 1)) sfxPlay3DSound(pPlayer->actor, 441, 1, 2); else sfxKill3DSound(pPlayer->actor, -1, 441); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void ThrowCan(int, PLAYER* pPlayer) { sfxKill3DSound(pPlayer->actor, -1, 441); double nSpeed = getThrowPower(pPlayer); sfxPlay3DSound(pPlayer->actor, 455, 1, 0); auto spawned = playerFireThing(pPlayer, 0, -9460 / 65536., kThingArmedSpray, nSpeed); if (spawned) { sfxPlay3DSound(spawned, 441, 0, 0); spawned->spr.shade = -128; evPostActor(spawned, pPlayer->fuseTime, kCmdOn, pPlayer->actor); spawned->xspr.Impact = 1; UseAmmo(pPlayer, 6, gAmmoItemData[0].count); pPlayer->throwPower = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void DropCan(int, PLAYER* pPlayer) { sfxKill3DSound(pPlayer->actor, -1, 441); auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0); if (spawned) { evPostActor(spawned, pPlayer->fuseTime, kCmdOn, pPlayer->actor); UseAmmo(pPlayer, 6, gAmmoItemData[0].count); } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void ExplodeCan(int, PLAYER* pPlayer) { sfxKill3DSound(pPlayer->actor, -1, 441); auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0); if (spawned) { evPostActor(spawned, 0, kCmdOn, pPlayer->actor); UseAmmo(pPlayer, 6, gAmmoItemData[0].count); StartQAV(pPlayer, kQAVCANBOOM); pPlayer->curWeapon = kWeapNone; pPlayer->throwPower = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void ThrowBundle(int, PLAYER* pPlayer) { sfxKill3DSound(pPlayer->actor, 16, -1); double nSpeed = getThrowPower(pPlayer); sfxPlay3DSound(pPlayer->actor, 455, 1, 0); auto spawned = playerFireThing(pPlayer, 0, -9460 / 65536., kThingArmedTNTBundle, nSpeed); if (spawned) { if (pPlayer->fuseTime < 0) spawned->xspr.Impact = 1; else evPostActor(spawned, pPlayer->fuseTime, kCmdOn, pPlayer->actor); UseAmmo(pPlayer, 5, 1); pPlayer->throwPower = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void DropBundle(int, PLAYER* pPlayer) { sfxKill3DSound(pPlayer->actor, 16, -1); auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0); if (spawned) { evPostActor(spawned, pPlayer->fuseTime, kCmdOn, pPlayer->actor); UseAmmo(pPlayer, 5, 1); } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void ExplodeBundle(int, PLAYER* pPlayer) { sfxKill3DSound(pPlayer->actor, 16, -1); auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0); if (spawned) { evPostActor(spawned, 0, kCmdOn, pPlayer->actor); UseAmmo(pPlayer, 5, 1); StartQAV(pPlayer, kQAVDYNEXPLO); pPlayer->curWeapon = kWeapNone; pPlayer->throwPower = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void ThrowProx(int, PLAYER* pPlayer) { double nSpeed = getThrowPower(pPlayer); sfxPlay3DSound(pPlayer->actor, 455, 1, 0); auto spawned = playerFireThing(pPlayer, 0, -9460 / 65536., kThingArmedProxBomb, nSpeed); if (spawned) { evPostActor(spawned, 240, kCmdOn, pPlayer->actor); UseAmmo(pPlayer, 10, 1); pPlayer->throwPower = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void DropProx(int, PLAYER* pPlayer) { auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedProxBomb, 0); if (spawned) { evPostActor(spawned, 240, kCmdOn, pPlayer->actor); UseAmmo(pPlayer, 10, 1); } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void ThrowRemote(int, PLAYER* pPlayer) { double nSpeed = getThrowPower(pPlayer); sfxPlay3DSound(pPlayer->actor, 455, 1, 0); auto spawned = playerFireThing(pPlayer, 0, -9460 / 65536., kThingArmedRemoteBomb, nSpeed); if (spawned) { spawned->xspr.rxID = 90 + (pPlayer->actor->spr.type - kDudePlayer1); UseAmmo(pPlayer, 11, 1); pPlayer->throwPower = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void DropRemote(int, PLAYER* pPlayer) { auto spawned = playerFireThing(pPlayer, 0, 0, kThingArmedRemoteBomb, 0); if (spawned) { spawned->xspr.rxID = 90 + (pPlayer->actor->spr.type - kDudePlayer1); UseAmmo(pPlayer, 11, 1); } } void FireRemote(int, PLAYER* pPlayer) { evSendGame(90 + (pPlayer->actor->spr.type - kDudePlayer1), kCmdOn); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- enum { kMaxShotgunBarrels = 4 }; void FireShotgun(int nTrigger, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; assert(nTrigger > 0 && nTrigger <= kMaxShotgunBarrels); if (nTrigger == 1) { sfxPlay3DSound(pPlayer->actor, 411, 2, 0); pPlayer->tiltEffect = 30; pPlayer->visibility = 20; } else { sfxPlay3DSound(pPlayer->actor, 412, 2, 0); pPlayer->tiltEffect = 50; pPlayer->visibility = 40; } int n = nTrigger << 4; for (int i = 0; i < n; i++) { double r1, r2, r3; VECTOR_TYPE nType; if (nTrigger == 1) { r1 = Random3F(1500, 14); r2 = Random3F(1500, 14); r3 = Random3F(500, 14); nType = kVectorShell; } else { r1 = Random3F(2500, 14); r2 = Random3F(2500, 14); r3 = Random3F(1500, 14); nType = kVectorShellAP; } actFireVector(actor, 0, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, pPlayer->aim + DVector3(r1, r2, r3), nType); } UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger); pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void EjectShell(int, PLAYER* pPlayer) { SpawnShellEject(pPlayer, 25 / 16., 35); SpawnShellEject(pPlayer, 3, 35); } void FireTommy(int nTrigger, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; sfxPlay3DSound(pPlayer->actor, 431, -1, 0); switch (nTrigger) { case 1: { double r1 = Random3F(400, 14); double r2 = Random3F(1200, 14); double r3 = Random3F(1200, 14); actFireVector(actor, 0, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, pPlayer->aim + DVector3(r3, r2, r1), kVectorTommyRegular); SpawnBulletEject(pPlayer, -15 / 16., -45); pPlayer->visibility = 20; break; } case 2: { double r1 = Random3F(400, 14); double r2 = Random3F(1200, 14); double r3 = Random3F(1200, 14); actFireVector(actor, -7.5, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, pPlayer->aim + DVector3(r3, r2, r1), kVectorTommyRegular); SpawnBulletEject(pPlayer, -140 / 16., -45); r1 = Random3(400); r2 = Random3(1200); r3 = Random3(1200); actFireVector(actor, 7.5, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, pPlayer->aim + DVector3(r3, r2, r1), kVectorTommyRegular); SpawnBulletEject(pPlayer, 140 / 16., 45); pPlayer->visibility = 30; break; } } UseAmmo(pPlayer, pPlayer->weaponAmmo, nTrigger); pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- enum { kMaxSpread = 14 }; static constexpr DAngle DAngle10 = DAngle::fromDeg(10); static constexpr DAngle DAngle20 = DAngle::fromDeg(20); void FireSpread(int nTrigger, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; assert(nTrigger > 0 && nTrigger <= kMaxSpread); DVector3 aim = pPlayer->aim; DAngle angle = ((aim.XY()).Angle() + ((DAngle20 * (nTrigger - 1)) / kMaxSpread - DAngle10)); DVector3 dv = DVector3(angle.ToVector(), aim.Z); sfxPlay3DSound(pPlayer->actor, 431, -1, 0); double r1, r2, r3; r1 = Random3F(300, 14); r2 = Random3F(600, 14); r3 = Random3F(600, 14); actFireVector(actor, 0, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, dv + DVector3(r3, r2, r1), kVectorTommyAP); int ri = Random2(90); r2 = Random2F(30, 4); SpawnBulletEject(pPlayer, r2, ri); pPlayer->visibility = 20; UseAmmo(pPlayer, pPlayer->weaponAmmo, 1); pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AltFireSpread(int nTrigger, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; assert(nTrigger > 0 && nTrigger <= kMaxSpread); DVector3 aim = pPlayer->aim; DAngle angle = ((aim.XY()).Angle() + ((DAngle20 * (nTrigger - 1)) / kMaxSpread - DAngle10)); DVector3 dv = DVector3(angle.ToVector(), aim.Z); sfxPlay3DSound(pPlayer->actor, 431, -1, 0); double r1, r2, r3; r1 = Random3F(300, 14); r2 = Random3F(600, 14); r3 = Random3F(600, 14); actFireVector(actor, -7.5, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, dv + DVector3(r3, r2, r1), kVectorTommyAP); int ri = Random2(45); r2 = Random2F(120, 4); SpawnBulletEject(pPlayer, r2, ri); r1 = Random3F(300, 14); r2 = Random3F(600, 14); r3 = Random3F(600, 14); actFireVector(actor, 7.5, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, dv + DVector3(r3, r2, r1), kVectorTommyAP); ri = Random2(-45); r2 = Random2F(-120, 4); SpawnBulletEject(pPlayer, r2, ri); pPlayer->tiltEffect = 20; pPlayer->visibility = 30; UseAmmo(pPlayer, pPlayer->weaponAmmo, 2); pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AltFireSpread2(int nTrigger, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; assert(nTrigger > 0 && nTrigger <= kMaxSpread); DVector3 aim = pPlayer->aim; DAngle angle = ((aim.XY()).Angle() + ((DAngle20 * (nTrigger - 1)) / kMaxSpread - DAngle10)); DVector3 dv = DVector3(angle.ToVector(), aim.Z); sfxPlay3DSound(pPlayer->actor, 431, -1, 0); if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2)) { double r1, r2, r3; r1 = Random3F(300, 14); r2 = Random3F(600, 14); r3 = Random3F(600, 14); actFireVector(actor, -7.5, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, dv + DVector3(r3, r2, r1), kVectorTommyAP); int ri = Random2(45); r2 = Random2F(120, 4); SpawnBulletEject(pPlayer, r2, ri); r1 = Random3F(300, 14); r2 = Random3F(600, 14); r3 = Random3F(600, 14); actFireVector(actor, 7.5, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, dv + DVector3(r3, r2, r1), kVectorTommyAP); ri = Random2(-45); r2 = Random2F(-120, 4); SpawnBulletEject(pPlayer, r2, ri); pPlayer->tiltEffect = 30; pPlayer->visibility = 45; UseAmmo(pPlayer, pPlayer->weaponAmmo, 2); } else { double r1, r2, r3; r1 = Random3F(300, 14); r2 = Random3F(600, 14); r3 = Random3F(600, 14); actFireVector(actor, 0, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, dv + DVector3(r3, r2, r1), kVectorTommyAP); int ri = Random2(90); r2 = Random2F(30, 4); SpawnBulletEject(pPlayer, r2, ri); pPlayer->tiltEffect = 20; pPlayer->visibility = 30; UseAmmo(pPlayer, pPlayer->weaponAmmo, 1); } pPlayer->flashEffect = 1; if (!checkAmmo2(pPlayer, 3, 1)) { WeaponLower(pPlayer); pPlayer->weaponState = -1; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FireFlare(int nTrigger, PLAYER* pPlayer) { auto plActor = pPlayer->actor; double offset = 0; switch (nTrigger) { case 2: offset = -7.5; break; case 3: offset = 7.5; break; } playerFireMissile(pPlayer, offset, pPlayer->aim, kMissileFlareRegular); UseAmmo(pPlayer, 1, 1); sfxPlay3DSound(pPlayer->actor, 420, 2, 0); pPlayer->visibility = 30; pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AltFireFlare(int nTrigger, PLAYER* pPlayer) { auto plActor = pPlayer->actor; double offset = 0; switch (nTrigger) { case 2: offset = -7.5; break; case 3: offset = 7.5; break; } playerFireMissile(pPlayer, offset, pPlayer->aim, kMissileFlareAlt); UseAmmo(pPlayer, 1, 8); sfxPlay3DSound(pPlayer->actor, 420, 2, 0); pPlayer->visibility = 45; pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FireVoodoo(int nTrigger, PLAYER* pPlayer) { nTrigger--; DBloodActor* actor = pPlayer->actor; auto plActor = pPlayer->actor; if (nTrigger == 4) { actDamageSprite(actor, actor, kDamageBullet, 1 << 4); return; } DBloodActor* targetactor = pPlayer->voodooTarget; if (!targetactor) return; if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, targetactor)) return; switch (nTrigger) { case 0: { sfxPlay3DSound(actor, 460, 2, 0); fxSpawnBlood(targetactor, 17 << 4); int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 17 << 4); UseAmmo(pPlayer, 9, nDamage / 4); break; } case 1: { sfxPlay3DSound(actor, 460, 2, 0); fxSpawnBlood(targetactor, 17 << 4); int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 9 << 4); if (targetactor->IsPlayerActor()) WeaponLower(&gPlayer[targetactor->spr.type - kDudePlayer1]); UseAmmo(pPlayer, 9, nDamage / 4); break; } case 3: { sfxPlay3DSound(actor, 463, 2, 0); fxSpawnBlood(targetactor, 17 << 4); int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 49 << 4); UseAmmo(pPlayer, 9, nDamage / 4); break; } case 2: { sfxPlay3DSound(actor, 460, 2, 0); fxSpawnBlood(targetactor, 17 << 4); int nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, 11 << 4); if (targetactor->IsPlayerActor()) { PLAYER* pOtherPlayer = &gPlayer[targetactor->spr.type - kDudePlayer1]; pOtherPlayer->blindEffect = 128; } UseAmmo(pPlayer, 9, nDamage / 4); break; } } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AltFireVoodoo(int nTrigger, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; if (nTrigger == 2) { // by NoOne: trying to simulate v1.0x voodoo here. // dunno how exactly it works, but at least it not spend all the ammo on alt fire if (gGameOptions.weaponsV10x && !VanillaMode()) { int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount); if (nCount > 0) { for (int i = 0; i < pPlayer->aimTargetsCount; i++) { DBloodActor* targetactor = pPlayer->aimTargets[i]; if (!targetactor) continue; if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, targetactor)) continue; double nDist = (targetactor->spr.pos.XY() - pPlayer->actor->spr.pos.XY()).Length(); if (nDist > 0 && nDist < 3200) { int vc = pPlayer->ammoCount[9] >> 3; int v8 = pPlayer->ammoCount[9] << 1; int nDamage = (v8 + Random(vc)) << 4; nDamage = int((nDamage * ((3200 - nDist) + 1/16.)) / 3200); nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, nDamage); if (targetactor->IsPlayerActor()) { PLAYER* pOtherPlayer = &gPlayer[targetactor->spr.type - kDudePlayer1]; if (!pOtherPlayer->godMode || !powerupCheck(pOtherPlayer, kPwUpDeathMask)) powerupActivate(pOtherPlayer, kPwUpDeliriumShroom); } fxSpawnBlood(targetactor, 0); } } } UseAmmo(pPlayer, 9, 20); pPlayer->weaponState = 0; return; } //int nAmmo = pPlayer->ammCount[9]; int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount); if (nCount > 0) { int v4 = pPlayer->ammoCount[9] - (pPlayer->ammoCount[9] / nCount) * nCount; for (int i = 0; i < pPlayer->aimTargetsCount; i++) { DBloodActor* targetactor = pPlayer->aimTargets[i]; if (!targetactor) continue; if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, targetactor)) continue; if (v4 > 0) v4--; double nDist = (targetactor->spr.pos.XY() - pPlayer->actor->spr.pos.XY()).Length(); if (nDist > 0 && nDist < 3200) { int vc = pPlayer->ammoCount[9] >> 3; int v8 = pPlayer->ammoCount[9] << 1; int nDamage = (v8 + Random2(vc)) << 4; nDamage = int((nDamage * ((3200 - nDist) + 1/16.)) / 3200); nDamage = actDamageSprite(actor, targetactor, kDamageSpirit, nDamage); UseAmmo(pPlayer, 9, nDamage); if (targetactor->IsPlayerActor()) { PLAYER* pOtherPlayer = &gPlayer[targetactor->spr.type - kDudePlayer1]; if (!pOtherPlayer->godMode || !powerupCheck(pOtherPlayer, kPwUpDeathMask)) powerupActivate(pOtherPlayer, kPwUpDeliriumShroom); } fxSpawnBlood(targetactor, 0); } } } UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]); pPlayer->hasWeapon[kWeapVoodooDoll] = 0; pPlayer->weaponState = -1; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void DropVoodoo(int, PLAYER* pPlayer) { sfxPlay3DSound(pPlayer->actor, 455, 2, 0); auto spawned = playerFireThing(pPlayer, 0, -4730 / 65536., kThingVoodooHead, 12.8); if (spawned) { spawned->xspr.data1 = pPlayer->ammoCount[9]; evPostActor(spawned, 90, kCallbackDropVoodoo); UseAmmo(pPlayer, 6, gAmmoItemData[0].count); UseAmmo(pPlayer, 9, pPlayer->ammoCount[9]); pPlayer->hasWeapon[kWeapVoodooDoll] = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- struct TeslaMissile { float offset; // offset int id; // id int ammouse; // ammo use int sound; // sound int light; // light int flash; // weapon flash }; //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FireTesla(int nTrigger, PLAYER* pPlayer) { TeslaMissile teslaMissile[6] = { { 0, 306, 1, 470, 20, 1 }, { -8.75f, 306, 1, 470, 30, 1 }, { 8.75f, 306, 1, 470, 30, 1 }, { 0, 302, 35, 471, 40, 1 }, { -8.75f, 302, 35, 471, 50, 1 }, { 8.75f, 302, 35, 471, 50, 1 }, }; if (nTrigger > 0 && nTrigger <= 6) { nTrigger--; auto plActor = pPlayer->actor; TeslaMissile* pMissile = &teslaMissile[nTrigger]; if (!checkAmmo2(pPlayer, 7, pMissile->ammouse)) { pMissile = &teslaMissile[0]; if (!checkAmmo2(pPlayer, 7, pMissile->ammouse)) { pPlayer->weaponState = -1; SetQAV(pPlayer, kQAVSGUNIDL2); pPlayer->flashEffect = 0; return; } } playerFireMissile(pPlayer, pMissile->offset, pPlayer->aim, pMissile->id); UseAmmo(pPlayer, pPlayer->weaponAmmo, pMissile->ammouse); sfxPlay3DSound(pPlayer->actor, pMissile->sound, 1, 0); pPlayer->visibility = pMissile->light; pPlayer->flashEffect = pMissile->flash; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AltFireTesla(int, PLAYER* pPlayer) { auto plActor = pPlayer->actor; playerFireMissile(pPlayer, 0., pPlayer->aim, kMissileTeslaAlt); UseAmmo(pPlayer, pPlayer->weaponAmmo, 35); sfxPlay3DSound(pPlayer->actor, 471, 2, 0); pPlayer->visibility = 40; pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FireNapalm(int nTrigger, PLAYER* pPlayer) { auto plActor = pPlayer->actor; double offset = 0; switch (nTrigger) { case 2: offset = -3.125; break; case 3: offset = 3.125; break; } playerFireMissile(pPlayer, offset, pPlayer->aim, kMissileFireballNapalm); sfxPlay3DSound(pPlayer->actor, 480, 2, 0); UseAmmo(pPlayer, 4, 1); pPlayer->flashEffect = 1; } void FireNapalm2(int, PLAYER* pPlayer) { auto plActor = pPlayer->actor; playerFireMissile(pPlayer, -7.5, pPlayer->aim, kMissileFireballNapalm); playerFireMissile(pPlayer, 7.5, pPlayer->aim, kMissileFireballNapalm); sfxPlay3DSound(pPlayer->actor, 480, 2, 0); UseAmmo(pPlayer, 4, 2); pPlayer->flashEffect = 1; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AltFireNapalm(int, PLAYER* pPlayer) { auto missile = playerFireThing(pPlayer, 0, -4730 / 65536., kThingNapalmBall, 18.13333); if (missile) { missile->xspr.data4 = ClipHigh(pPlayer->ammoCount[4], 12); UseAmmo(pPlayer, 4, missile->xspr.data4); seqSpawn(22, missile, -1); actBurnSprite(pPlayer->actor, missile, 600); evPostActor(missile, 0, kCallbackFXFlameLick); sfxPlay3DSound(missile, 480, 2, 0); pPlayer->visibility = 30; pPlayer->flashEffect = 1; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FireLifeLeech(int nTrigger, PLAYER* pPlayer) { if (!CheckAmmo(pPlayer, 8, 1)) return; double r1 = Random2F(2000, 14); double r2 = Random2F(2000, 14); double r3 = Random2F(1000, 14); DBloodActor* actor = pPlayer->actor; auto missileActor = playerFireMissile(pPlayer, 0, pPlayer->aim + DVector3(r1, r2, r3), 315); if (missileActor) { missileActor->SetTarget(pPlayer->aimTarget); missileActor->spr.Angles.Yaw = ((nTrigger == 2) ? DAngle180 : nullAngle); } if (checkAmmo2(pPlayer, 8, 1)) UseAmmo(pPlayer, 8, 1); else actDamageSprite(actor, actor, kDamageSpirit, 16); pPlayer->visibility = ClipHigh(pPlayer->visibility + 5, 50); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void AltFireLifeLeech(int, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; sfxPlay3DSound(pPlayer->actor, 455, 2, 0); auto missile = playerFireThing(pPlayer, 0, -4730 / 65536., kThingDroppedLifeLeech, 1.6); if (missile) { missile->spr.cstat |= CSTAT_SPRITE_BLOOD_BIT1; missile->xspr.Push = 1; missile->xspr.Proximity = 1; missile->xspr.DudeLockout = 1; missile->xspr.stateTimer = 1; evPostActor(missile, 120, kCallbackLeechStateTimer); if (gGameOptions.nGameType <= 1) { int nAmmo = pPlayer->ammoCount[8]; if (nAmmo < 25 && pPlayer->actor->xspr.health > unsigned((25 - nAmmo) << 4)) { actDamageSprite(actor, actor, kDamageSpirit, ((25 - nAmmo) << 4)); nAmmo = 25; } missile->xspr.data3 = nAmmo; UseAmmo(pPlayer, 8, nAmmo); } else { missile->xspr.data3 = pPlayer->ammoCount[8]; pPlayer->ammoCount[8] = 0; } pPlayer->hasWeapon[kWeapLifeLeech] = 0; } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void FireBeast(int, PLAYER* pPlayer) { DBloodActor* actor = pPlayer->actor; double r1 = Random2F(2000, 14); double r2 = Random2F(2000, 14); double r3 = Random2F(2000, 14); actFireVector(actor, 0, pPlayer->zWeapon - pPlayer->actor->spr.pos.Z, pPlayer->aim + DVector3(r1, r2, r3), kVectorBeastSlash); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static const uint8_t gWeaponUpgrade[][13] = { { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, }; int WeaponUpgrade(PLAYER* pPlayer, int newWeapon) { int weaponswitch = WeaponSwitch(pPlayer - gPlayer); int weapon = pPlayer->curWeapon; if (!checkLitSprayOrTNT(pPlayer) && (weaponswitch & 1) && (gWeaponUpgrade[pPlayer->curWeapon][newWeapon] || (weaponswitch & 2))) weapon = newWeapon; return weapon; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static const int OrderNext[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 1 }; static const int OrderPrev[] = { 12, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1 }; static int WeaponFindNext(PLAYER* pPlayer, int* a2, int bDir) { int weapon = pPlayer->curWeapon; do { if (bDir) weapon = OrderNext[weapon]; else weapon = OrderPrev[weapon]; if (weaponModes[weapon].update && pPlayer->hasWeapon[weapon]) { if (weapon == kWeapLifeLeech) { if (CheckAmmo(pPlayer, weaponModes[weapon].ammoType, 1)) break; } else { if (checkAmmo2(pPlayer, weaponModes[weapon].ammoType, 1)) break; } } } while (weapon != pPlayer->curWeapon); if (weapon == pPlayer->curWeapon) { if (!weaponModes[weapon].update || !CheckAmmo(pPlayer, weaponModes[weapon].ammoType, 1)) weapon = kWeapPitchFork; } if (a2) *a2 = 0; return weapon; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static int WeaponFindLoaded(PLAYER* pPlayer, int* a2) { int v4 = 1; int v14 = 0; if (weaponModes[pPlayer->curWeapon].update > 1) { for (int i = 0; i < weaponModes[pPlayer->curWeapon].update; i++) { if (CheckAmmo(pPlayer, weaponModes[pPlayer->curWeapon].ammoType, 1)) { v14 = i; v4 = pPlayer->curWeapon; break; } } } if (v4 == kWeapPitchFork) { int vc = 0; for (int i = 0; i < 14; i++) { int weapon = pPlayer->weaponOrder[vc][i]; if (pPlayer->hasWeapon[weapon]) { for (int j = 0; j < weaponModes[weapon].update; j++) { if (CheckWeaponAmmo(pPlayer, weapon, weaponModes[weapon].ammoType, 1)) { if (a2) *a2 = j; return weapon; } } } } } if (a2) *a2 = v14; return v4; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int processSprayCan(PLAYER* pPlayer) { const char bUseShootAsThrow = !VanillaMode() && (pPlayer->input.actions & SB_FIRE); switch (pPlayer->weaponState) { case 5: if (!(pPlayer->input.actions & SB_ALTFIRE) || bUseShootAsThrow) pPlayer->weaponState = 6; return 1; case 6: if ((pPlayer->input.actions & SB_ALTFIRE) && !bUseShootAsThrow) { pPlayer->weaponState = 3; pPlayer->fuseTime = pPlayer->weaponTimer; StartQAV(pPlayer, kQAVCANDROP, nClientDropCan); } else if (pPlayer->input.actions & SB_FIRE) { pPlayer->weaponState = 7; pPlayer->fuseTime = 0; pPlayer->throwTime = PlayClock; } return 1; case 7: { setThrowPower(pPlayer); if (!(pPlayer->input.actions & SB_FIRE)) { if (!pPlayer->fuseTime) pPlayer->fuseTime = pPlayer->weaponTimer; pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVCANTHRO, nClientThrowCan); } return 1; } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool processTNT(PLAYER* pPlayer) { const char bUseShootAsThrow = !VanillaMode() && (pPlayer->input.actions & SB_FIRE); switch (pPlayer->weaponState) { case 4: if (!(pPlayer->input.actions & SB_ALTFIRE) || bUseShootAsThrow) pPlayer->weaponState = 5; return 1; case 5: if ((pPlayer->input.actions & SB_ALTFIRE) && !bUseShootAsThrow) { pPlayer->weaponState = 1; pPlayer->fuseTime = pPlayer->weaponTimer; StartQAV(pPlayer, kQAVBUNDROP, nClientDropBundle); } else if (pPlayer->input.actions & SB_FIRE) { pPlayer->weaponState = 6; pPlayer->fuseTime = 0; pPlayer->throwTime = PlayClock; } return 1; case 6: { setThrowPower(pPlayer); if (!(pPlayer->input.actions & SB_FIRE)) { if (!pPlayer->fuseTime) pPlayer->fuseTime = pPlayer->weaponTimer; pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVBUNTHRO, nClientThrowBundle); } return 1; } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool processProxy(PLAYER* pPlayer) { switch (pPlayer->weaponState) { case 9: setThrowPower(pPlayer); pPlayer->weaponTimer = 0; pPlayer->qavTimer = 0; if (!(pPlayer->input.actions & SB_FIRE)) { pPlayer->weaponState = 8; StartQAV(pPlayer, kQAVPROXTHRO, nClientThrowProx); } return true; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool processRemote(PLAYER* pPlayer) { switch (pPlayer->weaponState) { case 13: setThrowPower(pPlayer); if (!(pPlayer->input.actions & SB_FIRE)) { pPlayer->weaponState = 11; StartQAV(pPlayer, kQAVREMTHRO, nClientThrowRemote); } return true; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool processLeech(PLAYER* pPlayer) { switch (pPlayer->weaponState) { case 4: pPlayer->weaponState = 6; StartQAV(pPlayer, kQAVSTAFIRE1, nClientFireLifeLeech, 1); return 1; case 6: if (!(pPlayer->input.actions & SB_ALTFIRE)) { pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVSTAFPOST); return 1; } break; case 8: pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVSTAFPOST); return 1; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- static bool processTesla(PLAYER* pPlayer) { switch (pPlayer->weaponState) { case 4: pPlayer->weaponState = 5; if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla, 1); else StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla, 1); return 1; case 5: if (!(pPlayer->input.actions & SB_FIRE)) { pPlayer->weaponState = 2; if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNPST); else StartQAV(pPlayer, kQAVSGUNPOST); return 1; } break; case 7: pPlayer->weaponState = 2; if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNPST); else StartQAV(pPlayer, kQAVSGUNPOST); return 1; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void WeaponProcess(PLAYER* pPlayer) { pPlayer->flashEffect = ClipLow(pPlayer->flashEffect - 1, 0); #ifdef NOONE_EXTENSIONS if (gPlayerCtrl[pPlayer->nPlayer].qavScene.initiator != nullptr && pPlayer->actor->xspr.health > 0) { playerQavSceneProcess(pPlayer, &gPlayerCtrl[pPlayer->nPlayer].qavScene); UpdateAimVector(pPlayer); return; } #endif int newweap = pPlayer->input.getNewWeapon(); if (newweap > 0 && newweap <= WeaponSel_MaxBlood) pPlayer->newWeapon = newweap; if (pPlayer->actor->xspr.health == 0) { pPlayer->qavLoop = 0; sfxKill3DSound(pPlayer->actor, 1, -1); } if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->curWeapon)) { if (checkLitSprayOrTNT(pPlayer)) { if (pPlayer->curWeapon == kWeapSpraycan) { pPlayer->fuseTime = pPlayer->weaponTimer; DropCan(1, pPlayer); pPlayer->weaponState = 3; } else if (pPlayer->curWeapon == kWeapDynamite) { pPlayer->fuseTime = pPlayer->weaponTimer; DropBundle(1, pPlayer); pPlayer->weaponState = 3; } } WeaponLower(pPlayer); pPlayer->throwPower = 0; } WeaponPlay(pPlayer); UpdateAimVector(pPlayer); pPlayer->weaponTimer -= 4; bool bShoot = pPlayer->input.actions & SB_FIRE; bool bShoot2 = pPlayer->input.actions & SB_ALTFIRE; const int prevNewWeaponVal = pPlayer->input.getNewWeapon(); // used to fix scroll issue for banned weapons if ((bShoot || bShoot2 || prevNewWeaponVal) && pPlayer->weaponQav == qavGetCorrectID(kQAVVDIDLE2)) pPlayer->weaponTimer = 0; if (pPlayer->qavLoop && pPlayer->actor->xspr.health > 0) { if (bShoot && CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1)) { auto pQAV = getQAV(pPlayer->weaponQav); while (pPlayer->weaponTimer <= 0) { pPlayer->weaponTimer += pQAV->duration; pPlayer->qavTimer += pQAV->duration; } } else { pPlayer->weaponTimer = 0; pPlayer->qavTimer = 0; pPlayer->qavLoop = 0; } return; } pPlayer->weaponTimer = ClipLow(pPlayer->weaponTimer, 0); switch (pPlayer->curWeapon) { case kWeapSpraycan: if (processSprayCan(pPlayer)) return; break; case kWeapDynamite: if (processTNT(pPlayer)) return; break; case kWeapProximity: if (processProxy(pPlayer)) return; break; case kWeapRemote: if (processRemote(pPlayer)) return; break; } if (pPlayer->weaponTimer > 0) return; if (pPlayer->actor->xspr.health == 0 || pPlayer->curWeapon == kWeapNone) pPlayer->weaponQav = kQAVNone; switch (pPlayer->curWeapon) { case kWeapLifeLeech: if (processLeech(pPlayer)) return; break; case kWeapTeslaCannon: if (processTesla(pPlayer)) return; break; } if (VanillaMode()) { if (pPlayer->nextWeapon) { sfxKill3DSound(pPlayer->actor, -1, 441); pPlayer->weaponState = 0; pPlayer->newWeapon = pPlayer->nextWeapon; pPlayer->nextWeapon = kWeapNone; } } if (pPlayer->input.getNewWeapon() == WeaponSel_Next) { pPlayer->input.setNewWeapon(kWeapNone); if (VanillaMode()) { pPlayer->weaponState = 0; } pPlayer->nextWeapon = kWeapNone; int t; int weapon = WeaponFindNext(pPlayer, &t, 1); pPlayer->weaponMode[weapon] = t; if (VanillaMode()) { if (pPlayer->curWeapon) { WeaponLower(pPlayer); pPlayer->nextWeapon = weapon; return; } } pPlayer->newWeapon = weapon; } else if (pPlayer->input.getNewWeapon() == WeaponSel_Prev) { pPlayer->input.setNewWeapon(kWeapNone); if (VanillaMode()) { pPlayer->weaponState = 0; } pPlayer->nextWeapon = kWeapNone; int t; int weapon = WeaponFindNext(pPlayer, &t, 0); pPlayer->weaponMode[weapon] = t; if (VanillaMode()) { if (pPlayer->curWeapon) { WeaponLower(pPlayer); pPlayer->nextWeapon = weapon; return; } } pPlayer->newWeapon = weapon; } else if (pPlayer->input.getNewWeapon() == WeaponSel_Alt) { int weapon; switch (pPlayer->curWeapon) { case kWeapDynamite: weapon = kWeapProximity; break; case kWeapProximity: weapon = kWeapRemote; break; case kWeapRemote: weapon = kWeapDynamite; break; default: return; } pPlayer->input.setNewWeapon(kWeapNone); pPlayer->weaponState = 0; pPlayer->nextWeapon = kWeapNone; int t = 0; pPlayer->weaponMode[weapon] = t; if (pPlayer->curWeapon) { WeaponLower(pPlayer); pPlayer->nextWeapon = weapon; return; } pPlayer->newWeapon = weapon; } if (!VanillaMode()) { if (pPlayer->nextWeapon) { sfxKill3DSound(pPlayer->actor, -1, 441); pPlayer->newWeapon = pPlayer->nextWeapon; pPlayer->nextWeapon = kWeapNone; } } if (pPlayer->weaponState == -1) { pPlayer->weaponState = 0; int t; int weapon = WeaponFindLoaded(pPlayer, &t); pPlayer->weaponMode[weapon] = t; if (pPlayer->curWeapon) { WeaponLower(pPlayer); pPlayer->nextWeapon = weapon; return; } pPlayer->newWeapon = weapon; } if (pPlayer->newWeapon) { if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->newWeapon) && !checkLitSprayOrTNT(pPlayer) && !VanillaMode()) // skip banned weapons when underwater and using next/prev weapon key inputs { if (prevNewWeaponVal == WeaponSel_Next || prevNewWeaponVal == WeaponSel_Prev) // if player switched weapons { int saveweapon = pPlayer->curWeapon; pPlayer->curWeapon = pPlayer->newWeapon; // set current banned weapon to curweapon so WeaponFindNext() can find the next weapon for (int i = 0; i < 3; i++) // attempt twice to find a new weapon { pPlayer->curWeapon = WeaponFindNext(pPlayer, NULL, (char)(prevNewWeaponVal == WeaponSel_Next)); if (!BannedUnderwater(pPlayer->curWeapon)) // if new weapon is not a banned weapon, set to new current weapon { pPlayer->newWeapon = pPlayer->curWeapon; pPlayer->weaponMode[pPlayer->newWeapon] = 0; break; } } pPlayer->curWeapon = saveweapon; } } if (pPlayer->newWeapon == kWeapDynamite) { if (pPlayer->curWeapon == kWeapDynamite) { if (checkAmmo2(pPlayer, 10, 1)) pPlayer->newWeapon = kWeapProximity; else if (checkAmmo2(pPlayer, 11, 1)) pPlayer->newWeapon = kWeapRemote; } else if (pPlayer->curWeapon == kWeapProximity) { if (checkAmmo2(pPlayer, 11, 1)) pPlayer->newWeapon = kWeapRemote; else if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0) pPlayer->newWeapon = kWeapDynamite; } else if (pPlayer->curWeapon == kWeapRemote) { if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0) pPlayer->newWeapon = kWeapDynamite; else if (checkAmmo2(pPlayer, 10, 1)) pPlayer->newWeapon = kWeapProximity; } else { if (checkAmmo2(pPlayer, 5, 1) && pPlayer->isUnderwater == 0) pPlayer->newWeapon = kWeapDynamite; else if (checkAmmo2(pPlayer, 10, 1)) pPlayer->newWeapon = kWeapProximity; else if (checkAmmo2(pPlayer, 11, 1)) pPlayer->newWeapon = kWeapRemote; } } else if ((pPlayer->newWeapon == kWeapSpraycan) && !VanillaMode()) { if ((pPlayer->curWeapon == kWeapSpraycan) && (pPlayer->weaponState == 2)) // fix spray can state glitch when switching from spray to tnt and back quickly { pPlayer->weaponState = 1; pPlayer->newWeapon = kWeapNone; return; } } if (pPlayer->actor->xspr.health == 0 || pPlayer->hasWeapon[pPlayer->newWeapon] == 0) { pPlayer->newWeapon = kWeapNone; return; } if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->newWeapon) && !checkLitSprayOrTNT(pPlayer)) { pPlayer->newWeapon = kWeapNone; return; } int nWeapon = pPlayer->newWeapon; int v4c = weaponModes[nWeapon].update; if (!pPlayer->curWeapon) { int nAmmoType = weaponModes[nWeapon].ammoType; if (v4c > 1) { if (CheckAmmo(pPlayer, nAmmoType, 1) || nAmmoType == 11) WeaponRaise(pPlayer); pPlayer->newWeapon = kWeapNone; } else { if (CheckWeaponAmmo(pPlayer, nWeapon, nAmmoType, 1)) WeaponRaise(pPlayer); else { pPlayer->weaponState = 0; int t; int weapon = WeaponFindLoaded(pPlayer, &t); pPlayer->weaponMode[weapon] = t; if (pPlayer->curWeapon) { WeaponLower(pPlayer); pPlayer->nextWeapon = weapon; return; } pPlayer->newWeapon = weapon; } } return; } if (nWeapon == pPlayer->curWeapon && v4c <= 1) { pPlayer->newWeapon = kWeapNone; return; } int i = 0; if (nWeapon == pPlayer->curWeapon) i = 1; for (; i <= v4c; i++) { int v6c = (pPlayer->weaponMode[nWeapon] + i) % v4c; if (CheckWeaponAmmo(pPlayer, nWeapon, weaponModes[nWeapon].ammoType, 1)) { WeaponLower(pPlayer); pPlayer->weaponMode[nWeapon] = v6c; return; } } pPlayer->newWeapon = kWeapNone; return; } if (pPlayer->curWeapon && !CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1) && pPlayer->weaponAmmo != 11) { pPlayer->weaponState = -1; return; } if (bShoot) { switch (pPlayer->curWeapon) { case kWeapPitchFork: StartQAV(pPlayer, kQAVPFORK, nClientFirePitchfork); return; case kWeapSpraycan: switch (pPlayer->weaponState) { case 3: pPlayer->weaponState = 4; StartQAV(pPlayer, kQAVCANFIRE, nClientFireSpray, 1); return; } break; case kWeapDynamite: switch (pPlayer->weaponState) { case 3: pPlayer->weaponState = 6; pPlayer->fuseTime = -1; pPlayer->throwTime = PlayClock; StartQAV(pPlayer, kQAVBUNFUSE, nClientExplodeBundle); return; } break; case kWeapProximity: switch (pPlayer->weaponState) { case 7: SetQAV(pPlayer, kQAVPROXIDLE); pPlayer->weaponState = 9; pPlayer->throwTime = PlayClock; return; } break; case kWeapRemote: switch (pPlayer->weaponState) { case 10: SetQAV(pPlayer, kQAVREMIDLE1); pPlayer->weaponState = 13; pPlayer->throwTime = PlayClock; return; case 11: pPlayer->weaponState = 12; StartQAV(pPlayer, kQAVREMFIRE, nClientFireRemote); return; } break; case kWeapShotgun: switch (pPlayer->weaponState) { case 7: pPlayer->weaponState = 6; StartQAV(pPlayer, kQAV2SHOTF2, nClientFireShotgun); return; case 3: pPlayer->weaponState = 2; StartQAV(pPlayer, kQAVSHOTF1, nClientFireShotgun); return; case 2: pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVSHOTF2, nClientFireShotgun); return; } break; case kWeapTommyGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2)) StartQAV(pPlayer, kQAV2TOMFIRE, nClientFireTommy, 1); else StartQAV(pPlayer, kQAVTOMFIRE, nClientFireTommy, 1); return; case kWeapFlareGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2)) StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare); else StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare); return; case kWeapVoodooDoll: { static int nChance[] = { 0xa000, 0xc000, 0xe000, 0x10000 }; int nRand = wrand() * 2; int i; for (i = 0; nChance[i] < nRand; i++) { } pPlayer->voodooTarget = pPlayer->aimTarget; if (pPlayer->voodooTarget == nullptr || pPlayer->voodooTarget->spr.statnum != kStatDude) i = 4; StartQAV(pPlayer, kQAVVDFIRE1 + i, nClientFireVoodoo); return; } case kWeapTeslaCannon: switch (pPlayer->weaponState) { case 2: pPlayer->weaponState = 4; if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla); else StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla); return; case 5: if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla); else StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla); return; } break; case kWeapNapalm: if (powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2NAPFIRE, nClientFireNapalm); else StartQAV(pPlayer, kQAVNAPFIRE, nClientFireNapalm); return; case kWeapLifeLeech: sfxPlay3DSound(pPlayer->actor, 494, 2, 0); StartQAV(pPlayer, kQAVSTAFIRE4, nClientFireLifeLeech); return; case kWeapBeast: StartQAV(pPlayer, kQAVBSTATAK1 + Random(4), nClientFireBeast); return; } } if (bShoot2) { switch (pPlayer->curWeapon) { case kWeapPitchFork: StartQAV(pPlayer, kQAVPFORK, nClientFirePitchfork); return; case kWeapSpraycan: switch (pPlayer->weaponState) { case 3: pPlayer->weaponState = 5; StartQAV(pPlayer, kQAVCANFIRE2, nClientExplodeCan); return; } break; case kWeapDynamite: switch (pPlayer->weaponState) { case 3: pPlayer->weaponState = 4; StartQAV(pPlayer, kQAVBUNFUSE, nClientExplodeBundle); return; case 7: pPlayer->weaponState = 8; StartQAV(pPlayer, kQAVPROXDROP, nClientDropProx); return; case 10: pPlayer->weaponState = 11; StartQAV(pPlayer, kQAVREMDROP, nClientDropRemote); return; case 11: if (pPlayer->ammoCount[11] > 0) { pPlayer->weaponState = 10; StartQAV(pPlayer, kQAVREMUP1); } return; } break; case kWeapProximity: switch (pPlayer->weaponState) { case 7: pPlayer->weaponState = 8; StartQAV(pPlayer, kQAVPROXDROP, nClientDropProx); return; } break; case kWeapRemote: switch (pPlayer->weaponState) { case 10: pPlayer->weaponState = 11; StartQAV(pPlayer, kQAVREMDROP, nClientDropRemote); return; case 11: if (pPlayer->ammoCount[11] > 0) { pPlayer->weaponState = 10; StartQAV(pPlayer, kQAVREMUP1); } return; } break; case kWeapShotgun: switch (pPlayer->weaponState) { case 7: pPlayer->weaponState = 6; StartQAV(pPlayer, kQAV2SHOTFIR, nClientFireShotgun); return; case 3: pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVSHOTF3, nClientFireShotgun); return; case 2: pPlayer->weaponState = 1; StartQAV(pPlayer, kQAVSHOTF2, nClientFireShotgun); return; } break; case kWeapTommyGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2)) StartQAV(pPlayer, kQAV2TOMALT, nClientAltFireSpread2); else StartQAV(pPlayer, kQAVTOMSPRED, nClientAltFireSpread2); return; case kWeapVoodooDoll: sfxPlay3DSound(pPlayer->actor, 461, 2, 0); StartQAV(pPlayer, kQAVVDSPEL1, nClientAltFireVoodoo); return; #if 0 case kWeapFlareGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2)) StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare, 0); else StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare, 0); return; #endif case kWeapTeslaCannon: if (checkAmmo2(pPlayer, 7, 35)) { if (checkAmmo2(pPlayer, 7, 70) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNALT, nClientFireTesla); else StartQAV(pPlayer, kQAVSGUNFIR4, nClientFireTesla); } else { if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) StartQAV(pPlayer, kQAV2SGUNFIR, nClientFireTesla); else StartQAV(pPlayer, kQAVSGUNFIR1, nClientFireTesla); } return; case kWeapNapalm: if (powerupCheck(pPlayer, kPwUpTwoGuns)) // by NoOne: allow napalm launcher alt fire act like in v1.0x versions if (gGameOptions.weaponsV10x && !VanillaMode()) StartQAV(pPlayer, kQAV2NAPFIR2, nClientFireNapalm2); else StartQAV(pPlayer, kQAV2NAPFIRE, nClientAltFireNapalm); else StartQAV(pPlayer, kQAVNAPFIRE, (gGameOptions.weaponsV10x && !VanillaMode()) ? nClientFireNapalm : nClientAltFireNapalm); return; case kWeapFlareGun: if (CheckAmmo(pPlayer, 1, 8)) { if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 16)) StartQAV(pPlayer, kQAVFLAR2FIR, nClientAltFireFlare); else StartQAV(pPlayer, kQAVFLARFIR2, nClientAltFireFlare); } else { if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 1, 2)) StartQAV(pPlayer, kQAVFLAR2FIR, nClientFireFlare); else StartQAV(pPlayer, kQAVFLARFIR2, nClientFireFlare); } return; case kWeapLifeLeech: if (gGameOptions.nGameType <= 1 && !checkAmmo2(pPlayer, 8, 1) && pPlayer->actor->xspr.health < (25 << 4)) { sfxPlay3DSound(pPlayer->actor, 494, 2, 0); StartQAV(pPlayer, kQAVSTAFIRE4, nClientFireLifeLeech); } else { StartQAV(pPlayer, kQAVSTAFDOWN); AltFireLifeLeech(1, pPlayer); pPlayer->weaponState = -1; } return; } } WeaponUpdateState(pPlayer); } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void teslaHit(DBloodActor* missileactor, int a2) { auto mpos = missileactor->spr.pos; int nDist = 300; auto pSector = missileactor->sector(); auto owneractor = missileactor->GetOwner(); const bool newSectCheckMethod = !cl_bloodvanillaexplosions && !VanillaMode(); // use new sector checking logic auto sectorMap = GetClosestSpriteSectors(pSector, mpos.XY(), nDist, nullptr, newSectCheckMethod); bool v4 = true; DBloodActor* actor = nullptr; actHitcodeToData(a2, &gHitInfo, &actor); if (a2 == 3 && actor && actor->spr.statnum == kStatDude) v4 = false; BloodStatIterator it(kStatDude); while (auto hitactor = it.Next()) { if (hitactor != owneractor || v4) { if (hitactor->spr.flags & 32) continue; if (CheckSector(sectorMap, hitactor) && CheckProximity(hitactor, mpos, pSector, nDist)) { int length = int((missileactor->spr.pos.XY() - hitactor->spr.pos.XY()).Length()); int nDamage = ClipLow((nDist - length + 20) >> 1, 10); if (hitactor == owneractor) nDamage /= 2; actDamageSprite(owneractor, hitactor, kDamageTesla, nDamage << 4); } } } it.Reset(kStatThing); while (auto hitactor = it.Next()) { if (hitactor->spr.flags & 32) continue; if (CheckSector(sectorMap, hitactor) && CheckProximity(hitactor, mpos, pSector, nDist)) { if (!hitactor->xspr.locked) { int length = int((missileactor->spr.pos.XY() - hitactor->spr.pos.XY()).Length()); int nDamage = ClipLow(nDist - length + 20, 20); actDamageSprite(owneractor, hitactor, kDamageTesla, nDamage << 4); } } } } END_BLD_NS